diff options
author | Brad King <brad.king@kitware.com> | 2008-01-14 14:20:58 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2008-01-14 14:20:58 (GMT) |
commit | 8262ccfd4efddd4928070c637ef7c414633b4b1e (patch) | |
tree | c276e3f4ec61422534586a008739bd668e1b9ce3 /Source | |
parent | 2c42f755225c0cb30f04f07c9f5bd23f65ad0bd2 (diff) | |
download | CMake-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.cxx | 92 | ||||
-rw-r--r-- | Source/cmGlobalXCodeGenerator.h | 3 | ||||
-rw-r--r-- | Source/cmLocalGenerator.cxx | 117 | ||||
-rw-r--r-- | Source/cmLocalGenerator.h | 11 | ||||
-rw-r--r-- | Source/cmLocalUnixMakefileGenerator3.cxx | 12 | ||||
-rw-r--r-- | Source/cmLocalVisualStudio6Generator.cxx | 76 | ||||
-rw-r--r-- | Source/cmLocalVisualStudio6Generator.h | 3 | ||||
-rw-r--r-- | Source/cmLocalVisualStudio7Generator.cxx | 301 | ||||
-rw-r--r-- | Source/cmLocalVisualStudio7Generator.h | 6 | ||||
-rw-r--r-- | Source/cmMakefileTargetGenerator.cxx | 53 | ||||
-rw-r--r-- | Source/cmSourceFile.cxx | 28 | ||||
-rw-r--r-- | Source/cmTarget.cxx | 28 |
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 << """ << define << """; - } - 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, |