diff options
Diffstat (limited to 'src')
42 files changed, 3791 insertions, 1376 deletions
| diff --git a/src/bootstrap.cc b/src/bootstrap.cc index 08f7b1f..be1b5ed 100644 --- a/src/bootstrap.cc +++ b/src/bootstrap.cc @@ -3,11 +3,12 @@  // See accompanying file LICENSE for details.  #include <iostream>  #include <array> +#include <cstdlib> +#include <span>  #define BOOTSTRAP -#include "libctor.h" -#include "settings.h" +#include "ctor.h"  #include "util.cc"  #include "rebuild.cc" @@ -17,39 +18,90 @@  #include "execute.cc"  #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 Configuration default_configuration{}; -const Configuration& configuration() +const ctor::configuration& ctor::get_configuration()  { -	return default_configuration; +	static ctor::configuration cfg; +	static bool initialised{false}; +	if(!initialised) +	{ +		cfg.build_toolchain = getToolChain(cfg.get(ctor::cfg::build_cxx, "/usr/bin/g++")); +		initialised = true; +	} + +	return cfg;  } -bool hasConfiguration(const std::string& key) +bool ctor::configuration::has(const std::string& key) const  {  	return false;  } -const std::string& getConfiguration(const std::string& key, -                                    const std::string& defaultValue) +std::string ctor::configuration::get(const std::string& key, const std::string& default_value) const +{ +	static auto paths = get_paths(); +	auto cxx_env = std::getenv("CXX"); +	if(key == ctor::cfg::build_cxx && cxx_env) +	{ +		static auto cxx_prog = locate(cxx_env, paths); +		return cxx_prog; +	} + +	auto cc_env = std::getenv("CC"); +	if(key == ctor::cfg::build_cc && cc_env) +	{ +		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 defaultValue; +	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;  	} -	Settings settings{}; +	ctor::settings settings{}; -	settings.builddir = getConfiguration(cfg::builddir, "build"); +	const auto& c = ctor::get_configuration(); +	settings.builddir = c.get(ctor::cfg::builddir, settings.builddir);  	settings.parallel_processes =  		std::max(1u, std::thread::hardware_concurrency() * 2 - 1);  	settings.verbose = 0; @@ -66,7 +118,8 @@ int main(int argc, char* argv[])  	auto& targets = getTargets(settings);  	for(const auto& target : targets)  	{ -		if(target.config.type != TargetType::UnitTest) +		if(target.config.type != ctor::target_type::unit_test && +		   target.config.type != ctor::target_type::unit_test_library)  		{  			non_unittest_targets.push_back(target);  		} diff --git a/src/build.cc b/src/build.cc index 8adbc54..a31f6a5 100644 --- a/src/build.cc +++ b/src/build.cc @@ -4,18 +4,20 @@  #include "build.h"  #include <future> -#include <vector>  #include <iostream>  #include <chrono> -#include <set>  #include <thread> +#include <list> +#include <algorithm> + +#include "ctor.h"  using namespace std::chrono_literals; -int build(const Settings& settings, +int build(const ctor::settings& settings,            const std::string& name, -          const std::list<std::shared_ptr<Task>>& tasks, -          const std::list<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) @@ -23,10 +25,11 @@ int build(const Settings& settings,  		std::cout << "Building '" << name << "'\n";  	} -	std::list<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.push_back(task);  		} @@ -35,7 +38,7 @@ int build(const Settings& settings,  	// 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()) @@ -81,7 +84,9 @@ int build(const Settings& settings,  					           return task->run();  				           }));  			started_one = true; -			std::this_thread::sleep_for(2ms); +			// Make sure we don't start tasks on top of each other to prevent +			// straining the disk. +			std::this_thread::sleep_for(50ms);  		}  		for(auto process = processes.begin(); @@ -103,27 +108,21 @@ int build(const Settings& settings,  			break;  		} -		if(started_one) -		{ -			std::this_thread::sleep_for(2ms); -		} -		else +		if(!started_one) // prevent polling too fast if no task is yet ready  		{ -			std::this_thread::sleep_for(200ms); +			std::this_thread::sleep_for(10ms);  		}  	} -	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;  		} @@ -132,56 +131,80 @@ int build(const Settings& settings,  	return 0;  } -namespace -{ -std::set<std::shared_ptr<Task>> getDepTasks(std::shared_ptr<Task> task) +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>> 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 Settings& settings, +int build(const ctor::settings& settings,            const std::string& name, -          const std::list<std::shared_ptr<Task>>& all_tasks, +          const std::vector<std::shared_ptr<Task>>& all_tasks,            bool dryrun)  {  	bool task_found{false};  	for(auto task : all_tasks)  	{ -		if(task->target() == name || // match exact target output (ex. build/foo.o) - -		   (!task->derived() && // if non-derived task: -		    ( task->buildConfig().target == name || // match name -		      task->buildConfig().name == name ) // or target -			  ) -			) +		if(*task == name)  		{  			task_found = true; -			auto depSet = getDepTasks(task); -			std::list<std::shared_ptr<Task>> ts; -			for(const auto& task : depSet) +			try  			{ -				ts.push_back(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; @@ -197,14 +220,14 @@ int build(const Settings& settings,  	return 0;  } -int build(const Settings& settings, +int build(const ctor::settings& settings,            const std::string& name,            const std::vector<Target>& targets, -          const std::list<std::shared_ptr<Task>>& all_tasks, +          const std::vector<std::shared_ptr<Task>>& all_tasks,            bool dryrun)  {  	bool task_found{false}; -	std::list<std::shared_ptr<Task>> ts; +	std::vector<std::shared_ptr<Task>> ts;  	for(const auto& target : targets)  	{ @@ -215,10 +238,20 @@ int build(const 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.push_back(task); +					return 1; // cycle detected  				}  			}  		} diff --git a/src/build.h b/src/build.h index 7be7517..7296f76 100644 --- a/src/build.h +++ b/src/build.h @@ -4,29 +4,37 @@  #pragma once  #include <string> -#include <list> +#include <vector>  #include <memory>  #include "task.h" -#include "settings.h"  #include "tasks.h" +namespace ctor { +struct settings; +} // namespace ctor:: +  //! Dry-run returns number of dirty tasks but otherwise does nothing. -int build(const Settings& settings, +int build(const ctor::settings& settings,            const std::string& name, -          const std::list<std::shared_ptr<Task>>& tasks, -          const std::list<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 Settings& settings, +int build(const ctor::settings& settings,            const std::string& name, -          const std::list<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 Settings& settings, +int build(const ctor::settings& settings,            const std::string& name,            const std::vector<Target>& targets, -          const std::list<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 4df6026..7a68f03 100644 --- a/src/configure.cc +++ b/src/configure.cc @@ -7,117 +7,96 @@  #include <filesystem>  #include <fstream>  #include <optional> +#include <span>  #include <getoptpp/getoptpp.hpp> -#include "settings.h"  #include "execute.h" -#include "libctor.h" +#include "ctor.h"  #include "tasks.h"  #include "rebuild.h" +#include "externals.h" +#include "tools.h" +#include "util.h" -std::filesystem::path configurationFile("configuration.cc"); -std::filesystem::path configHeaderFile("config.h"); +const std::filesystem::path configurationFile("configuration.cc"); +const std::filesystem::path configHeaderFile("config.h"); -const Configuration default_configuration{}; -const Configuration& __attribute__((weak)) configuration() +std::map<std::string, std::string> external_includedir; +std::map<std::string, std::string> external_libdir; + +const ctor::configuration& __attribute__((weak)) ctor::get_configuration()  { -	return default_configuration; +	static ctor::configuration cfg; +	static bool initialised{false}; +	if(!initialised) +	{ +		cfg.build_toolchain = getToolChain(cfg.get(ctor::cfg::build_cxx, "/usr/bin/g++")); +		initialised = true; +	} +	return cfg;  } -namespace ctor -{ +namespace ctor {  std::optional<std::string> includedir;  std::optional<std::string> libdir; -} +std::optional<std::string> builddir; +std::map<std::string, std::string> conf_values; +} // ctor:: -bool hasConfiguration(const std::string& key) +bool ctor::configuration::has(const std::string& key) const  { -	if(key == cfg::ctor_includedir && ctor::includedir) +	if(key == ctor::cfg::ctor_includedir && ctor::includedir) +	{ +		return true; +	} + +	if(key == ctor::cfg::ctor_libdir && ctor::libdir) +	{ +		return true; +	} + +	if(key == ctor::cfg::builddir && ctor::builddir)  	{  		return true;  	} -	if(key == cfg::ctor_libdir && ctor::libdir) +	if(ctor::conf_values.find(key) != ctor::conf_values.end())  	{  		return true;  	} -	const auto& c = configuration(); -	return c.tools.find(key) != c.tools.end(); +	return tools.find(key) != tools.end();  } -const std::string& getConfiguration(const std::string& key, -                                    const std::string& defaultValue) +std::string ctor::configuration::get(const std::string& key, const std::string& default_value) const  { -	if(key == cfg::ctor_includedir && ctor::includedir) +	if(key == ctor::cfg::ctor_includedir && ctor::includedir)  	{  		return *ctor::includedir;  	} -	if(key == cfg::ctor_libdir && ctor::libdir) +	if(key == ctor::cfg::ctor_libdir && ctor::libdir)  	{  		return *ctor::libdir;  	} -	const auto& c = configuration(); -	if(hasConfiguration(key)) +	if(key == ctor::cfg::builddir && ctor::builddir)  	{ -		return c.tools.at(key); +		return *ctor::builddir;  	} -	return defaultValue; -} - -std::string locate(const std::string& arch, const std::string& app) -{ -	std::string path_env = std::getenv("PATH"); -	//std::cout << path_env << "\n"; - -	std::string program = app; -	if(!arch.empty()) +	if(ctor::conf_values.find(key) != ctor::conf_values.end())  	{ -		program = arch + "-" + app; +		return ctor::conf_values[key];  	} -	std::cout << "Looking for: " << program << "\n"; -	std::vector<std::string> paths; +	if(has(key))  	{ -		std::stringstream ss(path_env); -		std::string path; -		while (std::getline(ss, path, ':')) -		{ -			paths.push_back(path); -		} -	} -	for(const auto& path_str : paths) -	{ -		std::filesystem::path path(path_str); -		auto prog_path = path / program; -		if(std::filesystem::exists(prog_path)) -		{ -			std::cout << "Found file " << app << " in path: " << path << "\n"; -			auto perms = std::filesystem::status(prog_path).permissions(); -			if((perms & std::filesystem::perms::owner_exec) != std::filesystem::perms::none) -			{ -				//std::cout << " - executable by owner\n"; -			} -			if((perms & std::filesystem::perms::group_exec) != std::filesystem::perms::none) -			{ -				//std::cout << " - executable by group\n"; -			} -			if((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none) -			{ -				//std::cout << " - executable by others\n"; -			} - -			return prog_path.string(); -		} +		return tools.at(key);  	} -	std::cerr << "Could not locate " << app << " for the " << arch << " architecture\n"; -	exit(1); -	return {}; +	return default_value;  }  class Args @@ -143,19 +122,100 @@ public:  	}  }; -int regenerateCache(const Settings& default_settings, +namespace { +std::ostream& operator<<(std::ostream& stream, const ctor::toolchain& toolchain) +{ +	switch(toolchain) +	{ +	case ctor::toolchain::any: +		stream << "ctor::toolchain::any"; +		break; +	case ctor::toolchain::none: +		stream << "ctor::toolchain::none"; +		break; +	case ctor::toolchain::gcc: +		stream << "ctor::toolchain::gcc"; +		break; +	case ctor::toolchain::clang: +		stream << "ctor::toolchain::clang"; +		break; +	} +	return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::arch& arch) +{ +	switch(arch) +	{ +	case ctor::arch::unix: +		stream << "ctor::arch::unix"; +		break; +	case ctor::arch::apple: +		stream << "ctor::arch::apple"; +		break; +	case ctor::arch::windows: +		stream << "ctor::arch::windows"; +		break; +	case ctor::arch::unknown: +		stream << "ctor::arch::unknown"; +		break; +	} +	return stream; +} + +std::ostream& operator<<(std::ostream& ostr, const ctor::c_flag& flag) +{ +	for(const auto& s : to_strings(ctor::toolchain::any, flag)) +	{ +		ostr << s; +	} +	return ostr; +} + +std::ostream& operator<<(std::ostream& ostr, const ctor::cxx_flag& flag) +{ +	for(const auto& s : to_strings(ctor::toolchain::any, flag)) +	{ +		ostr << s; +	} +	return ostr; +} + +std::ostream& operator<<(std::ostream& ostr, const ctor::ld_flag& flag) +{ +	for(const auto& s : to_strings(ctor::toolchain::any, flag)) +	{ +		ostr << s; +	} +	return ostr; +} + +std::ostream& operator<<(std::ostream& ostr, const ctor::asm_flag& flag) +{ +	for(const auto& s : to_strings(ctor::toolchain::any, flag)) +	{ +		ostr << s; +	} +	return ostr; +} +} + +// helper constant for the visitor +template<class> inline constexpr bool always_false_v = false; + +int regenerateCache(ctor::settings& settings, +                    const std::string& name,                      const std::vector<std::string>& args,                      const std::map<std::string, std::string>& env)  { -	Settings settings{default_settings};  	Args vargs(args);  	dg::Options opt;  	int key{128}; -	std::string build_arch; +	std::string build_arch_prefix;  	std::string build_path; -	std::string host_arch; +	std::string host_arch_prefix;  	std::string host_path;  	std::string cc_prog = "gcc";  	std::string cxx_prog = "g++"; @@ -163,12 +223,14 @@ int regenerateCache(const Settings& default_settings,  	std::string ld_prog = "ld";  	std::string ctor_includedir;  	std::string ctor_libdir; +	std::string builddir;  	opt.add("build-dir", required_argument, 'b',  	        "Set output directory for build files (default: '" +  	        settings.builddir + "').",  	        [&]() {  		        settings.builddir = optarg; +		        builddir = optarg;  		        return 0;  	        }); @@ -210,7 +272,7 @@ int regenerateCache(const Settings& default_settings,  	opt.add("build", required_argument, key++,  	        "Configure for building on specified architecture.",  	        [&]() { -		        build_arch = optarg; +		        build_arch_prefix = optarg;  		        return 0;  	        }); @@ -224,7 +286,7 @@ int regenerateCache(const Settings& default_settings,  	opt.add("host", required_argument, key++,  	        "Cross-compile to build programs to run on specified architecture.",  	        [&]() { -		        host_arch = optarg; +		        host_arch_prefix = optarg;  		        return 0;  	        }); @@ -249,48 +311,164 @@ int regenerateCache(const Settings& default_settings,  		        return 0;  	        }); +	// Resolv externals +	ctor::external_configurations externalConfigs; +	for(std::size_t i = 0; i < numExternalConfigFiles; ++i) +	{ +		auto newExternalConfigs = externalConfigFiles[i].cb(settings); +		externalConfigs.insert(externalConfigs.end(), +		                       newExternalConfigs.begin(), +		                       newExternalConfigs.end()); +	} + +	auto add_path_args = +		[&](const std::string& arg_name) +		{ +			opt.add(arg_name + "-includedir", required_argument, key++, +			        "Set path to " + arg_name + " header file.", +			        [&]() { +				        external_includedir[arg_name] = optarg; +				        return 0; +			        }); + +			opt.add(arg_name + "-libdir", required_argument, key++, +			        "Set path to " + arg_name + " libraries.", +			        [&]() { +				        external_libdir[arg_name] = optarg; +				        return 0; +			        }); +		}; + +	for(const auto& ext : externalConfigs) +	{ +		std::visit([&](auto&& arg) +		           { +			           using T = std::decay_t<decltype(arg)>; +			           if constexpr (std::is_same_v<T, ctor::external_manual>) +			           { +				           add_path_args(ext.name); +			           } +			           else +			           { +				           static_assert(always_false_v<T>, "non-exhaustive visitor!"); +			           } +		           }, ext.external); + +	} +  	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()) @@ -316,110 +494,309 @@ int regenerateCache(const Settings& default_settings,  		ld_prog = ld_env->second;  	} -	std::string host_cc = locate(host_arch, cc_prog); -	std::string host_cxx = locate(host_arch, cxx_prog); -	std::string host_ar = locate(host_arch, ar_prog); -	std::string host_ld = locate(host_arch, ld_prog); -	std::string build_cc = locate(build_arch, cc_prog); -	std::string build_cxx = locate(build_arch, cxx_prog); -	std::string build_ar = locate(build_arch, ar_prog); -	std::string build_ld = locate(build_arch, ld_prog); +	auto paths = get_paths(); -	// Resolv externals -	ExternalConfigurations externalConfigs; -	for(std::size_t i = 0; i < numExternalConfigFiles; ++i) +	auto path_env = env.find("PATH"); +	if(path_env != env.end())  	{ -		auto newExternalConfigs = externalConfigFiles[i].cb(); -		externalConfigs.insert(externalConfigs.end(), -		                       newExternalConfigs.begin(), -		                       newExternalConfigs.end()); +		paths = get_paths(path_env->second);  	} +	std::string host_cc; +	std::string host_cxx; +	std::string host_ld; +	std::string host_ar; +	ctor::toolchain host_toolchain{ctor::toolchain::none}; +	ctor::arch host_arch{ctor::arch::unknown}; +	if(needs_host) +	{ +		// Host detection +		if(needs_host_c) +		{ +			host_cc = locate(cc_prog, paths, host_arch_prefix); +			if(host_cc.empty()) +			{ +				std::cerr << "Could not locate host_cc prog" << std::endl; +				return 1; +			} +		} + +		if(needs_host_cxx) +		{ +			host_cxx = locate(cxx_prog, paths, host_arch_prefix); +			if(host_cxx.empty()) +			{ +				std::cerr << "Could not locate host_cxx prog" << std::endl; +				return 1; +			} +		} + +		if(needs_host_ar) +		{ +			host_ar = locate(ar_prog, paths, host_arch_prefix); +			if(host_ar.empty()) +			{ +				std::cerr << "Could not locate host_ar prog" << std::endl; +				return 1; +			} +		} + +		if(needs_host_ld) +		{ +			host_ld = locate(ld_prog, paths, host_arch_prefix); +			if(host_ld.empty()) +			{ +				std::cerr << "Could not locate host_ld prog" << std::endl; +				return 1; +			} +		} + +		if(needs_host_asm) +		{ +			// TODO +		} + +		host_toolchain = getToolChain(host_cxx); +		auto host_arch_str = get_arch(ctor::output_system::host); +		host_arch = get_arch(ctor::output_system::host, host_arch_str); + +		std::cout << "** Host architecture '" << host_arch_str << "': " << host_arch << std::endl; + +		if(host_arch == ctor::arch::unknown) +		{ +			std::cerr << "Could not detect host architecture" << std::endl; +			return 1; +		} +	} + +	std::string build_cc; +	std::string build_cxx; +	std::string build_ld; +	std::string build_ar; +	ctor::toolchain build_toolchain{ctor::toolchain::none}; +	ctor::arch build_arch{ctor::arch::unknown}; +	if(needs_build) +	{ +		// Build detection +		if(needs_build_c) +		{ +			build_cc = locate(cc_prog, paths, build_arch_prefix); +			if(build_cc.empty()) +			{ +				std::cerr << "Could not locate build_cc prog" << std::endl; +				return 1; +			} +		} + +		if(needs_build_cxx) +		{ +			build_cxx = locate(cxx_prog, paths, build_arch_prefix); +			if(build_cxx.empty()) +			{ +				std::cerr << "Could not locate build_cxx prog" << std::endl; +				return 1; +			} +		} + +		if(needs_build_ar) +		{ +			build_ar = locate(ar_prog, paths, build_arch_prefix); +			if(build_ar.empty()) +			{ +				std::cerr << "Could not locate build_ar prog" << std::endl; +				return 1; +			} +		} + +		if(needs_build_ld) +		{ +			build_ld = locate(ld_prog, paths, build_arch_prefix); +			if(build_ld.empty()) +			{ +				std::cerr << "Could not locate build_ld prog" << std::endl; +				return 1; +			} +		} + +		if(needs_build_asm) +		{ +			// TODO +		} + +		build_toolchain = getToolChain(build_cxx); +		auto build_arch_str = get_arch(ctor::output_system::build); +		build_arch = get_arch(ctor::output_system::build, build_arch_str); + +		std::cout << "** Build architecture '" << build_arch_str << "': " << build_arch << std::endl; + +		if(build_arch == ctor::arch::unknown) +		{ +			std::cerr << "Could not detect build architecture" << std::endl; +			return 1; +		} +	} + + +	// Store current values for execution in this execution context. +	if(!ctor_includedir.empty()) +	{ +		ctor::conf_values[ctor::cfg::ctor_includedir] = ctor_includedir; +	} +	if(!ctor_libdir.empty()) +	{ +		ctor::conf_values[ctor::cfg::ctor_libdir] = ctor_libdir; +	} +	if(!builddir.empty()) +	{ +		ctor::conf_values[ctor::cfg::builddir] = builddir; +	} +	ctor::conf_values[ctor::cfg::host_cxx] = host_cxx; +	ctor::conf_values[ctor::cfg::build_cxx] = build_cxx; +  	std::cout << "Writing results to: " << configurationFile.string() << "\n";  	{  		std::ofstream istr(configurationFile); -		istr << "#include <libctor.h>\n\n"; -		istr << "const Configuration& configuration()\n"; +		istr << "#include <ctor.h>\n\n"; +		istr << "const ctor::configuration& ctor::get_configuration()\n";  		istr << "{\n"; -		istr << "	static Configuration cfg =\n"; +		istr << "	static ctor::configuration cfg =\n";  		istr << "	{\n"; +		if(needs_host) +		{ +			istr << "		.host_toolchain = " << host_toolchain << ",\n"; +			istr << "		.host_arch = " << host_arch << ",\n"; +		} +		if(needs_build) +		{ +			istr << "		.build_toolchain = " << build_toolchain << ",\n"; +			istr << "		.build_arch = " << build_arch << ",\n"; +		}  		istr << "		.args = {";  		for(const auto& arg : args)  		{ -			istr << "\"" << arg << "\","; +			istr << "\"" << esc(arg) << "\",";  		}  		istr << "},\n"; -		istr << "		.env = {"; +		istr << "		.env = {\n";  		for(const auto& e : env)  		{ -			istr << "{\"" << e.first << "\", \"" << e.second << "\"}, "; +			istr << "			{\"" << esc(e.first) << "\", \"" << esc(e.second) << "\"},\n";  		} -		istr << "},\n"; +		istr << "		},\n";  		istr << "		.tools = {\n"; -		istr << "			{ \"" << cfg::builddir << "\", \"" << settings.builddir << "\" },\n"; -		istr << "			{ \"" << cfg::host_cc << "\", \"" << host_cc << "\" },\n"; -		istr << "			{ \"" << cfg::host_cxx << "\", \"" << host_cxx << "\" },\n"; -		istr << "			{ \"" << cfg::host_ar << "\", \"" << host_ar << "\" },\n"; -		istr << "			{ \"" << cfg::host_ld << "\", \"" << host_ld << "\" },\n"; -		istr << "			{ \"" << cfg::build_cc << "\", \"" << build_cc << "\" },\n"; -		istr << "			{ \"" << cfg::build_cxx << "\", \"" << build_cxx << "\" },\n"; -		istr << "			{ \"" << cfg::build_ar << "\", \"" << build_ar << "\" },\n"; -		istr << "			{ \"" << cfg::build_ld << "\", \"" << build_ld << "\" },\n"; +		if(!builddir.empty()) +		{ +			istr << "			{ \"" << ctor::cfg::builddir << "\", \"" << esc(builddir) << "\" },\n"; +			ctor::builddir = builddir; +		} +		if(needs_host) +		{ +			if(needs_host_c) +			{ +				istr << "			{ \"" << ctor::cfg::host_cc << "\", \"" << esc(host_cc) << "\" },\n"; +			} +			if(needs_host_cxx) +			{ +				istr << "			{ \"" << ctor::cfg::host_cxx << "\", \"" << esc(host_cxx) << "\" },\n"; +			} +			if(needs_host_ar) +			{ +				istr << "			{ \"" << ctor::cfg::host_ar << "\", \"" << esc(host_ar) << "\" },\n"; +			} +			if(needs_host_ld) +			{ +				istr << "			{ \"" << ctor::cfg::host_ld << "\", \"" << esc(host_ld) << "\" },\n"; +			} +		} +		if(needs_build) +		{ +			if(needs_build_c) +			{ +				istr << "			{ \"" << ctor::cfg::build_cc << "\", \"" << esc(build_cc) << "\" },\n"; +			} +			if(needs_build_cxx) +			{ +				istr << "			{ \"" << ctor::cfg::build_cxx << "\", \"" << esc(build_cxx) << "\" },\n"; +			} +			if(needs_build_ar) +			{ +				istr << "			{ \"" << ctor::cfg::build_ar << "\", \"" << esc(build_ar) << "\" },\n"; +			} +			if(needs_build_ld) +			{ +				istr << "			{ \"" << ctor::cfg::build_ld << "\", \"" << esc(build_ld) << "\" },\n"; +			} +		}  		if(!ctor_includedir.empty())  		{ -			istr << "			{ \"" << cfg::ctor_includedir << "\", \"" << ctor_includedir << "\" },\n"; +			istr << "			{ \"" << ctor::cfg::ctor_includedir << "\", \"" << esc(ctor_includedir) << "\" },\n";  			ctor::includedir = ctor_includedir;  		}  		if(!ctor_libdir.empty())  		{ -			istr << "			{ \"" << cfg::ctor_libdir << "\", \"" << ctor_libdir << "\" },\n"; +			istr << "			{ \"" << ctor::cfg::ctor_libdir << "\", \"" << esc(ctor_libdir) << "\" },\n";  			ctor::libdir = ctor_libdir;  		}  		istr << "		},\n";  		istr << "		.externals = {\n"; -		for(const auto& externalConfig : externalConfigs) +		for(const auto& ext : externalConfigs)  		{ -			istr << "			{ \"" << externalConfig.name << "\", {\n"; +			istr << "			{ \"" << esc(ext.name) << "\", {\n"; +			ctor::flags resolved_flags; +			if(std::holds_alternative<ctor::external_manual>(ext.external)) +			{ +				if(auto ret = resolv(settings, ext, +				                     std::get<ctor::external_manual>(ext.external), +				                     resolved_flags)) +				{ +					return ret; +				} +			} +			else +			{ +				std::cout << "Unknown external type\n"; +				return 1; +			} -			if(!externalConfig.flags.cxxflags.empty()) +			if(!resolved_flags.cflags.empty())  			{ -				istr << "			  .cxxflags = {"; -				for(const auto& flag : externalConfig.flags.cxxflags) +				istr << "			  .cflags = {"; +				for(const auto& flag : resolved_flags.cflags)  				{ -					istr << "\"" << flag << "\","; +					istr << flag << ",";  				}  				istr << "},\n";  			} -			if(!externalConfig.flags.cflags.empty()) +			if(!resolved_flags.cxxflags.empty())  			{ -				istr << "			  .cflags = {"; -				for(const auto& flag : externalConfig.flags.cflags) +				istr << "			  .cxxflags = {"; +				for(const auto& flag : resolved_flags.cxxflags)  				{ -					istr << "\"" << flag << "\","; +					istr << flag << ",";  				}  				istr << "},\n";  			} -			if(!externalConfig.flags.ldflags.empty()) +			if(!resolved_flags.ldflags.empty())  			{  				istr << "			  .ldflags = {"; -				for(const auto& flag : externalConfig.flags.ldflags) +				for(const auto& flag : resolved_flags.ldflags)  				{ -					istr << "\"" << flag << "\","; +					istr << flag << ",";  				}  				istr << "},\n";  			} -			if(!externalConfig.flags.asmflags.empty()) +			if(!resolved_flags.asmflags.empty())  			{  				istr << "			  .asmflags = {"; -				for(const auto& flag : externalConfig.flags.asmflags) +				for(const auto& flag : resolved_flags.asmflags)  				{ -					istr << "\"" << flag << "\","; +					istr << flag << ",";  				}  				istr << "},\n";  			} @@ -429,7 +806,7 @@ int regenerateCache(const Settings& default_settings,  		istr << "		},\n";  		istr << "	};\n";  		istr << "	return cfg;\n"; -		istr << "}\n\n"; +		istr << "}\n";  	}  	{ @@ -442,14 +819,16 @@ int regenerateCache(const Settings& default_settings,  	return 0;  } -int configure(const Settings& global_settings, int argc, char* argv[]) +int configure(const ctor::settings& global_settings, int argc, char* argv[])  { -	Settings settings{global_settings}; +	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; @@ -477,7 +856,13 @@ int configure(const Settings& global_settings, int argc, char* argv[])  		env["LD"] = ld_env;  	} -	auto ret = regenerateCache(settings, args, env); +	auto path_env = getenv("PATH"); +	if(path_env) +	{ +		env["PATH"] = path_env; +	} + +	auto ret = regenerateCache(settings, args_span[0], args, env);  	if(ret != 0)  	{  		return ret; @@ -488,36 +873,40 @@ int configure(const Settings& global_settings, int argc, char* argv[])  	return 0;  } -int reconfigure(const Settings& settings, int argc, char* argv[]) +int reconfigure(const ctor::settings& global_settings, int argc, char* argv[])  { +	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 = configuration(); +	const auto& cfg = ctor::get_configuration();  	std::cout << "Re-running configure:\n";  	for(const auto& e : cfg.env)  	{  		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; @@ -530,5 +919,5 @@ int reconfigure(const Settings& 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 16499d6..5344646 100644 --- a/src/configure.h +++ b/src/configure.h @@ -8,10 +8,12 @@  #include <map>  #include <vector> -struct Settings; +namespace ctor { +struct settings; +} // namespace ctor:: -extern std::filesystem::path configurationFile;; -extern std::filesystem::path configHeaderFile; +extern const std::filesystem::path configurationFile; +extern const std::filesystem::path configHeaderFile; -int configure(const Settings& settings, int argc, char* argv[]); -int reconfigure(const Settings& settings, int argc, char* argv[]); +int configure(const ctor::settings& settings, int argc, char* argv[]); +int reconfigure(const ctor::settings& settings, int argc, char* argv[]); diff --git a/src/ctor.h b/src/ctor.h new file mode 100644 index 0000000..191fed1 --- /dev/null +++ b/src/ctor.h @@ -0,0 +1,303 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include <source_location> +#include <string> +#include <vector> +#include <map> +#include <variant> +#include <cstddef> +#include <functional> +#include <string_view> + +namespace ctor { + +enum class target_type +{ +	automatic, // Default - deduce from target name and sources extensions + +	executable, +	static_library, +	dynamic_library, +	object, +	unit_test, +	unit_test_library, +	function, + +	unknown, +}; + +enum class language +{ +	automatic, // Default - deduce language from source extensions + +	c, +	cpp, +	assembler, +}; + +enum class output_system +{ +	host, // Output for the target system +	build, // Internal tool during cross-compilation +}; + +enum class arch +{ +	unix,    //!< Target platform architecture is unix-based (ie. linux, bsd, etc) +	apple,   //!< Target platform architecture is macos +	windows, //!< Target platform architecture is windows + +	unknown, //!< Target platform architecture has not yet detected or was not possible to detect +}; + +enum class toolchain +{ +	any, +	none, +	gcc, +	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 +	include_path,                    //  -I<arg> +	cpp_std,                         //  -std=<arg> +	optimization,                    //  -O<arg> +	position_independent_code,       //  -fPIC +	position_independent_executable, //  -fPIE +	define,                          //  -D<arg>[=<arg2>] +	custom,                          // entire option taken verbatim from <arg> +}; + +enum class c_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 +	include_path,                    //  -I<arg> +	c_std,                           //  -std=<arg> +	optimization,                    //  -O<arg> +	position_independent_code,       //  -fPIC +	position_independent_executable, //  -fPIE +	define,                          //  -D<arg>[=<arg2>] +	custom,                          // entire option taken verbatim from <arg> +}; + +enum class ld_opt +{ +	//                                  gcc/clang +	output,                          //  -o +	strip,                           //  -s +	warn_all,                        //  -Wall +	warnings_as_errors,              //  -Werror +	library_path,                    //  -L<arg> +	link,                            //  -l<arg> +	cpp_std,                         //  -std=<arg> +	build_shared,                    //  -shared +	threads,                         //  -pthread +	position_independent_code,       //  -fPIC +	position_independent_executable, //  -fPIE +	custom,                          // entire option taken verbatim from <arg> +}; + +enum class ar_opt +{ +	//                                  gcc/clang +	replace,                         // -r +	add_index,                       // -s +	create,                          // -c +	output,                          // <arg> + +	custom,                          // entire option taken verbatim from <arg> +}; + +enum class asm_opt +{ +	//                                  gcc/clang +	custom,                          // entire option taken verbatim from <arg> +}; + +template<typename T> +class flag +{ +public: +	flag(std::string_view str); +	flag(const char* str); +	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{}; +	std::string arg; +	std::string arg2; +}; + +using c_flag = ctor::flag<ctor::c_opt>; +using cxx_flag = ctor::flag<ctor::cxx_opt>; +using ld_flag = ctor::flag<ctor::ld_opt>; +using ar_flag = ctor::flag<ctor::ar_opt>; +using asm_flag = ctor::flag<ctor::asm_opt>; + +using c_flags = std::vector<ctor::c_flag>; +using cxx_flags = std::vector<ctor::cxx_flag>; +using ld_flags= std::vector<ctor::ld_flag>; +using ar_flags = std::vector<ctor::ar_flag>; +using asm_flags = std::vector<ctor::asm_flag>; + +struct flags +{ +	ctor::c_flags cflags; // flags for c compiler +	ctor::cxx_flags cxxflags; // flags for c++ compiler +	ctor::ld_flags ldflags; // flags for linker +	ctor::ar_flags arflags; // flags for archiver +	ctor::asm_flags asmflags; // flags for asm translator +}; + +struct settings +{ +	std::string builddir{"build"}; +	std::size_t parallel_processes{1}; +	int verbose{0}; // -1: completely silent, 0: normal, 1: verbose, ... +}; + +struct build_configuration; +using GeneratorCb = std::function<int(const std::string& input, +                                      const std::string& output, +                                      const build_configuration& config, +                                      const ctor::settings& settings)>; + +struct build_configuration +{ +	std::string name; // Name - used for referring in other configurations. +	ctor::target_type type{ctor::target_type::automatic}; +	ctor::output_system system{ctor::output_system::build}; +	std::string target; // Output target file for this configuration +	std::vector<ctor::source> sources; // source list +	std::vector<std::string> depends; // internal target dependencies +	ctor::flags flags; +	std::vector<std::string> externals; // externals used by this configuration +	GeneratorCb function; +}; + +using build_configurations = std::vector<build_configuration>; + +int reg(ctor::build_configurations (*cb)(const ctor::settings&), +        const std::source_location location = std::source_location::current()); + +// This type will use flags verbatim +struct external_manual +{ +	ctor::flags flags; +}; + + +struct external_configuration +{ +	std::string name; // Name for configuration +	ctor::output_system system{ctor::output_system::build}; +	std::variant<ctor::external_manual> external; +}; + +using external_configurations = std::vector<ctor::external_configuration>; + +int reg(ctor::external_configurations (*cb)(const ctor::settings&), +        const std::source_location location = std::source_location::current()); + +// Convenience macro - ugly but keeps things simple(r) +#define CTOR_CONCAT(a, b) CTOR_CONCAT_INNER(a, b) +#define CTOR_CONCAT_INNER(a, b) a ## b +#define CTOR_UNIQUE_NAME(base) CTOR_CONCAT(base, __LINE__) +#define REG(cb) namespace { int CTOR_UNIQUE_NAME(unique) = reg(cb); } + +// Predefined configuration keys +namespace cfg +{ +constexpr auto builddir = "builddir"; + +constexpr auto host_cc = "host-cc"; +constexpr auto host_cxx = "host-cxx"; +constexpr auto host_ar = "host-ar"; +constexpr auto host_ld = "host-ld"; + +constexpr auto build_cc = "build-cc"; +constexpr auto build_cxx = "build-cxx"; +constexpr auto build_ar = "build-ar"; +constexpr auto build_ld = "build-ld"; + +constexpr auto ctor_includedir = "ctor-includedir"; +constexpr auto ctor_libdir = "ctor-libdir"; +} + +struct configuration +{ +	bool has(const std::string& key) const; +	std::string get(const std::string& key, const std::string& default_value = {}) const; + +	ctor::toolchain host_toolchain{ctor::toolchain::none}; +	ctor::arch host_arch{ctor::arch::unknown}; + +	ctor::toolchain build_toolchain{ctor::toolchain::none}; +	ctor::arch build_arch{ctor::arch::unknown}; + +	std::vector<std::string> args; // vector of arguments used when last calling configure +	std::map<std::string, std::string> env; // env used when last calling configure + +	std::map<std::string, std::string> tools; // tools +	std::map<std::string, ctor::flags> externals; +}; + +const ctor::configuration& get_configuration(); + +} // ctor:: diff --git a/src/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 610ccdd..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,46 +79,61 @@ 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; +	} -		std::cout << cmd << "\n"; +	if(settings.verbose > 0) +	{ +		std::cout << cmd << std::endl;  	}  #if 1  	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.h b/src/externals.h new file mode 100644 index 0000000..7b4aa23 --- /dev/null +++ b/src/externals.h @@ -0,0 +1,6 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include "externals_manual.h" diff --git a/src/externals_manual.cc b/src/externals_manual.cc new file mode 100644 index 0000000..0563a5e --- /dev/null +++ b/src/externals_manual.cc @@ -0,0 +1,35 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#include "externals_manual.h" + +#include <map> + +#include "ctor.h" + +#include "util.h" +#include "tools.h" + +extern std::map<std::string, std::string> external_includedir; +extern std::map<std::string, std::string> external_libdir; + +int resolv([[maybe_unused]]const ctor::settings& settings, const ctor::external_configuration& config, +           const ctor::external_manual& ext, ctor::flags& flags) +{ +	flags = ext.flags; + +	auto inc = external_includedir.find(config.name); +	if(inc != external_includedir.end()) +	{ +		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.emplace_back(ctor::ld_opt::library_path, lib->second); +	} + +	return 0; +} diff --git a/src/externals_manual.h b/src/externals_manual.h new file mode 100644 index 0000000..a906ab7 --- /dev/null +++ b/src/externals_manual.h @@ -0,0 +1,15 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +namespace ctor { +struct settings; +struct external_configuration; +struct external_manual; +struct flags; +} // namespace ctor:: + +int resolv(const ctor::settings& settings, +           const ctor::external_configuration& name, +           const ctor::external_manual& ext, ctor::flags& flags); diff --git a/src/getoptpp b/src/getoptpp -Subproject 9ff20ef857429619267e3f156a4f81ad9e1eb8c +Subproject 5aba94355ec638c6f8612f86be309ed684979ae diff --git a/src/libctor.cc b/src/libctor.cc index a2c873e..aaf17c9 100644 --- a/src/libctor.cc +++ b/src/libctor.cc @@ -15,12 +15,11 @@  #include <deque>  #include <fstream>  #include <cstdlib> -#include <set> +#include <span>  #include <getoptpp/getoptpp.hpp> -#include "libctor.h" -#include "settings.h" +#include "ctor.h"  #include "configure.h"  #include "rebuild.h"  #include "tasks.h" @@ -29,18 +28,21 @@  int main(int argc, char* argv[])  { -	Settings settings{}; +	auto args = std::span(argv, static_cast<std::size_t>(argc)); -	settings.builddir = getConfiguration(cfg::builddir, settings.builddir); +	ctor::settings settings{}; +	const auto& c = ctor::get_configuration(); + +	settings.builddir = c.get(ctor::cfg::builddir, settings.builddir);  	settings.parallel_processes =  		std::max(1u, std::thread::hardware_concurrency()) * 2 - 1; -	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"; @@ -264,13 +267,12 @@ Options:  	if(print_configure_cmd)  	{  		no_default_build = true; -		std::cout << getConfiguration("cmd") << "\n"; +		std::cout << c.get("cmd") << "\n";  	}  	if(print_configure_db)  	{  		no_default_build = true; -		const auto& c = configuration();  		for(const auto& config : c.tools)  		{  			std::cout << config.first << ": " << config.second << "\n"; @@ -316,7 +318,8 @@ Options:  			auto& targets = getTargets(settings);  			for(const auto& target : targets)  			{ -				if(target.config.type == TargetType::UnitTest) +				if(target.config.type == ctor::target_type::unit_test || +				   target.config.type == ctor::target_type::unit_test_library)  				{  					unittest_targets.push_back(target);  				} @@ -338,7 +341,8 @@ Options:  				auto& targets = getTargets(settings);  				for(const auto& target : targets)  				{ -					if(target.config.type != TargetType::UnitTest) +					if(target.config.type != ctor::target_type::unit_test && +					   target.config.type != ctor::target_type::unit_test_library)  					{  						non_unittest_targets.push_back(target);  					} @@ -367,7 +371,8 @@ Options:  		auto& targets = getTargets(settings);  		for(const auto& target : targets)  		{ -			if(target.config.type != TargetType::UnitTest) +			if(target.config.type != ctor::target_type::unit_test && +			   target.config.type != ctor::target_type::unit_test_library)  			{  				non_unittest_targets.push_back(target);  			} diff --git a/src/libctor.h b/src/libctor.h deleted file mode 100644 index 0af33cb..0000000 --- a/src/libctor.h +++ /dev/null @@ -1,123 +0,0 @@ -// -*- c++ -*- -// Distributed under the BSD 2-Clause License. -// See accompanying file LICENSE for details. -#pragma once - -#include <source_location> -#include <string> -#include <vector> -#include <map> - -enum class TargetType -{ -	Auto, // Default - deduce from target name and sources extensions - -	Executable, -	StaticLibrary, -	DynamicLibrary, -	Object, -	UnitTest, -}; - -enum class Language -{ -	Auto, // Default - deduce language from source extensions - -	C, -	Cpp, -	Asm, -}; - -enum class OutputSystem -{ -	Host, // Output for the target system -	Build, // Internal tool during cross-compilation -}; - -struct Source -{ -	Source(const char* file) : file(file) {} -	Source(const std::string& file) : file(file) {} -	Source(const char* file, Language lang) : file(file), language(lang) {} -	Source(const std::string& file, Language lang) : file(file), language(lang) {} - -	std::string file; -	Language language{Language::Auto}; -}; - -struct Flags -{ -	std::vector<std::string> cxxflags; // flags for c++ compiler -	std::vector<std::string> cflags; // flags for c compiler -	std::vector<std::string> ldflags; // flags for linker -	std::vector<std::string> asmflags; // flags for asm translator -}; - -struct BuildConfiguration -{ -	std::string name; // Name - used for referring in other configurations. -	TargetType type{TargetType::Auto}; -	OutputSystem system{OutputSystem::Host}; -	std::string target; // Output target file for this configuration -	std::vector<Source> sources; // source list -	std::vector<std::string> depends; // internal target dependencies -	Flags flags; -	std::vector<std::string> externals; // externals used by this configuration -}; - -using BuildConfigurations = std::vector<BuildConfiguration>; - -int reg(BuildConfigurations (*cb)(), -        const std::source_location location = std::source_location::current()); - - -struct ExternalConfiguration -{ -	std::string name; // Name for configuration -	Flags flags; -	std::vector<std::string> libs; // libraries -}; - -using ExternalConfigurations = std::vector<ExternalConfiguration>; - -int reg(ExternalConfigurations (*cb)(), -        const std::source_location location = std::source_location::current()); - -// Convenience macro - ugly but keeps things simple(r) -#define CONCAT(a, b) CONCAT_INNER(a, b) -#define CONCAT_INNER(a, b) a ## b -#define UNIQUE_NAME(base) CONCAT(base, __LINE__) -#define REG(cb) namespace { int UNIQUE_NAME(unique) = reg(cb); } - -// Predefined configuration keys -namespace cfg -{ -constexpr auto builddir = "builddir"; - -constexpr auto host_cc = "host-cc"; -constexpr auto host_cxx = "host-cpp"; -constexpr auto host_ar = "host-ar"; -constexpr auto host_ld = "host-ld"; - -constexpr auto build_cc = "build-cc"; -constexpr auto build_cxx = "build-cpp"; -constexpr auto build_ar = "build-ar"; -constexpr auto build_ld = "build-ld"; - -constexpr auto ctor_includedir = "ctor-includedir"; -constexpr auto ctor_libdir = "ctor-libdir"; -} - -struct Configuration -{ -	std::vector<std::string> args; // vector of arguments used when last calling configure -	std::map<std::string, std::string> env; // env used when last calling configure - -	std::map<std::string, std::string> tools; // tools -	std::map<std::string, Flags> externals; -}; - -const Configuration& configuration(); -bool hasConfiguration(const std::string& key); -const std::string& getConfiguration(const std::string& key, -                                    const std::string& defaultValue = {}); diff --git a/src/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 50d7540..52245fe 100644 --- a/src/rebuild.cc +++ b/src/rebuild.cc @@ -7,18 +7,22 @@  #include <filesystem>  #include <algorithm>  #include <source_location> +#include <cstring> +#include <span>  #include "configure.h" -#include "settings.h" -#include "libctor.h" +#include "ctor.h"  #include "tasks.h"  #include "build.h"  #include "execute.h" +#include "tools.h" +#include "util.h"  std::array<BuildConfigurationEntry, 1024> configFiles;  std::size_t numConfigFiles{0}; -int reg(BuildConfigurations (*cb)(), +namespace ctor { +int reg(ctor::build_configurations (*cb)(const ctor::settings&),          const std::source_location location)  {  	// NOTE: std::cout cannot be used here @@ -29,12 +33,23 @@ int reg(BuildConfigurations (*cb)(),  		exit(1);  	} -	configFiles[numConfigFiles].file = location.file_name(); +	auto loc = std::filesystem::path(location.file_name()); +	if(loc.is_absolute()) +	{ +		auto pwd = std::filesystem::current_path(); +		auto rel = std::filesystem::relative(loc, pwd); +		configFiles[numConfigFiles].file = strdup(rel.string().data()); // NOTE: This intentionally leaks memory +	} +	else +	{ +		configFiles[numConfigFiles].file = location.file_name(); +	}  	configFiles[numConfigFiles].cb = cb;  	++numConfigFiles;  	return 0;  } +} // ctor::  int reg(const char* location)  { @@ -48,7 +63,7 @@ int reg(const char* location)  	configFiles[numConfigFiles].file = location;  	configFiles[numConfigFiles].cb = -		[](){ return std::vector<BuildConfiguration>{}; }; +		[](const ctor::settings&){ return std::vector<ctor::build_configuration>{}; };  	++numConfigFiles;  	return 0; @@ -56,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) @@ -97,7 +112,8 @@ int unreg(const char* location)  std::array<ExternalConfigurationEntry, 1024> externalConfigFiles;  std::size_t numExternalConfigFiles{0}; -int reg(ExternalConfigurations (*cb)(), +namespace ctor { +int reg(ctor::external_configurations (*cb)(const ctor::settings&),          const std::source_location location)  {  	// NOTE: std::cout cannot be used here @@ -114,10 +130,11 @@ int reg(ExternalConfigurations (*cb)(),  	return 0;  } +} // namespace ctor::  namespace  { -bool contains(const std::vector<Source>& sources, const std::string& file) +bool contains(const std::vector<ctor::source>& sources, const std::string& file)  {  	for(const auto& source : sources)  	{ @@ -131,9 +148,11 @@ bool contains(const std::vector<Source>& sources, const std::string& file)  }  } -bool recompileCheck(const Settings& global_settings, int argc, char* argv[], +bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[],                      bool relaunch_allowed)  { +	auto args_span = std::span(argv, static_cast<std::size_t>(argc)); +  	using namespace std::string_literals;  	if(global_settings.verbose > 1) @@ -141,36 +160,46 @@ bool recompileCheck(const Settings& global_settings, int argc, char* argv[],  		std::cout << "Recompile check (" << numConfigFiles << "):\n";  	} -	BuildConfiguration config; +	ctor::build_configuration config; +	config.type = ctor::target_type::executable;  	config.name = "ctor"; -	config.flags.cxxflags = -		std::vector<std::string>({ "-s", "-O3", "-std=c++20" }); -	if(hasConfiguration(cfg::ctor_includedir)) +	config.system = ctor::output_system::build; + +	config.flags.cxxflags.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("-I"s + getConfiguration(cfg::ctor_includedir)); +		config.flags.cxxflags.emplace_back(ctor::cxx_opt::include_path, +		                                   c.get(ctor::cfg::ctor_includedir));  	} -	if(hasConfiguration(cfg::ctor_libdir)) +	if(c.has(ctor::cfg::ctor_libdir))  	{ -		config.flags.ldflags.push_back("-L"s + getConfiguration(cfg::ctor_libdir)); +		config.flags.ldflags.emplace_back(ctor::ld_opt::library_path, +		                                  c.get(ctor::cfg::ctor_libdir));  	} -	config.flags.ldflags.push_back("-lctor"); -	config.flags.ldflags.push_back("-pthread"); - +	config.flags.ldflags.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); -	Settings settings{global_settings}; +	ctor::settings settings{global_settings};  	settings.verbose = -1; // Make check completely silent. -	settings.builddir += "/ctor"; // override builddir to use ctor subdir + +	// 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) @@ -184,7 +213,7 @@ bool recompileCheck(const 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);  		}  	} @@ -199,11 +228,11 @@ bool recompileCheck(const 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)  	{ @@ -229,22 +258,27 @@ bool recompileCheck(const Settings& global_settings, int argc, char* argv[],  	if(dirty_tasks)  	{  		std::cout << "Rebuilding config.\n"; -		build(settings, "ctor", tasks); // run for real +		auto ret = build(settings, "ctor", tasks); // run for real +		if(ret != 0) +		{ +			return ret; +		}  	}  	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/rebuild.h b/src/rebuild.h index 0845d30..efa6d42 100644 --- a/src/rebuild.h +++ b/src/rebuild.h @@ -6,20 +6,18 @@  #include <vector>  #include <array> -#include "libctor.h" - -class Settings; +#include "ctor.h"  struct BuildConfigurationEntry  {  	const char* file; -	BuildConfigurations (*cb)(); +	ctor::build_configurations (*cb)(const ctor::settings&);  };  struct ExternalConfigurationEntry  {  	const char* file; -	ExternalConfigurations (*cb)(); +	ctor::external_configurations (*cb)(const ctor::settings&);  };  extern std::array<BuildConfigurationEntry, 1024> configFiles; @@ -32,5 +30,5 @@ int reg(const char* location);  int unreg(const char* location);  //! Returns true of recompilation was needed. -bool recompileCheck(const Settings& settings, int argc, char* argv[], +bool recompileCheck(const ctor::settings& settings, int argc, char* argv[],                      bool relaunch_allowed = true); diff --git a/src/settings.h b/src/settings.h deleted file mode 100644 index 48e3619..0000000 --- a/src/settings.h +++ /dev/null @@ -1,13 +0,0 @@ -// -*- c++ -*- -// Distributed under the BSD 2-Clause License. -// See accompanying file LICENSE for details. -#pragma once - -#include <cstddef> - -struct Settings -{ -	std::string builddir{"build"}; -	std::size_t parallel_processes{1}; -	int verbose{0}; // -1: completely silent, 0: normal, 1: verbose, ... -}; diff --git a/src/task.cc b/src/task.cc index 8a9eefa..ef7731b 100644 --- a/src/task.cc +++ b/src/task.cc @@ -3,25 +3,32 @@  // See accompanying file LICENSE for details.  #include "task.h" -#include <unistd.h>  #include <iostream> +#include <algorithm> +#include <utility> -Task::Task(const BuildConfiguration& config) -	: 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(std::move(sourceDir_))  {  } -int Task::registerDepTasks(const std::list<std::shared_ptr<Task>>& tasks) +int Task::registerDepTasks(const std::vector<std::shared_ptr<Task>>& tasks)  {  	for(const auto& depStr : depends())  	{  		bool found{false};  		for(const auto& task : tasks)  		{ -			if(task->target() == depStr) +			if(*task == depStr)  			{ -				dependsTasks.push_back(task); +				if(std::find(dependsTasks.begin(), dependsTasks.end(), task) == dependsTasks.end()) +				{ +					dependsTasks.push_back(task); +				}  				found = true;  			}  		} @@ -33,16 +40,24 @@ int Task::registerDepTasks(const std::list<std::shared_ptr<Task>>& tasks)  		}  	} -	return 0; +	return registerDepTasksInner(tasks); +} + +bool Task::operator==(const std::string& depStr) +{ +	std::filesystem::path generated_output = sourceDir; +	generated_output /= target(); +	return +		(!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 +		;  }  std::string Task::name() const  { -	// If config name is not set, use target instead. -	if(config.name.empty()) -	{ -		return config.target; -	}  	return config.name;  } @@ -99,54 +114,60 @@ State Task::state() const  	return task_state.load();  } -const BuildConfiguration& Task::buildConfig() const +const ctor::build_configuration& Task::buildConfig() const  {  	return config;  } -TargetType Task::targetType() const +ctor::target_type Task::targetType() const  {  	return target_type;  } -Language Task::sourceLanguage() const +ctor::language Task::sourceLanguage() const  {  	return source_language;  } -OutputSystem Task::outputSystem() const +ctor::output_system Task::outputSystem() const  {  	return output_system;  }  std::string Task::compiler() const  { +	const auto& c = ctor::get_configuration();  	switch(sourceLanguage())  	{ -	case Language::C: +	case ctor::language::c:  		switch(outputSystem())  		{ -		case OutputSystem::Host: -			return getConfiguration(cfg::host_cc, "/usr/bin/gcc"); -		case OutputSystem::Build: -			return getConfiguration(cfg::build_cc, "/usr/bin/gcc"); +		case ctor::output_system::host: +			return c.get(ctor::cfg::host_cc, "/usr/bin/gcc"); +		case ctor::output_system::build: +			return c.get(ctor::cfg::build_cc, "/usr/bin/gcc");  		} -	case Language::Cpp: +		break; +	case ctor::language::cpp:  		switch(outputSystem())  		{ -		case OutputSystem::Host: -			return getConfiguration(cfg::host_cxx, "/usr/bin/g++"); -		case OutputSystem::Build: -			return getConfiguration(cfg::build_cxx, "/usr/bin/g++"); +		case ctor::output_system::host: +			return c.get(ctor::cfg::host_cxx, "/usr/bin/g++"); +		case ctor::output_system::build: +			return c.get(ctor::cfg::build_cxx, "/usr/bin/g++");  		} +		break;  	default:  		std::cerr << "Unknown CC target type\n";  		exit(1);  		break;  	} + +	std::cerr << "Unhandled compiler!\n"; +	exit(1);  } -std::list<std::shared_ptr<Task>> Task::getDependsTasks() +std::vector<std::shared_ptr<Task>> Task::getDependsTasks()  {  	return dependsTasks;  } @@ -6,10 +6,10 @@  #include <vector>  #include <string>  #include <atomic> -#include <list>  #include <memory> +#include <filesystem> -#include "libctor.h" +#include "ctor.h"  enum class State  { @@ -23,19 +23,30 @@ enum class State  class Task  {  public: -	Task(const BuildConfiguration& config); +	Task(const ctor::build_configuration& config, const ctor::settings& settings, +	     std::string sourceDir); +	virtual ~Task() = default; -	int registerDepTasks(const std::list<std::shared_ptr<Task>>& tasks); +	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; } -	virtual std::string name() const; +	bool operator==(const std::string& dep); + +	std::string name() const;  	bool dirty();  	bool ready();  	int run();  	State state() const;  	virtual int clean() = 0 ;  	virtual std::vector<std::string> depends() const = 0; + +	//! Raw target name as stated in ctor.cc config file or (in case of a derived +	//! target) the calculated target without builddir prefix.  	virtual std::string target() const = 0; +	//! Target file with full path prefix +	virtual std::filesystem::path targetFile() const = 0; +  	//! Returns true for tasks that are non-target tasks, ie. for example derived  	//! objects files from target sources.  	virtual bool derived() const = 0; @@ -45,14 +56,14 @@ public:  	//! Returns a reference to the originating build config.  	//! Note: the build config of a derived task will be that of its parent  	//! (target) task. -	const BuildConfiguration& buildConfig() const; +	const ctor::build_configuration& buildConfig() const; -	TargetType targetType() const; -	Language sourceLanguage() const; -	OutputSystem outputSystem() const; +	ctor::target_type targetType() const; +	ctor::language sourceLanguage() const; +	ctor::output_system outputSystem() const;  	std::string compiler() const; -	std::list<std::shared_ptr<Task>> getDependsTasks(); +	std::vector<std::shared_ptr<Task>> getDependsTasks();  	virtual std::string source() const { return {}; } @@ -62,9 +73,11 @@ protected:  	virtual bool dirtyInner() { return false; }  	std::vector<std::string> dependsStr; -	std::list<std::shared_ptr<Task>> dependsTasks; -	const BuildConfiguration& config; -	TargetType target_type{TargetType::Auto}; -	Language source_language{Language::Auto}; -	OutputSystem output_system{OutputSystem::Host}; +	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}; +	ctor::output_system output_system{ctor::output_system::host}; +	const ctor::settings& settings; +	std::string sourceDir;  }; diff --git a/src/task_ar.cc b/src/task_ar.cc index d4a4447..3b45cc2 100644 --- a/src/task_ar.cc +++ b/src/task_ar.cc @@ -6,25 +6,27 @@  #include <iostream>  #include <fstream> -#include "libctor.h" -#include "settings.h" +#include "ctor.h"  #include "execute.h"  #include "util.h" +#include "tools.h" -TaskAR::TaskAR(const BuildConfiguration& config, -               const Settings& settings, +TaskAR::TaskAR(const ctor::build_configuration& config_, +               const ctor::settings& settings_,                 const std::string& target,                 const std::vector<std::string>& objects, -               const std::string& sourcePath) -	: Task(config) -	, config(config) -	, settings(settings) +               const std::string& sourceDir_) +	: Task(config_, settings_, sourceDir_) +	, _targetFile(target) +	, config(config_) +	, settings(settings_) +	, sourceDir(sourceDir_)  { -	std::filesystem::path base = settings.builddir; -	base /= sourcePath; -	std::filesystem::create_directories(base); +	target_type = ctor::target_type::static_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,30 +36,29 @@ TaskAR::TaskAR(const BuildConfiguration& config,  	for(const auto& dep : config.depends)  	{ -		std::filesystem::path depFile = settings.builddir; -		depFile /= dep; -		depFiles.push_back(depFile); +		depFiles.emplace_back(dep);  	} -	flagsFile = base / targetFile.stem(); +	flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();  	flagsFile += ".flags"; -	target_type = TargetType::StaticLibrary; -	source_language = Language::C; +	source_language = ctor::language::c;  	for(const auto& source : config.sources)  	{  		std::filesystem::path sourceFile(source.file);  		// TODO: Use task languages instead  		if(sourceFile.extension().string() != ".c")  		{ -			source_language = Language::Cpp; +			source_language = ctor::language::cpp;  		}  	} + +	std::filesystem::create_directories(targetFile().parent_path());  }  bool TaskAR::dirtyInner()  { -	if(!std::filesystem::exists(targetFile)) +	if(!std::filesystem::exists(targetFile()))  	{  		return true;  	} @@ -67,15 +68,6 @@ bool TaskAR::dirtyInner()  		return true;  	} -	for(const auto& objectFile : objectFiles) -	{ -		if(std::filesystem::last_write_time(targetFile) <= -		   std::filesystem::last_write_time(objectFile)) -		{ -			return true; -		} -	} -  	{  		auto lastFlags = readFile(flagsFile.string());  		if(flagsString() != lastFlags) @@ -90,22 +82,20 @@ bool TaskAR::dirtyInner()  int TaskAR::runInner()  { -	std::string objectlist; -	for(const auto& objectFile : objectFiles) -	{ -		if(!objectlist.empty()) -		{ -			objectlist += " "; -		} -		objectlist += objectFile.string(); -	} +	auto toolchain = getToolChain(config.system);  	std::vector<std::string> args; -	args.push_back("rcs"); -	args.push_back(targetFile.string()); -	for(const auto& objectFile : objectFiles) +	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(objectFile.string()); +		args.push_back(task->targetFile().string());  	}  	{ // Write flags to file. @@ -115,29 +105,37 @@ int TaskAR::runInner()  	if(settings.verbose == 0)  	{ -		std::cout << "AR => " << targetFile.string() << "\n"; +		std::string output = "AR => " + targetFile().string() + '\n'; +		std::cout << output << std::flush;  	} +	const auto& c = ctor::get_configuration();  	std::string tool;  	switch(outputSystem())  	{ -	case OutputSystem::Host: -		tool = getConfiguration(cfg::host_ar, "/usr/bin/ar"); +	case ctor::output_system::host: +		tool = c.get(ctor::cfg::host_ar, "/usr/bin/ar");  		break; -	case OutputSystem::Build: -		tool = getConfiguration(cfg::build_ar, "/usr/bin/ar"); +	case ctor::output_system::build: +		tool = c.get(ctor::cfg::build_ar, "/usr/bin/ar");  		break;  	} -	return execute(tool, args, settings.verbose > 0); +	auto res = execute(settings, tool, args, c.env); +	if(res != 0) +	{ +		std::filesystem::remove(targetFile()); +	} + +	return res;  }  int TaskAR::clean()  { -	if(std::filesystem::exists(targetFile)) +	if(std::filesystem::exists(targetFile()))  	{ -		std::cout << "Removing " << targetFile.string() << "\n"; -		std::filesystem::remove(targetFile); +		std::cout << "Removing " << targetFile().string() << "\n"; +		std::filesystem::remove(targetFile());  	}  	if(std::filesystem::exists(flagsFile)) @@ -157,9 +155,9 @@ std::vector<std::string> TaskAR::depends() const  		deps.push_back(objectFile.string());  	} -	for(const auto& depFile : depFiles) +	for(const auto& dep : config.depends)  	{ -		deps.push_back(depFile.string()); +		deps.push_back(dep);  	}  	return deps; @@ -167,7 +165,12 @@ std::vector<std::string> TaskAR::depends() const  std::string TaskAR::target() const  { -	return targetFile.string(); +	return _targetFile.string(); +} + +std::filesystem::path TaskAR::targetFile() const +{ +	return std::filesystem::path(settings.builddir) / sourceDir / _targetFile;  }  bool TaskAR::derived() const @@ -177,16 +180,21 @@ bool TaskAR::derived() const  std::string TaskAR::flagsString() const  { +	auto toolchain = getToolChain(config.system);  	std::string flagsStr; +	bool first{true};  	for(const auto& flag : config.flags.ldflags)  	{ -		if(flag != config.flags.ldflags[0]) +		for(const auto& str : to_strings(toolchain, flag))  		{ -			flagsStr += " "; +			if(first) +			{ +				flagsStr += " "; +				first = false; +			} +			flagsStr += str;  		} -		flagsStr += flag;  	} -	flagsStr += "\n";  	for(const auto& dep : config.depends)  	{ diff --git a/src/task_ar.h b/src/task_ar.h index f540fef..601afeb 100644 --- a/src/task_ar.h +++ b/src/task_ar.h @@ -10,18 +10,16 @@  #include <future>  #include <filesystem> -struct BuildConfiguration; -struct Settings; -  class TaskAR  	: public Task  {  public: -	TaskAR(const BuildConfiguration& config, -	       const Settings& settings, +	TaskAR(const ctor::build_configuration& config, +	       const ctor::settings& settings,  	       const std::string& target,  	       const std::vector<std::string>& objects, -	       const std::string& sourcePath); +	       const std::string& sourceDir); +	virtual ~TaskAR() = default;  	bool dirtyInner() override; @@ -31,6 +29,8 @@ public:  	std::vector<std::string> depends() const override;  	std::string target() const override; +	std::filesystem::path targetFile() const override; +  	bool derived() const override;  private: @@ -38,9 +38,10 @@ private:  	std::vector<std::filesystem::path> objectFiles;  	std::vector<std::filesystem::path> depFiles; -	std::filesystem::path targetFile; +	std::filesystem::path _targetFile;  	std::filesystem::path flagsFile; -	const BuildConfiguration& config; -	const Settings& settings; +	const ctor::build_configuration& config; +	const ctor::settings& settings; +	std::string sourceDir;  }; diff --git a/src/task_cc.cc b/src/task_cc.cc index 1db9767..b9edcf7 100644 --- a/src/task_cc.cc +++ b/src/task_cc.cc @@ -6,89 +6,88 @@  #include <iostream>  #include <fstream>  #include <cassert> +#include <algorithm> -#include "libctor.h" -#include "settings.h" +#include "ctor.h"  #include "execute.h"  #include "util.h" - -namespace -{ -bool isClean(char c) -{ -	return c != '.' && c != '/'; -} - -std::string cleanUp(const std::string& path) -{ -	std::string cleaned; -	for(const auto& c : path) -	{ -		if(isClean(c)) -		{ -			cleaned += c; -		} -		else -		{ -			cleaned += '_'; -		} -	} -	return cleaned; -} -} - -TaskCC::TaskCC(const BuildConfiguration& config, const Settings& settings, -               const std::string& sourceDir, const Source& source) -	: Task(config) -	, config(config) -	, settings(settings) -	, sourceDir(sourceDir) +#include "tools.h" +#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 = settings.builddir; -	base /= sourceFile.parent_path(); -	std::filesystem::create_directories(base); +	std::filesystem::path base = sourceFile.parent_path();  	base /= cleanUp(config.target);  	base += "-";  	base += sourceFile.stem(); -	target_type = TargetType::Object; +	target_type = ctor::target_type::object; +	output_system = config.system;  	source_language = source.language; -	if(source_language == Language::Auto) +	if(source_language == ctor::language::automatic)  	{  		source_language = languageFromExtension(sourceFile);  	}  	switch(source_language)  	{ -	case Language::C: +	case ctor::language::c:  		base += "_c";  		break; -	case Language::Cpp: +	case ctor::language::cpp:  		base += "_cc";  		break; -	case Language::Asm: +	case ctor::language::assembler:  		base += "_asm";  		break; -	case Language::Auto: +	case ctor::language::automatic:  		assert(0 && "This should never happen");  		break;  	} -	targetFile = base; -	targetFile += ".o"; -	depsFile = base; +	if(source.output.empty()) +	{ +		_targetFile = base; +	} +	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 = base; +	flagsFile = targetFile().parent_path() / targetFile().stem();  	flagsFile += ".flags"; + +	std::filesystem::create_directories(targetFile().parent_path());  } -std::string TaskCC::name() const +int TaskCC::registerDepTasksInner(const std::vector<std::shared_ptr<Task>>& tasks)  { -	return target(); +	for(const auto& task : tasks) +	{ +		if(*task == _source.file) +		{ +			if(std::find(dependsTasks.begin(), dependsTasks.end(), task) == dependsTasks.end()) +			{ +				dependsTasks.push_back(task); +			} +		} +	} + +	return 0;  }  bool TaskCC::dirtyInner() @@ -99,7 +98,7 @@ bool TaskCC::dirtyInner()  		return true;  	} -	if(!std::filesystem::exists(targetFile)) +	if(!std::filesystem::exists(targetFile()))  	{  		//std::cout << "Missing targetFile\n";  		return true; @@ -133,11 +132,12 @@ 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) || -		   std::filesystem::last_write_time(targetFile) < +		   std::filesystem::last_write_time(targetFile()) <  		   std::filesystem::last_write_time(dep))  		{  			//std::cout << "The targetFile older than " << std::string(dep) << "\n"; @@ -146,7 +146,7 @@ bool TaskCC::dirtyInner()  	}  	if(std::filesystem::last_write_time(sourceFile) > -	   std::filesystem::last_write_time(targetFile)) +	   std::filesystem::last_write_time(targetFile()))  	{  		//std::cout << "The targetFile older than sourceFile\n";  		return true; @@ -172,20 +172,41 @@ int TaskCC::runInner()  	if(settings.verbose == 0)  	{ -		std::cout << compiler() << " " << -			sourceFile.lexically_normal().string() << " => " << -			targetFile.lexically_normal().string() << "\n"; +		std::string output; +		switch(sourceLanguage()) +		{ +		case ctor::language::c: +			output = "CC "; +			break; +		case ctor::language::cpp: +			output = "CXX "; +			break; +		case ctor::language::automatic: +		case ctor::language::assembler: +			// Only c/c++ handled by this task type. +			break; +		} +		output += sourceFile.lexically_normal().string() + " => " + +			targetFile().lexically_normal().string() + '\n'; +		std::cout << output << std::flush;  	} -	return execute(compiler(), args, settings.verbose > 0); +	const auto& cfg = ctor::get_configuration(); +	auto res = execute(settings, compiler(), args, cfg.env); +	if(res != 0) +	{ +		std::filesystem::remove(targetFile()); +	} + +	return res;  }  int TaskCC::clean()  { -	if(std::filesystem::exists(targetFile)) +	if(std::filesystem::exists(targetFile()))  	{ -		std::cout << "Removing " << targetFile.string() << "\n"; -		std::filesystem::remove(targetFile); +		std::cout << "Removing " << targetFile().string() << "\n"; +		std::filesystem::remove(targetFile());  	}  	if(std::filesystem::exists(depsFile)) @@ -210,7 +231,12 @@ std::vector<std::string> TaskCC::depends() const  std::string TaskCC::target() const  { -	return targetFile.string(); +	return _targetFile.string(); +} + +std::filesystem::path TaskCC::targetFile() const +{ +	return std::filesystem::path(settings.builddir) / _targetFile;  }  bool TaskCC::derived() const @@ -224,7 +250,7 @@ std::string TaskCC::toJSON() const  	json += "\t{\n";  	json += "\t\t\"directory\": \"" + sourceDir.string() + "\",\n";  	json += "\t\t\"file\": \"" + sourceFile.lexically_normal().string() + "\",\n"; -	json += "\t\t\"output\": \"" + targetFile.string() + "\",\n"; +	json += "\t\t\"output\": \"" + targetFile().string() + "\",\n";  	json += "\t\t\"arguments\": [ \"" + compiler() + "\"";  	auto args = getCompilerArgs();  	for(const auto& arg : args) @@ -243,17 +269,29 @@ std::string TaskCC::source() const  std::vector<std::string> TaskCC::flags() const  { +	std::vector<std::string> flags; +	auto toolchain = getToolChain(config.system); +  	switch(sourceLanguage())  	{ -	case Language::C: -		return config.flags.cflags; -	case Language::Cpp: -		return config.flags.cxxflags; +	case ctor::language::c: +		for(const auto& flag : config.flags.cflags) +		{ +			append(flags, to_strings(toolchain, flag)); +		} +		return flags; +	case ctor::language::cpp: +		for(const auto& flag : config.flags.cxxflags) +		{ +			append(flags, to_strings(toolchain, flag)); +		} +		return flags;  	default:  		std::cerr << "Unknown CC target type\n";  		exit(1);  		break;  	} +  }  std::string TaskCC::flagsString() const @@ -268,42 +306,100 @@ std::string TaskCC::flagsString() const  std::vector<std::string> TaskCC::getCompilerArgs() const  { +	auto toolchain = getToolChain(config.system);  	auto compiler_flags = flags();  	std::vector<std::string> args; -	args.push_back("-MMD"); -	if(std::filesystem::path(config.target).extension() == ".so") +	switch(sourceLanguage())  	{ -		// Add -fPIC arg to all contained object files -		args.push_back("-fPIC"); -	} +	case ctor::language::c: +		{ +			append(args, c_option(toolchain, ctor::c_opt::generate_dep_tree, depsFile.string())); -	args.push_back("-c"); -	args.push_back(sourceFile.string()); -	args.push_back("-o"); -	args.push_back(targetFile.string()); +			if(std::filesystem::path(config.target).extension() == ".so") +			{ +				// Add -fPIC arg to all contained object files +				append(args, c_option(toolchain, +				                      ctor::c_opt::position_independent_code)); +			} -	for(const auto& flag : compiler_flags) -	{ -		// Is arg an added include path? -		if(flag.substr(0, 2) == "-I") +			append(args, c_option(toolchain, ctor::c_opt::no_link)); +			args.push_back(sourceFile.string()); +			append(args, c_option(toolchain, +			                      ctor::c_opt::output, targetFile().string())); + +			// Relative include paths has to be altered to be relative to sourceDir +			for(const auto& flag : compiler_flags) +			{ +				auto option = c_option(flag, toolchain); +				switch(option.opt) +				{ +				case ctor::c_opt::include_path: +					{ +						std::filesystem::path path(option.arg); +						if(path.is_relative()) +						{ +							path = (sourceDir / path).lexically_normal(); +							append(args, c_option(toolchain, +							                      ctor::c_opt::include_path, path.string())); +						} +					} +					break; +				default: +					break; +				} + +				args.push_back(flag); +			} +		} +		break; + +	case ctor::language::cpp:  		{ -			std::string include_path = flag.substr(2); -			include_path.erase(0, include_path.find_first_not_of(' ')); -			std::filesystem::path path(include_path); +			append(args, cxx_option(toolchain, ctor::cxx_opt::generate_dep_tree, depsFile.string())); -			// Is it relative? -			if(path.is_relative()) +			if(std::filesystem::path(config.target).extension() == ".so")  			{ -				path = (sourceDir / path).lexically_normal(); -				std::string new_include_path = "-I" + path.string(); -				args.push_back(new_include_path); -				continue; +				// Add -fPIC arg to all contained object files +				append(args, cxx_option(toolchain, +				                        ctor::cxx_opt::position_independent_code));  			} + +			append(args, cxx_option(toolchain, ctor::cxx_opt::no_link)); +			args.push_back(sourceFile.string()); +			append(args, cxx_option(toolchain, +			                        ctor::cxx_opt::output, targetFile().string())); + +			// Relative include paths has to be altered to be relative to sourceDir +			for(const auto& flag : compiler_flags) +			{ +				auto option = cxx_option(flag, toolchain); +				switch(option.opt) +				{ +				case ctor::cxx_opt::include_path: +					{ +						std::filesystem::path path(option.arg); +						if(path.is_relative()) +						{ +							path = (sourceDir / path).lexically_normal(); +							append(args, cxx_option(toolchain, +							                        ctor::cxx_opt::include_path, path.string())); +						} +					} +					break; +				default: +					break; +				} + +				args.push_back(flag); +			} +  		} +		break; -		args.push_back(flag); +	default: +		break;  	}  	return args; diff --git a/src/task_cc.h b/src/task_cc.h index c309abd..2299fcd 100644 --- a/src/task_cc.h +++ b/src/task_cc.h @@ -10,18 +10,17 @@  #include <future>  #include <filesystem> -struct BuildConfiguration; -struct Settings; -  class TaskCC  	: public Task  {  public: -	TaskCC(const BuildConfiguration& config, -	       const Settings& settings, -	       const std::string& sourceDir, const Source& source); +	TaskCC(const ctor::build_configuration& config, +	       const ctor::settings& settings, +	       const std::string& sourceDir, const ctor::source& source); +	virtual ~TaskCC() = default; + +	int registerDepTasksInner(const std::vector<std::shared_ptr<Task>>& tasks) override; -	std::string name() const override;  	bool dirtyInner() override;  	int runInner() override; @@ -30,6 +29,8 @@ public:  	std::vector<std::string> depends() const override;  	std::string target() const override; +	std::filesystem::path targetFile() const override; +  	bool derived() const override;  	std::string toJSON() const override; @@ -42,11 +43,12 @@ protected:  	std::vector<std::string> getCompilerArgs() const;  	std::filesystem::path sourceFile; -	std::filesystem::path targetFile; +	std::filesystem::path _targetFile;  	std::filesystem::path depsFile;  	std::filesystem::path flagsFile; -	const BuildConfiguration& config; -	const Settings& settings; +	const ctor::build_configuration& config; +	const ctor::settings& settings;  	std::filesystem::path sourceDir; +	const ctor::source& _source;  }; diff --git a/src/task_fn.cc b/src/task_fn.cc new file mode 100644 index 0000000..b6b50ea --- /dev/null +++ b/src/task_fn.cc @@ -0,0 +1,128 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#include "task_fn.h" + +#include <iostream> +#include <fstream> +#include <cassert> + +#include "ctor.h" +#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_) +	, sourceFile(sourceDir_) +	, config(config_) +	, settings(settings_) +{ +	sourceFile /= source.file; + +	std::filesystem::create_directories(std::filesystem::path(settings.builddir) / sourceFile.parent_path()); + +	target_type = config.type; +	source_language = source.language; + +	if(source.output.empty()) +	{ +		std::cerr << "Missing output file for functional target\n"; +		exit(1); +	} + +	_targetFile = source.output; +} + +bool TaskFn::dirtyInner() +{ +	if(!std::filesystem::exists(sourceFile)) +	{ +		//std::cout << "Missing source file: " << std::string(sourceFile) << "\n"; +		return true; +	} + +	if(!std::filesystem::exists(targetFile())) +	{ +		//std::cout << "Missing targetFile\n"; +		return true; +	} + +	if(std::filesystem::last_write_time(sourceFile) > +	   std::filesystem::last_write_time(targetFile())) +	{ +		//std::cout << "The targetFile older than sourceFile\n"; +		return true; +	} + +	return false; +} + +int TaskFn::runInner() +{ +	if(!std::filesystem::exists(sourceFile)) +	{ +		std::cout << "Missing source file: " << sourceFile.string() << "\n"; +		return 1; +	} + +	if(settings.verbose >= 0) +	{ +		std::string output = "Fn " + +			sourceFile.lexically_normal().string() + " => " + +			targetFile().lexically_normal().string() + '\n'; +		std::cout << output << std::flush; +	} + +	auto res = config.function(sourceFile.string(), +	                           targetFile().string(), +	                           config, +	                           settings); +	if(res != 0) +	{ +		std::filesystem::remove(targetFile()); +	} + +	return res; +} + +int TaskFn::clean() +{ +	if(std::filesystem::exists(targetFile())) +	{ +		std::cout << "Removing " << targetFile().string() << "\n"; +		std::filesystem::remove(targetFile()); +	} + +	return 0; +} + +std::vector<std::string> TaskFn::depends() const +{ +	return {}; +} + +std::string TaskFn::target() const +{ +	return _targetFile.string(); +} + +std::filesystem::path TaskFn::targetFile() const +{ +	return std::filesystem::path(settings.builddir) / sourceDir / _targetFile; +} + +bool TaskFn::derived() const +{ +	return false; +} + +std::string TaskFn::toJSON() const +{ +	return {}; // TODO: Not sure how to express this... +} + +std::string TaskFn::source() const +{ +	return sourceFile.string(); +} diff --git a/src/task_fn.h b/src/task_fn.h new file mode 100644 index 0000000..1bad32c --- /dev/null +++ b/src/task_fn.h @@ -0,0 +1,44 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include "task.h" + +#include <vector> +#include <string> +#include <future> +#include <filesystem> + +class TaskFn +	: public Task +{ +public: +	TaskFn(const ctor::build_configuration& config, +	       const ctor::settings& settings, +	       const std::string& sourceDir, const ctor::source& source); +	virtual ~TaskFn() = default; + +	bool dirtyInner() override; + +	int runInner() override; +	int clean() override; + +	std::vector<std::string> depends() const override; + +	std::string target() const override; +	std::filesystem::path targetFile() const override; +	bool derived() const override; + +	std::string toJSON() const override; + +	std::string source() const override; + +protected: +	std::filesystem::path sourceFile; +	std::filesystem::path _targetFile; + +	const ctor::build_configuration& config; +	const ctor::settings& settings; +	std::filesystem::path sourceDir; +}; diff --git a/src/task_ld.cc b/src/task_ld.cc index 82c26a9..69c3a8a 100644 --- a/src/task_ld.cc +++ b/src/task_ld.cc @@ -6,46 +6,34 @@  #include <iostream>  #include <fstream> -#include "libctor.h" -#include "settings.h" +#include "ctor.h"  #include "execute.h" +#include "util.h" +#include "tools.h" -namespace -{ -std::string readFile(const std::string &fileName) -{ -	std::ifstream ifs(fileName.c_str(), std::ios::in | std::ios::binary | std::ios::ate); - -	std::ifstream::pos_type fileSize = ifs.tellg(); -	ifs.seekg(0, std::ios::beg); - -	std::vector<char> bytes(fileSize); -	ifs.read(bytes.data(), fileSize); - -	return std::string(bytes.data(), fileSize); -} -} // namespace :: - -TaskLD::TaskLD(const BuildConfiguration& config, -               const Settings& settings, +TaskLD::TaskLD(const ctor::build_configuration& config_, +               const ctor::settings& settings_,                 const std::string& target,                 const std::vector<std::string>& objects, -               const std::string& sourcePath) -	: Task(config) -	, config(config) -	, settings(settings) +               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; -	if(target_type == TargetType::Auto) +	output_system = config.system; + +	if(target_type == ctor::target_type::automatic)  	{ -		target_type = TargetType::Executable; +		target_type = ctor::target_type::executable;  	} -	std::filesystem::path base = settings.builddir; -	base /= sourcePath; -	std::filesystem::create_directories(base); - -	targetFile = base / target; +	_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; @@ -55,28 +43,28 @@ TaskLD::TaskLD(const BuildConfiguration& config,  	for(const auto& dep : config.depends)  	{ -		std::filesystem::path depFile = settings.builddir; -		depFile /= dep; -		depFiles.push_back(depFile); +		depFiles.emplace_back(dep);  	} -	flagsFile = base / targetFile.stem(); +	flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();  	flagsFile += ".flags"; -	source_language = Language::C; +	source_language = ctor::language::c;  	for(const auto& source : config.sources)  	{  		std::filesystem::path sourceFile(source.file);  		if(sourceFile.extension().string() != ".c")  		{ -			source_language = Language::Cpp; +			source_language = ctor::language::cpp;  		}  	} + +	std::filesystem::create_directories(targetFile().parent_path());  }  bool TaskLD::dirtyInner()  { -	if(!std::filesystem::exists(targetFile)) +	if(!std::filesystem::exists(targetFile()))  	{  		return true;  	} @@ -86,15 +74,6 @@ bool TaskLD::dirtyInner()  		return true;  	} -	for(const auto& objectFile : objectFiles) -	{ -		if(std::filesystem::last_write_time(targetFile) <= -		   std::filesystem::last_write_time(objectFile)) -		{ -			return true; -		} -	} -  	{  		auto lastFlags = readFile(flagsFile.string());  		if(flagsString() != lastFlags) @@ -109,31 +88,22 @@ bool TaskLD::dirtyInner()  int TaskLD::runInner()  { -	std::string objectlist; -	for(const auto& objectFile : objectFiles) -	{ -		if(!objectlist.empty()) -		{ -			objectlist += " "; -		} -		objectlist += objectFile.string(); -	} +	auto toolchain = getToolChain(config.system);  	std::vector<std::string> args; -	for(const auto& objectFile : objectFiles) -	{ -		args.push_back(objectFile.string()); -	} - -	for(const auto& depFile : depFiles) +	for(const auto& dep : getDependsTasks())  	{ -		if(depFile.extension() == ".so") +		auto depFile = dep->targetFile(); +		auto dep_type = target_type_from_extension(toolchain, depFile); +		if(dep_type == ctor::target_type::dynamic_library)  		{ -			args.push_back(std::string("-L") + settings.builddir); +			append(args, ld_option(toolchain, ctor::ld_opt::library_path, +			                       targetFile().parent_path().string()));  			auto lib = depFile.stem().string().substr(3); // strip 'lib' prefix -			args.push_back(std::string("-l") + lib); +			append(args, ld_option(toolchain, ctor::ld_opt::link, lib));  		} -		else if(depFile.extension() == ".a") +		else if(dep_type == ctor::target_type::static_library || +		        dep_type == ctor::target_type::object)  		{  			args.push_back(depFile.string());  		} @@ -141,10 +111,10 @@ int TaskLD::runInner()  	for(const auto& flag : config.flags.ldflags)  	{ -		args.push_back(flag); +		append(args, to_strings(toolchain, flag));  	} -	args.push_back("-o"); -	args.push_back(targetFile.string()); + +	append(args, ld_option(toolchain, ctor::ld_opt::output, targetFile().string()));  	{ // Write flags to file.  		std::ofstream flagsStream(flagsFile); @@ -153,19 +123,27 @@ int TaskLD::runInner()  	if(settings.verbose == 0)  	{ -		std::cout << "LD => " << targetFile.string() << "\n"; +		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()  { -	if(std::filesystem::exists(targetFile)) +	if(std::filesystem::exists(targetFile()))  	{ -		std::cout << "Removing " << targetFile.string() << "\n"; -		std::filesystem::remove(targetFile); +		std::cout << "Removing " << targetFile().string() << "\n"; +		std::filesystem::remove(targetFile());  	}  	if(std::filesystem::exists(flagsFile)) @@ -195,7 +173,12 @@ std::vector<std::string> TaskLD::depends() const  std::string TaskLD::target() const  { -	return targetFile.string(); +	return _targetFile.string(); +} + +std::filesystem::path TaskLD::targetFile() const +{ +	return std::filesystem::path(settings.builddir) / sourceDir / _targetFile;  }  bool TaskLD::derived() const @@ -205,16 +188,21 @@ bool TaskLD::derived() const  std::string TaskLD::flagsString() const  { +	auto toolchain = getToolChain(config.system);  	std::string flagsStr; +	bool first{true};  	for(const auto& flag : config.flags.ldflags)  	{ -		if(flag != config.flags.ldflags[0]) +		for(const auto& str : to_strings(toolchain, flag))  		{ -			flagsStr += " "; +			if(first) +			{ +				flagsStr += " "; +				first = false; +			} +			flagsStr += str;  		} -		flagsStr += flag;  	} -	flagsStr += "\n";  	for(const auto& dep : config.depends)  	{ diff --git a/src/task_ld.h b/src/task_ld.h index 516641f..c0e3ebb 100644 --- a/src/task_ld.h +++ b/src/task_ld.h @@ -10,18 +10,17 @@  #include <future>  #include <filesystem> -struct BuildConfiguration; -struct Settings; -  class TaskLD  	: public Task  {  public: -	TaskLD(const BuildConfiguration& config, -	       const Settings& settings, +	TaskLD(const ctor::build_configuration& config, +	       const ctor::settings& settings,  	       const std::string& target,  	       const std::vector<std::string>& objects, -	       const std::string& sourcePath); +	       const std::string& _sourceDir, +	       bool is_self); +	virtual ~TaskLD() = default;  	bool dirtyInner() override; @@ -31,6 +30,8 @@ public:  	std::vector<std::string> depends() const override;  	std::string target() const override; +	std::filesystem::path targetFile() const override; +  	bool derived() const override;  private: @@ -38,9 +39,11 @@ private:  	std::vector<std::filesystem::path> objectFiles;  	std::vector<std::filesystem::path> depFiles; -	std::filesystem::path targetFile; +	std::filesystem::path _targetFile;  	std::filesystem::path flagsFile; -	const BuildConfiguration& config; -	const Settings& settings; +	const ctor::build_configuration& config; +	const ctor::settings& settings; +	std::string sourceDir; +	bool is_self;  }; diff --git a/src/task_so.cc b/src/task_so.cc index bf47e05..c98e4a7 100644 --- a/src/task_so.cc +++ b/src/task_so.cc @@ -6,25 +6,29 @@  #include <iostream>  #include <fstream> -#include "libctor.h" -#include "settings.h" +#include "ctor.h"  #include "execute.h"  #include "util.h" +#include "tools.h" -TaskSO::TaskSO(const BuildConfiguration& config, -               const Settings& settings, +TaskSO::TaskSO(const ctor::build_configuration& config_, +               const ctor::settings& settings_,                 const std::string& target,                 const std::vector<std::string>& objects, -               const std::string& sourcePath) -	: Task(config) -	, config(config) -	, settings(settings) +               const std::string& sourceDir_) +	: Task(config_, settings_, sourceDir_) +	, config(config_) +	, settings(settings_) +	, sourceDir(sourceDir_)  { -	std::filesystem::path base = settings.builddir; -	base /= sourcePath; -	std::filesystem::create_directories(base); +	std::filesystem::path base = sourceDir; -	targetFile = base / target; +	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,30 +38,29 @@ TaskSO::TaskSO(const BuildConfiguration& config,  	for(const auto& dep : config.depends)  	{ -		std::filesystem::path depFile = settings.builddir; -		depFile /= dep; -		depFiles.push_back(depFile); +		depFiles.emplace_back(dep);  	} -	flagsFile = base / targetFile.stem(); +	flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();  	flagsFile += ".flags"; -	target_type = TargetType::DynamicLibrary; -	source_language = Language::C; +	source_language = ctor::language::c;  	for(const auto& source : config.sources)  	{  		std::filesystem::path sourceFile(source.file);  		// TODO: Use task languages instead  		if(sourceFile.extension().string() != ".c")  		{ -			source_language = Language::Cpp; +			source_language = ctor::language::cpp;  		}  	} + +	std::filesystem::create_directories(targetFile().parent_path());  }  bool TaskSO::dirtyInner()  { -	if(!std::filesystem::exists(targetFile)) +	if(!std::filesystem::exists(targetFile()))  	{  		return true;  	} @@ -67,15 +70,6 @@ bool TaskSO::dirtyInner()  		return true;  	} -	for(const auto& objectFile : objectFiles) -	{ -		if(std::filesystem::last_write_time(targetFile) <= -		   std::filesystem::last_write_time(objectFile)) -		{ -			return true; -		} -	} -  	{  		auto lastFlags = readFile(flagsFile.string());  		if(flagsString() != lastFlags) @@ -90,37 +84,23 @@ bool TaskSO::dirtyInner()  int TaskSO::runInner()  { -	std::string objectlist; -	for(const auto& objectFile : objectFiles) -	{ -		if(!objectlist.empty()) -		{ -			objectlist += " "; -		} -		objectlist += objectFile.string(); -	} +	auto toolchain = getToolChain(config.system);  	std::vector<std::string> args; -	args.push_back("-fPIC"); -	args.push_back("-shared"); +	append(args, ld_option(toolchain, ctor::ld_opt::position_independent_code)); +	append(args, ld_option(toolchain, ctor::ld_opt::build_shared)); -	args.push_back("-o"); -	args.push_back(targetFile.string()); +	append(args, ld_option(toolchain, ctor::ld_opt::output, targetFile().string())); -	for(const auto& objectFile : objectFiles) +	for(const auto& task : getDependsTasks())  	{ -		args.push_back(objectFile.string()); -	} - -	for(const auto& depFile : depFiles) -	{ -		args.push_back(depFile.string()); +		args.push_back(task->targetFile().string());  	}  	for(const auto& flag : config.flags.ldflags)  	{ -		args.push_back(flag); +		append(args, to_strings(toolchain, flag));  	}  	{ // Write flags to file. @@ -130,19 +110,27 @@ int TaskSO::runInner()  	if(settings.verbose == 0)  	{ -		std::cout << "LD => " << targetFile.string() << "\n"; +		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()  { -	if(std::filesystem::exists(targetFile)) +	if(std::filesystem::exists(targetFile()))  	{ -		std::cout << "Removing " << targetFile.string() << "\n"; -		std::filesystem::remove(targetFile); +		std::cout << "Removing " << targetFile().string() << "\n"; +		std::filesystem::remove(targetFile());  	}  	if(std::filesystem::exists(flagsFile)) @@ -172,7 +160,12 @@ std::vector<std::string> TaskSO::depends() const  std::string TaskSO::target() const  { -	return targetFile.string(); +	return _targetFile.string(); +} + +std::filesystem::path TaskSO::targetFile() const +{ +	return std::filesystem::path(settings.builddir) / sourceDir / _targetFile;  }  bool TaskSO::derived() const @@ -182,12 +175,21 @@ bool TaskSO::derived() const  std::string TaskSO::flagsString() const  { -	std::string flagsStr = compiler(); +	auto toolchain = getToolChain(config.system); +	std::string flagsStr; +	bool first{true};  	for(const auto& flag : config.flags.ldflags)  	{ -		flagsStr += " " + flag; +		for(const auto& str : to_strings(toolchain, flag)) +		{ +			if(first) +			{ +				flagsStr += " "; +				first = false; +			} +			flagsStr += str; +		}  	} -	flagsStr += "\n";  	for(const auto& dep : config.depends)  	{ diff --git a/src/task_so.h b/src/task_so.h index a249421..60af225 100644 --- a/src/task_so.h +++ b/src/task_so.h @@ -10,18 +10,16 @@  #include <future>  #include <filesystem> -struct BuildConfiguration; -struct Settings; -  class TaskSO  	: public Task  {  public: -	TaskSO(const BuildConfiguration& config, -	       const Settings& settings, +	TaskSO(const ctor::build_configuration& config, +	       const ctor::settings& settings,  	       const std::string& target,  	       const std::vector<std::string>& objects, -	       const std::string& sourcePath); +	       const std::string& sourceDir); +	virtual ~TaskSO() = default;  	bool dirtyInner() override; @@ -31,6 +29,8 @@ public:  	std::vector<std::string> depends() const override;  	std::string target() const override; +	std::filesystem::path targetFile() const override; +  	bool derived() const override;  private: @@ -38,9 +38,10 @@ private:  	std::vector<std::filesystem::path> objectFiles;  	std::vector<std::filesystem::path> depFiles; -	std::filesystem::path targetFile; +	std::filesystem::path _targetFile;  	std::filesystem::path flagsFile; -	const BuildConfiguration& config; -	const Settings& settings; +	const ctor::build_configuration& config; +	const ctor::settings& settings; +	std::string sourceDir;  }; diff --git a/src/tasks.cc b/src/tasks.cc index 8ab296f..2f9e47a 100644 --- a/src/tasks.cc +++ b/src/tasks.cc @@ -5,36 +5,42 @@  #include <filesystem>  #include <deque> +#include <list>  #include <iostream>  #include <algorithm> +#include <span> -#include "settings.h" -#include "libctor.h" +#include "ctor.h"  #include "task.h"  #include "task_cc.h"  #include "task_ld.h"  #include "task_ar.h"  #include "task_so.h" +#include "task_fn.h"  #include "rebuild.h"  #include "configure.h" +#include "util.h" +#include "tools.h" -const std::deque<Target>& getTargets(const Settings& settings, +const std::deque<Target>& getTargets(const ctor::settings& settings,                                       bool resolve_externals)  { +	auto config_files = std::span(configFiles).subspan(0, numConfigFiles); +  	static bool initialised{false};  	static std::deque<Target> targets;  	if(!initialised)  	{ -		const auto& externals = configuration().externals; -		for(std::size_t i = 0; i < numConfigFiles; ++i) +		const auto& externals = ctor::get_configuration().externals; +		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(); +			auto configs = config_file.cb(settings);  			for(auto& config : configs)  			{  				if(resolve_externals) @@ -49,21 +55,12 @@ const std::deque<Target>& getTargets(const Settings& settings,  							exit(1);  						}  						const auto& flags = externals.at(external); -						config.flags.cflags.insert(config.flags.cflags.end(), -						                           flags.cflags.begin(), -						                           flags.cflags.end()); -						config.flags.cxxflags.insert(config.flags.cxxflags.end(), -						                             flags.cxxflags.begin(), -						                             flags.cxxflags.end()); -						config.flags.ldflags.insert(config.flags.ldflags.end(), -						                            flags.ldflags.begin(), -						                            flags.ldflags.end()); -						config.flags.asmflags.insert(config.flags.asmflags.end(), -						                             flags.asmflags.begin(), -						                             flags.asmflags.end()); -						//config.libs.insert(config.libs.end(), -						//                   libs.begin(), -						//                   libs.end()); +						append(config.flags.cflags, flags.cflags); +						append(config.flags.cxxflags, flags.cxxflags); +						append(config.flags.ldflags, flags.ldflags); +						append(config.flags.arflags, flags.arflags); +						append(config.flags.asmflags, flags.asmflags); +						//append(config.libs.insert(config.libs libs);  					}  				} @@ -76,73 +73,93 @@ const std::deque<Target>& getTargets(const Settings& settings,  	return targets;  } -std::list<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config, -                                             const 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::vector<std::shared_ptr<Task>> tasks; +  	std::filesystem::path targetFile(config.target); -	TargetType target_type{config.type}; -	if(target_type == TargetType::Auto) +	ctor::target_type target_type{config.type}; +	if(target_type == ctor::target_type::automatic)  	{ -		if(targetFile.extension() == ".a") -		{ -			target_type = TargetType::StaticLibrary; -		} -		else if(targetFile.extension() == ".so") -		{ -			target_type = TargetType::DynamicLibrary; -		} -		else if(targetFile.extension() == "") +		if(config.function != nullptr)  		{ -			target_type = TargetType::Executable; +			target_type = ctor::target_type::function;  		}  		else  		{ -			std::cerr << "Could not deduce target type from target " << -				targetFile.string() << " please specify.\n"; -			exit(1); +			target_type = target_type_from_extension(ctor::toolchain::any, targetFile);  		}  	} +	const auto& c = ctor::get_configuration();  	std::vector<std::string> objects; -	std::list<std::shared_ptr<Task>> tasks; -	for(const auto& file : config.sources) +	if(target_type != ctor::target_type::function)  	{ -		tasks.emplace_back(std::make_shared<TaskCC>(config, settings, -		                                            sourceDir, file)); -		objects.push_back(tasks.back()->target()); +		for(const auto& source : config.sources) +		{ +			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 +	else +	{ +		for(const auto& file : config.sources) +		{ +			auto task = std::make_shared<TaskFn>(config, settings, sourceDir, file); +			tasks.push_back(task); +			objects.push_back(task->target()); +		} +	} +#endif  	switch(target_type)  	{ -	case TargetType::Auto: +	case ctor::target_type::automatic:  		// The target_type cannot be Auto  		break; -	case TargetType::StaticLibrary: -		tasks.emplace_back(std::make_shared<TaskAR>(config, settings, config.target, -		                                            objects, sourceDir)); +	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.push_back(std::make_shared<TaskAR>(config, settings, config.target, +		                                         objects, sourceDir));  		break;  #ifndef BOOTSTRAP -	case TargetType::DynamicLibrary: +	case ctor::target_type::dynamic_library:  		// TODO: Use C++20 starts_with  		if(targetFile.stem().string().substr(0, 3) != "lib")  		{  			std::cerr << "Dynamic library target must have 'lib' prefix\n";  			exit(1);  		} -		tasks.emplace_back(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 TargetType::Executable: -	case TargetType::UnitTest: -		tasks.emplace_back(std::make_shared<TaskLD>(config, settings, config.target, -		                                            objects, sourceDir)); +	case ctor::target_type::executable: +	case ctor::target_type::unit_test: +		tasks.push_back(std::make_shared<TaskLD>(config, settings, config.target, +		                                         objects, sourceDir, is_self));  		break; -	case TargetType::Object: +	case ctor::target_type::object: +	case ctor::target_type::function:  		break;  #else  	default: @@ -153,18 +170,19 @@ std::list<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config,  	return tasks;  } -std::shared_ptr<Task> getNextTask(const std::list<std::shared_ptr<Task>>& allTasks, -                                  std::list<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;  		}  	} @@ -172,19 +190,19 @@ std::shared_ptr<Task> getNextTask(const std::list<std::shared_ptr<Task>>& allTas  	return nullptr;  } -std::list<std::shared_ptr<Task>> getTasks(const 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::list<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); +			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 f2a77d4..6573784 100644 --- a/src/tasks.h +++ b/src/tasks.h @@ -4,40 +4,37 @@  #pragma once  #include <string> -#include <list>  #include <memory>  #include <deque>  #include "task.h" -class BuildConfiguration; -class Settings; -  struct Target  { -	BuildConfiguration config; +	ctor::build_configuration config;  	std::string path;  };  //! Get list of all registered targets -const std::deque<Target>& getTargets(const Settings& settings, +const std::deque<Target>& getTargets(const ctor::settings& settings,                                       bool resolve_externals = true);  //! Returns next dirty task from the dirtyTasks list that has all its dependencies  //! 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::list<std::shared_ptr<Task>>& allTasks, -                                  std::list<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::list<std::shared_ptr<Task>> getTasks(const 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::list<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config, -                                             const 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 new file mode 100644 index 0000000..8c9b7e2 --- /dev/null +++ b/src/tools.cc @@ -0,0 +1,1123 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#include "tools.h" + +#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) +{ +	// Adding to this enum should also imply adding to the unit-tests below +	switch(opt) +	{ +	case ctor::c_opt::output: stream << "ctor::c_opt::output"; break; +	case ctor::c_opt::debug: stream << "ctor::c_opt::debug"; break; +	case ctor::c_opt::warn_all: stream << "ctor::c_opt::warn_all"; break; +	case ctor::c_opt::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; +	case ctor::c_opt::include_path: stream << "ctor::c_opt::include_path"; break; +	case ctor::c_opt::c_std: stream << "ctor::c_opt::c_std"; break; +	case ctor::c_opt::optimization: stream << "ctor::c_opt::optimization"; break; +	case ctor::c_opt::position_independent_code: stream << "ctor::c_opt::position_independent_code"; break; +	case ctor::c_opt::position_independent_executable: stream << "ctor::c_opt::position_independent_executable"; break; +	case ctor::c_opt::define: stream << "ctor::c_opt::define"; break; +	case ctor::c_opt::custom: stream << "ctor::c_opt::custom"; break; +	} + +	return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::cxx_opt& opt) +{ +	// Adding to this enum should also imply adding to the unit-tests below +	switch(opt) +	{ +	case ctor::cxx_opt::output: stream << "ctor::cxx_opt::output"; break; +	case ctor::cxx_opt::debug: stream << "ctor::cxx_opt::debug"; break; +	case ctor::cxx_opt::warn_all: stream << "ctor::cxx_opt::warn_all"; break; +	case ctor::cxx_opt::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; +	case ctor::cxx_opt::include_path: stream << "ctor::cxx_opt::include_path"; break; +	case ctor::cxx_opt::cpp_std: stream << "ctor::cxx_opt::cpp_std"; break; +	case ctor::cxx_opt::optimization: stream << "ctor::cxx_opt::optimization"; break; +	case ctor::cxx_opt::position_independent_code: stream << "ctor::cxx_opt::position_independent_code"; break; +	case ctor::cxx_opt::position_independent_executable: stream << "ctor::cxx_opt::position_independent_executable"; break; +	case ctor::cxx_opt::define: stream << "ctor::cxx_opt::define"; break; +	case ctor::cxx_opt::custom: stream << "ctor::cxx_opt::custom"; break; +	} + +	return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::ld_opt& opt) +{ +	// Adding to this enum should also imply adding to the unit-tests below +	switch(opt) +	{ +	case ctor::ld_opt::output: stream << "ctor::ld_opt::output"; break; +	case ctor::ld_opt::strip: stream << "ctor::ld_opt::strip"; break; +	case ctor::ld_opt::warn_all: stream << "ctor::ld_opt::warn_all"; break; +	case ctor::ld_opt::warnings_as_errors: stream << "ctor::ld_opt::warnings_as_errors"; break; +	case ctor::ld_opt::library_path: stream << "ctor::ld_opt::library_path"; break; +	case ctor::ld_opt::link: stream << "ctor::ld_opt::link"; break; +	case ctor::ld_opt::cpp_std: stream << "ctor::ld_opt::cpp_std"; break; +	case ctor::ld_opt::build_shared: stream << "ctor::ld_opt::build_shared"; break; +	case ctor::ld_opt::threads: stream << "ctor::ld_opt::threads"; break; +	case ctor::ld_opt::position_independent_code: stream << "ctor::ld_opt::position_independent_code"; break; +	case ctor::ld_opt::position_independent_executable: stream << "ctor::ld_opt::position_independent_executable"; break; +	case ctor::ld_opt::custom: stream << "ctor::ld_opt::custom"; break; +	} + +	return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::ar_opt& opt) +{ +	// Adding to this enum should also imply adding to the unit-tests below +	switch(opt) +	{ +	case ctor::ar_opt::replace: stream << "ctor::ar_opt::replace"; break; +	case ctor::ar_opt::add_index: stream << "ctor::ar_opt::add_index"; break; +	case ctor::ar_opt::create: stream << "ctor::ar_opt::create"; break; +	case ctor::ar_opt::output: stream << "ctor::ar_opt::output"; break; +	case ctor::ar_opt::custom: stream << "ctor::ar_opt::custom"; break; +	} + +	return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::asm_opt& opt) +{ +	// Adding to this enum should also imply adding to the unit-tests below +	switch(opt) +	{ +	case ctor::asm_opt::custom: stream << "ctor::asm_opt::custom"; break; +	} + +	return stream; +} + +ctor::toolchain getToolChain(const std::string& compiler) +{ +	std::filesystem::path cc(compiler); +	auto cc_cmd = cc.stem().string(); + +	// Note: "g++" is a substring of "clang++" so "clang++" must be tested first. +	if(cc_cmd.find("clang++") != std::string::npos || +	   cc_cmd.find("clang") != std::string::npos) +	{ +		return ctor::toolchain::clang; +	} +	else if(cc_cmd.find("g++") != std::string::npos || +	        cc_cmd.find("gcc") != std::string::npos || +	        cc_cmd.find("mingw") != std::string::npos) +	{ +		return ctor::toolchain::gcc; +	} + +	std::cerr << "Unsupported output system.\n"; +	return ctor::toolchain::gcc; +} + +ctor::toolchain getToolChain(ctor::output_system system) +{ +	const auto& cfg = ctor::get_configuration(); +	if(system == ctor::output_system::host) +	{ +		if(cfg.host_toolchain != ctor::toolchain::none) +		{ +			return cfg.host_toolchain; +		} +		return getToolChain(cfg.get(ctor::cfg::host_cxx, "g++")); +	} +	else +	{ +		if(cfg.build_toolchain != ctor::toolchain::none) +		{ +			return cfg.build_toolchain; +		} +		return getToolChain(cfg.get(ctor::cfg::build_cxx, "g++")); +	} +} + +namespace gcc { +std::string get_arch(ctor::output_system system) +{ +	std::string cmd; + +	const auto& c = ctor::get_configuration(); +	switch(system) +	{ +	case ctor::output_system::host: +		cmd = c.get(ctor::cfg::host_cxx, "/usr/bin/g++"); +		break; +	case ctor::output_system::build: +		cmd = c.get(ctor::cfg::build_cxx, "/usr/bin/g++"); +		break; +	} + +	cmd += " -v"; +	cmd += " 2>&1"; +	auto pipe = popen(cmd.data(), "r"); +	if(!pipe) +	{ +		std::cerr << "Could not run compiler " << cmd << "\n"; +		return {};//ctor::arch::unknown; +	} + +	std::string arch; +	while(!feof(pipe)) +	{ +		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.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.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.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 }; +} + +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) +	{ +	case ctor::cxx_opt::output: +		return {"-o", arg}; +	case ctor::cxx_opt::debug: +		return {"-g"}; +	case ctor::cxx_opt::warn_all: +		return {"-Wall"}; +	case ctor::cxx_opt::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: +		return {"-MMD"}; +	case ctor::cxx_opt::no_link: +		return {"-c"}; +	case ctor::cxx_opt::include_path: +		return {"-I" + arg}; +	case ctor::cxx_opt::cpp_std: +		return {"-std=" + arg}; +	case ctor::cxx_opt::optimization: +		return {"-O" + arg}; +	case ctor::cxx_opt::position_independent_code: +		return {"-fPIC"}; +	case ctor::cxx_opt::position_independent_executable: +		return {"-fPIE"}; +	case ctor::cxx_opt::define: +		if(!arg2.empty()) +		{ +			return {"-D" + arg + "=" + arg2}; +		} +		return {"-D" + arg}; +	case ctor::cxx_opt::custom: +		return {arg}; +	} + +	std::cerr << "Unsupported compiler option.\n"; +	return {}; +} + +std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg, +                                  const std::string& arg2) +{ +	switch(opt) +	{ +	case ctor::c_opt::output: +		return {"-o", arg}; +	case ctor::c_opt::debug: +		return {"-g"}; +	case ctor::c_opt::warn_all: +		return {"-Wall"}; +	case ctor::c_opt::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: +		return {"-MMD"}; +	case ctor::c_opt::no_link: +		return {"-c"}; +	case ctor::c_opt::include_path: +		return {"-I" + arg}; +	case ctor::c_opt::c_std: +		return {"-std=" + arg}; +	case ctor::c_opt::optimization: +		return {"-O" + arg}; +	case ctor::c_opt::position_independent_code: +		return {"-fPIC"}; +	case ctor::c_opt::position_independent_executable: +		return {"-fPIE"}; +	case ctor::c_opt::define: +		if(!arg2.empty()) +		{ +			return {"-D" + arg + "=" + arg2}; +		} +		return {"-D" + arg}; +	case ctor::c_opt::custom: +		return {arg}; +	} + +	std::cerr << "Unsupported compiler option.\n"; +	return {}; +} + +std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg, +                                   [[maybe_unused]]const std::string& arg2) +{ +	switch(opt) +	{ +	case ctor::ld_opt::output: +		return {"-o", arg}; +	case ctor::ld_opt::strip: +		return {"-s"}; +	case ctor::ld_opt::warn_all: +		return {"-Wall"}; +	case ctor::ld_opt::warnings_as_errors: +		return {"-Werror"}; +	case ctor::ld_opt::library_path: +		return {"-L" + arg}; +	case ctor::ld_opt::link: +		return {"-l" + arg}; +	case ctor::ld_opt::cpp_std: +		return {"-std=" + arg}; +	case ctor::ld_opt::build_shared: +		return {"-shared"}; +	case ctor::ld_opt::threads: +		return {"-pthread"}; +	case ctor::ld_opt::position_independent_code: +		return {"-fPIC"}; +	case ctor::ld_opt::position_independent_executable: +		return {"-fPIE"}; +	case ctor::ld_opt::custom: +		return {arg}; +	} + +	std::cerr << "Unsupported compiler option.\n"; +	return {}; +} + +std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg, +                                   [[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}; +	} + +	std::cerr << "Unsupported compiler option.\n"; +	return {}; +} + +std::vector<std::string> asm_option(ctor::asm_opt opt, const std::string& arg, +                                    [[maybe_unused]]const std::string& arg2) +{ +	switch(opt) +	{ +	case ctor::asm_opt::custom: +		return {arg}; +	} + +	std::cerr << "Unsupported compiler option.\n"; +	return {}; +} +} // gcc:: + +std::string get_arch(ctor::output_system system) +{ +	auto toolchain = getToolChain(system); +	switch(toolchain) +	{ +	case ctor::toolchain::clang: +	case ctor::toolchain::gcc: +		return gcc::get_arch(system); +	case ctor::toolchain::any: +	case ctor::toolchain::none: +		break; +	} +	return {}; +} + +ctor::arch get_arch(ctor::output_system system, const std::string& str) +{ +	auto toolchain = getToolChain(system); +	switch(toolchain) +	{ +	case ctor::toolchain::clang: +	case ctor::toolchain::gcc: +		return gcc::get_arch(str); +	case ctor::toolchain::any: +	case ctor::toolchain::none: +		break; +	} +	return ctor::arch::unknown; +} + +std::vector<std::string> c_option(ctor::toolchain toolchain, +                                  ctor::c_opt opt, +                                  const std::string& arg, +                                  const std::string& arg2) +{ +	switch(toolchain) +	{ +	case ctor::toolchain::gcc: +	case ctor::toolchain::clang: +		return gcc::c_option(opt, arg, arg2); +	case ctor::toolchain::any: +		{ +			std::ostringstream ss; +			ss << "{" << opt; +			if(!arg.empty()) +			{ +				ss << ", \"" << arg << "\""; +			} +			if(!arg2.empty()) +			{ +				ss << ", \"" << arg2 << "\""; +			} +			ss << "}"; +			return { ss.str() }; +		} +	case ctor::toolchain::none: +		break; +	} + +	std::cerr << "Unsupported tool-chain.\n"; +	return {}; +} + +std::vector<std::string> cxx_option(ctor::toolchain toolchain, +                                    ctor::cxx_opt opt, +                                    const std::string& arg, +                                    const std::string& arg2) +{ +	switch(toolchain) +	{ +	case ctor::toolchain::gcc: +	case ctor::toolchain::clang: +		return gcc::cxx_option(opt, arg, arg2); +	case ctor::toolchain::any: +		{ +			std::ostringstream ss; +			ss << "{" << opt; +			if(!arg.empty()) +			{ +				ss << ", \"" << arg << "\""; +			} +			if(!arg2.empty()) +			{ +				ss << ", \"" << arg2 << "\""; +			} +			ss << "}"; +			return { ss.str() }; +		} +	case ctor::toolchain::none: +		break; +	} + +	std::cerr << "Unsupported tool-chain.\n"; +	return {}; +} + +std::vector<std::string> ld_option(ctor::toolchain toolchain, +                                   ctor::ld_opt opt, +                                   const std::string& arg, +                                   const std::string& arg2) +{ +	switch(toolchain) +	{ +	case ctor::toolchain::gcc: +	case ctor::toolchain::clang: +		return gcc::ld_option(opt, arg, arg2); +	case ctor::toolchain::any: +		{ +			std::ostringstream ss; +			ss << "{" << opt; +			if(!arg.empty()) +			{ +				ss << ", \"" << arg << "\""; +			} +			if(!arg2.empty()) +			{ +				ss << ", \"" << arg2 << "\""; +			} +			ss << "}"; +			return { ss.str() }; +		} +	case ctor::toolchain::none: +		break; +	} + +	std::cerr << "Unsupported tool-chain.\n"; +	return {}; +} + +std::vector<std::string> ar_option(ctor::toolchain toolchain, +                                   ctor::ar_opt opt, +                                   const std::string& arg, +                                   const std::string& arg2) +{ +	switch(toolchain) +	{ +	case ctor::toolchain::gcc: +	case ctor::toolchain::clang: +		return gcc::ar_option(opt, arg, arg2); +	case ctor::toolchain::any: +		{ +			std::ostringstream ss; +			ss << "{" << opt; +			if(!arg.empty()) +			{ +				ss << ", \"" << arg << "\""; +			} +			if(!arg2.empty()) +			{ +				ss << ", \"" << arg2 << "\""; +			} +			ss << "}"; +			return { ss.str() }; +		} +	case ctor::toolchain::none: +		break; +	} + +	std::cerr << "Unsupported tool-chain.\n"; +	return {}; +} + +std::vector<std::string> asm_option(ctor::toolchain toolchain, +                                    ctor::asm_opt opt, +                                    const std::string& arg, +                                    const std::string& arg2) +{ +	switch(toolchain) +	{ +	case ctor::toolchain::gcc: +	case ctor::toolchain::clang: +		return gcc::asm_option(opt, arg, arg2); +	case ctor::toolchain::any: +		{ +			std::ostringstream ss; +			ss << "{" << opt; +			if(!arg.empty()) +			{ +				ss << ", \"" << arg << "\""; +			} +			if(!arg2.empty()) +			{ +				ss << ", \"" << arg2 << "\""; +			} +			ss << "}"; +			return { ss.str() }; +		} +	case ctor::toolchain::none: +		break; +	} + +	std::cerr << "Unsupported tool-chain.\n"; +	return {}; +} + + +ctor::c_flag c_option(const std::string& flag, ctor::toolchain toolchain) +{ +	switch(toolchain) +	{ +	case ctor::toolchain::gcc: +	case ctor::toolchain::clang: +		return gcc::c_option(flag); +	case ctor::toolchain::any: +	case ctor::toolchain::none: +		break; +	} + +	return { ctor::c_opt::custom, flag }; +} + +ctor::cxx_flag cxx_option(const std::string& flag, ctor::toolchain toolchain) +{ +	switch(toolchain) +	{ +	case ctor::toolchain::gcc: +	case ctor::toolchain::clang: +		return gcc::cxx_option(flag); +	case ctor::toolchain::any: +	case ctor::toolchain::none: +		break; +	} + +	return { ctor::cxx_opt::custom, flag }; +} + +ctor::ld_flag ld_option(const std::string& flag, ctor::toolchain toolchain) +{ +	switch(toolchain) +	{ +	case ctor::toolchain::gcc: +	case ctor::toolchain::clang: +		return gcc::ld_option(flag); +	case ctor::toolchain::any: +	case ctor::toolchain::none: +		break; +	} + +	return { ctor::ld_opt::custom, flag }; +} + +ctor::ar_flag ar_option(const std::string& flag, ctor::toolchain toolchain) +{ +	switch(toolchain) +	{ +	case ctor::toolchain::gcc: +	case ctor::toolchain::clang: +		return gcc::ar_option(flag); +	case ctor::toolchain::any: +	case ctor::toolchain::none: +		break; +	} + +	return { ctor::ar_opt::custom, flag }; +} + +ctor::asm_flag asm_option(const std::string& flag, ctor::toolchain toolchain) +{ +	switch(toolchain) +	{ +	case ctor::toolchain::gcc: +	case ctor::toolchain::clang: +	case ctor::toolchain::any: +	case ctor::toolchain::none: +		break; +	} + +	return { ctor::asm_opt::custom, flag }; +} + +// Flag to string coversions + +std::vector<std::string> to_strings(ctor::toolchain toolchain, +                                    const ctor::c_flag& flag) +{ +	if(flag.toolchain == ctor::toolchain::any || +	   flag.toolchain == toolchain) +	{ +		return c_option(toolchain, flag.opt, flag.arg, flag.arg2); +	} + +	return {}; +} + +std::vector<std::string> to_strings(ctor::toolchain toolchain, +                                    const ctor::cxx_flag& flag) +{ +	if(flag.toolchain == ctor::toolchain::any || +	   flag.toolchain == toolchain) +	{ +		return cxx_option(toolchain, flag.opt, flag.arg, flag.arg2); +	} + +	return {}; +} + +std::vector<std::string> to_strings(ctor::toolchain toolchain, +                                    const ctor::ld_flag& flag) +{ +	if(flag.toolchain == ctor::toolchain::any || +	   flag.toolchain == toolchain) +	{ +		return ld_option(toolchain, flag.opt, flag.arg, flag.arg2); +	} + +	return {}; +} + +std::vector<std::string> to_strings(ctor::toolchain toolchain, +                                    const ctor::ar_flag& flag) +{ +	if(flag.toolchain == ctor::toolchain::any || +	   flag.toolchain == toolchain) +	{ +		return ar_option(toolchain, flag.opt, flag.arg, flag.arg2); +	} + +	return {}; +} + +std::vector<std::string> to_strings(ctor::toolchain toolchain, +                                    const ctor::asm_flag& flag) +{ +	if(flag.toolchain == ctor::toolchain::any || +	   flag.toolchain == toolchain) +	{ +		return asm_option(toolchain, flag.opt, flag.arg, flag.arg2); +	} + +	return {}; +} + +namespace { +ctor::toolchain guess_toolchain(const std::string& opt) +{ +	if(opt.empty()) +	{ +		return ctor::toolchain::any; +	} + +	if(opt[0] == '-') +	{ +		return ctor::toolchain::gcc; +	} + +	//if(opt[0] == '/') +	//{ +	//	return ctor::toolchain::msvc; +	//} +	return ctor::toolchain::any; +} +} + +template<> +ctor::flag<ctor::c_opt>::flag(const char* str) +{ +	*this = c_option(str, guess_toolchain(str)); +} + +template<> +ctor::flag<ctor::cxx_opt>::flag(const char* str) +{ +	*this = cxx_option(str, guess_toolchain(str)); +} + +template<> +ctor::flag<ctor::ld_opt>::flag(const char* str) +{ +	*this = ld_option(str, guess_toolchain(str)); +} + +template<> +ctor::flag<ctor::ar_opt>::flag(const char* str) +{ +	*this = ar_option(str, guess_toolchain(str)); +} + +template<> +ctor::flag<ctor::asm_opt>::flag(const char* str) +{ +	*this = asm_option(str, guess_toolchain(str)); +} + + +ctor::target_type target_type_from_extension(ctor::toolchain toolchain, +                                             const std::filesystem::path& file) +{ +	auto ext = to_lower(file.extension().string()); +	// Loosely based on: +	// https://en.wikipedia.org/wiki/List_of_file_formats#Object_code,_executable_files,_shared_and_dynamically_linked_libraries +	if(toolchain == ctor::toolchain::any || +	   toolchain == ctor::toolchain::gcc || +	   toolchain == ctor::toolchain::clang) +	{ +		if(ext == ".a") +		{ +			return ctor::target_type::static_library; +		} + +		if(ext == ".so" || +		   ext == ".dylib") +		{ +			return ctor::target_type::dynamic_library; +		} + +		if(ext == ".o") +		{ +			return ctor::target_type::object; +		} + +		if(ext == "" || +		   ext == ".bin" || +		   ext == ".run" || +		   ext == ".out") +		{ +			return ctor::target_type::executable; +		} +	} + +	if(toolchain == ctor::toolchain::any// || +	   //toolchain == ctor::toolchain::msvc || +	   //toolchain == ctor::toolchain::mingw || +		) +	{ +		if(ext == ".lib") +		{ +			return ctor::target_type::static_library; +		} + +		if(ext == ".dll") +		{ +			return ctor::target_type::dynamic_library; +		} + +		if(ext == ".obj") +		{ +			return ctor::target_type::object; +		} + +		if(ext == ".exe" || +		   ext == ".com") +		{ +			return ctor::target_type::executable; +		} +	} + +	return ctor::target_type::unknown; +} + + +std::filesystem::path extension(ctor::toolchain toolchain, +                                ctor::target_type target_type, +                                ctor::output_system system, +                                const std::filesystem::path& file) +{ +	auto type = target_type_from_extension(toolchain, file); +	if(type == target_type) +	{ +		// File already has the correct extension +		return file; +	} + +	const auto& c = ctor::get_configuration(); +	ctor::arch arch{}; +	switch(system) +	{ +	case ctor::output_system::host: +		arch = c.host_arch; +		break; +	case ctor::output_system::build: +		arch = c.build_arch; +		break; +	} + +	// This might be before configure - so detection is needed for boostrap +	if(arch == ctor::arch::unknown) +	{ +		arch = get_arch(system, get_arch(system)); +	} + +	std::string ext{file.extension().string()}; +	switch(target_type) +	{ +	case ctor::target_type::automatic: +		break; +	case ctor::target_type::executable: +	case ctor::target_type::unit_test: +		switch(arch) +		{ +		case ctor::arch::unix: +		case ctor::arch::apple: +			ext = ""; +			break; +		case ctor::arch::windows: +			ext = ".exe"; +			break; +		case ctor::arch::unknown: +			break; +		} +		break; +	case ctor::target_type::static_library: +	case ctor::target_type::unit_test_library: +		switch(arch) +		{ +		case ctor::arch::unix: +		case ctor::arch::apple: +			ext = ".a"; +			break; +		case ctor::arch::windows: +			ext = ".lib"; +			break; +		case ctor::arch::unknown: +			break; +		} +		break; +	case ctor::target_type::dynamic_library: +		switch(arch) +		{ +		case ctor::arch::unix: +			ext = ".so"; +			break; +		case ctor::arch::apple: +			ext = ".dylib"; +			break; +		case ctor::arch::windows: +			ext = ".dll"; +			break; +		case ctor::arch::unknown: +			break; +		} +		break; +	case ctor::target_type::object: +		switch(arch) +		{ +		case ctor::arch::unix: +		case ctor::arch::apple: +			ext = ".o"; +			break; +		case ctor::arch::windows: +			ext = ".obj"; +			break; +		case ctor::arch::unknown: +			break; +		} +		break; +	case ctor::target_type::function: +		break; +	case ctor::target_type::unknown: +		break; +	} + +	auto output{file}; +	output.replace_extension(ext); +	return output; +} diff --git a/src/tools.h b/src/tools.h new file mode 100644 index 0000000..0e7fc15 --- /dev/null +++ b/src/tools.h @@ -0,0 +1,129 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include <vector> +#include <string> +#include <ostream> +#include <filesystem> + +#include "ctor.h" + + +std::ostream& operator<<(std::ostream& stream, const ctor::c_opt& opt); +std::ostream& operator<<(std::ostream& stream, const ctor::cxx_opt& opt); +std::ostream& operator<<(std::ostream& stream, const ctor::ld_opt& opt); +std::ostream& operator<<(std::ostream& stream, const ctor::ar_opt& opt); +std::ostream& operator<<(std::ostream& stream, const ctor::asm_opt& opt); + +std::string get_arch(ctor::output_system system); +ctor::arch get_arch(ctor::output_system system, const std::string& str); + +//! Get tool-chain type from compiler path string +ctor::toolchain getToolChain(const std::string& compiler); + +//! Get tool-chain type from output system (via configuration) +ctor::toolchain getToolChain(ctor::output_system system); + + + +//! Get tool argument(s) for specific option type matching the supplied +//! tool-chain +std::vector<std::string> c_option(ctor::toolchain toolchain, +                                  ctor::c_opt option, +                                  const std::string& arg = {}, +                                  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& 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& 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& 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& arg2 = {}); + + + +//! Get ctor::c_opt enum value and argument from string, +//! ie. { ctor::c_opt::inlude_path, "foo/bar" } from "-Ifoo/bar" +//! Returns { ctor::c_opt::custom, flag } if unknown. +ctor::c_flag c_option(const std::string& flag, ctor::toolchain toolchain); + +//! Get ctor::cxx_opt enum value and argument from string, +//! ie. { ctor::cxx_opt::inlude_path, "foo/bar" } from "-Ifoo/bar" +//! Returns { ctor::cxx_opt::custom, flag } if unknown. +ctor::cxx_flag cxx_option(const std::string& flag, ctor::toolchain toolchain); + +//! Get ctor::ld_opt enum value and argument from string, +//! ie. { ctor::ld_opt::inlude_path, "foo/bar" } from "-Ifoo/bar" +//! Returns { ctor::ld_opt::custom, flag } if unknown. +ctor::ld_flag ld_option(const std::string& flag, ctor::toolchain toolchain); + +//! Get ctor::ar_opt enum value and argument from string, +//! ie. { ctor::ar_opt::inlude_path, "foo/bar" } from "-Ifoo/bar" +//! Returns { ctor::ar_opt::custom, flag } if unknown. +ctor::ar_flag ar_option(const std::string& flag, ctor::toolchain toolchain); + +//! Get ctor::asm_opt enum value and argument from string, +//! ie. { ctor::asm_opt::inlude_path, "foo/bar" } from "-Ifoo/bar" +//! Returns { ctor::asm_opt::custom, flag } if unknown. +ctor::asm_flag asm_option(const std::string& flag, ctor::toolchain toolchain); + + + +std::vector<std::string> to_strings(ctor::toolchain toolchain, +                                    const ctor::cxx_flag& flag); +std::vector<std::string> to_strings(ctor::toolchain toolchain, +                                    const ctor::c_flag& flag); +std::vector<std::string> to_strings(ctor::toolchain toolchain, +                                    const ctor::ld_flag& flag); +std::vector<std::string> to_strings(ctor::toolchain toolchain, +                                    const ctor::ar_flag& flag); +std::vector<std::string> to_strings(ctor::toolchain toolchain, +                                    const ctor::asm_flag& flag); + + +// Get target type from file extension +// If toolchain is not ::any only extensions for that toolchain will be accepted +// If no match is found ::unknown will be returned. +ctor::target_type target_type_from_extension(ctor::toolchain toolchain, +                                             const std::filesystem::path& file); + + +// Get appropriate extension from original extension and target type using +// the toolchain. +// ie. { gcc, static_lib, ".lib" } will return ".a" +//     { msvc, dynamic_lib, ".dylib" } will return ".dll" +//       ... +// If the supplied extension is normal for the supplied type and toolchain, then this is used, otherwise a conversion to the default extension to the given toolchain and type is given. +// The defaults for the toolchains are as follows: +// toolchain   executable static-lib dynamic-lib +//   gcc         (none)       .a        .so(.dylib on macos) +//   clang       (none)       .a        .so(.dylib on macos) +//   msvc        .exe         .lib      .dll +//   mingw       .exe         .lib      .dll +std::filesystem::path extension(ctor::toolchain toolchain, +                                ctor::target_type target_type, +                                ctor::output_system system, +                                const std::filesystem::path& file); diff --git a/src/unittest.cc b/src/unittest.cc index ade2d0a..b95a931 100644 --- a/src/unittest.cc +++ b/src/unittest.cc @@ -6,29 +6,33 @@  #include <iostream>  #include "execute.h" -#include "settings.h"  #include "task.h" -int runUnitTests(std::list<std::shared_ptr<Task>>& tasks, -                 const Settings& settings) +int runUnitTests(std::vector<std::shared_ptr<Task>>& tasks, +                 const ctor::settings& settings)  {  	bool ok{true}; -	std::cout << "Running unit-tests:\n"; +	std::cout << "Running unit-tests:" << std::endl;  	// Run unit-tests  	for(const auto& task : tasks)  	{ -		if(task->targetType() == TargetType::UnitTest) +		if(task->targetType() == ctor::target_type::unit_test)  		{ -			std::cout << task->name() << ": "; -			auto ret = execute(task->target(), {}, false); +			auto name = task->name(); +			if(name.empty()) +			{ +				name = task->target(); +			} +			std::cout << name << ": " << std::flush; +			auto ret = execute(settings, task->targetFile().string(), {}, {});  			ok &= ret == 0;  			if(ret == 0)  			{ -				std::cout << "OK\n"; +				std::cout << " OK" << std::endl;  			}  			else  			{ -				std::cout << "FAILED\n"; +				std::cout << " FAILED" << std::endl;  			}  		}  	} diff --git a/src/unittest.h b/src/unittest.h index 7eef0e2..6d1385e 100644 --- a/src/unittest.h +++ b/src/unittest.h @@ -3,11 +3,14 @@  // See accompanying file LICENSE for details.  #pragma once -#include <list> +#include <vector>  #include <memory>  class Task; -class Settings; -int runUnitTests(std::list<std::shared_ptr<Task>>& tasks, -                 const Settings& settings); +namespace ctor { +struct settings; +} // namespace ctor:: + +int runUnitTests(std::vector<std::shared_ptr<Task>>& tasks, +                 const ctor::settings& settings); diff --git a/src/util.cc b/src/util.cc index 9bf83cc..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,99 +22,165 @@ 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)) +	auto ext = file.extension().string(); + +	// First a few case sensitive comparisons +	if(ext == ".c")  	{ -		return {}; +		return ctor::language::c;  	} -	auto str = readFile(depFile); +	if(ext == ".C") +	{ +		return ctor::language::cpp; +	} + +	// The rest are compared in lowercase +	ext = to_lower(ext); + +	if(ext == ".cc" || +	   ext == ".cpp" || +	   ext == ".c++" || +	   ext == ".cp" || +	   ext == ".cxx") +	{ +		return ctor::language::cpp; +	} -	std::vector<std::string> output; -	std::string tmp; -	bool start{false}; -	bool in_whitespace{false}; -	for(const auto& c : str) +	if(ext == ".s" || +	   ext == ".asm")  	{ -		if(c == '\\' || c == '\n') +		return ctor::language::assembler; +	} + +	std::cerr << "Could not deduce language from " << file.string() << "\n"; +	exit(1); +	return {}; +} + +namespace +{ +bool isClean(char c) +{ +	return c != '.' && c != '/'; +} +} + +std::string cleanUp(const std::string& path) +{ +	std::string cleaned; +	for(const auto& c : path) +	{ +		if(isClean(c))  		{ -			continue; +			cleaned += c;  		} - -		if(c == ':') +		else  		{ -			start = true; -			continue; +			cleaned += '_';  		} +	} +	return cleaned; +} -		if(!start) +std::string esc(const std::string& in) +{ +	std::string out; +	for(auto c : in) +	{ +		switch(c)  		{ -			continue; +		case '\\': out += "\\\\"; break; +		case '"': out += "\\\""; break; +		default: +			out += c; +			break;  		} +	} +	return out; +} -		if(c == ' ' || c == '\t') -		{ -			if(in_whitespace) -			{ -				continue; -			} +std::vector<std::string> get_paths(const std::string& path_env) +{ +	std::vector<std::string> paths; -			if(!tmp.empty()) -			{ -				output.push_back(tmp); -			} -			tmp.clear(); -			in_whitespace = true; -		} -		else -		{ -			in_whitespace = false; -			tmp += c; -		} +#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(!tmp.empty()) +	if((perms & std::filesystem::perms::group_exec) != std::filesystem::perms::none)  	{ -		output.push_back(tmp); +		return true;  	} -	return output; +	if((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none) +	{ +		return true; +	} + +	return false;  } -Language languageFromExtension(const std::filesystem::path& file) +std::string locate(const std::string& prog, +                   const std::vector<std::string>& paths, +                   const std::string& arch)  { -	auto ext = file.extension().string(); -	if(ext == ".c") +	std::string program = prog; +	if(!arch.empty())  	{ -		return Language::C; +		program = arch + "-" + prog;  	} -	if(ext == ".C" || -	   ext == ".cc" || -	   ext == ".cpp" || -	   ext == ".CPP" || -	   ext == ".c++" || -	   ext == ".cp" || -	   ext == ".cxx") +	// first check if arch contains an absolute path to prog +	if(std::filesystem::exists(program))  	{ -		return Language::Cpp; +		if(check_executable(program)) +		{ +			return program; +		}  	} -	if(ext == ".s" || -	   ext == ".S" || -	   ext == ".asm") +	for(const auto& path_str : paths)  	{ -		return Language::Asm; +		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(); +			} +		}  	} -	std::cerr << "Could not deduce language from " << file.string() << "\n"; -	exit(1);  	return {};  } @@ -3,11 +3,28 @@  // See accompanying file LICENSE for details.  #pragma once -#include "libctor.h" +#include "ctor.h"  #include <string>  #include <filesystem> +#include <cstdlib> + +std::string to_lower(const std::string& str);  std::string readFile(const std::string& fileName); -std::vector<std::string> readDeps(const std::string& depFile); -Language languageFromExtension(const std::filesystem::path& file); +ctor::language languageFromExtension(const std::filesystem::path& file); +std::string cleanUp(const std::string& path); + +template<typename T> +void append(T& a, const T& b) +{ +	a.insert(a.end(), b.begin(), b.end()); +} + +std::string esc(const std::string& in); + +std::vector<std::string> get_paths(const std::string& path_env = std::getenv("PATH")); + +std::string locate(const std::string& app, +                   const std::vector<std::string>& paths, +                   const std::string& arch = {}); | 
