Skip to content

Commit d416d9b

Browse files
authored
Adding PATCHES keyword. (#558)
* Adding PATCHES keyword. * Formatting fix. * Move cpm_add_patches() outside if/else scopes. * cmake-format: add PATCHES to CPMAddPackage. * Integration tests for PATCHES command. * Use get_filename_component() in place of cmake_path() for use with all cmake versions 3.14 and above. * Added an example and improved comment for cpm_add_patches.
1 parent 76ca486 commit d416d9b

File tree

11 files changed

+226
-1
lines changed

11 files changed

+226
-1
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Afterwards, any targets defined in the dependency can be used directly.
6767
CPMAddPackage(
6868
NAME # The unique name of the dependency (should be the exported target's name)
6969
VERSION # The minimum version of the dependency (optional, defaults to 0)
70+
PATCHES # Patch files to be applied sequentially using patch and PATCH_OPTIONS (optional)
7071
OPTIONS # Configuration options passed to the dependency (optional)
7172
DOWNLOAD_ONLY # If set, the project is downloaded, but not configured (optional)
7273
[...] # Origin parameters forwarded to FetchContent_Declare, see below
@@ -78,6 +79,8 @@ If `GIT_TAG` hasn't been explicitly specified it defaults to `v(VERSION)`, a com
7879
On the other hand, if `VERSION` hasn't been explicitly specified, CPM can automatically identify the version from the git tag in some common cases.
7980
`GIT_TAG` can also be set to a specific commit or a branch name such as `master`, however this isn't recommended, as such packages will only be updated when the cache is cleared.
8081

82+
`PATCHES` takes a list of patch files to apply sequentially. For a basic example, see [Highway](examples/highway/CMakeLists.txt).
83+
8184
If an additional optional parameter `EXCLUDE_FROM_ALL` is set to a truthy value, then any targets defined inside the dependency won't be built by default. See the [CMake docs](https://cmake.org/cmake/help/latest/prop_tgt/EXCLUDE_FROM_ALL.html) for details.
8285

8386
If an additional optional parameter `SYSTEM` is set to a truthy value, the SYSTEM directory property of the subdirectory added will be set to true.

cmake/.cmake-format-additional_commands-cpm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ parse:
3131
EXCLUDE_FROM_ALL: 1
3232
SYSTEM: 1
3333
SOURCE_SUBDIR: 1
34+
PATCHES: +
3435
OPTIONS: +
3536
cpmfindpackage:
3637
pargs:

cmake/CPM.cmake

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,69 @@ function(cpm_check_git_working_dir_is_clean repoPath gitTag isClean)
464464

465465
endfunction()
466466

467+
# Add PATCH_COMMAND to CPM_ARGS_UNPARSED_ARGUMENTS. This method consumes a list of files in ARGN
468+
# then generates a `PATCH_COMMAND` appropriate for `ExternalProject_Add()`. This command is appended
469+
# to the parent scope's `CPM_ARGS_UNPARSED_ARGUMENTS`.
470+
function(cpm_add_patches)
471+
# Return if no patch files are supplied.
472+
if(NOT ARGN)
473+
return()
474+
endif()
475+
476+
# Find the patch program.
477+
find_program(PATCH_EXECUTABLE patch)
478+
if(WIN32 AND NOT PATCH_EXECUTABLE)
479+
# The Windows git executable is distributed with patch.exe. Find the path to the executable, if
480+
# it exists, then search `../../usr/bin` for patch.exe.
481+
find_package(Git QUIET)
482+
if(GIT_EXECUTABLE)
483+
get_filename_component(extra_search_path ${GIT_EXECUTABLE} DIRECTORY)
484+
get_filename_component(extra_search_path ${extra_search_path} DIRECTORY)
485+
get_filename_component(extra_search_path ${extra_search_path} DIRECTORY)
486+
find_program(PATCH_EXECUTABLE patch HINTS "${extra_search_path}/usr/bin")
487+
endif()
488+
endif()
489+
if(NOT PATCH_EXECUTABLE)
490+
message(FATAL_ERROR "Couldn't find `patch` executable to use with PATCHES keyword.")
491+
endif()
492+
493+
# Create a temporary
494+
set(temp_list ${CPM_ARGS_UNPARSED_ARGUMENTS})
495+
496+
# Ensure each file exists (or error out) and add it to the list.
497+
set(first_item True)
498+
foreach(PATCH_FILE ${ARGN})
499+
# Make sure the patch file exists, if we can't find it, try again in the current directory.
500+
if(NOT EXISTS "${PATCH_FILE}")
501+
if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}")
502+
message(FATAL_ERROR "Couldn't find patch file: '${PATCH_FILE}'")
503+
endif()
504+
set(PATCH_FILE "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}")
505+
endif()
506+
507+
# Convert to absolute path for use with patch file command.
508+
get_filename_component(PATCH_FILE "${PATCH_FILE}" ABSOLUTE)
509+
510+
# The first patch entry must be preceded by "PATCH_COMMAND" while the following items are
511+
# preceded by "&&".
512+
if(first_item)
513+
set(first_item False)
514+
list(APPEND temp_list "PATCH_COMMAND")
515+
else()
516+
list(APPEND temp_list "&&")
517+
endif()
518+
# Add the patch command to the list
519+
list(APPEND temp_list "${PATCH_EXECUTABLE}" "-p1" "<" "${PATCH_FILE}")
520+
endforeach()
521+
522+
# Move temp out into parent scope.
523+
set(CPM_ARGS_UNPARSED_ARGUMENTS
524+
${temp_list}
525+
PARENT_SCOPE
526+
)
527+
528+
endfunction()
529+
467530
# method to overwrite internal FetchContent properties, to allow using CPM.cmake to overload
468531
# FetchContent calls. As these are internal cmake properties, this method should be used carefully
469532
# and may need modification in future CMake versions. Source:
@@ -537,7 +600,7 @@ function(CPMAddPackage)
537600
CUSTOM_CACHE_KEY
538601
)
539602

