| #include <dds/util/time.hpp> | #include <dds/util/time.hpp> | ||||
| #include <fansi/styled.hpp> | #include <fansi/styled.hpp> | ||||
| #include <fmt/ostream.h> | |||||
| #include <array> | #include <array> | ||||
| #include <set> | #include <set> | ||||
| } | } | ||||
| } | } | ||||
| void write_lib_cmake(build_env_ref env, | |||||
| std::ostream& out, | |||||
| const package_plan& pkg, | |||||
| const library_plan& lib) { | |||||
| fmt::print(out, "# Library {}/{}\n", pkg.namespace_(), lib.name()); | |||||
| auto cmake_name = fmt::format("{}::{}", pkg.namespace_(), lib.name()); | |||||
| auto cm_kind = lib.archive_plan().has_value() ? "STATIC" : "INTERFACE"; | |||||
| fmt::print( | |||||
| out, | |||||
| "if(TARGET {0})\n" | |||||
| " get_target_property(dds_imported {0} dds_IMPORTED)\n" | |||||
| " if(NOT dds_imported)\n" | |||||
| " message(WARNING [[A target \"{0}\" is already defined, and not by a dds import]])\n" | |||||
| " endif()\n" | |||||
| "else()\n", | |||||
| cmake_name); | |||||
| fmt::print(out, | |||||
| " add_library({0} {1} IMPORTED GLOBAL)\n" | |||||
| " set_property(TARGET {0} PROPERTY dds_IMPORTED TRUE)\n" | |||||
| " set_property(TARGET {0} PROPERTY INTERFACE_INCLUDE_DIRECTORIES [[{2}]])\n", | |||||
| cmake_name, | |||||
| cm_kind, | |||||
| lib.library_().public_include_dir().generic_string()); | |||||
| for (auto&& use : lib.uses()) { | |||||
| fmt::print(out, | |||||
| " set_property(TARGET {} APPEND PROPERTY INTERFACE_LINK_LIBRARIES {}::{})\n", | |||||
| cmake_name, | |||||
| use.namespace_, | |||||
| use.name); | |||||
| } | |||||
| for (auto&& link : lib.links()) { | |||||
| fmt::print(out, | |||||
| " set_property(TARGET {} APPEND PROPERTY\n" | |||||
| " INTERFACE_LINK_LIBRARIES $<LINK_ONLY:{}::{}>)\n", | |||||
| cmake_name, | |||||
| link.namespace_, | |||||
| link.name); | |||||
| } | |||||
| if (auto& arc = lib.archive_plan()) { | |||||
| fmt::print(out, | |||||
| " set_property(TARGET {} PROPERTY IMPORTED_LOCATION [[{}]])\n", | |||||
| cmake_name, | |||||
| (env.output_root / arc->calc_archive_file_path(env.toolchain)).generic_string()); | |||||
| } | |||||
| fmt::print(out, "endif()\n"); | |||||
| } | |||||
| void write_cmake_pkg(build_env_ref env, std::ostream& out, const package_plan& pkg) { | |||||
| fmt::print(out, "## Imports for {}\n", pkg.name()); | |||||
| for (auto& lib : pkg.libraries()) { | |||||
| write_lib_cmake(env, out, pkg, lib); | |||||
| } | |||||
| fmt::print(out, "\n"); | |||||
| } | |||||
| void write_cmake(build_env_ref env, const build_plan& plan, path_ref cmake_out) { | |||||
| fs::create_directories(fs::absolute(cmake_out).parent_path()); | |||||
| auto out = open(cmake_out, std::ios::binary | std::ios::out); | |||||
| out << "## This CMake file was generated by `dds build-deps`. DO NOT EDIT!\n\n"; | |||||
| for (const auto& pkg : plan.packages()) { | |||||
| write_cmake_pkg(env, out, pkg); | |||||
| } | |||||
| } | |||||
| template <typename Func> | template <typename Func> | ||||
| void with_build_plan(const build_params& params, | void with_build_plan(const build_params& params, | ||||
| const std::vector<sdist_target>& sdists, | const std::vector<sdist_target>& sdists, | ||||
| if (params.emit_lmi) { | if (params.emit_lmi) { | ||||
| write_lmi(env, plan, params.out_root, *params.emit_lmi); | write_lmi(env, plan, params.out_root, *params.emit_lmi); | ||||
| } | } | ||||
| if (params.emit_cmake) { | |||||
| write_cmake(env, plan, *params.emit_cmake); | |||||
| } | |||||
| }); | }); | ||||
| } | } |
| fs::path out_root; | fs::path out_root; | ||||
| std::optional<fs::path> existing_lm_index; | std::optional<fs::path> existing_lm_index; | ||||
| std::optional<fs::path> emit_lmi; | std::optional<fs::path> emit_lmi; | ||||
| std::optional<fs::path> emit_cmake{}; | |||||
| dds::toolchain toolchain; | dds::toolchain toolchain; | ||||
| bool generate_compdb = true; | bool generate_compdb = true; | ||||
| int parallel_jobs = 0; | int parallel_jobs = 0; |
| .out_root = opts.out_path.value_or(fs::current_path() / "_deps"), | .out_root = opts.out_path.value_or(fs::current_path() / "_deps"), | ||||
| .existing_lm_index = {}, | .existing_lm_index = {}, | ||||
| .emit_lmi = opts.build.lm_index.value_or("INDEX.lmi"), | .emit_lmi = opts.build.lm_index.value_or("INDEX.lmi"), | ||||
| .emit_cmake = opts.build_deps.cmake_file, | |||||
| .toolchain = opts.load_toolchain(), | .toolchain = opts.load_toolchain(), | ||||
| .parallel_jobs = opts.jobs, | .parallel_jobs = opts.jobs, | ||||
| }; | }; |
| .can_repeat = true, | .can_repeat = true, | ||||
| .action = debate::push_back_onto(opts.build_deps.deps_files), | .action = debate::push_back_onto(opts.build_deps.deps_files), | ||||
| }); | }); | ||||
| build_deps_cmd.add_argument({ | |||||
| .long_spellings = {"cmake"}, | |||||
| .help = "Generate a CMake file at the given path that will create import targets for " | |||||
| "the dependencies", | |||||
| .valname = "<file-path>", | |||||
| .action = debate::put_into(opts.build_deps.cmake_file), | |||||
| }); | |||||
| build_deps_cmd.add_argument({ | build_deps_cmd.add_argument({ | ||||
| .help = "Dependency statement strings", | .help = "Dependency statement strings", | ||||
| .valname = "<dependency>", | .valname = "<dependency>", |
| std::vector<fs::path> deps_files; | std::vector<fs::path> deps_files; | ||||
| /// Dependency strings provided directly in the command-line | /// Dependency strings provided directly in the command-line | ||||
| std::vector<string> deps; | std::vector<string> deps; | ||||
| /// Path to a CMake import file to write | |||||
| opt_path cmake_file; | |||||
| } build_deps; | } build_deps; | ||||
| /** | /** |
| cmake_minimum_required(VERSION 3.12) | |||||
| project(TestProject) | |||||
| include(${PROJECT_BINARY_DIR}/libraries.cmake) | |||||
| add_executable(app main.cpp) | |||||
| target_link_libraries(app PRIVATE test::foo) |
| #include <foo.hpp> | |||||
| int main() { say_hello(); } |
| #pragma once | |||||
| extern void say_hello(); |
| #include <foo.hpp> | |||||
| #include <iostream> | |||||
| void say_hello() { std::cout << "Hello!\n"; } |
| import pytest | import pytest | ||||
| from dds_ci.testing import RepoServer, Project | |||||
| from dds_ci.testing import RepoServer, Project, ProjectOpener | |||||
| from dds_ci import proc, toolchain | |||||
| SIMPLE_CATALOG = { | SIMPLE_CATALOG = { | ||||
| "packages": { | "packages": { | ||||
| assert test_project.root.joinpath('_deps/_libman/neo-fun.lmp').is_file() | assert test_project.root.joinpath('_deps/_libman/neo-fun.lmp').is_file() | ||||
| assert test_project.root.joinpath('_deps/_libman/neo/fun.lml').is_file() | assert test_project.root.joinpath('_deps/_libman/neo/fun.lml').is_file() | ||||
| assert test_project.root.joinpath('INDEX.lmi').is_file() | assert test_project.root.joinpath('INDEX.lmi').is_file() | ||||
| def test_cmake_simple(project_opener: ProjectOpener) -> None: | |||||
| proj = project_opener.open('projects/simple') | |||||
| proj.dds.pkg_import(proj.root) | |||||
| cm_proj_dir = project_opener.test_dir / 'projects/simple-cmake' | |||||
| proj.build_root.mkdir(exist_ok=True, parents=True) | |||||
| proj.dds.run( | |||||
| [ | |||||
| 'build-deps', | |||||
| proj.dds.repo_dir_arg, | |||||
| 'foo@1.2.3', | |||||
| ('-t', ':gcc' if 'gcc' in toolchain.get_default_toolchain().name else ':msvc'), | |||||
| f'--cmake=libraries.cmake', | |||||
| ], | |||||
| cwd=proj.build_root, | |||||
| ) | |||||
| try: | |||||
| proc.check_run(['cmake', '-S', cm_proj_dir, '-B', proj.build_root]) | |||||
| except FileNotFoundError: | |||||
| assert False, 'Running the integration tests requires a CMake executable' | |||||
| proc.check_run(['cmake', '--build', proj.build_root]) |
| # Base build dependencies | # Base build dependencies | ||||
| RUN apk add "gcc=9.3.0-r2" "g++=9.3.0-r2" make python3 py3-pip \ | RUN apk add "gcc=9.3.0-r2" "g++=9.3.0-r2" make python3 py3-pip \ | ||||
| git openssl-libs-static openssl-dev ccache lld curl python3-dev | |||||
| git openssl-libs-static openssl-dev ccache lld curl python3-dev cmake | |||||
| # We use version-qualified names for compiler executables | # We use version-qualified names for compiler executables | ||||
| RUN ln -s $(type -P gcc) /usr/local/bin/gcc-9 && \ | RUN ln -s $(type -P gcc) /usr/local/bin/gcc-9 && \ |
| toolchain = toolchain or tc_mod.get_default_audit_toolchain() | toolchain = toolchain or tc_mod.get_default_audit_toolchain() | ||||
| self.run([ | self.run([ | ||||
| 'compile-file', | 'compile-file', | ||||
| self.catalog_path_arg, | |||||
| self.repo_dir_arg, | |||||
| paths, | paths, | ||||
| f'--toolchain={toolchain}', | f'--toolchain={toolchain}', | ||||
| f'{self.project_dir_flag}={project_dir}', | f'{self.project_dir_flag}={project_dir}', |