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:
parent
e6971df6ab
commit
3676fb4963
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
int iface_src();
|
||||
|
||||
int iface_debug();
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
#include "iface_debug.h"
|
||||
|
||||
int iface_debug()
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
int iface_src()
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
|
||||
#include "iface_debug.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
return 0;
|
||||
return iface_src() + iface_debug();
|
||||
}
|
||||
|
|
|
@ -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)
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
int iface()
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
int iface();
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
#include "iface.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
return iface();
|
||||
}
|
Loading…
Reference in New Issue