Bladeren bron

Significant refactor to support multiple libraries in a single project

default_compile_flags
vector-of-bool 5 jaren geleden
bovenliggende
commit
9eff7c9a10
15 gewijzigde bestanden met toevoegingen van 385 en 236 verwijderingen
  1. +0
    -0
      library.dds
  2. +1
    -0
      package.dds
  3. +293
    -202
      src/dds/build.cpp
  4. +2
    -3
      src/dds/build.hpp
  5. +3
    -1
      src/dds/compile.cpp
  6. +1
    -5
      src/dds/compile.hpp
  7. +4
    -10
      src/dds/ddslim.main.cpp
  8. +7
    -1
      src/dds/library.cpp
  9. +15
    -8
      src/dds/library.hpp
  10. +21
    -0
      src/dds/package_manifest.cpp
  11. +15
    -0
      src/dds/package_manifest.hpp
  12. +9
    -1
      src/dds/project.cpp
  13. +11
    -2
      src/dds/project.hpp
  14. +2
    -3
      src/dds/util.hpp
  15. +1
    -0
      src/libman/library.hpp

manifest.dds → library.dds Bestand weergeven


+ 1
- 0
package.dds Bestand weergeven

@@ -0,0 +1 @@
Name: ddslim

+ 293
- 202
src/dds/build.cpp Bestand weergeven

@@ -9,8 +9,11 @@
#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/filter.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/transform.hpp>

#include <algorithm>
@@ -24,12 +27,27 @@

using namespace dds;

using namespace ranges;
using namespace ranges::view;

