Home | History | Annotate | Download | only in gn
      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