gn_meta_sln.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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. import os
  5. import glob
  6. import re
  7. import sys
  8. from shutil import copyfile
  9. # Helpers
  10. def ensureExists(path):
  11. try:
  12. os.makedirs(path)
  13. except OSError:
  14. pass
  15. def writeLinesToFile(lines, fileName):
  16. ensureExists(os.path.dirname(fileName))
  17. with open(fileName, "w") as f:
  18. f.writelines(lines)
  19. def extractIdg(projFileName):
  20. result = []
  21. with open(projFileName) as projFile:
  22. lines = iter(projFile)
  23. for pLine in lines:
  24. if "<ItemDefinitionGroup" in pLine:
  25. while not "</ItemDefinitionGroup" in pLine:
  26. result.append(pLine)
  27. pLine = lines.next()
  28. result.append(pLine)
  29. return result
  30. # [ (name, hasSln), ... ]
  31. configs = []
  32. # Find all directories that can be used as configs (and record if they have VS
  33. # files present)
  34. for root, dirs, files in os.walk("out"):
  35. for outDir in dirs:
  36. gnFile = os.path.join("out", outDir, "build.ninja.d")
  37. if os.path.exists(gnFile):
  38. slnFile = os.path.join("out", outDir, "all.sln")
  39. configs.append((outDir, os.path.exists(slnFile)))
  40. break
  41. # Every project has a GUID that encodes the type. We only care about C++.
  42. cppTypeGuid = "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942"
  43. # name -> [ (config, pathToProject, GUID), ... ]
  44. allProjects = {}
  45. projectPattern = (r'Project\("\{' + cppTypeGuid +
  46. r'\}"\) = "([^"]*)", "([^"]*)", "\{([^\}]*)\}"')
  47. projectNamePattern = (r'obj/(.*)\.vcxproj')
  48. for config in configs:
  49. if config[1]:
  50. slnLines = iter(open("out/" + config[0] + "/all.sln"))
  51. for slnLine in slnLines:
  52. matchObj = re.match(projectPattern, slnLine)
  53. if matchObj:
  54. projPath = matchObj.group(2)
  55. nameObj = re.match(projectNamePattern, projPath)
  56. if nameObj:
  57. projName = nameObj.group(1).replace('/', '.')
  58. if not allProjects.has_key(projName):
  59. allProjects[projName] = []
  60. allProjects[projName].append((config[0], projPath,
  61. matchObj.group(3)))
  62. # We need something to work with. Typically, this will fail if no GN folders
  63. # have IDE files
  64. if len(allProjects) == 0:
  65. print "ERROR: At least one GN directory must have been built with --ide=vs"
  66. sys.exit()
  67. # Create a new solution. We arbitrarily use the first config as the GUID source
  68. # (but we need to match that behavior later, when we copy/generate the project
  69. # files).
  70. newSlnLines = []
  71. newSlnLines.append(
  72. 'Microsoft Visual Studio Solution File, Format Version 12.00\n')
  73. newSlnLines.append('# Visual Studio 2015\n')
  74. for projName, projConfigs in allProjects.items():
  75. newSlnLines.append('Project("{' + cppTypeGuid + '}") = "' + projName +
  76. '", "' + projConfigs[0][1] + '", "{' + projConfigs[0][2]
  77. + '}"\n')
  78. newSlnLines.append('EndProject\n')
  79. newSlnLines.append('Global\n')
  80. newSlnLines.append(
  81. '\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
  82. for config in configs:
  83. newSlnLines.append('\t\t' + config[0] + '|x64 = ' + config[0] + '|x64\n')
  84. newSlnLines.append('\tEndGlobalSection\n')
  85. newSlnLines.append(
  86. '\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
  87. for projName, projConfigs in allProjects.items():
  88. projGuid = projConfigs[0][2]
  89. for config in configs:
  90. newSlnLines.append('\t\t{' + projGuid + '}.' + config[0] +
  91. '|x64.ActiveCfg = ' + config[0] + '|x64\n')
  92. newSlnLines.append('\t\t{' + projGuid + '}.' + config[0] +
  93. '|x64.Build.0 = ' + config[0] + '|x64\n')
  94. newSlnLines.append('\tEndGlobalSection\n')
  95. newSlnLines.append('\tGlobalSection(SolutionProperties) = preSolution\n')
  96. newSlnLines.append('\t\tHideSolutionNode = FALSE\n')
  97. newSlnLines.append('\tEndGlobalSection\n')
  98. newSlnLines.append('\tGlobalSection(NestedProjects) = preSolution\n')
  99. newSlnLines.append('\tEndGlobalSection\n')
  100. newSlnLines.append('EndGlobal\n')
  101. # Write solution file
  102. writeLinesToFile(newSlnLines, "out/sln/skia.sln")
  103. idgHdr = "<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='"
  104. # Now, bring over the project files
  105. for projName, projConfigs in allProjects.items():
  106. # Paths to project and filter file in src and dst locations
  107. srcProjPath = os.path.join("out", projConfigs[0][0], projConfigs[0][1])
  108. dstProjPath = os.path.join("out", "sln", projConfigs[0][1])
  109. srcFilterPath = srcProjPath + ".filters"
  110. dstFilterPath = dstProjPath + ".filters"
  111. # Copy the filter file unmodified
  112. ensureExists(os.path.dirname(dstProjPath))
  113. copyfile(srcFilterPath, dstFilterPath)
  114. # Bring over the project file, modified with extra configs
  115. with open(srcProjPath) as srcProjFile:
  116. projLines = iter(srcProjFile)
  117. newProjLines = []
  118. for line in projLines:
  119. if "<ItemDefinitionGroup" in line:
  120. # This is a large group that contains many settings. We need to
  121. # replicate it, with conditions so it varies per configuration.
  122. idgLines = []
  123. while not "</ItemDefinitionGroup" in line:
  124. idgLines.append(line)
  125. line = projLines.next()
  126. idgLines.append(line)
  127. for projConfig in projConfigs:
  128. configIdgLines = extractIdg(os.path.join("out",
  129. projConfig[0],
  130. projConfig[1]))
  131. newProjLines.append(idgHdr + projConfig[0] + "|x64'\">\n")
  132. for idgLine in configIdgLines[1:]:
  133. newProjLines.append(idgLine)
  134. elif "ProjectConfigurations" in line:
  135. newProjLines.append(line)
  136. projConfigLines = [
  137. projLines.next(),
  138. projLines.next(),
  139. projLines.next(),
  140. projLines.next() ]
  141. for config in configs:
  142. for projConfigLine in projConfigLines:
  143. newProjLines.append(projConfigLine.replace("GN",
  144. config[0]))
  145. elif "<OutDir" in line:
  146. newProjLines.append(line.replace(projConfigs[0][0],
  147. "$(Configuration)"))
  148. else:
  149. newProjLines.append(line)
  150. with open(dstProjPath, "w") as newProj:
  151. newProj.writelines(newProjLines)