summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRoland Reichwein <mail@reichwein.it>2024-05-04 17:34:13 +0200
committerRoland Reichwein <mail@reichwein.it>2024-05-04 17:34:13 +0200
commit39c1729d8dcfffb0f72985411b8b489835d4fd5f (patch)
tree7530dd78c723d36da540d0a937bf7248f758978a
parent44479895f325cbbc283553dcb10b29a0af3b480b (diff)
Multitarget build
-rw-r--r--Builder.cpp133
-rw-r--r--Builder.h13
-rw-r--r--debian/README.Debian4
-rw-r--r--doc/Makefile.convenience5
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())};
diff --git a/Builder.h b/Builder.h
index 12338f0..2a7a6f4 100644
--- a/Builder.h
+++ b/Builder.h
@@ -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 $@