Selaa lähdekoodia

Unification of main and dependecy builds, and resolution of usage requirements

default_compile_flags
vector-of-bool 5 vuotta sitten
vanhempi
commit
0f33b65308
26 muutettua tiedostoa jossa 781 lisäystä ja 650 poistoa
  1. +61
    -305
      src/dds/build.cpp
  2. +0
    -123
      src/dds/build/compile.cpp
  3. +32
    -105
      src/dds/build/plan.cpp
  4. +14
    -44
      src/dds/build/plan.hpp
  5. +42
    -0
      src/dds/build/plan/archive.cpp
  6. +27
    -0
      src/dds/build/plan/archive.hpp
  7. +15
    -0
      src/dds/build/plan/base.hpp
  8. +66
    -0
      src/dds/build/plan/compile_file.cpp
  9. +22
    -19
      src/dds/build/plan/compile_file.hpp
  10. +41
    -0
      src/dds/build/plan/exe.cpp
  11. +37
    -0
      src/dds/build/plan/exe.hpp
  12. +11
    -0
      src/dds/build/plan/full.hpp
  13. +85
    -0
      src/dds/build/plan/library.cpp
  14. +41
    -0
      src/dds/build/plan/library.hpp
  15. +27
    -0
      src/dds/build/plan/package.hpp
  16. +2
    -1
      src/dds/ddslim.main.cpp
  17. +78
    -32
      src/dds/deps.cpp
  18. +1
    -1
      src/dds/deps.hpp
  19. +1
    -1
      src/dds/library.cpp
  20. +4
    -6
      src/dds/library.hpp
  21. +11
    -5
      src/dds/library_manifest.cpp
  22. +5
    -5
      src/dds/library_manifest.hpp
  23. +74
    -0
      src/dds/usage_reqs.cpp
  24. +49
    -0
      src/dds/usage_reqs.hpp
  25. +25
    -1
      src/libman/library.cpp
  26. +10
    -2
      src/libman/library.hpp

+ 61
- 305
src/dds/build.cpp Näytä tiedosto

@@ -1,21 +1,20 @@
#include "./build.hpp"

#include <dds/build/compile.hpp>
#include <dds/build/plan/compile_file.hpp>
#include <dds/logging.hpp>
#include <dds/proc.hpp>
#include <dds/project.hpp>
#include <dds/source.hpp>
#include <dds/toolchain.hpp>
#include <dds/usage_reqs.hpp>
#include <dds/util/algo.hpp>
#include <dds/util/string.hpp>
#include <libman/index.hpp>
#include <libman/parse.hpp>

#include <range/v3/action/join.hpp>
#include <range/v3/algorithm/for_each.hpp>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/drop.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/transform.hpp>

#include <algorithm>
@@ -103,10 +102,10 @@ fs::path export_project_library(const build_params& params,
}

for (const auto& use : lib.manifest().uses) {
pairs.emplace_back("Uses", use);
pairs.emplace_back("Uses", fmt::format("{}/{}", use.namespace_, use.name));
}
for (const auto& links : lib.manifest().links) {
pairs.emplace_back("Links", links);
pairs.emplace_back("Links", fmt::format("{}/{}", links.namespace_, links.name));
}

lm::write_pairs(lml_path, pairs);
@@ -141,322 +140,79 @@ void export_project(const build_params& params, const project& project) {
lm::write_pairs(export_root / "package.lmp", pairs);
}

void include_deps(const lm::index::library_index& lib_index,
std::vector<fs::path>& includes,
std::vector<std::string>& defines,
std::string_view usage_key,
bool is_public_usage) {
auto pair = split(usage_key, "/");
if (pair.size() != 2) {
throw compile_failure(fmt::format("Invalid `Uses`: {}", usage_key));
}

auto& pkg_ns = pair[0];
auto& lib = pair[1];

auto found = lib_index.find(std::pair(pkg_ns, lib));
if (found == lib_index.end()) {
throw compile_failure(
fmt::format("No library '{}/{}': Check that it is installed and available",
pkg_ns,
lib));
}

const lm::library& lm_lib = found->second;
extend(includes, lm_lib.include_paths);
extend(defines, lm_lib.preproc_defs);

for (const auto& uses : lm_lib.uses) {
include_deps(lib_index, includes, defines, uses, is_public_usage && true);
}
for (const auto& links : lm_lib.links) {
include_deps(lib_index, includes, defines, links, false);
}
}

