summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2023-01-20 08:37:29 +0100
committerBent Bisballe Nyeng <deva@aasimon.org>2023-01-20 09:02:45 +0100
commita5585150f0ff8d27ddd22792f521f1374a3eedd8 (patch)
tree3bb442e835502d452ebe5137ad7fca5f5156bb5f
parent3cbadb8f5c55020ece96fab0fc8f4f51da01888e (diff)
Add env to execute function.
-rw-r--r--src/configure.cc127
-rw-r--r--src/execute.cc82
-rw-r--r--src/execute.h4
-rw-r--r--src/task_ar.cc2
-rw-r--r--src/task_cc.cc2
-rw-r--r--src/task_ld.cc2
-rw-r--r--src/task_so.cc2
-rw-r--r--src/unittest.cc2
-rw-r--r--src/util.cc79
-rw-r--r--src/util.h6
-rw-r--r--test/ctor.cc14
-rw-r--r--test/execute_test.cc65
-rw-r--r--test/testprog.cc49
-rw-r--r--test/tmpfile.h48
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 {};
+}
diff --git a/src/util.h b/src/util.h
index 9fa6be2..eeb3206 100644
--- a/src/util.h
+++ b/src/util.h
@@ -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;
+};