PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/build/win/gn_meta_sln.py

https://github.com/chromium/chromium
Python | 214 lines | 170 code | 21 blank | 23 comment | 32 complexity | 3b65ed8ee305e8eaa9c4e81a23dfe2ef MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause
  1. # Copyright 2017 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. # gn_meta_sln.py
  6. # Helper utility to combine GN-generated Visual Studio projects into
  7. # a single meta-solution.
  8. from __future__ import print_function
  9. import os
  10. import glob
  11. import re
  12. import sys
  13. from shutil import copyfile
  14. # Helpers
  15. def EnsureExists(path):
  16. try:
  17. os.makedirs(path)
  18. except OSError:
  19. pass
  20. def WriteLinesToFile(lines, file_name):
  21. EnsureExists(os.path.dirname(file_name))
  22. with open(file_name, "w") as f:
  23. f.writelines(lines)
  24. def ExtractIdg(proj_file_name):
  25. result = []
  26. with open(proj_file_name) as proj_file:
  27. lines = iter(proj_file)
  28. for p_line in lines:
  29. if "<ItemDefinitionGroup" in p_line:
  30. while not "</ItemDefinitionGroup" in p_line:
  31. result.append(p_line)
  32. p_line = lines.next()
  33. result.append(p_line)
  34. return result
  35. # [ (name, solution_name, vs_version), ... ]
  36. configs = []
  37. def GetVSVersion(solution_file):
  38. with open(solution_file) as f:
  39. f.readline()
  40. comment = f.readline().strip()
  41. return comment[-4:]
  42. # Find all directories that can be used as configs (and record if they have VS
  43. # files present)
  44. for root, dirs, files in os.walk("out"):
  45. for out_dir in dirs:
  46. gn_file = os.path.join("out", out_dir, "build.ninja.d")
  47. if os.path.exists(gn_file):
  48. solutions = glob.glob(os.path.join("out", out_dir, "*.sln"))
  49. for solution in solutions:
  50. vs_version = GetVSVersion(solution)
  51. configs.append((out_dir, os.path.basename(solution),
  52. vs_version))
  53. break
  54. # Every project has a GUID that encodes the type. We only care about C++.
  55. cpp_type_guid = "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942"
  56. # Work around MSBuild limitations by always using a fixed arch.
  57. hard_coded_arch = "x64"
  58. # name -> [ (config, pathToProject, GUID, arch), ... ]
  59. all_projects = {}
  60. project_pattern = (r'Project\("\{' + cpp_type_guid +
  61. r'\}"\) = "([^"]*)", "([^"]*)", "\{([^\}]*)\}"')
  62. # We need something to work with. Typically, this will fail if no GN folders
  63. # have IDE files
  64. if len(configs) == 0:
  65. print("ERROR: At least one GN directory must have been built with --ide=vs")
  66. sys.exit()
  67. # Filter out configs which don't match the name and vs version of the first.
  68. name = configs[0][1]
  69. vs_version = configs[0][2]
  70. for config in configs:
  71. if config[1] != name or config[2] != vs_version:
  72. continue
  73. sln_lines = iter(open(os.path.join("out", config[0], config[1])))
  74. for sln_line in sln_lines:
  75. match_obj = re.match(project_pattern, sln_line)
  76. if match_obj:
  77. proj_name = match_obj.group(1)
  78. if proj_name not in all_projects:
  79. all_projects[proj_name] = []
  80. all_projects[proj_name].append((config[0], match_obj.group(2),
  81. match_obj.group(3)))
  82. # We need something to work with. Typically, this will fail if no GN folders
  83. # have IDE files
  84. if len(all_projects) == 0:
  85. print("ERROR: At least one GN directory must have been built with --ide=vs")
  86. sys.exit()
  87. # Create a new solution. We arbitrarily use the first config as the GUID source
  88. # (but we need to match that behavior later, when we copy/generate the project
  89. # files).
  90. new_sln_lines = []
  91. new_sln_lines.append(
  92. 'Microsoft Visual Studio Solution File, Format Version 12.00\n')
  93. new_sln_lines.append('# Visual Studio ' + vs_version + '\n')
  94. for proj_name, proj_configs in all_projects.items():
  95. new_sln_lines.append('Project("{' + cpp_type_guid + '}") = "' + proj_name +
  96. '", "' + proj_configs[0][1] + '", "{' +
  97. proj_configs[0][2] + '}"\n')
  98. new_sln_lines.append('EndProject\n')
  99. new_sln_lines.append('Global\n')
  100. new_sln_lines.append(
  101. '\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
  102. for config in configs:
  103. match = config[0] + '|' + hard_coded_arch
  104. new_sln_lines.append('\t\t' + match + ' = ' + match + '\n')
  105. new_sln_lines.append('\tEndGlobalSection\n')
  106. new_sln_lines.append(
  107. '\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
  108. for proj_name, proj_configs in all_projects.items():
  109. proj_guid = proj_configs[0][2]
  110. for config in configs:
  111. match = config[0] + '|' + hard_coded_arch
  112. new_sln_lines.append('\t\t{' + proj_guid + '}.' + match +
  113. '.ActiveCfg = ' + match + '\n')
  114. new_sln_lines.append('\t\t{' + proj_guid + '}.' + match +
  115. '.Build.0 = ' + match + '\n')
  116. new_sln_lines.append('\tEndGlobalSection\n')
  117. new_sln_lines.append('\tGlobalSection(SolutionProperties) = preSolution\n')
  118. new_sln_lines.append('\t\tHideSolutionNode = FALSE\n')
  119. new_sln_lines.append('\tEndGlobalSection\n')
  120. new_sln_lines.append('\tGlobalSection(NestedProjects) = preSolution\n')
  121. new_sln_lines.append('\tEndGlobalSection\n')
  122. new_sln_lines.append('EndGlobal\n')
  123. # Write solution file
  124. WriteLinesToFile(new_sln_lines, 'out/sln/' + name)
  125. idg_hdr = "<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='"
  126. configuration_template = """ <ProjectConfiguration Include="{config}|{arch}">
  127. <Configuration>{config}</Configuration>
  128. <Platform>{arch}</Platform>
  129. </ProjectConfiguration>
  130. """
  131. def FormatProjectConfig(config):
  132. return configuration_template.format(
  133. config = config[0], arch = hard_coded_arch)
  134. # Now, bring over the project files
  135. for proj_name, proj_configs in all_projects.items():
  136. # Paths to project and filter file in src and dst locations
  137. src_proj_path = os.path.join("out", proj_configs[0][0], proj_configs[0][1])
  138. dst_proj_path = os.path.join("out", "sln", proj_configs[0][1])
  139. src_filter_path = src_proj_path + ".filters"
  140. dst_filter_path = dst_proj_path + ".filters"
  141. # Copy the filter file unmodified
  142. EnsureExists(os.path.dirname(dst_proj_path))
  143. copyfile(src_filter_path, dst_filter_path)
  144. preferred_tool_arch = None
  145. config_arch = {}
  146. # Bring over the project file, modified with extra configs
  147. with open(src_proj_path) as src_proj_file:
  148. proj_lines = iter(src_proj_file)
  149. new_proj_lines = []
  150. for line in proj_lines:
  151. if "<ItemDefinitionGroup" in line:
  152. # This is a large group that contains many settings. We need to
  153. # replicate it, with conditions so it varies per configuration.
  154. idg_lines = []
  155. while not "</ItemDefinitionGroup" in line:
  156. idg_lines.append(line)
  157. line = proj_lines.next()
  158. idg_lines.append(line)
  159. for proj_config in proj_configs:
  160. config_idg_lines = ExtractIdg(os.path.join("out",
  161. proj_config[0],
  162. proj_config[1]))
  163. match = proj_config[0] + '|' + hard_coded_arch
  164. new_proj_lines.append(idg_hdr + match + "'\">\n")
  165. for idg_line in config_idg_lines[1:]:
  166. new_proj_lines.append(idg_line)
  167. elif "ProjectConfigurations" in line:
  168. new_proj_lines.append(line)
  169. proj_lines.next()
  170. proj_lines.next()
  171. proj_lines.next()
  172. proj_lines.next()
  173. for config in configs:
  174. new_proj_lines.append(FormatProjectConfig(config))
  175. elif "<OutDir" in line:
  176. new_proj_lines.append(line.replace(proj_configs[0][0],
  177. "$(Configuration)"))
  178. elif "<PreferredToolArchitecture" in line:
  179. new_proj_lines.append(" <PreferredToolArchitecture>" +
  180. hard_coded_arch +
  181. "</PreferredToolArchitecture>\n")
  182. else:
  183. new_proj_lines.append(line)
  184. with open(dst_proj_path, "w") as new_proj:
  185. new_proj.writelines(new_proj_lines)
  186. print('Wrote meta solution to out/sln/' + name)