Compare commits

..

11 Commits

Author SHA1 Message Date
Lars Melchior
4fad2eac0a remove option consistency check (#240) 2021-03-25 19:24:44 +02:00
flagarde
ee08119642 Scope package options to avoid changing the local scope (#235)
* Fix #222

* Fix #222

* move policy change and local options to cpm_add_subdirectory

* change default policy as well

see https://gitlab.kitware.com/cmake/cmake/-/issues/20312

* add test

* update check to not use the NOT operator

(interestingly it works as expected locally)

* simplify test by not using options

* check options in tests

Co-authored-by: Lars Melchior <TheLartians@users.noreply.github.com>
2021-03-25 15:59:34 +01:00
Lars Melchior
259f1be8e2 update cxxopts and add reasonable options (#239) 2021-03-25 14:20:06 +02:00
Lars Melchior
32b063eba5 Fix support for source subdirectories (#238)
* add support for source subdirectories

* style fixes

* remove debug log

* grammar fix in comment
2021-03-25 10:42:51 +01:00
Lars Melchior
d64d816585 Make CPMFindPackage less prominent in the Readme (#236)
Giving `CPMFindPackage` a less prominent position, as in almost all cases, `CPMAddPackage` should be preferred.

Following a related discussion in #222.
2021-03-22 12:37:11 +01:00
Lars Melchior
8e8dcc9a8d moved test repostitory to CPM.cmake namespace (#228) 2021-03-04 11:01:01 +02:00
Lars Melchior
8afc2af4f9 update more examples to use shorthand syntax (#223)
* update more examples to use shorthand syntax

* revert google-benchmark as tests require googletest to be installed

* update comments

* always quote single-arguments

* undo accidental deletion
2021-02-25 09:53:28 +02:00
Borislav Stanimirov
a3d1048ad6 Infer package name and version from URL (#220)
* Function to extract name and version from url. Some tests

* Rewrite. Previous version was not safe enough. More tests

* Allow underscore as a name-version separator (<name>_<ver>)

* CPMAddPackage can infer name and version from url

* Allow URL parse from single arg and uncomment tests

* Info about shorthand syntax in README

* Fix style

* Fixed typo

Co-authored-by: Lars Melchior <TheLartians@users.noreply.github.com>

* Explicit hash algorithm in shorthand URL example.
Also added tests which include a hash algorithm provided
We can't document a default until it's confirmed here: https://gitlab.kitware.com/cmake/cmake/-/issues/21859

Co-authored-by: Lars Melchior <TheLartians@users.noreply.github.com>
2021-02-23 20:15:45 +01:00
Lars Melchior
492e762591 update examples to use new syntax where possible (#218) 2021-02-23 08:14:32 +02:00
Borislav Stanimirov
3f6cbe7383 Single-argument shorthand syntax for CPMAddPackage (#207)
* Added quotes in equality checks so lists can be compared

* Function to parse argument of CPMAddPackage in case a single one was provided

* Error on URL type in CPMAddPackage single-arg

* Fixed format

* Support single argument syntax of CPMAddPackage

* Documenting and showcasing the new shorthand syntax of CPMAddPackage

* Auto EXCLUDE_FROM_ALL for the shorthand syntax

* Fixed accidental paste of TOLOWER

* Document why some test cases are commented out

Co-authored-by: Lars Melchior <TheLartians@users.noreply.github.com>

* Update README.md

Co-authored-by: Lars Melchior <TheLartians@users.noreply.github.com>

* Removed GitHub as the default package shorthand provider

Co-authored-by: Lars Melchior <TheLartians@users.noreply.github.com>
2021-02-22 21:12:06 +01:00
Borislav Stanimirov
4aadac1972 Improved regex match in cpm_package_name_from_git_uri (#206) 2021-02-19 09:45:04 +01:00
23 changed files with 498 additions and 173 deletions

View File

@@ -27,9 +27,9 @@ For everything else, the targets can be created manually after the dependency ha
## Usage
After `CPM.cmake` has been [added](#adding-cpm) to your project, the function `CPMAddPackage` or `CPMFindPackage` can be used to fetch and configure a dependency.
After `CPM.cmake` has been [added](#adding-cpm) to your project, the function `CPMAddPackage` can be used to fetch and configure a dependency.
Afterwards, any targets defined in the dependency can be used directly.
`CPMFindPackage` and `CPMAddPackage` take the following named parameters.
`CPMAddPackage` takes the following named parameters.
```cmake
CPMAddPackage(
@@ -45,16 +45,42 @@ The origin may be specified by a `GIT_REPOSITORY`, but other sources, such as di
If `GIT_TAG` hasn't been explicitly specified it defaults to `v(VERSION)`, a common convention for git projects.
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.
`GIT_TAG` can also be set to a specific commit or a branch name such as `master` to always download the most recent version.
The optional argument `FIND_PACKAGE_ARGUMENTS` can be specified to a string of parameters that will be passed to `find_package` if enabled (see below).
After calling `CPMAddPackage` or `CPMFindPackage`, the following variables are defined in the local scope, where `<dependency>` is the name of the dependency.
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.
A single-argument compact syntax is also supported:
```cmake
# A git package from a given uri with a version
CPMAddPackage("uri@version")
# A git package from a given uri with a git tag or commit hash, or branch name
CPMAddPackage("uri#tag")
# A git package with both version and tag provided
CPMAddPackage("uri@version#tag")
```
In the shorthand syntax if the URI is of the form `gh:user/name`, it is interpreted as GitHub URI and converted to `https://github.com/user/name.git`. If the URI is of the form `gl:user/name`, it is interpreted as a [GitLab](https://gitlab.com/explore/) URI and coverted to `https://gitlab.com/user/name.git`. Otherwise the URI used verbatim as a git URL. All packages added using the shorthand syntax will be added using the [EXCLUDE_FROM_ALL](https://cmake.org/cmake/help/latest/prop_tgt/EXCLUDE_FROM_ALL.html) flag.
The single-argument syntax also works for URLs:
```cmake
# An archive package from a given url. The version is inferred
CPMAddPackage("https://example.com/my-package-1.2.3.zip")
# An archive package from a given url with an MD5 hash provided
CPMAddPackage("https://example.com/my-package-1.2.3.zip#MD5=68e20f674a48be38d60e129f600faf7d")
# An archive package from a given url. The version is explicitly given
CPMAddPackage("https://example.com/my-package.zip@1.2.3")
```
After calling `CPMAddPackage`, the following variables are defined in the local scope, where `<dependency>` is the name of the dependency.
- `<dependency>_SOURCE_DIR` is the path to the source of the dependency.
- `<dependency>_BINARY_DIR` is the path to the build directory of the dependency.
- `<dependency>_ADDED` is set to `YES` if the dependency has not been added before, otherwise it is set to `NO`.
The difference between `CPMFindPackage` and `CPMAddPackage` is that `CPMFindPackage` will try to find a local dependency via CMake's `find_package` and fallback to `CPMAddPackage` if the dependency is not found.
This behaviour can be also modified globally via [CPM options](#options).
For using CPM.cmake projects with external package managers, such as conan or vcpkg, setting the variable [`CPM_USE_LOCAL_PACKAGES`](#options) will make CPM.cmake try to add a package through `find_package` first, and add it from source if it doesn't succeed.
In rare cases, this behaviour may be desirable by default. The function `CPMFindPackage` will try to find a local dependency via CMake's `find_package` and fallback to `CPMAddPackage`, if the dependency is not found.
## Full CMakeLists Example
@@ -69,11 +95,7 @@ add_executable(tests tests.cpp)
# add dependencies
include(cmake/CPM.cmake)
CPMAddPackage(
GITHUB_REPOSITORY catchorg/Catch2
VERSION 2.5.0
)
CPMAddPackage("gh:catchorg/Catch2@2.5.0")
# link dependencies
target_link_libraries(tests Catch2)
@@ -135,7 +157,6 @@ The most notable features are:
- A simpler to use API
- Version checking: CPM.cmake will check the version number of any added dependency and omit a warning if another dependency requires a more recent version.
- Options: any Options passed to a dependency are stored and compared on later use, so if another dependency tries to add an existing dependency with incompatible options a warning will be emitted to the user.
- Offline builds: CPM.cmake will override CMake's download and update commands, which allows new builds to be configured while offline if all dependencies [are available locally](#cpm_source_cache).
- Automatic shallow clone: if a version tag (e.g. `v2.2.0`) is provided and `CPM_SOURCE_CACHE` is used, CPM.cmake will perform a shallow clone of the dependency, which should be significantly faster while using less storage than a full clone.
- Overridable: all `CPMAddPackage` can be configured to use `find_package` by setting a [CMake flag](#cpm_use_local_packages), making it easy to integrate into projects that may require local versioning through the system's package manager.
@@ -173,6 +194,8 @@ CPM can be configured to use `find_package` to search for locally installed depe
If the option `CPM_LOCAL_PACKAGES_ONLY` is set, CPM will emit an error if the dependency is not found locally.
These options can also be set as environmental variables.
In the case that `find_package` requires additional arguments, the parameter `FIND_PACKAGE_ARGUMENTS` may be specified in the `CPMAddPackage` call. The value of this parameter will be forwarded to `find_package`.
## Local package override
Library developers are often in the situation where they work on a locally checked out dependency at the same time as on a consumer project.
@@ -244,51 +267,29 @@ See the [wiki](https://github.com/cpm-cmake/CPM.cmake/wiki/More-Snippets) for mo
### [Catch2](https://github.com/catchorg/Catch2)
```cmake
CPMAddPackage(
NAME Catch2
GITHUB_REPOSITORY catchorg/Catch2
VERSION 2.5.0
)
CPMAddPackage("gh:catchorg/Catch2@2.5.0")
```
### [Boost (via boost-cmake)](https://github.com/Orphis/boost-cmake)
```CMake
CPMAddPackage(
NAME boost-cmake
GITHUB_REPOSITORY Orphis/boost-cmake
VERSION 1.67.0
)
CPMAddPackage("gh:Orphis/boost-cmake@1.67.0")
```
### [cxxopts](https://github.com/jarro2783/cxxopts)
```cmake
CPMAddPackage(
NAME cxxopts
GITHUB_REPOSITORY jarro2783/cxxopts
VERSION 2.2.0
OPTIONS
"CXXOPTS_BUILD_EXAMPLES Off"
"CXXOPTS_BUILD_TESTS Off"
VERSION 2.2.1
OPTIONS "CXXOPTS_BUILD_EXAMPLES NO" "CXXOPTS_BUILD_TESTS NO" "CXXOPTS_ENABLE_INSTALL YES"
)
```
### [Yaml-cpp](https://github.com/jbeder/yaml-cpp)
```CMake
CPMAddPackage(
NAME yaml-cpp
GITHUB_REPOSITORY jbeder/yaml-cpp
# 0.6.2 uses deprecated CMake syntax
VERSION 0.6.3
# 0.6.3 is not released yet, so use a recent commit
GIT_TAG 012269756149ae99745b6dafefd415843d7420bb
OPTIONS
"YAML_CPP_BUILD_TESTS Off"
"YAML_CPP_BUILD_CONTRIB Off"
"YAML_CPP_BUILD_TOOLS Off"
)
CPMAddPackage("gh:jbeder/yaml-cpp#yaml-cpp-0.6.3@0.6.3")
```
### [google/benchmark](https://github.com/google/benchmark)
@@ -297,14 +298,13 @@ CPMAddPackage(
CPMAddPackage(
NAME benchmark
GITHUB_REPOSITORY google/benchmark
VERSION 1.4.1
OPTIONS
"BENCHMARK_ENABLE_TESTING Off"
VERSION 1.5.2
OPTIONS "BENCHMARK_ENABLE_TESTING Off"
)
if (benchmark_ADDED)
# compile with C++17
set_target_properties(benchmark PROPERTIES CXX_STANDARD 17)
if(benchmark_ADDED)
# enable c++11 to avoid compilation errors
set_target_properties(benchmark PROPERTIES CXX_STANDARD 11)
endif()
```

View File

@@ -132,12 +132,10 @@ if(NOT CPM_DONT_CREATE_PACKAGE_LOCK)
endif()
include(FetchContent)
include(CMakeParseArguments)
# Infer package name from git repository uri (path or url)
# Try to infer package name from git repository uri (path or url)
function(cpm_package_name_from_git_uri URI RESULT)
string(REGEX MATCH "([^/:]+)/?.git/?$" cpmGitUriMatch "${URI}")
if(DEFINED cpmGitUriMatch)
if("${URI}" MATCHES "([^/:]+)/?.git/?$")
set(${RESULT}
${CMAKE_MATCH_1}
PARENT_SCOPE
@@ -147,6 +145,52 @@ function(cpm_package_name_from_git_uri URI RESULT)
endif()
endfunction()
# Try to infer package name and version from a url
function(cpm_package_name_and_ver_from_url url outName outVer)
if(url MATCHES "[/\\?]([a-zA-Z0-9_\\.-]+)\\.(tar|tar\\.gz|tar\\.bz2|zip|ZIP)(\\?|/|$)")
# We matched an archive
set(filename "${CMAKE_MATCH_1}")
if(filename MATCHES "([a-zA-Z0-9_\\.-]+)[_-]v?(([0-9]+\\.)*[0-9]+[a-zA-Z0-9]*)")
# We matched <name>-<version> (ie foo-1.2.3)
set(${outName}
"${CMAKE_MATCH_1}"
PARENT_SCOPE
)
set(${outVer}
"${CMAKE_MATCH_2}"
PARENT_SCOPE
)
elseif(filename MATCHES "(([0-9]+\\.)+[0-9]+[a-zA-Z0-9]*)")
# We couldn't find a name, but we found a version
#
# In many cases (which we don't handle here) the url would look something like
# `irrelevant/ACTUAL_PACKAGE_NAME/irrelevant/1.2.3.zip`. In such a case we can't possibly
# distinguish the package name from the irrelevant bits. Moreover if we try to match the
# package name from the filename, we'd get bogus at best.
unset(${outName} PARENT_SCOPE)
set(${outVer}
"${CMAKE_MATCH_1}"
PARENT_SCOPE
)
else()
# Boldly assume that the file name is the package name.
#
# Yes, something like `irrelevant/ACTUAL_NAME/irrelevant/download.zip` will ruin our day, but
# such cases should be quite rare. No popular service does this... we think.
set(${outName}
"${filename}"
PARENT_SCOPE
)
unset(${outVer} PARENT_SCOPE)
endif()
else()
# No ideas yet what to do with non-archives
unset(${outName} PARENT_SCOPE)
unset(${outVer} PARENT_SCOPE)
endif()
endfunction()
# Initialize logging prefix
if(NOT CPM_INDENT)
set(CPM_INDENT
@@ -202,7 +246,7 @@ function(CPMFindPackage)
return()
endif()
cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" "${CPM_ARGS_OPTIONS}")
cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}")
if(CPM_PACKAGE_ALREADY_ADDED)
cpm_export_variables(${CPM_ARGS_NAME})
return()
@@ -218,7 +262,7 @@ function(CPMFindPackage)
endfunction()
# checks if a package has been added before
function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION CPM_ARGS_OPTIONS)
function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION)
if("${CPM_ARGS_NAME}" IN_LIST CPM_PACKAGES)
CPMGetPackageVersion(${CPM_ARGS_NAME} CPM_PACKAGE_VERSION)
if("${CPM_PACKAGE_VERSION}" VERSION_LESS "${CPM_ARGS_VERSION}")
@@ -227,17 +271,6 @@ function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION CPM_A
"${CPM_INDENT} requires a newer version of ${CPM_ARGS_NAME} (${CPM_ARGS_VERSION}) than currently included (${CPM_PACKAGE_VERSION})."
)
endif()
if(CPM_ARGS_OPTIONS)
foreach(OPTION ${CPM_ARGS_OPTIONS})
cpm_parse_option(${OPTION})
if(NOT "${${OPTION_KEY}}" STREQUAL "${OPTION_VALUE}")
message(
WARNING
"${CPM_INDENT} ignoring package option for ${CPM_ARGS_NAME}: ${OPTION_KEY} = ${OPTION_VALUE} (${${OPTION_KEY}})"
)
endif()
endforeach()
endif()
cpm_get_fetch_properties(${CPM_ARGS_NAME})
set(${CPM_ARGS_NAME}_ADDED NO)
set(CPM_PACKAGE_ALREADY_ADDED
@@ -253,8 +286,80 @@ function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION CPM_A
endif()
endfunction()
# Parse the argument of CPMAddPackage in case a single one was provided and convert it to a list of
# arguments which can then be parsed idiomatically. For example gh:foo/bar@1.2.3 will be converted
# to: GITHUB_REPOSITORY;foo/bar;VERSION;1.2.3
function(cpm_parse_add_package_single_arg arg outArgs)
# Look for a scheme
if("${arg}" MATCHES "^([a-zA-Z]+):(.+)$")
string(TOLOWER "${CMAKE_MATCH_1}" scheme)
set(uri "${CMAKE_MATCH_2}")
# Check for CPM-specific schemes
if(scheme STREQUAL "gh")
set(out "GITHUB_REPOSITORY;${uri}")
set(packageType "git")
elseif(scheme STREQUAL "gl")
set(out "GITLAB_REPOSITORY;${uri}")
set(packageType "git")
# A CPM-specific scheme was not found. Looks like this is a generic URL so try to determine
# type
elseif(arg MATCHES ".git/?(@|#|$)")
set(out "GIT_REPOSITORY;${arg}")
set(packageType "git")
else()
# Fall back to a URL
set(out "URL;${arg}")
set(packageType "archive")
# We could also check for SVN since FetchContent supports it, but SVN is so rare these days.
# We just won't bother with the additional complexity it will induce in this function. SVN is
# done by multi-arg
endif()
else()
if(arg MATCHES ".git/?(@|#|$)")
set(out "GIT_REPOSITORY;${arg}")
set(packageType "git")
else()
# Give up
message(FATAL_ERROR "CPM: Can't determine package type of '${arg}'")
endif()
endif()
# For all packages we interpret @... as version. Only replace the last occurence. Thus URIs
# containing '@' can be used
string(REGEX REPLACE "@([^@]+)$" ";VERSION;\\1" out "${out}")
# Parse the rest according to package type
if(packageType STREQUAL "git")
# For git repos we interpret #... as a tag or branch or commit hash
string(REGEX REPLACE "#([^#]+)$" ";GIT_TAG;\\1" out "${out}")
elseif(packageType STREQUAL "archive")
# For archives we interpret #... as a URL hash.
string(REGEX REPLACE "#([^#]+)$" ";URL_HASH;\\1" out "${out}")
# We don't try to parse the version if it's not provided explicitly. cpm_get_version_from_url
# should do this at a later point
else()
# We should never get here. This is an assertion and hitting it means there's a bug in the code
# above. A packageType was set, but not handled by this if-else.
message(FATAL_ERROR "CPM: Unsupported package type '${packageType}' of '${arg}'")
endif()
set(${outArgs}
${out}
PARENT_SCOPE
)
endfunction()
# Download and add a package from source
function(CPMAddPackage)
list(LENGTH ARGN argnLength)
if(argnLength EQUAL 1)
cpm_parse_add_package_single_arg("${ARGN}" ARGN)
# The shorthand syntax implies EXCLUDE_FROM_ALL
set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES")
endif()
set(oneValueArgs
NAME
@@ -271,9 +376,10 @@ function(CPMAddPackage)
NO_CACHE
GIT_SHALLOW
EXCLUDE_FROM_ALL
SOURCE_SUBDIR
)
set(multiValueArgs OPTIONS)
set(multiValueArgs URL OPTIONS)
cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}")
@@ -321,6 +427,24 @@ function(CPMAddPackage)
endif()
endif()
if(DEFINED CPM_ARGS_URL)
# If a name or version aren't provided, try to infer them from the URL
list(GET CPM_ARGS_URL 0 firstUrl)
cpm_package_name_and_ver_from_url(${firstUrl} nameFromUrl verFromUrl)
# If we fail to obtain name and version from the first URL, we could try other URLs if any.
# However multiple URLs are expected to be quite rare, so for now we won't bother.
# If the caller provided their own name and version, they trump the inferred ones.
if(NOT DEFINED CPM_ARGS_NAME)
set(CPM_ARGS_NAME ${nameFromUrl})
endif()
if(NOT DEFINED CPM_ARGS_VERSION)
set(CPM_ARGS_VERSION ${verFromUrl})
endif()
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS URL "${CPM_ARGS_URL}")
endif()
# Check for required arguments
if(NOT DEFINED CPM_ARGS_NAME)
@@ -331,7 +455,7 @@ function(CPMAddPackage)
endif()
# Check if package has been added before
cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" "${CPM_ARGS_OPTIONS}")
cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}")
if(CPM_PACKAGE_ALREADY_ADDED)
cpm_export_variables(${CPM_ARGS_NAME})
return()
@@ -358,7 +482,7 @@ function(CPMAddPackage)
CPMAddPackage(${declaration})
cpm_export_variables(${CPM_ARGS_NAME})
# checking again to ensure version and option compatibility
cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" "${CPM_ARGS_OPTIONS}")
cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}")
return()
endif()
@@ -380,16 +504,6 @@ function(CPMAddPackage)
CPMRegisterPackage("${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}")
if(CPM_ARGS_OPTIONS)
foreach(OPTION ${CPM_ARGS_OPTIONS})
cpm_parse_option(${OPTION})
set(${OPTION_KEY}
${OPTION_VALUE}
CACHE INTERNAL ""
)
endforeach()
endif()
if(DEFINED CPM_ARGS_GIT_TAG)
set(PACKAGE_INFO "${CPM_ARGS_GIT_TAG}")
elseif(DEFINED CPM_ARGS_SOURCE_DIR)
@@ -414,11 +528,11 @@ function(CPMAddPackage)
set(${CPM_ARGS_NAME}_BINARY_DIR ${CMAKE_BINARY_DIR}/_deps/${lower_case_name}-build)
set(${CPM_ARGS_NAME}_ADDED YES)
set(${CPM_ARGS_NAME}_SOURCE_DIR ${download_directory})
if(NOT CPM_ARGS_DOWNLOAD_ONLY AND EXISTS ${download_directory}/CMakeLists.txt)
cpm_add_subdirectory(
${download_directory} ${${CPM_ARGS_NAME}_BINARY_DIR} "${CPM_ARGS_EXCLUDE_FROM_ALL}"
)
endif()
cpm_add_subdirectory(
"${CPM_ARGS_NAME}" "${DOWNLOAD_ONLY}"
"${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" "${${CPM_ARGS_NAME}_BINARY_DIR}"
"${CPM_ARGS_EXCLUDE_FROM_ALL}" "${CPM_ARGS_OPTIONS}"
)
set(CPM_SKIP_FETCH TRUE)
set(PACKAGE_INFO "${PACKAGE_INFO} at ${download_directory}")
else()
@@ -457,7 +571,12 @@ function(CPMAddPackage)
cpm_declare_fetch(
"${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}" "${PACKAGE_INFO}" "${CPM_ARGS_UNPARSED_ARGUMENTS}"
)
cpm_fetch_package("${CPM_ARGS_NAME}" "${DOWNLOAD_ONLY}" "${CPM_ARGS_EXCLUDE_FROM_ALL}")
cpm_fetch_package("${CPM_ARGS_NAME}")
cpm_add_subdirectory(
"${CPM_ARGS_NAME}" "${DOWNLOAD_ONLY}"
"${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" "${${CPM_ARGS_NAME}_BINARY_DIR}"
"${CPM_ARGS_EXCLUDE_FROM_ALL}" "${CPM_ARGS_OPTIONS}"
)
cpm_get_fetch_properties("${CPM_ARGS_NAME}")
endif()
@@ -580,36 +699,62 @@ function(cpm_get_fetch_properties PACKAGE)
)
endfunction()
function(cpm_add_subdirectory SOURCE_DIR BINARY_DIR EXCLUDE)
if(EXCLUDE)
set(addSubdirectoryExtraArgs EXCLUDE_FROM_ALL)
else()
set(addSubdirectoryExtraArgs "")
# adds a package as a subdirectory if viable, according to provided options
function(
cpm_add_subdirectory
PACKAGE
DOWNLOAD_ONLY
SOURCE_DIR
BINARY_DIR
EXCLUDE
OPTIONS
)
if(NOT DOWNLOAD_ONLY AND EXISTS ${SOURCE_DIR}/CMakeLists.txt)
if(EXCLUDE)
set(addSubdirectoryExtraArgs EXCLUDE_FROM_ALL)
else()
set(addSubdirectoryExtraArgs "")
endif()
if(OPTIONS)
# the policy allows us to change options without caching
cmake_policy(SET CMP0077 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
foreach(OPTION ${OPTIONS})
cpm_parse_option(${OPTION})
set(${OPTION_KEY} ${OPTION_VALUE})
endforeach()
endif()
set(CPM_OLD_INDENT "${CPM_INDENT}")
set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:")
add_subdirectory(${SOURCE_DIR} ${BINARY_DIR} ${addSubdirectoryExtraArgs})
set(CPM_INDENT "${CPM_OLD_INDENT}")
endif()
add_subdirectory(${SOURCE_DIR} ${BINARY_DIR} ${addSubdirectoryExtraArgs})
endfunction()
# downloads a previously declared package via FetchContent
function(cpm_fetch_package PACKAGE DOWNLOAD_ONLY EXCLUDE)
# downloads a previously declared package via FetchContent and exports the variables
# `${PACKAGE}_SOURCE_DIR` and `${PACKAGE}_BINARY_DIR` to the parent scope
function(cpm_fetch_package PACKAGE)
if(${CPM_DRY_RUN})
message(STATUS "${CPM_INDENT} package ${PACKAGE} not fetched (dry run)")
return()
endif()
FetchContent_GetProperties(${PACKAGE})
string(TOLOWER "${PACKAGE}" lower_case_name)
if(NOT ${lower_case_name}_POPULATED)
FetchContent_Populate(${PACKAGE})
if(NOT DOWNLOAD_ONLY AND EXISTS ${${lower_case_name}_SOURCE_DIR}/CMakeLists.txt)
set(CPM_OLD_INDENT "${CPM_INDENT}")
set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:")
cpm_add_subdirectory(
${${lower_case_name}_SOURCE_DIR} ${${lower_case_name}_BINARY_DIR} "${EXCLUDE}"
)
set(CPM_INDENT "${CPM_OLD_INDENT}")
endif()
endif()
string(TOLOWER "${PACKAGE}" lower_case_name)
set(${PACKAGE}_SOURCE_DIR
${${lower_case_name}_SOURCE_DIR}
PARENT_SCOPE
)
set(${PACKAGE}_BINARY_DIR
${${lower_case_name}_BINARY_DIR}
PARENT_SCOPE
)
endfunction()
# splits a package option

View File

@@ -3,7 +3,7 @@ function(ASSERT_EQUAL)
message(FATAL_ERROR "assertion failed: invalid argument count: ${ARGC}")
endif()
if(NOT ${ARGV0} STREQUAL ${ARGV1})
if(NOT "${ARGV0}" STREQUAL "${ARGV1}")
message(FATAL_ERROR "assertion failed: '${ARGV0}' != '${ARGV1}'")
else()
message(STATUS "test passed: '${ARGV0}' == '${ARGV1}'")
@@ -15,7 +15,7 @@ function(ASSERT_NOT_EQUAL)
message(FATAL_ERROR "assertion failed: invalid argument count: ${ARGC}")
endif()
if(${ARGV0} STREQUAL ${ARGV1})
if("${ARGV0}" STREQUAL "${ARGV1}")
message(FATAL_ERROR "assertion failed: '${ARGV0}' == '${ARGV1}'")
else()
message(STATUS "test passed: '${ARGV0}' != '${ARGV1}'")
@@ -38,7 +38,7 @@ endfunction()
function(ASSERT_NOT_DEFINED KEY)
if(DEFINED ${KEY})
message(FATAL_ERROR "assertion failed: '${KEY}' is defiend")
message(FATAL_ERROR "assertion failed: '${KEY}' is defiend (${${KEY}})")
else()
message(STATUS "test passed: '${KEY}' is not defined")
endif()
@@ -48,7 +48,15 @@ function(ASSERT_TRUTHY KEY)
if(${${KEY}})
message(STATUS "test passed: '${KEY}' is set truthy")
else()
message(FATAL_ERROR "assertion failed: value of '${KEY}' is not true (${${KEY}})")
message(FATAL_ERROR "assertion failed: value of '${KEY}' is not truthy (${${KEY}})")
endif()
endfunction()
function(ASSERT_FALSY KEY)
if(${${KEY}})
message(FATAL_ERROR "assertion failed: value of '${KEY}' is not falsy (${${KEY}})")
else()
message(STATUS "test passed: '${KEY}' is set falsy")
endif()
endfunction()

View File

@@ -8,12 +8,7 @@ include(../../cmake/CPM.cmake)
find_package(Threads REQUIRED)
CPMAddPackage(
NAME asio
VERSION 1.18.1
GITHUB_REPOSITORY chriskohlhoff/asio
GIT_TAG asio-1-18-1 # asio uses non-standard version tag, we must specify GIT_TAG
)
CPMAddPackage("gh:chriskohlhoff/asio#asio-1-18-1@1.18.1")
# ASIO doesn't use CMake, we have to configure it manually. Extra notes for using on Windows:
#

View File

@@ -6,11 +6,7 @@ project(CPMExampleBenchmark)
include(../../cmake/CPM.cmake)
CPMAddPackage(
NAME fibonacci
GITLAB_REPOSITORY TheLartians/Fibonacci
VERSION 2.0
)
CPMAddPackage("gh:cpm-cmake/testpack-fibonacci@2.0")
CPMAddPackage(
NAME benchmark
@@ -20,7 +16,7 @@ CPMAddPackage(
)
if(benchmark_ADDED)
# Don't use C++14 because it doesn't work in some configurations.
# enable c++11 to avoid compilation errors
set_target_properties(benchmark PROPERTIES CXX_STANDARD 11)
endif()

View File

@@ -15,6 +15,7 @@ CPMFindPackage(
NAME Boost
GITHUB_REPOSITORY Orphis/boost-cmake
VERSION 1.67.0
# setting FIND_PACKAGE_ARGUMENTS allow usage with `CPM_USE_LOCAL_PACKAGES`
FIND_PACKAGE_ARGUMENTS "COMPONENTS system"
)

View File

@@ -6,17 +6,8 @@ project(CPMExampleCatch2)
include(../../cmake/CPM.cmake)
CPMAddPackage(
NAME fibonacci
GITHUB_REPOSITORY TheLartians/Fibonacci
VERSION 2.0
)
CPMAddPackage(
NAME Catch2
GITHUB_REPOSITORY catchorg/Catch2
VERSION 2.13.4
)
CPMAddPackage("gh:cpm-cmake/testpack-fibonacci@2.0")
CPMAddPackage("gh:catchorg/Catch2@2.13.4")
# ---- Create binary ----

View File

@@ -6,12 +6,7 @@ project(CPMExampleCXXOpts)
include(../../cmake/CPM.cmake)
CPMAddPackage(
NAME cxxopts
GITHUB_REPOSITORY jarro2783/cxxopts
VERSION 2.2.0
OPTIONS "CXXOPTS_BUILD_EXAMPLES Off" "CXXOPTS_BUILD_TESTS Off"
)
CPMAddPackage("gh:jarro2783/cxxopts@2.2.0")
# ---- Create binary ----

View File

@@ -6,17 +6,8 @@ project(CPMExampleDoctest)
include(../../cmake/CPM.cmake)
CPMAddPackage(
NAME fibonacci
GITLAB_REPOSITORY TheLartians/Fibonacci
VERSION 2.0
)
CPMAddPackage(
NAME doctest
GITHUB_REPOSITORY onqtam/doctest
GIT_TAG 2.4.5
)
CPMAddPackage("gh:cpm-cmake/testpack-fibonacci@2.0")
CPMAddPackage("gh:onqtam/doctest#2.4.5")
# ---- Create binary ----

View File

@@ -6,11 +6,7 @@ project(CPMJSONExample)
include(../../cmake/CPM.cmake)
CPMAddPackage(
NAME fmt
GIT_TAG 7.1.3
GITHUB_REPOSITORY fmtlib/fmt
)
CPMAddPackage("gh:fmtlib/fmt#7.1.3")
# ---- Executable ----

View File

@@ -6,11 +6,7 @@ project(CPMExampleGtest)
include(../../cmake/CPM.cmake)
CPMAddPackage(
NAME fibonacci
GITLAB_REPOSITORY TheLartians/Fibonacci
VERSION 2.0
)
CPMAddPackage("gh:cpm-cmake/testpack-fibonacci@2.0")
CPMAddPackage(
NAME googletest

View File

@@ -6,11 +6,7 @@ project(CPMlinenoiseExample)
include(../../cmake/CPM.cmake)
CPMAddPackage(
NAME linenoise
GIT_TAG 1.0
GITHUB_REPOSITORY antirez/linenoise
)
CPMAddPackage("gh:antirez/linenoise#1.0")
if(linenoise_ADDED)
add_library(linenoise ${linenoise_SOURCE_DIR}/linenoise.c)

View File

@@ -6,11 +6,7 @@ project(CPMSpdlogExample)
include(../../cmake/CPM.cmake)
CPMAddPackage(
NAME spdlog
GITHUB_REPOSITORY gabime/spdlog
VERSION 1.8.2
)
CPMAddPackage("gh:gabime/spdlog@1.8.2")
# ---- Executable ----

View File

@@ -6,13 +6,7 @@ project(CPMYamlExample)
include(../../cmake/CPM.cmake)
CPMAddPackage(
NAME yaml-cpp
GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
VERSION 0.6.3
GIT_TAG yaml-cpp-0.6.3
OPTIONS "YAML_CPP_BUILD_TESTS Off" "YAML_CPP_BUILD_CONTRIB Off" "YAML_CPP_BUILD_TOOLS Off"
)
CPMAddPackage("gh:jbeder/yaml-cpp#yaml-cpp-0.6.3@0.6.3")
# ---- Executable ----

View File

@@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
project(CPMTest)
# ---- Options ----
option(ENABLE_TEST_COVERAGE "Enable test coverage" OFF)
# ---- Dependencies ----
include(@CPM_PATH@/CPM.cmake)
CPMAddPackage(
NAME Dependency
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/dependency
OPTIONS "DEFINE_ALTERNATIVE_FUNCTION YES"
EXCLUDE_FROM_ALL YES
)
# ---- Dependencies ----
alternative_dependency_function()
# ---- Check parameters ----
include(@CPM_PATH@/testing.cmake)
message("DEFINE_ALTERNATIVE_FUNCTION: ${DEFINE_ALTERNATIVE_FUNCTION}")
# this option is overridden by CPM.cmake
ASSERT_NOT_DEFINED(DEFINE_ALTERNATIVE_FUNCTION)
# this option is leaked by the dependency
ASSERT_EQUAL(${LEAKED_OPTION} "OFF")

View File

@@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
project(CPMTest)
# ---- Dependencies ----
include(@CPM_PATH@/CPM.cmake)
CPMAddPackage(
NAME Dependency
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/dependency
SOURCE_SUBDIR inner
)
# ---- Call inner dependency method to validate correct addition of subdirectory ----
inner_dependency_function()

View File

@@ -1,3 +1,14 @@
function(dependency_function)
message("called external method")
endfunction()
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
option(DEFINE_ALTERNATIVE_FUNCTION "define the alternative function" OFF)
option(LEAKED_OPTION "this option will be leaked to the outer scope" OFF)
if(NOT DEFINE_ALTERNATIVE_FUNCTION)
function(dependency_function)
message("called external method")
endfunction()
else()
function(alternative_dependency_function)
message("called alternative external method")
endfunction()
endif()

View File

@@ -0,0 +1,3 @@
function(inner_dependency_function)
message("called inner method")
endfunction()

19
test/unit/options.cmake Normal file
View File

@@ -0,0 +1,19 @@
include(CMakePackageConfigHelpers)
include(${CPM_PATH}/testing.cmake)
set(TEST_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/source_dir)
set(TEST_DEPENDENCY_NAME Dependency)
configure_package_config_file(
"${CMAKE_CURRENT_LIST_DIR}/local_dependency/OptionsCMakeLists.txt.in"
"${CMAKE_CURRENT_LIST_DIR}/local_dependency/CMakeLists.txt"
INSTALL_DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/junk
)
execute_process(
COMMAND ${CMAKE_COMMAND} "-H${CMAKE_CURRENT_LIST_DIR}/local_dependency" "-B${TEST_BUILD_DIR}"
RESULT_VARIABLE ret
)
assert_equal(${ret} "0")

View File

@@ -0,0 +1,60 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
include(${CPM_PATH}/CPM.cmake)
include(${CPM_PATH}/testing.cmake)
cpm_package_name_and_ver_from_url("https://example.com/coolpack-1.2.3.zip" name ver)
assert_equal("coolpack" ${name})
assert_equal("1.2.3" ${ver})
cpm_package_name_and_ver_from_url("https://example.com/cool-pack-v1.3.tar.gz" name ver)
assert_equal("cool-pack" ${name})
assert_equal("1.3" ${ver})
cpm_package_name_and_ver_from_url(
"https://subd.zip.com/download.php?Cool.Pack-v1.2.3rc0.tar" name ver
)
assert_equal("Cool.Pack" ${name})
assert_equal("1.2.3rc0" ${ver})
cpm_package_name_and_ver_from_url(
"http://evil-1.2.tar.gz.com/Plan9_1.2.3a.tar.bz2?download" name ver
)
assert_equal("Plan9" ${name})
assert_equal("1.2.3a" ${ver})
cpm_package_name_and_ver_from_url(
"http://evil-1.2.tar.gz.com/Plan_9-1.2.3a.tar.bz2?download" name ver
)
assert_equal("Plan_9" ${name})
assert_equal("1.2.3a" ${ver})
cpm_package_name_and_ver_from_url(
"http://evil-1.2.tar.gz.com/Plan-9_1.2.3a.tar.bz2?download" name ver
)
assert_equal("Plan-9" ${name})
assert_equal("1.2.3a" ${ver})
cpm_package_name_and_ver_from_url("https://sf.com/distrib/SFLib-0.999.4.tar.gz/download" name ver)
assert_equal("SFLib" ${name})
assert_equal("0.999.4" ${ver})
cpm_package_name_and_ver_from_url("https://example.com/coolpack/v5.6.5rc44.zip" name ver)
assert_not_defined(name)
assert_equal("5.6.5rc44" ${ver})
cpm_package_name_and_ver_from_url("evil-1.3.zip.com/coolpack/release999.000beta.ZIP" name ver)
assert_not_defined(name)
assert_equal("999.000beta" ${ver})
cpm_package_name_and_ver_from_url("https://example.com/Foo55.tar.gz" name ver)
assert_equal("Foo55" ${name})
assert_not_defined(ver)
cpm_package_name_and_ver_from_url("https://example.com/foo" name ver)
assert_not_defined(name)
assert_not_defined(ver)
cpm_package_name_and_ver_from_url("example.zip.com/foo" name ver)
assert_not_defined(name)
assert_not_defined(ver)

View File

@@ -0,0 +1,65 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
include(${CPM_PATH}/CPM.cmake)
include(${CPM_PATH}/testing.cmake)
cpm_parse_add_package_single_arg("gh:cpm-cmake/CPM.cmake" args)
assert_equal("GITHUB_REPOSITORY;cpm-cmake/CPM.cmake" "${args}")
cpm_parse_add_package_single_arg("gh:cpm-cmake/CPM.cmake@1.2.3" args)
assert_equal("GITHUB_REPOSITORY;cpm-cmake/CPM.cmake;VERSION;1.2.3" "${args}")
cpm_parse_add_package_single_arg("gh:cpm-cmake/CPM.cmake#master" args)
assert_equal("GITHUB_REPOSITORY;cpm-cmake/CPM.cmake;GIT_TAG;master" "${args}")
cpm_parse_add_package_single_arg("gh:cpm-cmake/CPM.cmake@0.20.3#asdf" args)
assert_equal("GITHUB_REPOSITORY;cpm-cmake/CPM.cmake;VERSION;0.20.3;GIT_TAG;asdf" "${args}")
cpm_parse_add_package_single_arg("gh:a/b#c@d" args)
assert_equal("GITHUB_REPOSITORY;a/b;GIT_TAG;c;VERSION;d" "${args}")
cpm_parse_add_package_single_arg("gh:foo#c@d" args)
assert_equal("GITHUB_REPOSITORY;foo;GIT_TAG;c;VERSION;d" "${args}")
cpm_parse_add_package_single_arg("gh:Foo@5" args)
assert_equal("GITHUB_REPOSITORY;Foo;VERSION;5" "${args}")
cpm_parse_add_package_single_arg("gl:foo/bar" args)
assert_equal("GITLAB_REPOSITORY;foo/bar" "${args}")
cpm_parse_add_package_single_arg("gl:foo/Bar" args)
assert_equal("GITLAB_REPOSITORY;foo/Bar" "${args}")
cpm_parse_add_package_single_arg("https://github.com/cpm-cmake/CPM.cmake.git@0.30.5" args)
assert_equal("GIT_REPOSITORY;https://github.com/cpm-cmake/CPM.cmake.git;VERSION;0.30.5" "${args}")
cpm_parse_add_package_single_arg("git@host.xz:user/pkg.git@0.1.2" args)
assert_equal("GIT_REPOSITORY;git@host.xz:user/pkg.git;VERSION;0.1.2" "${args}")
cpm_parse_add_package_single_arg("git@host.xz:user/pkg.git@0.1.2#rc" args)
assert_equal("GIT_REPOSITORY;git@host.xz:user/pkg.git;VERSION;0.1.2;GIT_TAG;rc" "${args}")
cpm_parse_add_package_single_arg(
"ssh://user@host.xz:123/path/to/pkg.git#fragment@1.2.3#branch" args
)
assert_equal(
"GIT_REPOSITORY;ssh://user@host.xz:123/path/to/pkg.git#fragment;VERSION;1.2.3;GIT_TAG;branch"
"${args}"
)
cpm_parse_add_package_single_arg("https://example.org/foo.tar.gz" args)
assert_equal("URL;https://example.org/foo.tar.gz" "${args}")
cpm_parse_add_package_single_arg("https://example.org/foo.tar.gz#baadf00d@1.2.0" args)
assert_equal("URL;https://example.org/foo.tar.gz;URL_HASH;baadf00d;VERSION;1.2.0" "${args}")
cpm_parse_add_package_single_arg("https://example.org/foo.tar.gz#MD5=baadf00d" args)
assert_equal("URL;https://example.org/foo.tar.gz;URL_HASH;MD5=baadf00d" "${args}")
cpm_parse_add_package_single_arg("https://example.org/Foo.zip#SHA3_512=1337" args)
assert_equal("URL;https://example.org/Foo.zip;URL_HASH;SHA3_512=1337" "${args}")
cpm_parse_add_package_single_arg("ftp://user:pass@server/pathname.zip#fragment#0ddb411@0" args)
assert_equal(
"URL;ftp://user:pass@server/pathname.zip#fragment;URL_HASH;0ddb411;VERSION;0" "${args}"
)

View File

@@ -12,7 +12,7 @@ include(@CPM_PATH@/CPM.cmake)
CPMAddPackage(
NAME fibonacci
GIT_REPOSITORY https://github.com/TheLartians/Fibonacci.git
GIT_REPOSITORY https://github.com/cpm-cmake/testpack-fibonacci.git
VERSION @FIBONACCI_VERSION@
@FIBONACCI_PACKAGE_ARGS@
)

17
test/unit/subdir.cmake Normal file
View File

@@ -0,0 +1,17 @@
include(CMakePackageConfigHelpers)
include(${CPM_PATH}/testing.cmake)
set(TEST_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/source_dir)
configure_package_config_file(
"${CMAKE_CURRENT_LIST_DIR}/local_dependency/SubdirCMakeLists.txt.in"
"${CMAKE_CURRENT_LIST_DIR}/local_dependency/CMakeLists.txt"
INSTALL_DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/junk
)
execute_process(
COMMAND ${CMAKE_COMMAND} "-H${CMAKE_CURRENT_LIST_DIR}/local_dependency" "-B${TEST_BUILD_DIR}"
RESULT_VARIABLE ret
)
assert_equal(${ret} "0")