diff options
53 files changed, 3817 insertions, 1118 deletions
diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..c95b0eb --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,56 @@ +pipeline { + agent { label 'c++20' } + + stages { + stage('Clean') { + steps { + echo 'Cleaning workspace ...' + sh 'rm -Rf build*' + } + } + stage('Build-gcc') { + steps { + echo 'Building (gcc) ...' + sh 'BUILDDIR=build-gcc CXX=g++ ./bootstrap.sh' + } + } + stage('Test-gcc') { + steps { + echo 'Testing (gcc) ...' + sh './ctor check' + } + } + stage('Test-suite-gcc') { + steps { + echo 'Testing suite (gcc) ...' + sh '(cd test/suite; CTORDIR=../../build-gcc CXX=g++ ./test.sh)' + } + } + stage('Build-clang') { + steps { + echo 'Building (clang) ...' + sh 'BUILDDIR=build-clang CXX=clang++ ./bootstrap.sh' + } + } + stage('Test-clang') { + steps { + echo 'Testing (clang) ...' + sh './ctor check' + } + } + stage('Test-suite-clang') { + steps { + echo 'Testing suite (clang) ...' + sh '(cd test/suite; CTORDIR=../../build-clang CXX=clang++ ./test.sh)' + } + } + } + + post { + always { + xunit(thresholds: [ skipped(failureThreshold: '0'), + failed(failureThreshold: '0') ], + tools: [ CppUnit(pattern: 'build-*/test/*.xml') ]) + } + } +} @@ -16,7 +16,7 @@ Step 1: Create a build configuration, in C++ A really simple example ('hello_config.cc'): ```c++ -#include "libctor.h" +#include "ctor.h" namespace { diff --git a/bootstrap.sh b/bootstrap.sh index a5c11ac..9e8cdca 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,7 +1,13 @@ #!/bin/sh +set -e + +: ${CXX:=g++} +: ${BUILDDIR:=build} +CXX=$(which $CXX) + echo "Bootstrapping..." -g++ -std=c++20 -Wall -O3 -Isrc -pthread src/bootstrap.cc ctor.cc test/ctor.cc -o ctor && \ -./ctor && \ -g++ -std=c++20 -Wall -O3 -Isrc -pthread ctor.cc test/ctor.cc -Lbuild -lctor -o ctor && \ -./ctor configure --ctor-includedir=src --ctor-libdir=build && \ +$CXX -std=c++20 -Wall -O3 -Isrc -pthread src/bootstrap.cc ctor.cc test/ctor.cc -o ctor +./ctor +$CXX -std=c++20 -Wall -O3 -Isrc -pthread ctor.cc test/ctor.cc -L$BUILDDIR -lctor -o ctor +./ctor configure --ctor-includedir=src --ctor-libdir=$BUILDDIR --build-dir=$BUILDDIR echo "Done. Now run ./ctor to (re)build." @@ -1,16 +1,16 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h> namespace { -BuildConfigurations ctorConfigs(const Settings& settings) +ctor::build_configurations ctorConfigs(const ctor::settings& settings) { return { { - .type = TargetType::StaticLibrary, + .system = ctor::output_system::build, .target = "libctor.a", .sources = { "src/build.cc", @@ -26,6 +26,7 @@ BuildConfigurations ctorConfigs(const Settings& settings) "src/task_ld.cc", "src/task_so.cc", "src/tasks.cc", + "src/tools.cc", "src/util.cc", "src/unittest.cc", }, diff --git a/examples/cppbuild.cc b/examples/cppbuild.cc index eafac8d..9e1014f 100644 --- a/examples/cppbuild.cc +++ b/examples/cppbuild.cc @@ -1,4 +1,4 @@ -#include "libctor.h" +#include "ctor.h" namespace { diff --git a/examples/ctor.cc b/examples/ctor.cc index 1a02e90..6ed8c6c 100644 --- a/examples/ctor.cc +++ b/examples/ctor.cc @@ -1,7 +1,7 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include "libctor.h" +#include "ctor.h" namespace { diff --git a/examples/subdir/ctor.cc b/examples/subdir/ctor.cc index b5f5885..2e3de9f 100644 --- a/examples/subdir/ctor.cc +++ b/examples/subdir/ctor.cc @@ -1,7 +1,7 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include "../libctor.h" +#include "../ctor.h" namespace { diff --git a/src/bootstrap.cc b/src/bootstrap.cc index 6e4ef19..55bd3e0 100644 --- a/src/bootstrap.cc +++ b/src/bootstrap.cc @@ -3,10 +3,11 @@ // See accompanying file LICENSE for details. #include <iostream> #include <array> +#include <cstdlib> #define BOOTSTRAP -#include "libctor.h" +#include "ctor.h" #include "util.cc" #include "rebuild.cc" @@ -16,25 +17,62 @@ #include "execute.cc" #include "tasks.cc" #include "build.cc" +#include "tools.cc" std::filesystem::path configurationFile("configuration.cc"); std::filesystem::path configHeaderFile("config.h"); -const Configuration default_configuration{}; -const Configuration& configuration() +const ctor::configuration& ctor::get_configuration() { - return default_configuration; + static ctor::configuration cfg; + static bool initialised{false}; + if(!initialised) + { + cfg.build_toolchain = getToolChain(cfg.get(ctor::cfg::build_cxx, "/usr/bin/g++")); + initialised = true; + } + + return cfg; } -bool hasConfiguration(const std::string& key) +bool ctor::configuration::has(const std::string& key) const { return false; } -const std::string& getConfiguration(const std::string& key, - const std::string& defaultValue) +const std::string& ctor::configuration::get(const std::string& key, const std::string& default_value) const { - return defaultValue; + if(key == ctor::cfg::build_cxx && std::getenv("CXX")) + { + static std::string s = std::getenv("CXX"); + return s; + } + + if(key == ctor::cfg::build_cc && std::getenv("CC")) + { + static std::string s = std::getenv("CC"); + return s; + } + + if(key == ctor::cfg::build_ld && std::getenv("LD")) + { + static std::string s = std::getenv("LD"); + return s; + } + + if(key == ctor::cfg::build_ar && std::getenv("AR")) + { + static std::string s = std::getenv("AR"); + return s; + } + + if(key == ctor::cfg::builddir && std::getenv("BUILDDIR")) + { + static std::string s = std::getenv("BUILDDIR"); + return s; + } + + return default_value; } int main(int argc, char* argv[]) @@ -46,9 +84,10 @@ int main(int argc, char* argv[]) return 1; } - Settings settings{}; + ctor::settings settings{}; - settings.builddir = getConfiguration(cfg::builddir, "build"); + const auto& c = ctor::get_configuration(); + settings.builddir = c.get(ctor::cfg::builddir, settings.builddir); settings.parallel_processes = std::max(1u, std::thread::hardware_concurrency() * 2 - 1); settings.verbose = 0; @@ -65,8 +104,8 @@ int main(int argc, char* argv[]) auto& targets = getTargets(settings); for(const auto& target : targets) { - if(target.config.type != TargetType::UnitTest && - target.config.type != TargetType::UnitTestLib) + if(target.config.type != ctor::target_type::unit_test && + target.config.type != ctor::target_type::unit_test_library) { non_unittest_targets.push_back(target); } diff --git a/src/build.cc b/src/build.cc index 081c156..ad30719 100644 --- a/src/build.cc +++ b/src/build.cc @@ -11,11 +11,11 @@ #include <thread> #include <list> -#include "libctor.h" +#include "ctor.h" using namespace std::chrono_literals; -int build(const Settings& settings, +int build(const ctor::settings& settings, const std::string& name, const std::set<std::shared_ptr<Task>>& tasks, const std::set<std::shared_ptr<Task>>& all_tasks, @@ -84,7 +84,9 @@ int build(const Settings& settings, return task->run(); })); started_one = true; - std::this_thread::sleep_for(2ms); + // Make sure we don't start tasks on top of each other to prevent + // straining the disk. + std::this_thread::sleep_for(50ms); } for(auto process = processes.begin(); @@ -106,13 +108,9 @@ int build(const Settings& settings, break; } - if(started_one) + if(!started_one) // prevent polling too fast if no task is yet ready { - std::this_thread::sleep_for(2ms); - } - else - { - std::this_thread::sleep_for(200ms); + std::this_thread::sleep_for(10ms); } } @@ -156,7 +154,7 @@ std::set<std::shared_ptr<Task>> getDepTasks(std::shared_ptr<Task> task) } } -int build(const Settings& settings, +int build(const ctor::settings& settings, const std::string& name, const std::set<std::shared_ptr<Task>>& all_tasks, bool dryrun) @@ -194,7 +192,7 @@ int build(const Settings& settings, return 0; } -int build(const Settings& settings, +int build(const ctor::settings& settings, const std::string& name, const std::vector<Target>& targets, const std::set<std::shared_ptr<Task>>& all_tasks, diff --git a/src/build.h b/src/build.h index caf7a68..d74642c 100644 --- a/src/build.h +++ b/src/build.h @@ -10,23 +10,25 @@ #include "task.h" #include "tasks.h" -struct Settings; +namespace ctor { +struct settings; +} // namespace ctor:: //! Dry-run returns number of dirty tasks but otherwise does nothing. -int build(const Settings& settings, +int build(const ctor::settings& settings, const std::string& name, const std::set<std::shared_ptr<Task>>& tasks, const std::set<std::shared_ptr<Task>>& all_tasks, bool dryrun = false); //! Dry-run returns number of dirty tasks but otherwise does nothing. -int build(const Settings& settings, +int build(const ctor::settings& settings, const std::string& name, const std::set<std::shared_ptr<Task>>& all_tasks, bool dryrun = false); //! Dry-run returns number of dirty tasks but otherwise does nothing. -int build(const Settings& settings, +int build(const ctor::settings& settings, const std::string& name, const std::vector<Target>& targets, const std::set<std::shared_ptr<Task>>& all_tasks, diff --git a/src/configure.cc b/src/configure.cc index bc7d3e8..c08ed88 100644 --- a/src/configure.cc +++ b/src/configure.cc @@ -11,10 +11,12 @@ #include <getoptpp/getoptpp.hpp> #include "execute.h" -#include "libctor.h" +#include "ctor.h" #include "tasks.h" #include "rebuild.h" #include "externals.h" +#include "tools.h" +#include "util.h" std::filesystem::path configurationFile("configuration.cc"); std::filesystem::path configHeaderFile("config.h"); @@ -22,105 +24,78 @@ std::filesystem::path configHeaderFile("config.h"); std::map<std::string, std::string> external_includedir; std::map<std::string, std::string> external_libdir; -const Configuration default_configuration{}; -const Configuration& __attribute__((weak)) configuration() +const ctor::configuration& __attribute__((weak)) ctor::get_configuration() { - return default_configuration; + static ctor::configuration cfg; + static bool initialised{false}; + if(!initialised) + { + cfg.build_toolchain = getToolChain(cfg.get(ctor::cfg::build_cxx, "/usr/bin/g++")); + initialised = true; + } + return cfg; } -namespace ctor -{ +namespace ctor { std::optional<std::string> includedir; std::optional<std::string> libdir; -} +std::optional<std::string> builddir; +std::map<std::string, std::string> conf_values; +} // ctor:: -bool hasConfiguration(const std::string& key) +bool ctor::configuration::has(const std::string& key) const { - if(key == cfg::ctor_includedir && ctor::includedir) + if(key == ctor::cfg::ctor_includedir && ctor::includedir) { return true; } - if(key == cfg::ctor_libdir && ctor::libdir) + if(key == ctor::cfg::ctor_libdir && ctor::libdir) { return true; } - const auto& c = configuration(); - return c.tools.find(key) != c.tools.end(); + if(key == ctor::cfg::builddir && ctor::builddir) + { + return true; + } + + if(ctor::conf_values.find(key) != ctor::conf_values.end()) + { + return true; + } + + return tools.find(key) != tools.end(); } -const std::string& getConfiguration(const std::string& key, - const std::string& defaultValue) +const std::string& ctor::configuration::get(const std::string& key, const std::string& default_value) const { - if(key == cfg::ctor_includedir && ctor::includedir) + if(key == ctor::cfg::ctor_includedir && ctor::includedir) { return *ctor::includedir; } - if(key == cfg::ctor_libdir && ctor::libdir) + if(key == ctor::cfg::ctor_libdir && ctor::libdir) { return *ctor::libdir; } - const auto& c = configuration(); - if(hasConfiguration(key)) + if(key == ctor::cfg::builddir && ctor::builddir) { - return c.tools.at(key); + return *ctor::builddir; } - return defaultValue; -} - -std::string locate(const std::string& arch, const std::string& app) -{ - std::string path_env = std::getenv("PATH"); - //std::cout << path_env << "\n"; - - std::string program = app; - if(!arch.empty()) + if(ctor::conf_values.find(key) != ctor::conf_values.end()) { - program = arch + "-" + app; + return ctor::conf_values[key]; } - std::cout << "Looking for: " << program << "\n"; - std::vector<std::string> paths; + if(has(key)) { - std::stringstream ss(path_env); - std::string path; - while (std::getline(ss, path, ':')) - { - paths.push_back(path); - } - } - for(const auto& path_str : paths) - { - std::filesystem::path path(path_str); - auto prog_path = path / program; - if(std::filesystem::exists(prog_path)) - { - std::cout << "Found file " << app << " in path: " << path << "\n"; - auto perms = std::filesystem::status(prog_path).permissions(); - if((perms & std::filesystem::perms::owner_exec) != std::filesystem::perms::none) - { - //std::cout << " - executable by owner\n"; - } - if((perms & std::filesystem::perms::group_exec) != std::filesystem::perms::none) - { - //std::cout << " - executable by group\n"; - } - if((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none) - { - //std::cout << " - executable by others\n"; - } - - return prog_path.string(); - } + return tools.at(key); } - std::cerr << "Could not locate " << app << " for the " << arch << " architecture\n"; - exit(1); - return {}; + return default_value; } class Args @@ -146,22 +121,100 @@ public: } }; +namespace { +std::ostream& operator<<(std::ostream& stream, const ctor::toolchain& toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::any: + stream << "ctor::toolchain::any"; + break; + case ctor::toolchain::none: + stream << "ctor::toolchain::none"; + break; + case ctor::toolchain::gcc: + stream << "ctor::toolchain::gcc"; + break; + case ctor::toolchain::clang: + stream << "ctor::toolchain::clang"; + break; + } + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::arch& arch) +{ + switch(arch) + { + case ctor::arch::unix: + stream << "ctor::arch::unix"; + break; + case ctor::arch::apple: + stream << "ctor::arch::apple"; + break; + case ctor::arch::windows: + stream << "ctor::arch::windows"; + break; + case ctor::arch::unknown: + stream << "ctor::arch::unknown"; + break; + } + return stream; +} + +std::ostream& operator<<(std::ostream& ostr, const ctor::c_flag& flag) +{ + for(const auto& s : to_strings(ctor::toolchain::any, flag)) + { + ostr << s; + } + return ostr; +} + +std::ostream& operator<<(std::ostream& ostr, const ctor::cxx_flag& flag) +{ + for(const auto& s : to_strings(ctor::toolchain::any, flag)) + { + ostr << s; + } + return ostr; +} + +std::ostream& operator<<(std::ostream& ostr, const ctor::ld_flag& flag) +{ + for(const auto& s : to_strings(ctor::toolchain::any, flag)) + { + ostr << s; + } + return ostr; +} + +std::ostream& operator<<(std::ostream& ostr, const ctor::asm_flag& flag) +{ + for(const auto& s : to_strings(ctor::toolchain::any, flag)) + { + ostr << s; + } + return ostr; +} +} + // helper constant for the visitor template<class> inline constexpr bool always_false_v = false; -int regenerateCache(const Settings& default_settings, +int regenerateCache(ctor::settings& settings, + const std::string& name, const std::vector<std::string>& args, const std::map<std::string, std::string>& env) { - Settings settings{default_settings}; Args vargs(args); dg::Options opt; int key{128}; - std::string build_arch; + std::string build_arch_prefix; std::string build_path; - std::string host_arch; + std::string host_arch_prefix; std::string host_path; std::string cc_prog = "gcc"; std::string cxx_prog = "g++"; @@ -169,12 +222,14 @@ int regenerateCache(const Settings& default_settings, std::string ld_prog = "ld"; std::string ctor_includedir; std::string ctor_libdir; + std::string builddir; opt.add("build-dir", required_argument, 'b', "Set output directory for build files (default: '" + settings.builddir + "').", [&]() { settings.builddir = optarg; + builddir = optarg; return 0; }); @@ -216,7 +271,7 @@ int regenerateCache(const Settings& default_settings, opt.add("build", required_argument, key++, "Configure for building on specified architecture.", [&]() { - build_arch = optarg; + build_arch_prefix = optarg; return 0; }); @@ -230,7 +285,7 @@ int regenerateCache(const Settings& default_settings, opt.add("host", required_argument, key++, "Cross-compile to build programs to run on specified architecture.", [&]() { - host_arch = optarg; + host_arch_prefix = optarg; return 0; }); @@ -256,7 +311,7 @@ int regenerateCache(const Settings& default_settings, }); // Resolv externals - ExternalConfigurations externalConfigs; + ctor::external_configurations externalConfigs; for(std::size_t i = 0; i < numExternalConfigFiles; ++i) { auto newExternalConfigs = externalConfigFiles[i].cb(settings); @@ -288,7 +343,7 @@ int regenerateCache(const Settings& default_settings, std::visit([&](auto&& arg) { using T = std::decay_t<decltype(arg)>; - if constexpr (std::is_same_v<T, ExternalManual>) + if constexpr (std::is_same_v<T, ctor::external_manual>) { add_path_args(ext.name); } @@ -303,7 +358,9 @@ int regenerateCache(const Settings& default_settings, opt.add("help", no_argument, 'h', "Print this help text.", [&]() { - std::cout << "configure usage stuff\n"; + std::cout << "Configure how to build with " << name << "\n"; + std::cout << "Usage: " << name << " configure [options]\n\n"; + std::cout << "Options:\n"; opt.help(); exit(0); return 0; @@ -311,37 +368,107 @@ int regenerateCache(const Settings& default_settings, opt.process(vargs.size(), vargs.data()); - if(host_arch.empty()) + if(host_arch_prefix.empty()) { - host_arch = build_arch; + host_arch_prefix = build_arch_prefix; } auto tasks = getTasks(settings, {}, false); -/* - bool needs_cpp{false}; - bool needs_c{false}; - bool needs_ar{false}; - bool needs_asm{false}; + + bool needs_build{true}; // we always need to compile ctor itself + bool needs_build_c{false}; + bool needs_build_cxx{true}; // we always need to compile ctor itself + bool needs_build_ld{true}; // we always need to compile ctor itself + bool needs_build_ar{false}; + bool needs_build_asm{false}; + + bool needs_host_c{false}; + bool needs_host{false}; + bool needs_host_cxx{false}; + bool needs_host_ld{false}; + bool needs_host_ar{false}; + bool needs_host_asm{false}; + for(const auto& task :tasks) { - switch(task->sourceLanguage()) + switch(task->outputSystem()) { - case Language::Auto: - std::cerr << "TargetLanguage not deduced!\n"; - exit(1); - break; - case Language::C: - needs_cpp = false; - break; - case Language::Cpp: - needs_c = true; + case ctor::output_system::build: + needs_build = true; + switch(task->targetType()) + { + case ctor::target_type::executable: + case ctor::target_type::unit_test: + case ctor::target_type::dynamic_library: + needs_build_ld = true; + break; + case ctor::target_type::static_library: + case ctor::target_type::unit_test_library: + needs_build_ar = true; + break; + case ctor::target_type::object: + switch(task->sourceLanguage()) + { + case ctor::language::automatic: + std::cerr << "TargetLanguage not deduced!\n"; + exit(1); + break; + case ctor::language::c: + needs_build_c = true; + break; + case ctor::language::cpp: + needs_build_cxx = true; + break; + case ctor::language::assembler: + needs_build_asm = true; + break; + } + break; + case ctor::target_type::function: + case ctor::target_type::automatic: + case ctor::target_type::unknown: + break; + } break; - case Language::Asm: - needs_asm = true; + case ctor::output_system::host: + needs_host = true; + switch(task->targetType()) + { + case ctor::target_type::executable: + case ctor::target_type::unit_test: + case ctor::target_type::dynamic_library: + needs_host_ld = true; + break; + case ctor::target_type::static_library: + case ctor::target_type::unit_test_library: + needs_host_ar = true; + break; + case ctor::target_type::object: + switch(task->sourceLanguage()) + { + case ctor::language::automatic: + std::cerr << "TargetLanguage not deduced!\n"; + exit(1); + break; + case ctor::language::c: + needs_host_c = true; + break; + case ctor::language::cpp: + needs_host_cxx = true; + break; + case ctor::language::assembler: + needs_host_asm = true; + break; + } + break; + case ctor::target_type::function: + case ctor::target_type::automatic: + case ctor::target_type::unknown: + break; + } break; } } -*/ auto cc_env = env.find("CC"); if(cc_env != env.end()) @@ -367,54 +494,248 @@ int regenerateCache(const Settings& default_settings, ld_prog = ld_env->second; } - std::string host_cc = locate(host_arch, cc_prog); - std::string host_cxx = locate(host_arch, cxx_prog); - std::string host_ar = locate(host_arch, ar_prog); - std::string host_ld = locate(host_arch, ld_prog); - std::string build_cc = locate(build_arch, cc_prog); - std::string build_cxx = locate(build_arch, cxx_prog); - std::string build_ar = locate(build_arch, ar_prog); - std::string build_ld = locate(build_arch, ld_prog); + auto paths = get_paths(); + + auto path_env = env.find("PATH"); + if(path_env != env.end()) + { + paths = get_paths(path_env->second); + } + + std::string host_cc; + std::string host_cxx; + std::string host_ld; + std::string host_ar; + ctor::toolchain host_toolchain{ctor::toolchain::none}; + ctor::arch host_arch{ctor::arch::unknown}; + if(needs_host) + { + // Host detection + if(needs_host_c) + { + host_cc = locate(cc_prog, paths, host_arch_prefix); + if(host_cc.empty()) + { + std::cerr << "Could not locate host_cc prog" << std::endl; + return 1; + } + } + + if(needs_host_cxx) + { + host_cxx = locate(cxx_prog, paths, host_arch_prefix); + if(host_cxx.empty()) + { + std::cerr << "Could not locate host_cxx prog" << std::endl; + return 1; + } + } + + if(needs_host_ar) + { + host_ar = locate(ar_prog, paths, host_arch_prefix); + if(host_ar.empty()) + { + std::cerr << "Could not locate host_ar prog" << std::endl; + return 1; + } + } + + if(needs_host_ld) + { + host_ld = locate(ld_prog, paths, host_arch_prefix); + if(host_ld.empty()) + { + std::cerr << "Could not locate host_ld prog" << std::endl; + return 1; + } + } + + if(needs_host_asm) + { + // TODO + } + + host_toolchain = getToolChain(host_cxx); + auto host_arch_str = get_arch(ctor::output_system::host); + host_arch = get_arch(ctor::output_system::host, host_arch_str); + + std::cout << "** Host architecture '" << host_arch_str << "': " << host_arch << std::endl; + + if(host_arch == ctor::arch::unknown) + { + std::cerr << "Could not detect host architecture" << std::endl; + return 1; + } + } + + std::string build_cc; + std::string build_cxx; + std::string build_ld; + std::string build_ar; + ctor::toolchain build_toolchain{ctor::toolchain::none}; + ctor::arch build_arch{ctor::arch::unknown}; + if(needs_build) + { + // Build detection + if(needs_build_c) + { + build_cc = locate(cc_prog, paths, build_arch_prefix); + if(build_cc.empty()) + { + std::cerr << "Could not locate build_cc prog" << std::endl; + return 1; + } + } + + if(needs_build_cxx) + { + build_cxx = locate(cxx_prog, paths, build_arch_prefix); + if(build_cxx.empty()) + { + std::cerr << "Could not locate build_cxx prog" << std::endl; + return 1; + } + } + + if(needs_build_ar) + { + build_ar = locate(ar_prog, paths, build_arch_prefix); + if(build_ar.empty()) + { + std::cerr << "Could not locate build_ar prog" << std::endl; + return 1; + } + } + + if(needs_build_ld) + { + build_ld = locate(ld_prog, paths, build_arch_prefix); + if(build_ld.empty()) + { + std::cerr << "Could not locate build_ld prog" << std::endl; + return 1; + } + } + + if(needs_build_asm) + { + // TODO + } + + build_toolchain = getToolChain(build_cxx); + auto build_arch_str = get_arch(ctor::output_system::build); + build_arch = get_arch(ctor::output_system::build, build_arch_str); + + std::cout << "** Build architecture '" << build_arch_str << "': " << build_arch << std::endl; + + if(build_arch == ctor::arch::unknown) + { + std::cerr << "Could not detect build architecture" << std::endl; + return 1; + } + } + + + // Store current values for execution in this execution context. + if(!ctor_includedir.empty()) + { + ctor::conf_values[ctor::cfg::ctor_includedir] = ctor_includedir; + } + if(!ctor_libdir.empty()) + { + ctor::conf_values[ctor::cfg::ctor_libdir] = ctor_libdir; + } + if(!builddir.empty()) + { + ctor::conf_values[ctor::cfg::builddir] = builddir; + } + ctor::conf_values[ctor::cfg::host_cxx] = host_cxx; + ctor::conf_values[ctor::cfg::build_cxx] = build_cxx; std::cout << "Writing results to: " << configurationFile.string() << "\n"; { std::ofstream istr(configurationFile); - istr << "#include <libctor.h>\n\n"; - istr << "const Configuration& configuration()\n"; + istr << "#include <ctor.h>\n\n"; + istr << "const ctor::configuration& ctor::get_configuration()\n"; istr << "{\n"; - istr << " static Configuration cfg =\n"; + istr << " static ctor::configuration cfg =\n"; istr << " {\n"; + if(needs_host) + { + istr << " .host_toolchain = " << host_toolchain << ",\n"; + istr << " .host_arch = " << host_arch << ",\n"; + } + if(needs_build) + { + istr << " .build_toolchain = " << build_toolchain << ",\n"; + istr << " .build_arch = " << build_arch << ",\n"; + } istr << " .args = {"; for(const auto& arg : args) { - istr << "\"" << arg << "\","; + istr << "\"" << esc(arg) << "\","; } istr << "},\n"; - istr << " .env = {"; + istr << " .env = {\n"; for(const auto& e : env) { - istr << "{\"" << e.first << "\", \"" << e.second << "\"}, "; + istr << " {\"" << esc(e.first) << "\", \"" << esc(e.second) << "\"},\n"; } - istr << "},\n"; + istr << " },\n"; istr << " .tools = {\n"; - istr << " { \"" << cfg::builddir << "\", \"" << settings.builddir << "\" },\n"; - istr << " { \"" << cfg::host_cc << "\", \"" << host_cc << "\" },\n"; - istr << " { \"" << cfg::host_cxx << "\", \"" << host_cxx << "\" },\n"; - istr << " { \"" << cfg::host_ar << "\", \"" << host_ar << "\" },\n"; - istr << " { \"" << cfg::host_ld << "\", \"" << host_ld << "\" },\n"; - istr << " { \"" << cfg::build_cc << "\", \"" << build_cc << "\" },\n"; - istr << " { \"" << cfg::build_cxx << "\", \"" << build_cxx << "\" },\n"; - istr << " { \"" << cfg::build_ar << "\", \"" << build_ar << "\" },\n"; - istr << " { \"" << cfg::build_ld << "\", \"" << build_ld << "\" },\n"; + if(!builddir.empty()) + { + istr << " { \"" << ctor::cfg::builddir << "\", \"" << esc(builddir) << "\" },\n"; + ctor::builddir = builddir; + } + if(needs_host) + { + if(needs_host_c) + { + istr << " { \"" << ctor::cfg::host_cc << "\", \"" << esc(host_cc) << "\" },\n"; + } + if(needs_host_cxx) + { + istr << " { \"" << ctor::cfg::host_cxx << "\", \"" << esc(host_cxx) << "\" },\n"; + } + if(needs_host_ar) + { + istr << " { \"" << ctor::cfg::host_ar << "\", \"" << esc(host_ar) << "\" },\n"; + } + if(needs_host_ld) + { + istr << " { \"" << ctor::cfg::host_ld << "\", \"" << esc(host_ld) << "\" },\n"; + } + } + if(needs_build) + { + if(needs_build_c) + { + istr << " { \"" << ctor::cfg::build_cc << "\", \"" << esc(build_cc) << "\" },\n"; + } + if(needs_build_cxx) + { + istr << " { \"" << ctor::cfg::build_cxx << "\", \"" << esc(build_cxx) << "\" },\n"; + } + if(needs_build_ar) + { + istr << " { \"" << ctor::cfg::build_ar << "\", \"" << esc(build_ar) << "\" },\n"; + } + if(needs_build_ld) + { + istr << " { \"" << ctor::cfg::build_ld << "\", \"" << esc(build_ld) << "\" },\n"; + } + } if(!ctor_includedir.empty()) { - istr << " { \"" << cfg::ctor_includedir << "\", \"" << ctor_includedir << "\" },\n"; + istr << " { \"" << ctor::cfg::ctor_includedir << "\", \"" << esc(ctor_includedir) << "\" },\n"; ctor::includedir = ctor_includedir; } if(!ctor_libdir.empty()) { - istr << " { \"" << cfg::ctor_libdir << "\", \"" << ctor_libdir << "\" },\n"; + istr << " { \"" << ctor::cfg::ctor_libdir << "\", \"" << esc(ctor_libdir) << "\" },\n"; ctor::libdir = ctor_libdir; } @@ -423,12 +744,12 @@ int regenerateCache(const Settings& default_settings, for(const auto& ext : externalConfigs) { - istr << " { \"" << ext.name << "\", {\n"; - Flags resolved_flags; - if(std::holds_alternative<ExternalManual>(ext.external)) + istr << " { \"" << esc(ext.name) << "\", {\n"; + ctor::flags resolved_flags; + if(std::holds_alternative<ctor::external_manual>(ext.external)) { - if(auto ret = resolv(settings, ext.name, - std::get<ExternalManual>(ext.external), + if(auto ret = resolv(settings, ext, + std::get<ctor::external_manual>(ext.external), resolved_flags)) { return ret; @@ -440,22 +761,22 @@ int regenerateCache(const Settings& default_settings, return 1; } - if(!resolved_flags.cxxflags.empty()) + if(!resolved_flags.cflags.empty()) { - istr << " .cxxflags = {"; - for(const auto& flag : resolved_flags.cxxflags) + istr << " .cflags = {"; + for(const auto& flag : resolved_flags.cflags) { - istr << "\"" << flag << "\","; + istr << flag << ","; } istr << "},\n"; } - if(!resolved_flags.cflags.empty()) + if(!resolved_flags.cxxflags.empty()) { - istr << " .cflags = {"; - for(const auto& flag : resolved_flags.cflags) + istr << " .cxxflags = {"; + for(const auto& flag : resolved_flags.cxxflags) { - istr << "\"" << flag << "\","; + istr << flag << ","; } istr << "},\n"; } @@ -465,7 +786,7 @@ int regenerateCache(const Settings& default_settings, istr << " .ldflags = {"; for(const auto& flag : resolved_flags.ldflags) { - istr << "\"" << flag << "\","; + istr << flag << ","; } istr << "},\n"; } @@ -475,7 +796,7 @@ int regenerateCache(const Settings& default_settings, istr << " .asmflags = {"; for(const auto& flag : resolved_flags.asmflags) { - istr << "\"" << flag << "\","; + istr << flag << ","; } istr << "},\n"; } @@ -485,7 +806,7 @@ int regenerateCache(const Settings& default_settings, istr << " },\n"; istr << " };\n"; istr << " return cfg;\n"; - istr << "}\n\n"; + istr << "}\n"; } { @@ -498,9 +819,9 @@ int regenerateCache(const Settings& default_settings, return 0; } -int configure(const Settings& global_settings, int argc, char* argv[]) +int configure(const ctor::settings& global_settings, int argc, char* argv[]) { - Settings settings{global_settings}; + ctor::settings settings{global_settings}; std::vector<std::string> args; for(int i = 2; i < argc; ++i) // skip command and the first 'configure' arg @@ -533,7 +854,13 @@ int configure(const Settings& global_settings, int argc, char* argv[]) env["LD"] = ld_env; } - auto ret = regenerateCache(settings, args, env); + auto path_env = getenv("PATH"); + if(path_env) + { + env["PATH"] = path_env; + } + + auto ret = regenerateCache(settings, argv[0], args, env); if(ret != 0) { return ret; @@ -544,8 +871,10 @@ int configure(const Settings& global_settings, int argc, char* argv[]) return 0; } -int reconfigure(const Settings& settings, int argc, char* argv[]) +int reconfigure(const ctor::settings& global_settings, int argc, char* argv[]) { + ctor::settings settings{global_settings}; + bool no_rerun{false}; std::vector<std::string> args; @@ -559,7 +888,7 @@ int reconfigure(const Settings& settings, int argc, char* argv[]) args.push_back(argv[i]); } - const auto& cfg = configuration(); + const auto& cfg = ctor::get_configuration(); std::cout << "Re-running configure:\n"; for(const auto& e : cfg.env) @@ -573,7 +902,7 @@ int reconfigure(const Settings& settings, int argc, char* argv[]) } std::cout << "\n"; - auto ret = regenerateCache(settings, cfg.args, cfg.env); + auto ret = regenerateCache(settings, argv[0], cfg.args, cfg.env); if(ret != 0) { return ret; diff --git a/src/configure.cc.bak b/src/configure.cc.bak deleted file mode 100644 index bcbeea9..0000000 --- a/src/configure.cc.bak +++ /dev/null @@ -1,387 +0,0 @@ -// -*- c++ -*- -// Distributed under the BSD 2-Clause License. -// See accompanying file LICENSE for details. -#include "configure.h" - -#include <iostream> -#include <filesystem> -#include <fstream> - -#include <getoptpp/getoptpp.hpp> - -#include "settings.h" -#include "execute.h" -#include "libcppbuild.h" -#include "tasks.h" - -std::filesystem::path configurationFile("configuration.cc"); -std::filesystem::path configHeaderFile("config.h"); - -const std::map<std::string, std::string> default_configuration{}; -const std::map<std::string, std::string>& __attribute__((weak)) configuration() -{ - return default_configuration; -} - -bool hasConfiguration(const std::string& key) -{ - const auto& c = configuration(); - return c.find(key) != c.end(); -} - -const std::string& getConfiguration(const std::string& key, - const std::string& defaultValue) -{ - const auto& c = configuration(); - if(hasConfiguration(key)) - { - return c.at(key); - } - - return defaultValue; -} - - -/* -Configuration: - -h, --help display this help and exit - --help=short display options specific to this package - --help=recursive display the short help of all the included packages - -V, --version display version information and exit - -q, --quiet, --silent do not print `checking ...' messages - --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for `--cache-file=config.cache' - -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or `..'] - -Installation directories: - --prefix=PREFIX install architecture-independent files in PREFIX - [/usr/local] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] - -By default, `make install' will install all the files in -`/usr/local/bin', `/usr/local/lib' etc. You can specify -an installation prefix other than `/usr/local' using `--prefix', -for instance `--prefix=$HOME'. - -For better control, use the options below. - -Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/drumgizmo] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] - -Program names: - --program-prefix=PREFIX prepend PREFIX to installed program names - --program-suffix=SUFFIX append SUFFIX to installed program names - --program-transform-name=PROGRAM run sed PROGRAM on installed program names - -System types: - --build=BUILD configure for building on BUILD [guessed] - --host=HOST cross-compile to build programs to run on HOST [BUILD] - -Optional Features: - --disable-option-checking ignore unrecognized --enable/--with options - --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) - --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --enable-silent-rules less verbose build output (undo: "make V=1") - --disable-silent-rules verbose build output (undo: "make V=0") - --enable-dependency-tracking - do not reject slow dependency extractors - --disable-dependency-tracking - speeds up one-time build - --enable-shared[=PKGS] build shared libraries [default=yes] - --enable-static[=PKGS] build static libraries [default=yes] - --enable-fast-install[=PKGS] - optimize for fast installation [default=yes] - --disable-libtool-lock avoid locking (might break parallel builds) - --disable-largefile omit support for large files - --enable-gui=backend Use specified gui backend. Can be x11, win32, cocoa, - pugl-x11, pugl-win32, pugl-cocoa or auto - [default=auto] - --enable-custom-channel-count=count - Compile with specified number of output channels - [default=16] - --enable-lv2 Compile the LV2 plugin [default=no] - --enable-vst Compile the VST plugin [default=no] - --enable-cli Compile the command line interface [default=yes] - --disable-input-dummy Disable input dummy plugin [default=enabled] - --disable-input-test Disable input test plugin [default=enabled] - --disable-input-jackmidi - Disable input jackmidi plugin [default=enabled] - --disable-input-alsamidi - Disable input alsamidi plugin [default=enabled] - --disable-input-midifile - Disable input midifile plugin [default=enabled] - --disable-input-oss Disable input oss plugin [enabled by default on - FreeBSD, disabled otherwise] - --disable-output-dummy Disable output dummy plugin [default=enabled] - --disable-output-jackaudio - Disable output jack plugin [default=enabled] - --disable-output-alsa Disable output alsa plugin [default=enabled] - --disable-output-wavfile - Disable output wavfile plugin [default=enabled] - --disable-output-oss Disable output oss plugin [enabled by default on - FreeBSD, disabled otherwise] - --enable-sse=level Enable SSE Level 1, 2, 3 or auto [default=auto] - -Optional Packages: - --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] - --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use - both] - --with-aix-soname=aix|svr4|both - shared library versioning (aka "SONAME") variant to - provide on AIX, [default=aix]. - --with-gnu-ld assume the C compiler uses GNU ld [default=no] - --with-sysroot[=DIR] Search for dependent libraries within DIR (or the - compiler's sysroot if not specified). - --with-debug Build with debug support - --with-nls Build with nls support (default nls enabled) - --with-test Build unit tests - --with-lv2dir=DIR Use DIR as the lv2 plugin directory - [default=LIBDIR/lv2] - --with-vst-sources Point this to the vstsdk24 directory - -Some influential environment variables: - CXX C++ compiler command - CXXFLAGS C++ compiler flags - LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a - nonstandard directory <lib dir> - LIBS libraries to pass to the linker, e.g. -l<library> - CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if - you have headers in a nonstandard directory <include dir> - OBJC Objective C compiler command - OBJCFLAGS Objective C compiler flags - OBJCXX Objective C++ compiler command - OBJCXXFLAGS Objective C++ compiler flags - CC C compiler command - CFLAGS C compiler flags - LT_SYS_LIBRARY_PATH - User-defined run-time library search path. - CPP C preprocessor - CXXCPP C++ preprocessor - PKG_CONFIG path to pkg-config utility - PKG_CONFIG_PATH - directories to add to pkg-config's search path - PKG_CONFIG_LIBDIR - path overriding pkg-config's built-in search path - X11_CFLAGS C compiler flags for X11, overriding pkg-config - X11_LIBS linker flags for X11, overriding pkg-config - XEXT_CFLAGS C compiler flags for XEXT, overriding pkg-config - XEXT_LIBS linker flags for XEXT, overriding pkg-config - LV2_CFLAGS C compiler flags for LV2, overriding pkg-config - LV2_LIBS linker flags for LV2, overriding pkg-config - SMF_CFLAGS C compiler flags for SMF, overriding pkg-config - SMF_LIBS linker flags for SMF, overriding pkg-config - SNDFILE_CFLAGS - C compiler flags for SNDFILE, overriding pkg-config - SNDFILE_LIBS - linker flags for SNDFILE, overriding pkg-config - JACK_CFLAGS C compiler flags for JACK, overriding pkg-config - JACK_LIBS linker flags for JACK, overriding pkg-config - ALSA_CFLAGS C compiler flags for ALSA, overriding pkg-config - ALSA_LIBS linker flags for ALSA, overriding pkg-config - -Use these variables to override the choices made by `configure' or to help -it to find libraries and programs with nonstandard names/locations. - -Report bugs to the package provider. -*/ -int configure(int argc, char* argv[]) -{ - Settings settings; - - settings.builddir = "build"; - - std::string cmd_str; - for(int i = 0; i < argc; ++i) - { - if(i > 0) - { - cmd_str += " "; - } - cmd_str += argv[i]; - } - - dg::Options opt; - int key{256}; - - std::string build_arch; - std::string build_path; - std::string host_arch; - std::string host_path; - - opt.add("build-dir", required_argument, 'b', - "Set output directory for build files (default: '" + - settings.builddir + "').", - [&]() { - settings.builddir = optarg; - return 0; - }); - - opt.add("verbose", no_argument, 'v', - "Be verbose. Add multiple times for more verbosity.", - [&]() { - settings.verbose++; - return 0; - }); - - opt.add("build", required_argument, key++, - "Configure for building on specified architecture.", - [&]() { - build_arch = optarg; - return 0; - }); - - opt.add("build-path", required_argument, key++, - "Set path to build tool-chain.", - [&]() { - build_path = optarg; - return 0; - }); - - opt.add("host", required_argument, key++, - "Cross-compile to build programs to run on specified architecture.", - [&]() { - host_arch = optarg; - return 0; - }); - - opt.add("host-path", required_argument, key++, - "Set path to cross-compile tool-chain.", - [&]() { - host_path = optarg; - return 0; - }); - - opt.add("help", no_argument, 'h', - "Print this help text.", - [&]() { - std::cout << "configure usage stuff\n"; - opt.help(); - exit(0); - return 0; - }); - - opt.process(argc, argv); - - if(host_arch.empty()) - { - host_arch = build_arch; - } - - auto tasks = getTasks(settings); - - bool needs_cpp{false}; - bool needs_c{false}; - bool needs_ar{false}; - bool needs_asm{false}; - for(const auto& task :tasks) - { - switch(task->sourceLanguage()) - { - case Language::Auto: - std::cerr << "TargetLanguage not deduced!\n"; - exit(1); - break; - case Language::C: - needs_cpp = false; - break; - case Language::Cpp: - needs_c = true; - break; - case Language::Asm: - needs_asm = true; - break; - } - } - - // CC=clang - // CXX=clang++ - - std::string path_env = std::getenv("PATH"); - std::cout << path_env << "\n"; - - std::vector<std::string> paths; - - { - std::stringstream ss(path_env); - std::string path; - while (std::getline(ss, path, ':')) - { - paths.push_back(path); - } - } - for(const auto& path_str : paths) - { - std::filesystem::path path(path_str); - auto gcc = path / "gcc"; - if(std::filesystem::exists(gcc)) - { - std::cout << "Found file gcc in path: " << path << "\n"; - auto perms = std::filesystem::status(gcc).permissions(); - if((perms & std::filesystem::perms::owner_exec) != std::filesystem::perms::none) - { - std::cout << " - executable by owner\n"; - } - if((perms & std::filesystem::perms::group_exec) != std::filesystem::perms::none) - { - std::cout << " - executable by group\n"; - } - if((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none) - { - std::cout << " - executable by others\n"; - } - } - } - exit(0); - - { - std::ofstream istr(configurationFile); - istr << "#include \"libcppbuild.h\"\n\n"; - istr << "const std::map<std::string, std::string>& configuration()\n"; - istr << "{\n"; - istr << " static std::map<std::string, std::string> c =\n"; - istr << " {\n"; - istr << " { \"cmd\", \"" << cmd_str << "\" },\n"; - istr << " { \"" << cfg::builddir << "\", \"" << settings.builddir << "\" },\n"; - istr << " { \"" << cfg::target_cc << "\", \"/usr/bin/gcc\" },\n"; - istr << " { \"" << cfg::target_cpp << "\", \"/usr/bin/g++\" },\n"; - istr << " { \"" << cfg::target_ar << "\", \"/usr/bin/ar\" },\n"; - istr << " { \"" << cfg::target_ld << "\", \"/usr/bin/ld\" },\n"; - istr << " { \"" << cfg::host_cc << "\", \"/usr/bin/gcc\" },\n"; - istr << " { \"" << cfg::host_cpp << "\", \"/usr/bin/g++\" },\n"; - istr << " { \"" << cfg::host_ar << "\", \"/usr/bin/ar\" },\n"; - istr << " { \"" << cfg::host_ld << "\", \"/usr/bin/ld\" },\n"; - istr << " };\n"; - istr << " return c;\n"; - istr << "}\n"; - } - - { - std::ofstream istr(configHeaderFile); - istr << "#pragma once\n\n"; - istr << "#define HAS_FOO 1\n"; - istr << "//#define HAS_BAR 1\n"; - } - - return 0; -} diff --git a/src/configure.h b/src/configure.h index 16499d6..ac8d721 100644 --- a/src/configure.h +++ b/src/configure.h @@ -8,10 +8,12 @@ #include <map> #include <vector> -struct Settings; +namespace ctor { +struct settings; +} // namespace ctor:: extern std::filesystem::path configurationFile;; extern std::filesystem::path configHeaderFile; -int configure(const Settings& settings, int argc, char* argv[]); -int reconfigure(const Settings& settings, int argc, char* argv[]); +int configure(const ctor::settings& settings, int argc, char* argv[]); +int reconfigure(const ctor::settings& settings, int argc, char* argv[]); diff --git a/src/ctor.h b/src/ctor.h new file mode 100644 index 0000000..3b64cb5 --- /dev/null +++ b/src/ctor.h @@ -0,0 +1,282 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include <source_location> +#include <string> +#include <vector> +#include <map> +#include <variant> +#include <cstddef> +#include <functional> + +namespace ctor { + +enum class target_type +{ + automatic, // Default - deduce from target name and sources extensions + + executable, + static_library, + dynamic_library, + object, + unit_test, + unit_test_library, + function, + + unknown, +}; + +enum class language +{ + automatic, // Default - deduce language from source extensions + + c, + cpp, + assembler, +}; + +enum class output_system +{ + host, // Output for the target system + build, // Internal tool during cross-compilation +}; + +enum class arch +{ + unix, //!< Target platform architecture is unix-based (ie. linux, bsd, etc) + apple, //!< Target platform architecture is macos + windows, //!< Target platform architecture is windows + + unknown, //!< Target platform architecture has not yet detected or was not possible to detect +}; + +struct source +{ + source(const char* file) : file(file) {} + source(const std::string& file) : file(file) {} + source(const char* file, ctor::language lang) : file(file), language(lang) {} + source(const std::string& file, ctor::language lang) : file(file), language(lang) {} + + source(const char* file, const char* output) : file(file), output(output) {} + source(const std::string& file, const std::string& output) : file(file), output(output) {} + source(const char* file, ctor::language lang, const char* output) : file(file), language(lang), output(output) {} + source(const std::string& file, ctor::language lang, const std::string& output) : file(file), language(lang), output(output) {} + + std::string file; + ctor::language language{ctor::language::automatic}; + std::string output{}; +}; + +enum class toolchain +{ + any, + none, + gcc, + clang, +}; + +enum class cxx_opt +{ + // gcc/clang + output, // -o + debug, // -g + warn_all, // -Wall + warnings_as_errors, // -Werror + generate_dep_tree, // -MMD + no_link, // -c + include_path, // -I<arg> + cpp_std, // -std=<arg> + optimization, // -O<arg> + position_independent_code, // -fPIC + position_independent_executable, // -fPIE + custom, // entire option taken verbatim from <arg> +}; + +enum class c_opt +{ + // gcc/clang + output, // -o + debug, // -g + warn_all, // -Wall + warnings_as_errors, // -Werror + generate_dep_tree, // -MMD + no_link, // -c + include_path, // -I<arg> + c_std, // -std=<arg> + optimization, // -O<arg> + position_independent_code, // -fPIC + position_independent_executable, // -fPIE + custom, // entire option taken verbatim from <arg> +}; + +enum class ld_opt +{ + // gcc/clang + output, // -o + strip, // -s + warn_all, // -Wall + warnings_as_errors, // -Werror + library_path, // -L<arg> + link, // -l<arg> + cpp_std, // -std=<arg> + build_shared, // -shared + threads, // -pthread + position_independent_code, // -fPIC + position_independent_executable, // -fPIE + custom, // entire option taken verbatim from <arg> +}; + +enum class ar_opt +{ + // gcc/clang + replace, // -r + add_index, // -s + create, // -c + output, // <arg> + + custom, // entire option taken verbatim from <arg> +}; + +enum class asm_opt +{ + // gcc/clang + custom, // entire option taken verbatim from <arg> +}; + +template<typename T> +class flag +{ +public: + flag(const std::string& str); + flag(const char* str); + flag(T opt) : opt(opt) {} + flag(T opt, const std::string& arg) : opt(opt), arg(arg) {} + flag(T opt, const char* arg) : opt(opt), arg(arg) {} + flag(ctor::toolchain toolchain, T opt) : toolchain(toolchain), opt(opt) {} + flag(ctor::toolchain toolchain, T opt, const char* arg) : toolchain(toolchain), opt(opt), arg(arg) {} + flag(ctor::toolchain toolchain, T opt, const std::string& arg) : toolchain(toolchain), opt(opt), arg(arg) {} + + ctor::toolchain toolchain{ctor::toolchain::any}; + T opt; + std::string arg; +}; + +using c_flag = ctor::flag<ctor::c_opt>; +using cxx_flag = ctor::flag<ctor::cxx_opt>; +using ld_flag = ctor::flag<ctor::ld_opt>; +using ar_flag = ctor::flag<ctor::ar_opt>; +using asm_flag = ctor::flag<ctor::asm_opt>; + +using c_flags = std::vector<ctor::c_flag>; +using cxx_flags = std::vector<ctor::cxx_flag>; +using ld_flags= std::vector<ctor::ld_flag>; +using ar_flags = std::vector<ctor::ar_flag>; +using asm_flags = std::vector<ctor::asm_flag>; + +struct flags +{ + ctor::c_flags cflags; // flags for c compiler + ctor::cxx_flags cxxflags; // flags for c++ compiler + ctor::ld_flags ldflags; // flags for linker + ctor::ar_flags arflags; // flags for archiver + ctor::asm_flags asmflags; // flags for asm translator +}; + +struct settings +{ + std::string builddir{"build"}; + std::size_t parallel_processes{1}; + int verbose{0}; // -1: completely silent, 0: normal, 1: verbose, ... +}; + +struct build_configuration; +using GeneratorCb = std::function<int(const std::string& input, + const std::string& output, + const build_configuration& config, + const ctor::settings& settings)>; + +struct build_configuration +{ + std::string name; // Name - used for referring in other configurations. + ctor::target_type type{ctor::target_type::automatic}; + ctor::output_system system{ctor::output_system::build}; + std::string target; // Output target file for this configuration + std::vector<ctor::source> sources; // source list + std::vector<std::string> depends; // internal target dependencies + ctor::flags flags; + std::vector<std::string> externals; // externals used by this configuration + GeneratorCb function; +}; + +using build_configurations = std::vector<build_configuration>; + +int reg(ctor::build_configurations (*cb)(const ctor::settings&), + const std::source_location location = std::source_location::current()); + +// This type will use flags verbatim +struct external_manual +{ + ctor::flags flags; +}; + + +struct external_configuration +{ + std::string name; // Name for configuration + ctor::output_system system{ctor::output_system::build}; + std::variant<ctor::external_manual> external; +}; + +using external_configurations = std::vector<ctor::external_configuration>; + +int reg(ctor::external_configurations (*cb)(const ctor::settings&), + const std::source_location location = std::source_location::current()); + +// Convenience macro - ugly but keeps things simple(r) +#define CTOR_CONCAT(a, b) CTOR_CONCAT_INNER(a, b) +#define CTOR_CONCAT_INNER(a, b) a ## b +#define CTOR_UNIQUE_NAME(base) CTOR_CONCAT(base, __LINE__) +#define REG(cb) namespace { int CTOR_UNIQUE_NAME(unique) = reg(cb); } + +// Predefined configuration keys +namespace cfg +{ +constexpr auto builddir = "builddir"; + +constexpr auto host_cc = "host-cc"; +constexpr auto host_cxx = "host-cpp"; +constexpr auto host_ar = "host-ar"; +constexpr auto host_ld = "host-ld"; + +constexpr auto build_cc = "build-cc"; +constexpr auto build_cxx = "build-cpp"; +constexpr auto build_ar = "build-ar"; +constexpr auto build_ld = "build-ld"; + +constexpr auto ctor_includedir = "ctor-includedir"; +constexpr auto ctor_libdir = "ctor-libdir"; +} + +struct configuration +{ + bool has(const std::string& key) const; + const std::string& get(const std::string& key, const std::string& default_value = {}) const; + + ctor::toolchain host_toolchain{ctor::toolchain::none}; + ctor::arch host_arch{ctor::arch::unknown}; + + ctor::toolchain build_toolchain{ctor::toolchain::none}; + ctor::arch build_arch{ctor::arch::unknown}; + + std::vector<std::string> args; // vector of arguments used when last calling configure + std::map<std::string, std::string> env; // env used when last calling configure + + std::map<std::string, std::string> tools; // tools + std::map<std::string, ctor::flags> externals; +}; + +const ctor::configuration& get_configuration(); + +} // ctor:: diff --git a/src/execute.cc b/src/execute.cc index 610ccdd..b4013d0 100644 --- a/src/execute.cc +++ b/src/execute.cc @@ -20,6 +20,28 @@ https://stackoverflow.com/questions/4259629/what-is-the-difference-between-fork- namespace { +class Env + : public std::vector<char*> +{ +public: + Env(const std::vector<std::string>& args) + { + for(const auto& arg : args) + { + push_back(strdup(arg.data())); + } + push_back(nullptr); + } + + ~Env() + { + for(auto ptr : *this) + { + free(ptr); + } + } +}; + int parent_waitpid(pid_t pid) { int status; @@ -33,8 +55,11 @@ int parent_waitpid(pid_t pid) } } // namespace :: +extern char **environ; // see 'man environ' + int execute(const std::string& command, const std::vector<std::string>& args, + const std::map<std::string, std::string>& env, bool verbose) { std::vector<const char*> argv; @@ -45,46 +70,65 @@ int execute(const std::string& command, } argv.push_back(nullptr); - if(verbose) + std::string cmd; + for(const auto& arg : argv) { - std::string cmd; - for(const auto& arg : argv) + if(arg == nullptr) { - if(arg == nullptr) - { - break; - } - if(!cmd.empty()) - { - cmd += " "; - } - cmd += arg; + break; } + if(!cmd.empty()) + { + cmd += " "; + } + cmd += arg; + } - std::cout << cmd << "\n"; + if(verbose) + { + std::cout << cmd << std::endl; } #if 1 auto pid = vfork(); if(pid == 0) { - execv(command.data(), (char**)argv.data()); + std::vector<std::string> venv; + for(const auto& [key, value] : env) + { + venv.push_back(key + "=" + value); + } + + for(auto current = environ; *current; ++current) + { + venv.push_back(*current); + } + + Env penv(venv); + execve(command.data(), (char**)argv.data(), penv.data()); std::cout << "Could not execute " << command << ": " << strerror(errno) << "\n"; - _exit(1); // execv only returns if an error occurred + _exit(1); // execve only returns if an error occurred } - auto ret = parent_waitpid(pid); + return parent_waitpid(pid); #elif 0 pid_t pid; + std::vector<std::string> venv; + for(const auto& [key, value] : env) + { + venv.push_back(key + "=" + value); + } + Env penv(venv); if(posix_spawn(&pid, command.data(), nullptr, nullptr, - (char**)argv.data(), nullptr)) + (char**)argv.data(), penv.data())) { return 1; } - auto ret = parent_waitpid(pid); + return parent_waitpid(pid); #else - auto ret = system(cmd.data()); + (void)parent_waitpid; + return system(cmd.data()); #endif - return ret; + return 1; } diff --git a/src/execute.h b/src/execute.h index c750a83..336c3ef 100644 --- a/src/execute.h +++ b/src/execute.h @@ -5,7 +5,9 @@ #include <string> #include <vector> +#include <map> int execute(const std::string& command, - const std::vector<std::string>& args, + const std::vector<std::string>& args = {}, + const std::map<std::string, std::string>& env = {}, bool verbose = true); diff --git a/src/externals_manual.cc b/src/externals_manual.cc index 79c9be3..3b96263 100644 --- a/src/externals_manual.cc +++ b/src/externals_manual.cc @@ -5,27 +5,30 @@ #include <map> -#include "libctor.h" +#include "ctor.h" + +#include "util.h" +#include "tools.h" extern std::map<std::string, std::string> external_includedir; extern std::map<std::string, std::string> external_libdir; -int resolv(const Settings& settings, const std::string& name, - const ExternalManual& ext, Flags& flags) +int resolv(const ctor::settings& settings, const ctor::external_configuration& config, + const ctor::external_manual& ext, ctor::flags& flags) { flags = ext.flags; - auto inc = external_includedir.find(name); + auto inc = external_includedir.find(config.name); if(inc != external_includedir.end()) { - flags.cflags.push_back("-I" + inc->second); - flags.cxxflags.push_back("-I" + inc->second); + flags.cflags.push_back({ctor::c_opt::include_path, inc->second}); + flags.cxxflags.push_back({ctor::cxx_opt::include_path, inc->second}); } - auto lib = external_libdir.find(name); + auto lib = external_libdir.find(config.name); if(lib != external_libdir.end()) { - flags.ldflags.push_back("-L" + lib->second); + flags.ldflags.push_back({ctor::ld_opt::library_path, lib->second}); } return 0; diff --git a/src/externals_manual.h b/src/externals_manual.h index c7e3b9a..a906ab7 100644 --- a/src/externals_manual.h +++ b/src/externals_manual.h @@ -3,11 +3,13 @@ // See accompanying file LICENSE for details. #pragma once -#include <string> +namespace ctor { +struct settings; +struct external_configuration; +struct external_manual; +struct flags; +} // namespace ctor:: -struct Settings; -struct ExternalManual; -struct Flags; - -int resolv(const Settings& settings, const std::string& name, - const ExternalManual& ext, Flags& flags); +int resolv(const ctor::settings& settings, + const ctor::external_configuration& name, + const ctor::external_manual& ext, ctor::flags& flags); diff --git a/src/libctor.cc b/src/libctor.cc index d188771..21792e1 100644 --- a/src/libctor.cc +++ b/src/libctor.cc @@ -19,7 +19,7 @@ #include <getoptpp/getoptpp.hpp> -#include "libctor.h" +#include "ctor.h" #include "configure.h" #include "rebuild.h" #include "tasks.h" @@ -28,9 +28,10 @@ int main(int argc, char* argv[]) { - Settings settings{}; + ctor::settings settings{}; + const auto& c = ctor::get_configuration(); - settings.builddir = getConfiguration(cfg::builddir, settings.builddir); + settings.builddir = c.get(ctor::cfg::builddir, settings.builddir); settings.parallel_processes = std::max(1u, std::thread::hardware_concurrency()) * 2 - 1; @@ -263,13 +264,13 @@ Options: if(print_configure_cmd) { no_default_build = true; - std::cout << getConfiguration("cmd") << "\n"; + std::cout << c.get("cmd") << "\n"; } if(print_configure_db) { no_default_build = true; - const auto& c = configuration(); + const auto& c = ctor::get_configuration(); for(const auto& config : c.tools) { std::cout << config.first << ": " << config.second << "\n"; @@ -315,8 +316,8 @@ Options: auto& targets = getTargets(settings); for(const auto& target : targets) { - if(target.config.type == TargetType::UnitTest || - target.config.type == TargetType::UnitTestLib) + if(target.config.type == ctor::target_type::unit_test || + target.config.type == ctor::target_type::unit_test_library) { unittest_targets.push_back(target); } @@ -338,8 +339,8 @@ Options: auto& targets = getTargets(settings); for(const auto& target : targets) { - if(target.config.type != TargetType::UnitTest && - target.config.type != TargetType::UnitTestLib) + if(target.config.type != ctor::target_type::unit_test && + target.config.type != ctor::target_type::unit_test_library) { non_unittest_targets.push_back(target); } @@ -368,8 +369,8 @@ Options: auto& targets = getTargets(settings); for(const auto& target : targets) { - if(target.config.type != TargetType::UnitTest && - target.config.type != TargetType::UnitTestLib) + if(target.config.type != ctor::target_type::unit_test && + target.config.type != ctor::target_type::unit_test_library) { non_unittest_targets.push_back(target); } diff --git a/src/libctor.h b/src/libctor.h deleted file mode 100644 index 14fdf1d..0000000 --- a/src/libctor.h +++ /dev/null @@ -1,153 +0,0 @@ -// -*- c++ -*- -// Distributed under the BSD 2-Clause License. -// See accompanying file LICENSE for details. -#pragma once - -#include <source_location> -#include <string> -#include <vector> -#include <map> -#include <variant> -#include <cstddef> -#include <functional> - -enum class TargetType -{ - Auto, // Default - deduce from target name and sources extensions - - Executable, - StaticLibrary, - DynamicLibrary, - Object, - UnitTest, - UnitTestLib, - Function, -}; - -enum class Language -{ - Auto, // Default - deduce language from source extensions - - C, - Cpp, - Asm, -}; - -enum class OutputSystem -{ - Host, // Output for the target system - Build, // Internal tool during cross-compilation -}; - -struct Source -{ - Source(const char* file) : file(file) {} - Source(const std::string& file) : file(file) {} - Source(const char* file, Language lang) : file(file), language(lang) {} - Source(const std::string& file, Language lang) : file(file), language(lang) {} - - Source(const char* file, const char* output) : file(file), output(output) {} - Source(const std::string& file, const std::string& output) : file(file), output(output) {} - Source(const char* file, Language lang, const char* output) : file(file), language(lang), output(output) {} - Source(const std::string& file, Language lang, const std::string& output) : file(file), language(lang), output(output) {} - - std::string file; - Language language{Language::Auto}; - std::string output{}; -}; - -struct Flags -{ - std::vector<std::string> cxxflags; // flags for c++ compiler - std::vector<std::string> cflags; // flags for c compiler - std::vector<std::string> ldflags; // flags for linker - std::vector<std::string> asmflags; // flags for asm translator -}; - -struct Settings -{ - std::string builddir{"build"}; - std::size_t parallel_processes{1}; - int verbose{0}; // -1: completely silent, 0: normal, 1: verbose, ... -}; - -struct BuildConfiguration; -using GeneratorCb = std::function<int(const std::string& input, - const std::string& output, - const BuildConfiguration& config, - const Settings& settings)>; - -struct BuildConfiguration -{ - std::string name; // Name - used for referring in other configurations. - TargetType type{TargetType::Auto}; - OutputSystem system{OutputSystem::Host}; - std::string target; // Output target file for this configuration - std::vector<Source> sources; // source list - std::vector<std::string> depends; // internal target dependencies - Flags flags; - std::vector<std::string> externals; // externals used by this configuration - GeneratorCb function; -}; - -using BuildConfigurations = std::vector<BuildConfiguration>; - -int reg(BuildConfigurations (*cb)(const Settings&), - const std::source_location location = std::source_location::current()); - -// This type will use flags verbatim -struct ExternalManual -{ - Flags flags; -}; - - -struct ExternalConfiguration -{ - std::string name; // Name for configuration - std::variant<ExternalManual> external; -}; - -using ExternalConfigurations = std::vector<ExternalConfiguration>; - -int reg(ExternalConfigurations (*cb)(const Settings&), - const std::source_location location = std::source_location::current()); - -// Convenience macro - ugly but keeps things simple(r) -#define CONCAT(a, b) CONCAT_INNER(a, b) -#define CONCAT_INNER(a, b) a ## b -#define UNIQUE_NAME(base) CONCAT(base, __LINE__) -#define REG(cb) namespace { int UNIQUE_NAME(unique) = reg(cb); } - -// Predefined configuration keys -namespace cfg -{ -constexpr auto builddir = "builddir"; - -constexpr auto host_cc = "host-cc"; -constexpr auto host_cxx = "host-cpp"; -constexpr auto host_ar = "host-ar"; -constexpr auto host_ld = "host-ld"; - -constexpr auto build_cc = "build-cc"; -constexpr auto build_cxx = "build-cpp"; -constexpr auto build_ar = "build-ar"; -constexpr auto build_ld = "build-ld"; - -constexpr auto ctor_includedir = "ctor-includedir"; -constexpr auto ctor_libdir = "ctor-libdir"; -} - -struct Configuration -{ - std::vector<std::string> args; // vector of arguments used when last calling configure - std::map<std::string, std::string> env; // env used when last calling configure - - std::map<std::string, std::string> tools; // tools - std::map<std::string, Flags> externals; -}; - -const Configuration& configuration(); -bool hasConfiguration(const std::string& key); -const std::string& getConfiguration(const std::string& key, - const std::string& defaultValue = {}); diff --git a/src/rebuild.cc b/src/rebuild.cc index 3e0e78d..f0d52d9 100644 --- a/src/rebuild.cc +++ b/src/rebuild.cc @@ -7,17 +7,21 @@ #include <filesystem> #include <algorithm> #include <source_location> +#include <cstring> #include "configure.h" -#include "libctor.h" +#include "ctor.h" #include "tasks.h" #include "build.h" #include "execute.h" +#include "tools.h" +#include "util.h" std::array<BuildConfigurationEntry, 1024> configFiles; std::size_t numConfigFiles{0}; -int reg(BuildConfigurations (*cb)(const Settings&), +namespace ctor { +int reg(ctor::build_configurations (*cb)(const ctor::settings&), const std::source_location location) { // NOTE: std::cout cannot be used here @@ -28,12 +32,23 @@ int reg(BuildConfigurations (*cb)(const Settings&), exit(1); } - configFiles[numConfigFiles].file = location.file_name(); + auto loc = std::filesystem::path(location.file_name()); + if(loc.is_absolute()) + { + auto pwd = std::filesystem::current_path(); + auto rel = std::filesystem::relative(loc, pwd); + configFiles[numConfigFiles].file = strdup(rel.string().data()); // NOTE: This intentionally leaks memory + } + else + { + configFiles[numConfigFiles].file = location.file_name(); + } configFiles[numConfigFiles].cb = cb; ++numConfigFiles; return 0; } +} // ctor:: int reg(const char* location) { @@ -47,7 +62,7 @@ int reg(const char* location) configFiles[numConfigFiles].file = location; configFiles[numConfigFiles].cb = - [](const Settings&){ return std::vector<BuildConfiguration>{}; }; + [](const ctor::settings&){ return std::vector<ctor::build_configuration>{}; }; ++numConfigFiles; return 0; @@ -96,7 +111,8 @@ int unreg(const char* location) std::array<ExternalConfigurationEntry, 1024> externalConfigFiles; std::size_t numExternalConfigFiles{0}; -int reg(ExternalConfigurations (*cb)(const Settings&), +namespace ctor { +int reg(ctor::external_configurations (*cb)(const ctor::settings&), const std::source_location location) { // NOTE: std::cout cannot be used here @@ -113,10 +129,11 @@ int reg(ExternalConfigurations (*cb)(const Settings&), return 0; } +} // namespace ctor:: namespace { -bool contains(const std::vector<Source>& sources, const std::string& file) +bool contains(const std::vector<ctor::source>& sources, const std::string& file) { for(const auto& source : sources) { @@ -130,7 +147,7 @@ bool contains(const std::vector<Source>& sources, const std::string& file) } } -bool recompileCheck(const Settings& global_settings, int argc, char* argv[], +bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[], bool relaunch_allowed) { using namespace std::string_literals; @@ -140,24 +157,30 @@ bool recompileCheck(const Settings& global_settings, int argc, char* argv[], std::cout << "Recompile check (" << numConfigFiles << "):\n"; } - BuildConfiguration config; + ctor::build_configuration config; + config.type = ctor::target_type::executable; config.name = "ctor"; - config.flags.cxxflags = - std::vector<std::string>({ "-s", "-O3", "-std=c++20" }); - if(hasConfiguration(cfg::ctor_includedir)) + config.system = ctor::output_system::build; + + config.flags.cxxflags.push_back({ctor::cxx_opt::optimization, "3"}); + config.flags.cxxflags.push_back({ctor::cxx_opt::cpp_std, "c++20"}); + + const auto& c = ctor::get_configuration(); + if(c.has(ctor::cfg::ctor_includedir)) { - config.flags.cxxflags.push_back("-I"s + getConfiguration(cfg::ctor_includedir)); + config.flags.cxxflags.push_back({ctor::cxx_opt::include_path, + c.get(ctor::cfg::ctor_includedir)}); } - if(hasConfiguration(cfg::ctor_libdir)) + if(c.has(ctor::cfg::ctor_libdir)) { - config.flags.ldflags.push_back("-L"s + getConfiguration(cfg::ctor_libdir)); + config.flags.ldflags.push_back({ctor::ld_opt::library_path, c.get(ctor::cfg::ctor_libdir)}); } - config.flags.ldflags.push_back("-lctor"); - config.flags.ldflags.push_back("-pthread"); - + config.flags.ldflags.push_back({ctor::ld_opt::link, "ctor"}); + config.flags.ldflags.push_back({ctor::ld_opt::strip}); + config.flags.ldflags.push_back({ctor::ld_opt::threads}); - Settings settings{global_settings}; + ctor::settings settings{global_settings}; settings.verbose = -1; // Make check completely silent. settings.builddir += "/ctor"; // override builddir to use ctor subdir diff --git a/src/rebuild.h b/src/rebuild.h index f1255c6..efa6d42 100644 --- a/src/rebuild.h +++ b/src/rebuild.h @@ -6,20 +6,18 @@ #include <vector> #include <array> -#include "libctor.h" - -class Settings; +#include "ctor.h" struct BuildConfigurationEntry { const char* file; - BuildConfigurations (*cb)(const Settings&); + ctor::build_configurations (*cb)(const ctor::settings&); }; struct ExternalConfigurationEntry { const char* file; - ExternalConfigurations (*cb)(const Settings&); + ctor::external_configurations (*cb)(const ctor::settings&); }; extern std::array<BuildConfigurationEntry, 1024> configFiles; @@ -32,5 +30,5 @@ int reg(const char* location); int unreg(const char* location); //! Returns true of recompilation was needed. -bool recompileCheck(const Settings& settings, int argc, char* argv[], +bool recompileCheck(const ctor::settings& settings, int argc, char* argv[], bool relaunch_allowed = true); diff --git a/src/task.cc b/src/task.cc index fb50765..817ee3a 100644 --- a/src/task.cc +++ b/src/task.cc @@ -6,7 +6,7 @@ #include <unistd.h> #include <iostream> -Task::Task(const BuildConfiguration& config, const Settings& settings, +Task::Task(const ctor::build_configuration& config, const ctor::settings& settings, const std::string& sourceDir) : config(config) , output_system(config.system) @@ -107,45 +107,46 @@ State Task::state() const return task_state.load(); } -const BuildConfiguration& Task::buildConfig() const +const ctor::build_configuration& Task::buildConfig() const { return config; } -TargetType Task::targetType() const +ctor::target_type Task::targetType() const { return target_type; } -Language Task::sourceLanguage() const +ctor::language Task::sourceLanguage() const { return source_language; } -OutputSystem Task::outputSystem() const +ctor::output_system Task::outputSystem() const { return output_system; } std::string Task::compiler() const { + const auto& c = ctor::get_configuration(); switch(sourceLanguage()) { - case Language::C: + case ctor::language::c: switch(outputSystem()) { - case OutputSystem::Host: - return getConfiguration(cfg::host_cc, "/usr/bin/gcc"); - case OutputSystem::Build: - return getConfiguration(cfg::build_cc, "/usr/bin/gcc"); + case ctor::output_system::host: + return c.get(ctor::cfg::host_cc, "/usr/bin/gcc"); + case ctor::output_system::build: + return c.get(ctor::cfg::build_cc, "/usr/bin/gcc"); } - case Language::Cpp: + case ctor::language::cpp: switch(outputSystem()) { - case OutputSystem::Host: - return getConfiguration(cfg::host_cxx, "/usr/bin/g++"); - case OutputSystem::Build: - return getConfiguration(cfg::build_cxx, "/usr/bin/g++"); + case ctor::output_system::host: + return c.get(ctor::cfg::host_cxx, "/usr/bin/g++"); + case ctor::output_system::build: + return c.get(ctor::cfg::build_cxx, "/usr/bin/g++"); } default: std::cerr << "Unknown CC target type\n"; @@ -10,7 +10,7 @@ #include <memory> #include <filesystem> -#include "libctor.h" +#include "ctor.h" enum class State { @@ -21,13 +21,12 @@ enum class State Error, }; -struct Settings; - class Task { public: - Task(const BuildConfiguration& config, const Settings& settings, + Task(const ctor::build_configuration& config, const ctor::settings& settings, const std::string& sourceDir); + virtual ~Task() = default; int registerDepTasks(const std::set<std::shared_ptr<Task>>& tasks); virtual int registerDepTasksInner(const std::set<std::shared_ptr<Task>>& tasks) { return 0; } @@ -58,11 +57,11 @@ public: //! Returns a reference to the originating build config. //! Note: the build config of a derived task will be that of its parent //! (target) task. - const BuildConfiguration& buildConfig() const; + const ctor::build_configuration& buildConfig() const; - TargetType targetType() const; - Language sourceLanguage() const; - OutputSystem outputSystem() const; + ctor::target_type targetType() const; + ctor::language sourceLanguage() const; + ctor::output_system outputSystem() const; std::string compiler() const; std::set<std::shared_ptr<Task>> getDependsTasks(); @@ -76,10 +75,10 @@ protected: std::vector<std::string> dependsStr; std::set<std::shared_ptr<Task>> dependsTasks; - const BuildConfiguration& config; - TargetType target_type{TargetType::Auto}; - Language source_language{Language::Auto}; - OutputSystem output_system{OutputSystem::Host}; - const Settings& settings; + const ctor::build_configuration& config; + ctor::target_type target_type{ctor::target_type::automatic}; + ctor::language source_language{ctor::language::automatic}; + ctor::output_system output_system{ctor::output_system::host}; + const ctor::settings& settings; std::string sourceDir; }; diff --git a/src/task_ar.cc b/src/task_ar.cc index 3e1746c..19a65ae 100644 --- a/src/task_ar.cc +++ b/src/task_ar.cc @@ -6,12 +6,13 @@ #include <iostream> #include <fstream> -#include "libctor.h" +#include "ctor.h" #include "execute.h" #include "util.h" +#include "tools.h" -TaskAR::TaskAR(const BuildConfiguration& config, - const Settings& settings, +TaskAR::TaskAR(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& target, const std::vector<std::string>& objects, const std::string& sourceDir) @@ -20,9 +21,12 @@ TaskAR::TaskAR(const BuildConfiguration& config, , settings(settings) , sourceDir(sourceDir) { - std::filesystem::create_directories(std::filesystem::path(settings.builddir) / sourceDir); + target_type = ctor::target_type::static_library; + output_system = config.system; _targetFile = target; + auto toolchain = getToolChain(config.system); + _targetFile = extension(toolchain, target_type, config.system, _targetFile); for(const auto& object : objects) { std::filesystem::path objectFile = object; @@ -38,17 +42,18 @@ TaskAR::TaskAR(const BuildConfiguration& config, flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem(); flagsFile += ".flags"; - target_type = TargetType::StaticLibrary; - source_language = Language::C; + source_language = ctor::language::c; for(const auto& source : config.sources) { std::filesystem::path sourceFile(source.file); // TODO: Use task languages instead if(sourceFile.extension().string() != ".c") { - source_language = Language::Cpp; + source_language = ctor::language::cpp; } } + + std::filesystem::create_directories(targetFile().parent_path()); } bool TaskAR::dirtyInner() @@ -77,9 +82,17 @@ bool TaskAR::dirtyInner() int TaskAR::runInner() { + auto toolchain = getToolChain(config.system); + std::vector<std::string> args; - args.push_back("rcs"); - args.push_back(targetFile().string()); + for(const auto& flag : config.flags.arflags) + { + append(args, to_strings(toolchain, flag)); + } + append(args, ar_option(toolchain, ctor::ar_opt::replace)); + append(args, ar_option(toolchain, ctor::ar_opt::add_index)); + append(args, ar_option(toolchain, ctor::ar_opt::create)); + append(args, ar_option(toolchain, ctor::ar_opt::output, targetFile().string())); for(const auto& task : getDependsTasks()) { args.push_back(task->targetFile().string()); @@ -92,21 +105,22 @@ int TaskAR::runInner() if(settings.verbose == 0) { - std::cout << "AR => " << targetFile().string() << "\n"; + std::cout << "AR => " << targetFile().string() << std::endl; } + const auto& c = ctor::get_configuration(); std::string tool; switch(outputSystem()) { - case OutputSystem::Host: - tool = getConfiguration(cfg::host_ar, "/usr/bin/ar"); + case ctor::output_system::host: + tool = c.get(ctor::cfg::host_ar, "/usr/bin/ar"); break; - case OutputSystem::Build: - tool = getConfiguration(cfg::build_ar, "/usr/bin/ar"); + case ctor::output_system::build: + tool = c.get(ctor::cfg::build_ar, "/usr/bin/ar"); break; } - return execute(tool, args, settings.verbose > 0); + return execute(tool, args, {}, settings.verbose > 0); } int TaskAR::clean() @@ -159,14 +173,20 @@ bool TaskAR::derived() const std::string TaskAR::flagsString() const { + auto toolchain = getToolChain(config.system); std::string flagsStr; + bool first{true}; for(const auto& flag : config.flags.ldflags) { - if(flag != config.flags.ldflags[0]) + for(const auto& str : to_strings(toolchain, flag)) { - flagsStr += " "; + if(first) + { + flagsStr += " "; + first = false; + } + flagsStr += str; } - flagsStr += flag; } flagsStr += "\n"; diff --git a/src/task_ar.h b/src/task_ar.h index c76a852..601afeb 100644 --- a/src/task_ar.h +++ b/src/task_ar.h @@ -10,15 +10,12 @@ #include <future> #include <filesystem> -struct BuildConfiguration; -struct Settings; - class TaskAR : public Task { public: - TaskAR(const BuildConfiguration& config, - const Settings& settings, + TaskAR(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& target, const std::vector<std::string>& objects, const std::string& sourceDir); @@ -44,7 +41,7 @@ private: std::filesystem::path _targetFile; std::filesystem::path flagsFile; - const BuildConfiguration& config; - const Settings& settings; + const ctor::build_configuration& config; + const ctor::settings& settings; std::string sourceDir; }; diff --git a/src/task_cc.cc b/src/task_cc.cc index a55a619..294c5ea 100644 --- a/src/task_cc.cc +++ b/src/task_cc.cc @@ -7,12 +7,13 @@ #include <fstream> #include <cassert> -#include "libctor.h" +#include "ctor.h" #include "execute.h" #include "util.h" +#include "tools.h" -TaskCC::TaskCC(const BuildConfiguration& config, const Settings& settings, - const std::string& sourceDir, const Source& source) +TaskCC::TaskCC(const ctor::build_configuration& config, const ctor::settings& settings, + const std::string& sourceDir, const ctor::source& source) : Task(config, settings, sourceDir) , config(config) , settings(settings) @@ -23,31 +24,31 @@ TaskCC::TaskCC(const BuildConfiguration& config, const Settings& settings, sourceFile /= source.file; std::filesystem::path base = sourceFile.parent_path(); - std::filesystem::create_directories(std::filesystem::path(settings.builddir) / base); base /= cleanUp(config.target); base += "-"; base += sourceFile.stem(); - target_type = TargetType::Object; + target_type = ctor::target_type::object; + output_system = config.system; source_language = source.language; - if(source_language == Language::Auto) + if(source_language == ctor::language::automatic) { source_language = languageFromExtension(sourceFile); } switch(source_language) { - case Language::C: + case ctor::language::c: base += "_c"; break; - case Language::Cpp: + case ctor::language::cpp: base += "_cc"; break; - case Language::Asm: + case ctor::language::assembler: base += "_asm"; break; - case Language::Auto: + case ctor::language::automatic: assert(0 && "This should never happen"); break; } @@ -55,16 +56,20 @@ TaskCC::TaskCC(const BuildConfiguration& config, const Settings& settings, if(source.output.empty()) { _targetFile = base; - _targetFile += ".o"; } else { _targetFile = source.output; } + auto toolchain = getToolChain(config.system); + _targetFile = extension(toolchain, target_type, config.system, _targetFile); + depsFile = targetFile().parent_path() / targetFile().stem(); depsFile += ".d"; flagsFile = targetFile().parent_path() / targetFile().stem(); flagsFile += ".flags"; + + std::filesystem::create_directories(targetFile().parent_path()); } int TaskCC::registerDepTasksInner(const std::set<std::shared_ptr<Task>>& tasks) @@ -166,12 +171,25 @@ int TaskCC::runInner() if(settings.verbose == 0) { - std::cout << compiler() << " " << + switch(sourceLanguage()) + { + case ctor::language::c: + std::cout << "CC "; + break; + case ctor::language::cpp: + std::cout << "CXX "; + break; + case ctor::language::automatic: + case ctor::language::assembler: + // Only c/c++ handled by this task type. + break; + } + std::cout << sourceFile.lexically_normal().string() << " => " << - targetFile().lexically_normal().string() << "\n"; + targetFile().lexically_normal().string() << std::endl; } - return execute(compiler(), args, settings.verbose > 0); + return execute(compiler(), args, {}, settings.verbose > 0); } int TaskCC::clean() @@ -242,12 +260,23 @@ std::string TaskCC::source() const std::vector<std::string> TaskCC::flags() const { + std::vector<std::string> flags; + auto toolchain = getToolChain(config.system); + switch(sourceLanguage()) { - case Language::C: - return config.flags.cflags; - case Language::Cpp: - return config.flags.cxxflags; + case ctor::language::c: + for(const auto& flag : config.flags.cflags) + { + append(flags, to_strings(toolchain, flag)); + } + return flags; + case ctor::language::cpp: + for(const auto& flag : config.flags.cxxflags) + { + append(flags, to_strings(toolchain, flag)); + } + return flags; default: std::cerr << "Unknown CC target type\n"; exit(1); @@ -267,42 +296,100 @@ std::string TaskCC::flagsString() const std::vector<std::string> TaskCC::getCompilerArgs() const { + auto toolchain = getToolChain(config.system); auto compiler_flags = flags(); std::vector<std::string> args; - args.push_back("-MMD"); - if(std::filesystem::path(config.target).extension() == ".so") + switch(sourceLanguage()) { - // Add -fPIC arg to all contained object files - args.push_back("-fPIC"); - } + case ctor::language::c: + { + append(args, c_option(toolchain, ctor::c_opt::generate_dep_tree)); - args.push_back("-c"); - args.push_back(sourceFile.string()); - args.push_back("-o"); - args.push_back(targetFile().string()); + if(std::filesystem::path(config.target).extension() == ".so") + { + // Add -fPIC arg to all contained object files + append(args, c_option(toolchain, + ctor::c_opt::position_independent_code)); + } - for(const auto& flag : compiler_flags) - { - // Is arg an added include path? - if(flag.substr(0, 2) == "-I") + append(args, c_option(toolchain, ctor::c_opt::no_link)); + args.push_back(sourceFile.string()); + append(args, c_option(toolchain, + ctor::c_opt::output, targetFile().string())); + + // Relative include paths has to be altered to be relative to sourceDir + for(const auto& flag : compiler_flags) + { + auto option = c_option(flag, toolchain); + switch(option.opt) + { + case ctor::c_opt::include_path: + { + std::filesystem::path path(option.arg); + if(path.is_relative()) + { + path = (sourceDir / path).lexically_normal(); + append(args, c_option(toolchain, + ctor::c_opt::include_path, path.string())); + } + } + break; + default: + break; + } + + args.push_back(flag); + } + } + break; + + case ctor::language::cpp: { - std::string include_path = flag.substr(2); - include_path.erase(0, include_path.find_first_not_of(' ')); - std::filesystem::path path(include_path); + append(args, cxx_option(toolchain, ctor::cxx_opt::generate_dep_tree)); + + if(std::filesystem::path(config.target).extension() == ".so") + { + // Add -fPIC arg to all contained object files + append(args, cxx_option(toolchain, + ctor::cxx_opt::position_independent_code)); + } - // Is it relative? - if(path.is_relative()) + append(args, cxx_option(toolchain, ctor::cxx_opt::no_link)); + args.push_back(sourceFile.string()); + append(args, cxx_option(toolchain, + ctor::cxx_opt::output, targetFile().string())); + + // Relative include paths has to be altered to be relative to sourceDir + for(const auto& flag : compiler_flags) { - path = (sourceDir / path).lexically_normal(); - std::string new_include_path = "-I" + path.string(); - args.push_back(new_include_path); - continue; + auto option = cxx_option(flag, toolchain); + switch(option.opt) + { + case ctor::cxx_opt::include_path: + { + std::filesystem::path path(option.arg); + if(path.is_relative()) + { + path = (sourceDir / path).lexically_normal(); + append(args, cxx_option(toolchain, + ctor::cxx_opt::include_path, path.string())); + } + } + break; + default: + break; + } + + args.push_back(flag); } + } + break; - args.push_back(flag); + default: + break; } return args; diff --git a/src/task_cc.h b/src/task_cc.h index 0a0d96a..70bb13c 100644 --- a/src/task_cc.h +++ b/src/task_cc.h @@ -10,16 +10,13 @@ #include <future> #include <filesystem> -struct BuildConfiguration; -struct Settings; - class TaskCC : public Task { public: - TaskCC(const BuildConfiguration& config, - const Settings& settings, - const std::string& sourceDir, const Source& source); + TaskCC(const ctor::build_configuration& config, + const ctor::settings& settings, + const std::string& sourceDir, const ctor::source& source); virtual ~TaskCC() = default; int registerDepTasksInner(const std::set<std::shared_ptr<Task>>& tasks) override; @@ -51,8 +48,8 @@ protected: std::filesystem::path depsFile; std::filesystem::path flagsFile; - const BuildConfiguration& config; - const Settings& settings; + const ctor::build_configuration& config; + const ctor::settings& settings; std::filesystem::path sourceDir; - const Source& _source; + const ctor::source& _source; }; diff --git a/src/task_fn.cc b/src/task_fn.cc index ab00fae..b11ff15 100644 --- a/src/task_fn.cc +++ b/src/task_fn.cc @@ -7,12 +7,12 @@ #include <fstream> #include <cassert> -#include "libctor.h" +#include "ctor.h" #include "execute.h" #include "util.h" -TaskFn::TaskFn(const BuildConfiguration& config, const Settings& settings, - const std::string& sourceDir, const Source& source) +TaskFn::TaskFn(const ctor::build_configuration& config, const ctor::settings& settings, + const std::string& sourceDir, const ctor::source& source) : Task(config, settings, sourceDir) , config(config) , settings(settings) @@ -70,7 +70,7 @@ int TaskFn::runInner() { std::cout << "Fn" << " " << sourceFile.lexically_normal().string() << " => " << - targetFile().lexically_normal().string() << "\n"; + targetFile().lexically_normal().string() << std::endl; } return config.function(sourceFile.string(), diff --git a/src/task_fn.h b/src/task_fn.h index e350395..1bad32c 100644 --- a/src/task_fn.h +++ b/src/task_fn.h @@ -10,16 +10,13 @@ #include <future> #include <filesystem> -struct BuildConfiguration; -struct Settings; - class TaskFn : public Task { public: - TaskFn(const BuildConfiguration& config, - const Settings& settings, - const std::string& sourceDir, const Source& source); + TaskFn(const ctor::build_configuration& config, + const ctor::settings& settings, + const std::string& sourceDir, const ctor::source& source); virtual ~TaskFn() = default; bool dirtyInner() override; @@ -41,7 +38,7 @@ protected: std::filesystem::path sourceFile; std::filesystem::path _targetFile; - const BuildConfiguration& config; - const Settings& settings; + const ctor::build_configuration& config; + const ctor::settings& settings; std::filesystem::path sourceDir; }; diff --git a/src/task_ld.cc b/src/task_ld.cc index 3e05fb1..b0aa4ae 100644 --- a/src/task_ld.cc +++ b/src/task_ld.cc @@ -6,12 +6,13 @@ #include <iostream> #include <fstream> -#include "libctor.h" +#include "ctor.h" #include "execute.h" #include "util.h" +#include "tools.h" -TaskLD::TaskLD(const BuildConfiguration& config, - const Settings& settings, +TaskLD::TaskLD(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& target, const std::vector<std::string>& objects, const std::string& sourceDir) @@ -21,14 +22,16 @@ TaskLD::TaskLD(const BuildConfiguration& config, , sourceDir(sourceDir) { target_type = config.type; - if(target_type == TargetType::Auto) + output_system = config.system; + + if(target_type == ctor::target_type::automatic) { - target_type = TargetType::Executable; + target_type = ctor::target_type::executable; } - std::filesystem::create_directories(std::filesystem::path(settings.builddir) / sourceDir); - _targetFile = target; + auto toolchain = getToolChain(config.system); + _targetFile = extension(toolchain, target_type, config.system, _targetFile); for(const auto& object : objects) { std::filesystem::path objectFile = object; @@ -44,15 +47,17 @@ TaskLD::TaskLD(const BuildConfiguration& config, flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem(); flagsFile += ".flags"; - source_language = Language::C; + source_language = ctor::language::c; for(const auto& source : config.sources) { std::filesystem::path sourceFile(source.file); if(sourceFile.extension().string() != ".c") { - source_language = Language::Cpp; + source_language = ctor::language::cpp; } } + + std::filesystem::create_directories(targetFile().parent_path()); } bool TaskLD::dirtyInner() @@ -81,17 +86,22 @@ bool TaskLD::dirtyInner() int TaskLD::runInner() { + auto toolchain = getToolChain(config.system); + std::vector<std::string> args; for(const auto& dep : getDependsTasks()) { auto depFile = dep->targetFile(); - if(depFile.extension() == ".so") + auto dep_type = target_type_from_extension(toolchain, depFile); + if(dep_type == ctor::target_type::dynamic_library) { - args.push_back(std::string("-L") + settings.builddir); + append(args, ld_option(toolchain, ctor::ld_opt::library_path, + targetFile().parent_path().string())); auto lib = depFile.stem().string().substr(3); // strip 'lib' prefix - args.push_back(std::string("-l") + lib); + append(args, ld_option(toolchain, ctor::ld_opt::link, lib)); } - else if(depFile.extension() == ".a" || depFile.extension() == ".o") + else if(dep_type == ctor::target_type::static_library || + dep_type == ctor::target_type::object) { args.push_back(depFile.string()); } @@ -99,10 +109,10 @@ int TaskLD::runInner() for(const auto& flag : config.flags.ldflags) { - args.push_back(flag); + append(args, to_strings(toolchain, flag)); } - args.push_back("-o"); - args.push_back(targetFile().string()); + + append(args, ld_option(toolchain, ctor::ld_opt::output, targetFile().string())); { // Write flags to file. std::ofstream flagsStream(flagsFile); @@ -111,11 +121,11 @@ int TaskLD::runInner() if(settings.verbose == 0) { - std::cout << "LD => " << targetFile().string() << "\n"; + std::cout << "LD => " << targetFile().string() << std::endl; } auto tool = compiler(); - return execute(tool, args, settings.verbose > 0); + return execute(tool, args, {}, settings.verbose > 0); } int TaskLD::clean() @@ -168,14 +178,20 @@ bool TaskLD::derived() const std::string TaskLD::flagsString() const { + auto toolchain = getToolChain(config.system); std::string flagsStr; + bool first{true}; for(const auto& flag : config.flags.ldflags) { - if(flag != config.flags.ldflags[0]) + for(const auto& str : to_strings(toolchain, flag)) { - flagsStr += " "; + if(first) + { + flagsStr += " "; + first = false; + } + flagsStr += str; } - flagsStr += flag; } flagsStr += "\n"; diff --git a/src/task_ld.h b/src/task_ld.h index 8625075..dbe7db1 100644 --- a/src/task_ld.h +++ b/src/task_ld.h @@ -10,15 +10,12 @@ #include <future> #include <filesystem> -struct BuildConfiguration; -struct Settings; - class TaskLD : public Task { public: - TaskLD(const BuildConfiguration& config, - const Settings& settings, + TaskLD(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& target, const std::vector<std::string>& objects, const std::string& _sourceDir); @@ -44,7 +41,7 @@ private: std::filesystem::path _targetFile; std::filesystem::path flagsFile; - const BuildConfiguration& config; - const Settings& settings; + const ctor::build_configuration& config; + const ctor::settings& settings; std::string sourceDir; }; diff --git a/src/task_so.cc b/src/task_so.cc index 1af14b5..ba96388 100644 --- a/src/task_so.cc +++ b/src/task_so.cc @@ -6,12 +6,13 @@ #include <iostream> #include <fstream> -#include "libctor.h" +#include "ctor.h" #include "execute.h" #include "util.h" +#include "tools.h" -TaskSO::TaskSO(const BuildConfiguration& config, - const Settings& settings, +TaskSO::TaskSO(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& target, const std::vector<std::string>& objects, const std::string& sourceDir) @@ -21,9 +22,13 @@ TaskSO::TaskSO(const BuildConfiguration& config, , sourceDir(sourceDir) { std::filesystem::path base = sourceDir; - std::filesystem::create_directories(std::filesystem::path(settings.builddir) / base); + + target_type = ctor::target_type::dynamic_library; + output_system = config.system; _targetFile = base / target; + auto toolchain = getToolChain(config.system); + _targetFile = extension(toolchain, target_type, config.system, _targetFile); for(const auto& object : objects) { std::filesystem::path objectFile = object; @@ -33,25 +38,24 @@ TaskSO::TaskSO(const BuildConfiguration& config, for(const auto& dep : config.depends) { - std::filesystem::path depFile = settings.builddir; - depFile /= dep; - depFiles.push_back(depFile); + depFiles.push_back(dep); } flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem(); flagsFile += ".flags"; - target_type = TargetType::DynamicLibrary; - source_language = Language::C; + source_language = ctor::language::c; for(const auto& source : config.sources) { std::filesystem::path sourceFile(source.file); // TODO: Use task languages instead if(sourceFile.extension().string() != ".c") { - source_language = Language::Cpp; + source_language = ctor::language::cpp; } } + + std::filesystem::create_directories(targetFile().parent_path()); } bool TaskSO::dirtyInner() @@ -80,27 +84,23 @@ bool TaskSO::dirtyInner() int TaskSO::runInner() { + auto toolchain = getToolChain(config.system); + std::vector<std::string> args; - args.push_back("-fPIC"); - args.push_back("-shared"); + append(args, ld_option(toolchain, ctor::ld_opt::position_independent_code)); + append(args, ld_option(toolchain, ctor::ld_opt::build_shared)); - args.push_back("-o"); - args.push_back(targetFile().string()); + append(args, ld_option(toolchain, ctor::ld_opt::output, targetFile().string())); for(const auto& task : getDependsTasks()) { - args.push_back(task->target()); - } - - for(const auto& depFile : depFiles) - { - args.push_back(depFile.string()); + args.push_back(task->targetFile().string()); } for(const auto& flag : config.flags.ldflags) { - args.push_back(flag); + append(args, to_strings(toolchain, flag)); } { // Write flags to file. @@ -110,11 +110,11 @@ int TaskSO::runInner() if(settings.verbose == 0) { - std::cout << "LD => " << targetFile().string() << "\n"; + std::cout << "LD => " << targetFile().string() << std::endl; } auto tool = compiler(); - return execute(tool, args, settings.verbose > 0); + return execute(tool, args, {}, settings.verbose > 0); } int TaskSO::clean() @@ -167,10 +167,20 @@ bool TaskSO::derived() const std::string TaskSO::flagsString() const { - std::string flagsStr = compiler(); + auto toolchain = getToolChain(config.system); + std::string flagsStr; + bool first{true}; for(const auto& flag : config.flags.ldflags) { - flagsStr += " " + flag; + for(const auto& str : to_strings(toolchain, flag)) + { + if(first) + { + flagsStr += " "; + first = false; + } + flagsStr += str; + } } flagsStr += "\n"; diff --git a/src/task_so.h b/src/task_so.h index fe5d2fd..60af225 100644 --- a/src/task_so.h +++ b/src/task_so.h @@ -10,15 +10,12 @@ #include <future> #include <filesystem> -struct BuildConfiguration; -struct Settings; - class TaskSO : public Task { public: - TaskSO(const BuildConfiguration& config, - const Settings& settings, + TaskSO(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& target, const std::vector<std::string>& objects, const std::string& sourceDir); @@ -44,7 +41,7 @@ private: std::filesystem::path _targetFile; std::filesystem::path flagsFile; - const BuildConfiguration& config; - const Settings& settings; + const ctor::build_configuration& config; + const ctor::settings& settings; std::string sourceDir; }; diff --git a/src/tasks.cc b/src/tasks.cc index 68b2476..22ad2d6 100644 --- a/src/tasks.cc +++ b/src/tasks.cc @@ -9,7 +9,7 @@ #include <iostream> #include <algorithm> -#include "libctor.h" +#include "ctor.h" #include "task.h" #include "task_cc.h" #include "task_ld.h" @@ -18,15 +18,17 @@ #include "task_fn.h" #include "rebuild.h" #include "configure.h" +#include "util.h" +#include "tools.h" -const std::deque<Target>& getTargets(const Settings& settings, +const std::deque<Target>& getTargets(const ctor::settings& settings, bool resolve_externals) { static bool initialised{false}; static std::deque<Target> targets; if(!initialised) { - const auto& externals = configuration().externals; + const auto& externals = ctor::get_configuration().externals; for(std::size_t i = 0; i < numConfigFiles; ++i) { std::string path = @@ -50,21 +52,12 @@ const std::deque<Target>& getTargets(const Settings& settings, exit(1); } const auto& flags = externals.at(external); - config.flags.cflags.insert(config.flags.cflags.end(), - flags.cflags.begin(), - flags.cflags.end()); - config.flags.cxxflags.insert(config.flags.cxxflags.end(), - flags.cxxflags.begin(), - flags.cxxflags.end()); - config.flags.ldflags.insert(config.flags.ldflags.end(), - flags.ldflags.begin(), - flags.ldflags.end()); - config.flags.asmflags.insert(config.flags.asmflags.end(), - flags.asmflags.begin(), - flags.asmflags.end()); - //config.libs.insert(config.libs.end(), - // libs.begin(), - // libs.end()); + append(config.flags.cflags, flags.cflags); + append(config.flags.cxxflags, flags.cxxflags); + append(config.flags.ldflags, flags.ldflags); + append(config.flags.arflags, flags.arflags); + append(config.flags.asmflags, flags.asmflags); + //append(config.libs.insert(config.libs libs); } } @@ -77,43 +70,29 @@ const std::deque<Target>& getTargets(const Settings& settings, return targets; } -std::set<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config, - const Settings& settings, +std::set<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& sourceDir) { std::set<std::shared_ptr<Task>> tasks; std::filesystem::path targetFile(config.target); - TargetType target_type{config.type}; - if(target_type == TargetType::Auto) + ctor::target_type target_type{config.type}; + if(target_type == ctor::target_type::automatic) { if(config.function != nullptr) { - target_type = TargetType::Function; - } - else if(targetFile.extension() == ".a") - { - target_type = TargetType::StaticLibrary; - } - else if(targetFile.extension() == ".so") - { - target_type = TargetType::DynamicLibrary; - } - else if(targetFile.extension() == "") - { - target_type = TargetType::Executable; + target_type = ctor::target_type::function; } else { - std::cerr << "Could not deduce target type from target " << - targetFile.string() << " please specify.\n"; - exit(1); + target_type = target_type_from_extension(ctor::toolchain::any, targetFile); } } std::vector<std::string> objects; - if(target_type != TargetType::Function) + if(target_type != ctor::target_type::function) { for(const auto& file : config.sources) { @@ -136,17 +115,23 @@ std::set<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config, switch(target_type) { - case TargetType::Auto: + case ctor::target_type::automatic: // The target_type cannot be Auto break; - case TargetType::StaticLibrary: - case TargetType::UnitTestLib: + case ctor::target_type::unknown: + std::cerr << "Could not deduce target type from target " << + targetFile.string() << " please specify.\n"; + exit(1); + break; + + case ctor::target_type::static_library: + case ctor::target_type::unit_test_library: tasks.insert(std::make_shared<TaskAR>(config, settings, config.target, objects, sourceDir)); break; #ifndef BOOTSTRAP - case TargetType::DynamicLibrary: + case ctor::target_type::dynamic_library: // TODO: Use C++20 starts_with if(targetFile.stem().string().substr(0, 3) != "lib") { @@ -157,14 +142,14 @@ std::set<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config, objects, sourceDir)); break; - case TargetType::Executable: - case TargetType::UnitTest: + case ctor::target_type::executable: + case ctor::target_type::unit_test: tasks.insert(std::make_shared<TaskLD>(config, settings, config.target, objects, sourceDir)); break; - case TargetType::Object: - case TargetType::Function: + case ctor::target_type::object: + case ctor::target_type::function: break; #else default: @@ -194,7 +179,7 @@ std::shared_ptr<Task> getNextTask(const std::set<std::shared_ptr<Task>>& allTask return nullptr; } -std::set<std::shared_ptr<Task>> getTasks(const Settings& settings, +std::set<std::shared_ptr<Task>> getTasks(const ctor::settings& settings, const std::vector<std::string> names, bool resolve_externals) { diff --git a/src/tasks.h b/src/tasks.h index c547432..ed1bf60 100644 --- a/src/tasks.h +++ b/src/tasks.h @@ -10,17 +10,14 @@ #include "task.h" -class BuildConfiguration; -class Settings; - struct Target { - BuildConfiguration config; + ctor::build_configuration config; std::string path; }; //! Get list of all registered targets -const std::deque<Target>& getTargets(const Settings& settings, +const std::deque<Target>& getTargets(const ctor::settings& settings, bool resolve_externals = true); //! Returns next dirty task from the dirtyTasks list that has all its dependencies @@ -32,12 +29,12 @@ std::shared_ptr<Task> getNextTask(const std::set<std::shared_ptr<Task>>& allTask //! Get list of tasks filtered by name including each of their direct //! dependency tasks (ie. objects tasks from their sources). -std::set<std::shared_ptr<Task>> getTasks(const Settings& settings, +std::set<std::shared_ptr<Task>> getTasks(const ctor::settings& settings, const std::vector<std::string> names = {}, bool resolve_externals = true); //! Generate list of targets from a single configuration, including the final //! link target and all its objects files (if any). -std::set<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config, - const Settings& settings, +std::set<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& sourceDir); diff --git a/src/tools.cc b/src/tools.cc new file mode 100644 index 0000000..be94794 --- /dev/null +++ b/src/tools.cc @@ -0,0 +1,942 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#include "tools.h" + +#include <filesystem> +#include <iostream> +#include <sstream> + +#include <cassert> +#include <cstdio> + +#include "util.h" + +std::ostream& operator<<(std::ostream& stream, const ctor::c_opt& opt) +{ + // Adding to this enum should also imply adding to the unit-tests below + switch(opt) + { + case ctor::c_opt::output: stream << "ctor::c_opt::output"; break; + case ctor::c_opt::debug: stream << "ctor::c_opt::debug"; break; + case ctor::c_opt::warn_all: stream << "ctor::c_opt::warn_all"; break; + case ctor::c_opt::warnings_as_errors: stream << "ctor::c_opt::warnings_as_errors"; break; + case ctor::c_opt::generate_dep_tree: stream << "ctor::c_opt::generate_dep_tree"; break; + case ctor::c_opt::no_link: stream << "ctor::c_opt::no_link"; break; + case ctor::c_opt::include_path: stream << "ctor::c_opt::include_path"; break; + case ctor::c_opt::c_std: stream << "ctor::c_opt::c_std"; break; + case ctor::c_opt::optimization: stream << "ctor::c_opt::optimization"; break; + case ctor::c_opt::position_independent_code: stream << "ctor::c_opt::position_independent_code"; break; + case ctor::c_opt::position_independent_executable: stream << "ctor::c_opt::position_independent_executable"; break; + case ctor::c_opt::custom: stream << "ctor::c_opt::custom"; break; + } + + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::cxx_opt& opt) +{ + // Adding to this enum should also imply adding to the unit-tests below + switch(opt) + { + case ctor::cxx_opt::output: stream << "ctor::cxx_opt::output"; break; + case ctor::cxx_opt::debug: stream << "ctor::cxx_opt::debug"; break; + case ctor::cxx_opt::warn_all: stream << "ctor::cxx_opt::warn_all"; break; + case ctor::cxx_opt::warnings_as_errors: stream << "ctor::cxx_opt::warnings_as_errors"; break; + case ctor::cxx_opt::generate_dep_tree: stream << "ctor::cxx_opt::generate_dep_tree"; break; + case ctor::cxx_opt::no_link: stream << "ctor::cxx_opt::no_link"; break; + case ctor::cxx_opt::include_path: stream << "ctor::cxx_opt::include_path"; break; + case ctor::cxx_opt::cpp_std: stream << "ctor::cxx_opt::cpp_std"; break; + case ctor::cxx_opt::optimization: stream << "ctor::cxx_opt::optimization"; break; + case ctor::cxx_opt::position_independent_code: stream << "ctor::cxx_opt::position_independent_code"; break; + case ctor::cxx_opt::position_independent_executable: stream << "ctor::cxx_opt::position_independent_executable"; break; + case ctor::cxx_opt::custom: stream << "ctor::cxx_opt::custom"; break; + } + + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::ld_opt& opt) +{ + // Adding to this enum should also imply adding to the unit-tests below + switch(opt) + { + case ctor::ld_opt::output: stream << "ctor::ld_opt::output"; break; + case ctor::ld_opt::strip: stream << "ctor::ld_opt::strip"; break; + case ctor::ld_opt::warn_all: stream << "ctor::ld_opt::warn_all"; break; + case ctor::ld_opt::warnings_as_errors: stream << "ctor::ld_opt::warnings_as_errors"; break; + case ctor::ld_opt::library_path: stream << "ctor::ld_opt::library_path"; break; + case ctor::ld_opt::link: stream << "ctor::ld_opt::link"; break; + case ctor::ld_opt::cpp_std: stream << "ctor::ld_opt::cpp_std"; break; + case ctor::ld_opt::build_shared: stream << "ctor::ld_opt::build_shared"; break; + case ctor::ld_opt::threads: stream << "ctor::ld_opt::threads"; break; + case ctor::ld_opt::position_independent_code: stream << "ctor::ld_opt::position_independent_code"; break; + case ctor::ld_opt::position_independent_executable: stream << "ctor::ld_opt::position_independent_executable"; break; + case ctor::ld_opt::custom: stream << "ctor::ld_opt::custom"; break; + } + + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::ar_opt& opt) +{ + // Adding to this enum should also imply adding to the unit-tests below + switch(opt) + { + case ctor::ar_opt::replace: stream << "ctor::ar_opt::replace"; break; + case ctor::ar_opt::add_index: stream << "ctor::ar_opt::add_index"; break; + case ctor::ar_opt::create: stream << "ctor::ar_opt::create"; break; + case ctor::ar_opt::output: stream << "ctor::ar_opt::output"; break; + case ctor::ar_opt::custom: stream << "ctor::ar_opt::custom"; break; + } + + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::asm_opt& opt) +{ + // Adding to this enum should also imply adding to the unit-tests below + switch(opt) + { + case ctor::asm_opt::custom: stream << "ctor::asm_opt::custom"; break; + } + + return stream; +} + +ctor::toolchain getToolChain(const std::string& compiler) +{ + std::filesystem::path cc(compiler); + auto cc_cmd = cc.stem().string(); + + // Note: "g++" is a substring of "clang++" so "clang++" must be tested first. + if(cc_cmd.find("clang++") != std::string::npos || + cc_cmd.find("clang") != std::string::npos) + { + return ctor::toolchain::clang; + } + else if(cc_cmd.find("g++") != std::string::npos || + cc_cmd.find("gcc") != std::string::npos || + cc_cmd.find("mingw") != std::string::npos) + { + return ctor::toolchain::gcc; + } + + std::cerr << "Unsupported output system.\n"; + return ctor::toolchain::gcc; +} + +ctor::toolchain getToolChain(ctor::output_system system) +{ + const auto& cfg = ctor::get_configuration(); + if(system == ctor::output_system::host) + { + if(cfg.host_toolchain != ctor::toolchain::none) + { + return cfg.host_toolchain; + } + return getToolChain(cfg.get(ctor::cfg::host_cxx, "g++")); + } + else + { + if(cfg.build_toolchain != ctor::toolchain::none) + { + return cfg.build_toolchain; + } + return getToolChain(cfg.get(ctor::cfg::build_cxx, "g++")); + } +} + +namespace gcc { +std::string get_arch(ctor::output_system system) +{ + std::string cmd; + + const auto& c = ctor::get_configuration(); + switch(system) + { + case ctor::output_system::host: + cmd = c.get(ctor::cfg::host_cxx, "/usr/bin/g++"); + break; + case ctor::output_system::build: + cmd = c.get(ctor::cfg::build_cxx, "/usr/bin/g++"); + break; + } + + cmd += " -v"; + cmd += " 2>&1"; + auto pipe = popen(cmd.data(), "r"); + if(!pipe) + { + std::cerr << "Could not run compiler " << cmd << "\n"; + return {};//ctor::arch::unknown; + } + + std::string arch; + while(!feof(pipe)) + { + char buf[1024]; + if(fgets(buf, sizeof(buf), pipe) != nullptr) + { + arch = buf; + if(arch.starts_with("Target:")) + { + break; + } + } + } + + pclose(pipe); + + // Remove trailing newline + if(arch.ends_with("\n")) + { + arch = arch.substr(0, arch.length() - 1); + } + + // Remove 'Target: ' prefix + arch = arch.substr(8); + return arch; +} + +ctor::arch get_arch(const std::string& str) +{ + // gcc -v 2>&1 | grep Target + // Target: x86_64-apple-darwin19.6.0 + // Target: x86_64-pc-linux-gnu + // Target: arm-linux-gnueabihf + // Target: x86_64-linux-gnu + // Target: x86_64-unknown-freebsd13.1 + // # x86_64-w64-mingw32-c++-win32 -v 2>&1 | grep Target + // Target: x86_64-w64-mingw32 + // # i686-w64-mingw32-c++-win32 -v 2>&1 | grep Target + // Target: i686-w64-mingw32 + + if(str.find("apple") != std::string::npos || + str.find("darwin") != std::string::npos) + { + return ctor::arch::apple; + } + + if(str.find("linux") != std::string::npos || + str.find("bsd") != std::string::npos) + { + return ctor::arch::unix; + } + + if(str.find("mingw") != std::string::npos) + { + return ctor::arch::windows; + } + + std::cerr << "Could not deduce gcc arch from '" << str << "'" << std::endl; + + return ctor::arch::unknown; +} + +ctor::c_flag c_option(const std::string& flag) +{ + if(flag.starts_with("-I")) + { + std::string path = flag.substr(2); + path.erase(0, path.find_first_not_of(' ')); + return { ctor::c_opt::include_path, path }; + } + + return { ctor::c_opt::custom, flag }; +} + +ctor::cxx_flag cxx_option(const std::string& flag) +{ + if(flag.starts_with("-I")) + { + std::string path = flag.substr(2); + path.erase(0, path.find_first_not_of(' ')); + return { ctor::cxx_opt::include_path, path }; + } + + return { ctor::cxx_opt::custom, flag }; +} + +ctor::ld_flag ld_option(const std::string& flag) +{ + if(flag.starts_with("-L")) + { + std::string path = flag.substr(2); + path.erase(0, path.find_first_not_of(' ')); + return { ctor::ld_opt::library_path, path }; + } + + return { ctor::ld_opt::custom, flag }; +} + +ctor::ar_flag ar_option(const std::string& flag) +{ + return { ctor::ar_opt::custom, flag }; +} + +std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg) +{ + switch(opt) + { + case ctor::cxx_opt::output: + return {"-o", arg}; + case ctor::cxx_opt::debug: + return {"-g"}; + case ctor::cxx_opt::warn_all: + return {"-Wall"}; + case ctor::cxx_opt::warnings_as_errors: + return {"-Werror"}; + case ctor::cxx_opt::generate_dep_tree: + return {"-MMD"}; + case ctor::cxx_opt::no_link: + return {"-c"}; + case ctor::cxx_opt::include_path: + return {"-I" + arg}; + case ctor::cxx_opt::cpp_std: + return {"-std=" + arg}; + case ctor::cxx_opt::optimization: + return {"-O" + arg}; + case ctor::cxx_opt::position_independent_code: + return {"-fPIC"}; + case ctor::cxx_opt::position_independent_executable: + return {"-fPIE"}; + case ctor::cxx_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} + +std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg) +{ + switch(opt) + { + case ctor::c_opt::output: + return {"-o", arg}; + case ctor::c_opt::debug: + return {"-g"}; + case ctor::c_opt::warn_all: + return {"-Wall"}; + case ctor::c_opt::warnings_as_errors: + return {"-Werror"}; + case ctor::c_opt::generate_dep_tree: + return {"-MMD"}; + case ctor::c_opt::no_link: + return {"-c"}; + case ctor::c_opt::include_path: + return {"-I" + arg}; + case ctor::c_opt::c_std: + return {"-std=" + arg}; + case ctor::c_opt::optimization: + return {"-O" + arg}; + case ctor::c_opt::position_independent_code: + return {"-fPIC"}; + case ctor::c_opt::position_independent_executable: + return {"-fPIE"}; + case ctor::c_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} + +std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg) +{ + switch(opt) + { + case ctor::ld_opt::output: + return {"-o", arg}; + case ctor::ld_opt::strip: + return {"-s"}; + case ctor::ld_opt::warn_all: + return {"-Wall"}; + case ctor::ld_opt::warnings_as_errors: + return {"-Werror"}; + case ctor::ld_opt::library_path: + return {"-L" + arg}; + case ctor::ld_opt::link: + return {"-l" + arg}; + case ctor::ld_opt::cpp_std: + return {"-std=" + arg}; + case ctor::ld_opt::build_shared: + return {"-shared"}; + case ctor::ld_opt::threads: + return {"-pthread"}; + case ctor::ld_opt::position_independent_code: + return {"-fPIC"}; + case ctor::ld_opt::position_independent_executable: + return {"-fPIE"}; + case ctor::ld_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} + +std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg) +{ + switch(opt) + { + case ctor::ar_opt::replace: + return {"-r"}; + case ctor::ar_opt::add_index: + return {"-s"}; + case ctor::ar_opt::create: + return {"-c"}; + case ctor::ar_opt::output: + return {arg}; + case ctor::ar_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} + +std::vector<std::string> asm_option(ctor::asm_opt opt, const std::string& arg) +{ + switch(opt) + { + case ctor::asm_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} +} // gcc:: + +std::string get_arch(ctor::output_system system) +{ + auto toolchain = getToolChain(system); + switch(toolchain) + { + case ctor::toolchain::clang: + case ctor::toolchain::gcc: + return gcc::get_arch(system); + case ctor::toolchain::any: + case ctor::toolchain::none: + break; + } + return {}; +} + +ctor::arch get_arch(ctor::output_system system, const std::string& str) +{ + auto toolchain = getToolChain(system); + switch(toolchain) + { + case ctor::toolchain::clang: + case ctor::toolchain::gcc: + return gcc::get_arch(str); + case ctor::toolchain::any: + case ctor::toolchain::none: + break; + } + return ctor::arch::unknown; +} + +std::vector<std::string> c_option(ctor::toolchain toolchain, + ctor::c_opt opt, + const std::string& arg) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::c_option(opt, arg); + case ctor::toolchain::any: + { + std::ostringstream ss; + ss << "{" << opt; + if(!arg.empty()) + { + ss << ", \"" << arg << "\""; + } + ss << "}"; + return { ss.str() }; + } + case ctor::toolchain::none: + break; + } + + std::cerr << "Unsupported tool-chain.\n"; + return {}; +} + +std::vector<std::string> cxx_option(ctor::toolchain toolchain, + ctor::cxx_opt opt, + const std::string& arg) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::cxx_option(opt, arg); + case ctor::toolchain::any: + { + std::ostringstream ss; + ss << "{" << opt; + if(!arg.empty()) + { + ss << ", \"" << arg << "\""; + } + ss << "}"; + return { ss.str() }; + } + case ctor::toolchain::none: + break; + } + + std::cerr << "Unsupported tool-chain.\n"; + return {}; +} + +std::vector<std::string> ld_option(ctor::toolchain toolchain, + ctor::ld_opt opt, + const std::string& arg) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::ld_option(opt, arg); + case ctor::toolchain::any: + { + std::ostringstream ss; + ss << "{" << opt; + if(!arg.empty()) + { + ss << ", \"" << arg << "\""; + } + ss << "}"; + return { ss.str() }; + } + case ctor::toolchain::none: + break; + } + + std::cerr << "Unsupported tool-chain.\n"; + return {}; +} + +std::vector<std::string> ar_option(ctor::toolchain toolchain, + ctor::ar_opt opt, + const std::string& arg) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::ar_option(opt, arg); + case ctor::toolchain::any: + { + std::ostringstream ss; + ss << "{" << opt; + if(!arg.empty()) + { + ss << ", \"" << arg << "\""; + } + ss << "}"; + return { ss.str() }; + } + case ctor::toolchain::none: + break; + } + + std::cerr << "Unsupported tool-chain.\n"; + return {}; +} + +std::vector<std::string> asm_option(ctor::toolchain toolchain, + ctor::asm_opt opt, + const std::string& arg) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::asm_option(opt, arg); + case ctor::toolchain::any: + { + std::ostringstream ss; + ss << "{" << opt; + if(!arg.empty()) + { + ss << ", \"" << arg << "\""; + } + ss << "}"; + return { ss.str() }; + } + case ctor::toolchain::none: + break; + } + + std::cerr << "Unsupported tool-chain.\n"; + return {}; +} + + +ctor::c_flag c_option(const std::string& flag, ctor::toolchain toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::c_option(flag); + case ctor::toolchain::any: + case ctor::toolchain::none: + break; + } + + return { ctor::c_opt::custom, flag }; +} + +ctor::cxx_flag cxx_option(const std::string& flag, ctor::toolchain toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::cxx_option(flag); + case ctor::toolchain::any: + case ctor::toolchain::none: + break; + } + + return { ctor::cxx_opt::custom, flag }; +} + +ctor::ld_flag ld_option(const std::string& flag, ctor::toolchain toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::ld_option(flag); + case ctor::toolchain::any: + case ctor::toolchain::none: + break; + } + + return { ctor::ld_opt::custom, flag }; +} + +ctor::ar_flag ar_option(const std::string& flag, ctor::toolchain toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::ar_option(flag); + case ctor::toolchain::any: + case ctor::toolchain::none: + break; + } + + return { ctor::ar_opt::custom, flag }; +} + +ctor::asm_flag asm_option(const std::string& flag, ctor::toolchain toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + case ctor::toolchain::any: + case ctor::toolchain::none: + break; + } + + return { ctor::asm_opt::custom, flag }; +} + +// Flag to string coversions + +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::c_flag& flag) +{ + if(flag.toolchain == ctor::toolchain::any || + flag.toolchain == toolchain) + { + return c_option(toolchain, flag.opt, flag.arg); + } + + return {}; +} + +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::cxx_flag& flag) +{ + if(flag.toolchain == ctor::toolchain::any || + flag.toolchain == toolchain) + { + return cxx_option(toolchain, flag.opt, flag.arg); + } + + return {}; +} + +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::ld_flag& flag) +{ + if(flag.toolchain == ctor::toolchain::any || + flag.toolchain == toolchain) + { + return ld_option(toolchain, flag.opt, flag.arg); + } + + return {}; +} + +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::ar_flag& flag) +{ + if(flag.toolchain == ctor::toolchain::any || + flag.toolchain == toolchain) + { + return ar_option(toolchain, flag.opt, flag.arg); + } + + return {}; +} + +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::asm_flag& flag) +{ + if(flag.toolchain == ctor::toolchain::any || + flag.toolchain == toolchain) + { + return asm_option(toolchain, flag.opt, flag.arg); + } + + return {}; +} + +namespace { +ctor::toolchain guess_toolchain(const std::string& opt) +{ + if(opt.empty()) + { + return ctor::toolchain::any; + } + + if(opt[0] == '-') + { + return ctor::toolchain::gcc; + } + + //if(opt[0] == '/') + //{ + // return ctor::toolchain::msvc; + //} + return ctor::toolchain::any; +} +} + +template<> +ctor::flag<ctor::c_opt>::flag(const char* str) +{ + *this = c_option(str, guess_toolchain(str)); +} + +template<> +ctor::flag<ctor::cxx_opt>::flag(const char* str) +{ + *this = cxx_option(str, guess_toolchain(str)); +} + +template<> +ctor::flag<ctor::ld_opt>::flag(const char* str) +{ + *this = ld_option(str, guess_toolchain(str)); +} + +template<> +ctor::flag<ctor::ar_opt>::flag(const char* str) +{ + *this = ar_option(str, guess_toolchain(str)); +} + +template<> +ctor::flag<ctor::asm_opt>::flag(const char* str) +{ + *this = asm_option(str, guess_toolchain(str)); +} + + +ctor::target_type target_type_from_extension(ctor::toolchain toolchain, + const std::filesystem::path& file) +{ + auto ext = to_lower(file.extension().string()); + // Loosely based on: + // https://en.wikipedia.org/wiki/List_of_file_formats#Object_code,_executable_files,_shared_and_dynamically_linked_libraries + if(toolchain == ctor::toolchain::any || + toolchain == ctor::toolchain::gcc || + toolchain == ctor::toolchain::clang) + { + if(ext == ".a") + { + return ctor::target_type::static_library; + } + + if(ext == ".so" || + ext == ".dylib") + { + return ctor::target_type::dynamic_library; + } + + if(ext == ".o") + { + return ctor::target_type::object; + } + + if(ext == "" || + ext == ".bin" || + ext == ".run" || + ext == ".out") + { + return ctor::target_type::executable; + } + } + + if(toolchain == ctor::toolchain::any// || + //toolchain == ctor::toolchain::msvc || + //toolchain == ctor::toolchain::mingw || + ) + { + if(ext == ".lib") + { + return ctor::target_type::static_library; + } + + if(ext == ".dll") + { + return ctor::target_type::dynamic_library; + } + + if(ext == ".obj") + { + return ctor::target_type::object; + } + + if(ext == ".exe" || + ext == ".com") + { + return ctor::target_type::executable; + } + } + + return ctor::target_type::unknown; +} + + +std::filesystem::path extension(ctor::toolchain toolchain, + ctor::target_type target_type, + ctor::output_system system, + const std::filesystem::path& file) +{ + auto type = target_type_from_extension(toolchain, file); + if(type == target_type) + { + // File already has the correct extension + return file; + } + + const auto& c = ctor::get_configuration(); + ctor::arch arch{}; + switch(system) + { + case ctor::output_system::host: + arch = c.host_arch; + break; + case ctor::output_system::build: + arch = c.build_arch; + break; + } + + // This might be before configure - so detection is needed for boostrap + if(arch == ctor::arch::unknown) + { + arch = get_arch(system, get_arch(system)); + } + + std::string ext{file.extension().string()}; + switch(target_type) + { + case ctor::target_type::automatic: + break; + case ctor::target_type::executable: + case ctor::target_type::unit_test: + switch(arch) + { + case ctor::arch::unix: + case ctor::arch::apple: + ext = ""; + break; + case ctor::arch::windows: + ext = ".exe"; + break; + case ctor::arch::unknown: + break; + } + break; + case ctor::target_type::static_library: + case ctor::target_type::unit_test_library: + switch(arch) + { + case ctor::arch::unix: + case ctor::arch::apple: + ext = ".a"; + break; + case ctor::arch::windows: + ext = ".lib"; + break; + case ctor::arch::unknown: + break; + } + break; + case ctor::target_type::dynamic_library: + switch(arch) + { + case ctor::arch::unix: + ext = ".so"; + break; + case ctor::arch::apple: + ext = ".dylib"; + break; + case ctor::arch::windows: + ext = ".dll"; + break; + case ctor::arch::unknown: + break; + } + break; + case ctor::target_type::object: + switch(arch) + { + case ctor::arch::unix: + case ctor::arch::apple: + ext = ".o"; + break; + case ctor::arch::windows: + ext = ".obj"; + break; + case ctor::arch::unknown: + break; + } + break; + case ctor::target_type::function: + break; + case ctor::target_type::unknown: + break; + } + + auto output{file}; + output.replace_extension(ext); + return output; +} diff --git a/src/tools.h b/src/tools.h new file mode 100644 index 0000000..188d49f --- /dev/null +++ b/src/tools.h @@ -0,0 +1,124 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include <vector> +#include <string> +#include <ostream> +#include <filesystem> + +#include "ctor.h" + + +std::ostream& operator<<(std::ostream& stream, const ctor::c_opt& opt); +std::ostream& operator<<(std::ostream& stream, const ctor::cxx_opt& opt); +std::ostream& operator<<(std::ostream& stream, const ctor::ld_opt& opt); +std::ostream& operator<<(std::ostream& stream, const ctor::ar_opt& opt); +std::ostream& operator<<(std::ostream& stream, const ctor::asm_opt& opt); + +std::string get_arch(ctor::output_system system); +ctor::arch get_arch(ctor::output_system system, const std::string& str); + +//! Get tool-chain type from compiler path string +ctor::toolchain getToolChain(const std::string& compiler); + +//! Get tool-chain type from output system (via configuration) +ctor::toolchain getToolChain(ctor::output_system system); + + + +//! Get tool argument(s) for specific option type matching the supplied +//! tool-chain +std::vector<std::string> c_option(ctor::toolchain toolchain, + ctor::c_opt option, + const std::string& arg = {}); + +//! Get tool argument(s) for specific option type matching the supplied +//! tool-chain +std::vector<std::string> cxx_option(ctor::toolchain toolchain, + ctor::cxx_opt option, + const std::string& arg = {}); + +//! Get tool argument(s) for specific option type matching the supplied +//! tool-chain +std::vector<std::string> ld_option(ctor::toolchain toolchain, + ctor::ld_opt option, + const std::string& arg = {}); + +//! Get tool argument(s) for specific option type matching the supplied +//! tool-chain +std::vector<std::string> ar_option(ctor::toolchain toolchain, + ctor::ar_opt option, + const std::string& arg = {}); + +//! Get tool argument(s) for specific option type matching the supplied +//! tool-chain +std::vector<std::string> asm_option(ctor::toolchain toolchain, + ctor::asm_opt option, + const std::string& arg = {}); + + + +//! Get ctor::c_opt enum value and argument from string, +//! ie. { ctor::c_opt::inlude_path, "foo/bar" } from "-Ifoo/bar" +//! Returns { ctor::c_opt::custom, flag } if unknown. +ctor::c_flag c_option(const std::string& flag, ctor::toolchain toolchain); + +//! Get ctor::cxx_opt enum value and argument from string, +//! ie. { ctor::cxx_opt::inlude_path, "foo/bar" } from "-Ifoo/bar" +//! Returns { ctor::cxx_opt::custom, flag } if unknown. +ctor::cxx_flag cxx_option(const std::string& flag, ctor::toolchain toolchain); + +//! Get ctor::ld_opt enum value and argument from string, +//! ie. { ctor::ld_opt::inlude_path, "foo/bar" } from "-Ifoo/bar" +//! Returns { ctor::ld_opt::custom, flag } if unknown. +ctor::ld_flag ld_option(const std::string& flag, ctor::toolchain toolchain); + +//! Get ctor::ar_opt enum value and argument from string, +//! ie. { ctor::ar_opt::inlude_path, "foo/bar" } from "-Ifoo/bar" +//! Returns { ctor::ar_opt::custom, flag } if unknown. +ctor::ar_flag ar_option(const std::string& flag, ctor::toolchain toolchain); + +//! Get ctor::asm_opt enum value and argument from string, +//! ie. { ctor::asm_opt::inlude_path, "foo/bar" } from "-Ifoo/bar" +//! Returns { ctor::asm_opt::custom, flag } if unknown. +ctor::asm_flag asm_option(const std::string& flag, ctor::toolchain toolchain); + + + +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::cxx_flag& flag); +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::c_flag& flag); +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::ld_flag& flag); +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::ar_flag& flag); +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::asm_flag& flag); + + +// Get target type from file extension +// If toolchain is not ::any only extensions for that toolchain will be accepted +// If no match is found ::unknown will be returned. +ctor::target_type target_type_from_extension(ctor::toolchain toolchain, + const std::filesystem::path& file); + + +// Get appropriate extension from original extension and target type using +// the toolchain. +// ie. { gcc, static_lib, ".lib" } will return ".a" +// { msvc, dynamic_lib, ".dylib" } will return ".dll" +// ... +// If the supplied extension is normal for the supplied type and toolchain, then this is used, otherwise a conversion to the default extension to the given toolchain and type is given. +// The defaults for the toolchains are as follows: +// toolchain executable static-lib dynamic-lib +// gcc (none) .a .so(.dylib on macos) +// clang (none) .a .so(.dylib on macos) +// msvc .exe .lib .dll +// mingw .exe .lib .dll +std::filesystem::path extension(ctor::toolchain toolchain, + ctor::target_type target_type, + ctor::output_system system, + const std::filesystem::path& file); diff --git a/src/unittest.cc b/src/unittest.cc index f18de47..1e32878 100644 --- a/src/unittest.cc +++ b/src/unittest.cc @@ -9,14 +9,14 @@ #include "task.h" int runUnitTests(std::set<std::shared_ptr<Task>>& tasks, - const Settings& settings) + const ctor::settings& settings) { bool ok{true}; - std::cout << "Running unit-tests:\n"; + std::cout << "Running unit-tests:" << std::endl; // Run unit-tests for(const auto& task : tasks) { - if(task->targetType() == TargetType::UnitTest) + if(task->targetType() == ctor::target_type::unit_test) { auto name = task->name(); if(name.empty()) @@ -24,15 +24,15 @@ int runUnitTests(std::set<std::shared_ptr<Task>>& tasks, name = task->target(); } std::cout << name << ": " << std::flush; - auto ret = execute(task->targetFile(), {}, settings.verbose > 0); + auto ret = execute(task->targetFile(), {}, {}, settings.verbose > 0); ok &= ret == 0; if(ret == 0) { - std::cout << " OK\n"; + std::cout << " OK" << std::endl; } else { - std::cout << " FAILED\n"; + std::cout << " FAILED" << std::endl; } } } diff --git a/src/unittest.h b/src/unittest.h index 8dee33c..2880319 100644 --- a/src/unittest.h +++ b/src/unittest.h @@ -7,7 +7,10 @@ #include <memory> class Task; -class Settings; + +namespace ctor { +struct settings; +} // namespace ctor:: int runUnitTests(std::set<std::shared_ptr<Task>>& tasks, - const Settings& settings); + const ctor::settings& settings); diff --git a/src/util.cc b/src/util.cc index 92560b6..76d380b 100644 --- a/src/util.cc +++ b/src/util.cc @@ -5,6 +5,14 @@ #include <iostream> #include <fstream> +#include <algorithm> + +std::string to_lower(const std::string& str) +{ + std::string out{str}; + std::transform(out.begin(), out.end(), out.begin(), ::tolower); + return out; +} std::string readFile(const std::string& fileName) { @@ -80,30 +88,37 @@ std::vector<std::string> readDeps(const std::string& depFile) return output; } -Language languageFromExtension(const std::filesystem::path& file) +ctor::language languageFromExtension(const std::filesystem::path& file) { auto ext = file.extension().string(); + + // First a few case sensitive comparisons if(ext == ".c") { - return Language::C; + return ctor::language::c; + } + + if(ext == ".C") + { + return ctor::language::cpp; } - if(ext == ".C" || - ext == ".cc" || + // The rest are compared in lowercase + ext = to_lower(ext); + + if(ext == ".cc" || ext == ".cpp" || - ext == ".CPP" || ext == ".c++" || ext == ".cp" || ext == ".cxx") { - return Language::Cpp; + return ctor::language::cpp; } if(ext == ".s" || - ext == ".S" || ext == ".asm") { - return Language::Asm; + return ctor::language::assembler; } std::cerr << "Could not deduce language from " << file.string() << "\n"; @@ -135,3 +150,97 @@ std::string cleanUp(const std::string& path) } return cleaned; } + +std::string esc(const std::string& in) +{ + std::string out; + for(auto c : in) + { + switch(c) + { + case '\\': out += "\\\\"; break; + case '"': out += "\\\""; break; + default: + out += c; + break; + } + } + return out; +} + +std::vector<std::string> get_paths(const std::string& path_env) +{ + std::vector<std::string> paths; + +#ifdef _WIN32 + const char sep{';'}; +#else + const char sep{':'}; +#endif + + std::stringstream ss(path_env); + std::string path; + while (std::getline(ss, path, sep)) + { + paths.push_back(path); + } + + return paths; +} + +bool check_executable(const std::filesystem::path& prog) +{ + auto perms = std::filesystem::status(prog).permissions(); + + if((perms & std::filesystem::perms::owner_exec) != std::filesystem::perms::none) + { + return true; + } + + if((perms & std::filesystem::perms::group_exec) != std::filesystem::perms::none) + { + return true; + } + + if((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none) + { + return true; + } + + return false; +} + +std::string locate(const std::string& prog, + const std::vector<std::string>& paths, + const std::string& arch) +{ + std::string program = prog; + if(!arch.empty()) + { + program = arch + "-" + prog; + } + + // first check if arch contains an absolute path to prog + if(std::filesystem::exists(program)) + { + if(check_executable(program)) + { + return program; + } + } + + for(const auto& path_str : paths) + { + std::filesystem::path path(path_str); + auto prog_path = path / program; + if(std::filesystem::exists(prog_path)) + { + if(check_executable(prog_path)) + { + return prog_path.string(); + } + } + } + + return {}; +} @@ -3,12 +3,29 @@ // See accompanying file LICENSE for details. #pragma once -#include "libctor.h" +#include "ctor.h" #include <string> #include <filesystem> +#include <cstdlib> + +std::string to_lower(const std::string& str); std::string readFile(const std::string& fileName); std::vector<std::string> readDeps(const std::string& depFile); -Language languageFromExtension(const std::filesystem::path& file); +ctor::language languageFromExtension(const std::filesystem::path& file); std::string cleanUp(const std::string& path); + +template<typename T> +void append(T& a, const T& b) +{ + a.insert(a.end(), b.begin(), b.end()); +} + +std::string esc(const std::string& in); + +std::vector<std::string> get_paths(const std::string& path_env = std::getenv("PATH")); + +std::string locate(const std::string& app, + const std::vector<std::string>& paths, + const std::string& arch = {}); diff --git a/test/ctor.cc b/test/ctor.cc index c24ded7..ccf1c31 100644 --- a/test/ctor.cc +++ b/test/ctor.cc @@ -1,25 +1,41 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h> namespace { -BuildConfigurations ctorTestConfigs(const Settings& settings) +ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) { return { { - .type = TargetType::UnitTest, + .type = ctor::target_type::unit_test, + .system = ctor::output_system::build, + .target = "testprog", + .sources = { + "testprog.cc", + }, + .flags = { + .cxxflags = { + "-std=c++20", "-O3", "-Wall", "-Werror", + }, + }, + }, + { + .type = ctor::target_type::unit_test, + .system = ctor::output_system::build, .target = "execute_test", .sources = { "execute_test.cc", "testmain.cc", "../src/execute.cc", + "../src/util.cc", }, + .depends = { "testprog", }, .flags = { .cxxflags = { - "-std=c++20", "-O3", "-s", "-Wall", "-Werror", + "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"execute\"", }, @@ -27,7 +43,8 @@ BuildConfigurations ctorTestConfigs(const Settings& settings) }, }, { - .type = TargetType::UnitTest, + .type = ctor::target_type::unit_test, + .system = ctor::output_system::build, .target = "tasks_test", .sources = { "tasks_test.cc", @@ -36,7 +53,7 @@ BuildConfigurations ctorTestConfigs(const Settings& settings) .depends = { "libctor_nomain.a" }, .flags = { .cxxflags = { - "-std=c++20", "-O3", "-s", "-Wall", "-Werror", + "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"tasks\"", }, @@ -44,7 +61,8 @@ BuildConfigurations ctorTestConfigs(const Settings& settings) }, }, { - .type = TargetType::UnitTest, + .type = ctor::target_type::unit_test, + .system = ctor::output_system::build, .target = "source_type_test", .sources = { "source_type_test.cc", @@ -53,7 +71,7 @@ BuildConfigurations ctorTestConfigs(const Settings& settings) .depends = { "libctor_nomain.a" }, .flags = { .cxxflags = { - "-std=c++20", "-O3", "-s", "-Wall", "-Werror", + "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"source_type\"", }, @@ -61,7 +79,27 @@ BuildConfigurations ctorTestConfigs(const Settings& settings) }, }, { - .type = TargetType::UnitTestLib, + .type = ctor::target_type::unit_test, + .system = ctor::output_system::build, + .target = "tools_test", + .sources = { + "tools_test.cc", + "testmain.cc", + "../src/util.cc", + "../src/tools.cc", + }, + //.depends = { "libctor_nomain.a" }, + .flags = { + .cxxflags = { + "-std=c++20", "-O3", "-Wall", "-Werror", + "-I../src", "-Iuunit", + "-DOUTPUT=\"tools\"", + }, + }, + }, + { + .type = ctor::target_type::unit_test_library, + .system = ctor::output_system::build, .target = "libctor_nomain.a", .sources = { "../src/build.cc", @@ -75,12 +113,13 @@ BuildConfigurations ctorTestConfigs(const Settings& settings) "../src/task_fn.cc", "../src/task_ld.cc", "../src/task_so.cc", + "../src/tools.cc", "../src/util.cc", "../src/externals_manual.cc", }, .flags = { .cxxflags = { - "-std=c++20", "-O3", "-s", "-Wall", "-Werror", + "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", }, .ldflags = { "-pthread" }, diff --git a/test/execute_test.cc b/test/execute_test.cc index 9da18dc..03b3c2a 100644 --- a/test/execute_test.cc +++ b/test/execute_test.cc @@ -1,6 +1,14 @@ #include <uunit.h> +#include <fstream> +#include <map> +#include <set> + #include <execute.h> +#include <util.h> + +#include "paths.h" +#include "tmpfile.h" class ExecuteTest : public uUnit @@ -8,14 +16,61 @@ class ExecuteTest public: ExecuteTest() { - uTEST(ExecuteTest::runit); + uTEST(ExecuteTest::return_value); + uTEST(ExecuteTest::env); + } + + void return_value() + { + auto cur_path = std::filesystem::path(paths::argv_0).parent_path(); + std::vector<std::string> paths{{cur_path.string()}}; + auto cmd = locate("testprog", paths); + uASSERT(!cmd.empty()); + + uASSERT_EQUAL(0, execute(cmd, {"retval", "0"}, {}, false)); + uASSERT_EQUAL(1, execute(cmd, {"retval", "1"}, {}, false)); + uASSERT_EQUAL(1, execute("no-such-binary", {}, {}, false)); } - void runit() + void env() { - uASSERT_EQUAL(0, execute("/bin/true", {}, false)); - uASSERT_EQUAL(1, execute("/bin/false", {}, false)); - uASSERT_EQUAL(1, execute("no-such-binary", {}, false)); + auto cur_path = std::filesystem::path(paths::argv_0).parent_path(); + std::vector<std::string> paths{{cur_path.string()}}; + auto cmd = locate("testprog", paths); + uASSERT(!cmd.empty()); + + tmp_file tmp; + + std::map<std::string, std::string> env; + + // New env vars + env["foo"] = "bar"; + env["bar"] = "42"; + + // Overwrite the exiting LANG var + env["LANG"] = "foo"; + + uASSERT_EQUAL(0, execute(cmd, {"envdump", tmp.get()}, env, false)); + + std::set<std::string> vars; + { + std::ifstream infile(tmp.get()); + std::string line; + while (std::getline(infile, line)) + { + vars.insert(line); + } + } + + // Check the two explicitly set vars + uASSERT(vars.find("foo=bar") != vars.end()); + uASSERT(vars.find("bar=42") != vars.end()); + + // Check the one that should have overwritten the existing one (probably LANG=en_US.UTF-8 or something) + uASSERT(vars.find("LANG=foo") != vars.end()); + + // Check that other vars are also there (ie. the env wasn't cleared on entry) + uASSERT(vars.size() > 3); } }; diff --git a/test/source_type_test.cc b/test/source_type_test.cc index ed7e783..288f1e5 100644 --- a/test/source_type_test.cc +++ b/test/source_type_test.cc @@ -1,37 +1,40 @@ -#include <uunit.h> - -#include <libctor.h> +#include <ctor.h> #include <task_cc.h> -std::ostream& operator<<(std::ostream& stream, const Language& lang) +std::ostream& operator<<(std::ostream& stream, const ctor::language& lang); + +#include <uunit.h> + +std::ostream& operator<<(std::ostream& stream, const ctor::language& lang) { switch(lang) { - case Language::Auto: - stream << "Language::Auto"; + case ctor::language::automatic: + stream << "ctor::language::automatic"; break; - case Language::C: - stream << "Language::C"; + case ctor::language::c: + stream << "ctor::language::c"; break; - case Language::Cpp: - stream << "Language::Cpp"; + case ctor::language::cpp: + stream << "ctor::language::cpp"; break; - case Language::Asm: - stream << "Language::Asm"; + case ctor::language::assembler: + stream << "ctor::language::assembler"; break; } return stream; } + class TestableTaskCC : public TaskCC { public: - TestableTaskCC(const Source& source) + TestableTaskCC(const ctor::source& source) : TaskCC({}, {}, "build", source) {} - Language language() const + ctor::language language() const { return source_language; } @@ -50,22 +53,22 @@ public: { { // c++ TestableTaskCC task("hello.cc"); - uASSERT_EQUAL(Language::Cpp, task.language()); + uASSERT_EQUAL(ctor::language::cpp, task.language()); } { // c TestableTaskCC task("hello.c"); - uASSERT_EQUAL(Language::C, task.language()); + uASSERT_EQUAL(ctor::language::c, task.language()); } { // asm TestableTaskCC task("hello.s"); - uASSERT_EQUAL(Language::Asm, task.language()); + uASSERT_EQUAL(ctor::language::assembler, task.language()); } { // custom/explicit language - TestableTaskCC task( {"hello.foo", Language::Asm} ); - uASSERT_EQUAL(Language::Asm, task.language()); + TestableTaskCC task( {"hello.foo", ctor::language::assembler} ); + uASSERT_EQUAL(ctor::language::assembler, task.language()); } // Note: Failure state will result in exit(1) so cannot be tested diff --git a/test/suite/ctor_files/ctor.cc.bar b/test/suite/ctor_files/ctor.cc.bar index 92456cb..218f9cc 100644 --- a/test/suite/ctor_files/ctor.cc.bar +++ b/test/suite/ctor_files/ctor.cc.bar @@ -1,12 +1,12 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h> //#include "config.h" namespace { -BuildConfigurations ctorConfigs() +ctor::build_configurations ctorConfigs(const ctor::settings& settings) { return { @@ -30,17 +30,19 @@ BuildConfigurations ctorConfigs() }; } -ExternalConfigurations ctorExtConfigs() +ctor::external_configurations ctorExtConfigs(const ctor::settings& settings) { return { { .name = "bar", - .flags = { - .cxxflags = { "-D_A_", "-DBAR"}, - .cflags = { "-D_B_" }, - .ldflags = { "-D_C_" }, - .asmflags = { "-D_D_" }, + .external = ctor::external_manual{ + .flags = { + .cflags = { "-D_B_" }, + .cxxflags = { "-D_A_", "-DBAR"}, + .ldflags = { "-D_C_" }, + .asmflags = { "-D_D_" }, + }, }, // Creates --with-foo-prefix arg to configure which will be used for // -L and -I flags. diff --git a/test/suite/ctor_files/ctor.cc.base b/test/suite/ctor_files/ctor.cc.base index 6c60513..eab39c4 100644 --- a/test/suite/ctor_files/ctor.cc.base +++ b/test/suite/ctor_files/ctor.cc.base @@ -1,12 +1,12 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h> //#include "config.h" namespace { -BuildConfigurations ctorConfigs() +ctor::build_configurations ctorConfigs(const ctor::settings& settings) { return { @@ -30,17 +30,20 @@ BuildConfigurations ctorConfigs() }; } -ExternalConfigurations ctorExtConfigs() +ctor::external_configurations ctorExtConfigs(const ctor::settings& settings) { return { { .name = "bar", - .flags = { - .cxxflags = { "-D_A_", "-DFOO"}, - .cflags = { "-D_B_" }, - .ldflags = { "-D_C_" }, - .asmflags = { "-D_D_" }, + .external = ctor::external_manual + { + .flags = { + .cflags = { "-D_B_" }, + .cxxflags = { "-D_A_", "-DFOO"}, + .ldflags = { "-D_C_" }, + .asmflags = { "-D_D_" }, + }, }, // Creates --with-foo-prefix arg to configure which will be used for // -L and -I flags. diff --git a/test/suite/ctor_files/ctor.cc.multi b/test/suite/ctor_files/ctor.cc.multi index 9db2517..2b88afe 100644 --- a/test/suite/ctor_files/ctor.cc.multi +++ b/test/suite/ctor_files/ctor.cc.multi @@ -1,14 +1,14 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h> //#include "config.h" #include "foobar.h" namespace { -BuildConfigurations ctorConfigs() +ctor::build_configurations ctorConfigs(const ctor::settings& settings) { return { @@ -32,17 +32,19 @@ BuildConfigurations ctorConfigs() }; } -ExternalConfigurations ctorExtConfigs() +ctor::external_configurations ctorExtConfigs(const ctor::settings& settings) { return { { .name = "bar", - .flags = { - .cxxflags = { "-D_A_", "-DFOO"}, - .cflags = { "-D_B_" }, - .ldflags = { "-D_C_" }, - .asmflags = { "-D_D_" }, + .external = ctor::external_manual{ + .flags = { + .cflags = { "-D_B_" }, + .cxxflags = { "-D_A_", "-DFOO"}, + .ldflags = { "-D_C_" }, + .asmflags = { "-D_D_" }, + }, }, // Creates --with-foo-prefix arg to configure which will be used for // -L and -I flags. diff --git a/test/suite/test.sh b/test/suite/test.sh index c980154..c112351 100755 --- a/test/suite/test.sh +++ b/test/suite/test.sh @@ -1,4 +1,9 @@ #!/bin/bash +: ${CXX:=g++} +: ${CTORDIR:=../../build} +: ${BUILDDIR:=build} + +CXX=$(which $CXX) function fail { @@ -13,21 +18,22 @@ function ctor } # Wipe the board -rm -Rf build +rm -Rf ${BUILDDIR} rm -f configuration.cc rm -f ctor +echo "** ctor_files/ctor.cc.base" cp ctor_files/ctor.cc.base ctor.cc # Compile bootstrap binary -g++ -pthread -std=c++20 -L../../build -lctor -I../../src ctor.cc -o ctor || fail ${LINENO} +$CXX -pthread -std=c++20 -L${CTORDIR} -lctor -I../../src ctor.cc -o ctor || fail ${LINENO} # No build files should have been created yet -[ -d build ] && fail ${LINENO} +[ -d ${BUILDDIR} ] && fail ${LINENO} # capture md5 sum of ctor binary before configure is called MD5=`md5sum ctor` -ctor configure --ctor-includedir ../../src --ctor-libdir ../../build +ctor configure --ctor-includedir ../../src --ctor-libdir=${CTORDIR} --build-dir=${BUILDDIR} # ctor should be rebuilt at this point, so md5 sum should have changed (echo $MD5 | md5sum --status -c) && fail ${LINENO} @@ -36,7 +42,7 @@ ctor configure --ctor-includedir ../../src --ctor-libdir ../../build [ ! -f configuration.cc ] && fail ${LINENO} # Shouldn't compile anything yet - only configure -[ -f build/hello-hello_cc.o ] && fail ${LINENO} +[ -f ${BUILDDIR}/hello-hello_cc.o ] && fail ${LINENO} MD5=`md5sum ctor` @@ -44,12 +50,12 @@ MD5=`md5sum ctor` ctor -v # Compiled object should now exist -[ ! -f build/hello-hello_cc.o ] && fail ${LINENO} +[ ! -f ${BUILDDIR}/hello-hello_cc.o ] && fail ${LINENO} # ctor should not have been rebuilt, so md5 sum should be the same (echo $MD5 | md5sum --status -c) || fail ${LINENO} -MOD1=`stat -c %Y build/hello-hello_cc.o` +MOD1=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` touch hello.cc sleep 1.1 @@ -57,10 +63,11 @@ sleep 1.1 ctor -v # Object file should have been recompiled -MOD2=`stat -c %Y build/hello-hello_cc.o` +MOD2=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` [[ $MOD1 == $MOD2 ]] && fail ${LINENO} # Replacve -DFOO with -DBAR in foo external.cxxflags +echo "** ctor_files/ctor.cc.bar" cp ctor_files/ctor.cc.bar ctor.cc MD5C=`md5sum configuration.cc` @@ -71,22 +78,23 @@ sleep 1.1 # Run normally to reconfigure, rebuild ctor and rebuild hello.cc ctor -v -MOD2=`stat -c %Y build/hello-hello_cc.o` +MOD2=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` [[ $MOD1 == $MOD2 ]] && fail ${LINENO} (echo $MD5C | md5sum --status -c) && fail ${LINENO} (echo $MD5 | md5sum --status -c) && fail ${LINENO} +echo "** ctor_files/ctor.cc.multi" cp ctor_files/ctor.cc.multi ctor.cc MD5C=`md5sum configuration.cc` MD5=`md5sum ctor` -MOD1=`stat -c %Y build/hello-hello_cc.o` +MOD1=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` sleep 1.1 # Run normally to reconfigure, rebuild ctor and rebuild hello.cc ctor -v -MOD2=`stat -c %Y build/hello-hello_cc.o` +MOD2=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` [[ $MOD1 == $MOD2 ]] && fail ${LINENO} (echo $MD5C | md5sum --status -c) && fail ${LINENO} (echo $MD5 | md5sum --status -c) && fail ${LINENO} @@ -102,3 +110,5 @@ ctor -v MOD2=`stat -c %Y ctor` [[ $MOD1 == $MOD2 ]] && fail ${LINENO} + +exit 0 diff --git a/test/tasks_test.cc b/test/tasks_test.cc index 2e0ffc7..5f1db26 100644 --- a/test/tasks_test.cc +++ b/test/tasks_test.cc @@ -1,11 +1,11 @@ #include <uunit.h> -#include <libctor.h> +#include <ctor.h> #include <tasks.h> namespace { -BuildConfigurations ctorTestConfigs1(const Settings&) +ctor::build_configurations ctorTestConfigs1(const ctor::settings&) { return { @@ -19,7 +19,7 @@ BuildConfigurations ctorTestConfigs1(const Settings&) }; } -BuildConfigurations ctorTestConfigs2(const Settings&) +ctor::build_configurations ctorTestConfigs2(const ctor::settings&) { return { @@ -91,7 +91,7 @@ public: void getTargets_test() { using namespace std::string_literals; - Settings settings{}; + ctor::settings settings{}; const auto& targets = getTargets(settings); uASSERT_EQUAL(4u, targets.size()); @@ -109,7 +109,7 @@ public: void getTasks_test() { using namespace std::string_literals; - Settings settings{ .builddir = "foo" }; + ctor::settings settings{ .builddir = "foo" }; { auto tasks = getTasks(settings); uASSERT_EQUAL(6u, tasks.size()); @@ -139,7 +139,7 @@ public: void getNextTask_test() { using namespace std::string_literals; - Settings settings{}; + ctor::settings settings{}; { // Zero (Empty) std::set<std::shared_ptr<Task>> allTasks; diff --git a/test/testprog.cc b/test/testprog.cc new file mode 100644 index 0000000..dbfb665 --- /dev/null +++ b/test/testprog.cc @@ -0,0 +1,49 @@ +#include <iostream> +#include <fstream> +#include <string> + +extern const char **environ; // see 'man environ' + +int main(int argc, const char* argv[]) +{ + if(argc < 2) + { + return 0; + } + + std::string cmd = argv[1]; + + if(cmd == "envdump") + { + if(argc < 3) + { + return 0; + } + std::ofstream ostrm(argv[2], std::ios::binary); + for(auto current = environ; *current; ++current) + { + ostrm << (*current) << "\n"; + } + } + + if(cmd == "retval") + { + if(argc < 3) + { + return 0; + } + return std::stoi(argv[2]); + } + + if(cmd == "abort") + { + abort(); + } + + if(cmd == "throw") + { + throw "ouch"; + } + + return 0; +} diff --git a/test/tmpfile.h b/test/tmpfile.h new file mode 100644 index 0000000..5d114d0 --- /dev/null +++ b/test/tmpfile.h @@ -0,0 +1,48 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include <cstdlib> +#include <unistd.h> + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#endif + +struct tmp_file +{ + tmp_file(const std::string& data = {}) + { + int fd; +#ifdef _WIN32 + char templ[] = "ctor_tmp_file-XXXXXX"; // buffer for filename + _mktemp_s(templ, sizeof(templ)); + fd = open(templ, O_CREAT | O_RDWR); +#else + char templ[] = "/tmp/ctor_tmp_file-XXXXXX"; // buffer for filename + fd = mkstemp(templ); +#endif + filename = templ; + auto sz = write(fd, data.data(), data.size()); + (void)sz; + close(fd); + } + + ~tmp_file() + { + unlink(filename.data()); + } + + const std::string& get() const + { + return filename; + } + +private: + std::string filename; +}; diff --git a/test/tools_test.cc b/test/tools_test.cc new file mode 100644 index 0000000..a428ea1 --- /dev/null +++ b/test/tools_test.cc @@ -0,0 +1,945 @@ +#include <vector> +#include <string> +#include <ostream> +#include <initializer_list> +#include <cassert> + +#include <tools.h> + +std::ostream& operator<<(std::ostream& stream, const ctor::toolchain& toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::none: + stream << "ctor::toolchain::none"; + break; + case ctor::toolchain::any: + stream << "ctor::toolchain::any"; + break; + case ctor::toolchain::gcc: + stream << "ctor::toolchain::gcc"; + break; + case ctor::toolchain::clang: + stream << "ctor::toolchain::clang"; + break; + } + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const std::vector<std::string>& vs) +{ + bool first{true}; + stream << "{ "; + for(const auto& v : vs) + { + if(!first) + { + stream << ", "; + } + stream << "'" << v << "'"; + first = false; + } + stream << " }"; + + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::c_flag& flag) +{ + stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::cxx_flag& flag) +{ + stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::ld_flag& flag) +{ + stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::ar_flag& flag) +{ + stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::asm_flag& flag) +{ + stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; + return stream; +} + +bool operator!=(const ctor::c_flag& a, const ctor::c_flag& b) +{ + return + a.opt != b.opt || + a.arg != b.arg || + a.toolchain != b.toolchain; +} + +bool operator!=(const ctor::cxx_flag& a, const ctor::cxx_flag& b) +{ + return + a.opt != b.opt || + a.arg != b.arg || + a.toolchain != b.toolchain; +} +bool operator!=(const ctor::ld_flag& a, const ctor::ld_flag& b) +{ + return + a.opt != b.opt || + a.arg != b.arg || + a.toolchain != b.toolchain; +} + +bool operator!=(const ctor::ar_flag& a, const ctor::ar_flag& b) +{ + return + a.opt != b.opt || + a.arg != b.arg || + a.toolchain != b.toolchain; +} +bool operator!=(const ctor::asm_flag& a, const ctor::asm_flag& b) +{ + return + a.opt != b.opt || + a.arg != b.arg || + a.toolchain != b.toolchain; +} + +#include <uunit.h> + +namespace { +std::string conf_host_cxx{}; +std::string conf_build_cxx{}; +} + +const ctor::configuration& ctor::get_configuration() +{ + static ctor::configuration cfg; + return cfg; +} + +const std::string& ctor::configuration::get(const std::string& key, const std::string& defval) const +{ + if(key == ctor::cfg::host_cxx) + { + return conf_host_cxx; + } + + if(key == ctor::cfg::build_cxx) + { + return conf_build_cxx; + } + + assert(false); // bad key + + static std::string res{}; + return res; +} + +class ToolsTest + : public uUnit +{ +public: + ToolsTest() + { + uTEST(ToolsTest::getToolChain_test); + + uTEST(ToolsTest::getOption_toolchain_c_test); + uTEST(ToolsTest::getOption_toolchain_cxx_test); + uTEST(ToolsTest::getOption_toolchain_ld_test); + uTEST(ToolsTest::getOption_toolchain_ar_test); + uTEST(ToolsTest::getOption_toolchain_asm_test); + + uTEST(ToolsTest::getOption_str_c_test); + uTEST(ToolsTest::getOption_str_cxx_test); + uTEST(ToolsTest::getOption_str_ld_test); + uTEST(ToolsTest::getOption_str_ar_test); + uTEST(ToolsTest::getOption_str_asm_test); + + uTEST(ToolsTest::to_strings_c_test); + uTEST(ToolsTest::to_strings_cxx_test); + uTEST(ToolsTest::to_strings_ld_test); + uTEST(ToolsTest::to_strings_ar_test); + uTEST(ToolsTest::to_strings_asm_test); + } + + void getToolChain_test() + { + // + // gcc + // + uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/gcc")); + uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/gcc-10")); + uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/x86_64-pc-linux-gnu-g++-9.3.0")); + + uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/g++")); + uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/g++-10")); + uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/x86_64-pc-linux-gnu-g++-9.3.0")); + + // + // clang + // + uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang")); + uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang-16")); + uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/lib/llvm/16/bin/i686-pc-linux-gnu-clang-16")); + + uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang++")); + uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang++-16")); + uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/lib/llvm/16/bin/i686-pc-linux-gnu-clang++-16")); + } + + + void getOption_toolchain_c_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // + // gcc + // + exp = { "-o", "foo" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-g" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::debug); + uASSERT_EQUAL(exp, act); + + exp = { "-Wall" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "-Werror" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "-MMD" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::generate_dep_tree); + uASSERT_EQUAL(exp, act); + + exp = { "-c" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::no_link); + uASSERT_EQUAL(exp, act); + + exp = { "-Ifoo" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::include_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-std=foo" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::c_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-Ofoo" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::optimization, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIC" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIE" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { "-o", "foo" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-g" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::debug); + uASSERT_EQUAL(exp, act); + + exp = { "-Wall" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "-Werror" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "-MMD" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::generate_dep_tree); + uASSERT_EQUAL(exp, act); + + exp = { "-c" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::no_link); + uASSERT_EQUAL(exp, act); + + exp = { "-Ifoo" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::include_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-std=foo" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::c_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-Ofoo" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::optimization, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIC" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIE" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // any + // + exp = { "{ctor::c_opt::output, \"foo\"}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::debug}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::debug); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::warn_all}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::warnings_as_errors}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::generate_dep_tree}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::generate_dep_tree); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::no_link}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::no_link); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::include_path, \"foo\"}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::include_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::c_std, \"foo\"}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::c_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::optimization, \"foo\"}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::optimization, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::position_independent_code}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::position_independent_executable}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::custom, \"-foo\"}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + } + + void getOption_toolchain_cxx_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // + // gcc + // + exp = { "-o", "foo" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-g" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::debug); + uASSERT_EQUAL(exp, act); + + exp = { "-Wall" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "-Werror" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "-MMD" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::generate_dep_tree); + uASSERT_EQUAL(exp, act); + + exp = { "-c" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::no_link); + uASSERT_EQUAL(exp, act); + + exp = { "-Ifoo" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::include_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-std=foo" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::cpp_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-Ofoo" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::optimization, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIC" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIE" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { "-o", "foo" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-g" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::debug); + uASSERT_EQUAL(exp, act); + + exp = { "-Wall" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "-Werror" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "-MMD" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::generate_dep_tree); + uASSERT_EQUAL(exp, act); + + exp = { "-c" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::no_link); + uASSERT_EQUAL(exp, act); + + exp = { "-Ifoo" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::include_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-std=foo" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::cpp_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-Ofoo" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::optimization, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIC" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIE" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // any + // + exp = { "{ctor::cxx_opt::output, \"foo\"}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::debug}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::debug); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::warn_all}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::warnings_as_errors}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::generate_dep_tree}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::generate_dep_tree); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::no_link}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::no_link); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::include_path, \"foo\"}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::include_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::cpp_std, \"foo\"}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::cpp_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::optimization, \"foo\"}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::optimization, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::position_independent_code}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::position_independent_executable}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::custom, \"-foo\"}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + } + + void getOption_toolchain_ld_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // + // gcc + // + exp = { "-o", "foo" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-s" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::strip); + uASSERT_EQUAL(exp, act); + + exp = { "-Wall" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "-Werror" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "-Lfoo" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::library_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-lfoo" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::link, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-std=foo" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::cpp_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-shared" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::build_shared); + uASSERT_EQUAL(exp, act); + + exp = { "-pthread" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::threads); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIC" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIE" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { "-o", "foo" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-s" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::strip); + uASSERT_EQUAL(exp, act); + + exp = { "-Wall" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "-Werror" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "-Lfoo" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::library_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-lfoo" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::link, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-std=foo" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::cpp_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-shared" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::build_shared); + uASSERT_EQUAL(exp, act); + + exp = { "-pthread" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::threads); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIC" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIE" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // any + // + exp = { "{ctor::ld_opt::output, \"foo\"}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::strip}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::strip); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::warn_all}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::warnings_as_errors}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::library_path, \"foo\"}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::library_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::link, \"foo\"}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::link, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::cpp_std, \"foo\"}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::cpp_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::build_shared}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::build_shared); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::threads}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::threads); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::position_independent_code}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::position_independent_executable}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::custom, \"-foo\"}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + } + + void getOption_toolchain_ar_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // + // gcc + // + exp = { "-r" }; + act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::replace); + uASSERT_EQUAL(exp, act); + + exp = { "-s" }; + act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::add_index); + uASSERT_EQUAL(exp, act); + + exp = { "-c" }; + act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::create); + uASSERT_EQUAL(exp, act); + + exp = { "foo" }; + act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { "-r" }; + act = ar_option(ctor::toolchain::clang, ctor::ar_opt::replace); + uASSERT_EQUAL(exp, act); + + exp = { "-s" }; + act = ar_option(ctor::toolchain::clang, ctor::ar_opt::add_index); + uASSERT_EQUAL(exp, act); + + exp = { "-c" }; + act = ar_option(ctor::toolchain::clang, ctor::ar_opt::create); + uASSERT_EQUAL(exp, act); + + exp = { "foo" }; + act = ar_option(ctor::toolchain::clang, ctor::ar_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = ar_option(ctor::toolchain::clang, ctor::ar_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // any + // + exp = { "{ctor::ar_opt::custom, \"-foo\"}" }; + act = ar_option(ctor::toolchain::any, ctor::ar_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); +} + + void getOption_toolchain_asm_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // + // gcc + // + exp = { "-foo" }; + act = asm_option(ctor::toolchain::gcc, ctor::asm_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { "-foo" }; + act = asm_option(ctor::toolchain::clang, ctor::asm_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // any + // + exp = { "{ctor::asm_opt::custom, \"-foo\"}" }; + act = asm_option(ctor::toolchain::any, ctor::asm_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + } + + + void getOption_str_c_test() + { + ctor::c_flag exp(""); + ctor::c_flag act(""); + + // + // gcc + // + exp = { ctor::c_opt::include_path, "foo" }; + act = c_option("-Ifoo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + exp = { ctor::c_opt::custom, "foo" }; + act = c_option("foo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { ctor::c_opt::include_path, "foo" }; + act = c_option("-Ifoo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + + exp = { ctor::c_opt::custom, "foo" }; + act = c_option("foo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + } + + void getOption_str_cxx_test() + { + ctor::cxx_flag exp(""); + ctor::cxx_flag act(""); + + // + // gcc + // + exp = { ctor::cxx_opt::include_path, "foo" }; + act = cxx_option("-Ifoo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + exp = { ctor::cxx_opt::custom, "foo" }; + act = cxx_option("foo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { ctor::cxx_opt::include_path, "foo" }; + act = cxx_option("-Ifoo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + + exp = { ctor::cxx_opt::custom, "foo" }; + act = cxx_option("foo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + } + + void getOption_str_ld_test() + { + ctor::ld_flag exp(""); + ctor::ld_flag act(""); + + // + // gcc + // + exp = { ctor::ld_opt::library_path, "foo" }; + act = ld_option("-Lfoo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + exp = { ctor::ld_opt::custom, "foo" }; + act = ld_option("foo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { ctor::ld_opt::library_path, "foo" }; + act = ld_option("-Lfoo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + + exp = { ctor::ld_opt::custom, "foo" }; + act = ld_option("foo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + } + + void getOption_str_ar_test() + { + ctor::ar_flag exp(""); + ctor::ar_flag act(""); + + // + // gcc + // + exp = { ctor::ar_opt::custom, "foo" }; + act = ar_option("foo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { ctor::ar_opt::custom, "foo" }; + act = ar_option("foo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + } + + void getOption_str_asm_test() + { + ctor::asm_flag exp(""); + ctor::asm_flag act(""); + + // + // gcc + // + exp = { ctor::asm_opt::custom, "foo" }; + act = asm_option("foo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { ctor::asm_opt::custom, "foo" }; + act = asm_option("foo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + } + + + void to_strings_c_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // Mismatching toolchain (required vs actual) results in no output + // otherwise to_strings is just a proxy for c_option + act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::c_opt::no_link}); + uASSERT_EQUAL(exp, act); + } + + void to_strings_cxx_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // Mismatching toolchain (required vs actual) results in no output + // otherwise to_strings is just a proxy for cxx_option + act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::cxx_opt::no_link}); + uASSERT_EQUAL(exp, act); + } + + void to_strings_ld_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // Mismatching toolchain (required vs actual) results in no output + // otherwise to_strings is just a proxy for ld_option + act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::ld_opt::strip}); + uASSERT_EQUAL(exp, act); + } + + void to_strings_ar_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // Mismatching toolchain (required vs actual) results in no output + // otherwise to_strings is just a proxy for ar_option + act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::ar_opt::custom, "foo"}); + uASSERT_EQUAL(exp, act); + } + + void to_strings_asm_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // Mismatching toolchain (required vs actual) results in no output + // otherwise to_strings is just a proxy for asm_option + act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::asm_opt::custom, "foo"}); + uASSERT_EQUAL(exp, act); + } +}; + +// Registers the fixture into the 'registry' +static ToolsTest test; |