void add_deps_to_build(build_plan& plan, | void add_deps_to_build(build_plan& plan, | ||||
usage_requirement_map& ureqs, | usage_requirement_map& ureqs, | ||||
const build_params& params, | const build_params& params, | ||||
const package_manifest& man, | |||||
build_env_ref env) { | build_env_ref env) { | ||||
auto sd_idx = params.dep_sdists // | auto sd_idx = params.dep_sdists // | ||||
| ranges::views::transform([](const auto& sd) { | | ranges::views::transform([](const auto& sd) { | ||||
if (params.existing_lm_index) { | if (params.existing_lm_index) { | ||||
ureqs = load_usage_requirements(params.root, params.out_root, *params.existing_lm_index); | ureqs = load_usage_requirements(params.root, params.out_root, *params.existing_lm_index); | ||||
} else { | } else { | ||||
add_deps_to_build(plan, ureqs, params, man, env); | |||||
add_deps_to_build(plan, ureqs, params, env); | |||||
} | } | ||||
// Initialize the build plan for this project. | // Initialize the build plan for this project. |
} | } | ||||
} get{*this}; | } get{*this}; | ||||
struct { | |||||
cli_deps& parent; | |||||
args::Command cmd{parent.deps_group, "build", "Build project dependencies"}; | |||||
common_flags _common{cmd}; | |||||
path_flag build_dir{cmd, | |||||
"build_dir", | |||||
"Directory where build results will be stored", | |||||
{"deps-build-dir"}, | |||||
dds::fs::current_path() / "_build/deps"}; | |||||
path_flag lmi_path{cmd, | |||||
"lmi_path", | |||||
"Destination for the INDEX.lmi file", | |||||
{"lmi-path"}, | |||||
dds::fs::current_path() / "_build/INDEX.lmi"}; | |||||
args::Flag no_lmi{cmd, | |||||
"no_lmi", | |||||
"If specified, will not generate an INDEX.lmi", | |||||
{"skip-lmi"}}; | |||||
repo_path_flag repo_where{cmd}; | |||||
toolchain_flag tc_filepath{cmd}; | |||||
int run() { | |||||
auto man = parent.load_package_manifest(); | |||||
auto deps = dds::repository::with_repository( // | |||||
repo_where.Get(), | |||||
dds::repo_flags::read, | |||||
[&](dds::repository repo) { return repo.solve(man.dependencies); }); | |||||
auto tc = tc_filepath.get_toolchain(); | |||||
auto bdir = build_dir.Get(); | |||||
dds::fs::create_directories(bdir); | |||||
auto db = dds::database::open(bdir / ".dds.db"); | |||||
dds::build_env env{std::move(tc), bdir, db}; | |||||
auto plan = dds::create_deps_build_plan(deps, env); | |||||
plan.compile_all(env, 6); | |||||
plan.archive_all(env, 6); | |||||
if (!no_lmi.Get()) { | |||||
write_libman_index(lmi_path.Get(), plan, env); | |||||
} | |||||
return 0; | |||||
} | |||||
} build{*this}; | |||||
int run() { | int run() { | ||||
if (ls.cmd) { | if (ls.cmd) { | ||||
return ls.run(); | return ls.run(); | ||||
} else if (build.cmd) { | |||||
return build.run(); | |||||
} else if (get.cmd) { | } else if (get.cmd) { | ||||
return get.run(); | return get.run(); | ||||
} | } |
} | } | ||||
} | } | ||||
using sdist_index_type = std::map<std::string, std::reference_wrapper<const sdist>>; | |||||
using sdist_names = std::set<std::string>; | |||||
namespace { | |||||
void resolve_ureqs_(shared_compile_file_rules& rules, | |||||
const package_manifest& man, | |||||
const sdist_index_type& sd_idx) { | |||||
for (const dependency& dep : man.dependencies) { | |||||
auto found = sd_idx.find(dep.name); | |||||
if (found == sd_idx.end()) { | |||||
throw std::runtime_error( | |||||
fmt::format("Unable to resolve dependency '{}' (required by '{}')", | |||||
dep.name, | |||||
man.pkg_id.to_string())); | |||||
} | |||||
resolve_ureqs_(rules, found->second.get().manifest, sd_idx); | |||||
auto lib_src = found->second.get().path / "src"; | |||||
auto lib_include = found->second.get().path / "include"; | |||||
if (fs::exists(lib_include)) { | |||||
rules.include_dirs().push_back(lib_include); | |||||
} else { | |||||
rules.include_dirs().push_back(lib_src); | |||||
} | |||||
} | |||||
} | |||||
void resolve_ureqs(shared_compile_file_rules rules, | |||||
const sdist& sd, | |||||
const library& lib, | |||||
const library_plan& lib_plan, | |||||
build_env_ref env, | |||||
usage_requirement_map& ureqs) { | |||||
// Add the transitive requirements for this library to our compile rules. | |||||
for (auto&& use : lib.manifest().uses) { | |||||
ureqs.apply(rules, use.namespace_, use.name); | |||||
} | |||||
// Create usage requirements for this libary. | |||||
lm::library& reqs = ureqs.add(sd.manifest.namespace_, lib.manifest().name); | |||||
reqs.include_paths.push_back(lib.public_include_dir()); | |||||
reqs.name = lib.manifest().name; | |||||
reqs.uses = lib.manifest().uses; | |||||
reqs.links = lib.manifest().links; | |||||
if (lib_plan.create_archive()) { | |||||
reqs.linkable_path = lib_plan.create_archive()->calc_archive_file_path(env); | |||||
} | |||||
// TODO: preprocessor definitions | |||||
} | |||||
void add_sdist_to_dep_plan(build_plan& plan, | |||||
const sdist& sd, | |||||
build_env_ref env, | |||||
const sdist_index_type& sd_idx, | |||||
usage_requirement_map& ureqs, | |||||
sdist_names& already_added) { | |||||
if (already_added.find(sd.manifest.pkg_id.name) != already_added.end()) { | |||||
// We've already loaded this package into the plan. | |||||
return; | |||||
} | |||||
spdlog::debug("Add to plan: {}", sd.manifest.pkg_id.name); | |||||
// First, load every dependency | |||||
for (const auto& dep : sd.manifest.dependencies) { | |||||
auto other = sd_idx.find(dep.name); | |||||
assert(other != sd_idx.end() | |||||
&& "Failed to load a transitive dependency shortly after initializing them. What?"); | |||||
add_sdist_to_dep_plan(plan, other->second, env, sd_idx, ureqs, already_added); | |||||
} | |||||
// Record that we have been processed: | |||||
already_added.insert(sd.manifest.pkg_id.name); | |||||
// Add the package: | |||||
auto& pkg = plan.add_package(package_plan(sd.manifest.pkg_id.name, sd.manifest.namespace_)); | |||||
auto libs = collect_libraries(sd.path); | |||||
for (const auto& lib : libs) { | |||||
shared_compile_file_rules comp_rules = lib.base_compile_rules(); | |||||
library_build_params params; | |||||
auto lib_plan = library_plan::create(lib, params, ureqs); | |||||
resolve_ureqs(comp_rules, sd, lib, lib_plan, env, ureqs); | |||||
pkg.add_library(std::move(lib_plan)); | |||||
} | |||||
} | |||||
} // namespace | |||||
build_plan dds::create_deps_build_plan(const std::vector<sdist>& deps, build_env_ref env) { | |||||
auto sd_idx = deps // | |||||
| ranges::views::transform([](const auto& sd) { | |||||
return std::pair(sd.manifest.pkg_id.name, std::cref(sd)); | |||||
}) // | |||||
| ranges::to<sdist_index_type>(); | |||||
build_plan plan; | |||||
usage_requirement_map ureqs; | |||||
sdist_names already_added; | |||||
for (const sdist& sd : deps) { | |||||
spdlog::info("Recording dependency: {}", sd.manifest.pkg_id.name); | |||||
add_sdist_to_dep_plan(plan, sd, env, sd_idx, ureqs, already_added); | |||||
} | |||||
return plan; | |||||
} | |||||
namespace { | namespace { | ||||
fs::path generate_lml(const library_plan& lib, path_ref libdir, const build_env& env) { | fs::path generate_lml(const library_plan& lib, path_ref libdir, const build_env& env) { |
static dependency parse_depends_string(std::string_view str); | static dependency parse_depends_string(std::string_view str); | ||||
}; | }; | ||||
build_plan create_deps_build_plan(const std::vector<sdist>& deps, build_env_ref env); | |||||
void write_libman_index(path_ref where, const build_plan& plan, const build_env& env); | void write_libman_index(path_ref where, const build_plan& plan, const build_env& env); | ||||
} // namespace dds | } // namespace dds |
['--no-warnings'] if not warnings else [], | ['--no-warnings'] if not warnings else [], | ||||
['--export'] if export else [], | ['--export'] if export else [], | ||||
f'--toolchain={toolchain or self.default_builtin_toolchain}', | f'--toolchain={toolchain or self.default_builtin_toolchain}', | ||||
f'--lm-index={self.lmi_path}', | |||||
f'--repo-dir={self.repo_dir}', | |||||
self.project_dir_arg, | self.project_dir_arg, | ||||
]) | ]) | ||||
dds.deps_get() | dds.deps_get() | ||||
assert dds.repo_dir.exists(), '`deps get` did not generate a repo directory' | assert dds.repo_dir.exists(), '`deps get` did not generate a repo directory' | ||||
assert not dds.lmi_path.exists() | |||||
dds.deps_build() | |||||
assert dds.lmi_path.exists(), '`deps build` did not generate the build dir' | |||||
@dds_fixture_conf_1('use-remote') | @dds_fixture_conf_1('use-remote') | ||||
def test_use_nlohmann_json_remote(dds: DDS): | def test_use_nlohmann_json_remote(dds: DDS): | ||||
dds.catalog_import(dds.source_root / 'catalog.json') | dds.catalog_import(dds.source_root / 'catalog.json') | ||||
dds.deps_get() | dds.deps_get() | ||||
dds.deps_build() | |||||
dds.build(apps=True) | dds.build(apps=True) | ||||
app_exe = dds.build_dir / f'app{dds.exe_suffix}' | app_exe = dds.build_dir / f'app{dds.exe_suffix}' |
dds.deps_get() | dds.deps_get() | ||||
tc_fname = 'gcc.tc.dds' if 'gcc' in dds.default_builtin_toolchain else 'msvc.tc.dds' | tc_fname = 'gcc.tc.dds' if 'gcc' in dds.default_builtin_toolchain else 'msvc.tc.dds' | ||||
tc = str(dds.test_dir / tc_fname) | tc = str(dds.test_dir / tc_fname) | ||||
dds.deps_build(toolchain=tc) | |||||
dds.build(toolchain=tc, apps=True) | dds.build(toolchain=tc, apps=True) | ||||
proc.check_run((dds.build_dir / 'use-spdlog').with_suffix(dds.exe_suffix)) | proc.check_run((dds.build_dir / 'use-spdlog').with_suffix(dds.exe_suffix)) |