Преглед на файлове

Mostly-complete libman import support!

default_compile_flags
vector-of-bool преди 5 години
родител
ревизия
ea8132e02e
променени са 14 файла, в които са добавени 454 реда и са изтрити 37 реда
  1. +55
    -1
      src/dds/build.cpp
  2. +1
    -0
      src/dds/build.hpp
  3. +8
    -1
      src/dds/ddslim.main.cpp
  4. +45
    -15
      src/dds/util.hpp
  5. +3
    -0
      src/libman/fmt.hpp
  6. +66
    -0
      src/libman/index.cpp
  7. +21
    -0
      src/libman/index.hpp
  8. +37
    -0
      src/libman/library.cpp
  9. +22
    -0
      src/libman/library.hpp
  10. +28
    -0
      src/libman/package.cpp
  11. +19
    -0
      src/libman/package.hpp
  12. +1
    -20
      src/libman/parse.cpp
  13. +138
    -0
      src/libman/parse.hpp
  14. +10
    -0
      src/libman/util.hpp

+ 55
- 1
src/dds/build.cpp Целия файл

@@ -5,6 +5,7 @@
#include <dds/proc.hpp>
#include <dds/source.hpp>
#include <dds/toolchain.hpp>
#include <libman/index.hpp>
#include <libman/parse.hpp>

