// Build up the link command | // Build up the link command | ||||
link_exe_spec spec; | link_exe_spec spec; | ||||
spec.output = calc_executable_path(env); | spec.output = calc_executable_path(env); | ||||
spec.inputs = _input_libs; | |||||
dds_log(debug, "Performing link for {}", spec.output.string()); | dds_log(debug, "Performing link for {}", spec.output.string()); | ||||
// The main object should be a linker input, of course. | |||||
auto main_obj = _main_compile.calc_object_file_path(env); | |||||
dds_log(trace, "Add entry point object file: {}", main_obj.string()); | |||||
spec.inputs.push_back(std::move(main_obj)); | |||||
for (const lm::usage& links : _links) { | for (const lm::usage& links : _links) { | ||||
dds_log(trace, " - Link with: {}/{}", links.name, links.namespace_); | dds_log(trace, " - Link with: {}/{}", links.name, links.namespace_); | ||||
extend(spec.inputs, env.ureqs.link_paths(links)); | extend(spec.inputs, env.ureqs.link_paths(links)); | ||||
dds_log(trace, "Executable has no corresponding archive library input"); | dds_log(trace, "Executable has no corresponding archive library input"); | ||||
} | } | ||||
// The main object should be a linker input, of course. | |||||
auto main_obj = _main_compile.calc_object_file_path(env); | |||||
dds_log(trace, "Add entry point object file: {}", main_obj.string()); | |||||
spec.inputs.push_back(std::move(main_obj)); | |||||
// Linker inputs are order-dependent in some cases. The top-most input should appear first, and | |||||
// its dependencies should appear later. Because of the way inputs were generated, they appear | |||||
// sorted with the dependencies coming earlier than the dependees. We can simply reverse the | |||||
// order and linking will work. | |||||
std::reverse(spec.inputs.begin(), spec.inputs.end()); | |||||
// Do it! | // Do it! | ||||
const auto link_command | const auto link_command | ||||
= env.toolchain.create_link_executable_command(spec, dds::fs::current_path(), env.knobs); | = env.toolchain.create_link_executable_command(spec, dds::fs::current_path(), env.knobs); |
* single source file defines the entry point and some set of linker inputs. | * single source file defines the entry point and some set of linker inputs. | ||||
*/ | */ | ||||
class link_executable_plan { | class link_executable_plan { | ||||
/// The linker inputs that should be linked into the executable | |||||
std::vector<fs::path> _input_libs; | |||||
/// Usage requirements for this executable | /// Usage requirements for this executable | ||||
std::vector<lm::usage> _links; | std::vector<lm::usage> _links; | ||||
/// The compilation plan for the entry-point source file | /// The compilation plan for the entry-point source file | ||||
public: | public: | ||||
/** | /** | ||||
* Create a new instance | * Create a new instance | ||||
* @param in_libs Linker inputs for the executable | |||||
* @param links The library identifiers that the executable should link with | * @param links The library identifiers that the executable should link with | ||||
* @param cfp The file compilation that defines the entrypoint of the application | * @param cfp The file compilation that defines the entrypoint of the application | ||||
* @param out_subdir The subdirectory of the build root in which the executable should be placed | * @param out_subdir The subdirectory of the build root in which the executable should be placed | ||||
* @param name_ The name of the executable | * @param name_ The name of the executable | ||||
*/ | */ | ||||
link_executable_plan(std::vector<fs::path> in_libs, | |||||
std::vector<lm::usage> links, | |||||
link_executable_plan(std::vector<lm::usage> links, | |||||
compile_file_plan cfp, | compile_file_plan cfp, | ||||
path_ref out_subdir, | path_ref out_subdir, | ||||
std::string name_) | std::string name_) | ||||
: _input_libs(std::move(in_libs)) | |||||
, _links(std::move(links)) | |||||
: _links(std::move(links)) | |||||
, _main_compile(std::move(cfp)) | , _main_compile(std::move(cfp)) | ||||
, _out_subdir(out_subdir) | , _out_subdir(out_subdir) | ||||
, _name(std::move(name_)) {} | , _name(std::move(name_)) {} |
extend(links, lib.manifest().uses); | extend(links, lib.manifest().uses); | ||||
extend(links, lib.manifest().links); | extend(links, lib.manifest().links); | ||||
// Linker inputs for tests may contain additional code for test execution | |||||
std::vector<fs::path> link_libs; | |||||
std::vector<fs::path> test_link_libs = params.test_link_files; | |||||
// There may also be additional usage requirements for tests | // There may also be additional usage requirements for tests | ||||
auto test_rules = compile_rules.clone(); | auto test_rules = compile_rules.clone(); | ||||
auto test_links = links; | auto test_links = links; | ||||
std::vector<link_executable_plan> link_executables; | std::vector<link_executable_plan> link_executables; | ||||
for (const source_file& source : ranges::views::concat(app_sources, test_sources)) { | for (const source_file& source : ranges::views::concat(app_sources, test_sources)) { | ||||
const bool is_test = source.kind == source_kind::test; | const bool is_test = source.kind == source_kind::test; | ||||
if (is_test && !params.build_tests) { | |||||
// This is a test, but we don't want to build tests | |||||
continue; | |||||
} | |||||
if (!is_test && !params.build_apps) { | |||||
// This is an app, but we don't want to build apps | |||||
continue; | |||||
} | |||||
// Pick a subdir based on app/test | // Pick a subdir based on app/test | ||||
const auto subdir_base = is_test ? params.out_subdir / "test" : params.out_subdir; | const auto subdir_base = is_test ? params.out_subdir / "test" : params.out_subdir; | ||||
// Put test/app executables in a further subdirectory based on the source file path | // Put test/app executables in a further subdirectory based on the source file path | ||||
// Pick compile rules based on app/test | // Pick compile rules based on app/test | ||||
auto rules = is_test ? test_rules : compile_rules; | auto rules = is_test ? test_rules : compile_rules; | ||||
// Pick input libs based on app/test | // Pick input libs based on app/test | ||||
auto& exe_link_libs = is_test ? test_link_libs : link_libs; | |||||
auto& exe_links = is_test ? test_links : links; | |||||
auto& exe_links = is_test ? test_links : links; | |||||
// TODO: Apps/tests should only see the _public_ include dir, not both | // TODO: Apps/tests should only see the _public_ include dir, not both | ||||
auto exe = link_executable_plan{exe_link_libs, | |||||
exe_links, | |||||
auto exe = link_executable_plan{exe_links, | |||||
compile_file_plan(rules, | compile_file_plan(rules, | ||||
source, | source, | ||||
qual_name, | qual_name, |
/// Directories that should be on the #include search path when compiling tests | /// Directories that should be on the #include search path when compiling tests | ||||
std::vector<fs::path> test_include_dirs; | std::vector<fs::path> test_include_dirs; | ||||
/// Files that should be added as inputs when linking test executables | |||||
std::vector<fs::path> test_link_files; | |||||
/// Libraries that are used by tests | /// Libraries that are used by tests | ||||
std::vector<lm::usage> test_uses; | std::vector<lm::usage> test_uses; |