Переглянути джерело

New toolchain config file format

default_compile_flags
vector-of-bool 5 роки тому
джерело
коміт
a019ed9e0b
9 змінених файлів з 692 додано та 143 видалено
  1. +1
    -1
      src/dds/build/plan/compile_file.cpp
  2. +2
    -1
      src/dds/dds.main.cpp
  3. +484
    -0
      src/dds/toolchain/from_dds.cpp
  4. +18
    -0
      src/dds/toolchain/from_dds.hpp
  5. +127
    -0
      src/dds/toolchain/from_dds.test.cpp
  6. +1
    -102
      src/dds/toolchain/toolchain.cpp
  7. +0
    -24
      src/dds/toolchain/toolchain.hpp
  8. +50
    -15
      src/libman/parse.hpp
  9. +9
    -0
      src/libman/parse_fwd.hpp

+ 1
- 1
src/dds/build/plan/compile_file.cpp Переглянути файл

@@ -39,7 +39,7 @@ void compile_file_plan::compile(const build_env& env) const {
auto end_time = std::chrono::steady_clock::now();
auto dur_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);

spdlog::info("{} - {:>5n}ms", msg, dur_ms.count());
spdlog::info("{} - {:>7n}ms", msg, dur_ms.count());

if (!compile_res.okay()) {
spdlog::error("Compilation failed: {}", _source.path.string());

+ 2
- 1
src/dds/dds.main.cpp Переглянути файл

@@ -2,6 +2,7 @@
#include <dds/logging.hpp>
#include <dds/repo/repo.hpp>
#include <dds/sdist.hpp>
#include <dds/toolchain/from_dds.hpp>
#include <dds/util/fs.hpp>
#include <dds/util/paths.hpp>
#include <dds/util/signal.hpp>
@@ -40,7 +41,7 @@ struct toolchain_flag : string_flag {
}
return std::move(*tc);
} else {
return dds::toolchain::load_from_file(tc_path);
return dds::parse_toolchain_dds(dds::slurp_file(tc_path));
}
}
};

+ 484
- 0
src/dds/toolchain/from_dds.cpp Переглянути файл

@@ -0,0 +1,484 @@
#include "./from_dds.hpp"

#include <dds/toolchain/prep.hpp>
#include <dds/toolchain/toolchain.hpp>
#include <dds/util/algo.hpp>
#include <libman/parse.hpp>

#include <spdlog/fmt/fmt.h>

#include <map>
#include <optional>
#include <tuple>
#include <vector>

using namespace dds;

using fmt::format;
using std::optional;
using std::string;
using std::vector;
using string_seq = vector<string>;
using opt_string = optional<string>;
using opt_str_seq = optional<string_seq>;
using strv = std::string_view;

toolchain dds::parse_toolchain_dds(strv str, strv context) {
auto kvs = lm::parse_string(str);
return parse_toolchain_dds(kvs, context);
}

struct read_argv_acc {
strv my_key;
opt_str_seq& out;

bool operator()(strv, strv key, strv value) const {
if (key != my_key) {
return false;
}
if (!out) {
out.emplace();
}
auto cmd = split_shell_string(value);
extend(*out, cmd);
return true;
}
};

struct read_argv {
strv my_key;
opt_str_seq& out;

bool operator()(strv ctx, strv key, strv value) const {
if (key != my_key) {
return false;
}
if (out.has_value()) {
throw std::runtime_error(
format("{}: More than one value provided for key '{}'", ctx, key));
}
out.emplace(split_shell_string(value));
return true;
}
};

template <typename T, typename Func>
T read_opt(const std::optional<T>& what, Func&& fn) {
if (!what.has_value()) {
return fn();
}
return *what;
}

