// -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. #include "configure.h" #include #include #include #include #include #include "settings.h" #include "execute.h" #include "libctor.h" #include "tasks.h" #include "rebuild.h" std::filesystem::path configurationFile("configuration.cc"); std::filesystem::path configHeaderFile("config.h"); const Configuration default_configuration{}; const Configuration& __attribute__((weak)) configuration() { return default_configuration; } namespace ctor { std::optional includedir; std::optional libdir; } bool hasConfiguration(const std::string& key) { if(key == cfg::ctor_includedir && ctor::includedir) { return true; } if(key == cfg::ctor_libdir && ctor::libdir) { return true; } const auto& c = configuration(); return c.tools.find(key) != c.tools.end(); } const std::string& getConfiguration(const std::string& key, const std::string& defaultValue) { if(key == cfg::ctor_includedir && ctor::includedir) { return *ctor::includedir; } if(key == cfg::ctor_libdir && ctor::libdir) { return *ctor::libdir; } const auto& c = configuration(); if(hasConfiguration(key)) { return c.tools.at(key); } return defaultValue; } std::string locate(const std::string& arch, const std::string& app) { std::string path_env = std::getenv("PATH"); std::cout << path_env << "\n"; std::string program = app; if(!arch.empty()) { program = arch + "-" + app; } std::cout << "Looking for: " << program << "\n"; std::vector 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 { public: Args(const std::vector& args) { resize(args.size() + 1); (*this)[0] = strdup("./ctor"); for(std::size_t i = 0; i < size() - 1; ++i) { (*this)[i + 1] = strdup(args[i].data()); } } ~Args() { for(std::size_t i = 0; i < size(); ++i) { free((*this)[i]); } } }; int regenerateCache(const Settings& default_settings, const std::vector& args, const std::map& env) { Settings settings{default_settings}; Args vargs(args); dg::Options opt; int key{128}; std::string build_arch; std::string build_path; std::string host_arch; std::string host_path; std::string cc_prog = "gcc"; std::string cxx_prog = "g++"; std::string ar_prog = "ar"; std::string ld_prog = "ld"; std::string ctor_includedir; std::string ctor_libdir; 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("cc", required_argument, key++, "Use specified c-compiler instead of gcc.", [&]() { cc_prog = optarg; return 0; }); opt.add("cxx", required_argument, key++, "Use specified c++-compiler instead of g++.", [&]() { cxx_prog = optarg; return 0; }); opt.add("ar", required_argument, key++, "Use specified archiver instead of ar.", [&]() { ar_prog = optarg; return 0; }); opt.add("ld", required_argument, key++, "Use specified linker instead of ld.", [&]() { ld_prog = optarg; 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("ctor-includedir", required_argument, key++, "Set path to ctor header file, used for re-compiling.", [&]() { ctor_includedir = optarg; return 0; }); opt.add("ctor-libdir", required_argument, key++, "Set path to ctor library file, used for re-compiling.", [&]() { ctor_libdir = 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(vargs.size(), vargs.data()); 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; } } */ auto cc_env = env.find("CC"); if(cc_env != env.end()) { cc_prog = cc_env->second; } auto cxx_env = env.find("CXX"); if(cxx_env != env.end()) { cxx_prog = cxx_env->second; } auto ar_env = env.find("AR"); if(ar_env != env.end()) { ar_prog = ar_env->second; } auto ld_env = env.find("LD"); if(ld_env != env.end()) { 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); // Resolv externals ExternalConfigurations externalConfigs; for(std::size_t i = 0; i < numExternalConfigFiles; ++i) { auto newExternalConfigs = externalConfigFiles[i].cb(); externalConfigs.insert(externalConfigs.end(), newExternalConfigs.begin(), newExternalConfigs.end()); } std::cout << "Writing results to: " << configurationFile.string() << "\n"; { std::ofstream istr(configurationFile); istr << "#include \n\n"; istr << "const Configuration& configuration()\n"; istr << "{\n"; istr << " static Configuration cfg =\n"; istr << " {\n"; istr << " .args = {"; for(const auto& arg : args) { istr << "\"" << arg << "\","; } istr << "},\n"; istr << " .env = {"; for(const auto& e : env) { istr << "{\"" << e.first << "\", \"" << e.second << "\"}, "; } istr << "},\n"; istr << " .tools = {\n"; istr << " { \"" << cfg::builddir << "\", \"" << settings.builddir << "\" },\n"; istr << " { \"" << cfg::host_cc << "\", \"" << host_cc << "\" },\n"; istr << " { \"" << cfg::host_cxx << "\", \"" << host_cxx << "\" },\n"; istr << " { \"" << cfg::host_ar << "\", \"" << host_ar << "\" },\n"; istr << " { \"" << cfg::host_ld << "\", \"" << host_ld << "\" },\n"; istr << " { \"" << cfg::build_cc << "\", \"" << build_cc << "\" },\n"; istr << " { \"" << cfg::build_cxx << "\", \"" << build_cxx << "\" },\n"; istr << " { \"" << cfg::build_ar << "\", \"" << build_ar << "\" },\n"; istr << " { \"" << cfg::build_ld << "\", \"" << build_ld << "\" },\n"; if(!ctor_includedir.empty()) { istr << " { \"" << cfg::ctor_includedir << "\", \"" << ctor_includedir << "\" },\n"; ctor::includedir = ctor_includedir; } if(!ctor_libdir.empty()) { istr << " { \"" << cfg::ctor_libdir << "\", \"" << ctor_libdir << "\" },\n"; ctor::libdir = ctor_libdir; } istr << " },\n"; istr << " .externals = {\n"; for(const auto& externalConfig : externalConfigs) { istr << " { \"" << externalConfig.name << "\", {\n"; if(!externalConfig.flags.cxxflags.empty()) { istr << " .cxxflags = {"; for(const auto& flag : externalConfig.flags.cxxflags) { istr << "\"" << flag << "\","; } istr << "},\n"; } if(!externalConfig.flags.cflags.empty()) { istr << " .cflags = {"; for(const auto& flag : externalConfig.flags.cflags) { istr << "\"" << flag << "\","; } istr << "},\n"; } if(!externalConfig.flags.ldflags.empty()) { istr << " .ldflags = {"; for(const auto& flag : externalConfig.flags.ldflags) { istr << "\"" << flag << "\","; } istr << "},\n"; } if(!externalConfig.flags.asmflags.empty()) { istr << " .asmflags = {"; for(const auto& flag : externalConfig.flags.asmflags) { istr << "\"" << flag << "\","; } istr << "},\n"; } istr << " }},\n"; } istr << " },\n"; istr << " };\n"; istr << " return cfg;\n"; istr << "}\n\n"; } { std::ofstream istr(configHeaderFile); istr << "#pragma once\n\n"; istr << "#define HAS_FOO 1\n"; istr << "//#define HAS_BAR 1\n"; } return 0; } int configure(const Settings& global_settings, int argc, char* argv[]) { Settings settings{global_settings}; std::vector args; for(int i = 2; i < argc; ++i) // skip command and the first 'configure' arg { args.push_back(argv[i]); } std::map env; auto cc_env = getenv("CC"); if(cc_env) { env["CC"] = cc_env; } auto cxx_env = getenv("CXX"); if(cxx_env) { env["CXX"] = cxx_env; } auto ar_env = getenv("AR"); if(ar_env) { env["AR"] = ar_env; } auto ld_env = getenv("LD"); if(ld_env) { env["LD"] = ld_env; } auto ret = regenerateCache(settings, args, env); if(ret != 0) { return ret; } recompileCheck(settings, argc, argv, false); return 0; } int reconfigure(const Settings& settings, int argc, char* argv[]) { bool no_rerun{false}; std::vector args; for(int i = 2; i < argc; ++i) // skip executable name and 'reconfigure' arg { if(i == 2 && std::string(argv[i]) == "--no-rerun") { no_rerun = true; continue; } args.push_back(argv[i]); } const auto& cfg = configuration(); std::cout << "Re-running configure:\n"; for(const auto& e : cfg.env) { std::cout << e.first << "=\"" << e.second << "\" "; } std::cout << argv[0] << " configure "; for(const auto& arg : cfg.args) { std::cout << arg << " "; } std::cout << "\n"; auto ret = regenerateCache(settings, cfg.args, cfg.env); if(ret != 0) { return ret; } recompileCheck(settings, 1, argv, false); if(no_rerun) { return 0; // this was originally invoked by configure, don't loop } return execute(argv[0], args); }