Browse Source

Add support for tweak-headers

default_compile_flags
vector-of-bool 3 years ago
parent
commit
bd7236131d
18 changed files with 155 additions and 4 deletions
  1. +35
    -1
      src/dds/build/builder.cpp
  2. +2
    -1
      src/dds/build/params.hpp
  3. +1
    -0
      src/dds/cli/cmd/build.cpp
  4. +1
    -0
      src/dds/cli/cmd/build_deps.cpp
  5. +1
    -0
      src/dds/cli/cmd/compile_file.cpp
  6. +14
    -0
      src/dds/cli/options.cpp
  7. +1
    -0
      src/dds/cli/options.hpp
  8. +14
    -0
      src/dds/toolchain/toolchain.cpp
  9. +3
    -0
      src/dds/toolchain/toolchain.hpp
  10. +21
    -0
      tests/projects/tweaks/include/tweakable.config.hpp
  11. +7
    -0
      tests/projects/tweaks/include/tweakable.hpp
  12. +3
    -0
      tests/projects/tweaks/library.jsonc
  13. +5
    -0
      tests/projects/tweaks/package.json5
  14. +6
    -0
      tests/projects/tweaks/src/tweakable.cpp
  15. +3
    -0
      tests/projects/tweaks/src/tweakable.main.cpp
  16. +29
    -0
      tests/test_tweaks.py
  17. +2
    -0
      tools/dds_ci/dds.py
  18. +7
    -2
      tools/dds_ci/testing/fixtures.py

+ 35
- 1
src/dds/build/builder.cpp View File

} }
} }


/**
* @brief Calculate a hash of the directory layout of the given directory.
*
* Because a tweaks-dir is specifically designed to have files added/removed within it, and
* its contents are inspected by `__has_include`, we need to have a way to invalidate any caches
* when the content of that directory changes. We don't care to hash the contents of the files,
* since those will already break any caches.
*/
std::string hash_tweaks_dir(const fs::path& tweaks_dir) {
if (!fs::is_directory(tweaks_dir)) {
return "0"; // No tweaks directory, no cache to bust
}
std::vector<fs::path> children{fs::recursive_directory_iterator{tweaks_dir},
fs::recursive_directory_iterator{}};
std::sort(children.begin(), children.end());
// A really simple inline djb2 hash
std::uint32_t hash = 5381;
for (auto& p : children) {
for (std::uint32_t c : fs::weakly_canonical(p).string()) {
hash = ((hash << 5) + hash) + c;
}
}
return std::to_string(hash);
}

template <typename Func> template <typename Func>
void with_build_plan(const build_params& params, void with_build_plan(const build_params& params,
const std::vector<sdist_target>& sdists, const std::vector<sdist_target>& sdists,
params.out_root, params.out_root,
db, db,
toolchain_knobs{ toolchain_knobs{
.is_tty = stdout_is_a_tty(),
.is_tty = stdout_is_a_tty(),
.tweaks_dir = params.tweaks_dir,
}, },
ureqs, ureqs,
}; };


if (env.knobs.tweaks_dir) {
env.knobs.cache_buster = hash_tweaks_dir(*env.knobs.tweaks_dir);
dds_log(trace,
"Build cache-buster value for tweaks-dir [{}] content is '{}'",
*env.knobs.tweaks_dir,
*env.knobs.cache_buster);
}

if (st.generate_catch2_main) { if (st.generate_catch2_main) {
auto catch_lib = prepare_test_driver(params, test_lib::catch_main, env); auto catch_lib = prepare_test_driver(params, test_lib::catch_main, env);
ureqs.add(".dds", "Catch-Main") = catch_lib; ureqs.add(".dds", "Catch-Main") = catch_lib;

+ 2
- 1
src/dds/build/params.hpp View File

std::optional<fs::path> existing_lm_index; std::optional<fs::path> existing_lm_index;
std::optional<fs::path> emit_lmi; std::optional<fs::path> emit_lmi;
std::optional<fs::path> emit_cmake{}; std::optional<fs::path> emit_cmake{};
std::optional<fs::path> tweaks_dir{};
dds::toolchain toolchain; dds::toolchain toolchain;
bool generate_compdb = true; bool generate_compdb = true;
int parallel_jobs = 0; int parallel_jobs = 0;
}; };


} // namespace dds
} // namespace dds

+ 1
- 0
src/dds/cli/cmd/build.cpp View File

.out_root = opts.out_path.value_or(fs::current_path() / "_build"), .out_root = opts.out_path.value_or(fs::current_path() / "_build"),
.existing_lm_index = opts.build.lm_index, .existing_lm_index = opts.build.lm_index,
.emit_lmi = {}, .emit_lmi = {},
.tweaks_dir = opts.build.tweaks_dir,
.toolchain = opts.load_toolchain(), .toolchain = opts.load_toolchain(),
.parallel_jobs = opts.jobs, .parallel_jobs = opts.jobs,
}); });

+ 1
- 0
src/dds/cli/cmd/build_deps.cpp View File

