"patternProperties": { | "patternProperties": { | ||||
"^\\$": {} | "^\\$": {} | ||||
}, | }, | ||||
"$schema": "https://json-schema.org/draft/2019-09/schema", | |||||
"definitions": { | "definitions": { | ||||
"command_line_flags": { | "command_line_flags": { | ||||
"anyOf": [ | "anyOf": [ | ||||
"$ref": "#/definitions/command_line_flags" | "$ref": "#/definitions/command_line_flags" | ||||
}, | }, | ||||
"debug": { | "debug": { | ||||
"description": "Enable the generation of debug information", | |||||
"type": "boolean", | |||||
"default": true | |||||
"description": "Tweak the generation of debug information", | |||||
"default": true, | |||||
"oneOf": [ | |||||
{ | |||||
"type": "string", | |||||
"enum": [ | |||||
"embedded", | |||||
"split" | |||||
] | |||||
}, | |||||
{ | |||||
"type": "boolean" | |||||
} | |||||
] | |||||
}, | }, | ||||
"optimize": { | "optimize": { | ||||
"description": "Optimize generated code", | "description": "Optimize generated code", | ||||
"type": "boolean", | "type": "boolean", | ||||
"default": true | "default": true | ||||
}, | }, | ||||
"runtime": { | |||||
"description": "Select the runtime/stdlib modes", | |||||
"type": "object", | |||||
"additionalProperties": false, | |||||
"properties": { | |||||
"static": { | |||||
"type": "boolean", | |||||
"description": "If `true`, enable static stdlib/runtime linking" | |||||
}, | |||||
"debug": { | |||||
"type": "boolean", | |||||
"description": "If 'true', enable debug for the stdlib/runtime" | |||||
} | |||||
} | |||||
}, | |||||
"advanced": { | "advanced": { | ||||
"type": "object", | "type": "object", | ||||
"additionalProperties": false, | "additionalProperties": false, |
opt_string_seq link_flags; | opt_string_seq link_flags; | ||||
opt_string_seq warning_flags; | opt_string_seq warning_flags; | ||||
optional<bool> do_debug; | |||||
optional<bool> debug_bool; | |||||
opt_string debug_str; | |||||
optional<bool> do_optimize; | optional<bool> do_optimize; | ||||
optional<bool> runtime_static; | |||||
optional<bool> runtime_debug; | |||||
// Advanced-mode: | // Advanced-mode: | ||||
opt_string deps_mode_str; | opt_string deps_mode_str; | ||||
KEY_EXTEND_FLAGS(link_flags), | KEY_EXTEND_FLAGS(link_flags), | ||||
KEY_EXTEND_FLAGS(compiler_launcher), | KEY_EXTEND_FLAGS(compiler_launcher), | ||||
if_key{"debug", | if_key{"debug", | ||||
require_type<bool>("`debug` must be a boolean value"), | |||||
put_into{do_debug}}, | |||||
if_type<bool>(put_into{debug_bool}), | |||||
if_type<std::string>(put_into{debug_str}), | |||||
reject_with{"'debug' must be a bool or string"}}, | |||||
if_key{"optimize", | if_key{"optimize", | ||||
require_type<bool>("`optimize` must be a boolean value"), | require_type<bool>("`optimize` must be a boolean value"), | ||||
put_into{do_optimize}}, | put_into{do_optimize}}, | ||||
if_key{"flags", extend_flags("flags", common_flags)}, | if_key{"flags", extend_flags("flags", common_flags)}, | ||||
if_key{"runtime", | |||||
require_type<json5::data::mapping_type>("'runtime' must be a JSON object"), | |||||
mapping{if_key{"static", | |||||
require_type<bool>("'/runtime/static' should be a boolean"), | |||||
put_into(runtime_static)}, | |||||
if_key{"debug", | |||||
require_type<bool>("'/runtime/debug' should be a boolean"), | |||||
put_into(runtime_debug)}, | |||||
[](auto&& key, auto&&) { | |||||
fail("Unknown 'runtime' key '{}'", key); | |||||
return dc_reject_t(); | |||||
}}}, | |||||
if_key{ | if_key{ | ||||
"advanced", | "advanced", | ||||
require_type<json5::data::mapping_type>("`advanced` must be a mapping"), | require_type<json5::data::mapping_type>("`advanced` must be a mapping"), | ||||
"flags", | "flags", | ||||
"debug", | "debug", | ||||
"optimize", | "optimize", | ||||
"runtime", | |||||
}); | }); | ||||
fail(context, | fail(context, | ||||
"Unknown toolchain config key ‘{}’ (Did you mean ‘{}’?)", | "Unknown toolchain config key ‘{}’ (Did you mean ‘{}’?)", | ||||
fail(context, rej_opt->message); | fail(context, rej_opt->message); | ||||
} | } | ||||
if (debug_str.has_value() && debug_str != "embedded" && debug_str != "split" | |||||
&& debug_str != "none") { | |||||
fail(context, "'debug' string must be one of 'none', 'embedded', or 'split'"); | |||||
} | |||||
enum compiler_id_e_t { | enum compiler_id_e_t { | ||||
no_comp_id, | no_comp_id, | ||||
msvc, | msvc, | ||||
return cxx_ver_iter->second; | return cxx_ver_iter->second; | ||||
}; | }; | ||||
auto get_link_flags = [&]() -> string_seq { | |||||
auto get_runtime_flags = [&]() -> string_seq { | |||||
string_seq ret; | string_seq ret; | ||||
if (is_msvc) { | if (is_msvc) { | ||||
strv rt_lib = "/MT"; | |||||
if (do_optimize.value_or(false)) { | |||||
extend(ret, {"/O2"}); | |||||
std::string rt_flag = "/M"; | |||||
// Select debug/release runtime flag. Default is 'true' | |||||
if (runtime_static.value_or(true)) { | |||||
rt_flag.push_back('T'); | |||||
} else { | |||||
rt_flag.push_back('D'); | |||||
} | } | ||||
if (do_debug.value_or(false)) { | |||||
extend(ret, {"/Z7", "/DEBUG"}); | |||||
rt_lib = "/MTd"; | |||||
if (runtime_debug.value_or(debug_bool.value_or(false) || debug_str == "embedded" | |||||
|| debug_str == "split")) { | |||||
rt_flag.push_back('d'); | |||||
} | } | ||||
ret.emplace_back(rt_lib); | |||||
ret.push_back(rt_flag); | |||||
} else if (is_gnu_like) { | } else if (is_gnu_like) { | ||||
if (do_optimize.value_or(false)) { | |||||
extend(ret, {"-O2"}); | |||||
if (runtime_static.value_or(false)) { | |||||
extend(ret, {"-static-libgcc", "-static-libstdc++"}); | |||||
} | } | ||||
if (do_debug.value_or(false)) { | |||||
extend(ret, {"-g"}); | |||||
if (runtime_debug.value_or(false)) { | |||||
extend(ret, {"-D_GLIBCXX_DEBUG"}); | |||||
} | } | ||||
} else { | |||||
// No flags if we don't know the compiler | |||||
} | |||||
return ret; | |||||
}; | |||||
auto get_optim_flags = [&]() -> string_seq { | |||||
if (do_optimize != true) { | |||||
return {}; | |||||
} | } | ||||
if (is_msvc) { | |||||
return {"/O2"}; | |||||
} else if (is_gnu_like) { | |||||
return {"-O2"}; | |||||
} else { | |||||
return {}; | |||||
} | |||||
}; | |||||
auto get_debug_flags = [&]() -> string_seq { | |||||
if (is_msvc) { | |||||
if (debug_bool == true || debug_str == "embedded") { | |||||
return {"/Z7"}; | |||||
} else if (debug_str == "split") { | |||||
return {"/Zi"}; | |||||
} else { | |||||
// Do not generate any debug infro | |||||
return {}; | |||||
} | |||||
} else if (is_gnu_like) { | |||||
if (debug_bool == true || debug_str == "embedded") { | |||||
return {"-g"}; | |||||
} else if (debug_str == "split") { | |||||
return {"-g", "-gsplit-dwarf"}; | |||||
} else { | |||||
// Do not generate debug info | |||||
return {}; | |||||
} | |||||
} else { | |||||
// Cannont deduce the debug flags | |||||
return {}; | |||||
} | |||||
}; | |||||
auto get_link_flags = [&]() -> string_seq { | |||||
string_seq ret; | |||||
extend(ret, get_runtime_flags()); | |||||
extend(ret, get_optim_flags()); | |||||
extend(ret, get_debug_flags()); | |||||
if (link_flags) { | if (link_flags) { | ||||
extend(ret, *link_flags); | extend(ret, *link_flags); | ||||
} | } | ||||
auto get_flags = [&](language lang) -> string_seq { | auto get_flags = [&](language lang) -> string_seq { | ||||
string_seq ret; | string_seq ret; | ||||
extend(ret, get_runtime_flags()); | |||||
extend(ret, get_optim_flags()); | |||||
extend(ret, get_debug_flags()); | |||||
if (is_msvc) { | if (is_msvc) { | ||||
strv rt_lib = "/MT"; | |||||
if (do_optimize.has_value() && *do_optimize) { | |||||
extend(ret, {"/O2"}); | |||||
} | |||||
if (do_debug.has_value() && *do_debug) { | |||||
extend(ret, {"/Z7", "/DEBUG"}); | |||||
rt_lib = "/MTd"; | |||||
} | |||||
ret.emplace_back(rt_lib); | |||||
if (lang == language::cxx) { | if (lang == language::cxx) { | ||||
extend(ret, {"/EHsc"}); | extend(ret, {"/EHsc"}); | ||||
} | } | ||||
extend(ret, {"/nologo", "/permissive-", "[flags]", "/c", "[in]", "/Fo[out]"}); | 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) { | |||||
extend(ret, {"-O2"}); | |||||
} | |||||
if (do_debug.has_value() && *do_debug) { | |||||
extend(ret, {"-g"}); | |||||
} | |||||
extend(ret, {"-fPIC", "-pthread", "[flags]", "-c", "[in]", "-o[out]"}); | extend(ret, {"-fPIC", "-pthread", "[flags]", "-c", "[in]", "-o[out]"}); | ||||
} | } | ||||
if (common_flags) { | if (common_flags) { |
"ar rcs stuff.a foo.o bar.o", | "ar rcs stuff.a foo.o bar.o", | ||||
"g++ -fPIC foo.o bar.a -pthread -omeow.exe -O2 -g"); | "g++ -fPIC foo.o bar.a -pthread -omeow.exe -O2 -g"); | ||||
check_tc_compile( | |||||
"{compiler_id: 'gnu', debug: 'split', optimize: true}", | |||||
"g++ -O2 -g -gsplit-dwarf -fPIC -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | |||||
"g++ -O2 -g -gsplit-dwarf -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 -gsplit-dwarf"); | |||||
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", | ||||
"cl.exe /MT /EHsc /nologo /permissive- /W4 /showIncludes /c foo.cpp /Fofoo.o", | "cl.exe /MT /EHsc /nologo /permissive- /W4 /showIncludes /c foo.cpp /Fofoo.o", | ||||
check_tc_compile( | check_tc_compile( | ||||
"{compiler_id: 'msvc', debug: true}", | "{compiler_id: 'msvc', debug: true}", | ||||
"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", | |||||
"cl.exe /MTd /Z7 /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | |||||
"cl.exe /MTd /Z7 /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 /MTd /Z7"); | |||||
check_tc_compile( | |||||
"{compiler_id: 'msvc', debug: 'embedded'}", | |||||
"cl.exe /MTd /Z7 /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | |||||
"cl.exe /MTd /Z7 /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 /MTd /Z7"); | |||||
check_tc_compile( | |||||
"{compiler_id: 'msvc', debug: 'split'}", | |||||
"cl.exe /MTd /Zi /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | |||||
"cl.exe /MTd /Zi /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 /MTd /Zi"); | |||||
check_tc_compile( | check_tc_compile( | ||||
"{compiler_id: 'msvc', flags: '-DFOO'}", | "{compiler_id: 'msvc', flags: '-DFOO'}", | ||||
"cl.exe /MT /EHsc /nologo /permissive- /W4 /showIncludes /c foo.cpp /Fofoo.o -DFOO", | "cl.exe /MT /EHsc /nologo /permissive- /W4 /showIncludes /c foo.cpp /Fofoo.o -DFOO", | ||||
"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', runtime: {static: false}}", | |||||
"cl.exe /MD /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | |||||
"cl.exe /MD /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 /MD"); | |||||
check_tc_compile( | |||||
"{compiler_id: 'msvc', runtime: {static: false}, debug: true}", | |||||
"cl.exe /MDd /Z7 /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | |||||
"cl.exe /MDd /Z7 /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 /MDd /Z7"); | |||||
check_tc_compile("{compiler_id: 'msvc', runtime: {static: false, debug: true}}", | |||||
"cl.exe /MDd /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | |||||
"cl.exe /MDd /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 /MDd"); | |||||
} | } | ||||
TEST_CASE("Manipulate a toolchain and file compilation") { | TEST_CASE("Manipulate a toolchain and file compilation") { |