540-
set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND)
603+
set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND PATCHES)
541604

542605
cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}")
543606

@@ -628,6 +691,7 @@ function(CPMAddPackage)
628691
SOURCE_DIR "${PACKAGE_SOURCE}"
629692
EXCLUDE_FROM_ALL "${CPM_ARGS_EXCLUDE_FROM_ALL}"
630693
SYSTEM "${CPM_ARGS_SYSTEM}"
694+
PATCHES "${CPM_ARGS_PATCHES}"
631695
OPTIONS "${CPM_ARGS_OPTIONS}"
632696
SOURCE_SUBDIR "${CPM_ARGS_SOURCE_SUBDIR}"
633697
DOWNLOAD_ONLY "${DOWNLOAD_ONLY}"
@@ -683,6 +747,8 @@ function(CPMAddPackage)
683747
set(CPM_FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/_deps)
684748
endif()
685749

750+
cpm_add_patches(${CPM_ARGS_PATCHES})
751+
686752
if(DEFINED CPM_ARGS_DOWNLOAD_COMMAND)
687753
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS DOWNLOAD_COMMAND ${CPM_ARGS_DOWNLOAD_COMMAND})
688754
elseif(DEFINED CPM_ARGS_SOURCE_DIR)

examples/highway/CMakeLists.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
2+
3+
project(CPMExamplePatchHighway)
4+
5+
# ---- Dependencies ----
6+
7+
include(../../cmake/CPM.cmake)
8+
9+
# Google's highway Includes a SIMD sorting function that is faster than x86-simd-sort for larger
10+
# arrays. See: https://github.com/google/highway/blob/master/g3doc/quick_reference.md
11+
CPMAddPackage(
12+
NAME highway
13+
URL https://github.com/google/highway/archive/refs/tags/1.1.0.tar.gz
14+
URL_HASH SHA256=354a8b4539b588e70b98ec70844273e3f2741302c4c377bcc4e81b3d1866f7c9
15+
PATCHES "highway.patch" # This adds SYSTEM to the includes.
16+
OPTIONS "HWY_ENABLE_EXAMPLES OFF" "HWY_ENABLE_INSTALL OFF" "HWY_ENABLE_TESTS OFF"
17+
)
18+
19+
# ---- Executable ----
20+
21+
if(LINUX)
22+
# This would cause a float compare error inside the highway header code if the patch is NOT
23+
# applied.
24+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wfloat-equal")
25+
endif()
26+
27+
add_executable(CPMExamplePatchHighway "main.cpp")
28+
target_link_libraries(CPMExamplePatchHighway hwy hwy_contrib)