namespace {

struct archive_failure : std::runtime_error {
using runtime_error::runtime_error;
};

/**
* Return an iterable over every library in the project
*/
auto iter_libraries(const project& pr) {
std::vector<std::reference_wrapper<const library>> libs;
if (pr.main_library()) {
libs.push_back(*pr.main_library());
}
extend(libs, pr.submodules());
return libs;
}

fs::path object_file_path(fs::path source_path, const build_params& params) {
auto obj_dir = params.out_root / "obj";
auto obj_relpath = fs::relative(source_path, params.root);
@@ -39,6 +57,11 @@ fs::path object_file_path(fs::path source_path, const build_params& params) {
return obj_path;
}

fs::path lib_archive_path(const build_params& params, const library& lib) {
return params.out_root
/ (fmt::format("lib{}{}", lib.name(), params.toolchain.archive_suffix()));
}

void copy_headers(const fs::path& source, const fs::path& dest, const source_list& sources) {
for (auto& file : sources) {
if (file.kind != source_kind::header) {
@@ -52,134 +75,115 @@ void copy_headers(const fs::path& source, const fs::path& dest, const source_lis
}
}

void generate_export(const build_params& params,
fs::path archive_file,
const source_list& sources) {
const auto export_root = params.out_root / fmt::format("{}.lpk", params.export_name);
spdlog::info("Generating library export: {}", export_root.string());
fs::remove_all(export_root);
fs::create_directories(export_root);
const auto archive_dest = export_root / archive_file.filename();
fs::copy_file(archive_file, archive_dest);

auto header_root = params.root / "include";
const auto header_dest = export_root / "include";
fs::path export_project_library(const build_params& params,
const library& lib,
const project& project,
path_ref export_root) {
auto relpath = fs::relative(lib.base_dir(), project.root());
auto lib_out_root = export_root / relpath;
auto header_root = lib.base_dir() / "include";
if (!fs::is_directory(header_root)) {
header_root = params.root / "src";
header_root = lib.base_dir() / "src";
}

auto lml_path = export_root / fmt::format("{}.lml", lib.name());
auto lml_parent_dir = lml_path.parent_path();

std::vector<lm::pair> pairs;
pairs.emplace_back("Type", "Library");
pairs.emplace_back("Name", lib.name());

if (fs::is_directory(header_root)) {
copy_headers(header_root, header_dest, sources);
auto header_dest = lib_out_root / "include";
copy_headers(header_root, header_dest, lib.sources());
pairs.emplace_back("Include-Path", fs::relative(header_dest, lml_parent_dir).string());
}

std::vector<lm::pair> lm_pairs;
lm_pairs.emplace_back("Type", "Package");
lm_pairs.emplace_back("Name", params.export_name);
lm_pairs.emplace_back("Namespace", params.export_name);
lm_pairs.emplace_back("Library", "lib.lml");
lm::write_pairs(export_root / "package.lmp", lm_pairs);

lm_pairs.clear();
lm_pairs.emplace_back("Type", "Library");
lm_pairs.emplace_back("Name", params.export_name);
lm_pairs.emplace_back("Path", fs::relative(archive_dest, export_root).string());
lm_pairs.emplace_back("Include-Path", fs::relative(header_dest, export_root).string());
lm::write_pairs(export_root / "lib.lml", lm_pairs);
}
auto ar_path = lib_archive_path(params, lib);
if (fs::is_regular_file(ar_path)) {
auto ar_dest = lib_out_root / ar_path.filename();
fs::copy_file(ar_path, ar_dest);
pairs.emplace_back("Path", fs::relative(ar_dest, lml_parent_dir).string());
}

fs::path
link_test(const fs::path& source_file, const build_params& params, const fs::path& lib_archive) {
const auto obj_file = object_file_path(source_file, params);
if (!fs::exists(obj_file)) {
throw compile_failure(
fmt::format("Unable to find a generated test object file where expected ({})",
obj_file.string()));
for (const auto& use : lib.manifest().uses) {
pairs.emplace_back("Uses", use);
}
for (const auto& links : lib.manifest().links) {
pairs.emplace_back("Links", links);
}

const auto test_name = source_file.stem().stem().string();
link_exe_spec spec;
extend(spec.inputs, {obj_file, lib_archive});
spec.output = params.out_root
/ fs::relative(source_file, params.root)
.replace_filename(test_name + params.toolchain.executable_suffix());
const auto link_command = params.toolchain.create_link_executable_command(spec);
lm::write_pairs(lml_path, pairs);
return lml_path;
}

spdlog::info("Linking test executable: {}", spec.output.string());
fs::create_directories(spec.output.parent_path());
auto proc_res = run_proc(link_command);
if (!proc_res.okay()) {
void export_project(const build_params& params, const project& project) {
if (project.manifest().name.empty()) {
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));
fmt::format("Cannot generate an export when the project has no name (Provide a package.dds with a `Name` field)"));
}
const auto export_root = params.out_root / fmt::format("{}.lpk", project.manifest().name);
spdlog::info("Generating project export: {}", export_root.string());
fs::remove_all(export_root);
fs::create_directories(export_root);

return spec.output;
std::vector<lm::pair> pairs;

pairs.emplace_back("Type", "Package");
pairs.emplace_back("Name", project.manifest().name);
pairs.emplace_back("Namespace", project.manifest().name);

auto all_libs = iter_libraries(project);
extend(pairs,
all_libs //
| transform(DDS_TL(export_project_library(params, _1, project, export_root))) //
| transform(DDS_TL(lm::pair("Library", _1.string()))) //
);

lm::write_pairs(export_root / "package.lmp", pairs);
}

void link_app(const fs::path& source_file,
const build_params& params,
const fs::path& lib_archive) {
const auto obj_file = object_file_path(source_file, params);
if (!fs::exists(obj_file)) {
throw compile_failure(
fmt::format("Unable to find a generated app object file where expected ({})",
obj_file.string()));
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));
}

const auto app_name = source_file.stem().stem().string();
link_exe_spec spec;
extend(spec.inputs, {obj_file, lib_archive});
spec.output = params.out_root / (app_name + params.toolchain.executable_suffix() + ".tmp");
const auto link_command = params.toolchain.create_link_executable_command(spec);
auto& pkg_ns = pair[0];
auto& lib = pair[1];

spdlog::info("Linking application executable: {}", spec.output.string());
auto proc_res = run_proc(link_command);
if (proc_res.retc != 0) {
throw compile_failure(fmt::format(
"Failed to link application executable '{}'. Link command [{}] returned {}:\n{}",
spec.output.string(),
quote_command(link_command),
proc_res.retc,
proc_res.output));
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));
}
fs::rename(spec.output, spec.output.parent_path() / spec.output.stem());
}

