| @@ -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, | |||