Merge topic 'target-COMPILE_OPTIONS'

24466f2 Add target_compile_options command.
80ca9c4 Add COMPILE_OPTIONS target property.
7cb2308 cmTarget: Rename LinkInterfaceIncludeDirectoriesEntries
47f80d9 cmTarget: Rename struct to be more re-usable.
1319a14 Add <LANG>_COMPILER_ID generator expressions.
3549676 Add cmLocalGenerator::GetCompileOptions.
f3ad863 VS6: Rename some variables to correspond to config values.
This commit is contained in:
Brad King 2013-06-03 09:57:44 -04:00 committed by CMake Topic Stage
commit e57b6a2521
25 changed files with 752 additions and 63 deletions

View File

@ -29,6 +29,7 @@
#include "cmSourceGroupCommand.cxx"
#include "cmSubdirDependsCommand.cxx"
#include "cmTargetCompileDefinitionsCommand.cxx"
#include "cmTargetCompileOptionsCommand.cxx"
#include "cmTargetIncludeDirectoriesCommand.cxx"
#include "cmTargetPropCommandBase.cxx"
#include "cmUseMangledMesaCommand.cxx"
@ -71,6 +72,7 @@ void GetPredefinedCommands(std::list<cmCommand*>&
commands.push_back(new cmSubdirDependsCommand);
commands.push_back(new cmTargetIncludeDirectoriesCommand);
commands.push_back(new cmTargetCompileDefinitionsCommand);
commands.push_back(new cmTargetCompileOptionsCommand);
commands.push_back(new cmUseMangledMesaCommand);
commands.push_back(new cmUtilitySourceCommand);
commands.push_back(new cmVariableRequiresCommand);

View File

@ -40,6 +40,14 @@
"is exported using export(), or when the target is used by another " \
"target in the same buildsystem. Expands to the empty string " \
"otherwise.\n" \
" $<C_COMPILER_ID> = The CMake-id of the C compiler " \
"used.\n" \
" $<C_COMPILER_ID:comp> = '1' if the CMake-id of the C " \
"compiler matches comp, otherwise '0'.\n" \
" $<CXX_COMPILER_ID> = The CMake-id of the CXX compiler " \
"used.\n" \
" $<CXX_COMPILER_ID:comp> = '1' if the CMake-id of the CXX " \
"compiler matches comp, otherwise '0'.\n" \
" $<TARGET_FILE:tgt> = main file (.exe, .so.1.2, .a)\n" \
" $<TARGET_LINKER_FILE:tgt> = file used to link (.a, .lib, .so)\n" \
" $<TARGET_SONAME_FILE:tgt> = file with soname (.so.3)\n" \

View File

@ -429,7 +429,9 @@ cmExtraSublimeTextGenerator::ComputeFlagsForObject(cmSourceFile* source,
lg->AppendFlags(flags, makefile->GetDefineFlags());
// Add target-specific flags.
if(target->GetProperty("COMPILE_FLAGS"))
std::string targetFlags;
lg->GetCompileOptions(targetFlags, target, config);
if (!targetFlags.empty())
{
std::string langIncludeExpr = "CMAKE_";
langIncludeExpr += language;
@ -440,7 +442,7 @@ cmExtraSublimeTextGenerator::ComputeFlagsForObject(cmSourceFile* source,
cmsys::RegularExpression r(regex);
std::vector<std::string> args;
cmSystemTools::
ParseWindowsCommandLine(target->GetProperty("COMPILE_FLAGS"), args);
ParseWindowsCommandLine(targetFlags.c_str(), args);
for(std::vector<std::string>::iterator i = args.begin();
i != args.end(); ++i)
{
@ -452,7 +454,7 @@ cmExtraSublimeTextGenerator::ComputeFlagsForObject(cmSourceFile* source,
}
else
{
lg->AppendFlags(flags, target->GetProperty("COMPILE_FLAGS"));
lg->AppendFlags(flags, targetFlags.c_str());
}
}

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

@ -246,6 +246,104 @@ static const struct SemicolonNode : public cmGeneratorExpressionNode
}
} semicolonNode;
//----------------------------------------------------------------------------
struct CompilerIdNode : public cmGeneratorExpressionNode
{
CompilerIdNode() {}
virtual int NumExpectedParameters() const { return ZeroOrMoreParameters; }
std::string EvaluateWithLanguage(const std::vector<std::string> &parameters,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content,
cmGeneratorExpressionDAGChecker *,
const std::string &lang) const
{
const char *compilerId = context->Makefile ?
context->Makefile->GetSafeDefinition((
"CMAKE_" + lang + "_COMPILER_ID").c_str()) : "";
if (parameters.size() == 0)
{
return compilerId ? compilerId : "";
}
else
{
cmsys::RegularExpression compilerIdValidator;
compilerIdValidator.compile("^[A-Za-z0-9_]*$");
if (!compilerIdValidator.find(parameters.begin()->c_str()))
{
reportError(context, content->GetOriginalExpression(),
"Expression syntax not recognized.");
return std::string();
}
if (!compilerId)
{
return parameters.front().empty() ? "1" : "0";
}
if (cmsysString_strcasecmp(parameters.begin()->c_str(), compilerId) == 0)
{
return "1";
}
return "0";
}
}
};
//----------------------------------------------------------------------------
static const struct CCompilerIdNode : public CompilerIdNode
{
CCompilerIdNode() {}
std::string Evaluate(const std::vector<std::string> &parameters,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content,
cmGeneratorExpressionDAGChecker *dagChecker) const
{
if (parameters.size() != 0 && parameters.size() != 1)
{
reportError(context, content->GetOriginalExpression(),
"$<C_COMPILER_ID> expression requires one or two parameters");
return std::string();
}
if (!context->HeadTarget)
{
reportError(context, content->GetOriginalExpression(),
"$<C_COMPILER_ID> may only be used with targets. It may not "
"be used with add_custom_command.");
}
return this->EvaluateWithLanguage(parameters, context, content,
dagChecker, "C");
}
} cCompilerIdNode;
//----------------------------------------------------------------------------
static const struct CXXCompilerIdNode : public CompilerIdNode
{
CXXCompilerIdNode() {}
std::string Evaluate(const std::vector<std::string> &parameters,
cmGeneratorExpressionContext *context,
const GeneratorExpressionContent *content,
cmGeneratorExpressionDAGChecker *dagChecker) const
{
if (parameters.size() != 0 && parameters.size() != 1)
{
reportError(context, content->GetOriginalExpression(),
"$<CXX_COMPILER_ID> expression requires one or two parameters");
return std::string();
}
if (!context->HeadTarget)
{
reportError(context, content->GetOriginalExpression(),
"$<CXX_COMPILER_ID> may only be used with targets. It may not "
"be used with add_custom_command.");
}
return this->EvaluateWithLanguage(parameters, context, content,
dagChecker, "CXX");
}
} cxxCompilerIdNode;
//----------------------------------------------------------------------------
static const struct ConfigurationNode : public cmGeneratorExpressionNode
{
@ -397,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,
@ -604,7 +703,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
else
{
assert(dagCheckerParent->EvaluatingIncludeDirectories()
|| dagCheckerParent->EvaluatingCompileDefinitions());
|| dagCheckerParent->EvaluatingCompileDefinitions()
|| dagCheckerParent->EvaluatingCompileOptions());
}
}
@ -623,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
@ -1055,6 +1160,10 @@ cmGeneratorExpressionNode* GetNode(const std::string &identifier)
return &orNode;
else if (identifier == "NOT")
return &notNode;
else if (identifier == "C_COMPILER_ID")
return &cCompilerIdNode;
else if (identifier == "CXX_COMPILER_ID")
return &cxxCompilerIdNode;
else if (identifier == "CONFIGURATION")
return &configurationNode;
else if (identifier == "CONFIG")

View File

@ -681,9 +681,11 @@ cmGlobalXCodeGenerator::CreateXCodeSourceFile(cmLocalGenerator* lg,
{
// Add flags from target and source file properties.
std::string flags;
if(cmtarget.GetProperty("COMPILE_FLAGS"))
std::string targetFlags;
lg->GetCompileOptions(targetFlags, &cmtarget, 0); // TODO: Config?
if(!targetFlags.empty())
{
lg->AppendFlags(flags, cmtarget.GetProperty("COMPILE_FLAGS"));
lg->AppendFlags(flags, targetFlags.c_str());
}
const char* srcfmt = sf->GetProperty("Fortran_FORMAT");
switch(this->CurrentLocalGenerator->GetFortranFormat(srcfmt))

View File

@ -1324,6 +1324,26 @@ std::string cmLocalGenerator::GetIncludeFlags(
return flags;
}
//----------------------------------------------------------------------------
void cmLocalGenerator::GetCompileOptions(std::string& flags,
cmTarget* target,
const char *config)
{
// Add target-specific flags.
if(const char *prop = target->GetProperty("COMPILE_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());
}
}
//----------------------------------------------------------------------------
void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs,
cmGeneratorTarget* target,

View File

@ -215,6 +215,9 @@ public:
cmGeneratorTarget* target,
const char* lang = "C", const char *config = 0,
bool stripImplicitInclDirs = true);
void GetCompileOptions(std::string& flags,
cmTarget* target,
const char *config);
/** Compute the language used to compile the given source file. */
const char* GetSourceFileLanguage(const cmSourceFile& source);

View File

@ -1639,9 +1639,9 @@ void cmLocalVisualStudio6Generator
// store flags for each configuration
std::string flags = " ";
std::string flagsRelease = " ";
std::string flagsMinSize = " ";
std::string flagsMinSizeRel = " ";
std::string flagsDebug = " ";
std::string flagsDebugRel = " ";
std::string flagsRelWithDebInfo = " ";
if(target.GetType() >= cmTarget::EXECUTABLE &&
target.GetType() <= cmTarget::OBJECT_LIBRARY)
{
@ -1664,16 +1664,16 @@ void cmLocalVisualStudio6Generator
flagsRelease += " -DCMAKE_INTDIR=\\\"Release\\\" ";
flagVar = baseFlagVar + "_MINSIZEREL";
flagsMinSize = this->Makefile->GetSafeDefinition(flagVar.c_str());
flagsMinSize += " -DCMAKE_INTDIR=\\\"MinSizeRel\\\" ";
flagsMinSizeRel = this->Makefile->GetSafeDefinition(flagVar.c_str());
flagsMinSizeRel += " -DCMAKE_INTDIR=\\\"MinSizeRel\\\" ";
flagVar = baseFlagVar + "_DEBUG";
flagsDebug = this->Makefile->GetSafeDefinition(flagVar.c_str());
flagsDebug += " -DCMAKE_INTDIR=\\\"Debug\\\" ";
flagVar = baseFlagVar + "_RELWITHDEBINFO";
flagsDebugRel = this->Makefile->GetSafeDefinition(flagVar.c_str());
flagsDebugRel += " -DCMAKE_INTDIR=\\\"RelWithDebInfo\\\" ";
flagsRelWithDebInfo = this->Makefile->GetSafeDefinition(flagVar.c_str());
flagsRelWithDebInfo += " -DCMAKE_INTDIR=\\\"RelWithDebInfo\\\" ";
}
// if _UNICODE and _SBCS are not found, then add -D_MBCS
@ -1686,12 +1686,31 @@ void cmLocalVisualStudio6Generator
flags += " /D \"_MBCS\"";
}
{
std::string targetFlags;
this->GetCompileOptions(targetFlags, &target, 0);
// Add per-target flags.
if(const char* targetFlags = target.GetProperty("COMPILE_FLAGS"))
if(!targetFlags.empty())
{
flags += " ";
flags += targetFlags;
}
}
#define ADD_FLAGS(CONFIG) \
{ \
std::string targetFlags; \
this->GetCompileOptions(targetFlags, &target, #CONFIG); \
if(!targetFlags.empty()) \
{ \
flags ## CONFIG += " "; \
flags ## CONFIG += targetFlags; \
} \
}
ADD_FLAGS(Debug)
ADD_FLAGS(Release)
ADD_FLAGS(MinSizeRel)
ADD_FLAGS(RelWithDebInfo)
// Add per-target and per-configuration preprocessor definitions.
std::set<std::string> definesSet;
@ -1731,19 +1750,19 @@ void cmLocalVisualStudio6Generator
flags += defines;
flagsDebug += debugDefines;
flagsRelease += releaseDefines;
flagsMinSize += minsizeDefines;
flagsDebugRel += debugrelDefines;
flagsMinSizeRel += minsizeDefines;
flagsRelWithDebInfo += debugrelDefines;
// The template files have CXX FLAGS in them, that need to be replaced.
// There are not separate CXX and C template files, so we use the same
// variable names. The previous code sets up flags* variables to contain
// the correct C or CXX flags
cmSystemTools::ReplaceString(line, "CMAKE_CXX_FLAGS_MINSIZEREL",
flagsMinSize.c_str());
flagsMinSizeRel.c_str());
cmSystemTools::ReplaceString(line, "CMAKE_CXX_FLAGS_DEBUG",
flagsDebug.c_str());
cmSystemTools::ReplaceString(line, "CMAKE_CXX_FLAGS_RELWITHDEBINFO",
flagsDebugRel.c_str());
flagsRelWithDebInfo.c_str());
cmSystemTools::ReplaceString(line, "CMAKE_CXX_FLAGS_RELEASE",
flagsRelease.c_str());
cmSystemTools::ReplaceString(line, "CMAKE_CXX_FLAGS", flags.c_str());

View File

@ -723,8 +723,10 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout,
}
}
std::string targetFlags;
this->GetCompileOptions(targetFlags, &target, configName);
// Add the target-specific flags.
if(const char* targetFlags = target.GetProperty("COMPILE_FLAGS"))
if(!targetFlags.empty())
{
flags += " ";
flags += targetFlags;

View File

@ -332,21 +332,25 @@ void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
this->Makefile->GetSafeDefinition(compiler.c_str()) << "\n";
}
std::string targetFlags;
for(std::set<cmStdString>::const_iterator l = languages.begin();
l != languages.end(); ++l)
{
*this->FlagFileStream << *l << "_FLAGS = " << this->GetFlags(*l) << "\n\n";
*this->FlagFileStream << *l << "_DEFINES = " << this->GetDefines(*l) <<
"\n\n";
std::string targetLangFlags;
this->LocalGenerator->GetCompileOptions(targetLangFlags, this->Target,
this->LocalGenerator->ConfigurationName.c_str());
if (!targetFlags.empty() && targetFlags != targetLangFlags)
{
targetFlags += " " + targetLangFlags;
}
}
// Add target-specific flags.
if(this->Target->GetProperty("COMPILE_FLAGS"))
if (!targetFlags.empty())
{
std::string flags;
this->LocalGenerator->AppendFlags
(flags, this->Target->GetProperty("COMPILE_FLAGS"));
*this->FlagFileStream << "# TARGET_FLAGS = " << flags << "\n\n";
*this->FlagFileStream << "# TARGET_FLAGS = " << targetFlags << "\n\n";
}
}
@ -532,8 +536,13 @@ cmMakefileTargetGenerator
langFlags += "_FLAGS)";
this->LocalGenerator->AppendFlags(flags, langFlags.c_str());
// Add target-specific flags.
if(this->Target->GetProperty("COMPILE_FLAGS"))
std::string configUpper =
cmSystemTools::UpperCase(this->LocalGenerator->ConfigurationName);
std::string targetFlags;
this->LocalGenerator->GetCompileOptions(targetFlags, this->Target,
configUpper.c_str());
if (!targetFlags.empty())
{
std::string langIncludeExpr = "CMAKE_";
langIncludeExpr += lang;
@ -545,7 +554,7 @@ cmMakefileTargetGenerator
cmsys::RegularExpression r(regex);
std::vector<std::string> args;
cmSystemTools::ParseWindowsCommandLine(
this->Target->GetProperty("COMPILE_FLAGS"),
targetFlags.c_str(),
args);
for(std::vector<std::string>::iterator i = args.begin();
i != args.end(); ++i)
@ -559,8 +568,7 @@ cmMakefileTargetGenerator
}
else
{
this->LocalGenerator->AppendFlags
(flags, this->Target->GetProperty("COMPILE_FLAGS"));
this->LocalGenerator->AppendFlags(flags, targetFlags.c_str());
}
}
@ -594,8 +602,6 @@ cmMakefileTargetGenerator
<< compile_defs << "\n"
<< "\n";
}
std::string configUpper =
cmSystemTools::UpperCase(this->LocalGenerator->ConfigurationName);
std::string defPropName = "COMPILE_DEFINITIONS_";
defPropName += configUpper;
if(const char* config_compile_defs =

View File

@ -151,9 +151,9 @@ cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source,
this->GetConfigName());
// Add include directory flags.
const char *config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE");
{
std::vector<std::string> includes;
const char *config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE");
this->LocalGenerator->GetIncludeDirectories(includes,
this->GeneratorTarget,
language.c_str(), config);
@ -171,7 +171,9 @@ cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source,
this->LocalGenerator->AppendFlags(flags, this->Makefile->GetDefineFlags());
// Add target-specific flags.
if(this->Target->GetProperty("COMPILE_FLAGS"))
std::string targetFlags;
this->LocalGenerator->GetCompileOptions(targetFlags, this->Target, config);
if(!targetFlags.empty())
{
std::string langIncludeExpr = "CMAKE_";
langIncludeExpr += language;
@ -183,7 +185,7 @@ cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source,
cmsys::RegularExpression r(regex);
std::vector<std::string> args;
cmSystemTools::ParseWindowsCommandLine(
this->Target->GetProperty("COMPILE_FLAGS"),
targetFlags.c_str(),
args);
for(std::vector<std::string>::iterator i = args.begin();
i != args.end(); ++i)
@ -198,7 +200,7 @@ cmNinjaTargetGenerator::ComputeFlagsForObject(cmSourceFile *source,
else
{
this->LocalGenerator->AppendFlags
(flags, this->Target->GetProperty("COMPILE_FLAGS"));
(flags, targetFlags.c_str());
}
}

View File

@ -130,31 +130,35 @@ public:
typedef std::map<cmSourceFile*, SourceEntry> SourceEntriesType;
SourceEntriesType SourceEntries;
struct IncludeDirectoriesEntry {
IncludeDirectoriesEntry(cmsys::auto_ptr<cmCompiledGeneratorExpression> cge,
struct TargetPropertyEntry {
TargetPropertyEntry(cmsys::auto_ptr<cmCompiledGeneratorExpression> cge,
const std::string &targetName = std::string())
: ge(cge), TargetName(targetName)
{}
const cmsys::auto_ptr<cmCompiledGeneratorExpression> ge;
std::vector<std::string> CachedIncludes;
std::vector<std::string> CachedEntries;
const std::string TargetName;
};
std::vector<IncludeDirectoriesEntry*> IncludeDirectoriesEntries;
std::vector<cmValueWithOrigin> LinkInterfaceIncludeDirectoriesEntries;
std::vector<TargetPropertyEntry*> IncludeDirectoriesEntries;
std::vector<TargetPropertyEntry*> CompileOptionsEntries;
std::vector<cmValueWithOrigin> LinkInterfacePropertyEntries;
std::map<std::string, std::vector<IncludeDirectoriesEntry*> >
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;
};
//----------------------------------------------------------------------------
void deleteAndClear(
std::vector<cmTargetInternals::IncludeDirectoriesEntry*> &entries)
std::vector<cmTargetInternals::TargetPropertyEntry*> &entries)
{
for (std::vector<cmTargetInternals::IncludeDirectoriesEntry*>::const_iterator
for (std::vector<cmTargetInternals::TargetPropertyEntry*>::const_iterator
it = entries.begin(),
end = entries.end();
it != end; ++it)
@ -167,10 +171,10 @@ void deleteAndClear(
//----------------------------------------------------------------------------
void deleteAndClear(
std::map<std::string,
std::vector<cmTargetInternals::IncludeDirectoriesEntry*> > &entries)
std::vector<cmTargetInternals::TargetPropertyEntry*> > &entries)
{
for (std::map<std::string,
std::vector<cmTargetInternals::IncludeDirectoriesEntry*> >::iterator
std::vector<cmTargetInternals::TargetPropertyEntry*> >::iterator
it = entries.begin(), end = entries.end(); it != end; ++it)
{
deleteAndClear(it->second);
@ -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.",
@ -2751,7 +2783,18 @@ void cmTarget::SetProperty(const char* prop, const char* value)
deleteAndClear(this->Internal->IncludeDirectoriesEntries);
cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(value);
this->Internal->IncludeDirectoriesEntries.push_back(
new cmTargetInternals::IncludeDirectoriesEntry(cge));
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())
@ -2764,14 +2807,14 @@ void cmTarget::SetProperty(const char* prop, const char* value)
}
if (strcmp(prop, "LINK_LIBRARIES") == 0)
{
this->Internal->LinkInterfaceIncludeDirectoriesEntries.clear();
this->Internal->LinkInterfacePropertyEntries.clear();
if (cmGeneratorExpression::IsValidTargetName(value)
|| cmGeneratorExpression::Find(value) != std::string::npos)
{
cmListFileBacktrace lfbt;
this->Makefile->GetBacktrace(lfbt);
cmValueWithOrigin entry(value, lfbt);
this->Internal->LinkInterfaceIncludeDirectoriesEntries.push_back(entry);
this->Internal->LinkInterfacePropertyEntries.push_back(entry);
}
// Fall through
}
@ -2793,7 +2836,16 @@ void cmTarget::AppendProperty(const char* prop, const char* value,
this->Makefile->GetBacktrace(lfbt);
cmGeneratorExpression ge(lfbt);
this->Internal->IncludeDirectoriesEntries.push_back(
new cmTargetInternals::IncludeDirectoriesEntry(ge.Parse(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())
@ -2812,7 +2864,7 @@ void cmTarget::AppendProperty(const char* prop, const char* value,
cmListFileBacktrace lfbt;
this->Makefile->GetBacktrace(lfbt);
cmValueWithOrigin entry(value, lfbt);
this->Internal->LinkInterfaceIncludeDirectoriesEntries.push_back(entry);
this->Internal->LinkInterfacePropertyEntries.push_back(entry);
}
// Fall through
}
@ -2877,17 +2929,31 @@ void cmTarget::InsertInclude(const cmValueWithOrigin &entry,
{
cmGeneratorExpression ge(entry.Backtrace);
std::vector<cmTargetInternals::IncludeDirectoriesEntry*>::iterator position
std::vector<cmTargetInternals::TargetPropertyEntry*>::iterator position
= before ? this->Internal->IncludeDirectoriesEntries.begin()
: this->Internal->IncludeDirectoriesEntries.end();
this->Internal->IncludeDirectoriesEntries.insert(position,
new cmTargetInternals::IncludeDirectoriesEntry(ge.Parse(entry.Value)));
new cmTargetInternals::TargetPropertyEntry(ge.Parse(entry.Value)));
}
//----------------------------------------------------------------------------
void cmTarget::InsertCompileOption(const cmValueWithOrigin &entry,
bool before)
{
cmGeneratorExpression ge(entry.Backtrace);
std::vector<cmTargetInternals::TargetPropertyEntry*>::iterator position
= before ? this->Internal->CompileOptionsEntries.begin()
: this->Internal->CompileOptionsEntries.end();
this->Internal->CompileOptionsEntries.insert(position,
new cmTargetInternals::TargetPropertyEntry(ge.Parse(entry.Value)));
}
//----------------------------------------------------------------------------
static void processIncludeDirectories(cmTarget *tgt,
const std::vector<cmTargetInternals::IncludeDirectoriesEntry*> &entries,
const std::vector<cmTargetInternals::TargetPropertyEntry*> &entries,
std::vector<std::string> &includes,
std::set<std::string> &uniqueIncludes,
cmGeneratorExpressionDAGChecker *dagChecker,
@ -2895,12 +2961,12 @@ static void processIncludeDirectories(cmTarget *tgt,
{
cmMakefile *mf = tgt->GetMakefile();
for (std::vector<cmTargetInternals::IncludeDirectoriesEntry*>::const_iterator
for (std::vector<cmTargetInternals::TargetPropertyEntry*>::const_iterator
it = entries.begin(), end = entries.end(); it != end; ++it)
{
bool testIsOff = true;
bool cacheIncludes = false;
std::vector<std::string> entryIncludes = (*it)->CachedIncludes;
std::vector<std::string> entryIncludes = (*it)->CachedEntries;
if(!entryIncludes.empty())
{
testIsOff = false;
@ -3003,7 +3069,7 @@ static void processIncludeDirectories(cmTarget *tgt,
}
if (cacheIncludes)
{
(*it)->CachedIncludes = entryIncludes;
(*it)->CachedEntries = entryIncludes;
}
if (!usedIncludes.empty())
{
@ -3059,8 +3125,8 @@ std::vector<std::string> cmTarget::GetIncludeDirectories(const char *config)
if (!this->Internal->CacheLinkInterfaceIncludeDirectoriesDone[configString])
{
for (std::vector<cmValueWithOrigin>::const_iterator
it = this->Internal->LinkInterfaceIncludeDirectoriesEntries.begin(),
end = this->Internal->LinkInterfaceIncludeDirectoriesEntries.end();
it = this->Internal->LinkInterfacePropertyEntries.begin(),
end = this->Internal->LinkInterfacePropertyEntries.end();
it != end; ++it)
{
{
@ -3089,7 +3155,7 @@ std::vector<std::string> cmTarget::GetIncludeDirectories(const char *config)
this->Internal
->CachedLinkInterfaceIncludeDirectoriesEntries[configString].push_back(
new cmTargetInternals::IncludeDirectoriesEntry(cge,
new cmTargetInternals::TargetPropertyEntry(cge,
it->Value));
}
}
@ -3116,6 +3182,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)
{
@ -3541,9 +3760,9 @@ const char *cmTarget::GetProperty(const char* prop,
static std::string output;
output = "";
std::string sep;
typedef cmTargetInternals::IncludeDirectoriesEntry
IncludeDirectoriesEntry;
for (std::vector<IncludeDirectoriesEntry*>::const_iterator
typedef cmTargetInternals::TargetPropertyEntry
TargetPropertyEntry;
for (std::vector<TargetPropertyEntry*>::const_iterator
it = this->Internal->IncludeDirectoriesEntries.begin(),
end = this->Internal->IncludeDirectoriesEntries.end();
it != end; ++it)
@ -3554,6 +3773,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)
{
@ -6263,6 +6500,7 @@ cmTargetInternalPointer
cmTargetInternalPointer::~cmTargetInternalPointer()
{
deleteAndClear(this->Pointer->IncludeDirectoriesEntries);
deleteAndClear(this->Pointer->CompileOptionsEntries);
delete this->Pointer;
}

View File

@ -511,9 +511,14 @@ public:
std::vector<std::string> GetIncludeDirectories(const char *config);
void InsertInclude(const cmValueWithOrigin &entry,
bool before = false);
void InsertCompileOption(const cmValueWithOrigin &entry,
bool before = false);
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);
@ -643,6 +648,7 @@ private:
bool IsApple;
bool IsImportedTarget;
bool DebugIncludesDone;
bool DebugCompileOptionsDone;
mutable std::set<std::string> LinkImplicitNullProperties;
bool BuildInterfaceIncludesAppended;

View File

@ -0,0 +1,62 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2013 Stephen Kelly <steveire@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmTargetCompileOptionsCommand.h"
bool cmTargetCompileOptionsCommand
::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
{
return this->HandleArguments(args, "COMPILE_OPTIONS", PROCESS_BEFORE);
}
void cmTargetCompileOptionsCommand
::HandleImportedTarget(const std::string &tgt)
{
cmOStringStream e;
e << "Cannot specify compile options for imported target \""
<< tgt << "\".";
this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
}
void cmTargetCompileOptionsCommand
::HandleMissingTarget(const std::string &name)
{
cmOStringStream e;
e << "Cannot specify compile options for target \"" << name << "\" "
"which is not built by this project.";
this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
}
//----------------------------------------------------------------------------
std::string cmTargetCompileOptionsCommand
::Join(const std::vector<std::string> &content)
{
std::string defs;
std::string sep;
for(std::vector<std::string>::const_iterator it = content.begin();
it != content.end(); ++it)
{
defs += sep + *it;
sep = ";";
}
return defs;
}
//----------------------------------------------------------------------------
void cmTargetCompileOptionsCommand
::HandleDirectContent(cmTarget *tgt, const std::vector<std::string> &content,
bool)
{
cmListFileBacktrace lfbt;
this->Makefile->GetBacktrace(lfbt);
cmValueWithOrigin entry(this->Join(content), lfbt);
tgt->InsertCompileOption(entry);
}

View File

@ -0,0 +1,90 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2013 Stephen Kelly <steveire@gmail.com>
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmTargetCompileOptionsCommand_h
#define cmTargetCompileOptionsCommand_h
#include "cmTargetPropCommandBase.h"
class cmTargetCompileOptionsCommand : public cmTargetPropCommandBase
{
public:
/**
* This is a virtual constructor for the command.
*/
virtual cmCommand* Clone()
{
return new cmTargetCompileOptionsCommand;
}
/**
* This is called when the command is first encountered in
* the CMakeLists.txt file.
*/
virtual bool InitialPass(std::vector<std::string> const& args,
cmExecutionStatus &status);
/**
* The name of the command as specified in CMakeList.txt.
*/
virtual const char* GetName() const { return "target_compile_options";}
/**
* Succinct documentation.
*/
virtual const char* GetTerseDocumentation() const
{
return
"Add compile options to a target.";
}
/**
* More documentation.
*/
virtual const char* GetFullDocumentation() const
{
return
" target_compile_options(<target> [BEFORE] "
"<INTERFACE|PUBLIC|PRIVATE> [items1...]\n"
" [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])\n"
"Specify compile options to use when compiling a given target. "
"The named <target> must have been created by a command such as "
"add_executable or add_library and must not be an IMPORTED target. "
"If BEFORE is specified, the content will be prepended to the property "
"instead of being appended.\n"
"The INTERFACE, PUBLIC and PRIVATE keywords are required to specify "
"the scope of the following arguments. PRIVATE and PUBLIC items will "
"populate the COMPILE_OPTIONS property of <target>. PUBLIC and "
"INTERFACE items will populate the INTERFACE_COMPILE_OPTIONS "
"property of <target>. "
"The following arguments specify compile opitions. "
"Repeated calls for the same <target> append items in the order called."
"\n"
"Arguments to target_compile_options may use \"generator "
"expressions\" with the syntax \"$<...>\". "
CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS
;
}
cmTypeMacro(cmTargetCompileOptionsCommand, cmTargetPropCommandBase);
private:
virtual void HandleImportedTarget(const std::string &tgt);
virtual void HandleMissingTarget(const std::string &name);
virtual void HandleDirectContent(cmTarget *tgt,
const std::vector<std::string> &content,
bool prepend);
virtual std::string Join(const std::vector<std::string> &content);
};
#endif

View File

@ -1270,8 +1270,12 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
flags += " /TP ";
}
}
std::string targetFlags;
this->LocalGenerator->GetCompileOptions(targetFlags, this->Target,
configName.c_str());
// Add the target-specific flags.
if(const char* targetFlags = this->Target->GetProperty("COMPILE_FLAGS"))
if(!targetFlags.empty())
{
flags += " ";
flags += targetFlags;

View File

@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 2.8)
project(target_compile_options)
add_executable(target_compile_options
"${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
)
target_compile_options(target_compile_options
PRIVATE $<$<CXX_COMPILER_ID:GNU>:-DMY_PRIVATE_DEFINE>
PUBLIC $<$<CXX_COMPILER_ID:GNU>:-DMY_PUBLIC_DEFINE>
INTERFACE $<$<CXX_COMPILER_ID:GNU>:-DMY_INTERFACE_DEFINE>
)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
target_compile_definitions(target_compile_options
PRIVATE
"DO_GNU_TESTS"
)
endif()
add_executable(consumer
"${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp"
)
target_compile_options(consumer
PRIVATE $<$<CXX_COMPILER_ID:GNU>:$<TARGET_PROPERTY:target_compile_options,INTERFACE_COMPILE_OPTIONS>>
)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
target_compile_definitions(consumer
PRIVATE
"DO_GNU_TESTS"
)
endif()

View File

@ -0,0 +1,18 @@
#ifdef DO_GNU_TESTS
# ifdef MY_PRIVATE_DEFINE
# error Unexpected MY_PRIVATE_DEFINE
# endif
# ifndef MY_PUBLIC_DEFINE
# error Expected MY_PUBLIC_DEFINE
# endif
# ifndef MY_INTERFACE_DEFINE
# error Expected MY_INTERFACE_DEFINE
# endif
#endif
int main() { return 0; }

View File

@ -0,0 +1,18 @@
#ifdef DO_GNU_TESTS
# ifndef MY_PRIVATE_DEFINE
# error Expected MY_PRIVATE_DEFINE
# endif
# ifndef MY_PUBLIC_DEFINE
# error Expected MY_PUBLIC_DEFINE
# endif
# ifdef MY_INTERFACE_DEFINE
# error Unexpected MY_INTERFACE_DEFINE
# endif
#endif
int main() { return 0; }

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")
@ -1990,6 +1991,7 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
ADD_TEST_MACRO(CMakeCommands.target_link_libraries target_link_libraries)
ADD_TEST_MACRO(CMakeCommands.target_include_directories target_include_directories)
ADD_TEST_MACRO(CMakeCommands.target_compile_definitions target_compile_definitions)
ADD_TEST_MACRO(CMakeCommands.target_compile_options target_compile_options)
configure_file(
"${CMake_SOURCE_DIR}/Tests/CTestTestCrash/test.cmake.in"

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)
{
}