diff options
-rw-r--r-- | Builder.cpp | 72 | ||||
-rw-r--r-- | Builder.h | 6 | ||||
-rw-r--r-- | LanguageSettings.cpp | 18 | ||||
-rw-r--r-- | file.cpp | 16 | ||||
-rw-r--r-- | file.h | 4 | ||||
-rw-r--r-- | test-ymake.cpp | 14 |
6 files changed, 94 insertions, 36 deletions
diff --git a/Builder.cpp b/Builder.cpp index cc756f2..4850be3 100644 --- a/Builder.cpp +++ b/Builder.cpp @@ -55,7 +55,13 @@ namespace { // iterate over all <build> elements for (const auto& element: ptree.get_child(topelement)) { if (is_match(element, target)) { - result.push_back(element.second.get<std::string>("name")); + fs::path target_name{element.second.get<std::string>("name")}; + result.push_back(target_name); + + // special case dynamic lib: add links automatically + if (is_dynamic_lib(target_name)) { + result.push_back(soname_short(target_name)); + } } } @@ -285,22 +291,33 @@ std::vector<fs::path> Builder::make_depfile_from(const fs::path& p) const return deps_from_depfile(depfile); } -std::unordered_map<fs::path, std::vector<fs::path>> Builder::get_dependencies(const pt::ptree& ptree) const +std::unordered_map<fs::path, std::vector<fs::path>> Builder::get_dependencies(const pt::ptree& ptree, bool include_sources) const { std::unordered_map<fs::path, std::vector<fs::path>> dependencies; 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(element.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); + // add target + fs::path target{get_target(element.second)}; + dependencies.emplace(target, get_objects(element.second)); + + // add dynamic lib links + if (is_dynamic_lib(target)) { + dependencies.emplace(soname_shorter(target), std::vector<fs::path>{{target}}); + dependencies.emplace(soname_short(target), std::vector<fs::path>{{soname_shorter(target)}}); + } + + if (include_sources) { + // add source dependencies of *.o + std::vector<fs::path> sources{get_sources(element.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); + } } } } @@ -397,9 +414,8 @@ void Builder::build_filelist() { } } -// build everything according to specified configuration -void Builder::build() { - _dependencies = get_dependencies(_ptree); +std::unordered_set<fs::path> Builder::get_buildlist(std::function<bool(const fs::path&)> outdated_pred) { + std::unordered_set<fs::path> result; // create build list by depth-first search //std::cout << "Calculating build list..." << std::endl; @@ -418,11 +434,20 @@ void Builder::build() { container.push(i); } - if (is_outdated(current) && is_buildable_by_extension(current)) { - _buildlist.insert(current); + if (outdated_pred(current) && is_buildable_by_extension(current)) { + result.insert(current); } } + return result; +} + +// build everything according to specified configuration +void Builder::build() { + _dependencies = get_dependencies(_ptree); + + _buildlist = get_buildlist([&](const fs::path& i)->bool{return is_outdated(i);}); + //std::cout << "Build list:" << std::endl; //for (auto &i: _buildlist) { // std::cout << " " << i << std::endl; @@ -431,9 +456,16 @@ void Builder::build() { build_filelist(); } -void Builder::clean() const { - std::vector<fs::path> cleanlist{_all_objects}; - std::copy(_all_targets.cbegin(), _all_targets.cend(), std::back_inserter(cleanlist)); +void Builder::clean() { + _dependencies = get_dependencies(_ptree, false); + std::unordered_set<fs::path> cleanlist{get_buildlist([](const fs::path& i)->bool{ return true;})}; + + 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(add.begin(), add.end(), std::inserter(cleanlist, cleanlist.begin())); std::vector<std::string> commands; for (auto &i: cleanlist) { @@ -1,6 +1,7 @@ #pragma once #include <filesystem> +#include <functional> #include <string> #include <unordered_map> #include <unordered_set> @@ -19,10 +20,10 @@ public: void build(); void run_tests(); - void clean() const; + void clean(); private: - std::unordered_map<std::filesystem::path, std::vector<std::filesystem::path>> get_dependencies(const boost::property_tree::ptree& ptree) const; + std::unordered_map<std::filesystem::path, std::vector<std::filesystem::path>> get_dependencies(const boost::property_tree::ptree& ptree, bool include_sources = true) const; std::vector<std::filesystem::path> dependencies_of(const std::filesystem::path& p) const; std::vector<std::filesystem::path> include_paths_of_object(const std::filesystem::path& p) const; std::filesystem::path target_from_object(const std::filesystem::path& object) const; @@ -31,6 +32,7 @@ private: bool is_outdated(const std::filesystem::path& p, const std::vector<std::filesystem::path> &dependencies) const; std::vector<std::filesystem::path> make_depfile_from(const std::filesystem::path& p) const; std::filesystem::path get_compile_unit_source_from_object(const std::filesystem::path& path); + std::unordered_set<std::filesystem::path> get_buildlist(std::function<bool(const std::filesystem::path&)> outdated_pred); void build_file(const std::filesystem::path& p); void build_filelist(); diff --git a/LanguageSettings.cpp b/LanguageSettings.cpp index 8da144f..5eae731 100644 --- a/LanguageSettings.cpp +++ b/LanguageSettings.cpp @@ -74,7 +74,6 @@ std::string LanguageSettings::getLinkCommand(const std::filesystem::path& target const std::vector<std::filesystem::path> &inputs, const std::vector<std::string>& link_libs) const { - std::string input_string{std::accumulate(inputs.begin(), inputs.end(), std::string{}, [](const std::string& sum, const fs::path& p){ return sum + " " + p.string(); })}; @@ -84,11 +83,10 @@ std::string LanguageSettings::getLinkCommand(const std::filesystem::path& target AR, target.string(), input_string); - } else { + } else if (is_executable_target(target) || is_dynamic_lib(target)) { // dynamic link: -shared -Wl,-soname,libXXX.so.N -o libXXX.so.N.M.O - std::string LDFLAGS_add{is_dynamic_lib(target) ? fmt::format(" -shared -Wl,-soname,{} -o {}", - soname_shorter(target.string()).string(), - target.string()) : ""s}; + std::string LDFLAGS_add{is_dynamic_lib(target) ? fmt::format(" -shared -Wl,-soname,{}", + soname_shorter(target.string()).string()) : ""s}; std::string link_libs_string{std::accumulate(link_libs.begin(), link_libs.end(), std::string(), [](const std::string& sum, const std::string& i){ return sum + " -l" + i; })}; @@ -103,6 +101,14 @@ std::string LanguageSettings::getLinkCommand(const std::filesystem::path& target LIBS.empty() ? ""s : (" "s + LIBS), link_libs_string, target.string()); + } else if (is_dynamic_lib_link(target)) { + // special case for dynamic libs: add links + if (inputs.size() != 1) { + throw std::runtime_error("For symlink "s + target.string() + ", can link to exactly one file, found "s + std::to_string(inputs.size())); + } + return fmt::format("ln -sf {} {}", inputs[0].filename().string(), target.string()); + } else { + throw std::runtime_error("No implementation for target type: "s + target.string()); } } @@ -116,8 +122,6 @@ std::string LanguageSettings::getDepCommand(const std::filesystem::path& target, // variables: // CC // CFLAGS -// AR // ARFLAGS // AS // ASFLAGS -// dynamic lib: @@ -16,7 +16,7 @@ const fs::path YMakefile{"YMakefile"}; // type of file can be built from dependencies bool is_buildable_by_extension(const fs::path& p) { fs::path ext{p.extension()}; - return ext.empty() || ext == ".o" || is_dynamic_lib(p) || is_static_lib(p) ; + return ext.empty() || ext == ".o" || is_dynamic_lib(p) || is_dynamic_lib_link(p) || is_static_lib(p) ; } namespace { @@ -29,6 +29,11 @@ bool is_compile_unit_source_by_extension(const fs::path& p) { return compile_unit_source_types.find(ext) != compile_unit_source_types.end(); } +bool is_executable_target(const std::filesystem::path& p) +{ + return p.extension().empty(); +} + // e.g. libxyz.so.1.2.3 (the shorter versions are links to this one) bool is_dynamic_lib(const std::filesystem::path& p) { @@ -36,6 +41,13 @@ bool is_dynamic_lib(const std::filesystem::path& p) return name.find(".so.") != name.npos && std::count(name.begin(), name.end(), '.') == 4; } +// e.g. libxyz.so.1 libxyz.so +bool is_dynamic_lib_link(const std::filesystem::path& p) +{ + std::string name{p.filename()}; + return name.find(".so") != name.npos && std::count(name.begin(), name.end(), '.') < 4; +} + // in: libxyz.so.1.2.3 // out: libxyz.so.1 std::filesystem::path soname_shorter(const std::filesystem::path& p) @@ -95,7 +107,7 @@ std::filesystem::path simplified_path(const std::filesystem::path& p) return p; } -bool is_executable(const std::string& file) { +bool is_executable_command(const std::string& file) { std::vector<std::string> paths {Reichwein::Stringhelper::split(env_value("PATH"), ":")}; for (const auto& i: paths) { @@ -9,7 +9,9 @@ extern const std::filesystem::path YMakefile; bool is_buildable_by_extension(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); 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); std::filesystem::path soname_shorter(const std::filesystem::path& p); std::filesystem::path soname_short(const std::filesystem::path& p); @@ -18,5 +20,5 @@ std::filesystem::path soname_short(const std::filesystem::path& p); std::filesystem::path simplified_path(const std::filesystem::path& p); std::string env_value(const std::string& key); -bool is_executable(const std::string& file); +bool is_executable_command(const std::string& file); std::string find_executable(const std::vector<std::string>& list); diff --git a/test-ymake.cpp b/test-ymake.cpp index c2dd78e..bf59bbc 100644 --- a/test-ymake.cpp +++ b/test-ymake.cpp @@ -433,7 +433,7 @@ TEST_F(ymakeTest, test_only) TEST_F(ymakeTest, build_with_gcc) { - if (!is_executable("g++")) { + if (!is_executable_command("g++")) { GTEST_SKIP() << "g++ not available"; } @@ -463,7 +463,7 @@ TEST_F(ymakeTest, build_with_gcc) TEST_F(ymakeTest, build_with_clang) { - if (!is_executable("clang++")) { + if (!is_executable_command("clang++")) { GTEST_SKIP() << "clang++ not available"; } @@ -629,12 +629,14 @@ TEST_F(ymakeTest, build_one_dynamic_lib) int result = run_command("../ymake", output); EXPECT_EQ(result, 0); - ASSERT_EQ(output.size(), 3); // 2x compile, link + ASSERT_EQ(output.size(), 5); // 2x compile, link, 2 symlinks EXPECT_EQ(output[2].substr(0, 4), "g++ "); EXPECT_TRUE(fs::exists("hello.d")); EXPECT_TRUE(fs::exists("hello.o")); EXPECT_TRUE(fs::exists("hello.so.1.2.3")); + EXPECT_TRUE(fs::exists("hello.so.1")); + EXPECT_TRUE(fs::exists("hello.so")); EXPECT_TRUE(fs::exists("second.d")); EXPECT_TRUE(fs::exists("second.o")); @@ -645,11 +647,13 @@ TEST_F(ymakeTest, build_one_dynamic_lib) result = run_command("../ymake clean", output); EXPECT_EQ(result, 0); - ASSERT_EQ(output.size(), 5); // deleted 5 files + ASSERT_EQ(output.size(), 7); // deleted 7 files EXPECT_TRUE(!fs::exists("hello.d")); EXPECT_TRUE(!fs::exists("hello.o")); EXPECT_TRUE(!fs::exists("hello.so.1.2.3")); + EXPECT_TRUE(!fs::exists("hello.so.1")); + EXPECT_TRUE(!fs::exists("hello.so")); EXPECT_TRUE(!fs::exists("second.d")); EXPECT_TRUE(!fs::exists("second.o")); @@ -662,6 +666,8 @@ TEST_F(ymakeTest, build_one_dynamic_lib) // TODO: // use static lib // use dynamic lib +// use static lib from subdir +// use dynamic lib from subdir TEST_F(yscanTest, no_cpp_file) { |