From 4f2d9d2da56b13b5780387492a400baa03d5bf70 Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 27 Apr 2012 08:34:49 -0400 Subject: [PATCH 1/5] VS10: Refactor custom commands to use WriteSource All other source file elements are already written through WriteSource. Refactor custom command source element generation into WriteSource too. --- Source/cmVisualStudio10TargetGenerator.cxx | 32 +++++++++++++--------- Source/cmVisualStudio10TargetGenerator.h | 2 +- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 9c25b0cd4..9a0c5e1be 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -491,14 +491,9 @@ cmVisualStudio10TargetGenerator::WriteCustomRule(cmSourceFile* source, std::vector *configs = static_cast (this->GlobalGenerator)->GetConfigurations(); - this->WriteString("Makefile->GetCurrentOutputDirectory(), - sourcePath.c_str()); - this->ConvertToWindowsSlash(path); - (*this->BuildFileStream ) << path << "\">\n"; + + this->WriteSource("CustomBuild", source, ">\n"); + for(std::vector::iterator i = configs->begin(); i != configs->end(); ++i) { @@ -796,15 +791,26 @@ WriteGroupSources(const char* name, } void cmVisualStudio10TargetGenerator::WriteSource( - const char* tool, cmSourceFile* sf, bool end) + const char* tool, cmSourceFile* sf, const char* end) { std::string sourceFile = sf->GetFullPath(); - // do not use a relative path here because it means that you - // can not use as long a path to the file. + if(sf->GetCustomCommand()) + { + // custom command sources must use relative paths or they will + // not show up in the GUI. + sourceFile = cmSystemTools::RelativePath( + this->Makefile->GetCurrentOutputDirectory(), + sourceFile.c_str()); + } + else + { + // do not use a relative path here because it means that you + // can not use as long a path to the file. + } this->ConvertToWindowsSlash(sourceFile); this->WriteString("<", 2); (*this->BuildFileStream ) << tool << - " Include=\"" << sourceFile << (end? "\" />\n" : "\" "); + " Include=\"" << sourceFile << "\"" << (end? end : " />\n"); } void cmVisualStudio10TargetGenerator::WriteSources( @@ -836,7 +842,7 @@ void cmVisualStudio10TargetGenerator::WriteAllSources() bool cl = strcmp(lang, "C") == 0 || strcmp(lang, "CXX") == 0; bool rc = strcmp(lang, "RC") == 0; const char* tool = cl? "ClCompile" : (rc? "ResourceCompile" : "None"); - this->WriteSource(tool, *si, false); + this->WriteSource(tool, *si, " "); // ouput any flags specific to this source file if(cl && this->OutputSourceSpecificFlags(*si)) { diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 20a443ba4..1e0f9c197 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -47,7 +47,7 @@ private: void WriteString(const char* line, int indentLevel); void WriteProjectConfigurations(); void WriteProjectConfigurationValues(); - void WriteSource(const char* tool, cmSourceFile* sf, bool end = true); + void WriteSource(const char* tool, cmSourceFile* sf, const char* end = 0); void WriteSources(const char* tool, std::vector const&); void WriteAllSources(); void WriteDotNetReferences(); From c2ba6ba4fc0d807f36a6a9fcb1c166b776750dae Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 27 Apr 2012 09:21:08 -0400 Subject: [PATCH 2/5] VS10: Simplify vcxproj.filter file generation Remove the duplicate source file classification logic used to generate the filter files. Instead record during the main vcxproj file generation the source files generated for each tool. Also record whether or not each source file is converted to a relative path. Use the recorded result during filter generation to ensure consistency between the project file and filter file. --- Source/cmVisualStudio10TargetGenerator.cxx | 78 +++++----------------- Source/cmVisualStudio10TargetGenerator.h | 13 +++- 2 files changed, 26 insertions(+), 65 deletions(-) diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 9a0c5e1be..c4d5415c0 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -554,13 +554,6 @@ void cmVisualStudio10TargetGenerator::WriteGroups() std::vector classes = this->Target->GetSourceFiles(); std::set groupsUsed; - std::vector clCompile; - std::vector customBuild; - std::vector none; - std::vector headers; - std::vector idls; - std::vector resource; - for(std::vector::const_iterator s = classes.begin(); s != classes.end(); s++) { @@ -569,40 +562,6 @@ void cmVisualStudio10TargetGenerator::WriteGroups() cmSourceGroup& sourceGroup = this->Makefile->FindSourceGroup(source.c_str(), sourceGroups); groupsUsed.insert(&sourceGroup); - const char* lang = sf->GetLanguage(); - bool header = (*s)->GetPropertyAsBool("HEADER_FILE_ONLY") - || this->GlobalGenerator->IgnoreFile - ((*s)->GetExtension().c_str()); - std::string ext = - cmSystemTools::LowerCase((*s)->GetExtension()); - if(!lang) - { - lang = "None"; - } - if(header) - { - headers.push_back(sf); - } - else if(lang[0] == 'C') - { - clCompile.push_back(sf); - } - else if(strcmp(lang, "RC") == 0) - { - resource.push_back(sf); - } - else if(sf->GetCustomCommand()) - { - customBuild.push_back(sf); - } - else if(ext == "idl") - { - idls.push_back(sf); - } - else - { - none.push_back(sf); - } } this->AddMissingSourceGroups(groupsUsed, sourceGroups); @@ -624,11 +583,11 @@ void cmVisualStudio10TargetGenerator::WriteGroups() "xmlns=\"http://schemas.microsoft.com/" "developer/msbuild/2003\">\n", 0); - this->WriteGroupSources("ClCompile", clCompile, sourceGroups); - this->WriteGroupSources("ClInclude", headers, sourceGroups); - this->WriteGroupSources("ResourceCompile", resource, sourceGroups); - this->WriteGroupSources("Midl", idls, sourceGroups); - this->WriteGroupSources("CustomBuild", customBuild, sourceGroups); + for(ToolSourceMap::const_iterator ti = this->Tools.begin(); + ti != this->Tools.end(); ++ti) + { + this->WriteGroupSources(ti->first.c_str(), ti->second, sourceGroups); + } // Add object library contents as external objects. std::vector objs; @@ -685,7 +644,6 @@ void cmVisualStudio10TargetGenerator::WriteGroups() this->WriteString("\n", 2); } this->WriteString("\n", 1); - this->WriteGroupSources("None", none, sourceGroups); this->WriteString("\n", 0); // restore stream pointer this->BuildFileStream = save; @@ -745,32 +703,23 @@ cmVisualStudio10TargetGenerator::AddMissingSourceGroups( void cmVisualStudio10TargetGenerator:: WriteGroupSources(const char* name, - std::vector const& sources, + ToolSources const& sources, std::vector& sourceGroups) { this->WriteString("\n", 1); - for(std::vector::const_iterator s = sources.begin(); + for(ToolSources::const_iterator s = sources.begin(); s != sources.end(); ++s) { - cmSourceFile* sf = *s; - if(sf->GetExtension() == "obj") - { - continue; - } + cmSourceFile* sf = s->SourceFile; std::string const& source = sf->GetFullPath(); cmSourceGroup& sourceGroup = this->Makefile->FindSourceGroup(source.c_str(), sourceGroups); const char* filter = sourceGroup.GetFullName(); this->WriteString("<", 2); - std::string path = source; - // custom command sources must use relative paths or they will - // not show up in the GUI. - if(sf->GetCustomCommand()) - { - path = cmSystemTools::RelativePath( + std::string path = s->RelativePath? + cmSystemTools::RelativePath( this->Makefile->GetCurrentOutputDirectory(), - source.c_str()); - } + source.c_str()) : source; this->ConvertToWindowsSlash(path); (*this->BuildFileStream) << name << " Include=\"" << path; @@ -794,7 +743,8 @@ void cmVisualStudio10TargetGenerator::WriteSource( const char* tool, cmSourceFile* sf, const char* end) { std::string sourceFile = sf->GetFullPath(); - if(sf->GetCustomCommand()) + bool relative = sf->GetCustomCommand()? true:false; + if(relative) { // custom command sources must use relative paths or they will // not show up in the GUI. @@ -811,6 +761,8 @@ void cmVisualStudio10TargetGenerator::WriteSource( this->WriteString("<", 2); (*this->BuildFileStream ) << tool << " Include=\"" << sourceFile << "\"" << (end? end : " />\n"); + ToolSource toolSource = {sf, relative}; + this->Tools[tool].push_back(toolSource); } void cmVisualStudio10TargetGenerator::WriteSources( diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 1e0f9c197..c00d328a6 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -43,6 +43,13 @@ public: ); private: + struct ToolSource + { + cmSourceFile* SourceFile; + bool RelativePath; + }; + struct ToolSources: public std::vector {}; + void ConvertToWindowsSlash(std::string& s); void WriteString(const char* line, int indentLevel); void WriteProjectConfigurations(); @@ -77,8 +84,7 @@ private: void WriteEvents(std::string const& configName); void WriteEvent(const char* name, std::vector & commands, std::string const& configName); - void WriteGroupSources(const char* name, - std::vector const& sources, + void WriteGroupSources(const char* name, ToolSources const& sources, std::vector& ); void AddMissingSourceGroups(std::set& groupsUsed, const std::vector& allGroups); @@ -99,6 +105,9 @@ private: cmGeneratedFileStream* BuildFileStream; cmLocalVisualStudio7Generator* LocalGenerator; std::set SourcesVisited; + + typedef std::map ToolSourceMap; + ToolSourceMap Tools; }; #endif From 4248132e59a8401a96d9c20ef155d80e439e7346 Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 27 Apr 2012 10:42:23 -0400 Subject: [PATCH 3/5] VS10: Convert paths normally unless forced to relative Most CMake generators produce relative paths within the build tree and full paths to files outside the build tree. Make VS 10 and VS 11 project files consistent with this approach except for paths forced to be relative to work around a VS 10 bug. --- Source/cmVisualStudio10TargetGenerator.cxx | 39 +++++++++++----------- Source/cmVisualStudio10TargetGenerator.h | 1 + 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index c4d5415c0..7c38d4b6b 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -536,6 +536,18 @@ cmVisualStudio10TargetGenerator::WriteCustomRule(cmSourceFile* source, this->WriteString("\n", 2); } +std::string +cmVisualStudio10TargetGenerator::ConvertPath(std::string const& path, + bool forceRelative) +{ + return forceRelative + ? cmSystemTools::RelativePath( + this->Makefile->GetCurrentOutputDirectory(), path.c_str()) + : this->LocalGenerator->Convert(path.c_str(), + cmLocalGenerator::START_OUTPUT, + cmLocalGenerator::UNCHANGED); +} + void cmVisualStudio10TargetGenerator::ConvertToWindowsSlash(std::string& s) { // first convert all of the slashes @@ -716,10 +728,7 @@ WriteGroupSources(const char* name, this->Makefile->FindSourceGroup(source.c_str(), sourceGroups); const char* filter = sourceGroup.GetFullName(); this->WriteString("<", 2); - std::string path = s->RelativePath? - cmSystemTools::RelativePath( - this->Makefile->GetCurrentOutputDirectory(), - source.c_str()) : source; + std::string path = this->ConvertPath(source, s->RelativePath); this->ConvertToWindowsSlash(path); (*this->BuildFileStream) << name << " Include=\"" << path; @@ -743,25 +752,17 @@ void cmVisualStudio10TargetGenerator::WriteSource( const char* tool, cmSourceFile* sf, const char* end) { std::string sourceFile = sf->GetFullPath(); - bool relative = sf->GetCustomCommand()? true:false; - if(relative) - { - // custom command sources must use relative paths or they will - // not show up in the GUI. - sourceFile = cmSystemTools::RelativePath( - this->Makefile->GetCurrentOutputDirectory(), - sourceFile.c_str()); - } - else - { - // do not use a relative path here because it means that you - // can not use as long a path to the file. - } + // do not use a relative path here because it means that you + // can not use as long a path to the file. + // custom command sources must use relative paths or they will + // not show up in the GUI. + bool forceRelative = sf->GetCustomCommand()? true:false; + sourceFile = this->ConvertPath(sourceFile, forceRelative); this->ConvertToWindowsSlash(sourceFile); this->WriteString("<", 2); (*this->BuildFileStream ) << tool << " Include=\"" << sourceFile << "\"" << (end? end : " />\n"); - ToolSource toolSource = {sf, relative}; + ToolSource toolSource = {sf, forceRelative}; this->Tools[tool].push_back(toolSource); } diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index c00d328a6..2d5ec2a6b 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -50,6 +50,7 @@ private: }; struct ToolSources: public std::vector {}; + std::string ConvertPath(std::string const& path, bool forceRelative); void ConvertToWindowsSlash(std::string& s); void WriteString(const char* line, int indentLevel); void WriteProjectConfigurations(); From b2e7c7aef0375d5d676632d8cc3a7c2d96d6a8fa Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 27 Apr 2012 11:00:22 -0400 Subject: [PATCH 4/5] VS11: Do not use source path conversion workaround specific to VS 10 CMake <= 2.8.4 generated VS 10 project files with a relative path to source files. Then commit ed0075bd (Use relative paths for custom command inputs, 2011-06-22) switched to using relative paths only for source files holding custom commands and full paths for other sources. This behavior was inhereted by the VS 11 generator but is not needed so use the workaround only for exactly VS 10. Explain the behavior in comments. --- Source/cmVisualStudio10TargetGenerator.cxx | 29 ++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 7c38d4b6b..f8e4c30b3 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -751,13 +751,28 @@ WriteGroupSources(const char* name, void cmVisualStudio10TargetGenerator::WriteSource( const char* tool, cmSourceFile* sf, const char* end) { - std::string sourceFile = sf->GetFullPath(); - // do not use a relative path here because it means that you - // can not use as long a path to the file. - // custom command sources must use relative paths or they will - // not show up in the GUI. - bool forceRelative = sf->GetCustomCommand()? true:false; - sourceFile = this->ConvertPath(sourceFile, forceRelative); + // Visual Studio tools append relative paths to the current dir, as in: + // + // c:\path\to\current\dir\..\..\..\relative\path\to\source.c + // + // and fail if this exceeds the maximum allowed path length. Our path + // conversion uses full paths outside the build tree to allow deeper trees. + bool forceRelative = false; + std::string sourceFile = this->ConvertPath(sf->GetFullPath(), false); + if(this->LocalGenerator->GetVersion() == cmLocalVisualStudioGenerator::VS10 + && cmSystemTools::FileIsFullPath(sourceFile.c_str())) + { + // Normal path conversion resulted in a full path. VS 10 (but not 11) + // refuses to show the property page in the IDE for a source file with a + // full path (not starting in a '.' or '/' AFAICT). CMake <= 2.8.4 used a + // relative path but to allow deeper build trees now CMake uses a full + // path except for custom commands which work only as relative paths. + if(sf->GetCustomCommand()) + { + forceRelative = true; + sourceFile = this->ConvertPath(sf->GetFullPath(), forceRelative); + } + } this->ConvertToWindowsSlash(sourceFile); this->WriteString("<", 2); (*this->BuildFileStream ) << tool << From d931ce9fe0c26cfddb766901cfd1ccbc9def71c7 Mon Sep 17 00:00:00 2001 From: Brad King Date: Fri, 27 Apr 2012 14:13:02 -0400 Subject: [PATCH 5/5] VS10: Generate relative source paths when possible (#12570) Since commit ed0075bd (Use relative paths for custom command inputs, 2011-06-22) CMake generates full paths to source files in VS 10 project files to avoid trouble with deep source/build tree paths. However, the VS 10 IDE will not populate the source file property dialog for a file referenced by full path. Instead use a relative path when possible. When not possible produce a detailed warning explaining the problem and suggesting use of shorter directory paths. --- Source/cmGlobalVisualStudio10Generator.cxx | 48 ++++++++++++++++++++++ Source/cmGlobalVisualStudio10Generator.h | 14 +++++++ Source/cmVisualStudio10TargetGenerator.cxx | 19 +++++++-- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index 28d738a8f..18a786d78 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -13,6 +13,7 @@ #include "cmGlobalVisualStudio10Generator.h" #include "cmLocalVisualStudio10Generator.h" #include "cmMakefile.h" +#include "cmSourceFile.h" #include "cmake.h" @@ -50,6 +51,38 @@ cmLocalGenerator *cmGlobalVisualStudio10Generator::CreateLocalGenerator() return lg; } +//---------------------------------------------------------------------------- +void cmGlobalVisualStudio10Generator::Generate() +{ + this->LongestSource = LongestSourcePath(); + this->cmGlobalVisualStudio8Generator::Generate(); + if(this->LongestSource.Length > 0) + { + cmMakefile* mf = this->LongestSource.Target->GetMakefile(); + cmOStringStream e; + e << + "The binary and/or source directory paths may be too long to generate " + "Visual Studio 10 files for this project. " + "Consider choosing shorter directory names to build this project with " + "Visual Studio 10. " + "A more detailed explanation follows." + "\n" + "There is a bug in the VS 10 IDE that renders property dialog fields " + "blank for files referenced by full path in the project file. " + "However, CMake must reference at least one file by full path:\n" + " " << this->LongestSource.SourceFile->GetFullPath() << "\n" + "This is because some Visual Studio tools would append the relative " + "path to the end of the referencing directory path, as in:\n" + " " << mf->GetCurrentOutputDirectory() << "/" + << this->LongestSource.SourceRel << "\n" + "and then incorrectly complain that the file does not exist because " + "the path length is too long for some internal buffer or API. " + "To avoid this problem CMake must use a full path for this file " + "which then triggers the VS 10 property dialog bug."; + mf->IssueMessage(cmake::WARNING, e.str().c_str()); + } +} + //---------------------------------------------------------------------------- void cmGlobalVisualStudio10Generator ::GetDocumentation(cmDocumentationEntry& entry) const @@ -230,3 +263,18 @@ cmGlobalVisualStudio10Generator ruleFile += ".rule"; return ruleFile; } + +//---------------------------------------------------------------------------- +void cmGlobalVisualStudio10Generator::PathTooLong( + cmTarget* target, cmSourceFile* sf, std::string const& sfRel) +{ + size_t len = (strlen(target->GetMakefile()->GetCurrentOutputDirectory()) + + 1 + sfRel.length()); + if(len > this->LongestSource.Length) + { + this->LongestSource.Length = len; + this->LongestSource.Target = target; + this->LongestSource.SourceFile = sf; + this->LongestSource.SourceRel = sfRel; + } +} diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index e2cc14ece..423656352 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -46,6 +46,8 @@ public: ///! create the correct local generator virtual cmLocalGenerator *CreateLocalGenerator(); + virtual void Generate(); + /** * Try to determine system infomation such as shared library * extension, pthreads, byte order etc. @@ -79,10 +81,22 @@ public: /** Generate an .rule file path for a given command output. */ virtual std::string GenerateRuleFile(std::string const& output) const; + void PathTooLong(cmTarget* target, cmSourceFile* sf, + std::string const& sfRel); protected: virtual const char* GetIDEVersion() { return "10.0"; } std::string PlatformToolset; bool ExpressEdition; +private: + struct LongestSourcePath + { + LongestSourcePath(): Length(0), Target(0), SourceFile(0) {} + size_t Length; + cmTarget* Target; + cmSourceFile* SourceFile; + std::string SourceRel; + }; + LongestSourcePath LongestSource; }; #endif diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index f8e4c30b3..9a97ab0e3 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -765,12 +765,23 @@ void cmVisualStudio10TargetGenerator::WriteSource( // Normal path conversion resulted in a full path. VS 10 (but not 11) // refuses to show the property page in the IDE for a source file with a // full path (not starting in a '.' or '/' AFAICT). CMake <= 2.8.4 used a - // relative path but to allow deeper build trees now CMake uses a full - // path except for custom commands which work only as relative paths. - if(sf->GetCustomCommand()) + // relative path but to allow deeper build trees CMake 2.8.[5678] used a + // full path except for custom commands. Custom commands do not work + // without a relative path, but they do not seem to be involved in tools + // with the above behavior. For other sources we now use a relative path + // when the combined path will not be too long so property pages appear. + std::string sourceRel = this->ConvertPath(sf->GetFullPath(), true); + size_t const maxLen = 250; + if(sf->GetCustomCommand() || + ((strlen(this->Makefile->GetCurrentOutputDirectory()) + 1 + + sourceRel.length()) <= maxLen)) { forceRelative = true; - sourceFile = this->ConvertPath(sf->GetFullPath(), forceRelative); + sourceFile = sourceRel; + } + else + { + this->GlobalGenerator->PathTooLong(this->Target, sf, sourceRel); } } this->ConvertToWindowsSlash(sourceFile);