diff options
Diffstat (limited to 'src/tools.cc')
| -rw-r--r-- | src/tools.cc | 1123 | 
1 files changed, 1123 insertions, 0 deletions
| 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; +} | 
