This is a very rudimentary version. All it does is copy and rename the template header file and place it in the build directory in a special subdirectory that is added as an #include-path. There is some code duplication and cleanup necessary. There are a few "magic strings" and "magic paths" that need to be removed as well.default_compile_flags
@@ -153,6 +153,10 @@ prepare_ureqs(const build_plan& plan, const toolchain& toolchain, path_ref out_r | |||
if (const auto& arc = lib.archive_plan()) { | |||
lib_reqs.linkable_path = out_root / arc->calc_archive_file_path(toolchain); | |||
} | |||
if (lib.has_generated_headers()) { | |||
lib_reqs.include_paths.push_back(out_root / "__dds/gen" | |||
/ lib.output_subdirectory()); | |||
} | |||
} | |||
} | |||
return ureqs; | |||
@@ -225,6 +229,8 @@ void builder::build(const build_params& params) const { | |||
generate_compdb(plan, env); | |||
} | |||
plan.render_all(env); | |||
dds::stopwatch sw; | |||
plan.compile_all(env, params.parallel_jobs); | |||
spdlog::info("Compilation completed in {:n}ms", sw.elapsed_ms().count()); |
@@ -74,6 +74,17 @@ bool parallel_run(Range&& rng, int n_jobs, Fn&& fn) { | |||
} // namespace | |||
void build_plan::render_all(build_env_ref env) const { | |||
auto templates = _packages // | |||
| ranges::view::transform(&package_plan::libraries) // | |||
| ranges::view::join // | |||
| ranges::view::transform(&library_plan::templates) // | |||
| ranges::view::join; | |||
for (const render_template_plan& tmpl : templates) { | |||
tmpl.render(env); | |||
} | |||
} | |||
void build_plan::compile_all(const build_env& env, int njobs) const { | |||
auto okay = dds::compile_all(iter_compilations(*this), env, njobs); | |||
if (!okay) { |
@@ -28,6 +28,10 @@ public: | |||
* All of the packages in this plan | |||
*/ | |||
auto& packages() const noexcept { return _packages; } | |||
/** | |||
* Render all config templates in the plan. | |||
*/ | |||
void render_all(const build_env& env) const; | |||
/** | |||
* Compile all files in the plan. | |||
*/ |
@@ -17,6 +17,7 @@ library_plan library_plan::create(const library_root& lib, | |||
std::vector<source_file> app_sources; | |||
std::vector<source_file> test_sources; | |||
std::vector<source_file> lib_sources; | |||
std::vector<source_file> template_sources; | |||
auto qual_name = std::string(qual_name_.value_or(lib.manifest().name)); | |||
@@ -34,6 +35,8 @@ library_plan library_plan::create(const library_root& lib, | |||
app_sources.push_back(sfile); | |||
} else if (sfile.kind == source_kind::source) { | |||
lib_sources.push_back(sfile); | |||
} else if (sfile.kind == source_kind::header_template) { | |||
template_sources.push_back(sfile); | |||
} else { | |||
assert(sfile.kind == source_kind::header); | |||
} | |||
@@ -45,6 +48,10 @@ library_plan library_plan::create(const library_root& lib, | |||
compile_rules.enable_warnings() = params.enable_warnings; | |||
compile_rules.uses() = lib.manifest().uses; | |||
if (!template_sources.empty()) { | |||
compile_rules.include_dirs().push_back("__dds/gen" / params.out_subdir); | |||
} | |||
// Convert the library sources into their respective file compilation plans. | |||
auto lib_compile_files = // | |||
lib_sources // | |||
@@ -55,12 +62,12 @@ library_plan library_plan::create(const library_root& lib, | |||
// If we have any compiled library files, generate a static library archive | |||
// for this library | |||
std::optional<create_archive_plan> create_archive; | |||
std::optional<create_archive_plan> archive_plan; | |||
if (!lib_compile_files.empty()) { | |||
create_archive.emplace(lib.manifest().name, | |||
qual_name, | |||
params.out_subdir, | |||
std::move(lib_compile_files)); | |||
archive_plan.emplace(lib.manifest().name, | |||
qual_name, | |||
params.out_subdir, | |||
std::move(lib_compile_files)); | |||
} | |||
// Collect the paths to linker inputs that should be used when generating executables for this | |||
@@ -104,6 +111,16 @@ library_plan library_plan::create(const library_root& lib, | |||
link_executables.emplace_back(std::move(exe)); | |||
} | |||
std::vector<render_template_plan> render_templates; | |||
for (const auto& sf : template_sources) { | |||
render_templates.emplace_back(sf, "__dds/gen" / params.out_subdir); | |||
} | |||
// Done! | |||
return library_plan{lib, qual_name, std::move(create_archive), std::move(link_executables)}; | |||
return library_plan{lib, | |||
qual_name, | |||
params.out_subdir, | |||
std::move(archive_plan), | |||
std::move(link_executables), | |||
std::move(render_templates)}; | |||
} |
@@ -2,6 +2,7 @@ | |||
#include <dds/build/plan/archive.hpp> | |||
#include <dds/build/plan/exe.hpp> | |||
#include <dds/build/plan/template.hpp> | |||
#include <dds/library/root.hpp> | |||
#include <dds/usage_reqs.hpp> | |||
#include <dds/util/fs.hpp> | |||
@@ -56,10 +57,14 @@ class library_plan { | |||
library_root _lib; | |||
/// The qualified name of the library | |||
std::string _qual_name; | |||
/// The library's subdirectory within the output directory | |||
fs::path _subdir; | |||
/// The `create_archive_plan` for this library, if applicable | |||
std::optional<create_archive_plan> _create_archive; | |||
/// The executables that should be linked as part of this library's build | |||
std::vector<link_executable_plan> _link_exes; | |||
/// The templates that must be rendered for this library | |||
std::vector<render_template_plan> _templates; | |||
public: | |||
/** | |||
@@ -70,12 +75,16 @@ public: | |||
*/ | |||
library_plan(library_root lib, | |||
std::string_view qual_name, | |||
fs::path subdir, | |||
std::optional<create_archive_plan> ar, | |||
std::vector<link_executable_plan> exes) | |||
std::vector<link_executable_plan> exes, | |||
std::vector<render_template_plan> tmpls) | |||
: _lib(std::move(lib)) | |||
, _qual_name(qual_name) | |||
, _subdir(std::move(subdir)) | |||
, _create_archive(std::move(ar)) | |||
, _link_exes(std::move(exes)) {} | |||
, _link_exes(std::move(exes)) | |||
, _templates(std::move(tmpls)) {} | |||
/** | |||
* Get the underlying library object | |||
@@ -89,6 +98,10 @@ public: | |||
* Get the qualified name of the library, as if for a libman usage requirement | |||
*/ | |||
auto& qualified_name() const noexcept { return _qual_name; } | |||
/** | |||
* The output subdirectory of this library plan | |||
*/ | |||
path_ref output_subdirectory() const noexcept { return _subdir; } | |||
/** | |||
* The directory that defines the source root of the library. | |||
*/ | |||
@@ -98,6 +111,10 @@ public: | |||
* components | |||
*/ | |||
auto& archive_plan() const noexcept { return _create_archive; } | |||
/** | |||
* The template rendering plans for this library. | |||
*/ | |||
auto& templates() const noexcept { return _templates; } | |||
/** | |||
* The executables that should be created by this library | |||
*/ | |||
@@ -110,6 +127,10 @@ public: | |||
* The library identifiers that are linked by this library | |||
*/ | |||
auto& links() const noexcept { return _lib.manifest().links; } | |||
/** | |||
* Return `true` if this object has generated header files | |||
*/ | |||
bool has_generated_headers() const noexcept { return !templates().empty(); } | |||
/** | |||
* Named constructor: Create a new `library_plan` automatically from some build-time parameters. | |||
@@ -129,4 +150,4 @@ public: | |||
std::optional<std::string_view> qual_name); | |||
}; | |||
} // namespace dds | |||
} // namespace dds |
@@ -0,0 +1,16 @@ | |||
#include <dds/build/plan/template.hpp> | |||
#include <dds/util/fs.hpp> | |||
using namespace dds; | |||
void render_template_plan::render(build_env_ref env) const { | |||
auto content = slurp_file(_source.path); | |||
// Calculate the destination of the template rendering | |||
auto dest = env.output_root / _subdir / _source.relative_path(); | |||
dest.replace_filename(dest.stem().stem().filename().string() + dest.extension().string()); | |||
fs::create_directories(dest.parent_path()); | |||
fs::copy_file(_source.path, dest, fs::copy_options::overwrite_existing); | |||
} |
@@ -0,0 +1,36 @@ | |||
#pragma once | |||
#include <dds/build/plan/base.hpp> | |||
#include <dds/source/file.hpp> | |||
#include <utility> | |||
namespace dds { | |||
class render_template_plan { | |||
/** | |||
* The source file that defines the config template | |||
*/ | |||
source_file _source; | |||
/** | |||
* The subdirectory in which the template should be rendered. | |||
*/ | |||
fs::path _subdir; | |||
public: | |||
/** | |||
* Create a new instance | |||
* @param sf The source file of the template | |||
* @param subdir The subdirectort into which the template should render | |||
*/ | |||
render_template_plan(source_file sf, path_ref subdir) | |||
: _source(std::move(sf)) | |||
, _subdir(subdir) {} | |||
/** | |||
* Render the template into its output directory | |||
*/ | |||
void render(build_env_ref) const; | |||
}; | |||
} // namespace dds |
@@ -0,0 +1,5 @@ | |||
#pragma once | |||
#include <string> | |||
int config_file_value = 42; |
@@ -0,0 +1,5 @@ | |||
#include <info.hpp> | |||
#include <cassert> | |||
int main() { assert(config_file_value == 42); } |
@@ -0,0 +1,9 @@ | |||
import pytest | |||
from tests import DDS, dds_fixture_conf_1 | |||
@dds_fixture_conf_1('copy_only') | |||
def test_config_template(dds: DDS): | |||
dds.build() | |||
assert (dds.build_dir / '__dds/gen/info.hpp').is_file() |
@@ -52,11 +52,11 @@ class DDS: | |||
full_cmd = itertools.chain([self.dds_exe], cmd) | |||
return proc.run(full_cmd, cwd=cwd or self.source_root) | |||
def run(self, cmd: proc.CommandLine, *, | |||
cwd: Path = None) -> subprocess.CompletedProcess: | |||
def run(self, cmd: proc.CommandLine, *, cwd: Path = None, | |||
check=True) -> subprocess.CompletedProcess: | |||
cmdline = list(proc.flatten_cmd(cmd)) | |||
res = self.run_unchecked(cmd, cwd=cwd) | |||
if res.returncode != 0: | |||
if res.returncode != 0 and check: | |||
raise subprocess.CalledProcessError( | |||
res.returncode, [self.dds_exe] + cmdline, res.stdout) | |||
return res | |||
@@ -86,18 +86,22 @@ class DDS: | |||
toolchain: str = None, | |||
apps: bool = True, | |||
warnings: bool = True, | |||
tests: bool = True) -> subprocess.CompletedProcess: | |||
return self.run([ | |||
'build', | |||
f'--out={self.build_dir}', | |||
f'--toolchain={toolchain or self.default_builtin_toolchain}', | |||
f'--catalog={self.catalog_path}', | |||
f'--repo-dir={self.repo_dir}', | |||
['--no-tests'] if not tests else [], | |||
['--no-apps'] if not apps else [], | |||
['--no-warnings'] if not warnings else [], | |||
self.project_dir_arg, | |||
]) | |||
tests: bool = True, | |||
check: bool = True) -> subprocess.CompletedProcess: | |||
return self.run( | |||
[ | |||
'build', | |||
f'--out={self.build_dir}', | |||
f'--toolchain={toolchain or self.default_builtin_toolchain}', | |||
f'--catalog={self.catalog_path}', | |||
f'--repo-dir={self.repo_dir}', | |||
['--no-tests'] if not tests else [], | |||
['--no-apps'] if not apps else [], | |||
['--no-warnings'] if not warnings else [], | |||
self.project_dir_arg, | |||
], | |||
check=check, | |||
) | |||
def sdist_create(self) -> subprocess.CompletedProcess: | |||
return self.run([ |