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.

This commit is contained in:
Brad King 2008-01-14 09:20:58 -05:00
parent 2c42f75522
commit 8262ccfd4e
29 changed files with 1184 additions and 162 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 += "'";
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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