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 |