Browse Source

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.
Brad King 12 years ago
parent
commit
e57b6a2521

+ 2 - 0
Source/cmCommands.cxx

@@ -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);

+ 8 - 0
Source/cmDocumentGeneratorExpressions.h

@@ -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"            \

+ 5 - 3
Source/cmExtraSublimeTextGenerator.cxx

@@ -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());
       }
     }
 

+ 8 - 0
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 );
+}

+ 1 - 0
Source/cmGeneratorExpressionDAGChecker.h

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

+ 110 - 1
Source/cmGeneratorExpressionEvaluator.cxx

@@ -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")

+ 4 - 2
Source/cmGlobalXCodeGenerator.cxx

@@ -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))

+ 20 - 0
Source/cmLocalGenerator.cxx

@@ -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,

+ 3 - 0
Source/cmLocalGenerator.h

@@ -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);

+ 30 - 11
Source/cmLocalVisualStudio6Generator.cxx

@@ -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());

+ 3 - 1
Source/cmLocalVisualStudio7Generator.cxx

@@ -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;

+ 19 - 13
Source/cmMakefileTargetGenerator.cxx

@@ -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 =

+ 6 - 4
Source/cmNinjaTargetGenerator.cxx

@@ -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());
       }
     }
 

+ 265 - 27
Source/cmTarget.cxx

@@ -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;
 }
 

+ 6 - 0
Source/cmTarget.h

@@ -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;
 

+ 62 - 0
Source/cmTargetCompileOptionsCommand.cxx

@@ -0,0 +1,62 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2013 Stephen Kelly <[email protected]>
+
+  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);
+}

+ 90 - 0
Source/cmTargetCompileOptionsCommand.h

@@ -0,0 +1,90 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2013 Stephen Kelly <[email protected]>
+
+  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

+ 5 - 1
Source/cmVisualStudio10TargetGenerator.cxx

@@ -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;

+ 35 - 0
Tests/CMakeCommands/target_compile_options/CMakeLists.txt

@@ -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()

+ 18 - 0
Tests/CMakeCommands/target_compile_options/consumer.cpp

@@ -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; }

+ 18 - 0
Tests/CMakeCommands/target_compile_options/main.cpp

@@ -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; }

+ 2 - 0
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")
@@ -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"

+ 16 - 0
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 "$<$<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()

+ 11 - 0
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;
+}

+ 5 - 0
Tests/CompileOptions/other.cpp

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