toolchain dds::parse_toolchain_dds(const lm::pair_list& pairs, strv context) {
opt_string compiler_id;
opt_string c_compiler_fpath;
opt_string cxx_compiler_fpath;
opt_string c_version;
opt_string cxx_version;
opt_string archive_prefix;
opt_string archive_suffix;
opt_string obj_prefix;
opt_string obj_suffix;
opt_string exe_prefix;
opt_string exe_suffix;
optional<bool> do_debug;
optional<bool> do_optimize;
opt_str_seq include_template;
opt_str_seq define_template;
opt_str_seq warning_flags;
opt_str_seq flags;
opt_str_seq c_flags;
opt_str_seq cxx_flags;
opt_str_seq c_compile_file;
opt_str_seq cxx_compile_file;
opt_str_seq create_archive;
opt_str_seq link_executable;
opt_str_seq compile_launcher;

lm::read(context,
pairs,
// Base compile info:
lm::read_opt("Compiler-ID", compiler_id),
lm::read_opt("C-Compiler", c_compiler_fpath),
lm::read_opt("C++-Compiler", cxx_compiler_fpath),
// Language options
lm::read_opt("C-Version", c_version),
lm::read_opt("C++-Version", cxx_version),
// Flag templates
read_argv{"Include-Template", include_template},
read_argv{"Define-Template", define_template},
// Flags
read_argv_acc{"Warning-Flags", warning_flags},
read_argv_acc{"Flags", flags},
read_argv_acc{"C-Flags", c_flags},
read_argv_acc{"C++-Flags", cxx_flags},
// Options for flags
lm::read_bool("Optimize", do_optimize),
lm::read_bool("Debug", do_debug),
// Miscellaneous
read_argv{"Compiler-Launcher", compile_launcher},
// Command templates
read_argv{"C-Compile-File", c_compile_file},
read_argv{"C++-Compile-File", cxx_compile_file},
read_argv{"Create-Archive", create_archive},
read_argv{"Link-Executable", link_executable},
// Filename affixes
lm::read_opt("Archive-Prefix", archive_prefix),
lm::read_opt("Archive-Suffix", archive_suffix),
lm::read_opt("Object-Prefix", obj_prefix),
lm::read_opt("Object-Suffix", obj_suffix),
lm::read_opt("Executable-Prefix", exe_prefix),
lm::read_opt("Executable-Suffix", exe_suffix),
// Die:
lm::reject_unknown());

toolchain_prep tc;

auto fail = [&](auto msg, auto... args)[[noreturn]]->void {
throw std::runtime_error(
format("{}: Failed to generate toolchain: {}", context, msg, args...));
};

enum compiler_id_e_t {
no_comp_id,
msvc,
clang,
gnu,
} compiler_id_e
= [&] {
if (!compiler_id) {
return no_comp_id;
} else if (compiler_id == "MSVC") {
return msvc;
} else if (compiler_id == "GNU") {
return gnu;
} else if (compiler_id == "Clang") {
return clang;
} else {
fail("Unknown Compiler-ID '{}'", *compiler_id);
}
}();

bool is_gnu = compiler_id_e == gnu;
bool is_clang = compiler_id_e == clang;
bool is_msvc = compiler_id_e == msvc;
bool is_gnu_like = is_gnu || is_clang;

// Now convert the flags we've been given into a real toolchain
auto get_compiler = [&](bool is_cxx) -> string {
if (is_cxx && cxx_compiler_fpath) {
return *cxx_compiler_fpath;
}
if (!is_cxx && c_compiler_fpath) {
return *c_compiler_fpath;
}
if (!compiler_id.has_value()) {
fail("Unable to determine what compiler to use.");
}
if (is_gnu) {
return is_cxx ? "g++" : "gcc";
}
if (is_clang) {
return is_cxx ? "clang++" : "clang";
}
if (is_msvc) {
return "cl.exe";
}
assert(false && "Compiler name deduction failed");
std::terminate();
};

enum c_version_e_t {
c_none,
c89,
c99,
c11,
c18,
} c_version_e
= [&] {
if (!c_version) {
return c_none;
} else if (c_version == "c89") {
return c89;
} else if (c_version == "c99") {
return c99;
} else if (c_version == "c11") {
return c11;
} else if (c_version == "c18") {
return c18;
} else {
fail("Unknown C-Version '{}'", *c_version);
}
}();

enum cxx_version_e_t {
cxx_none,
cxx98,
cxx03,
cxx11,
cxx14,
cxx17,
cxx20,
} cxx_version_e
= [&] {
if (!cxx_version) {
return cxx_none;
} else if (cxx_version == "c++98") {
return cxx98;
} else if (cxx_version == "c++03") {
return cxx03;
} else if (cxx_version == "c++11") {
return cxx11;
} else if (cxx_version == "c++14") {
return cxx14;
} else if (cxx_version == "c++17") {
return cxx17;
} else if (cxx_version == "c++20") {
return cxx20;
} else {
fail("Unknown C++-Version '{}'", *cxx_version);
}
}();

std::map<std::tuple<compiler_id_e_t, c_version_e_t>, string_seq> c_version_flag_table = {
{{msvc, c_none}, {}},
{{msvc, c89}, {}},
{{msvc, c99}, {}},
{{msvc, c11}, {}},
{{msvc, c18}, {}},
{{gnu, c_none}, {}},
{{gnu, c89}, {"-std=c89"}},
{{gnu, c99}, {"-std=c99"}},
{{gnu, c11}, {"-std=c11"}},
{{gnu, c18}, {"-std=c18"}},
{{clang, c_none}, {}},
{{clang, c89}, {"-std=c89"}},
{{clang, c99}, {"-std=c99"}},
{{clang, c11}, {"-std=c11"}},
{{clang, c18}, {"-std=c18"}},
};

auto get_c_version_flags = [&]() -> string_seq {
if (!compiler_id.has_value()) {
fail("Unable to deduce flags for 'C-Version' without setting 'Compiler-ID'");
}
auto c_ver_iter = c_version_flag_table.find({compiler_id_e, c_version_e});
assert(c_ver_iter != c_version_flag_table.end());
return c_ver_iter->second;
};

std::map<std::tuple<compiler_id_e_t, cxx_version_e_t>, string_seq> cxx_version_flag_table = {
{{msvc, cxx_none}, {}},
{{msvc, cxx98}, {}},
{{msvc, cxx03}, {}},
{{msvc, cxx11}, {}},
{{msvc, cxx14}, {"/std:c++14"}},
{{msvc, cxx17}, {"/std:c++17"}},
{{msvc, cxx20}, {"/std:c++latest"}},
{{gnu, cxx_none}, {}},
{{gnu, cxx98}, {"-std=c++98"}},
{{gnu, cxx03}, {"-std=c++03"}},
{{gnu, cxx11}, {"-std=c++11"}},
{{gnu, cxx14}, {"-std=c++14"}},
{{gnu, cxx17}, {"-std=c++17"}},
{{gnu, cxx20}, {"-std=c++20"}},
{{clang, cxx_none}, {}},
{{clang, cxx98}, {"-std=c++98"}},
{{clang, cxx03}, {"-std=c++03"}},
{{clang, cxx11}, {"-std=c++11"}},
{{clang, cxx14}, {"-std=c++14"}},
{{clang, cxx17}, {"-std=c++17"}},
{{clang, cxx20}, {"-std=c++20"}},
};

auto get_cxx_version_flags = [&]() -> string_seq {
if (!compiler_id.has_value()) {
fail("Unable to deduce flags for 'C++-Version' without setting 'Compiler-ID'");
}
auto cxx_ver_iter = cxx_version_flag_table.find({compiler_id_e, cxx_version_e});
assert(cxx_ver_iter != cxx_version_flag_table.end());
return cxx_ver_iter->second;
};

auto get_flags = [&](bool is_cxx) -> string_seq {
string_seq ret;
if (flags) {
extend(ret, *flags);
}
if (is_cxx && cxx_flags) {
extend(ret, *cxx_flags);
}
if (!is_cxx && c_flags) {
extend(ret, *c_flags);
}
if (!is_cxx && c_version) {
extend(ret, get_c_version_flags());
}
if (is_cxx && cxx_version) {
extend(ret, get_cxx_version_flags());
}
if (is_msvc) {
strv rt_lib = "/MT";
if (do_optimize.has_value() && *do_optimize) {
extend(ret, {"/O2"});
}
if (do_debug.has_value() && *do_debug) {
extend(ret, {"/Z7", "/DEBUG", "/MTd"});
rt_lib = "/MTd";
}
ret.emplace_back(rt_lib);
extend(ret, {"/nologo", "<FLAGS>", "/c", "<IN>", "/permissive-", "/Fo<OUT>"});
if (is_cxx) {
extend(ret, {"/EHsc"});
}
} else if (is_gnu_like) {
if (do_optimize.has_value() && *do_optimize) {
extend(ret, {"-O2"});
}
if (do_debug.has_value() && *do_debug) {
extend(ret, {"-g"});
}
extend(ret,
{"-fPIC",
"-fdiagnostics-color",
"-pthread",
"<FLAGS>",
"-c",
"<IN>",
"-o<OUT>"});
}
return ret;
};

tc.c_compile = read_opt(c_compile_file, [&] {
string_seq c;
if (compile_launcher) {
extend(c, *compile_launcher);
}
c.push_back(get_compiler(false));
extend(c, get_flags(false));
return c;
});

tc.cxx_compile = read_opt(cxx_compile_file, [&] {
string_seq cxx;
if (compile_launcher) {
extend(cxx, *compile_launcher);
}
cxx.push_back(get_compiler(true));
extend(cxx, get_flags(true));
return cxx;
});

tc.include_template = read_opt(include_template, [&]() -> string_seq {
if (!compiler_id) {
fail("Cannot deduce 'Include-Template' without 'Compiler-ID'");
}
if (is_gnu_like) {
return {"-I", "<PATH>"};
} else if (compiler_id == "MSVC") {
return {"/I", "<PATH>"};
}
assert(false && "Include-Template deduction failed");
std::terminate();
});

tc.define_template = read_opt(define_template, [&]() -> string_seq {
if (!compiler_id) {
fail("Cannot deduce 'Define-Template' without 'Compiler-ID'");
}
if (is_gnu_like) {
return {"-D", "<DEF>"};
} else if (compiler_id == "MSVC") {
return {"/D", "<DEF>"};
}
assert(false && "Define-Template deduction failed");
std::terminate();
});

tc.archive_prefix = archive_prefix.value_or("lib");
tc.archive_suffix = read_opt(archive_suffix, [&] {
if (!compiler_id) {
fail("Cannot deduce library file extension without Compiler-ID");
}
if (is_gnu) {
return ".a";
} else if (is_msvc) {
return ".lib";
}
assert(false && "No archive suffix");
std::terminate();
});

tc.object_prefix = obj_prefix.value_or("");
tc.object_suffix = read_opt(obj_suffix, [&] {
if (!compiler_id) {
fail("Cannot deduce object file extension without Compiler-ID");
}
if (is_gnu) {
return ".o";
} else if (is_msvc) {
return ".obj";
}
assert(false && "No object file suffix");
std::terminate();
});

tc.exe_prefix = exe_prefix.value_or("");
tc.exe_suffix = read_opt(exe_suffix, [&] {
#ifdef _WIN32
return ".exe";
#else
return "";
#endif
});

tc.warning_flags = read_opt(warning_flags, [&]() -> string_seq {
if (!compiler_id) {
// No error. Just no warning flags
return {};
}
if (is_msvc) {
return {"/W4", "/WX"};
} else if (is_gnu_like) {
return {"-Wall", "-Wextra", "-Wpedantic", "-Wconversion"};
}
assert(false && "No warning flags");
std::terminate();
});

tc.link_archive = read_opt(create_archive, [&]() -> string_seq {
if (!compiler_id) {
fail("Unable to deduce archive creation rules without a Compiler-ID");
}
if (is_msvc) {
return {"lib", "/nologo", "/OUT:<OUT>", "<IN>"};
} else if (is_gnu_like) {
return {"ar", "rcs", "<OUT>", "<IN>"};
}
assert(false && "No archive command");
std::terminate();
});

tc.link_exe = read_opt(link_executable, [&]() -> string_seq {
if (!compiler_id) {
fail("Unable to deduce how to link executables without a Compiler-ID");
}
if (is_msvc) {
return {get_compiler(true), "/nologo", "/EHsc", "<IN>", "/Fe<OUT>"};
} else if (is_gnu_like) {
return {get_compiler(true),
"-fPIC",
"-fdiagnostics-color",
"<IN>",
"-pthread",
"-lstdc++fs",
"-o<OUT>"};
}
assert(false && "No link-exe command");
std::terminate();
});

return tc.realize();
}

