Skip to content

Commit 05bfbfb

Browse files
committed
Add an option to use a package manager
Projects can now also be generated to use a package manager (Conan or vcpkg). The -p flag accepts a "conan" or "vcpkg" argument to select either. Omit the flag to generate projects without dependencies already setup.
1 parent 47cde57 commit 05bfbfb

39 files changed

+610
-123
lines changed

.github/workflows/ci.yml

+27-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ jobs:
2828
matrix:
2929
job: [1, 2, 3, 4, 5, 6, 7, 8]
3030

31+
pm: [none, conan, vcpkg]
32+
3133
os: [macos-latest, ubuntu-latest, windows-2022]
3234

3335
include:
@@ -40,7 +42,7 @@ jobs:
4042
- { job: 7, type: "--c -h --examples", name: "Library (C): header-only", flag: "" }
4143
- { job: 8, type: "--c -e", name: "Executable (C)", flag: "" }
4244

43-
name: ${{ matrix.name }} (${{ matrix.os }})
45+
name: "${{ matrix.name }} (${{ matrix.os }}) (PM: ${{ matrix.pm }})"
4446

4547
runs-on: ${{ matrix.os }}
4648

@@ -57,7 +59,7 @@ jobs:
5759
- name: Create project
5860
run: |
5961
python -m zipapp cmake-init -p "/usr/bin/env python3"
60-
python cmake-init.pyz ${{ matrix.type }} proj
62+
python cmake-init.pyz ${{ matrix.type }} -p ${{ matrix.pm }} proj
6163
6264
- name: Test --vcpkg mode
6365
working-directory: proj
@@ -70,6 +72,25 @@ jobs:
7072
working-directory: proj
7173
run: cmake -D FORMAT_COMMAND=clang-format-11 -P cmake/lint.cmake
7274

75+
- name: Install Conan
76+
if: matrix.pm == 'conan'
77+
working-directory: proj
78+
shell: bash
79+
run: |
80+
pip3 install conan
81+
conan profile new default --detect
82+
if [ ${{ matrix.os }} = ubuntu-latest ]; then
83+
conan profile update settings.compiler.libcxx=libstdc++11 default
84+
fi
85+
conan install . -if conan -b missing
86+
87+
- name: Install vcpkg
88+
if: matrix.pm == 'vcpkg'
89+
uses: friendlyanon/setup-vcpkg@v1
90+
with:
91+
committish: "5cf60186a241e84e8232641ee973395d4fde90e1"
92+
ignore-reserve-cache-error: true
93+
7394
- name: Configure
7495
shell: pwsh
7596
run: cmake -S proj "--preset=ci-$("${{ matrix.os }}".split("-")[0])"
@@ -81,5 +102,9 @@ jobs:
81102
- name: Install
82103
run: cmake --install proj/build --config Release --prefix proj/prefix
83104

105+
- name: Setup PATH
106+
if: matrix.os == 'windows-2022' && matrix.pm != 'none'
107+
run: Add-Content "$env:GITHUB_PATH" "$(Get-Location)\proj\build\Release"
108+
84109
- name: Test
85110
run: ctest --test-dir proj/build -C Release

cmake-init/cmake_init.py

+47-4
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,22 @@ def ask(*args, **kwargs):
168168
"include_source": False,
169169
"has_source": True,
170170
"cpus": os.cpu_count(),
171+
"pm_name": "",
171172
}
173+
package_manager = ask(
174+
"Package manager to use ([N]one/[c]onan/[v]cpkg)",
175+
cli_args.package_manager or "n",
176+
mapper=lambda v: v[0:1].lower(),
177+
predicate=lambda v: v in ["n", "c", "v"],
178+
header="""\
179+
Choosing Conan requires it to be installed. Choosing vcpkg requires the
180+
VCPKG_ROOT environment variable to be setup to vcpkg's root directory.""",
181+
)
182+
d["vcpkg"] = package_manager == "v"
183+
d["conan"] = package_manager == "c"
184+
d["pm"] = package_manager != "n"
185+
if d["pm"]:
186+
d["pm_name"] = "conan" if d["conan"] else "vcpkg"
172187
d["uc_name"] = d["name"].upper().replace("-", "_")
173188
if d["type_id"] != "e":
174189
key = "c_examples" if cli_args.c else "cpp_examples"
@@ -216,20 +231,38 @@ def should_write_examples(d, at):
216231