#include <algorithm>
@@ -167,7 +168,58 @@ void link_apps(const source_list& sources,
dds::compilation_set collect_compiles(const build_params& params, const library_manifest& man) {
source_list sources = source_file::collect_pf_sources(params.root);
const bool need_compile_deps = params.build_tests || params.build_apps || params.build_deps;
if (need_compile_deps) {

std::vector<fs::path> dep_includes;
std::vector<std::string> dep_defines;
if (need_compile_deps && (!man.uses.empty() || !man.links.empty())) {
fs::path lm_index_path = params.lm_index;
for (auto cand : {"INDEX.lmi", "_build/INDEX.lmi"}) {
if (!lm_index_path.empty()) {
break;
}
lm_index_path = params.root / cand;
}
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();

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

spdlog::critical("Dependency resolution isn't done yet");
}

@@ -190,7 +242,9 @@ dds::compilation_set collect_compiles(const build_params& params, const library_
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"));


+ 1
- 0
src/dds/build.hpp Целия файл

@@ -12,6 +12,7 @@ namespace dds {
struct build_params {
fs::path root;
fs::path out_root;
fs::path lm_index;
dds::toolchain toolchain;
std::string export_name;
bool do_export = false;

+ 8
- 1
src/dds/ddslim.main.cpp Целия файл

@@ -1,7 +1,7 @@
#include <dds/build.hpp>
#include <libman/parse.hpp>
#include <dds/logging.hpp>
#include <dds/util.hpp>
#include <libman/parse.hpp>

#include <args.hxx>

@@ -53,6 +53,12 @@ struct cli_build {
args::Flag build_apps{cmd, "build_apps", "Build applications", {"apps", 'A'}};
args::Flag export_{cmd, "export", "Generate a library export", {"export", 'E'}};

path_flag lm_index{cmd,
"lm_index",
"Path to a libman index (usually INDEX.lmi)",
{"--lm-index", 'I'},
dds::fs::path()};

args::Flag enable_warnings{cmd,
"enable_warnings",
"Enable compiler warnings",
@@ -95,6 +101,7 @@ struct cli_build {
params.build_apps = build_apps.Get();
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";
if (exists(man_filepath)) {

+ 45
- 15
src/dds/util.hpp Целия файл

@@ -7,6 +7,8 @@

namespace dds {

inline namespace file_utils {

namespace fs = std::filesystem;

using path_ref = const fs::path&;
@@ -32,6 +34,48 @@ inline std::string slurp_file(const fs::path& path) {
return contents;
}

} // namespace file_utils

template <typename Container, typename Predicate>
void erase_if(Container& c, Predicate&& p) {
auto erase_point = std::remove_if(c.begin(), c.end(), p);
c.erase(erase_point, c.end());
}

template <typename Container, typename Other>
void extend(Container& c, const Other& o) {
c.insert(c.end(), o.begin(), o.end());
}

template <typename Container, typename Item>
void extend(Container& c, std::initializer_list<Item> il) {
c.insert(c.end(), il.begin(), il.end());
}

inline namespace string_utils {

inline std::string_view sview(std::string_view::const_iterator beg,
std::string_view::const_iterator end) {
return std::string_view(&*beg, static_cast<std::size_t>(std::distance(beg, end)));
}

inline std::string_view trim(std::string_view s) {
auto iter = s.begin();
auto end = s.end();
while (iter != end && std::isspace(*iter)) {
++iter;
}
auto riter = s.rbegin();
auto rend = s.rend();
while (riter != rend && std::isspace(*riter)) {
++riter;
}
auto new_end = riter.base();
return sview(iter, new_end);
}

inline std::string trim(std::string&& s) { return std::string(trim(s)); }

inline bool ends_with(std::string_view s, std::string_view key) {
auto found = s.rfind(key);
return found != s.npos && found == s.size() - key.size();
@@ -74,21 +118,7 @@ replace(std::vector<std::string> strings, std::string_view key, std::string_view
return strings;
}

template <typename Container, typename Predicate>
void erase_if(Container& c, Predicate&& p) {
auto erase_point = std::remove_if(c.begin(), c.end(), p);
c.erase(erase_point, c.end());
}

template <typename Container, typename Other>
void extend(Container& c, const Other& o) {
c.insert(c.end(), o.begin(), o.end());
}

template <typename Container, typename Item>
void extend(Container& c, std::initializer_list<Item> il) {
c.insert(c.end(), il.begin(), il.end());
}
} // namespace string_utils

} // namespace dds


+ 3
- 0
src/libman/fmt.hpp Целия файл

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

#include <spdlog/fmt/fmt.h>

+ 66
- 0
src/libman/index.cpp Целия файл

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

#include <libman/fmt.hpp>
#include <libman/parse.hpp>

using namespace lm;

lm::index index::from_file(path_ref fpath) {
const auto kvs = parse_file(fpath);
// const auto type = kvs.find("Type");
// if (!type || type->value() != "Index") {
// throw std::runtime_error(
// fmt::format("Libman file has missing/incorrect 'Type' ({})", fpath.string()));
// }

index ret;

std::optional<std::string> type;
std::vector<std::string> package_lines;

read(fmt::format("Reading libman index file '{}'", fpath.string()),
kvs,
read_required("Type", type),
read_check_eq("Type", "Index"),
read_accumulate("Package", package_lines));

for (const auto& pkg_line : package_lines) {
auto items = dds::split(pkg_line, ";");
std::transform(items.begin(), items.end(), items.begin(), [](auto s) { return trim(s); });
if (items.size() != 2) {
throw std::runtime_error(
fmt::format("Invalid 'Package' field in index file ({}): 'Package: {}'",
fpath.string(),
pkg_line));
}

auto pkg = package::from_file(fpath.parent_path() / items[1]);
if (pkg.name != items[0]) {
// throw std::runtime_error(fmt::format(
// "Package file ({}) listed different name '{}' than the index file '{}'",
// items[1],
// pkg.name,
// items[0]));
}
ret.packages.push_back(std::move(pkg));
}

return ret;
}

index::library_index index::build_library_index() const {
library_index ret;
for (auto& pkg : packages) {
for (auto& lib : pkg.libraries) {
auto pair = std::pair(pkg.namespace_, lib.name);
bool did_insert = ret.try_emplace(pair, lib).second;
if (!did_insert) {
throw std::runtime_error(
fmt::format("Duplicate library '{}/{}' defined in the libman tree",
pair.first,
pair.second));
}
}
}
return ret;
}

+ 21
- 0
src/libman/index.hpp Целия файл

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

#include <libman/package.hpp>
#include <libman/util.hpp>

#include <functional>
#include <map>

namespace lm {

class index {
public:
std::vector<package> packages;
static index from_file(path_ref);

using library_index
= std::map<std::pair<std::string, std::string>, std::reference_wrapper<const library>>;
library_index build_library_index() const;
};

} // namespace lm

+ 37
- 0
src/libman/library.cpp Целия файл

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

#include <libman/parse.hpp>

#include <spdlog/spdlog.h>

using namespace lm;

library library::from_file(path_ref fpath) {
auto pairs = parse_file(fpath);

library ret;

std::string _type_;
read(fmt::format("Reading library manifest file '{}'", fpath.string()),
pairs,
read_required("Type", _type_),
read_check_eq("Type", "Library"),
read_required("Name", ret.name),
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("Special-Uses", ret.special_uses));

auto make_absolute = [&](path_ref p) { return fpath.parent_path() / p; };
std::transform(ret.include_paths.begin(),
ret.include_paths.end(),
ret.include_paths.begin(),
make_absolute);

if (ret.linkable_path) {
ret.linkable_path = make_absolute(*ret.linkable_path);
}

return ret;
}

+ 22
- 0
src/libman/library.hpp Целия файл

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

#include <libman/util.hpp>

#include <optional>
#include <string>

namespace lm {

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> special_uses;

static library from_file(path_ref);
};

} // namespace lm

+ 28
- 0
src/libman/package.cpp Целия файл

@@ -0,0 +1,28 @@
#include "./package.hpp"

#include <libman/fmt.hpp>
#include <libman/parse.hpp>

using namespace lm;

package package::from_file(path_ref fpath) {
package ret;
auto pairs = parse_file(fpath);

std::string _type_;
std::vector<fs::path> libraries;
read(fmt::format("Reading package file '{}'", fpath.string()),
pairs,
read_required("Type", _type_),
read_check_eq("Type", "Package"),
read_required("Name", ret.name),
read_required("Namespace", ret.namespace_),
read_accumulate("Requires", ret.requires),
read_accumulate("Library", libraries));

for (path_ref lib_path : libraries) {
ret.libraries.push_back(library::from_file(fpath.parent_path() / lib_path));
}

return ret;
}

+ 19
- 0
src/libman/package.hpp Целия файл

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

#include <libman/library.hpp>
#include <libman/util.hpp>

namespace lm {

class package {
public:
std::string name;
std::string namespace_;
std::vector<std::string> requires;
std::vector<library> libraries;
fs::path lmp_path;

static package from_file(path_ref);
};

} // namespace lm

+ 1
- 20
src/libman/parse.cpp Целия файл

@@ -1,6 +1,6 @@
#include "./parse.hpp"

#include <dds/util.hpp>
#include <libman/util.hpp>

#include <spdlog/fmt/fmt.h>

@@ -15,25 +15,6 @@ using namespace lm;

namespace {

std::string_view sview(std::string_view::const_iterator beg, std::string_view::const_iterator end) {
return std::string_view(&*beg, static_cast<std::size_t>(std::distance(beg, end)));
}

std::string_view trim(std::string_view s) {
auto iter = s.begin();
auto end = s.end();
while (iter != end && std::isspace(*iter)) {
++iter;
}
auto riter = s.rbegin();
auto rend = s.rend();
while (riter != rend && std::isspace(*riter)) {
++riter;
}
auto new_end = riter.base();
return sview(iter, new_end);
}

void parse_line(std::vector<pair>& pairs, const std::string_view whole_line) {
const auto line = trim(whole_line);
if (line.empty() || line[0] == '#') {

+ 138
- 0
src/libman/parse.hpp Целия файл

@@ -2,7 +2,9 @@

#include <cassert>
#include <filesystem>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

@@ -19,6 +21,15 @@ public:

auto& key() const noexcept { return _key; }
auto& value() const noexcept { return _value; }

template <std::size_t I>
std::string_view get() const {
if constexpr (I == 0) {
return key();
} else if constexpr (I == 1) {
return value();
}
}
};

class pair_iterator {
@@ -112,4 +123,131 @@ inline void write_pairs(const std::filesystem::path& fpath, const pair_list& pai
write_pairs(fpath, pairs.items());
}

template <typename What>
class read_required {
std::string_view _key;
What& _ref;
bool _did_read = false;

public:
read_required(std::string_view key, What& ref)
: _key(key)
, _ref(ref) {}

int read_one(std::string_view context, std::string_view key, std::string_view value) {
if (key != _key) {
return 0;
}
if (_did_read) {
throw std::runtime_error(std::string(context) + ": Duplicated key '" + std::string(key)
+ "' is not allowed");
}
_did_read = true;
_ref = What(value);
return 1;
}

void validate(std::string_view context) const {
if (!_did_read) {
throw std::runtime_error(std::string(context) + ": Missing required key '"
+ std::string(_key) + "'");
}
}
};

template <typename T>
class read_opt {
std::string_view _key;
std::optional<T>& _ref;

public:
read_opt(std::string_view key, std::optional<T>& ref)
: _key(key)
, _ref(ref) {}

int read_one(std::string_view context, std::string_view key, std::string_view value) {
if (key != _key) {
return 0;
}
if (_ref.has_value()) {
throw std::runtime_error(std::string(context) + ": Duplicated key '" + std::string(key)
+ "' is not allowed.");
}
_ref.emplace(value);
return 1;
}

void validate(std::string_view) {}
};

class read_check_eq {
std::string_view _key;
std::string_view _expect;

public:
read_check_eq(std::string_view key, std::string_view value)
: _key(key)
, _expect(value) {}

int read_one(std::string_view context, std::string_view key, std::string_view value) const {
if (key != _key) {
return 0;
}
if (value != _expect) {
throw std::runtime_error(std::string(context) + ": Expected key '" + std::string(key)
+ "' to have value '" + std::string(_expect) + "' (Got '"
+ std::string(value) + "')");
}
return 1;
}

void validate(std::string_view) {}
};

template <typename Container>
class read_accumulate {
std::string_view _key;
Container& _items;

public:
read_accumulate(std::string_view key, Container& c)
: _key(key)
, _items(c) {}

int read_one(std::string_view, std::string_view key, std::string_view value) const {
if (key == _key) {
_items.emplace_back(value);
return 1;
}
return 0;
}

void validate(std::string_view) {}
};

template <typename... Items>
auto read(std::string_view context[[maybe_unused]], const pair_list& pairs, Items... is) {
std::vector<pair> bad_pairs;
for (auto [key, value] : pairs.items()) {
auto nread = (is.read_one(context, key, value) + ... + 0);
if (nread == 0) {
bad_pairs.emplace_back(key, value);
}
}
(is.validate(context), ...);
return bad_pairs;
}

} // namespace lm

namespace std {

template <>
struct tuple_size<lm::pair> : std::integral_constant<int, 2> {};

template <std::size_t N>
struct tuple_element<N, lm::pair> {
using type = std::string_view;
};

} // namespace std

+ 10
- 0
src/libman/util.hpp Целия файл

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

#include <dds/util.hpp>

namespace lm {

using namespace dds::file_utils;
using namespace dds::string_utils;

} // namespace lm

Loading…
Отказ
Запис