• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import os
6 import glob
7 import re
8 import sys
9 from shutil import copyfile
10 
11 # Helpers
12 def ensureExists(path):
13     try:
14         os.makedirs(path)
15     except OSError:
16         pass
17 
18 def writeLinesToFile(lines, fileName):
19     ensureExists(os.path.dirname(fileName))
20     with open(fileName, "w") as f:
21         f.writelines(lines)
22 
23 def 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), ... ]
36 configs = []
37 
38 # Find all directories that can be used as configs (and record if they have VS
39 # files present)
40 for root, dirs, files in os.walk("out"):
41     for outDir in dirs:
42         gnFile = os.path.join("out", outDir, "build.ninja.d")
43         if os.path.exists(gnFile):
44             slnFile = os.path.join("out", outDir, "all.sln")
45             configs.append((outDir, os.path.exists(slnFile)))
46     break
47 
48 # Every project has a GUID that encodes the type. We only care about C++.
49 cppTypeGuid = "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942"
50 
51 # name -> [ (config, pathToProject, GUID), ... ]
52 allProjects = {}
53 projectPattern = (r'Project\("\{' + cppTypeGuid +
54                   r'\}"\) = "([^"]*)", "([^"]*)", "\{([^\}]*)\}"')
55 projectNamePattern = (r'obj/(.*)\.vcxproj')
56 
57 for 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:
63                 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)))
71 
72 # We need something to work with. Typically, this will fail if no GN folders
73 # have IDE files
74 if len(allProjects) == 0:
75     print "ERROR: At least one GN directory must have been built with --ide=vs"
76     sys.exit()
77 
78 # 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).
81 newSlnLines = []
82 newSlnLines.append(
83     'Microsoft Visual Studio Solution File, Format Version 12.00\n')
84 newSlnLines.append('# Visual Studio 2015\n')
85 for 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')
90 
91 newSlnLines.append('Global\n')
92 newSlnLines.append(
93     '\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
94 for config in configs:
95     newSlnLines.append('\t\t' + config[0] + '|x64 = ' + config[0] + '|x64\n')
96 newSlnLines.append('\tEndGlobalSection\n')
97 newSlnLines.append(
98     '\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
99 for 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')
106 newSlnLines.append('\tEndGlobalSection\n')
107 newSlnLines.append('\tGlobalSection(SolutionProperties) = preSolution\n')
108 newSlnLines.append('\t\tHideSolutionNode = FALSE\n')
109 newSlnLines.append('\tEndGlobalSection\n')
110 newSlnLines.append('\tGlobalSection(NestedProjects) = preSolution\n')
111 newSlnLines.append('\tEndGlobalSection\n')
112 newSlnLines.append('EndGlobal\n')
113 
114 # Write solution file
115 writeLinesToFile(newSlnLines, "out/sln/skia.sln")
116 
117 idgHdr = "<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='"
118 
119 # Now, bring over the project files
120 for 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:
133         projLines = iter(srcProjFile)
134         newProjLines = []
135         for line in projLines:
136             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:
152                 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:
160                         newProjLines.append(projConfigLine.replace("GN",
161                                                                    config[0]))
162             elif "<OutDir" in line:
163                 newProjLines.append(line.replace(projConfigs[0][0],
164                                                  "$(Configuration)"))
165             else:
166                 newProjLines.append(line)
167         with open(dstProjPath, "w") as newProj:
168             newProj.writelines(newProjLines)
169