cmTarget: Allow transitive evaluation of SOURCES property.

Extend the cmGeneratorExpressionDAGChecker with an interface
returning the name of the top target.  Use that to determine
when there is a DAG violation, as required by the RunCMake.Languages
tests.
This commit is contained in:
Stephen Kelly 2014-02-13 20:52:21 +01:00
parent e6971df6ab
commit 3676fb4963
18 changed files with 258 additions and 43 deletions

View File

@ -152,6 +152,7 @@ Properties on Targets
/prop_tgt/INTERFACE_INCLUDE_DIRECTORIES /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES
/prop_tgt/INTERFACE_LINK_LIBRARIES /prop_tgt/INTERFACE_LINK_LIBRARIES
/prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE /prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE
/prop_tgt/INTERFACE_SOURCES
/prop_tgt/INTERFACE_SYSTEM_INCLUDE_DIRECTORIES /prop_tgt/INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
/prop_tgt/INTERPROCEDURAL_OPTIMIZATION_CONFIG /prop_tgt/INTERPROCEDURAL_OPTIMIZATION_CONFIG
/prop_tgt/INTERPROCEDURAL_OPTIMIZATION /prop_tgt/INTERPROCEDURAL_OPTIMIZATION

View File

@ -0,0 +1,15 @@
INTERFACE_SOURCES
-----------------
List of interface sources to pass to the compiler.
Targets may populate this property to publish the sources
for consuming targets to compile. Consuming
targets can add entries to their own :prop_tgt:`SOURCES` property
such as ``$<TARGET_PROPERTY:foo,INTERFACE_SOURCES>`` to use the
sources specified in the interface of ``foo``.
Contents of ``INTERFACE_SOURCES`` may use "generator expressions"
with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)`
manual for available expressions. See the :manual:`cmake-buildsystem(7)`
manual for more on defining buildsystem properties.

View File

@ -0,0 +1,5 @@
target-INTERFACE_SOURCES
------------------------
* A new :prop_tgt:`INTERFACE_SOURCES` target property was introduced. This is
consumed by dependent targets, which compile and link the listed sources.

View File

@ -179,6 +179,18 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(const char *tgt)
|| strcmp(prop, "INTERFACE_LINK_LIBRARIES") == 0; || strcmp(prop, "INTERFACE_LINK_LIBRARIES") == 0;
} }
std::string cmGeneratorExpressionDAGChecker::TopTarget() const
{
const cmGeneratorExpressionDAGChecker *top = this;
const cmGeneratorExpressionDAGChecker *parent = this->Parent;
while (parent)
{
top = parent;
parent = parent->Parent;
}
return top->Target;
}
enum TransitiveProperty { enum TransitiveProperty {
#define DEFINE_ENUM_ENTRY(NAME) NAME, #define DEFINE_ENUM_ENTRY(NAME) NAME,
CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(DEFINE_ENUM_ENTRY) CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(DEFINE_ENUM_ENTRY)

View File

@ -25,7 +25,8 @@
SELECT(F, EvaluatingSystemIncludeDirectories, SYSTEM_INCLUDE_DIRECTORIES) \ SELECT(F, EvaluatingSystemIncludeDirectories, SYSTEM_INCLUDE_DIRECTORIES) \
SELECT(F, EvaluatingCompileDefinitions, COMPILE_DEFINITIONS) \ SELECT(F, EvaluatingCompileDefinitions, COMPILE_DEFINITIONS) \
SELECT(F, EvaluatingCompileOptions, COMPILE_OPTIONS) \ SELECT(F, EvaluatingCompileOptions, COMPILE_OPTIONS) \
SELECT(F, EvaluatingAutoUicOptions, AUTOUIC_OPTIONS) SELECT(F, EvaluatingAutoUicOptions, AUTOUIC_OPTIONS) \
SELECT(F, EvaluatingSources, SOURCES)
#define CM_FOR_EACH_TRANSITIVE_PROPERTY(F) \ #define CM_FOR_EACH_TRANSITIVE_PROPERTY(F) \
CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH) CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH)
@ -70,6 +71,8 @@ struct cmGeneratorExpressionDAGChecker
void SetTransitivePropertiesOnly() void SetTransitivePropertiesOnly()
{ this->TransitivePropertiesOnly = true; } { this->TransitivePropertiesOnly = true; }
std::string TopTarget() const;
private: private:
Result CheckGraph() const; Result CheckGraph() const;

View File

@ -985,7 +985,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
if (propertyName == "LINKER_LANGUAGE") if (propertyName == "LINKER_LANGUAGE")
{ {
if (target->LinkLanguagePropagatesToDependents() && if (target->LinkLanguagePropagatesToDependents() &&
dagCheckerParent && dagCheckerParent->EvaluatingLinkLibraries()) dagCheckerParent && (dagCheckerParent->EvaluatingLinkLibraries()
|| dagCheckerParent->EvaluatingSources()))
{ {
reportError(context, content->GetOriginalExpression(), reportError(context, content->GetOriginalExpression(),
"LINKER_LANGUAGE target property can not be used while evaluating " "LINKER_LANGUAGE target property can not be used while evaluating "
@ -1569,7 +1570,9 @@ struct TargetFilesystemArtifact : public cmGeneratorExpressionNode
"Target \"" + name + "\" is not an executable or library."); "Target \"" + name + "\" is not an executable or library.");
return std::string(); return std::string();
} }
if (dagChecker && dagChecker->EvaluatingLinkLibraries(name.c_str())) if (dagChecker && (dagChecker->EvaluatingLinkLibraries(name.c_str())
|| (dagChecker->EvaluatingSources()
&& name == dagChecker->TopTarget())))
{ {
::reportError(context, content->GetOriginalExpression(), ::reportError(context, content->GetOriginalExpression(),
"Expressions which require the linker language may not " "Expressions which require the linker language may not "

View File

@ -159,10 +159,13 @@ public:
CachedLinkInterfaceCompileOptionsEntries; CachedLinkInterfaceCompileOptionsEntries;
mutable std::map<std::string, std::vector<TargetPropertyEntry*> > mutable std::map<std::string, std::vector<TargetPropertyEntry*> >
CachedLinkInterfaceCompileDefinitionsEntries; CachedLinkInterfaceCompileDefinitionsEntries;
mutable std::map<std::string, std::vector<TargetPropertyEntry*> >
CachedLinkInterfaceSourcesEntries;
mutable std::map<std::string, bool> CacheLinkInterfaceIncludeDirectoriesDone; mutable std::map<std::string, bool> CacheLinkInterfaceIncludeDirectoriesDone;
mutable std::map<std::string, bool> CacheLinkInterfaceCompileDefinitionsDone; mutable std::map<std::string, bool> CacheLinkInterfaceCompileDefinitionsDone;
mutable std::map<std::string, bool> CacheLinkInterfaceCompileOptionsDone; mutable std::map<std::string, bool> CacheLinkInterfaceCompileOptionsDone;
mutable std::map<std::string, bool> CacheLinkInterfaceSourcesDone;
}; };
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -198,6 +201,7 @@ cmTargetInternals::~cmTargetInternals()
deleteAndClear(this->CachedLinkInterfaceIncludeDirectoriesEntries); deleteAndClear(this->CachedLinkInterfaceIncludeDirectoriesEntries);
deleteAndClear(this->CachedLinkInterfaceCompileOptionsEntries); deleteAndClear(this->CachedLinkInterfaceCompileOptionsEntries);
deleteAndClear(this->CachedLinkInterfaceCompileDefinitionsEntries); deleteAndClear(this->CachedLinkInterfaceCompileDefinitionsEntries);
deleteAndClear(this->CachedLinkInterfaceSourcesEntries);
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -542,41 +546,155 @@ bool cmTarget::IsBundleOnApple() const
this->IsCFBundleOnApple(); this->IsCFBundleOnApple();
} }
//----------------------------------------------------------------------------
static void processSources(cmTarget const* tgt,
const std::vector<cmTargetInternals::TargetPropertyEntry*> &entries,
std::vector<std::string> &srcs,
std::set<std::string> &uniqueSrcs,
cmGeneratorExpressionDAGChecker *dagChecker,
cmTarget const* head,
std::string const& config)
{
cmMakefile *mf = tgt->GetMakefile();
for (std::vector<cmTargetInternals::TargetPropertyEntry*>::const_iterator
it = entries.begin(), end = entries.end(); it != end; ++it)
{
bool cacheSources = false;
std::vector<std::string> entrySources = (*it)->CachedEntries;
if(entrySources.empty())
{
cmSystemTools::ExpandListArgument((*it)->ge->Evaluate(mf,
config,
false,
head ? head : tgt,
tgt,
dagChecker),
entrySources);
if (mf->IsGeneratingBuildSystem()
&& !(*it)->ge->GetHadContextSensitiveCondition())
{
cacheSources = true;
}
for(std::vector<std::string>::iterator i = entrySources.begin();
i != entrySources.end(); ++i)
{
std::string& src = *i;
cmSourceFile* sf = mf->GetOrCreateSource(src);
std::string e;
src = sf->GetFullPath(&e);
if(src.empty())
{
if(!e.empty())
{
cmake* cm = mf->GetCMakeInstance();
cm->IssueMessage(cmake::FATAL_ERROR, e,
tgt->GetBacktrace());
}
return;
}
}
if (cacheSources)
{
(*it)->CachedEntries = entrySources;
}
}
for(std::vector<std::string>::iterator
li = entrySources.begin(); li != entrySources.end(); ++li)
{
std::string src = *li;
if(uniqueSrcs.insert(src).second)
{
srcs.push_back(src);
}
}
}
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmTarget::GetSourceFiles(std::vector<std::string> &files, void cmTarget::GetSourceFiles(std::vector<std::string> &files,
const std::string& config) const const std::string& config,
cmTarget const* head) const
{ {
assert(this->GetType() != INTERFACE_LIBRARY); assert(this->GetType() != INTERFACE_LIBRARY);
for(std::vector<cmTargetInternals::TargetPropertyEntry*>::const_iterator
si = this->Internal->SourceEntries.begin();
si != this->Internal->SourceEntries.end(); ++si)
{
std::vector<std::string> srcs;
cmSystemTools::ExpandListArgument((*si)->ge->Evaluate(this->Makefile,
config,
false,
this),
srcs);
for(std::vector<std::string>::const_iterator i = srcs.begin();
i != srcs.end(); ++i) cmListFileBacktrace lfbt;
cmGeneratorExpressionDAGChecker dagChecker(lfbt,
this->GetName(),
"SOURCES", 0, 0);
std::set<std::string> uniqueSrcs;
processSources(this,
this->Internal->SourceEntries,
files,
uniqueSrcs,
&dagChecker,
head,
config);
if (!this->Internal->CacheLinkInterfaceSourcesDone[config])
{
for (std::vector<cmValueWithOrigin>::const_iterator
it = this->Internal->LinkImplementationPropertyEntries.begin(),
end = this->Internal->LinkImplementationPropertyEntries.end();
it != end; ++it)
{ {
std::string src = *i; if (!cmGeneratorExpression::IsValidTargetName(it->Value)
cmSourceFile* sf = this->Makefile->GetOrCreateSource(src); && cmGeneratorExpression::Find(it->Value) == std::string::npos)
std::string e;
src = sf->GetFullPath(&e);
if(src.empty())
{ {
if(!e.empty()) continue;
{ }
cmake* cm = this->Makefile->GetCMakeInstance(); {
cm->IssueMessage(cmake::FATAL_ERROR, e, cmGeneratorExpression ge(lfbt);
this->GetBacktrace()); cmsys::auto_ptr<cmCompiledGeneratorExpression> cge =
} ge.Parse(it->Value);
return; std::string targetResult = cge->Evaluate(this->Makefile, config,
false, this, 0, &dagChecker);
if (!this->Makefile->FindTargetToUse(targetResult))
{
continue;
} }
files.push_back(src);
} }
std::string sourceGenex = "$<TARGET_PROPERTY:" +
it->Value + ",INTERFACE_SOURCES>";
if (cmGeneratorExpression::Find(it->Value) != std::string::npos)
{
// Because it->Value is a generator expression, ensure that it
// evaluates to the non-empty string before being used in the
// TARGET_PROPERTY expression.
sourceGenex = "$<$<BOOL:" + it->Value + ">:" + sourceGenex + ">";
}
cmGeneratorExpression ge(it->Backtrace);
cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(
sourceGenex);
this->Internal
->CachedLinkInterfaceSourcesEntries[config].push_back(
new cmTargetInternals::TargetPropertyEntry(cge,
it->Value));
}
}
processSources(this,
this->Internal->CachedLinkInterfaceSourcesEntries[config],
files,
uniqueSrcs,
&dagChecker,
head,
config);
if (!this->Makefile->IsGeneratingBuildSystem())
{
deleteAndClear(this->Internal->CachedLinkInterfaceSourcesEntries);
}
else
{
this->Internal->CacheLinkInterfaceSourcesDone[config] = true;
} }
} }
@ -639,10 +757,11 @@ cmTarget::GetConfigCommonSourceFiles(std::vector<cmSourceFile*>& files) const
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmTarget::GetSourceFiles(std::vector<cmSourceFile*> &files, void cmTarget::GetSourceFiles(std::vector<cmSourceFile*> &files,
const std::string& config) const const std::string& config,
cmTarget const* head) const
{ {
std::vector<std::string> srcs; std::vector<std::string> srcs;
this->GetSourceFiles(srcs, config); this->GetSourceFiles(srcs, config, head);
std::set<cmSourceFile*> emitted; std::set<cmSourceFile*> emitted;
@ -5053,10 +5172,11 @@ bool cmTarget::IsLinkInterfaceDependentNumberMaxProperty(const std::string &p,
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmTarget::GetLanguages(std::set<std::string>& languages, void cmTarget::GetLanguages(std::set<std::string>& languages,
const std::string& config) const const std::string& config,
cmTarget const* head) const
{ {
std::vector<cmSourceFile*> sourceFiles; std::vector<cmSourceFile*> sourceFiles;
this->GetSourceFiles(sourceFiles, config); this->GetSourceFiles(sourceFiles, config, head);
for(std::vector<cmSourceFile*>::const_iterator for(std::vector<cmSourceFile*>::const_iterator
i = sourceFiles.begin(); i != sourceFiles.end(); ++i) i = sourceFiles.begin(); i != sourceFiles.end(); ++i)
{ {
@ -5111,7 +5231,7 @@ void cmTarget::GetLanguages(std::set<std::string>& languages,
for(std::vector<cmTarget*>::const_iterator for(std::vector<cmTarget*>::const_iterator
i = objectLibraries.begin(); i != objectLibraries.end(); ++i) i = objectLibraries.begin(); i != objectLibraries.end(); ++i)
{ {
(*i)->GetLanguages(languages, config); (*i)->GetLanguages(languages, config, head);
} }
} }
@ -6050,7 +6170,7 @@ cmTarget::GetLinkImplementation(const std::string& config,
// Compute the link implementation for this configuration. // Compute the link implementation for this configuration.
LinkImplementation impl; LinkImplementation impl;
this->ComputeLinkImplementation(config, impl, head); this->ComputeLinkImplementation(config, impl, head);
this->ComputeLinkImplementationLanguages(config, impl); this->ComputeLinkImplementationLanguages(config, impl, head);
// Store the information for this configuration. // Store the information for this configuration.
cmTargetInternals::LinkImplMapType::value_type entry(key, impl); cmTargetInternals::LinkImplMapType::value_type entry(key, impl);
@ -6058,7 +6178,7 @@ cmTarget::GetLinkImplementation(const std::string& config,
} }
else if (i->second.Languages.empty()) else if (i->second.Languages.empty())
{ {
this->ComputeLinkImplementationLanguages(config, i->second); this->ComputeLinkImplementationLanguages(config, i->second, head);
} }
return &i->second; return &i->second;
@ -6172,12 +6292,13 @@ void cmTarget::ComputeLinkImplementation(const std::string& config,
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void void
cmTarget::ComputeLinkImplementationLanguages(const std::string& config, cmTarget::ComputeLinkImplementationLanguages(const std::string& config,
LinkImplementation& impl) const LinkImplementation& impl,
cmTarget const* head) const
{ {
// This target needs runtime libraries for its source languages. // This target needs runtime libraries for its source languages.
std::set<std::string> languages; std::set<std::string> languages;
// Get languages used in our source files. // Get languages used in our source files.
this->GetLanguages(languages, config); this->GetLanguages(languages, config, head);
// Copy the set of langauges to the link implementation. // Copy the set of langauges to the link implementation.
for(std::set<std::string>::iterator li = languages.begin(); for(std::set<std::string>::iterator li = languages.begin();
li != languages.end(); ++li) li != languages.end(); ++li)

View File

@ -136,9 +136,11 @@ public:
* Get the list of the source files used by this target * Get the list of the source files used by this target
*/ */
void GetSourceFiles(std::vector<std::string> &files, void GetSourceFiles(std::vector<std::string> &files,
const std::string& config) const; const std::string& config,
cmTarget const* head = 0) const;
void GetSourceFiles(std::vector<cmSourceFile*> &files, void GetSourceFiles(std::vector<cmSourceFile*> &files,
const std::string& config) const; const std::string& config,
cmTarget const* head = 0) const;
bool GetConfigCommonSourceFiles(std::vector<cmSourceFile*>& files) const; bool GetConfigCommonSourceFiles(std::vector<cmSourceFile*>& files) const;
/** /**
@ -469,7 +471,8 @@ public:
// information to forward these property changes to the targets // information to forward these property changes to the targets
// until we have per-target object file properties. // until we have per-target object file properties.
void GetLanguages(std::set<std::string>& languages, void GetLanguages(std::set<std::string>& languages,
const std::string& config) const; std::string const& config,
cmTarget const* head = 0) const;
/** Return whether this target is an executable with symbol exports /** Return whether this target is an executable with symbol exports
enabled. */ enabled. */
@ -743,7 +746,8 @@ private:
LinkImplementation& impl, LinkImplementation& impl,
cmTarget const* head) const; cmTarget const* head) const;
void ComputeLinkImplementationLanguages(const std::string& config, void ComputeLinkImplementationLanguages(const std::string& config,
LinkImplementation& impl) const; LinkImplementation& impl,
cmTarget const* head) const;
void ComputeLinkClosure(const std::string& config, LinkClosure& lc, void ComputeLinkClosure(const std::string& config, LinkClosure& lc,
cmTarget const* head) const; cmTarget const* head) const;

View File

@ -282,6 +282,7 @@ if(BUILD_TESTING)
set(ConfigSources_BUILD_OPTIONS -DCMAKE_BUILD_TYPE=Debug) set(ConfigSources_BUILD_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
ADD_TEST_MACRO(ConfigSources ConfigSources) ADD_TEST_MACRO(ConfigSources ConfigSources)
endif() endif()
ADD_TEST_MACRO(SourcesProperty SourcesProperty)
set_tests_properties(EmptyLibrary PROPERTIES set_tests_properties(EmptyLibrary PROPERTIES
PASS_REGULAR_EXPRESSION "CMake Error: CMake can not determine linker language for target: test") PASS_REGULAR_EXPRESSION "CMake Error: CMake can not determine linker language for target: test")
ADD_TEST_MACRO(CrossCompile CrossCompile) ADD_TEST_MACRO(CrossCompile CrossCompile)

View File

@ -3,7 +3,15 @@ cmake_minimum_required(VERSION 3.0)
project(ConfigSources) project(ConfigSources)
add_library(iface INTERFACE)
set_property(TARGET iface PROPERTY INTERFACE_SOURCES
iface_src.cpp
$<$<CONFIG:Debug>:iface_debug_src.cpp>
$<$<CONFIG:Release>:does_not_exist.cpp>
)
add_executable(ConfigSources add_executable(ConfigSources
$<$<CONFIG:Debug>:main.cpp> $<$<CONFIG:Debug>:main.cpp>
$<$<CONFIG:Release>:does_not_exist.cpp> $<$<CONFIG:Release>:does_not_exist.cpp>
) )
target_link_libraries(ConfigSources iface)

View File

@ -0,0 +1,4 @@
int iface_src();
int iface_debug();

View File

@ -0,0 +1,7 @@
#include "iface_debug.h"
int iface_debug()
{
return 0;
}

View File

@ -0,0 +1,5 @@
int iface_src()
{
return 0;
}

View File

@ -1,5 +1,7 @@
#include "iface_debug.h"
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
return 0; return iface_src() + iface_debug();
} }

View File

@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.0)
project(SourcesProperty)
add_library(iface INTERFACE)
set_property(TARGET iface PROPERTY INTERFACE_SOURCES iface.cpp)
add_executable(SourcesProperty main.cpp)
target_link_libraries(SourcesProperty iface)

View File

@ -0,0 +1,5 @@
int iface()
{
return 0;
}

View File

@ -0,0 +1,2 @@
int iface();

View File

@ -0,0 +1,7 @@
#include "iface.h"
int main(int argc, char** argv)
{
return iface();
}