Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a24d84b
All rapids-cmake tests will properly hook into using the rapids-cmake…
robertmaynard Jan 29, 2024
cbba2a0
Add rapids_cpm_generate_pinned_versions to support reproducible builds
robertmaynard Jan 23, 2024
cfe884b
Ensure we can apply git format-patches always
robertmaynard Jan 31, 2024
0d0111e
add debug code to failing test
robertmaynard Jan 31, 2024
f93edc8
Fix logic found by CI
robertmaynard Jan 31, 2024
86f4c2e
Fix docs issues
robertmaynard Feb 6, 2024
cb613cf
Fix docs issues
robertmaynard Feb 6, 2024
2a97e9d
Fix docs issues
robertmaynard Feb 6, 2024
51cf575
Refactor internal logic to remove unneeded hooks
robertmaynard Feb 6, 2024
6d65041
Apply suggestions from code review
robertmaynard Feb 22, 2024
cf29f15
Correct git captilization
robertmaynard Feb 22, 2024
90b4a98
Remove unneeded files from cpm pin format-patches test
robertmaynard Feb 22, 2024
cdea83c
Update testing/export/write_language-multiple-nested-enables/CMakeLis…
robertmaynard Feb 22, 2024
e351cdb
Cleaned up how we handle trailing commas
robertmaynard Feb 22, 2024
e15e952
Correct style issues found by CI
robertmaynard Feb 22, 2024
5ce2caf
Refactor gpv name to pinning
robertmaynard Feb 22, 2024
9101346
Remove unneeded whitespace from json
robertmaynard Feb 26, 2024
1fc2b4b
Refactor rapids_cpm_pinning_create_and_set_member
robertmaynard Feb 26, 2024
dd87f9d
Apply suggestions from code review
robertmaynard Feb 26, 2024
89d93b1
Don't quote boolean types in the json we write
robertmaynard Feb 26, 2024
af13b6c
Merge branch 'branch-24.04' into fea/generate_pinned_manifest_file
robertmaynard Feb 27, 2024
22a849c
Apply pre-commit hooks for pretty-format-json.
bdice Feb 27, 2024
a6b4cb9
Correct issues found by code review
robertmaynard Feb 28, 2024
7cb161f
Leverage CMake for pretty enough printing
robertmaynard Feb 29, 2024
e0a9c47
More corrections found in code review
robertmaynard Mar 1, 2024
dc0e01f
Document functions in pinning_write_file.cmake
robertmaynard Mar 1, 2024
6a6a6ef
Update tests to pass in the projects to verify
robertmaynard Mar 1, 2024
df1567a
Update tests to pass in the projects to verify
robertmaynard Mar 1, 2024
5a5081b
Remove unneeded variable expansion from tests
robertmaynard Mar 1, 2024
6ad1956
Correctly handle CPM calls that resolve to find_package
robertmaynard Mar 1, 2024
49b4035
Correct style issues found by CI
robertmaynard Mar 1, 2024
7542f30
Add messaging around non source packages
robertmaynard Mar 4, 2024
97a8b45
Correct style issues found by CI
robertmaynard Mar 4, 2024
80ca70c
Correct style issues found by CI
robertmaynard Mar 4, 2024
75e0db8
reduce verbosity of rapids_cpm_generate_pinned_versions messasges
robertmaynard Mar 4, 2024
f341f37
Correct style issues found by CI
robertmaynard Mar 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion cmake-format-rapids-cmake.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,21 @@
"PATCH_COMMAND": "1"
}
},
"rapids_cpm_init": {
"rapids_cpm_generate_pinned_versions": {
"pargs": {
"nargs": 0
},
"kwargs": {
"OUTPUT": 1
}
},
"rapids_cpm_init": {
"pargs": {
"nargs": 0,
"flags": [
"GENERATE_PINNED_VERSIONS"
]
},
"kwargs": {
"OVERRIDE": 1
}
Expand Down
1 change: 1 addition & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ tracking of these dependencies for correct export support.

/command/rapids_cpm_init
/command/rapids_cpm_find
/command/rapids_cpm_generate_pinned_versions
/command/rapids_cpm_package_override

.. _`cpm_pre-configured_packages`:
Expand Down
1 change: 1 addition & 0 deletions docs/command/rapids_cpm_generate_pinned_versions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.. cmake-module:: ../../rapids-cmake/cpm/generate_pinned_versions.cmake
31 changes: 31 additions & 0 deletions rapids-cmake/cpm/detail/pinning_root_dir_hook.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#=============================================================================
# Copyright (c) 2024, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#=============================================================================
include_guard(GLOBAL)

# Make sure we always have CMake 3.23 policies when executing this file since we can be executing in
# directories of users of rapids-cmake which have a lower minimum cmake version and therefore
# different policies
#
cmake_policy(PUSH)
cmake_policy(VERSION 3.23)

# Include the needed functions that write out the the pinned versions file
include("${rapids-cmake-dir}/cpm/detail/pinning_write_file.cmake")

# Compute and write out the pinned versions file
rapids_cpm_pinning_write_file()

cmake_policy(POP)
206 changes: 206 additions & 0 deletions rapids-cmake/cpm/detail/pinning_write_file.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
#=============================================================================
# Copyright (c) 2024, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#=============================================================================
include_guard(GLOBAL)

#[=======================================================================[.rst:
rapids_cpm_pinning_extract_source_git_info
------------------------------------------

.. versionadded:: v24.04.00


#]=======================================================================]
function(rapids_cpm_pinning_extract_source_git_info package git_url_var git_sha_var)
set(source_dir "${CPM_PACKAGE_${package}_SOURCE_DIR}")
set(_RAPIDS_URL)
set(_RAPIDS_SHA)
if(EXISTS "${source_dir}")
execute_process(COMMAND ${GIT_EXECUTABLE} ls-remote --get-url
WORKING_DIRECTORY ${source_dir}
ERROR_QUIET
OUTPUT_VARIABLE _RAPIDS_URL
OUTPUT_STRIP_TRAILING_WHITESPACE)
# Need to handle when we have applied N patch sets to the git repo and therefore the latest
# commit is just local
#
# Find all commits on our branch back to the common parent ( what we cloned )
#
execute_process(COMMAND ${GIT_EXECUTABLE} show-branch --current --sha1-name
WORKING_DIRECTORY ${source_dir}
ERROR_QUIET
OUTPUT_VARIABLE _rapids_commit_stack
OUTPUT_STRIP_TRAILING_WHITESPACE)
# The last entry in the output that has "* [" is our commit
#
# Find that line and convert the `* [short-sha1] Commit Message` to a list that is ` *
# ;short-sha1;Commit Message` and extract the short sha1
string(FIND "${_rapids_commit_stack}" "* [" position REVERSE)
if(position LESS 0)
# No changes to the repo so use the `HEAD` keyword
set(short_sha HEAD)
else()
string(SUBSTRING "${_rapids_commit_stack}" ${position} -1 _rapids_commit_stack)
string(REGEX REPLACE "(\\[|\\])" ";" _rapids_commit_stack "${_rapids_commit_stack}")
list(GET _rapids_commit_stack 1 short_sha)
endif()

# Convert from the short sha1 ( could be keyword `HEAD` ) to a full SHA1
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse ${short_sha}
WORKING_DIRECTORY ${source_dir}
ERROR_QUIET
OUTPUT_VARIABLE _RAPIDS_SHA
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
# Only set the provided variables if we extracted the information
if(_RAPIDS_URL)
set(${git_url_var} "${_RAPIDS_URL}" PARENT_SCOPE)
endif()
if(_RAPIDS_SHA)
set(${git_sha_var} "${_RAPIDS_SHA}" PARENT_SCOPE)
endif()

endfunction()

#[=======================================================================[.rst:
rapids_cpm_pinning_create_and_set_member
----------------------------------------

.. versionadded:: v24.04.00

Insert the given json key value pair into the provided json data variable

#]=======================================================================]
function(rapids_cpm_pinning_create_and_set_member json_blob_var key value)

# Identify special values types that shouldn't be treated as a string
# https://gitlab.kitware.com/cmake/cmake/-/issues/25716
if(value MATCHES "(^true$|^false$|^null$|^\\{|^\\[)")
# value is a json type that doesn't need quotes
string(JSON json_blob ERROR_VARIABLE err_var SET "${${json_blob_var}}" ${key} ${value})
else()
# We need to quote 'value' so that it is a valid string json element.
string(JSON json_blob ERROR_VARIABLE err_var SET "${${json_blob_var}}" ${key} "\"${value}\"")
endif()
set(${json_blob_var} "${json_blob}" PARENT_SCOPE)
endfunction()

#[=======================================================================[.rst:
rapids_cpm_pinning_add_json_entry
---------------------------------

.. versionadded:: v24.04.00

#]=======================================================================]
function(rapids_cpm_pinning_add_json_entry json_var package_name url_var sha_var)
include("${rapids-cmake-dir}/cpm/detail/get_default_json.cmake")
include("${rapids-cmake-dir}/cpm/detail/get_override_json.cmake")
get_default_json(${package_name} json_data)
get_override_json(${package_name} override_json_data)

set(url_string)
set(sha_string)
if(${url_var})
string(CONFIGURE [=["git_url": "${${url_var}}",]=] url_string)
endif()
if(${sha_var})
string(CONFIGURE [=["git_tag": "${${sha_var}}",]=] sha_string)
endif()
# We start with a default template, and only add members that don't exist
string(CONFIGURE [=[{
"version": "${CPM_PACKAGE_${package_name}_VERSION}",
${url_string}
${sha_string}
"git_shallow": false,
"always_download": true
}]=]
pinned_json_entry)

foreach(data IN LISTS override_json_data json_data)
if(NOT data)
# Need to handle both json_data and the override being empty
continue()
endif()
string(JSON entry_count LENGTH "${data}")
math(EXPR entry_count "${entry_count} - 1")
# cmake-lint: disable=E1120
foreach(index RANGE ${entry_count})
string(JSON member MEMBER "${data}" ${index})
string(JSON existing_value ERROR_VARIABLE dont_have GET "${pinned_json_entry}" ${member})
if(dont_have)
string(JSON value GET "${data}" ${member})
rapids_cpm_pinning_create_and_set_member(pinned_json_entry ${member} ${value})
endif()
endforeach()
endforeach()
set(${json_var} "\"${package_name}\": ${pinned_json_entry}" PARENT_SCOPE)
endfunction()

#[=======================================================================[.rst:
rapids_cpm_pinning_write_file
-----------------------------

.. versionadded:: v24.04.00

This function will write out the pinned version info to the provided files when we are in the root
CMakeLists.txt
#]=======================================================================]
function(rapids_cpm_pinning_write_file)

find_package(Git QUIET REQUIRED)

set(_rapids_json
[=[
{
"root": {
"packages": {
]=])

list(POP_BACK CPM_PACKAGES last_package)
foreach(package IN LISTS CPM_PACKAGES last_package)
# Clear variables so we don't re-use them between packages when one package doesn't have a git
# url or sha
set(git_url)
set(git_sha)
set(not_last_package TRUE)
if(package STREQUAL last_package)
set(not_last_package FALSE)
endif()
rapids_cpm_pinning_extract_source_git_info(${package} git_url git_sha)
rapids_cpm_pinning_add_json_entry(_rapids_entry ${package} git_url git_sha)
if(not_last_package)
string(APPEND _rapids_entry [=[,
]=])
else()
string(APPEND _rapids_entry [=[
]=])
endif()
string(APPEND _rapids_json "${_rapids_entry}")
endforeach()

set(post_amble [=[
}}}]=])
string(APPEND _rapids_json "${post_amble}")

# We extract everything out of the fake `root` element so that we get a pretty JSON format from
# CMake.
string(JSON _rapids_json GET "${_rapids_json}" root)

get_property(write_paths GLOBAL PROPERTY rapids_cpm_generate_pin_files)
foreach(path IN LISTS write_paths)
file(WRITE "${path}" "${_rapids_json}")
endforeach()

endfunction()
107 changes: 107 additions & 0 deletions rapids-cmake/cpm/generate_pinned_versions.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#=============================================================================
# Copyright (c) 2024, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#=============================================================================
include_guard(GLOBAL)

#[=======================================================================[.rst:
rapids_cpm_generate_pinned_versions
-----------------------------------

.. versionadded:: v24.04.00

Generate a json file with all dependencies with pinned version values

.. code-block:: cmake

rapids_cpm_generate_pinned_versions( OUTPUT <json_verions_output_path> )

Generates a json file with all `CPM` dependencies with pinned version values.
This allows for subsequent reproducible builds using the exact same state.

The rapids-cmake default `versions.json` uses branch names or git tag names
for dependencies. This is done so that projects can 'live at head' of dependencies.
By using :cmake:command:`rapids_cpm_package_override` a project can specify a custom
`versions.json` that specifies exact git SHA's so that projects have reproducible builds.

:cmake:command:`rapids_cpm_generate_pinned_versions` can be used to transform a set of
rapids-cmake dependencies from branch names to pinned values. This can be used in subsequent
builds, e.g:

1. Have CI run with `versions.json` which tracks dependency by branch name
2. Store the generated pinned `versions.json` from the CI builds
3. If build is good, create the release branch and commit the generated pinned `versions.json`
to have reproducible builds for that release

``OUTPUT``
Specify a file path where the pinned versions information will be written. Can be called multiple
times and each unique path will be written to.

The generated json file will have the following entries for each package:

.. code-block:: json

{
"version": "<CPM_PACKAGE_<package_name>_VERSION>",
"git_url": "<deduced>",
"git_tag": "<deduced>",
"git_shallow": false,
"always_download": true
}


If the original package (or override) also had any `patches`, or `proprietary_binary`
fields those will be propagated to the generated entry.

.. note::
The git SHA1 computed for each package is found by finding the most recent
commit that can be cloned from the url.

This means that for proper reproducible builds, all patches must be encapsulated
in the input json files or as CPM `PATCH_COMMAND`.

#]=======================================================================]
function(rapids_cpm_generate_pinned_versions)
list(APPEND CMAKE_MESSAGE_CONTEXT "rapids.cpm.generate_pinned_versions")

set(_rapids_options)
set(_rapids_one_value OUTPUT)
set(_rapids_multi_value)
cmake_parse_arguments(_RAPIDS "${_rapids_options}" "${_rapids_one_value}"
"${_rapids_multi_value}" ${ARGN})

if(NOT _RAPIDS_OUTPUT)
message(FATAL_ERROR "rapids_cpm_generate_pinned_versions requires an `OUTPUT` argument")
endif()

find_package(Git QUIET)
if(NOT Git_FOUND)
message(FATAL_ERROR "rapids_cpm_generate_pinned_versions requires 'git' to exist")
endif()

# Append the requested write path for `detail/write_pinned_versions.cmake`
set_property(GLOBAL APPEND PROPERTY rapids_cpm_generate_pin_files "${_RAPIDS_OUTPUT}")

get_property(already_hooked GLOBAL PROPERTY rapids_cpm_generate_pin_hook SET)
if(NOT already_hooked)
# install a hook that writes out the pinned versions at the end of the root level CMakeLists.txt
# execution so we get all CPM packages added. Plus we can compute the paths once, and write out
# `N` times if needed
set(root_dir "${${CMAKE_PROJECT_NAME}_SOURCE_DIR}")
cmake_language(DEFER DIRECTORY ${root_dir} ID rapids_cpm_generate_pinned_versions CALL include
"${rapids-cmake-dir}/cpm/detail/pinning_root_dir_hook.cmake")
set_property(GLOBAL PROPERTY rapids_cpm_generate_pin_hook "ON")
endif()

endfunction()
Loading