| // 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; |