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