summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Boeckel <ben.boeckel@kitware.com>2023-01-14 23:58:28 -0500
committerRobert Maynard <rmaynard@nvidia.com>2023-04-12 10:43:48 -0400
commit0fb923c46041d67110c8e0907afdf66b3b25f25a (patch)
tree471126bf0dcb9ef4481716d38524b57a01ca2973
parent6c11f7e4a8805189835c04a398864f96081b3183 (diff)
downloadcmake-0fb923c46041d67110c8e0907afdf66b3b25f25a.tar.gz
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
-rw-r--r--Help/manual/cmake-generator-expressions.7.rst19
-rw-r--r--Help/release/dev/genex-compile-only.rst5
-rw-r--r--Source/cmExportFileGenerator.cxx16
-rw-r--r--Source/cmGeneratorExpressionNode.cxx25
-rw-r--r--Source/cmTargetLinkLibrariesCommand.cxx1
-rw-r--r--Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-result.txt1
-rw-r--r--Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-stderr.txt8
-rw-r--r--Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling.cmake1
-rw-r--r--Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake1
9 files changed, 76 insertions, 1 deletions
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index 9da37999b1..186a9d25f9 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/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
diff --git a/Help/release/dev/genex-compile-only.rst b/Help/release/dev/genex-compile-only.rst
new file mode 100644
index 0000000000..1f898d8620
--- /dev/null
+++ b/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.
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index 6e7ef4e735..e997158c3e 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/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()) {
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index a47366b4a1..a221498fda 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/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 },
diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx
index 0b123b2d61..03d7c9fea9 100644
--- a/Source/cmTargetLinkLibrariesCommand.cxx
+++ b/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) ||
diff --git a/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-result.txt b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-stderr.txt b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-stderr.txt
new file mode 100644
index 0000000000..5e7fce2a1b
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling.cmake b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling.cmake
new file mode 100644
index 0000000000..1bc75f98e5
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling.cmake
@@ -0,0 +1 @@
+add_custom_target(Custom ALL COMMAND ${CMAKE_COMMAND} -E echo $<COMPILE_ONLY:something>)
diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
index b139210d72..3fd994721b 100644
--- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
+++ b/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)