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
| if (const auto& arc = lib.archive_plan()) { | if (const auto& arc = lib.archive_plan()) { | ||||
| lib_reqs.linkable_path = out_root / arc->calc_archive_file_path(toolchain); | 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; | return ureqs; | ||||
| generate_compdb(plan, env); | generate_compdb(plan, env); | ||||
| } | } | ||||
| plan.render_all(env); | |||||
| dds::stopwatch sw; | dds::stopwatch sw; | ||||
| plan.compile_all(env, params.parallel_jobs); | plan.compile_all(env, params.parallel_jobs); | ||||
| spdlog::info("Compilation completed in {:n}ms", sw.elapsed_ms().count()); | spdlog::info("Compilation completed in {:n}ms", sw.elapsed_ms().count()); |
| } // namespace | } // 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 { | void build_plan::compile_all(const build_env& env, int njobs) const { | ||||
| auto okay = dds::compile_all(iter_compilations(*this), env, njobs); | auto okay = dds::compile_all(iter_compilations(*this), env, njobs); | ||||
| if (!okay) { | if (!okay) { |
| * All of the packages in this plan | * All of the packages in this plan | ||||
| */ | */ | ||||
| auto& packages() const noexcept { return _packages; } | 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. | * Compile all files in the plan. | ||||
| */ | */ |
| std::vector<source_file> app_sources; | std::vector<source_file> app_sources; | ||||
| std::vector<source_file> test_sources; | std::vector<source_file> test_sources; | ||||
| std::vector<source_file> lib_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)); | auto qual_name = std::string(qual_name_.value_or(lib.manifest().name)); | ||||
| app_sources.push_back(sfile); | app_sources.push_back(sfile); | ||||
| } else if (sfile.kind == source_kind::source) { | } else if (sfile.kind == source_kind::source) { | ||||
| lib_sources.push_back(sfile); | lib_sources.push_back(sfile); | ||||
| } else if (sfile.kind == source_kind::header_template) { | |||||
| template_sources.push_back(sfile); | |||||
| } else { | } else { | ||||
| assert(sfile.kind == source_kind::header); | assert(sfile.kind == source_kind::header); | ||||
| } | } | ||||
| compile_rules.enable_warnings() = params.enable_warnings; | compile_rules.enable_warnings() = params.enable_warnings; | ||||
| compile_rules.uses() = lib.manifest().uses; | 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. | // Convert the library sources into their respective file compilation plans. | ||||
| auto lib_compile_files = // | auto lib_compile_files = // | ||||
| lib_sources // | lib_sources // | ||||
| // If we have any compiled library files, generate a static library archive | // If we have any compiled library files, generate a static library archive | ||||
| // for this library | // for this library | ||||
| std::optional<create_archive_plan> create_archive; | |||||
| std::optional<create_archive_plan> archive_plan; | |||||
| if (!lib_compile_files.empty()) { | 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 | // Collect the paths to linker inputs that should be used when generating executables for this | ||||
| link_executables.emplace_back(std::move(exe)); | 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! | // 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)}; | |||||
| } | } |
| #include <dds/build/plan/archive.hpp> | #include <dds/build/plan/archive.hpp> | ||||
| #include <dds/build/plan/exe.hpp> | #include <dds/build/plan/exe.hpp> | ||||
| #include <dds/build/plan/template.hpp> | |||||
| #include <dds/library/root.hpp> | #include <dds/library/root.hpp> | ||||
| #include <dds/usage_reqs.hpp> | #include <dds/usage_reqs.hpp> | ||||
| #include <dds/util/fs.hpp> | #include <dds/util/fs.hpp> | ||||
| library_root _lib; | library_root _lib; | ||||
| /// The qualified name of the library | /// The qualified name of the library | ||||
| std::string _qual_name; | std::string _qual_name; | ||||
| /// The library's subdirectory within the output directory | |||||
| fs::path _subdir; | |||||
| /// The `create_archive_plan` for this library, if applicable | /// The `create_archive_plan` for this library, if applicable | ||||
| std::optional<create_archive_plan> _create_archive; | std::optional<create_archive_plan> _create_archive; | ||||
| /// The executables that should be linked as part of this library's build | /// The executables that should be linked as part of this library's build | ||||
| std::vector<link_executable_plan> _link_exes; | std::vector<link_executable_plan> _link_exes; | ||||
| /// The templates that must be rendered for this library | |||||
| std::vector<render_template_plan> _templates; | |||||
| public: | public: | ||||
| /** | /** | ||||
| */ | */ | ||||
| library_plan(library_root lib, | library_plan(library_root lib, | ||||
| std::string_view qual_name, | std::string_view qual_name, | ||||
| fs::path subdir, | |||||
| std::optional<create_archive_plan> ar, | 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)) | : _lib(std::move(lib)) | ||||
| , _qual_name(qual_name) | , _qual_name(qual_name) | ||||
| , _subdir(std::move(subdir)) | |||||
| , _create_archive(std::move(ar)) | , _create_archive(std::move(ar)) | ||||
| , _link_exes(std::move(exes)) {} | |||||
| , _link_exes(std::move(exes)) | |||||
| , _templates(std::move(tmpls)) {} | |||||
| /** | /** | ||||
| * Get the underlying library object | * Get the underlying library object | ||||
| * Get the qualified name of the library, as if for a libman usage requirement | * Get the qualified name of the library, as if for a libman usage requirement | ||||
| */ | */ | ||||
| auto& qualified_name() const noexcept { return _qual_name; } | 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. | * The directory that defines the source root of the library. | ||||
| */ | */ | ||||
| * components | * components | ||||
| */ | */ | ||||
| auto& archive_plan() const noexcept { return _create_archive; } | 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 | * The executables that should be created by this library | ||||
| */ | */ | ||||
| * The library identifiers that are linked by this library | * The library identifiers that are linked by this library | ||||
| */ | */ | ||||
| auto& links() const noexcept { return _lib.manifest().links; } | 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. | * Named constructor: Create a new `library_plan` automatically from some build-time parameters. | ||||
| std::optional<std::string_view> qual_name); | std::optional<std::string_view> qual_name); | ||||
| }; | }; | ||||
| } // namespace dds | |||||
| } // namespace dds |
| #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); | |||||
| } |
| #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 |
| #pragma once | |||||
| #include <string> | |||||
| int config_file_value = 42; |
| #include <info.hpp> | |||||
| #include <cassert> | |||||
| int main() { assert(config_file_value == 42); } |
| 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() |
| full_cmd = itertools.chain([self.dds_exe], cmd) | full_cmd = itertools.chain([self.dds_exe], cmd) | ||||
| return proc.run(full_cmd, cwd=cwd or self.source_root) | 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)) | cmdline = list(proc.flatten_cmd(cmd)) | ||||
| res = self.run_unchecked(cmd, cwd=cwd) | res = self.run_unchecked(cmd, cwd=cwd) | ||||
| if res.returncode != 0: | |||||
| if res.returncode != 0 and check: | |||||
| raise subprocess.CalledProcessError( | raise subprocess.CalledProcessError( | ||||
| res.returncode, [self.dds_exe] + cmdline, res.stdout) | res.returncode, [self.dds_exe] + cmdline, res.stdout) | ||||
| return res | return res | ||||
| toolchain: str = None, | toolchain: str = None, | ||||
| apps: bool = True, | apps: bool = True, | ||||
| warnings: 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: | def sdist_create(self) -> subprocess.CompletedProcess: | ||||
| return self.run([ | return self.run([ |