Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(140)

Unified Diff: tools/gn/visual_studio_writer.cc

Issue 1570113002: Visual Studio generators for GN (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix tests failing on non-Windows platforms Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/gn/visual_studio_writer.cc
diff --git a/tools/gn/visual_studio_writer.cc b/tools/gn/visual_studio_writer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..968e046b72fb129979ea276b5fe8cb0f5cc0aabb
--- /dev/null
+++ b/tools/gn/visual_studio_writer.cc
@@ -0,0 +1,754 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/gn/visual_studio_writer.h"
+
+#include <algorithm>
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "tools/gn/builder.h"
+#include "tools/gn/config.h"
+#include "tools/gn/config_values_extractors.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/path_output.h"
+#include "tools/gn/source_file_type.h"
+#include "tools/gn/standard_out.h"
+#include "tools/gn/target.h"
+#include "tools/gn/variables.h"
+#include "tools/gn/visual_studio_utils.h"
+#include "tools/gn/xml_element_writer.h"
+
+#if defined(OS_WIN)
+#include "base/win/registry.h"
+#endif
+
+namespace {
+
+struct SemicolonSeparatedWriter {
+ void operator()(const std::string& value, std::ostream& out) const {
+ out << value + ';';
+ }
+};
+
+struct IncludeDirWriter {
+ explicit IncludeDirWriter(PathOutput& path_output)
+ : path_output_(path_output) {}
+ ~IncludeDirWriter() = default;
+
+ void operator()(const SourceDir& dir, std::ostream& out) const {
+ path_output_.WriteDir(out, dir, PathOutput::DIR_NO_LAST_SLASH);
+ out << ";";
+ }
+
+ PathOutput& path_output_;
+};
+
+struct SourceFileWriter {
+ SourceFileWriter(PathOutput& path_output, const SourceFile& source_file)
+ : path_output_(path_output), source_file_(source_file) {}
+ ~SourceFileWriter() = default;
+
+ void operator()(std::ostream& out) const {
+ path_output_.WriteFile(out, source_file_);
+ }
+
+ PathOutput& path_output_;
+ const SourceFile& source_file_;
+};
+
+const char kToolsetVersion[] = "v140"; // Visual Studio 2015
+const char kVisualStudioVersion[] = "14.0"; // Visual Studio 2015
+const char kWindowsKitsVersion[] = "10"; // Windows 10 SDK
+const char kWindowsKitsIncludeVersion[] = "10.0.10240.0"; // Windows 10 SDK
+
+const char kGuidTypeProject[] = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}";
+const char kGuidTypeFolder[] = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}";
+const char kGuidSeedProject[] = "project";
+const char kGuidSeedFolder[] = "folder";
+const char kGuidSeedFilter[] = "filter";
+
+std::string GetWindowsKitsIncludeDirs() {
+ std::string kits_path;
+
+#if defined(OS_WIN)
+ const base::char16* const subkeys[] = {
+ L"SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots",
+ L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows Kits\\Installed Roots"};
+
+ base::string16 value_name =
+ base::ASCIIToUTF16("KitsRoot") + base::ASCIIToUTF16(kWindowsKitsVersion);
+
+ for (const base::char16* subkey : subkeys) {
+ base::win::RegKey key(HKEY_LOCAL_MACHINE, subkey, KEY_READ);
+ base::string16 value;
+ if (key.ReadValue(value_name.c_str(), &value) == ERROR_SUCCESS) {
+ kits_path = base::UTF16ToUTF8(value);
+ break;
+ }
+ }
+#endif // OS_WIN
+
+ if (kits_path.empty()) {
+ kits_path = std::string("C:\\Program Files (x86)\\Windows Kits\\") +
+ kWindowsKitsVersion + "\\";
+ }
+
+ return kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\shared;" +
+ kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\um;" +
+ kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\winrt;";
+}
+
+std::string GetConfigurationType(const Target* target, Err* err) {
+ switch (target->output_type()) {
+ case Target::EXECUTABLE:
+ return "Application";
+ case Target::SHARED_LIBRARY:
+ case Target::LOADABLE_MODULE:
+ return "DynamicLibrary";
+ case Target::STATIC_LIBRARY:
+ case Target::SOURCE_SET:
+ return "StaticLibrary";
+
+ default:
+ *err = Err(Location(),
+ "Visual Studio doesn't support '" + target->label().name() +
+ "' target output type: " +
+ Target::GetStringForOutputType(target->output_type()));
+ return std::string();
+ }
+}
+
+void ParseCompilerOptions(const std::vector<std::string>& cflags,
+ CompilerOptions* options) {
+ for (const std::string& flag : cflags)
+ ParseCompilerOption(flag, options);
+}
+
+void ParseCompilerOptions(const Target* target, CompilerOptions* options) {
+ for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
+ ParseCompilerOptions(iter.cur().cflags(), options);
+ ParseCompilerOptions(iter.cur().cflags_c(), options);
+ ParseCompilerOptions(iter.cur().cflags_cc(), options);
+ }
+}
+
+// Returns a string piece pointing into the input string identifying the parent
+// directory path, excluding the last slash. Note that the input pointer must
+// outlive the output.
+base::StringPiece FindParentDir(const std::string* path) {
+ DCHECK(path && !path->empty());
+ for (int i = static_cast<int>(path->size()) - 2; i >= 0; --i) {
+ if (IsSlash((*path)[i]))
+ return base::StringPiece(path->data(), i);
+ }
+ return base::StringPiece();
+}
+
+bool HasSameContent(std::stringstream& data_1, const base::FilePath& data_2) {
+ // Compare file sizes first. Quick and will save us some time if they are
+ // different sizes.
+ int64_t data_1_len = data_1.tellp();
+
+ int64_t data_2_len;
+ if (!base::GetFileSize(data_2, &data_2_len) || data_1_len != data_2_len)
+ return false;
+
+ std::string data_2_data;
+ data_2_data.resize(data_2_len);
+ if (!base::ReadFileToString(data_2, &data_2_data))
+ return false;
+
+ std::string data_1_data;
+ data_1_data = data_1.str();
+
+ return data_1_data == data_2_data;
+}
+
+} // namespace
+
+VisualStudioWriter::SolutionEntry::SolutionEntry(const std::string& _name,
+ const std::string& _path,
+ const std::string& _guid)
+ : name(_name), path(_path), guid(_guid), parent_folder(nullptr) {}
+
+VisualStudioWriter::SolutionEntry::~SolutionEntry() = default;
+
+VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings)
+ : build_settings_(build_settings) {
+ const Value* value = build_settings->build_args().GetArgOverride("is_debug");
+ is_debug_config_ = value == nullptr || value->boolean_value();
+ config_platform_ = "Win32";
+ value = build_settings->build_args().GetArgOverride(variables::kTargetCpu);
+ if (value != nullptr && value->string_value() == "x64")
+ config_platform_ = "x64";
+
+ windows_kits_include_dirs_ = GetWindowsKitsIncludeDirs();
+}
+
+VisualStudioWriter::~VisualStudioWriter() {
+ STLDeleteContainerPointers(projects_.begin(), projects_.end());
+ STLDeleteContainerPointers(folders_.begin(), folders_.end());
+}
+
+// static
+bool VisualStudioWriter::RunAndWriteFiles(const BuildSettings* build_settings,
+ Builder* builder,
+ Err* err) {
+ std::vector<const Target*> targets = builder->GetAllResolvedTargets();
+
+ VisualStudioWriter writer(build_settings);
+ writer.projects_.reserve(targets.size());
+ writer.folders_.reserve(targets.size());
+
+ std::set<std::string> processed_targets;
+ for (const Target* target : targets) {
+ // Skip targets which are duplicated in vector.
+ std::string target_path =
+ target->label().dir().value() + target->label().name();
+ if (processed_targets.find(target_path) != processed_targets.end())
+ continue;
+
+ // Skip actions and groups.
+ if (target->output_type() == Target::GROUP ||
+ target->output_type() == Target::COPY_FILES ||
+ target->output_type() == Target::ACTION ||
+ target->output_type() == Target::ACTION_FOREACH) {
+ continue;
+ }
+
+ if (!writer.WriteProjectFiles(target, err))
+ return false;
+
+ processed_targets.insert(target_path);
+ }
+
+ if (writer.projects_.empty()) {
+ *err = Err(Location(), "No Visual Studio projects generated.");
+ return false;
+ }
+
+ writer.ResolveSolutionFolders();
+ return writer.WriteSolutionFile(err);
+}
+
+bool VisualStudioWriter::WriteProjectFiles(const Target* target, Err* err) {
+ SourceFile target_file = GetTargetOutputDir(target).ResolveRelativeFile(
+ Value(nullptr, target->label().name() + ".vcxproj"), err);
+ if (target_file.is_null())
+ return false;
+
+ base::FilePath vcxproj_path = build_settings_->GetFullPath(target_file);
+ std::string vcxproj_path_str = FilePathToUTF8(vcxproj_path);
+
+ projects_.push_back(
+ new SolutionEntry(target->label().name(), vcxproj_path_str,
+ MakeGuid(vcxproj_path_str, kGuidSeedProject)));
+ projects_.back()->label_dir_path =
+ FilePathToUTF8(build_settings_->GetFullPath(target->label().dir()));
+
+ std::stringstream vcxproj_string_out;
+ if (!WriteProjectFileContents(vcxproj_string_out, *projects_.back(), target,
+ err)) {
+ projects_.pop_back();
+ return false;
+ }
+
+ // Only write the content to the file if it's different. That is
+ // both a performance optimization and more importantly, prevents
+ // Visual Studio from reloading the projects.
+ if (!HasSameContent(vcxproj_string_out, vcxproj_path)) {
+ std::string content = vcxproj_string_out.str();
+ int size = static_cast<int>(content.size());
+ if (base::WriteFile(vcxproj_path, content.c_str(), size) != size) {
+ *err = Err(Location(), "Couldn't open " + target->label().name() +
+ ".vcxproj for writing");
+ return false;
+ }
+ }
+
+ base::FilePath filters_path = UTF8ToFilePath(vcxproj_path_str + ".filters");
+
+ std::stringstream filters_string_out;
+ WriteFiltersFileContents(filters_string_out, target);
+
+ if (!HasSameContent(filters_string_out, filters_path)) {
+ std::string content = filters_string_out.str();
+ int size = static_cast<int>(content.size());
+ if (base::WriteFile(filters_path, content.c_str(), size) != size) {
+ *err = Err(Location(), "Couldn't open " + target->label().name() +
+ ".vcxproj.filters for writing");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool VisualStudioWriter::WriteProjectFileContents(
+ std::ostream& out,
+ const SolutionEntry& solution_project,
+ const Target* target,
+ Err* err) {
+ PathOutput path_output(GetTargetOutputDir(target),
+ build_settings_->root_path_utf8(),
+ EscapingMode::ESCAPE_NONE);
+
+ out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
+ XmlElementWriter project(
+ out, "Project",
+ XmlAttributes("DefaultTargets", "Build")
+ .add("ToolsVersion", kVisualStudioVersion)
+ .add("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"));
+
+ {
+ scoped_ptr<XmlElementWriter> configurations = project.SubElement(
+ "ItemGroup", XmlAttributes("Label", "ProjectConfigurations"));
+ std::string config_name = is_debug_config_ ? "Debug" : "Release";
+ scoped_ptr<XmlElementWriter> project_config = configurations->SubElement(
+ "ProjectConfiguration",
+ XmlAttributes("Include", config_name + '|' + config_platform_));
+ project_config->SubElement("Configuration")->Text(config_name);
+ project_config->SubElement("Platform")->Text(config_platform_);
+ }
+
+ {
+ scoped_ptr<XmlElementWriter> globals =
+ project.SubElement("PropertyGroup", XmlAttributes("Label", "Globals"));
+ globals->SubElement("ProjectGuid")->Text(solution_project.guid);
+ globals->SubElement("Keyword")->Text("Win32Proj");
+ globals->SubElement("RootNamespace")->Text(target->label().name());
+ globals->SubElement("IgnoreWarnCompileDuplicatedFilename")->Text("true");
+ globals->SubElement("PreferredToolArchitecture")->Text("x64");
+ }
+
+ project.SubElement(
+ "Import", XmlAttributes("Project",
+ "$(VCTargetsPath)\\Microsoft.Cpp.Default.props"));
+
+ {
+ scoped_ptr<XmlElementWriter> configuration = project.SubElement(
+ "PropertyGroup", XmlAttributes("Label", "Configuration"));
+ configuration->SubElement("CharacterSet")->Text("Unicode");
+ std::string configuration_type = GetConfigurationType(target, err);
+ if (configuration_type.empty())
+ return false;
+ configuration->SubElement("ConfigurationType")->Text(configuration_type);
+ }
+
+ {
+ scoped_ptr<XmlElementWriter> locals =
+ project.SubElement("PropertyGroup", XmlAttributes("Label", "Locals"));
+ locals->SubElement("PlatformToolset")->Text(kToolsetVersion);
+ }
+
+ project.SubElement(
+ "Import",
+ XmlAttributes("Project", "$(VCTargetsPath)\\Microsoft.Cpp.props"));
+ project.SubElement(
+ "Import",
+ XmlAttributes("Project",
+ "$(VCTargetsPath)\\BuildCustomizations\\masm.props"));
+ project.SubElement("ImportGroup",
+ XmlAttributes("Label", "ExtensionSettings"));
+
+ {
+ scoped_ptr<XmlElementWriter> property_sheets = project.SubElement(
+ "ImportGroup", XmlAttributes("Label", "PropertySheets"));
+ property_sheets->SubElement(
+ "Import",
+ XmlAttributes(
+ "Condition",
+ "exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')")
+ .add("Label", "LocalAppDataPlatform")
+ .add("Project",
+ "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props"));
+ }
+
+ project.SubElement("PropertyGroup", XmlAttributes("Label", "UserMacros"));
+
+ {
+ scoped_ptr<XmlElementWriter> properties =
+ project.SubElement("PropertyGroup");
+ {
+ scoped_ptr<XmlElementWriter> out_dir = properties->SubElement("OutDir");
+ path_output.WriteDir(out_dir->StartContent(false),
+ build_settings_->build_dir(),
+ PathOutput::DIR_INCLUDE_LAST_SLASH);
+ }
+ properties->SubElement("TargetName")->Text("$(ProjectName)");
+ properties->SubElement("TargetPath")
+ ->Text("$(OutDir)\\$(ProjectName)$(TargetExt)");
+ }
+
+ {
+ scoped_ptr<XmlElementWriter> item_definitions =
+ project.SubElement("ItemDefinitionGroup");
+ {
+ scoped_ptr<XmlElementWriter> cl_compile =
+ item_definitions->SubElement("ClCompile");
+ {
+ scoped_ptr<XmlElementWriter> include_dirs =
+ cl_compile->SubElement("AdditionalIncludeDirectories");
+ RecursiveTargetConfigToStream<SourceDir>(
+ target, &ConfigValues::include_dirs, IncludeDirWriter(path_output),
+ include_dirs->StartContent(false));
+ include_dirs->Text(windows_kits_include_dirs_ +
+ "$(VSInstallDir)\\VC\\atlmfc\\include;" +
+ "%(AdditionalIncludeDirectories)");
+ }
+ CompilerOptions options;
+ ParseCompilerOptions(target, &options);
+ if (!options.additional_options.empty()) {
+ cl_compile->SubElement("AdditionalOptions")
+ ->Text(options.additional_options + "%(AdditionalOptions)");
+ }
+ if (!options.buffer_security_check.empty()) {
+ cl_compile->SubElement("BufferSecurityCheck")
+ ->Text(options.buffer_security_check);
+ }
+ cl_compile->SubElement("CompileAsWinRT")->Text("false");
+ cl_compile->SubElement("DebugInformationFormat")->Text("ProgramDatabase");
+ if (!options.disable_specific_warnings.empty()) {
+ cl_compile->SubElement("DisableSpecificWarnings")
+ ->Text(options.disable_specific_warnings +
+ "%(DisableSpecificWarnings)");
+ }
+ cl_compile->SubElement("ExceptionHandling")->Text("false");
+ if (!options.forced_include_files.empty()) {
+ cl_compile->SubElement("ForcedIncludeFiles")
+ ->Text(options.forced_include_files);
+ }
+ cl_compile->SubElement("MinimalRebuild")->Text("false");
+ if (!options.optimization.empty())
+ cl_compile->SubElement("Optimization")->Text(options.optimization);
+ if (target->config_values().has_precompiled_headers()) {
+ cl_compile->SubElement("PrecompiledHeader")->Text("Use");
+ cl_compile->SubElement("PrecompiledHeaderFile")
+ ->Text(target->config_values().precompiled_header());
+ } else {
+ cl_compile->SubElement("PrecompiledHeader")->Text("NotUsing");
+ }
+ {
+ scoped_ptr<XmlElementWriter> preprocessor_definitions =
+ cl_compile->SubElement("PreprocessorDefinitions");
+ RecursiveTargetConfigToStream<std::string>(
+ target, &ConfigValues::defines, SemicolonSeparatedWriter(),
+ preprocessor_definitions->StartContent(false));
+ preprocessor_definitions->Text("%(PreprocessorDefinitions)");
+ }
+ if (!options.runtime_library.empty())
+ cl_compile->SubElement("RuntimeLibrary")->Text(options.runtime_library);
+ if (!options.treat_warning_as_error.empty()) {
+ cl_compile->SubElement("TreatWarningAsError")
+ ->Text(options.treat_warning_as_error);
+ }
+ if (!options.warning_level.empty())
+ cl_compile->SubElement("WarningLevel")->Text(options.warning_level);
+ }
+
+ // We don't include resource compilation and link options as ninja files
+ // are used to generate real build.
+ }
+
+ {
+ scoped_ptr<XmlElementWriter> group = project.SubElement("ItemGroup");
+ if (!target->config_values().precompiled_source().is_null()) {
+ group
+ ->SubElement(
+ "ClCompile", "Include",
+ SourceFileWriter(path_output,
+ target->config_values().precompiled_source()))
+ ->SubElement("PrecompiledHeader")
+ ->Text("Create");
+ }
+
+ for (const SourceFile& file : target->sources()) {
+ SourceFileType type = GetSourceFileType(file);
+ if (type == SOURCE_H || type == SOURCE_CPP || type == SOURCE_C) {
+ group->SubElement(type == SOURCE_H ? "ClInclude" : "ClCompile",
+ "Include", SourceFileWriter(path_output, file));
+ }
+ }
+ }
+
+ project.SubElement(
+ "Import",
+ XmlAttributes("Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets"));
+ project.SubElement(
+ "Import",
+ XmlAttributes("Project",
+ "$(VCTargetsPath)\\BuildCustomizations\\masm.targets"));
+ project.SubElement("ImportGroup", XmlAttributes("Label", "ExtensionTargets"));
+
+ {
+ scoped_ptr<XmlElementWriter> build =
+ project.SubElement("Target", XmlAttributes("Name", "Build"));
+ build->SubElement(
+ "Exec",
+ XmlAttributes("Command", "call ninja.exe -C $(OutDir) $(ProjectName)"));
+ }
+
+ {
+ scoped_ptr<XmlElementWriter> clean =
+ project.SubElement("Target", XmlAttributes("Name", "Clean"));
+ clean->SubElement(
+ "Exec",
+ XmlAttributes("Command",
+ "call ninja.exe -C $(OutDir) -tclean $(ProjectName)"));
+ }
+
+ return true;
+}
+
+void VisualStudioWriter::WriteFiltersFileContents(std::ostream& out,
+ const Target* target) {
+ out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
+ XmlElementWriter project(
+ out, "Project",
+ XmlAttributes("ToolsVersion", "4.0")
+ .add("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"));
+
+ std::ostringstream files_out;
+
+ {
+ scoped_ptr<XmlElementWriter> filters_group =
+ project.SubElement("ItemGroup");
+ XmlElementWriter files_group(files_out, "ItemGroup", XmlAttributes(), 2);
+
+ // File paths are relative to vcxproj files which are generated to out dirs.
+ // Filters tree structure need to reflect source directories and be relative
+ // to target file. We need two path outputs then.
+ PathOutput file_path_output(GetTargetOutputDir(target),
+ build_settings_->root_path_utf8(),
+ EscapingMode::ESCAPE_NONE);
+ PathOutput filter_path_output(target->label().dir(),
+ build_settings_->root_path_utf8(),
+ EscapingMode::ESCAPE_NONE);
+
+ std::set<std::string> processed_filters;
+
+ for (const SourceFile& file : target->sources()) {
+ SourceFileType type = GetSourceFileType(file);
+ if (type == SOURCE_H || type == SOURCE_CPP || type == SOURCE_C) {
+ scoped_ptr<XmlElementWriter> cl_item = files_group.SubElement(
+ type == SOURCE_H ? "ClInclude" : "ClCompile", "Include",
+ SourceFileWriter(file_path_output, file));
+
+ std::ostringstream target_relative_out;
+ filter_path_output.WriteFile(target_relative_out, file);
+ std::string target_relative_path = target_relative_out.str();
+ ConvertPathToSystem(&target_relative_path);
+ base::StringPiece filter_path = FindParentDir(&target_relative_path);
+
+ if (!filter_path.empty()) {
+ std::string filter_path_str = filter_path.as_string();
+ while (processed_filters.find(filter_path_str) ==
+ processed_filters.end()) {
+ auto it = processed_filters.insert(filter_path_str).first;
+ filters_group
+ ->SubElement("Filter",
+ XmlAttributes("Include", filter_path_str))
+ ->SubElement("UniqueIdentifier")
+ ->Text(MakeGuid(filter_path_str, kGuidSeedFilter));
+ filter_path_str = FindParentDir(&(*it)).as_string();
+ if (filter_path_str.empty())
+ break;
+ }
+ cl_item->SubElement("Filter")->Text(filter_path);
+ }
+ }
+ }
+ }
+
+ project.Text(files_out.str());
+}
+
+bool VisualStudioWriter::WriteSolutionFile(Err* err) {
+ SourceFile sln_file = build_settings_->build_dir().ResolveRelativeFile(
+ Value(nullptr, "all.sln"), err);
+ if (sln_file.is_null())
+ return false;
+
+ base::FilePath sln_path = build_settings_->GetFullPath(sln_file);
+
+ std::stringstream string_out;
+ WriteSolutionFileContents(string_out, sln_path.DirName());
+
+ // Only write the content to the file if it's different. That is
+ // both a performance optimization and more importantly, prevents
+ // Visual Studio from reloading the projects.
+ if (HasSameContent(string_out, sln_path))
+ return true;
+
+ std::string content = string_out.str();
+ int size = static_cast<int>(content.size());
+ if (base::WriteFile(sln_path, content.c_str(), size) != size) {
+ *err = Err(Location(), "Couldn't open all.sln for writing");
+ return false;
+ }
+
+ return true;
+}
+
+void VisualStudioWriter::WriteSolutionFileContents(
+ std::ostream& out,
+ const base::FilePath& solution_dir_path) {
+ out << "Microsoft Visual Studio Solution File, Format Version 12.00"
+ << std::endl;
+ out << "# Visual Studio 2015" << std::endl;
+
+ SourceDir solution_dir(FilePathToUTF8(solution_dir_path));
+ for (const SolutionEntry* folder : folders_) {
+ out << "Project(\"" << kGuidTypeFolder << "\") = \"(" << folder->name
+ << ")\", \"" << RebasePath(folder->path, solution_dir, "/") << "\", \""
+ << folder->guid << "\"" << std::endl;
+ out << "EndProject" << std::endl;
+ }
+
+ for (const SolutionEntry* project : projects_) {
+ out << "Project(\"" << kGuidTypeProject << "\") = \"" << project->name
+ << "\", \"" << RebasePath(project->path, solution_dir, "/") << "\", \""
+ << project->guid << "\"" << std::endl;
+ out << "EndProject" << std::endl;
+ }
+
+ out << "Global" << std::endl;
+
+ out << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"
+ << std::endl;
+ const std::string config_mode =
+ std::string(is_debug_config_ ? "Debug" : "Release") + '|' +
+ config_platform_;
+ out << "\t\t" << config_mode << " = " << config_mode << std::endl;
+ out << "\tEndGlobalSection" << std::endl;
+
+ out << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"
+ << std::endl;
+ for (const SolutionEntry* project : projects_) {
+ out << "\t\t" << project->guid << '.' << config_mode
+ << ".ActiveCfg = " << config_mode << std::endl;
+ out << "\t\t" << project->guid << '.' << config_mode
+ << ".Build.0 = " << config_mode << std::endl;
+ }
+ out << "\tEndGlobalSection" << std::endl;
+
+ out << "\tGlobalSection(SolutionProperties) = preSolution" << std::endl;
+ out << "\t\tHideSolutionNode = FALSE" << std::endl;
+ out << "\tEndGlobalSection" << std::endl;
+
+ out << "\tGlobalSection(NestedProjects) = preSolution" << std::endl;
+ for (const SolutionEntry* folder : folders_) {
+ if (folder->parent_folder) {
+ out << "\t\t" << folder->guid << " = " << folder->parent_folder->guid
+ << std::endl;
+ }
+ }
+ for (const SolutionEntry* project : projects_) {
+ out << "\t\t" << project->guid << " = " << project->parent_folder->guid
+ << std::endl;
+ }
+ out << "\tEndGlobalSection" << std::endl;
+
+ out << "EndGlobal" << std::endl;
+}
+
+void VisualStudioWriter::ResolveSolutionFolders() {
+ root_folder_path_.clear();
+
+ // Get all project directories. Create solution folder for each directory.
+ std::map<base::StringPiece, SolutionEntry*> processed_paths;
+ for (SolutionEntry* project : projects_) {
+ base::StringPiece folder_path = project->label_dir_path;
+ if (IsSlash(folder_path[folder_path.size() - 1]))
+ folder_path = folder_path.substr(0, folder_path.size() - 1);
+ auto it = processed_paths.find(folder_path);
+ if (it != processed_paths.end()) {
+ project->parent_folder = it->second;
+ } else {
+ std::string folder_path_str = folder_path.as_string();
+ SolutionEntry* folder = new SolutionEntry(
+ FindLastDirComponent(SourceDir(folder_path)).as_string(),
+ folder_path_str, MakeGuid(folder_path_str, kGuidSeedFolder));
+ folders_.push_back(folder);
+ project->parent_folder = folder;
+ processed_paths[folder_path] = folder;
+
+ if (root_folder_path_.empty()) {
+ root_folder_path_ = folder_path_str;
+ } else {
+ size_t common_prefix_len = 0;
+ size_t max_common_length =
+ std::min(root_folder_path_.size(), folder_path.size());
+ size_t i;
+ for (i = common_prefix_len; i < max_common_length; ++i) {
+ if (IsSlash(root_folder_path_[i]) && IsSlash(folder_path[i]))
+ common_prefix_len = i + 1;
+ else if (root_folder_path_[i] != folder_path[i])
+ break;
+ }
+ if (i == max_common_length)
+ common_prefix_len = max_common_length;
+ if (common_prefix_len < root_folder_path_.size()) {
+ if (IsSlash(root_folder_path_[common_prefix_len - 1]))
+ --common_prefix_len;
+ root_folder_path_ = root_folder_path_.substr(0, common_prefix_len);
+ }
+ }
+ }
+ }
+
+ // Create also all parent folders up to |root_folder_path_|.
+ SolutionEntries additional_folders;
+ for (SolutionEntry* folder : folders_) {
+ if (folder->path == root_folder_path_)
+ continue;
+
+ base::StringPiece parent_path;
+ while ((parent_path = FindParentDir(&folder->path)) != root_folder_path_) {
+ auto it = processed_paths.find(parent_path);
+ if (it != processed_paths.end()) {
+ folder = it->second;
+ } else {
+ folder = new SolutionEntry(
+ FindLastDirComponent(SourceDir(parent_path)).as_string(),
+ parent_path.as_string(),
+ MakeGuid(parent_path.as_string(), kGuidSeedFolder));
+ additional_folders.push_back(folder);
+ processed_paths[parent_path] = folder;
+ }
+ }
+ }
+ folders_.insert(folders_.end(), additional_folders.begin(),
+ additional_folders.end());
+
+ // Sort folders by path.
+ std::sort(folders_.begin(), folders_.end(),
+ [](const SolutionEntry* a, const SolutionEntry* b) {
+ return a->path < b->path;
+ });
+
+ // Match subfolders with their parents. Since |folders_| are sorted by path we
+ // know that parent folder always precedes its children in vector.
+ SolutionEntries parents;
+ for (SolutionEntry* folder : folders_) {
+ while (!parents.empty()) {
+ if (base::StartsWith(folder->path, parents.back()->path,
+ base::CompareCase::SENSITIVE)) {
+ folder->parent_folder = parents.back();
+ break;
+ } else {
+ parents.pop_back();
+ }
+ }
+ parents.push_back(folder);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698