[cmake-commits] king committed cmGlobalXCodeGenerator.cxx 1.175 1.176 cmGlobalXCodeGenerator.h 1.51 1.52 cmLocalGenerator.cxx 1.250 1.251 cmLocalGenerator.h 1.94 1.95 cmLocalUnixMakefileGenerator3.cxx 1.231 1.232 cmLocalVisualStudio6Generator.cxx 1.133 1.134 cmLocalVisualStudio6Generator.h 1.21 1.22 cmLocalVisualStudio7Generator.cxx 1.210 1.211 cmLocalVisualStudio7Generator.h 1.46 1.47 cmMakefileTargetGenerator.cxx 1.82 1.83 cmSourceFile.cxx 1.40 1.41 cmTarget.cxx 1.172 1.173

cmake-commits at cmake.org cmake-commits at cmake.org
Mon Jan 14 09:21:00 EST 2008


Update of /cvsroot/CMake/CMake/Source
In directory public:/mounts/ram/cvs-serv16504/Source

Modified Files:
	cmGlobalXCodeGenerator.cxx cmGlobalXCodeGenerator.h 
	cmLocalGenerator.cxx cmLocalGenerator.h 
	cmLocalUnixMakefileGenerator3.cxx 
	cmLocalVisualStudio6Generator.cxx 
	cmLocalVisualStudio6Generator.h 
	cmLocalVisualStudio7Generator.cxx 
	cmLocalVisualStudio7Generator.h cmMakefileTargetGenerator.cxx 
	cmSourceFile.cxx cmTarget.cxx 
Log Message:
ENH: Create COMPILE_DEFINITIONS property for targets and source files.  Create <config>_COMPILE_DEFINITIONS property as per-configuration version.  Add Preprocess test to test the feature.  Document limitations on Xcode and VS6 generators.


Index: cmLocalVisualStudio7Generator.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmLocalVisualStudio7Generator.cxx,v
retrieving revision 1.210
retrieving revision 1.211
diff -u -d -r1.210 -r1.211
--- cmLocalVisualStudio7Generator.cxx	13 Jan 2008 21:36:20 -0000	1.210
+++ cmLocalVisualStudio7Generator.cxx	14 Jan 2008 14:20:57 -0000	1.211
@@ -384,7 +384,8 @@
     Compiler,
     Linker
   };
-  cmLocalVisualStudio7GeneratorOptions(Tool tool,
+  cmLocalVisualStudio7GeneratorOptions(cmLocalVisualStudio7Generator* lg,
+                                       Tool tool,
                                        cmVS7FlagTable const* extraTable = 0);
 
   // Store options from command line flags.
@@ -398,6 +399,7 @@
 
   // Store definitions and flags.
   void AddDefine(const std::string& define);
+  void AddDefines(const char* defines);
   void AddFlag(const char* flag, const char* value);
 
   // Check for specific options.
@@ -413,6 +415,8 @@
                                const char* suffix);
 
 private:
+  cmLocalVisualStudio7Generator* LocalGenerator;
+
   // create a map of xml tags to the values they should have in the output
   // for example, "BufferSecurityCheck" = "TRUE"
   // first fill this table with the values for the configuration
@@ -423,7 +427,7 @@
   std::map<cmStdString, cmStdString> FlagMap;
 
   // Preprocessor definitions.
-  std::vector<cmStdString> Defines;
+  std::vector<std::string> Defines;
 
   // Unrecognized flags that get no special handling.
   cmStdString FlagString;
@@ -516,14 +520,20 @@
     flags += targetFlags;
     }
 
+  std::string configUpper = cmSystemTools::UpperCase(configName);
+  std::string defPropName = configUpper;
+  defPropName += "_COMPILE_DEFINITIONS";
+
   // Get preprocessor definitions for this directory.
   std::string defineFlags = this->Makefile->GetDefineFlags();
 
   // Construct a set of build options for this target.
-  Options targetOptions(Options::Compiler, this->ExtraFlagTable);
+  Options targetOptions(this, Options::Compiler, this->ExtraFlagTable);
   targetOptions.FixExceptionHandlingDefault();
   targetOptions.Parse(flags.c_str());
   targetOptions.Parse(defineFlags.c_str());
+  targetOptions.AddDefines(target.GetProperty("COMPILE_DEFINITIONS"));
+  targetOptions.AddDefines(target.GetProperty(defPropName.c_str()));
   targetOptions.SetVerboseMakefile(
     this->Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE"));
 
@@ -703,7 +713,7 @@
     extraLinkOptions += " ";
     extraLinkOptions += targetLinkFlags;
     }
-  Options linkOptions(Options::Linker);
+  Options linkOptions(this, Options::Linker);
   linkOptions.Parse(extraLinkOptions.c_str());
   switch(target.GetType())
     {
@@ -1027,6 +1037,135 @@
   this->WriteVCProjFooter(fout);
 }
 
