From 89db496cf82f87cfa93202c488bd05048d534146 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Wed, 3 Apr 2024 12:28:11 -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, rather than just a time stamp, the first of the two values in VERSION.TXT now defaults to an actual version identifier of the usual form for experimental builds (i.e. 0.0.YYYYMMDD.HHMMSS+git). This can be overridden by undocumented CMake variables, which are used by wheel and CI builds to inject "real" version numbers. (The git SHA can be similarly overridden, which is needed for Linux wheel builds as the Docker build environment has only the raw sources and is therefore unable to obtain the git SHA on its own.) Finally, tweak wheel builds to make use of these new mechanisms. --- CMakeLists.txt | 40 ++++++++++--------- tools/install/libdrake/VERSION.TXT.in | 2 +- tools/install/libdrake/generate_version.cmake | 30 ++++++++++++++ tools/wheel/Dockerfile | 6 ++- tools/wheel/image/build-drake.sh | 2 + tools/wheel/macos/build-wheel.sh | 2 + tools/wheel/wheel_builder/linux.py | 11 +++++ tools/wheel/wheel_builder/macos.py | 3 ++ 8 files changed, 74 insertions(+), 22 deletions(-) create mode 100644 tools/install/libdrake/generate_version.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index a7ea334e923b..6bede481c75c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -563,33 +563,35 @@ 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" ) +set(GENERATE_DRAKE_VERSION_ARGS) +if(DEFINED DRAKE_VERSION_OVERRIDE) + list(APPEND GENERATE_DRAKE_VERSION_ARGS + "-DDRAKE_VERSION_OVERRIDE=${DRAKE_VERSION_OVERRIDE}") +endif() +if(DEFINED DRAKE_GIT_SHA_OVERRIDE) + list(APPEND GENERATE_DRAKE_VERSION_ARGS + "-DDRAKE_GIT_SHA_OVERRIDE=${DRAKE_GIT_SHA_OVERRIDE}") +endif() + +add_custom_target(drake_version ALL + COMMAND "${CMAKE_COMMAND}" + ${GENERATE_DRAKE_VERSION_ARGS} + "-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..9fe03841060e --- /dev/null +++ b/tools/install/libdrake/generate_version.cmake @@ -0,0 +1,30 @@ +set(GIT_REVISION HEAD) +set(BUILD_IDENTIFIER unknown) + +if(DEFINED DRAKE_GIT_SHA_OVERRIDE) + set(GIT_REVISION "${DRAKE_GIT_SHA_OVERRIDE}") +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 DRAKE_VERSION_OVERRIDE) + set(DRAKE_VERSION "${DRAKE_VERSION_OVERRIDE}") +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..dbf976444fa7 100644 --- a/tools/wheel/Dockerfile +++ b/tools/wheel/Dockerfile @@ -52,6 +52,10 @@ ADD image/drake-src.tar /opt/drake-wheel-build/drake/ FROM clean AS wheel ARG DRAKE_VERSION +ARG DRAKE_GIT_SHA + +ENV DRAKE_VERSION=${DRAKE_VERSION} +ENV DRAKE_GIT_SHA=${DRAKE_GIT_SHA} RUN --mount=type=ssh \ --mount=type=cache,target=/var/cache/bazel \ @@ -61,6 +65,4 @@ ADD image/build-wheel.sh /image/ ADD image/setup.py /opt/drake-wheel-build/wheel/ ADD content /opt/drake-wheel-content -ENV DRAKE_VERSION=${DRAKE_VERSION} - RUN /image/build-wheel.sh diff --git a/tools/wheel/image/build-drake.sh b/tools/wheel/image/build-drake.sh index bcf849b6214f..30c947d2bd84 100755 --- a/tools/wheel/image/build-drake.sh +++ b/tools/wheel/image/build-drake.sh @@ -23,6 +23,8 @@ EOF # Install Drake using our wheel-build-specific Python interpreter. cmake ../drake \ + -DDRAKE_VERSION_OVERRIDE="${DRAKE_VERSION}" \ + -DDRAKE_GIT_SHA_OVERRIDE="${DRAKE_GIT_SHA}" \ -DCMAKE_INSTALL_PREFIX=/opt/drake \ -DPython_EXECUTABLE=/usr/local/bin/python make install diff --git a/tools/wheel/macos/build-wheel.sh b/tools/wheel/macos/build-wheel.sh index 05b29862b03d..6b47c243e056 100755 --- a/tools/wheel/macos/build-wheel.sh +++ b/tools/wheel/macos/build-wheel.sh @@ -88,6 +88,8 @@ EOF # Install Drake. cmake "$git_root" \ + -DDRAKE_VERSION_OVERRIDE="${DRAKE_VERSION}" \ + -DDRAKE_GIT_SHA_OVERRIDE="${DRAKE_GIT_SHA}" \ -DCMAKE_INSTALL_PREFIX="/opt/drake-dist/$python" \ -DPython_EXECUTABLE="$pyvenv_root/bin/$python" make install 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',