.existing_lm_index = {}, .existing_lm_index = {},
.emit_lmi = opts.build.lm_index.value_or("INDEX.lmi"), .emit_lmi = opts.build.lm_index.value_or("INDEX.lmi"),
.emit_cmake = opts.build_deps.cmake_file, .emit_cmake = opts.build_deps.cmake_file,
.tweaks_dir = opts.build.tweaks_dir,
.toolchain = opts.load_toolchain(), .toolchain = opts.load_toolchain(),
.parallel_jobs = opts.jobs, .parallel_jobs = opts.jobs,
}; };

+ 1
- 0
src/dds/cli/cmd/compile_file.cpp View File

.out_root = opts.out_path.value_or(fs::current_path() / "_build"), .out_root = opts.out_path.value_or(fs::current_path() / "_build"),
.existing_lm_index = opts.build.lm_index, .existing_lm_index = opts.build.lm_index,
.emit_lmi = {}, .emit_lmi = {},
.tweaks_dir = opts.build.tweaks_dir,
.toolchain = opts.load_toolchain(), .toolchain = opts.load_toolchain(),
.parallel_jobs = opts.jobs, .parallel_jobs = opts.jobs,
}); });

+ 14
- 0
src/dds/cli/options.cpp View File

.action = put_into(opts.repoman.repo_dir), .action = put_into(opts.repoman.repo_dir),
}; };


argument tweaks_dir_arg{
.long_spellings = {"tweaks-dir"},
.short_spellings = {"TD"},
.help
= "Base directory of "
"\x1b]8;;https://vector-of-bool.github.io/2020/10/04/lib-configuration.html\x1b\\tweak "
"headers\x1b]8;;\x1b\\ that should be available to the build.",
.valname = "<dir>",
.action = put_into(opts.build.tweaks_dir),
};

