@@ -353,6 +353,14 @@ of an external library. | |||
Override the *command template* for the flags to set a preprocessor definition. | |||
``tty_flags`` | |||
------------- | |||
Supply additional flags when compiling/linking that will only be applied if | |||
standard output is an ANSI-capable terminal. (e.g. On GNU and Clang this will | |||
be ``-fdiagnostics-color`` by default.) | |||
``obj_prefix``, ``obj_suffix``, ``archive_prefix``, ``archive_suffix``, | |||
``exe_prefix``, and ``exe_suffix`` | |||
---------------------------------- |
@@ -153,6 +153,10 @@ | |||
"description": "Set the command template for linking executable binaries", | |||
"$ref": "#/definitions/command_line_flags" | |||
}, | |||
"tty_flags": { | |||
"description": "Set command line flags that will be applied only if stdout is an ANSI-capable terminal", | |||
"$ref": "#/definitions/command_line_flags" | |||
}, | |||
"obj_prefix": { | |||
"description": "Set the filename prefix for object files", | |||
"type": "string" |
@@ -6,6 +6,7 @@ | |||
#include <dds/compdb.hpp> | |||
#include <dds/error/errors.hpp> | |||
#include <dds/usage_reqs.hpp> | |||
#include <dds/util/output.hpp> | |||
#include <dds/util/time.hpp> | |||
#include <spdlog/spdlog.h> | |||
@@ -214,7 +215,15 @@ void builder::build(const build_params& params) const { | |||
state st; | |||
auto plan = prepare_build_plan(st, _sdists); | |||
auto ureqs = prepare_ureqs(plan, params.toolchain, params.out_root); | |||
build_env env{params.toolchain, params.out_root, db, ureqs}; | |||
build_env env{ | |||
params.toolchain, | |||
params.out_root, | |||
db, | |||
toolchain_knobs{ | |||
.is_tty = stdout_is_a_tty(), | |||
}, | |||
ureqs, | |||
}; | |||
if (st.generate_catch2_main) { | |||
auto catch_lib = prepare_test_driver(params, test_lib::catch_main, env); |
@@ -24,7 +24,7 @@ void create_archive_plan::archive(const build_env& env) const { | |||
archive_spec ar; | |||
ar.input_files = std::move(objects); | |||
ar.out_path = env.output_root / calc_archive_file_path(env.toolchain); | |||
auto ar_cmd = env.toolchain.create_archive_command(ar); | |||
auto ar_cmd = env.toolchain.create_archive_command(ar, env.knobs); | |||
// `out_relpath` is purely for the benefit of the user to have a short name | |||
// in the logs |
@@ -12,6 +12,8 @@ struct build_env { | |||
fs::path output_root; | |||
database& db; | |||
toolchain_knobs knobs; | |||
const usage_requirement_map& ureqs; | |||
}; | |||
@@ -26,7 +26,7 @@ compile_command_info compile_file_plan::generate_compile_command(build_env_ref e | |||
extend(spec.external_include_dirs, env.ureqs.include_paths(use)); | |||
} | |||
extend(spec.definitions, _rules.defs()); | |||
return env.toolchain.create_compile_command(spec); | |||
return env.toolchain.create_compile_command(spec, env.knobs); | |||
} | |||
fs::path compile_file_plan::calc_object_file_path(const build_env& env) const noexcept { |
@@ -43,7 +43,7 @@ void link_executable_plan::link(build_env_ref env, const library_plan& lib) cons | |||
std::reverse(spec.inputs.begin(), spec.inputs.end()); | |||
// Do it! | |||
const auto link_command = env.toolchain.create_link_executable_command(spec); | |||
const auto link_command = env.toolchain.create_link_executable_command(spec, env.knobs); | |||
fs::create_directories(spec.output.parent_path()); | |||
auto msg = fmt::format("[{}] Link: {:30}", | |||
lib.qualified_name(), |
@@ -80,6 +80,7 @@ toolchain dds::parse_toolchain_json_data(const json5::data& dat, std::string_vie | |||
opt_string_seq cxx_compile_file; | |||
opt_string_seq create_archive; | |||
opt_string_seq link_executable; | |||
opt_string_seq tty_flags; | |||
// For copy-pasting convenience: ‘{}’ | |||
@@ -150,6 +151,7 @@ toolchain dds::parse_toolchain_json_data(const json5::data& dat, std::string_vie | |||
KEY_EXTEND_FLAGS(cxx_compile_file), | |||
KEY_EXTEND_FLAGS(create_archive), | |||
KEY_EXTEND_FLAGS(link_executable), | |||
KEY_EXTEND_FLAGS(tty_flags), | |||
KEY_STRING(obj_prefix), | |||
KEY_STRING(obj_suffix), | |||
KEY_STRING(archive_prefix), | |||
@@ -174,6 +176,7 @@ toolchain dds::parse_toolchain_json_data(const json5::data& dat, std::string_vie | |||
"archive_suffix", | |||
"exe_prefix", | |||
"exe_suffix", | |||
"tty_flags", | |||
}); | |||
fail(context, | |||
"Unknown toolchain advanced-config key ‘{}’ (Did you mean ‘{}’?)", | |||
@@ -450,14 +453,7 @@ toolchain dds::parse_toolchain_json_data(const json5::data& dat, std::string_vie | |||
if (do_debug.has_value() && *do_debug) { | |||
extend(ret, {"-g"}); | |||
} | |||
extend(ret, | |||
{"-fPIC", | |||
"-fdiagnostics-color", | |||
"-pthread", | |||
"<FLAGS>", | |||
"-c", | |||
"<IN>", | |||
"-o<OUT>"}); | |||
extend(ret, {"-fPIC", "-pthread", "<FLAGS>", "-c", "<IN>", "-o<OUT>"}); | |||
} | |||
if (common_flags) { | |||
extend(ret, *common_flags); | |||
@@ -619,7 +615,6 @@ toolchain dds::parse_toolchain_json_data(const json5::data& dat, std::string_vie | |||
} else if (is_gnu_like) { | |||
ret = {get_compiler_executable_path(language::cxx), | |||
"-fPIC", | |||
"-fdiagnostics-color", | |||
"<IN>", | |||
"-pthread", | |||
"-o<OUT>"}; | |||
@@ -631,5 +626,22 @@ toolchain dds::parse_toolchain_json_data(const json5::data& dat, std::string_vie | |||
return ret; | |||
}); | |||
tc.tty_flags = read_opt(tty_flags, [&]() -> string_seq { | |||
if (!compiler_id) { | |||
// Don't deduce any flags. This is a non-error, as these flags should be purely | |||
// aesthetic | |||
return {}; | |||
} | |||
if (is_msvc) { | |||
// MSVC doesn't have any special TTY flags (yet...) | |||
return {}; | |||
} else if (is_gnu_like) { | |||
return {"-fdiagnostics-color"}; | |||
} else { | |||
assert(false && "Impossible compiler_id while deducing `tty_flags`"); | |||
std::terminate(); | |||
} | |||
}); | |||
return tc.realize(); | |||
} |
@@ -15,12 +15,12 @@ void check_tc_compile(std::string_view tc_content, | |||
dds::compile_file_spec cf; | |||
cf.source_path = "foo.cpp"; | |||
cf.out_path = "foo.o"; | |||
auto cf_cmd = tc.create_compile_command(cf); | |||
auto cf_cmd = tc.create_compile_command(cf, dds::toolchain_knobs{}); | |||
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 = tc.create_compile_command(cf, dds::toolchain_knobs{}); | |||
cf_cmd_str = dds::quote_command(cf_cmd.command); | |||
CHECK(cf_cmd_str == expected_compile_warnings); | |||
@@ -28,7 +28,7 @@ void check_tc_compile(std::string_view tc_content, | |||
ar_spec.input_files.push_back("foo.o"); | |||
ar_spec.input_files.push_back("bar.o"); | |||
ar_spec.out_path = "stuff.a"; | |||
auto ar_cmd = tc.create_archive_command(ar_spec); | |||
auto ar_cmd = tc.create_archive_command(ar_spec, dds::toolchain_knobs{}); | |||
auto ar_cmd_str = dds::quote_command(ar_cmd); | |||
CHECK(ar_cmd_str == expected_ar); | |||
@@ -36,7 +36,7 @@ void check_tc_compile(std::string_view tc_content, | |||
exe_spec.inputs.push_back("foo.o"); | |||
exe_spec.inputs.push_back("bar.a"); | |||
exe_spec.output = "meow.exe"; | |||
auto exe_cmd = tc.create_link_executable_command(exe_spec); | |||
auto exe_cmd = tc.create_link_executable_command(exe_spec, dds::toolchain_knobs{}); | |||
auto exe_cmd_str = dds::quote_command(exe_cmd); | |||
CHECK(exe_cmd_str == expected_exe); | |||
} | |||
@@ -44,30 +44,27 @@ void check_tc_compile(std::string_view tc_content, | |||
} // namespace | |||
TEST_CASE("Generating toolchain commands") { | |||
check_tc_compile( | |||
"{compiler_id: 'gnu'}", | |||
"g++ -fPIC -fdiagnostics-color -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | |||
"g++ -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | |||
"-MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | |||
"ar rcs stuff.a foo.o bar.o", | |||
"g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -omeow.exe"); | |||
check_tc_compile( | |||
"{compiler_id: 'gnu', debug: true}", | |||
"g++ -g -fPIC -fdiagnostics-color -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | |||
"g++ -g -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | |||
"-MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | |||
"ar rcs stuff.a foo.o bar.o", | |||
"g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -omeow.exe -g"); | |||
check_tc_compile( | |||
"{compiler_id: 'gnu', debug: true, optimize: true}", | |||
"g++ -O2 -g -fPIC -fdiagnostics-color -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp " | |||
"-ofoo.o", | |||
"g++ -O2 -g -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | |||
"-MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | |||
"ar rcs stuff.a foo.o bar.o", | |||
"g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -omeow.exe -O2 -g"); | |||
check_tc_compile("{compiler_id: 'gnu'}", | |||
"g++ -fPIC -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | |||
"g++ -fPIC -pthread -Wall -Wextra -Wpedantic -Wconversion " | |||
"-MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | |||
"ar rcs stuff.a foo.o bar.o", | |||
"g++ -fPIC foo.o bar.a -pthread -omeow.exe"); | |||
check_tc_compile("{compiler_id: 'gnu', debug: true}", | |||
"g++ -g -fPIC -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | |||
"g++ -g -fPIC -pthread -Wall -Wextra -Wpedantic -Wconversion " | |||
"-MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | |||
"ar rcs stuff.a foo.o bar.o", | |||
"g++ -fPIC foo.o bar.a -pthread -omeow.exe -g"); | |||
check_tc_compile("{compiler_id: 'gnu', debug: true, optimize: true}", | |||
"g++ -O2 -g -fPIC -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp " | |||
"-ofoo.o", | |||
"g++ -O2 -g -fPIC -pthread -Wall -Wextra -Wpedantic -Wconversion " | |||
"-MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | |||
"ar rcs stuff.a foo.o bar.o", | |||
"g++ -fPIC foo.o bar.a -pthread -omeow.exe -O2 -g"); | |||
check_tc_compile("{compiler_id: 'msvc'}", | |||
"cl.exe /MT /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | |||
@@ -91,17 +88,15 @@ TEST_CASE("Generating toolchain commands") { | |||
} | |||
TEST_CASE("Manipulate a toolchain and file compilation") { | |||
auto tc = dds::parse_toolchain_json5("{compiler_id: 'gnu'}"); | |||
dds::compile_file_spec cfs; | |||
cfs.source_path = "foo.cpp"; | |||
cfs.out_path = "foo.o"; | |||
auto cmd = tc.create_compile_command(cfs); | |||
auto cmd = tc.create_compile_command(cfs, dds::toolchain_knobs{}); | |||
CHECK(cmd.command | |||
== std::vector<std::string>{"g++", | |||
"-fPIC", | |||
"-fdiagnostics-color", | |||
"-pthread", | |||
"-MD", | |||
"-MF", | |||
@@ -113,12 +108,12 @@ TEST_CASE("Manipulate a toolchain and file compilation") { | |||
"-ofoo.o"}); | |||
cfs.definitions.push_back("FOO=BAR"); | |||
cmd = tc.create_compile_command(cfs); | |||
cmd = tc.create_compile_command(cfs, dds::toolchain_knobs{.is_tty = true}); | |||
CHECK(cmd.command | |||
== std::vector<std::string>{"g++", | |||
"-fPIC", | |||
"-fdiagnostics-color", | |||
"-pthread", | |||
"-fdiagnostics-color", | |||
"-D", | |||
"FOO=BAR", | |||
"-MD", | |||
@@ -131,11 +126,10 @@ TEST_CASE("Manipulate a toolchain and file compilation") { | |||
"-ofoo.o"}); | |||
cfs.include_dirs.push_back("fake-dir"); | |||
cmd = tc.create_compile_command(cfs); | |||
cmd = tc.create_compile_command(cfs, dds::toolchain_knobs{}); | |||
CHECK(cmd.command | |||
== std::vector<std::string>{"g++", | |||
"-fPIC", | |||
"-fdiagnostics-color", | |||
"-pthread", | |||
"-I", | |||
"fake-dir", | |||
@@ -149,4 +143,4 @@ TEST_CASE("Manipulate a toolchain and file compilation") { | |||
"-c", | |||
"foo.cpp", | |||
"-ofoo.o"}); | |||
} | |||
} |
@@ -19,6 +19,7 @@ struct toolchain_prep { | |||
string_seq link_archive; | |||
string_seq link_exe; | |||
string_seq warning_flags; | |||
string_seq tty_flags; | |||
std::string archive_prefix; | |||
std::string archive_suffix; |
@@ -36,6 +36,7 @@ toolchain toolchain::realize(const toolchain_prep& prep) { | |||
ret._exe_prefix = prep.exe_prefix; | |||
ret._exe_suffix = prep.exe_suffix; | |||
ret._deps_mode = prep.deps_mode; | |||
ret._tty_flags = prep.tty_flags; | |||
return ret; | |||
} | |||
@@ -51,10 +52,8 @@ vector<string> toolchain::definition_args(std::string_view s) const noexcept { | |||
return replace(_def_template, "<DEF>", s); | |||
} | |||
compile_command_info | |||
toolchain::create_compile_command(const compile_file_spec& spec) const noexcept { | |||
vector<string> flags; | |||
compile_command_info toolchain::create_compile_command(const compile_file_spec& spec, | |||
toolchain_knobs knobs) const noexcept { | |||
using namespace std::literals; | |||
language lang = spec.lang; | |||
@@ -66,7 +65,10 @@ toolchain::create_compile_command(const compile_file_spec& spec) const noexcept | |||
} | |||
} | |||
auto& cmd_template = lang == language::c ? _c_compile : _cxx_compile; | |||
vector<string> flags; | |||
if (knobs.is_tty) { | |||
extend(flags, _tty_flags); | |||
} | |||
for (auto&& inc_dir : spec.include_dirs) { | |||
auto inc_args = include_args(inc_dir); | |||
@@ -103,6 +105,7 @@ toolchain::create_compile_command(const compile_file_spec& spec) const noexcept | |||
} | |||
vector<string> command; | |||
auto& cmd_template = lang == language::c ? _c_compile : _cxx_compile; | |||
for (auto arg : cmd_template) { | |||
if (arg == "<FLAGS>") { | |||
extend(command, flags); | |||
@@ -115,7 +118,8 @@ toolchain::create_compile_command(const compile_file_spec& spec) const noexcept | |||
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, | |||
toolchain_knobs) const noexcept { | |||
vector<string> cmd; | |||
for (auto& arg : _link_archive) { | |||
if (arg == "<IN>") { | |||
@@ -130,7 +134,8 @@ vector<string> toolchain::create_archive_command(const archive_spec& spec) const | |||
return cmd; | |||
} | |||
vector<string> toolchain::create_link_executable_command(const link_exe_spec& spec) const noexcept { | |||
vector<string> toolchain::create_link_executable_command(const link_exe_spec& spec, | |||
toolchain_knobs) const noexcept { | |||
vector<string> cmd; | |||
for (auto& arg : _link_exe) { | |||
if (arg == "<IN>") { |
@@ -16,6 +16,10 @@ enum class language { | |||
cxx, | |||
}; | |||
struct toolchain_knobs { | |||
bool is_tty = false; | |||
}; | |||
struct compile_file_spec { | |||
fs::path source_path; | |||
fs::path out_path; | |||
@@ -54,6 +58,7 @@ class toolchain { | |||
string_seq _link_archive; | |||
string_seq _link_exe; | |||
string_seq _warning_flags; | |||
string_seq _tty_flags; | |||
std::string _archive_prefix; | |||
std::string _archive_suffix; | |||
@@ -77,9 +82,12 @@ public: | |||
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> external_include_args(const fs::path& p) 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; | |||
compile_command_info create_compile_command(const compile_file_spec&, | |||
toolchain_knobs) const noexcept; | |||
std::vector<std::string> create_archive_command(const archive_spec&, | |||
toolchain_knobs) const noexcept; | |||
std::vector<std::string> create_link_executable_command(const link_exe_spec&, | |||
toolchain_knobs) const noexcept; | |||
static std::optional<toolchain> get_builtin(std::string_view key) noexcept; | |||
static std::optional<toolchain> get_default(); |
@@ -0,0 +1,7 @@ | |||
#pragma once | |||
namespace dds { | |||
bool stdout_is_a_tty() noexcept; | |||
} // namespace dds |
@@ -0,0 +1,11 @@ | |||
#if !_WIN32 | |||
#include <dds/util/output.hpp> | |||
#include <unistd.h> | |||
using namespace dds; | |||
bool dds::stdout_is_a_tty() noexcept { return ::isatty(STDOUT_FILENO) != 0; } | |||
#endif |
@@ -0,0 +1,10 @@ | |||
#if _WIN32 | |||
#include <dds/util/output.hpp> | |||
bool dds::stdout_is_a_tty() noexcept { | |||
// XXX: Newer Windows consoles support ANSI color, so this should be made smarter | |||
return false; | |||
} | |||
#endif |
@@ -1,10 +1,9 @@ | |||
Compiler-ID: GNU | |||
C++-Version: C++17 | |||
C-Compiler: gcc-9 | |||
C++-Compiler: g++-9 | |||
# Range-v3 0.10.0 contains an accidental conversion warning | |||
Flags: -D SPDLOG_COMPILED_LIB -Werror=return-type -Wno-conversion | |||
C++-Flags: -fconcepts | |||
Flags: -D SPDLOG_COMPILED_LIB -Werror=return-type -Wno-sign-compare -Wno-conversion | |||
C++-Flags: -fconcepts -std=c++2a | |||
# Link-Flags: -static-libgcc -static-libstdc++ | |||
Debug: True | |||
#Optimize: True |
@@ -3,13 +3,14 @@ | |||
"compiler_id": "gnu", | |||
"c_compiler": "gcc-9", | |||
"cxx_compiler": "g++-9", | |||
"cxx_version": "c++17", | |||
// "cxx_version": "c++17", | |||
"flags": [ | |||
"-DSPDLOG_COMPILED_LIB", // Required to use a compiled spdlog | |||
"-Werror=return-type" | |||
"-Werror=return-type", | |||
], | |||
"cxx_flags": [ | |||
"-fconcepts" | |||
"-fconcepts", | |||
"-std=c++2a", | |||
], | |||
// "debug": true, | |||
"optimize": true, |