@@ -12,8 +12,7 @@ | |||
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)}; | |||
spec.enable_warnings = _rules.enable_warnings(); | |||
extend(spec.include_dirs, _rules.include_dirs()); | |||
@@ -21,7 +20,7 @@ std::vector<std::string> compile_file_plan::generate_compile_command(build_env_r | |||
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); | |||
fs::create_directories(obj_path.parent_path()); | |||
@@ -30,13 +29,14 @@ void compile_file_plan::compile(const build_env& env) const { | |||
fs::relative(_source.path, _source.basis_path).string()); | |||
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()); | |||
if (!compile_res.okay()) { | |||
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())); | |||
} | |||
@@ -48,9 +48,11 @@ void compile_file_plan::compile(const build_env& env) const { | |||
if (!compile_res.output.empty()) { | |||
spdlog::warn("While compiling file {} [{}]:\n{}", | |||
_source.path.string(), | |||
quote_command(cmd), | |||
quote_command(cmd.command), | |||
compile_res.output); | |||
} | |||
return std::nullopt; | |||
} | |||
fs::path compile_file_plan::calc_object_file_path(const build_env& env) const noexcept { |
@@ -2,6 +2,7 @@ | |||
#include <dds/build/plan/base.hpp> | |||
#include <dds/source.hpp> | |||
#include <dds/toolchain/deps.hpp> | |||
#include <memory> | |||
@@ -55,13 +56,13 @@ public: | |||
, _qualifier(qual) | |||
, _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; } | |||
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 |
@@ -12,10 +12,10 @@ void dds::generate_compdb(const build_plan& plan, build_env_ref env) { | |||
auto compdb = nlohmann::json::array(); | |||
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({ | |||
{"directory", env.output_root.string()}, | |||
{"arguments", command}, | |||
{"arguments", cmd_info.command}, | |||
{"file", cf.source_path().string()}, | |||
}); | |||
compdb.push_back(std::move(entry)); |
@@ -0,0 +1,21 @@ | |||
#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 |
@@ -90,6 +90,7 @@ toolchain dds::parse_toolchain_dds(const lm::pair_list& pairs, strv context) { | |||
opt_string obj_suffix; | |||
opt_string exe_prefix; | |||
opt_string exe_suffix; | |||
opt_string deps_mode_str; | |||
optional<bool> do_debug; | |||
optional<bool> do_optimize; | |||
opt_str_seq include_template; | |||
@@ -128,6 +129,7 @@ toolchain dds::parse_toolchain_dds(const lm::pair_list& pairs, strv context) { | |||
lm::read_bool("Debug", do_debug), | |||
// Miscellaneous | |||
read_argv{"Compiler-Launcher", compile_launcher}, | |||
lm::read_opt("Deps-Mode", deps_mode_str), | |||
// Command templates | |||
read_argv{"C-Compile-File", c_compile_file}, | |||
read_argv{"C++-Compile-File", cxx_compile_file}, | |||
@@ -170,6 +172,26 @@ toolchain dds::parse_toolchain_dds(const lm::pair_list& pairs, strv context) { | |||
bool is_msvc = compiler_id_e == msvc; | |||
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 | |||
auto get_compiler = [&](language lang) -> string { | |||
if (lang == language::cxx && cxx_compiler_fpath) { | |||
@@ -356,10 +378,10 @@ toolchain dds::parse_toolchain_dds(const lm::pair_list& pairs, strv context) { | |||
rt_lib = "/MTd"; | |||
} | |||
ret.emplace_back(rt_lib); | |||
extend(ret, {"/nologo", "<FLAGS>", "/permissive-", "/c", "<IN>", "/Fo<OUT>"}); | |||
if (lang == language::cxx) { | |||
extend(ret, {"/EHsc"}); | |||
} | |||
extend(ret, {"/nologo", "/permissive-", "<FLAGS>", "/c", "<IN>", "/Fo<OUT>"}); | |||
} else if (is_gnu_like) { | |||
if (do_optimize.has_value() && *do_optimize) { | |||
extend(ret, {"-O2"}); | |||
@@ -382,6 +404,8 @@ toolchain dds::parse_toolchain_dds(const lm::pair_list& pairs, strv context) { | |||
return ret; | |||
}; | |||
tc.deps_mode = deps_mode; | |||
tc.c_compile = read_opt(c_compile_file, [&] { | |||
string_seq c; | |||
if (compile_launcher) { | |||
@@ -408,7 +432,7 @@ toolchain dds::parse_toolchain_dds(const lm::pair_list& pairs, strv context) { | |||
} | |||
if (is_gnu_like) { | |||
return {"-I", "<PATH>"}; | |||
} else if (compiler_id == "MSVC") { | |||
} else if (is_msvc) { | |||
return {"/I", "<PATH>"}; | |||
} | |||
assert(false && "Include-Template deduction failed"); | |||
@@ -421,7 +445,7 @@ toolchain dds::parse_toolchain_dds(const lm::pair_list& pairs, strv context) { | |||
} | |||
if (is_gnu_like) { | |||
return {"-D", "<DEF>"}; | |||
} else if (compiler_id == "MSVC") { | |||
} else if (is_msvc) { | |||
return {"/D", "<DEF>"}; | |||
} | |||
assert(false && "Define-Template deduction failed"); |
@@ -18,12 +18,12 @@ void check_tc_compile(std::string_view tc_content, | |||
cf.source_path = "foo.cpp"; | |||
cf.out_path = "foo.o"; | |||
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); | |||
cf.enable_warnings = true; | |||
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); | |||
dds::archive_spec ar_spec; | |||
@@ -45,37 +45,37 @@ void check_tc_compile(std::string_view tc_content, | |||
TEST_CASE("Generating toolchain commands") { | |||
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 " | |||
"-c foo.cpp -ofoo.o", | |||
"-MD -MF foo.o.d -c foo.cpp -ofoo.o", | |||
"ar rcs stuff.a foo.o bar.o", | |||
"g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -lstdc++fs -omeow.exe"); | |||
check_tc_compile( | |||
"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 " | |||
"-c foo.cpp -ofoo.o", | |||
"-MD -MF foo.o.d -c foo.cpp -ofoo.o", | |||
"ar rcs stuff.a foo.o bar.o", | |||
"g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -lstdc++fs -omeow.exe -g"); | |||
check_tc_compile( | |||
"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 " | |||
"-c foo.cpp -ofoo.o", | |||
"-MD -MF foo.o.d -c foo.cpp -ofoo.o", | |||
"ar rcs stuff.a foo.o bar.o", | |||
"g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -lstdc++fs -omeow.exe -O2 -g"); | |||
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", | |||
"cl.exe /nologo /EHsc foo.o bar.a /Femeow.exe /MT"); | |||
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", | |||
"cl.exe /nologo /EHsc foo.o bar.a /Femeow.exe /Z7 /DEBUG /MTd"); | |||
@@ -87,31 +87,37 @@ TEST_CASE("Generating toolchain commands") { | |||
cfs.source_path = "foo.cpp"; | |||
cfs.out_path = "foo.o"; | |||
auto cmd = tc.create_compile_command(cfs); | |||
CHECK(cmd | |||
CHECK(cmd.command | |||
== std::vector<std::string>{"g++", | |||
"-fPIC", | |||
"-fdiagnostics-color", | |||
"-pthread", | |||
"-MD", | |||
"-MF", | |||
"foo.o.d", | |||
"-c", | |||
"foo.cpp", | |||
"-ofoo.o"}); | |||
cfs.definitions.push_back("FOO=BAR"); | |||
cmd = tc.create_compile_command(cfs); | |||
CHECK(cmd | |||
CHECK(cmd.command | |||
== std::vector<std::string>{"g++", | |||
"-fPIC", | |||
"-fdiagnostics-color", | |||
"-pthread", | |||
"-D", | |||
"FOO=BAR", | |||
"-MD", | |||
"-MF", | |||
"foo.o.d", | |||
"-c", | |||
"foo.cpp", | |||
"-ofoo.o"}); | |||
cfs.include_dirs.push_back("fake-dir"); | |||
cmd = tc.create_compile_command(cfs); | |||
CHECK(cmd | |||
CHECK(cmd.command | |||
== std::vector<std::string>{"g++", | |||
"-fPIC", | |||
"-fdiagnostics-color", | |||
@@ -120,6 +126,9 @@ TEST_CASE("Generating toolchain commands") { | |||
"fake-dir", | |||
"-D", | |||
"FOO=BAR", | |||
"-MD", | |||
"-MF", | |||
"foo.o.d", | |||
"-c", | |||
"foo.cpp", | |||
"-ofoo.o"}); |
@@ -1,5 +1,7 @@ | |||
#pragma once | |||
#include <dds/toolchain/deps.hpp> | |||
#include <string> | |||
#include <vector> | |||
@@ -24,6 +26,8 @@ struct toolchain_prep { | |||
std::string exe_prefix; | |||
std::string exe_suffix; | |||
enum deps_mode deps_mode; | |||
toolchain realize() const; | |||
}; | |||
@@ -32,6 +32,7 @@ toolchain toolchain::realize(const toolchain_prep& prep) { | |||
ret._object_suffix = prep.object_suffix; | |||
ret._exe_prefix = prep.exe_prefix; | |||
ret._exe_suffix = prep.exe_suffix; | |||
ret._deps_mode = prep.deps_mode; | |||
return ret; | |||
} | |||
@@ -99,9 +100,12 @@ vector<string> toolchain::definition_args(std::string_view s) const noexcept { | |||
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; | |||
using namespace std::literals; | |||
language lang = spec.lang; | |||
if (lang == language::automatic) { | |||
if (spec.source_path.extension() == ".c" || spec.source_path.extension() == ".C") { | |||
@@ -127,6 +131,16 @@ vector<string> toolchain::create_compile_command(const compile_file_spec& spec) | |||
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; | |||
for (auto arg : cmd_template) { | |||
if (arg == "<FLAGS>") { | |||
@@ -137,7 +151,7 @@ vector<string> toolchain::create_compile_command(const compile_file_spec& spec) | |||
command.push_back(arg); | |||
} | |||
} | |||
return command; | |||
return {command, gnu_depfile_path}; | |||
} | |||
vector<string> toolchain::create_archive_command(const archive_spec& spec) const noexcept { |
@@ -1,5 +1,6 @@ | |||
#pragma once | |||
#include <dds/toolchain/deps.hpp> | |||
#include <dds/util/fs.hpp> | |||
#include <optional> | |||
@@ -26,6 +27,11 @@ struct compile_file_spec { | |||
bool enable_warnings = false; | |||
}; | |||
struct compile_command_info { | |||
std::vector<std::string> command; | |||
std::optional<fs::path> gnu_depfile_path; | |||
}; | |||
struct archive_spec { | |||
std::vector<fs::path> input_files; | |||
fs::path out_path; | |||
@@ -56,6 +62,8 @@ class toolchain { | |||
std::string _exe_prefix; | |||
std::string _exe_suffix; | |||
enum deps_mode _deps_mode; | |||
public: | |||
toolchain() = default; | |||
@@ -64,10 +72,11 @@ public: | |||
auto& archive_suffix() const noexcept { return _archive_suffix; } | |||
auto& object_suffix() const noexcept { return _object_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> 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_link_executable_command(const link_exe_spec&) const noexcept; | |||