Commit 3676fb49 authored by Stephen Kelly's avatar Stephen Kelly
Browse files

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.
parent e6971df6
......@@ -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
......
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.
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);
}
//----------------------------------------------------------------------------
......@@ -543,43 +547,157 @@ bool cmTarget::IsBundleOnApple() const
}
//----------------------------------------------------------------------------
void cmTarget::GetSourceFiles(std::vector<std::string> &files,
const std::string& config) const
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)
{
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);
cmMakefile *mf = tgt->GetMakefile();
for(std::vector<std::string>::const_iterator i = srcs.begin();
i != srcs.end(); ++i)
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())
{
std::string src = *i;
cmSourceFile* sf = this->Makefile->GetOrCreateSource(src);
std::string e;
src = sf->GetFullPath(&e);
if(src.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)
{
if(!e.empty())
std::string& src = *i;
cmSourceFile* sf = mf->GetOrCreateSource(src);
std::string e;
src = sf->GetFullPath(&e);
if(src.empty())
{
cmake* cm = this->Makefile->GetCMakeInstance();
cm->IssueMessage(cmake::FATAL_ERROR, e,
this->GetBacktrace());
if(!e.empty())
{
cmake* cm = mf->GetCMakeInstance();
cm->IssueMessage(cmake::FATAL_ERROR, e,
tgt->GetBacktrace());
}
return;
}
return;
}
files.push_back(src);
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,
cmTarget const* head) const
{
assert(this->GetType() != INTERFACE_LIBRARY);
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)
{
if (!cmGeneratorExpression::IsValidTargetName(it->Value)
&& cmGeneratorExpression::Find(it->Value) == std::string::npos)
{
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;
}
}
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;
}
}
//----------------------------------------------------------------------------
bool
cmTarget::GetConfigCommonSourceFiles(std::vector<cmSourceFile*>& files) const
......@@ -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)
int iface_src();
int iface_debug();
#include "iface_debug.h"
int iface_debug()
{
return 0;
}
int iface_src()
{
return 0;
}
#include "iface_debug.h"
int main(int argc, char** argv)
{
return 0;
return iface_src() + iface_debug();
}
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)
int iface()
{
return 0;
}
#include "iface.h"
int main(int argc, char** argv)
{
return iface();
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment