Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(431)

Side by Side Diff: tools/gn/visual_studio_writer.cc

Issue 1570113002: Visual Studio generators for GN (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix tests failing on non-Windows platforms Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 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 <algorithm>
8 #include <map>
9 #include <set>
10 #include <string>
11
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "tools/gn/builder.h"
18 #include "tools/gn/config.h"
19 #include "tools/gn/config_values_extractors.h"
20 #include "tools/gn/filesystem_utils.h"
21 #include "tools/gn/parse_tree.h"
22 #include "tools/gn/path_output.h"
23 #include "tools/gn/source_file_type.h"
24 #include "tools/gn/standard_out.h"
25 #include "tools/gn/target.h"
26 #include "tools/gn/variables.h"
27 #include "tools/gn/visual_studio_utils.h"
28 #include "tools/gn/xml_element_writer.h"
29
30 #if defined(OS_WIN)
31 #include "base/win/registry.h"
32 #endif
33
34 namespace {
35
36 struct SemicolonSeparatedWriter {
37 void operator()(const std::string& value, std::ostream& out) const {
38 out << value + ';';
39 }
40 };
41
42 struct IncludeDirWriter {
43 explicit IncludeDirWriter(PathOutput& path_output)
44 : path_output_(path_output) {}
45 ~IncludeDirWriter() = default;
46
47 void operator()(const SourceDir& dir, std::ostream& out) const {
48 path_output_.WriteDir(out, dir, PathOutput::DIR_NO_LAST_SLASH);
49 out << ";";
50 }
51
52 PathOutput& path_output_;
53 };
54
55 struct SourceFileWriter {
56 SourceFileWriter(PathOutput& path_output, const SourceFile& source_file)
57 : path_output_(path_output), source_file_(source_file) {}
58 ~SourceFileWriter() = default;
59
60 void operator()(std::ostream& out) const {
61 path_output_.WriteFile(out, source_file_);
62 }
63
64 PathOutput& path_output_;
65 const SourceFile& source_file_;
66 };
67
68 const char kToolsetVersion[] = "v140"; // Visual Studio 2015
69 const char kVisualStudioVersion[] = "14.0"; // Visual Studio 2015
70 const char kWindowsKitsVersion[] = "10"; // Windows 10 SDK
71 const char kWindowsKitsIncludeVersion[] = "10.0.10240.0"; // Windows 10 SDK
72
73 const char kGuidTypeProject[] = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}";
74 const char kGuidTypeFolder[] = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}";
75 const char kGuidSeedProject[] = "project";
76 const char kGuidSeedFolder[] = "folder";
77 const char kGuidSeedFilter[] = "filter";
78
79 std::string GetWindowsKitsIncludeDirs() {
80 std::string kits_path;
81
82 #if defined(OS_WIN)
83 const base::char16* const subkeys[] = {
84 L"SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots",
85 L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows Kits\\Installed Roots"};
86
87 base::string16 value_name =
88 base::ASCIIToUTF16("KitsRoot") + base::ASCIIToUTF16(kWindowsKitsVersion);
89
90 for (const base::char16* subkey : subkeys) {
91 base::win::RegKey key(HKEY_LOCAL_MACHINE, subkey, KEY_READ);
92 base::string16 value;
93 if (key.ReadValue(value_name.c_str(), &value) == ERROR_SUCCESS) {
94 kits_path = base::UTF16ToUTF8(value);
95 break;
96 }
97 }
98 #endif // OS_WIN
99
100 if (kits_path.empty()) {
101 kits_path = std::string("C:\\Program Files (x86)\\Windows Kits\\") +
102 kWindowsKitsVersion + "\\";
103 }
104
105 return kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\shared;" +
106 kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\um;" +
107 kits_path + "Include\\" + kWindowsKitsIncludeVersion + "\\winrt;";
108 }
109
110 std::string GetConfigurationType(const Target* target, Err* err) {
111 switch (target->output_type()) {
112 case Target::EXECUTABLE:
113 return "Application";
114 case Target::SHARED_LIBRARY:
115 case Target::LOADABLE_MODULE:
116 return "DynamicLibrary";
117 case Target::STATIC_LIBRARY:
118 case Target::SOURCE_SET:
119 return "StaticLibrary";
120
121 default:
122 *err = Err(Location(),
123 "Visual Studio doesn't support '" + target->label().name() +
124 "' target output type: " +
125 Target::GetStringForOutputType(target->output_type()));
126 return std::string();
127 }
128 }
129
130 void ParseCompilerOptions(const std::vector<std::string>& cflags,
131 CompilerOptions* options) {
132 for (const std::string& flag : cflags)
133 ParseCompilerOption(flag, options);
134 }
135
136 void ParseCompilerOptions(const Target* target, CompilerOptions* options) {
137 for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
138 ParseCompilerOptions(iter.cur().cflags(), options);
139 ParseCompilerOptions(iter.cur().cflags_c(), options);
140 ParseCompilerOptions(iter.cur().cflags_cc(), options);
141 }
142 }
143
144 // Returns a string piece pointing into the input string identifying the parent
145 // directory path, excluding the last slash. Note that the input pointer must
146 // outlive the output.
147 base::StringPiece FindParentDir(const std::string* path) {
148 DCHECK(path && !path->empty());
149 for (int i = static_cast<int>(path->size()) - 2; i >= 0; --i) {
150 if (IsSlash((*path)[i]))
151 return base::StringPiece(path->data(), i);
152 }
153 return base::StringPiece();
154 }
155
156 bool HasSameContent(std::stringstream& data_1, const base::FilePath& data_2) {
157 // Compare file sizes first. Quick and will save us some time if they are
158 // different sizes.
159 int64_t data_1_len = data_1.tellp();
160
161 int64_t data_2_len;
162 if (!base::GetFileSize(data_2, &data_2_len) || data_1_len != data_2_len)
163 return false;
164
165 std::string data_2_data;
166 data_2_data.resize(data_2_len);
167 if (!base::ReadFileToString(data_2, &data_2_data))
168 return false;
169
170 std::string data_1_data;
171 data_1_data = data_1.str();
172
173 return data_1_data == data_2_data;
174 }
175
176 } // namespace
177
178 VisualStudioWriter::SolutionEntry::SolutionEntry(const std::string& _name,
179 const std::string& _path,
180 const std::string& _guid)
181 : name(_name), path(_path), guid(_guid), parent_folder(nullptr) {}
182
183 VisualStudioWriter::SolutionEntry::~SolutionEntry() = default;
184
185 VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings)
186 : build_settings_(build_settings) {
187 const Value* value = build_settings->build_args().GetArgOverride("is_debug");
188 is_debug_config_ = value == nullptr || value->boolean_value();
189 config_platform_ = "Win32";
190 value = build_settings->build_args().GetArgOverride(variables::kTargetCpu);
191 if (value != nullptr && value->string_value() == "x64")
192 config_platform_ = "x64";
193
194 windows_kits_include_dirs_ = GetWindowsKitsIncludeDirs();
195 }
196
197 VisualStudioWriter::~VisualStudioWriter() {
198 STLDeleteContainerPointers(projects_.begin(), projects_.end());
199 STLDeleteContainerPointers(folders_.begin(), folders_.end());
200 }
201
202 // static
203 bool VisualStudioWriter::RunAndWriteFiles(const BuildSettings* build_settings,
204 Builder* builder,
205 Err* err) {
206 std::vector<const Target*> targets = builder->GetAllResolvedTargets();
207
208 VisualStudioWriter writer(build_settings);
209 writer.projects_.reserve(targets.size());
210 writer.folders_.reserve(targets.size());
211
212 std::set<std::string> processed_targets;
213 for (const Target* target : targets) {
214 // Skip targets which are duplicated in vector.
215 std::string target_path =
216 target->label().dir().value() + target->label().name();
217 if (processed_targets.find(target_path) != processed_targets.end())
218 continue;
219
220 // Skip actions and groups.
221 if (target->output_type() == Target::GROUP ||
222 target->output_type() == Target::COPY_FILES ||
223 target->output_type() == Target::ACTION ||
224 target->output_type() == Target::ACTION_FOREACH) {
225 continue;
226 }
227
228 if (!writer.WriteProjectFiles(target, err))
229 return false;
230
231 processed_targets.insert(target_path);
232 }
233
234 if (writer.projects_.empty()) {
235 *err = Err(Location(), "No Visual Studio projects generated.");
236 return false;
237 }
238
239 writer.ResolveSolutionFolders();
240 return writer.WriteSolutionFile(err);
241 }
242
243 bool VisualStudioWriter::WriteProjectFiles(const Target* target, Err* err) {
244 SourceFile target_file = GetTargetOutputDir(target).ResolveRelativeFile(
245 Value(nullptr, target->label().name() + ".vcxproj"), err);
246 if (target_file.is_null())
247 return false;
248
249 base::FilePath vcxproj_path = build_settings_->GetFullPath(target_file);
250 std::string vcxproj_path_str = FilePathToUTF8(vcxproj_path);
251
252 projects_.push_back(
253 new SolutionEntry(target->label().name(), vcxproj_path_str,
254 MakeGuid(vcxproj_path_str, kGuidSeedProject)));
255 projects_.back()->label_dir_path =
256 FilePathToUTF8(build_settings_->GetFullPath(target->label().dir()));
257
258 std::stringstream vcxproj_string_out;
259 if (!WriteProjectFileContents(vcxproj_string_out, *projects_.back(), target,
260 err)) {
261 projects_.pop_back();
262 return false;
263 }
264
265 // Only write the content to the file if it's different. That is
266 // both a performance optimization and more importantly, prevents
267 // Visual Studio from reloading the projects.
268 if (!HasSameContent(vcxproj_string_out, vcxproj_path)) {
269 std::string content = vcxproj_string_out.str();
270 int size = static_cast<int>(content.size());
271 if (base::WriteFile(vcxproj_path, content.c_str(), size) != size) {
272 *err = Err(Location(), "Couldn't open " + target->label().name() +
273 ".vcxproj for writing");
274 return false;
275 }
276 }
277
278 base::FilePath filters_path = UTF8ToFilePath(vcxproj_path_str + ".filters");
279
280 std::stringstream filters_string_out;
281 WriteFiltersFileContents(filters_string_out, target);
282
283 if (!HasSameContent(filters_string_out, filters_path)) {
284 std::string content = filters_string_out.str();
285 int size = static_cast<int>(content.size());
286 if (base::WriteFile(filters_path, content.c_str(), size) != size) {
287 *err = Err(Location(), "Couldn't open " + target->label().name() +
288 ".vcxproj.filters for writing");
289 return false;
290 }
291 }
292
293 return true;
294 }
295
296 bool VisualStudioWriter::WriteProjectFileContents(
297 std::ostream& out,
298 const SolutionEntry& solution_project,
299 const Target* target,
300 Err* err) {
301 PathOutput path_output(GetTargetOutputDir(target),
302 build_settings_->root_path_utf8(),
303 EscapingMode::ESCAPE_NONE);
304
305 out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
306 XmlElementWriter project(
307 out, "Project",
308 XmlAttributes("DefaultTargets", "Build")
309 .add("ToolsVersion", kVisualStudioVersion)
310 .add("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"));
311
312 {
313 scoped_ptr<XmlElementWriter> configurations = project.SubElement(
314 "ItemGroup", XmlAttributes("Label", "ProjectConfigurations"));
315 std::string config_name = is_debug_config_ ? "Debug" : "Release";
316 scoped_ptr<XmlElementWriter> project_config = configurations->SubElement(
317 "ProjectConfiguration",
318 XmlAttributes("Include", config_name + '|' + config_platform_));
319 project_config->SubElement("Configuration")->Text(config_name);
320 project_config->SubElement("Platform")->Text(config_platform_);
321 }
322
323 {
324 scoped_ptr<XmlElementWriter> globals =
325 project.SubElement("PropertyGroup", XmlAttributes("Label", "Globals"));
326 globals->SubElement("ProjectGuid")->Text(solution_project.guid);
327 globals->SubElement("Keyword")->Text("Win32Proj");
328 globals->SubElement("RootNamespace")->Text(target->label().name());
329 globals->SubElement("IgnoreWarnCompileDuplicatedFilename")->Text("true");
330 globals->SubElement("PreferredToolArchitecture")->Text("x64");
331 }
332
333 project.SubElement(
334 "Import", XmlAttributes("Project",
335 "$(VCTargetsPath)\\Microsoft.Cpp.Default.props"));
336
337 {
338 scoped_ptr<XmlElementWriter> configuration = project.SubElement(
339 "PropertyGroup", XmlAttributes("Label", "Configuration"));
340 configuration->SubElement("CharacterSet")->Text("Unicode");
341 std::string configuration_type = GetConfigurationType(target, err);
342 if (configuration_type.empty())
343 return false;
344 configuration->SubElement("ConfigurationType")->Text(configuration_type);
345 }
346
347 {
348 scoped_ptr<XmlElementWriter> locals =
349 project.SubElement("PropertyGroup", XmlAttributes("Label", "Locals"));
350 locals->SubElement("PlatformToolset")->Text(kToolsetVersion);
351 }
352
353 project.SubElement(
354 "Import",
355 XmlAttributes("Project", "$(VCTargetsPath)\\Microsoft.Cpp.props"));
356 project.SubElement(
357 "Import",
358 XmlAttributes("Project",
359 "$(VCTargetsPath)\\BuildCustomizations\\masm.props"));
360 project.SubElement("ImportGroup",
361 XmlAttributes("Label", "ExtensionSettings"));
362
363 {
364 scoped_ptr<XmlElementWriter> property_sheets = project.SubElement(
365 "ImportGroup", XmlAttributes("Label", "PropertySheets"));
366 property_sheets->SubElement(
367 "Import",
368 XmlAttributes(
369 "Condition",
370 "exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')")
371 .add("Label", "LocalAppDataPlatform")
372 .add("Project",
373 "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props"));
374 }
375
376 project.SubElement("PropertyGroup", XmlAttributes("Label", "UserMacros"));
377
378 {
379 scoped_ptr<XmlElementWriter> properties =
380 project.SubElement("PropertyGroup");
381 {
382 scoped_ptr<XmlElementWriter> out_dir = properties->SubElement("OutDir");
383 path_output.WriteDir(out_dir->StartContent(false),
384 build_settings_->build_dir(),
385 PathOutput::DIR_INCLUDE_LAST_SLASH);
386 }
387 properties->SubElement("TargetName")->Text("$(ProjectName)");
388 properties->SubElement("TargetPath")
389 ->Text("$(OutDir)\\$(ProjectName)$(TargetExt)");
390 }
391
392 {
393 scoped_ptr<XmlElementWriter> item_definitions =
394 project.SubElement("ItemDefinitionGroup");
395 {
396 scoped_ptr<XmlElementWriter> cl_compile =
397 item_definitions->SubElement("ClCompile");
398 {
399 scoped_ptr<XmlElementWriter> include_dirs =
400 cl_compile->SubElement("AdditionalIncludeDirectories");
401 RecursiveTargetConfigToStream<SourceDir>(
402 target, &ConfigValues::include_dirs, IncludeDirWriter(path_output),
403 include_dirs->StartContent(false));
404 include_dirs->Text(windows_kits_include_dirs_ +
405 "$(VSInstallDir)\\VC\\atlmfc\\include;" +
406 "%(AdditionalIncludeDirectories)");
407 }
408 CompilerOptions options;
409 ParseCompilerOptions(target, &options);
410 if (!options.additional_options.empty()) {
411 cl_compile->SubElement("AdditionalOptions")
412 ->Text(options.additional_options + "%(AdditionalOptions)");
413 }
414 if (!options.buffer_security_check.empty()) {
415 cl_compile->SubElement("BufferSecurityCheck")
416 ->Text(options.buffer_security_check);
417 }
418 cl_compile->SubElement("CompileAsWinRT")->Text("false");
419 cl_compile->SubElement("DebugInformationFormat")->Text("ProgramDatabase");
420 if (!options.disable_specific_warnings.empty()) {
421 cl_compile->SubElement("DisableSpecificWarnings")
422 ->Text(options.disable_specific_warnings +
423 "%(DisableSpecificWarnings)");
424 }
425 cl_compile->SubElement("ExceptionHandling")->Text("false");
426 if (!options.forced_include_files.empty()) {
427 cl_compile->SubElement("ForcedIncludeFiles")
428 ->Text(options.forced_include_files);
429 }
430 cl_compile->SubElement("MinimalRebuild")->Text("false");
431 if (!options.optimization.empty())
432 cl_compile->SubElement("Optimization")->Text(options.optimization);
433 if (target->config_values().has_precompiled_headers()) {
434 cl_compile->SubElement("PrecompiledHeader")->Text("Use");
435 cl_compile->SubElement("PrecompiledHeaderFile")
436 ->Text(target->config_values().precompiled_header());
437 } else {
438 cl_compile->SubElement("PrecompiledHeader")->Text("NotUsing");
439 }
440 {
441 scoped_ptr<XmlElementWriter> preprocessor_definitions =
442 cl_compile->SubElement("PreprocessorDefinitions");
443 RecursiveTargetConfigToStream<std::string>(
444 target, &ConfigValues::defines, SemicolonSeparatedWriter(),
445 preprocessor_definitions->StartContent(false));
446 preprocessor_definitions->Text("%(PreprocessorDefinitions)");
447 }
448 if (!options.runtime_library.empty())
449 cl_compile->SubElement("RuntimeLibrary")->Text(options.runtime_library);
450 if (!options.treat_warning_as_error.empty()) {
451 cl_compile->SubElement("TreatWarningAsError")
452 ->Text(options.treat_warning_as_error);
453 }
454 if (!options.warning_level.empty())
455 cl_compile->SubElement("WarningLevel")->Text(options.warning_level);
456 }
457
458 // We don't include resource compilation and link options as ninja files
459 // are used to generate real build.
460 }
461
462 {
463 scoped_ptr<XmlElementWriter> group = project.SubElement("ItemGroup");
464 if (!target->config_values().precompiled_source().is_null()) {
465 group
466 ->SubElement(
467 "ClCompile", "Include",
468 SourceFileWriter(path_output,
469 target->config_values().precompiled_source()))
470 ->SubElement("PrecompiledHeader")
471 ->Text("Create");
472 }
473
474 for (const SourceFile& file : target->sources()) {
475 SourceFileType type = GetSourceFileType(file);
476 if (type == SOURCE_H || type == SOURCE_CPP || type == SOURCE_C) {
477 group->SubElement(type == SOURCE_H ? "ClInclude" : "ClCompile",
478 "Include", SourceFileWriter(path_output, file));
479 }
480 }
481 }
482
483 project.SubElement(
484 "Import",
485 XmlAttributes("Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets"));
486 project.SubElement(
487 "Import",
488 XmlAttributes("Project",
489 "$(VCTargetsPath)\\BuildCustomizations\\masm.targets"));
490 project.SubElement("ImportGroup", XmlAttributes("Label", "ExtensionTargets"));
491
492 {
493 scoped_ptr<XmlElementWriter> build =
494 project.SubElement("Target", XmlAttributes("Name", "Build"));
495 build->SubElement(
496 "Exec",
497 XmlAttributes("Command", "call ninja.exe -C $(OutDir) $(ProjectName)"));
498 }
499
500 {
501 scoped_ptr<XmlElementWriter> clean =
502 project.SubElement("Target", XmlAttributes("Name", "Clean"));
503 clean->SubElement(
504 "Exec",
505 XmlAttributes("Command",
506 "call ninja.exe -C $(OutDir) -tclean $(ProjectName)"));
507 }
508
509 return true;
510 }
511
512 void VisualStudioWriter::WriteFiltersFileContents(std::ostream& out,
513 const Target* target) {
514 out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
515 XmlElementWriter project(
516 out, "Project",
517 XmlAttributes("ToolsVersion", "4.0")
518 .add("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"));
519
520 std::ostringstream files_out;
521
522 {
523 scoped_ptr<XmlElementWriter> filters_group =
524 project.SubElement("ItemGroup");
525 XmlElementWriter files_group(files_out, "ItemGroup", XmlAttributes(), 2);
526
527 // File paths are relative to vcxproj files which are generated to out dirs.
528 // Filters tree structure need to reflect source directories and be relative
529 // to target file. We need two path outputs then.
530 PathOutput file_path_output(GetTargetOutputDir(target),
531 build_settings_->root_path_utf8(),
532 EscapingMode::ESCAPE_NONE);
533 PathOutput filter_path_output(target->label().dir(),
534 build_settings_->root_path_utf8(),
535 EscapingMode::ESCAPE_NONE);
536
537 std::set<std::string> processed_filters;
538
539 for (const SourceFile& file : target->sources()) {
540 SourceFileType type = GetSourceFileType(file);
541 if (type == SOURCE_H || type == SOURCE_CPP || type == SOURCE_C) {
542 scoped_ptr<XmlElementWriter> cl_item = files_group.SubElement(
543 type == SOURCE_H ? "ClInclude" : "ClCompile", "Include",
544 SourceFileWriter(file_path_output, file));
545
546 std::ostringstream target_relative_out;
547 filter_path_output.WriteFile(target_relative_out, file);
548 std::string target_relative_path = target_relative_out.str();
549 ConvertPathToSystem(&target_relative_path);
550 base::StringPiece filter_path = FindParentDir(&target_relative_path);
551
552 if (!filter_path.empty()) {
553 std::string filter_path_str = filter_path.as_string();
554 while (processed_filters.find(filter_path_str) ==
555 processed_filters.end()) {
556 auto it = processed_filters.insert(filter_path_str).first;
557 filters_group
558 ->SubElement("Filter",
559 XmlAttributes("Include", filter_path_str))
560 ->SubElement("UniqueIdentifier")
561 ->Text(MakeGuid(filter_path_str, kGuidSeedFilter));
562 filter_path_str = FindParentDir(&(*it)).as_string();
563 if (filter_path_str.empty())
564 break;
565 }
566 cl_item->SubElement("Filter")->Text(filter_path);
567 }
568 }
569 }
570 }
571
572 project.Text(files_out.str());
573 }
574
575 bool VisualStudioWriter::WriteSolutionFile(Err* err) {
576 SourceFile sln_file = build_settings_->build_dir().ResolveRelativeFile(
577 Value(nullptr, "all.sln"), err);
578 if (sln_file.is_null())
579 return false;
580
581 base::FilePath sln_path = build_settings_->GetFullPath(sln_file);
582
583 std::stringstream string_out;
584 WriteSolutionFileContents(string_out, sln_path.DirName());
585
586 // Only write the content to the file if it's different. That is
587 // both a performance optimization and more importantly, prevents
588 // Visual Studio from reloading the projects.
589 if (HasSameContent(string_out, sln_path))
590 return true;
591
592 std::string content = string_out.str();
593 int size = static_cast<int>(content.size());
594 if (base::WriteFile(sln_path, content.c_str(), size) != size) {
595 *err = Err(Location(), "Couldn't open all.sln for writing");
596 return false;
597 }
598
599 return true;
600 }
601
602 void VisualStudioWriter::WriteSolutionFileContents(
603 std::ostream& out,
604 const base::FilePath& solution_dir_path) {
605 out << "Microsoft Visual Studio Solution File, Format Version 12.00"
606 << std::endl;
607 out << "# Visual Studio 2015" << std::endl;
608
609 SourceDir solution_dir(FilePathToUTF8(solution_dir_path));
610 for (const SolutionEntry* folder : folders_) {
611 out << "Project(\"" << kGuidTypeFolder << "\") = \"(" << folder->name
612 << ")\", \"" << RebasePath(folder->path, solution_dir, "/") << "\", \""
613 << folder->guid << "\"" << std::endl;
614 out << "EndProject" << std::endl;
615 }
616
617 for (const SolutionEntry* project : projects_) {
618 out << "Project(\"" << kGuidTypeProject << "\") = \"" << project->name
619 << "\", \"" << RebasePath(project->path, solution_dir, "/") << "\", \""
620 << project->guid << "\"" << std::endl;
621 out << "EndProject" << std::endl;
622 }
623
624 out << "Global" << std::endl;
625
626 out << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"
627 << std::endl;
628 const std::string config_mode =
629 std::string(is_debug_config_ ? "Debug" : "Release") + '|' +
630 config_platform_;
631 out << "\t\t" << config_mode << " = " << config_mode << std::endl;
632 out << "\tEndGlobalSection" << std::endl;
633
634 out << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"
635 << std::endl;
636 for (const SolutionEntry* project : projects_) {
637 out << "\t\t" << project->guid << '.' << config_mode
638 << ".ActiveCfg = " << config_mode << std::endl;
639 out << "\t\t" << project->guid << '.' << config_mode
640 << ".Build.0 = " << config_mode << std::endl;
641 }
642 out << "\tEndGlobalSection" << std::endl;
643
644 out << "\tGlobalSection(SolutionProperties) = preSolution" << std::endl;
645 out << "\t\tHideSolutionNode = FALSE" << std::endl;
646 out << "\tEndGlobalSection" << std::endl;
647
648 out << "\tGlobalSection(NestedProjects) = preSolution" << std::endl;
649 for (const SolutionEntry* folder : folders_) {
650 if (folder->parent_folder) {
651 out << "\t\t" << folder->guid << " = " << folder->parent_folder->guid
652 << std::endl;
653 }
654 }
655 for (const SolutionEntry* project : projects_) {
656 out << "\t\t" << project->guid << " = " << project->parent_folder->guid
657 << std::endl;
658 }
659 out << "\tEndGlobalSection" << std::endl;
660
661 out << "EndGlobal" << std::endl;
662 }
663
664 void VisualStudioWriter::ResolveSolutionFolders() {
665 root_folder_path_.clear();
666
667 // Get all project directories. Create solution folder for each directory.
668 std::map<base::StringPiece, SolutionEntry*> processed_paths;
669 for (SolutionEntry* project : projects_) {
670 base::StringPiece folder_path = project->label_dir_path;
671 if (IsSlash(folder_path[folder_path.size() - 1]))
672 folder_path = folder_path.substr(0, folder_path.size() - 1);
673 auto it = processed_paths.find(folder_path);
674 if (it != processed_paths.end()) {
675 project->parent_folder = it->second;
676 } else {
677 std::string folder_path_str = folder_path.as_string();
678 SolutionEntry* folder = new SolutionEntry(
679 FindLastDirComponent(SourceDir(folder_path)).as_string(),
680 folder_path_str, MakeGuid(folder_path_str, kGuidSeedFolder));
681 folders_.push_back(folder);
682 project->parent_folder = folder;
683 processed_paths[folder_path] = folder;
684
685 if (root_folder_path_.empty()) {
686 root_folder_path_ = folder_path_str;
687 } else {
688 size_t common_prefix_len = 0;
689 size_t max_common_length =
690 std::min(root_folder_path_.size(), folder_path.size());
691 size_t i;
692 for (i = common_prefix_len; i < max_common_length; ++i) {
693 if (IsSlash(root_folder_path_[i]) && IsSlash(folder_path[i]))
694 common_prefix_len = i + 1;
695 else if (root_folder_path_[i] != folder_path[i])
696 break;
697 }
698 if (i == max_common_length)
699 common_prefix_len = max_common_length;
700 if (common_prefix_len < root_folder_path_.size()) {
701 if (IsSlash(root_folder_path_[common_prefix_len - 1]))
702 --common_prefix_len;
703 root_folder_path_ = root_folder_path_.substr(0, common_prefix_len);
704 }
705 }
706 }
707 }
708
709 // Create also all parent folders up to |root_folder_path_|.
710 SolutionEntries additional_folders;
711 for (SolutionEntry* folder : folders_) {
712 if (folder->path == root_folder_path_)
713 continue;
714
715 base::StringPiece parent_path;
716 while ((parent_path = FindParentDir(&folder->path)) != root_folder_path_) {
717 auto it = processed_paths.find(parent_path);
718 if (it != processed_paths.end()) {
719 folder = it->second;
720 } else {
721 folder = new SolutionEntry(
722 FindLastDirComponent(SourceDir(parent_path)).as_string(),
723 parent_path.as_string(),
724 MakeGuid(parent_path.as_string(), kGuidSeedFolder));
725 additional_folders.push_back(folder);
726 processed_paths[parent_path] = folder;
727 }
728 }
729 }
730 folders_.insert(folders_.end(), additional_folders.begin(),
731 additional_folders.end());
732
733 // Sort folders by path.
734 std::sort(folders_.begin(), folders_.end(),
735 [](const SolutionEntry* a, const SolutionEntry* b) {
736 return a->path < b->path;
737 });
738
739 // Match subfolders with their parents. Since |folders_| are sorted by path we
740 // know that parent folder always precedes its children in vector.
741 SolutionEntries parents;
742 for (SolutionEntry* folder : folders_) {
743 while (!parents.empty()) {
744 if (base::StartsWith(folder->path, parents.back()->path,
745 base::CompareCase::SENSITIVE)) {
746 folder->parent_folder = parents.back();
747 break;
748 } else {
749 parents.pop_back();
750 }
751 }
752 parents.push_back(folder);
753 }
754 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698