std::vector<compile_file_plan> file_compilations_of_lib(const build_params& params,
const library& lib) {
const auto& sources = lib.all_sources();

std::vector<fs::path> dep_includes;
std::vector<std::string> dep_defines;

if (!lib.manifest().uses.empty() || !lib.manifest().links.empty()) {
fs::path lm_index_path = params.lm_index;
for (auto cand : {fs::path("INDEX.lmi"), params.out_root / "INDEX.lmi"}) {
if (fs::exists(lm_index_path)) {
break;
}
lm_index_path = params.root / cand;
usage_requirement_map
load_usage_requirements(path_ref project_root, path_ref build_root, path_ref user_lm_index) {
fs::path lm_index_path = user_lm_index;
for (auto cand : {project_root / "INDEX.lmi", build_root / "INDEX.lmi"}) {
if (fs::exists(lm_index_path) || !user_lm_index.empty()) {
break;
}
if (!fs::exists(lm_index_path)) {
throw compile_failure(
"No `INDEX.lmi` found, but we need to pull in dependencies."
"Use a package manager to generate an INDEX.lmi");
}
auto lm_index = lm::index::from_file(lm_index_path);
auto lib_index = lm_index.build_library_index();

for (const auto& uses : lib.manifest().uses) {
include_deps(lib_index, dep_includes, dep_defines, uses, true);
}
for (const auto& links : lib.manifest().links) {
include_deps(lib_index, dep_includes, dep_defines, links, false);
}
spdlog::critical("Dependency resolution isn't fully implemented yet!!");
lm_index_path = cand;
}
if (sources.empty()) {
spdlog::info("No source files found to compile");
if (!fs::exists(lm_index_path)) {
spdlog::warn("No INDEX.lmi found, so we won't be able to load/use any dependencies");
return {};
}

auto should_compile_source = [&](const source_file& sf) {
return (sf.kind == source_kind::source || (sf.kind == source_kind::app && params.build_apps)
|| (sf.kind == source_kind::test && params.build_tests));
};

shared_compile_file_rules rules;
extend(rules.defs(), lib.manifest().private_defines);
extend(rules.defs(), dep_defines);
extend(rules.include_dirs(), lib.manifest().private_includes);
extend(rules.include_dirs(), dep_includes);
rules.include_dirs().push_back(fs::absolute(lib.path() / "src"));
rules.include_dirs().push_back(fs::absolute(lib.path() / "include"));
rules.enable_warnings() = params.enable_warnings;

return //
sources //
| filter(should_compile_source) //
| transform([&](auto&& src) {
return compile_file_plan{rules,
"obj/" + lib.manifest().name,
src,
lib.manifest().name};
}) //
| to_vector;
lm::index idx = lm::index::from_file(lm_index_path);
return usage_requirement_map::from_lm_index(idx);
}

std::vector<dds::compile_file_plan> collect_compiles(const build_params& params,
const project& project) {
auto libs = iter_libraries(project);
return //
libs //
| transform([&](auto&& lib) { return file_compilations_of_lib(params, lib); }) //
| ranges::actions::join //
| to_vector //
;
}

using object_file_index = std::map<fs::path, fs::path>;

/**
* Obtain the path to the object file that corresponds to the named source file
*/
fs::path obj_for_source(const object_file_index& idx, path_ref source_path) {
auto iter = idx.find(source_path);
if (iter == idx.end()) {
assert(false && "Lookup on invalid source file");
std::terminate();
}
return iter->second;
}

/**
* Create the static library archive for the given library object.
*/
std::optional<fs::path> create_lib_archive(const build_params& params,
const library& lib,
const object_file_index& obj_idx) {
archive_spec arc;
arc.out_path = lib_archive_path(params, lib);

// Collect object files that make up that library
arc.input_files = //
lib.all_sources() //
| filter([](auto&& s) { return s.kind == source_kind::source; }) //
| transform([&](auto&& s) { return obj_for_source(obj_idx, s.path); }) //
| to_vector //
;

if (arc.input_files.empty()) {
return std::nullopt;
}

auto ar_cmd = params.toolchain.create_archive_command(arc);
if (fs::exists(arc.out_path)) {
fs::remove(arc.out_path);
}

spdlog::info("Create archive for {}: {}", lib.manifest().name, arc.out_path.string());
fs::create_directories(arc.out_path.parent_path());
auto ar_res = run_proc(ar_cmd);
if (!ar_res.okay()) {
spdlog::error("Failure creating archive library {}", arc.out_path);
spdlog::error("Subcommand failed: {}", quote_command(ar_cmd));
spdlog::error("Subcommand produced output:\n{}", ar_res.output);
throw archive_failure("Failed to create the library archive");
}

return arc.out_path;
}

/**
* Link a single test executable identified by a single source file
*/
fs::path link_one_exe(path_ref dest,
path_ref source_file,
const build_params& params,
const library& lib,
const object_file_index& obj_idx) {
auto main_obj = obj_for_source(obj_idx, source_file);
assert(fs::exists(main_obj));

link_exe_spec spec;
spec.inputs.push_back(main_obj);
auto lib_arc_path = lib_archive_path(params, lib);
if (fs::is_regular_file(lib_arc_path)) {
spec.inputs.push_back(lib_arc_path);
}
spec.output = dest;
const auto link_command = params.toolchain.create_link_executable_command(spec);
} // namespace

spdlog::info("Create executable: {}", (fs::relative(spec.output, params.out_root)).string());
fs::create_directories(spec.output.parent_path());
auto proc_res = run_proc(link_command);
if (!proc_res.okay()) {
throw compile_failure(
fmt::format("Failed to link test executable '{}'. Link command [{}] returned {}:\n{}",
spec.output.string(),
quote_command(link_command),
proc_res.retc,
proc_res.output));
void dds::build(const build_params& params, const package_manifest& man) {
auto libs = collect_libraries(params.root);
if (!libs.size()) {
spdlog::warn("Nothing found to build!");
return;
}

return spec.output;
}
build_plan plan;
auto& pkg = plan.add_package(package_plan(man.name, man.namespace_));

template <typename GetExeNameFn>
std::vector<fs::path> link_executables(source_kind sk,
GetExeNameFn&& get_exe_path,
const build_params& params,
const library& lib,
const object_file_index& obj_idx) {
return //
lib.all_sources() //
| filter([&](auto&& s) { return s.kind == sk; }) //
| transform([&](auto&& s) {
return link_one_exe(get_exe_path(s), s.path, params, lib, obj_idx);
}) //
| to_vector //
;
}
usage_requirement_map ureqs
= load_usage_requirements(params.root, params.out_root, params.lm_index);

struct link_results {
std::optional<fs::path> archive_path;
std::vector<fs::path> test_exes;
std::vector<fs::path> app_exes;
};

link_results
link_project_lib(const build_params& params, const library& lib, const object_file_index& obj_idx) {
link_results res;
auto op_arc_path = create_lib_archive(params, lib, obj_idx);
if (op_arc_path) {
res.archive_path = *op_arc_path;
}

auto get_test_exe_path = [&](const source_file sf) {
return params.out_root
/ fs::relative(sf.path, params.root)
.replace_filename(sf.path.stem().stem().string()
+ params.toolchain.executable_suffix());
};

auto get_app_exe_path = [&](const source_file& sf) {
return params.out_root
/ (sf.path.stem().stem().string() + params.toolchain.executable_suffix());
};

// Link test executables
if (params.build_tests) {
extend(res.test_exes,
link_executables(source_kind::test, get_test_exe_path, params, lib, obj_idx));
library_build_params lib_params;
lib_params.build_tests = params.build_tests;
lib_params.build_apps = params.build_apps;
for (const library& lib : libs) {
lib_params.out_subdir = fs::relative(lib.path(), params.root);
pkg.add_library(library_plan::create(lib, lib_params, ureqs));
}

if (params.build_apps) {
extend(res.app_exes,
link_executables(source_kind::app, get_app_exe_path, params, lib, obj_idx));
}

return res;
}

std::vector<link_results> link_project(const build_params& params,
const project& pr,
const std::vector<compile_file_plan>& compilations) {
auto obj_index = //
ranges::views::all(compilations) //
| transform([&](const compile_file_plan& comp) -> std::pair<fs::path, fs::path> {
return std::pair(comp.source.path,
comp.get_object_file_path(
build_env{params.toolchain, params.out_root}));
}) //
| ranges::to<object_file_index>() //
;

auto libs = iter_libraries(pr);
return libs //
| transform([&](auto&& lib) { return link_project_lib(params, lib, obj_index); }) //
| to_vector;
}

} // namespace

void dds::build(const build_params& params, const package_manifest&) {
auto libs = collect_libraries(params.root);
// auto sroot = dds::sroot{params.root};
// auto comp_rules = sroot.base_compile_rules();

// sroot_build_params sr_params;
// sr_params.main_name = man.name;
// sr_params.build_tests = params.build_tests;
// sr_params.build_apps = params.build_apps;
// sr_params.compile_rules = comp_rules;
// build_plan plan;
// plan.add_sroot(sroot, sr_params);
// plan.compile_all(params.toolchain, params.parallel_jobs, params.out_root);
dds::build_env env{params.toolchain, params.out_root};
plan.compile_all(env, params.parallel_jobs);
plan.archive_all(env, params.parallel_jobs);
plan.link_all(env, params.parallel_jobs);

auto project = project::from_directory(params.root);

auto compiles = collect_compiles(params, project);

dds::build_env env{params.toolchain, params.out_root};

dds::execute_all(compiles, params.parallel_jobs, env);
// dds::execute_all(compiles, params.parallel_jobs, env);

using namespace ranges::views;

auto link_res = link_project(params, project, compiles);
auto all_tests = link_res //
| transform([](auto&& link) { return link.test_exes; }) //
| ranges::actions::join;
int n_test_fails = 0;
for (path_ref test_exe : all_tests) {
spdlog::info("Running test: {}", fs::relative(test_exe, params.out_root).string());
const auto test_res = run_proc({test_exe.string()});
if (!test_res.okay()) {
spdlog::error("TEST FAILED\n{}", test_res.output);
n_test_fails++;
}
}
if (n_test_fails) {
throw compile_failure("Test failures during build");
}
if (params.do_export) {
export_project(params, project);
}
// auto link_res = link_project(params, project, compiles);

// auto all_tests = link_res //
// | transform([](auto&& link) { return link.test_exes; }) //
// | ranges::actions::join;

// int n_test_fails = 0;
// for (path_ref test_exe : all_tests) {
// spdlog::info("Running test: {}", fs::relative(test_exe, params.out_root).string());
// const auto test_res = run_proc({test_exe.string()});
// if (!test_res.okay()) {
// spdlog::error("TEST FAILED\n{}", test_res.output);
// n_test_fails++;
// }
// }

// if (n_test_fails) {
// throw compile_failure("Test failures during build");
// }

// if (params.do_export) {
// export_project(params, project);
// }
}

+ 0
- 123
src/dds/build/compile.cpp Näytä tiedosto

@@ -1,123 +0,0 @@
#include "./compile.hpp"

#include <dds/proc.hpp>
#include <dds/util/algo.hpp>
#include <dds/util/signal.hpp>

#include <spdlog/spdlog.h>

#include <atomic>
#include <mutex>
#include <thread>
#include <vector>

using namespace dds;

void compile_file_plan::compile(const build_env& env) const {
const auto obj_path = get_object_file_path(env);
fs::create_directories(obj_path.parent_path());

spdlog::info("[{}] Compile: {}",
qualifier,
fs::relative(source.path, source.basis_path).string());
auto start_time = std::chrono::steady_clock::now();

compile_file_spec spec{source.path, obj_path};
spec.enable_warnings = rules.enable_warnings();

extend(spec.include_dirs, rules.include_dirs());
extend(spec.definitions, rules.defs());

auto cmd = env.toolchain.create_compile_command(spec);
auto compile_res = run_proc(cmd);

auto end_time = std::chrono::steady_clock::now();
auto dur_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);

spdlog::info("[{}] Compile: {} - {:n}ms",
qualifier,
fs::relative(source.path, source.basis_path).string(),
dur_ms.count());

if (!compile_res.okay()) {
spdlog::error("Compilation failed: {}", source.path.string());
spdlog::error("Subcommand FAILED: {}\n{}", quote_command(cmd), compile_res.output);
throw compile_failure(fmt::format("Compilation failed for {}", source.path.string()));
}

// MSVC prints the filename of the source file. Dunno why, but they do.
if (compile_res.output.find(spec.source_path.filename().string() + "\r\n") == 0) {
compile_res.output.erase(0, spec.source_path.filename().string().length() + 2);
}

if (!compile_res.output.empty()) {
spdlog::warn("While compiling file {} [{}]:\n{}",
spec.source_path.string(),
quote_command(cmd),
compile_res.output);
}
}

fs::path compile_file_plan::get_object_file_path(const build_env& env) const noexcept {
auto relpath = fs::relative(source.path, source.basis_path);
auto ret = env.output_root / subdir / relpath;
ret.replace_filename(relpath.filename().string() + env.toolchain.object_suffix());
return ret;
}

void dds::execute_all(const std::vector<compile_file_plan>& compilations,
int n_jobs,
const build_env& env) {
// We don't bother with a nice thread pool, as the overhead of compiling
// source files dwarfs the cost of interlocking.
std::mutex mut;

auto comp_iter = compilations.begin();
const auto end_iter = compilations.end();

std::vector<std::exception_ptr> exceptions;

auto compile_one = [&]() mutable {
while (true) {
std::unique_lock lk{mut};
if (!exceptions.empty()) {
break;
}
if (comp_iter == end_iter) {
break;
}
auto& compilation = *comp_iter++;
lk.unlock();
try {
compilation.compile(env);
cancellation_point();
} catch (...) {
lk.lock();
exceptions.push_back(std::current_exception());
break;
}
}
};

std::unique_lock lk{mut};
std::vector<std::thread> threads;
if (n_jobs < 1) {
n_jobs = std::thread::hardware_concurrency() + 2;
}
std::generate_n(std::back_inserter(threads), n_jobs, [&] { return std::thread(compile_one); });
spdlog::info("Parallel compile with {} threads", threads.size());
lk.unlock();
for (auto& t : threads) {
t.join();
}
for (auto eptr : exceptions) {
try {
std::rethrow_exception(eptr);
} catch (const std::exception& e) {
spdlog::error(e.what());
}
}
if (!exceptions.empty()) {
throw compile_failure("Failed to compile library sources");
}
}

+ 32
- 105
src/dds/build/plan.cpp Näytä tiedosto

@@ -3,11 +3,10 @@
#include <dds/proc.hpp>

#include <range/v3/action/join.hpp>
#include <range/v3/view/concat.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/repeat_n.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/zip.hpp>

#include <spdlog/spdlog.h>

@@ -17,62 +16,6 @@

using namespace dds;

library_plan library_plan::create(const library& lib, const library_build_params& params) {
std::vector<compile_file_plan> compile_files;
std::vector<create_exe_plan> link_executables;
std::optional<create_archive_plan> create_archive;
bool should_create_archive = false;

std::vector<source_file> app_sources;
std::vector<source_file> test_sources;
std::vector<source_file> lib_sources;

auto src_dir = lib.src_dir();
if (src_dir.exists()) {
auto all_sources = src_dir.sources();
auto to_compile = all_sources | ranges::views::filter([&](const source_file& sf) {
return (sf.kind == source_kind::source
|| (sf.kind == source_kind::app && params.build_apps)
|| (sf.kind == source_kind::test && params.build_tests));
});

for (const auto& sfile : to_compile) {
compile_file_plan cf_plan;
cf_plan.source = sfile;
cf_plan.qualifier = lib.manifest().name;
cf_plan.rules = params.compile_rules;
cf_plan.subdir = fs::path("obj") / lib.manifest().name;
compile_files.push_back(std::move(cf_plan));
if (sfile.kind == source_kind::test) {
test_sources.push_back(sfile);
} else if (sfile.kind == source_kind::app) {
app_sources.push_back(sfile);
} else {
should_create_archive = true;
lib_sources.push_back(sfile);
}
}
}

if (!app_sources.empty() || !test_sources.empty()) {
spdlog::critical("Apps/tests not implemented on this code path");
}

if (should_create_archive) {
create_archive_plan ar_plan;
ar_plan.name = lib.manifest().name;
ar_plan.out_dir = params.out_subdir;
create_archive.emplace(std::move(ar_plan));
}

return library_plan{lib.manifest().name,
lib.path(),
params.out_subdir,
compile_files,
create_archive,
link_executables};
}

namespace {

template <typename Range, typename Fn>
@@ -129,53 +72,38 @@ bool parallel_run(Range&& rng, int n_jobs, Fn&& fn) {

} // namespace

fs::path create_archive_plan::archive_file_path(const build_env& env) const noexcept {
return env.output_root / fmt::format("{}{}{}", "lib", name, env.toolchain.archive_suffix());
}

void create_archive_plan::archive(const build_env& env,
const std::vector<fs::path>& objects) const {
archive_spec ar;
ar.input_files = objects;
ar.out_path = archive_file_path(env);
auto ar_cmd = env.toolchain.create_archive_command(ar);
auto out_relpath = fs::relative(ar.out_path, env.output_root).string();

spdlog::info("[{}] Archive: {}", name, out_relpath);
auto start_time = std::chrono::steady_clock::now();
auto ar_res = run_proc(ar_cmd);
auto end_time = std::chrono::steady_clock::now();
auto dur_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
spdlog::info("[{}] Archive: {} - {:n}ms", name, out_relpath, dur_ms.count());

if (!ar_res.okay()) {
spdlog::error("Creating static library archive failed: {}", out_relpath);
spdlog::error("Subcommand FAILED: {}\n{}", quote_command(ar_cmd), ar_res.output);
throw std::runtime_error(
fmt::format("Creating archive [{}] failed for '{}'", out_relpath, name));
}
}

namespace {

auto all_libraries(const build_plan& plan) {
return //
plan.build_packages //
| ranges::views::transform(&package_plan::create_libraries) //
| ranges::views::join //
return //
plan.packages() //
| ranges::views::transform(&package_plan::libraries) //
| ranges::views::join //
;
}

} // namespace

void build_plan::compile_all(const build_env& env, int njobs) const {
auto all_compiles = //
all_libraries(*this) //
| ranges::views::transform(&library_plan::compile_files) //
| ranges::views::join //
auto lib_compiles = //
all_libraries(*this) //
| ranges::views::transform(&library_plan::create_archive) //
| ranges::views::filter([&](auto&& opt) { return bool(opt); }) //
| ranges::views::transform([&](auto&& opt) -> auto& { return opt->compile_files(); }) //
| ranges::views::join //
;

auto exe_compiles = //
all_libraries(*this) //
| ranges::views::transform(&library_plan::executables) //
| ranges::views::join //
| ranges::views::transform(&link_executable_plan::main_compile_file) //
;

auto okay = parallel_run(all_compiles, njobs, [&](const auto& cf) { cf.compile(env); });
auto all_compiles = ranges::views::concat(lib_compiles, exe_compiles);

auto okay
= parallel_run(all_compiles, njobs, [&](const compile_file_plan& cf) { cf.compile(env); });
if (!okay) {
throw std::runtime_error("Compilation failed.");
}
@@ -183,17 +111,16 @@ void build_plan::compile_all(const build_env& env, int njobs) const {

void build_plan::archive_all(const build_env& env, int njobs) const {
parallel_run(all_libraries(*this), njobs, [&](const library_plan& lib) {
if (!lib.create_archive) {
return;
if (lib.create_archive()) {
lib.create_archive()->archive(env);
}
const auto& objects = ranges::views::all(lib.compile_files) //
| ranges::views::filter([](auto&& comp) {
return comp.source.kind == source_kind::source;
}) //
| ranges::views::transform(
[&](auto&& comp) { return comp.get_object_file_path(env); }) //
| ranges::to_vector //
;
lib.create_archive->archive(env, objects);
});
}

void build_plan::link_all(const build_env& env, int) const {
for (auto&& lib : all_libraries(*this)) {
for (auto&& exe : lib.executables()) {
exe.link(env, lib);
}
}
}

+ 14
- 44
src/dds/build/plan.hpp Näytä tiedosto

@@ -1,56 +1,26 @@
#pragma once

#include <dds/build/compile.hpp>
#include <dds/build/params.hpp>
#include <dds/build/plan/archive.hpp>
#include <dds/build/plan/compile_file.hpp>
#include <dds/build/plan/exe.hpp>
#include <dds/build/plan/library.hpp>
#include <dds/build/plan/package.hpp>
#include <dds/library.hpp>

#include <dds/toolchain.hpp>
#include <dds/util/fs.hpp>

namespace dds {

struct create_archive_plan {
std::string name;
fs::path out_dir;

fs::path archive_file_path(const build_env& env) const noexcept;

void archive(const build_env& env, const std::vector<fs::path>& objects) const;
};

struct create_exe_plan {
std::vector<fs::path> in_sources;
std::string name;
fs::path out_dir;
};

struct library_plan {
std::string name;
fs::path source_root;
fs::path out_subdir;
std::vector<compile_file_plan> compile_files;
std::optional<create_archive_plan> create_archive;
std::vector<create_exe_plan> link_executables;
class build_plan {
std::vector<package_plan> _packages;

static library_plan create(const library& lib, const library_build_params& params);
};

struct package_plan {
std::string name;
std::string namespace_;
std::vector<std::string> pkg_requires;
std::vector<library_plan> create_libraries;

void add_library(const library& lib, const library_build_params& params) {
create_libraries.push_back(library_plan::create(lib, params));
public:
package_plan& add_package(package_plan p) noexcept {
return _packages.emplace_back(std::move(p));
}
};

struct build_plan {
std::vector<package_plan> build_packages;

void compile_all(const build_env& env, int njobs) const;
void archive_all(const build_env& env, int njobs) const;
auto& packages() const noexcept { return _packages; }
void compile_all(const build_env& env, int njobs) const;
void archive_all(const build_env& env, int njobs) const;
void link_all(const build_env& env, int njobs) const;
};

} // namespace dds

+ 42
- 0
src/dds/build/plan/archive.cpp Näytä tiedosto

@@ -0,0 +1,42 @@
#include "./archive.hpp"

#include <dds/proc.hpp>

#include <range/v3/view/transform.hpp>
#include <spdlog/spdlog.h>

using namespace dds;

fs::path create_archive_plan::calc_archive_file_path(const build_env& env) const noexcept {
return env.output_root / fmt::format("{}{}{}", "lib", _name, env.toolchain.archive_suffix());
}

void create_archive_plan::archive(const build_env& env) const {
const auto objects = //
_compile_files //
| ranges::views::transform([&](auto&& cf) { return cf.calc_object_file_path(env); })
| ranges::to_vector //
;
archive_spec ar;
ar.input_files = std::move(objects);
ar.out_path = calc_archive_file_path(env);
auto ar_cmd = env.toolchain.create_archive_command(ar);
auto out_relpath = fs::relative(ar.out_path, env.output_root).string();
if (fs::exists(ar.out_path)) {
fs::remove(ar.out_path);
}

spdlog::info("[{}] Archive: {}", _name, out_relpath);
auto start_time = std::chrono::steady_clock::now();
auto ar_res = run_proc(ar_cmd);
auto end_time = std::chrono::steady_clock::now();
auto dur_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
spdlog::info("[{}] Archive: {} - {:n}ms", _name, out_relpath, dur_ms.count());

if (!ar_res.okay()) {
spdlog::error("Creating static library archive failed: {}", out_relpath);
spdlog::error("Subcommand FAILED: {}\n{}", quote_command(ar_cmd), ar_res.output);
throw std::runtime_error(
fmt::format("Creating archive [{}] failed for '{}'", out_relpath, _name));
}
}

+ 27
- 0
src/dds/build/plan/archive.hpp Näytä tiedosto

@@ -0,0 +1,27 @@
#pragma once

#include <dds/build/plan/compile_file.hpp>
#include <dds/util/fs.hpp>

#include <string>

namespace dds {

class create_archive_plan {
std::string _name;
fs::path _subdir;
std::vector<compile_file_plan> _compile_files;

public:
create_archive_plan(std::string_view name, path_ref subdir, std::vector<compile_file_plan> cfs)
: _name(name)
, _subdir(subdir)
, _compile_files(std::move(cfs)) {}

fs::path calc_archive_file_path(build_env_ref env) const noexcept;
auto& compile_files() const noexcept { return _compile_files; }

void archive(build_env_ref env) const;
};

} // namespace dds

+ 15
- 0
src/dds/build/plan/base.hpp Näytä tiedosto

@@ -0,0 +1,15 @@
#pragma once

#include <dds/toolchain.hpp>
#include <dds/util/fs.hpp>

namespace dds {

struct build_env {
dds::toolchain toolchain;
fs::path output_root;
};

using build_env_ref = const build_env&;

} // namespace dds

+ 66
- 0
src/dds/build/plan/compile_file.cpp Näytä tiedosto

@@ -0,0 +1,66 @@
#include "./compile_file.hpp"

#include <dds/proc.hpp>
#include <dds/util/algo.hpp>
#include <dds/util/signal.hpp>

#include <spdlog/spdlog.h>

#include <atomic>
#include <mutex>
#include <thread>
#include <vector>

using namespace dds;

void compile_file_plan::compile(const build_env& env) const {
const auto obj_path = calc_object_file_path(env);
fs::create_directories(obj_path.parent_path());

spdlog::info("[{}] Compile: {}",
_qualifier,
fs::relative(_source.path, _source.basis_path).string());
auto start_time = std::chrono::steady_clock::now();

compile_file_spec spec{_source.path, obj_path};
spec.enable_warnings = _rules.enable_warnings();

extend(spec.include_dirs, _rules.include_dirs());
extend(spec.definitions, _rules.defs());

auto cmd = env.toolchain.create_compile_command(spec);
auto compile_res = run_proc(cmd);

auto end_time = std::chrono::steady_clock::now();
auto dur_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);

spdlog::info("[{}] Compile: {} - {:n}ms",
_qualifier,
fs::relative(_source.path, _source.basis_path).string(),
dur_ms.count());

if (!compile_res.okay()) {
spdlog::error("Compilation failed: {}", _source.path.string());
spdlog::error("Subcommand FAILED: {}\n{}", quote_command(cmd), compile_res.output);
throw compile_failure(fmt::format("Compilation failed for {}", _source.path.string()));
}

// MSVC prints the filename of the source file. Dunno why, but they do.
if (compile_res.output.find(spec.source_path.filename().string() + "\r\n") == 0) {
compile_res.output.erase(0, spec.source_path.filename().string().length() + 2);
}

if (!compile_res.output.empty()) {
spdlog::warn("While compiling file {} [{}]:\n{}",
spec.source_path.string(),
quote_command(cmd),
compile_res.output);
}
}

fs::path compile_file_plan::calc_object_file_path(const build_env& env) const noexcept {
auto relpath = fs::relative(_source.path, _source.basis_path);
auto ret = env.output_root / _subdir / relpath;
ret.replace_filename(relpath.filename().string() + env.toolchain.object_suffix());
return ret;
}

src/dds/build/compile.hpp → src/dds/build/plan/compile_file.hpp Näytä tiedosto

@@ -1,20 +1,12 @@
#pragma once

#include <dds/build/plan/base.hpp>
#include <dds/source.hpp>
#include <dds/toolchain.hpp>
#include <dds/util/fs.hpp>

#include <memory>
#include <optional>
#include <stdexcept>

namespace dds {

struct build_env {
dds::toolchain toolchain;
fs::path output_root;
};

struct compile_failure : std::runtime_error {
using runtime_error::runtime_error;
};
@@ -41,16 +33,27 @@ public:
auto& enable_warnings() const noexcept { return _impl->enable_warnings; }
};

struct compile_file_plan {
shared_compile_file_rules rules;
fs::path subdir;
dds::source_file source;
std::string qualifier;
class compile_file_plan {
shared_compile_file_rules _rules;
source_file _source;
std::string _qualifier;
fs::path _subdir;

fs::path get_object_file_path(const build_env& env) const noexcept;
void compile(const build_env&) const;
public:
compile_file_plan(shared_compile_file_rules rules,
source_file sf,
std::string_view qual,
path_ref subdir)
: _rules(rules)
, _source(std::move(sf))
, _qualifier(qual)
, _subdir(subdir) {}

const source_file& source() const noexcept { return _source; }
path_ref source_path() const noexcept { return _source.path; }

fs::path calc_object_file_path(build_env_ref env) const noexcept;
void compile(build_env_ref) const;
};

void execute_all(const std::vector<compile_file_plan>&, int n_jobs, const build_env& env);

} // namespace dds
} // namespace dds

+ 41
- 0
src/dds/build/plan/exe.cpp Näytä tiedosto

@@ -0,0 +1,41 @@
#include "./exe.hpp"

#include <dds/build/plan/library.hpp>
#include <dds/proc.hpp>

#include <range/v3/algorithm/find_if.hpp>
#include <spdlog/spdlog.h>

#include <cassert>

using namespace dds;

fs::path link_executable_plan::calc_executable_path(build_env_ref env) const noexcept {
return env.output_root / _out_subdir / (_name + env.toolchain.executable_suffix());
}

void link_executable_plan::link(build_env_ref env, const library_plan& lib) const {
const auto out_path = calc_executable_path(env);

link_exe_spec spec;
spec.output = out_path;
spec.inputs = _input_libs;
if (lib.create_archive()) {
spec.inputs.push_back(lib.create_archive()->calc_archive_file_path(env));
auto main_obj = _main_compile.calc_object_file_path(env);
spec.inputs.push_back(std::move(main_obj));
}

const auto link_command = env.toolchain.create_link_executable_command(spec);
spdlog::info("Linking executable: {}", fs::relative(spec.output, env.output_root).string());
fs::create_directories(out_path.parent_path());
auto proc_res = run_proc(link_command);
if (!proc_res.okay()) {
throw compile_failure(
fmt::format("Failed to link test executable '{}'. Link command [{}] returned {}:\n{}",
spec.output.string(),
quote_command(link_command),
proc_res.retc,
proc_res.output));
}
}

+ 37
- 0
src/dds/build/plan/exe.hpp Näytä tiedosto

@@ -0,0 +1,37 @@
#pragma once

#include <dds/build/plan/compile_file.hpp>
#include <dds/util/fs.hpp>

#include <set>
#include <tuple>
#include <vector>

namespace dds {

class library_plan;

class link_executable_plan {
std::vector<fs::path> _input_libs;
compile_file_plan _main_compile;
fs::path _out_subdir;
std::string _name;

public:
link_executable_plan(std::vector<fs::path> in_libs,
compile_file_plan cfp,
path_ref out_subdir,
std::string name_)
: _input_libs(std::move(in_libs))
, _main_compile(std::move(cfp))
, _out_subdir(out_subdir)
, _name(std::move(name_)) {}

auto& main_compile_file() const noexcept { return _main_compile; }

fs::path calc_executable_path(const build_env& env) const noexcept;

void link(const build_env&, const library_plan&) const;
};

} // namespace dds

+ 11
- 0
src/dds/build/plan/full.hpp Näytä tiedosto

@@ -0,0 +1,11 @@
#pragma once

#include <build/plan/package.hpp>

#include <map>

namespace dds {

class build_plan2 {};

} // namespace dds

+ 85
- 0
src/dds/build/plan/library.cpp Näytä tiedosto

@@ -0,0 +1,85 @@
#include "./library.hpp"

#include <dds/util/algo.hpp>

#include <range/v3/view/filter.hpp>
#include <range/v3/view/concat.hpp>
#include <spdlog/spdlog.h>

using namespace dds;

library_plan library_plan::create(const library& lib,
const library_build_params& params,
const usage_requirement_map& ureqs) {
std::vector<compile_file_plan> compile_files;
std::vector<link_executable_plan> link_executables;
std::optional<create_archive_plan> create_archive;

std::vector<source_file> app_sources;
std::vector<source_file> test_sources;
std::vector<source_file> lib_sources;

auto src_dir = lib.src_dir();
if (src_dir.exists()) {
auto all_sources = src_dir.sources();
auto to_compile = all_sources | ranges::views::filter([&](const source_file& sf) {
return (sf.kind == source_kind::source
|| (sf.kind == source_kind::app && params.build_apps)
|| (sf.kind == source_kind::test && params.build_tests));
});

for (const auto& sfile : to_compile) {
if (sfile.kind == source_kind::test) {
test_sources.push_back(sfile);
} else if (sfile.kind == source_kind::app) {
app_sources.push_back(sfile);
} else {
lib_sources.push_back(sfile);
}
}
}

auto compile_rules = lib.base_compile_rules();
for (const auto& use : lib.manifest().uses) {
ureqs.apply(compile_rules, use.namespace_, use.name);
}

for (const auto& sf : lib_sources) {
compile_files.emplace_back(compile_rules,
sf,
lib.manifest().name,
params.out_subdir / "obj");
}

if (!lib_sources.empty()) {
create_archive.emplace(lib.manifest().name, params.out_subdir, std::move(compile_files));
}

std::vector<fs::path> in_libs;
for (auto& use : lib.manifest().uses) {
extend(in_libs, ureqs.link_paths(use.namespace_, use.name));
}
for (auto& link : lib.manifest().links) {
extend(in_libs, ureqs.link_paths(link.namespace_, link.name));
}

for (const source_file& source : ranges::views::concat(app_sources, test_sources)) {
auto subdir = source.kind == source_kind::test ? params.out_subdir / "test" : params.out_subdir;
link_executables.emplace_back(in_libs,
compile_file_plan(compile_rules,
source,
lib.manifest().name,
params.out_subdir / "obj"),
subdir,
source.path.stem().stem().string());
}

if (!app_sources.empty()) {
spdlog::critical("Apps not implemented on this code path!");
}

return library_plan{lib.manifest().name,
lib.path(),
std::move(create_archive),
std::move(link_executables)};
}

+ 41
- 0
src/dds/build/plan/library.hpp Näytä tiedosto

@@ -0,0 +1,41 @@
#pragma once

#include <dds/build/plan/archive.hpp>
#include <dds/build/plan/exe.hpp>
#include <dds/library.hpp>
#include <dds/usage_reqs.hpp>
#include <dds/util/fs.hpp>

#include <map>
#include <optional>
#include <string>
#include <vector>

namespace dds {

class library_plan {
std::string _name;
fs::path _source_root;
std::optional<create_archive_plan> _create_archive;
std::vector<link_executable_plan> _link_exes;

public:
library_plan(std::string_view name,
path_ref source_root,
std::optional<create_archive_plan> ar,
std::vector<link_executable_plan> exes)
: _name(name)
, _source_root(source_root)
, _create_archive(std::move(ar))
, _link_exes(std::move(exes)) {}

path_ref source_root() const noexcept { return _source_root; }
auto& name() const noexcept { return _name; }
auto& create_archive() const noexcept { return _create_archive; }
auto& executables() const noexcept { return _link_exes; }

static library_plan
create(const library&, const library_build_params&, const usage_requirement_map&);
};

} // namespace dds

+ 27
- 0
src/dds/build/plan/package.hpp Näytä tiedosto

@@ -0,0 +1,27 @@
#pragma once

#include <dds/build/plan/library.hpp>

#include <map>
#include <string>

namespace dds {

class package_plan {
std::string _name;
std::string _namespace;
std::vector<library_plan> _libraries;

public:
package_plan(std::string_view name, std::string_view namespace_)
: _name(name)
, _namespace(namespace_) {}

void add_library(library_plan lp) { _libraries.emplace_back(std::move(lp)); }

auto& name() const noexcept { return _name; }
auto& namespace_() const noexcept { return _namespace; }
auto& libraries() const noexcept { return _libraries; }
};

} // namespace dds

+ 2
- 1
src/dds/ddslim.main.cpp Näytä tiedosto

@@ -375,10 +375,11 @@ struct cli_deps {
man.dependencies.end());
});

auto plan = dds::create_deps_build_plan(deps);
auto tc = tc_filepath.get_toolchain();
auto bdir = build_dir.Get();
dds::build_env env{std::move(tc), bdir};

auto plan = dds::create_deps_build_plan(deps, env);
plan.compile_all(env, 6);
plan.archive_all(env, 6);
if (!no_lmi.Get()) {

+ 78
- 32
src/dds/deps.cpp Näytä tiedosto

@@ -2,6 +2,8 @@

#include <dds/repo/repo.hpp>
#include <dds/sdist.hpp>
#include <dds/usage_reqs.hpp>
#include <dds/util/algo.hpp>
#include <dds/util/string.hpp>
#include <libman/index.hpp>
#include <libman/parse.hpp>
@@ -72,12 +74,13 @@ void detail::do_find_deps(const repository& repo, const dependency& dep, std::ve
}

using sdist_index_type = std::map<std::string, std::reference_wrapper<const sdist>>;
using sdist_names = std::set<std::string>;

namespace {

void linkup_dependencies(shared_compile_file_rules& rules,
const package_manifest& man,
const sdist_index_type& sd_idx) {
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()) {
@@ -86,7 +89,7 @@ void linkup_dependencies(shared_compile_file_rules& rules,
dep.name,
man.name));
}
linkup_dependencies(rules, found->second.get().manifest, sd_idx);
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)) {
@@ -97,32 +100,75 @@ void linkup_dependencies(shared_compile_file_rules& rules,
}
}

void add_sdist_to_dep_plan(build_plan& plan, const sdist& sd, const sdist_index_type& sd_idx) {
auto& pkg = plan.build_packages.emplace_back();
pkg.name = sd.manifest.name;
pkg.namespace_ = sd.manifest.namespace_;
auto libs = collect_libraries(sd.path);
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.name) != already_added.end()) {
// We've already loaded this package into the plan.
return;
}
spdlog::debug("Add to plan: {}", sd.manifest.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.name);
// Add the package:
auto& pkg = plan.add_package(package_plan(sd.manifest.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();
linkup_dependencies(comp_rules, sd.manifest, sd_idx);
library_build_params params;
params.compile_rules = comp_rules;
pkg.add_library(lib, params);
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) {
auto sd_idx = deps | ranges::views::transform([](const auto& sd) {
return std::pair(sd.manifest.name, std::cref(sd));
})
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.name, std::cref(sd)); }) //
| ranges::to<sdist_index_type>();

build_plan plan;
build_plan plan;
usage_requirement_map ureqs;
sdist_names already_added;
for (const sdist& sd : deps) {
spdlog::info("Recording dependency: {}", sd.manifest.name);
add_sdist_to_dep_plan(plan, sd, sd_idx);
add_sdist_to_dep_plan(plan, sd, env, sd_idx, ureqs, already_added);
}
return plan;
}
@@ -130,20 +176,20 @@ build_plan dds::create_deps_build_plan(const std::vector<sdist>& deps) {
namespace {

fs::path generate_lml(const library_plan& lib, path_ref libdir, const build_env& env) {
auto fname = lib.name + ".lml";
auto fname = lib.name() + ".lml";
auto lml_path = libdir / fname;

std::vector<lm::pair> kvs;
kvs.emplace_back("Type", "Library");
kvs.emplace_back("Name", lib.name);
if (lib.create_archive) {
kvs.emplace_back("Name", lib.name());
if (lib.create_archive()) {
kvs.emplace_back("Path",
fs::relative(lib.create_archive->archive_file_path(env),
fs::relative(lib.create_archive()->calc_archive_file_path(env),
lml_path.parent_path())
.string());
}
auto pub_inc_dir = lib.source_root / "include";
auto src_dir = lib.source_root / "src";
auto pub_inc_dir = lib.source_root() / "include";
auto src_dir = lib.source_root() / "src";
if (fs::exists(src_dir)) {
pub_inc_dir = src_dir;
}
@@ -157,16 +203,16 @@ fs::path generate_lml(const library_plan& lib, path_ref libdir, const build_env&
}

fs::path generate_lmp(const package_plan& pkg, path_ref basedir, const build_env& env) {
auto fname = pkg.name + ".lmp";
auto fname = pkg.name() + ".lmp";
auto lmp_path = basedir / fname;

std::vector<lm::pair> kvs;
kvs.emplace_back("Type", "Package");
kvs.emplace_back("Name", pkg.name);
kvs.emplace_back("Namespace", pkg.namespace_);
kvs.emplace_back("Name", pkg.name());
kvs.emplace_back("Namespace", pkg.namespace_());

for (auto&& lib : pkg.create_libraries) {
auto lml = generate_lml(lib, basedir / pkg.name, env);
for (auto&& lib : pkg.libraries()) {
auto lml = generate_lml(lib, basedir / pkg.name(), env);
kvs.emplace_back("Library", fs::relative(lml, lmp_path.parent_path()).string());
}

@@ -184,11 +230,11 @@ void dds::write_libman_index(path_ref out_filepath, const build_plan& plan, cons
auto lm_items_dir = out_filepath.parent_path() / "_libman";
std::vector<lm::pair> kvs;
kvs.emplace_back("Type", "Index");
for (const package_plan& pkg : plan.build_packages) {
for (const package_plan& pkg : plan.packages()) {
auto pkg_lmp = generate_lmp(pkg, lm_items_dir, env);
kvs.emplace_back("Package",
fmt::format("{}; {}",
pkg.name,
pkg.name(),
fs::relative(pkg_lmp, out_filepath.parent_path()).string()));
}
lm::write_pairs(out_filepath, kvs);

+ 1
- 1
src/dds/deps.hpp Näytä tiedosto

@@ -42,7 +42,7 @@ inline std::vector<sdist> find_dependencies(const repository& repo, Iter it, Snt
return acc;
}

build_plan create_deps_build_plan(const std::vector<sdist>& deps);
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);


+ 1
- 1
src/dds/library.cpp Näytä tiedosto

@@ -1,6 +1,6 @@
#include <dds/library.hpp>

#include <dds/build/compile.hpp>
#include <dds/build/plan/compile_file.hpp>
#include <dds/build/source_dir.hpp>
#include <dds/util/algo.hpp>


+ 4
- 6
src/dds/library.hpp Näytä tiedosto

@@ -1,6 +1,6 @@
#pragma once

#include <dds/build/compile.hpp>
#include <dds/build/plan/compile_file.hpp>
#include <dds/build/source_dir.hpp>
#include <dds/library_manifest.hpp>
#include <dds/source.hpp>
@@ -42,11 +42,9 @@ public:
};

struct library_build_params {
fs::path out_subdir;
bool build_tests = false;
bool build_apps = false;
std::vector<fs::path> rt_link_libraries;
shared_compile_file_rules compile_rules;
fs::path out_subdir;
bool build_tests = false;
bool build_apps = false;
};

std::vector<library> collect_libraries(path_ref where);

+ 11
- 5
src/dds/library_manifest.cpp Näytä tiedosto

@@ -1,5 +1,8 @@
#include "./library_manifest.hpp"

#include <dds/util/algo.hpp>
#include <range/v3/view/transform.hpp>

#include <libman/parse.hpp>

#include <spdlog/fmt/fmt.h>
@@ -10,13 +13,16 @@ library_manifest library_manifest::load_from_file(const fs::path& fpath) {
auto kvs = lm::parse_file(fpath);
library_manifest ret;
ret.name = fpath.parent_path().filename().string();
std::vector<std::string> uses_strings;
std::vector<std::string> links_strings;
lm::read(fmt::format("Reading library manifest {}", fpath.string()),
kvs,
lm::read_accumulate("Private-Include", ret.private_includes),
lm::read_accumulate("Private-Define", ret.private_defines),
lm::read_accumulate("Uses", ret.uses),
lm::read_accumulate("Links", ret.links),
lm::read_opt("Name", ret.name),
lm::read_accumulate("Uses", uses_strings),
lm::read_accumulate("Links", links_strings),
lm::read_required("Name", ret.name),
lm::reject_unknown());

extend(ret.uses, ranges::views::transform(uses_strings, lm::split_usage_string));
extend(ret.links, ranges::views::transform(links_strings, lm::split_usage_string));
return ret;
}

+ 5
- 5
src/dds/library_manifest.hpp Näytä tiedosto

@@ -2,16 +2,16 @@

#include <dds/util/fs.hpp>

#include <libman/library.hpp>

#include <vector>

namespace dds {

struct library_manifest {
std::string name;
std::vector<fs::path> private_includes;
std::vector<std::string> private_defines;
std::vector<std::string> uses;
std::vector<std::string> links;
std::string name;
std::vector<lm::usage> uses;
std::vector<lm::usage> links;

static library_manifest load_from_file(const fs::path&);
};

+ 74
- 0
src/dds/usage_reqs.cpp Näytä tiedosto

@@ -0,0 +1,74 @@
#include "./usage_reqs.hpp"

#include <dds/build/plan/compile_file.hpp>
#include <dds/util/algo.hpp>

#include <spdlog/fmt/fmt.h>

#include <stdexcept>

using namespace dds;

const lm::library* usage_requirement_map::get(std::string ns, std::string name) const noexcept {
auto found = _reqs.find(library_key{ns, name});
if (found == _reqs.end()) {
return nullptr;
}
return &found->second;
}

lm::library& usage_requirement_map::add(std::string ns, std::string name) {
auto pair = std::pair(library_key{ns, name}, lm::library{});
auto [inserted, did_insert] = _reqs.try_emplace(library_key{ns, name}, lm::library());
if (!did_insert) {
throw std::runtime_error(
fmt::format("More than one library is registered as {}/{}", ns, name));
}
return inserted->second;
}

void usage_requirement_map::apply(shared_compile_file_rules rules,
std::string ns,
std::string name) const {
auto reqs = get(ns, name);
if (!reqs) {
throw std::runtime_error(
fmt::format("Unable to resolve usage requirements for '{}/{}'", ns, name));
}

for (auto&& use : reqs->uses) {
apply(rules, use.namespace_, use.name);
}

extend(rules.include_dirs(), reqs->include_paths);
extend(rules.defs(), reqs->preproc_defs);
}

usage_requirement_map usage_requirement_map::from_lm_index(const lm::index& idx) noexcept {
usage_requirement_map ret;
for (const auto& pkg : idx.packages) {
for (const auto& lib : pkg.libraries) {
ret.add(pkg.namespace_, lib.name, lib);
}
}
return ret;
}

std::vector<fs::path> usage_requirement_map::link_paths(std::string ns, std::string name) const {
auto req = get(ns, name);
if (!req) {
throw std::runtime_error(
fmt::format("Unable to find linking requirement '{}/{}'", ns, name));
}
std::vector<fs::path> ret;
if (req->linkable_path) {
ret.push_back(*req->linkable_path);
}
for (const auto& dep : req->uses) {
extend(ret, link_paths(dep.namespace_, dep.name));
}
for (const auto& link : req->links) {
extend(ret, link_paths(link.namespace_, link.name));
}
return ret;
}

+ 49
- 0
src/dds/usage_reqs.hpp Näytä tiedosto

@@ -0,0 +1,49 @@
#pragma once

#include <dds/util/fs.hpp>
#include <libman/index.hpp>
#include <libman/library.hpp>

#include <map>
#include <string>

namespace dds {

class shared_compile_file_rules;

class usage_requirement_map {

struct library_key {
std::string namespace_;
std::string name;
};

struct library_key_compare {
bool operator()(const library_key& lhs, const library_key& rhs) const noexcept {
if (lhs.namespace_ < rhs.namespace_) {
return true;
}
if (lhs.namespace_ > rhs.namespace_) {
return false;
}
if (lhs.name < rhs.name) {
return true;
}
return false;
}
};

std::map<library_key, lm::library, library_key_compare> _reqs;

public:
const lm::library* get(std::string ns, std::string name) const noexcept;
lm::library& add(std::string ns, std::string name);
void add(std::string ns, std::string name, lm::library lib) { add(ns, name) = lib; }

void apply(shared_compile_file_rules, std::string ns, std::string name) const;
std::vector<fs::path> link_paths(std::string ns, std::string name) const;

static usage_requirement_map from_lm_index(const lm::index&) noexcept;
};

} // namespace dds

+ 25
- 1
src/libman/library.cpp Näytä tiedosto

@@ -11,6 +11,9 @@ library library::from_file(path_ref fpath) {

library ret;

std::vector<std::string> uses_strs;
std::vector<std::string> links_strs;

std::string _type_;
read(fmt::format("Reading library manifest file '{}'", fpath.string()),
pairs,
@@ -20,9 +23,20 @@ library library::from_file(path_ref fpath) {
read_opt("Path", ret.linkable_path),
read_accumulate("Include-Path", ret.include_paths),
read_accumulate("Preprocessor-Define", ret.preproc_defs),
read_accumulate("Uses", ret.uses),
read_accumulate("Uses", uses_strs),
read_accumulate("Links", links_strs),
read_accumulate("Special-Uses", ret.special_uses));

auto split_req = [](auto&& str) {};

for (auto&& uses_str : uses_strs) {
ret.uses.push_back(split_usage_string(uses_str));
}

for (auto&& links_str : links_strs) {
ret.links.push_back(split_usage_string(links_str));
}

auto make_absolute = [&](path_ref p) { return fpath.parent_path() / p; };
std::transform(ret.include_paths.begin(),
ret.include_paths.end(),
@@ -34,4 +48,14 @@ library library::from_file(path_ref fpath) {
}

return ret;
}

usage lm::split_usage_string(std::string_view str) {
auto sl_pos = str.find('/');
if (sl_pos == str.npos) {
throw std::runtime_error("Invalid Uses/Links specifier: " + std::string(str));
}
auto ns = str.substr(0, sl_pos);
auto name = str.substr(sl_pos + 1);
return usage{std::string(ns), std::string(name)};
}

+ 10
- 2
src/libman/library.hpp Näytä tiedosto

@@ -4,17 +4,25 @@

#include <optional>
#include <string>
#include <string_view>

namespace lm {

struct usage {
std::string namespace_;
std::string name;
};

usage split_usage_string(std::string_view);

class library {
public:
std::string name;
std::optional<fs::path> linkable_path;
std::vector<fs::path> include_paths;
std::vector<std::string> preproc_defs;
std::vector<std::string> uses;
std::vector<std::string> links;
std::vector<usage> uses;
std::vector<usage> links;
std::vector<std::string> special_uses;

static library from_file(path_ref);

Loading…
Peruuta
Tallenna