| 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)) |