mirror of
https://github.com/build-cpp/cmkr
synced 2026-06-08 13:22:55 +00:00
Generator executable example
This commit is contained in:
@@ -9,18 +9,44 @@ nav_order: 12
|
||||
|
||||
# Tests add_custom_command and add_custom_target support
|
||||
|
||||
|
||||
This test demonstrates add_custom_command and add_custom_target support in cmkr.
|
||||
|
||||
```toml
|
||||
#
|
||||
# There are two forms of add_custom_command in CMake:
|
||||
# 1. Output form: generates files that can be consumed by other targets
|
||||
# 2. Target form: runs commands at build time for a specific target (pre-build, pre-link, post-build)
|
||||
|
||||
[project]
|
||||
name = "custom-command"
|
||||
description = "Tests add_custom_command and add_custom_target support"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Simple executable demonstrating post-build events
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.hello]
|
||||
type = "executable"
|
||||
sources = ["src/hello.cpp"]
|
||||
|
||||
# This post-build command runs after the 'hello' executable is built.
|
||||
# build-event can be: "pre-build", "pre-link", or "post-build"
|
||||
[[target.hello.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["${CMAKE_COMMAND}", "-E", "echo", "Built executable: $<TARGET_FILE_NAME:hello>"]
|
||||
comment = "Print the built executable name"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Executable with code generation (output form custom command)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.custom-command]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
include-directories = ["${CMAKE_CURRENT_BINARY_DIR}/generated"]
|
||||
|
||||
# Output form: This custom command generates source files before building.
|
||||
# The outputs are automatically added as sources to the target.
|
||||
[[target.custom-command.custom-command]]
|
||||
outputs = ["${CMAKE_CURRENT_BINARY_DIR}/generated/generated.cpp"]
|
||||
byproducts = ["${CMAKE_CURRENT_BINARY_DIR}/generated/generated.hpp"]
|
||||
@@ -35,6 +61,7 @@ command = [
|
||||
comment = "Generate source files"
|
||||
verbatim = true
|
||||
|
||||
# Target form: This post-build command runs after 'custom-command' is built.
|
||||
[[target.custom-command.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = [
|
||||
@@ -47,6 +74,12 @@ byproducts = ["${CMAKE_CURRENT_BINARY_DIR}/custom-command-post-build.stamp"]
|
||||
comment = "Create a post-build stamp file"
|
||||
verbatim = true
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Custom target (add_custom_target)
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# A custom target runs commands independently of any executable/library.
|
||||
# Setting 'all = true' makes it run as part of the default build.
|
||||
[target.custom-codegen]
|
||||
type = "custom"
|
||||
all = true
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
# Automatically generated from tests/generator-executable/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Tests using an executable to generate sources for another target
|
||||
permalink: /examples/generator-executable
|
||||
parent: Examples
|
||||
nav_order: 13
|
||||
---
|
||||
|
||||
# Tests using an executable to generate sources for another target
|
||||
|
||||
This test demonstrates a common pattern: using an executable to generate sources
|
||||
|
||||
for another target. The generator runs at build time and produces code that is
|
||||
|
||||
consumed by the main executable.
|
||||
|
||||
```toml
|
||||
#
|
||||
# CMake handles the dependency automatically: the generator executable is built
|
||||
# first, then it runs to produce the generated sources, and finally the main
|
||||
# executable is built using those sources.
|
||||
|
||||
[project]
|
||||
name = "generator-executable"
|
||||
description = "Tests using an executable to generate sources for another target"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Generator executable: produces code at build time
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.generate_numbers]
|
||||
type = "executable"
|
||||
sources = ["src/generate_numbers.cpp"]
|
||||
|
||||
# Post-build: confirm the generator was built
|
||||
[[target.generate_numbers.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["${CMAKE_COMMAND}", "-E", "echo", "Generator built successfully: $<TARGET_FILE_NAME:generate_numbers>"]
|
||||
comment = "Confirm generator executable was built"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Main executable: uses generated sources
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.main]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
include-directories = ["${CMAKE_CURRENT_BINARY_DIR}/generated"]
|
||||
|
||||
# Output form custom command: runs the generate_numbers executable to generate sources.
|
||||
# The outputs are automatically added as sources to this target.
|
||||
# The DEPENDS on "generate_numbers" ensures the generator is built before this runs.
|
||||
[[target.main.custom-command]]
|
||||
outputs = ["${CMAKE_CURRENT_BINARY_DIR}/generated/numbers.cpp"]
|
||||
depends = ["generate_numbers"]
|
||||
command = ["$<TARGET_FILE:generate_numbers>", "${CMAKE_CURRENT_BINARY_DIR}/generated/numbers.cpp"]
|
||||
comment = "Run generate_numbers to generate numbers.cpp"
|
||||
|
||||
# Post-build: run the executable to verify it works
|
||||
[[target.main.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["$<TARGET_FILE:main>"]
|
||||
comment = "Run the main executable to verify generated code works"
|
||||
```
|
||||
|
||||
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/generator-executable/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/generator-executable/cmake.toml).</sub></sup>
|
||||
Generated
+10
@@ -141,3 +141,13 @@ add_test(
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
generator-executable
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/generator-executable"
|
||||
COMMAND
|
||||
"$<TARGET_FILE:cmkr>"
|
||||
build
|
||||
)
|
||||
|
||||
@@ -77,3 +77,9 @@ name = "custom-command"
|
||||
working-directory = "custom-command"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "generator-executable"
|
||||
working-directory = "generator-executable"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# This test demonstrates a common pattern: using an executable to generate sources
|
||||
# for another target. The generator runs at build time and produces code that is
|
||||
# consumed by the main executable.
|
||||
#
|
||||
# CMake handles the dependency automatically: the generator executable is built
|
||||
# first, then it runs to produce the generated sources, and finally the main
|
||||
# executable is built using those sources.
|
||||
|
||||
[project]
|
||||
name = "generator-executable"
|
||||
description = "Tests using an executable to generate sources for another target"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Generator executable: produces code at build time
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.generate_numbers]
|
||||
type = "executable"
|
||||
sources = ["src/generate_numbers.cpp"]
|
||||
|
||||
# Post-build: confirm the generator was built
|
||||
[[target.generate_numbers.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["${CMAKE_COMMAND}", "-E", "echo", "Generator built successfully: $<TARGET_FILE_NAME:generate_numbers>"]
|
||||
comment = "Confirm generator executable was built"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Main executable: uses generated sources
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
[target.main]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
include-directories = ["${CMAKE_CURRENT_BINARY_DIR}/generated"]
|
||||
|
||||
# Output form custom command: runs the generate_numbers executable to generate sources.
|
||||
# The outputs are automatically added as sources to this target.
|
||||
# The DEPENDS on "generate_numbers" ensures the generator is built before this runs.
|
||||
[[target.main.custom-command]]
|
||||
outputs = ["${CMAKE_CURRENT_BINARY_DIR}/generated/numbers.cpp"]
|
||||
depends = ["generate_numbers"]
|
||||
command = ["$<TARGET_FILE:generate_numbers>", "${CMAKE_CURRENT_BINARY_DIR}/generated/numbers.cpp"]
|
||||
comment = "Run generate_numbers to generate numbers.cpp"
|
||||
|
||||
# Post-build: run the executable to verify it works
|
||||
[[target.main.custom-command]]
|
||||
build-event = "post-build"
|
||||
command = ["$<TARGET_FILE:main>"]
|
||||
comment = "Run the main executable to verify generated code works"
|
||||
@@ -0,0 +1,49 @@
|
||||
// Simple code generator that produces a C++ source file with a function
|
||||
// that returns a vector of numbers.
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
std::cerr << "Usage: " << argv[0] << " <output_file>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string output_path = argv[1];
|
||||
|
||||
// Create output directory if needed
|
||||
auto last_slash = output_path.find_last_of("/\\");
|
||||
if (last_slash != std::string::npos) {
|
||||
// Note: In a real generator, you'd create the directory
|
||||
// CMake creates it for us when using add_custom_command
|
||||
}
|
||||
|
||||
std::ofstream out(output_path);
|
||||
if (!out) {
|
||||
std::cerr << "Failed to open: " << output_path << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
out << "// Auto-generated by codegen - DO NOT EDIT\n";
|
||||
out << "#include <vector>\n";
|
||||
out << "#include <cstddef>\n";
|
||||
out << "\n";
|
||||
out << "std::vector<int> get_numbers() {\n";
|
||||
out << " return {";
|
||||
for (int i = 1; i <= 10; ++i) {
|
||||
if (i > 1)
|
||||
out << ", ";
|
||||
out << i * i; // 1, 4, 9, 16, 25, 36, 49, 64, 81, 100
|
||||
}
|
||||
out << "};\n";
|
||||
out << "}\n";
|
||||
out << "\n";
|
||||
out << "std::size_t get_numbers_count() {\n";
|
||||
out << " return 10;\n";
|
||||
out << "}\n";
|
||||
|
||||
std::cout << "Generated: " << output_path << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Main executable that uses the generated code
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
// Functions generated by codegen executable
|
||||
std::vector<int> get_numbers();
|
||||
std::size_t get_numbers_count();
|
||||
|
||||
int main() {
|
||||
std::cout << "Numbers from generated code:" << std::endl;
|
||||
|
||||
auto numbers = get_numbers();
|
||||
for (std::size_t i = 0; i < numbers.size(); ++i) {
|
||||
std::cout << " numbers[" << i << "] = " << numbers[i] << std::endl;
|
||||
}
|
||||
|
||||
// Verify the count
|
||||
if (numbers.size() == get_numbers_count()) {
|
||||
std::cout << "Success: Generated " << numbers.size() << " numbers!" << std::endl;
|
||||
return 0;
|
||||
} else {
|
||||
std::cerr << "Error: Count mismatch!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user