Browse Source

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.

Brad King 18 years ago
parent
commit
8262ccfd4e

+ 1 - 1
Modules/CMakeCInformation.cmake

@@ -141,7 +141,7 @@ ENDIF(NOT CMAKE_C_CREATE_STATIC_LIBRARY)
 # compile a C file into an object file
 IF(NOT CMAKE_C_COMPILE_OBJECT)
   SET(CMAKE_C_COMPILE_OBJECT
-    "<CMAKE_C_COMPILER> <FLAGS> -o <OBJECT>   -c <SOURCE>")
+    "<CMAKE_C_COMPILER> <DEFINES> <FLAGS> -o <OBJECT>   -c <SOURCE>")
 ENDIF(NOT CMAKE_C_COMPILE_OBJECT)
 
 IF(NOT CMAKE_C_LINK_EXECUTABLE)

+ 1 - 1
Modules/CMakeCXXInformation.cmake

@@ -201,7 +201,7 @@ ENDIF(NOT CMAKE_CXX_CREATE_STATIC_LIBRARY)
 # compile a C++ file into an object file
 IF(NOT CMAKE_CXX_COMPILE_OBJECT)
   SET(CMAKE_CXX_COMPILE_OBJECT
-    "<CMAKE_CXX_COMPILER>  <FLAGS> -o <OBJECT> -c <SOURCE>")
+    "<CMAKE_CXX_COMPILER>  <DEFINES> <FLAGS> -o <OBJECT> -c <SOURCE>")
 ENDIF(NOT CMAKE_CXX_COMPILE_OBJECT)
 
 IF(NOT CMAKE_CXX_LINK_EXECUTABLE)

+ 5 - 5
Modules/Platform/AIX.cmake

@@ -33,15 +33,15 @@ ELSE(CMAKE_COMPILER_IS_GNUCC)
 ENDIF(CMAKE_COMPILER_IS_GNUCC)
 
 IF(NOT CMAKE_COMPILER_IS_GNUCC)
