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_LINK_LIBRARIES
/prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE
/prop_tgt/INTERFACE_SOURCES
/prop_tgt/INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
/prop_tgt/INTERPROCEDURAL_OPTIMIZATION_CONFIG
/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;
}
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 {
#define DEFINE_ENUM_ENTRY(NAME) NAME,
CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(DEFINE_ENUM_ENTRY)

View File

@ -25,7 +25,8 @@
SELECT(F, EvaluatingSystemIncludeDirectories, SYSTEM_INCLUDE_DIRECTORIES) \
SELECT(F, EvaluatingCompileDefinitions, COMPILE_DEFINITIONS) \
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) \
CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH)
@ -70,6 +71,8 @@ struct cmGeneratorExpressionDAGChecker
void SetTransitivePropertiesOnly()
{ this->TransitivePropertiesOnly = true; }
std::string TopTarget() const;
private:
Result CheckGraph() const;

View File

@ -985,7 +985,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
if (propertyName == "LINKER_LANGUAGE")
{
if (target->LinkLanguagePropagatesToDependents() &&
dagCheckerParent && dagCheckerParent->EvaluatingLinkLibraries())
dagCheckerParent && (dagCheckerParent->EvaluatingLinkLibraries()
|| dagCheckerParent->EvaluatingSources()))
{
reportError(context, content->GetOriginalExpression(),
"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.");
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(),
"Expressions which require the linker language may not "

View File

@ -159,10 +159,13 @@ public:
CachedLinkInterfaceCompileOptionsEntries;
mutable std::map<std::string, std::vector<TargetPropertyEntry*> >
CachedLinkInterfaceCompileDefinitionsEntries;
mutable std::map<std::string, std::vector<TargetPropertyEntry*> >
CachedLinkInterfaceSourcesEntries;
mutable std::map<std::string, bool> CacheLinkInterfaceIncludeDirectoriesDone;
mutable std::map<std::string, bool> CacheLinkInterfaceCompileDefinitionsDone;
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->CachedLinkInterfaceCompileOptionsEntries);
deleteAndClear(this->CachedLinkInterfaceCompileDefinitionsEntries);
deleteAndClear(this->CachedLinkInterfaceSourcesEntries);
}
//----------------------------------------------------------------------------
@ -542,41 +546,155 @@ bool cmTarget::IsBundleOnApple() const
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,
const std::string& config) const
const std::string& config,
cmTarget const* head) const
{
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;
cmSourceFile* sf = this->Makefile->GetOrCreateSource(src);
std::string e;
src = sf->GetFullPath(&e);
if(src.empty())
if (!cmGeneratorExpression::IsValidTargetName(it->Value)
&& cmGeneratorExpression::Find(it->Value) == std::string::npos)
{
if(!e.empty())
{
cmake* cm = this->Makefile->GetCMakeInstance();
cm->IssueMessage(cmake::FATAL_ERROR, e,
this->GetBacktrace());
}
return;
continue;
}
{
cmGeneratorExpression ge(lfbt);
cmsys::auto_ptr<cmCompiledGeneratorExpression> cge =
ge.Parse(it->Value);
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,
const std::string& config) const
const std::string& config,
cmTarget const* head) const
{
std::vector<std::string> srcs;
this->GetSourceFiles(srcs, config);
this->GetSourceFiles(srcs, config, head);
std::set<cmSourceFile*> emitted;
@ -5053,10 +5172,11 @@ bool cmTarget::IsLinkInterfaceDependentNumberMaxProperty(const std::string &p,
//----------------------------------------------------------------------------
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;
this->GetSourceFiles(sourceFiles, config);
this->GetSourceFiles(sourceFiles, config, head);
for(std::vector<cmSourceFile*>::const_iterator
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
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.
LinkImplementation impl;
this->ComputeLinkImplementation(config, impl, head);
this->ComputeLinkImplementationLanguages(config, impl);
this->ComputeLinkImplementationLanguages(config, impl, head);
// Store the information for this configuration.
cmTargetInternals::LinkImplMapType::value_type entry(key, impl);
@ -6058,7 +6178,7 @@ cmTarget::GetLinkImplementation(const std::string& config,
}
else if (i->second.Languages.empty())
{
this->ComputeLinkImplementationLanguages(config, i->second);
this->ComputeLinkImplementationLanguages(config, i->second, head);
}
return &i->second;
@ -6172,12 +6292,13 @@ void cmTarget::ComputeLinkImplementation(const std::string& config,
//----------------------------------------------------------------------------
void
cmTarget::ComputeLinkImplementationLanguages(const std::string& config,
LinkImplementation& impl) const
LinkImplementation& impl,
cmTarget const* head) const
{
// This target needs runtime libraries for its source languages.
std::set<std::string> languages;
// 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.
for(std::set<std::string>::iterator li = languages.begin();
li != languages.end(); ++li)

View File

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

View File

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

View File

@ -3,7 +3,15 @@ cmake_minimum_required(VERSION 3.0)
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
$<$<CONFIG:Debug>:main.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)
{
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();
}