summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRoland Reichwein <mail@reichwein.it>2024-05-05 17:15:49 +0200
committerRoland Reichwein <mail@reichwein.it>2024-05-05 17:15:49 +0200
commit81bcfee54362c736e865ebeb638aeadfa9cc9e24 (patch)
tree4da9b8f2f233dacf4c0da72e1f26b55181a58be3
parent28609f436966f731f91e84d10c1d7d0621b4abe8 (diff)
Factored out LanguageSettings
-rw-r--r--Builder.cpp147
-rw-r--r--Builder.h7
-rw-r--r--LanguageSettings.cpp104
-rw-r--r--LanguageSettings.h26
-rw-r--r--Makefile2
-rw-r--r--test-ymake.cpp1
-rw-r--r--ymake.cpp4
7 files changed, 233 insertions, 58 deletions
diff --git a/Builder.cpp b/Builder.cpp
index 51ac90f..86f6920 100644
--- a/Builder.cpp
+++ b/Builder.cpp
@@ -60,6 +60,16 @@ namespace {
return sources;
}
+ // get link libs of single target, ptree is <build> subtree
+ std::vector<std::string> get_link_libs_of_build(const pt::ptree& ptree) {
+ std::vector<std::string> linklibs;
+ for (const pt::ptree::value_type &v: ptree) {
+ if (v.first == "linklib")
+ linklibs.push_back(v.second.data());
+ }
+ return linklibs;
+ }
+
// ptree is main tree
std::vector<fs::path> get_all_sources(const pt::ptree& ptree) {
std::vector<fs::path> result;
@@ -105,20 +115,6 @@ namespace {
return t_p < t_other;
}
- // outdated according to dependency file list, non-recursively
- bool is_outdated(const fs::path& p, const std::vector<fs::path> &dependencies) {
- if (!fs::exists(p))
- return true;
-
- for (const auto& dep: dependencies) {
- if (!fs::exists(dep) || is_older(p, dep)) {
- return true;
- }
- }
-
- return false;
- }
-
std::vector<fs::path> deps_from_depfile(const fs::path& path) {
std::string depfile_content{Reichwein::File::getFile(path)};
std::vector<std::string> parts {Reichwein::Stringhelper::split(depfile_content, ":\r\n")};
@@ -138,43 +134,19 @@ namespace {
return depfile;
}
- // return contained dependencies
- // input: cpp
- std::vector<fs::path> make_depfile_from(const fs::path& p) {
- fs::path depfile{depfile_name_from(p)};
-
- // check if depfile exists and if it contains up to date info
- if (!fs::exists(depfile) || is_outdated(depfile, deps_from_depfile(depfile))) {
- // actually create depfile
- int result{system(fmt::format("g++ -MM -MF {} -c {}", depfile.string(), p.string()).c_str())};
- if (result != 0) {
- throw std::runtime_error(fmt::format("Depfile {} can't be created", depfile.string()));
- }
- }
-
- return deps_from_depfile(depfile);
- }
-
- 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;
+ std::unordered_map<fs::path, std::vector<std::string>> get_link_libs(const pt::ptree& ptree) {
+ std::unordered_map<fs::path, std::vector<std::string>> link_libs_map;
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);
+ std::vector<std::string> link_libs{get_link_libs_of_build(build.second)};
+ if (!link_libs.empty()) {
+ link_libs_map.emplace(get_target(build.second), link_libs);
}
}
}
- return dependencies;
+ return link_libs_map;
}
}
@@ -182,7 +154,8 @@ namespace {
Builder::Builder(const pt::ptree& ptree):
_ptree(ptree),
_all_targets{get_all_targets(ptree)},
- _all_objects{get_all_objects(ptree)}
+ _all_objects{get_all_objects(ptree)},
+ _link_libs{get_link_libs(ptree)}
{
// intentionally defer creation of _dependencies to build()
// to prevent creation of .d files in clean()
@@ -197,6 +170,15 @@ std::vector<fs::path> Builder::dependencies_of(const fs::path& p) const
}
}
+std::vector<std::string> Builder::link_libs_of(const fs::path& p) const
+{
+ try {
+ return _link_libs.at(p);
+ } catch (const std::out_of_range& ex) {
+ return {}; // empty by default
+ }
+}
+
// outdated according to dependency tree, recursively
bool Builder::is_outdated(const fs::path& p) const
{
@@ -217,24 +199,79 @@ bool Builder::is_outdated(const fs::path& p) const
return false;
}
+// outdated according to dependency file list, non-recursively
+bool Builder::is_outdated(const fs::path& p, const std::vector<fs::path> &dependencies) const
+{
+ if (!fs::exists(p))
+ return true;
+
+ for (const auto& dep: dependencies) {
+ if (!fs::exists(dep) || is_older(p, dep)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// return contained dependencies
+// input: cpp
+std::vector<fs::path> Builder::make_depfile_from(const fs::path& p) const
+{
+ fs::path depfile{depfile_name_from(p)};
+
+ // check if depfile exists and if it contains up to date info
+ if (!fs::exists(depfile) || is_outdated(depfile, deps_from_depfile(depfile))) {
+ // actually create depfile
+ int result{system(_lang.getDepCommand(depfile.string(), p.string()).c_str())};
+ if (result != 0) {
+ throw std::runtime_error(fmt::format("Depfile {} can't be created", depfile.string()));
+ }
+ }
+
+ 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>> dependencies;
+
+ 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;
+}
+
// build 1 file
void Builder::build_file(const fs::path& p) {
std::string command;
if (p.extension() == ".o") {
- // compile
- fs::path cppfile{p};
- cppfile.replace_extension("cpp");
- command = fmt::format("g++ -std=c++17 -c {} -o {}", cppfile.string(), p.string());
+ // compile
+ std::vector<fs::path> source_files{dependencies_of(p)};
+ auto it{std::find_if(source_files.begin(), source_files.end(), is_compile_unit_source_by_extension)};
+ if (it == source_files.end()) {
+ throw std::runtime_error(fmt::format("No source file found for {}", p.string()));
+ }
+ command = _lang.getCompileCommand(p, *it);
} else {
// link
- command = "g++";
std::vector<fs::path> objects{dependencies_of(p)};
- for (auto &i: objects) {
- command += fmt::format(" {}", i.string());
- }
- command += " -lreichwein -lfmt";
- command += fmt::format(" -o {}", p.string());
+ std::vector<std::string> link_libs{link_libs_of(p)};
+ command = _lang.getLinkCommand(p, objects, link_libs);
}
std::cout << command << std::endl;
diff --git a/Builder.h b/Builder.h
index 52fc17a..3169c3b 100644
--- a/Builder.h
+++ b/Builder.h
@@ -7,6 +7,7 @@
#include <boost/property_tree/ptree.hpp>
+#include "LanguageSettings.h"
#include "ProcessRunner.h"
class Builder
@@ -18,8 +19,12 @@ public:
void clean() const;
private:
+ std::unordered_map<std::filesystem::path, std::vector<std::filesystem::path>> get_dependencies(const boost::property_tree::ptree& ptree) const;
std::vector<std::filesystem::path> dependencies_of(const std::filesystem::path& p) const;
+ std::vector<std::string> link_libs_of(const std::filesystem::path& p) const;
bool is_outdated(const std::filesystem::path& p) const;
+ 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;
void build_file(const std::filesystem::path& p);
void build_filelist();
@@ -30,7 +35,9 @@ private:
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;
+ std::unordered_map<std::filesystem::path, std::vector<std::string>> _link_libs;
+ LanguageSettings _lang;
ProcessRunner _runner;
std::unordered_set<std::filesystem::path> _buildlist; // build done
diff --git a/LanguageSettings.cpp b/LanguageSettings.cpp
new file mode 100644
index 0000000..3ded5cf
--- /dev/null
+++ b/LanguageSettings.cpp
@@ -0,0 +1,104 @@
+#include "LanguageSettings.h"
+
+#include <cstdlib>
+#include <iostream>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include <fmt/format.h>
+
+#include <libreichwein/stringhelper.h>
+
+namespace fs = std::filesystem;
+
+namespace {
+ std::string env_value(const std::string& key) {
+ char* value_ptr{std::getenv(key.c_str())};
+ if (value_ptr == nullptr) {
+ return {};
+ } else {
+ return value_ptr;
+ }
+ }
+
+ // returns "" if not found
+ std::string find_executable(const std::vector<std::string>& list) {
+ std::vector<std::string> paths {Reichwein::Stringhelper::split(env_value("PATH"), ":")};
+
+ for (const auto& i: list) {
+ for (const auto& j: paths) {
+ if (fs::exists(j + "/" + i)) {
+ return i;
+ }
+ }
+ }
+
+ return {};
+ }
+
+}
+
+LanguageSettings::LanguageSettings():
+ // C++
+ CXX{env_value("CXX")},
+ CXXFLAGS{env_value("CXXFLAGS")},
+ LDFLAGS{env_value("LDFLAGS")},
+ LDLIBS{env_value("LDLIBS")},
+ LIBS{env_value("LIBS")}
+ // C
+ // CFLAGS
+{
+ std::string _default_compiler(find_executable({"g++", "clang++"}));
+
+ if (_default_compiler.empty()) {
+ throw std::runtime_error("No default C++ compiler found.");
+ }
+
+ // set defaults
+ if (CXX.empty()) {
+ CXX = _default_compiler;
+ }
+
+ if (CXXFLAGS.empty()) {
+ CXXFLAGS = "-std=c++17";
+ }
+}
+
+std::string LanguageSettings::getCompileCommand(const std::filesystem::path& target, const std::filesystem::path &source) const
+{
+ // compile: $(CXX) $(CXXFLAGS) -c $< -o $@
+ return fmt::format("{} {} -c {} -o {}", CXX, CXXFLAGS, source.string(), target.string());
+}
+
+std::string LanguageSettings::getLinkCommand(const std::filesystem::path& target,
+ const std::vector<std::filesystem::path> &inputs,
+ const std::vector<std::string>& link_libs) const
+{
+ // link: $(CXX) $(LDFLAGS) $^ $(LDLIBS) $(LIBS) -o $@
+
+ std::string input_string;
+ for (const auto& i: inputs) {
+ if (!input_string.empty()) {
+ input_string += " ";
+ }
+ input_string += i.string();
+ }
+
+ std::string link_libs_string;
+ for (const auto& i: link_libs) {
+ if (!link_libs_string.empty()) {
+ link_libs_string += " ";
+ }
+ link_libs_string += "-l" + i;
+ }
+
+ return fmt::format("{} {} {} {} {} {} -o {}", CXX, CXXFLAGS, input_string, LDLIBS, LIBS, link_libs_string, target.string());
+}
+
+std::string LanguageSettings::getDepCommand(const std::filesystem::path& target, const std::filesystem::path &source) const
+{
+ // g++ -MM -MF <depfile> -c <cppfile>
+ return fmt::format("{} -MM -MF {} -c {}", CXX, target.string(), source.string());
+}
+
diff --git a/LanguageSettings.h b/LanguageSettings.h
new file mode 100644
index 0000000..97519e3
--- /dev/null
+++ b/LanguageSettings.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <filesystem>
+#include <string>
+#include <vector>
+
+class LanguageSettings
+{
+public:
+ LanguageSettings();
+
+ std::string getCompileCommand(const std::filesystem::path& target, const std::filesystem::path &source) const;
+ std::string getLinkCommand(const std::filesystem::path& target,
+ const std::vector<std::filesystem::path> &inputs,
+ const std::vector<std::string>& link_libs) const;
+ std::string getDepCommand(const std::filesystem::path& target, const std::filesystem::path &source) const;
+
+private:
+ std::string CXX;
+ std::string CXXFLAGS;
+
+ std::string LDFLAGS;
+ std::string LDLIBS;
+ std::string LIBS;
+};
+
diff --git a/Makefile b/Makefile
index 961bc37..c74723e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
PROJECTNAME=ymake
-SRC=main.cpp ymake.cpp Builder.cpp ProcessRunner.cpp file.cpp
+SRC=main.cpp ymake.cpp Builder.cpp ProcessRunner.cpp file.cpp LanguageSettings.cpp
OBJ=$(SRC:.cpp=.o)
YSCAN=yscan
diff --git a/test-ymake.cpp b/test-ymake.cpp
index fcd424f..a6edc5f 100644
--- a/test-ymake.cpp
+++ b/test-ymake.cpp
@@ -197,6 +197,7 @@ TEST_F(ymakeTest, build_three_cpp)
// TODO: test .c .cc .cpp
// TODO: multiple builds
// TODO: test tests
+// TODO: test g++/clang++
TEST_F(yscanTest, no_cpp_file)
{
diff --git a/ymake.cpp b/ymake.cpp
index 62ddeb1..4747555 100644
--- a/ymake.cpp
+++ b/ymake.cpp
@@ -51,7 +51,7 @@ int ymake(int argc, char* argv[])
action = argv[1];
} else {
std::cerr << "Invalid arguments." << std::endl;
- usage;
+ usage();
exit(1);
}
@@ -68,7 +68,7 @@ int ymake(int argc, char* argv[])
throw std::runtime_error("unimplemented");
} else {
std::cerr << "Invalid action: " << action << std::endl;
- usage;
+ usage();
exit(1);
}