+ 18
- 0
src/dds/toolchain/from_dds.hpp Переглянути файл

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

#include <dds/toolchain/toolchain.hpp>

#include <libman/parse_fwd.hpp>

#include <string_view>

namespace dds {

class toolchain;

toolchain parse_toolchain_dds(std::string_view str,
std::string_view context = "Loading toolchain file");
toolchain parse_toolchain_dds(const lm::pair_list&,
std::string_view context = "Loading toolchain file");

} // namespace dds

+ 127
- 0
src/dds/toolchain/from_dds.test.cpp Переглянути файл

@@ -0,0 +1,127 @@
#include <dds/toolchain/from_dds.hpp>

#include <dds/proc.hpp>

#include <dds/util.test.hpp>

namespace {

void check_tc_compile(std::string_view tc_content,
std::string_view compile,
std::string_view compile_warnings,
std::string_view ar,
std::string_view exe) {
auto tc = dds::parse_toolchain_dds(tc_content);
bool any_error = false;

dds::compile_file_spec cf;
cf.source_path = "foo.cpp";
cf.out_path = "foo.o";
auto cf_cmd = tc.create_compile_command(cf);
auto cf_cmd_str = dds::quote_command(cf_cmd);
if (cf_cmd_str != compile) {
std::cerr << "Compile command came out incorrect!\n";
std::cerr << " Expected: " << compile << '\n';
std::cerr << " Actual: " << cf_cmd_str << "\n\n";
any_error = true;
}

cf.enable_warnings = true;
cf_cmd = tc.create_compile_command(cf);
cf_cmd_str = dds::quote_command(cf_cmd);
if (cf_cmd_str != compile_warnings) {
std::cerr << "Compile command (with warnings) came out incorrect!\n";
std::cerr << " Expected: " << compile_warnings << '\n';
std::cerr << " Actual: " << cf_cmd_str << "\n\n";
any_error = true;
}

dds::archive_spec ar_spec;
ar_spec.input_files.push_back("foo.o");
ar_spec.input_files.push_back("bar.o");
ar_spec.out_path = "stuff.a";
auto ar_cmd = tc.create_archive_command(ar_spec);
auto ar_cmd_str = dds::quote_command(ar_cmd);
if (ar_cmd_str != ar) {
std::cerr << "Archive command came out incorrect!\n";
std::cerr << " Expected: " << ar << '\n';
std::cerr << " Actual: " << ar_cmd_str << "\n\n";
any_error = true;
}

dds::link_exe_spec exe_spec;
exe_spec.inputs.push_back("foo.o");
exe_spec.inputs.push_back("bar.a");
exe_spec.output = "meow.exe";
auto exe_cmd = tc.create_link_executable_command(exe_spec);
auto exe_cmd_str = dds::quote_command(exe_cmd);
if (exe_cmd_str != exe) {
std::cerr << "Executable linking command came out incorrect!\n";
std::cerr << " Expected: " << exe << '\n';
std::cerr << " Actual: " << exe_cmd_str << "\n\n";
any_error = true;
}

if (any_error) {
std::cerr << "The error-producing toolchain file content:\n" << tc_content << '\n';
dds::S_failed_checks++;
}
}

void run_tests() {
check_tc_compile("Compiler-ID: GNU",
"g++ -fPIC -fdiagnostics-color -pthread -c foo.cpp -ofoo.o",
"g++ -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion "
"-c foo.cpp -ofoo.o",
"ar rcs stuff.a foo.o bar.o",
"g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -lstdc++fs -omeow.exe");

auto tc = dds::parse_toolchain_dds(R"(
Compiler-ID: GNU
)");

dds::compile_file_spec cfs;
cfs.source_path = "foo.cpp";
cfs.out_path = "foo.o";
auto cmd = tc.create_compile_command(cfs);
CHECK(cmd
== std::vector<std::string>{"g++",
"-fPIC",
"-fdiagnostics-color",
"-pthread",
"-c",
"foo.cpp",
"-ofoo.o"});

cfs.definitions.push_back("FOO=BAR");
cmd = tc.create_compile_command(cfs);
CHECK(cmd
== std::vector<std::string>{"g++",
"-fPIC",
"-fdiagnostics-color",
"-pthread",
"-D",
"FOO=BAR",
"-c",
"foo.cpp",
"-ofoo.o"});

cfs.include_dirs.push_back("fake-dir");
cmd = tc.create_compile_command(cfs);
CHECK(cmd
== std::vector<std::string>{"g++",
"-fPIC",
"-fdiagnostics-color",
"-pthread",
"-I",
"fake-dir",
"-D",
"FOO=BAR",
"-c",
"foo.cpp",
"-ofoo.o"});
}

} // namespace

