Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "tools/gn/visual_studio_writer.h" | |
| 6 | |
| 7 #include <fstream> | |
| 8 #include <iostream> | |
| 9 #include <string> | |
| 10 #include <utility> | |
| 11 | |
| 12 #include "base/logging.h" | |
| 13 #include "base/md5.h" | |
| 14 #include "base/memory/scoped_ptr.h" | |
| 15 #include "base/strings/string_util.h" | |
| 16 #include "tools/gn/builder.h" | |
| 17 #include "tools/gn/config.h" | |
| 18 #include "tools/gn/config_values_extractors.h" | |
| 19 #include "tools/gn/filesystem_utils.h" | |
| 20 #include "tools/gn/parse_tree.h" | |
| 21 #include "tools/gn/path_output.h" | |
| 22 #include "tools/gn/source_file_type.h" | |
| 23 #include "tools/gn/target.h" | |
| 24 #include "tools/gn/variables.h" | |
| 25 | |
| 26 struct ProjectConfiguration { | |
| 27 bool is_debug; | |
| 28 std::string platform; | |
| 29 }; | |
| 30 | |
| 31 namespace { | |
| 32 | |
| 33 using XmlAttributes = std::vector<std::pair<std::string, std::string>>; | |
| 34 | |
| 35 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.
| |
| 36 public: | |
| 37 XmlElement(std::ostream& out, | |
| 38 const std::string& tag, | |
| 39 const XmlAttributes& attributes); | |
| 40 XmlElement(std::ostream& out, | |
| 41 const std::string& tag, | |
| 42 const XmlAttributes& attributes, | |
| 43 int indent); | |
| 44 template <class Writer> | |
| 45 XmlElement(std::ostream& out, | |
| 46 const std::string& tag, | |
| 47 const std::string& attribute_name, | |
| 48 const Writer& attribute_value_writer, | |
| 49 int indent); | |
| 50 ~XmlElement(); | |
| 51 | |
| 52 void Text(const std::string& content); | |
| 53 | |
| 54 scoped_ptr<XmlElement> SubElement(const std::string& tag); | |
| 55 scoped_ptr<XmlElement> SubElement(const std::string& tag, | |
| 56 const XmlAttributes& attributes); | |
| 57 template <class Writer> | |
| 58 scoped_ptr<XmlElement> SubElement(const std::string& tag, | |
| 59 const std::string& attribute_name, | |
| 60 const Writer& attribute_value_writer); | |
| 61 | |
| 62 private: | |
| 63 std::ostream& out_; | |
| 64 std::string tag_; | |
| 65 int indent_; | |
| 66 bool one_line_; | |
| 67 | |
| 68 DISALLOW_COPY_AND_ASSIGN(XmlElement); | |
| 69 }; | |
| 70 | |
| 71 XmlElement::XmlElement(std::ostream& out, | |
| 72 const std::string& tag, | |
| 73 const XmlAttributes& attributes) | |
| 74 : XmlElement(out, tag, attributes, 0) {} | |
| 75 | |
| 76 XmlElement::XmlElement(std::ostream& out, | |
| 77 const std::string& tag, | |
| 78 const XmlAttributes& attributes, | |
| 79 int indent) | |
| 80 : out_(out), tag_(tag), indent_(indent), one_line_(true) { | |
| 81 out << std::string(indent, ' ') << '<' << tag; | |
| 82 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.
| |
| 83 out << ' ' << attribute.first << "=\"" << attribute.second << '"'; | |
| 84 } | |
| 85 out << '>'; | |
| 86 } | |
| 87 | |
| 88 template <class Writer> | |
| 89 XmlElement::XmlElement(std::ostream& out, | |
| 90 const std::string& tag, | |
| 91 const std::string& attribute_name, | |
| 92 const Writer& attribute_value_writer, | |
| 93 int indent) | |
| 94 : out_(out), tag_(tag), indent_(indent), one_line_(true) { | |
| 95 out << std::string(indent, ' ') << '<' << tag; | |
| 96 out << ' ' << attribute_name << "=\""; | |
| 97 attribute_value_writer(out); | |
| 98 out << "\">"; | |
| 99 } | |
| 100 | |
| 101 XmlElement::~XmlElement() { | |
| 102 if (!one_line_) | |
| 103 out_ << std::string(indent_, ' '); | |
| 104 out_ << "</" << tag_ << '>' << std::endl; | |
| 105 } | |
| 106 | |
| 107 void XmlElement::Text(const std::string& content) { | |
| 108 out_ << content; | |
| 109 } | |
| 110 | |
| 111 scoped_ptr<XmlElement> XmlElement::SubElement(const std::string& tag) { | |
| 112 return SubElement(tag, XmlAttributes()); | |
| 113 } | |
| 114 | |
| 115 scoped_ptr<XmlElement> XmlElement::SubElement(const std::string& tag, | |
| 116 const XmlAttributes& attributes) { | |
| 117 if (one_line_) { | |
| 118 out_ << std::endl; | |
| 119 one_line_ = false; | |
| 120 } | |
| 121 return make_scoped_ptr(new XmlElement(out_, tag, attributes, indent_ + 2)); | |
| 122 } | |
| 123 | |
| 124 template <class Writer> | |
| 125 scoped_ptr<XmlElement> XmlElement::SubElement( | |
| 126 const std::string& tag, | |
| 127 const std::string& attribute_name, | |
| 128 const Writer& attribute_value_writer) { | |
| 129 if (one_line_) { | |
| 130 out_ << std::endl; | |
| 131 one_line_ = false; | |
| 132 } | |
| 133 return make_scoped_ptr(new XmlElement(out_, tag, attribute_name, | |
| 134 attribute_value_writer, indent_ + 2)); | |
| 135 } | |
| 136 | |
| 137 struct SemicolonSeparatedWriter { | |
| 138 void operator()(const std::string& value, std::ostream& out) const { | |
| 139 out << value + ';'; | |
| 140 } | |
| 141 }; | |
| 142 | |
| 143 struct IncludeDirWriter { | |
| 144 explicit IncludeDirWriter(PathOutput& path_output) | |
| 145 : path_output_(path_output) {} | |
| 146 ~IncludeDirWriter() = default; | |
| 147 | |
| 148 void operator()(const SourceDir& dir, std::ostream& out) const { | |
| 149 path_output_.WriteDir(out, dir, PathOutput::DIR_NO_LAST_SLASH); | |
| 150 out << ";"; | |
| 151 } | |
| 152 | |
| 153 PathOutput& path_output_; | |
| 154 }; | |
| 155 | |
| 156 struct SourceFileWriter { | |
| 157 SourceFileWriter(PathOutput& path_output, const SourceFile& source_file) | |
| 158 : path_output_(path_output), source_file_(source_file) {} | |
| 159 ~SourceFileWriter() = default; | |
| 160 | |
| 161 void operator()(std::ostream& out) const { | |
| 162 path_output_.WriteFile(out, source_file_); | |
| 163 } | |
| 164 | |
| 165 PathOutput& path_output_; | |
| 166 const SourceFile& source_file_; | |
| 167 }; | |
| 168 | |
| 169 // Some compiler options which will be written to project file. We don't need to | |
| 170 // specify all options because generated project file is going to be used only | |
| 171 // for compilation of single file. For real build ninja files are used. | |
| 172 struct CompilerOptions { | |
| 173 std::string additional_options; | |
| 174 std::string buffer_security_check; | |
| 175 std::string forced_include_files; | |
| 176 std::string disable_specific_warnings; | |
| 177 std::string optimization; | |
| 178 std::string runtime_library; | |
| 179 std::string treat_warning_as_error; | |
| 180 std::string warning_level; | |
| 181 }; | |
| 182 | |
| 183 const char kToolsetVersion[] = "v140"; // Visual Studio 2015 | |
| 184 const char kVisualStudioVersion[] = "14.0"; // Visual Studio 2015 | |
| 185 const char kWindowsKitsVersion[] = "10"; // Windows 10 SDK | |
| 186 const char kWindowsKitsIncludeVersion[] = "10.0.10240.0"; // Windows 10 SDK | |
| 187 | |
| 188 std::string GetWindowsKitsIncludeDirs() { | |
| 189 // 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
| |
| 190 std::string kits_path = | |
| 191 std::string("C:\\Program Files (x86)\\Windows Kits\\") + | |
| 192 kWindowsKitsVersion + "\\"; | |
| 193 return kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\shared;" + | |
| 194 kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\um;" + | |
| 195 kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\winrt;"; | |
| 196 } | |
| 197 | |
| 198 std::string MakeProjectGuid(std::string target_name) { | |
| 199 std::string str = base::MD5String(target_name); | |
| 200 return '{' + str.substr(0, 8) + '-' + str.substr(8, 4) + '-' + | |
| 201 str.substr(12, 4) + '-' + str.substr(16, 4) + '-' + | |
| 202 str.substr(20, 12) + '}'; | |
| 203 } | |
| 204 | |
| 205 std::string GetConfigurationType(const Target* target, Err* err) { | |
| 206 switch (target->output_type()) { | |
| 207 case Target::EXECUTABLE: | |
| 208 return "Application"; | |
| 209 case Target::SHARED_LIBRARY: | |
| 210 case Target::LOADABLE_MODULE: | |
| 211 return "DynamicLibrary"; | |
| 212 case Target::STATIC_LIBRARY: | |
| 213 case Target::SOURCE_SET: | |
| 214 return "StaticLibrary"; | |
| 215 } | |
| 216 | |
| 217 *err = Err(Location(), | |
| 218 "Visual Studio doesn't support '" + target->label().name() + | |
| 219 "' target output type: " + | |
| 220 Target::GetStringForOutputType(target->output_type())); | |
| 221 return ""; | |
|
brettw
2016/01/08 23:51:50
return std::string();
instead. More semantically c
Tomasz Moniuszko
2016/01/21 10:50:03
Done.
| |
| 222 } | |
| 223 | |
| 224 #define SetOption(condition, member, value) \ | |
| 225 if (condition) { \ | |
| 226 options->member = value; \ | |
| 227 return; \ | |
| 228 } | |
| 229 | |
| 230 #define AppendOption(condition, member, value, separator) \ | |
| 231 if (condition) { \ | |
| 232 options->member += value + separator; \ | |
| 233 return; \ | |
| 234 } | |
| 235 | |
| 236 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
| |
| 237 if (cflag.size() > 2 && cflag[0] == '/') { | |
| 238 switch (cflag[1]) { | |
| 239 case 'F': | |
| 240 AppendOption(cflag.size() > 3 && cflag[2] == 'I', forced_include_files, | |
| 241 cflag.substr(3), ';') | |
| 242 break; | |
| 243 | |
| 244 case 'G': | |
| 245 if (cflag[2] == 'S') { | |
| 246 SetOption(cflag.size() == 3, buffer_security_check, "true") | |
| 247 SetOption(cflag.size() == 4 && cflag[3] == '-', buffer_security_check, | |
| 248 "false") | |
| 249 } | |
| 250 break; | |
| 251 | |
| 252 case 'M': | |
| 253 switch (cflag[2]) { | |
| 254 case 'D': | |
| 255 SetOption(cflag.size() == 3, runtime_library, "MultiThreadedDLL") | |
| 256 SetOption(cflag.size() == 4 && cflag[3] == 'd', runtime_library, | |
| 257 "MultiThreadedDebugDLL") | |
| 258 break; | |
| 259 | |
| 260 case 'T': | |
| 261 SetOption(cflag.size() == 3, runtime_library, "MultiThreaded") | |
| 262 SetOption(cflag.size() == 4 && cflag[3] == 'd', runtime_library, | |
| 263 "MultiThreadedDebug") | |
| 264 break; | |
| 265 } | |
| 266 break; | |
| 267 | |
| 268 case 'O': | |
| 269 switch (cflag[2]) { | |
| 270 case '2': | |
| 271 SetOption(cflag.size() == 3, optimization, "MaxSpeed") | |
| 272 break; | |
| 273 | |
| 274 case 'd': | |
| 275 SetOption(cflag.size() == 3, optimization, "Disabled") | |
| 276 break; | |
| 277 } | |
| 278 break; | |
| 279 | |
| 280 case 'T': | |
| 281 // Skip flags that cause treating all source files as C and C++ files. | |
| 282 if (cflag.size() == 3 && (cflag[2] == 'C' || cflag[2] == 'P')) | |
| 283 return; | |
| 284 break; | |
| 285 | |
| 286 case 'W': | |
| 287 switch (cflag[2]) { | |
| 288 case '0': | |
| 289 case '1': | |
| 290 case '2': | |
| 291 case '3': | |
| 292 case '4': | |
| 293 SetOption(cflag.size() == 3, warning_level, | |
| 294 std::string("Level") + cflag[2]) | |
| 295 break; | |
| 296 | |
| 297 case 'X': | |
| 298 SetOption(cflag.size() == 3, treat_warning_as_error, "true") | |
| 299 break; | |
| 300 } | |
| 301 break; | |
| 302 | |
| 303 case 'w': | |
| 304 AppendOption(cflag.size() > 3 && cflag[2] == 'd', | |
| 305 disable_specific_warnings, cflag.substr(3), ';') | |
| 306 break; | |
| 307 } | |
| 308 } | |
| 309 | |
| 310 // Put everything else into additional_options. | |
| 311 options->additional_options += cflag + ' '; | |
| 312 } | |
| 313 | |
| 314 #undef SetOption | |
| 315 #undef AppendOption | |
| 316 | |
| 317 void ParseCompilerOptions(const std::vector<std::string>& cflags, | |
| 318 CompilerOptions* options) { | |
| 319 for (const std::string& flag : cflags) { | |
|
brettw
2016/01/08 23:51:50
No {}
Tomasz Moniuszko
2016/01/21 10:50:03
Done.
| |
| 320 ParseCompilerOption(flag, options); | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 void ParseCompilerOptions(const Target* target, CompilerOptions* options) { | |
| 325 for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) { | |
| 326 ParseCompilerOptions(iter.cur().cflags(), options); | |
| 327 ParseCompilerOptions(iter.cur().cflags_c(), options); | |
| 328 ParseCompilerOptions(iter.cur().cflags_cc(), options); | |
| 329 } | |
| 330 } | |
| 331 | |
| 332 bool WriteMsBuildProject(const BuildSettings* build_settings, | |
| 333 std::ostream& out, | |
| 334 const Target* target, | |
| 335 const ProjectConfiguration& config, | |
| 336 Err* err) { | |
| 337 PathOutput path_output(target->label().dir(), | |
| 338 build_settings->root_path_utf8(), | |
| 339 EscapingMode::ESCAPE_NONE); | |
| 340 | |
| 341 out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl; | |
| 342 XmlElement project( | |
| 343 out, "Project", | |
| 344 {{"DefaultTargets", "Build"}, | |
| 345 {"ToolsVersion", kVisualStudioVersion}, | |
| 346 {"xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"}}); | |
| 347 | |
| 348 { | |
| 349 scoped_ptr<XmlElement> configurations = | |
| 350 project.SubElement("ItemGroup", {{"Label", "ProjectConfigurations"}}); | |
| 351 std::string config_name = config.is_debug ? "Debug" : "Release"; | |
| 352 scoped_ptr<XmlElement> project_config = configurations->SubElement( | |
| 353 "ProjectConfiguration", | |
| 354 {{"Include", config_name + '|' + config.platform}}); | |
| 355 project_config->SubElement("Configuration")->Text(config_name); | |
| 356 project_config->SubElement("Platform")->Text(config.platform); | |
| 357 } | |
| 358 | |
| 359 { | |
| 360 scoped_ptr<XmlElement> globals = | |
| 361 project.SubElement("PropertyGroup", {{"Label", "Globals"}}); | |
| 362 globals->SubElement("ProjectGuid") | |
| 363 ->Text(MakeProjectGuid(target->label().name())); | |
| 364 globals->SubElement("Keyword")->Text("Win32Proj"); | |
| 365 globals->SubElement("RootNamespace")->Text(target->label().name()); | |
| 366 globals->SubElement("IgnoreWarnCompileDuplicatedFilename")->Text("true"); | |
| 367 globals->SubElement("PreferredToolArchitecture")->Text("x64"); | |
| 368 } | |
| 369 | |
| 370 project.SubElement( | |
| 371 "Import", {{"Project", "$(VCTargetsPath)\\Microsoft.Cpp.Default.props"}}); | |
| 372 | |
| 373 { | |
| 374 scoped_ptr<XmlElement> configuration = | |
| 375 project.SubElement("PropertyGroup", {{"Label", "Configuration"}}); | |
| 376 configuration->SubElement("CharacterSet")->Text("Unicode"); | |
| 377 std::string configuration_type = GetConfigurationType(target, err); | |
| 378 if (configuration_type.empty()) | |
| 379 return false; | |
| 380 configuration->SubElement("ConfigurationType")->Text(configuration_type); | |
| 381 } | |
| 382 | |
| 383 { | |
| 384 scoped_ptr<XmlElement> locals = | |
| 385 project.SubElement("PropertyGroup", {{"Label", "Locals"}}); | |
| 386 locals->SubElement("PlatformToolset")->Text(kToolsetVersion); | |
| 387 } | |
| 388 | |
| 389 project.SubElement("Import", | |
| 390 {{"Project", "$(VCTargetsPath)\\Microsoft.Cpp.props"}}); | |
| 391 project.SubElement( | |
| 392 "Import", | |
| 393 {{"Project", "$(VCTargetsPath)\\BuildCustomizations\\masm.props"}}); | |
| 394 project.SubElement("ImportGroup", {{"Label", "ExtensionSettings"}}); | |
| 395 | |
| 396 { | |
| 397 scoped_ptr<XmlElement> property_sheets = | |
| 398 project.SubElement("ImportGroup", {{"Label", "PropertySheets"}}); | |
| 399 property_sheets->SubElement( | |
| 400 "Import", | |
| 401 { | |
| 402 {"Condition", | |
| 403 "exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')"}, | |
| 404 {"Label", "LocalAppDataPlatform"}, | |
| 405 {"Project", "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props"}, | |
| 406 }); | |
| 407 } | |
| 408 | |
| 409 project.SubElement("PropertyGroup", {{"Label", "UserMacros"}}); | |
| 410 | |
| 411 { | |
| 412 scoped_ptr<XmlElement> properties = project.SubElement("PropertyGroup"); | |
| 413 { | |
| 414 scoped_ptr<XmlElement> out_dir = properties->SubElement("OutDir"); | |
| 415 path_output.WriteDir(out, build_settings->build_dir(), | |
| 416 PathOutput::DIR_INCLUDE_LAST_SLASH); | |
| 417 } | |
| 418 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
| |
| 419 ->Text(config.is_debug ? "true" : "false"); | |
| 420 properties->SubElement("TargetName")->Text("$(ProjectName)"); | |
| 421 properties->SubElement("TargetPath") | |
| 422 ->Text("$(OutDir)\\$(ProjectName)$(TargetExt)"); | |
| 423 } | |
| 424 | |
| 425 { | |
| 426 scoped_ptr<XmlElement> item_definitions = | |
| 427 project.SubElement("ItemDefinitionGroup"); | |
| 428 { | |
| 429 scoped_ptr<XmlElement> cl_compile = | |
| 430 item_definitions->SubElement("ClCompile"); | |
| 431 { | |
| 432 scoped_ptr<XmlElement> include_dirs = | |
| 433 cl_compile->SubElement("AdditionalIncludeDirectories"); | |
| 434 RecursiveTargetConfigToStream<SourceDir>( | |
| 435 target, &ConfigValues::include_dirs, IncludeDirWriter(path_output), | |
| 436 out); | |
| 437 include_dirs->Text(GetWindowsKitsIncludeDirs() + | |
| 438 "$(VSInstallDir)\\VC\\atlmfc\\include;" + | |
| 439 "%(AdditionalIncludeDirectories)"); | |
| 440 } | |
| 441 CompilerOptions options; | |
| 442 ParseCompilerOptions(target, &options); | |
| 443 if (!options.additional_options.empty()) { | |
| 444 cl_compile->SubElement("AdditionalOptions") | |
| 445 ->Text(options.additional_options + "%(AdditionalOptions)"); | |
| 446 } | |
| 447 if (!options.buffer_security_check.empty()) { | |
| 448 cl_compile->SubElement("BufferSecurityCheck") | |
| 449 ->Text(options.buffer_security_check); | |
| 450 } | |
| 451 cl_compile->SubElement("CompileAsWinRT")->Text("false"); | |
| 452 cl_compile->SubElement("DebugInformationFormat")->Text("ProgramDatabase"); | |
| 453 if (!options.disable_specific_warnings.empty()) { | |
| 454 cl_compile->SubElement("DisableSpecificWarnings") | |
| 455 ->Text(options.disable_specific_warnings + | |
| 456 "%(DisableSpecificWarnings)"); | |
| 457 } | |
| 458 cl_compile->SubElement("ExceptionHandling")->Text("false"); | |
| 459 if (!options.forced_include_files.empty()) { | |
| 460 cl_compile->SubElement("ForcedIncludeFiles") | |
| 461 ->Text(options.forced_include_files); | |
| 462 } | |
| 463 cl_compile->SubElement("MinimalRebuild")->Text("false"); | |
| 464 if (!options.optimization.empty()) | |
| 465 cl_compile->SubElement("Optimization")->Text(options.optimization); | |
| 466 if (target->config_values().has_precompiled_headers()) { | |
| 467 cl_compile->SubElement("PrecompiledHeader")->Text("Use"); | |
| 468 cl_compile->SubElement("PrecompiledHeaderFile") | |
| 469 ->Text(target->config_values().precompiled_header()); | |
| 470 } else { | |
| 471 cl_compile->SubElement("PrecompiledHeader")->Text("NotUsing"); | |
| 472 } | |
| 473 { | |
| 474 scoped_ptr<XmlElement> preprocessor_definitions = | |
| 475 cl_compile->SubElement("PreprocessorDefinitions"); | |
| 476 RecursiveTargetConfigToStream<std::string>( | |
| 477 target, &ConfigValues::defines, SemicolonSeparatedWriter(), out); | |
| 478 preprocessor_definitions->Text("%(PreprocessorDefinitions)"); | |
| 479 } | |
| 480 if (!options.runtime_library.empty()) | |
| 481 cl_compile->SubElement("RuntimeLibrary")->Text(options.runtime_library); | |
| 482 if (!options.treat_warning_as_error.empty()) { | |
| 483 cl_compile->SubElement("TreatWarningAsError") | |
| 484 ->Text(options.treat_warning_as_error); | |
| 485 } | |
| 486 if (!options.warning_level.empty()) | |
| 487 cl_compile->SubElement("WarningLevel")->Text(options.warning_level); | |
| 488 } | |
| 489 | |
| 490 // We don't include resource compilation and link options as ninja files | |
| 491 // are used to generate real build. | |
| 492 } | |
| 493 | |
| 494 { | |
| 495 scoped_ptr<XmlElement> group = project.SubElement("ItemGroup"); | |
| 496 if (!target->config_values().precompiled_source().is_null()) { | |
| 497 group->SubElement( | |
| 498 "ClCompile", "Include", | |
| 499 SourceFileWriter(path_output, | |
| 500 target->config_values().precompiled_source())) | |
| 501 ->SubElement("PrecompiledHeader") | |
| 502 ->Text("Create"); | |
| 503 } | |
| 504 | |
| 505 for (const SourceFile& file : target->sources()) { | |
| 506 SourceFileType type = GetSourceFileType(file); | |
| 507 if (type == SOURCE_H || type == SOURCE_CPP || type == SOURCE_C) { | |
| 508 group->SubElement("ClCompile", "Include", | |
| 509 SourceFileWriter(path_output, file)); | |
| 510 } | |
| 511 } | |
| 512 } | |
| 513 | |
| 514 project.SubElement("Import", | |
| 515 {{"Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets"}}); | |
| 516 project.SubElement( | |
| 517 "Import", | |
| 518 {{"Project", "$(VCTargetsPath)\\BuildCustomizations\\masm.targets"}}); | |
| 519 project.SubElement("ImportGroup", {{"Label", "ExtensionTargets"}}); | |
| 520 | |
| 521 { | |
| 522 scoped_ptr<XmlElement> build = | |
| 523 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.
| |
| 524 build->SubElement( | |
| 525 "Exec", {{"Command", "call ninja.exe -C $(OutDir) $(ProjectName)"}}); | |
| 526 } | |
| 527 | |
| 528 { | |
| 529 scoped_ptr<XmlElement> clean = | |
| 530 project.SubElement("Target", {{"Name", "Clean"}}); | |
| 531 clean->SubElement( | |
| 532 "Exec", | |
| 533 {{"Command", "call ninja.exe -C $(OutDir) -tclean $(ProjectName)"}}); | |
| 534 } | |
| 535 | |
| 536 return true; | |
| 537 } | |
| 538 | |
| 539 } // namespace | |
| 540 | |
| 541 VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings) | |
| 542 : build_settings_(build_settings) {} | |
| 543 | |
| 544 VisualStudioWriter::~VisualStudioWriter() {} | |
| 545 | |
| 546 // static | |
| 547 bool VisualStudioWriter::RunAndWriteFiles(const BuildSettings* build_settings, | |
| 548 Builder* builder, | |
| 549 Err* err) { | |
| 550 std::vector<const Target*> targets = builder->GetAllResolvedTargets(); | |
| 551 | |
| 552 VisualStudioWriter writer(build_settings); | |
| 553 | |
| 554 ProjectConfiguration config; | |
| 555 const Value* value = build_settings->build_args().GetArgOverride("is_debug"); | |
| 556 config.is_debug = value == nullptr || value->boolean_value(); | |
| 557 config.platform = "Win32"; | |
| 558 value = build_settings->build_args().GetArgOverride(variables::kTargetCpu); | |
| 559 if (value != nullptr && value->string_value() == "x64") | |
| 560 config.platform = "x64"; | |
| 561 | |
| 562 for (const Target* target : targets) { | |
| 563 // Skip actions and groups. | |
| 564 if (target->output_type() == Target::GROUP || | |
| 565 target->output_type() == Target::COPY_FILES || | |
| 566 target->output_type() == Target::ACTION || | |
| 567 target->output_type() == Target::ACTION_FOREACH) { | |
| 568 continue; | |
| 569 } | |
| 570 | |
| 571 if (!writer.WriteProjectFile(target, config, err)) | |
| 572 return false; | |
| 573 } | |
| 574 | |
| 575 return writer.WriteSolutionFile(targets); | |
| 576 } | |
| 577 | |
| 578 bool VisualStudioWriter::WriteProjectFile(const Target* target, | |
| 579 const ProjectConfiguration& config, | |
| 580 Err* err) const { | |
| 581 SourceFile target_file = target->label().dir().ResolveRelativeFile( | |
| 582 Value(nullptr, target->label().name() + ".vcxproj"), err); | |
| 583 if (target_file.is_null()) | |
| 584 return false; | |
| 585 | |
| 586 base::FilePath vcxproj_file(build_settings_->GetFullPath(target_file)); | |
| 587 | |
| 588 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
| |
| 589 file.open(FilePathToUTF8(vcxproj_file).c_str(), | |
| 590 std::ios_base::out | std::ios_base::binary); | |
| 591 if (file.fail()) { | |
| 592 *err = Err(Location(), "Couldn't open " + target->label().name() + | |
| 593 ".vcxproj for writing"); | |
| 594 return false; | |
| 595 } | |
| 596 | |
| 597 return WriteMsBuildProject(build_settings_, file, target, config, err); | |
| 598 } | |
| 599 | |
| 600 bool VisualStudioWriter::WriteSolutionFile( | |
| 601 const std::vector<const Target*>& targets) const { | |
| 602 return true; | |
| 603 } | |
| OLD | NEW |