summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRoland Reichwein <mail@reichwein.it>2024-05-12 16:07:53 +0200
committerRoland Reichwein <mail@reichwein.it>2024-05-12 16:07:53 +0200
commit8a4dfbbbe76a2aef35427b7915d6e28bab165c43 (patch)
tree5df3fe373ca755d51d8b06b7515ca3c296f54cc6
parenta7e016c2c633667b561a0f26ebde88cb26571d1c (diff)
Build static and dynamic libs
-rw-r--r--Builder.cpp72
-rw-r--r--Builder.h6
-rw-r--r--LanguageSettings.cpp18
-rw-r--r--file.cpp16
-rw-r--r--file.h4
-rw-r--r--test-ymake.cpp14
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) {
diff --git a/Builder.h b/Builder.h
index 42c5af4..bd11599 100644
--- a/Builder.h
+++ b/Builder.h
@@ -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:
diff --git a/file.cpp b/file.cpp
index 6de8b07..31695e5 100644
--- a/file.cpp
+++ b/file.cpp
@@ -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) {
diff --git a/file.h b/file.h
index ba3dfdd..a49825c 100644
--- a/file.h
+++ b/file.h
@@ -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)
{