From 49b3512a9a19d1f871db10c123e86d097928a19f Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Thu, 28 Mar 2024 15:14:43 -0400 Subject: [PATCH] Overhaul how VERSION.TXT is generated Generate VERSION.TXT at build time rather than configure time. Since configuring only needs to happen infrequently, it was possible that the contents would be significantly out of date if a user configures, then after some time, fetches the latest changes and builds again. By moving generation to build time, we ensure that the time stamp (which includes a time and not just a day) is always maximally correct, and likewise that the git SHA is correct. Additionally, three other changes are made to the VERSION.TXT contents. First, rather than just a time stamp, the default value is now an actual version identifier following the usual pattern of experimental builds (i.e. 0.0.YYYYMMDD.HHMMSS+git). Second, if the environment variable DRAKE_VERSION is set, its value overrides the value that would otherwise appears in VERSION.TXT. This mechanism is intended to allow official builds a way to inject release version numbers, as opposed to the current mechanism of replacing VERSION.TXT. Third, the git SHA can also be overridden by the environment variable DRAKE_GIT_SHA. This is needed by wheel builds, because the Docker environment has only the raw source files and not the accompanying git content. Finally, ensure that wheel builds export the wheel version and git SHA to the build environment so that they are picked up when generating VERSION.TXT. (Strictly speaking, the latter is only needed, and only done, for Linux builds.) --- CMakeLists.txt | 29 +++++++----------- tools/install/libdrake/VERSION.TXT.in | 2 +- tools/install/libdrake/generate_version.cmake | 30 +++++++++++++++++++ tools/wheel/Dockerfile | 2 ++ tools/wheel/wheel_builder/linux.py | 11 +++++++ tools/wheel/wheel_builder/macos.py | 3 ++ 6 files changed, 57 insertions(+), 20 deletions(-) create mode 100644 tools/install/libdrake/generate_version.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index a7ea334e923b..ee139aa0528c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -563,33 +563,24 @@ configure_file(cmake/bazel.rc.in drake_build_cwd/.bazelrc @ONLY) configure_file(cmake/WORKSPACE.in drake_build_cwd/WORKSPACE.bazel @ONLY) file(CREATE_LINK "${PROJECT_SOURCE_DIR}/.bazeliskrc" drake_build_cwd/.bazeliskrc SYMBOLIC) -set(GIT_DIR "${PROJECT_SOURCE_DIR}/.git") -set(GIT_REVISION HEAD) - find_package(Git) -if(GIT_FOUND AND EXISTS "${GIT_DIR}") - execute_process(COMMAND - "${GIT_EXECUTABLE}" "--git-dir=${GIT_DIR}" rev-parse HEAD - RESULT_VARIABLE GIT_REV_PARSE_RESULT_VARIABLE - OUTPUT_VARIABLE GIT_REV_PARSE_OUTPUT_VARIABLE - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - if(GIT_REV_PARSE_RESULT_VARIABLE EQUAL 0) - set(GIT_REVISION "${GIT_REV_PARSE_OUTPUT_VARIABLE}") - endif() -endif() - -string(TIMESTAMP BUILD_TIMESTAMP "%Y%m%d%H%M%S") - -configure_file(tools/install/libdrake/VERSION.TXT.in VERSION.TXT @ONLY) +set(GIT_DIR "${PROJECT_SOURCE_DIR}/.git") execute_process( COMMAND "${Bazel_EXECUTABLE}" info --announce_rc WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/drake_build_cwd" ) +add_custom_target(drake_version ALL + COMMAND "${CMAKE_COMMAND}" + "-DGIT_DIR=${GIT_DIR}" + "-DGIT_EXECUTABLE=${GIT_EXECUTABLE}" + "-DINPUT_FILE=${PROJECT_SOURCE_DIR}/tools/install/libdrake/VERSION.TXT.in" + "-DOUTPUT_FILE=${PROJECT_BINARY_DIR}/VERSION.TXT" + -P "${PROJECT_SOURCE_DIR}/tools/install/libdrake/generate_version.cmake" +) + add_custom_target(drake_cxx_python ALL COMMAND "${Bazel_EXECUTABLE}" build ${BAZEL_INSTALL_TARGET} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/drake_build_cwd" diff --git a/tools/install/libdrake/VERSION.TXT.in b/tools/install/libdrake/VERSION.TXT.in index f9bf6709cd23..ae8c763f1253 100644 --- a/tools/install/libdrake/VERSION.TXT.in +++ b/tools/install/libdrake/VERSION.TXT.in @@ -1 +1 @@ -@BUILD_TIMESTAMP@ @GIT_REVISION@ +@DRAKE_VERSION@ @GIT_REVISION@ diff --git a/tools/install/libdrake/generate_version.cmake b/tools/install/libdrake/generate_version.cmake new file mode 100644 index 000000000000..2e199f001638 --- /dev/null +++ b/tools/install/libdrake/generate_version.cmake @@ -0,0 +1,30 @@ +set(GIT_REVISION HEAD) +set(BUILD_IDENTIFIER unknown) + +if(DEFINED ENV{DRAKE_GIT_SHA}) + set(GIT_REVISION "$ENV{DRAKE_GIT_SHA}") +else() + if(GIT_EXECUTABLE AND EXISTS "${GIT_DIR}") + execute_process(COMMAND + "${GIT_EXECUTABLE}" "--git-dir=${GIT_DIR}" rev-parse HEAD + RESULT_VARIABLE GIT_REV_PARSE_RESULT_VARIABLE + OUTPUT_VARIABLE GIT_REV_PARSE_OUTPUT_VARIABLE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if(GIT_REV_PARSE_RESULT_VARIABLE EQUAL 0) + set(GIT_REVISION "${GIT_REV_PARSE_OUTPUT_VARIABLE}") + string(SUBSTRING ${GIT_REVISION} 0 8 GIT_REVISION_SHORT) + set(BUILD_IDENTIFIER git${GIT_REVISION_SHORT}) + endif() + endif() +endif() + +if(DEFINED ENV{DRAKE_VERSION}) + set(DRAKE_VERSION "$ENV{DRAKE_VERSION}") +else() + string(TIMESTAMP BUILD_TIMESTAMP "%Y%m%d.%H%M%S") + set(DRAKE_VERSION "0.0.${BUILD_TIMESTAMP}+${BUILD_IDENTIFIER}") +endif() + +configure_file(${INPUT_FILE} ${OUTPUT_FILE} @ONLY) diff --git a/tools/wheel/Dockerfile b/tools/wheel/Dockerfile index 47eb21840f4c..0ecd9c70713e 100644 --- a/tools/wheel/Dockerfile +++ b/tools/wheel/Dockerfile @@ -52,6 +52,7 @@ ADD image/drake-src.tar /opt/drake-wheel-build/drake/ FROM clean AS wheel ARG DRAKE_VERSION +ARG DRAKE_GIT_SHA RUN --mount=type=ssh \ --mount=type=cache,target=/var/cache/bazel \ @@ -62,5 +63,6 @@ ADD image/setup.py /opt/drake-wheel-build/wheel/ ADD content /opt/drake-wheel-content ENV DRAKE_VERSION=${DRAKE_VERSION} +ENV DRAKE_GIT_SHA=${DRAKE_GIT_SHA} RUN /image/build-wheel.sh diff --git a/tools/wheel/wheel_builder/linux.py b/tools/wheel/wheel_builder/linux.py index c19a120ae1da..ac19f23a564a 100644 --- a/tools/wheel/wheel_builder/linux.py +++ b/tools/wheel/wheel_builder/linux.py @@ -85,6 +85,16 @@ def _cleanup(): _docker('image', 'rm', *_images_to_remove) +def _git_sha(path): + """ + Determines the git SHA of the repository which contains or is rooted at the + specified `path`. + """ + command = ['git', 'rev-parse', 'HEAD'] + raw = subprocess.check_output(command, cwd=path) + return raw.decode(sys.stdout.encoding).strip() + + def _git_root(path): """ Determines the canonical repository root of the working tree which includes @@ -213,6 +223,7 @@ def _build_image(target, identifier, options): args = [ '--ssh', 'default', '--build-arg', f'DRAKE_VERSION={options.version}', + '--build-arg', f'DRAKE_GIT_SHA={_git_sha(resource_root)}', ] + _target_args(target, BUILD) if not options.keep_containers: args.append('--force-rm') diff --git a/tools/wheel/wheel_builder/macos.py b/tools/wheel/wheel_builder/macos.py index 7066abce733b..3fa7c0bdb048 100644 --- a/tools/wheel/wheel_builder/macos.py +++ b/tools/wheel/wheel_builder/macos.py @@ -180,6 +180,9 @@ def build(options): sdk_path = subprocess.check_output(['xcrun', '--show-sdk-path'], text=True) environment['SDKROOT'] = sdk_path.strip() + # Inject the build version into the environment. + environment['DRAKE_VERSION'] = options.version + # Build the wheel dependencies. if options.dependencies: build_deps_script = os.path.join(resource_root, 'macos',