void do_setup(argument_parser& parser) noexcept { void do_setup(argument_parser& parser) noexcept {
parser.add_argument({ parser.add_argument({
.long_spellings = {"log-level"}, .long_spellings = {"log-level"},
build_cmd.add_argument(lm_index_arg.dup()).help build_cmd.add_argument(lm_index_arg.dup()).help
= "Path to a libman index file to use for loading project dependencies"; = "Path to a libman index file to use for loading project dependencies";
build_cmd.add_argument(jobs_arg.dup()); build_cmd.add_argument(jobs_arg.dup());
build_cmd.add_argument(tweaks_dir_arg.dup());
} }


void setup_compile_file_cmd(argument_parser& compile_file_cmd) noexcept { void setup_compile_file_cmd(argument_parser& compile_file_cmd) noexcept {
= "Set the maximum number of files to compile in parallel"; = "Set the maximum number of files to compile in parallel";
compile_file_cmd.add_argument(lm_index_arg.dup()); compile_file_cmd.add_argument(lm_index_arg.dup());
compile_file_cmd.add_argument(out_arg.dup()); compile_file_cmd.add_argument(out_arg.dup());
compile_file_cmd.add_argument(tweaks_dir_arg.dup());
compile_file_cmd.add_argument({ compile_file_cmd.add_argument({
.help = "One or more source files to compile", .help = "One or more source files to compile",
.valname = "<source-files>", .valname = "<source-files>",
.valname = "<file-path>", .valname = "<file-path>",
.action = debate::put_into(opts.build_deps.cmake_file), .action = debate::put_into(opts.build_deps.cmake_file),
}); });
build_deps_cmd.add_argument(tweaks_dir_arg.dup());
build_deps_cmd.add_argument({ build_deps_cmd.add_argument({
.help = "Dependency statement strings", .help = "Dependency statement strings",
.valname = "<dependency>", .valname = "<dependency>",

+ 1
- 0
src/dds/cli/options.hpp View File

opt_path lm_index; opt_path lm_index;
std::vector<string> add_repos; std::vector<string> add_repos;
bool update_repos = false; bool update_repos = false;
opt_path tweaks_dir;
} build; } build;


/** /**

+ 14
- 0
src/dds/toolchain/toolchain.cpp View File

extend(flags, _tty_flags); extend(flags, _tty_flags);
} }


if (knobs.cache_buster) {
// This is simply a CPP definition that is used to "bust" any caches that rely on inspecting
// the command-line of the compiler (including our own).
auto def = replace(_def_template, "[def]", "__dds_cachebust=" + *knobs.cache_buster);
extend(flags, def);
}

dds_log(trace, "#include-search dirs:"); dds_log(trace, "#include-search dirs:");
for (auto&& inc_dir : spec.include_dirs) { for (auto&& inc_dir : spec.include_dirs) {
dds_log(trace, " - search: {}", inc_dir.string()); dds_log(trace, " - search: {}", inc_dir.string());
extend(flags, inc_args); extend(flags, inc_args);
} }


if (knobs.tweaks_dir) {
dds_log(trace, " - search (tweaks): {}", knobs.tweaks_dir->string());
auto shortest = shortest_path_from(*knobs.tweaks_dir, cwd);
auto tweak_inc_args = include_args(shortest);
extend(flags, tweak_inc_args);
}

for (auto&& def : spec.definitions) { for (auto&& def : spec.definitions) {
auto def_args = definition_args(def); auto def_args = definition_args(def);
extend(flags, def_args); extend(flags, def_args);

+ 3
- 0
src/dds/toolchain/toolchain.hpp View File



struct toolchain_knobs { struct toolchain_knobs {
bool is_tty = false; bool is_tty = false;
// Directory storing tweaks for the compilation
std::optional<fs::path> tweaks_dir{};
std::optional<std::string> cache_buster{};
}; };


struct compile_file_spec { struct compile_file_spec {

+ 21
- 0
tests/projects/tweaks/include/tweakable.config.hpp View File

#pragma once

#if __has_include(<tweakable.tweaks.hpp>)
#include <tweakable.tweaks.hpp>
#endif

namespace tweakable {

namespace config {

namespace defaults {

const int value = 99;

} // namespace defaults

using namespace defaults;

} // namespace config

} // namespace tweakable

+ 7
- 0
tests/projects/tweaks/include/tweakable.hpp View File

#pragma once

namespace tweakable {

extern int get_value();

} // namespace tweakable

+ 3
- 0
tests/projects/tweaks/library.jsonc View File

{
"name": "foo"
}

+ 5
- 0
tests/projects/tweaks/package.json5 View File

{
name: 'tweakable',
version: '1.2.3',
"namespace": "test",
}

+ 6
- 0
tests/projects/tweaks/src/tweakable.cpp View File

#include <tweakable.config.hpp>
#include <tweakable.hpp>

#include <iostream>

int tweakable::get_value() { return tweakable::config::value; }

+ 3
- 0
tests/projects/tweaks/src/tweakable.main.cpp View File

#include <tweakable.hpp>

int main() { return tweakable::get_value(); }

+ 29
- 0
tests/test_tweaks.py View File

from dds_ci.testing.fixtures import ProjectOpener
from dds_ci import paths, proc


def test_lib_with_tweaks(project_opener: ProjectOpener) -> None:
pr = project_opener.open('projects/tweaks')
pr.build()
app = pr.build_root / ('tweakable' + paths.EXE_SUFFIX)
res = proc.run([app])
# The default value is 99:
assert res.returncode == 99
# Build again, but with an empty/non-existent tweaks directory
pr.build(tweaks_dir=pr.root / 'conf')
res = proc.run([app])
assert res.returncode == 99
# Now write a tweaks header and rebuild:
pr.write(
'conf/tweakable.tweaks.hpp', r'''
#pragma once

namespace tweakable {
namespace config {
const int value = 41;
}
}
''')
pr.build(tweaks_dir=pr.root / 'conf')
res = proc.run([app])
assert res.returncode == 41

+ 2
- 0
tools/dds_ci/dds.py View File

toolchain: Optional[Path] = None, toolchain: Optional[Path] = None,
build_root: Optional[Path] = None, build_root: Optional[Path] = None,
jobs: Optional[int] = None, jobs: Optional[int] = None,
tweaks_dir: Optional[Path] = None,
more_args: Optional[proc.CommandLine] = None, more_args: Optional[proc.CommandLine] = None,
timeout: Optional[int] = None) -> None: timeout: Optional[int] = None) -> None:
""" """
f'--jobs={jobs}', f'--jobs={jobs}',
f'{self.project_dir_flag}={root}', f'{self.project_dir_flag}={root}',
f'--out={build_root}', f'--out={build_root}',
f'--tweaks-dir={tweaks_dir}' if tweaks_dir else (),
more_args or (), more_args or (),
], ],
timeout=timeout, timeout=timeout,

+ 7
- 2
tools/dds_ci/testing/fixtures.py View File

"""Argument for --project""" """Argument for --project"""
return f'--project={self.root}' return f'--project={self.root}'


def build(self, *, toolchain: Optional[Pathish] = None, timeout: Optional[int] = None) -> None:
def build(self,
*,
toolchain: Optional[Pathish] = None,
timeout: Optional[int] = None,
tweaks_dir: Optional[Path] = None) -> None:
""" """
Execute 'dds build' on the project Execute 'dds build' on the project
""" """
build_root=self.build_root, build_root=self.build_root,
toolchain=tc, toolchain=tc,
timeout=timeout, timeout=timeout,
more_args=['-ldebug'])
tweaks_dir=tweaks_dir,
more_args=['-ltrace'])


def compile_file(self, *paths: Pathish, toolchain: Optional[Pathish] = None) -> None: def compile_file(self, *paths: Pathish, toolchain: Optional[Pathish] = None) -> None:
with tc_mod.fixup_toolchain(toolchain or tc_mod.get_default_test_toolchain()) as tc: with tc_mod.fixup_toolchain(toolchain or tc_mod.get_default_test_toolchain()) as tc:

Loading…
Cancel
Save