Browse Source

Add `tty_flags`, and only pass -fdiagnostics-color when output is a TTY

default_compile_flags
vector-of-bool 4 years ago
parent
commit
64706f6da9
17 changed files with 136 additions and 65 deletions
  1. +8
    -0
      docs/guide/toolchains.rst
  2. +4
    -0
      res/toolchain-schema.json
  3. +10
    -1
      src/dds/build/builder.cpp
  4. +1
    -1
      src/dds/build/plan/archive.cpp
  5. +2
    -0
      src/dds/build/plan/base.hpp
  6. +1
    -1
      src/dds/build/plan/compile_file.cpp
  7. +1
    -1
      src/dds/build/plan/exe.cpp
  8. +21
    -9
      src/dds/toolchain/from_json.cpp
  9. +30
    -36
      src/dds/toolchain/from_json.test.cpp
  10. +1
    -0
      src/dds/toolchain/prep.hpp
  11. +12
    -7
      src/dds/toolchain/toolchain.cpp
  12. +11
    -3
      src/dds/toolchain/toolchain.hpp
  13. +7
    -0
      src/dds/util/output.hpp
  14. +11
    -0
      src/dds/util/output.nix.cpp
  15. +10
    -0
      src/dds/util/output.win.cpp
  16. +2
    -3
      tools/gcc-9.dds
  17. +4
    -3
      tools/gcc-9.jsonc

+ 8
- 0
docs/guide/toolchains.rst View File

Override the *command template* for the flags to set a preprocessor definition. 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``, ``obj_prefix``, ``obj_suffix``, ``archive_prefix``, ``archive_suffix``,
``exe_prefix``, and ``exe_suffix`` ``exe_prefix``, and ``exe_suffix``
---------------------------------- ----------------------------------

+ 4
- 0
res/toolchain-schema.json View File

"description": "Set the command template for linking executable binaries", "description": "Set the command template for linking executable binaries",
"$ref": "#/definitions/command_line_flags" "$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": { "obj_prefix": {
"description": "Set the filename prefix for object files", "description": "Set the filename prefix for object files",
"type": "string" "type": "string"

+ 10
- 1
src/dds/build/builder.cpp View File

#include <dds/compdb.hpp> #include <dds/compdb.hpp>
#include <dds/error/errors.hpp> #include <dds/error/errors.hpp>
#include <dds/usage_reqs.hpp> #include <dds/usage_reqs.hpp>
#include <dds/util/output.hpp>
#include <dds/util/time.hpp> #include <dds/util/time.hpp>


#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
state st; state st;
auto plan = prepare_build_plan(st, _sdists); auto plan = prepare_build_plan(st, _sdists);
auto ureqs = prepare_ureqs(plan, params.toolchain, params.out_root); 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) { if (st.generate_catch2_main) {
auto catch_lib = prepare_test_driver(params, test_lib::catch_main, env); auto catch_lib = prepare_test_driver(params, test_lib::catch_main, env);

+ 1
- 1
src/dds/build/plan/archive.cpp View File

archive_spec ar; archive_spec ar;
ar.input_files = std::move(objects); ar.input_files = std::move(objects);
ar.out_path = env.output_root / calc_archive_file_path(env.toolchain); 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 // `out_relpath` is purely for the benefit of the user to have a short name
// in the logs // in the logs

+ 2
- 0
src/dds/build/plan/base.hpp View File

fs::path output_root; fs::path output_root;
database& db; database& db;


toolchain_knobs knobs;

const usage_requirement_map& ureqs; const usage_requirement_map& ureqs;
}; };



+ 1
- 1
src/dds/build/plan/compile_file.cpp View File

extend(spec.external_include_dirs, env.ureqs.include_paths(use)); extend(spec.external_include_dirs, env.ureqs.include_paths(use));
} }
extend(spec.definitions, _rules.defs()); 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 { fs::path compile_file_plan::calc_object_file_path(const build_env& env) const noexcept {

+ 1
- 1
src/dds/build/plan/exe.cpp View File

std::reverse(spec.inputs.begin(), spec.inputs.end()); std::reverse(spec.inputs.begin(), spec.inputs.end());


// Do it! // 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()); fs::create_directories(spec.output.parent_path());
auto msg = fmt::format("[{}] Link: {:30}", auto msg = fmt::format("[{}] Link: {:30}",
lib.qualified_name(), lib.qualified_name(),

+ 21
- 9
src/dds/toolchain/from_json.cpp View File

opt_string_seq cxx_compile_file; opt_string_seq cxx_compile_file;
opt_string_seq create_archive; opt_string_seq create_archive;
opt_string_seq link_executable; opt_string_seq link_executable;
opt_string_seq tty_flags;


// For copy-pasting convenience: ‘{}’ // For copy-pasting convenience: ‘{}’


KEY_EXTEND_FLAGS(cxx_compile_file), KEY_EXTEND_FLAGS(cxx_compile_file),
KEY_EXTEND_FLAGS(create_archive), KEY_EXTEND_FLAGS(create_archive),
KEY_EXTEND_FLAGS(link_executable), KEY_EXTEND_FLAGS(link_executable),
KEY_EXTEND_FLAGS(tty_flags),
KEY_STRING(obj_prefix), KEY_STRING(obj_prefix),
KEY_STRING(obj_suffix), KEY_STRING(obj_suffix),
KEY_STRING(archive_prefix), KEY_STRING(archive_prefix),
"archive_suffix", "archive_suffix",
"exe_prefix", "exe_prefix",
"exe_suffix", "exe_suffix",
"tty_flags",
}); });
fail(context, fail(context,
"Unknown toolchain advanced-config key ‘{}’ (Did you mean ‘{}’?)", "Unknown toolchain advanced-config key ‘{}’ (Did you mean ‘{}’?)",
if (do_debug.has_value() && *do_debug) { if (do_debug.has_value() && *do_debug) {
extend(ret, {"-g"}); 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) { if (common_flags) {
extend(ret, *common_flags); extend(ret, *common_flags);
} else if (is_gnu_like) { } else if (is_gnu_like) {
ret = {get_compiler_executable_path(language::cxx), ret = {get_compiler_executable_path(language::cxx),
"-fPIC", "-fPIC",
"-fdiagnostics-color",
"<IN>", "<IN>",
"-pthread", "-pthread",
"-o<OUT>"}; "-o<OUT>"};
return ret; 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(); return tc.realize();
} }

+ 30
- 36
src/dds/toolchain/from_json.test.cpp View File

dds::compile_file_spec cf; dds::compile_file_spec cf;
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, dds::toolchain_knobs{});
auto cf_cmd_str = dds::quote_command(cf_cmd.command); 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, dds::toolchain_knobs{});
cf_cmd_str = dds::quote_command(cf_cmd.command); cf_cmd_str = dds::quote_command(cf_cmd.command);
CHECK(cf_cmd_str == expected_compile_warnings); CHECK(cf_cmd_str == expected_compile_warnings);


ar_spec.input_files.push_back("foo.o"); ar_spec.input_files.push_back("foo.o");
ar_spec.input_files.push_back("bar.o"); ar_spec.input_files.push_back("bar.o");
ar_spec.out_path = "stuff.a"; 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); auto ar_cmd_str = dds::quote_command(ar_cmd);
CHECK(ar_cmd_str == expected_ar); CHECK(ar_cmd_str == expected_ar);


exe_spec.inputs.push_back("foo.o"); exe_spec.inputs.push_back("foo.o");
exe_spec.inputs.push_back("bar.a"); exe_spec.inputs.push_back("bar.a");
exe_spec.output = "meow.exe"; 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); auto exe_cmd_str = dds::quote_command(exe_cmd);
CHECK(exe_cmd_str == expected_exe); CHECK(exe_cmd_str == expected_exe);
} }
} // namespace } // namespace


TEST_CASE("Generating toolchain commands") { 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'}", check_tc_compile("{compiler_id: 'msvc'}",
"cl.exe /MT /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", "cl.exe /MT /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o",
} }


