diff options
-rw-r--r-- | src/configure.cc | 127 | ||||
-rw-r--r-- | src/execute.cc | 82 | ||||
-rw-r--r-- | src/execute.h | 4 | ||||
-rw-r--r-- | src/task_ar.cc | 2 | ||||
-rw-r--r-- | src/task_cc.cc | 2 | ||||
-rw-r--r-- | src/task_ld.cc | 2 | ||||
-rw-r--r-- | src/task_so.cc | 2 | ||||
-rw-r--r-- | src/unittest.cc | 2 | ||||
-rw-r--r-- | src/util.cc | 79 | ||||
-rw-r--r-- | src/util.h | 6 | ||||
-rw-r--r-- | test/ctor.cc | 14 | ||||
-rw-r--r-- | test/execute_test.cc | 65 | ||||
-rw-r--r-- | test/testprog.cc | 49 | ||||
-rw-r--r-- | test/tmpfile.h | 48 |
14 files changed, 395 insertions, 89 deletions
diff --git a/src/configure.cc b/src/configure.cc index 10f3056..b2639cb 100644 --- a/src/configure.cc +++ b/src/configure.cc @@ -98,57 +98,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*> { @@ -475,11 +424,36 @@ int regenerateCache(ctor::settings& settings, ld_prog = ld_env->second; } + auto paths = get_paths(); + // Host detection - auto host_cc = locate(host_arch_prefix, cc_prog); - auto host_cxx = locate(host_arch_prefix, cxx_prog); - auto host_ar = locate(host_arch_prefix, ar_prog); - auto host_ld = locate(host_arch_prefix, ld_prog); + auto 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; + } + + auto 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; + } + + auto 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; + } + + auto 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; + } auto host_toolchain = getToolChain(host_cxx); auto host_arch_str = get_arch(ctor::output_system::host); @@ -487,11 +461,40 @@ int regenerateCache(ctor::settings& settings, std::cout << "** Host architecture '" << host_arch_str << "': " << host_arch << std::endl; + if(host_arch == ctor::arch::unknown) + { + std::cerr << "Could not detect host architecture" << std::endl; + return 1; + } + // Build detection - auto build_cc = locate(build_arch_prefix, cc_prog); - auto build_cxx = locate(build_arch_prefix, cxx_prog); - auto build_ar = locate(build_arch_prefix, ar_prog); - auto build_ld = locate(build_arch_prefix, ld_prog); + auto 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; + } + + auto 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; + } + + auto 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; + } + + auto 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; + } auto build_toolchain = getToolChain(build_cxx); auto build_arch_str = get_arch(ctor::output_system::build); @@ -499,6 +502,12 @@ int regenerateCache(ctor::settings& settings, 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; + } + if(!host_cxx.empty()) { // This is needed for bootstrapping (when running configure for the first time) diff --git a/src/execute.cc b/src/execute.cc index 20a4a02..b4013d0 100644 --- a/src/execute.cc +++ b/src/execute.cc @@ -20,6 +20,28 @@ https://stackoverflow.com/questions/4259629/what-is-the-difference-between-fork- namespace { +class Env + : public std::vector<char*> +{ +public: + Env(const std::vector<std::string>& args) + { + for(const auto& arg : args) + { + push_back(strdup(arg.data())); + } + push_back(nullptr); + } + + ~Env() + { + for(auto ptr : *this) + { + free(ptr); + } + } +}; + int parent_waitpid(pid_t pid) { int status; @@ -33,8 +55,11 @@ int parent_waitpid(pid_t pid) } } // namespace :: +extern char **environ; // see 'man environ' + int execute(const std::string& command, const std::vector<std::string>& args, + const std::map<std::string, std::string>& env, bool verbose) { std::vector<const char*> argv; @@ -45,22 +70,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) { - if(arg == nullptr) - { - break; - } - if(!cmd.empty()) - { - cmd += " "; - } - cmd += arg; + break; } + if(!cmd.empty()) + { + cmd += " "; + } + cmd += arg; + } + if(verbose) + { std::cout << cmd << std::endl; } @@ -68,23 +93,42 @@ int execute(const std::string& command, auto pid = vfork(); if(pid == 0) { - execv(command.data(), (char**)argv.data()); + std::vector<std::string> venv; + for(const auto& [key, value] : env) + { + venv.push_back(key + "=" + value); + } + + for(auto current = environ; *current; ++current) + { + venv.push_back(*current); + } + + Env penv(venv); + execve(command.data(), (char**)argv.data(), penv.data()); std::cout << "Could not execute " << command << ": " << strerror(errno) << "\n"; - _exit(1); // execv only returns if an error occurred + _exit(1); // execve only returns if an error occurred } - auto ret = parent_waitpid(pid); + return parent_waitpid(pid); #elif 0 pid_t pid; + std::vector<std::string> venv; + for(const auto& [key, value] : env) + { + venv.push_back(key + "=" + value); + } + Env penv(venv); if(posix_spawn(&pid, command.data(), nullptr, nullptr, - (char**)argv.data(), nullptr)) + (char**)argv.data(), penv.data())) { return 1; } - auto ret = parent_waitpid(pid); + return parent_waitpid(pid); #else - auto ret = system(cmd.data()); + (void)parent_waitpid; + return system(cmd.data()); #endif - return ret; + return 1; } diff --git a/src/execute.h b/src/execute.h index c750a83..336c3ef 100644 --- a/src/execute.h +++ b/src/execute.h @@ -5,7 +5,9 @@ #include <string> #include <vector> +#include <map> int execute(const std::string& command, - const std::vector<std::string>& args, + const std::vector<std::string>& args = {}, + const std::map<std::string, std::string>& env = {}, bool verbose = true); diff --git a/src/task_ar.cc b/src/task_ar.cc index 605ab17..9cd78d0 100644 --- a/src/task_ar.cc +++ b/src/task_ar.cc @@ -118,7 +118,7 @@ int TaskAR::runInner() break; } - return execute(tool, args, settings.verbose > 0); + return execute(tool, args, {}, settings.verbose > 0); } int TaskAR::clean() diff --git a/src/task_cc.cc b/src/task_cc.cc index 46662ad..2238b67 100644 --- a/src/task_cc.cc +++ b/src/task_cc.cc @@ -187,7 +187,7 @@ int TaskCC::runInner() targetFile().lexically_normal().string() << std::endl; } - return execute(compiler(), args, settings.verbose > 0); + return execute(compiler(), args, {}, settings.verbose > 0); } int TaskCC::clean() diff --git a/src/task_ld.cc b/src/task_ld.cc index 908b641..a98ef44 100644 --- a/src/task_ld.cc +++ b/src/task_ld.cc @@ -123,7 +123,7 @@ int TaskLD::runInner() } auto tool = compiler(); - return execute(tool, args, settings.verbose > 0); + return execute(tool, args, {}, settings.verbose > 0); } int TaskLD::clean() diff --git a/src/task_so.cc b/src/task_so.cc index 522f6f2..e99205d 100644 --- a/src/task_so.cc +++ b/src/task_so.cc @@ -111,7 +111,7 @@ int TaskSO::runInner() } auto tool = compiler(); - return execute(tool, args, settings.verbose > 0); + return execute(tool, args, {}, settings.verbose > 0); } int TaskSO::clean() diff --git a/src/unittest.cc b/src/unittest.cc index 237b2e3..1e32878 100644 --- a/src/unittest.cc +++ b/src/unittest.cc @@ -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(task->targetFile(), {}, {}, settings.verbose > 0); ok &= ret == 0; if(ret == 0) { diff --git a/src/util.cc b/src/util.cc index fe16471..db5a5f6 100644 --- a/src/util.cc +++ b/src/util.cc @@ -167,3 +167,82 @@ std::string esc(const std::string& in) } return out; } + +std::vector<std::string> get_paths() +{ + std::vector<std::string> paths; + + std::string path_env = std::getenv("PATH"); + +#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 {}; +} @@ -22,3 +22,9 @@ void append(T& a, const T& b) } std::string esc(const std::string& in); + +std::vector<std::string> get_paths(); + +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 3649a10..ea5ae1e 100644 --- a/test/ctor.cc +++ b/test/ctor.cc @@ -11,12 +11,26 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) { { .type = ctor::target_type::unit_test, + .target = "testprog", + .sources = { + "testprog.cc", + }, + .flags = { + .cxxflags = { + "-std=c++20", "-O3", "-Wall", "-Werror", + }, + }, + }, + { + .type = ctor::target_type::unit_test, .target = "execute_test", .sources = { "execute_test.cc", "testmain.cc", "../src/execute.cc", + "../src/util.cc", }, + .depends = { "testprog", }, .flags = { .cxxflags = { "-std=c++20", "-O3", "-Wall", "-Werror", diff --git a/test/execute_test.cc b/test/execute_test.cc index 9da18dc..03b3c2a 100644 --- a/test/execute_test.cc +++ b/test/execute_test.cc @@ -1,6 +1,14 @@ #include <uunit.h> +#include <fstream> +#include <map> +#include <set> + #include <execute.h> +#include <util.h> + +#include "paths.h" +#include "tmpfile.h" class ExecuteTest : public uUnit @@ -8,14 +16,61 @@ class ExecuteTest public: ExecuteTest() { - uTEST(ExecuteTest::runit); + uTEST(ExecuteTest::return_value); + uTEST(ExecuteTest::env); + } + + void return_value() + { + auto cur_path = std::filesystem::path(paths::argv_0).parent_path(); + std::vector<std::string> paths{{cur_path.string()}}; + auto cmd = locate("testprog", paths); + uASSERT(!cmd.empty()); + + uASSERT_EQUAL(0, execute(cmd, {"retval", "0"}, {}, false)); + uASSERT_EQUAL(1, execute(cmd, {"retval", "1"}, {}, false)); + uASSERT_EQUAL(1, execute("no-such-binary", {}, {}, false)); } - void runit() + void env() { - uASSERT_EQUAL(0, execute("/bin/true", {}, false)); - uASSERT_EQUAL(1, execute("/bin/false", {}, false)); - uASSERT_EQUAL(1, execute("no-such-binary", {}, false)); + auto cur_path = std::filesystem::path(paths::argv_0).parent_path(); + std::vector<std::string> paths{{cur_path.string()}}; + auto cmd = locate("testprog", paths); + uASSERT(!cmd.empty()); + + tmp_file tmp; + + std::map<std::string, std::string> env; + + // New env vars + env["foo"] = "bar"; + env["bar"] = "42"; + + // Overwrite the exiting LANG var + env["LANG"] = "foo"; + + uASSERT_EQUAL(0, execute(cmd, {"envdump", tmp.get()}, env, false)); + + std::set<std::string> vars; + { + std::ifstream infile(tmp.get()); + std::string line; + while (std::getline(infile, line)) + { + vars.insert(line); + } + } + + // Check the two explicitly set vars + uASSERT(vars.find("foo=bar") != vars.end()); + uASSERT(vars.find("bar=42") != vars.end()); + + // Check the one that should have overwritten the existing one (probably LANG=en_US.UTF-8 or something) + uASSERT(vars.find("LANG=foo") != vars.end()); + + // Check that other vars are also there (ie. the env wasn't cleared on entry) + uASSERT(vars.size() > 3); } }; diff --git a/test/testprog.cc b/test/testprog.cc new file mode 100644 index 0000000..dbfb665 --- /dev/null +++ b/test/testprog.cc @@ -0,0 +1,49 @@ +#include <iostream> +#include <fstream> +#include <string> + +extern const char **environ; // see 'man environ' + +int main(int argc, const char* argv[]) +{ + if(argc < 2) + { + return 0; + } + + std::string cmd = argv[1]; + + if(cmd == "envdump") + { + if(argc < 3) + { + return 0; + } + std::ofstream ostrm(argv[2], std::ios::binary); + for(auto current = environ; *current; ++current) + { + ostrm << (*current) << "\n"; + } + } + + if(cmd == "retval") + { + if(argc < 3) + { + return 0; + } + return std::stoi(argv[2]); + } + + if(cmd == "abort") + { + abort(); + } + + if(cmd == "throw") + { + throw "ouch"; + } + + return 0; +} diff --git a/test/tmpfile.h b/test/tmpfile.h new file mode 100644 index 0000000..5d114d0 --- /dev/null +++ b/test/tmpfile.h @@ -0,0 +1,48 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include <cstdlib> +#include <unistd.h> + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#endif + +struct tmp_file +{ + tmp_file(const std::string& data = {}) + { + int fd; +#ifdef _WIN32 + char templ[] = "ctor_tmp_file-XXXXXX"; // buffer for filename + _mktemp_s(templ, sizeof(templ)); + fd = open(templ, O_CREAT | O_RDWR); +#else + char templ[] = "/tmp/ctor_tmp_file-XXXXXX"; // buffer for filename + fd = mkstemp(templ); +#endif + filename = templ; + auto sz = write(fd, data.data(), data.size()); + (void)sz; + close(fd); + } + + ~tmp_file() + { + unlink(filename.data()); + } + + const std::string& get() const + { + return filename; + } + +private: + std::string filename; +}; |