summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Help/manual/cmake-properties.7.rst1
-rw-r--r--Help/prop_tgt/INTERFACE_SOURCES.rst15
-rw-r--r--Help/release/dev/target-INTERFACE_SOURCES.rst5
-rw-r--r--Source/cmGeneratorExpressionDAGChecker.cxx12
-rw-r--r--Source/cmGeneratorExpressionDAGChecker.h5
-rw-r--r--Source/cmGeneratorExpressionEvaluator.cxx7
-rw-r--r--Source/cmTarget.cxx191
-rw-r--r--Source/cmTarget.h12
-rw-r--r--Tests/CMakeLists.txt1
-rw-r--r--Tests/ConfigSources/CMakeLists.txt8
-rw-r--r--Tests/ConfigSources/iface_debug.h4
-rw-r--r--Tests/ConfigSources/iface_debug_src.cpp7
-rw-r--r--Tests/ConfigSources/iface_src.cpp5
-rw-r--r--Tests/ConfigSources/main.cpp4
-rw-r--r--Tests/SourcesProperty/CMakeLists.txt10
-rw-r--r--Tests/SourcesProperty/iface.cpp5
-rw-r--r--Tests/SourcesProperty/iface.h2
-rw-r--r--Tests/SourcesProperty/main.cpp7
18 files changed, 258 insertions, 43 deletions
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 6ea5839c84..fd16eb95fd 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -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
diff --git a/Help/prop_tgt/INTERFACE_SOURCES.rst b/Help/prop_tgt/INTERFACE_SOURCES.rst
new file mode 100644
index 0000000000..fb28231a31
--- /dev/null
+++ b/Help/prop_tgt/INTERFACE_SOURCES.rst
@@ -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.
diff --git a/Help/release/dev/target-INTERFACE_SOURCES.rst b/Help/release/dev/target-INTERFACE_SOURCES.rst
new file mode 100644
index 0000000000..4e34943211
--- /dev/null
+++ b/Help/release/dev/target-INTERFACE_SOURCES.rst
@@ -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.
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx
index 07efba9bc9..7f8e6943a7 100644
--- a/Source/cmGeneratorExpressionDAGChecker.cxx
+++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -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)
diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h
index 6cbbd2a076..b3147f7e14 100644
--- a/Source/cmGeneratorExpressionDAGChecker.h
+++ b/Source/cmGeneratorExpressionDAGChecker.h
@@ -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;
diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx
index a3926750f9..59e3aec6ce 100644
--- a/Source/cmGeneratorExpressionEvaluator.cxx
+++ b/Source/cmGeneratorExpressionEvaluator.cxx
@@ -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 "
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index fbd7315865..0b5734de50 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -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,44 +547,158 @@ 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)
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 042b441d70..15c49ea024 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -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;
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index e77133a559..1c474ab136 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -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)
diff --git a/Tests/ConfigSources/CMakeLists.txt b/Tests/ConfigSources/CMakeLists.txt
index 68a4233dba..c272257ca3 100644
--- a/Tests/ConfigSources/CMakeLists.txt
+++ b/Tests/ConfigSources/CMakeLists.txt
@@ -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)
diff --git a/Tests/ConfigSources/iface_debug.h b/Tests/ConfigSources/iface_debug.h
new file mode 100644
index 0000000000..a23d7374b8
--- /dev/null
+++ b/Tests/ConfigSources/iface_debug.h
@@ -0,0 +1,4 @@
+
+int iface_src();
+
+int iface_debug();
diff --git a/Tests/ConfigSources/iface_debug_src.cpp b/Tests/ConfigSources/iface_debug_src.cpp
new file mode 100644
index 0000000000..63b22fcd14
--- /dev/null
+++ b/Tests/ConfigSources/iface_debug_src.cpp
@@ -0,0 +1,7 @@
+
+#include "iface_debug.h"
+
+int iface_debug()
+{
+ return 0;
+}
diff --git a/Tests/ConfigSources/iface_src.cpp b/Tests/ConfigSources/iface_src.cpp
new file mode 100644
index 0000000000..c3a0c8f1e5
--- /dev/null
+++ b/Tests/ConfigSources/iface_src.cpp
@@ -0,0 +1,5 @@
+
+int iface_src()
+{
+ return 0;
+}
diff --git a/Tests/ConfigSources/main.cpp b/Tests/ConfigSources/main.cpp
index 1c19e8dc31..71af72f700 100644
--- a/Tests/ConfigSources/main.cpp
+++ b/Tests/ConfigSources/main.cpp
@@ -1,5 +1,7 @@
+#include "iface_debug.h"
+
int main(int argc, char** argv)
{
- return 0;
+ return iface_src() + iface_debug();
}
diff --git a/Tests/SourcesProperty/CMakeLists.txt b/Tests/SourcesProperty/CMakeLists.txt
new file mode 100644
index 0000000000..0b3097e49f
--- /dev/null
+++ b/Tests/SourcesProperty/CMakeLists.txt
@@ -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)
diff --git a/Tests/SourcesProperty/iface.cpp b/Tests/SourcesProperty/iface.cpp
new file mode 100644
index 0000000000..e38ac3778b
--- /dev/null
+++ b/Tests/SourcesProperty/iface.cpp
@@ -0,0 +1,5 @@
+
+int iface()
+{
+ return 0;
+}
diff --git a/Tests/SourcesProperty/iface.h b/Tests/SourcesProperty/iface.h
new file mode 100644
index 0000000000..2cac248ef5
--- /dev/null
+++ b/Tests/SourcesProperty/iface.h
@@ -0,0 +1,2 @@
+
+int iface();
diff --git a/Tests/SourcesProperty/main.cpp b/Tests/SourcesProperty/main.cpp
new file mode 100644
index 0000000000..ae4f305676
--- /dev/null
+++ b/Tests/SourcesProperty/main.cpp
@@ -0,0 +1,7 @@
+
+#include "iface.h"
+
+int main(int argc, char** argv)
+{
+ return iface();
+}