-  SET (CMAKE_C_CREATE_PREPROCESSED_SOURCE "<CMAKE_C_COMPILER> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
-  SET (CMAKE_C_CREATE_ASSEMBLY_SOURCE "<CMAKE_C_COMPILER> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
+  SET (CMAKE_C_CREATE_PREPROCESSED_SOURCE "<CMAKE_C_COMPILER> <DEFINES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
+  SET (CMAKE_C_CREATE_ASSEMBLY_SOURCE "<CMAKE_C_COMPILER> <DEFINES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
 ENDIF(NOT CMAKE_COMPILER_IS_GNUCC)
 
 IF(NOT CMAKE_COMPILER_IS_GNUCXX)
   SET(CMAKE_CXX_COMPILE_OBJECT
-    "<CMAKE_CXX_COMPILER> -+ <FLAGS> -o <OBJECT> -c <SOURCE>")
-  SET (CMAKE_CXX_CREATE_PREPROCESSED_SOURCE "<CMAKE_CXX_COMPILER> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
-  SET (CMAKE_CXX_CREATE_ASSEMBLY_SOURCE "<CMAKE_CXX_COMPILER> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
+    "<CMAKE_CXX_COMPILER> -+ <DEFINES> <FLAGS> -o <OBJECT> -c <SOURCE>")
+  SET (CMAKE_CXX_CREATE_PREPROCESSED_SOURCE "<CMAKE_CXX_COMPILER> <DEFINES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
+  SET (CMAKE_CXX_CREATE_ASSEMBLY_SOURCE "<CMAKE_CXX_COMPILER> <DEFINES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
 ENDIF(NOT CMAKE_COMPILER_IS_GNUCXX)
 
 

+ 1 - 1
Modules/Platform/Generic-SDCC-C.cmake

@@ -38,7 +38,7 @@ IF(NOT DEFINED CMAKE_EXE_LINKER_FLAGS_INIT)
 ENDIF(NOT DEFINED CMAKE_EXE_LINKER_FLAGS_INIT)
 
 # compile a C file into an object file
-SET(CMAKE_C_COMPILE_OBJECT  "<CMAKE_C_COMPILER> <FLAGS> -o <OBJECT> -c <SOURCE>")
+SET(CMAKE_C_COMPILE_OBJECT  "<CMAKE_C_COMPILER> <DEFINES> <FLAGS> -o <OBJECT> -c <SOURCE>")
 
 # link object files to an executable
 SET(CMAKE_C_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <FLAGS> <OBJECTS> --out-fmt-ihx -o  <TARGET> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES>")

+ 1 - 1
Modules/Platform/QNX.cmake

@@ -28,6 +28,6 @@ FOREACH(type SHARED_LIBRARY SHARED_MODULE EXE)
 ENDFOREACH(type)
 # force the language to be c++ since qnx only has gcc and not g++ and c++?
 SET(CMAKE_CXX_COMPILE_OBJECT
-  "<CMAKE_CXX_COMPILER> -x c++ <FLAGS> -o <OBJECT> -c <SOURCE>")
+  "<CMAKE_CXX_COMPILER> -x c++ <DEFINES> <FLAGS> -o <OBJECT> -c <SOURCE>")
 
 INCLUDE(Platform/UnixPaths)

+ 6 - 2
Modules/Platform/Windows-bcc32.cmake

@@ -63,12 +63,16 @@ SET(CMAKE_CXX_CREATE_STATIC_LIBRARY  "tlib ${CMAKE_START_TEMP_FILE}/p512 <LINK_F
 SET(CMAKE_C_CREATE_STATIC_LIBRARY ${CMAKE_CXX_CREATE_STATIC_LIBRARY})
 
 # compile a C++ file into an object file
+# place <DEFINES> outside the response file because Borland refuses
+# to parse quotes from the response file.
 SET(CMAKE_CXX_COMPILE_OBJECT
-    "<CMAKE_CXX_COMPILER>  ${CMAKE_START_TEMP_FILE}-DWIN32 -P  <FLAGS>  -o<OBJECT> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_CXX_COMPILER> <DEFINES> ${CMAKE_START_TEMP_FILE}-DWIN32 -P <FLAGS> -o<OBJECT> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 # compile a C file into an object file
+# place <DEFINES> outside the response file because Borland refuses
+# to parse quotes from the response file.
 SET(CMAKE_C_COMPILE_OBJECT
-    "<CMAKE_C_COMPILER> ${CMAKE_START_TEMP_FILE}-DWIN32 -o<OBJECT>  <FLAGS> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_C_COMPILER> <DEFINES> ${CMAKE_START_TEMP_FILE}-DWIN32 -o<OBJECT> <FLAGS> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 
 SET(CMAKE_C_LINK_EXECUTABLE

+ 2 - 2
Modules/Platform/Windows-icl.cmake

@@ -27,11 +27,11 @@ SET(CMAKE_C_CREATE_STATIC_LIBRARY ${CMAKE_CXX_CREATE_STATIC_LIBRARY})
 
 # compile a C++ file into an object file
 SET(CMAKE_CXX_COMPILE_OBJECT
-    "<CMAKE_CXX_COMPILER>  ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} /TP -DWIN32 /Fo<OBJECT> <FLAGS> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_CXX_COMPILER>  ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} /TP -DWIN32 /Fo<OBJECT> <DEFINES> <FLAGS> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 # compile a C file into an object file
 SET(CMAKE_C_COMPILE_OBJECT
-    "<CMAKE_C_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} -DWIN32 /Fo<OBJECT>  <FLAGS> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_C_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} -DWIN32 /Fo<OBJECT> <DEFINES> <FLAGS> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 
 SET(CMAKE_C_LINK_EXECUTABLE

+ 4 - 4
Modules/Platform/Windows-wcl386.cmake

@@ -36,19 +36,19 @@ SET(CMAKE_CXX_LINK_EXECUTABLE ${CMAKE_C_LINK_EXECUTABLE})
 
 # compile a C++ file into an object file
 SET(CMAKE_CXX_COMPILE_OBJECT
-    "<CMAKE_CXX_COMPILER>  ${CMAKE_START_TEMP_FILE}  ${CMAKE_WCL_QUIET} <FLAGS>  -dWIN32 -fo<OBJECT> -c -cc++ <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_CXX_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_WCL_QUIET} <FLAGS> -dWIN32 -d+ <DEFINES> -fo<OBJECT> -c -cc++ <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 # compile a C file into an object file
 SET(CMAKE_C_COMPILE_OBJECT
-    "<CMAKE_C_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_WCL_QUIET} <FLAGS>  -dWIN32 -fo<OBJECT>   -c -cc <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_C_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_WCL_QUIET} <FLAGS> -dWIN32 -d+ <DEFINES> -fo<OBJECT> -c -cc <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 # preprocess a C source file
 SET(CMAKE_C_CREATE_PREPROCESSED_SOURCE
-    "<CMAKE_C_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_WCL_QUIET} <FLAGS> -dWIN32 -fo<PREPROCESSED_SOURCE> -pl -cc <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_C_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_WCL_QUIET} <FLAGS> -dWIN32 -d+ <DEFINES> -fo<PREPROCESSED_SOURCE> -pl -cc <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 # preprocess a C++ source file
 SET(CMAKE_CXX_CREATE_PREPROCESSED_SOURCE
-    "<CMAKE_CXX_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_WCL_QUIET} <FLAGS> -dWIN32 -fo<PREPROCESSED_SOURCE> -pl -cc++ <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_CXX_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_WCL_QUIET} <FLAGS> -dWIN32 -d+ <DEFINES> -fo<PREPROCESSED_SOURCE> -pl -cc++ <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 SET(CMAKE_CXX_CREATE_SHARED_MODULE
  "wlink ${CMAKE_START_TEMP_FILE} system nt_dll  ${CMAKE_WLINK_QUIET} name <TARGET> option caseexact  file {<OBJECTS>} <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}")

+ 4 - 4
Modules/Platform/cl.cmake

@@ -34,11 +34,11 @@ SET(CMAKE_C_CREATE_STATIC_LIBRARY "${CMAKE_CXX_CREATE_STATIC_LIBRARY}")
 
 # compile a C++ file into an object file
 SET(CMAKE_CXX_COMPILE_OBJECT
-    "<CMAKE_CXX_COMPILER>  ${CMAKE_START_TEMP_FILE}  ${CMAKE_CL_NOLOGO} <FLAGS>  /TP /Fo<OBJECT> /Fd<TARGET_PDB> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_CXX_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} <FLAGS> <DEFINES> /TP /Fo<OBJECT> /Fd<TARGET_PDB> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 # compile a C file into an object file
 SET(CMAKE_C_COMPILE_OBJECT
-    "<CMAKE_C_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} <FLAGS> /Fo<OBJECT> /Fd<TARGET_PDB>  -c <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_C_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} <FLAGS> <DEFINES> /Fo<OBJECT> /Fd<TARGET_PDB> -c <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 
 SET(CMAKE_C_LINK_EXECUTABLE
@@ -48,10 +48,10 @@ SET(CMAKE_CXX_LINK_EXECUTABLE
     "<CMAKE_CXX_COMPILER> ${CMAKE_CL_NOLOGO} ${CMAKE_START_TEMP_FILE} <FLAGS> <OBJECTS> /Fe<TARGET> /Fd<TARGET_PDB> -link /implib:<TARGET_IMPLIB> /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES>${CMAKE_END_TEMP_FILE}")
 
 SET(CMAKE_C_CREATE_PREPROCESSED_SOURCE
-    "<CMAKE_C_COMPILER> > <PREPROCESSED_SOURCE> ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} <FLAGS> -E <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_C_COMPILER> > <PREPROCESSED_SOURCE> ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} <FLAGS> <DEFINES> -E <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 SET(CMAKE_CXX_CREATE_PREPROCESSED_SOURCE
-    "<CMAKE_CXX_COMPILER> > <PREPROCESSED_SOURCE> ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} <FLAGS> /TP -E <SOURCE>${CMAKE_END_TEMP_FILE}")
+    "<CMAKE_CXX_COMPILER> > <PREPROCESSED_SOURCE> ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} <FLAGS> <DEFINES> /TP -E <SOURCE>${CMAKE_END_TEMP_FILE}")
 
 SET(CMAKE_C_CREATE_ASSEMBLY_SOURCE
     "<CMAKE_C_COMPILER> ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} <FLAGS> /FAs /FoNUL /Fa<ASSEMBLY_SOURCE> /c <SOURCE>${CMAKE_END_TEMP_FILE}")

+ 83 - 9
Source/cmGlobalXCodeGenerator.cxx

@@ -453,6 +453,9 @@ cmGlobalXCodeGenerator::CreateXCodeSourceFile(cmLocalGenerator* lg,
   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 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
   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 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
   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 @@ std::string cmGlobalXCodeGenerator::LookupFlags(const char* varNamePrefix,
     }
   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 += "'";
+    }
+}

+ 3 - 0
Source/cmGlobalXCodeGenerator.h

@@ -173,6 +173,9 @@ private:
                           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"; }

+ 117 - 0
Source/cmLocalGenerator.cxx

@@ -856,6 +856,10 @@ cmLocalGenerator::ExpandRuleVariable(std::string const& variable,
       return replaceValues.ObjectsQuoted;
       }
     }
+  if(replaceValues.Defines && variable == "DEFINES")
+    {
+    return replaceValues.Defines;
+    }
   if(replaceValues.TargetPDB )
     {
     if(variable == "TARGET_PDB")
@@ -2213,6 +2217,77 @@ void cmLocalGenerator::AppendFlags(std::string& flags,
     }
 }
 
+//----------------------------------------------------------------------------
+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,
@@ -2963,3 +3038,45 @@ bool cmLocalGenerator::NeedBackwardsCompatibility(unsigned int major,
   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;
+}

+ 11 - 0
Source/cmLocalGenerator.h

@@ -139,6 +139,12 @@ public:
   ///! 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 @@ public:
     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 @@ protected:
   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

+ 2 - 10
Source/cmLocalUnixMakefileGenerator3.cxx

@@ -338,17 +338,9 @@ cmLocalUnixMakefileGenerator3
     // 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;
     }
 

+ 72 - 4
Source/cmLocalVisualStudio6Generator.cxx

@@ -413,6 +413,29 @@ void cmLocalVisualStudio6Generator
       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 @@ void cmLocalVisualStudio6Generator
         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 @@ void cmLocalVisualStudio6Generator
             {
             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 @@ void cmLocalVisualStudio6Generator
       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 @@ cmLocalVisualStudio6Generator
   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;
+}

+ 3 - 0
Source/cmLocalVisualStudio6Generator.h

@@ -103,6 +103,9 @@ private:
   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

+ 189 - 112
Source/cmLocalVisualStudio7Generator.cxx

@@ -384,7 +384,8 @@ public:
     Compiler,
     Linker
   };
-  cmLocalVisualStudio7GeneratorOptions(Tool tool,
+  cmLocalVisualStudio7GeneratorOptions(cmLocalVisualStudio7Generator* lg,
+                                       Tool tool,
                                        cmVS7FlagTable const* extraTable = 0);
 
   // Store options from command line flags.
@@ -398,6 +399,7 @@ public:
 
   // 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 @@ public:
                                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 @@ private:
   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 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout,
     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 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout,
     extraLinkOptions += " ";
     extraLinkOptions += targetLinkFlags;
     }
-  Options linkOptions(Options::Linker);
+  Options linkOptions(this, Options::Linker);
   linkOptions.Parse(extraLinkOptions.c_str());
   switch(target.GetType())
     {
@@ -1027,6 +1037,135 @@ void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout,
   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 @@ void cmLocalVisualStudio7Generator
         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 @@ void cmLocalVisualStudio7Generator
       // 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 @@ void cmLocalVisualStudio7Generator
           {
           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 @@ void cmLocalVisualStudio7Generator::
 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 @@ WriteCustomRule(std::ostream& fout,
 
   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 @@ std::string cmLocalVisualStudio7Generator
 
 //----------------------------------------------------------------------------
 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.
@@ -1706,6 +1784,16 @@ void cmLocalVisualStudio7GeneratorOptions::AddDefine(const std::string& def)
   this->Defines.push_back(def);
 }
 
+//----------------------------------------------------------------------------
+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 @@ void cmLocalVisualStudio7GeneratorOptions::AddFlag(const char* flag,
 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 @@ cmLocalVisualStudio7GeneratorOptions
 
   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;

+ 5 - 1
Source/cmLocalVisualStudio7Generator.h

@@ -26,6 +26,7 @@ class cmSourceGroup;
 struct cmVS7FlagTable;
 
 class cmLocalVisualStudio7GeneratorOptions;
+class cmLocalVisualStudio7GeneratorFCInfo;
 
 /** \class cmLocalVisualStudio7Generator
  * \brief Write Visual Studio .NET project files.
@@ -68,6 +69,7 @@ public:
     { 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 @@ private:
   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 @@ private:
                   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;

+ 51 - 2
Source/cmMakefileTargetGenerator.cxx

@@ -254,6 +254,7 @@ void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
     {
     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 @@ void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
       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 @@ void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
       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 @@ cmMakefileTargetGenerator
                           << "\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 @@ cmMakefileTargetGenerator
   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 @@ cmMakefileTargetGenerator
                         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 @@ cmMakefileTargetGenerator
                         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();

+ 27 - 1
Source/cmSourceFile.cxx

@@ -340,7 +340,33 @@ void cmSourceFile::DefineProperties(cmake *cm)
     ("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, 

+ 26 - 2
Source/cmTarget.cxx

@@ -67,8 +67,32 @@ void cmTarget::DefineProperties(cmake *cm)
     ("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,

+ 1 - 0
Tests/CMakeLists.txt

@@ -49,6 +49,7 @@ IF(BUILD_TESTING)
   ADD_TEST_MACRO(Properties Properties)
   ADD_TEST_MACRO(Assembler HelloAsm)
   ADD_TEST_MACRO(SourceGroups SourceGroups)
+  ADD_TEST_MACRO(Preprocess Preprocess)
   
   IF (CMAKE_STRICT)
     ADD_TEST_MACRO(DocTest DocTest)

+ 181 - 0
Tests/Preprocess/CMakeLists.txt

@@ -0,0 +1,181 @@
+project(Preprocess)
+
+# This test is meant both as a test and as a reference for supported
+# syntax on native tool command lines.
+
+#-----------------------------------------------------------------------------
+# Construct a C-string literal to test passing through a definition on
+# the command line.  We configure the value into a header so it can be
+# checked in the executable at runtime.  The semicolon is handled
+# specially because it needs to be escaped in the COMPILE_DEFINITIONS
+# property value to avoid separating definitions but the string value
+# must not have it escaped inside the configured header.
+set(STRING_EXTRA "")
+
+if("${CMAKE_GENERATOR}" MATCHES "Make" AND MSVC)
+  set(NMAKE 1)
+endif("${CMAKE_GENERATOR}" MATCHES "Make" AND MSVC)
+
+if(NOT BORLAND)
+  # Borland: ;
+  # The Borland compiler will simply not accept a non-escaped semicolon
+  # on the command line.  If it is escaped \; then the escape character
+  # shows up in the preprocessing output too.
+  set(SEMICOLON "\;")
+endif(NOT BORLAND)
+
+if(NOT BORLAND AND NOT WATCOM)
+  # Borland, WMake: multiple spaces
+  # The make tool seems to remove extra whitespace from inside
+  # quoted strings when passing to the compiler.  It does not have
+  # trouble passing to other tools, and the compiler may be directly
+  # invoked from the command line.
+  set(STRING_EXTRA "${STRING_EXTRA}  ")
+endif(NOT BORLAND AND NOT WATCOM)
+
+if(NOT "${CMAKE_GENERATOR}" MATCHES "Visual Studio")
+  # VS: ,
+  # Visual Studio will not accept a comma in the value of a definition.
+  # The comma-separated list of PreprocessorDefinitions in the project
+  # file seems to be parsed before the content of entries is examined.
+  set(STRING_EXTRA "${STRING_EXTRA},")
+endif(NOT "${CMAKE_GENERATOR}" MATCHES "Visual Studio")
+
+if(NOT MINGW)
+  # MinGW: &
+  # When inside -D"FOO=\"a & b\"" MinGW make wants -D"FOO=\"a "&" b\""
+  # but it does not like quoted ampersand elsewhere.
+  set(STRING_EXTRA "${STRING_EXTRA}&")
+endif(NOT MINGW)
+
+if(NOT MINGW)
+  # MinGW: |
+  # When inside -D"FOO=\"a | b\"" MinGW make wants -D"FOO=\"a "|" b\""
+  # but it does not like quoted pipe elsewhere.
+  set(STRING_EXTRA "${STRING_EXTRA}|")
+endif(NOT MINGW)
+
+if(NOT BORLAND AND NOT MINGW AND NOT NMAKE)
+  # Borland, NMake, MinGW: ^
+  # When inside -D"FOO=\"a ^ b\"" they make wants -D"FOO=\"a "^" b\""
+  # but do not like quoted carrot elsewhere.  In NMake the non-quoted
+  # syntax works when the flags are not in a make variable.
+  set(STRING_EXTRA "${STRING_EXTRA}^")
+endif(NOT BORLAND AND NOT MINGW AND NOT NMAKE)
+
+if(NOT BORLAND AND NOT MINGW AND NOT NMAKE)
+  # Borland, MinGW: < >
+  # Angle-brackets have funny behavior that is hard to escape.
+  set(STRING_EXTRA "${STRING_EXTRA}<>")
+endif(NOT BORLAND AND NOT MINGW AND NOT NMAKE)
+
+# General: \"
+# Make tools do not reliably accept \\\" syntax:
+#  - MinGW and MSYS make tools crash with \\\"
+#  - Borland make actually wants a mis-matched quote \\"
+#    or $(BACKSLASH)\" where BACKSLASH is a variable set to \\
+#  - VS IDE gets confused about the bounds of the definition value \\\"
+#  - NMake is okay with just \\\"
+if(NMAKE OR "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles")
+  set(STRING_EXTRA "${STRING_EXTRA}\\\"")
+endif(NMAKE OR "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles")
+
+# General: #
+# MSVC will not accept a # in the value of a string definition on the
+# command line.  The character seems to be simply replaced by an
+# equals =.  According to "cl -help" definitions may be specified by
+# -DMACRO#VALUE as well as -DMACRO=VALUE.  It must be implemented by a
+# simple search-and-replace.
+#
+# The Borland compiler will parse both # and \# as just # but the make
+# tool seems to want \# sometimes and not others.
+#
+# Unix make does not like # in variable settings without extra
+# escaping.  This could probably be fixed but since MSVC does not
+# support it and it is not an operator it is not worthwhile.
+
+# Compose the final test string.
+set(STRING_VALUE "hello `~!@$%*)(_+-=}{][:'.?/ ${STRING_EXTRA}world")
+
+#-----------------------------------------------------------------------------
+# Function-style macro command-line support:
+#   - Borland does not support
+#   - MSVC does not support
+#   - Watcom does not support
+#   - GCC supports
+
+# Too few platforms support this to bother implementing.
+# People can just configure headers with the macros.
+
+#-----------------------------------------------------------------------------
+# Construct a sample expression to pass as a macro definition.
+
+set(EXPR "x*y+!(x==(y+1*2))*f(x%2)")
+
+if(NOT WATCOM)
+  # Watcom does not support - or / because it parses them as options.
+  set(EXPR "${EXPR}+y/x-x")
+endif(NOT WATCOM)
+
+#-----------------------------------------------------------------------------
+
+# Inform the test if the debug configuration is getting built.
+# The NDEBUG definition takes care of this for release.
+set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DPREPROCESS_DEBUG")
+set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DPREPROCESS_DEBUG")
+
+# Inform the test if it built from Xcode or VS6 IDE.
+if(XCODE)
+  set(PREPROCESS_XCODE 1)
+endif(XCODE)
+if("${CMAKE_GENERATOR}" MATCHES "Visual Studio 6")
+  set(PREPROCESS_VS6 1)
+  set(VS6 _vs6)
+endif("${CMAKE_GENERATOR}" MATCHES "Visual Studio 6")
+
+# Test old-style definitions.
+add_definitions(-DOLD_DEF -DOLD_EXPR=2)
+
+set(FILE_PATH "${Preprocess_SOURCE_DIR}/file_def.h")
+set(TARGET_PATH "${Preprocess_SOURCE_DIR}/target_def.h")
+
+# Create a list of definition property strings.
+set(TARGET_DEFS "TARGET_DEF")
+set(FILE_DEFS "FILE_DEF")
+
+# Add definitions with values.  VS6 does not support this.
+if(NOT PREPROCESS_VS6)
+  list(APPEND TARGET_DEFS
+    "TARGET_STRING=\"${STRING_VALUE}${SEMICOLON}\""
+    "TARGET_EXPR=${EXPR}"
+    "TARGET_PATH=\"${TARGET_PATH}\""
+    )
+  list(APPEND FILE_DEFS
+    "FILE_STRING=\"${STRING_VALUE}${SEMICOLON}\""
+    "FILE_EXPR=${EXPR}"
+    "FILE_PATH=\"${FILE_PATH}\""
+    )
+endif(NOT PREPROCESS_VS6)
+
+add_executable(Preprocess preprocess.c preprocess${VS6}.cxx)
+set_target_properties(Preprocess PROPERTIES
+  COMPILE_DEFINITIONS "${TARGET_DEFS}"
+  DEBUG_COMPILE_DEFINITIONS "TARGET_DEF_DEBUG"
+  RELEASE_COMPILE_DEFINITIONS "TARGET_DEF_RELEASE"
+  )
+set_source_files_properties(preprocess.c preprocess${VS6}.cxx PROPERTIES
+  COMPILE_DEFINITIONS "${FILE_DEFS}"
+  DEBUG_COMPILE_DEFINITIONS "FILE_DEF_DEBUG"
+  RELEASE_COMPILE_DEFINITIONS "FILE_DEF_RELEASE"
+  )
+
+# Helper target for running test manually in build tree.
+add_custom_target(drive COMMAND Preprocess)
+
+# Configure the header file with the desired string value.
+if(SEMICOLON)
+  set(STRING_VALUE "${STRING_VALUE};")
+endif(SEMICOLON)
+configure_file(${Preprocess_SOURCE_DIR}/preprocess.h.in
+               ${Preprocess_BINARY_DIR}/preprocess.h)
+include_directories(${Preprocess_BINARY_DIR})

+ 1 - 0
Tests/Preprocess/file_def.h

@@ -0,0 +1 @@
+#define FILE_PATH_DEF

+ 170 - 0
Tests/Preprocess/preprocess.c

@@ -0,0 +1,170 @@
+#include <preprocess.h>
+
+#include FILE_PATH
+#include TARGET_PATH
+
+#include <string.h>
+#include <stdio.h>
+
+int check_defines_C(void)
+{
+  int result = 1;
+#ifndef PREPROCESS_VS6
+  if(strcmp(FILE_STRING, STRING_VALUE) != 0)
+    {
+    fprintf(stderr,
+            "FILE_STRING has wrong value in C [%s]\n", FILE_STRING);
+    result = 0;
+    }
+  if(strcmp(TARGET_STRING, STRING_VALUE) != 0)
+    {
+    fprintf(stderr,
+            "TARGET_STRING has wrong value in C [%s]\n", TARGET_STRING);
+    result = 0;
+    }
+  {
+  int x = 2;
+  int y = 3;
+  if((FILE_EXPR) != (EXPR))
+    {
+    fprintf(stderr, "FILE_EXPR did not work in C [%s]\n",
+            TO_STRING(FILE_EXPR));
+    result = 0;
+    }
+  if((TARGET_EXPR) != (EXPR))
+    {
+    fprintf(stderr, "TARGET_EXPR did not work in C [%s]\n",
+            TO_STRING(FILE_EXPR));
+    result = 0;
+    }
+  }
+#endif
+#ifdef NDEBUG
+# ifdef FILE_DEF_DEBUG
+  {
+  fprintf(stderr, "FILE_DEF_DEBUG should not be defined in C\n");
+  result = 0;
+  }
+# endif
+# ifdef TARGET_DEF_DEBUG
+  {
+  fprintf(stderr, "TARGET_DEF_DEBUG should not be defined in C\n");
+  result = 0;
+  }
+# endif
+# ifndef FILE_DEF_RELEASE
+#  ifndef PREPROCESS_XCODE
+  {
+  fprintf(stderr, "FILE_DEF_RELEASE should be defined in C\n");
+  result = 0;
+  }
+#  endif
+# endif
+# ifndef TARGET_DEF_RELEASE
+  {
+  fprintf(stderr, "TARGET_DEF_RELEASE should be defined in C\n");
+  result = 0;
+  }
+# endif
+#endif
+#ifdef PREPROCESS_DEBUG
+# ifndef FILE_DEF_DEBUG
+#  ifndef PREPROCESS_XCODE
+  {
+  fprintf(stderr, "FILE_DEF_DEBUG should be defined in C\n");
+  result = 0;
+  }
+#  endif
+# endif
+# ifndef TARGET_DEF_DEBUG
+  {
+  fprintf(stderr, "TARGET_DEF_DEBUG should be defined in C\n");
+  result = 0;
+  }
+# endif
+# ifdef FILE_DEF_RELEASE
+  {
+  fprintf(stderr, "FILE_DEF_RELEASE should not be defined in C\n");
+  result = 0;
+  }
+# endif
+# ifdef TARGET_DEF_RELEASE
+  {
+  fprintf(stderr, "TARGET_DEF_RELEASE should not be defined in C\n");
+  result = 0;
+  }
+# endif
+#endif
+#if defined(FILE_DEF_DEBUG) || defined(TARGET_DEF_DEBUG)
+# if !defined(FILE_DEF_DEBUG) || !defined(TARGET_DEF_DEBUG)
+#  ifndef PREPROCESS_XCODE
+  {
+  fprintf(stderr,
+          "FILE_DEF_DEBUG and TARGET_DEF_DEBUG inconsistent in C\n");
+  result = 0;
+  }
+#  endif
+# endif
+# if defined(FILE_DEF_RELEASE) || defined(TARGET_DEF_RELEASE)
+  {
+  fprintf(stderr, "DEBUG and RELEASE definitions inconsistent in C\n");
+  result = 0;
+  }
+# endif
+#endif
+#if defined(FILE_DEF_RELEASE) || defined(TARGET_DEF_RELEASE)
+# if !defined(FILE_DEF_RELEASE) || !defined(TARGET_DEF_RELEASE)
+#  ifndef PREPROCESS_XCODE
+  {
+  fprintf(stderr,
+          "FILE_DEF_RELEASE and TARGET_DEF_RELEASE inconsistent in C\n");
+  result = 0;
+  }
+#  endif
+# endif
+# if defined(FILE_DEF_DEBUG) || defined(TARGET_DEF_DEBUG)
+  {
+  fprintf(stderr, "RELEASE and DEBUG definitions inconsistent in C\n");
+  result = 0;
+  }
+# endif
+#endif
+#ifndef FILE_PATH_DEF
+  {
+  fprintf(stderr, "FILE_PATH_DEF not defined in C\n");
+  result = 0;
+  }
+#endif
+#ifndef TARGET_PATH_DEF
+  {
+  fprintf(stderr, "TARGET_PATH_DEF not defined in C\n");
+  result = 0;
+  }
+#endif
+#ifndef FILE_DEF
+  {
+  fprintf(stderr, "FILE_DEF not defined in C\n");
+  result = 0;
+  }
+#endif
+#ifndef TARGET_DEF
+  {
+  fprintf(stderr, "TARGET_DEF not defined in C\n");
+  result = 0;
+  }
+#endif
+#ifndef OLD_DEF
+  {
+  fprintf(stderr, "OLD_DEF not defined in C\n");
+  result = 0;
+  }
+#endif
+#if !defined(OLD_EXPR) || OLD_EXPR != 2
+  {
+  fprintf(stderr, "OLD_EXPR id not work in C [%s]\n",
+          TO_STRING(OLD_EXPR));
+  result = 0;
+  }
+#endif
+  return result;
+}

+ 197 - 0
Tests/Preprocess/preprocess.cxx

@@ -0,0 +1,197 @@
+#include <preprocess.h>
+
+#include FILE_PATH
+#include TARGET_PATH
+
+#include <string.h>
+#include <stdio.h>
+
+extern "C" int check_defines_C(void);
+
+int check_defines_CXX()
+{
+  int result = 1;
+#ifndef PREPROCESS_VS6
+  if(strcmp(FILE_STRING, STRING_VALUE) != 0)
+    {
+    fprintf(stderr,
+            "FILE_STRING has wrong value in CXX [%s]\n", FILE_STRING);
+    result = 0;
+    }
+  if(strcmp(TARGET_STRING, STRING_VALUE) != 0)
+    {
+    fprintf(stderr,
+            "TARGET_STRING has wrong value in CXX [%s]\n", TARGET_STRING);
+    result = 0;
+    }
+  {
+  int x = 2;
+  int y = 3;
+  if((FILE_EXPR) != (EXPR))
+    {
+    fprintf(stderr, "FILE_EXPR did not work in CXX [%s]\n",
+            TO_STRING(FILE_EXPR));
+    result = 0;
+    }
+  if((TARGET_EXPR) != (EXPR))
+    {
+    fprintf(stderr, "TARGET_EXPR did not work in CXX [%s]\n",
+            TO_STRING(FILE_EXPR));
+    result = 0;
+    }
+  }
+#endif
+#ifdef NDEBUG
+# ifdef FILE_DEF_DEBUG
+  {
+  fprintf(stderr, "FILE_DEF_DEBUG should not be defined in CXX\n");
+  result = 0;
+  }
+# endif
+# ifdef TARGET_DEF_DEBUG
+  {
+  fprintf(stderr, "TARGET_DEF_DEBUG should not be defined in CXX\n");
+  result = 0;
+  }
+# endif
+# ifndef FILE_DEF_RELEASE
+#  ifndef PREPROCESS_XCODE
+  {
+  fprintf(stderr, "FILE_DEF_RELEASE should be defined in CXX\n");
+  result = 0;
+  }
+#  endif
+# endif
+# ifndef TARGET_DEF_RELEASE
+  {
+  fprintf(stderr, "TARGET_DEF_RELEASE should be defined in CXX\n");
+  result = 0;
+  }
+# endif
+#endif
+#ifdef PREPROCESS_DEBUG
+# ifndef FILE_DEF_DEBUG
+#  ifndef PREPROCESS_XCODE
+  {
+  fprintf(stderr, "FILE_DEF_DEBUG should be defined in CXX\n");
+  result = 0;
+  }
+#  endif
+# endif
+# ifndef TARGET_DEF_DEBUG
+  {
+  fprintf(stderr, "TARGET_DEF_DEBUG should be defined in CXX\n");
+  result = 0;
+  }
+# endif
+# ifdef FILE_DEF_RELEASE
+  {
+  fprintf(stderr, "FILE_DEF_RELEASE should not be defined in CXX\n");
+  result = 0;
+  }
+# endif
+# ifdef TARGET_DEF_RELEASE
+  {
+  fprintf(stderr, "TARGET_DEF_RELEASE should not be defined in CXX\n");
+  result = 0;
+  }
+# endif
+#endif
+#if defined(FILE_DEF_DEBUG) || defined(TARGET_DEF_DEBUG)
+# if !defined(FILE_DEF_DEBUG) || !defined(TARGET_DEF_DEBUG)
+#  ifndef PREPROCESS_XCODE
+  {
+  fprintf(stderr,
+          "FILE_DEF_DEBUG and TARGET_DEF_DEBUG inconsistent in CXX\n");
+  result = 0;
+  }
+#  endif
+# endif
+# if defined(FILE_DEF_RELEASE) || defined(TARGET_DEF_RELEASE)
+  {
+  fprintf(stderr, "DEBUG and RELEASE definitions inconsistent in CXX\n");
+  result = 0;
+  }
+# endif
+#endif
+#if defined(FILE_DEF_RELEASE) || defined(TARGET_DEF_RELEASE)
+# if !defined(FILE_DEF_RELEASE) || !defined(TARGET_DEF_RELEASE)
+#  ifndef PREPROCESS_XCODE
+  {
+  fprintf(stderr,
+          "FILE_DEF_RELEASE and TARGET_DEF_RELEASE inconsistent in CXX\n");
+  result = 0;
+  }
+#  endif
+# endif
+# if defined(FILE_DEF_DEBUG) || defined(TARGET_DEF_DEBUG)
+  {
+  fprintf(stderr, "RELEASE and DEBUG definitions inconsistent in CXX\n");
+  result = 0;
+  }
+# endif
+#endif
+#ifndef FILE_PATH_DEF
+  {
+  fprintf(stderr, "FILE_PATH_DEF not defined in CXX\n");
+  result = 0;
+  }
+#endif
+#ifndef TARGET_PATH_DEF
+  {
+  fprintf(stderr, "TARGET_PATH_DEF not defined in CXX\n");
+  result = 0;
+  }
+#endif
+#ifndef FILE_DEF
+  {
+  fprintf(stderr, "FILE_DEF not defined in CXX\n");
+  result = 0;
+  }
+#endif
+#ifndef TARGET_DEF
+  {
+  fprintf(stderr, "TARGET_DEF not defined in CXX\n");
+  result = 0;
+  }
+#endif
+#ifndef OLD_DEF
+  {
+  fprintf(stderr, "OLD_DEF not defined in CXX\n");
+  result = 0;
+  }
+#endif
+#if !defined(OLD_EXPR) || OLD_EXPR != 2
+  {
+  fprintf(stderr, "OLD_EXPR id not work in C [%s]\n",
+          TO_STRING(OLD_EXPR));
+  result = 0;
+  }
+#endif
+  return result;
+}
+
+int main()
+{
+  int result = 1;
+
+  if(!check_defines_C())
+    {
+    result = 0;
+    }
+
+  if(!check_defines_CXX())
+    {
+    result = 0;
+    }
+
+  if(result)
+    {
+    printf("All preprocessor definitions are correct.\n");
+    return 0;
+    }
+  else
+    {
+    return 1;
+    }
+}

+ 16 - 0
Tests/Preprocess/preprocess.h.in

@@ -0,0 +1,16 @@
+/* Define configured macros.  */
+#define STRING_VALUE "@STRING_VALUE@"
+#define EXPR @EXPR@
+#cmakedefine PREPROCESS_XCODE
+#cmakedefine PREPROCESS_VS6
+
+#ifdef PREPROCESS_VS6
+# define FILE_PATH "@FILE_PATH@"
+# define TARGET_PATH "@TARGET_PATH@"
+#endif
+
+/* Declarations and macros shared by all sources.  */
+#define TO_STRING(x) TO_STRING0(x)
+#define TO_STRING0(x) #x
+
+static int f(int i) { return i*3; }

+ 3 - 0
Tests/Preprocess/preprocess_vs6.cxx

@@ -0,0 +1,3 @@
+// The VS6 IDE does not support object name configuration so we need a
+// source file with a different name.  Include the real source file.
+#include "preprocess.cxx"

+ 1 - 0
Tests/Preprocess/target_def.h

@@ -0,0 +1 @@
+#define TARGET_PATH_DEF