summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2008-01-14 14:20:58 (GMT)
committerBrad King <brad.king@kitware.com>2008-01-14 14:20:58 (GMT)
commit8262ccfd4efddd4928070c637ef7c414633b4b1e (patch)
treec276e3f4ec61422534586a008739bd668e1b9ce3 /Source
parent2c42f755225c0cb30f04f07c9f5bd23f65ad0bd2 (diff)
downloadCMake-8262ccfd4efddd4928070c637ef7c414633b4b1e.zip
CMake-8262ccfd4efddd4928070c637ef7c414633b4b1e.tar.gz
CMake-8262ccfd4efddd4928070c637ef7c414633b4b1e.tar.bz2
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.
Diffstat (limited to 'Source')
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx92
-rw-r--r--Source/cmGlobalXCodeGenerator.h3
-rw-r--r--Source/cmLocalGenerator.cxx117
-rw-r--r--Source/cmLocalGenerator.h11
-rw-r--r--Source/cmLocalUnixMakefileGenerator3.cxx12
-rw-r--r--Source/cmLocalVisualStudio6Generator.cxx76
-rw-r--r--Source/cmLocalVisualStudio6Generator.h3
-rw-r--r--Source/cmLocalVisualStudio7Generator.cxx301
-rw-r--r--Source/cmLocalVisualStudio7Generator.h6
-rw-r--r--Source/cmMakefileTargetGenerator.cxx53
-rw-r--r--Source/cmSourceFile.cxx28
-rw-r--r--Source/cmTarget.cxx28
12 files changed, 589 insertions, 141 deletions
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 44d857a..b59e949 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/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 += "'";
+ }
+}
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index aa4f3be..2b289fa 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/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"; }
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 1af2f80..636ee8d 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/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")
@@ -2214,6 +2218,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,
const char* default_comment)
@@ -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;
+}
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index 12e7451..23458c9 100644
--- a/Source/cmLocalGenerator.h
+++ b/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
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx
index 1613b82..adabd0a 100644
--- a/Source/cmLocalUnixMakefileGenerator3.cxx
+++ b/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;
}
diff --git a/Source/cmLocalVisualStudio6Generator.cxx b/Source/cmLocalVisualStudio6Generator.cxx
index dda7e60..0f67bf1 100644
--- a/Source/cmLocalVisualStudio6Generator.cxx
+++ b/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;
+}
diff --git a/Source/cmLocalVisualStudio6Generator.h b/Source/cmLocalVisualStudio6Generator.h
index 85dea08..e361a18 100644
--- a/Source/cmLocalVisualStudio6Generator.h
+++ b/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
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index 51ebcbc..ab3d024 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/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.
@@ -1707,6 +1785,16 @@ void cmLocalVisualStudio7GeneratorOptions::AddDefine(const std::string& 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;
diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h
index 7055fe0..3687289 100644
--- a/Source/cmLocalVisualStudio7Generator.h
+++ b/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;
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 6c3002e..86c567d 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/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();
diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx
index a8f9285..c902111 100644
--- a/Source/cmSourceFile.cxx
+++ b/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,
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 4f0c5ac..016aa1c 100644
--- a/Source/cmTarget.cxx
+++ b/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,