浏览代码

cmTarget: Add COMPILE_FEATURES target property.

Use the contents of it to upgrade the CXX_STANDARD target property,
if appropriate.  This will have the effect of adding the -std=c++11
compile flag or other language specification on GNU when that is
needed for the feature.
Stephen Kelly 11 年之前
父节点
当前提交
03355d6b5b

+ 1 - 0
Help/manual/cmake-properties.7.rst

@@ -98,6 +98,7 @@ Properties on Targets
    /prop_tgt/COMPATIBLE_INTERFACE_STRING
    /prop_tgt/COMPILE_DEFINITIONS_CONFIG
    /prop_tgt/COMPILE_DEFINITIONS
+   /prop_tgt/COMPILE_FEATURES
    /prop_tgt/COMPILE_FLAGS
    /prop_tgt/COMPILE_OPTIONS
    /prop_tgt/COMPILE_PDB_NAME

+ 7 - 0
Help/prop_tgt/COMPILE_FEATURES.rst

@@ -0,0 +1,7 @@
+COMPILE_FEATURES
+----------------
+
+Compiler features enabled for this target.
+
+The list of features in this property are a subset of the features listed
+in the :variable:`CMAKE_CXX_COMPILE_FEATURES` variable.

+ 6 - 0
Help/release/dev/compile-language-features.rst

