summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRoland Reichwein <mail@reichwein.it>2024-06-15 14:24:14 +0200
committerRoland Reichwein <mail@reichwein.it>2024-06-15 14:24:14 +0200
commitec955eb6726b31e26d17c34474619b60f3563194 (patch)
tree3ce74889185f0b2a3a9a13d67ccf9b3812479e83
parentd9e360bb95d4fced4974bb716f993c81626417cb (diff)
Automatically depend on YMakefile (rebuild if necessary), testsHEADmaster
-rw-r--r--Builder.cpp15
-rw-r--r--MakefileReader.cpp14
-rw-r--r--file.cpp16
-rw-r--r--file.h4
-rw-r--r--test-ymake.cpp85
5 files changed, 123 insertions, 11 deletions
diff --git a/Builder.cpp b/Builder.cpp
index 69ecf48..d64ba27 100644
--- a/Builder.cpp
+++ b/Builder.cpp
@@ -326,6 +326,13 @@ std::unordered_map<fs::path, std::vector<fs::path>> Builder::get_dependencies(co
}
}
+ // add dependencies to YMakefiles
+ for (auto& i: dependencies) {
+ if (is_buildable_by_name(i.first) && fs::exists(i.first.parent_path() / YMakefile)) {
+ i.second.push_back(i.first.parent_path() / YMakefile);
+ }
+ }
+
return dependencies;
}
@@ -339,7 +346,9 @@ void Builder::build_file(const fs::path& p) {
command = _lang.getCompileCommand(p, source_file, target_from_object(p), include_paths_of_object(p));
} else {
// link
- std::vector<fs::path> objects{dependencies_of(p)};
+ std::vector<fs::path> deps{dependencies_of(p)};
+ std::vector<fs::path> objects;
+ std::copy_if(deps.begin(), deps.end(), std::back_inserter(objects), is_linkable);
std::vector<fs::path> link_libs{link_libs_of(p)};
command = _lang.getLinkCommand(p, objects, link_libs);
}
@@ -438,7 +447,7 @@ std::unordered_set<fs::path> Builder::get_buildlist(std::function<bool(const fs:
container.push(i);
}
- if (outdated_pred(current) && is_buildable_by_extension(current)) {
+ if (outdated_pred(current) && is_buildable_by_name(current)) {
result.insert(current);
}
}
@@ -467,7 +476,7 @@ void Builder::clean() {
std::unordered_set<fs::path> add;
for (const auto& i: cleanlist) {
std::vector<fs::path> deps{dependencies_of(i)};
- std::copy(deps.begin(), deps.end(), std::inserter(add, add.begin()));
+ std::copy_if(deps.begin(), deps.end(), std::inserter(add, add.begin()), is_buildable_by_name);
}
std::copy(add.begin(), add.end(), std::inserter(cleanlist, cleanlist.begin()));
diff --git a/MakefileReader.cpp b/MakefileReader.cpp
index ebcc0a8..2a5fa98 100644
--- a/MakefileReader.cpp
+++ b/MakefileReader.cpp
@@ -10,6 +10,7 @@ namespace fs = std::filesystem;
namespace {
// the elements with this name are considered paths, where relative prefixes will be added if necessary
std::vector<std::string> path_elements{"name", "source", "test"};
+ const std::string topelement{"ymake"};
// add path to respective elements in tree, recursively
void adjust_path(pt::ptree::value_type& element, const fs::path& path) {
@@ -30,8 +31,8 @@ namespace {
tree = makefile_tree; // including top level single "ymake" element
} else {
// copy all elements inside single "ymake"
- pt::ptree& target{tree.get_child("ymake")};
- const pt::ptree& source{makefile_tree.get_child("ymake")};
+ pt::ptree& target{tree.get_child(topelement)};
+ const pt::ptree& source{makefile_tree.get_child(topelement)};
for (const auto& i: source) {
auto it {target.push_back(i)};
auto &element{*it};
@@ -49,16 +50,25 @@ pt::ptree MakefileReader::read(const fs::path& path) const
{
pt::ptree ptree;
+ // YMakefile needs to exist, but it can be in a subdir, so we need to
+ // iterate first over directories
+ bool makefile_found{};
+
for (const fs::directory_entry& dir_entry: fs::recursive_directory_iterator(path)) {
pt::ptree makefile_tree;
fs::path path{dir_entry.path()};
if (dir_entry.is_regular_file() && path.filename() == YMakefile) {
+ makefile_found = true;
pt::read_xml(path, makefile_tree, pt::xml_parser::no_comments | pt::xml_parser::trim_whitespace);
join(ptree, makefile_tree, simplified_path(path.parent_path()));
}
}
+ if (!makefile_found) {
+ throw std::runtime_error("YMakefile not found");
+ }
+
return ptree;
}
diff --git a/file.cpp b/file.cpp
index 6211471..2284cb3 100644
--- a/file.cpp
+++ b/file.cpp
@@ -14,9 +14,9 @@ using namespace std::string_literals;
const fs::path YMakefile{"YMakefile"};
// type of file can be built from dependencies
-bool is_buildable_by_extension(const fs::path& p) {
+bool is_buildable_by_name(const fs::path& p) {
fs::path ext{p.extension()};
- return ext.empty() || ext == ".o" || is_dynamic_lib(p) || is_dynamic_lib_link(p) || is_static_lib(p) ;
+ return (ext.empty() || ext == ".o" || is_dynamic_lib(p) || is_dynamic_lib_link(p) || is_static_lib(p)) && p.filename() != YMakefile;
}
namespace {
@@ -48,6 +48,16 @@ bool is_dynamic_lib_link(const std::filesystem::path& p)
return name.find(".so") != name.npos && std::count(name.begin(), name.end(), '.') < 4;
}
+bool is_lib(const std::filesystem::path& p)
+{
+ return is_dynamic_lib(p) || is_static_lib(p) || is_dynamic_lib_link(p) || is_external_lib(p);
+}
+
+bool is_linkable(const std::filesystem::path& p)
+{
+ return is_lib(p) || p.extension() == ".o";
+}
+
// in: libxyz.so.1.2.3
// out: libxyz.so.1
std::filesystem::path soname_shorter(const std::filesystem::path& p)
@@ -119,7 +129,7 @@ bool is_static_lib(const std::filesystem::path& p)
bool is_external_lib(const std::filesystem::path& p)
{
- return !(is_static_lib(p) || is_dynamic_lib(p) || is_dynamic_lib_link(p));
+ return !(is_static_lib(p) || is_dynamic_lib(p) || is_dynamic_lib_link(p)) && p.filename() != YMakefile;
}
std::filesystem::path simplified_path(const std::filesystem::path& p)
diff --git a/file.h b/file.h
index d9a117b..c2d1797 100644
--- a/file.h
+++ b/file.h
@@ -6,7 +6,7 @@
extern const std::filesystem::path YMakefile;
-bool is_buildable_by_extension(const std::filesystem::path& p);
+bool is_buildable_by_name(const std::filesystem::path& p);
bool is_compile_unit_source_by_extension(const std::filesystem::path& p);
bool is_executable_target(const std::filesystem::path& p);
@@ -14,6 +14,8 @@ bool is_dynamic_lib(const std::filesystem::path& p);
bool is_dynamic_lib_link(const std::filesystem::path& p);
bool is_static_lib(const std::filesystem::path& p);
bool is_external_lib(const std::filesystem::path& p);
+bool is_lib(const std::filesystem::path& p);
+bool is_linkable(const std::filesystem::path& p);
std::filesystem::path soname_shorter(const std::filesystem::path& p);
std::filesystem::path soname_short(const std::filesystem::path& p);
std::string dynamic_lib_name(const std::filesystem::path& p);
diff --git a/test-ymake.cpp b/test-ymake.cpp
index 9d6cd43..973e827 100644
--- a/test-ymake.cpp
+++ b/test-ymake.cpp
@@ -171,11 +171,16 @@ TEST_F(ymakeTest, clean_one_cpp)
TEST_F(ymakeTest, YMakefile_missing)
{
- int result = run_command("../ymake");
+ std::vector<std::string> output;
+ int result = run_command("../ymake", output);
EXPECT_NE(result, 0);
+ ASSERT_EQ(output.size(), 1);
+ EXPECT_EQ(output[0], "ymake: YMakefile not found");
- result = run_command("../ymake clean");
+ result = run_command("../ymake clean", output);
EXPECT_NE(result, 0);
+ ASSERT_EQ(output.size(), 1);
+ EXPECT_EQ(output[0], "ymake: YMakefile not found");
}
TEST_F(ymakeTest, build_three_cpp)
@@ -229,6 +234,41 @@ TEST_F(ymakeTest, build_three_cpp)
EXPECT_TRUE(!fs::exists("hello"));
}
+TEST_F(ymakeTest, rebuild)
+{
+ create_file("hello.cpp", R"(int main(int argc, char* argv[])
+{
+ return 0;
+})");
+ create_file("second.cpp", R"(
+#include "second.h"
+)");
+ create_file("second.h", R"(
+#pragma once
+)");
+
+ create_file("YMakefile", R"(
+<ymake>
+ <build>
+ <name>hello</name>
+ <source>hello.cpp</source>
+ <source>second.cpp</source>
+ </build>
+</ymake>
+)");
+
+ std::vector<std::string> output;
+ int result = run_command("../ymake", output);
+ EXPECT_EQ(result, 0);
+
+ EXPECT_EQ(output.size(), 3); // compile 2, link 1
+
+ run_command("touch hello.cpp");
+ result = run_command("../ymake", output);
+ EXPECT_EQ(result, 0);
+ EXPECT_EQ(output.size(), 2); // compile 1, link 1
+}
+
TEST_F(ymakeTest, build_cpp_c_cc)
{
create_file("hello.cpp", R"(int main(int argc, char* argv[])
@@ -1181,6 +1221,47 @@ __attribute__((visibility("default"))) int hello();
EXPECT_TRUE(!fs::exists("runmain"));
}
+TEST_F(ymakeTest, dependencies_on_YMakefile)
+{
+ create_file("hello.cpp", R"(int main(int argc, char* argv[])
+{
+ return 0;
+})");
+
+ create_file("YMakefile", R"(
+<ymake>
+ <build>
+ <name>hello</name>
+ <source>hello.cpp</source>
+ </build>
+</ymake>
+)");
+
+ std::vector<std::string> output;
+ int result = run_command("../ymake", output);
+ EXPECT_EQ(result, 0);
+
+ EXPECT_EQ(output.size(), 2); // compile, link
+
+ result = run_command("./hello", output);
+ EXPECT_EQ(result, 0);
+ EXPECT_EQ(output.size(), 0); // run
+
+ result = run_command("../ymake", output);
+ EXPECT_EQ(result, 0);
+ EXPECT_EQ(output.size(), 1); // "Everything up to date"
+ EXPECT_EQ(output[0], "Everything up to date.");
+
+ result = run_command("touch YMakefile");
+ EXPECT_EQ(result, 0);
+
+ result = run_command("../ymake", output);
+ EXPECT_EQ(result, 0);
+
+ EXPECT_EQ(output.size(), 2); // compile, link
+}
+
+
TEST_F(yscanTest, no_cpp_file)
{
std::string output;