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