| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "tools/gn/visual_studio_writer.h" | 5 #include "tools/gn/visual_studio_writer.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <map> | 8 #include <map> |
| 9 #include <set> | 9 #include <set> |
| 10 #include <string> | 10 #include <string> |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 175 | 175 |
| 176 } // namespace | 176 } // namespace |
| 177 | 177 |
| 178 VisualStudioWriter::SolutionEntry::SolutionEntry(const std::string& _name, | 178 VisualStudioWriter::SolutionEntry::SolutionEntry(const std::string& _name, |
| 179 const std::string& _path, | 179 const std::string& _path, |
| 180 const std::string& _guid) | 180 const std::string& _guid) |
| 181 : name(_name), path(_path), guid(_guid), parent_folder(nullptr) {} | 181 : name(_name), path(_path), guid(_guid), parent_folder(nullptr) {} |
| 182 | 182 |
| 183 VisualStudioWriter::SolutionEntry::~SolutionEntry() = default; | 183 VisualStudioWriter::SolutionEntry::~SolutionEntry() = default; |
| 184 | 184 |
| 185 VisualStudioWriter::SolutionProject::SolutionProject( |
| 186 const std::string& _name, |
| 187 const std::string& _path, |
| 188 const std::string& _guid, |
| 189 const std::string& _label_dir_path, |
| 190 const std::string& _config_platform) |
| 191 : SolutionEntry(_name, _path, _guid), |
| 192 label_dir_path(_label_dir_path), |
| 193 config_platform(_config_platform) {} |
| 194 |
| 195 VisualStudioWriter::SolutionProject::~SolutionProject() = default; |
| 196 |
| 185 VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings) | 197 VisualStudioWriter::VisualStudioWriter(const BuildSettings* build_settings) |
| 186 : build_settings_(build_settings) { | 198 : build_settings_(build_settings) { |
| 187 const Value* value = build_settings->build_args().GetArgOverride("is_debug"); | 199 const Value* value = build_settings->build_args().GetArgOverride("is_debug"); |
| 188 is_debug_config_ = value == nullptr || value->boolean_value(); | 200 is_debug_config_ = value == nullptr || value->boolean_value(); |
| 189 config_platform_ = "Win32"; | 201 config_platform_ = "Win32"; |
| 190 value = build_settings->build_args().GetArgOverride(variables::kTargetCpu); | 202 value = build_settings->build_args().GetArgOverride(variables::kTargetCpu); |
| 191 if (value != nullptr && value->string_value() == "x64") | 203 if (value != nullptr && value->string_value() == "x64") |
| 192 config_platform_ = "x64"; | 204 config_platform_ = "x64"; |
| 193 | 205 |
| 194 windows_kits_include_dirs_ = GetWindowsKitsIncludeDirs(); | 206 windows_kits_include_dirs_ = GetWindowsKitsIncludeDirs(); |
| 195 } | 207 } |
| 196 | 208 |
| 197 VisualStudioWriter::~VisualStudioWriter() { | 209 VisualStudioWriter::~VisualStudioWriter() { |
| 198 STLDeleteContainerPointers(projects_.begin(), projects_.end()); | 210 STLDeleteContainerPointers(projects_.begin(), projects_.end()); |
| 199 STLDeleteContainerPointers(folders_.begin(), folders_.end()); | 211 STLDeleteContainerPointers(folders_.begin(), folders_.end()); |
| 200 } | 212 } |
| 201 | 213 |
| 202 // static | 214 // static |
| 203 bool VisualStudioWriter::RunAndWriteFiles(const BuildSettings* build_settings, | 215 bool VisualStudioWriter::RunAndWriteFiles(const BuildSettings* build_settings, |
| 204 Builder* builder, | 216 Builder* builder, |
| 205 Err* err) { | 217 Err* err) { |
| 206 std::vector<const Target*> targets = builder->GetAllResolvedTargets(); | 218 std::vector<const Target*> targets = builder->GetAllResolvedTargets(); |
| 207 | 219 |
| 208 VisualStudioWriter writer(build_settings); | 220 VisualStudioWriter writer(build_settings); |
| 209 writer.projects_.reserve(targets.size()); | 221 writer.projects_.reserve(targets.size()); |
| 210 writer.folders_.reserve(targets.size()); | 222 writer.folders_.reserve(targets.size()); |
| 211 | 223 |
| 212 std::set<std::string> processed_targets; | |
| 213 for (const Target* target : targets) { | 224 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. | 225 // Skip actions and groups. |
| 221 if (target->output_type() == Target::GROUP || | 226 if (target->output_type() == Target::GROUP || |
| 222 target->output_type() == Target::COPY_FILES || | 227 target->output_type() == Target::COPY_FILES || |
| 223 target->output_type() == Target::ACTION || | 228 target->output_type() == Target::ACTION || |
| 224 target->output_type() == Target::ACTION_FOREACH) { | 229 target->output_type() == Target::ACTION_FOREACH) { |
| 225 continue; | 230 continue; |
| 226 } | 231 } |
| 227 | 232 |
| 228 if (!writer.WriteProjectFiles(target, err)) | 233 if (!writer.WriteProjectFiles(target, err)) |
| 229 return false; | 234 return false; |
| 230 | |
| 231 processed_targets.insert(target_path); | |
| 232 } | 235 } |
| 233 | 236 |
| 234 if (writer.projects_.empty()) { | 237 if (writer.projects_.empty()) { |
| 235 *err = Err(Location(), "No Visual Studio projects generated."); | 238 *err = Err(Location(), "No Visual Studio projects generated."); |
| 236 return false; | 239 return false; |
| 237 } | 240 } |
| 238 | 241 |
| 239 writer.ResolveSolutionFolders(); | 242 writer.ResolveSolutionFolders(); |
| 240 return writer.WriteSolutionFile(err); | 243 return writer.WriteSolutionFile(err); |
| 241 } | 244 } |
| 242 | 245 |
| 243 bool VisualStudioWriter::WriteProjectFiles(const Target* target, Err* err) { | 246 bool VisualStudioWriter::WriteProjectFiles(const Target* target, Err* err) { |
| 247 std::string project_name = target->label().name(); |
| 248 std::string project_config_platform = config_platform_; |
| 249 if (!target->settings()->is_default()) { |
| 250 project_name += "_" + target->toolchain()->label().name(); |
| 251 project_config_platform = target->toolchain() |
| 252 ->settings() |
| 253 ->build_settings() |
| 254 ->build_args() |
| 255 .GetArgOverride(variables::kCurrentCpu) |
| 256 ->string_value(); |
| 257 if (project_config_platform == "x86") |
| 258 project_config_platform = "Win32"; |
| 259 } |
| 260 |
| 244 SourceFile target_file = GetTargetOutputDir(target).ResolveRelativeFile( | 261 SourceFile target_file = GetTargetOutputDir(target).ResolveRelativeFile( |
| 245 Value(nullptr, target->label().name() + ".vcxproj"), err); | 262 Value(nullptr, project_name + ".vcxproj"), err); |
| 246 if (target_file.is_null()) | 263 if (target_file.is_null()) |
| 247 return false; | 264 return false; |
| 248 | 265 |
| 249 base::FilePath vcxproj_path = build_settings_->GetFullPath(target_file); | 266 base::FilePath vcxproj_path = build_settings_->GetFullPath(target_file); |
| 250 std::string vcxproj_path_str = FilePathToUTF8(vcxproj_path); | 267 std::string vcxproj_path_str = FilePathToUTF8(vcxproj_path); |
| 251 | 268 |
| 252 projects_.push_back( | 269 projects_.push_back(new SolutionProject( |
| 253 new SolutionEntry(target->label().name(), vcxproj_path_str, | 270 project_name, vcxproj_path_str, |
| 254 MakeGuid(vcxproj_path_str, kGuidSeedProject))); | 271 MakeGuid(vcxproj_path_str, kGuidSeedProject), |
| 255 projects_.back()->label_dir_path = | 272 FilePathToUTF8(build_settings_->GetFullPath(target->label().dir())), |
| 256 FilePathToUTF8(build_settings_->GetFullPath(target->label().dir())); | 273 project_config_platform)); |
| 257 | 274 |
| 258 std::stringstream vcxproj_string_out; | 275 std::stringstream vcxproj_string_out; |
| 259 if (!WriteProjectFileContents(vcxproj_string_out, *projects_.back(), target, | 276 if (!WriteProjectFileContents(vcxproj_string_out, *projects_.back(), target, |
| 260 err)) { | 277 err)) { |
| 261 projects_.pop_back(); | 278 projects_.pop_back(); |
| 262 return false; | 279 return false; |
| 263 } | 280 } |
| 264 | 281 |
| 265 // Only write the content to the file if it's different. That is | 282 // Only write the content to the file if it's different. That is |
| 266 // both a performance optimization and more importantly, prevents | 283 // both a performance optimization and more importantly, prevents |
| (...skipping 21 matching lines...) Expand all Loading... |
| 288 ".vcxproj.filters for writing"); | 305 ".vcxproj.filters for writing"); |
| 289 return false; | 306 return false; |
| 290 } | 307 } |
| 291 } | 308 } |
| 292 | 309 |
| 293 return true; | 310 return true; |
| 294 } | 311 } |
| 295 | 312 |
| 296 bool VisualStudioWriter::WriteProjectFileContents( | 313 bool VisualStudioWriter::WriteProjectFileContents( |
| 297 std::ostream& out, | 314 std::ostream& out, |
| 298 const SolutionEntry& solution_project, | 315 const SolutionProject& solution_project, |
| 299 const Target* target, | 316 const Target* target, |
| 300 Err* err) { | 317 Err* err) { |
| 301 PathOutput path_output(GetTargetOutputDir(target), | 318 PathOutput path_output(GetTargetOutputDir(target), |
| 302 build_settings_->root_path_utf8(), | 319 build_settings_->root_path_utf8(), |
| 303 EscapingMode::ESCAPE_NONE); | 320 EscapingMode::ESCAPE_NONE); |
| 304 | 321 |
| 305 out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl; | 322 out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl; |
| 306 XmlElementWriter project( | 323 XmlElementWriter project( |
| 307 out, "Project", | 324 out, "Project", |
| 308 XmlAttributes("DefaultTargets", "Build") | 325 XmlAttributes("DefaultTargets", "Build") |
| 309 .add("ToolsVersion", kVisualStudioVersion) | 326 .add("ToolsVersion", kVisualStudioVersion) |
| 310 .add("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003")); | 327 .add("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003")); |
| 311 | 328 |
| 312 { | 329 { |
| 313 scoped_ptr<XmlElementWriter> configurations = project.SubElement( | 330 scoped_ptr<XmlElementWriter> configurations = project.SubElement( |
| 314 "ItemGroup", XmlAttributes("Label", "ProjectConfigurations")); | 331 "ItemGroup", XmlAttributes("Label", "ProjectConfigurations")); |
| 315 std::string config_name = is_debug_config_ ? "Debug" : "Release"; | 332 std::string config_name = is_debug_config_ ? "Debug" : "Release"; |
| 316 scoped_ptr<XmlElementWriter> project_config = configurations->SubElement( | 333 scoped_ptr<XmlElementWriter> project_config = configurations->SubElement( |
| 317 "ProjectConfiguration", | 334 "ProjectConfiguration", |
| 318 XmlAttributes("Include", config_name + '|' + config_platform_)); | 335 XmlAttributes("Include", |
| 336 config_name + '|' + solution_project.config_platform)); |
| 319 project_config->SubElement("Configuration")->Text(config_name); | 337 project_config->SubElement("Configuration")->Text(config_name); |
| 320 project_config->SubElement("Platform")->Text(config_platform_); | 338 project_config->SubElement("Platform") |
| 339 ->Text(solution_project.config_platform); |
| 321 } | 340 } |
| 322 | 341 |
| 323 { | 342 { |
| 324 scoped_ptr<XmlElementWriter> globals = | 343 scoped_ptr<XmlElementWriter> globals = |
| 325 project.SubElement("PropertyGroup", XmlAttributes("Label", "Globals")); | 344 project.SubElement("PropertyGroup", XmlAttributes("Label", "Globals")); |
| 326 globals->SubElement("ProjectGuid")->Text(solution_project.guid); | 345 globals->SubElement("ProjectGuid")->Text(solution_project.guid); |
| 327 globals->SubElement("Keyword")->Text("Win32Proj"); | 346 globals->SubElement("Keyword")->Text("Win32Proj"); |
| 328 globals->SubElement("RootNamespace")->Text(target->label().name()); | 347 globals->SubElement("RootNamespace")->Text(target->label().name()); |
| 329 globals->SubElement("IgnoreWarnCompileDuplicatedFilename")->Text("true"); | 348 globals->SubElement("IgnoreWarnCompileDuplicatedFilename")->Text("true"); |
| 330 globals->SubElement("PreferredToolArchitecture")->Text("x64"); | 349 globals->SubElement("PreferredToolArchitecture")->Text("x64"); |
| (...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 618 out << "Project(\"" << kGuidTypeProject << "\") = \"" << project->name | 637 out << "Project(\"" << kGuidTypeProject << "\") = \"" << project->name |
| 619 << "\", \"" << RebasePath(project->path, solution_dir, "/") << "\", \"" | 638 << "\", \"" << RebasePath(project->path, solution_dir, "/") << "\", \"" |
| 620 << project->guid << "\"" << std::endl; | 639 << project->guid << "\"" << std::endl; |
| 621 out << "EndProject" << std::endl; | 640 out << "EndProject" << std::endl; |
| 622 } | 641 } |
| 623 | 642 |
| 624 out << "Global" << std::endl; | 643 out << "Global" << std::endl; |
| 625 | 644 |
| 626 out << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution" | 645 out << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution" |
| 627 << std::endl; | 646 << std::endl; |
| 628 const std::string config_mode = | 647 const std::string config_mode_prefix = |
| 629 std::string(is_debug_config_ ? "Debug" : "Release") + '|' + | 648 std::string(is_debug_config_ ? "Debug" : "Release") + '|'; |
| 630 config_platform_; | 649 const std::string config_mode = config_mode_prefix + config_platform_; |
| 631 out << "\t\t" << config_mode << " = " << config_mode << std::endl; | 650 out << "\t\t" << config_mode << " = " << config_mode << std::endl; |
| 632 out << "\tEndGlobalSection" << std::endl; | 651 out << "\tEndGlobalSection" << std::endl; |
| 633 | 652 |
| 634 out << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution" | 653 out << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution" |
| 635 << std::endl; | 654 << std::endl; |
| 636 for (const SolutionEntry* project : projects_) { | 655 for (const SolutionProject* project : projects_) { |
| 656 const std::string project_config_mode = |
| 657 config_mode_prefix + project->config_platform; |
| 637 out << "\t\t" << project->guid << '.' << config_mode | 658 out << "\t\t" << project->guid << '.' << config_mode |
| 638 << ".ActiveCfg = " << config_mode << std::endl; | 659 << ".ActiveCfg = " << project_config_mode << std::endl; |
| 639 out << "\t\t" << project->guid << '.' << config_mode | 660 out << "\t\t" << project->guid << '.' << config_mode |
| 640 << ".Build.0 = " << config_mode << std::endl; | 661 << ".Build.0 = " << project_config_mode << std::endl; |
| 641 } | 662 } |
| 642 out << "\tEndGlobalSection" << std::endl; | 663 out << "\tEndGlobalSection" << std::endl; |
| 643 | 664 |
| 644 out << "\tGlobalSection(SolutionProperties) = preSolution" << std::endl; | 665 out << "\tGlobalSection(SolutionProperties) = preSolution" << std::endl; |
| 645 out << "\t\tHideSolutionNode = FALSE" << std::endl; | 666 out << "\t\tHideSolutionNode = FALSE" << std::endl; |
| 646 out << "\tEndGlobalSection" << std::endl; | 667 out << "\tEndGlobalSection" << std::endl; |
| 647 | 668 |
| 648 out << "\tGlobalSection(NestedProjects) = preSolution" << std::endl; | 669 out << "\tGlobalSection(NestedProjects) = preSolution" << std::endl; |
| 649 for (const SolutionEntry* folder : folders_) { | 670 for (const SolutionEntry* folder : folders_) { |
| 650 if (folder->parent_folder) { | 671 if (folder->parent_folder) { |
| 651 out << "\t\t" << folder->guid << " = " << folder->parent_folder->guid | 672 out << "\t\t" << folder->guid << " = " << folder->parent_folder->guid |
| 652 << std::endl; | 673 << std::endl; |
| 653 } | 674 } |
| 654 } | 675 } |
| 655 for (const SolutionEntry* project : projects_) { | 676 for (const SolutionEntry* project : projects_) { |
| 656 out << "\t\t" << project->guid << " = " << project->parent_folder->guid | 677 out << "\t\t" << project->guid << " = " << project->parent_folder->guid |
| 657 << std::endl; | 678 << std::endl; |
| 658 } | 679 } |
| 659 out << "\tEndGlobalSection" << std::endl; | 680 out << "\tEndGlobalSection" << std::endl; |
| 660 | 681 |
| 661 out << "EndGlobal" << std::endl; | 682 out << "EndGlobal" << std::endl; |
| 662 } | 683 } |
| 663 | 684 |
| 664 void VisualStudioWriter::ResolveSolutionFolders() { | 685 void VisualStudioWriter::ResolveSolutionFolders() { |
| 665 root_folder_path_.clear(); | 686 root_folder_path_.clear(); |
| 666 | 687 |
| 667 // Get all project directories. Create solution folder for each directory. | 688 // Get all project directories. Create solution folder for each directory. |
| 668 std::map<base::StringPiece, SolutionEntry*> processed_paths; | 689 std::map<base::StringPiece, SolutionEntry*> processed_paths; |
| 669 for (SolutionEntry* project : projects_) { | 690 for (SolutionProject* project : projects_) { |
| 670 base::StringPiece folder_path = project->label_dir_path; | 691 base::StringPiece folder_path = project->label_dir_path; |
| 671 if (IsSlash(folder_path[folder_path.size() - 1])) | 692 if (IsSlash(folder_path[folder_path.size() - 1])) |
| 672 folder_path = folder_path.substr(0, folder_path.size() - 1); | 693 folder_path = folder_path.substr(0, folder_path.size() - 1); |
| 673 auto it = processed_paths.find(folder_path); | 694 auto it = processed_paths.find(folder_path); |
| 674 if (it != processed_paths.end()) { | 695 if (it != processed_paths.end()) { |
| 675 project->parent_folder = it->second; | 696 project->parent_folder = it->second; |
| 676 } else { | 697 } else { |
| 677 std::string folder_path_str = folder_path.as_string(); | 698 std::string folder_path_str = folder_path.as_string(); |
| 678 SolutionEntry* folder = new SolutionEntry( | 699 SolutionEntry* folder = new SolutionEntry( |
| 679 FindLastDirComponent(SourceDir(folder_path)).as_string(), | 700 FindLastDirComponent(SourceDir(folder_path)).as_string(), |
| (...skipping 20 matching lines...) Expand all Loading... |
| 700 if (common_prefix_len < root_folder_path_.size()) { | 721 if (common_prefix_len < root_folder_path_.size()) { |
| 701 if (IsSlash(root_folder_path_[common_prefix_len - 1])) | 722 if (IsSlash(root_folder_path_[common_prefix_len - 1])) |
| 702 --common_prefix_len; | 723 --common_prefix_len; |
| 703 root_folder_path_ = root_folder_path_.substr(0, common_prefix_len); | 724 root_folder_path_ = root_folder_path_.substr(0, common_prefix_len); |
| 704 } | 725 } |
| 705 } | 726 } |
| 706 } | 727 } |
| 707 } | 728 } |
| 708 | 729 |
| 709 // Create also all parent folders up to |root_folder_path_|. | 730 // Create also all parent folders up to |root_folder_path_|. |
| 710 SolutionEntries additional_folders; | 731 SolutionFolders additional_folders; |
| 711 for (SolutionEntry* folder : folders_) { | 732 for (SolutionEntry* folder : folders_) { |
| 712 if (folder->path == root_folder_path_) | 733 if (folder->path == root_folder_path_) |
| 713 continue; | 734 continue; |
| 714 | 735 |
| 715 base::StringPiece parent_path; | 736 base::StringPiece parent_path; |
| 716 while ((parent_path = FindParentDir(&folder->path)) != root_folder_path_) { | 737 while ((parent_path = FindParentDir(&folder->path)) != root_folder_path_) { |
| 717 auto it = processed_paths.find(parent_path); | 738 auto it = processed_paths.find(parent_path); |
| 718 if (it != processed_paths.end()) { | 739 if (it != processed_paths.end()) { |
| 719 folder = it->second; | 740 folder = it->second; |
| 720 } else { | 741 } else { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 731 additional_folders.end()); | 752 additional_folders.end()); |
| 732 | 753 |
| 733 // Sort folders by path. | 754 // Sort folders by path. |
| 734 std::sort(folders_.begin(), folders_.end(), | 755 std::sort(folders_.begin(), folders_.end(), |
| 735 [](const SolutionEntry* a, const SolutionEntry* b) { | 756 [](const SolutionEntry* a, const SolutionEntry* b) { |
| 736 return a->path < b->path; | 757 return a->path < b->path; |
| 737 }); | 758 }); |
| 738 | 759 |
| 739 // Match subfolders with their parents. Since |folders_| are sorted by path we | 760 // Match subfolders with their parents. Since |folders_| are sorted by path we |
| 740 // know that parent folder always precedes its children in vector. | 761 // know that parent folder always precedes its children in vector. |
| 741 SolutionEntries parents; | 762 SolutionFolders parents; |
| 742 for (SolutionEntry* folder : folders_) { | 763 for (SolutionEntry* folder : folders_) { |
| 743 while (!parents.empty()) { | 764 while (!parents.empty()) { |
| 744 if (base::StartsWith(folder->path, parents.back()->path, | 765 if (base::StartsWith(folder->path, parents.back()->path, |
| 745 base::CompareCase::SENSITIVE)) { | 766 base::CompareCase::SENSITIVE)) { |
| 746 folder->parent_folder = parents.back(); | 767 folder->parent_folder = parents.back(); |
| 747 break; | 768 break; |
| 748 } else { | 769 } else { |
| 749 parents.pop_back(); | 770 parents.pop_back(); |
| 750 } | 771 } |
| 751 } | 772 } |
| 752 parents.push_back(folder); | 773 parents.push_back(folder); |
| 753 } | 774 } |
| 754 } | 775 } |
| OLD | NEW |