Chromium Code Reviews| 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..f2838531b6686759d4fc408edbe3b84149194d4d |
| --- /dev/null |
| +++ b/tools/gn/visual_studio_writer.cc |
| @@ -0,0 +1,603 @@ |
| +// Copyright (c) 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 <fstream> |
| +#include <iostream> |
| +#include <string> |
| +#include <utility> |
| + |
| +#include "base/logging.h" |
| +#include "base/md5.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/strings/string_util.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/target.h" |
| +#include "tools/gn/variables.h" |
| + |
| +struct ProjectConfiguration { |
| + bool is_debug; |
| + std::string platform; |
| +}; |
| + |
| +namespace { |
| + |
| +using XmlAttributes = std::vector<std::pair<std::string, std::string>>; |
| + |
| +class XmlElement { |
|
brettw
2016/01/08 23:51:50
I feel like this should be XMLElementOuptut or XML
Tomasz Moniuszko
2016/01/21 10:50:02
Done.
|
| + public: |
| + XmlElement(std::ostream& out, |
| + const std::string& tag, |
| + const XmlAttributes& attributes); |
| + XmlElement(std::ostream& out, |
| + const std::string& tag, |
| + const XmlAttributes& attributes, |
| + int indent); |
| + template <class Writer> |
| + XmlElement(std::ostream& out, |
| + const std::string& tag, |
| + const std::string& attribute_name, |
| + const Writer& attribute_value_writer, |
| + int indent); |
| + ~XmlElement(); |
| + |
| + void Text(const std::string& content); |
| + |
| + scoped_ptr<XmlElement> SubElement(const std::string& tag); |
| + scoped_ptr<XmlElement> SubElement(const std::string& tag, |
| + const XmlAttributes& attributes); |
| + template <class Writer> |
| + scoped_ptr<XmlElement> SubElement(const std::string& tag, |
| + const std::string& attribute_name, |
| + const Writer& attribute_value_writer); |
| + |
| + private: |
| + std::ostream& out_; |
| + std::string tag_; |
| + int indent_; |
| + bool one_line_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(XmlElement); |
| +}; |
| + |
| +XmlElement::XmlElement(std::ostream& out, |
| + const std::string& tag, |
| + const XmlAttributes& attributes) |
| + : XmlElement(out, tag, attributes, 0) {} |
| + |
| +XmlElement::XmlElement(std::ostream& out, |
| + const std::string& tag, |
| + const XmlAttributes& attributes, |
| + int indent) |
| + : out_(out), tag_(tag), indent_(indent), one_line_(true) { |
| + out << std::string(indent, ' ') << '<' << tag; |
| + for (auto attribute : attributes) { |
|
brettw
2016/01/08 23:51:50
I haven't been using {} for single-line stuff like
Tomasz Moniuszko
2016/01/21 10:50:03
Done.
|
| + out << ' ' << attribute.first << "=\"" << attribute.second << '"'; |
| + } |
| + out << '>'; |
| +} |
| + |
| +template <class Writer> |
| +XmlElement::XmlElement(std::ostream& out, |
| + const std::string& tag, |
| + const std::string& attribute_name, |
| + const Writer& attribute_value_writer, |
| + int indent) |
| + : out_(out), tag_(tag), indent_(indent), one_line_(true) { |
| + out << std::string(indent, ' ') << '<' << tag; |
| + out << ' ' << attribute_name << "=\""; |
| + attribute_value_writer(out); |
| + out << "\">"; |
| +} |
| + |
| +XmlElement::~XmlElement() { |
| + if (!one_line_) |
| + out_ << std::string(indent_, ' '); |
| + out_ << "</" << tag_ << '>' << std::endl; |
| +} |
| + |
| +void XmlElement::Text(const std::string& content) { |
| + out_ << content; |
| +} |
| + |
| +scoped_ptr<XmlElement> XmlElement::SubElement(const std::string& tag) { |
| + return SubElement(tag, XmlAttributes()); |
| +} |
| + |
| +scoped_ptr<XmlElement> XmlElement::SubElement(const std::string& tag, |
| + const XmlAttributes& attributes) { |
| + if (one_line_) { |
| + out_ << std::endl; |
| + one_line_ = false; |
| + } |
| + return make_scoped_ptr(new XmlElement(out_, tag, attributes, indent_ + 2)); |
| +} |
| + |
| +template <class Writer> |
| +scoped_ptr<XmlElement> XmlElement::SubElement( |
| + const std::string& tag, |
| + const std::string& attribute_name, |
| + const Writer& attribute_value_writer) { |
| + if (one_line_) { |
| + out_ << std::endl; |
| + one_line_ = false; |
| + } |
| + return make_scoped_ptr(new XmlElement(out_, tag, attribute_name, |
| + attribute_value_writer, indent_ + 2)); |
| +} |
| + |
| +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_; |
| +}; |
| + |
| +// Some compiler options which will be written to project file. We don't need to |
| +// specify all options because generated project file is going to be used only |
| +// for compilation of single file. For real build ninja files are used. |
| +struct CompilerOptions { |
| + std::string additional_options; |
| + std::string buffer_security_check; |
| + std::string forced_include_files; |
| + std::string disable_specific_warnings; |
| + std::string optimization; |
| + std::string runtime_library; |
| + std::string treat_warning_as_error; |
| + std::string warning_level; |
| +}; |
| + |
| +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 |
| + |
| +std::string GetWindowsKitsIncludeDirs() { |
| + // TODO(tmoniuszko): This should be taken from GN args or system environment. |
|
brettw
2016/01/08 23:51:50
I think we should be able to extract these from th
Tomasz Moniuszko
2016/01/21 10:50:03
It's not available in include_dirs. In GYP build i
|
| + std::string 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 MakeProjectGuid(std::string target_name) { |
| + std::string str = base::MD5String(target_name); |
| + return '{' + str.substr(0, 8) + '-' + str.substr(8, 4) + '-' + |
| + str.substr(12, 4) + '-' + str.substr(16, 4) + '-' + |
| + str.substr(20, 12) + '}'; |
| +} |
| + |
| +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"; |
| + } |
| + |
| + *err = Err(Location(), |
| + "Visual Studio doesn't support '" + target->label().name() + |
| + "' target output type: " + |
| + Target::GetStringForOutputType(target->output_type())); |
| + return ""; |
|
brettw
2016/01/08 23:51:50
return std::string();
instead. More semantically c
Tomasz Moniuszko
2016/01/21 10:50:03
Done.
|
| +} |
| + |
| +#define SetOption(condition, member, value) \ |
| + if (condition) { \ |
| + options->member = value; \ |
| + return; \ |
| + } |
| + |
| +#define AppendOption(condition, member, value, separator) \ |
| + if (condition) { \ |
| + options->member += value + separator; \ |
| + return; \ |
| + } |
| + |
| +void ParseCompilerOption(const std::string& cflag, CompilerOptions* options) { |
|
brettw
2016/01/08 23:51:50
You can do this in a follow-up, but I would sugges
brettw
2016/01/08 23:51:50
Just checking that we really need to do this. I mi
Tomasz Moniuszko
2016/01/21 10:50:02
It doesn't work properly because project propertie
|
| + if (cflag.size() > 2 && cflag[0] == '/') { |
| + switch (cflag[1]) { |
| + case 'F': |
| + AppendOption(cflag.size() > 3 && cflag[2] == 'I', forced_include_files, |
| + cflag.substr(3), ';') |
| + break; |
| + |
| + case 'G': |
| + if (cflag[2] == 'S') { |
| + SetOption(cflag.size() == 3, buffer_security_check, "true") |
| + SetOption(cflag.size() == 4 && cflag[3] == '-', buffer_security_check, |
| + "false") |
| + } |
| + break; |
| + |
| + case 'M': |
| + switch (cflag[2]) { |
| + case 'D': |
| + SetOption(cflag.size() == 3, runtime_library, "MultiThreadedDLL") |
| + SetOption(cflag.size() == 4 && cflag[3] == 'd', runtime_library, |
| + "MultiThreadedDebugDLL") |
| + break; |
| + |
| + case 'T': |
| + SetOption(cflag.size() == 3, runtime_library, "MultiThreaded") |
| + SetOption(cflag.size() == 4 && cflag[3] == 'd', runtime_library, |
| + "MultiThreadedDebug") |
| + break; |
| + } |
| + break; |
| + |
| + case 'O': |
| + switch (cflag[2]) { |
| + case '2': |
| + SetOption(cflag.size() == 3, optimization, "MaxSpeed") |
| + break; |
| + |
| + case 'd': |
| + SetOption(cflag.size() == 3, optimization, "Disabled") |
| + break; |
| + } |
| + break; |
| + |
| + case 'T': |
| + // Skip flags that cause treating all source files as C and C++ files. |
| + if (cflag.size() == 3 && (cflag[2] == 'C' || cflag[2] == 'P')) |
| + return; |
| + break; |
| + |
| + case 'W': |
| + switch (cflag[2]) { |
| + case '0': |
| + case '1': |
| + case '2': |
| + case '3': |
| + case '4': |
| + SetOption(cflag.size() == 3, warning_level, |
| + std::string("Level") + cflag[2]) |
| + break; |
| + |
| + case 'X': |
| + SetOption(cflag.size() == 3, treat_warning_as_error, "true") |
| + break; |
| + } |
| + break; |
| + |
| + case 'w': |
| + AppendOption(cflag.size() > 3 && cflag[2] == 'd', |
| + disable_specific_warnings, cflag.substr(3), ';') |
| + break; |
| + } |
| + } |
| + |
| + // Put everything else into additional_options. |
| + options->additional_options += cflag + ' '; |
| +} |
| + |
| +#undef SetOption |
| +#undef AppendOption |
| + |
| +void ParseCompilerOptions(const std::vector<std::string>& cflags, |
| + CompilerOptions* options) { |
| + for (const std::string& flag : cflags) { |
|
brettw
2016/01/08 23:51:50
No {}
Tomasz Moniuszko
2016/01/21 10:50:03
Done.
|
| + 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); |
| + } |
| +} |
| + |
| +bool WriteMsBuildProject(const BuildSettings* build_settings, |
| + std::ostream& out, |
| + const Target* target, |
| + const ProjectConfiguration& config, |
| + Err* err) { |
| + PathOutput path_output(target->label().dir(), |
| + build_settings->root_path_utf8(), |
| + EscapingMode::ESCAPE_NONE); |
| + |
| + out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl; |
| + XmlElement project( |
| + out, "Project", |
| + {{"DefaultTargets", "Build"}, |
| + {"ToolsVersion", kVisualStudioVersion}, |
| + {"xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"}}); |
| + |
| + { |
| + scoped_ptr<XmlElement> configurations = |
| + project.SubElement("ItemGroup", {{"Label", "ProjectConfigurations"}}); |
| + std::string config_name = config.is_debug ? "Debug" : "Release"; |
| + scoped_ptr<XmlElement> project_config = configurations->SubElement( |
| + "ProjectConfiguration", |
| + {{"Include", config_name + '|' + config.platform}}); |
| + project_config->SubElement("Configuration")->Text(config_name); |
| + project_config->SubElement("Platform")->Text(config.platform); |
| + } |
| + |
| + { |
| + scoped_ptr<XmlElement> globals = |
| + project.SubElement("PropertyGroup", {{"Label", "Globals"}}); |
| + globals->SubElement("ProjectGuid") |
| + ->Text(MakeProjectGuid(target->label().name())); |
| + 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", {{"Project", "$(VCTargetsPath)\\Microsoft.Cpp.Default.props"}}); |
| + |
| + { |
| + scoped_ptr<XmlElement> configuration = |
| + project.SubElement("PropertyGroup", {{"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<XmlElement> locals = |
| + project.SubElement("PropertyGroup", {{"Label", "Locals"}}); |
| + locals->SubElement("PlatformToolset")->Text(kToolsetVersion); |
| + } |
| + |
| + project.SubElement("Import", |
| + {{"Project", "$(VCTargetsPath)\\Microsoft.Cpp.props"}}); |
| + project.SubElement( |
| + "Import", |
| + {{"Project", "$(VCTargetsPath)\\BuildCustomizations\\masm.props"}}); |
| + project.SubElement("ImportGroup", {{"Label", "ExtensionSettings"}}); |
| + |
| + { |
| + scoped_ptr<XmlElement> property_sheets = |
| + project.SubElement("ImportGroup", {{"Label", "PropertySheets"}}); |
| + property_sheets->SubElement( |
| + "Import", |
| + { |
| + {"Condition", |
| + "exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')"}, |
| + {"Label", "LocalAppDataPlatform"}, |
| + {"Project", "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props"}, |
| + }); |
| + } |
| + |
| + project.SubElement("PropertyGroup", {{"Label", "UserMacros"}}); |
| + |
| + { |
| + scoped_ptr<XmlElement> properties = project.SubElement("PropertyGroup"); |
| + { |
| + scoped_ptr<XmlElement> out_dir = properties->SubElement("OutDir"); |
| + path_output.WriteDir(out, build_settings->build_dir(), |
| + PathOutput::DIR_INCLUDE_LAST_SLASH); |
| + } |
| + properties->SubElement("LinkIncremental") |
|
Daniel Bratell
2016/01/14 12:08:56
LinkIncremental really needed? VS will not do any
Tomasz Moniuszko
2016/01/21 10:50:02
I thought it may conflict with command-line option
|
| + ->Text(config.is_debug ? "true" : "false"); |
| + properties->SubElement("TargetName")->Text("$(ProjectName)"); |
| + properties->SubElement("TargetPath") |
| + ->Text("$(OutDir)\\$(ProjectName)$(TargetExt)"); |
| + } |
| + |
| + { |
| + scoped_ptr<XmlElement> item_definitions = |
| + project.SubElement("ItemDefinitionGroup"); |
| + { |
| + scoped_ptr<XmlElement> cl_compile = |
| + item_definitions->SubElement("ClCompile"); |
| + { |
| + scoped_ptr<XmlElement> include_dirs = |
| + cl_compile->SubElement("AdditionalIncludeDirectories"); |
| + RecursiveTargetConfigToStream<SourceDir>( |
| + target, &ConfigValues::include_dirs, IncludeDirWriter(path_output), |
| + out); |
| + include_dirs->Text(GetWindowsKitsIncludeDirs() + |
| + "$(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<XmlElement> preprocessor_definitions = |
| + cl_compile->SubElement("PreprocessorDefinitions"); |
| + RecursiveTargetConfigToStream<std::string>( |
| + target, &ConfigValues::defines, SemicolonSeparatedWriter(), out); |
| + 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<XmlElement> 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("ClCompile", "Include", |
| + SourceFileWriter(path_output, file)); |
| + } |
| + } |
| + } |
| + |
| + project.SubElement("Import", |
| + {{"Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets"}}); |
| + project.SubElement( |
| + "Import", |
| + {{"Project", "$(VCTargetsPath)\\BuildCustomizations\\masm.targets"}}); |
| + project.SubElement("ImportGroup", {{"Label", "ExtensionTargets"}}); |
| + |
| + { |
| + scoped_ptr<XmlElement> build = |
| + project.SubElement("Target", {{"Name", "Build"}}); |
|
brettw
2016/01/08 23:51:50
This {{...}} is "uniform initialization syntax" ri
Tomasz Moniuszko
2016/01/21 10:50:03
Done.
|
| + build->SubElement( |
| + "Exec", {{"Command", "call ninja.exe -C $(OutDir) $(ProjectName)"}}); |
| + } |
| + |
| + { |
| + scoped_ptr<XmlElement> clean = |
| + project.SubElement("Target", {{"Name", "Clean"}}); |
| + clean->SubElement( |
| + "Exec", |
| + {{"Command", "call ninja.exe -C $(OutDir) -tclean $(ProjectName)"}}); |
| + } |
| + |
| + return true; |
| +} |
| + |
| +} // namespace |
| + |
| +VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings) |
| + : build_settings_(build_settings) {} |
| + |
| +VisualStudioWriter::~VisualStudioWriter() {} |
| + |
| +// static |
| +bool VisualStudioWriter::RunAndWriteFiles(const BuildSettings* build_settings, |
| + Builder* builder, |
| + Err* err) { |
| + std::vector<const Target*> targets = builder->GetAllResolvedTargets(); |
| + |
| + VisualStudioWriter writer(build_settings); |
| + |
| + ProjectConfiguration config; |
| + const Value* value = build_settings->build_args().GetArgOverride("is_debug"); |
| + config.is_debug = 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"; |
| + |
| + for (const Target* target : targets) { |
| + // 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.WriteProjectFile(target, config, err)) |
| + return false; |
| + } |
| + |
| + return writer.WriteSolutionFile(targets); |
| +} |
| + |
| +bool VisualStudioWriter::WriteProjectFile(const Target* target, |
| + const ProjectConfiguration& config, |
| + Err* err) const { |
| + SourceFile target_file = target->label().dir().ResolveRelativeFile( |
| + Value(nullptr, target->label().name() + ".vcxproj"), err); |
| + if (target_file.is_null()) |
| + return false; |
| + |
| + base::FilePath vcxproj_file(build_settings_->GetFullPath(target_file)); |
| + |
| + std::ofstream file; |
|
brettw
2016/01/08 23:51:50
In the other writers, you can see I made a strings
Daniel Bratell
2016/01/14 15:41:14
Just tested. Such a change changes the generation
|
| + file.open(FilePathToUTF8(vcxproj_file).c_str(), |
| + std::ios_base::out | std::ios_base::binary); |
| + if (file.fail()) { |
| + *err = Err(Location(), "Couldn't open " + target->label().name() + |
| + ".vcxproj for writing"); |
| + return false; |
| + } |
| + |
| + return WriteMsBuildProject(build_settings_, file, target, config, err); |
| +} |
| + |
| +bool VisualStudioWriter::WriteSolutionFile( |
| + const std::vector<const Target*>& targets) const { |
| + return true; |
| +} |