Skip to content

Commit 3f6cbe7

Browse files
iboBTheLartians
andauthored
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>
1 parent 4aadac1 commit 3f6cbe7

File tree

4 files changed

+159
-16
lines changed

4 files changed

+159
-16
lines changed

README.md

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@ On the other hand, if `VERSION` hasn't been explicitly specified, CPM can automa
4747
`GIT_TAG` can also be set to a specific commit or a branch name such as `master` to always download the most recent version.
4848
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).
4949

50+
A single-argument compact syntax is also supported:
51+
52+
```cmake
53+
# A git package from a given uri with a version
54+
CPMAddPackage("uri@version")
55+
# A git package from a given uri with a git tag or commit hash, or branch name
56+
CPMAddPackage("uri#tag")
57+
# A git package with both version and tag provided
58+
CPMAddPackage("uri@version#tag")
59+
```
60+
61+
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.
62+
5063
After calling `CPMAddPackage` or `CPMFindPackage`, the following variables are defined in the local scope, where `<dependency>` is the name of the dependency.
5164

5265
- `<dependency>_SOURCE_DIR` is the path to the source of the dependency.
@@ -70,10 +83,7 @@ add_executable(tests tests.cpp)
7083
# add dependencies
7184
include(cmake/CPM.cmake)
7285
73-
CPMAddPackage(
74-
GITHUB_REPOSITORY catchorg/Catch2
75-
VERSION 2.5.0
76-
)
86+
CPMAddPackage(gh:catchorg/Catch2@2.5.0)
7787
7888
# link dependencies
7989
target_link_libraries(tests Catch2)
@@ -244,21 +254,13 @@ See the [wiki](https://github.com/cpm-cmake/CPM.cmake/wiki/More-Snippets) for mo
244254
### [Catch2](https://github.com/catchorg/Catch2)
245255

246256
```cmake
247-
CPMAddPackage(
248-
NAME Catch2
249-
GITHUB_REPOSITORY catchorg/Catch2
250-
VERSION 2.5.0
251-
)
257+
CPMAddPackage(gh:catchorg/Catch2@2.5.0)
252258
```
253259

254260
### [Boost (via boost-cmake)](https://github.com/Orphis/boost-cmake)
255261

256262
```CMake
257-
CPMAddPackage(
258-
NAME boost-cmake
259-
GITHUB_REPOSITORY Orphis/boost-cmake
260-
VERSION 1.67.0
261-
)
263+
CPMAddPackage(gh:Orphis/boost-cmake@1.67.0)
262264
```
263265

264266
### [cxxopts](https://github.com/jarro2783/cxxopts)

cmake/CPM.cmake

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,85 @@ function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION CPM_A
252252
endif()
253253
endfunction()
254254

255+
# Parse the argument of CPMAddPackage in case a single one was provided and convert it to a list of
256+
# arguments which can then be parsed idiomatically. For example gh:foo/bar@1.2.3 will be converted
257+
# to: GITHUB_REPOSITORY;foo/bar;VERSION;1.2.3
258+
function(cpm_parse_add_package_single_arg arg outArgs)
259+
# Look for a scheme
260+
if("${arg}" MATCHES "^([a-zA-Z]+):(.+)$")
261+
string(TOLOWER "${CMAKE_MATCH_1}" scheme)
262+
set(uri "${CMAKE_MATCH_2}")
263+
264+
# Check for CPM-specific schemes
265+
if(scheme STREQUAL "gh")
266+
set(out "GITHUB_REPOSITORY;${uri}")
267+
set(packageType "git")
268+
elseif(scheme STREQUAL "gl")
269+
set(out "GITLAB_REPOSITORY;${uri}")
270+
set(packageType "git")
271+
# A CPM-specific scheme was not found. Looks like this is a generic URL so try to determine
272+
# type
273+
elseif(arg MATCHES ".git/?(@|#|$)")
274+
set(out "GIT_REPOSITORY;${arg}")
275+
set(packageType "git")
276+
else()
277+
# This error here is temporary. We can't provide URLs from here until we support inferring the
278+
# package name from an url. When this is supported, remove this error as well as commented out
279+
# tests in test/unit/parse_add_package_single_arg.cmake
280+
message(FATAL_ERROR "CPM: Unsupported package type of '${arg}'")
281+
282+
# Fall back to a URL
283+
set(out "URL;${arg}")
284+
set(packageType "archive")
285+
286+
# We could also check for SVN since FetchContent supports it, but SVN is so rare these days.
287+
# We just won't bother with the additional complexity it will induce in this function. SVN is
288+
# done by multi-arg
289+
endif()
290+
else()
291+
if(arg MATCHES ".git/?(@|#|$)")
292+
set(out "GIT_REPOSITORY;${arg}")
293+
set(packageType "git")
294+
else()
295+
# Give up
296+
message(FATAL_ERROR "CPM: Can't determine package type of '${arg}'")
297+
endif()
298+
endif()
299+
300+
# For all packages we interpret @... as version. Only replace the last occurence. Thus URIs
301+
# containing '@' can be used
302+
string(REGEX REPLACE "@([^@]+)$" ";VERSION;\\1" out "${out}")
303+
304+
# Parse the rest according to package type
305+
if(packageType STREQUAL "git")
306+
# For git repos we interpret #... as a tag or branch or commit hash
307+
string(REGEX REPLACE "#([^#]+)$" ";GIT_TAG;\\1" out "${out}")
308+
elseif(packageType STREQUAL "archive")
309+
# For archives we interpret #... as a URL hash.
310+
string(REGEX REPLACE "#([^#]+)$" ";URL_HASH;\\1" out "${out}")
311+
# We don't try to parse the version if it's not provided explicitly. cpm_get_version_from_url
312+
# should do this at a later point
313+
else()
314+
# We should never get here. This is an assertion and hitting it means there's a bug in the code
315+
# above. A packageType was set, but not handled by this if-else.
316+
message(FATAL_ERROR "CPM: Unsupported package type '${packageType}' of '${arg}'")
317+
endif()
318+
319+
set(${outArgs}
320+
${out}
321+
PARENT_SCOPE
322+
)
323+
endfunction()
324+
255325
# Download and add a package from source
256326
function(CPMAddPackage)
327+
list(LENGTH ARGN argnLength)
328+
if(argnLength EQUAL 1)
329+
cpm_parse_add_package_single_arg("${ARGN}" ARGN)
330+
331+
# The shorthand syntax implies EXCLUDE_FROM_ALL
332+
set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES")
333+
endif()
257334

258335
set(oneValueArgs
259336
NAME

cmake/testing.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ function(ASSERT_EQUAL)
33
message(FATAL_ERROR "assertion failed: invalid argument count: ${ARGC}")
44
endif()
55

6-
if(NOT ${ARGV0} STREQUAL ${ARGV1})
6+
if(NOT "${ARGV0}" STREQUAL "${ARGV1}")
77
message(FATAL_ERROR "assertion failed: '${ARGV0}' != '${ARGV1}'")
88
else()
99
message(STATUS "test passed: '${ARGV0}' == '${ARGV1}'")
@@ -15,7 +15,7 @@ function(ASSERT_NOT_EQUAL)
1515
message(FATAL_ERROR "assertion failed: invalid argument count: ${ARGC}")
1616
endif()
1717

18-
if(${ARGV0} STREQUAL ${ARGV1})
18+
if("${ARGV0}" STREQUAL "${ARGV1}")
1919
message(FATAL_ERROR "assertion failed: '${ARGV0}' == '${ARGV1}'")
2020
else()
2121
message(STATUS "test passed: '${ARGV0}' != '${ARGV1}'")
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
2+
3+
include(${CPM_PATH}/CPM.cmake)
4+
include(${CPM_PATH}/testing.cmake)
5+
6+
cpm_parse_add_package_single_arg("gh:cpm-cmake/CPM.cmake" args)
7+
assert_equal("GITHUB_REPOSITORY;cpm-cmake/CPM.cmake" "${args}")
8+
9+
cpm_parse_add_package_single_arg("gh:cpm-cmake/CPM.cmake@1.2.3" args)
10+
assert_equal("GITHUB_REPOSITORY;cpm-cmake/CPM.cmake;VERSION;1.2.3" "${args}")
11+
12+
cpm_parse_add_package_single_arg("gh:cpm-cmake/CPM.cmake#master" args)
13+
assert_equal("GITHUB_REPOSITORY;cpm-cmake/CPM.cmake;GIT_TAG;master" "${args}")
14+
15+
cpm_parse_add_package_single_arg("gh:cpm-cmake/CPM.cmake@0.20.3#asdf" args)
16+
assert_equal("GITHUB_REPOSITORY;cpm-cmake/CPM.cmake;VERSION;0.20.3;GIT_TAG;asdf" "${args}")
17+
18+
cpm_parse_add_package_single_arg("gh:a/b#c@d" args)
19+
assert_equal("GITHUB_REPOSITORY;a/b;GIT_TAG;c;VERSION;d" "${args}")
20+
21+
cpm_parse_add_package_single_arg("gh:foo#c@d" args)
22+
assert_equal("GITHUB_REPOSITORY;foo;GIT_TAG;c;VERSION;d" "${args}")
23+
24+
cpm_parse_add_package_single_arg("gh:Foo@5" args)
25+
assert_equal("GITHUB_REPOSITORY;Foo;VERSION;5" "${args}")
26+
27+
cpm_parse_add_package_single_arg("gl:foo/bar" args)
28+
assert_equal("GITLAB_REPOSITORY;foo/bar" "${args}")
29+
30+
cpm_parse_add_package_single_arg("gl:foo/Bar" args)
31+
assert_equal("GITLAB_REPOSITORY;foo/Bar" "${args}")
32+
33+
cpm_parse_add_package_single_arg("https://github.com/cpm-cmake/CPM.cmake.git@0.30.5" args)
34+
assert_equal("GIT_REPOSITORY;https://github.com/cpm-cmake/CPM.cmake.git;VERSION;0.30.5" "${args}")
35+
36+
cpm_parse_add_package_single_arg("git@host.xz:user/pkg.git@0.1.2" args)
37+
assert_equal("GIT_REPOSITORY;git@host.xz:user/pkg.git;VERSION;0.1.2" "${args}")
38+
39+
cpm_parse_add_package_single_arg("git@host.xz:user/pkg.git@0.1.2#rc" args)
40+
assert_equal("GIT_REPOSITORY;git@host.xz:user/pkg.git;VERSION;0.1.2;GIT_TAG;rc" "${args}")
41+
42+
cpm_parse_add_package_single_arg(
43+
"ssh://user@host.xz:123/path/to/pkg.git#fragment@1.2.3#branch" args
44+
)
45+
assert_equal(
46+
"GIT_REPOSITORY;ssh://user@host.xz:123/path/to/pkg.git#fragment;VERSION;1.2.3;GIT_TAG;branch"
47+
"${args}"
48+
)
49+
50+
# The following test cases are to be used in the future, once single-argument archives are supported
51+
52+
# cpm_parse_add_package_single_arg("https://example.org/foo.tar.gz" args)
53+
54+
# assert_equal("URL;https://example.org/foo.tar.gz" "${args}")
55+
56+
# cpm_parse_add_package_single_arg("https://example.org/foo.tar.gz#baadf00d@1.2.0" args)
57+
58+
# assert_equal("URL;https://example.org/foo.tar.gz;URL_HASH;baadf00d;VERSION;1.2.0" "${args}")
59+
60+
# cpm_parse_add_package_single_arg("ftp://user:password@server/pathname.zip#fragment#0ddb411@0"
61+
# args)
62+
63+
# assert_equal("URL;ftp://user:password@server/pathname.zip#fragment;URL_HASH;0ddb411;VERSION;0"
64+
# "${args}")

0 commit comments

Comments
 (0)