diff options
author | Roland Reichwein <mail@reichwein.it> | 2024-05-04 17:34:13 +0200 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2024-05-04 17:34:13 +0200 |
commit | 39c1729d8dcfffb0f72985411b8b489835d4fd5f (patch) | |
tree | 7530dd78c723d36da540d0a937bf7248f758978a | |
parent | 44479895f325cbbc283553dcb10b29a0af3b480b (diff) |
Multitarget build
-rw-r--r-- | Builder.cpp | 133 | ||||
-rw-r--r-- | Builder.h | 13 | ||||
-rw-r--r-- | debian/README.Debian | 4 | ||||
-rw-r--r-- | doc/Makefile.convenience | 5 |
4 files changed, 109 insertions, 46 deletions
diff --git a/Builder.cpp b/Builder.cpp index 58176d4..cbe6466 100644 --- a/Builder.cpp +++ b/Builder.cpp @@ -31,19 +31,56 @@ using namespace std::chrono_literals; using namespace std::string_literals; namespace { + // get name of single target, ptree is <build> subtree fs::path get_target(const pt::ptree& ptree) { - return ptree.get<std::string>("ymake.build.name"); + 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> 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")); + } + } + + return result; + } + + // get sources of single target, ptree is <build> subtree std::vector<fs::path> get_sources(const pt::ptree& ptree) { std::vector<fs::path> sources; - for (const pt::ptree::value_type &v: ptree.get_child("ymake.build")) { + for (const pt::ptree::value_type &v: ptree) { if (v.first == "source") sources.push_back(v.second.data()); } return sources; } + // ptree is main tree + std::vector<fs::path> get_all_sources(const pt::ptree& ptree) { + std::vector<fs::path> result; + + // iterate over all <build> elements + for (const auto& build: ptree.get_child("ymake")) { + if (build.first == "build") { + // iterate over all <source> elements + for (const auto& source: build.second) { + if (source.first == "source") { + result.push_back(source.second.data()); + } + } + } + } + + return result; + } + + // get objects for corresponding sources of single target, + // ptree is <build> subtree std::vector<fs::path> get_objects(const pt::ptree& ptree) { std::vector<fs::path> objects{get_sources(ptree)}; for (auto &i: objects) { @@ -52,6 +89,15 @@ namespace { return objects; } + // ptree is main tree + std::vector<fs::path> get_all_objects(const pt::ptree& ptree) { + std::vector<fs::path> objects{get_all_sources(ptree)}; + for (auto &i: objects) { + i.replace_extension("o"); + } + return objects; + } + // both need to exist bool is_older(const fs::path& p, const fs::path& other) { auto t_p{fs::last_write_time(p)}; @@ -79,7 +125,7 @@ namespace { if (parts.size() >= 2) { std::vector<std::string> deps {Reichwein::Stringhelper::split(parts[1], " ")}; std::vector<fs::path> result; - std::transform(deps.cbegin(), deps.cend(), std::back_inserter(result), [](const std::string& s){ return s; }); + std::copy(deps.cbegin(), deps.cend(), std::back_inserter(result)); return result; } else { throw std::runtime_error("Bad depfile contents: "s + path.string()); @@ -111,30 +157,37 @@ namespace { std::unordered_map<fs::path, std::vector<fs::path>> get_dependencies(const pt::ptree& ptree) { std::unordered_map<fs::path, std::vector<fs::path>> dependencies; - dependencies.emplace(get_target(ptree), get_objects(ptree)); - std::vector<fs::path> sources{get_sources(ptree)}; - for (const auto& p: sources) { - fs::path p_obj{p}; - p_obj.replace_extension("o"); - std::vector<fs::path> deps {make_depfile_from(p)}; - // keep .d files for now to speed dependencies detection on following runs - //fs::remove(depfile_name_from(p)); - dependencies.emplace(p_obj, deps); + + for (const auto& build: ptree.get_child("ymake")) { + if (build.first == "build") { + dependencies.emplace(get_target(build.second), get_objects(build.second)); + + std::vector<fs::path> sources{get_sources(build.second)}; + for (const auto& p: sources) { + fs::path p_obj{p}; + p_obj.replace_extension("o"); + std::vector<fs::path> deps {make_depfile_from(p)}; + // keep .d files for now to speed dependencies detection on following runs + //fs::remove(depfile_name_from(p)); + dependencies.emplace(p_obj, deps); + } + } } + return dependencies; } } Builder::Builder(const pt::ptree& ptree): - _target{get_target(ptree)}, - _objects{get_objects(ptree)}, - _sources{get_sources(ptree)}, + _all_targets{get_all_targets(ptree)}, + _all_objects{get_all_objects(ptree)}, _dependencies{get_dependencies(ptree)} { } -std::vector<fs::path> Builder::dependencies_of(const fs::path& p) { +std::vector<fs::path> Builder::dependencies_of(const fs::path& p) const +{ try { return _dependencies.at(p); } catch (const std::out_of_range& ex) { @@ -143,7 +196,8 @@ std::vector<fs::path> Builder::dependencies_of(const fs::path& p) { } // outdated according to dependency tree, recursively -bool Builder::is_outdated(const fs::path& p) { +bool Builder::is_outdated(const fs::path& p) const +{ if (!fs::exists(p)) return true; @@ -198,12 +252,12 @@ void Builder::cleanup() { // build list of files // eats up _buildlist -void Builder::build_list() { +void Builder::build_filelist() { if (_buildlist.empty()) { std::cout << "Everything up to date." << std::endl; } else { - std::cout << "Running commands: " << std::endl; + // std::cout << "Running commands: " << std::endl; } while (!_buildlist.empty()) { @@ -237,6 +291,7 @@ void Builder::build_list() { _buildlist.erase(current); _activelist.insert(current); + // wait until process slot is available while (_runner.full() || _runner.finished() > 0) { cleanup(); } @@ -256,17 +311,15 @@ void Builder::build_list() { // build everything according to specified configuration void Builder::build() { - std::cout << "Target: " << _target << std::endl; - - std::cout << "Sources: " << std::endl; - for (auto &i: _sources) { - std::cout << " " << i << std::endl; - } // create build list by depth-first search - std::cout << "Calculating build list..." << std::endl; + //std::cout << "Calculating build list..." << std::endl; std::stack<fs::path> container; // temporary container for search algorithm - container.push(_target); + + for (const auto& target: _all_targets) { + container.push(target); + } + while (!container.empty()) { fs::path current{container.top()}; container.pop(); @@ -281,30 +334,32 @@ void Builder::build() { } } - std::cout << "Build list:" << std::endl; - for (auto &i: _buildlist) { - std::cout << " " << i << std::endl; - } + //std::cout << "Build list:" << std::endl; + //for (auto &i: _buildlist) { + // std::cout << " " << i << std::endl; + //} - build_list(); + build_filelist(); } -void Builder::clean() { - std::vector<fs::path> cleanlist{_objects}; - cleanlist.push_back(_target); +void Builder::clean() const { + std::vector<fs::path> cleanlist{_all_objects}; + std::copy(_all_targets.cbegin(), _all_targets.cend(), std::back_inserter(cleanlist)); std::vector<std::string> commands; for (auto &i: cleanlist) { if (fs::exists(i)) { commands.push_back(fmt::format("rm -f {}", i.string())); } - fs::path depfile{depfile_name_from(i)}; - if (fs::exists(depfile)) { - commands.push_back(fmt::format("rm -f {}", depfile.string())); + if (i.extension() == ".o") { + fs::path depfile{depfile_name_from(i)}; + if (fs::exists(depfile)) { + commands.push_back(fmt::format("rm -f {}", depfile.string())); + } } } - std::cout << "Running commands: " << std::endl; + //std::cout << "Running commands: " << std::endl; for (auto &i: commands) { std::cout << i << std::endl; int result{system(i.c_str())}; @@ -15,20 +15,19 @@ public: Builder(const boost::property_tree::ptree& ptree); void build(); - void clean(); + void clean() const; private: - std::vector<std::filesystem::path> dependencies_of(const std::filesystem::path& p); - bool is_outdated(const std::filesystem::path& p); + std::vector<std::filesystem::path> dependencies_of(const std::filesystem::path& p) const; + bool is_outdated(const std::filesystem::path& p) const; void build_file(const std::filesystem::path& p); - void build_list(); + void build_filelist(); void cleanup(); - std::filesystem::path _target; - std::vector<std::filesystem::path> _objects; - std::vector<std::filesystem::path> _sources; + std::vector<std::filesystem::path> _all_targets; + std::vector<std::filesystem::path> _all_objects; std::unordered_map<std::filesystem::path, std::vector<std::filesystem::path>> _dependencies; ProcessRunner _runner; diff --git a/debian/README.Debian b/debian/README.Debian index cc7a1c9..5c569bd 100644 --- a/debian/README.Debian +++ b/debian/README.Debian @@ -11,6 +11,10 @@ Usage Run "ymake" like "make" to build programs with an YMakefile. +Run "ymake test" to build and run tests. + +Run "yscan" to create preliminary XMakefile for current directory. Check and adjust contents before use. + Contact ------- diff --git a/doc/Makefile.convenience b/doc/Makefile.convenience new file mode 100644 index 0000000..fb2f356 --- /dev/null +++ b/doc/Makefile.convenience @@ -0,0 +1,5 @@ +default: + ../ymake + +%: + ../ymake $@ |