瀏覽代碼

cmExportFileGenerator: export private compile info for C++ modules

When consuming exported targets which contain C++ modules, the consuming
project must be able to recompile BMI files using the original target's
flags. This is because a module source may use some private target usage
requirement but not want to propagate it to consumers. To facilitate
this, export the private information as necessary for consumers to be
able to perform the BMI compilations.
Ben Boeckel 2 年之前
父節點
當前提交
249cd3efad
共有 23 個文件被更改,包括 573 次插入0 次删除
  1. 5 0
      Help/manual/cmake-properties.7.rst
  2. 14 0
      Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS.rst
  3. 13 0
      Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_FEATURES.rst
  4. 13 0
      Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_OPTIONS.rst
  5. 14 0
      Help/prop_tgt/IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES.rst
  6. 11 0
      Help/prop_tgt/IMPORTED_CXX_MODULES_LINK_LIBRARIES.rst
  7. 9 0
      Source/cmExportBuildFileGenerator.cxx
  8. 72 0
      Source/cmExportFileGenerator.cxx
  9. 3 0
      Source/cmExportFileGenerator.h
  10. 7 0
      Source/cmExportInstallFileGenerator.cxx
  11. 2 0
      Tests/RunCMake/CXXModules/RunCMakeTest.cmake
  12. 4 0
      Tests/RunCMake/CXXModules/examples/export-usage-build-stderr.txt
  13. 110 0
      Tests/RunCMake/CXXModules/examples/export-usage-build/CMakeLists.txt
  14. 6 0
      Tests/RunCMake/CXXModules/examples/export-usage-build/forward.cxx
  15. 8 0
      Tests/RunCMake/CXXModules/examples/export-usage-build/importable.cxx
  16. 6 0
      Tests/RunCMake/CXXModules/examples/export-usage-build/private.cxx
  17. 69 0
      Tests/RunCMake/CXXModules/examples/export-usage-build/test/CMakeLists.txt
  18. 4 0
      Tests/RunCMake/CXXModules/examples/export-usage-install-stderr.txt
  19. 114 0
      Tests/RunCMake/CXXModules/examples/export-usage-install/CMakeLists.txt
  20. 6 0
      Tests/RunCMake/CXXModules/examples/export-usage-install/forward.cxx
  21. 8 0
      Tests/RunCMake/CXXModules/examples/export-usage-install/importable.cxx
  22. 6 0
      Tests/RunCMake/CXXModules/examples/export-usage-install/private.cxx
  23. 69 0
      Tests/RunCMake/CXXModules/examples/export-usage-install/test/CMakeLists.txt

+ 5 - 0
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

+ 14 - 0
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.

+ 13 - 0
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.

+ 13 - 0
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

+ 14 - 0
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.

+ 11 - 0
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.

+ 9 - 0
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,

+ 72 - 0
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)

+ 3 - 0
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);

+ 7 - 0
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;

+ 2 - 0
Tests/RunCMake/CXXModules/RunCMakeTest.cmake

@@ -187,6 +187,7 @@ 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)
 endif ()
 
@@ -201,6 +202,7 @@ 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)
   endif ()
 endif ()

+ 4 - 0
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.

+ 110 - 0
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")

+ 6 - 0
Tests/RunCMake/CXXModules/examples/export-usage-build/forward.cxx

@@ -0,0 +1,6 @@
+import priv;
+
+int forwarding()
+{
+  return from_private();
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/export-usage-build/importable.cxx

@@ -0,0 +1,8 @@
+export module importable;
+
+int forwarding();
+
+export int from_import()
+{
+  return forwarding();
+}

+ 6 - 0
Tests/RunCMake/CXXModules/examples/export-usage-build/private.cxx

@@ -0,0 +1,6 @@
+export module priv;
+
+export int from_private()
+{
+  return 0;
+}

+ 69 - 0
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 "a816ed09-43d1-40e5-bc8c-1a2824ee194e")
+
+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 ()

+ 4 - 0
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.

+ 114 - 0
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")

+ 6 - 0
Tests/RunCMake/CXXModules/examples/export-usage-install/forward.cxx

@@ -0,0 +1,6 @@
+import priv;
+
+int forwarding()
+{
+  return from_private();
+}

+ 8 - 0
Tests/RunCMake/CXXModules/examples/export-usage-install/importable.cxx

@@ -0,0 +1,8 @@
+export module importable;
+
+int forwarding();
+
+export int from_import()
+{
+  return forwarding();
+}

+ 6 - 0
Tests/RunCMake/CXXModules/examples/export-usage-install/private.cxx

@@ -0,0 +1,6 @@
+export module priv;
+
+export int from_private()
+{
+  return 0;
+}

+ 69 - 0
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 "a816ed09-43d1-40e5-bc8c-1a2824ee194e")
+
+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 ()