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