std::vector<fs::path> link_tests(const source_list& sources,
const build_params& params,
const library_manifest&,
const fs::path& lib_archive) {
std::vector<fs::path> exes;
for (const auto& source_file : sources) {
if (source_file.kind == source_kind::test) {
exes.push_back(link_test(source_file.path, params, lib_archive));
}
}
return exes;
}
const lm::library& lm_lib = found->second;
extend(includes, lm_lib.include_paths);
extend(defines, lm_lib.preproc_defs);

void link_apps(const source_list& sources,
const build_params& params,
const fs::path& lib_archive) {
for (const auto& source_file : sources) {
if (source_file.kind == source_kind::app) {
link_app(source_file.path, params, lib_archive);
}
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);
}
}

dds::compilation_set collect_compiles(const build_params& params, const library_manifest& man) {
auto project = project::from_directory(params.root);
assert(project.main_library());
const auto& sources = project.main_library()->sources();

const bool need_compile_deps = params.build_tests || params.build_apps || params.build_deps;
std::vector<file_compilation> file_compilations_of_lib(const build_params& params,
const library& lib) {
const auto& sources = lib.sources();

std::vector<fs::path> dep_includes;
std::vector<std::string> dep_defines;
if (need_compile_deps && (!man.uses.empty() || !man.links.empty())) {

if (!lib.manifest().uses.empty() || !lib.manifest().links.empty()) {
fs::path lm_index_path = params.lm_index;
for (auto cand : {"INDEX.lmi", "_build/INDEX.lmi"}) {
if (!lm_index_path.empty()) {
@@ -195,106 +199,87 @@ dds::compilation_set collect_compiles(const build_params& params, const library_
auto lm_index = lm::index::from_file(lm_index_path);
auto lib_index = lm_index.build_library_index();

auto collect_more_deps = [&](auto& uses_key) {
auto pair = split(uses_key, "/");
if (pair.size() != 2) {
throw compile_failure(fmt::format("Invalid `Uses`: {}", uses_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(dep_includes, lm_lib.include_paths);
extend(dep_defines, lm_lib.preproc_defs);

// TODO: RECURSE!
// for (auto next_usage : lm_lib.uses) {
// recurse(recurse, next_usage);
// }
};

// TODO: Set compilation flags on each file set (as needed)

for (auto& uses : man.uses) {
collect_more_deps(uses);
for (const auto& uses : lib.manifest().uses) {
include_deps(lib_index, dep_includes, dep_defines, uses, true);
}

spdlog::critical("Dependency resolution isn't done yet");
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!!");
}

if (sources.empty()) {
spdlog::info("No source files found to compile");
}

compilation_set comps;
for (auto& sf : sources) {
if (sf.kind == source_kind::header) {
continue;
}
if (sf.kind == source_kind::app && !params.build_apps) {
continue;
}
if (sf.kind == source_kind::test && !params.build_tests) {
continue;
}

compilation_rules rules;
rules.base_path() = params.root / "src";
extend(rules.defs(), man.private_defines);
extend(rules.defs(), dep_defines);
extend(rules.include_dirs(), man.private_includes);
extend(rules.include_dirs(), dep_includes);
rules.include_dirs().push_back(fs::absolute(params.root / "src"));
rules.include_dirs().push_back(fs::absolute(params.root / "include"));

const auto obj_path = object_file_path(sf.path, params);
comps.compilations.push_back(file_compilation{std::move(rules),
sf,
obj_path,
params.export_name,
params.enable_warnings});
}
return comps;
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));
};

compilation_rules rules;
rules.base_path() = lib.base_dir() / "src";
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.base_dir() / "src"));
rules.include_dirs().push_back(fs::absolute(lib.base_dir() / "include"));

return //
sources //
| filter(should_compile_source) //
| transform(DDS_TL(file_compilation{rules,
_1,
object_file_path(_1.path, params),
lib.name(),
params.enable_warnings})) //
| to_vector;
}

} // namespace

void dds::build(const build_params& params, const library_manifest& man) {
auto include_dir = params.root / "include";
auto src_dir = params.root / "src";

auto compiles = collect_compiles(params, man);

compiles.execute_all(params.toolchain, params.parallel_jobs);
std::vector<dds::file_compilation> collect_compiles(const build_params& params,
const project& project) {
auto libs = iter_libraries(project);
return //
libs //
| transform(DDS_TL(file_compilations_of_lib(params, _1))) //
| ranges::actions::join //
| to_vector //
;
}

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

source_list sources = compiles.compilations //
| transform(DDS_TL(_1.source)) //
| ranges::to_vector;
/**
* 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.
*/
fs::path create_lib_archive(const build_params& params,
const library& lib,
const object_file_index& obj_idx) {
archive_spec arc;
// Collect object files that make up that library
arc.input_files = compiles.compilations
// Only library sources
| filter(DDS_TL(_1.source.kind == source_kind::source)) //
| transform(DDS_TL(_1.obj)) //
| ranges::to_vector;
arc.out_path = lib_archive_path(params, lib);

arc.out_path = params.out_root
/ (fmt::format("lib{}{}", params.export_name, params.toolchain.archive_suffix()));

// Create the static library archive
spdlog::info("Create archive {}", arc.out_path.string());
// Collect object files that make up that library
arc.input_files = //
lib.sources() //
| filter(DDS_TL(_1.kind == source_kind::source)) //
| transform(DDS_TL(obj_for_source(obj_idx, _1.path))) //
| to_vector //
;

spdlog::info("Create archive for {}: {}", lib.name(), arc.out_path.string());
auto ar_cmd = params.toolchain.create_archive_command(arc);
if (fs::exists(arc.out_path)) {
fs::remove(arc.out_path);
@@ -308,28 +293,134 @@ void dds::build(const build_params& params, const library_manifest& man) {
throw archive_failure("Failed to create the library archive");
}

// Link any test executables
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;
extend(spec.inputs, {main_obj, lib_archive_path(params, lib)});
spec.output = dest;
const auto link_command = params.toolchain.create_link_executable_command(spec);

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

return spec.output;
}

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.sources() //
| filter(DDS_TL(_1.kind == sk)) //
| transform(DDS_TL(link_one_exe(get_exe_path(_1), _1.path, params, lib, obj_idx))) //
| to_vector //
;
}

struct link_results {
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;
res.archive_path = create_lib_archive(params, lib, obj_idx);

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) {
test_exes = link_tests(sources, params, man, arc.out_path);
extend(res.test_exes,
link_executables(source_kind::test, get_test_exe_path, params, lib, obj_idx));
}

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

if (params.do_export) {
generate_export(params, arc.out_path, sources);
}
return res;
}

if (params.build_tests) {
for (const auto& exe : test_exes) {
spdlog::info("Running test: {}", fs::relative(exe, params.out_root).string());
const auto test_res = run_proc({exe.string()});
if (!test_res.okay()) {
spdlog::error("TEST FAILED:\n{}", test_res.output);
}
std::vector<link_results> link_project(const build_params& params,
const project& pr,
const std::vector<file_compilation>& compilations) {
auto obj_index = //
compilations //
| transform(DDS_TL(std::pair(_1.source.path, _1.obj))) //
| to<object_file_index> //
;

auto libs = iter_libraries(pr);
return libs //
| transform(DDS_TL(link_project_lib(params, _1, obj_index))) //
| to_vector;
}

} // namespace

void dds::build(const build_params& params, const package_manifest&) {
auto project = project::from_directory(params.root);

auto compiles = collect_compiles(params, project);

dds::execute_all(compiles, params.toolchain, params.parallel_jobs);

using namespace ranges::view;

auto link_res = link_project(params, project, compiles);

auto all_tests = link_res //
| transform(DDS_TL(_1.test_exes)) //
| action::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++;
}
spdlog::info("Test run finished");
}

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

+ 2
- 3
src/dds/build.hpp Bestand weergeven

@@ -1,7 +1,7 @@
#ifndef DDS_BUILD_HPP_INCLUDED
#define DDS_BUILD_HPP_INCLUDED

#include <dds/manifest.hpp>
#include <dds/package_manifest.hpp>
#include <dds/toolchain.hpp>
#include <dds/util.hpp>

@@ -14,7 +14,6 @@ struct build_params {
fs::path out_root;
fs::path lm_index;
dds::toolchain toolchain;
std::string export_name;
bool do_export = false;
bool build_tests = false;
bool enable_warnings = false;
@@ -23,7 +22,7 @@ struct build_params {
int parallel_jobs = 0;
};

void build(const build_params&, const library_manifest& man);
void build(const build_params&, const package_manifest& man);

} // namespace dds


+ 3
- 1
src/dds/compile.cpp Bestand weergeven

@@ -55,7 +55,9 @@ void file_compilation::compile(const toolchain& tc) const {
}
}

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

+ 1
- 5
src/dds/compile.hpp Bestand weergeven

@@ -46,10 +46,6 @@ struct file_compilation {
void compile(const toolchain& tc) const;
};

struct compilation_set {
std::vector<file_compilation> compilations;

void execute_all(const toolchain& tc, int n_jobs) const;
};
void execute_all(const std::vector<file_compilation>&, const toolchain& tc, int n_jobs);

} // namespace dds

+ 4
- 10
src/dds/ddslim.main.cpp Bestand weergeven

@@ -2,6 +2,7 @@
#include <dds/logging.hpp>
#include <dds/sdist.hpp>
#include <dds/util.hpp>

#include <libman/parse.hpp>

#include <args.hxx>
@@ -35,12 +36,6 @@ struct common_project_flags {
"Path to the directory containing the project",
{"project-dir"},
dds::fs::current_path()};

string_flag export_name{cmd,
"export_name",
"Set the name of othe project",
{'n', "export-name"},
dds::fs::current_path().filename().string()};
};

