1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-18 00:02:21 +08:00

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.

See: #15415
This commit is contained in:
Robert Maynard
2023-04-11 15:49:52 -04:00
parent 0fb923c460
commit c42630ee62
10 changed files with 65 additions and 12 deletions

View File

@@ -966,15 +966,18 @@ Compile Context
.. versionadded:: 3.27 .. versionadded:: 3.27
Content of ``...``, except while collecting :ref:`Target Usage Requirements`, Content of ``...``, when collecting :ref:`Target Usage Requirements`,
in which case it is the empty string. This is intended for use in an otherwise it is the empty string. This is intended for use in an
:prop_tgt:`INTERFACE_LINK_LIBRARIES` target property, typically populated :prop_tgt:`INTERFACE_LINK_LIBRARIES` and :prop_tgt:`LINK_LIBRARIES` target
via the :command:`target_link_libraries` command, to specify private properties, typically populated via the :command:`target_link_libraries` command.
compilation requirements without other usage requirements. Provides compilation usage requirements without any linking requirements.
Use cases include header-only usage where all usages are known to not have Use cases include header-only usage where all usages are known to not have
linking requirements (e.g., all-``inline`` or C++ template libraries). linking requirements (e.g., all-``inline`` or C++ template libraries).
Note that for proper evaluation of this expression requires policy :policy:`CMP0099`
to be set to `NEW`.
Linker Language And ID Linker Language And ID
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -27,6 +27,7 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
, Content(content) , Content(content)
, Backtrace(std::move(backtrace)) , Backtrace(std::move(backtrace))
, TransitivePropertiesOnly(false) , TransitivePropertiesOnly(false)
, CMP0131(false)
{ {
this->Initialize(); this->Initialize();
} }
@@ -41,6 +42,7 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
, Content(content) , Content(content)
, Backtrace() , Backtrace()
, TransitivePropertiesOnly(false) , TransitivePropertiesOnly(false)
, CMP0131(false)
{ {
this->Initialize(); this->Initialize();
} }
@@ -143,6 +145,12 @@ bool cmGeneratorExpressionDAGChecker::GetTransitivePropertiesOnly() const
return this->Top()->TransitivePropertiesOnly; return this->Top()->TransitivePropertiesOnly;
} }
bool cmGeneratorExpressionDAGChecker::GetTransitivePropertiesOnlyCMP0131()
const
{
return this->Top()->CMP0131;
}
bool cmGeneratorExpressionDAGChecker::EvaluatingGenexExpression() const bool cmGeneratorExpressionDAGChecker::EvaluatingGenexExpression() const
{ {
return cmHasLiteralPrefix(this->Property, "TARGET_GENEX_EVAL:") || return cmHasLiteralPrefix(this->Property, "TARGET_GENEX_EVAL:") ||

View File

@@ -90,6 +90,9 @@ struct cmGeneratorExpressionDAGChecker
bool GetTransitivePropertiesOnly() const; bool GetTransitivePropertiesOnly() const;
void SetTransitivePropertiesOnly() { this->TransitivePropertiesOnly = true; } void SetTransitivePropertiesOnly() { this->TransitivePropertiesOnly = true; }
bool GetTransitivePropertiesOnlyCMP0131() const;
void SetTransitivePropertiesOnlyCMP0131() { this->CMP0131 = true; }
cmGeneratorExpressionDAGChecker const* Top() const; cmGeneratorExpressionDAGChecker const* Top() const;
cmGeneratorTarget const* TopTarget() const; cmGeneratorTarget const* TopTarget() const;
@@ -105,4 +108,5 @@ private:
const cmListFileBacktrace Backtrace; const cmListFileBacktrace Backtrace;
Result CheckResult; Result CheckResult;
bool TransitivePropertiesOnly; bool TransitivePropertiesOnly;
bool CMP0131;
}; };

View File

@@ -1363,14 +1363,13 @@ static const struct CompileOnlyNode : public cmGeneratorExpressionNode
{ {
if (!dagChecker) { if (!dagChecker) {
reportError(context, content->GetOriginalExpression(), reportError(context, content->GetOriginalExpression(),
"$<COMPILE_ONLY:...> may only be used via linking"); "$<COMPILE_ONLY:...> may only be used for linking");
return std::string(); return std::string();
} }
// Linking checks for the inverse, so compiling is the opposite.
if (dagChecker->GetTransitivePropertiesOnly()) { if (dagChecker->GetTransitivePropertiesOnly()) {
return parameters.front(); return parameters.front();
} }
return std::string(); return std::string{};
} }
} compileOnlyNode; } compileOnlyNode;
@@ -1389,8 +1388,7 @@ static const struct LinkOnlyNode : public cmGeneratorExpressionNode
"$<LINK_ONLY:...> may only be used for linking"); "$<LINK_ONLY:...> may only be used for linking");
return std::string(); return std::string();
} }
// Compile-only checks for the inverse, so linking is the opposite. if (!dagChecker->GetTransitivePropertiesOnlyCMP0131()) {
if (!dagChecker->GetTransitivePropertiesOnly()) {
return parameters.front(); return parameters.front();
} }
return std::string(); return std::string();

View File

@@ -6791,6 +6791,7 @@ void cmGeneratorTarget::ExpandLinkItems(
// requirements. // requirements.
if (interfaceFor == LinkInterfaceFor::Usage) { if (interfaceFor == LinkInterfaceFor::Usage) {
dagChecker.SetTransitivePropertiesOnly(); dagChecker.SetTransitivePropertiesOnly();
dagChecker.SetTransitivePropertiesOnlyCMP0131();
} }
cmMakefile const* mf = this->LocalGenerator->GetMakefile(); cmMakefile const* mf = this->LocalGenerator->GetMakefile();
LookupLinkItemScope scope{ this->LocalGenerator }; LookupLinkItemScope scope{ this->LocalGenerator };
@@ -8229,6 +8230,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
// The $<LINK_ONLY> expression may be used to specify link dependencies // The $<LINK_ONLY> expression may be used to specify link dependencies
// that are otherwise excluded from usage requirements. // that are otherwise excluded from usage requirements.
if (implFor == LinkInterfaceFor::Usage) { if (implFor == LinkInterfaceFor::Usage) {
dagChecker.SetTransitivePropertiesOnly();
switch (this->GetPolicyStatusCMP0131()) { switch (this->GetPolicyStatusCMP0131()) {
case cmPolicies::WARN: case cmPolicies::WARN:
case cmPolicies::OLD: case cmPolicies::OLD:
@@ -8236,7 +8238,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS: case cmPolicies::REQUIRED_ALWAYS:
case cmPolicies::NEW: case cmPolicies::NEW:
dagChecker.SetTransitivePropertiesOnly(); dagChecker.SetTransitivePropertiesOnlyCMP0131();
break; break;
} }
} }

View File

@@ -155,3 +155,19 @@ target_link_libraries(TopDir SubDirC)
add_library(TopDirImported IMPORTED INTERFACE) add_library(TopDirImported IMPORTED INTERFACE)
target_compile_definitions(TopDirImported INTERFACE DEF_TopDirImported) target_compile_definitions(TopDirImported INTERFACE DEF_TopDirImported)
cmake_policy(POP) cmake_policy(POP)
#----------------------------------------------------------------------------
# Test $<COMPILE_ONLY:> genex.
cmake_policy(SET CMP0099 NEW)
add_library(dont_link_too SHARED compile_only.cpp)
target_compile_definitions(dont_link_too PUBLIC USE_EXAMPLE)
target_link_options(dont_link_too INTERFACE invalid_link_option)
target_link_libraries(dont_link_too INTERFACE invalid_link_library)
add_library(uses_compile_only_genex SHARED compile_only.cpp)
target_link_libraries(uses_compile_only_genex PUBLIC $<COMPILE_ONLY:dont_link_too>)
add_library(uses_compile_only_genex_static STATIC compile_only.cpp)
target_link_libraries(uses_compile_only_genex_static PRIVATE $<COMPILE_ONLY:dont_link_too>)
add_executable(uses_via_static_linking main.cxx)
target_link_libraries(uses_via_static_linking PRIVATE uses_compile_only_genex_static)

View File

@@ -0,0 +1,8 @@
#ifndef USE_EXAMPLE
# error "Missing propagated define"
#endif
// Solaris needs non-empty content so ensure
// we have at least one symbol
int Solaris_requires_a_symbol_here = 0;

View File

@@ -22,9 +22,18 @@ add_executable(testExe2 testExe2.c)
set_property(TARGET testExe2 PROPERTY ENABLE_EXPORTS 1) set_property(TARGET testExe2 PROPERTY ENABLE_EXPORTS 1)
set_property(TARGET testExe2 PROPERTY LINK_INTERFACE_LIBRARIES testExe2lib) set_property(TARGET testExe2 PROPERTY LINK_INTERFACE_LIBRARIES testExe2lib)
add_library(compileOnly INTERFACE)
target_compile_definitions(compileOnly INTERFACE FROM_compileOnly)
target_link_options(compileOnly INTERFACE -fthis-flag-does-not-exist)
add_library(testLib1 STATIC testLib1.c) add_library(testLib1 STATIC testLib1.c)
add_library(testLib2 STATIC testLib2.c) add_library(testLib2 STATIC testLib2.c)
target_link_libraries(testLib2 testLib1) target_link_libraries(testLib2 testLib1)
target_link_libraries(testLib2
PRIVATE
testLib1
"$<COMPILE_ONLY:compileOnly>")
# Test install(FILES) with generator expressions referencing testLib1. # Test install(FILES) with generator expressions referencing testLib1.
add_custom_command(TARGET testLib1 POST_BUILD add_custom_command(TARGET testLib1 POST_BUILD
@@ -556,6 +565,7 @@ install(FILES
# Install and export from install tree. # Install and export from install tree.
install( install(
TARGETS TARGETS
compileOnly
testExe1 testLib1 testLib2 testExe2 testLib3 testLib4 testExe3 testExe4 testExe1 testLib1 testLib2 testExe2 testLib3 testLib4 testExe3 testExe4
testExe2lib testLib4lib testLib4libdbg testLib4libopt testExe2lib testLib4lib testLib4libdbg testLib4libopt
testLib6 testLib7 testLib8 testLib6 testLib7 testLib8

View File

@@ -1,4 +1,8 @@
#ifndef FROM_compileOnly
# error "Usage requirements from `compileOnly` not found"
#endif
extern int testLib1(void); extern int testLib1(void);
int testLib2(void) int testLib2(void)

View File

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