217232

218233
def should_install_file(name, d):
219-
if name == "install-config.cmake" and d["type_id"] == "e":
234+
if name == "vcpkg.json" and not d["vcpkg"]:
235+
return False
236+
if name == "conanfile.txt" and not d["conan"]:
237+
return False
238+
if name == "install-config.cmake" and d["exe"]:
239+
return False
240+
if name == "windows-set-path.cmake" and d["pm"]:
241+
return False
242+
if name == "install-script.cmake" and not d["exe"]:
243+
return False
244+
if name == "header_impl.c" and (not d["c_header"] or not d["pm"]):
220245
return False
221-
if name == "install-script.cmake" and d["type_id"] != "e":
246+
if name == "clang-11.profile" and not d["conan"]:
222247
return False
223248
return True
224249

225250

251+
def transform_path(path, d):
252+
if not d["exe"] and d["pm"] and path.endswith("install-config.cmake"):
253+
return f"{path}.in"
254+
if d["c"] and d["pm"] and path.endswith("_test.c"):
255+
return f"{path}pp"
256+
return path
257+
258+
226259
def write_dir(path, d, overwrite, zip_path):
227260
for entry in zip_path.iterdir():
228261
name = entry.name.replace("__name__", d["name"])
229262
next_path = os.path.join(path, name)
230263
if entry.is_file():
231264
if should_install_file(name, d):
232-
write_file(next_path, d, overwrite, entry)
265+
write_file(transform_path(next_path, d), d, overwrite, entry)
233266
elif name != "example" or should_write_examples(d, entry.at):
234267
mkdir(next_path)
235268
write_dir(next_path, d, overwrite, entry)
@@ -273,11 +306,15 @@ def git_init(cwd):
273306

