| # Copyright 2016 The Chromium Authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 |  | 
 | import os | 
 | import glob | 
 | import re | 
 | import sys | 
 | from shutil import copyfile | 
 |  | 
 | # Helpers | 
 | def ensureExists(path): | 
 |     try: | 
 |         os.makedirs(path) | 
 |     except OSError: | 
 |         pass | 
 |  | 
 | def writeLinesToFile(lines, fileName): | 
 |     ensureExists(os.path.dirname(fileName)) | 
 |     with open(fileName, "w") as f: | 
 |         f.writelines(lines) | 
 |  | 
 | def extractIdg(projFileName): | 
 |     result = [] | 
 |     with open(projFileName) as projFile: | 
 |         lines = iter(projFile) | 
 |         for pLine in lines: | 
 |             if "<ItemDefinitionGroup" in pLine: | 
 |                 while not "</ItemDefinitionGroup" in pLine: | 
 |                     result.append(pLine) | 
 |                     pLine = lines.next() | 
 |                 result.append(pLine) | 
 |                 return result | 
 |  | 
 | # [ (name, hasSln), ... ] | 
 | configs = [] | 
 |  | 
 | # Find all directories that can be used as configs (and record if they have VS | 
 | # files present) | 
 | for root, dirs, files in os.walk("out"): | 
 |     for outDir in dirs: | 
 |         gnFile = os.path.join("out", outDir, "build.ninja.d") | 
 |         if os.path.exists(gnFile): | 
 |             slnFile = os.path.join("out", outDir, "all.sln") | 
 |             configs.append((outDir, os.path.exists(slnFile))) | 
 |     break | 
 |  | 
 | # Every project has a GUID that encodes the type. We only care about C++. | 
 | cppTypeGuid = "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942" | 
 |  | 
 | # name -> [ (config, pathToProject, GUID), ... ] | 
 | allProjects = {} | 
 | projectPattern = (r'Project\("\{' + cppTypeGuid + | 
 |                   r'\}"\) = "([^"]*)", "([^"]*)", "\{([^\}]*)\}"') | 
 |  | 
 | for config in configs: | 
 |     if config[1]: | 
 |         slnLines = iter(open("out/" + config[0] + "/all.sln")) | 
 |         for slnLine in slnLines: | 
 |             matchObj = re.match(projectPattern, slnLine) | 
 |             if matchObj: | 
 |                 projName = matchObj.group(1) | 
 |                 if not allProjects.has_key(projName): | 
 |                     allProjects[projName] = [] | 
 |                 allProjects[projName].append((config[0], matchObj.group(2), | 
 |                                               matchObj.group(3))) | 
 |  | 
 | # We need something to work with. Typically, this will fail if no GN folders | 
 | # have IDE files | 
 | if len(allProjects) == 0: | 
 |     print "ERROR: At least one GN directory must have been built with --ide=vs" | 
 |     sys.exit() | 
 |  | 
 | # Create a new solution. We arbitrarily use the first config as the GUID source | 
 | # (but we need to match that behavior later, when we copy/generate the project | 
 | # files). | 
 | newSlnLines = [] | 
 | newSlnLines.append( | 
 |     'Microsoft Visual Studio Solution File, Format Version 12.00\n') | 
 | newSlnLines.append('# Visual Studio 2015\n') | 
 | for projName, projConfigs in allProjects.items(): | 
 |     newSlnLines.append('Project("{' + cppTypeGuid + '}") = "' + projName + | 
 |                        '", "' + projConfigs[0][1] + '", "{' + projConfigs[0][2] | 
 |                        + '}"\n') | 
 |     newSlnLines.append('EndProject\n') | 
 |  | 
 | newSlnLines.append('Global\n') | 
 | newSlnLines.append( | 
 |     '\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') | 
 | for config in configs: | 
 |     newSlnLines.append('\t\t' + config[0] + '|x64 = ' + config[0] + '|x64\n') | 
 | newSlnLines.append('\tEndGlobalSection\n') | 
 | newSlnLines.append( | 
 |     '\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') | 
 | for projName, projConfigs in allProjects.items(): | 
 |     projGuid = projConfigs[0][2] | 
 |     for config in configs: | 
 |         newSlnLines.append('\t\t{' + projGuid + '}.' + config[0] + | 
 |                            '|x64.ActiveCfg = ' + config[0] + '|x64\n') | 
 |         newSlnLines.append('\t\t{' + projGuid + '}.' + config[0] + | 
 |                            '|x64.Build.0 = ' + config[0] + '|x64\n') | 
 | newSlnLines.append('\tEndGlobalSection\n') | 
 | newSlnLines.append('\tGlobalSection(SolutionProperties) = preSolution\n') | 
 | newSlnLines.append('\t\tHideSolutionNode = FALSE\n') | 
 | newSlnLines.append('\tEndGlobalSection\n') | 
 | newSlnLines.append('\tGlobalSection(NestedProjects) = preSolution\n') | 
 | newSlnLines.append('\tEndGlobalSection\n') | 
 | newSlnLines.append('EndGlobal\n') | 
 |  | 
 | # Write solution file | 
 | writeLinesToFile(newSlnLines, "out/sln/skia.sln") | 
 |  | 
 | idgHdr = "<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='" | 
 |  | 
 | # Now, bring over the project files | 
 | for projName, projConfigs in allProjects.items(): | 
 |     # Paths to project and filter file in src and dst locations | 
 |     srcProjPath = os.path.join("out", projConfigs[0][0], projConfigs[0][1]) | 
 |     dstProjPath = os.path.join("out", "sln", projConfigs[0][1]) | 
 |     srcFilterPath = srcProjPath + ".filters" | 
 |     dstFilterPath = dstProjPath + ".filters" | 
 |  | 
 |     # Copy the filter file unmodified | 
 |     ensureExists(os.path.dirname(dstProjPath)) | 
 |     copyfile(srcFilterPath, dstFilterPath) | 
 |  | 
 |     # Bring over the project file, modified with extra configs | 
 |     with open(srcProjPath) as srcProjFile: | 
 |         projLines = iter(srcProjFile) | 
 |         newProjLines = [] | 
 |         for line in projLines: | 
 |             if "<ItemDefinitionGroup" in line: | 
 |                 # This is a large group that contains many settings. We need to | 
 |                 # replicate it, with conditions so it varies per configuration. | 
 |                 idgLines = [] | 
 |                 while not "</ItemDefinitionGroup" in line: | 
 |                     idgLines.append(line) | 
 |                     line = projLines.next() | 
 |                 idgLines.append(line) | 
 |                 for projConfig in projConfigs: | 
 |                     configIdgLines = extractIdg(os.path.join("out", | 
 |                                                              projConfig[0], | 
 |                                                              projConfig[1])) | 
 |                     newProjLines.append(idgHdr + projConfig[0] + "|x64'\">\n") | 
 |                     for idgLine in configIdgLines[1:]: | 
 |                         newProjLines.append(idgLine) | 
 |             elif "ProjectConfigurations" in line: | 
 |                 newProjLines.append(line) | 
 |                 projConfigLines = [ | 
 |                     projLines.next(), | 
 |                     projLines.next(), | 
 |                     projLines.next(), | 
 |                     projLines.next() ] | 
 |                 for config in configs: | 
 |                     for projConfigLine in projConfigLines: | 
 |                         newProjLines.append(projConfigLine.replace("GN", | 
 |                                                                    config[0])) | 
 |             elif "<OutDir" in line: | 
 |                 newProjLines.append(line.replace(projConfigs[0][0], | 
 |                                                  "$(Configuration)")) | 
 |             else: | 
 |                 newProjLines.append(line) | 
 |         with open(dstProjPath, "w") as newProj: | 
 |             newProj.writelines(newProjLines) |