diff options
author | Brad King <brad.king@kitware.com> | 2023-08-21 13:49:24 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2023-08-21 13:49:48 (GMT) |
commit | 0788accdfcd8f29b3853e7e1e91325515c62f1bd (patch) | |
tree | fc5338d4cba5928a4a675363dfd17b06b78eeaa6 | |
parent | 355f658550d3432c29ddaf0d92aa048fa71b2d53 (diff) | |
parent | 48ee946fdcb8a610653e7ba42e9b8dba6942dbfb (diff) | |
download | CMake-0788accdfcd8f29b3853e7e1e91325515c62f1bd.zip CMake-0788accdfcd8f29b3853e7e1e91325515c62f1bd.tar.gz CMake-0788accdfcd8f29b3853e7e1e91325515c62f1bd.tar.bz2 |
Merge topic 'imported-cxxmodules'
48ee946fdc cmExperimental: recycle the C++ modules API UUID
1a1806a71b gitlab-ci: declare `bmionly` support for modules where possible
457a12f3f9 Tests/RunCMake/CXXModules: add tests which use modules from imported targets
9b9ec70b54 Ninja: generate scanning and build rules for C++20 module synthetic targets
80ef50a191 CXXModules: add a variable for BMI-only compilation
80d6544398 cxxmodules: generate synthetic targets as an initial pass
3dc6676ecc cmSyntheticTargetCache: add a struct for synthetic target caching
cb356b540c cmCxxModuleUsageEffects: add a class to capture module usage effects
...
Acked-by: Kitware Robot <kwrobot@kitware.com>
Tested-by: buildbot <buildbot@kitware.com>
Merge-request: !8535
76 files changed, 1681 insertions, 75 deletions
diff --git a/.gitlab/ci/configure_fedora38_ninja_clang.cmake b/.gitlab/ci/configure_fedora38_ninja_clang.cmake index 4454647..848c5b6 100644 --- a/.gitlab/ci/configure_fedora38_ninja_clang.cmake +++ b/.gitlab/ci/configure_fedora38_ninja_clang.cmake @@ -1,4 +1,4 @@ -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared" CACHE STRING "") +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly" CACHE STRING "") set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_clang.cmake" CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora38_common_clang.cmake") diff --git a/.gitlab/ci/configure_fedora38_ninja_multi_clang.cmake b/.gitlab/ci/configure_fedora38_ninja_multi_clang.cmake index 4454647..848c5b6 100644 --- a/.gitlab/ci/configure_fedora38_ninja_multi_clang.cmake +++ b/.gitlab/ci/configure_fedora38_ninja_multi_clang.cmake @@ -1,4 +1,4 @@ -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared" CACHE STRING "") +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly" CACHE STRING "") set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_clang.cmake" CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora38_common_clang.cmake") diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake index e9121ae..8342db6 100644 --- a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake +++ b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake @@ -1,4 +1,4 @@ -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi" CACHE STRING "") +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,bmionly" CACHE STRING "") set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_gcc.cmake" CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake index e9121ae..8342db6 100644 --- a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake +++ b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake @@ -1,4 +1,4 @@ -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi" CACHE STRING "") +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,bmionly" CACHE STRING "") set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_gcc.cmake" CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_windows_clang_ninja.cmake b/.gitlab/ci/configure_windows_clang_ninja.cmake index d1f0f52..8a65eef 100644 --- a/.gitlab/ci/configure_windows_clang_ninja.cmake +++ b/.gitlab/ci/configure_windows_clang_ninja.cmake @@ -1,5 +1,5 @@ if("$ENV{CMAKE_CI_BUILD_NAME}" MATCHES "(^|_)gnu(_|$)") - set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared" CACHE STRING "") + set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly" CACHE STRING "") set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_clang.cmake" CACHE STRING "") endif() include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_clang_common.cmake") diff --git a/.gitlab/ci/configure_windows_msvc_cxx_modules_common.cmake b/.gitlab/ci/configure_windows_msvc_cxx_modules_common.cmake index 5f2a215..2349bfd 100644 --- a/.gitlab/ci/configure_windows_msvc_cxx_modules_common.cmake +++ b/.gitlab/ci/configure_windows_msvc_cxx_modules_common.cmake @@ -1,2 +1,2 @@ -set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,shared,export_bmi,install_bmi" CACHE STRING "") +set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,shared,export_bmi,install_bmi,bmionly" CACHE STRING "") set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_msvc.cmake" CACHE STRING "") diff --git a/.gitlab/ci/cxx_modules_rules_gcc.cmake b/.gitlab/ci/cxx_modules_rules_gcc.cmake index 3726f6d..020cb1f 100644 --- a/.gitlab/ci/cxx_modules_rules_gcc.cmake +++ b/.gitlab/ci/cxx_modules_rules_gcc.cmake @@ -17,3 +17,4 @@ string(CONCAT CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG " -fdeps-format=p1689r5" # Force C++ as a language. " -x c++") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_BMI_ONLY_FLAG "-fmodule-only") diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst index 5bfbf8d..046d214 100644 --- a/Help/dev/experimental.rst +++ b/Help/dev/experimental.rst @@ -18,7 +18,7 @@ C++20 Module APIs ================= Variable: ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` -Value: ``a816ed09-43d1-40e5-bc8c-1a2824ee194e`` +Value: ``ac01f462-0f5f-432a-86aa-acef252918a6`` In order to support C++20 modules, there are a number of behaviors that have CMake APIs to provide the required features to build and export them from a @@ -99,6 +99,10 @@ dependencies to the file specified by the ``<DYNDEP_FILE>`` placeholder. The ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_DEPFILE_FORMAT`` file may be set to ``msvc`` for scandep rules which use ``msvc``-style dependency reporting. +In order to support ``IMPORTED`` targets with associated C++20 module sources, +the ``CMAKE_EXPERIMENTAL_CXX_MODULE_BMI_ONLY_FLAG`` variable must be provided +to have the compiler only output a BMI instead of a BMI and an object file. + The module dependencies should be written in the format described by the `P1689r5`_ paper. @@ -113,6 +117,8 @@ For compilers that generate module maps, tell CMake as follows: set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "gcc") set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "${compiler_flags_for_module_map} -fmodule-mapper=<MODULE_MAP_FILE>") + set(CMAKE_EXPERIMENTAL_CXX_MODULE_BMI_ONLY_FLAG + "-fmodule-only") Currently, the only supported formats are, ``clang``, ``gcc``, and ``msvc``. The ``gcc`` format is described in the GCC documentation, but the relevant diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index c8a433d..fa1d297 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -240,6 +240,11 @@ Properties on Targets /prop_tgt/IMPORTED /prop_tgt/IMPORTED_COMMON_LANGUAGE_RUNTIME /prop_tgt/IMPORTED_CONFIGURATIONS + /prop_tgt/IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS + /prop_tgt/IMPORTED_CXX_MODULES_COMPILE_FEATURES + /prop_tgt/IMPORTED_CXX_MODULES_COMPILE_OPTIONS + /prop_tgt/IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES + /prop_tgt/IMPORTED_CXX_MODULES_LINK_LIBRARIES /prop_tgt/IMPORTED_GLOBAL /prop_tgt/IMPORTED_IMPLIB /prop_tgt/IMPORTED_IMPLIB_CONFIG diff --git a/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS.rst b/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS.rst new file mode 100644 index 0000000..88687b2 --- /dev/null +++ b/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS.rst @@ -0,0 +1,14 @@ +IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS +---------------------------------------- + +.. versionadded:: 3.28 + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Preprocessor definitions for compiling an ``IMPORTED`` target's C++ module +sources. + +CMake will automatically drop some definitions that are not supported +by the native build tool. diff --git a/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_FEATURES.rst b/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_FEATURES.rst new file mode 100644 index 0000000..c3eb7fb --- /dev/null +++ b/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_FEATURES.rst @@ -0,0 +1,13 @@ +IMPORTED_CXX_MODULES_COMPILE_FEATURES +------------------------------------- + +.. versionadded:: 3.28 + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +Compiler features enabled for this ``IMPORTED`` target's C++ modules. + +The value of this property is used by the generators to set the include +paths for the compiler. diff --git a/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_OPTIONS.rst b/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_OPTIONS.rst new file mode 100644 index 0000000..5c62c77 --- /dev/null +++ b/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_OPTIONS.rst @@ -0,0 +1,13 @@ +IMPORTED_CXX_MODULES_COMPILE_OPTIONS +------------------------------------ + +.. versionadded:: 3.28 + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +List of options to pass to the compiler for this ``IMPORTED`` target's C++ +modules. + +.. include:: ../command/OPTIONS_SHELL.txt diff --git a/Help/prop_tgt/IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES.rst b/Help/prop_tgt/IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES.rst new file mode 100644 index 0000000..08a993d --- /dev/null +++ b/Help/prop_tgt/IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES.rst @@ -0,0 +1,14 @@ +IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES +---------------------------------------- + +.. versionadded:: 3.28 + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +List of preprocessor include file search directories when compiling C++ +modules for ``IMPORTED`` targets. + +The value of this property is used by the generators to set the include +paths for the compiler. diff --git a/Help/prop_tgt/IMPORTED_CXX_MODULES_LINK_LIBRARIES.rst b/Help/prop_tgt/IMPORTED_CXX_MODULES_LINK_LIBRARIES.rst new file mode 100644 index 0000000..5111dc5 --- /dev/null +++ b/Help/prop_tgt/IMPORTED_CXX_MODULES_LINK_LIBRARIES.rst @@ -0,0 +1,11 @@ +IMPORTED_CXX_MODULES_LINK_LIBRARIES +----------------------------------- + +.. versionadded:: 3.28 + +.. note :: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` + +List of direct dependencies to use for usage requirements for C++ modules in +the target's C++ modules. diff --git a/Modules/Compiler/Clang-CXX.cmake b/Modules/Compiler/Clang-CXX.cmake index a74e90b..1167ba8 100644 --- a/Modules/Compiler/Clang-CXX.cmake +++ b/Modules/Compiler/Clang-CXX.cmake @@ -43,5 +43,6 @@ if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) " > <DYNDEP_FILE>") set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "clang") set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>") + set(CMAKE_EXPERIMENTAL_CXX_MODULE_BMI_ONLY_FLAG "--precompile") endif() endif() diff --git a/Modules/Compiler/MSVC-CXX.cmake b/Modules/Compiler/MSVC-CXX.cmake index 10a9073..b03f826 100644 --- a/Modules/Compiler/MSVC-CXX.cmake +++ b/Modules/Compiler/MSVC-CXX.cmake @@ -87,4 +87,5 @@ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "19.34") set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_DEPFILE_FORMAT "msvc") set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "msvc") set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>") + set(CMAKE_EXPERIMENTAL_CXX_MODULE_BMI_ONLY_FLAG "-ifcOnly;-ifcOutput;<OBJECT>") endif () diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index c40bfce..37d407b 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -171,6 +171,8 @@ add_library( cmCustomCommandTypes.h cmCxxModuleMapper.cxx cmCxxModuleMapper.h + cmCxxModuleUsageEffects.cxx + cmCxxModuleUsageEffects.h cmDefinitions.cxx cmDefinitions.h cmDependencyProvider.h @@ -299,6 +301,8 @@ add_library( cmGraphAdjacencyList.h cmGraphVizWriter.cxx cmGraphVizWriter.h + cmImportedCxxModuleInfo.cxx + cmImportedCxxModuleInfo.h cmInstallGenerator.h cmInstallGenerator.cxx cmInstallGetRuntimeDependenciesGenerator.h @@ -424,6 +428,7 @@ add_library( cmStateTypes.h cmStringAlgorithms.cxx cmStringAlgorithms.h + cmSyntheticTargetCache.h cmSystemTools.cxx cmSystemTools.h cmTarget.cxx diff --git a/Source/cmCxxModuleUsageEffects.cxx b/Source/cmCxxModuleUsageEffects.cxx new file mode 100644 index 0000000..eea5245 --- /dev/null +++ b/Source/cmCxxModuleUsageEffects.cxx @@ -0,0 +1,21 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCxxModuleUsageEffects.h" + +cmCxxModuleUsageEffects::cmCxxModuleUsageEffects( + cmGeneratorTarget const* /*gt*/) + : Hash("0000000000000000000000000000000000000000") +{ + // TODO: collect information from the generator target as to what might + // affect module consumption. +} + +void cmCxxModuleUsageEffects::ApplyToTarget(cmTarget* /*tgt*/) +{ + // TODO: apply the information collected in the constructor +} + +std::string const& cmCxxModuleUsageEffects::GetHash() const +{ + return this->Hash; +} diff --git a/Source/cmCxxModuleUsageEffects.h b/Source/cmCxxModuleUsageEffects.h new file mode 100644 index 0000000..a63919c --- /dev/null +++ b/Source/cmCxxModuleUsageEffects.h @@ -0,0 +1,22 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <string> + +class cmGeneratorTarget; +class cmTarget; + +class cmCxxModuleUsageEffects +{ +public: + cmCxxModuleUsageEffects(cmGeneratorTarget const* gt); + + void ApplyToTarget(cmTarget* tgt); + std::string const& GetHash() const; + +private: + std::string Hash; +}; diff --git a/Source/cmDyndepCollation.cxx b/Source/cmDyndepCollation.cxx index 80e1357..dfab975 100644 --- a/Source/cmDyndepCollation.cxx +++ b/Source/cmDyndepCollation.cxx @@ -73,20 +73,36 @@ Json::Value CollationInformationCxxModules( gt->LocalGenerator, config, gt); } - std::map<std::string, cmSourceFile const*> sf_map; + enum class CompileType { - std::vector<cmSourceFile const*> objectSources; - gt->GetObjectSources(objectSources, config); - for (auto const* sf : objectSources) { + ObjectAndBmi, + BmiOnly, + }; + std::map<std::string, std::pair<cmSourceFile const*, CompileType>> sf_map; + { + auto fill_sf_map = [gt, tgt, &sf_map](cmSourceFile const* sf, + CompileType type) { auto full_path = sf->GetFullPath(); if (full_path.empty()) { gt->Makefile->IssueMessage( MessageType::INTERNAL_ERROR, cmStrCat("Target \"", tgt->GetName(), "\" has a full path-less source file.")); - continue; + return; } - sf_map[full_path] = sf; + sf_map[full_path] = std::make_pair(sf, type); + }; + + std::vector<cmSourceFile const*> objectSources; + gt->GetObjectSources(objectSources, config); + for (auto const* sf : objectSources) { + fill_sf_map(sf, CompileType::ObjectAndBmi); + } + + std::vector<cmSourceFile const*> cxxModuleSources; + gt->GetCxxModuleSources(cxxModuleSources, config); + for (auto const* sf : cxxModuleSources) { + fill_sf_map(sf, CompileType::BmiOnly); } } @@ -113,7 +129,8 @@ Json::Value CollationInformationCxxModules( continue; } - auto const* sf = lookup->second; + auto const* sf = lookup->second.first; + CompileType const ct = lookup->second.second; if (!sf) { gt->Makefile->IssueMessage( @@ -123,11 +140,14 @@ Json::Value CollationInformationCxxModules( continue; } - auto obj_path = cb.ObjectFilePath(sf, config); + auto obj_path = ct == CompileType::ObjectAndBmi + ? cb.ObjectFilePath(sf, config) + : cb.BmiFilePath(sf, config); Json::Value& tdi_module_info = tdi_cxx_module_info[obj_path] = Json::objectValue; tdi_module_info["source"] = file; + tdi_module_info["bmi-only"] = ct == CompileType::BmiOnly; tdi_module_info["relative-directory"] = files_per_dir.first; tdi_module_info["name"] = file_set->GetName(); tdi_module_info["type"] = file_set->GetType(); @@ -269,10 +289,11 @@ void cmDyndepCollation::AddCollationInformation( struct CxxModuleFileSet { std::string Name; + bool BmiOnly = false; std::string RelativeDirectory; std::string SourcePath; std::string Type; - cmFileSetVisibility Visibility; + cmFileSetVisibility Visibility = cmFileSetVisibility::Private; cm::optional<std::string> Destination; }; @@ -356,6 +377,7 @@ cmDyndepCollation::ParseExportInfo(Json::Value const& tdi) CxxModuleFileSet& fsi = export_info->ObjectToFileSet[i.key().asString()]; auto const& tdi_cxx_module_info = *i; fsi.Name = tdi_cxx_module_info["name"].asString(); + fsi.BmiOnly = tdi_cxx_module_info["bmi-only"].asBool(); fsi.RelativeDirectory = tdi_cxx_module_info["relative-directory"].asString(); if (!fsi.RelativeDirectory.empty() && @@ -644,3 +666,16 @@ bool cmDyndepCollation::IsObjectPrivate( auto const& file_set = fileset_info_itr->second; return !cmFileSetVisibilityIsForInterface(file_set.Visibility); } + +bool cmDyndepCollation::IsBmiOnly(cmCxxModuleExportInfo const& exportInfo, + std::string const& object) +{ +#ifdef _WIN32 + auto object_path = object; + cmSystemTools::ConvertToUnixSlashes(object_path); +#else + auto const& object_path = object; +#endif + auto fs = exportInfo.ObjectToFileSet.find(object_path); + return (fs != exportInfo.ObjectToFileSet.end()) && fs->second.BmiOnly; +} diff --git a/Source/cmDyndepCollation.h b/Source/cmDyndepCollation.h index 48afe2b..b193467 100644 --- a/Source/cmDyndepCollation.h +++ b/Source/cmDyndepCollation.h @@ -23,6 +23,8 @@ struct cmDyndepGeneratorCallbacks { std::function<std::string(cmSourceFile const* sf, std::string const& config)> ObjectFilePath; + std::function<std::string(cmSourceFile const* sf, std::string const& config)> + BmiFilePath; }; struct cmDyndepMetadataCallbacks @@ -51,4 +53,7 @@ struct cmDyndepCollation cmDyndepMetadataCallbacks const& cb); static bool IsObjectPrivate(std::string const& object, cmCxxModuleExportInfo const& export_info); + + static bool IsBmiOnly(cmCxxModuleExportInfo const& exportInfo, + std::string const& object); }; diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx index dd8f16e..104ab81 100644 --- a/Source/cmExperimental.cxx +++ b/Source/cmExperimental.cxx @@ -21,7 +21,7 @@ namespace { cmExperimental::FeatureData LookupTable[] = { // CxxModuleCMakeApi { "CxxModuleCMakeApi", - "a816ed09-43d1-40e5-bc8c-1a2824ee194e", + "ac01f462-0f5f-432a-86aa-acef252918a6", "CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API", "CMake's C++ module support is experimental. It is meant only for " "experimentation and feedback to CMake developers.", diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index fd35786..69572f4 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -126,6 +126,15 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) properties); std::string errorMessage; + if (!this->PopulateCxxModuleExportProperties( + gte, properties, cmGeneratorExpression::BuildInterface, + errorMessage)) { + this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, errorMessage, + this->LG->GetMakefile()->GetBacktrace()); + return false; + } + if (!this->PopulateExportProperties(gte, properties, errorMessage)) { this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( MessageType::FATAL_ERROR, errorMessage, diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 41234f4..5a12297 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -9,6 +9,7 @@ #include <utility> #include <cm/memory> +#include <cmext/string_view> #include "cmsys/FStream.hxx" @@ -1255,6 +1256,77 @@ void cmExportFileGenerator::GenerateImportedFileChecksCode( os << ")\n\n"; } +bool cmExportFileGenerator::PopulateCxxModuleExportProperties( + cmGeneratorTarget const* gte, ImportPropertyMap& properties, + cmGeneratorExpression::PreprocessContext ctx, std::string& errorMessage) +{ + if (!gte->HaveCxx20ModuleSources(&errorMessage)) { + return true; + } + + const cm::static_string_view exportedDirectModuleProperties[] = { + "CXX_EXTENSIONS"_s, + }; + for (auto const& propName : exportedDirectModuleProperties) { + auto const propNameStr = std::string(propName); + cmValue prop = gte->Target->GetComputedProperty( + propNameStr, *gte->Target->GetMakefile()); + if (!prop) { + prop = gte->Target->GetProperty(propNameStr); + } + if (prop) { + properties[propNameStr] = cmGeneratorExpression::Preprocess(*prop, ctx); + } + } + + const cm::static_string_view exportedModuleProperties[] = { + "INCLUDE_DIRECTORIES"_s, + "COMPILE_DEFINITIONS"_s, + "COMPILE_OPTIONS"_s, + "COMPILE_FEATURES"_s, + }; + for (auto const& propName : exportedModuleProperties) { + auto const propNameStr = std::string(propName); + cmValue prop = gte->Target->GetComputedProperty( + propNameStr, *gte->Target->GetMakefile()); + if (!prop) { + prop = gte->Target->GetProperty(propNameStr); + } + if (prop) { + auto const exportedPropName = + cmStrCat("IMPORTED_CXX_MODULES_", propName); + properties[exportedPropName] = + cmGeneratorExpression::Preprocess(*prop, ctx); + } + } + + const cm::static_string_view exportedLinkModuleProperties[] = { + "LINK_LIBRARIES"_s, + }; + for (auto const& propName : exportedLinkModuleProperties) { + auto const propNameStr = std::string(propName); + cmValue prop = gte->Target->GetComputedProperty( + propNameStr, *gte->Target->GetMakefile()); + if (!prop) { + prop = gte->Target->GetProperty(propNameStr); + } + if (prop) { + auto const exportedPropName = + cmStrCat("IMPORTED_CXX_MODULES_", propName); + auto value = cmGeneratorExpression::Preprocess(*prop, ctx); + this->ResolveTargetsInGeneratorExpressions( + value, gte, cmExportFileGenerator::ReplaceFreeTargets); + std::vector<std::string> wrappedValues; + for (auto& item : cmList{ value }) { + wrappedValues.push_back(cmStrCat("$<COMPILE_ONLY:", item, '>')); + } + properties[exportedPropName] = cmJoin(wrappedValues, ";"); + } + } + + return true; +} + bool cmExportFileGenerator::PopulateExportProperties( cmGeneratorTarget const* gte, ImportPropertyMap& properties, std::string& errorMessage) diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index fdda878..6fa19ee 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -175,6 +175,9 @@ protected: virtual void GenerateRequiredCMakeVersion(std::ostream& os, const char* versionString); + bool PopulateCxxModuleExportProperties( + cmGeneratorTarget const* gte, ImportPropertyMap& properties, + cmGeneratorExpression::PreprocessContext ctx, std::string& errorMessage); bool PopulateExportProperties(cmGeneratorTarget const* gte, ImportPropertyMap& properties, std::string& errorMessage); diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index 6cf3a09..908bb31 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -126,6 +126,13 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) gt, cmGeneratorExpression::InstallInterface, properties); std::string errorMessage; + if (!this->PopulateCxxModuleExportProperties( + gt, properties, cmGeneratorExpression::InstallInterface, + errorMessage)) { + cmSystemTools::Error(errorMessage); + return false; + } + if (!this->PopulateExportProperties(gt, properties, errorMessage)) { cmSystemTools::Error(errorMessage); return false; diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index 812b1b5..3c1bc14 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx @@ -1678,6 +1678,7 @@ Json::Value Target::DumpSource(cmGeneratorTarget::SourceAndKind const& sk, } switch (sk.Kind) { + case cmGeneratorTarget::SourceKindCxxModuleSource: case cmGeneratorTarget::SourceKindObjectSource: { source["compileGroupIndex"] = this->AddSourceCompileGroup(sk.Source.Value, si); diff --git a/Source/cmFileSet.cxx b/Source/cmFileSet.cxx index 48a2570..bcf7fba 100644 --- a/Source/cmFileSet.cxx +++ b/Source/cmFileSet.cxx @@ -7,6 +7,7 @@ #include <utility> #include <vector> +#include <cmext/algorithm> #include <cmext/string_view> #include "cmsys/RegularExpression.hxx" @@ -88,6 +89,12 @@ cmFileSet::cmFileSet(cmake& cmakeInstance, std::string name, std::string type, { } +void cmFileSet::CopyEntries(cmFileSet const* fs) +{ + cm::append(this->DirectoryEntries, fs->DirectoryEntries); + cm::append(this->FileEntries, fs->FileEntries); +} + void cmFileSet::ClearDirectoryEntries() { this->DirectoryEntries.clear(); diff --git a/Source/cmFileSet.h b/Source/cmFileSet.h index 54d430c..c508e2b 100644 --- a/Source/cmFileSet.h +++ b/Source/cmFileSet.h @@ -41,6 +41,8 @@ public: const std::string& GetType() const { return this->Type; } cmFileSetVisibility GetVisibility() const { return this->Visibility; } + void CopyEntries(cmFileSet const* fs); + void ClearDirectoryEntries(); void AddDirectoryEntry(BT<std::string> directories); const std::vector<BT<std::string>>& GetDirectoryEntries() const diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index eb4ba90..70f51b0 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -27,7 +27,9 @@ #include "cmAlgorithms.h" #include "cmComputeLinkInformation.h" +#include "cmCryptoHash.h" #include "cmCustomCommandGenerator.h" +#include "cmCxxModuleUsageEffects.h" #include "cmEvaluatedTargetProperty.h" #include "cmExperimental.h" #include "cmFileSet.h" @@ -52,6 +54,7 @@ #include "cmStandardLevelResolver.h" #include "cmState.h" #include "cmStringAlgorithms.h" +#include "cmSyntheticTargetCache.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetLinkLibraryType.h" @@ -1071,6 +1074,12 @@ void cmGeneratorTarget::GetHeaderSources( IMPLEMENT_VISIT(SourceKindHeader); } +void cmGeneratorTarget::GetCxxModuleSources( + std::vector<cmSourceFile const*>& data, const std::string& config) const +{ + IMPLEMENT_VISIT(SourceKindCxxModuleSource); +} + void cmGeneratorTarget::GetExtraSources(std::vector<cmSourceFile const*>& data, const std::string& config) const { @@ -1953,8 +1962,12 @@ void cmGeneratorTarget::ComputeKindedSources(KindedSources& files, // Compute the kind (classification) of this source file. SourceKind kind; std::string ext = cmSystemTools::LowerCase(sf->GetExtension()); + cmFileSet const* fs = this->GetFileSetForSource(config, sf); if (sf->GetCustomCommand()) { kind = SourceKindCustomCommand; + } else if (!this->Target->IsNormal() && !this->Target->IsImported() && + fs && (fs->GetType() == "CXX_MODULES"_s)) { + kind = SourceKindCxxModuleSource; } else if (this->Target->GetType() == cmStateEnums::UTILITY || this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 @@ -8220,6 +8233,96 @@ void ComputeLinkImplTransitive(cmGeneratorTarget const* self, } } +bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, + std::string const& config) +{ + cmOptionalLinkImplementation impl; + this->ComputeLinkImplementationLibraries(config, impl, this, + LinkInterfaceFor::Link); + + cmCxxModuleUsageEffects usage(this); + + auto& SyntheticDeps = this->Configs[config].SyntheticDeps; + + for (auto const& entry : impl.Libraries) { + auto const* gt = entry.Target; + if (!gt || !gt->IsImported()) { + continue; + } + + if (gt->HaveCxx20ModuleSources()) { + auto hasher = cmCryptoHash::New("SHA3_512"); + constexpr size_t HASH_TRUNCATION = 12; + auto dirhash = hasher->HashString( + gt->GetLocalGenerator()->GetCurrentBinaryDirectory()); + std::string safeName = gt->GetName(); + cmSystemTools::ReplaceString(safeName, ":", "_"); + auto targetIdent = + hasher->HashString(cmStrCat("@d_", dirhash, "@u_", usage.GetHash())); + std::string targetName = + cmStrCat(safeName, "@synth_", targetIdent.substr(0, HASH_TRUNCATION)); + + // Check the cache to see if this instance of the imported target has + // already been created. + auto cached = cache.CxxModuleTargets.find(targetName); + cmGeneratorTarget const* synthDep = nullptr; + if (cached == cache.CxxModuleTargets.end()) { + auto const* model = gt->Target; + auto* mf = gt->Makefile; + auto* lg = gt->GetLocalGenerator(); + auto* tgt = mf->AddSynthesizedTarget(cmStateEnums::INTERFACE_LIBRARY, + targetName); + + // Copy relevant information from the existing IMPORTED target. + + // Copy policies to the target. + tgt->CopyPolicyStatuses(model); + + // Copy file sets. + { + auto fsNames = model->GetAllFileSetNames(); + for (auto const& fsName : fsNames) { + auto const* fs = model->GetFileSet(fsName); + if (!fs) { + mf->IssueMessage(MessageType::INTERNAL_ERROR, + cmStrCat("Failed to find file set named '", + fsName, "' on target '", + tgt->GetName(), '\'')); + continue; + } + auto* newFs = tgt + ->GetOrCreateFileSet(fs->GetName(), fs->GetType(), + fs->GetVisibility()) + .first; + newFs->CopyEntries(fs); + } + } + + // Copy imported C++ module properties. + tgt->CopyImportedCxxModulesEntries(model); + + // Copy other properties which may affect the C++ module BMI + // generation. + tgt->CopyImportedCxxModulesProperties(model); + + // Apply usage requirements to the target. + usage.ApplyToTarget(tgt); + + // Create the generator target and attach it to the local generator. + auto gtp = cm::make_unique<cmGeneratorTarget>(tgt, lg); + synthDep = gtp.get(); + lg->AddGeneratorTarget(std::move(gtp)); + } else { + synthDep = cached->second; + } + + SyntheticDeps[gt].push_back(synthDep); + } + } + + return true; +} + void cmGeneratorTarget::ComputeLinkImplementationLibraries( const std::string& config, cmOptionalLinkImplementation& impl, cmGeneratorTarget const* head, LinkInterfaceFor implFor) const @@ -8227,6 +8330,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries( cmLocalGenerator const* lg = this->LocalGenerator; cmMakefile const* mf = lg->GetMakefile(); cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries(); + auto const& synthTargetsForConfig = this->Configs[config].SyntheticDeps; // Collect libraries directly linked in this configuration. for (auto const& entry : entryRange) { // Keep this logic in sync with ExpandLinkItems. @@ -8316,7 +8420,15 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries( // The entry is meant for this configuration. cmLinkItem item = this->ResolveLinkItem(BT<std::string>(name, entry.Backtrace), lg); - if (!item.Target) { + if (item.Target) { + auto depsForTarget = synthTargetsForConfig.find(item.Target); + if (depsForTarget != synthTargetsForConfig.end()) { + for (auto const* depForTarget : depsForTarget->second) { + cmLinkItem synthItem(depForTarget, item.Cross, item.Backtrace); + impl.Libraries.emplace_back(std::move(synthItem), false); + } + } + } else { // Report explicitly linked object files separately. std::string const& maybeObj = item.AsStr(); if (cmSystemTools::FileIsFullPath(maybeObj)) { @@ -8930,24 +9042,28 @@ bool cmGeneratorTarget::HaveFortranSources(std::string const& config) const }); } -bool cmGeneratorTarget::HaveCxx20ModuleSources() const +bool cmGeneratorTarget::HaveCxx20ModuleSources(std::string* errorMessage) const { auto const& fs_names = this->Target->GetAllFileSetNames(); - return std::any_of(fs_names.begin(), fs_names.end(), - [this](std::string const& name) -> bool { - auto const* file_set = this->Target->GetFileSet(name); - if (!file_set) { - this->Makefile->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target \"", this->Target->GetName(), - "\" is tracked to have file set \"", name, - "\", but it was not found.")); - return false; - } - - auto const& fs_type = file_set->GetType(); - return fs_type == "CXX_MODULES"_s; - }); + return std::any_of( + fs_names.begin(), fs_names.end(), + [this, errorMessage](std::string const& name) -> bool { + auto const* file_set = this->Target->GetFileSet(name); + if (!file_set) { + auto message = cmStrCat("Target \"", this->Target->GetName(), + "\" is tracked to have file set \"", name, + "\", but it was not found."); + if (errorMessage) { + *errorMessage = std::move(message); + } else { + this->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, message); + } + return false; + } + + auto const& fs_type = file_set->GetType(); + return fs_type == "CXX_MODULES"_s; + }); } cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport( diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index a32e742..f347133 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -31,6 +31,7 @@ class cmGlobalGenerator; class cmLocalGenerator; class cmMakefile; class cmSourceFile; +struct cmSyntheticTargetCache; class cmTarget; struct cmGeneratorExpressionContext; @@ -116,6 +117,7 @@ public: SourceKindCertificate, SourceKindCustomCommand, SourceKindExternalObject, + SourceKindCxxModuleSource, SourceKindExtra, SourceKindHeader, SourceKindIDL, @@ -186,6 +188,8 @@ public: const std::string& config) const; void GetHeaderSources(std::vector<cmSourceFile const*>&, const std::string& config) const; + void GetCxxModuleSources(std::vector<cmSourceFile const*>&, + const std::string& config) const; void GetExtraSources(std::vector<cmSourceFile const*>&, const std::string& config) const; void GetCustomCommands(std::vector<cmSourceFile const*>&, @@ -929,6 +933,9 @@ public: std::string GetImportedXcFrameworkPath(const std::string& config) const; + bool DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, + std::string const& config); + private: void AddSourceCommon(const std::string& src, bool before = false); @@ -1264,8 +1271,11 @@ public: * * This will inspect the target itself to see if C++20 module * support is expected to work based on its sources. + * + * If `errorMessage` is given a non-`nullptr`, any error message will be + * stored in it, otherwise the error will be reported directly. */ - bool HaveCxx20ModuleSources() const; + bool HaveCxx20ModuleSources(std::string* errorMessage = nullptr) const; enum class Cxx20SupportLevel { @@ -1301,6 +1311,8 @@ private: { bool BuiltFileSetCache = false; std::map<std::string, cmFileSet const*> FileSetCache; + std::map<cmGeneratorTarget const*, std::vector<cmGeneratorTarget const*>> + SyntheticDeps; }; mutable std::map<std::string, InfoByConfig> Configs; }; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 9af6e9b..d5099ee 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -55,6 +55,7 @@ #include "cmStateDirectory.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" +#include "cmSyntheticTargetCache.h" #include "cmSystemTools.h" #include "cmValue.h" #include "cmVersion.h" @@ -1560,6 +1561,17 @@ bool cmGlobalGenerator::Compute() } #endif + // Iterate through all targets and set up C++20 module targets. + // Create target templates for each imported target with C++20 modules. + // INTERFACE library with BMI-generating rules and a collation step? + // Maybe INTERFACE libraries with modules files should just do BMI-only? + // Make `add_dependencies(imported_target + // $<$<TARGET_NAME_IF_EXISTS:uses_imported>:synth1> + // $<$<TARGET_NAME_IF_EXISTS:other_uses_imported>:synth2>)` + if (!this->DiscoverSyntheticTargets()) { + return false; + } + // Add generator specific helper commands for (const auto& localGen : this->LocalGenerators) { localGen->AddHelperCommands(); @@ -1784,6 +1796,34 @@ void cmGlobalGenerator::ComputeTargetOrder(cmGeneratorTarget const* gt, entry->second = index++; } +bool cmGlobalGenerator::DiscoverSyntheticTargets() +{ + cmSyntheticTargetCache cache; + + for (auto const& gen : this->LocalGenerators) { + // Because DiscoverSyntheticTargets() adds generator targets, we need to + // cache the existing list of generator targets before starting. + std::vector<cmGeneratorTarget*> genTargets; + genTargets.reserve(gen->GetGeneratorTargets().size()); + for (auto const& tgt : gen->GetGeneratorTargets()) { + genTargets.push_back(tgt.get()); + } + + for (auto* tgt : genTargets) { + std::vector<std::string> const& configs = + tgt->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + + for (auto const& config : configs) { + if (!tgt->DiscoverSyntheticTargets(cache, config)) { + return false; + } + } + } + } + + return true; +} + bool cmGlobalGenerator::AddHeaderSetVerification() { for (auto const& gen : this->LocalGenerators) { diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 563ebb6..6d29dc1 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -662,6 +662,8 @@ protected: virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const; + bool DiscoverSyntheticTargets(); + bool AddHeaderSetVerification(); bool AddAutomaticSources(); diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 9ca1b2e..54c3737 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -2643,7 +2643,9 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( for (cmScanDepInfo const& object : objects) { for (auto const& p : object.Provides) { std::string mod; - if (!p.CompiledModulePath.empty()) { + if (cmDyndepCollation::IsBmiOnly(export_info, object.PrimaryOutput)) { + mod = object.PrimaryOutput; + } else if (!p.CompiledModulePath.empty()) { // The scanner provided the path to the module file. mod = p.CompiledModulePath; if (!cmSystemTools::FileIsFullPath(mod)) { @@ -2714,8 +2716,12 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( build.Outputs[0] = this->ConvertToNinjaPath(object.PrimaryOutput); build.ImplicitOuts.clear(); for (auto const& p : object.Provides) { - build.ImplicitOuts.push_back( - this->ConvertToNinjaPath(mod_files[p.LogicalName].BmiPath)); + auto const implicitOut = + this->ConvertToNinjaPath(mod_files[p.LogicalName].BmiPath); + // Ignore the `provides` when the BMI is the output. + if (implicitOut != build.Outputs[0]) { + build.ImplicitOuts.emplace_back(implicitOut); + } } build.ImplicitDeps.clear(); for (auto const& r : object.Requires) { diff --git a/Source/cmImportedCxxModuleInfo.cxx b/Source/cmImportedCxxModuleInfo.cxx new file mode 100644 index 0000000..9e3ac9a --- /dev/null +++ b/Source/cmImportedCxxModuleInfo.cxx @@ -0,0 +1,76 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmImportedCxxModuleInfo.h" + +#include <cstddef> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "cmCryptoHash.h" +#include "cmList.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +bool ImportedCxxModuleLookup::Initialized() const +{ + return this->DoneInit; +} + +void ImportedCxxModuleLookup::Initialize(std::string const& importedModules) +{ + for (auto const& entry : cmList{ importedModules }) { + auto nameSep = entry.find('='); + if (nameSep == std::string::npos) { + // Invalid entry; ignore. + continue; + } + + auto name = entry.substr(0, nameSep); + + auto sourceSep = entry.find(',', nameSep); + std::string source; + if (sourceSep == std::string::npos) { + source = entry.substr(nameSep + 1); + } else { + source = entry.substr(nameSep + 1, sourceSep - nameSep - 1); + } + + std::vector<std::string> bmis; + if (sourceSep != std::string::npos) { + auto bmiPaths = entry.substr(sourceSep + 1); + bmis = cmSystemTools::SplitString(bmiPaths, ','); + } + + this->ImportedInfo.emplace(source, + ImportedCxxModuleInfo{ name, std::move(bmis) }); + } + + this->DoneInit = true; +} + +std::string ImportedCxxModuleLookup::BmiNameForSource(std::string const& path) +{ + auto genit = this->GeneratorInfo.find(path); + if (genit != this->GeneratorInfo.end()) { + return genit->second.BmiName; + } + + auto importit = this->ImportedInfo.find(path); + std::string bmiName; + auto hasher = cmCryptoHash::New("SHA3_512"); + constexpr size_t HASH_TRUNCATION = 12; + if (importit != this->ImportedInfo.end()) { + auto safename = hasher->HashString(importit->second.Name); + bmiName = cmStrCat(safename.substr(0, HASH_TRUNCATION), ".bmi"); + } else { + auto dirhash = hasher->HashString(path); + bmiName = cmStrCat(dirhash.substr(0, HASH_TRUNCATION), ".bmi"); + } + + this->GeneratorInfo.emplace( + path, ImportedCxxModuleGeneratorInfo{ &importit->second, bmiName }); + return bmiName; +} diff --git a/Source/cmImportedCxxModuleInfo.h b/Source/cmImportedCxxModuleInfo.h new file mode 100644 index 0000000..e052283 --- /dev/null +++ b/Source/cmImportedCxxModuleInfo.h @@ -0,0 +1,37 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <map> +#include <string> +#include <vector> + +struct ImportedCxxModuleInfo +{ + std::string const Name; + std::vector<std::string> const AvailableBmis; +}; + +struct ImportedCxxModuleGeneratorInfo +{ + ImportedCxxModuleInfo const* ImportedInfo; + std::string const BmiName; +}; + +struct ImportedCxxModuleLookup +{ + ImportedCxxModuleLookup() = default; + ~ImportedCxxModuleLookup() = default; + + bool Initialized() const; + void Initialize(std::string const& importedModules); + + std::string BmiNameForSource(std::string const& path); + +private: + bool DoneInit = false; + std::map<std::string, ImportedCxxModuleInfo> ImportedInfo; + std::map<std::string, ImportedCxxModuleGeneratorInfo> GeneratorInfo; +}; diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 089498b..48c30b6 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -62,12 +62,15 @@ cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() = default; void cmNinjaNormalTargetGenerator::Generate(const std::string& config) { - std::string lang = this->GeneratorTarget->GetLinkerLanguage(config); - if (this->TargetLinkLanguage(config).empty()) { - cmSystemTools::Error( - cmStrCat("CMake can not determine linker language for target: ", - this->GetGeneratorTarget()->GetName())); - return; + if (this->GetGeneratorTarget()->GetType() != + cmStateEnums::INTERFACE_LIBRARY) { + std::string lang = this->GeneratorTarget->GetLinkerLanguage(config); + if (this->TargetLinkLanguage(config).empty()) { + cmSystemTools::Error( + cmStrCat("CMake can not determine linker language for target: ", + this->GetGeneratorTarget()->GetName())); + return; + } } // Write the rules for each language. @@ -87,6 +90,34 @@ void cmNinjaNormalTargetGenerator::Generate(const std::string& config) if (this->GetGeneratorTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) { this->WriteObjectLibStatement(config); + } else if (this->GetGeneratorTarget()->GetType() == + cmStateEnums::INTERFACE_LIBRARY) { + bool haveCxxModuleSources = false; + if (this->GetGeneratorTarget()->HaveCxx20ModuleSources()) { + haveCxxModuleSources = true; + } + + if (!haveCxxModuleSources) { + cmSystemTools::Error(cmStrCat( + "Ninja does not support INTERFACE libraries without C++ module " + "sources as a normal target: ", + this->GetGeneratorTarget()->GetName())); + return; + } + + firstForConfig = true; + for (auto const& fileConfig : this->GetConfigNames()) { + if (!this->GetGlobalGenerator() + ->GetCrossConfigs(fileConfig) + .count(config)) { + continue; + } + if (haveCxxModuleSources) { + this->WriteCxxModuleLibraryStatement(config, fileConfig, + firstForConfig); + } + firstForConfig = false; + } } else { firstForConfig = true; for (auto const& fileConfig : this->GetConfigNames()) { @@ -123,12 +154,26 @@ void cmNinjaNormalTargetGenerator::WriteLanguagesRules( #endif // Write rules for languages compiled in this target. - std::set<std::string> languages; - std::vector<cmSourceFile const*> sourceFiles; - this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config); - if (this->HaveRequiredLanguages(sourceFiles, languages)) { - for (std::string const& language : languages) { - this->WriteLanguageRules(language, config); + { + std::set<std::string> languages; + std::vector<cmSourceFile const*> sourceFiles; + this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config); + if (this->HaveRequiredLanguages(sourceFiles, languages)) { + for (std::string const& language : languages) { + this->WriteLanguageRules(language, config); + } + } + } + + // Write rules for languages in BMI-only rules. + { + std::set<std::string> languages; + std::vector<cmSourceFile const*> sourceFiles; + this->GetGeneratorTarget()->GetCxxModuleSources(sourceFiles, config); + if (this->HaveRequiredLanguages(sourceFiles, languages)) { + for (std::string const& language : languages) { + this->WriteLanguageRules(language, config); + } } } } @@ -1637,6 +1682,34 @@ void cmNinjaNormalTargetGenerator::WriteObjectLibStatement( this->GetTargetName(), this->GetGeneratorTarget(), config); } +void cmNinjaNormalTargetGenerator::WriteCxxModuleLibraryStatement( + const std::string& config, const std::string& /*fileConfig*/, + bool firstForConfig) +{ + // TODO: How to use `fileConfig` properly? + + // Write a phony output that depends on the scanning output. + { + cmNinjaBuild build("phony"); + build.Comment = + cmStrCat("Imported C++ module library ", this->GetTargetName()); + this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(), + build.Outputs, config); + if (firstForConfig) { + this->GetLocalGenerator()->AppendTargetOutputs( + this->GetGeneratorTarget(), + this->GetGlobalGenerator()->GetByproductsForCleanTarget(config), + config); + } + build.ExplicitDeps.emplace_back(this->GetDyndepFilePath("CXX", config)); + this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), build); + } + + // Add aliases for the target name. + this->GetGlobalGenerator()->AddTargetAlias( + this->GetTargetName(), this->GetGeneratorTarget(), config); +} + cmGeneratorTarget::Names cmNinjaNormalTargetGenerator::TargetNames( const std::string& config) const { diff --git a/Source/cmNinjaNormalTargetGenerator.h b/Source/cmNinjaNormalTargetGenerator.h index 187ea46..3ef0230 100644 --- a/Source/cmNinjaNormalTargetGenerator.h +++ b/Source/cmNinjaNormalTargetGenerator.h @@ -49,6 +49,9 @@ private: const std::string& output); void WriteObjectLibStatement(const std::string& config); + void WriteCxxModuleLibraryStatement(const std::string& config, + const std::string& fileConfig, + bool firstForConfig); std::vector<std::string> ComputeLinkCmd(const std::string& config); std::vector<std::string> ComputeDeviceLinkCmd(); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 6792cd7..09f8495 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -28,6 +28,7 @@ #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" +#include "cmGlobalCommonGenerator.h" #include "cmGlobalNinjaGenerator.h" #include "cmList.h" #include "cmLocalGenerator.h" @@ -59,8 +60,13 @@ std::unique_ptr<cmNinjaTargetGenerator> cmNinjaTargetGenerator::New( case cmStateEnums::OBJECT_LIBRARY: return cm::make_unique<cmNinjaNormalTargetGenerator>(target); - case cmStateEnums::UTILITY: case cmStateEnums::INTERFACE_LIBRARY: + if (target->HaveCxx20ModuleSources()) { + return cm::make_unique<cmNinjaNormalTargetGenerator>(target); + } + CM_FALLTHROUGH; + + case cmStateEnums::UTILITY: case cmStateEnums::GLOBAL_TARGET: return cm::make_unique<cmNinjaUtilityTargetGenerator>(target); @@ -167,7 +173,7 @@ std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget( // Refactor it. std::string cmNinjaTargetGenerator::ComputeFlagsForObject( cmSourceFile const* source, const std::string& language, - const std::string& config) + const std::string& config, const std::string& objectFileName) { std::unordered_map<std::string, std::string> pchSources; std::vector<std::string> architectures = @@ -247,6 +253,18 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject( "\nin a file set of type \"", fs->GetType(), R"(" but the source is not classified as a "CXX" source.)")); } + + if (!this->GeneratorTarget->Target->IsNormal()) { + auto flag = this->GetMakefile()->GetSafeDefinition( + "CMAKE_EXPERIMENTAL_CXX_MODULE_BMI_ONLY_FLAG"); + cmRulePlaceholderExpander::RuleVariables compileObjectVars; + compileObjectVars.Object = objectFileName.c_str(); + auto rulePlaceholderExpander = + this->GetLocalGenerator()->CreateRulePlaceholderExpander(); + rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), + flag, compileObjectVars); + this->LocalGenerator->AppendCompileOptions(flags, flag); + } } return flags; @@ -394,6 +412,31 @@ std::string cmNinjaTargetGenerator::GetObjectFilePath( return path; } +std::string cmNinjaTargetGenerator::GetBmiFilePath( + cmSourceFile const* source, const std::string& config) const +{ + std::string path = this->LocalGenerator->GetHomeRelativeOutputPath(); + if (!path.empty()) { + path += '/'; + } + + auto& importedConfigInfo = this->Configs.at(config).ImportedCxxModules; + if (!importedConfigInfo.Initialized()) { + std::string configUpper = cmSystemTools::UpperCase(config); + std::string propName = cmStrCat("IMPORTED_CXX_MODULES_", configUpper); + auto value = this->GeneratorTarget->GetSafeProperty(propName); + importedConfigInfo.Initialize(value); + } + + std::string bmiName = + importedConfigInfo.BmiNameForSource(source->GetFullPath()); + + path += cmStrCat( + this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), + this->GetGlobalGenerator()->ConfigDirectory(config), '/', bmiName); + return path; +} + std::string cmNinjaTargetGenerator::GetClangTidyReplacementsFilePath( std::string const& directory, cmSourceFile const& source, std::string const& config) const @@ -1027,6 +1070,16 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( } } + { + std::vector<cmSourceFile const*> bmiOnlySources; + this->GeneratorTarget->GetCxxModuleSources(bmiOnlySources, config); + + for (cmSourceFile const* sf : bmiOnlySources) { + this->WriteCxxModuleBmiBuildStatement(sf, config, fileConfig, + firstForConfig); + } + } + for (auto const& langScanningFiles : this->Configs[config].ScanningInfo) { std::string const& language = langScanningFiles.first; std::vector<ScanningFiles> const& scanningFiles = langScanningFiles.second; @@ -1149,22 +1202,22 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName, scanBuild.Variables["OBJ_FILE"] = objectFileName; // Tell dependency scanner where to store dyndep intermediate results. - std::string const& ddiFile = cmStrCat(objectFileName, ".ddi"); - scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; + std::string ddiFileName = cmStrCat(objectFileName, ".ddi"); + scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFileName; // Outputs of the scan/preprocessor build statement. if (compilePP) { scanBuild.Outputs.push_back(ppFileName); - scanBuild.ImplicitOuts.push_back(ddiFile); + scanBuild.ImplicitOuts.push_back(ddiFileName); } else { - scanBuild.Outputs.push_back(ddiFile); + scanBuild.Outputs.push_back(ddiFileName); scanBuild.Variables["PREPROCESSED_OUTPUT_FILE"] = ppFileName; if (!compilationPreprocesses) { // Compilation does not preprocess and we are not compiling an // already-preprocessed source. Make compilation depend on the scan // results to honor implicit dependencies discovered during scanning // (such as Fortran INCLUDE directives). - objBuild.ImplicitDeps.emplace_back(ddiFile); + objBuild.ImplicitDeps.emplace_back(ddiFileName); } } @@ -1214,7 +1267,8 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( cmNinjaBuild objBuild(this->LanguageCompilerRule( language, config, needDyndep ? WithScanning::Yes : WithScanning::No)); cmNinjaVars& vars = objBuild.Variables; - vars["FLAGS"] = this->ComputeFlagsForObject(source, language, config); + vars["FLAGS"] = + this->ComputeFlagsForObject(source, language, config, objectFileName); vars["DEFINES"] = this->ComputeDefines(source, language, config); vars["INCLUDES"] = this->ComputeIncludes(source, language, config); @@ -1545,6 +1599,155 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } } +void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement( + cmSourceFile const* source, const std::string& config, + const std::string& fileConfig, bool firstForConfig) +{ + std::string const language = source->GetLanguage(); + if (language != "CXX"_s) { + this->GetMakefile()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Source file '", source->GetFullPath(), "' of target '", + this->GetTargetName(), "' is a '", language, + "' source but must be 'CXX' in order to have a BMI build " + "statement generated.")); + return; + } + + std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source); + std::string const bmiDir = this->ConvertToNinjaPath( + cmStrCat(this->GeneratorTarget->GetSupportDirectory(), + this->GetGlobalGenerator()->ConfigDirectory(config))); + std::string const bmiFileName = + this->ConvertToNinjaPath(this->GetBmiFilePath(source, config)); + std::string const bmiFileDir = cmSystemTools::GetFilenamePath(bmiFileName); + + int const commandLineLengthLimit = this->ForceResponseFile() ? -1 : 0; + + cmNinjaBuild bmiBuild( + this->LanguageCompilerRule(language, config, WithScanning::Yes)); + cmNinjaVars& vars = bmiBuild.Variables; + vars["FLAGS"] = + this->ComputeFlagsForObject(source, language, config, bmiFileName); + vars["DEFINES"] = this->ComputeDefines(source, language, config); + vars["INCLUDES"] = this->ComputeIncludes(source, language, config); + + if (this->GetMakefile()->GetSafeDefinition( + cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) { + bool replaceExt(false); + if (!language.empty()) { + std::string repVar = + cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE"); + replaceExt = this->Makefile->IsOn(repVar); + } + if (!replaceExt) { + // use original code + vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( + cmStrCat(bmiFileName, ".d"), cmOutputConverter::SHELL); + } else { + // Replace the original source file extension with the + // depend file extension. + std::string dependFileName = cmStrCat( + cmSystemTools::GetFilenameWithoutLastExtension(bmiFileName), ".d"); + vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( + cmStrCat(bmiFileDir, '/', dependFileName), cmOutputConverter::SHELL); + } + } + + std::string d = + this->GeneratorTarget->GetClangTidyExportFixesDirectory(language); + if (!d.empty()) { + this->GlobalCommonGenerator->AddClangTidyExportFixesDir(d); + std::string fixesFile = + this->GetClangTidyReplacementsFilePath(d, *source, config); + this->GlobalCommonGenerator->AddClangTidyExportFixesFile(fixesFile); + cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(fixesFile)); + fixesFile = this->ConvertToNinjaPath(fixesFile); + vars["CLANG_TIDY_EXPORT_FIXES"] = fixesFile; + } + + if (firstForConfig) { + this->ExportObjectCompileCommand( + language, sourceFilePath, bmiDir, bmiFileName, bmiFileDir, vars["FLAGS"], + vars["DEFINES"], vars["INCLUDES"], config); + } + + bmiBuild.Outputs.push_back(bmiFileName); + bmiBuild.ExplicitDeps.push_back(sourceFilePath); + + std::vector<std::string> depList; + + std::vector<std::string> architectures = + this->GeneratorTarget->GetAppleArchs(config, language); + if (architectures.empty()) { + architectures.emplace_back(); + } + + bmiBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config)); + + // For some cases we scan to dynamically discover dependencies. + std::string modmapFormat; + if (true) { + std::string const modmapFormatVar = + cmStrCat("CMAKE_EXPERIMENTAL_", language, "_MODULE_MAP_FORMAT"); + modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar); + } + + { + bool const compilePPWithDefines = this->CompileWithDefines(language); + + std::string scanRuleName = this->LanguageScanRule(language, config); + std::string ppFileName = cmStrCat(bmiFileName, ".ddi.i"); + + cmNinjaBuild ppBuild = GetScanBuildStatement( + scanRuleName, ppFileName, false, compilePPWithDefines, true, bmiBuild, + vars, bmiFileName, this->LocalGenerator); + + ScanningFiles scanningFiles; + + if (firstForConfig) { + scanningFiles.ScanningOutput = cmStrCat(bmiFileName, ".ddi"); + } + + this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), + ppBuild.Variables); + + this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), + ppBuild, commandLineLengthLimit); + + std::string const dyndep = this->GetDyndepFilePath(language, config); + bmiBuild.OrderOnlyDeps.push_back(dyndep); + vars["dyndep"] = dyndep; + + if (!modmapFormat.empty()) { + std::string ddModmapFile = cmStrCat(bmiFileName, ".modmap"); + vars["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile; + scanningFiles.ModuleMapFile = std::move(ddModmapFile); + } + + if (!scanningFiles.IsEmpty()) { + this->Configs[config].ScanningInfo[language].emplace_back(scanningFiles); + } + } + + this->EnsureParentDirectoryExists(bmiFileName); + + vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( + bmiDir, cmOutputConverter::SHELL); + vars["OBJECT_FILE_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( + bmiFileDir, cmOutputConverter::SHELL); + + this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), + vars); + + this->SetMsvcTargetPdbVariable(vars, config); + + bmiBuild.RspFile = cmStrCat(bmiFileName, ".rsp"); + + this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), + bmiBuild, commandLineLengthLimit); +} + void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, const std::string& config) { @@ -1605,6 +1808,9 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, cb.ObjectFilePath = [this](cmSourceFile const* sf, std::string const& cnf) { return this->GetObjectFilePath(sf, cnf); }; + cb.BmiFilePath = [this](cmSourceFile const* sf, std::string const& cnf) { + return this->GetBmiFilePath(sf, cnf); + }; #if !defined(CMAKE_BOOTSTRAP) cmDyndepCollation::AddCollationInformation(tdi, this->GeneratorTarget, diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 8c38499..49e7018 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -15,6 +15,7 @@ #include "cmCommonTargetGenerator.h" #include "cmGlobalNinjaGenerator.h" +#include "cmImportedCxxModuleInfo.h" #include "cmNinjaTypes.h" #include "cmOSXBundleGenerator.h" @@ -91,7 +92,8 @@ protected: */ std::string ComputeFlagsForObject(cmSourceFile const* source, const std::string& language, - const std::string& config); + const std::string& config, + const std::string& objectFileName); void AddIncludeFlags(std::string& flags, std::string const& lang, const std::string& config) override; @@ -129,6 +131,8 @@ protected: /// @return the object file path for the given @a source. std::string GetObjectFilePath(cmSourceFile const* source, const std::string& config) const; + std::string GetBmiFilePath(cmSourceFile const* source, + const std::string& config) const; /// @return the preprocessed source file path for the given @a source. std::string GetPreprocessedFilePath(cmSourceFile const* source, @@ -163,6 +167,10 @@ protected: void WriteObjectBuildStatements(const std::string& config, const std::string& fileConfig, bool firstForConfig); + void WriteCxxModuleBmiBuildStatement(cmSourceFile const* source, + const std::string& config, + const std::string& fileConfig, + bool firstForConfig); void WriteObjectBuildStatement(cmSourceFile const* source, const std::string& config, const std::string& fileConfig, @@ -239,6 +247,8 @@ private: cmNinjaDeps Objects; // Dyndep Support std::map<std::string, std::vector<ScanningFiles>> ScanningInfo; + // Imported C++ module info. + mutable ImportedCxxModuleLookup ImportedCxxModules; // Swift Support Json::Value SwiftOutputMap; std::vector<cmCustomCommand const*> CustomCommands; diff --git a/Source/cmSyntheticTargetCache.h b/Source/cmSyntheticTargetCache.h new file mode 100644 index 0000000..22d1533 --- /dev/null +++ b/Source/cmSyntheticTargetCache.h @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <map> +#include <string> + +class cmGeneratorTarget; + +struct cmSyntheticTargetCache +{ + std::map<std::string, cmGeneratorTarget const*> CxxModuleTargets; +}; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 7cc5d2e..ace93e8 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -246,9 +246,9 @@ struct UsageRequirementProperty { } - void CopyFromDirectory(cmBTStringRange directoryEntries) + void CopyFromEntries(cmBTStringRange entries) { - return cm::append(this->Entries, directoryEntries); + return cm::append(this->Entries, entries); } enum class Action @@ -673,6 +673,11 @@ public: UsageRequirementProperty InterfaceLinkLibraries; UsageRequirementProperty InterfaceLinkLibrariesDirect; UsageRequirementProperty InterfaceLinkLibrariesDirectExclude; + UsageRequirementProperty ImportedCxxModulesIncludeDirectories; + UsageRequirementProperty ImportedCxxModulesCompileDefinitions; + UsageRequirementProperty ImportedCxxModulesCompileFeatures; + UsageRequirementProperty ImportedCxxModulesCompileOptions; + UsageRequirementProperty ImportedCxxModulesLinkLibraries; FileSetType HeadersFileSets; FileSetType CxxModulesFileSets; @@ -723,6 +728,14 @@ cmTargetInternals::cmTargetInternals() , InterfaceLinkLibrariesDirect("INTERFACE_LINK_LIBRARIES_DIRECT"_s) , InterfaceLinkLibrariesDirectExclude( "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"_s) + , ImportedCxxModulesIncludeDirectories( + "IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES"_s) + , ImportedCxxModulesCompileDefinitions( + "IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS"_s) + , ImportedCxxModulesCompileFeatures( + "IMPORTED_CXX_MODULES_COMPILE_FEATURES"_s) + , ImportedCxxModulesCompileOptions("IMPORTED_CXX_MODULES_COMPILE_OPTIONS"_s) + , ImportedCxxModulesLinkLibraries("IMPORTED_CXX_MODULES_LINK_LIBRARIES"_s) , HeadersFileSets("HEADERS"_s, "HEADER_DIRS"_s, "HEADER_SET"_s, "HEADER_DIRS_"_s, "HEADER_SET_"_s, "Header"_s, "The default header set"_s, "Header set"_s, @@ -951,7 +964,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, if (this->IsNormal()) { // Initialize the INCLUDE_DIRECTORIES property based on the current value // of the same directory property: - this->impl->IncludeDirectories.CopyFromDirectory( + this->impl->IncludeDirectories.CopyFromEntries( this->impl->Makefile->GetIncludeDirectoriesEntries()); { @@ -960,11 +973,11 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, sysInc.end()); } - this->impl->CompileOptions.CopyFromDirectory( + this->impl->CompileOptions.CopyFromEntries( this->impl->Makefile->GetCompileOptionsEntries()); - this->impl->LinkOptions.CopyFromDirectory( + this->impl->LinkOptions.CopyFromEntries( this->impl->Makefile->GetLinkOptionsEntries()); - this->impl->LinkDirectories.CopyFromDirectory( + this->impl->LinkDirectories.CopyFromEntries( this->impl->Makefile->GetLinkDirectoriesEntries()); } @@ -1735,6 +1748,186 @@ cmBTStringRange cmTarget::GetLinkInterfaceDirectExcludeEntries() const return cmMakeRange(this->impl->InterfaceLinkLibrariesDirectExclude.Entries); } +void cmTarget::CopyPolicyStatuses(cmTarget const* tgt) +{ + // Normal targets cannot be the target of a copy. + assert(!this->IsNormal()); + // Imported targets cannot be the target of a copy. + assert(!this->IsImported()); + // Only imported targets can be the source of a copy. + assert(tgt->IsImported()); + + this->impl->PolicyMap = tgt->impl->PolicyMap; +} + +void cmTarget::CopyImportedCxxModulesEntries(cmTarget const* tgt) +{ + // Normal targets cannot be the target of a copy. + assert(!this->IsNormal()); + // Imported targets cannot be the target of a copy. + assert(!this->IsImported()); + // Only imported targets can be the source of a copy. + assert(tgt->IsImported()); + + this->impl->IncludeDirectories.Entries.clear(); + this->impl->IncludeDirectories.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesIncludeDirectories.Entries)); + this->impl->CompileDefinitions.Entries.clear(); + this->impl->CompileDefinitions.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesCompileDefinitions.Entries)); + this->impl->CompileFeatures.Entries.clear(); + this->impl->CompileFeatures.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesCompileFeatures.Entries)); + this->impl->CompileOptions.Entries.clear(); + this->impl->CompileOptions.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesCompileOptions.Entries)); + this->impl->LinkLibraries.Entries.clear(); + this->impl->LinkLibraries.CopyFromEntries( + cmMakeRange(tgt->impl->LinkLibraries.Entries)); + + // Copy the C++ module fileset entries from `tgt`'s `INTERFACE` to this + // target's `PRIVATE`. + this->impl->CxxModulesFileSets.SelfEntries.Entries.clear(); + this->impl->CxxModulesFileSets.SelfEntries.Entries = + tgt->impl->CxxModulesFileSets.InterfaceEntries.Entries; +} + +void cmTarget::CopyImportedCxxModulesProperties(cmTarget const* tgt) +{ + // Normal targets cannot be the target of a copy. + assert(!this->IsNormal()); + // Imported targets cannot be the target of a copy. + assert(!this->IsImported()); + // Only imported targets can be the source of a copy. + assert(tgt->IsImported()); + + // The list of properties that are relevant here include: + // - compilation-specific properties for any language or platform + // - compilation-specific properties for C++ + // - build graph-specific properties that affect compilation + // - IDE metadata properties + // - static analysis properties + + static const std::string propertiesToCopy[] = { + // Compilation properties + "DEFINE_SYMBOL", + "DEPRECATION", + "NO_SYSTEM_FROM_IMPORTED", + "POSITION_INDEPENDENT_CODE", + "VISIBILITY_INLINES_HIDDEN", + // -- Platforms + // ---- Android + "ANDROID_API", + "ANDROID_API_MIN", + "ANDROID_ARCH", + "ANDROID_STL_TYPE", + // ---- macOS + "OSX_ARCHITECTURES", + // ---- Windows + "MSVC_DEBUG_INFORMATION_FORMAT", + "MSVC_RUNTIME_LIBRARY", + "VS_PLATFORM_TOOLSET", + // ---- OpenWatcom + "WATCOM_RUNTIME_LIBRARY", + // -- Language + // ---- C++ + "CXX_COMPILER_LAUNCHER", + "CXX_STANDARD", + "CXX_STANDARD_REQUIRED", + "CXX_EXTENSIONS", + "CXX_VISIBILITY_PRESET", + + // Static analysis + "CXX_CLANG_TIDY", + "CXX_CLANG_TIDY_EXPORT_FIXES_DIR", + "CXX_CPPLINT", + "CXX_CPPCHECK", + "CXX_INCLUDE_WHAT_YOU_USE", + + // Build graph properties + "EXCLUDE_FROM_ALL", + "EXCLUDE_FROM_DEFAULT_BUILD", + "OPTIMIZE_DEPENDENCIES", + // -- Ninja + "JOB_POOL_COMPILE", + // -- Visual Studio + "VS_NO_COMPILE_BATCHING", + "VS_PROJECT_IMPORT", + + // Metadata + "EchoString", + "EXPORT_COMPILE_COMMANDS", + "FOLDER", + "LABELS", + "PROJECT_LABEL", + "SYSTEM", + }; + + auto copyProperty = [this, tgt](std::string const& prop) -> cmValue { + cmValue value = tgt->GetProperty(prop); + // Always set the property; it may have been explicitly unset. + this->SetProperty(prop, value); + return value; + }; + + for (auto const& prop : propertiesToCopy) { + copyProperty(prop); + } + + static const cm::static_string_view perConfigPropertiesToCopy[] = { + "EXCLUDE_FROM_DEFAULT_BUILD_"_s, + "IMPORTED_CXX_MODULES_"_s, + "MAP_IMPORTED_CONFIG_"_s, + "OSX_ARCHITECTURES_"_s, + }; + + std::vector<std::string> configNames = + this->impl->Makefile->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + for (std::string const& configName : configNames) { + std::string configUpper = cmSystemTools::UpperCase(configName); + for (auto const& perConfigProp : perConfigPropertiesToCopy) { + copyProperty(cmStrCat(perConfigProp, configUpper)); + } + } + + if (this->GetGlobalGenerator()->IsXcode()) { + cmValue xcodeGenerateScheme = copyProperty("XCODE_GENERATE_SCHEME"); + + // TODO: Make sure these show up on the imported target in the first place + // XCODE_ATTRIBUTE_??? + + if (xcodeGenerateScheme.IsOn()) { +#ifdef __APPLE__ + static const std::string xcodeSchemePropertiesToCopy[] = { + // FIXME: Do all of these apply? Do they matter? + "XCODE_SCHEME_ADDRESS_SANITIZER", + "XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN", + "XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER", + "XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS", + "XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE", + "XCODE_SCHEME_ENABLE_GPU_API_VALIDATION", + "XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION", + "XCODE_SCHEME_GUARD_MALLOC", + "XCODE_SCHEME_LAUNCH_CONFIGURATION", + "XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP", + "XCODE_SCHEME_MALLOC_GUARD_EDGES", + "XCODE_SCHEME_MALLOC_SCRIBBLE", + "XCODE_SCHEME_MALLOC_STACK", + "XCODE_SCHEME_THREAD_SANITIZER", + "XCODE_SCHEME_THREAD_SANITIZER_STOP", + "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER", + "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP", + "XCODE_SCHEME_ZOMBIE_OBJECTS", + }; + + for (auto const& xcodeProperty : xcodeSchemePropertiesToCopy) { + copyProperty(xcodeProperty); + } +#endif + } + } +} + cmBTStringRange cmTarget::GetHeaderSetsEntries() const { return cmMakeRange(this->impl->HeadersFileSets.SelfEntries.Entries); @@ -1777,6 +1970,11 @@ MAKE_PROP(IMPORTED); MAKE_PROP(IMPORTED_GLOBAL); MAKE_PROP(INCLUDE_DIRECTORIES); MAKE_PROP(LINK_OPTIONS); +MAKE_PROP(IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES); +MAKE_PROP(IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS); +MAKE_PROP(IMPORTED_CXX_MODULES_COMPILE_FEATURES); +MAKE_PROP(IMPORTED_CXX_MODULES_COMPILE_OPTIONS); +MAKE_PROP(IMPORTED_CXX_MODULES_LINK_LIBRARIES); MAKE_PROP(LINK_DIRECTORIES); MAKE_PROP(LINK_LIBRARIES); MAKE_PROP(MANUALLY_ADDED_DEPENDENCIES); @@ -1846,6 +2044,11 @@ void cmTarget::SetProperty(const std::string& prop, cmValue value) &this->impl->InterfaceLinkLibraries, &this->impl->InterfaceLinkLibrariesDirect, &this->impl->InterfaceLinkLibrariesDirectExclude, + &this->impl->ImportedCxxModulesIncludeDirectories, + &this->impl->ImportedCxxModulesCompileDefinitions, + &this->impl->ImportedCxxModulesCompileFeatures, + &this->impl->ImportedCxxModulesCompileOptions, + &this->impl->ImportedCxxModulesLinkLibraries, }; for (auto* usageRequirement : usageRequirements) { @@ -2019,6 +2222,11 @@ void cmTarget::AppendProperty(const std::string& prop, &this->impl->InterfaceLinkLibraries, &this->impl->InterfaceLinkLibrariesDirect, &this->impl->InterfaceLinkLibrariesDirectExclude, + &this->impl->ImportedCxxModulesIncludeDirectories, + &this->impl->ImportedCxxModulesCompileDefinitions, + &this->impl->ImportedCxxModulesCompileFeatures, + &this->impl->ImportedCxxModulesCompileOptions, + &this->impl->ImportedCxxModulesLinkLibraries, }; for (auto* usageRequirement : usageRequirements) { @@ -2445,6 +2653,11 @@ cmValue cmTarget::GetProperty(const std::string& prop) const propINTERFACE_LINK_LIBRARIES, propINTERFACE_LINK_LIBRARIES_DIRECT, propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE, + propIMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES, + propIMPORTED_CXX_MODULES_COMPILE_DEFINITIONS, + propIMPORTED_CXX_MODULES_COMPILE_FEATURES, + propIMPORTED_CXX_MODULES_COMPILE_OPTIONS, + propIMPORTED_CXX_MODULES_LINK_LIBRARIES, }; if (specialProps.count(prop)) { if (prop == propC_STANDARD || prop == propCXX_STANDARD || @@ -2470,6 +2683,11 @@ cmValue cmTarget::GetProperty(const std::string& prop) const &this->impl->InterfaceLinkLibraries, &this->impl->InterfaceLinkLibrariesDirect, &this->impl->InterfaceLinkLibrariesDirectExclude, + &this->impl->ImportedCxxModulesIncludeDirectories, + &this->impl->ImportedCxxModulesCompileDefinitions, + &this->impl->ImportedCxxModulesCompileFeatures, + &this->impl->ImportedCxxModulesCompileOptions, + &this->impl->ImportedCxxModulesLinkLibraries, }; for (auto const* usageRequirement : usageRequirements) { @@ -2675,6 +2893,9 @@ bool cmTarget::CanCompileSources() const if (this->IsImported()) { return false; } + if (this->IsSynthetic()) { + return true; + } switch (this->GetType()) { case cmStateEnums::EXECUTABLE: case cmStateEnums::STATIC_LIBRARY: diff --git a/Source/cmTarget.h b/Source/cmTarget.h index dae997f..b77ea0c 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -291,6 +291,10 @@ public: cmBTStringRange GetLinkInterfaceDirectEntries() const; cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const; + void CopyPolicyStatuses(cmTarget const* tgt); + void CopyImportedCxxModulesEntries(cmTarget const* tgt); + void CopyImportedCxxModulesProperties(cmTarget const* tgt); + cmBTStringRange GetHeaderSetsEntries() const; cmBTStringRange GetCxxModuleSetsEntries() const; diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 48f3197..1bd4c57 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -2517,6 +2517,7 @@ void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0) case cmGeneratorTarget::SourceKindModuleDefinition: tool = "None"; break; + case cmGeneratorTarget::SourceKindCxxModuleSource: case cmGeneratorTarget::SourceKindUnityBatched: case cmGeneratorTarget::SourceKindObjectSource: { const std::string& lang = si.Source->GetLanguage(); diff --git a/Tests/RunCMake/CXXModules/CMakeLists.txt b/Tests/RunCMake/CXXModules/CMakeLists.txt index ecc66b6..e23023d 100644 --- a/Tests/RunCMake/CXXModules/CMakeLists.txt +++ b/Tests/RunCMake/CXXModules/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.23) project(${RunCMake_TEST} NONE) -set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "a816ed09-43d1-40e5-bc8c-1a2824ee194e") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake index 25670bd..0ca9945 100644 --- a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake +++ b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake @@ -187,7 +187,20 @@ endif () if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) run_cxx_module_test(export-interface-no-properties-build) run_cxx_module_test(export-interface-build) + run_cxx_module_test(export-usage-build) run_cxx_module_test(export-bmi-and-interface-build) + + if ("collation" IN_LIST CMake_TEST_MODULE_COMPILATION AND + "bmionly" IN_LIST CMake_TEST_MODULE_COMPILATION) + set(test_suffix export-interface-build) + run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build") + + set(test_suffix export-interface-no-properties-build) + run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build" -DNO_PROPERTIES=1) + + set(test_suffix export-bmi-and-interface-build) + run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build" -DWITH_BMIS=1) + endif () endif () # All of the following tests perform installation. @@ -201,6 +214,21 @@ if ("install_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) run_cxx_module_test(export-interface-no-properties-install) run_cxx_module_test(export-interface-install) + run_cxx_module_test(export-usage-install) run_cxx_module_test(export-bmi-and-interface-install) + + if ("collation" IN_LIST CMake_TEST_MODULE_COMPILATION AND + "bmionly" IN_LIST CMake_TEST_MODULE_COMPILATION) + set(RunCMake_CXXModules_INSTALL 0) + set(test_suffix export-interface-install) + run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install") + + set(test_suffix export-interface-no-properties-install) + run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install" -DNO_PROPERTIES=1) + + set(test_suffix export-bmi-and-interface-install) + run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install" -DWITH_BMIS=1) + set(RunCMake_CXXModules_INSTALL 1) + endif () endif () endif () diff --git a/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake b/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake index 6238d37..5f32364 100644 --- a/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake +++ b/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake @@ -1,4 +1,4 @@ -set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "a816ed09-43d1-40e5-bc8c-1a2824ee194e") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") if (NOT EXISTS "${CMake_TEST_MODULE_COMPILATION_RULES}") message(FATAL_ERROR diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt index 4d7a85b..c17577c 100644 --- a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.24) project(cxx_modules_library NONE) -set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "a816ed09-43d1-40e5-bc8c-1a2824ee194e") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") find_package(export_bmi_and_interfaces REQUIRED) diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt index b96328f..d608d67 100644 --- a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt +++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.24) project(cxx_modules_library NONE) -set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "a816ed09-43d1-40e5-bc8c-1a2824ee194e") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") find_package(export_bmi_and_interfaces REQUIRED) diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt index 74f16a6..106bd1e 100644 --- a/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt +++ b/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.24) project(cxx_modules_library NONE) -set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "a816ed09-43d1-40e5-bc8c-1a2824ee194e") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") find_package(export_interfaces REQUIRED) diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt index a4c9225..c19283b 100644 --- a/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt +++ b/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.24) project(cxx_modules_library NONE) -set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "a816ed09-43d1-40e5-bc8c-1a2824ee194e") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") find_package(export_interfaces REQUIRED) diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build/test/CMakeLists.txt index 7f145ba2..fba05f4 100644 --- a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build/test/CMakeLists.txt +++ b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build/test/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.24) project(cxx_modules_library NONE) -set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "a816ed09-43d1-40e5-bc8c-1a2824ee194e") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") find_package(export_interfaces_no_properties REQUIRED) diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install/test/CMakeLists.txt index 7f145ba2..fba05f4 100644 --- a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install/test/CMakeLists.txt +++ b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install/test/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.24) project(cxx_modules_library NONE) -set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "a816ed09-43d1-40e5-bc8c-1a2824ee194e") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") find_package(export_interfaces_no_properties REQUIRED) diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-usage-build-stderr.txt new file mode 100644 index 0000000..78bdf2b --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-usage-build-stderr.txt @@ -0,0 +1,4 @@ +CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-usage-build/CMakeLists.txt new file mode 100644 index 0000000..86a608b --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-usage-build/CMakeLists.txt @@ -0,0 +1,110 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_export_usage CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(export_usage STATIC) +target_sources(export_usage + PRIVATE + forward.cxx + PRIVATE + FILE_SET modules_private TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + private.cxx + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx) +target_compile_features(export_usage PUBLIC cxx_std_20) + +list(APPEND CMAKE_CXX_KNOWN_FEATURES + exported + buildiface + installiface + buildlocaliface) + +target_include_directories(export_usage + PRIVATE + "/usr/exported" + "$<BUILD_INTERFACE:/usr/buildiface>" + "$<INSTALL_INTERFACE:/usr/installiface>" + "$<BUILD_LOCAL_INTERFACE:/usr/buildlocaliface>") +target_compile_definitions(export_usage + PRIVATE + "exported" + "$<BUILD_INTERFACE:buildiface>" + "$<INSTALL_INTERFACE:installiface>" + "$<BUILD_LOCAL_INTERFACE:buildlocaliface>") +target_compile_features(export_usage + PRIVATE + "cxx_std_11" + "$<BUILD_INTERFACE:cxx_std_14>" + "$<INSTALL_INTERFACE:cxx_std_17>" + "$<BUILD_LOCAL_INTERFACE:cxx_std_20>") + +if (MSVC) + set(variable_flag "-constexpr:depth") +else () + set(variable_flag "-fconstexpr-depth=") +endif () + +target_compile_options(export_usage + PRIVATE + "${variable_flag}100" + "$<BUILD_INTERFACE:${variable_flag}200>" + "$<INSTALL_INTERFACE:${variable_flag}300>" + "$<BUILD_LOCAL_INTERFACE:${variable_flag}400>") + +add_library(export_used INTERFACE) +add_library(export_build INTERFACE) +add_library(export_install INTERFACE) +add_library(export_never INTERFACE) + +target_link_libraries(export_usage + PRIVATE + "export_used" + "$<BUILD_INTERFACE:export_build>" + "$<INSTALL_INTERFACE:export_install>" + "$<BUILD_LOCAL_INTERFACE:export_never>") + +install(TARGETS export_usage + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu") +export(EXPORT CXXModules + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/export_usage-targets.cmake") +install(TARGETS export_used export_build export_install + EXPORT CXXModulesDeps) +export(EXPORT CXXModulesDeps + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/export_usage-dep-targets.cmake") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_usage-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_usage-dep-targets.cmake\") +include(\"\${CMAKE_CURRENT_LIST_DIR}/export_usage-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") + +set(generator + -G "${CMAKE_GENERATOR}") +if (CMAKE_GENERATOR_TOOLSET) + list(APPEND generator + -T "${CMAKE_GENERATOR_TOOLSET}") +endif () +if (CMAKE_GENERATOR_PLATFORM) + list(APPEND generator + -A "${CMAKE_GENERATOR_PLATFORM}") +endif () + +add_test(NAME export_usage_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_dir=${CMAKE_CURRENT_SOURCE_DIR}" + "-Dexport_interfaces_flag=${variable_flag}" + "-Dexport_usage_DIR=${CMAKE_CURRENT_BINARY_DIR}" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test") diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-build/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-usage-build/forward.cxx new file mode 100644 index 0000000..7f53271 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-usage-build/forward.cxx @@ -0,0 +1,6 @@ +import priv; + +int forwarding() +{ + return from_private(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-build/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-usage-build/importable.cxx new file mode 100644 index 0000000..8dfc41b --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-usage-build/importable.cxx @@ -0,0 +1,10 @@ +export module importable; + +extern "C++" { +int forwarding(); +} + +export int from_import() +{ + return forwarding(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-build/private.cxx b/Tests/RunCMake/CXXModules/examples/export-usage-build/private.cxx new file mode 100644 index 0000000..c5b719a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-usage-build/private.cxx @@ -0,0 +1,6 @@ +export module priv; + +export int from_private() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-usage-build/test/CMakeLists.txt new file mode 100644 index 0000000..adec9e7 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-usage-build/test/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_library NONE) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") + +find_package(export_usage REQUIRED) + +if (NOT TARGET CXXModules::export_usage) + message(FATAL_ERROR + "Missing imported target") +endif () + +if (NOT TARGET CXXModules::export_used) + message(FATAL_ERROR + "Missing imported target") +endif () + +if (NOT TARGET CXXModules::export_build) + message(FATAL_ERROR + "Missing imported target") +endif () + +if (NOT TARGET CXXModules::export_install) + message(FATAL_ERROR + "Missing imported target") +endif () + +if (TARGET CXXModules::export_never) + message(FATAL_ERROR + "Extra imported target") +endif () + +function (check_property expected property) + get_property(actual TARGET CXXModules::export_usage + PROPERTY "${property}") + if (NOT actual STREQUAL expected) + message(SEND_ERROR + "Mismatch for ${property}:\n expected: ${expected}\n actual: ${actual}") + endif () +endfunction () + +check_property("/usr/exported;/usr/buildiface" "IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES") +check_property("exported;buildiface" "IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS") +check_property("cxx_std_20;cxx_std_11;cxx_std_14" "IMPORTED_CXX_MODULES_COMPILE_FEATURES") +check_property("${export_interfaces_flag}100;${export_interfaces_flag}200" "IMPORTED_CXX_MODULES_COMPILE_OPTIONS") +check_property("$<COMPILE_ONLY:CXXModules::export_used>;$<COMPILE_ONLY:CXXModules::export_build>" "IMPORTED_CXX_MODULES_LINK_LIBRARIES") + +# Extract the export-dependent targets from the export file. +file(STRINGS "${export_usage_DIR}/export_usage-targets.cmake" usage_dependent_targets + REGEX "foreach._target ") +# Rudimentary argument splitting. +string(REPLACE " " ";" usage_dependent_targets "${usage_dependent_targets}") +# Keep only "target" names. +list(FILTER usage_dependent_targets INCLUDE REGEX "CXXModules::") +# Strip quotes. +string(REPLACE "\"" "" usage_dependent_targets "${usage_dependent_targets}") + +if (NOT "CXXModules::export_used" IN_LIST usage_dependent_targets) + message(SEND_ERROR + "The main export does not require the 'CXXModules::export_used' target") +endif () +if (NOT "CXXModules::export_build" IN_LIST usage_dependent_targets) + message(SEND_ERROR + "The main export does not require the 'CXXModules::export_build' target") +endif () +if ("CXXModules::export_install" IN_LIST usage_dependent_targets) + message(SEND_ERROR + "The main export requires the 'CXXModules::export_install' target") +endif () diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-usage-install-stderr.txt new file mode 100644 index 0000000..78bdf2b --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-usage-install-stderr.txt @@ -0,0 +1,4 @@ +CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-usage-install/CMakeLists.txt new file mode 100644 index 0000000..11f53b0 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-usage-install/CMakeLists.txt @@ -0,0 +1,114 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_export_usage CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(export_usage STATIC) +target_sources(export_usage + PRIVATE + forward.cxx + PRIVATE + FILE_SET modules_private TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + private.cxx + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx) +target_compile_features(export_usage PUBLIC cxx_std_20) + +list(APPEND CMAKE_CXX_KNOWN_FEATURES + exported + buildiface + installiface + buildlocaliface) + +target_include_directories(export_usage + PRIVATE + "/usr/exported" + "$<BUILD_INTERFACE:/usr/buildiface>" + "$<INSTALL_INTERFACE:/usr/installiface>" + "$<BUILD_LOCAL_INTERFACE:/usr/buildlocaliface>") +target_compile_definitions(export_usage + PRIVATE + "exported" + "$<BUILD_INTERFACE:buildiface>" + "$<INSTALL_INTERFACE:installiface>" + "$<BUILD_LOCAL_INTERFACE:buildlocaliface>") +target_compile_features(export_usage + PRIVATE + "cxx_std_11" + "$<BUILD_INTERFACE:cxx_std_14>" + "$<INSTALL_INTERFACE:cxx_std_17>" + "$<BUILD_LOCAL_INTERFACE:cxx_std_20>") + +if (MSVC) + set(variable_flag "-constexpr:depth") +else () + set(variable_flag "-fconstexpr-depth=") +endif () + +target_compile_options(export_usage + PRIVATE + "${variable_flag}100" + "$<BUILD_INTERFACE:${variable_flag}200>" + "$<INSTALL_INTERFACE:${variable_flag}300>" + "$<BUILD_LOCAL_INTERFACE:${variable_flag}400>") + +add_library(export_used INTERFACE) +add_library(export_build INTERFACE) +add_library(export_install INTERFACE) +add_library(export_never INTERFACE) + +target_link_libraries(export_usage + PRIVATE + "export_used" + "$<BUILD_INTERFACE:export_build>" + "$<INSTALL_INTERFACE:export_install>" + "$<BUILD_LOCAL_INTERFACE:export_never>") + +install(TARGETS export_usage + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu") +install(EXPORT CXXModules + NAMESPACE CXXModules:: + DESTINATION "lib/cmake/export_usage" + FILE "export_usage-targets.cmake") +install(TARGETS export_used export_build export_install + EXPORT CXXModulesDeps) +install(EXPORT CXXModulesDeps + NAMESPACE CXXModules:: + DESTINATION "lib/cmake/export_usage" + FILE "export_usage-dep-targets.cmake") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_usage-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_usage-dep-targets.cmake\") +include(\"\${CMAKE_CURRENT_LIST_DIR}/export_usage-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/export_usage-config.cmake" + DESTINATION "lib/cmake/export_usage") + +set(generator + -G "${CMAKE_GENERATOR}") +if (CMAKE_GENERATOR_TOOLSET) + list(APPEND generator + -T "${CMAKE_GENERATOR_TOOLSET}") +endif () +if (CMAKE_GENERATOR_PLATFORM) + list(APPEND generator + -A "${CMAKE_GENERATOR_PLATFORM}") +endif () + +add_test(NAME export_usage_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/miu" + "-Dexport_interfaces_flag=${variable_flag}" + "-Dexport_usage_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/export_usage" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test") diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-install/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-usage-install/forward.cxx new file mode 100644 index 0000000..7f53271 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-usage-install/forward.cxx @@ -0,0 +1,6 @@ +import priv; + +int forwarding() +{ + return from_private(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-install/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-usage-install/importable.cxx new file mode 100644 index 0000000..8dfc41b --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-usage-install/importable.cxx @@ -0,0 +1,10 @@ +export module importable; + +extern "C++" { +int forwarding(); +} + +export int from_import() +{ + return forwarding(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-install/private.cxx b/Tests/RunCMake/CXXModules/examples/export-usage-install/private.cxx new file mode 100644 index 0000000..c5b719a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-usage-install/private.cxx @@ -0,0 +1,6 @@ +export module priv; + +export int from_private() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-usage-install/test/CMakeLists.txt new file mode 100644 index 0000000..9ccd63a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-usage-install/test/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_library NONE) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") + +find_package(export_usage REQUIRED) + +if (NOT TARGET CXXModules::export_usage) + message(FATAL_ERROR + "Missing imported target") +endif () + +if (NOT TARGET CXXModules::export_used) + message(FATAL_ERROR + "Missing imported target") +endif () + +if (NOT TARGET CXXModules::export_build) + message(FATAL_ERROR + "Missing imported target") +endif () + +if (NOT TARGET CXXModules::export_install) + message(FATAL_ERROR + "Missing imported target") +endif () + +if (TARGET CXXModules::export_never) + message(FATAL_ERROR + "Extra imported target") +endif () + +function (check_property expected property) + get_property(actual TARGET CXXModules::export_usage + PROPERTY "${property}") + if (NOT actual STREQUAL expected) + message(SEND_ERROR + "Mismatch for ${property}:\n expected: ${expected}\n actual : ${actual}") + endif () +endfunction () + +check_property("/usr/exported;/usr/installiface" "IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES") +check_property("exported;installiface" "IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS") +check_property("cxx_std_20;cxx_std_11;cxx_std_17" "IMPORTED_CXX_MODULES_COMPILE_FEATURES") +check_property("${export_interfaces_flag}100;${export_interfaces_flag}300" "IMPORTED_CXX_MODULES_COMPILE_OPTIONS") +check_property("$<COMPILE_ONLY:CXXModules::export_used>;$<COMPILE_ONLY:CXXModules::export_install>" "IMPORTED_CXX_MODULES_LINK_LIBRARIES") + +# Extract the export-dependent targets from the export file. +file(STRINGS "${export_usage_DIR}/export_usage-targets.cmake" usage_dependent_targets + REGEX "foreach._target ") +# Rudimentary argument splitting. +string(REPLACE " " ";" usage_dependent_targets "${usage_dependent_targets}") +# Keep only "target" names. +list(FILTER usage_dependent_targets INCLUDE REGEX "CXXModules::") +# Strip quotes. +string(REPLACE "\"" "" usage_dependent_targets "${usage_dependent_targets}") + +if (NOT "CXXModules::export_used" IN_LIST usage_dependent_targets) + message(SEND_ERROR + "The main export does not require the 'CXXModules::export_used' target") +endif () +if ("CXXModules::export_build" IN_LIST usage_dependent_targets) + message(SEND_ERROR + "The main export requires the 'CXXModules::export_build' target") +endif () +if (NOT "CXXModules::export_install" IN_LIST usage_dependent_targets) + message(SEND_ERROR + "The main export does not require the 'CXXModules::export_install' target") +endif () diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-build-stderr.txt new file mode 100644 index 0000000..71ee795 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-build-stderr.txt @@ -0,0 +1,7 @@ +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-build/export_bmi_and_interfaces-targets.cmake:[0-9]* \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + .*/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-build/export_bmi_and_interfaces-config.cmake:1 \(include\) + CMakeLists.txt:15 \(find_package\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-install-stderr.txt new file mode 100644 index 0000000..d22b2a1 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-install-stderr.txt @@ -0,0 +1,7 @@ +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-install/lib/cmake/export_bmi_and_interfaces/export_bmi_and_interfaces-targets.cmake:[0-9]* \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + .*/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-install/lib/cmake/export_bmi_and_interfaces/export_bmi_and_interfaces-config.cmake:1 \(include\) + CMakeLists.txt:15 \(find_package\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-build-stderr.txt new file mode 100644 index 0000000..f79abbc --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-build-stderr.txt @@ -0,0 +1,7 @@ +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-interface-build-build/export_interfaces-targets.cmake:[0-9]* \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + .*/Tests/RunCMake/CXXModules/examples/export-interface-build-build/export_interfaces-config.cmake:1 \(include\) + CMakeLists.txt:15 \(find_package\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-install-stderr.txt new file mode 100644 index 0000000..32f9452 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-install-stderr.txt @@ -0,0 +1,7 @@ +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-interface-install-install/lib/cmake/export_interfaces/export_interfaces-targets.cmake:[0-9]* \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + .*/Tests/RunCMake/CXXModules/examples/export-interface-install-install/lib/cmake/export_interfaces/export_interfaces-config.cmake:1 \(include\) + CMakeLists.txt:15 \(find_package\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-build-stderr.txt new file mode 100644 index 0000000..9254936 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-build-stderr.txt @@ -0,0 +1,7 @@ +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build-build/export_interfaces_no_properties-targets.cmake:[0-9]* \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + .*/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build-build/export_interfaces_no_properties-config.cmake:1 \(include\) + CMakeLists.txt:15 \(find_package\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-install-stderr.txt new file mode 100644 index 0000000..71269f4 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-install-stderr.txt @@ -0,0 +1,7 @@ +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install-install/lib/cmake/export_interfaces_no_properties/export_interfaces_no_properties-targets.cmake:[0-9]* \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + .*/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install-install/lib/cmake/export_interfaces_no_properties/export_interfaces_no_properties-config.cmake:1 \(include\) + CMakeLists.txt:15 \(find_package\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt new file mode 100644 index 0000000..3e6f379 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_import_interfaces CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +if (NO_PROPERTIES) + set(package_name "export_interfaces_no_properties") +elseif (WITH_BMIS) + set(package_name "export_bmi_and_interfaces") +else () + set(package_name "export_interfaces") +endif () +set(target_name "CXXModules::${package_name}") + +find_package("${package_name}" REQUIRED) + +add_executable(use_import_interfaces) +target_sources(use_import_interfaces + PRIVATE + use.cxx) +target_compile_features(use_import_interfaces PRIVATE cxx_std_20) +target_link_libraries(use_import_interfaces PRIVATE "${target_name}") + +add_test(NAME use_import_interfaces COMMAND use_import_interfaces) diff --git a/Tests/RunCMake/CXXModules/examples/import-modules/use.cxx b/Tests/RunCMake/CXXModules/examples/import-modules/use.cxx new file mode 100644 index 0000000..feb38d2 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules/use.cxx @@ -0,0 +1,6 @@ +import importable; + +int main(int argc, char* argv[]) +{ + return from_import(); +} diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake index 0826686..9a8429d 100644 --- a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake +++ b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake @@ -1,6 +1,6 @@ enable_language(C) -set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "a816ed09-43d1-40e5-bc8c-1a2824ee194e") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") add_library(lib1 STATIC empty.c) target_sources(lib1 PRIVATE FILE_SET UNKNOWN) diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake index 7935178..f63308c 100644 --- a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake +++ b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake @@ -1,6 +1,6 @@ enable_language(C) -set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "a816ed09-43d1-40e5-bc8c-1a2824ee194e") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") add_library(lib1 STATIC empty.c) target_sources(lib1 PRIVATE FILE_SET a TYPE UNKNOWN) @@ -333,6 +333,7 @@ CMAKE_CXX_SOURCES="\ cmCustomCommandGenerator \ cmCustomCommandLines \ cmCxxModuleMapper \ + cmCxxModuleUsageEffects \ cmDefinePropertyCommand \ cmDefinitions \ cmDocumentationFormatter \ @@ -392,6 +393,7 @@ CMAKE_CXX_SOURCES="\ cmGlobVerificationManager \ cmHexFileConverter \ cmIfCommand \ + cmImportedCxxModuleInfo \ cmIncludeCommand \ cmIncludeGuardCommand \ cmIncludeDirectoryCommand \ |