274307
def print_tips(d):
275308
config = " --config Debug" if is_windows else ""
309+
conan = ""
310+
if d["conan"]:
311+
conan = """
312+
conan install . -if conan -s build_type=Debug -b missing"""
276313
print(f"""\
277314
To get you started with the project in developer mode, you may configure,
278315
build, install and test with the following commands from the project directory,
279316
in that order:
280-
317+
{conan}
281318
cmake --preset=dev
282319
cmake --build --preset=dev
283320
cmake --install build/dev{config} --prefix prefix
@@ -509,6 +546,12 @@ def main(zip):
509546
const="n",
510547
help="generate examples for a library",
511548
)
549+
p.add_argument(
550+
"-p",
551+
metavar="pm",
552+
dest="package_manager",
553+
help="package manager to use (Options are: conan, vcpkg)",
554+
)
512555
args = p.parse_args()
513556
if args.dummy:
514557
p.print_help()

cmake-init/templates/c/executable/CMakeLists.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ target_include_directories(
2626
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/source>"
2727
)
2828

29-
target_compile_features({= name =}_lib PUBLIC c_std_{= std =})
29+
target_compile_features({= name =}_lib PUBLIC c_std_{= std =}){% if pm %}
30+
31+
find_package(json-c REQUIRED)
32+
target_link_libraries({= name =}_lib PRIVATE json-c::json-c){% end %}
3033

3134
# ---- Declare executable ----
3235

Original file line numberDiff line numberDiff line change
@@ -1,8 +1,63 @@
1-
#include "lib.h"
1+
#include "lib.h"{% if pm %}
2+
3+
#include <json-c/json_object.h>
4+
#include <json-c/json_tokener.h>
5+
#include <stddef.h>
6+
#include <stdlib.h>
7+
#include <string.h>
8+
9+
static const char json[] = "{\"name\":\"{= name =}\"}";{% end %}
210

311
library create_library()
412
{
5-
library lib;
6-
lib.name = "{= name =}";
13+
library lib;{% if pm %}
14+
struct json_tokener* tokener = NULL;
15+
struct json_object* object = NULL;
16+
struct json_object* name_object = NULL;
17+
const char* json_name = NULL;
18+
size_t name_size = 0;
19+
char* name = NULL;
20+
21+
tokener = json_tokener_new();
22+
if (tokener == NULL) {
23+
goto exit;
24+
}
25+
26+
object = json_tokener_parse_ex(tokener, json, sizeof(json));
27+
if (object == NULL) {
28+
goto cleanup_tokener;
29+
}
30+
31+
if (json_object_object_get_ex(object, "name", &name_object) == 0) {
32+
goto cleanup_object;
33+
}
34+
35+
json_name = json_object_get_string(name_object);
36+
if (json_name == NULL) {
37+
goto cleanup_object;
38+
}
39+
40+
name_size = strlen(json_name) + 1;
41+
name = malloc(name_size);
42+
if (name == NULL) {
43+
goto cleanup_object;
44+
}
45+
46+
(void)memcpy(name, json_name, name_size);
47+
48+
cleanup_object:
49+
(void)json_object_put(object);
50+
51+
cleanup_tokener:
52+
json_tokener_free(tokener);
53+
54+
exit:
55+
lib.name = name;{% else %}
56+
lib.name = "{= name =}";{% end %}
757
return lib;
8-
}
58+
}{% if pm %}
59+
60+
void destroy_library(library* lib)
61+
{
62+
free((void*)lib->name);
63+
}{% end %}

cmake-init/templates/c/executable/source/lib.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,9 @@ typedef struct library {
1010
/**
1111
* @brief Creates an instance of library with the name of the project
1212
*/
13-
library create_library(void);
13+
library create_library(void);{% if pm %}
14+
15+
/**
16+
* @brief Destroys resources held by the library
17+
*/
18+
void destroy_library(library* lib);{% end %}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
1-
#include <stdio.h>
1+
{% if pm %}#include <stddef.h>
2+
{% end %}#include <stdio.h>
23

34
#include "lib.h"
45

56
int main(int argc, const char* argv[])
67
{
8+
library lib = create_library();
9+
710
(void)argc;
811
(void)argv;
9-
10-
library lib = create_library();
11-
printf("Hello from %%s!", lib.name);
12+
{% if not pm %}
13+
(void)printf("Hello from %s!", lib.name);{% else %}
14+
if (lib.name == NULL) {
15+
(void)puts("Hello from unknown! (JSON parsing failed in library)");
16+
} else {
17+
(void)printf("Hello from %s!", lib.name);
18+
}
19+
destroy_library(&lib);{% end %}
1220
return 0;
1321
}

cmake-init/templates/c/executable/test/CMakeLists.txt

-13
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,37 @@
1-
#include <string.h>
1+
{% if pm %}#include <memory>
2+
#include <string>
23

3-
#include "lib.h"
4+
#include <catch2/catch.hpp>{% else %}#include <string.h>{% end %}
45

6+
{% if pm %}extern "C" {
7+
{% end %}#include "lib.h"
8+
{% if pm %}}
9+
10+
namespace {
11+
12+
struct library_delete
13+
{
14+
void operator()(void* p) const
15+
{
16+
destroy_library(static_cast<library*>(p));
17+
}
18+
};
19+
20+
} // namespace
21+
22+
TEST_CASE("Name is {= name =}", "[library]")
23+
{
24+
library lib = create_library();
25+
auto ptr = std::unique_ptr<library, library_delete>(&lib, {});
26+
27+
REQUIRE(std::string("{= name =}") == lib.name);
28+
}{% else %}
529
int main(int argc, const char* argv[])
630
{
31+
library lib = create_library();
32+
733
(void)argc;
834
(void)argv;
935

10-
library lib = create_library();
11-
1236
return strcmp(lib.name, "{= name =}") == 0 ? 0 : 1;
13-
}
37+
}{% end %}

cmake-init/templates/c/header/CMakeLists.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ target_include_directories(
2929
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
3030
)
3131

32-
target_compile_features({= name =}_{= name =} INTERFACE c_std_{= std =})
32+
target_compile_features({= name =}_{= name =} INTERFACE c_std_{= std =}){% if pm %}
33+
34+
find_package(json-c REQUIRED)
35+
target_link_libraries({= name =}_{= name =} INTERFACE json-c::json-c){% end %}
3336

3437
# ---- Install rules ----
3538

0 commit comments

Comments
 (0)