Skip to content

Commit 570ec4c

Browse files
committed
Protobuf example
1 parent fc00cf7 commit 570ec4c

11 files changed

Lines changed: 438 additions & 1 deletion

File tree

docs/cmake-toml.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ tag = "v0.1"
199199
shallow = false # shallow clone (--depth 1)
200200
system = false
201201
subdir = "" # folder containing CMakeLists.txt
202+
# Set cache variables before FetchContent_MakeAvailable
203+
options = { BUILD_TESTS = false, BUILD_EXAMPLES = false }
202204

203205
# Include a CMake project from a URL
204206
[fetch-content.urlcontent]
@@ -213,10 +215,19 @@ sha1 = "502a4e25b8b209889c99c7fa0732102682c2e4ff"
213215
condition = "mycondition"
214216
svn = "https://svn-host.com/url"
215217
rev = "svn_rev"
218+
219+
# For many options, you can also use a separate table:
220+
# [fetch-content.bigproject.options]
221+
# BUILD_TESTS = false
222+
# BUILD_EXAMPLES = false
223+
# SOME_STRING_OPTION = "value"
224+
# SOME_LIST_OPTION = ["item1", "item2"] # becomes "item1;item2"
216225
```
217226

218227
Table keys that match CMake variable names (`[A-Z_]+`) will be passed to the [`FetchContent_Declare`](https://cmake.org/cmake/help/latest/module/FetchContent.html#command:fetchcontent_declare) command.
219228

229+
The `options` field allows setting CACHE variables before `FetchContent_MakeAvailable` is called. This is useful for configuring options on the fetched dependency (e.g., disabling tests). Boolean values become `ON`/`OFF`, arrays become semicolon-separated strings.
230+
220231
## Targets
221232

222233
```toml

docs/examples/protobuf.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
---
2+
# Automatically generated from tests/protobuf/cmake.toml - DO NOT EDIT
3+
layout: default
4+
title: Demonstrates protobuf integration with cmkr custom commands
5+
permalink: /examples/protobuf
6+
parent: Examples
7+
nav_order: 14
8+
---
9+
10+
# Demonstrates protobuf integration with cmkr custom commands
11+
12+
This test demonstrates using protobuf with cmkr for code generation.
13+
14+
```toml
15+
#
16+
# The example:
17+
# 1. Fetches protobuf from GitHub using FetchContent
18+
# 2. Uses a custom command to generate C++ sources from a .proto file
19+
# 3. Links an executable against the generated sources and protobuf library
20+
# 4. Serializes and deserializes a simple message
21+
#
22+
# Note: protobuf v21.x is used because newer versions (v22+) require abseil-cpp
23+
# as a dependency, which significantly complicates the build.
24+
25+
[cmake]
26+
version = "3.18...3.31"
27+
28+
[project]
29+
name = "protobuf-example"
30+
description = "Demonstrates protobuf integration with cmkr custom commands"
31+
32+
# -----------------------------------------------------------------------------
33+
# Fetch protobuf from GitHub
34+
# Using v21.12 (last version before abseil-cpp became required)
35+
# -----------------------------------------------------------------------------
36+
37+
[fetch-content.protobuf]
38+
git = "https://github.com/protocolbuffers/protobuf"
39+
tag = "v21.12"
40+
options = {
41+
protobuf_BUILD_TESTS = false,
42+
protobuf_BUILD_EXAMPLES = false,
43+
protobuf_BUILD_LIBPROTOC = false,
44+
protobuf_BUILD_SHARED_LIBS = false,
45+
protobuf_MSVC_STATIC_RUNTIME = false,
46+
SKIP_INSTALL_ALL = true,
47+
protobuf_WITH_ZLIB = false,
48+
}
49+
50+
# -----------------------------------------------------------------------------
51+
# Main executable using protobuf
52+
# -----------------------------------------------------------------------------
53+
54+
[target.protobuf_example]
55+
type = "executable"
56+
sources = [
57+
"src/main.cpp",
58+
]
59+
include-directories = [
60+
"${CMAKE_CURRENT_BINARY_DIR}/generated",
61+
]
62+
link-libraries = ["protobuf::libprotobuf"]
63+
compile-features = ["cxx_std_17"]
64+
65+
# Create the generated directory before running protoc
66+
cmake-before = """
67+
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated")
68+
"""
69+
70+
# Custom command to generate C++ sources from .proto file
71+
# This uses protoc from the fetched protobuf repository
72+
[[target.protobuf_example.custom-command]]
73+
outputs = [
74+
"${CMAKE_CURRENT_BINARY_DIR}/generated/addressbook.pb.h",
75+
"${CMAKE_CURRENT_BINARY_DIR}/generated/addressbook.pb.cc",
76+
]
77+
depends = [
78+
"${CMAKE_CURRENT_SOURCE_DIR}/proto/addressbook.proto",
79+
"protobuf::protoc", # Ensure protoc is built first
80+
]
81+
command = [
82+
"$<TARGET_FILE:protobuf::protoc>",
83+
"--cpp_out=${CMAKE_CURRENT_BINARY_DIR}/generated",
84+
"-I${CMAKE_CURRENT_SOURCE_DIR}/proto",
85+
"${CMAKE_CURRENT_SOURCE_DIR}/proto/addressbook.proto",
86+
]
87+
comment = "Generate C++ sources from addressbook.proto"
88+
verbatim = true
89+
90+
# Post-build: run the executable to verify it works
91+
[[target.protobuf_example.custom-command]]
92+
build-event = "post-build"
93+
command = ["$<TARGET_FILE:protobuf_example>"]
94+
comment = "Run protobuf_example to verify serialization/deserialization works"
95+
```
96+
97+
98+
99+
<sup><sub>This page was automatically generated from [tests/protobuf/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/protobuf/cmake.toml).</sub></sup>