+struct cmLVS7GFileConfig
+{
+  std::string ObjectName;
+  std::string CompileFlags;
+  std::string CompileDefs;
+  std::string CompileDefsConfig;
+  std::string AdditionalDeps;
+  bool ExcludedFromBuild;
+};
+
+class cmLocalVisualStudio7GeneratorFCInfo
+{
+public:
+  cmLocalVisualStudio7GeneratorFCInfo(cmLocalVisualStudio7Generator* lg,
+                                      cmTarget& target,
+                                      cmSourceFile const& sf,
+                                      std::vector<std::string>* configs,
+                                      std::string::size_type dir_len);
+  std::map<cmStdString, cmLVS7GFileConfig> FileConfigMap;
+};
+
+cmLocalVisualStudio7GeneratorFCInfo
+::cmLocalVisualStudio7GeneratorFCInfo(cmLocalVisualStudio7Generator* lg,
+                                      cmTarget& target,
+                                      cmSourceFile const& sf,
+                                      std::vector<std::string>* configs,
+                                      std::string::size_type dir_len)
+{
+  std::string objectName;
+  if(lg->NeedObjectName.find(&sf) != lg->NeedObjectName.end())
+    {
+    objectName = lg->GetObjectFileNameWithoutTarget(sf, dir_len);
+    }
+
+  // Compute per-source, per-config information.
+  for(std::vector<std::string>::iterator i = configs->begin();
+      i != configs->end(); ++i)
+    {
+    std::string configUpper = cmSystemTools::UpperCase(*i);
+    cmLVS7GFileConfig fc;
+    bool needfc = false;
+    if(!objectName.empty())
+      {
+      fc.ObjectName = objectName;
+      needfc = true;
+      }
+    if(const char* cflags = sf.GetProperty("COMPILE_FLAGS"))
+      {
+      fc.CompileFlags = cflags;
+      needfc = true;
+      }
+    if(const char* cdefs = sf.GetProperty("COMPILE_DEFINITIONS"))
+      {
+      fc.CompileDefs = cdefs;
+      needfc = true;
+      }
+    std::string defPropName = configUpper;
+    defPropName += "_COMPILE_DEFINITIONS";
+    if(const char* ccdefs = sf.GetProperty(defPropName.c_str()))
+      {
+      fc.CompileDefsConfig = ccdefs;
+      needfc = true;
+      }
+
+    // Check for extra object-file dependencies.
+    if(const char* deps = sf.GetProperty("OBJECT_DEPENDS"))
+      {
+      std::vector<std::string> depends;
+      cmSystemTools::ExpandListArgument(deps, depends);
+      const char* sep = "";
+      for(std::vector<std::string>::iterator j = depends.begin();
+          j != depends.end(); ++j)
+        {
+        fc.AdditionalDeps += sep;
+        fc.AdditionalDeps += lg->ConvertToXMLOutputPath(j->c_str());
+        sep = ";";
+        needfc = true;
+        }
+      }
+
+    const char* lang =
+      lg->GlobalGenerator->GetLanguageFromExtension
+      (sf.GetExtension().c_str());
+    const char* sourceLang = lg->GetSourceFileLanguage(sf);
+    const char* linkLanguage = target.GetLinkerLanguage
+      (lg->GetGlobalGenerator());
+    bool needForceLang = false;
+    // source file does not match its extension language
+    if(lang && sourceLang && strcmp(lang, sourceLang) != 0)
+      {
+      needForceLang = true;
+      lang = sourceLang;
+      }
+    // If lang is set, the compiler will generate code automatically.
+    // If HEADER_FILE_ONLY is set, we must suppress this generation in
+    // the project file
+    fc.ExcludedFromBuild =
+      (lang && sf.GetPropertyAsBool("HEADER_FILE_ONLY"));
+    if(fc.ExcludedFromBuild)
+      {
+      needfc = true;
+      }
+
+    // if the source file does not match the linker language
+    // then force c or c++
+    if(needForceLang || (linkLanguage && lang
+                         && strcmp(lang, linkLanguage) != 0))
+      {
+      if(strcmp(lang, "CXX") == 0)
+        {
+        // force a C++ file type
+        fc.CompileFlags += " /TP ";
+        needfc = true;
+        }
+      else if(strcmp(lang, "C") == 0)
+        {
+        // force to c
+        fc.CompileFlags += " /TC ";
+        needfc = true;
+        }
+      }
+
+    if(needfc)
+      {
+      this->FileConfigMap[*i] = fc;
+      }
+    }
+}
+
 void cmLocalVisualStudio7Generator
 ::WriteGroup(const cmSourceGroup *sg, cmTarget target, 
              std::ostream &fout, const char *libName, 
@@ -1075,76 +1214,8 @@
         sourceFiles.begin(); sf != sourceFiles.end(); ++sf)
     {
     std::string source = (*sf)->GetFullPath();
-    const cmCustomCommand *command = (*sf)->GetCustomCommand();
-    std::string compileFlags;
-    std::string additionalDeps;
-    if(this->NeedObjectName.find(*sf) != this->NeedObjectName.end())
-      {
-      objectName = this->GetObjectFileNameWithoutTarget(*(*sf), dir_len);
-      }
-    else
-      {
-      objectName = "";
-      }
-    // Add per-source flags.
-    const char* cflags = (*sf)->GetProperty("COMPILE_FLAGS");
-    if(cflags)
-      {
-      compileFlags += " ";
-      compileFlags += cflags;
-      }
-    const char* lang =
-      this->GlobalGenerator->GetLanguageFromExtension
-      ((*sf)->GetExtension().c_str());
-    const char* sourceLang = this->GetSourceFileLanguage(*(*sf));
-    const char* linkLanguage = target.GetLinkerLanguage
-      (this->GetGlobalGenerator());
-    bool needForceLang = false;
-    // source file does not match its extension language
-    if(lang && sourceLang && strcmp(lang, sourceLang) != 0)
-      {
-      needForceLang = true;
-      lang = sourceLang;
-      }
-    // If lang is set, the compiler will generate code automatically.
-    // If HEADER_FILE_ONLY is set, we must suppress this generation in
-    // the project file
-    bool excludedFromBuild = 
-      (lang && (*sf)->GetPropertyAsBool("HEADER_FILE_ONLY")); 
+    FCInfo fcinfo(this, target, *(*sf), configs, dir_len);
 
-    // if the source file does not match the linker language
-    // then force c or c++
-    if(needForceLang || (linkLanguage && lang 
-                         && strcmp(lang, linkLanguage) != 0))
-      {
-      if(strcmp(lang, "CXX") == 0)
-        {
-        // force a C++ file type
-        compileFlags += " /TP ";
-        }
-      else if(strcmp(lang, "C") == 0)
-        {
-        // force to c 
-        compileFlags += " /TC ";
-        }
-      }
-    // Check for extra object-file dependencies.
-    const char* deps = (*sf)->GetProperty("OBJECT_DEPENDS");
-    if(deps)
-      {
-      std::vector<std::string> depends;
-      cmSystemTools::ExpandListArgument(deps, depends);
-      if(!depends.empty())
-        {
-        std::vector<std::string>::iterator i = depends.begin();
-        additionalDeps = this->ConvertToXMLOutputPath(i->c_str());
-        for(++i;i != depends.end(); ++i)
-          {
-          additionalDeps += ";";
-          additionalDeps += this->ConvertToXMLOutputPath(i->c_str());
-          }
-        }
-      }
     if (source != libName || target.GetType() == cmTarget::UTILITY ||
       target.GetType() == cmTarget::GLOBAL_TARGET )
       {
@@ -1153,13 +1224,11 @@
       // Tell MS-Dev what the source is.  If the compiler knows how to
       // build it, then it will.
       fout << "\t\t\t\tRelativePath=\"" << d << "\">\n";
-      if (command)
+      if(cmCustomCommand const* command = (*sf)->GetCustomCommand())
         {
-        const char* flags = compileFlags.size() ? compileFlags.c_str(): 0;
-        this->WriteCustomRule(fout, source.c_str(), *command, flags);
+        this->WriteCustomRule(fout, source.c_str(), *command, fcinfo);
         }
-      else if(compileFlags.size() || additionalDeps.length() 
-              || objectName.size() || excludedFromBuild)
+      else if(!fcinfo.FileConfigMap.empty())
         {
         const char* aCompilerTool = "VCCLCompilerTool";
         std::string ext = (*sf)->GetExtension();
@@ -1176,37 +1245,44 @@
           {
           aCompilerTool = "VCCustomBuildTool";
           }
-        for(std::vector<std::string>::iterator i = configs->begin();
-            i != configs->end(); ++i)
+        for(std::map<cmStdString, cmLVS7GFileConfig>::const_iterator
+              fci = fcinfo.FileConfigMap.begin();
+            fci != fcinfo.FileConfigMap.end(); ++fci)
           {
+          cmLVS7GFileConfig const& fc = fci->second;
           fout << "\t\t\t\t<FileConfiguration\n"
-               << "\t\t\t\t\tName=\""  << *i 
+               << "\t\t\t\t\tName=\""  << fci->first
                << "|" << this->PlatformName << "\"";
-          if(excludedFromBuild)
+          if(fc.ExcludedFromBuild)
             {
             fout << " ExcludedFromBuild=\"true\"";
             }
           fout << ">\n";
           fout << "\t\t\t\t\t<Tool\n"
                << "\t\t\t\t\tName=\"" << aCompilerTool << "\"\n";
-          if(!compileFlags.empty())
+          if(!fc.CompileFlags.empty() ||
+             !fc.CompileDefs.empty() ||
+             !fc.CompileDefsConfig.empty())
             {
-            Options fileOptions(Options::Compiler, this->ExtraFlagTable);
-            fileOptions.Parse(compileFlags.c_str());
+            Options fileOptions(this, Options::Compiler,
+                                this->ExtraFlagTable);
+            fileOptions.Parse(fc.CompileFlags.c_str());
+            fileOptions.AddDefines(fc.CompileDefs.c_str());
+            fileOptions.AddDefines(fc.CompileDefsConfig.c_str());
             fileOptions.OutputAdditionalOptions(fout, "\t\t\t\t\t", "\n");
             fileOptions.OutputFlagMap(fout, "\t\t\t\t\t");
             fileOptions.OutputPreprocessorDefinitions(fout,
                                                       "\t\t\t\t\t", "\n");
             }
-          if(additionalDeps.length())
+          if(!fc.AdditionalDeps.empty())
             {
             fout << "\t\t\t\t\tAdditionalDependencies=\""
-                 << additionalDeps.c_str() << "\"\n";
+                 << fc.AdditionalDeps.c_str() << "\"\n";
             }
-          if(objectName.size())
+          if(!fc.ObjectName.empty())
             {
             fout << "\t\t\t\t\tObjectFile=\"$(IntDir)/"
-                 << objectName.c_str() << "\"\n";
+                 << fc.ObjectName.c_str() << "\"\n";
             }
           fout << "\t\t\t\t\t/>\n"
                << "\t\t\t\t</FileConfiguration>\n";
@@ -1234,7 +1310,7 @@
 WriteCustomRule(std::ostream& fout,
                 const char* source,
                 const cmCustomCommand& command,
-                const char* compileFlags)
+                FCInfo& fcinfo)
 {
   std::string comment = this->ConstructComment(command);
   
@@ -1246,14 +1322,15 @@
 
   for(i = configs->begin(); i != configs->end(); ++i)
     {
+    cmLVS7GFileConfig const& fc = fcinfo.FileConfigMap[*i];
     fout << "\t\t\t\t<FileConfiguration\n";
     fout << "\t\t\t\t\tName=\"" << *i << "|" << this->PlatformName << "\">\n";
-    if(compileFlags)
+    if(!fc.CompileFlags.empty())
       {
       fout << "\t\t\t\t\t<Tool\n"
            << "\t\t\t\t\tName=\"VCCLCompilerTool\"\n"
            << "\t\t\t\t\tAdditionalOptions=\""
-           << this->EscapeForXML(compileFlags) << "\"/>\n";
+           << this->EscapeForXML(fc.CompileFlags.c_str()) << "\"/>\n";
       }
 
     std::string script = 
@@ -1659,9 +1736,10 @@
 
 //----------------------------------------------------------------------------
 cmLocalVisualStudio7GeneratorOptions
-::cmLocalVisualStudio7GeneratorOptions(Tool tool,
+::cmLocalVisualStudio7GeneratorOptions(cmLocalVisualStudio7Generator* lg,
+                                       Tool tool,
                                        cmVS7FlagTable const* extraTable):
-  CurrentTool(tool),
+  LocalGenerator(lg), CurrentTool(tool),
   DoingDefine(false), FlagTable(0), ExtraFlagTable(extraTable)
 {
   // Choose the flag table for the requested tool.
@@ -1707,6 +1785,16 @@
 }
 
 //----------------------------------------------------------------------------
+void cmLocalVisualStudio7GeneratorOptions::AddDefines(const char* defines)
+{
+  if(defines)
+    {
+    // Expand the list of definitions.
+    cmSystemTools::ExpandListArgument(defines, this->Defines);
+    }
+}
+
+//----------------------------------------------------------------------------
 void cmLocalVisualStudio7GeneratorOptions::AddFlag(const char* flag,
                                                    const char* value)
 {
@@ -1717,7 +1805,7 @@
 bool cmLocalVisualStudio7GeneratorOptions::UsingUnicode()
 {
   // Look for the a _UNICODE definition.
-  for(std::vector<cmStdString>::const_iterator di = this->Defines.begin();
+  for(std::vector<std::string>::const_iterator di = this->Defines.begin();
       di != this->Defines.end(); ++di)
     {
     if(*di == "_UNICODE")
@@ -1886,29 +1974,18 @@
 
   fout << prefix <<  "PreprocessorDefinitions=\"";
   const char* comma = "";
-  for(std::vector<cmStdString>::const_iterator di = this->Defines.begin();
+  for(std::vector<std::string>::const_iterator di = this->Defines.begin();
       di != this->Defines.end(); ++di)
     {
-    // Double-quotes in the value of the definition must be escaped
-    // with a backslash.
-    std::string define = di->c_str();
-    cmSystemTools::ReplaceString(define, "\"", "\\\"");
+    // Escape the definition for the compiler.
+    std::string define =
+      this->LocalGenerator->EscapeForShell(di->c_str(), true);
 
     // Escape this flag for the IDE.
     define = cmLocalVisualStudio7GeneratorEscapeForXML(define.c_str());
 
-    // Write this flag.  Quote it if the definition is not
-    // alphanumeric.
-    if(define.find_first_not_of(
-         "-_abcdefghigklmnopqrstuvwxyz1234567890ABCDEFGHIGKLMNOPQRSTUVWXYZ")
-       != define.npos)
-      {
-      fout << comma << "&quot;" << define << "&quot;";
-      }
-    else
-      {
-      fout << comma << define;
-      }
+    // Store the flag in the project file.
+    fout << comma << define;
     comma = ",";
     }
   fout << "\"" << suffix;

Index: cmLocalVisualStudio6Generator.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmLocalVisualStudio6Generator.h,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -d -r1.21 -r1.22
--- cmLocalVisualStudio6Generator.h	10 Aug 2007 17:02:59 -0000	1.21
+++ cmLocalVisualStudio6Generator.h	14 Jan 2008 14:20:57 -0000	1.22
@@ -103,6 +103,9 @@
   std::vector<std::string> Configurations;
 
   std::string GetConfigName(std::string const& configuration) const;
+
+  // Special definition check for VS6.
+  virtual bool CheckDefinition(std::string const& define) const;
 };
 
 #endif

Index: cmLocalVisualStudio7Generator.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmLocalVisualStudio7Generator.h,v
retrieving revision 1.46
retrieving revision 1.47
diff -u -d -r1.46 -r1.47
--- cmLocalVisualStudio7Generator.h	10 Dec 2007 00:58:33 -0000	1.46
+++ cmLocalVisualStudio7Generator.h	14 Jan 2008 14:20:57 -0000	1.47
@@ -26,6 +26,7 @@
 struct cmVS7FlagTable;
 
 class cmLocalVisualStudio7GeneratorOptions;
+class cmLocalVisualStudio7GeneratorFCInfo;
 
 /** \class cmLocalVisualStudio7Generator
  * \brief Write Visual Studio .NET project files.
@@ -68,6 +69,7 @@
     { this->ExtraFlagTable = table; }
 private:
   typedef cmLocalVisualStudio7GeneratorOptions Options;
+  typedef cmLocalVisualStudio7GeneratorFCInfo FCInfo;
   void ReadAndStoreExternalGUID(const char* name,
                                 const char* path);
   std::string GetBuildTypeLinkerFlags(std::string rootLinkerFlags,
@@ -109,7 +111,7 @@
   void WriteCustomRule(std::ostream& fout,
                        const char* source,
                        const cmCustomCommand& command,
-                       const char* extraFlags);
+                       FCInfo& fcinfo);
   void WriteTargetVersionAttribute(std::ostream& fout, cmTarget& target);
 
   void WriteGroup(const cmSourceGroup *sg, 
@@ -117,6 +119,8 @@
                   const char *libName, std::vector<std::string> *configs);
   virtual std::string GetTargetDirectory(cmTarget const&) const;
 
+  friend class cmLocalVisualStudio7GeneratorFCInfo;
+
   cmVS7FlagTable const* ExtraFlagTable;
   std::string ModuleDefinitionFile;
   int Version;

Index: cmLocalGenerator.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmLocalGenerator.cxx,v
retrieving revision 1.250
retrieving revision 1.251
diff -u -d -r1.250 -r1.251
--- cmLocalGenerator.cxx	13 Jan 2008 21:59:09 -0000	1.250
+++ cmLocalGenerator.cxx	14 Jan 2008 14:20:57 -0000	1.251
@@ -856,6 +856,10 @@
       return replaceValues.ObjectsQuoted;
       }
     }
+  if(replaceValues.Defines && variable == "DEFINES")
+    {
+    return replaceValues.Defines;
+    }
   if(replaceValues.TargetPDB )
     {
     if(variable == "TARGET_PDB")
@@ -2214,6 +2218,77 @@
 }
 
 //----------------------------------------------------------------------------
+void cmLocalGenerator::AppendDefines(std::string& defines,
+                                     const char* defines_list)
+{
+  // Short-circuit if there are no definitions.
+  if(!defines_list)
+    {
+    return;
+    }
+
+  // Expand the list of definitions.
+  std::vector<std::string> defines_vec;
+  cmSystemTools::ExpandListArgument(defines_list, defines_vec);
+
+  // Short-circuit if there are no definitions.
+  if(defines_vec.empty())
+    {
+    return;
+    }
+
+  // Separate from previous definitions with a space.
+  if(!defines.empty())
+    {
+    defines += " ";
+    }
+
+  // Add each definition to the command line with appropriate escapes.
+  const char* dsep = "-D";
+  for(std::vector<std::string>::const_iterator di = defines_vec.begin();
+      di != defines_vec.end(); ++di)
+    {
+    // Skip unsupported definitions.
+    if(!this->CheckDefinition(*di))
+      {
+      continue;
+      }
+
+    // Append the -D
+    defines += dsep;
+
+    // Append the definition with proper escaping.
+    if(this->WatcomWMake)
+      {
+      // The Watcom compiler does its own command line parsing instead
+      // of using the windows shell rules.  Definitions are one of
+      //   -DNAME
+      //   -DNAME=<cpp-token>
+      //   -DNAME="c-string with spaces and other characters(?@#$)"
+      //
+      // Watcom will properly parse each of these cases from the
+      // command line without any escapes.  However we still have to
+      // get the '$' and '#' characters through WMake as '$$' and
+      // '$#'.
+      for(const char* c = di->c_str(); *c; ++c)
+        {
+        if(*c == '$' || *c == '#')
+          {
+          defines += '$';
+          }
+        defines += *c;
+        }
+      }
+    else
+      {
+      // Make the definition appear properly on the command line.
+      defines += this->EscapeForShell(di->c_str(), true);
+      }
+    dsep = " -D";
+    }
+}
+
+//----------------------------------------------------------------------------
 std::string
 cmLocalGenerator::ConstructComment(const cmCustomCommand& cc,
                                    const char* default_comment)
@@ -2963,3 +3038,45 @@
   return (actual_compat &&
           actual_compat <= CMake_VERSION_ENCODE(major, minor, patch));
 }
+
+//----------------------------------------------------------------------------
+bool cmLocalGenerator::CheckDefinition(std::string const& define) const
+{
+  // Many compilers do not support -DNAME(arg)=sdf so we disable it.
+  bool function_style = false;
+  for(const char* c = define.c_str(); *c && *c != '='; ++c)
+    {
+    if(*c == '(')
+      {
+      function_style = true;
+      break;
+      }
+    }
+  if(function_style)
+    {
+    cmOStringStream e;
+    e << "WARNING: Function-style preprocessor definitions may not be "
+      << "passed on the compiler command line because many compilers "
+      << "do not support it.\n"
+      << "CMake is dropping a preprocessor definition: " << define << "\n"
+      << "Consider defining the macro in a (configured) header file.\n";
+    cmSystemTools::Message(e.str().c_str());
+    return false;
+    }
+
+  // Many compilers do not support # in the value so we disable it.
+  if(define.find_first_of("#") != define.npos)
+    {
+    cmOStringStream e;
+    e << "WARNING: Peprocessor definitions containing '#' may not be "
+      << "passed on the compiler command line because many compilers "
+      << "do not support it.\n"
+      << "CMake is dropping a preprocessor definition: " << define << "\n"
+      << "Consider defining the macro in a (configured) header file.\n";
+    cmSystemTools::Message(e.str().c_str());
+    return false;
+    }
+
+  // Assume it is supported.
+  return true;
+}

Index: cmGlobalXCodeGenerator.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmGlobalXCodeGenerator.cxx,v
retrieving revision 1.175
retrieving revision 1.176
diff -u -d -r1.175 -r1.176
--- cmGlobalXCodeGenerator.cxx	10 Jan 2008 20:17:23 -0000	1.175
+++ cmGlobalXCodeGenerator.cxx	14 Jan 2008 14:20:57 -0000	1.176
@@ -453,6 +453,9 @@
   lg->AppendFlags(flags, sf->GetProperty("COMPILE_FLAGS"));
   cmSystemTools::ReplaceString(flags, "\"", "\\\"");
 
+  // Add per-source definitions.
+  this->AppendDefines(flags, sf->GetProperty("COMPILE_DEFINITIONS"), true);
+
   // Using a map and the full path guarantees that we will always get the same
   // fileRef object for any given full path.
   //
@@ -1260,12 +1263,6 @@
   bool shared = ((target.GetType() == cmTarget::SHARED_LIBRARY) ||
                  (target.GetType() == cmTarget::MODULE_LIBRARY));
 
-  // Add the export symbol definition for shared library objects.
-  if(const char* exportMacro = target.GetExportMacro())
-    {
-    defFlags += "-D";
-    defFlags += exportMacro;
-    }
   const char* lang = target.GetLinkerLanguage(this);
   std::string cflags;
   if(lang)
@@ -1291,12 +1288,28 @@
   cmSystemTools::ReplaceString(defFlags, "\"", "\\\"");
   cmSystemTools::ReplaceString(flags, "\"", "\\\"");
   cmSystemTools::ReplaceString(cflags, "\"", "\\\"");
+
+  // Add preprocessor definitions for this target and configuration.
+  std::string ppDefs;
   if(this->XcodeVersion > 15)
     {
-    buildSettings->AddAttribute
-      ("GCC_PREPROCESSOR_DEFINITIONS", 
-       this->CreateString("CMAKE_INTDIR=\\\\\"$(CONFIGURATION)\\\\\""));
+    this->AppendDefines(ppDefs, "CMAKE_INTDIR=\"$(CONFIGURATION)\"");
+    }
+  if(const char* exportMacro = target.GetExportMacro())
+    {
+    // Add the export symbol definition for shared library objects.
+    this->AppendDefines(ppDefs, exportMacro);
+    }
+  this->AppendDefines(ppDefs, target.GetProperty("COMPILE_DEFINITIONS"));
+  if(configName)
+    {
+    std::string defVarName = cmSystemTools::UpperCase(configName);
+    defVarName += "_COMPILE_DEFINITIONS";
+    this->AppendDefines(ppDefs, target.GetProperty(defVarName.c_str()));
     }
+  buildSettings->AddAttribute
+    ("GCC_PREPROCESSOR_DEFINITIONS", this->CreateString(ppDefs.c_str()));
+
   std::string extraLinkOptions;
   if(target.GetType() == cmTarget::EXECUTABLE)
     {
@@ -2887,3 +2900,64 @@
     }
   return default_flags;
 }
+
+//----------------------------------------------------------------------------
+void cmGlobalXCodeGenerator::AppendDefines(std::string& defs,
+                                           const char* defines_list,
+                                           bool dflag)
+{
+  // Skip this if there are no definitions.
+  if(!defines_list)
+    {
+    return;
+    }
+
+  // Expand the list of definitions.
+  std::vector<std::string> defines;
+  cmSystemTools::ExpandListArgument(defines_list, defines);
+
+  // GCC_PREPROCESSOR_DEFINITIONS is a space-separated list of definitions.
+  // We escape everything as follows:
+  //   - Place each definition in single quotes ''
+  //   - Escape a single quote as \\'
+  //   - Escape a backslash as \\\\
+  // Note that in the code below we need one more level of escapes for
+  // C string syntax in this source file.
+  const char* sep = defs.empty()? "" : " ";
+  for(std::vector<std::string>::const_iterator di = defines.begin();
+      di != defines.end(); ++di)
+    {
+    // Separate from previous definition.
+    defs += sep;
+    sep = " ";
+
+    // Open single quote.
+    defs += "'";
+
+    // Add -D flag if requested.
+    if(dflag)
+      {
+      defs += "-D";
+      }
+
+    // Escaped definition string.
+    for(const char* c = di->c_str(); *c; ++c)
+      {
+      if(*c == '\'')
+        {
+        defs += "\\\\'";
+        }
+      else if(*c == '\\')
+        {
+        defs += "\\\\\\\\";
+        }
+      else
+        {
+        defs += *c;
+        }
+      }
+
+    // Close single quote.
+    defs += "'";
+    }
+}

Index: cmLocalVisualStudio6Generator.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmLocalVisualStudio6Generator.cxx,v
retrieving revision 1.133
retrieving revision 1.134
diff -u -d -r1.133 -r1.134
--- cmLocalVisualStudio6Generator.cxx	20 Nov 2007 16:18:04 -0000	1.133
+++ cmLocalVisualStudio6Generator.cxx	14 Jan 2008 14:20:57 -0000	1.134
@@ -413,6 +413,29 @@
       compileFlags += cflags;
       }
 
+    // Add per-source and per-configuration preprocessor definitions.
+    std::map<cmStdString, cmStdString> cdmap;
+    this->AppendDefines(compileFlags,
+                        (*sf)->GetProperty("COMPILE_DEFINITIONS"));
+    if(const char* cdefs = (*sf)->GetProperty("DEBUG_COMPILE_DEFINITIONS"))
+      {
+      this->AppendDefines(cdmap["DEBUG"], cdefs);
+      }
+    if(const char* cdefs = (*sf)->GetProperty("RELEASE_COMPILE_DEFINITIONS"))
+      {
+      this->AppendDefines(cdmap["RELEASE"], cdefs);
+      }
+    if(const char* cdefs =
+       (*sf)->GetProperty("MINSIZEREL_COMPILE_DEFINITIONS"))
+      {
+      this->AppendDefines(cdmap["MINSIZEREL"], cdefs);
+      }
+    if(const char* cdefs =
+       (*sf)->GetProperty("RELWITHDEBINFO_COMPILE_DEFINITIONS"))
+      {
+      this->AppendDefines(cdmap["RELWITHDEBINFO"], cdefs);
+      }
+
     const char* lang = this->GetSourceFileLanguage(*(*sf));
     if(lang)
       {
@@ -464,12 +487,14 @@
         this->WriteCustomRule(fout, source.c_str(), *command, flags);
         }
       else if(!compileFlags.empty() || !objectNameDir.empty() ||
-              excludedFromBuild)
+              excludedFromBuild || !cdmap.empty())
         {
         for(std::vector<std::string>::iterator i
               = this->Configurations.begin(); 
             i != this->Configurations.end(); ++i)
           { 
+          // Strip the subdirectory name out of the configuration name.
+          std::string config = this->GetConfigName(*i);
           if (i == this->Configurations.begin())
             {
             fout << "!IF  \"$(CFG)\" == " << i->c_str() << std::endl;
@@ -486,11 +511,14 @@
             {
             fout << "\n# ADD CPP " << compileFlags << "\n\n";
             }
+          std::map<cmStdString, cmStdString>::iterator cdi =
+            cdmap.find(cmSystemTools::UpperCase(config));
+          if(cdi != cdmap.end() && !cdi->second.empty())
+            {
+            fout << "\n# ADD CPP " << cdi->second << "\n\n";
+            }
           if(!objectNameDir.empty())
             {
-            // Strip the subdirectory name out of the configuration name.
-            std::string config = this->GetConfigName(*i);
-
             // Setup an alternate object file directory.
             fout << "\n# PROP Intermediate_Dir \""
                  << config << "/" << objectNameDir << "\"\n\n";
@@ -1474,6 +1502,19 @@
       flags += targetFlags;
       }
 
+    // Add per-target and per-configuration preprocessor definitions.
+    this->AppendDefines(flags, target.GetProperty("COMPILE_DEFINITIONS"));
+    this->AppendDefines(flagsDebug,
+                        target.GetProperty("DEBUG_COMPILE_DEFINITIONS"));
+    this->AppendDefines(flagsRelease,
+                        target.GetProperty("RELEASE_COMPILE_DEFINITIONS"));
+    this->AppendDefines
+      (flagsMinSize,
+       target.GetProperty("MINSIZEREL_COMPILE_DEFINITIONS"));
+    this->AppendDefines
+      (flagsDebugRel,
+       target.GetProperty("RELWITHDEBINFO_COMPILE_DEFINITIONS"));
+
     // 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
@@ -1584,3 +1625,30 @@
   config = config.substr(0, config.size()-1);
   return config;
 }
+
+//----------------------------------------------------------------------------
+bool
+cmLocalVisualStudio6Generator
+::CheckDefinition(std::string const& define) const
+{
+  // Perform the standard check first.
+  if(!this->cmLocalGenerator::CheckDefinition(define))
+    {
+    return false;
+    }
+
+  // Now do the VS6-specific check.
+  if(define.find_first_of("=") != define.npos)
+    {
+    cmOStringStream e;
+    e << "WARNING: The VS6 IDE does not support preprocessor definitions "
+      << "with values.\n"
+      << "CMake is dropping a preprocessor definition: " << define << "\n"
+      << "Consider defining the macro in a (configured) header file.\n";
+    cmSystemTools::Message(e.str().c_str());
+    return false;
+    }
+
+  // Assume it is supported.
+  return true;
+}

Index: cmSourceFile.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmSourceFile.cxx,v
retrieving revision 1.40
retrieving revision 1.41
diff -u -d -r1.40 -r1.41
--- cmSourceFile.cxx	10 Jan 2008 03:09:19 -0000	1.40
+++ cmSourceFile.cxx	14 Jan 2008 14:20:57 -0000	1.41
@@ -340,7 +340,33 @@
     ("COMPILE_FLAGS", cmProperty::SOURCE_FILE, 
      "Additional flags to be added when compiling this source file.",
      "These flags will be added to the list of compile flags when "
-     "this source file.");
+     "this source file builds.  Use COMPILE_DEFINITIONS to pass additional "
+     "preprocessor definitions.");
+
+  cm->DefineProperty
+    ("COMPILE_DEFINITIONS", cmProperty::SOURCE_FILE,
+     "Preprocessor definitions for compiling this source file.",
+     "The COMPILE_DEFINITIONS property may be set to a list of preprocessor "
+     "definitions using the syntax VAR or VAR=value.  Function-style "
+     "definitions are not supported.  CMake will automatically escape "
+     "the value correctly for the native build system (note that CMake "
+     "language syntax may require escapes to specify some values).  "
+     "This property may be set on a per-configuration basis using the name "
+     "<CONFIG>_COMPILE_DEFINITIONS where <CONFIG> is an upper-case name "
+     "(ex. \"DEBUG_COMPILE_DEFINITIONS\").\n"
+     "CMake will automatically drop some definitions that "
+     "are not supported by the native build tool.  "
+     "The VS6 IDE does not support definitions with values "
+     "(but NMake does).  Xcode does not support per-configuration "
+     "definitions on source files.\n"
+     "Dislaimer: Most native build tools have poor support for escaping "
+     "certain values.  CMake has work-arounds for many cases but some "
+     "values may just not be possible to pass correctly.  If a value "
+     "does not seem to be escaped correctly, do not attempt to "
+     "work-around the problem by adding escape sequences to the value.  "
+     "Your work-around may break in a future version of CMake that "
+     "has improved escape support.  Instead consider defining the macro "
+     "in a (configured) header file.  Then report the limitation.");
 
   cm->DefineProperty
     ("EXTERNAL_OBJECT", cmProperty::SOURCE_FILE, 

Index: cmLocalUnixMakefileGenerator3.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmLocalUnixMakefileGenerator3.cxx,v
retrieving revision 1.231
retrieving revision 1.232
diff -u -d -r1.231 -r1.232
--- cmLocalUnixMakefileGenerator3.cxx	9 Jan 2008 15:30:10 -0000	1.231
+++ cmLocalUnixMakefileGenerator3.cxx	14 Jan 2008 14:20:57 -0000	1.232
@@ -338,17 +338,9 @@
     // Add a rule to drive the rule below.
     std::vector<std::string> depends;
     depends.push_back(output);
-    std::vector<std::string> commands;
-    cmGlobalUnixMakefileGenerator3* gg =
-      static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
-    std::string emptyCommand = gg->GetEmptyRuleHackCommand();
-    if(!emptyCommand.empty())
-      {
-      commands.push_back(emptyCommand);
-      }
-
+    std::vector<std::string> no_commands;
     this->WriteMakeRule(ruleFileStream, 0,
-                        outNoExt.c_str(), depends, commands, true, true);
+                        outNoExt.c_str(), depends, no_commands, true, true);
     inHelp = false;
     }
 

Index: cmMakefileTargetGenerator.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmMakefileTargetGenerator.cxx,v
retrieving revision 1.82
retrieving revision 1.83
diff -u -d -r1.82 -r1.83
--- cmMakefileTargetGenerator.cxx	7 Jan 2008 21:12:37 -0000	1.82
+++ cmMakefileTargetGenerator.cxx	14 Jan 2008 14:20:57 -0000	1.83
@@ -254,6 +254,7 @@
     {
     const char *lang = l->c_str();
     std::string flags;
+    std::string defines;
     bool shared = ((this->Target->GetType() == cmTarget::SHARED_LIBRARY) ||
                    (this->Target->GetType() == cmTarget::MODULE_LIBRARY));
 
@@ -264,6 +265,15 @@
       flags += exportMacro;
       }
 
+    // Add preprocessor definitions for this target and configuration.
+    this->LocalGenerator->AppendDefines
+      (defines, this->Target->GetProperty("COMPILE_DEFINITIONS"));
+    std::string defPropName =
+      cmSystemTools::UpperCase(this->LocalGenerator->ConfigurationName);
+    defPropName += "_COMPILE_DEFINITIONS";
+    this->LocalGenerator->AppendDefines
+      (defines, this->Target->GetProperty(defPropName.c_str()));
+
     // Add language-specific flags.
     this->LocalGenerator
       ->AddLanguageFlags(flags, lang,
@@ -286,6 +296,7 @@
       AppendFlags(flags,this->GetFrameworkFlags().c_str());
 
     *this->FlagFileStream << lang << "_FLAGS = " << flags << "\n\n";
+    *this->FlagFileStream << lang << "_DEFINES = " << defines << "\n\n";
     }
 
   // Add target-specific flags.
@@ -437,6 +448,35 @@
                           << "\n";
     }
 
+  // Add language-specific defines.
+  std::string defines = "$(";
+  defines += lang;
+  defines += "_DEFINES)";
+
+  // Add source-sepcific preprocessor definitions.
+  if(const char* compile_defs = source.GetProperty("COMPILE_DEFINITIONS"))
+    {
+    this->LocalGenerator->AppendDefines(defines, compile_defs);
+    *this->FlagFileStream << "# Custom defines: "
+                          << relativeObj << "_DEFINES = "
+                          << compile_defs << "\n"
+                          << "\n";
+    }
+  std::string configUpper =
+    cmSystemTools::UpperCase(this->LocalGenerator->ConfigurationName);
+  std::string defPropName = configUpper;
+  defPropName += "_COMPILE_DEFINITIONS";
+  if(const char* config_compile_defs =
+     source.GetProperty(defPropName.c_str()))
+    {
+    this->LocalGenerator->AppendDefines(defines, config_compile_defs);
+    *this->FlagFileStream
+      << "# Custom defines: "
+      << relativeObj << "_DEFINES_" << configUpper
+      << " = " << config_compile_defs << "\n"
+      << "\n";
+    }
+
   // Get the output paths for source and object files.
   std::string sourceFile = source.GetFullPath();
   if(this->LocalGenerator->UseRelativePaths)
@@ -522,6 +562,7 @@
   std::string objectDir = cmSystemTools::GetFilenamePath(obj);
   vars.ObjectDir = objectDir.c_str();
   vars.Flags = flags.c_str();
+  vars.Defines = defines.c_str();
 
   // Expand placeholders in the commands.
   for(std::vector<std::string>::iterator i = commands.begin();
@@ -601,7 +642,11 @@
                         preprocessCommands.begin(),
                         preprocessCommands.end());
 
-        vars.PreprocessedSource = objI.c_str();
+        std::string shellObjI =
+          this->Convert(objI.c_str(),
+                        cmLocalGenerator::NONE,
+                        cmLocalGenerator::SHELL).c_str();
+        vars.PreprocessedSource = shellObjI.c_str();
 
         // Expand placeholders in the commands.
         for(std::vector<std::string>::iterator i = commands.begin();
@@ -653,7 +698,11 @@
                         assemblyCommands.begin(),
                         assemblyCommands.end());
 
-        vars.AssemblySource = objS.c_str();
+        std::string shellObjS =
+          this->Convert(objS.c_str(),
+                        cmLocalGenerator::NONE,
+                        cmLocalGenerator::SHELL).c_str();
+        vars.AssemblySource = shellObjS.c_str();
 
         // Expand placeholders in the commands.
         for(std::vector<std::string>::iterator i = commands.begin();

Index: cmLocalGenerator.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmLocalGenerator.h,v
retrieving revision 1.94
retrieving revision 1.95
diff -u -d -r1.94 -r1.95
--- cmLocalGenerator.h	13 Jan 2008 21:36:20 -0000	1.94
+++ cmLocalGenerator.h	14 Jan 2008 14:20:57 -0000	1.95
@@ -139,6 +139,12 @@
   ///! Get the include flags for the current makefile and language
   const char* GetIncludeFlags(const char* lang); 
 
+  /**
+   * Encode a list of preprocessor definitions for the compiler
+   * command line.
+   */
+  void AppendDefines(std::string& defines, const char* defines_list);
+
   /** Translate a dependency as given in CMake code to the name to
       appear in a generated build file.  If the given name is that of
       a CMake target it will be transformed to the real output
@@ -207,6 +213,7 @@
     const char* TargetInstallNameDir;
     const char* LinkFlags;
     const char* LanguageCompileFlags;
+    const char* Defines;
   };
 
   /** Escape the given string to be used as a command line argument in
@@ -324,6 +331,10 @@
   std::string FindRelativePathTopBinary();
   void SetupPathConversions();
 
+  /** Check whether the native build system supports the given
+      definition.  Issues a warning.  */
+  virtual bool CheckDefinition(std::string const& define) const;
+
   cmMakefile *Makefile;
   cmGlobalGenerator *GlobalGenerator;
   // members used for relative path function ConvertToMakefilePath

Index: cmGlobalXCodeGenerator.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmGlobalXCodeGenerator.h,v
retrieving revision 1.51
retrieving revision 1.52
diff -u -d -r1.51 -r1.52
--- cmGlobalXCodeGenerator.h	14 Aug 2007 15:45:14 -0000	1.51
+++ cmGlobalXCodeGenerator.h	14 Jan 2008 14:20:57 -0000	1.52
@@ -173,6 +173,9 @@
                           const char* varNameSuffix,
                           const char* default_flags);
 
+  void AppendDefines(std::string& defs, const char* defines_list,
+                     bool dflag = false);
+
 protected:
   virtual const char* GetInstallTargetName()      { return "install"; }
   virtual const char* GetPackageTargetName()      { return "package"; }

Index: cmTarget.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmTarget.cxx,v
retrieving revision 1.172
retrieving revision 1.173
diff -u -d -r1.172 -r1.173
--- cmTarget.cxx	10 Jan 2008 03:09:19 -0000	1.172
+++ cmTarget.cxx	14 Jan 2008 14:20:57 -0000	1.173
@@ -67,8 +67,32 @@
     ("COMPILE_FLAGS", cmProperty::TARGET,
      "Additional flags to use when compiling this target's sources.",
      "The COMPILE_FLAGS property sets additional compiler flags used "
-     "to build sources within the target.  It may also be used to pass "
-     "additional preprocessor definitions.");
+     "to build sources within the target.  Use COMPILE_DEFINITIONS "
+     "to pass additional preprocessor definitions.");
+
+  cm->DefineProperty
+    ("COMPILE_DEFINITIONS", cmProperty::TARGET,
+     "Preprocessor definitions for compiling this target's sources.",
+     "The COMPILE_DEFINITIONS property may be set to a list of preprocessor "
+     "definitions using the syntax VAR or VAR=value.  Function-style "
+     "definitions are not supported.  CMake will automatically escape "
+     "the value correctly for the native build system (note that CMake "
+     "language syntax may require escapes to specify some values).  "
+     "This property may be set on a per-configuration basis using the name "
+     "<CONFIG>_COMPILE_DEFINITIONS where <CONFIG> is an upper-case name "
+     "(ex. \"DEBUG_COMPILE_DEFINITIONS\").\n"
+     "CMake will automatically drop some definitions that "
+     "are not supported by the native build tool.  "
+     "The VS6 IDE does not support definitions with values "
+     "(but NMake does).\n"
+     "Dislaimer: Most native build tools have poor support for escaping "
+     "certain values.  CMake has work-arounds for many cases but some "
+     "values may just not be possible to pass correctly.  If a value "
+     "does not seem to be escaped correctly, do not attempt to "
+     "work-around the problem by adding escape sequences to the value.  "
+     "Your work-around may break in a future version of CMake that "
+     "has improved escape support.  Instead consider defining the macro "
+     "in a (configured) header file.  Then report the limitation.");
 
   cm->DefineProperty
     ("DEFINE_SYMBOL", cmProperty::TARGET,



More information about the Cmake-commits mailing list