using namespace dds; | using namespace dds; | ||||
std::vector<std::string> compile_file_plan::generate_compile_command(build_env_ref env) const | |||||
noexcept { | |||||
compile_command_info compile_file_plan::generate_compile_command(build_env_ref env) const noexcept { | |||||
compile_file_spec spec{_source.path, calc_object_file_path(env)}; | compile_file_spec spec{_source.path, calc_object_file_path(env)}; | ||||
spec.enable_warnings = _rules.enable_warnings(); | spec.enable_warnings = _rules.enable_warnings(); | ||||
extend(spec.include_dirs, _rules.include_dirs()); | extend(spec.include_dirs, _rules.include_dirs()); | ||||
return env.toolchain.create_compile_command(spec); | return env.toolchain.create_compile_command(spec); | ||||
} | } | ||||
void compile_file_plan::compile(const build_env& env) const { | |||||
std::optional<deps_info> compile_file_plan::compile(const build_env& env) const { | |||||
const auto obj_path = calc_object_file_path(env); | const auto obj_path = calc_object_file_path(env); | ||||
fs::create_directories(obj_path.parent_path()); | fs::create_directories(obj_path.parent_path()); | ||||
fs::relative(_source.path, _source.basis_path).string()); | fs::relative(_source.path, _source.basis_path).string()); | ||||
spdlog::info(msg); | spdlog::info(msg); | ||||
auto cmd = generate_compile_command(env); | |||||
auto&& [dur_ms, compile_res] = timed<std::chrono::milliseconds>([&] { return run_proc(cmd); }); | |||||
auto cmd = generate_compile_command(env); | |||||
auto&& [dur_ms, compile_res] | |||||
= timed<std::chrono::milliseconds>([&] { return run_proc(cmd.command); }); | |||||
spdlog::info("{} - {:>7n}ms", msg, dur_ms.count()); | spdlog::info("{} - {:>7n}ms", msg, dur_ms.count()); | ||||
if (!compile_res.okay()) { | if (!compile_res.okay()) { | ||||
spdlog::error("Compilation failed: {}", _source.path.string()); | spdlog::error("Compilation failed: {}", _source.path.string()); | ||||
spdlog::error("Subcommand FAILED: {}\n{}", quote_command(cmd), compile_res.output); | |||||
spdlog::error("Subcommand FAILED: {}\n{}", quote_command(cmd.command), compile_res.output); | |||||
throw compile_failure(fmt::format("Compilation failed for {}", _source.path.string())); | throw compile_failure(fmt::format("Compilation failed for {}", _source.path.string())); | ||||
} | } | ||||
if (!compile_res.output.empty()) { | if (!compile_res.output.empty()) { | ||||
spdlog::warn("While compiling file {} [{}]:\n{}", | spdlog::warn("While compiling file {} [{}]:\n{}", | ||||
_source.path.string(), | _source.path.string(), | ||||
quote_command(cmd), | |||||
quote_command(cmd.command), | |||||
compile_res.output); | compile_res.output); | ||||
} | } | ||||
return std::nullopt; | |||||
} | } | ||||
fs::path compile_file_plan::calc_object_file_path(const build_env& env) const noexcept { | fs::path compile_file_plan::calc_object_file_path(const build_env& env) const noexcept { |
#include <dds/build/plan/base.hpp> | #include <dds/build/plan/base.hpp> | ||||
#include <dds/source.hpp> | #include <dds/source.hpp> | ||||
#include <dds/toolchain/deps.hpp> | |||||
#include <memory> | #include <memory> | ||||
, _qualifier(qual) | , _qualifier(qual) | ||||
, _subdir(subdir) {} | , _subdir(subdir) {} | ||||
std::vector<std::string> generate_compile_command(build_env_ref) const noexcept; | |||||
compile_command_info generate_compile_command(build_env_ref) const noexcept; | |||||
const source_file& source() const noexcept { return _source; } | const source_file& source() const noexcept { return _source; } | ||||
path_ref source_path() const noexcept { return _source.path; } | path_ref source_path() const noexcept { return _source.path; } | ||||
fs::path calc_object_file_path(build_env_ref env) const noexcept; | |||||
void compile(build_env_ref) const; | |||||
fs::path calc_object_file_path(build_env_ref env) const noexcept; | |||||
std::optional<deps_info> compile(build_env_ref) const; | |||||
}; | }; | ||||
} // namespace dds | } // namespace dds |
auto compdb = nlohmann::json::array(); | auto compdb = nlohmann::json::array(); | ||||
for (const compile_file_plan& cf : iter_compilations(plan)) { | for (const compile_file_plan& cf : iter_compilations(plan)) { | ||||
auto command = cf.generate_compile_command(env); | |||||
auto cmd_info = cf.generate_compile_command(env); | |||||
auto entry = nlohmann::json::object({ | auto entry = nlohmann::json::object({ | ||||
{"directory", env.output_root.string()}, | {"directory", env.output_root.string()}, | ||||
{"arguments", command}, | |||||
{"arguments", cmd_info.command}, | |||||
{"file", cf.source_path().string()}, | {"file", cf.source_path().string()}, | ||||
}); | }); | ||||
compdb.push_back(std::move(entry)); | compdb.push_back(std::move(entry)); |
#pragma once | |||||
#include <dds/util/fs.hpp> | |||||
#include <vector> | |||||
namespace dds { | |||||
enum class deps_mode { | |||||
none, | |||||
msvc, | |||||
gnu, | |||||
}; | |||||
struct deps_info { | |||||
fs::path output; | |||||
std::vector<fs::path> inputs; | |||||
std::vector<std::string> command; | |||||
}; | |||||
} // namespace dds |
opt_string obj_suffix; | opt_string obj_suffix; | ||||
opt_string exe_prefix; | opt_string exe_prefix; | ||||
opt_string exe_suffix; | opt_string exe_suffix; | ||||
opt_string deps_mode_str; | |||||
optional<bool> do_debug; | optional<bool> do_debug; | ||||
optional<bool> do_optimize; | optional<bool> do_optimize; | ||||
opt_str_seq include_template; | opt_str_seq include_template; | ||||
lm::read_bool("Debug", do_debug), | lm::read_bool("Debug", do_debug), | ||||
// Miscellaneous | // Miscellaneous | ||||
read_argv{"Compiler-Launcher", compile_launcher}, | read_argv{"Compiler-Launcher", compile_launcher}, | ||||
lm::read_opt("Deps-Mode", deps_mode_str), | |||||
// Command templates | // Command templates | ||||
read_argv{"C-Compile-File", c_compile_file}, | read_argv{"C-Compile-File", c_compile_file}, | ||||
read_argv{"C++-Compile-File", cxx_compile_file}, | read_argv{"C++-Compile-File", cxx_compile_file}, | ||||
bool is_msvc = compiler_id_e == msvc; | bool is_msvc = compiler_id_e == msvc; | ||||
bool is_gnu_like = is_gnu || is_clang; | bool is_gnu_like = is_gnu || is_clang; | ||||
const enum deps_mode deps_mode = [&] { | |||||
if (!deps_mode_str.has_value()) { | |||||
if (is_gnu_like) { | |||||
return deps_mode::gnu; | |||||
} else if (is_msvc) { | |||||
return deps_mode::msvc; | |||||
} else { | |||||
return deps_mode::none; | |||||
} | |||||
} else if (deps_mode_str == "GNU") { | |||||
return deps_mode::gnu; | |||||
} else if (deps_mode_str == "MSVC") { | |||||
return deps_mode::msvc; | |||||
} else if (deps_mode_str == "None") { | |||||
return deps_mode::none; | |||||
} else { | |||||
fail(context, "Unknown Deps-Mode '{}'", *deps_mode_str); | |||||
} | |||||
}(); | |||||
// Now convert the flags we've been given into a real toolchain | // Now convert the flags we've been given into a real toolchain | ||||
auto get_compiler = [&](language lang) -> string { | auto get_compiler = [&](language lang) -> string { | ||||
if (lang == language::cxx && cxx_compiler_fpath) { | if (lang == language::cxx && cxx_compiler_fpath) { | ||||
rt_lib = "/MTd"; | rt_lib = "/MTd"; | ||||
} | } | ||||
ret.emplace_back(rt_lib); | ret.emplace_back(rt_lib); | ||||
extend(ret, {"/nologo", "<FLAGS>", "/permissive-", "/c", "<IN>", "/Fo<OUT>"}); | |||||
if (lang == language::cxx) { | if (lang == language::cxx) { | ||||
extend(ret, {"/EHsc"}); | extend(ret, {"/EHsc"}); | ||||
} | } | ||||
extend(ret, {"/nologo", "/permissive-", "<FLAGS>", "/c", "<IN>", "/Fo<OUT>"}); | |||||
} else if (is_gnu_like) { | } else if (is_gnu_like) { | ||||
if (do_optimize.has_value() && *do_optimize) { | if (do_optimize.has_value() && *do_optimize) { | ||||
extend(ret, {"-O2"}); | extend(ret, {"-O2"}); | ||||
return ret; | return ret; | ||||
}; | }; | ||||
tc.deps_mode = deps_mode; | |||||
tc.c_compile = read_opt(c_compile_file, [&] { | tc.c_compile = read_opt(c_compile_file, [&] { | ||||
string_seq c; | string_seq c; | ||||
if (compile_launcher) { | if (compile_launcher) { | ||||
} | } | ||||
if (is_gnu_like) { | if (is_gnu_like) { | ||||
return {"-I", "<PATH>"}; | return {"-I", "<PATH>"}; | ||||
} else if (compiler_id == "MSVC") { | |||||
} else if (is_msvc) { | |||||
return {"/I", "<PATH>"}; | return {"/I", "<PATH>"}; | ||||
} | } | ||||
assert(false && "Include-Template deduction failed"); | assert(false && "Include-Template deduction failed"); | ||||
} | } | ||||
if (is_gnu_like) { | if (is_gnu_like) { | ||||
return {"-D", "<DEF>"}; | return {"-D", "<DEF>"}; | ||||
} else if (compiler_id == "MSVC") { | |||||
} else if (is_msvc) { | |||||
return {"/D", "<DEF>"}; | return {"/D", "<DEF>"}; | ||||
} | } | ||||
assert(false && "Define-Template deduction failed"); | assert(false && "Define-Template deduction failed"); |
cf.source_path = "foo.cpp"; | cf.source_path = "foo.cpp"; | ||||
cf.out_path = "foo.o"; | cf.out_path = "foo.o"; | ||||
auto cf_cmd = tc.create_compile_command(cf); | auto cf_cmd = tc.create_compile_command(cf); | ||||
auto cf_cmd_str = dds::quote_command(cf_cmd); | |||||
auto cf_cmd_str = dds::quote_command(cf_cmd.command); | |||||
CHECK(cf_cmd_str == expected_compile); | CHECK(cf_cmd_str == expected_compile); | ||||
cf.enable_warnings = true; | cf.enable_warnings = true; | ||||
cf_cmd = tc.create_compile_command(cf); | cf_cmd = tc.create_compile_command(cf); | ||||
cf_cmd_str = dds::quote_command(cf_cmd); | |||||
cf_cmd_str = dds::quote_command(cf_cmd.command); | |||||
CHECK(cf_cmd_str == expected_compile_warnings); | CHECK(cf_cmd_str == expected_compile_warnings); | ||||
dds::archive_spec ar_spec; | dds::archive_spec ar_spec; | ||||
TEST_CASE("Generating toolchain commands") { | TEST_CASE("Generating toolchain commands") { | ||||
check_tc_compile("Compiler-ID: GNU", | check_tc_compile("Compiler-ID: GNU", | ||||
"g++ -fPIC -fdiagnostics-color -pthread -c foo.cpp -ofoo.o", | |||||
"g++ -fPIC -fdiagnostics-color -pthread -MD -MF foo.o.d -c foo.cpp -ofoo.o", | |||||
"g++ -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | "g++ -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | ||||
"-c foo.cpp -ofoo.o", | |||||
"-MD -MF foo.o.d -c foo.cpp -ofoo.o", | |||||
"ar rcs stuff.a foo.o bar.o", | "ar rcs stuff.a foo.o bar.o", | ||||
"g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -lstdc++fs -omeow.exe"); | "g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -lstdc++fs -omeow.exe"); | ||||
check_tc_compile( | check_tc_compile( | ||||
"Compiler-ID: GNU\nDebug: True", | "Compiler-ID: GNU\nDebug: True", | ||||
"g++ -g -fPIC -fdiagnostics-color -pthread -c foo.cpp -ofoo.o", | |||||
"g++ -g -fPIC -fdiagnostics-color -pthread -MD -MF foo.o.d -c foo.cpp -ofoo.o", | |||||
"g++ -g -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | "g++ -g -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | ||||
"-c foo.cpp -ofoo.o", | |||||
"-MD -MF foo.o.d -c foo.cpp -ofoo.o", | |||||
"ar rcs stuff.a foo.o bar.o", | "ar rcs stuff.a foo.o bar.o", | ||||
"g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -lstdc++fs -omeow.exe -g"); | "g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -lstdc++fs -omeow.exe -g"); | ||||
check_tc_compile( | check_tc_compile( | ||||
"Compiler-ID: GNU\nDebug: True\nOptimize: True", | "Compiler-ID: GNU\nDebug: True\nOptimize: True", | ||||
"g++ -O2 -g -fPIC -fdiagnostics-color -pthread -c foo.cpp -ofoo.o", | |||||
"g++ -O2 -g -fPIC -fdiagnostics-color -pthread -MD -MF foo.o.d -c foo.cpp -ofoo.o", | |||||
"g++ -O2 -g -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | "g++ -O2 -g -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | ||||
"-c foo.cpp -ofoo.o", | |||||
"-MD -MF foo.o.d -c foo.cpp -ofoo.o", | |||||
"ar rcs stuff.a foo.o bar.o", | "ar rcs stuff.a foo.o bar.o", | ||||
"g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -lstdc++fs -omeow.exe -O2 -g"); | "g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -lstdc++fs -omeow.exe -O2 -g"); | ||||
check_tc_compile("Compiler-ID: MSVC", | check_tc_compile("Compiler-ID: MSVC", | ||||
"cl.exe /MT /nologo /permissive- /c foo.cpp /Fofoo.o /EHsc", | |||||
"cl.exe /MT /nologo /W4 /permissive- /c foo.cpp /Fofoo.o /EHsc", | |||||
"cl.exe /MT /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | |||||
"cl.exe /MT /EHsc /nologo /permissive- /W4 /showIncludes /c foo.cpp /Fofoo.o", | |||||
"lib /nologo /OUT:stuff.a foo.o bar.o", | "lib /nologo /OUT:stuff.a foo.o bar.o", | ||||
"cl.exe /nologo /EHsc foo.o bar.a /Femeow.exe /MT"); | "cl.exe /nologo /EHsc foo.o bar.a /Femeow.exe /MT"); | ||||
check_tc_compile("Compiler-ID: MSVC\nDebug: True", | check_tc_compile("Compiler-ID: MSVC\nDebug: True", | ||||
"cl.exe /Z7 /DEBUG /MTd /nologo /permissive- /c foo.cpp /Fofoo.o /EHsc", | |||||
"cl.exe /Z7 /DEBUG /MTd /nologo /W4 /permissive- /c foo.cpp /Fofoo.o /EHsc", | |||||
"cl.exe /Z7 /DEBUG /MTd /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | |||||
"cl.exe /Z7 /DEBUG /MTd /EHsc /nologo /permissive- /W4 /showIncludes /c foo.cpp /Fofoo.o", | |||||
"lib /nologo /OUT:stuff.a foo.o bar.o", | "lib /nologo /OUT:stuff.a foo.o bar.o", | ||||
"cl.exe /nologo /EHsc foo.o bar.a /Femeow.exe /Z7 /DEBUG /MTd"); | "cl.exe /nologo /EHsc foo.o bar.a /Femeow.exe /Z7 /DEBUG /MTd"); | ||||
cfs.source_path = "foo.cpp"; | cfs.source_path = "foo.cpp"; | ||||
cfs.out_path = "foo.o"; | cfs.out_path = "foo.o"; | ||||
auto cmd = tc.create_compile_command(cfs); | auto cmd = tc.create_compile_command(cfs); | ||||
CHECK(cmd | |||||
CHECK(cmd.command | |||||
== std::vector<std::string>{"g++", | == std::vector<std::string>{"g++", | ||||
"-fPIC", | "-fPIC", | ||||
"-fdiagnostics-color", | "-fdiagnostics-color", | ||||
"-pthread", | "-pthread", | ||||
"-MD", | |||||
"-MF", | |||||
"foo.o.d", | |||||
"-c", | "-c", | ||||
"foo.cpp", | "foo.cpp", | ||||
"-ofoo.o"}); | "-ofoo.o"}); | ||||
cfs.definitions.push_back("FOO=BAR"); | cfs.definitions.push_back("FOO=BAR"); | ||||
cmd = tc.create_compile_command(cfs); | cmd = tc.create_compile_command(cfs); | ||||
CHECK(cmd | |||||
CHECK(cmd.command | |||||
== std::vector<std::string>{"g++", | == std::vector<std::string>{"g++", | ||||
"-fPIC", | "-fPIC", | ||||
"-fdiagnostics-color", | "-fdiagnostics-color", | ||||
"-pthread", | "-pthread", | ||||
"-D", | "-D", | ||||
"FOO=BAR", | "FOO=BAR", | ||||
"-MD", | |||||
"-MF", | |||||
"foo.o.d", | |||||
"-c", | "-c", | ||||
"foo.cpp", | "foo.cpp", | ||||
"-ofoo.o"}); | "-ofoo.o"}); | ||||
cfs.include_dirs.push_back("fake-dir"); | cfs.include_dirs.push_back("fake-dir"); | ||||
cmd = tc.create_compile_command(cfs); | cmd = tc.create_compile_command(cfs); | ||||
CHECK(cmd | |||||
CHECK(cmd.command | |||||
== std::vector<std::string>{"g++", | == std::vector<std::string>{"g++", | ||||
"-fPIC", | "-fPIC", | ||||
"-fdiagnostics-color", | "-fdiagnostics-color", | ||||
"fake-dir", | "fake-dir", | ||||
"-D", | "-D", | ||||
"FOO=BAR", | "FOO=BAR", | ||||
"-MD", | |||||
"-MF", | |||||
"foo.o.d", | |||||
"-c", | "-c", | ||||
"foo.cpp", | "foo.cpp", | ||||
"-ofoo.o"}); | "-ofoo.o"}); |
#pragma once | #pragma once | ||||
#include <dds/toolchain/deps.hpp> | |||||
#include <string> | #include <string> | ||||
#include <vector> | #include <vector> | ||||
std::string exe_prefix; | std::string exe_prefix; | ||||
std::string exe_suffix; | std::string exe_suffix; | ||||
enum deps_mode deps_mode; | |||||
toolchain realize() const; | toolchain realize() const; | ||||
}; | }; | ||||
ret._object_suffix = prep.object_suffix; | ret._object_suffix = prep.object_suffix; | ||||
ret._exe_prefix = prep.exe_prefix; | ret._exe_prefix = prep.exe_prefix; | ||||
ret._exe_suffix = prep.exe_suffix; | ret._exe_suffix = prep.exe_suffix; | ||||
ret._deps_mode = prep.deps_mode; | |||||
return ret; | return ret; | ||||
} | } | ||||
return replace(_def_template, "<DEF>", s); | return replace(_def_template, "<DEF>", s); | ||||
} | } | ||||
vector<string> toolchain::create_compile_command(const compile_file_spec& spec) const noexcept { | |||||
compile_command_info toolchain::create_compile_command(const compile_file_spec& spec) const | |||||
noexcept { | |||||
vector<string> flags; | vector<string> flags; | ||||
using namespace std::literals; | |||||
language lang = spec.lang; | language lang = spec.lang; | ||||
if (lang == language::automatic) { | if (lang == language::automatic) { | ||||
if (spec.source_path.extension() == ".c" || spec.source_path.extension() == ".C") { | if (spec.source_path.extension() == ".c" || spec.source_path.extension() == ".C") { | ||||
extend(flags, _warning_flags); | extend(flags, _warning_flags); | ||||
} | } | ||||
std::optional<fs::path> gnu_depfile_path; | |||||
if (_deps_mode == deps_mode::gnu) { | |||||
gnu_depfile_path = spec.out_path; | |||||
gnu_depfile_path->replace_extension(gnu_depfile_path->extension().string() + ".d"); | |||||
extend(flags, {"-MD"sv, "-MF"sv, std::string_view(gnu_depfile_path->string())}); | |||||
} else if (_deps_mode == deps_mode::msvc) { | |||||
flags.push_back("/showIncludes"); | |||||
} | |||||
vector<string> command; | vector<string> command; | ||||
for (auto arg : cmd_template) { | for (auto arg : cmd_template) { | ||||
if (arg == "<FLAGS>") { | if (arg == "<FLAGS>") { | ||||
command.push_back(arg); | command.push_back(arg); | ||||
} | } | ||||
} | } | ||||
return command; | |||||
return {command, gnu_depfile_path}; | |||||
} | } | ||||
vector<string> toolchain::create_archive_command(const archive_spec& spec) const noexcept { | vector<string> toolchain::create_archive_command(const archive_spec& spec) const noexcept { |
#pragma once | #pragma once | ||||
#include <dds/toolchain/deps.hpp> | |||||
#include <dds/util/fs.hpp> | #include <dds/util/fs.hpp> | ||||
#include <optional> | #include <optional> | ||||
bool enable_warnings = false; | bool enable_warnings = false; | ||||
}; | }; | ||||
struct compile_command_info { | |||||
std::vector<std::string> command; | |||||
std::optional<fs::path> gnu_depfile_path; | |||||
}; | |||||
struct archive_spec { | struct archive_spec { | ||||
std::vector<fs::path> input_files; | std::vector<fs::path> input_files; | ||||
fs::path out_path; | fs::path out_path; | ||||
std::string _exe_prefix; | std::string _exe_prefix; | ||||
std::string _exe_suffix; | std::string _exe_suffix; | ||||
enum deps_mode _deps_mode; | |||||
public: | public: | ||||
toolchain() = default; | toolchain() = default; | ||||
auto& archive_suffix() const noexcept { return _archive_suffix; } | auto& archive_suffix() const noexcept { return _archive_suffix; } | ||||
auto& object_suffix() const noexcept { return _object_suffix; } | auto& object_suffix() const noexcept { return _object_suffix; } | ||||
auto& executable_suffix() const noexcept { return _exe_suffix; } | auto& executable_suffix() const noexcept { return _exe_suffix; } | ||||
auto deps_mode() const noexcept { return _deps_mode; } | |||||
std::vector<std::string> definition_args(std::string_view s) const noexcept; | std::vector<std::string> definition_args(std::string_view s) const noexcept; | ||||
std::vector<std::string> include_args(const fs::path& p) const noexcept; | std::vector<std::string> include_args(const fs::path& p) const noexcept; | ||||
std::vector<std::string> create_compile_command(const compile_file_spec&) const noexcept; | |||||
compile_command_info create_compile_command(const compile_file_spec&) const noexcept; | |||||
std::vector<std::string> create_archive_command(const archive_spec&) const noexcept; | std::vector<std::string> create_archive_command(const archive_spec&) const noexcept; | ||||
std::vector<std::string> create_link_executable_command(const link_exe_spec&) const noexcept; | std::vector<std::string> create_link_executable_command(const link_exe_spec&) const noexcept; | ||||