summaryrefslogtreecommitdiffhomepage
path: root/Builder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Builder.cpp')
-rw-r--r--Builder.cpp124
1 files changed, 82 insertions, 42 deletions
diff --git a/Builder.cpp b/Builder.cpp
index 86f6920..57f731e 100644
--- a/Builder.cpp
+++ b/Builder.cpp
@@ -17,6 +17,7 @@
#include <unordered_set>
#include <vector>
+#include <boost/process.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
@@ -25,25 +26,36 @@
#include <libreichwein/file.h>
#include <libreichwein/stringhelper.h>
+namespace bp = boost::process;
namespace fs = std::filesystem;
namespace pt = boost::property_tree;
using namespace std::chrono_literals;
using namespace std::string_literals;
namespace {
+ const std::string topelement{"ymake"};
+
+ bool is_match(const pt::ptree::value_type& element, const std::string& target) {
+ if (target == "*") {
+ return true;
+ } else {
+ return element.first == target;
+ }
+ }
+
// get name of single target, ptree is <build> subtree
fs::path get_target(const pt::ptree& ptree) {
return ptree.get<std::string>("name");
}
// ptree is main tree
- std::vector<fs::path> get_all_targets(const pt::ptree& ptree) {
+ std::vector<fs::path> get_all_targets(const pt::ptree& ptree, const std::string& target) {
std::vector<fs::path> result;
// iterate over all <build> elements
- for (const auto& build: ptree.get_child("ymake")) {
- if (build.first == "build") {
- result.push_back(build.second.get<std::string>("name"));
+ for (const auto& element: ptree.get_child(topelement)) {
+ if (is_match(element, target)) {
+ result.push_back(element.second.get<std::string>("name"));
}
}
@@ -60,25 +72,15 @@ namespace {
return sources;
}
- // get link libs of single target, ptree is <build> subtree
- std::vector<std::string> get_link_libs_of_build(const pt::ptree& ptree) {
- std::vector<std::string> linklibs;
- for (const pt::ptree::value_type &v: ptree) {
- if (v.first == "linklib")
- linklibs.push_back(v.second.data());
- }
- return linklibs;
- }
-
// ptree is main tree
- std::vector<fs::path> get_all_sources(const pt::ptree& ptree) {
+ std::vector<fs::path> get_all_sources(const pt::ptree& ptree, const std::string& target) {
std::vector<fs::path> result;
// iterate over all <build> elements
- for (const auto& build: ptree.get_child("ymake")) {
- if (build.first == "build") {
+ for (const auto& element: ptree.get_child(topelement)) {
+ if (is_match(element, target)) {
// iterate over all <source> elements
- for (const auto& source: build.second) {
+ for (const auto& source: element.second) {
if (source.first == "source") {
result.push_back(source.second.data());
}
@@ -100,8 +102,8 @@ namespace {
}
// ptree is main tree
- std::vector<fs::path> get_all_objects(const pt::ptree& ptree) {
- std::vector<fs::path> objects{get_all_sources(ptree)};
+ std::vector<fs::path> get_all_objects(const pt::ptree& ptree, const std::string& target) {
+ std::vector<fs::path> objects{get_all_sources(ptree, target)};
for (auto &i: objects) {
i.replace_extension("o");
}
@@ -134,28 +136,44 @@ namespace {
return depfile;
}
- std::unordered_map<fs::path, std::vector<std::string>> get_link_libs(const pt::ptree& ptree) {
- std::unordered_map<fs::path, std::vector<std::string>> link_libs_map;
+ // T = std::string, fs::path
+ template<typename T>
+ // get specified subelements (as list) of single target, ptree is <build> subtree
+ std::vector<T> get_subelements_of_build(const pt::ptree& ptree, const std::string& subelement) {
+ std::vector<T> result;
+ for (const pt::ptree::value_type &v: ptree) {
+ if (v.first == subelement) {
+ result.push_back(v.second.data());
+ }
+ }
+ return result;
+ }
- for (const auto& build: ptree.get_child("ymake")) {
- if (build.first == "build") {
- std::vector<std::string> link_libs{get_link_libs_of_build(build.second)};
- if (!link_libs.empty()) {
- link_libs_map.emplace(get_target(build.second), link_libs);
+ // T = std::string, fs::path
+ template<typename T>
+ std::unordered_map<fs::path, std::vector<T>> get_subelements(const pt::ptree& ptree, const std::string& target, const std::string& subelement) {
+ std::unordered_map<fs::path, std::vector<T>> result_map;
+
+ for (const auto& element: ptree.get_child(topelement)) {
+ if (is_match(element, target)) {
+ std::vector<T> subelements{get_subelements_of_build<T>(element.second, subelement)};
+ if (!subelements.empty()) {
+ result_map.emplace(get_target(element.second), subelements);
}
}
}
- return link_libs_map;
+ return result_map;
}
-
}
-Builder::Builder(const pt::ptree& ptree):
+Builder::Builder(const pt::ptree& ptree, const std::string& target):
_ptree(ptree),
- _all_targets{get_all_targets(ptree)},
- _all_objects{get_all_objects(ptree)},
- _link_libs{get_link_libs(ptree)}
+ _target(target),
+ _all_targets{get_all_targets(ptree, target)},
+ _all_objects{get_all_objects(ptree, target)},
+ _include_paths{get_subelements<fs::path>(ptree, target, "includepath")},
+ _link_libs{get_subelements<std::string>(ptree, target, "linklib")}
{
// intentionally defer creation of _dependencies to build()
// to prevent creation of .d files in clean()
@@ -179,6 +197,15 @@ std::vector<std::string> Builder::link_libs_of(const fs::path& p) const
}
}
+std::vector<fs::path> Builder::include_paths_of(const fs::path& p) const
+{
+ try {
+ return _include_paths.at(p);
+ } catch (const std::out_of_range& ex) {
+ return {}; // empty by default
+ }
+}
+
// outdated according to dependency tree, recursively
bool Builder::is_outdated(const fs::path& p) const
{
@@ -236,11 +263,11 @@ std::unordered_map<fs::path, std::vector<fs::path>> Builder::get_dependencies(co
{
std::unordered_map<fs::path, std::vector<fs::path>> dependencies;
- for (const auto& build: ptree.get_child("ymake")) {
- if (build.first == "build") {
- dependencies.emplace(get_target(build.second), get_objects(build.second));
+ for (const auto& element: ptree.get_child(topelement)) {
+ if (is_match(element, _target)) {
+ dependencies.emplace(get_target(element.second), get_objects(element.second));
- std::vector<fs::path> sources{get_sources(build.second)};
+ std::vector<fs::path> sources{get_sources(element.second)};
for (const auto& p: sources) {
fs::path p_obj{p};
p_obj.replace_extension("o");
@@ -266,7 +293,8 @@ void Builder::build_file(const fs::path& p) {
if (it == source_files.end()) {
throw std::runtime_error(fmt::format("No source file found for {}", p.string()));
}
- command = _lang.getCompileCommand(p, *it);
+ //std::cout << "DEBUG: " << (*_include_paths.begin()).first << " " << (*_include_paths.begin()).second.size() << " " << p.string() << std::endl;
+ command = _lang.getCompileCommand(p, *it, include_paths_of(p));
} else {
// link
std::vector<fs::path> objects{dependencies_of(p)};
@@ -278,7 +306,7 @@ void Builder::build_file(const fs::path& p) {
_runner.spawn(p.string(), command.c_str());
}
-void Builder::cleanup() {
+void Builder::cleanup_buildlist() {
std::string path;
int exit_code{_runner.wait_one(path)};
_activelist.erase(fs::path{path});
@@ -322,7 +350,7 @@ void Builder::build_filelist() {
if (current.empty()) {
std::this_thread::sleep_for(10ms); // short wait before retry
if (_runner.finished() > 0) {
- cleanup();
+ cleanup_buildlist();
}
}
}
@@ -332,7 +360,7 @@ void Builder::build_filelist() {
// wait until process slot is available
while (_runner.full() || _runner.finished() > 0) {
- cleanup();
+ cleanup_buildlist();
}
build_file(current); // calls spawn() on _runner
@@ -340,7 +368,7 @@ void Builder::build_filelist() {
// final cleanup
while (_runner.finished() != 0 || _runner.running() != 0) {
- cleanup();
+ cleanup_buildlist();
}
if (!_activelist.empty()) {
@@ -409,3 +437,15 @@ void Builder::clean() const {
}
}
+// build tests and run them
+void Builder::run_tests() {
+ for (const auto& i: _all_targets) {
+ std::string command{(fs::path("./") / i).string()};
+ std::cout << "Running test: " << command << std::endl;
+ int result {bp::system(command)};
+ if (result != 0) {
+ throw std::runtime_error("Test failed: " + i.string() + ", exit code: " + std::to_string(result));
+ }
+ }
+}
+