Add COMPILE_OPTIONS target property.

This method reads generator expressions from the COMPILE_OPTIONS
target property, as well as INTERFACE_COMPILE_OPTIONS from linked
dependents.
This commit is contained in:
Stephen Kelly 2013-05-16 15:15:28 +02:00
parent 7cb23084b2
commit 80ca9c4b41
10 changed files with 286 additions and 1 deletions

View File

@ -168,3 +168,11 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingCompileDefinitions() const
|| strcmp(prop, "INTERFACE_COMPILE_DEFINITIONS") == 0
|| strncmp(prop, "COMPILE_DEFINITIONS_", 20) == 0);
}
//----------------------------------------------------------------------------
bool cmGeneratorExpressionDAGChecker::EvaluatingCompileOptions() const
{
const char *prop = this->Property.c_str();
return (strcmp(prop, "COMPILE_OPTIONS") == 0
|| strcmp(prop, "INTERFACE_COMPILE_OPTIONS") == 0 );
}

View File

@ -40,6 +40,7 @@ struct cmGeneratorExpressionDAGChecker
bool EvaluatingLinkLibraries();
bool EvaluatingIncludeDirectories() const;
bool EvaluatingCompileDefinitions() const;
bool EvaluatingCompileOptions() const;
private:
Result checkGraph() const;

View File

@ -495,6 +495,7 @@ static const struct JoinNode : public cmGeneratorExpressionNode
static const char* targetPropertyTransitiveWhitelist[] = {
"INTERFACE_INCLUDE_DIRECTORIES"
, "INTERFACE_COMPILE_DEFINITIONS"
, "INTERFACE_COMPILE_OPTIONS"
};
std::string getLinkedTargetsContent(const std::vector<std::string> &libraries,
@ -702,7 +703,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
else
{
assert(dagCheckerParent->EvaluatingIncludeDirectories()
|| dagCheckerParent->EvaluatingCompileDefinitions());
|| dagCheckerParent->EvaluatingCompileDefinitions()
|| dagCheckerParent->EvaluatingCompileOptions());
}
}
@ -721,6 +723,11 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
{
interfacePropertyName = "INTERFACE_COMPILE_DEFINITIONS";
}
else if (propertyName == "INTERFACE_COMPILE_OPTIONS"
|| propertyName == "COMPILE_OPTIONS")
{
interfacePropertyName = "INTERFACE_COMPILE_OPTIONS";
}
const char **transBegin = targetPropertyTransitiveWhitelist;
const char **transEnd = targetPropertyTransitiveWhitelist

View File

@ -1334,6 +1334,14 @@ void cmLocalGenerator::GetCompileOptions(std::string& flags,
{
this->AppendFlags(flags, prop);
}
std::vector<std::string> opts; // TODO: Emitted.
target->GetCompileOptions(opts, config);
for(std::vector<std::string>::const_iterator li = opts.begin();
li != opts.end(); ++li)
{
this->AppendFlags(flags, li->c_str());
}
}
//----------------------------------------------------------------------------

View File

