mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-18 08:51:52 +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:
@@ -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
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@@ -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:") ||
|
||||||
|
@@ -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;
|
||||||
};
|
};
|
||||||
|
@@ -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();
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
|
@@ -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;
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
|
@@ -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\)
|
||||||
|
Reference in New Issue
Block a user