From 80ca9c4b41ecdce069a6c3f4c1b558084a748876 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Thu, 16 May 2013 15:15:28 +0200 Subject: [PATCH] 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. --- Source/cmGeneratorExpressionDAGChecker.cxx | 8 + Source/cmGeneratorExpressionDAGChecker.h | 1 + Source/cmGeneratorExpressionEvaluator.cxx | 9 +- Source/cmLocalGenerator.cxx | 8 + Source/cmTarget.cxx | 224 +++++++++++++++++++++ Source/cmTarget.h | 4 + Tests/CMakeLists.txt | 1 + Tests/CompileOptions/CMakeLists.txt | 16 ++ Tests/CompileOptions/main.cpp | 11 + Tests/CompileOptions/other.cpp | 5 + 10 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 Tests/CompileOptions/CMakeLists.txt create mode 100644 Tests/CompileOptions/main.cpp create mode 100644 Tests/CompileOptions/other.cpp diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index 5cb50b9b8..e5ffb0cd5 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -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 ); +} diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index 62a5cdf38..8d9fd76ab 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h @@ -40,6 +40,7 @@ struct cmGeneratorExpressionDAGChecker bool EvaluatingLinkLibraries(); bool EvaluatingIncludeDirectories() const; bool EvaluatingCompileDefinitions() const; + bool EvaluatingCompileOptions() const; private: Result checkGraph() const; diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx index f27761aeb..5e7d00d70 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -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 &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 diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 9429450e4..57e25a16d 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1334,6 +1334,14 @@ void cmLocalGenerator::GetCompileOptions(std::string& flags, { this->AppendFlags(flags, prop); } + + std::vector opts; // TODO: Emitted. + target->GetCompileOptions(opts, config); + for(std::vector::const_iterator li = opts.begin(); + li != opts.end(); ++li) + { + this->AppendFlags(flags, li->c_str()); + } } //---------------------------------------------------------------------------- diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index a54f80bad..ea5552efd 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -140,14 +140,18 @@ public: const std::string TargetName; }; std::vector IncludeDirectoriesEntries; + std::vector CompileOptionsEntries; std::vector LinkInterfacePropertyEntries; std::map > CachedLinkInterfaceIncludeDirectoriesEntries; + std::map > + CachedLinkInterfaceCompileOptionsEntries; std::map CachedLinkInterfaceCompileDefinitions; std::map CacheLinkInterfaceIncludeDirectoriesDone; std::map CacheLinkInterfaceCompileDefinitionsDone; + std::map 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 $ 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 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 cmTarget::GetIncludeDirectories(const char *config) return includes; } +//---------------------------------------------------------------------------- +static void processCompileOptions(cmTarget *tgt, + const std::vector &entries, + std::vector &options, + std::set &uniqueOptions, + cmGeneratorExpressionDAGChecker *dagChecker, + const char *config, bool debugOptions) +{ + cmMakefile *mf = tgt->GetMakefile(); + + for (std::vector::const_iterator + it = entries.begin(), end = entries.end(); it != end; ++it) + { + bool cacheOptions = false; + std::vector 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::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 &result, + const char *config) +{ + std::set uniqueOptions; + cmListFileBacktrace lfbt; + + cmGeneratorExpressionDAGChecker dagChecker(lfbt, + this->GetName(), + "COMPILE_OPTIONS", 0, 0); + + std::vector 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::const_iterator + it = this->Internal->LinkInterfacePropertyEntries.begin(), + end = this->Internal->LinkInterfacePropertyEntries.end(); + it != end; ++it) + { + { + cmGeneratorExpression ge(lfbt); + cmsys::auto_ptr 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 = "$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 = "$<$Value + ">:" + optionGenex + ">"; + } + cmGeneratorExpression ge(it->Backtrace); + cmsys::auto_ptr 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::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; } diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 508fc11ea..7ec10dff1 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -503,6 +503,9 @@ public: void AppendBuildInterfaceIncludes(); + void GetCompileOptions(std::vector &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 LinkImplicitNullProperties; bool BuildInterfaceIncludesAppended; diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 0b221e885..3a28be200 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -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") diff --git a/Tests/CompileOptions/CMakeLists.txt b/Tests/CompileOptions/CMakeLists.txt new file mode 100644 index 000000000..6d8a96a66 --- /dev/null +++ b/Tests/CompileOptions/CMakeLists.txt @@ -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 "$<$:-DTEST_DEFINE>") +target_link_libraries(CompileOptions testlib) + +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + target_compile_definitions(CompileOptions + PRIVATE + "DO_GNU_TESTS" + ) +endif() diff --git a/Tests/CompileOptions/main.cpp b/Tests/CompileOptions/main.cpp new file mode 100644 index 000000000..0d390503f --- /dev/null +++ b/Tests/CompileOptions/main.cpp @@ -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; +} diff --git a/Tests/CompileOptions/other.cpp b/Tests/CompileOptions/other.cpp new file mode 100644 index 000000000..0e3437579 --- /dev/null +++ b/Tests/CompileOptions/other.cpp @@ -0,0 +1,5 @@ + +void foo(void) +{ + +}