struct cli_sdist {
@@ -134,7 +129,6 @@ struct cli_build {
dds::build_params params;
params.root = project.root.Get();
params.out_root = out.Get();
params.export_name = project.export_name.Get();
params.toolchain = _get_toolchain();
params.do_export = export_.Get();
params.build_tests = build_tests.Get();
@@ -142,10 +136,10 @@ struct cli_build {
params.enable_warnings = enable_warnings.Get();
params.parallel_jobs = num_jobs.Get();
params.lm_index = lm_index.Get();
dds::library_manifest man;
const auto man_filepath = params.root / "manifest.dds";
dds::package_manifest man;
const auto man_filepath = params.root / "package.dds";
if (exists(man_filepath)) {
man = dds::library_manifest::load_from_file(man_filepath);
man = dds::package_manifest::load_from_file(man_filepath);
}
if (full.Get()) {
params.do_export = true;

+ 7
- 1
src/dds/library.cpp Bestand weergeven

@@ -51,7 +51,13 @@ pf_info collect_pf_sources(path_ref path) {
library library::from_directory(path_ref lib_dir, std::string_view name) {
auto [sources, inc_dir, src_dir] = collect_pf_sources(lib_dir);

auto lib = library(lib_dir, name, std::move(sources));
library_manifest man;
auto man_path = lib_dir / "library.dds";
if (fs::is_regular_file(man_path)) {
man = library_manifest::load_from_file(man_path);
}

auto lib = library(lib_dir, name, std::move(sources), std::move(man));

if (fs::exists(inc_dir)) {
lib._pub_inc_dir = inc_dir;

+ 15
- 8
src/dds/library.hpp Bestand weergeven

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

#include <dds/library_manifest.hpp>
#include <dds/source.hpp>

#include <optional>
@@ -13,16 +14,18 @@ struct library_ident {
};

class library {
fs::path _base_dir;
std::string _name;
source_list _sources;
fs::path _pub_inc_dir;
fs::path _priv_inc_dir;

library(path_ref dir, std::string_view name, source_list&& src)
fs::path _base_dir;
std::string _name;
source_list _sources;
fs::path _pub_inc_dir;
fs::path _priv_inc_dir;
library_manifest _man;

library(path_ref dir, std::string_view name, source_list&& src, library_manifest&& man)
: _base_dir(dir)
, _name(name)
, _sources(std::move(src)) {}
, _sources(std::move(src))
, _man(std::move(man)) {}

public:
static library from_directory(path_ref, std::string_view name);
@@ -30,6 +33,10 @@ public:
return from_directory(path, path.filename().string());
}

auto& name() const noexcept { return _name; }

auto& manifest() const noexcept { return _man; }

path_ref base_dir() const noexcept { return _base_dir; }
path_ref public_include_dir() const noexcept { return _pub_inc_dir; }
path_ref private_include_dir() const noexcept { return _priv_inc_dir; }

+ 21
- 0
src/dds/package_manifest.cpp Bestand weergeven

@@ -0,0 +1,21 @@
#include "./package_manifest.hpp"

#include <libman/parse.hpp>

#include <spdlog/fmt/fmt.h>

using namespace dds;

package_manifest package_manifest::load_from_file(const fs::path& fpath) {
auto kvs = lm::parse_file(fpath);
package_manifest ret;
for (auto& pair : kvs.items()) {
if (pair.key() == "Name") {
ret.name = pair.value();
} else {
throw std::runtime_error(
fmt::format("Unknown key in file '{}': {}", fpath.string(), pair.key()));
}
}
return ret;
}

+ 15
- 0
src/dds/package_manifest.hpp Bestand weergeven

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

#include <dds/util.hpp>

#include <string>

namespace dds {

struct package_manifest {
std::string name;

static package_manifest load_from_file(path_ref);
};

} // namespace dds

+ 9
- 1
src/dds/project.cpp Bestand weergeven

@@ -30,5 +30,13 @@ project project::from_directory(path_ref pf_dir_path) {
if (has_library_dirs(pf_dir_path)) {
main_lib = library::from_directory(pf_dir_path);
}
return project(pf_dir_path, std::move(main_lib), collect_submodules(pf_dir_path / "libs"));
package_manifest man;
auto man_path = pf_dir_path / "package.dds";
if (fs::is_regular_file(man_path)) {
man = package_manifest::load_from_file(man_path);
}
return project(pf_dir_path,
std::move(main_lib),
collect_submodules(pf_dir_path / "libs"),
std::move(man));
}

+ 11
- 2
src/dds/project.hpp Bestand weergeven

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

#include <dds/library.hpp>
#include <dds/package_manifest.hpp>
#include <dds/util.hpp>

#include <optional>
@@ -12,11 +13,16 @@ class project {
fs::path _root;
std::optional<library> _main_lib;
std::vector<library> _submodules;
package_manifest _man;

project(path_ref root, std::optional<library>&& ml, std::vector<library>&& mods)
project(path_ref root,
std::optional<library>&& ml,
std::vector<library>&& mods,
package_manifest&& man)
: _root(root)
, _main_lib(std::move(ml))
, _submodules(std::move(mods)) {}
, _submodules(std::move(mods))
, _man(std::move(man)) {}

public:
static project from_directory(path_ref pr_dir);
@@ -28,6 +34,9 @@ public:
return nullptr;
}

auto& submodules() const noexcept { return _submodules; }
auto& manifest() const noexcept { return _man; }

path_ref root() const noexcept { return _root; }
};


+ 2
- 3
src/dds/util.hpp Bestand weergeven

@@ -37,11 +37,10 @@ decltype(auto) nth_arg(A&& a = nullptr, B&& b = nullptr, C&& c = nullptr, D&& d
[[maybe_unused]] auto&& _3 = ::dds::detail::nth_arg<2>((decltype(_args_)&&)(_args_)...); \
[[maybe_unused]] auto&& _4 = ::dds::detail::nth_arg<3>((decltype(_args_)&&)(_args_)...); \
static_assert(sizeof...(_args_) <= 4); \
decltype(auto) result = (__VA_ARGS__); \
return result; \
return (__VA_ARGS__); \
}

#define DDS_TL [] DDS_CTL
#define DDS_TL [&] DDS_CTL

inline namespace file_utils {


+ 1
- 0
src/libman/library.hpp Bestand weergeven

@@ -14,6 +14,7 @@ public:
std::vector<fs::path> include_paths;
std::vector<std::string> preproc_defs;
std::vector<std::string> uses;
std::vector<std::string> links;
std::vector<std::string> special_uses;

static library from_file(path_ref);

Laden…
Annuleren
Opslaan