Final touches

main
Duncan Ogilvie 3 years ago
parent 8d13ccbeaa
commit d905be1d13

1
CMakeLists.txt generated

@ -86,7 +86,6 @@ list(APPEND cmkr_SOURCES
"include/arguments.hpp"
"include/build.hpp"
"include/cmake_generator.hpp"
"include/enum_helper.hpp"
"include/error.hpp"
"include/fs.hpp"
"include/help.hpp"

@ -1,70 +0,0 @@
// https://codereview.stackexchange.com/a/14315
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
// This is the type that will hold all the strings.
// Each enumeration type will declare its own specialization.
// Any enum that does not have a specialization will generate a compiler error
// indicating that there is no definition of this variable (as there should be
// be no definition of a generic version).
template <typename T>
struct enumStrings {
static std::vector<std::string> data;
};
// This is a utility type.
// Created automatically. Should not be used directly.
template <typename T>
struct enumRefHolder {
T &enumVal;
enumRefHolder(T &enumVal) : enumVal(enumVal) {}
};
template <typename T>
struct enumConstRefHolder {
T const &enumVal;
enumConstRefHolder(T const &enumVal) : enumVal(enumVal) {}
};
// The next two functions do the actual work of reading/writing an
// enum as a string.
template <typename T>
std::ostream &operator<<(std::ostream &str, enumConstRefHolder<T> const &data) {
return str << enumStrings<T>::data[data.enumVal];
}
template <typename T>
std::istream &operator>>(std::istream &str, enumRefHolder<T> const &data) {
std::string value;
str >> value;
// These two can be made easier to read in C++11
// using std::begin() and std::end()
//
auto begin = std::begin(enumStrings<T>::data);
auto end = std::end(enumStrings<T>::data);
auto find = std::find(begin, end, value);
if (find != end) {
data.enumVal = static_cast<T>(std::distance(begin, find));
} else {
throw std::invalid_argument("");
}
return str;
}
// This is the public interface:
// use the ability of function to deduce their template type without
// being explicitly told to create the correct type of enumRefHolder<T>
template <typename T>
enumConstRefHolder<T> enumToString(T const &e) {
return enumConstRefHolder<T>(e);
}
template <typename T>
enumRefHolder<T> enumFromString(T &e) {
return enumRefHolder<T>(e);
}

@ -57,13 +57,15 @@ enum TargetType {
target_custom,
target_object,
target_template,
target_COUNT,
target_last,
};
extern const char *targetTypeNames[target_last];
struct Target {
std::string name;
TargetType type = {};
std::string type_string;
TargetType type = target_last;
std::string type_name;
ConditionVector headers;
ConditionVector sources;

@ -780,7 +780,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
// Check if this target is using a template.
if (target.type == parser::target_template) {
for (const auto &t : project.templates) {
if (target.type_string == t.outline.name) {
if (target.type_name == t.outline.name) {
tmplate = &t;
tmplate_cs = std::unique_ptr<ConditionScope>(new ConditionScope(gen, tmplate->outline.condition));
}
@ -895,6 +895,8 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
if (tmplate->pass_sources_to_add_function) {
cmd(add_command)(target.name, target_type_string, "${" + sources_var + "}");
} else {
cmd(add_command)(target.name, target_type_string).endl();
// clang-format off
cmd("if")(sources_var);
cmd("target_sources")(target.name, target_type == parser::target_interface ? "INTERFACE" : "PRIVATE", "${" + sources_var + "}");

@ -1,40 +1,27 @@
#include "project_parser.hpp"
#include "enum_helper.hpp"
#include "fs.hpp"
#include <deque>
#include <stdexcept>
#include <toml.hpp>
#include <tsl/ordered_map.h>
template <>
std::vector<std::string> enumStrings<cmkr::parser::TargetType>::data{"executable", "library", "shared", "static",
"interface", "custom", "object", "template"};
namespace cmkr {
namespace parser {
using TomlBasicValue = toml::basic_value<toml::discard_comments, tsl::ordered_map, std::vector>;
const char *targetTypeNames[target_last] = {"executable", "library", "shared", "static", "interface", "custom", "object", "template"};
template <typename EnumType>
static EnumType to_enum(const std::string &str, const std::string &help_name) {
EnumType value;
try {
std::stringstream ss(str);
ss >> enumFromString(value);
} catch (std::invalid_argument &) {
std::string supported;
for (const auto &s : enumStrings<EnumType>::data) {
if (!supported.empty()) {
supported += ", ";
static TargetType parse_targetType(const std::string &name) {
for (int i = 0; i < target_last; i++) {
if (name == targetTypeNames[i]) {
return static_cast<TargetType>(i);
}
supported += s;
}
throw std::runtime_error("Unknown " + help_name + "'" + str + "'! Supported types are: " + supported);
}
return value;
return target_last;
}
using TomlBasicValue = toml::basic_value<toml::discard_comments, tsl::ordered_map, std::vector>;
static std::string format_key_error(const std::string &error, const toml::key &ky, const TomlBasicValue &value) {
auto loc = value.location();
auto line_number_str = std::to_string(loc.line());
@ -43,7 +30,7 @@ static std::string format_key_error(const std::string &error, const toml::key &k
std::ostringstream oss;
oss << "[error] " << error << '\n';
oss << " --> " << loc.file_name() << '\n';
oss << " --> " << loc.file_name() << ':' << loc.line() << '\n';
oss << std::string(line_width + 2, ' ') << "|\n";
oss << ' ' << line_number_str << " | " << line_str << '\n';
@ -56,7 +43,6 @@ static std::string format_key_error(const std::string &error, const toml::key &k
if (key_start != std::string::npos) {
oss << std::string(key_start + 1, ' ') << std::string(ky.length(), '~');
}
oss << '\n';
return oss.str();
}
@ -242,6 +228,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
conditions["x32"] = R"cmake(CMAKE_SIZEOF_VOID_P EQUAL 4)cmake";
} else {
conditions = parent->conditions;
templates = parent->templates;
}
if (toml.contains("conditions")) {
@ -387,16 +374,43 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
throw std::runtime_error("[[bin]] has been renamed to [[target]]");
}
auto parse_target = [&](const std::string &name, TomlChecker& t) {
auto parse_target = [&](const std::string &name, TomlChecker &t, bool isTemplate) {
Target target;
target.name = name;
t.required("type", target.type_string);
t.required("type", target.type_name);
target.type = parse_targetType(target.type_name);
target.type = to_enum<TargetType>(target.type_string, "target type");
// Users cannot set this target type
if (target.type == target_template) {
target.type = target_last;
}
if (target.type >= target_COUNT) {
if (!isTemplate && target.type == target_last) {
for (const auto &tmplate : templates) {
if (target.type_name == tmplate.outline.name) {
target.type = target_template;
break;
}
}
}
if (target.type == target_last) {
std::string error = "Unknown target type '" + target.type_name + "'\n";
error += "Available types:\n";
for (std::string type_name : targetTypeNames) {
if (type_name != "template") {
error += " - " + type_name + "\n";
}
}
if (!isTemplate && !templates.empty()) {
error += "Available templates:\n";
for (const auto &tmplate : templates) {
error += " - " + tmplate.outline.name + "\n";
}
}
error.pop_back(); // Remove last newline
throw std::runtime_error(format_key_error(error, target.type_name, t.find("type")));
}
t.optional("headers", target.headers);
@ -477,26 +491,37 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
if (toml.contains("template")) {
const auto &ts = toml::find(toml, "template").as_table();
for (const auto &itr : ts) {
Template tmplate;
auto &t = checker.create(itr.second);
tmplate.outline = parse_target(itr.first, t);
const auto &name = itr.first;
for (const auto &type_name : targetTypeNames) {
if (name == type_name) {
throw std::runtime_error(format_key_error("Reserved template name '" + name + "'", name, itr.second));
}
}
for (const auto &tmplate : templates) {
if (name == tmplate.outline.name) {
throw std::runtime_error(format_key_error("Template '" + name + "' already defined", name, itr.second));
}
}
Template tmplate;
tmplate.outline = parse_target(name, t, true);
t.optional("add-function", tmplate.add_function);
t.optional("pass-sources-to-add-function", tmplate.pass_sources_to_add_function);
templates.push_back(tmplate);
enumStrings<TargetType>::data.push_back(tmplate.outline.name);
}
}
if (toml.contains("target")) {
const auto &ts = toml::find(toml, "target").as_table();
for (const auto &itr : ts) {
auto &t = checker.create(itr.second);
targets.push_back(parse_target(itr.first, t));
targets.push_back(parse_target(itr.first, t, false));
}
}

Loading…
Cancel
Save