@ -140,14 +140,18 @@ public:
const std::string TargetName;
};
std::vector<TargetPropertyEntry*> IncludeDirectoriesEntries;
std::vector<TargetPropertyEntry*> CompileOptionsEntries;
std::vector<cmValueWithOrigin> LinkInterfacePropertyEntries;
std::map<std::string, std::vector<TargetPropertyEntry*> >
CachedLinkInterfaceIncludeDirectoriesEntries;
std::map<std::string, std::vector<TargetPropertyEntry*> >
CachedLinkInterfaceCompileOptionsEntries;
std::map<std::string, std::string> CachedLinkInterfaceCompileDefinitions;
std::map<std::string, bool> CacheLinkInterfaceIncludeDirectoriesDone;
std::map<std::string, bool> CacheLinkInterfaceCompileDefinitionsDone;
std::map<std::string, bool> CacheLinkInterfaceCompileOptionsDone;
};
//----------------------------------------------------------------------------
@ -181,6 +185,7 @@ void deleteAndClear(
cmTargetInternals::~cmTargetInternals()
{
deleteAndClear(this->CachedLinkInterfaceIncludeDirectoriesEntries);
deleteAndClear(this->CachedLinkInterfaceCompileOptionsEntries);
}
//----------------------------------------------------------------------------
@ -199,6 +204,7 @@ cmTarget::cmTarget()
this->IsImportedTarget = false;
this->BuildInterfaceIncludesAppended = false;
this->DebugIncludesDone = false;
this->DebugCompileOptionsDone = false;
}
//----------------------------------------------------------------------------
@ -286,6 +292,32 @@ void cmTarget::DefineProperties(cmake *cm)
"Per-configuration preprocessor definitions on a target.",
"This is the configuration-specific version of COMPILE_DEFINITIONS.");
cm->DefineProperty
("COMPILE_OPTIONS", cmProperty::TARGET,
"List of options to pass to the compiler.",
"This property specifies the list of options specified "
"so far for this property. "
"This property exists on targets only. "
"\n"
"The target property values are used by the generators to set "
"the options for the compiler.\n"
"Contents of COMPILE_OPTIONS may use \"generator expressions\" with "
"the syntax \"$<...>\". "
CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS
CM_DOCUMENT_LANGUAGE_GENERATOR_EXPRESSIONS);
cm->DefineProperty
("INTERFACE_COMPILE_OPTIONS", cmProperty::TARGET,
"List of interface options to pass to the compiler.",
"Targets may populate this property to publish the compile options "
"required to compile against the headers for the target. Consuming "
"targets can add entries to their own COMPILE_OPTIONS property such "
"as $<TARGET_PROPERTY:foo,INTERFACE_COMPILE_OPTIONS> to use the "
"compile options specified in the interface of 'foo'."
"\n"
CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS
CM_DOCUMENT_LANGUAGE_GENERATOR_EXPRESSIONS);
cm->DefineProperty
("DEFINE_SYMBOL", cmProperty::TARGET,
"Define a symbol when compiling this target's sources.",
@ -2731,6 +2763,17 @@ void cmTarget::SetProperty(const char* prop, const char* value)
new cmTargetInternals::TargetPropertyEntry(cge));
return;
}
if(strcmp(prop,"COMPILE_OPTIONS") == 0)
{
cmListFileBacktrace lfbt;
this->Makefile->GetBacktrace(lfbt);
cmGeneratorExpression ge(lfbt);
deleteAndClear(this->Internal->CompileOptionsEntries);
cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(value);
this->Internal->CompileOptionsEntries.push_back(
new cmTargetInternals::TargetPropertyEntry(cge));
return;
}
if(strcmp(prop,"EXPORT_NAME") == 0 && this->IsImported())
{
cmOStringStream e;
@ -2773,6 +2816,15 @@ void cmTarget::AppendProperty(const char* prop, const char* value,
new cmTargetInternals::TargetPropertyEntry(ge.Parse(value)));
return;
}
if(strcmp(prop,"COMPILE_OPTIONS") == 0)
{
cmListFileBacktrace lfbt;
this->Makefile->GetBacktrace(lfbt);
cmGeneratorExpression ge(lfbt);
this->Internal->CompileOptionsEntries.push_back(
new cmTargetInternals::TargetPropertyEntry(ge.Parse(value)));
return;
}
if(strcmp(prop,"EXPORT_NAME") == 0 && this->IsImported())
{
cmOStringStream e;
@ -3093,6 +3145,159 @@ std::vector<std::string> cmTarget::GetIncludeDirectories(const char *config)
return includes;
}
//----------------------------------------------------------------------------
static void processCompileOptions(cmTarget *tgt,
const std::vector<cmTargetInternals::TargetPropertyEntry*> &entries,
std::vector<std::string> &options,
std::set<std::string> &uniqueOptions,
cmGeneratorExpressionDAGChecker *dagChecker,
const char *config, bool debugOptions)
{
cmMakefile *mf = tgt->GetMakefile();
for (std::vector<cmTargetInternals::TargetPropertyEntry*>::const_iterator
it = entries.begin(), end = entries.end(); it != end; ++it)
{
bool cacheOptions = false;
std::vector<std::string> entryOptions = (*it)->CachedEntries;
if(entryOptions.empty())
{
cmSystemTools::ExpandListArgument((*it)->ge->Evaluate(mf,
config,
false,
tgt,
dagChecker),
entryOptions);
if (mf->IsGeneratingBuildSystem()
&& !(*it)->ge->GetHadContextSensitiveCondition())
{
cacheOptions = true;
}
}
std::string usedOptions;
for(std::vector<std::string>::iterator
li = entryOptions.begin(); li != entryOptions.end(); ++li)
{
std::string opt = *li;
if(uniqueOptions.insert(opt).second)
{
options.push_back(opt);
if (debugOptions)
{
usedOptions += " * " + opt + "\n";
}
}
}
if (cacheOptions)
{
(*it)->CachedEntries = entryOptions;
}
if (!usedOptions.empty())
{
mf->GetCMakeInstance()->IssueMessage(cmake::LOG,
std::string("Used compile options for target ")
+ tgt->GetName() + ":\n"
+ usedOptions, (*it)->ge->GetBacktrace());
}
}
}
//----------------------------------------------------------------------------
void cmTarget::GetCompileOptions(std::vector<std::string> &result,
const char *config)
{
std::set<std::string> uniqueOptions;
cmListFileBacktrace lfbt;
cmGeneratorExpressionDAGChecker dagChecker(lfbt,
this->GetName(),
"COMPILE_OPTIONS", 0, 0);
std::vector<std::string> debugProperties;
const char *debugProp =
this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
if (debugProp)
{
cmSystemTools::ExpandListArgument(debugProp, debugProperties);
}
bool debugOptions = !this->DebugCompileOptionsDone
&& std::find(debugProperties.begin(),
debugProperties.end(),
"COMPILE_OPTIONS")
!= debugProperties.end();
if (this->Makefile->IsGeneratingBuildSystem())
{
this->DebugCompileOptionsDone = true;
}
processCompileOptions(this,
this->Internal->CompileOptionsEntries,
result,
uniqueOptions,
&dagChecker,
config,
debugOptions);
std::string configString = config ? config : "";
if (!this->Internal->CacheLinkInterfaceCompileOptionsDone[configString])
{
for (std::vector<cmValueWithOrigin>::const_iterator
it = this->Internal->LinkInterfacePropertyEntries.begin(),
end = this->Internal->LinkInterfacePropertyEntries.end();
it != end; ++it)
{
{
cmGeneratorExpression ge(lfbt);
cmsys::auto_ptr<cmCompiledGeneratorExpression> cge =
ge.Parse(it->Value);
std::string targetResult = cge->Evaluate(this->Makefile, config,
false, this, 0, 0);
if (!this->Makefile->FindTargetToUse(targetResult.c_str()))
{
continue;
}
}
std::string optionGenex = "$<TARGET_PROPERTY:" +
it->Value + ",INTERFACE_COMPILE_OPTIONS>";
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.
optionGenex = "$<$<BOOL:" + it->Value + ">:" + optionGenex + ">";
}
cmGeneratorExpression ge(it->Backtrace);
cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(
optionGenex);
this->Internal
->CachedLinkInterfaceCompileOptionsEntries[configString].push_back(
new cmTargetInternals::TargetPropertyEntry(cge,
it->Value));
}
}
processCompileOptions(this,
this->Internal->CachedLinkInterfaceCompileOptionsEntries[configString],
result,
uniqueOptions,
&dagChecker,
config,
debugOptions);
if (!this->Makefile->IsGeneratingBuildSystem())
{
deleteAndClear(this->Internal->CachedLinkInterfaceCompileOptionsEntries);
}
else
{
this->Internal->CacheLinkInterfaceCompileOptionsDone[configString] = true;
}
}
//----------------------------------------------------------------------------
std::string cmTarget::GetCompileDefinitions(const char *config)
{
@ -3525,6 +3730,24 @@ const char *cmTarget::GetProperty(const char* prop,
}
return output.c_str();
}
if(strcmp(prop,"COMPILE_OPTIONS") == 0)
{
static std::string output;
output = "";
std::string sep;
typedef cmTargetInternals::TargetPropertyEntry
TargetPropertyEntry;
for (std::vector<TargetPropertyEntry*>::const_iterator
it = this->Internal->CompileOptionsEntries.begin(),
end = this->Internal->CompileOptionsEntries.end();
it != end; ++it)
{
output += sep;
output += (*it)->ge->GetInput();
sep = ";";
}
return output.c_str();
}
if (strcmp(prop,"IMPORTED") == 0)
{
@ -6109,6 +6332,7 @@ cmTargetInternalPointer
cmTargetInternalPointer::~cmTargetInternalPointer()
{
deleteAndClear(this->Pointer->IncludeDirectoriesEntries);
deleteAndClear(this->Pointer->CompileOptionsEntries);
delete this->Pointer;
}

View File

@ -503,6 +503,9 @@ public:
void AppendBuildInterfaceIncludes();
void GetCompileOptions(std::vector<std::string> &result,
const char *config);
bool IsNullImpliedByLinkLibraries(const std::string &p);
bool IsLinkInterfaceDependentBoolProperty(const std::string &p,
const char *config);
@ -627,6 +630,7 @@ private:
bool IsApple;
bool IsImportedTarget;
bool DebugIncludesDone;
bool DebugCompileOptionsDone;
mutable std::set<std::string> LinkImplicitNullProperties;
bool BuildInterfaceIncludesAppended;

View File

@ -245,6 +245,7 @@ if(BUILD_TESTING)
ADD_TEST_MACRO(PolicyScope PolicyScope)
ADD_TEST_MACRO(EmptyLibrary EmptyLibrary)
ADD_TEST_MACRO(CompileDefinitions CompileDefinitions)
ADD_TEST_MACRO(CompileOptions CompileOptions)
ADD_TEST_MACRO(CompatibleInterface CompatibleInterface)
set_tests_properties(EmptyLibrary PROPERTIES
PASS_REGULAR_EXPRESSION "CMake Error: CMake can not determine linker language for target:test")

View File

@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 2.8)
project(CompileOptions)
add_library(testlib other.cpp)
add_executable(CompileOptions main.cpp)
set_property(TARGET CompileOptions PROPERTY COMPILE_OPTIONS "$<$<CXX_COMPILER_ID:GNU>:-DTEST_DEFINE>")
target_link_libraries(CompileOptions testlib)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
target_compile_definitions(CompileOptions
PRIVATE
"DO_GNU_TESTS"
)
endif()

View File

@ -0,0 +1,11 @@
#ifdef DO_GNU_TESTS
# ifndef TEST_DEFINE
# error Expected TEST_DEFINE
# endif
#endif
int main(int argc, char **argv)
{
return 0;
}

View File

@ -0,0 +1,5 @@
void foo(void)
{
}