include/project_parser.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,15 @@ struct Subdir {
255255
ConditionVector include_after;
256256
};
257257

258+
struct ContentOption {
259+
mpark::variant<bool, std::string> value;
260+
};
261+
258262
struct Content {
259263
std::string name;
260264
std::string condition;
261265
tsl::ordered_map<std::string, std::string> arguments;
266+
tsl::ordered_map<std::string, ContentOption> options;
262267

263268
Condition<std::string> cmake_before;
264269
Condition<std::string> cmake_after;

src/cmake_generator.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ void generate_project(const std::string &type) {
256256
generate_gitfile(".gitattributes", {"/**/CMakeLists.txt linguist-generated", "/**/cmkr.cmake linguist-vendored"});
257257

258258
// Generate .gitignore with reasonable defaults for CMake
259-
generate_gitfile(".gitignore", {"build*/", "cmake-build*/", "CMakerLists.txt", "CMakeLists.txt.user"});
259+
generate_gitfile(".gitignore", {"build*/", "cmake-build*/", "CMakerLists.txt", "CMakeLists.txt.user", "CMakeUserPresets.json"});
260260

261261
tsl::ordered_map<std::string, std::string> variables = {
262262
{"@name", name},
@@ -1118,6 +1118,22 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
11181118
gen.conditional_includes(content.include_before);
11191119
gen.conditional_cmake(content.cmake_before);
11201120

1121+
// Set cache variables before FetchContent
1122+
if (!content.options.empty()) {
1123+
for (const auto &opt : content.options) {
1124+
std::string value;
1125+
std::string type;
1126+
if (opt.second.value.index() == 0) {
1127+
value = mpark::get<0>(opt.second.value) ? "ON" : "OFF";
1128+
type = "BOOL";
1129+
} else {
1130+
value = mpark::get<1>(opt.second.value);
1131+
type = "STRING";
1132+
}
1133+
cmd("set")(opt.first, value, "CACHE", type, RawArg("\"\""), "FORCE");
1134+
}
1135+
}
1136+
11211137
std::string version_info;
11221138
if (content.arguments.contains("GIT_TAG")) {
11231139
version_info = " (" + content.arguments.at("GIT_TAG") + ")";

src/project_parser.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,37 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
593593
"system", "");
594594
}
595595

596+
// Parse options table - these are CACHE variables set before FetchContent
597+
if (c.contains("options")) {
598+
c.visit("options");
599+
const auto &opts = toml::find(itr.second, "options").as_table();
600+
for (const auto &optItr : opts) {
601+
ContentOption opt;
602+
if (optItr.second.is_boolean()) {
603+
opt.value = optItr.second.as_boolean();
604+
} else if (optItr.second.is_array()) {
605+
// Arrays become semicolon-separated lists
606+
std::string list_value;
607+
for (const auto &list_val : optItr.second.as_array()) {
608+
if (!list_value.empty()) {
609+
list_value += ';';
610+
}
611+
list_value += list_val.as_string();
612+
}
613+
opt.value = list_value;
614+
} else {
615+
opt.value = optItr.second.as_string();
616+
}
617+
content.options.emplace(optItr.first, opt);
618+
}
619+
}
620+
596621
for (const auto &argItr : itr.second.as_table()) {
622+
// Skip keys that were already handled (like "options")
623+
if (c.visisted(argItr.first)) {
624+
continue;
625+
}
626+
597627
std::string value;
598628
if (argItr.second.is_array()) {
599629
for (const auto &list_val : argItr.second.as_array()) {

tests/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/cmake.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,9 @@ name = "generator-executable"
8383
working-directory = "generator-executable"
8484
command = "$<TARGET_FILE:cmkr>"
8585
arguments = ["build"]
86+
87+
[[test]]
88+
name = "protobuf"
89+
working-directory = "protobuf"
90+
command = "$<TARGET_FILE:cmkr>"
91+
arguments = ["build"]

tests/protobuf/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build/
2+
*.bin

tests/protobuf/cmake.toml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# This test demonstrates using protobuf with cmkr for code generation.
2+
#
3+
# The example:
4+
# 1. Fetches protobuf from GitHub using FetchContent
5+
# 2. Uses a custom command to generate C++ sources from a .proto file
6+
# 3. Links an executable against the generated sources and protobuf library
7+
# 4. Serializes and deserializes a simple message
8+
#
9+
# Note: protobuf v21.x is used because newer versions (v22+) require abseil-cpp
10+
# as a dependency, which significantly complicates the build.
11+
12+
[cmake]
13+
version = "3.18...3.31"
14+
15+
[project]
16+
name = "protobuf-example"
17+
description = "Demonstrates protobuf integration with cmkr custom commands"
18+
19+
# -----------------------------------------------------------------------------
20+
# Fetch protobuf from GitHub
21+
# Using v21.12 (last version before abseil-cpp became required)
22+
# -----------------------------------------------------------------------------
23+
24+
[fetch-content.protobuf]
25+
git = "https://github.com/protocolbuffers/protobuf"
26+
tag = "v21.12"
27+
options = {
28+
protobuf_BUILD_TESTS = false,
29+
protobuf_BUILD_EXAMPLES = false,
30+
protobuf_BUILD_LIBPROTOC = false,
31+
protobuf_BUILD_SHARED_LIBS = false,
32+
protobuf_MSVC_STATIC_RUNTIME = false,
33+
SKIP_INSTALL_ALL = true,
34+
protobuf_WITH_ZLIB = false,
35+
}
36+
37+
# -----------------------------------------------------------------------------
38+
# Main executable using protobuf
39+
# -----------------------------------------------------------------------------
40+
41+
[target.protobuf_example]
42+
type = "executable"
43+
sources = [
44+
"src/main.cpp",
45+
]
46+
include-directories = [
47+
"${CMAKE_CURRENT_BINARY_DIR}/generated",
48+
]
49+
link-libraries = ["protobuf::libprotobuf"]
50+
compile-features = ["cxx_std_17"]
51+
52+
# Create the generated directory before running protoc
53+
cmake-before = """
54+
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated")
55+
"""
56+
57+
# Custom command to generate C++ sources from .proto file
58+
# This uses protoc from the fetched protobuf repository
59+
[[target.protobuf_example.custom-command]]
60+
outputs = [
61+
"${CMAKE_CURRENT_BINARY_DIR}/generated/addressbook.pb.h",
62+
"${CMAKE_CURRENT_BINARY_DIR}/generated/addressbook.pb.cc",
63+
]
64+
depends = [
65+
"${CMAKE_CURRENT_SOURCE_DIR}/proto/addressbook.proto",
66+
"protobuf::protoc", # Ensure protoc is built first
67+
]
68+
command = [
69+
"$<TARGET_FILE:protobuf::protoc>",
70+
"--cpp_out=${CMAKE_CURRENT_BINARY_DIR}/generated",
71+
"-I${CMAKE_CURRENT_SOURCE_DIR}/proto",
72+
"${CMAKE_CURRENT_SOURCE_DIR}/proto/addressbook.proto",
73+
]
74+
comment = "Generate C++ sources from addressbook.proto"
75+
verbatim = true
76+
77+
# Post-build: run the executable to verify it works
78+
[[target.protobuf_example.custom-command]]
79+
build-event = "post-build"
80+
command = ["$<TARGET_FILE:protobuf_example>"]
81+
comment = "Run protobuf_example to verify serialization/deserialization works"
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// A simple address book example demonstrating protobuf serialization.
2+
// Based on the official protobuf tutorial example.
3+
4+
syntax = "proto3";
5+
6+
package tutorial;
7+
8+
// A person with basic contact information
9+
message Person {
10+
string name = 1;
11+
int32 id = 2;
12+
string email = 3;
13+
14+
enum PhoneType {
15+
MOBILE = 0;
16+
HOME = 1;
17+
WORK = 2;
18+
}
19+
20+
message PhoneNumber {
21+
string number = 1;
22+
PhoneType type = 2;
23+
}
24+
25+
repeated PhoneNumber phones = 4;
26+
}
27+
28+
// An address book containing multiple people
29+
message AddressBook {
30+
repeated Person people = 1;
31+
}

0 commit comments

Comments
 (0)