DDS_TEST_MAIN;

+ 1
- 102
src/dds/toolchain/toolchain.cpp Переглянути файл

@@ -4,10 +4,7 @@
#include <dds/util/algo.hpp>
#include <dds/util/string.hpp>

#include <libman/parse.hpp>

#include <spdlog/fmt/fmt.h>

#include <cassert>
#include <optional>
#include <string>
#include <vector>
@@ -20,14 +17,6 @@ using std::string_view;
using std::vector;
using opt_string = optional<string>;

namespace {

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

} // namespace

toolchain toolchain::realize(const toolchain_prep& prep) {
toolchain ret;
ret._c_compile = prep.c_compile;
@@ -46,96 +35,6 @@ toolchain toolchain::realize(const toolchain_prep& prep) {
return ret;
}

toolchain toolchain::load_from_file(fs::path p) {
opt_string inc_template;
opt_string def_template;

opt_string c_compile_template;
opt_string cxx_compile_template;
opt_string create_archive_template;
opt_string link_exe_template;
opt_string warning_flags;

opt_string archive_prefix;
opt_string archive_suffix;
opt_string object_suffix;
opt_string exe_suffix;

auto require_key = [](auto k, auto& opt) {
if (!opt.has_value()) {
throw invalid_toolchain(
fmt::format("Toolchain file is missing a required key '{}'", k));
}
};

auto kvs = lm::parse_file(p);
for (auto&& pair : kvs.items()) {
auto& key = pair.key();
auto& value = pair.value();

auto try_single = [&](auto k, auto& opt) {
if (key == k) {
if (opt.has_value()) {
throw invalid_toolchain(fmt::format("Duplicated key '{}'", key));
}
opt = value;
return true;
}
return false;
};

// clang-format off
bool found_single = false // Bool to force alignment
// Argument templates
|| try_single("Include-Template", inc_template)
|| try_single("Define-Template", def_template)
// Command templates
|| try_single("Compile-C-Template", c_compile_template)
|| try_single("Compile-C++-Template", cxx_compile_template)
|| try_single("Create-Archive-Template", create_archive_template)
|| try_single("Link-Executable-Template", link_exe_template)
|| try_single("Warning-Flags", warning_flags)
|| try_single("Archive-Prefix", archive_prefix)
|| try_single("Archive-Suffix", archive_suffix)
|| try_single("Object-Suffix", object_suffix)
|| try_single("Executable-Suffix", exe_suffix)
|| false;
// clang-format on

if (found_single) {
continue;
}

throw invalid_toolchain(fmt::format("Unknown toolchain file key '{}'", key));
}

require_key("Include-Template", inc_template);
require_key("Define-Template", def_template);

require_key("Compile-C-Template", c_compile_template);
require_key("Compile-C++-Template", cxx_compile_template);
require_key("Create-Archive-Template", create_archive_template);
require_key("Link-Executable-Template", link_exe_template);

require_key("Archive-Suffix", archive_suffix);
require_key("Object-Suffix", object_suffix);
require_key("Executable-Suffix", exe_suffix);

return toolchain{
c_compile_template.value(),
cxx_compile_template.value(),
inc_template.value(),
def_template.value(),
create_archive_template.value(),
link_exe_template.value(),
warning_flags.value_or(""),
archive_prefix.value_or("lib"),
archive_suffix.value(),
object_suffix.value(),
exe_suffix.value(),
};
}

vector<string> dds::split_shell_string(std::string_view shell) {
char cur_quote = 0;
bool is_escaped = false;

+ 0
- 24
src/dds/toolchain/toolchain.hpp Переглянути файл

@@ -58,31 +58,7 @@ class toolchain {
public:
toolchain() = default;

toolchain(std::string_view c_compile,
std::string_view cxx_compile,
std::string_view inc_template,
std::string_view def_template,
std::string_view archive_template,
std::string_view link_exe_template,
std::string_view warning_flags,
std::string_view archive_prefix,
std::string_view archive_suffix,
std::string_view object_suffix,
std::string_view exe_suffix)
: _c_compile(split_shell_string(c_compile))
, _cxx_compile(split_shell_string(cxx_compile))
, _inc_template(split_shell_string(inc_template))
, _def_template(split_shell_string(def_template))
, _link_archive(split_shell_string(archive_template))
, _link_exe(split_shell_string(link_exe_template))
, _warning_flags(split_shell_string(warning_flags))
, _archive_prefix(archive_prefix)
, _archive_suffix(archive_suffix)
, _object_suffix(object_suffix)
, _exe_suffix(exe_suffix) {}

static toolchain realize(const toolchain_prep&);
static toolchain load_from_file(fs::path);

auto& archive_suffix() const noexcept { return _archive_suffix; }
auto& object_suffix() const noexcept { return _object_suffix; }

+ 50
- 15
src/libman/parse.hpp Переглянути файл

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

#include <libman/parse_fwd.hpp>

#include <cassert>
#include <filesystem>
#include <optional>
@@ -134,7 +136,7 @@ public:
: _key(key)
, _ref(ref) {}

int read_one(std::string_view context, std::string_view key, std::string_view value) {
int operator()(std::string_view context, std::string_view key, std::string_view value) {
if (key != _key) {
return 0;
}
@@ -166,7 +168,7 @@ public:
: _key(key)
, _ref(ref) {}

int read_one(std::string_view context, std::string_view key, std::string_view value) {
int operator()(std::string_view context, std::string_view key, std::string_view value) {
if (key != _key) {
return 0;
}
@@ -177,8 +179,39 @@ public:
_ref = T(value);
return 1;
}
};

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

void validate(std::string_view) {}
public:
read_bool(std::string_view key, T& ref)
: _key(key)
, _ref(ref) {}

bool operator()(std::string_view context, std::string_view key, std::string_view value) {
if (key != _key) {
return false;
}
if (_did_read) {
throw std::runtime_error(std::string(context) + ": Duplicate key '" + std::string(key)
+ "' is not allowed.");
}
if (value == "true" || value == "True") {
_ref = true;
} else if (value == "false" || value == "False") {
_ref = false;
} else {
throw std::runtime_error(std::string(context) + ": Invalid value '" + std::string(value)
+ "' for key '" + std::string(key)
+ ".' Expected `true` or `false`.");
}
_did_read = true;
return true;
}
};

class read_check_eq {
@@ -190,7 +223,7 @@ public:
: _key(key)
, _expect(value) {}

int read_one(std::string_view context, std::string_view key, std::string_view value) const {
int operator()(std::string_view context, std::string_view key, std::string_view value) const {
if (key != _key) {
return 0;
}
@@ -201,8 +234,6 @@ public:
}
return 1;
}

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

template <typename Container>
@@ -215,36 +246,40 @@ public:
: _key(key)
, _items(c) {}

int read_one(std::string_view, std::string_view key, std::string_view value) const {
int operator()(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) {}
};

class reject_unknown {
public:
int read_one(std::string_view context, std::string_view key, std::string_view) const {
int operator()(std::string_view context, std::string_view key, std::string_view) const {
throw std::runtime_error(std::string(context) + ": Unknown key '" + std::string(key) + "'");
}

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

template <typename T>
auto validate_reader(T&& t, std::string_view context, int) -> decltype(t.validate(context)) {
t.validate(context);
}

template <typename T>
void validate_reader(T&&, std::string_view, ...) {}

template <typename... Items>
auto read(std::string_view context [[maybe_unused]], const pair_list& pairs, Items... is) {
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 did_read = (is.read_one(context, key, value) || ...);
auto did_read = (is(context, key, value) || ...);
if (did_read) {
bad_pairs.emplace_back(key, value);
}
}
(is.validate(context), ...);
(validate_reader(is, context, 0), ...);
return bad_pairs;
}


+ 9
- 0
src/libman/parse_fwd.hpp Переглянути файл

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

namespace lm {

class pair;
class pair_iterator;
class pair_list;

} // namespace lm

Завантаження…
Відмінити
Зберегти