| 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);
 | 
| +  }
 | 
| +}
 | 
| 
 |