@@ -6,3 +6,9 @@ target-language-features
   compile options such as ``-std=c++11`` or ``-std=gnu++11``. The
   :variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_EXTENSIONS`
   variables may be set to initialize the target properties.
+
+* New :prop_tgt:`COMPILE_FEATURES` target property may contain a list
+  of features required to compile a target.  CMake uses this
+  information to ensure that the compiler in use is capable of building
+  the target, and to add any necessary compile flags to support language
+  features.

+ 13 - 0
Source/cmLocalGenerator.cxx

@@ -1459,6 +1459,19 @@ void cmLocalGenerator::AddCompileOptions(
       this->AppendFlagEscape(flags, *i);
       }
     }
+  if (const char* featureProp = target->GetProperty("COMPILE_FEATURES"))
+    {
+    std::vector<std::string> features;
+    cmSystemTools::ExpandListArgument(featureProp, features);
+    for(std::vector<std::string>::const_iterator it = features.begin();
+        it != features.end(); ++it)
+      {
+      if (!this->Makefile->AddRequiredTargetFeature(target, *it))
+        {
+        return;
+        }
+      }
+    }
   this->AddCompilerRequirementFlag(flags, target, lang);
 }
 

+ 113 - 0
Source/cmMakefile.cxx

@@ -41,6 +41,8 @@
 #include <ctype.h> // for isspace
 #include <assert.h>
 
+#define FOR_EACH_CXX_FEATURE(F)
+
 class cmMakefile::Internals
 {
 public:
@@ -4494,3 +4496,114 @@ void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm)
     pm[pid] = this->GetPolicyStatus(pid);
     }
 }
+
+#define FEATURE_STRING(F) , #F
+
+static const char * const CXX_FEATURES[] = {
+  0
+  FOR_EACH_CXX_FEATURE(FEATURE_STRING)
+};
+
+static const char * const CXX_STANDARDS[] = {
+    "98"
+  , "11"
+};
+
+//----------------------------------------------------------------------------
+bool cmMakefile::
+AddRequiredTargetFeature(cmTarget *target, const std::string& feature,
+                         std::string *error) const
+{
+  bool isCxxFeature = std::find_if(cmArrayBegin(CXX_FEATURES) + 1,
+              cmArrayEnd(CXX_FEATURES), cmStrCmp(feature))
+              != cmArrayEnd(CXX_FEATURES);
+  if (!isCxxFeature)
+    {
+    cmOStringStream e;
+    e << "specified unknown feature \"" << feature << "\" specified for "
+      "target \"" << target->GetName() << "\".";
+    if (error)
+      {
+      *error = e.str();
+      }
+    else
+      {
+      this->IssueMessage(cmake::FATAL_ERROR, e.str());
+      }
+    return false;
+    }
+
+  std::string lang = "CXX";
+
+  const char* featuresKnown =
+    this->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES");
+
+  if (!featuresKnown || !*featuresKnown)
+    {
+    // We know of no features for the compiler at all.
+    return true;
+    }
+
+  std::vector<std::string> availableFeatures;
+  cmSystemTools::ExpandListArgument(featuresKnown, availableFeatures);
+  if (std::find(availableFeatures.begin(),
+                availableFeatures.end(),
+                feature) == availableFeatures.end())
+    {
+    cmOStringStream e;
+    e << "The compiler feature \"" << feature
+      << "\" is not known to compiler\n\""
+      << this->GetDefinition("CMAKE_" + lang + "_COMPILER_ID")
+      << "\"\nversion "
+      << this->GetDefinition("CMAKE_" + lang + "_COMPILER_VERSION") << ".";
+    this->IssueMessage(cmake::FATAL_ERROR, e.str());
+    return false;
+    }
+
+  target->AppendProperty("COMPILE_FEATURES", feature.c_str());
+
+  bool needCxx11 = false;
+
+  if (const char *propCxx11 =
+          this->GetDefinition("CMAKE_CXX11_COMPILE_FEATURES"))
+    {
+    std::vector<std::string> props;
+    cmSystemTools::ExpandListArgument(propCxx11, props);
+    needCxx11 = std::find(props.begin(), props.end(), feature) != props.end();
+    }
+
+  const char *existingCxxStandard = target->GetProperty("CXX_STANDARD");
+  if (existingCxxStandard)
+    {
+    if (std::find_if(cmArrayBegin(CXX_STANDARDS), cmArrayEnd(CXX_STANDARDS),
+                  cmStrCmp(existingCxxStandard)) == cmArrayEnd(CXX_STANDARDS))
+      {
+      cmOStringStream e;
+      e << "The CXX_STANDARD property on target \"" << target->GetName()
+        << "\" contained an invalid value: \"" << existingCxxStandard << "\".";
+      this->IssueMessage(cmake::FATAL_ERROR, e.str());
+      return false;
+      }
+    }
+  const char * const *existingCxxIt = existingCxxStandard
+                                    ? std::find_if(cmArrayBegin(CXX_STANDARDS),
+                                      cmArrayEnd(CXX_STANDARDS),
+                                      cmStrCmp(existingCxxStandard))
+                                    : cmArrayEnd(CXX_STANDARDS);
+
+  bool setCxx11 = needCxx11 && !existingCxxStandard;
+
+  if (needCxx11 && existingCxxStandard && existingCxxIt <
+                                    std::find_if(cmArrayBegin(CXX_STANDARDS),
+                                      cmArrayEnd(CXX_STANDARDS),
+                                      cmStrCmp("11")))
+    {
+    setCxx11 = true;
+    }
+
+  if (setCxx11)
+    {
+    target->SetProperty("CXX_STANDARD", "11");
+    }
+  return true;
+}

+ 4 - 0
Source/cmMakefile.h

@@ -885,6 +885,10 @@ public:
 
   bool PolicyOptionalWarningEnabled(std::string const& var);
 
+  bool AddRequiredTargetFeature(cmTarget *target,
+                                const std::string& feature,
+                                std::string *error = 0) const;
+
 protected:
   // add link libraries and directories to the target
   void AddGlobalLinkInformation(const std::string& name, cmTarget& target);

+ 40 - 0
Source/cmTarget.cxx

@@ -153,6 +153,7 @@ public:
   };
   std::vector<TargetPropertyEntry*> IncludeDirectoriesEntries;
   std::vector<TargetPropertyEntry*> CompileOptionsEntries;
+  std::vector<TargetPropertyEntry*> CompileFeaturesEntries;
   std::vector<TargetPropertyEntry*> CompileDefinitionsEntries;
   std::vector<TargetPropertyEntry*> SourceEntries;
   std::vector<cmValueWithOrigin> LinkImplementationPropertyEntries;
@@ -1722,6 +1723,17 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
                           new cmTargetInternals::TargetPropertyEntry(cge));
     return;
     }
+  if(prop == "COMPILE_FEATURES")
+    {
+    cmListFileBacktrace lfbt;
+    this->Makefile->GetBacktrace(lfbt);
+    cmGeneratorExpression ge(lfbt);
+    deleteAndClear(this->Internal->CompileFeaturesEntries);
+    cmsys::auto_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(value);
+    this->Internal->CompileFeaturesEntries.push_back(
+                          new cmTargetInternals::TargetPropertyEntry(cge));
+    return;
+    }
   if(prop == "COMPILE_DEFINITIONS")
     {
     cmListFileBacktrace lfbt;
@@ -1812,6 +1824,15 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value,
               new cmTargetInternals::TargetPropertyEntry(ge.Parse(value)));
     return;
     }
+  if(prop == "COMPILE_FEATURES")
+    {
+    cmListFileBacktrace lfbt;
+    this->Makefile->GetBacktrace(lfbt);
+    cmGeneratorExpression ge(lfbt);
+    this->Internal->CompileFeaturesEntries.push_back(
+              new cmTargetInternals::TargetPropertyEntry(ge.Parse(value)));
+    return;
+    }
   if(prop == "COMPILE_DEFINITIONS")
     {
     cmListFileBacktrace lfbt;
@@ -3109,6 +3130,24 @@ const char *cmTarget::GetProperty(const std::string& prop,
       }
     return output.c_str();
     }
+  if(prop == "COMPILE_FEATURES")
+    {
+    static std::string output;
+    output = "";
+    std::string sep;
+    typedef cmTargetInternals::TargetPropertyEntry
+                                TargetPropertyEntry;
+    for (std::vector<TargetPropertyEntry*>::const_iterator
+        it = this->Internal->CompileFeaturesEntries.begin(),
+        end = this->Internal->CompileFeaturesEntries.end();
+        it != end; ++it)
+      {
+      output += sep;
+      output += (*it)->ge->GetInput();
+      sep = ";";
+      }
+    return output.c_str();
+    }
   if(prop == "COMPILE_DEFINITIONS")
     {
     static std::string output;
@@ -6886,6 +6925,7 @@ cmTargetInternalPointer::~cmTargetInternalPointer()
 {
   deleteAndClear(this->Pointer->IncludeDirectoriesEntries);
   deleteAndClear(this->Pointer->CompileOptionsEntries);
+  deleteAndClear(this->Pointer->CompileFeaturesEntries);
   deleteAndClear(this->Pointer->CompileDefinitionsEntries);
   deleteAndClear(this->Pointer->SourceEntries);
   delete this->Pointer;