TEST_CASE("Manipulate a toolchain and file compilation") { TEST_CASE("Manipulate a toolchain and file compilation") {

auto tc = dds::parse_toolchain_json5("{compiler_id: 'gnu'}"); auto tc = dds::parse_toolchain_json5("{compiler_id: 'gnu'}");


dds::compile_file_spec cfs; dds::compile_file_spec cfs;
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, dds::toolchain_knobs{});
CHECK(cmd.command CHECK(cmd.command
== std::vector<std::string>{"g++", == std::vector<std::string>{"g++",
"-fPIC", "-fPIC",
"-fdiagnostics-color",
"-pthread", "-pthread",
"-MD", "-MD",
"-MF", "-MF",
"-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, dds::toolchain_knobs{.is_tty = true});
CHECK(cmd.command CHECK(cmd.command
== std::vector<std::string>{"g++", == std::vector<std::string>{"g++",
"-fPIC", "-fPIC",
"-fdiagnostics-color",
"-pthread", "-pthread",
"-fdiagnostics-color",
"-D", "-D",
"FOO=BAR", "FOO=BAR",
"-MD", "-MD",
"-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, dds::toolchain_knobs{});
CHECK(cmd.command CHECK(cmd.command
== std::vector<std::string>{"g++", == std::vector<std::string>{"g++",
"-fPIC", "-fPIC",
"-fdiagnostics-color",
"-pthread", "-pthread",
"-I", "-I",
"fake-dir", "fake-dir",
"-c", "-c",
"foo.cpp", "foo.cpp",
"-ofoo.o"}); "-ofoo.o"});
}
}

+ 1
- 0
src/dds/toolchain/prep.hpp View File

string_seq link_archive; string_seq link_archive;
string_seq link_exe; string_seq link_exe;
string_seq warning_flags; string_seq warning_flags;
string_seq tty_flags;


std::string archive_prefix; std::string archive_prefix;
std::string archive_suffix; std::string archive_suffix;

+ 12
- 7
src/dds/toolchain/toolchain.cpp View File

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; ret._deps_mode = prep.deps_mode;
ret._tty_flags = prep.tty_flags;
return ret; return ret;
} }


return replace(_def_template, "<DEF>", s); 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; using namespace std::literals;


language lang = spec.lang; language lang = spec.lang;
} }
} }


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) { for (auto&& inc_dir : spec.include_dirs) {
auto inc_args = include_args(inc_dir); auto inc_args = include_args(inc_dir);
} }


vector<string> command; vector<string> command;
auto& cmd_template = lang == language::c ? _c_compile : _cxx_compile;
for (auto arg : cmd_template) { for (auto arg : cmd_template) {
if (arg == "<FLAGS>") { if (arg == "<FLAGS>") {
extend(command, flags); extend(command, flags);
return {command, gnu_depfile_path}; 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; vector<string> cmd;
for (auto& arg : _link_archive) { for (auto& arg : _link_archive) {
if (arg == "<IN>") { if (arg == "<IN>") {
return cmd; 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; vector<string> cmd;
for (auto& arg : _link_exe) { for (auto& arg : _link_exe) {
if (arg == "<IN>") { if (arg == "<IN>") {

+ 11
- 3
src/dds/toolchain/toolchain.hpp View File

cxx, cxx,
}; };


struct toolchain_knobs {
bool is_tty = false;
};

struct compile_file_spec { struct compile_file_spec {
fs::path source_path; fs::path source_path;
fs::path out_path; fs::path out_path;
string_seq _link_archive; string_seq _link_archive;
string_seq _link_exe; string_seq _link_exe;
string_seq _warning_flags; string_seq _warning_flags;
string_seq _tty_flags;


std::string _archive_prefix; std::string _archive_prefix;
std::string _archive_suffix; std::string _archive_suffix;
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> external_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_builtin(std::string_view key) noexcept;
static std::optional<toolchain> get_default(); static std::optional<toolchain> get_default();

+ 7
- 0
src/dds/util/output.hpp View File

#pragma once

namespace dds {

bool stdout_is_a_tty() noexcept;

} // namespace dds

+ 11
- 0
src/dds/util/output.nix.cpp View File

#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

+ 10
- 0
src/dds/util/output.win.cpp View File

#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

+ 2
- 3
tools/gcc-9.dds View File

Compiler-ID: GNU Compiler-ID: GNU
C++-Version: C++17
C-Compiler: gcc-9 C-Compiler: gcc-9
C++-Compiler: g++-9 C++-Compiler: g++-9
# Range-v3 0.10.0 contains an accidental conversion warning # 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++ # Link-Flags: -static-libgcc -static-libstdc++
Debug: True Debug: True
#Optimize: True #Optimize: True

+ 4
- 3
tools/gcc-9.jsonc View File

"compiler_id": "gnu", "compiler_id": "gnu",
"c_compiler": "gcc-9", "c_compiler": "gcc-9",
"cxx_compiler": "g++-9", "cxx_compiler": "g++-9",
"cxx_version": "c++17",
// "cxx_version": "c++17",
"flags": [ "flags": [
"-DSPDLOG_COMPILED_LIB", // Required to use a compiled spdlog "-DSPDLOG_COMPILED_LIB", // Required to use a compiled spdlog
"-Werror=return-type"
"-Werror=return-type",
], ],
"cxx_flags": [ "cxx_flags": [
"-fconcepts"
"-fconcepts",
"-std=c++2a",
], ],
// "debug": true, // "debug": true,
"optimize": true, "optimize": true,

Loading…
Cancel
Save