examples/highway/highway.patch

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Common subdirectories: a/.bcr and b/.bcr
2+
Common subdirectories: a/.github and b/.github
3+
diff -u a/CMakeLists.txt b/CMakeLists.txt
4+
--- a/CMakeLists.txt 2024-05-21 12:50:37.738318520 -0500
5+
+++ b/CMakeLists.txt 2024-05-21 12:49:59.914226871 -0500
6+
@@ -350,7 +350,7 @@
7+
target_compile_options(hwy PRIVATE ${HWY_FLAGS})
8+
set_property(TARGET hwy PROPERTY POSITION_INDEPENDENT_CODE ON)
9+
set_target_properties(hwy PROPERTIES VERSION ${LIBRARY_VERSION} SOVERSION ${LIBRARY_SOVERSION})
10+
-target_include_directories(hwy PUBLIC
11+
+target_include_directories(hwy SYSTEM PUBLIC
12+
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
13+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
14+
target_compile_features(hwy PUBLIC cxx_std_11)
15+
@@ -370,7 +370,7 @@
16+
target_compile_options(hwy_contrib PRIVATE ${HWY_FLAGS})
17+
set_property(TARGET hwy_contrib PROPERTY POSITION_INDEPENDENT_CODE ON)
18+
set_target_properties(hwy_contrib PROPERTIES VERSION ${LIBRARY_VERSION} SOVERSION ${LIBRARY_SOVERSION})
19+
-target_include_directories(hwy_contrib PUBLIC
20+
+target_include_directories(hwy_contrib SYSTEM PUBLIC
21+
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
22+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
23+
target_compile_features(hwy_contrib PUBLIC cxx_std_11)
24+
Common subdirectories: a/cmake and b/cmake
25+
Common subdirectories: a/debian and b/debian
26+
Common subdirectories: a/docs and b/docs
27+
Common subdirectories: a/g3doc and b/g3doc
28+
Common subdirectories: a/hwy and b/hwy

examples/highway/main.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <hwy/contrib/sort/vqsort.h> // hwy::VQSort() for large data sets
2+
3+
#include <cstdint>
4+
#include <random>
5+
#include <vector>
6+
7+
// Use hwy::VQSort to sort larger vectors
8+
inline void sort_large(std::vector<double>& v) {
9+
hwy::VQSort(v.data(), v.size(), hwy::SortAscending{});
10+
}
11+
12+
int main(int, char**) {
13+
std::random_device random_device;
14+
std::default_random_engine random_engine(random_device());
15+
std::uniform_real_distribution<double> uniform_dist(0.0, 100.0);
16+
17+
const std::size_t sz = 100000;
18+
std::vector<double> v;
19+
v.reserve(sz);
20+
for (std::size_t i = 0; i < sz; ++i) {
21+
v.push_back(uniform_dist(random_engine));
22+
}
23+
24+
sort_large(v);
25+
26+
return 0;
27+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
2+
3+
project(using-patch-adder)
4+
5+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
6+
7+
include("%{cpm_path}")
8+
9+
%{packages}
10+
11+
add_executable(using-patch-adder using-patch-adder.cpp)
12+
13+
target_link_libraries(using-patch-adder adder)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
diff --git a/code/adder/adder.hpp b/code/adder/adder.hpp
2+
index fdb9324..7c2fa00 100644
3+
--- a/code/adder/adder.hpp
4+
+++ b/code/adder/adder.hpp
5+
@@ -1,6 +1,6 @@
6+
#pragma once
7+
8+
-namespace adder
9+
+namespace patched
10+
{
11+
int add(int a, int b);
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
diff --git a/code/adder/adder.cpp b/code/adder/adder.cpp
2+
index fc6e767..44b1197 100644
3+
--- a/code/adder/adder.cpp
4+
+++ b/code/adder/adder.cpp
5+
@@ -1,6 +1,6 @@
6+
#include "adder.hpp"
7+
8+
-namespace adder
9+
+namespace patched
10+
{
11+
int add(int a, int b)
12+
{
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include <adder/adder.hpp>
2+
#include <cstdio>
3+
4+
int main() {
5+
int sum = patched::add(5, 3);
6+
std::printf("%d\n", sum);
7+
return 0;
8+
}

0 commit comments

Comments
 (0)