Преглед на файлове

cmGeneratorExpressionNode: implement `COMPILE_ONLY` genex

This generator expression is the inverse of `LINK_ONLY` and only coveys
usage requirements for the purposes of compilation. Its intended use is
to avoid needing to export targets that do not have link usage
requirements (e.g., header-only libraries) when used by another target.

It will also be used to represent private usage requirements on exported
C++ module-containing targets in the future.

Eventually there should be logic to collapse nesting of
`$<COMPILE_ONLY>` and `$<LINK_ONLY>` when generating instances of
either. A TODO is left in the code for this case.

See: #15415
Ben Boeckel преди 2 години
родител
ревизия
0fb923c460

+ 18 - 1
Help/manual/cmake-generator-expressions.7.rst

@@ -959,6 +959,22 @@ Compile Features
   :manual:`cmake-compile-features(7)` manual for information on
   compile features and a list of supported compilers.
 
+Compile Context
+^^^^^^^^^^^^^^^
+
+.. genex:: $<COMPILE_ONLY:...>
+
+  .. versionadded:: 3.27
+
+  Content of ``...``, except while collecting :ref:`Target Usage Requirements`,
+  in which case it is the empty string.  This is intended for use in an
+  :prop_tgt:`INTERFACE_LINK_LIBRARIES` target property, typically populated
+  via the :command:`target_link_libraries` command, to specify private
+  compilation requirements without other usage requirements.
+
+  Use cases include header-only usage where all usages are known to not have
+  linking requirements (e.g., all-``inline`` or C++ template libraries).
+
 Linker Language And ID
 ^^^^^^^^^^^^^^^^^^^^^^
 
@@ -1339,7 +1355,8 @@ Link Context
   in which case it is the empty string.  This is intended for use in an
   :prop_tgt:`INTERFACE_LINK_LIBRARIES` target property, typically populated
   via the :command:`target_link_libraries` command, to specify private link
-  dependencies without other usage requirements.
+  dependencies without other usage requirements such as include directories or
+  compile options.
 
   .. versionadded:: 3.24
     ``LINK_ONLY`` may also be used in a :prop_tgt:`LINK_LIBRARIES` target

+ 5 - 0
Help/release/dev/genex-compile-only.rst

@@ -0,0 +1,5 @@
+genex-compile-only
+------------------
+
+* The :genex:`COMPILE_ONLY` generator expression has been added which provides
+  compilation usage requirements without any linking requirements.

+ 16 - 0
Source/cmExportFileGenerator.cxx

@@ -734,6 +734,22 @@ void cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
     lastPos = nameStartPos + libName.size() + 1;
   }
 
+  while (errorString.empty() &&
+         (pos = input.find("$<COMPILE_ONLY:", lastPos)) != std::string::npos) {
+    std::string::size_type nameStartPos = pos + cmStrLen("$<COMPILE_ONLY:");
+    std::string::size_type endPos = input.find('>', nameStartPos);
+    if (endPos == std::string::npos) {
+      errorString = "$<COMPILE_ONLY:...> expression incomplete";
+      break;
+    }
+    std::string libName = input.substr(nameStartPos, endPos - nameStartPos);
+    if (cmGeneratorExpression::IsValidTargetName(libName) &&
+        this->AddTargetNamespace(libName, target, lg)) {
+      input.replace(nameStartPos, endPos - nameStartPos, libName);
+    }
+    lastPos = nameStartPos + libName.size() + 1;
+  }
+
   this->ReplaceInstallPrefix(input);
 
   if (!errorString.empty()) {

+ 25 - 0
Source/cmGeneratorExpressionNode.cxx

@@ -1351,6 +1351,29 @@ static const VersionNode<cmSystemTools::OP_LESS> versionLessNode;
 static const VersionNode<cmSystemTools::OP_LESS_EQUAL> versionLessEqNode;
 static const VersionNode<cmSystemTools::OP_EQUAL> versionEqualNode;
 
+static const struct CompileOnlyNode : public cmGeneratorExpressionNode
+{
+  CompileOnlyNode() {} // NOLINT(modernize-use-equals-default)
+
+  std::string Evaluate(
+    const std::vector<std::string>& parameters,
+    cmGeneratorExpressionContext* context,
+    const GeneratorExpressionContent* content,
+    cmGeneratorExpressionDAGChecker* dagChecker) const override
+  {
+    if (!dagChecker) {
+      reportError(context, content->GetOriginalExpression(),
+                  "$<COMPILE_ONLY:...> may only be used via linking");
+      return std::string();
+    }
+    // Linking checks for the inverse, so compiling is the opposite.
+    if (dagChecker->GetTransitivePropertiesOnly()) {
+      return parameters.front();
+    }
+    return std::string();
+  }
+} compileOnlyNode;
+
 static const struct LinkOnlyNode : public cmGeneratorExpressionNode
 {
   LinkOnlyNode() {} // NOLINT(modernize-use-equals-default)
@@ -1366,6 +1389,7 @@ static const struct LinkOnlyNode : public cmGeneratorExpressionNode
                   "$<LINK_ONLY:...> may only be used for linking");
       return std::string();
     }
+    // Compile-only checks for the inverse, so linking is the opposite.
     if (!dagChecker->GetTransitivePropertiesOnly()) {
       return parameters.front();
     }
@@ -3805,6 +3829,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
     { "BUILD_LOCAL_INTERFACE", &buildLocalInterfaceNode },
     { "INSTALL_PREFIX", &installPrefixNode },
     { "JOIN", &joinNode },
+    { "COMPILE_ONLY", &compileOnlyNode },
     { "LINK_ONLY", &linkOnlyNode },
     { "COMPILE_LANG_AND_ID", &languageAndIdNode },
     { "COMPILE_LANGUAGE", &languageNode },

+ 1 - 0
Source/cmTargetLinkLibrariesCommand.cxx

@@ -552,6 +552,7 @@ bool TLL::HandleLibrary(ProcessingState currentProcessingState,
       currentProcessingState == ProcessingPlainPrivateInterface) {
     if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY ||
         this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
+      // TODO: Detect and no-op `$<COMPILE_ONLY>` genexes here.
       std::string configLib =
         this->Target->GetDebugGeneratorExpressions(lib, llt);
       if (cmGeneratorExpression::IsValidTargetName(lib) ||

+ 1 - 0
Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-result.txt

@@ -0,0 +1 @@
+1

+ 8 - 0
Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error at COMPILE_ONLY-not-compiling.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<COMPILE_ONLY:something>
+
+  \$<COMPILE_ONLY:...> may only be used via linking
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling.cmake

@@ -0,0 +1 @@
+add_custom_target(Custom ALL COMMAND ${CMAKE_COMMAND} -E echo $<COMPILE_ONLY:something>)

+ 1 - 0
Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake

@@ -22,6 +22,7 @@ run_cmake(NonValidTarget-CXX_COMPILER_VERSION)
 run_cmake(NonValidTarget-Fortran_COMPILER_VERSION)
 run_cmake(NonValidTarget-TARGET_PROPERTY)
 run_cmake(NonValidTarget-TARGET_POLICY)
+run_cmake(COMPILE_ONLY-not-compiling)
 run_cmake(LINK_ONLY-not-linking)
 run_cmake(TARGET_EXISTS-no-arg)
 run_cmake(TARGET_EXISTS-empty-arg)