summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jenkinsfile66
-rwxr-xr-xbootstrap.sh11
-rw-r--r--ctor.cc8
-rw-r--r--src/bootstrap.cc56
-rw-r--r--src/build.cc114
-rw-r--r--src/build.h15
-rw-r--r--src/configure.cc475
-rw-r--r--src/configure.cc.bak387
-rw-r--r--src/configure.h4
-rw-r--r--src/ctor.h100
-rw-r--r--src/deps.cc120
-rw-r--r--src/deps.h12
-rw-r--r--src/execute.cc99
-rw-r--r--src/execute.h13
-rw-r--r--src/externals_manual.cc8
m---------src/getoptpp0
-rw-r--r--src/libctor.cc28
-rw-r--r--src/pointerlist.cc123
-rw-r--r--src/pointerlist.h74
-rw-r--r--src/rebuild.cc48
-rw-r--r--src/task.cc38
-rw-r--r--src/task.h13
-rw-r--r--src/task_ar.cc50
-rw-r--r--src/task_cc.cc66
-rw-r--r--src/task_cc.h3
-rw-r--r--src/task_fn.cc35
-rw-r--r--src/task_ld.cc45
-rw-r--r--src/task_ld.h4
-rw-r--r--src/task_so.cc38
-rw-r--r--src/tasks.cc114
-rw-r--r--src/tasks.h18
-rw-r--r--src/tools.cc543
-rw-r--r--src/tools.h43
-rw-r--r--src/unittest.cc4
-rw-r--r--src/unittest.h4
-rw-r--r--src/util.cc179
-rw-r--r--src/util.h15
-rw-r--r--test/ctor.cc78
-rw-r--r--test/cycle_test.cc87
-rw-r--r--test/deps_test.cc97
-rw-r--r--test/deps_test_data/empty.d0
-rw-r--r--test/deps_test_data/missing_colon.d1
-rw-r--r--test/deps_test_data/multiline.d4
-rw-r--r--test/deps_test_data/no_deps.d1
-rw-r--r--test/deps_test_data/no_newline.d1
-rw-r--r--test/deps_test_data/spaces.d1
-rw-r--r--test/deps_test_data/trivial.d1
-rw-r--r--test/execute_test.cc83
-rw-r--r--test/pointerlist_test.cc320
-rw-r--r--test/tasks_test.cc173
-rw-r--r--test/testprog.cc53
-rw-r--r--test/tmpfile.h48
-rw-r--r--test/tools_test.cc69
m---------test/uunit0
54 files changed, 2932 insertions, 1058 deletions
diff --git a/Jenkinsfile b/Jenkinsfile
index c95b0eb..290f412 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -1,56 +1,48 @@
pipeline {
- agent { label 'c++20' }
-
+ agent any
stages {
- stage('Clean') {
+ ////////////////////////////////////////////////////
+ stage('Linux gcc') {
+ agent { label 'linux && gcc && c++20' }
steps {
echo 'Cleaning workspace ...'
- sh 'rm -Rf build*'
- }
- }
- stage('Build-gcc') {
- steps {
+ sh 'git clean -d -x -f'
echo 'Building (gcc) ...'
- sh 'BUILDDIR=build-gcc CXX=g++ ./bootstrap.sh'
- }
- }
- stage('Test-gcc') {
- steps {
+ sh 'CXX=g++ ./bootstrap.sh'
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)'
+ sh '(cd test/suite; CTORDIR=../../build CXX=g++ ./test.sh)'
}
- }
- stage('Build-clang') {
- steps {
- echo 'Building (clang) ...'
- sh 'BUILDDIR=build-clang CXX=clang++ ./bootstrap.sh'
+ post {
+ always {
+ xunit(thresholds: [ skipped(failureThreshold: '0'),
+ failed(failureThreshold: '0') ],
+ tools: [ CppUnit(pattern: 'build/test/*.xml') ])
+ }
}
}
- stage('Test-clang') {
+ ////////////////////////////////////////////////////
+ stage('Linux clang') {
+ agent { label 'linux && clang && c++20' }
steps {
+ echo 'Cleaning workspace ...'
+ sh 'git clean -d -x -f'
+ echo 'Building (clang) ...'
+ sh 'CXX=clang++ ./bootstrap.sh'
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)'
+ sh '(cd test/suite; CTORDIR=../../build CXX=clang++ ./test.sh)'
+ }
+ post {
+ always {
+ xunit(thresholds: [ skipped(failureThreshold: '0'),
+ failed(failureThreshold: '0') ],
+ tools: [ CppUnit(pattern: 'build/test/*.xml') ])
+ }
}
}
- }
-
- post {
- always {
- xunit(thresholds: [ skipped(failureThreshold: '0'),
- failed(failureThreshold: '0') ],
- tools: [ CppUnit(pattern: 'build-*/test/*.xml') ])
- }
+ ////////////////////////////////////////////////////
}
}
diff --git a/bootstrap.sh b/bootstrap.sh
index 76503bb..d10f54b 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -1,11 +1,12 @@
#!/bin/sh
+set -e
+
: ${CXX:=g++}
: ${BUILDDIR:=build}
-CXX=$(which $CXX)
echo "Bootstrapping..."
-$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&& \
+$CXX -std=c++20 -Wall -O3 -Isrc -pthread src/bootstrap.cc 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."
diff --git a/ctor.cc b/ctor.cc
index c9faa5e..67b1465 100644
--- a/ctor.cc
+++ b/ctor.cc
@@ -10,14 +10,16 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
return
{
{
- .type = ctor::target_type::static_library,
+ .system = ctor::output_system::build,
.target = "libctor.a",
.sources = {
"src/build.cc",
"src/configure.cc",
+ "src/deps.cc",
"src/execute.cc",
"src/externals_manual.cc",
"src/libctor.cc",
+ "src/pointerlist.cc",
"src/rebuild.cc",
"src/task.cc",
"src/task_ar.cc",
@@ -37,6 +39,10 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
"-g",
"-Wall",
"-Werror",
+ "-Wextra",
+ "-Wshadow",
+ "-Wconversion",
+// "-Wnrvo",
"-Isrc",
},
},
diff --git a/src/bootstrap.cc b/src/bootstrap.cc
index 0604527..be1b5ed 100644
--- a/src/bootstrap.cc
+++ b/src/bootstrap.cc
@@ -4,6 +4,7 @@
#include <iostream>
#include <array>
#include <cstdlib>
+#include <span>
#define BOOTSTRAP
@@ -18,9 +19,10 @@
#include "tasks.cc"
#include "build.cc"
#include "tools.cc"
+#include "pointerlist.cc"
-std::filesystem::path configurationFile("configuration.cc");
-std::filesystem::path configHeaderFile("config.h");
+const std::filesystem::path configurationFile("configuration.cc");
+const std::filesystem::path configHeaderFile("config.h");
const ctor::configuration& ctor::get_configuration()
{
@@ -28,7 +30,7 @@ const ctor::configuration& ctor::get_configuration()
static bool initialised{false};
if(!initialised)
{
- cfg.host_toolchain = getToolChain(cfg.get(ctor::cfg::host_cxx, "/usr/bin/g++"));
+ cfg.build_toolchain = getToolChain(cfg.get(ctor::cfg::build_cxx, "/usr/bin/g++"));
initialised = true;
}
@@ -40,28 +42,58 @@ bool ctor::configuration::has(const std::string& key) const
return false;
}
-const std::string& ctor::configuration::get(const std::string& key, const std::string& default_value) const
+std::string ctor::configuration::get(const std::string& key, const std::string& default_value) const
{
- if(key == ctor::cfg::host_cxx && std::getenv("CXX"))
+ static auto paths = get_paths();
+ auto cxx_env = std::getenv("CXX");
+ if(key == ctor::cfg::build_cxx && cxx_env)
{
- static std::string s = std::getenv("CXX");
- return s;
+ static auto cxx_prog = locate(cxx_env, paths);
+ return cxx_prog;
}
- if(key == ctor::cfg::builddir && std::getenv("BUILDDIR"))
+ auto cc_env = std::getenv("CC");
+ if(key == ctor::cfg::build_cc && cc_env)
{
- static std::string s = std::getenv("BUILDDIR");
- return s;
+ static auto cc_prog = locate(cc_env, paths);
+ return cc_prog;
+ }
+
+ auto ld_env = std::getenv("LD");
+ if(key == ctor::cfg::build_ld && ld_env)
+ {
+ static auto ld_prog = locate(ld_env, paths);
+ return ld_prog;
+ }
+
+ auto ar_env = std::getenv("AR");
+ if(key == ctor::cfg::build_ar && ar_env)
+ {
+ static auto ar_prog = locate(ar_env, paths);
+ return ar_prog;
+ }
+
+ auto builddir_env = std::getenv("BUILDDIR");
+ if(key == ctor::cfg::builddir && builddir_env)
+ {
+ return builddir_env;
}
return default_value;
}
+std::vector<std::string> readDeps(const std::string& depFile,
+ ctor::toolchain toolchain)
+{
+ return {};
+}
+
int main(int argc, char* argv[])
{
- if(argc > 1)
+ auto args = std::span(argv, static_cast<std::size_t>(argc));
+ if(args.size() > 1)
{
- std::cerr << "This is a minimal bootstrap version of " << argv[0] <<
+ std::cerr << "This is a minimal bootstrap version of " << args[0] <<
" which doesn't support any arguments\n";
return 1;
}
diff --git a/src/build.cc b/src/build.cc
index ad30719..a31f6a5 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -4,12 +4,11 @@
#include "build.h"
#include <future>
-#include <vector>
#include <iostream>
#include <chrono>
-#include <set>
#include <thread>
#include <list>
+#include <algorithm>
#include "ctor.h"
@@ -17,8 +16,8 @@ using namespace std::chrono_literals;
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,
+ const std::vector<std::shared_ptr<Task>>& tasks,
+ const std::vector<std::shared_ptr<Task>>& all_tasks,
bool dryrun)
{
if(settings.verbose > 1)
@@ -26,19 +25,20 @@ int build(const ctor::settings& settings,
std::cout << "Building '" << name << "'\n";
}
- std::set<std::shared_ptr<Task>> dirtyTasks;
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
for(auto task : tasks)
{
- if(task->dirty())
+ if(task->dirty() &&
+ std::find(dirtyTasks.begin(), dirtyTasks.end(), task) == dirtyTasks.end())
{
- dirtyTasks.insert(task);
+ dirtyTasks.push_back(task);
}
}
// Dry-run returns number of dirty tasks but otherwise does nothing.
if(dryrun)
{
- return dirtyTasks.size();
+ return static_cast<int>(dirtyTasks.size());
}
if(dirtyTasks.empty())
@@ -114,17 +114,15 @@ int build(const ctor::settings& settings,
}
}
- for(auto process = processes.begin();
- process != processes.end();
- ++process)
+ for(auto& process : processes)
{
- if(process->valid() == false)
+ if(process.valid() == false)
{
continue;
}
- process->wait();
- auto ret = process->get();
- if(ret != 0)
+ process.wait();
+ auto ret = process.get();
+ if (ret != 0)
{
return ret;
}
@@ -133,30 +131,50 @@ int build(const ctor::settings& settings,
return 0;
}
-namespace
+std::vector<std::shared_ptr<Task>> getDepTasks(std::shared_ptr<Task> task,
+ std::vector<std::shared_ptr<Task>> trace)
{
-std::set<std::shared_ptr<Task>> getDepTasks(std::shared_ptr<Task> task)
-{
- std::set<std::shared_ptr<Task>> tasks;
- tasks.insert(task);
+ std::vector<std::shared_ptr<Task>> tasks;
+ tasks.push_back(task);
+ trace.push_back(task);
auto deps = task->getDependsTasks();
for(const auto& dep : deps)
{
- auto depSet = getDepTasks(dep);
- for(const auto& dep : depSet)
+ if(std::find(trace.begin(), trace.end(), dep) != trace.end())
+ {
+ trace.push_back(dep);
+ std::cerr << "Error: Cyclic dependency detected: ";
+ bool first{true};
+ for(auto t : trace)
+ {
+ if(!first)
+ {
+ std::cerr << " -> ";
+ }
+
+ first = false;
+ std::cerr << t->target();
+ }
+ std::cerr << '\n';
+ throw 1;
+ }
+ auto depSet = getDepTasks(dep, trace);
+ for(const auto& dep_inner : depSet)
{
- tasks.insert(dep);
+ if(std::find(tasks.begin(), tasks.end(), dep_inner) == tasks.end())
+ {
+ tasks.push_back(dep_inner);
+ }
}
}
return tasks;
}
-}
int build(const ctor::settings& settings,
const std::string& name,
- const std::set<std::shared_ptr<Task>>& all_tasks,
+ const std::vector<std::shared_ptr<Task>>& all_tasks,
bool dryrun)
{
bool task_found{false};
@@ -166,17 +184,27 @@ int build(const ctor::settings& settings,
{
task_found = true;
- auto depSet = getDepTasks(task);
- std::set<std::shared_ptr<Task>> ts;
- for(const auto& task : depSet)
+ try
{
- ts.insert(task);
- }
+ auto depSet = getDepTasks(task);
+ std::vector<std::shared_ptr<Task>> ts;
+ for(const auto& task_inner : depSet)
+ {
+ if(std::find(ts.begin(), ts.end(), task_inner) == ts.end())
+ {
+ ts.push_back(task_inner);
+ }
+ }
- auto ret = build(settings, name, ts, all_tasks, dryrun);
- if(ret != 0)
+ auto ret = build(settings, name, ts, all_tasks, dryrun);
+ if(ret != 0)
+ {
+ return ret;
+ }
+ }
+ catch(...)
{
- return ret;
+ return 1; // cycle detected
}
break;
@@ -195,11 +223,11 @@ int build(const ctor::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,
+ const std::vector<std::shared_ptr<Task>>& all_tasks,
bool dryrun)
{
bool task_found{false};
- std::set<std::shared_ptr<Task>> ts;
+ std::vector<std::shared_ptr<Task>> ts;
for(const auto& target : targets)
{
@@ -210,10 +238,20 @@ int build(const ctor::settings& settings,
{
task_found = true;
- auto depSet = getDepTasks(task);
- for(const auto& task : depSet)
+ try
+ {
+ auto depSet = getDepTasks(task);
+ for(const auto& task_inner : depSet)
+ {
+ if(std::find(ts.begin(), ts.end(), task_inner) == ts.end())
+ {
+ ts.push_back(task_inner);
+ }
+ }
+ }
+ catch(...)
{
- ts.insert(task);
+ return 1; // cycle detected
}
}
}
diff --git a/src/build.h b/src/build.h
index d74642c..7296f76 100644
--- a/src/build.h
+++ b/src/build.h
@@ -4,7 +4,7 @@
#pragma once
#include <string>
-#include <set>
+#include <vector>
#include <memory>
#include "task.h"
@@ -17,19 +17,24 @@ struct settings;
//! Dry-run returns number of dirty tasks but otherwise does nothing.
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,
+ const std::vector<std::shared_ptr<Task>>& tasks,
+ const std::vector<std::shared_ptr<Task>>& all_tasks,
bool dryrun = false);
//! Dry-run returns number of dirty tasks but otherwise does nothing.
int build(const ctor::settings& settings,
const std::string& name,
- const std::set<std::shared_ptr<Task>>& all_tasks,
+ const std::vector<std::shared_ptr<Task>>& all_tasks,
bool dryrun = false);
//! Dry-run returns number of dirty tasks but otherwise does nothing.
int build(const ctor::settings& settings,
const std::string& name,
const std::vector<Target>& targets,
- const std::set<std::shared_ptr<Task>>& all_tasks,
+ const std::vector<std::shared_ptr<Task>>& all_tasks,
bool dryrun = false);
+
+// Recursively build vector of dependency tasks from source task.
+// Throws if a cycle is detected.
+std::vector<std::shared_ptr<Task>> getDepTasks(std::shared_ptr<Task> task,
+ std::vector<std::shared_ptr<Task>> trace = {});
diff --git a/src/configure.cc b/src/configure.cc
index 8e88092..7a68f03 100644
--- a/src/configure.cc
+++ b/src/configure.cc
@@ -7,6 +7,7 @@
#include <filesystem>
#include <fstream>
#include <optional>
+#include <span>
#include <getoptpp/getoptpp.hpp>
@@ -16,9 +17,10 @@
#include "rebuild.h"
#include "externals.h"
#include "tools.h"
+#include "util.h"
-std::filesystem::path configurationFile("configuration.cc");
-std::filesystem::path configHeaderFile("config.h");
+const std::filesystem::path configurationFile("configuration.cc");
+const std::filesystem::path configHeaderFile("config.h");
std::map<std::string, std::string> external_includedir;
std::map<std::string, std::string> external_libdir;
@@ -29,7 +31,7 @@ const ctor::configuration& __attribute__((weak)) ctor::get_configuration()
static bool initialised{false};
if(!initialised)
{
- cfg.host_toolchain = getToolChain(cfg.get(ctor::cfg::host_cxx, "g++"));
+ cfg.build_toolchain = getToolChain(cfg.get(ctor::cfg::build_cxx, "/usr/bin/g++"));
initialised = true;
}
return cfg;
@@ -67,7 +69,7 @@ bool ctor::configuration::has(const std::string& key) const
return tools.find(key) != tools.end();
}
-const std::string& ctor::configuration::get(const std::string& key, const std::string& default_value) const
+std::string ctor::configuration::get(const std::string& key, const std::string& default_value) const
{
if(key == ctor::cfg::ctor_includedir && ctor::includedir)
{
@@ -97,57 +99,6 @@ const std::string& ctor::configuration::get(const std::string& key, const std::s
return default_value;
}
-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())
- {
- program = arch + "-" + app;
- }
- std::cout << "Looking for: " << program << "\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 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();
- }
- }
-
- std::cerr << "Could not locate " << app << " for the " << arch << " architecture\n";
- exit(1);
- return {};
-}
-
class Args
: public std::vector<char*>
{
@@ -192,6 +143,26 @@ std::ostream& operator<<(std::ostream& stream, const ctor::toolchain& toolchain)
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))
@@ -233,6 +204,7 @@ std::ostream& operator<<(std::ostream& ostr, const ctor::asm_flag& flag)
template<class> inline constexpr bool always_false_v = false;
int regenerateCache(ctor::settings& settings,
+ const std::string& name,
const std::vector<std::string>& args,
const std::map<std::string, std::string>& env)
{
@@ -241,9 +213,9 @@ int regenerateCache(ctor::settings& settings,
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++";
@@ -300,7 +272,7 @@ int regenerateCache(ctor::settings& settings,
opt.add("build", required_argument, key++,
"Configure for building on specified architecture.",
[&]() {
- build_arch = optarg;
+ build_arch_prefix = optarg;
return 0;
});
@@ -314,7 +286,7 @@ int regenerateCache(ctor::settings& 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;
});
@@ -350,19 +322,19 @@ int regenerateCache(ctor::settings& settings,
}
auto add_path_args =
- [&](const std::string& name)
+ [&](const std::string& arg_name)
{
- opt.add(name + "-includedir", required_argument, key++,
- "Set path to " + name + " header file.",
+ opt.add(arg_name + "-includedir", required_argument, key++,
+ "Set path to " + arg_name + " header file.",
[&]() {
- external_includedir[name] = optarg;
+ external_includedir[arg_name] = optarg;
return 0;
});
- opt.add(name + "-libdir", required_argument, key++,
- "Set path to " + name + " libraries.",
+ opt.add(arg_name + "-libdir", required_argument, key++,
+ "Set path to " + arg_name + " libraries.",
[&]() {
- external_libdir[name] = optarg;
+ external_libdir[arg_name] = optarg;
return 0;
});
};
@@ -386,46 +358,117 @@ int regenerateCache(ctor::settings& settings,
opt.add("help", no_argument, 'h',
"Print this help text.",
- [&]() {
- std::cout << "configure usage stuff\n";
+ [&]() -> int {
+ 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;
});
- opt.process(vargs.size(), vargs.data());
+ opt.process(static_cast<int>(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())
@@ -451,21 +494,149 @@ int regenerateCache(ctor::settings& 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_cxx.empty())
+ 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)
{
- // This is needed for bootstrapping (when running configure for the first time)
- ctor::conf_values[ctor::cfg::host_cxx] = host_cxx;
+ // 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())
{
@@ -490,43 +661,81 @@ int regenerateCache(ctor::settings& settings,
istr << "{\n";
istr << " static ctor::configuration cfg =\n";
istr << " {\n";
- istr << " .host_toolchain = " << getToolChain(host_cxx) << ",\n";
- istr << " .build_toolchain = " << getToolChain(build_cxx) << ",\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";
if(!builddir.empty())
{
- istr << " { \"" << ctor::cfg::builddir << "\", \"" << builddir << "\" },\n";
+ istr << " { \"" << ctor::cfg::builddir << "\", \"" << esc(builddir) << "\" },\n";
ctor::builddir = builddir;
}
- istr << " { \"" << ctor::cfg::host_cc << "\", \"" << host_cc << "\" },\n";
- istr << " { \"" << ctor::cfg::host_cxx << "\", \"" << host_cxx << "\" },\n";
- istr << " { \"" << ctor::cfg::host_ar << "\", \"" << host_ar << "\" },\n";
- istr << " { \"" << ctor::cfg::host_ld << "\", \"" << host_ld << "\" },\n";
- istr << " { \"" << ctor::cfg::build_cc << "\", \"" << build_cc << "\" },\n";
- istr << " { \"" << ctor::cfg::build_cxx << "\", \"" << build_cxx << "\" },\n";
- istr << " { \"" << ctor::cfg::build_ar << "\", \"" << build_ar << "\" },\n";
- istr << " { \"" << ctor::cfg::build_ld << "\", \"" << build_ld << "\" },\n";
+ 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 << " { \"" << ctor::cfg::ctor_includedir << "\", \"" << ctor_includedir << "\" },\n";
+ istr << " { \"" << ctor::cfg::ctor_includedir << "\", \"" << esc(ctor_includedir) << "\" },\n";
ctor::includedir = ctor_includedir;
}
if(!ctor_libdir.empty())
{
- istr << " { \"" << ctor::cfg::ctor_libdir << "\", \"" << ctor_libdir << "\" },\n";
+ istr << " { \"" << ctor::cfg::ctor_libdir << "\", \"" << esc(ctor_libdir) << "\" },\n";
ctor::libdir = ctor_libdir;
}
@@ -535,7 +744,7 @@ int regenerateCache(ctor::settings& settings,
for(const auto& ext : externalConfigs)
{
- istr << " { \"" << ext.name << "\", {\n";
+ istr << " { \"" << esc(ext.name) << "\", {\n";
ctor::flags resolved_flags;
if(std::holds_alternative<ctor::external_manual>(ext.external))
{
@@ -612,12 +821,14 @@ int regenerateCache(ctor::settings& settings,
int configure(const ctor::settings& global_settings, int argc, char* argv[])
{
+ auto args_span = std::span(argv, static_cast<std::size_t>(argc));
+
ctor::settings settings{global_settings};
std::vector<std::string> args;
- for(int i = 2; i < argc; ++i) // skip command and the first 'configure' arg
+ for(std::size_t i = 2; i < args_span.size(); ++i) // skip command and the first 'configure' arg
{
- args.push_back(argv[i]);
+ args.emplace_back(args_span[i]);
}
std::map<std::string, std::string> env;
@@ -645,7 +856,13 @@ int configure(const ctor::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, args_span[0], args, env);
if(ret != 0)
{
return ret;
@@ -658,19 +875,21 @@ int configure(const ctor::settings& global_settings, int argc, char* argv[])
int reconfigure(const ctor::settings& global_settings, int argc, char* argv[])
{
+ auto args_span = std::span(argv, static_cast<std::size_t>(argc));
+
ctor::settings settings{global_settings};
bool no_rerun{false};
std::vector<std::string> args;
- for(int i = 2; i < argc; ++i) // skip executable name and 'reconfigure' arg
+ for(std::size_t i = 2; i < args_span.size(); ++i) // skip executable name and 'reconfigure' arg
{
- if(i == 2 && std::string(argv[i]) == "--no-rerun")
+ if(i == 2 && std::string(args_span[i]) == "--no-rerun")
{
no_rerun = true;
continue;
}
- args.push_back(argv[i]);
+ args.emplace_back(args_span[i]);
}
const auto& cfg = ctor::get_configuration();
@@ -680,14 +899,14 @@ int reconfigure(const ctor::settings& global_settings, int argc, char* argv[])
{
std::cout << e.first << "=\"" << e.second << "\" ";
}
- std::cout << argv[0] << " configure ";
+ std::cout << args_span[0] << " configure ";
for(const auto& arg : cfg.args)
{
std::cout << arg << " ";
}
std::cout << "\n";
- auto ret = regenerateCache(settings, cfg.args, cfg.env);
+ auto ret = regenerateCache(settings, args_span[0], cfg.args, cfg.env);
if(ret != 0)
{
return ret;
@@ -700,5 +919,5 @@ int reconfigure(const ctor::settings& global_settings, int argc, char* argv[])
return 0; // this was originally invoked by configure, don't loop
}
- return execute(argv[0], args);
+ return execute(settings, args_span[0], args);
}
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 ac8d721..5344646 100644
--- a/src/configure.h
+++ b/src/configure.h
@@ -12,8 +12,8 @@ namespace ctor {
struct settings;
} // namespace ctor::
-extern std::filesystem::path configurationFile;;
-extern std::filesystem::path configHeaderFile;
+extern const std::filesystem::path configurationFile;
+extern const std::filesystem::path configHeaderFile;
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
index 36aa46a..191fed1 100644
--- a/src/ctor.h
+++ b/src/ctor.h
@@ -10,6 +10,7 @@
#include <variant>
#include <cstddef>
#include <functional>
+#include <string_view>
namespace ctor {
@@ -24,6 +25,8 @@ enum class target_type
unit_test,
unit_test_library,
function,
+
+ unknown,
};
enum class language
@@ -41,21 +44,13 @@ enum class output_system
build, // Internal tool during cross-compilation
};
-struct source
+enum class arch
{
- 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) {}
+ unix, //!< Target platform architecture is unix-based (ie. linux, bsd, etc)
+ apple, //!< Target platform architecture is macos
+ windows, //!< Target platform architecture is windows
- std::string file;
- ctor::language language{ctor::language::automatic};
- std::string output{};
+ unknown, //!< Target platform architecture has not yet detected or was not possible to detect
};
enum class toolchain
@@ -66,12 +61,38 @@ enum class toolchain
clang,
};
+struct source
+{
+ source(const char* file_) : file(file_) {} // convenience ctor
+
+ source(std::string_view file_) : source(file_, ctor::language::automatic) {}
+ source(std::string_view file_, ctor::language lang_) : file(file_), language(lang_) {}
+
+ source(std::string_view file_, std::string_view output_) : file(file_), output(output_) {}
+ source(std::string_view file_, ctor::language lang_, std::string_view output_) : file(file_), language(lang_), output(output_) {}
+
+ source(ctor::toolchain toolchain_, std::string_view file_) : file(file_), toolchain(toolchain_) {}
+ source(ctor::toolchain toolchain_, std::string_view file_, ctor::language lang_) : file(file_), toolchain(toolchain_), language(lang_) {}
+
+ source(ctor::toolchain toolchain_, std::string_view file_, std::string_view output_) : file(file_), toolchain(toolchain_), output(output_) {}
+
+ source(ctor::toolchain toolchain_, std::string_view file_, ctor::language lang_, std::string_view output_) : file(file_), toolchain(toolchain_), language(lang_), output(output_) {}
+
+ std::string file;
+ ctor::toolchain toolchain{ctor::toolchain::any};
+ ctor::language language{ctor::language::automatic};
+ std::string output{};
+};
+
enum class cxx_opt
{
// gcc/clang
output, // -o
debug, // -g
warn_all, // -Wall
+ warn_conversion, // -Wconversion
+ warn_shadow, // -Wshadow
+ warn_extra, // -Wextra
warnings_as_errors, // -Werror
generate_dep_tree, // -MMD
no_link, // -c
@@ -80,6 +101,7 @@ enum class cxx_opt
optimization, // -O<arg>
position_independent_code, // -fPIC
position_independent_executable, // -fPIE
+ define, // -D<arg>[=<arg2>]
custom, // entire option taken verbatim from <arg>
};
@@ -89,6 +111,9 @@ enum class c_opt
output, // -o
debug, // -g
warn_all, // -Wall
+ warn_conversion, // -Wconversion
+ warn_shadow, // -Wshadow
+ warn_extra, // -Wextra
warnings_as_errors, // -Werror
generate_dep_tree, // -MMD
no_link, // -c
@@ -97,6 +122,7 @@ enum class c_opt
optimization, // -O<arg>
position_independent_code, // -fPIC
position_independent_executable, // -fPIE
+ define, // -D<arg>[=<arg2>]
custom, // entire option taken verbatim from <arg>
};
@@ -120,6 +146,11 @@ enum class ld_opt
enum class ar_opt
{
// gcc/clang
+ replace, // -r
+ add_index, // -s
+ create, // -c
+ output, // <arg>
+
custom, // entire option taken verbatim from <arg>
};
@@ -133,18 +164,24 @@ template<typename T>
class flag
{
public:
- flag(const std::string& str);
+ flag(std::string_view 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) {}
+ flag(T opt_) : opt(opt_) {}
+ flag(T opt_, std::string_view arg_, std::string_view arg2_ = "")
+ : opt(opt_), arg(arg_), arg2(arg2_) {}
+ flag(T opt_, const char* arg_, const char* arg2_ = "")
+ : opt(opt_), arg(arg_), arg2(arg2_) {}
+ flag(ctor::toolchain toolchain_, T opt_)
+ : toolchain(toolchain_), opt(opt_) {}
+ flag(ctor::toolchain toolchain_, T opt_, const char* arg_, const char* arg2_ = "")
+ : toolchain(toolchain_), opt(opt_), arg(arg_), arg2(arg2_) {}
+ flag(ctor::toolchain toolchain_, T opt_, std::string_view arg_, std::string_view arg2_ = "")
+ : toolchain(toolchain_), opt(opt_), arg(arg_), arg2(arg2_) {}
ctor::toolchain toolchain{ctor::toolchain::any};
- T opt;
+ T opt{};
std::string arg;
+ std::string arg2;
};
using c_flag = ctor::flag<ctor::c_opt>;
@@ -185,7 +222,7 @@ 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::host};
+ 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
@@ -209,7 +246,7 @@ struct external_manual
struct external_configuration
{
std::string name; // Name for configuration
- ctor::output_system system{ctor::output_system::host};
+ ctor::output_system system{ctor::output_system::build};
std::variant<ctor::external_manual> external;
};
@@ -219,10 +256,10 @@ 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 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); }
+#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
@@ -230,12 +267,12 @@ namespace cfg
constexpr auto builddir = "builddir";
constexpr auto host_cc = "host-cc";
-constexpr auto host_cxx = "host-cpp";
+constexpr auto host_cxx = "host-cxx";
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_cxx = "build-cxx";
constexpr auto build_ar = "build-ar";
constexpr auto build_ld = "build-ld";
@@ -246,10 +283,13 @@ 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;
+ 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
diff --git a/src/deps.cc b/src/deps.cc
new file mode 100644
index 0000000..9400b35
--- /dev/null
+++ b/src/deps.cc
@@ -0,0 +1,120 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include "deps.h"
+
+#include "util.h"
+
+#include <fstream>
+
+namespace {
+/* Format example:
+build/src/libctor_a-libctor_cc.o: src/libctor.cc \
+ src/getoptpp/getoptpp.hpp src/ctor.h src/configure.h src/rebuild.h \
+ src/A\ B\ C.h src/task.h src/build.h src/unittest.h
+ */
+std::vector<std::string> readDepsMake(const std::string& dep_file)
+{
+ auto str = readFile(dep_file);
+
+ std::vector<std::string> output;
+ std::string tmp;
+
+ enum class State
+ {
+ pre_colon,
+ post_colon,
+ in_escape,
+ } state{State::pre_colon};
+
+ auto old_state{state};
+ for(const auto& c : str)
+ {
+ if(c == '\r')
+ {
+ // just always ignore windows extra newline char
+ continue;
+ }
+
+ switch(state)
+ {
+ case State::pre_colon:
+ if(c == ':') // ignore everything until the colon
+ {
+ state = State::post_colon;
+ continue;
+ }
+ continue;
+
+ case State::post_colon:
+ if(c == '\n')
+ {
+ // done
+ if(!tmp.empty())
+ {
+ output.emplace_back(tmp);
+ }
+ return output;
+ }
+ if(c == '\\')
+ {
+ old_state = state;
+ state = State::in_escape;
+ continue;
+ }
+ if(c == '\t' || c == ' ') // new token
+ {
+ if(!tmp.empty())
+ {
+ output.emplace_back(tmp);
+ }
+ tmp.clear();
+ continue;
+ }
+ break;
+
+ case State::in_escape:
+ if(c == '\n')
+ {
+ // linewrap
+ state = old_state;
+ continue;
+ }
+ state = old_state;
+ break;
+ }
+
+ tmp += c;
+ }
+
+ if(!tmp.empty())
+ {
+ output.emplace_back(tmp);
+ }
+
+ return output;
+}
+}
+
+std::vector<std::string> readDeps(const std::string& dep_file,
+ ctor::toolchain toolchain)
+{
+ if(!std::filesystem::exists(dep_file))
+ {
+ return {};
+ }
+
+ switch(toolchain)
+ {
+ case ctor::toolchain::any:
+ case ctor::toolchain::none:
+ return {};
+
+ // makefile .d file based:
+ case ctor::toolchain::gcc:
+ case ctor::toolchain::clang:
+ return readDepsMake(dep_file);
+ }
+
+ return {};
+}
diff --git a/src/deps.h b/src/deps.h
new file mode 100644
index 0000000..be0dfb2
--- /dev/null
+++ b/src/deps.h
@@ -0,0 +1,12 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#pragma once
+
+#include "ctor.h"
+
+#include <vector>
+#include <string>
+
+std::vector<std::string> readDeps(const std::string& dep_file,
+ ctor::toolchain toolchain);
diff --git a/src/execute.cc b/src/execute.cc
index 20a4a02..ad6c2a2 100644
--- a/src/execute.cc
+++ b/src/execute.cc
@@ -3,6 +3,9 @@
// See accompanying file LICENSE for details.
#include "execute.h"
+#include "ctor.h"
+#include "pointerlist.h"
+
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
@@ -20,22 +23,53 @@ https://stackoverflow.com/questions/4259629/what-is-the-difference-between-fork-
namespace
{
+
int parent_waitpid(pid_t pid)
{
- int status;
+ int status{};
+
+ auto rc_pid = waitpid(pid, &status, 0);
- if(waitpid(pid, &status, 0) != pid)
+ if(rc_pid > 0)
{
- return 1;
+ if(WIFEXITED(status))
+ {
+ // Child exited with normally
+ return WEXITSTATUS(status);
+ }
+ if(WIFSIGNALED(status))
+ {
+ // Child exited via signal (segfault, abort, ...)
+ std::cerr << strsignal(status) << '\n';
+ return WTERMSIG(status);
+ }
+ }
+ else
+ { // No PID returned, this is an error
+ if(errno == ECHILD)
+ {
+ // No children exist.
+ return 1;
+ }
+ else
+ {
+ // Unexpected error.
+ abort();
+ }
}
- return WEXITSTATUS(status);
+ // Should never happen...
+ return 1;
}
} // namespace ::
-int execute(const std::string& command,
+extern char **environ; // see 'man environ'
+
+int execute(const ctor::settings& settings,
+ const std::string& command,
const std::vector<std::string>& args,
- bool verbose)
+ const std::map<std::string, std::string>& env,
+ [[maybe_unused]] bool terminate)
{
std::vector<const char*> argv;
argv.push_back(command.data());
@@ -45,22 +79,22 @@ 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)
+ {
+ break;
+ }
+ if(!cmd.empty())
{
- if(arg == nullptr)
- {
- break;
- }
- if(!cmd.empty())
- {
- cmd += " ";
- }
- cmd += arg;
+ cmd += " ";
}
+ cmd += arg;
+ }
+ if(settings.verbose > 0)
+ {
std::cout << cmd << std::endl;
}
@@ -68,23 +102,38 @@ int execute(const std::string& command,
auto pid = vfork();
if(pid == 0)
{
- execv(command.data(), (char**)argv.data());
+ EnvMap envmap((const char**)environ);
+ for(const auto& [key, value] : env)
+ {
+ envmap.insert(key + "=" + value);
+ }
+
+ auto [_, envv] = envmap.get();
+ execve(command.data(), const_cast<char* const *>(argv.data()),
+ const_cast<char* const *>(envv));
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..4288bb7 100644
--- a/src/execute.h
+++ b/src/execute.h
@@ -5,7 +5,14 @@
#include <string>
#include <vector>
+#include <map>
-int execute(const std::string& command,
- const std::vector<std::string>& args,
- bool verbose = true);
+namespace ctor {
+struct settings;
+}//ctor::
+
+int execute(const ctor::settings& settings,
+ const std::string& command,
+ const std::vector<std::string>& args = {},
+ const std::map<std::string, std::string>& env = {},
+ bool terminate = false);
diff --git a/src/externals_manual.cc b/src/externals_manual.cc
index 3b96263..0563a5e 100644
--- a/src/externals_manual.cc
+++ b/src/externals_manual.cc
@@ -13,7 +13,7 @@
extern std::map<std::string, std::string> external_includedir;
extern std::map<std::string, std::string> external_libdir;
-int resolv(const ctor::settings& settings, const ctor::external_configuration& config,
+int resolv([[maybe_unused]]const ctor::settings& settings, const ctor::external_configuration& config,
const ctor::external_manual& ext, ctor::flags& flags)
{
flags = ext.flags;
@@ -21,14 +21,14 @@ int resolv(const ctor::settings& settings, const ctor::external_configuration& c
auto inc = external_includedir.find(config.name);
if(inc != external_includedir.end())
{
- flags.cflags.push_back({ctor::c_opt::include_path, inc->second});
- flags.cxxflags.push_back({ctor::cxx_opt::include_path, inc->second});
+ flags.cflags.emplace_back(ctor::c_opt::include_path, inc->second);
+ flags.cxxflags.emplace_back(ctor::cxx_opt::include_path, inc->second);
}
auto lib = external_libdir.find(config.name);
if(lib != external_libdir.end())
{
- flags.ldflags.push_back({ctor::ld_opt::library_path, lib->second});
+ flags.ldflags.emplace_back(ctor::ld_opt::library_path, lib->second);
}
return 0;
diff --git a/src/getoptpp b/src/getoptpp
-Subproject 9ff20ef857429619267e3f156a4f81ad9e1eb8c
+Subproject 5aba94355ec638c6f8612f86be309ed684979ae
diff --git a/src/libctor.cc b/src/libctor.cc
index 21792e1..aaf17c9 100644
--- a/src/libctor.cc
+++ b/src/libctor.cc
@@ -15,7 +15,7 @@
#include <deque>
#include <fstream>
#include <cstdlib>
-#include <set>
+#include <span>
#include <getoptpp/getoptpp.hpp>
@@ -28,6 +28,8 @@
int main(int argc, char* argv[])
{
+ auto args = std::span(argv, static_cast<std::size_t>(argc));
+
ctor::settings settings{};
const auto& c = ctor::get_configuration();
@@ -35,12 +37,12 @@ int main(int argc, char* argv[])
settings.parallel_processes =
std::max(1u, std::thread::hardware_concurrency()) * 2 - 1;
- if(argc > 1 && std::string(argv[1]) == "configure")
+ if(args.size() > 1 && std::string(args[1]) == "configure")
{
return configure(settings, argc, argv);
}
- if(argc > 1 && std::string(argv[1]) == "reconfigure")
+ if(args.size() > 1 && std::string(args[1]) == "reconfigure")
{
return reconfigure(settings, argc, argv);
}
@@ -65,7 +67,8 @@ int main(int argc, char* argv[])
[&]() {
try
{
- settings.parallel_processes = std::stoi(optarg);
+ settings.parallel_processes =
+ static_cast<std::size_t>(std::stoi(optarg));
}
catch(...)
{
@@ -101,7 +104,7 @@ int main(int argc, char* argv[])
"Add specified file to the build configurations.",
[&]() {
no_relaunch = true;
- add_files.push_back(optarg);
+ add_files.emplace_back(optarg);
return 0;
});
@@ -109,7 +112,7 @@ int main(int argc, char* argv[])
"Remove specified file from the build configurations.",
[&]() {
no_relaunch = true;
- remove_files.push_back(optarg);
+ remove_files.emplace_back(optarg);
return 0;
});
@@ -156,8 +159,8 @@ int main(int argc, char* argv[])
opt.add("help", no_argument, 'h',
"Print this help text.",
- [&]() {
- std::cout << "Usage: " << argv[0] << " [options] [target] ...\n";
+ [&]() -> int {
+ std::cout << "Usage: " << args[0] << " [options] [target] ...\n";
std::cout <<
R"_( where target can be either:
configure - run configuration step (cannot be used with other targets).
@@ -171,7 +174,6 @@ Options:
)_";
opt.help();
exit(0);
- return 0;
});
opt.process(argc, argv);
@@ -185,17 +187,18 @@ Options:
if(list_files)
{
no_default_build = true;
- std::set<std::string> files;
+ std::vector<std::string> files;
for(std::size_t i = 0; i < numConfigFiles; ++i)
{
- files.insert(configFiles[i].file);
+ files.emplace_back(configFiles[i].file);
}
for(std::size_t i = 0; i < numExternalConfigFiles; ++i)
{
- files.insert(externalConfigFiles[i].file);
+ files.emplace_back(externalConfigFiles[i].file);
}
+ std::sort(files.begin(), files.end());
for(const auto& file : files)
{
std::cout << file << "\n";
@@ -270,7 +273,6 @@ Options:
if(print_configure_db)
{
no_default_build = true;
- const auto& c = ctor::get_configuration();
for(const auto& config : c.tools)
{
std::cout << config.first << ": " << config.second << "\n";
diff --git a/src/pointerlist.cc b/src/pointerlist.cc
new file mode 100644
index 0000000..c0242f7
--- /dev/null
+++ b/src/pointerlist.cc
@@ -0,0 +1,123 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include "pointerlist.h"
+
+#include <cstring>
+
+PointerList::PointerList(int argc, const char* const argv[])
+{
+ for(int i = 0; i < argc; ++i)
+ {
+ push_back(argv[i]);
+ }
+}
+
+std::pair<int, const char* const*> PointerList::get()
+{
+ argptrs.clear();
+ for(const auto& arg : *this)
+ {
+ argptrs.push_back(arg.data());
+ }
+ argptrs.push_back(nullptr);
+
+ return {argptrs.size() - 1, // size not counting the nullptr at the end
+ argptrs.data()};
+}
+
+EnvMap::EnvMap(const char* const env[])
+{
+ if(env == nullptr)
+ {
+ return;
+ }
+
+ auto ptr = env;
+ while(*ptr)
+ {
+ insert(std::string_view(*ptr));
+ ++ptr;
+ }
+}
+
+EnvMap::EnvMap(const char* env)
+{
+ if(env == nullptr)
+ {
+ return;
+ }
+
+ auto ptr = env;
+ while(*ptr)
+ {
+ insert(ptr);
+ ptr += strlen(ptr) + 1;
+ }
+}
+
+std::string EnvMap::operator[](const std::string& key) const
+{
+ try
+ {
+ return data.at(key);
+ }
+ catch(...)
+ {
+ return {};
+ }
+}
+
+void EnvMap::clear()
+{
+ data.clear();
+}
+
+void EnvMap::insert(std::string_view key_value)
+{
+ auto equals_sign = key_value.find('=');
+ if(equals_sign == std::string::npos)
+ {
+ insert({key_value, ""});
+ return;
+ }
+ std::string key{key_value.substr(0, equals_sign)};
+ std::string value{key_value.substr(equals_sign + 1)}; // skip '='
+ insert({key, value});
+}
+
+void EnvMap::insert(const std::pair<std::string_view, std::string_view>& item)
+{
+ data[std::string(item.first)] = item.second;
+}
+
+bool EnvMap::contains(std::string_view key) const
+{
+ return data.contains(std::string{key});
+}
+
+std::size_t EnvMap::size() const
+{
+ return data.size();
+}
+
+std::string EnvMap::stringify() const
+{
+ std::string str;
+ for(const auto& [key, value] : data)
+ {
+ str += key + "=" + value + '\0';
+ }
+ str += '\0';
+ return str;
+}
+
+std::pair<int, const char* const*> EnvMap::get()
+{
+ pointerlist.clear();
+ for(const auto& [key, value] : data)
+ {
+ pointerlist.push_back(key + "=" + value + '\0');
+ }
+ return pointerlist.get();
+}
diff --git a/src/pointerlist.h b/src/pointerlist.h
new file mode 100644
index 0000000..988bb26
--- /dev/null
+++ b/src/pointerlist.h
@@ -0,0 +1,74 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#pragma once
+
+#include <string>
+#include <vector>
+#include <deque>
+#include <utility>
+#include <map>
+
+//! Maintains an (owning) list of string args and converts them to argc/argv
+//! compatible arguments on request.
+//! The returned pointers are guaranteed to be valid as long as the PointerList
+//! object lifetime is not exceeded.
+class PointerList
+ : public std::deque<std::string>
+{
+public:
+ PointerList() = default;
+ PointerList(int argc, const char* const argv[]);
+
+ //! Returns argc/argv pair from the current list of args
+ //! The argv entry after the last is a nullptr (not included in the argc)
+ std::pair<int, const char* const*> get();
+
+private:
+ std::vector<const char*> argptrs;
+};
+
+
+//! Maintains an owning map of strings representing the env.
+class EnvMap
+{
+public:
+ EnvMap() = default;
+
+ //! Initialize from an array of pointers to key=value\0 strings terminated
+ //! by \0
+ EnvMap(const char* const env[]);
+
+ //! Initialize from a string of the format
+ //! key1=val\0key2=val\0...keyN=val\0\0
+ EnvMap(const char* env);
+
+ std::string operator[](const std::string& key) const;
+
+ //! Clear all items in the map
+ void clear();
+
+ //! Insert string from format: key=value
+ void insert(std::string_view key_value);
+
+ //! Regular map insert
+ void insert(const std::pair<std::string_view, std::string_view>& item);
+
+ //! Checks if the container contains element with specific key
+ bool contains(std::string_view key) const;
+
+ std::size_t size() const;
+
+ //! Return string with the following format:
+ //! key1=val\0key2=val\0...keyN=val\0\0
+ std::string stringify() const;
+
+ //! Returns the map as argc/argv pair where each pointer points to a string
+ //! of the format key=value\0 and is terminated with a nullptr
+ std::pair<int, const char* const*> get();
+
+private:
+ std::map<std::string, std::string> data{};
+
+ PointerList pointerlist;
+};
diff --git a/src/rebuild.cc b/src/rebuild.cc
index c97452d..52245fe 100644
--- a/src/rebuild.cc
+++ b/src/rebuild.cc
@@ -8,6 +8,7 @@
#include <algorithm>
#include <source_location>
#include <cstring>
+#include <span>
#include "configure.h"
#include "ctor.h"
@@ -70,7 +71,7 @@ int reg(const char* location)
int unreg(const char* location)
{
- std::size_t found{0};
+ int found{0};
for(std::size_t i = 0; i < numConfigFiles;)
{
if(std::string(location) == configFiles[i].file)
@@ -150,6 +151,8 @@ bool contains(const std::vector<ctor::source>& sources, const std::string& file)
bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[],
bool relaunch_allowed)
{
+ auto args_span = std::span(argv, static_cast<std::size_t>(argc));
+
using namespace std::string_literals;
if(global_settings.verbose > 1)
@@ -159,38 +162,44 @@ bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[
ctor::build_configuration config;
+ config.type = ctor::target_type::executable;
config.name = "ctor";
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"});
+ config.flags.cxxflags.emplace_back(ctor::cxx_opt::optimization, "3");
+ config.flags.cxxflags.emplace_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({ctor::cxx_opt::include_path,
- c.get(ctor::cfg::ctor_includedir)});
+ config.flags.cxxflags.emplace_back(ctor::cxx_opt::include_path,
+ c.get(ctor::cfg::ctor_includedir));
}
if(c.has(ctor::cfg::ctor_libdir))
{
- config.flags.ldflags.push_back({ctor::ld_opt::library_path, c.get(ctor::cfg::ctor_libdir)});
+ config.flags.ldflags.emplace_back(ctor::ld_opt::library_path,
+ c.get(ctor::cfg::ctor_libdir));
}
- config.flags.ldflags.push_back({ctor::ld_opt::link, "ctor"});
- config.flags.ldflags.push_back({ctor::ld_opt::threads});
+ config.flags.ldflags.emplace_back(ctor::ld_opt::link, "ctor");
+ config.flags.ldflags.emplace_back(ctor::ld_opt::strip);
+ config.flags.ldflags.emplace_back(ctor::ld_opt::threads);
ctor::settings settings{global_settings};
settings.verbose = -1; // Make check completely silent.
- settings.builddir += "/ctor"; // override builddir to use ctor subdir
+
+ // override builddir to use ctor subdir
+ auto ctor_builddir = std::filesystem::path(settings.builddir) / "ctor";
+ settings.builddir = ctor_builddir.string();
{
std::filesystem::path buildfile = settings.builddir;
- std::filesystem::path currentfile = argv[0];
+ std::filesystem::path currentfile = args_span[0];
config.target = std::filesystem::relative(currentfile, buildfile).string();
}
if(std::filesystem::exists(configurationFile))
{
- config.sources.push_back(configurationFile.string());
+ config.sources.emplace_back(configurationFile.string());
}
for(std::size_t i = 0; i < numConfigFiles; ++i)
@@ -204,7 +213,7 @@ bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[
// Ensure that files containing multiple configurations are only added once.
if(!contains(config.sources, location))
{
- config.sources.push_back(location);
+ config.sources.emplace_back(location);
}
}
@@ -219,11 +228,11 @@ bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[
// Ensure that files containing multiple configurations are only added once.
if(!contains(config.sources, location))
{
- config.sources.push_back(location);
+ config.sources.emplace_back(location);
}
}
- auto tasks = taskFactory({config}, settings, {});
+ auto tasks = taskFactory({config}, settings, {}, true);
for(auto task : tasks)
{
@@ -259,16 +268,17 @@ bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[
if(reconfigure)
{
std::vector<std::string> args;
- args.push_back("reconfigure");
+ args.emplace_back("reconfigure");
if(!relaunch_allowed)
{
- args.push_back("--no-rerun");
+ args.emplace_back("--no-rerun");
}
- for(int i = 1; i < argc; ++i)
+ for(std::size_t i = 1; i < args_span.size(); ++i)
{
- args.push_back(argv[i]);
+ args.emplace_back(args_span[i]);
}
- auto ret = execute(argv[0], args);
+
+ auto ret = execute(settings, args_span[0], args);
//if(ret != 0)
{
exit(ret);
diff --git a/src/task.cc b/src/task.cc
index 817ee3a..ef7731b 100644
--- a/src/task.cc
+++ b/src/task.cc
@@ -3,19 +3,20 @@
// See accompanying file LICENSE for details.
#include "task.h"
-#include <unistd.h>
#include <iostream>
+#include <algorithm>
+#include <utility>
-Task::Task(const ctor::build_configuration& config, const ctor::settings& settings,
- const std::string& sourceDir)
- : config(config)
+Task::Task(const ctor::build_configuration& config_, const ctor::settings& settings_,
+ std::string sourceDir_)
+ : config(config_)
, output_system(config.system)
- , settings(settings)
- , sourceDir(sourceDir)
+ , settings(settings_)
+ , sourceDir(std::move(sourceDir_))
{
}
-int Task::registerDepTasks(const std::set<std::shared_ptr<Task>>& tasks)
+int Task::registerDepTasks(const std::vector<std::shared_ptr<Task>>& tasks)
{
for(const auto& depStr : depends())
{
@@ -24,7 +25,10 @@ int Task::registerDepTasks(const std::set<std::shared_ptr<Task>>& tasks)
{
if(*task == depStr)
{
- dependsTasks.insert(task);
+ if(std::find(dependsTasks.begin(), dependsTasks.end(), task) == dependsTasks.end())
+ {
+ dependsTasks.push_back(task);
+ }
found = true;
}
}
@@ -41,11 +45,14 @@ int Task::registerDepTasks(const std::set<std::shared_ptr<Task>>& tasks)
bool Task::operator==(const std::string& depStr)
{
+ std::filesystem::path generated_output = sourceDir;
+ generated_output /= target();
return
- name() == depStr ||
- target() == depStr ||
- sourceDir + "/" + target() == depStr ||
- targetFile().string() == depStr
+ (!derived() && name() == depStr) || // compare to name
+ (!derived() && config.target == depStr) || // compare to stated target (ex. foo.a)
+ target() == depStr || // compare to derived (derived to foo.lib on msvc)
+ generated_output == depStr || // not sure what this is for?!
+ targetFile().string() == depStr // compare to target output file
;
}
@@ -140,6 +147,7 @@ std::string Task::compiler() const
case ctor::output_system::build:
return c.get(ctor::cfg::build_cc, "/usr/bin/gcc");
}
+ break;
case ctor::language::cpp:
switch(outputSystem())
{
@@ -148,14 +156,18 @@ std::string Task::compiler() const
case ctor::output_system::build:
return c.get(ctor::cfg::build_cxx, "/usr/bin/g++");
}
+ break;
default:
std::cerr << "Unknown CC target type\n";
exit(1);
break;
}
+
+ std::cerr << "Unhandled compiler!\n";
+ exit(1);
}
-std::set<std::shared_ptr<Task>> Task::getDependsTasks()
+std::vector<std::shared_ptr<Task>> Task::getDependsTasks()
{
return dependsTasks;
}
diff --git a/src/task.h b/src/task.h
index 9e99f25..32d0de3 100644
--- a/src/task.h
+++ b/src/task.h
@@ -6,7 +6,6 @@
#include <vector>
#include <string>
#include <atomic>
-#include <set>
#include <memory>
#include <filesystem>
@@ -25,15 +24,15 @@ class Task
{
public:
Task(const ctor::build_configuration& config, const ctor::settings& settings,
- const std::string& sourceDir);
+ 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; }
+ int registerDepTasks(const std::vector<std::shared_ptr<Task>>& tasks);
+ virtual int registerDepTasksInner([[maybe_unused]]const std::vector<std::shared_ptr<Task>>& tasks) { return 0; }
bool operator==(const std::string& dep);
- virtual std::string name() const;
+ std::string name() const;
bool dirty();
bool ready();
int run();
@@ -64,7 +63,7 @@ public:
ctor::output_system outputSystem() const;
std::string compiler() const;
- std::set<std::shared_ptr<Task>> getDependsTasks();
+ std::vector<std::shared_ptr<Task>> getDependsTasks();
virtual std::string source() const { return {}; }
@@ -74,7 +73,7 @@ protected:
virtual bool dirtyInner() { return false; }
std::vector<std::string> dependsStr;
- std::set<std::shared_ptr<Task>> dependsTasks;
+ std::vector<std::shared_ptr<Task>> dependsTasks;
const ctor::build_configuration& config;
ctor::target_type target_type{ctor::target_type::automatic};
ctor::language source_language{ctor::language::automatic};
diff --git a/src/task_ar.cc b/src/task_ar.cc
index 81ced0f..3b45cc2 100644
--- a/src/task_ar.cc
+++ b/src/task_ar.cc
@@ -11,19 +11,22 @@
#include "util.h"
#include "tools.h"
-TaskAR::TaskAR(const ctor::build_configuration& config,
- const ctor::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)
- : Task(config, settings, sourceDir)
- , config(config)
- , settings(settings)
- , sourceDir(sourceDir)
+ const std::string& sourceDir_)
+ : Task(config_, settings_, sourceDir_)
+ , _targetFile(target)
+ , config(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;
@@ -33,13 +36,12 @@ TaskAR::TaskAR(const ctor::build_configuration& config,
for(const auto& dep : config.depends)
{
- depFiles.push_back(dep);
+ depFiles.emplace_back(dep);
}
flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();
flagsFile += ".flags";
- target_type = ctor::target_type::static_library;
source_language = ctor::language::c;
for(const auto& source : config.sources)
{
@@ -50,6 +52,8 @@ TaskAR::TaskAR(const ctor::build_configuration& config,
source_language = ctor::language::cpp;
}
}
+
+ std::filesystem::create_directories(targetFile().parent_path());
}
bool TaskAR::dirtyInner()
@@ -78,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());
@@ -93,7 +105,8 @@ int TaskAR::runInner()
if(settings.verbose == 0)
{
- std::cout << "AR => " << targetFile().string() << std::endl;
+ std::string output = "AR => " + targetFile().string() + '\n';
+ std::cout << output << std::flush;
}
const auto& c = ctor::get_configuration();
@@ -108,7 +121,13 @@ int TaskAR::runInner()
break;
}
- return execute(tool, args, settings.verbose > 0);
+ auto res = execute(settings, tool, args, c.env);
+ if(res != 0)
+ {
+ std::filesystem::remove(targetFile());
+ }
+
+ return res;
}
int TaskAR::clean()
@@ -176,7 +195,6 @@ std::string TaskAR::flagsString() const
flagsStr += str;
}
}
- flagsStr += "\n";
for(const auto& dep : config.depends)
{
diff --git a/src/task_cc.cc b/src/task_cc.cc
index 56915f5..b9edcf7 100644
--- a/src/task_cc.cc
+++ b/src/task_cc.cc
@@ -6,31 +6,33 @@
#include <iostream>
#include <fstream>
#include <cassert>
+#include <algorithm>
#include "ctor.h"
#include "execute.h"
#include "util.h"
#include "tools.h"
-
-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)
- , sourceDir(sourceDir)
+#include "deps.h"
+
+TaskCC::TaskCC(const ctor::build_configuration& config_, const ctor::settings& settings_,
+ const std::string& sourceDir_, const ctor::source& source)
+ : Task(config_, settings_, sourceDir_)
+ , sourceFile(sourceDir_)
+ , config(config_)
+ , settings(settings_)
+ , sourceDir(sourceDir_)
, _source(source)
{
- sourceFile = sourceDir;
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 = ctor::target_type::object;
+ output_system = config.system;
source_language = source.language;
if(source_language == ctor::language::automatic)
{
@@ -56,36 +58,38 @@ TaskCC::TaskCC(const ctor::build_configuration& config, const ctor::settings& se
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)
+int TaskCC::registerDepTasksInner(const std::vector<std::shared_ptr<Task>>& tasks)
{
for(const auto& task : tasks)
{
if(*task == _source.file)
{
- dependsTasks.insert(task);
+ if(std::find(dependsTasks.begin(), dependsTasks.end(), task) == dependsTasks.end())
+ {
+ dependsTasks.push_back(task);
+ }
}
}
return 0;
}
-std::string TaskCC::name() const
-{
- return {};
-}
-
bool TaskCC::dirtyInner()
{
if(!std::filesystem::exists(sourceFile))
@@ -128,7 +132,8 @@ bool TaskCC::dirtyInner()
}
}
- auto depList = readDeps(depsFile.string());
+ auto toolchain = getToolChain(config.system);
+ auto depList = readDeps(depsFile.string(), toolchain);
for(const auto& dep : depList)
{
if(!std::filesystem::exists(dep) ||
@@ -167,25 +172,33 @@ int TaskCC::runInner()
if(settings.verbose == 0)
{
+ std::string output;
switch(sourceLanguage())
{
case ctor::language::c:
- std::cout << "CC ";
+ output = "CC ";
break;
case ctor::language::cpp:
- std::cout << "CXX ";
+ output = "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() << std::endl;
+ output += sourceFile.lexically_normal().string() + " => " +
+ targetFile().lexically_normal().string() + '\n';
+ std::cout << output << std::flush;
+ }
+
+ const auto& cfg = ctor::get_configuration();
+ auto res = execute(settings, compiler(), args, cfg.env);
+ if(res != 0)
+ {
+ std::filesystem::remove(targetFile());
}
- return execute(compiler(), args, settings.verbose > 0);
+ return res;
}
int TaskCC::clean()
@@ -278,6 +291,7 @@ std::vector<std::string> TaskCC::flags() const
exit(1);
break;
}
+
}
std::string TaskCC::flagsString() const
@@ -301,7 +315,7 @@ std::vector<std::string> TaskCC::getCompilerArgs() const
{
case ctor::language::c:
{
- append(args, c_option(toolchain, ctor::c_opt::generate_dep_tree));
+ append(args, c_option(toolchain, ctor::c_opt::generate_dep_tree, depsFile.string()));
if(std::filesystem::path(config.target).extension() == ".so")
{
@@ -343,7 +357,7 @@ std::vector<std::string> TaskCC::getCompilerArgs() const
case ctor::language::cpp:
{
- append(args, cxx_option(toolchain, ctor::cxx_opt::generate_dep_tree));
+ append(args, cxx_option(toolchain, ctor::cxx_opt::generate_dep_tree, depsFile.string()));
if(std::filesystem::path(config.target).extension() == ".so")
{
diff --git a/src/task_cc.h b/src/task_cc.h
index 70bb13c..2299fcd 100644
--- a/src/task_cc.h
+++ b/src/task_cc.h
@@ -19,9 +19,8 @@ public:
const std::string& sourceDir, const ctor::source& source);
virtual ~TaskCC() = default;
- int registerDepTasksInner(const std::set<std::shared_ptr<Task>>& tasks) override;
+ int registerDepTasksInner(const std::vector<std::shared_ptr<Task>>& tasks) override;
- std::string name() const override;
bool dirtyInner() override;
int runInner() override;
diff --git a/src/task_fn.cc b/src/task_fn.cc
index b11ff15..b6b50ea 100644
--- a/src/task_fn.cc
+++ b/src/task_fn.cc
@@ -11,13 +11,13 @@
#include "execute.h"
#include "util.h"
-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)
+TaskFn::TaskFn(const ctor::build_configuration& config_, const ctor::settings& settings_,
+ const std::string& sourceDir_, const ctor::source& source)
+ : Task(config_, settings_, sourceDir_)
+ , sourceFile(sourceDir_)
+ , config(config_)
+ , settings(settings_)
{
- sourceFile = sourceDir;
sourceFile /= source.file;
std::filesystem::create_directories(std::filesystem::path(settings.builddir) / sourceFile.parent_path());
@@ -68,15 +68,22 @@ int TaskFn::runInner()
if(settings.verbose >= 0)
{
- std::cout << "Fn" << " " <<
- sourceFile.lexically_normal().string() << " => " <<
- targetFile().lexically_normal().string() << std::endl;
+ std::string output = "Fn " +
+ sourceFile.lexically_normal().string() + " => " +
+ targetFile().lexically_normal().string() + '\n';
+ std::cout << output << std::flush;
}
- return config.function(sourceFile.string(),
- targetFile().string(),
- config,
- settings);
+ auto res = config.function(sourceFile.string(),
+ targetFile().string(),
+ config,
+ settings);
+ if(res != 0)
+ {
+ std::filesystem::remove(targetFile());
+ }
+
+ return res;
}
int TaskFn::clean()
@@ -97,7 +104,7 @@ std::vector<std::string> TaskFn::depends() const
std::string TaskFn::target() const
{
- return _targetFile;
+ return _targetFile.string();
}
std::filesystem::path TaskFn::targetFile() const
diff --git a/src/task_ld.cc b/src/task_ld.cc
index 3d917e4..69c3a8a 100644
--- a/src/task_ld.cc
+++ b/src/task_ld.cc
@@ -11,25 +11,29 @@
#include "util.h"
#include "tools.h"
-TaskLD::TaskLD(const ctor::build_configuration& config,
- const ctor::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)
- : Task(config, settings, sourceDir)
- , config(config)
- , settings(settings)
- , sourceDir(sourceDir)
+ const std::string& sourceDir_,
+ bool is_self_)
+ : Task(config_, settings_, sourceDir_)
+ , config(config_)
+ , settings(settings_)
+ , sourceDir(sourceDir_)
+ , is_self(is_self_)
{
target_type = config.type;
+ output_system = config.system;
+
if(target_type == ctor::target_type::automatic)
{
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;
@@ -39,7 +43,7 @@ TaskLD::TaskLD(const ctor::build_configuration& config,
for(const auto& dep : config.depends)
{
- depFiles.push_back(dep);
+ depFiles.emplace_back(dep);
}
flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();
@@ -54,6 +58,8 @@ TaskLD::TaskLD(const ctor::build_configuration& config,
source_language = ctor::language::cpp;
}
}
+
+ std::filesystem::create_directories(targetFile().parent_path());
}
bool TaskLD::dirtyInner()
@@ -88,14 +94,16 @@ int TaskLD::runInner()
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)
{
append(args, ld_option(toolchain, ctor::ld_opt::library_path,
targetFile().parent_path().string()));
auto lib = depFile.stem().string().substr(3); // strip 'lib' prefix
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());
}
@@ -115,11 +123,19 @@ int TaskLD::runInner()
if(settings.verbose == 0)
{
- std::cout << "LD => " << targetFile().string() << std::endl;
+ std::string output = "LD => " + targetFile().string() + '\n';
+ std::cout << output << std::flush;
}
auto tool = compiler();
- return execute(tool, args, settings.verbose > 0);
+ const auto& c = ctor::get_configuration();
+ auto res = execute(settings, tool, args, c.env, is_self);
+ if(res != 0)
+ {
+ std::filesystem::remove(targetFile());
+ }
+
+ return res;
}
int TaskLD::clean()
@@ -187,7 +203,6 @@ std::string TaskLD::flagsString() const
flagsStr += str;
}
}
- flagsStr += "\n";
for(const auto& dep : config.depends)
{
diff --git a/src/task_ld.h b/src/task_ld.h
index dbe7db1..c0e3ebb 100644
--- a/src/task_ld.h
+++ b/src/task_ld.h
@@ -18,7 +18,8 @@ public:
const ctor::settings& settings,
const std::string& target,
const std::vector<std::string>& objects,
- const std::string& _sourceDir);
+ const std::string& _sourceDir,
+ bool is_self);
virtual ~TaskLD() = default;
bool dirtyInner() override;
@@ -44,4 +45,5 @@ private:
const ctor::build_configuration& config;
const ctor::settings& settings;
std::string sourceDir;
+ bool is_self;
};
diff --git a/src/task_so.cc b/src/task_so.cc
index 9061591..c98e4a7 100644
--- a/src/task_so.cc
+++ b/src/task_so.cc
@@ -11,20 +11,24 @@
#include "util.h"
#include "tools.h"
-TaskSO::TaskSO(const ctor::build_configuration& config,
- const ctor::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)
- : Task(config, settings, sourceDir)
- , config(config)
- , settings(settings)
- , sourceDir(sourceDir)
+ const std::string& sourceDir_)
+ : Task(config_, settings_, sourceDir_)
+ , config(config_)
+ , settings(settings_)
+ , 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;
@@ -34,13 +38,12 @@ TaskSO::TaskSO(const ctor::build_configuration& config,
for(const auto& dep : config.depends)
{
- depFiles.push_back(dep);
+ depFiles.emplace_back(dep);
}
flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();
flagsFile += ".flags";
- target_type = ctor::target_type::dynamic_library;
source_language = ctor::language::c;
for(const auto& source : config.sources)
{
@@ -51,6 +54,8 @@ TaskSO::TaskSO(const ctor::build_configuration& config,
source_language = ctor::language::cpp;
}
}
+
+ std::filesystem::create_directories(targetFile().parent_path());
}
bool TaskSO::dirtyInner()
@@ -105,11 +110,19 @@ int TaskSO::runInner()
if(settings.verbose == 0)
{
- std::cout << "LD => " << targetFile().string() << std::endl;
+ std::string output = "LD => " + targetFile().string() + '\n';
+ std::cout << output << std::flush;
}
auto tool = compiler();
- return execute(tool, args, settings.verbose > 0);
+ const auto& cfg = ctor::get_configuration();
+ auto res = execute(settings, tool, args, cfg.env);
+ if(res != 0)
+ {
+ std::filesystem::remove(targetFile());
+ }
+
+ return res;
}
int TaskSO::clean()
@@ -177,7 +190,6 @@ std::string TaskSO::flagsString() const
flagsStr += str;
}
}
- flagsStr += "\n";
for(const auto& dep : config.depends)
{
diff --git a/src/tasks.cc b/src/tasks.cc
index 67bed2b..2f9e47a 100644
--- a/src/tasks.cc
+++ b/src/tasks.cc
@@ -8,6 +8,7 @@
#include <list>
#include <iostream>
#include <algorithm>
+#include <span>
#include "ctor.h"
#include "task.h"
@@ -18,24 +19,28 @@
#include "task_fn.h"
#include "rebuild.h"
#include "configure.h"
+#include "util.h"
+#include "tools.h"
const std::deque<Target>& getTargets(const ctor::settings& settings,
bool resolve_externals)
{
+ auto config_files = std::span(configFiles).subspan(0, numConfigFiles);
+
static bool initialised{false};
static std::deque<Target> targets;
if(!initialised)
{
const auto& externals = ctor::get_configuration().externals;
- for(std::size_t i = 0; i < numConfigFiles; ++i)
+ for(const auto& config_file : config_files)
{
std::string path =
- std::filesystem::path(configFiles[i].file).parent_path().string();
+ std::filesystem::path(config_file.file).parent_path().string();
if(settings.verbose > 1)
{
- std::cout << configFiles[i].file << " in path " << path << "\n";
+ std::cout << config_file.file << " in path " << path << "\n";
}
- auto configs = configFiles[i].cb(settings);
+ auto configs = config_file.cb(settings);
for(auto& config : configs)
{
if(resolve_externals)
@@ -50,21 +55,12 @@ const std::deque<Target>& getTargets(const ctor::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,11 +73,12 @@ const std::deque<Target>& getTargets(const ctor::settings& settings,
return targets;
}
-std::set<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration& config,
- const ctor::settings& settings,
- const std::string& sourceDir)
+std::vector<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration& config,
+ const ctor::settings& settings,
+ const std::string& sourceDir,
+ bool is_self)
{
- std::set<std::shared_ptr<Task>> tasks;
+ std::vector<std::shared_ptr<Task>> tasks;
std::filesystem::path targetFile(config.target);
@@ -92,34 +89,26 @@ std::set<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration& con
{
target_type = ctor::target_type::function;
}
- else if(targetFile.extension() == ".a")
- {
- target_type = ctor::target_type::static_library;
- }
- else if(targetFile.extension() == ".so")
- {
- target_type = ctor::target_type::dynamic_library;
- }
- else if(targetFile.extension() == "")
- {
- target_type = ctor::target_type::executable;
- }
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);
}
}
+ const auto& c = ctor::get_configuration();
std::vector<std::string> objects;
if(target_type != ctor::target_type::function)
{
- for(const auto& file : config.sources)
+ for(const auto& source : config.sources)
{
- auto task = std::make_shared<TaskCC>(config, settings, sourceDir, file);
- tasks.insert(task);
- objects.push_back(task->targetFile().string());
+ if(source.toolchain == ctor::toolchain::any ||
+ (config.system == ctor::output_system::build && source.toolchain == c.build_toolchain) ||
+ (config.system == ctor::output_system::host && source.toolchain == c.host_toolchain))
+ {
+ auto task = std::make_shared<TaskCC>(config, settings, sourceDir, source);
+ tasks.push_back(task);
+ objects.push_back(task->targetFile().string());
+ }
}
}
#ifndef BOOTSTRAP
@@ -128,7 +117,7 @@ std::set<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration& con
for(const auto& file : config.sources)
{
auto task = std::make_shared<TaskFn>(config, settings, sourceDir, file);
- tasks.insert(task);
+ tasks.push_back(task);
objects.push_back(task->target());
}
}
@@ -140,10 +129,16 @@ std::set<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration& con
// The target_type cannot be Auto
break;
+ 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));
+ tasks.push_back(std::make_shared<TaskAR>(config, settings, config.target,
+ objects, sourceDir));
break;
#ifndef BOOTSTRAP
case ctor::target_type::dynamic_library:
@@ -153,14 +148,14 @@ std::set<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration& con
std::cerr << "Dynamic library target must have 'lib' prefix\n";
exit(1);
}
- tasks.insert(std::make_shared<TaskSO>(config, settings, config.target,
- objects, sourceDir));
+ tasks.push_back(std::make_shared<TaskSO>(config, settings, config.target,
+ objects, sourceDir));
break;
case ctor::target_type::executable:
case ctor::target_type::unit_test:
- tasks.insert(std::make_shared<TaskLD>(config, settings, config.target,
- objects, sourceDir));
+ tasks.push_back(std::make_shared<TaskLD>(config, settings, config.target,
+ objects, sourceDir, is_self));
break;
case ctor::target_type::object:
@@ -175,18 +170,19 @@ std::set<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration& con
return tasks;
}
-std::shared_ptr<Task> getNextTask(const std::set<std::shared_ptr<Task>>& allTasks,
- std::set<std::shared_ptr<Task>>& dirtyTasks)
+std::shared_ptr<Task> getNextTask([[maybe_unused]]const std::vector<std::shared_ptr<Task>>& allTasks,
+ std::vector<std::shared_ptr<Task>>& dirtyTasks)
{
for(auto dirtyTask = dirtyTasks.begin();
dirtyTask != dirtyTasks.end();
++dirtyTask)
{
+ auto task = *dirtyTask;
//std::cout << "Examining target " << (*dirtyTask)->target() << "\n";
- if((*dirtyTask)->ready())
+ if(task->ready())
{
dirtyTasks.erase(dirtyTask);
- return *dirtyTask;
+ return task;
}
}
@@ -194,20 +190,20 @@ std::shared_ptr<Task> getNextTask(const std::set<std::shared_ptr<Task>>& allTask
return nullptr;
}
-std::set<std::shared_ptr<Task>> getTasks(const ctor::settings& settings,
- const std::vector<std::string> names,
- bool resolve_externals)
+std::vector<std::shared_ptr<Task>> getTasks(const ctor::settings& settings,
+ const std::vector<std::string> names,
+ bool resolve_externals)
{
auto& targets = getTargets(settings, resolve_externals);
- std::set<std::shared_ptr<Task>> tasks;
+ std::vector<std::shared_ptr<Task>> tasks;
for(const auto& target : targets)
{
if(names.empty() ||
std::find(std::begin(names), std::end(names), target.config.target) != std::end(names))
{
std::vector<std::string> objects;
- auto t = taskFactory(target.config, settings, target.path);
- tasks.insert(t.begin(), t.end());
+ auto t = taskFactory(target.config, settings, target.path, false);
+ tasks.insert(tasks.end(), t.begin(), t.end());
}
}
diff --git a/src/tasks.h b/src/tasks.h
index ed1bf60..6573784 100644
--- a/src/tasks.h
+++ b/src/tasks.h
@@ -4,7 +4,6 @@
#pragma once
#include <string>
-#include <set>
#include <memory>
#include <deque>
@@ -24,17 +23,18 @@ const std::deque<Target>& getTargets(const ctor::settings& settings,
//! fulfilled.
//! The returned task is removed from the dirty list.
//! Return nullptr if no dirty task is ready.
-std::shared_ptr<Task> getNextTask(const std::set<std::shared_ptr<Task>>& allTasks,
- std::set<std::shared_ptr<Task>>& dirtyTasks);
+std::shared_ptr<Task> getNextTask(const std::vector<std::shared_ptr<Task>>& allTasks,
+ std::vector<std::shared_ptr<Task>>& dirtyTasks);
//! 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 ctor::settings& settings,
- const std::vector<std::string> names = {},
- bool resolve_externals = true);
+std::vector<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 ctor::build_configuration& config,
- const ctor::settings& settings,
- const std::string& sourceDir);
+std::vector<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration& config,
+ const ctor::settings& settings,
+ const std::string& sourceDir,
+ bool is_self);
diff --git a/src/tools.cc b/src/tools.cc
index 476386b..8c9b7e2 100644
--- a/src/tools.cc
+++ b/src/tools.cc
@@ -6,8 +6,12 @@
#include <filesystem>
#include <iostream>
#include <sstream>
+#include <array>
#include <cassert>
+#include <cstdio>
+
+#include "util.h"
std::ostream& operator<<(std::ostream& stream, const ctor::c_opt& opt)
{
@@ -17,6 +21,9 @@ std::ostream& operator<<(std::ostream& stream, const ctor::c_opt& 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::warn_conversion: stream << "ctor::c_opt::warn_conversion"; break;
+ case ctor::c_opt::warn_shadow: stream << "ctor::c_opt::warn_shadow"; break;
+ case ctor::c_opt::warn_extra: stream << "ctor::c_opt::warn_extra"; 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;
@@ -25,6 +32,7 @@ std::ostream& operator<<(std::ostream& stream, const ctor::c_opt& opt)
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::define: stream << "ctor::c_opt::define"; break;
case ctor::c_opt::custom: stream << "ctor::c_opt::custom"; break;
}
@@ -39,6 +47,9 @@ std::ostream& operator<<(std::ostream& stream, const ctor::cxx_opt& 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::warn_conversion: stream << "ctor::cxx_opt::warn_conversion"; break;
+ case ctor::cxx_opt::warn_shadow: stream << "ctor::cxx_opt::warn_shadow"; break;
+ case ctor::cxx_opt::warn_extra: stream << "ctor::cxx_opt::warn_extra"; 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;
@@ -47,6 +58,7 @@ std::ostream& operator<<(std::ostream& stream, const ctor::cxx_opt& opt)
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::define: stream << "ctor::cxx_opt::define"; break;
case ctor::cxx_opt::custom: stream << "ctor::cxx_opt::custom"; break;
}
@@ -80,6 +92,10 @@ 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;
}
@@ -103,11 +119,14 @@ ctor::toolchain getToolChain(const std::string& 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)
+ 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)
+ 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;
}
@@ -138,43 +157,253 @@ ctor::toolchain getToolChain(ctor::output_system system)
}
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))
+ {
+ constexpr auto buffer_size{1024};
+ std::array<char, buffer_size> buf{};
+ if(fgets(buf.data(), buf.size(), pipe) != nullptr)
+ {
+ arch = buf.data();
+ 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
+ constexpr auto prefix_length{8};
+ arch = arch.substr(prefix_length);
+ 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.substr(0, 2) == "-I")
+ 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 };
}
+ if(flag.starts_with("-std="))
+ {
+ std::string std = flag.substr(5);
+ return { ctor::c_opt::c_std, std };
+ }
+
+ if(flag.starts_with("-O"))
+ {
+ std::string opt = flag.substr(2, 1);
+ return { ctor::c_opt::optimization, opt };
+ }
+
+ if(flag.starts_with("-Wall"))
+ {
+ return { ctor::c_opt::warn_all };
+ }
+
+ if(flag.starts_with("-Wconversion"))
+ {
+ return { ctor::c_opt::warn_conversion};
+ }
+
+ if(flag.starts_with("-Wshadow"))
+ {
+ return { ctor::c_opt::warn_shadow};
+ }
+
+ if(flag.starts_with("-Wextra"))
+ {
+ return { ctor::c_opt::warn_extra};
+ }
+
+ if(flag.starts_with("-Werror"))
+ {
+ return { ctor::c_opt::warnings_as_errors };
+ }
+
+ if(flag.starts_with("-g"))
+ {
+ return { ctor::c_opt::debug };
+ }
+
+ if(flag.starts_with("-D"))
+ {
+ std::string def = flag.substr(2);
+ auto pos = def.find('=');
+ if(pos != def.npos)
+ {
+ return { ctor::c_opt::define, def.substr(0, pos), def.substr(pos + 1) };
+ }
+ else
+ {
+ return { ctor::c_opt::define, def };
+ }
+ }
return { ctor::c_opt::custom, flag };
}
ctor::cxx_flag cxx_option(const std::string& flag)
{
- if(flag.substr(0, 2) == "-I")
+ 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 };
}
+ if(flag.starts_with("-std="))
+ {
+ std::string std = flag.substr(5);
+ return { ctor::cxx_opt::cpp_std, std };
+ }
+
+ if(flag.starts_with("-O"))
+ {
+ std::string opt = flag.substr(2, 1);
+ return { ctor::cxx_opt::optimization, opt };
+ }
+
+ if(flag.starts_with("-Wall"))
+ {
+ return { ctor::cxx_opt::warn_all };
+ }
+
+ if(flag.starts_with("-Werror"))
+ {
+ return { ctor::cxx_opt::warnings_as_errors };
+ }
+
+ if(flag.starts_with("-Wconversion"))
+ {
+ return { ctor::cxx_opt::warn_conversion};
+ }
+
+ if(flag.starts_with("-Wshadow"))
+ {
+ return { ctor::cxx_opt::warn_shadow};
+ }
+
+ if(flag.starts_with("-Wextra"))
+ {
+ return { ctor::cxx_opt::warn_extra};
+ }
+
+ if(flag.starts_with("-g"))
+ {
+ return { ctor::cxx_opt::debug };
+ }
+
+ if(flag.starts_with("-D"))
+ {
+ std::string def = flag.substr(2);
+ auto pos = def.find('=');
+ if(pos != def.npos)
+ {
+ return { ctor::cxx_opt::define, def.substr(0, pos), def.substr(pos + 1) };
+ }
+ else
+ {
+ return { ctor::cxx_opt::define, def };
+ }
+ }
+
return { ctor::cxx_opt::custom, flag };
}
ctor::ld_flag ld_option(const std::string& flag)
{
- if(flag.substr(0, 2) == "-L")
+ 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 };
}
+ if(flag.starts_with("-pthread"))
+ {
+ return { ctor::ld_opt::threads };
+ }
+
return { ctor::ld_opt::custom, flag };
}
-std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg)
+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,
+ const std::string& arg2)
{
switch(opt)
{
@@ -184,6 +413,12 @@ std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg)
return {"-g"};
case ctor::cxx_opt::warn_all:
return {"-Wall"};
+ case ctor::cxx_opt::warn_conversion:
+ return {"-Wconversion"};
+ case ctor::cxx_opt::warn_shadow:
+ return {"-Wshadow"};
+ case ctor::cxx_opt::warn_extra:
+ return {"-Wextra"};
case ctor::cxx_opt::warnings_as_errors:
return {"-Werror"};
case ctor::cxx_opt::generate_dep_tree:
@@ -200,6 +435,12 @@ std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg)
return {"-fPIC"};
case ctor::cxx_opt::position_independent_executable:
return {"-fPIE"};
+ case ctor::cxx_opt::define:
+ if(!arg2.empty())
+ {
+ return {"-D" + arg + "=" + arg2};
+ }
+ return {"-D" + arg};
case ctor::cxx_opt::custom:
return {arg};
}
@@ -208,7 +449,8 @@ std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg)
return {};
}
-std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg)
+std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg,
+ const std::string& arg2)
{
switch(opt)
{
@@ -218,6 +460,12 @@ std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg)
return {"-g"};
case ctor::c_opt::warn_all:
return {"-Wall"};
+ case ctor::c_opt::warn_conversion:
+ return {"-Wconversion"};
+ case ctor::c_opt::warn_shadow:
+ return {"-Wshadow"};
+ case ctor::c_opt::warn_extra:
+ return {"-Wextra"};
case ctor::c_opt::warnings_as_errors:
return {"-Werror"};
case ctor::c_opt::generate_dep_tree:
@@ -234,6 +482,12 @@ std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg)
return {"-fPIC"};
case ctor::c_opt::position_independent_executable:
return {"-fPIE"};
+ case ctor::c_opt::define:
+ if(!arg2.empty())
+ {
+ return {"-D" + arg + "=" + arg2};
+ }
+ return {"-D" + arg};
case ctor::c_opt::custom:
return {arg};
}
@@ -242,7 +496,8 @@ std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg)
return {};
}
-std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg)
+std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg,
+ [[maybe_unused]]const std::string& arg2)
{
switch(opt)
{
@@ -276,10 +531,19 @@ std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg)
return {};
}
-std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg)
+std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg,
+ [[maybe_unused]]const std::string& arg2)
{
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};
}
@@ -288,7 +552,8 @@ std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg)
return {};
}
-std::vector<std::string> asm_option(ctor::asm_opt opt, const std::string& arg)
+std::vector<std::string> asm_option(ctor::asm_opt opt, const std::string& arg,
+ [[maybe_unused]]const std::string& arg2)
{
switch(opt)
{
@@ -301,16 +566,46 @@ std::vector<std::string> asm_option(ctor::asm_opt opt, const std::string& arg)
}
} // 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)
+ const std::string& arg,
+ const std::string& arg2)
{
switch(toolchain)
{
case ctor::toolchain::gcc:
case ctor::toolchain::clang:
- return gcc::c_option(opt, arg);
+ return gcc::c_option(opt, arg, arg2);
case ctor::toolchain::any:
{
std::ostringstream ss;
@@ -319,6 +614,10 @@ std::vector<std::string> c_option(ctor::toolchain toolchain,
{
ss << ", \"" << arg << "\"";
}
+ if(!arg2.empty())
+ {
+ ss << ", \"" << arg2 << "\"";
+ }
ss << "}";
return { ss.str() };
}
@@ -332,13 +631,14 @@ std::vector<std::string> c_option(ctor::toolchain toolchain,
std::vector<std::string> cxx_option(ctor::toolchain toolchain,
ctor::cxx_opt opt,
- const std::string& arg)
+ const std::string& arg,
+ const std::string& arg2)
{
switch(toolchain)
{
case ctor::toolchain::gcc:
case ctor::toolchain::clang:
- return gcc::cxx_option(opt, arg);
+ return gcc::cxx_option(opt, arg, arg2);
case ctor::toolchain::any:
{
std::ostringstream ss;
@@ -347,6 +647,10 @@ std::vector<std::string> cxx_option(ctor::toolchain toolchain,
{
ss << ", \"" << arg << "\"";
}
+ if(!arg2.empty())
+ {
+ ss << ", \"" << arg2 << "\"";
+ }
ss << "}";
return { ss.str() };
}
@@ -360,13 +664,14 @@ std::vector<std::string> cxx_option(ctor::toolchain toolchain,
std::vector<std::string> ld_option(ctor::toolchain toolchain,
ctor::ld_opt opt,
- const std::string& arg)
+ const std::string& arg,
+ const std::string& arg2)
{
switch(toolchain)
{
case ctor::toolchain::gcc:
case ctor::toolchain::clang:
- return gcc::ld_option(opt, arg);
+ return gcc::ld_option(opt, arg, arg2);
case ctor::toolchain::any:
{
std::ostringstream ss;
@@ -375,6 +680,10 @@ std::vector<std::string> ld_option(ctor::toolchain toolchain,
{
ss << ", \"" << arg << "\"";
}
+ if(!arg2.empty())
+ {
+ ss << ", \"" << arg2 << "\"";
+ }
ss << "}";
return { ss.str() };
}
@@ -388,13 +697,14 @@ std::vector<std::string> ld_option(ctor::toolchain toolchain,
std::vector<std::string> ar_option(ctor::toolchain toolchain,
ctor::ar_opt opt,
- const std::string& arg)
+ const std::string& arg,
+ const std::string& arg2)
{
switch(toolchain)
{
case ctor::toolchain::gcc:
case ctor::toolchain::clang:
- return gcc::ar_option(opt, arg);
+ return gcc::ar_option(opt, arg, arg2);
case ctor::toolchain::any:
{
std::ostringstream ss;
@@ -403,6 +713,10 @@ std::vector<std::string> ar_option(ctor::toolchain toolchain,
{
ss << ", \"" << arg << "\"";
}
+ if(!arg2.empty())
+ {
+ ss << ", \"" << arg2 << "\"";
+ }
ss << "}";
return { ss.str() };
}
@@ -416,13 +730,14 @@ std::vector<std::string> ar_option(ctor::toolchain toolchain,
std::vector<std::string> asm_option(ctor::toolchain toolchain,
ctor::asm_opt opt,
- const std::string& arg)
+ const std::string& arg,
+ const std::string& arg2)
{
switch(toolchain)
{
case ctor::toolchain::gcc:
case ctor::toolchain::clang:
- return gcc::asm_option(opt, arg);
+ return gcc::asm_option(opt, arg, arg2);
case ctor::toolchain::any:
{
std::ostringstream ss;
@@ -431,6 +746,10 @@ std::vector<std::string> asm_option(ctor::toolchain toolchain,
{
ss << ", \"" << arg << "\"";
}
+ if(!arg2.empty())
+ {
+ ss << ", \"" << arg2 << "\"";
+ }
ss << "}";
return { ss.str() };
}
@@ -494,6 +813,7 @@ ctor::ar_flag ar_option(const std::string& flag, ctor::toolchain toolchain)
{
case ctor::toolchain::gcc:
case ctor::toolchain::clang:
+ return gcc::ar_option(flag);
case ctor::toolchain::any:
case ctor::toolchain::none:
break;
@@ -524,7 +844,7 @@ std::vector<std::string> to_strings(ctor::toolchain toolchain,
if(flag.toolchain == ctor::toolchain::any ||
flag.toolchain == toolchain)
{
- return c_option(toolchain, flag.opt, flag.arg);
+ return c_option(toolchain, flag.opt, flag.arg, flag.arg2);
}
return {};
@@ -536,7 +856,7 @@ std::vector<std::string> to_strings(ctor::toolchain toolchain,
if(flag.toolchain == ctor::toolchain::any ||
flag.toolchain == toolchain)
{
- return cxx_option(toolchain, flag.opt, flag.arg);
+ return cxx_option(toolchain, flag.opt, flag.arg, flag.arg2);
}
return {};
@@ -548,7 +868,7 @@ std::vector<std::string> to_strings(ctor::toolchain toolchain,
if(flag.toolchain == ctor::toolchain::any ||
flag.toolchain == toolchain)
{
- return ld_option(toolchain, flag.opt, flag.arg);
+ return ld_option(toolchain, flag.opt, flag.arg, flag.arg2);
}
return {};
@@ -560,7 +880,7 @@ std::vector<std::string> to_strings(ctor::toolchain toolchain,
if(flag.toolchain == ctor::toolchain::any ||
flag.toolchain == toolchain)
{
- return ar_option(toolchain, flag.opt, flag.arg);
+ return ar_option(toolchain, flag.opt, flag.arg, flag.arg2);
}
return {};
@@ -572,7 +892,7 @@ std::vector<std::string> to_strings(ctor::toolchain toolchain,
if(flag.toolchain == ctor::toolchain::any ||
flag.toolchain == toolchain)
{
- return asm_option(toolchain, flag.opt, flag.arg);
+ return asm_option(toolchain, flag.opt, flag.arg, flag.arg2);
}
return {};
@@ -628,3 +948,176 @@ 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
index b1285ec..0e7fc15 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -6,6 +6,7 @@
#include <vector>
#include <string>
#include <ostream>
+#include <filesystem>
#include "ctor.h"
@@ -16,6 +17,8 @@ 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);
@@ -29,31 +32,36 @@ ctor::toolchain getToolChain(ctor::output_system system);
//! tool-chain
std::vector<std::string> c_option(ctor::toolchain toolchain,
ctor::c_opt option,
- const std::string& arg = {});
+ const std::string& arg = {},
+ const std::string& arg2 = {});
//! 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 = {});
+ const std::string& arg = {},
+ const std::string& arg2 = {});
//! 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 = {});
+ const std::string& arg = {},
+ const std::string& arg2 = {});
//! 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 = {});
+ const std::string& arg = {},
+ const std::string& arg2 = {});
//! 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 = {});
+ const std::string& arg = {},
+ const std::string& arg2 = {});
@@ -94,3 +102,28 @@ 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 237b2e3..b95a931 100644
--- a/src/unittest.cc
+++ b/src/unittest.cc
@@ -8,7 +8,7 @@
#include "execute.h"
#include "task.h"
-int runUnitTests(std::set<std::shared_ptr<Task>>& tasks,
+int runUnitTests(std::vector<std::shared_ptr<Task>>& tasks,
const ctor::settings& settings)
{
bool ok{true};
@@ -24,7 +24,7 @@ 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(settings, task->targetFile().string(), {}, {});
ok &= ret == 0;
if(ret == 0)
{
diff --git a/src/unittest.h b/src/unittest.h
index 2880319..6d1385e 100644
--- a/src/unittest.h
+++ b/src/unittest.h
@@ -3,7 +3,7 @@
// See accompanying file LICENSE for details.
#pragma once
-#include <set>
+#include <vector>
#include <memory>
class Task;
@@ -12,5 +12,5 @@ namespace ctor {
struct settings;
} // namespace ctor::
-int runUnitTests(std::set<std::shared_ptr<Task>>& tasks,
+int runUnitTests(std::vector<std::shared_ptr<Task>>& tasks,
const ctor::settings& settings);
diff --git a/src/util.cc b/src/util.cc
index ee56ede..dbd4c3c 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)
{
@@ -14,84 +22,32 @@ std::string readFile(const std::string& fileName)
std::ifstream::pos_type fileSize = ifs.tellg();
ifs.seekg(0, std::ios::beg);
- std::vector<char> bytes(fileSize);
+ std::vector<char> bytes(static_cast<std::size_t>(fileSize));
ifs.read(bytes.data(), fileSize);
- return std::string(bytes.data(), fileSize);
+ return {bytes.data(), static_cast<std::size_t>(fileSize)};
}
-std::vector<std::string> readDeps(const std::string& depFile)
+ctor::language languageFromExtension(const std::filesystem::path& file)
{
- if(!std::filesystem::exists(depFile))
- {
- return {};
- }
-
- auto str = readFile(depFile);
+ auto ext = file.extension().string();
- std::vector<std::string> output;
- std::string tmp;
- bool start{false};
- bool in_whitespace{false};
- for(const auto& c : str)
+ // First a few case sensitive comparisons
+ if(ext == ".c")
{
- if(c == '\\' || c == '\n')
- {
- continue;
- }
-
- if(c == ':')
- {
- start = true;
- continue;
- }
-
- if(!start)
- {
- continue;
- }
-
- if(c == ' ' || c == '\t')
- {
- if(in_whitespace)
- {
- continue;
- }
-
- if(!tmp.empty())
- {
- output.push_back(tmp);
- }
- tmp.clear();
- in_whitespace = true;
- }
- else
- {
- in_whitespace = false;
- tmp += c;
- }
+ return ctor::language::c;
}
- if(!tmp.empty())
+ if(ext == ".C")
{
- output.push_back(tmp);
+ return ctor::language::cpp;
}
- return output;
-}
+ // The rest are compared in lowercase
+ ext = to_lower(ext);
-ctor::language languageFromExtension(const std::filesystem::path& file)
-{
- auto ext = file.extension().string();
- if(ext == ".c")
- {
- return ctor::language::c;
- }
-
- if(ext == ".C" ||
- ext == ".cc" ||
+ if(ext == ".cc" ||
ext == ".cpp" ||
- ext == ".CPP" ||
ext == ".c++" ||
ext == ".cp" ||
ext == ".cxx")
@@ -100,7 +56,6 @@ ctor::language languageFromExtension(const std::filesystem::path& file)
}
if(ext == ".s" ||
- ext == ".S" ||
ext == ".asm")
{
return ctor::language::assembler;
@@ -135,3 +90,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 {};
+}
diff --git a/src/util.h b/src/util.h
index c7318aa..0a90049 100644
--- a/src/util.h
+++ b/src/util.h
@@ -7,9 +7,11 @@
#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);
ctor::language languageFromExtension(const std::filesystem::path& file);
std::string cleanUp(const std::string& path);
@@ -19,7 +21,10 @@ void append(T& a, const T& b)
a.insert(a.end(), b.begin(), b.end());
}
-//using cxx_flags = std::vector<ctor::cxx_flag>;
-//using c_flags = std::vector<std::string>;
-//using ld_flags= std::vector<std::string>;
-//using asm_flags = std::vector<std::string>;
+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 a2ad64d..31c63ab 100644
--- a/test/ctor.cc
+++ b/test/ctor.cc
@@ -11,12 +11,65 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
{
{
.type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
+ .target = "pointerlist_test",
+ .sources = {
+ "pointerlist_test.cc",
+ "testmain.cc",
+ "../src/pointerlist.cc",
+ },
+ .flags = {
+ .cxxflags = {
+ "-std=c++20", "-O3", "-Wall", "-Werror",
+ "-I../src", "-Iuunit",
+ "-DOUTPUT=\"pointerlist\"",
+ },
+ },
+ },
+ {
+ .type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
+ .target = "deps_test",
+ .sources = {
+ "deps_test.cc",
+ "testmain.cc",
+ "../src/deps.cc",
+ "../src/util.cc",
+ },
+ .depends = { "testprog", },
+ .flags = {
+ .cxxflags = {
+ "-std=c++20", "-O3", "-Wall", "-Werror",
+ "-I../src", "-Iuunit",
+ "-DOUTPUT=\"deps\"",
+ },
+ },
+ },
+ {
+ .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/pointerlist.cc",
+ "../src/util.cc",
},
+ .depends = { "testprog", },
.flags = {
.cxxflags = {
"-std=c++20", "-O3", "-Wall", "-Werror",
@@ -28,6 +81,7 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
},
{
.type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
.target = "tasks_test",
.sources = {
"tasks_test.cc",
@@ -45,6 +99,25 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
},
{
.type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
+ .target = "cycle_test",
+ .sources = {
+ "cycle_test.cc",
+ "testmain.cc",
+ },
+ .depends = { "libctor_nomain.a" },
+ .flags = {
+ .cxxflags = {
+ "-std=c++20", "-O3", "-Wall", "-Werror",
+ "-I../src", "-Iuunit",
+ "-DOUTPUT=\"cycle\"",
+ },
+ .ldflags = { "-pthread" },
+ },
+ },
+ {
+ .type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
.target = "source_type_test",
.sources = {
"source_type_test.cc",
@@ -62,10 +135,12 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
},
{
.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" },
@@ -79,11 +154,14 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
},
{
.type = ctor::target_type::unit_test_library,
+ .system = ctor::output_system::build,
.target = "libctor_nomain.a",
.sources = {
"../src/build.cc",
"../src/configure.cc",
+ "../src/deps.cc",
"../src/execute.cc",
+ "../src/pointerlist.cc",
"../src/rebuild.cc",
"../src/tasks.cc",
"../src/task.cc",
diff --git a/test/cycle_test.cc b/test/cycle_test.cc
new file mode 100644
index 0000000..3b45632
--- /dev/null
+++ b/test/cycle_test.cc
@@ -0,0 +1,87 @@
+#include <uunit.h>
+
+#include <ctor.h>
+#include <build.h>
+
+namespace
+{
+ctor::build_configurations ctorTestConfigsCyclic(const ctor::settings&)
+{
+ return
+ {
+ // No dependency
+ {
+ .target = "target0",
+ },
+
+ // Direct (self-depends)
+ {
+ .target = "target1",
+ .depends = { "target1" },
+ },
+
+ // Indirect cyclic depends
+ {
+ .target = "target2",
+ .depends = { "target3" },
+ },
+ {
+ .target = "target3",
+ .depends = { "target2" },
+ },
+ };
+}
+}
+
+REG(ctorTestConfigsCyclic);
+
+class CycleTest
+ : public uUnit
+{
+public:
+ CycleTest()
+ {
+ uTEST(CycleTest::getTargets_test);
+ }
+
+ void getTargets_test()
+ {
+ using namespace std::string_literals;
+ ctor::settings settings{};
+ const auto& tasks = getTasks(settings);
+ uASSERT_EQUAL(4u, tasks.size());
+
+ uASSERT_EQUAL("target0"s, tasks[0]->target());
+ uASSERT_EQUAL("target1"s, tasks[1]->target());
+ uASSERT_EQUAL("target2"s, tasks[2]->target());
+ uASSERT_EQUAL("target3"s, tasks[3]->target());
+
+ for(auto task : tasks)
+ {
+ if(task->registerDepTasks(tasks))
+ {
+ uASSERT(false);
+ }
+ }
+
+ bool exc;
+ exc = false;
+ try { getDepTasks(tasks[0]); } catch(...) { exc = true; }
+ uASSERT(!exc);
+
+ exc = false;
+ try { getDepTasks(tasks[1]); } catch(...) { exc = true; }
+ uASSERT(exc);
+
+ exc = false;
+ try { getDepTasks(tasks[2]); } catch(...) { exc = true; }
+ uASSERT(exc);
+
+ exc = false;
+ try { getDepTasks(tasks[3]); } catch(...) { exc = true; }
+ uASSERT(exc);
+ }
+};
+
+// Registers the fixture into the 'registry'
+static CycleTest test;
diff --git a/test/deps_test.cc b/test/deps_test.cc
new file mode 100644
index 0000000..762b0e5
--- /dev/null
+++ b/test/deps_test.cc
@@ -0,0 +1,97 @@
+#include <uunit.h>
+
+#include <fstream>
+#include <map>
+#include <vector>
+
+#include <deps.h>
+#include <algorithm>
+
+#include "paths.h"
+#include "tmpfile.h"
+
+class DepsTest
+ : public uUnit
+{
+public:
+ DepsTest()
+ {
+ uTEST(DepsTest::parser_test);
+ }
+
+ void parser_test()
+ {
+ using namespace std::string_literals;
+
+ auto test_data = paths::top_srcdir / "test" / "deps_test_data";
+
+ auto empty = test_data / "empty.d";
+ auto trivial = test_data / "trivial.d";
+ auto no_newline = test_data / "no_newline.d";
+ auto no_deps = test_data / "no_deps.d";
+ auto trailing_whitespace = test_data / "trailing_whitespace.d";
+ auto spaces = test_data / "spaces.d";
+ auto multiline = test_data / "multiline.d";
+ auto no_such_file = test_data / "no_such_file.d"; // doesn't exist
+ auto missing_colon = test_data / "missing_colon.d";
+
+ {
+ auto res = readDeps(empty.string(), ctor::toolchain::gcc);
+ uASSERT(res.empty());
+ }
+
+ {
+ auto res = readDeps(trivial.string(), ctor::toolchain::gcc);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("x.cc"s, res[0]);
+ }
+
+ {
+ auto res = readDeps(no_newline.string(), ctor::toolchain::gcc);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("x.cc"s, res[0]);
+ }
+
+ {
+ auto res = readDeps(no_deps.string(), ctor::toolchain::gcc);
+ uASSERT_EQUAL(0u, res.size());
+ }
+
+ {
+ auto res = readDeps(spaces.string(), ctor::toolchain::gcc);
+ uASSERT_EQUAL(2u, res.size());
+ uASSERT_EQUAL("x y.cc"s, res[0]);
+ uASSERT_EQUAL("x y.h"s, res[1]);
+ }
+
+ {
+ auto res = readDeps(multiline.string(), ctor::toolchain::gcc);
+ uASSERT_EQUAL(12u, res.size());
+ uASSERT_EQUAL("src/configure.cc"s, res[0]);
+ uASSERT_EQUAL("src/configure.h"s, res[1]);
+ uASSERT_EQUAL("src/getoptpp/getoptpp.hpp"s, res[2]);
+ uASSERT_EQUAL("src/execute.h"s, res[3]);
+ uASSERT_EQUAL("src/ctor.h"s, res[4]);
+ uASSERT_EQUAL("src/tasks.h"s, res[5]);
+ uASSERT_EQUAL("src/task.h"s, res[6]);
+ uASSERT_EQUAL("src/rebuild.h"s, res[7]);
+ uASSERT_EQUAL("src/externals.h"s, res[8]);
+ uASSERT_EQUAL("src/externals_manual.h"s, res[9]);
+ uASSERT_EQUAL("src/tools.h"s, res[10]);
+ uASSERT_EQUAL("src/util.h"s, res[11]);
+ }
+
+ {
+ auto res = readDeps(no_such_file.string(), ctor::toolchain::gcc);
+ uASSERT(res.empty());
+ }
+
+ {
+ auto res = readDeps(missing_colon.string(), ctor::toolchain::gcc);
+ uASSERT(res.empty());
+ }
+ }
+};
+
+// Registers the fixture into the 'registry'
+static DepsTest test;
diff --git a/test/deps_test_data/empty.d b/test/deps_test_data/empty.d
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/deps_test_data/empty.d
diff --git a/test/deps_test_data/missing_colon.d b/test/deps_test_data/missing_colon.d
new file mode 100644
index 0000000..e46996c
--- /dev/null
+++ b/test/deps_test_data/missing_colon.d
@@ -0,0 +1 @@
+x.cc x.h
diff --git a/test/deps_test_data/multiline.d b/test/deps_test_data/multiline.d
new file mode 100644
index 0000000..8862ab0
--- /dev/null
+++ b/test/deps_test_data/multiline.d
@@ -0,0 +1,4 @@
+build/src/libctor_a-configure_cc.o: src/configure.cc src/configure.h \
+ src/getoptpp/getoptpp.hpp src/execute.h src/ctor.h src/tasks.h \
+ src/task.h src/rebuild.h src/externals.h src/externals_manual.h \
+ src/tools.h src/util.h
diff --git a/test/deps_test_data/no_deps.d b/test/deps_test_data/no_deps.d
new file mode 100644
index 0000000..e7cccf6
--- /dev/null
+++ b/test/deps_test_data/no_deps.d
@@ -0,0 +1 @@
+x.o:
diff --git a/test/deps_test_data/no_newline.d b/test/deps_test_data/no_newline.d
new file mode 100644
index 0000000..88829ea
--- /dev/null
+++ b/test/deps_test_data/no_newline.d
@@ -0,0 +1 @@
+x.o: x.cc \ No newline at end of file
diff --git a/test/deps_test_data/spaces.d b/test/deps_test_data/spaces.d
new file mode 100644
index 0000000..c53fd64
--- /dev/null
+++ b/test/deps_test_data/spaces.d
@@ -0,0 +1 @@
+x\ y.o: x\ y.cc x\ y.h
diff --git a/test/deps_test_data/trivial.d b/test/deps_test_data/trivial.d
new file mode 100644
index 0000000..15a0c29
--- /dev/null
+++ b/test/deps_test_data/trivial.d
@@ -0,0 +1 @@
+x.o: x.cc
diff --git a/test/execute_test.cc b/test/execute_test.cc
index 9da18dc..722b6ea 100644
--- a/test/execute_test.cc
+++ b/test/execute_test.cc
@@ -1,6 +1,15 @@
#include <uunit.h>
+#include <fstream>
+#include <map>
+#include <vector>
+
#include <execute.h>
+#include <util.h>
+#include <algorithm>
+
+#include "paths.h"
+#include "tmpfile.h"
class ExecuteTest
: public uUnit
@@ -8,14 +17,78 @@ class ExecuteTest
public:
ExecuteTest()
{
- uTEST(ExecuteTest::runit);
+ uTEST(ExecuteTest::return_value);
+ uTEST(ExecuteTest::env);
+ }
+
+ void return_value()
+ {
+ ctor::settings s;
+ 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());
+
+ auto value = execute(s, cmd, {"retval", "0"}, {}, false);
+ uASSERT_EQUAL(0, value);
+ value = execute(s, cmd, {"retval", "1"}, {}, false);
+ uASSERT_EQUAL(1, value);
+ value = execute(s, "no-such-binary", {}, {}, false);
+ uASSERT_EQUAL(1, value);
+ value = execute(s, cmd, {"segfault"}, {}, false);
+ uASSERT_EQUAL(11, value);
+ value = execute(s, cmd, {"throw"}, {}, false);
+ uASSERT_EQUAL(6, value);
+ value = execute(s, cmd, {"abort"}, {}, false);
+ uASSERT_EQUAL(6, value);
}
- 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));
+ using namespace std::string_literals;
+
+ ctor::settings s;
+ 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";
+
+ auto value = execute(s, cmd, {"envdump", tmp.get()}, env, false);
+ uASSERT_EQUAL(0, value);
+
+ std::vector<std::string> vars;
+ {
+ std::ifstream infile(tmp.get());
+ std::string line;
+ while (std::getline(infile, line))
+ {
+ vars.push_back(line);
+ }
+ }
+
+ // Check the two explicitly set vars
+ auto chk = std::find(vars.begin(), vars.end(), "foo=bar"s);
+ uASSERT(chk != vars.end());
+ chk = std::find(vars.begin(), vars.end(), "bar=42"s);
+ uASSERT(chk != vars.end());
+
+ // Check the one that should have overwritten the existing one (probably LANG=en_US.UTF-8 or something)
+ chk = std::find(vars.begin(), vars.end(), "LANG=foo"s);
+ uASSERT(chk != 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/pointerlist_test.cc b/test/pointerlist_test.cc
new file mode 100644
index 0000000..4274473
--- /dev/null
+++ b/test/pointerlist_test.cc
@@ -0,0 +1,320 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include <uunit.h>
+
+#include <set>
+#include <algorithm>
+
+#include "pointerlist.h"
+
+class PointerListTest
+ : public uUnit
+{
+public:
+ PointerListTest()
+ {
+ uTEST(PointerListTest::test_zom_pointerlist_push);
+ uTEST(PointerListTest::test_zom_pointerlist_from_args);
+ uTEST(PointerListTest::test_zom_envmap_insert);
+ uTEST(PointerListTest::test_zom_envmap_from_env);
+ uTEST(PointerListTest::test_exceptional_env);
+ }
+
+ void test_zom_pointerlist_push()
+ {
+ using namespace std::string_literals;
+
+ { // Zero
+ PointerList args;
+ uASSERT_EQUAL(0u, args.size());
+ auto [argc, argv] = args.get();
+ uASSERT_EQUAL(0, argc);
+ uASSERT(nullptr != argv);
+ uASSERT_EQUAL(nullptr, argv[0]);
+ }
+
+ { // One
+ PointerList args;
+ args.push_back("hello");
+ uASSERT_EQUAL(1u, args.size());
+ auto [argc, argv] = args.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello"s, std::string(argv[0]));
+ }
+
+ { // Many
+ PointerList args;
+ args.push_back("hello");
+ args.push_back("dear");
+ args.push_back("world");
+ uASSERT_EQUAL(3u, args.size());
+ auto [argc, argv] = args.get();
+ uASSERT_EQUAL(3, argc);
+ uASSERT_EQUAL("hello"s, std::string(argv[0]));
+ uASSERT_EQUAL("dear"s, std::string(argv[1]));
+ uASSERT_EQUAL("world"s, std::string(argv[2]));
+ }
+ }
+
+ void test_zom_pointerlist_from_args()
+ {
+ using namespace std::string_literals;
+
+ { // Zero
+ PointerList args(0, nullptr);
+ uASSERT_EQUAL(0u, args.size());
+ auto [argc, argv] = args.get();
+ uASSERT_EQUAL(0, argc);
+ uASSERT(nullptr != argv);
+ uASSERT_EQUAL(nullptr, argv[0]);
+ }
+
+ { // One
+ int _argc{1};
+ const char* _argv[] = { "hello" };
+ PointerList args(_argc, _argv);
+ uASSERT_EQUAL(1u, args.size());
+ auto [argc, argv] = args.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello"s, std::string(argv[0]));
+ }
+
+ { // Many
+ int _argc{3};
+ const char* _argv[] = { "hello", "dear", "world" };
+ PointerList args(_argc, _argv);
+ uASSERT_EQUAL(3u, args.size());
+ auto [argc, argv] = args.get();
+ uASSERT_EQUAL(3, argc);
+ // order must be preserved
+ uASSERT_EQUAL("hello"s, std::string(argv[0]));
+ uASSERT_EQUAL("dear"s, std::string(argv[1]));
+ uASSERT_EQUAL("world"s, std::string(argv[2]));
+ }
+ }
+
+ void test_zom_envmap_insert()
+ {
+ using namespace std::string_literals;
+
+ { // Zero
+ EnvMap env;
+ uASSERT_EQUAL(0u, env.size());
+
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(0, argc);
+ uASSERT(nullptr != argv);
+ uASSERT_EQUAL(nullptr, argv[0]);
+
+ auto str = env.stringify();
+ uASSERT_EQUAL(1u, str.size());
+ uASSERT_EQUAL('\0', str[0]);
+ }
+
+ { // One (key only)
+ EnvMap env;
+ env.insert("hello");
+ uASSERT_EQUAL(1u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello="s, std::string(argv[0]));
+ uASSERT_EQUAL(""s, env["hello"]);
+ }
+ { // One (with value)
+ EnvMap env;
+ env.insert("hello=world");
+ uASSERT_EQUAL(1u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello=world"s, std::string(argv[0]));
+ uASSERT_EQUAL("world"s, env["hello"]);
+
+ uASSERT_EQUAL("hello=world\0\0"s, env.stringify());
+ }
+
+ { // Overwrite one
+ EnvMap env;
+ env.insert("hello=world");
+ uASSERT_EQUAL(1u, env.size());
+ uASSERT_EQUAL("world"s, env["hello"s]);
+
+ env.insert("hello=foo");
+ uASSERT_EQUAL(1u, env.size());
+ uASSERT_EQUAL("foo"s, env["hello"s]);
+ }
+
+ { // Many
+ EnvMap env;
+ env.insert("hello=world");
+ env.insert("world=leader");
+ env.insert("dear=boar");
+ uASSERT_EQUAL(3u, env.size());
+
+ uASSERT_EQUAL("boar"s, env["dear"s]);
+ uASSERT_EQUAL("world"s, env["hello"s]);
+ uASSERT_EQUAL("leader"s, env["world"s]);
+
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(3, argc);
+ // store and sort to verify unordered
+ std::vector<std::string> vals{argv[0], argv[1], argv[2]};
+ std::ranges::sort(vals);
+ uASSERT_EQUAL("dear=boar"s, vals[0]);
+ uASSERT_EQUAL("hello=world"s, vals[1]);
+ uASSERT_EQUAL("world=leader"s, vals[2]);
+
+ // test all combinations since ordering is not a requirement
+ // exactly one of the must be true (boolean XOR)
+ auto str = env.stringify();
+ uASSERT(((((("dear=boar\0hello=world\0world=leader\0\0"s == str) !=
+ ("dear=boar\0world=leader\0hello=world\0\0"s == str)) !=
+ ("hello=world\0dear=boar\0world=leader\0\0"s == str)) !=
+ ("hello=world\0world=leader\0dear=boar\0\0"s == str)) !=
+ ("world=leader\0dear=boar\0hello=world\0\0"s == str)) !=
+ ("world=leader\0hello=world\0dear=boar\0\0"s == str));
+ }
+ }
+
+ void test_zom_envmap_from_env()
+ {
+ using namespace std::string_literals;
+
+ { // Zero
+ const char* penv = nullptr;
+ EnvMap env(penv);
+ uASSERT_EQUAL(0u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(0, argc);
+ uASSERT(nullptr != argv);
+ uASSERT_EQUAL(nullptr, argv[0]);
+ }
+
+ { // Zero
+ const char** ppenv = nullptr;
+ EnvMap env(ppenv);
+ uASSERT_EQUAL(0u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(0, argc);
+ uASSERT(nullptr != argv);
+ uASSERT_EQUAL(nullptr, argv[0]);
+ }
+
+ { // One (key only)
+ const char* ptr = "hello\0\0";
+ EnvMap env(ptr);
+ uASSERT_EQUAL(1u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello="s, std::string(argv[0]));
+ uASSERT_EQUAL(""s, env["hello"]);
+ }
+
+ { // One (key only)
+ const char* ptr[] = {"hello", nullptr};
+ EnvMap env(ptr);
+ uASSERT_EQUAL(1u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello="s, std::string(argv[0]));
+ uASSERT_EQUAL(""s, env["hello"]);
+ }
+
+ { // One (with value)
+ const char* ptr = "hello=world\0\0";
+ EnvMap env(ptr);
+ uASSERT_EQUAL(1u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello=world"s, std::string(argv[0]));
+ uASSERT_EQUAL("world"s, env["hello"]);
+
+ uASSERT_EQUAL("hello=world\0\0"s, env.stringify());
+ }
+
+ { // One (with value)
+ const char* ptr[] = {"hello=world\0", nullptr};
+ EnvMap env(ptr);
+ uASSERT_EQUAL(1u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello=world"s, std::string(argv[0]));
+ uASSERT_EQUAL("world"s, env["hello"]);
+
+ uASSERT_EQUAL("hello=world\0\0"s, env.stringify());
+ }
+
+ { // Many
+ const char* ptr = "hello=world\0world=leader\0dear=boar\0\0";
+ EnvMap env(ptr);
+ uASSERT_EQUAL(3u, env.size());
+
+ uASSERT_EQUAL("boar"s, env["dear"s]);
+ uASSERT_EQUAL("world"s, env["hello"s]);
+ uASSERT_EQUAL("leader"s, env["world"s]);
+
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(3, argc);
+ // store and sort to verify unordered
+ std::vector<std::string> vals{argv[0], argv[1], argv[2]};
+ std::ranges::sort(vals);
+ uASSERT_EQUAL("dear=boar"s, vals[0]);
+ uASSERT_EQUAL("hello=world"s, vals[1]);
+ uASSERT_EQUAL("world=leader"s, vals[2]);
+
+ // test all combinations since ordering is not a requirement
+ // exactly one of the must be true (boolean XOR)
+ auto str = env.stringify();
+ uASSERT(((((("dear=boar\0hello=world\0world=leader\0\0"s == str) !=
+ ("dear=boar\0world=leader\0hello=world\0\0"s == str)) !=
+ ("hello=world\0dear=boar\0world=leader\0\0"s == str)) !=
+ ("hello=world\0world=leader\0dear=boar\0\0"s == str)) !=
+ ("world=leader\0dear=boar\0hello=world\0\0"s == str)) !=
+ ("world=leader\0hello=world\0dear=boar\0\0"s == str));
+ }
+
+ { // Many
+ const char* ptr[] =
+ {"hello=world\0", "world=leader\0", "dear=boar\0", nullptr};
+ EnvMap env(ptr);
+ uASSERT_EQUAL(3u, env.size());
+
+ uASSERT_EQUAL("boar"s, env["dear"s]);
+ uASSERT_EQUAL("world"s, env["hello"s]);
+ uASSERT_EQUAL("leader"s, env["world"s]);
+
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(3, argc);
+ // store and sort to verify unordered
+ std::vector<std::string> vals{argv[0], argv[1], argv[2]};
+ std::ranges::sort(vals);
+ uASSERT_EQUAL("dear=boar"s, vals[0]);
+ uASSERT_EQUAL("hello=world"s, vals[1]);
+ uASSERT_EQUAL("world=leader"s, vals[2]);
+
+ // test all combinations since ordering is not a requirement
+ // exactly one of the must be true (boolean XOR)
+ auto str = env.stringify();
+ uASSERT(((((("dear=boar\0hello=world\0world=leader\0\0"s == str) !=
+ ("dear=boar\0world=leader\0hello=world\0\0"s == str)) !=
+ ("hello=world\0dear=boar\0world=leader\0\0"s == str)) !=
+ ("hello=world\0world=leader\0dear=boar\0\0"s == str)) !=
+ ("world=leader\0dear=boar\0hello=world\0\0"s == str)) !=
+ ("world=leader\0hello=world\0dear=boar\0\0"s == str));
+ }
+ }
+
+ void test_exceptional_env()
+ {
+ using namespace std::string_literals;
+
+ { // Zero
+ EnvMap env;
+ uASSERT_EQUAL(0u, env.size());
+ uASSERT_EQUAL(""s, env["foo"]); // lookup of non-existing key
+ }
+ }
+};
+
+// Registers the fixture into the 'registry'
+static PointerListTest test;
diff --git a/test/tasks_test.cc b/test/tasks_test.cc
index 5f1db26..b444bd5 100644
--- a/test/tasks_test.cc
+++ b/test/tasks_test.cc
@@ -10,6 +10,7 @@ ctor::build_configurations ctorTestConfigs1(const ctor::settings&)
return
{
{
+ .name = "Target1",
.target = "target1",
.sources = {"foo.cc", "bar.c"},
},
@@ -33,16 +34,29 @@ ctor::build_configurations ctorTestConfigs2(const ctor::settings&)
}
}
+namespace test_global {
+ctor::toolchain toolchain{};
+ctor::arch arch{};
+}
+const ctor::configuration& ctor::get_configuration()
+{
+ static ctor::configuration cfg{};
+ cfg.build_toolchain = test_global::toolchain;
+ cfg.build_arch = test_global::arch;
+ return cfg;
+}
+
+
REG(ctorTestConfigs1);
REG(ctorTestConfigs2);
-std::size_t count(const std::set<std::shared_ptr<Task>>& tasks,
- const std::string& name)
+std::size_t count(const std::vector<std::shared_ptr<Task>>& tasks,
+ const std::filesystem::path& name)
{
auto cnt{0u};
for(const auto& task : tasks)
{
- if(task->target() == name)
+ if(task->target() == name.string())
{
cnt++;
}
@@ -54,16 +68,17 @@ class TestTask
: public Task
{
public:
- TestTask(const std::string& name, bool dirty,
+ TestTask(const ctor::build_configuration& config,
+ const ctor::settings& settings,
+ const std::string& name, bool dirty,
const std::vector<std::string>& deps = {})
- : Task({}, {}, {})
+ : Task(config, settings, {})
, task_name(name)
, task_dirty(dirty)
, task_deps(deps)
{
}
- std::string name() const override { return task_name; }
int clean() override { return 0; }
std::vector<std::string> depends() const override { return task_deps; }
std::string target() const override { return task_name; }
@@ -81,11 +96,14 @@ class TasksTest
: public uUnit
{
public:
+ using fs = std::filesystem::path;
+
TasksTest()
{
uTEST(TasksTest::getTargets_test);
uTEST(TasksTest::getTasks_test);
uTEST(TasksTest::getNextTask_test);
+ uTEST(TasksTest::comparison_test);
}
void getTargets_test()
@@ -108,27 +126,29 @@ public:
void getTasks_test()
{
+ test_global::toolchain = ctor::toolchain::gcc;
+ test_global::arch = ctor::arch::unix;
+
using namespace std::string_literals;
ctor::settings settings{ .builddir = "foo" };
{
auto tasks = getTasks(settings);
uASSERT_EQUAL(6u, tasks.size());
- // Note: count() is used here because the order of
- // std::set<std::shared_ptr<T>> is not deterministic.
- uASSERT_EQUAL(1u, count(tasks, "target1"s));
- uASSERT_EQUAL(1u, count(tasks, "target2"s));
- uASSERT_EQUAL(1u, count(tasks, "target3"s));
- uASSERT_EQUAL(1u, count(tasks, "target4"s));
- uASSERT_EQUAL(1u, count(tasks, "test/target1-foo_cc.o"s));
- uASSERT_EQUAL(1u, count(tasks, "test/target1-bar_c.o"s));
+ // Note: count() is used here because the order doesn't matter
+ uASSERT_EQUAL(1u, count(tasks, fs("target1")));
+ uASSERT_EQUAL(1u, count(tasks, fs("target2")));
+ uASSERT_EQUAL(1u, count(tasks, fs("target3")));
+ uASSERT_EQUAL(1u, count(tasks, fs("target4")));
+ uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-foo_cc.o"));
+ uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-bar_c.o"));
}
{
auto tasks = getTasks(settings, {"target1", "target3"});
uASSERT_EQUAL(4u, tasks.size());
- uASSERT_EQUAL(1u, count(tasks, "target1"s));
- uASSERT_EQUAL(1u, count(tasks, "target3"s));
- uASSERT_EQUAL(1u, count(tasks, "test/target1-foo_cc.o"s));
- uASSERT_EQUAL(1u, count(tasks, "test/target1-bar_c.o"s));
+ uASSERT_EQUAL(1u, count(tasks, fs("target1")));
+ uASSERT_EQUAL(1u, count(tasks, fs("target3")));
+ uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-foo_cc.o"));
+ uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-bar_c.o"));
}
{
auto tasks = getTasks(settings, {"no-such-target"});
@@ -142,8 +162,8 @@ public:
ctor::settings settings{};
{ // Zero (Empty)
- std::set<std::shared_ptr<Task>> allTasks;
- std::set<std::shared_ptr<Task>> dirtyTasks;
+ std::vector<std::shared_ptr<Task>> allTasks;
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
for(auto& task : dirtyTasks)
{
@@ -154,12 +174,13 @@ public:
}
{ // Zero (One task, no dirty)
- auto task1 = std::make_shared<TestTask>("task1", false);
+ ctor::build_configuration config;
+ auto task1 = std::make_shared<TestTask>(config, settings, "task1", false);
- std::set<std::shared_ptr<Task>> allTasks;
- allTasks.insert(task1);
+ std::vector<std::shared_ptr<Task>> allTasks;
+ allTasks.push_back(task1);
- std::set<std::shared_ptr<Task>> dirtyTasks;
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
for(auto& task : dirtyTasks)
{
@@ -170,13 +191,14 @@ public:
}
{ // One (One task, one dirty)
- auto task1 = std::make_shared<TestTask>("task1", true);
+ ctor::build_configuration config;
+ auto task1 = std::make_shared<TestTask>(config, settings, "task1", true);
- std::set<std::shared_ptr<Task>> allTasks;
- allTasks.insert(task1);
+ std::vector<std::shared_ptr<Task>> allTasks;
+ allTasks.push_back(task1);
- std::set<std::shared_ptr<Task>> dirtyTasks;
- dirtyTasks.insert(task1);
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
+ dirtyTasks.push_back(task1);
for(auto& task : dirtyTasks)
{
@@ -188,15 +210,16 @@ public:
}
{ // One (Two tasks, one dirty)
- auto task1 = std::make_shared<TestTask>("task1", false);
- auto task2 = std::make_shared<TestTask>("task2", true);
+ ctor::build_configuration config;
+ auto task1 = std::make_shared<TestTask>(config, settings, "task1", false);
+ auto task2 = std::make_shared<TestTask>(config, settings, "task2", true);
- std::set<std::shared_ptr<Task>> allTasks;
- allTasks.insert(task1);
- allTasks.insert(task2);
+ std::vector<std::shared_ptr<Task>> allTasks;
+ allTasks.push_back(task1);
+ allTasks.push_back(task2);
- std::set<std::shared_ptr<Task>> dirtyTasks;
- dirtyTasks.insert(task2);
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
+ dirtyTasks.push_back(task2);
for(auto& task : dirtyTasks)
{
@@ -208,17 +231,19 @@ public:
}
{ // One (Two tasks, one dirty which depends on the other)
- auto task1 = std::make_shared<TestTask>("task1", false);
+ ctor::build_configuration config;
+ auto task1 = std::make_shared<TestTask>(config, settings, "task1", false);
std::vector<std::string> deps = {"task1"};
- auto task2 = std::make_shared<TestTask>("task2", true, deps);
+ auto task2 =
+ std::make_shared<TestTask>(config, settings, "task2", true, deps);
- std::set<std::shared_ptr<Task>> allTasks;
- allTasks.insert(task1);
- allTasks.insert(task2);
+ std::vector<std::shared_ptr<Task>> allTasks;
+ allTasks.push_back(task1);
+ allTasks.push_back(task2);
- std::set<std::shared_ptr<Task>> dirtyTasks;
- dirtyTasks.insert(task2);
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
+ dirtyTasks.push_back(task2);
for(auto& task : dirtyTasks)
{
@@ -230,18 +255,20 @@ public:
}
{ // One (Two tasks, Both dirty, one depends on the other)
- auto task1 = std::make_shared<TestTask>("task1", true);
+ ctor::build_configuration config{};
+ auto task1 = std::make_shared<TestTask>(config, settings, "task1", true);
std::vector<std::string> deps = {"task1"};
- auto task2 = std::make_shared<TestTask>("task2", true, deps);
+ auto task2 =
+ std::make_shared<TestTask>(config, settings, "task2", true, deps);
- std::set<std::shared_ptr<Task>> allTasks;
- allTasks.insert(task2);
- allTasks.insert(task1);
+ std::vector<std::shared_ptr<Task>> allTasks;
+ allTasks.push_back(task2);
+ allTasks.push_back(task1);
- std::set<std::shared_ptr<Task>> dirtyTasks;
- dirtyTasks.insert(task2);
- dirtyTasks.insert(task1);
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
+ dirtyTasks.push_back(task2);
+ dirtyTasks.push_back(task1);
for(auto& task : dirtyTasks)
{
@@ -251,7 +278,51 @@ public:
uASSERT_EQUAL(task1, getNextTask(allTasks, dirtyTasks));
uASSERT_EQUAL(1u, dirtyTasks.size());
}
+ }
+ void comparison_test()
+ {
+ test_global::toolchain = ctor::toolchain::gcc;
+ test_global::arch = ctor::arch::unix;
+
+ using namespace std::string_literals;
+ ctor::settings settings{ .builddir = "foo" };
+ { // Test Task::operator==
+ auto tasks = getTasks(settings, {"target1"});
+ uASSERT_EQUAL(3u, tasks.size());
+ uASSERT_EQUAL(1u, count(tasks, fs("target1")));
+ uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-foo_cc.o"));
+ uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-bar_c.o"));
+
+ int cnt1{};
+ int cnt2{};
+ int cnt3{};
+ for(const auto& task : tasks)
+ {
+ if(task->target() == fs("target1"))
+ {
+ ++cnt1;
+ uASSERT(*task == "target1");
+ uASSERT(*task == "Target1");
+ }
+ if(task->target() == fs("test")/"target1-foo_cc.o")
+ {
+ ++cnt2;
+ uASSERT(*task != "target1");
+ uASSERT(*task != "Target1");
+ }
+ if(task->target() == fs("test")/"target1-bar_c.o")
+ {
+ ++cnt3;
+ uASSERT(*task != "target1");
+ uASSERT(*task != "Target1");
+ }
+ }
+ // Assert that we did actually perform all three tests exactly once
+ uASSERT_EQUAL(1, cnt1);
+ uASSERT_EQUAL(1, cnt2);
+ uASSERT_EQUAL(1, cnt3);
+ }
}
};
diff --git a/test/testprog.cc b/test/testprog.cc
new file mode 100644
index 0000000..faf1498
--- /dev/null
+++ b/test/testprog.cc
@@ -0,0 +1,53 @@
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <signal.h>
+
+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 == "segfault")
+ {
+ raise(SIGSEGV);
+ }
+
+ 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
index 1d377b7..46ab084 100644
--- a/test/tools_test.cc
+++ b/test/tools_test.cc
@@ -114,33 +114,27 @@ bool operator!=(const ctor::asm_flag& a, const ctor::asm_flag& b)
#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
+std::string ctor::configuration::get(const std::string& key, [[maybe_unused]]const std::string& default_value) const
{
if(key == ctor::cfg::host_cxx)
{
- return conf_host_cxx;
+ return {};
}
if(key == ctor::cfg::build_cxx)
{
- return conf_build_cxx;
+ return {};
}
assert(false); // bad key
- static std::string res{};
- return res;
+ return {};
}
class ToolsTest
@@ -172,9 +166,24 @@ public:
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"));
@@ -666,22 +675,54 @@ public:
//
// 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 = asm_option(ctor::toolchain::gcc, ctor::asm_opt::custom, "-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 = asm_option(ctor::toolchain::clang, ctor::asm_opt::custom, "-foo");
+ act = ar_option(ctor::toolchain::clang, ctor::ar_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");
+ exp = { "{ctor::ar_opt::custom, \"-foo\"}" };
+ act = ar_option(ctor::toolchain::any, ctor::ar_opt::custom, "-foo");
uASSERT_EQUAL(exp, act);
}
diff --git a/test/uunit b/test/uunit
-Subproject bc078da645412c6b36ef59e635d6c35d11088c9
+Subproject 0f371777e02dd068f9675a05a29230221d5d6a7