diff options
| author | Roland Reichwein <mail@reichwein.it> | 2024-06-15 14:24:14 +0200 | 
|---|---|---|
| committer | Roland Reichwein <mail@reichwein.it> | 2024-06-15 14:24:14 +0200 | 
| commit | ec955eb6726b31e26d17c34474619b60f3563194 (patch) | |
| tree | 3ce74889185f0b2a3a9a13d67ccf9b3812479e83 | |
| parent | d9e360bb95d4fced4974bb716f993c81626417cb (diff) | |
| -rw-r--r-- | Builder.cpp | 15 | ||||
| -rw-r--r-- | MakefileReader.cpp | 14 | ||||
| -rw-r--r-- | file.cpp | 16 | ||||
| -rw-r--r-- | file.h | 4 | ||||
| -rw-r--r-- | test-ymake.cpp | 85 | 
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;  } @@ -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) @@ -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;  | 
