1*6777b538SAndroid Build Coastguard Worker# Copyright 2017 The Chromium Authors 2*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 3*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 4*6777b538SAndroid Build Coastguard Worker# 5*6777b538SAndroid Build Coastguard Worker# gn_meta_sln.py 6*6777b538SAndroid Build Coastguard Worker# Helper utility to combine GN-generated Visual Studio projects into 7*6777b538SAndroid Build Coastguard Worker# a single meta-solution. 8*6777b538SAndroid Build Coastguard Worker 9*6777b538SAndroid Build Coastguard Worker 10*6777b538SAndroid Build Coastguard Workerimport os 11*6777b538SAndroid Build Coastguard Workerimport glob 12*6777b538SAndroid Build Coastguard Workerimport re 13*6777b538SAndroid Build Coastguard Workerimport sys 14*6777b538SAndroid Build Coastguard Workerfrom shutil import copyfile 15*6777b538SAndroid Build Coastguard Worker 16*6777b538SAndroid Build Coastguard Worker# Helpers 17*6777b538SAndroid Build Coastguard Workerdef EnsureExists(path): 18*6777b538SAndroid Build Coastguard Worker try: 19*6777b538SAndroid Build Coastguard Worker os.makedirs(path) 20*6777b538SAndroid Build Coastguard Worker except OSError: 21*6777b538SAndroid Build Coastguard Worker pass 22*6777b538SAndroid Build Coastguard Worker 23*6777b538SAndroid Build Coastguard Workerdef WriteLinesToFile(lines, file_name): 24*6777b538SAndroid Build Coastguard Worker EnsureExists(os.path.dirname(file_name)) 25*6777b538SAndroid Build Coastguard Worker with open(file_name, "w") as f: 26*6777b538SAndroid Build Coastguard Worker f.writelines(lines) 27*6777b538SAndroid Build Coastguard Worker 28*6777b538SAndroid Build Coastguard Workerdef ExtractIdg(proj_file_name): 29*6777b538SAndroid Build Coastguard Worker result = [] 30*6777b538SAndroid Build Coastguard Worker with open(proj_file_name) as proj_file: 31*6777b538SAndroid Build Coastguard Worker lines = iter(proj_file) 32*6777b538SAndroid Build Coastguard Worker for p_line in lines: 33*6777b538SAndroid Build Coastguard Worker if "<ItemDefinitionGroup" in p_line: 34*6777b538SAndroid Build Coastguard Worker while not "</ItemDefinitionGroup" in p_line: 35*6777b538SAndroid Build Coastguard Worker result.append(p_line) 36*6777b538SAndroid Build Coastguard Worker p_line = lines.next() 37*6777b538SAndroid Build Coastguard Worker result.append(p_line) 38*6777b538SAndroid Build Coastguard Worker return result 39*6777b538SAndroid Build Coastguard Worker 40*6777b538SAndroid Build Coastguard Worker# [ (name, solution_name, vs_version), ... ] 41*6777b538SAndroid Build Coastguard Workerconfigs = [] 42*6777b538SAndroid Build Coastguard Worker 43*6777b538SAndroid Build Coastguard Workerdef GetVSVersion(solution_file): 44*6777b538SAndroid Build Coastguard Worker with open(solution_file) as f: 45*6777b538SAndroid Build Coastguard Worker f.readline() 46*6777b538SAndroid Build Coastguard Worker comment = f.readline().strip() 47*6777b538SAndroid Build Coastguard Worker return comment[-4:] 48*6777b538SAndroid Build Coastguard Worker 49*6777b538SAndroid Build Coastguard Worker# Find all directories that can be used as configs (and record if they have VS 50*6777b538SAndroid Build Coastguard Worker# files present) 51*6777b538SAndroid Build Coastguard Workerfor root, dirs, files in os.walk("out"): 52*6777b538SAndroid Build Coastguard Worker for out_dir in dirs: 53*6777b538SAndroid Build Coastguard Worker gn_file = os.path.join("out", out_dir, "build.ninja.d") 54*6777b538SAndroid Build Coastguard Worker if os.path.exists(gn_file): 55*6777b538SAndroid Build Coastguard Worker solutions = glob.glob(os.path.join("out", out_dir, "*.sln")) 56*6777b538SAndroid Build Coastguard Worker for solution in solutions: 57*6777b538SAndroid Build Coastguard Worker vs_version = GetVSVersion(solution) 58*6777b538SAndroid Build Coastguard Worker configs.append((out_dir, os.path.basename(solution), 59*6777b538SAndroid Build Coastguard Worker vs_version)) 60*6777b538SAndroid Build Coastguard Worker break 61*6777b538SAndroid Build Coastguard Worker 62*6777b538SAndroid Build Coastguard Worker# Every project has a GUID that encodes the type. We only care about C++. 63*6777b538SAndroid Build Coastguard Workercpp_type_guid = "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942" 64*6777b538SAndroid Build Coastguard Worker 65*6777b538SAndroid Build Coastguard Worker# Work around MSBuild limitations by always using a fixed arch. 66*6777b538SAndroid Build Coastguard Workerhard_coded_arch = "x64" 67*6777b538SAndroid Build Coastguard Worker 68*6777b538SAndroid Build Coastguard Worker# name -> [ (config, pathToProject, GUID, arch), ... ] 69*6777b538SAndroid Build Coastguard Workerall_projects = {} 70*6777b538SAndroid Build Coastguard Workerproject_pattern = (r'Project\("\{' + cpp_type_guid + 71*6777b538SAndroid Build Coastguard Worker r'\}"\) = "([^"]*)", "([^"]*)", "\{([^\}]*)\}"') 72*6777b538SAndroid Build Coastguard Worker 73*6777b538SAndroid Build Coastguard Worker# We need something to work with. Typically, this will fail if no GN folders 74*6777b538SAndroid Build Coastguard Worker# have IDE files 75*6777b538SAndroid Build Coastguard Workerif len(configs) == 0: 76*6777b538SAndroid Build Coastguard Worker print("ERROR: At least one GN directory must have been built with --ide=vs") 77*6777b538SAndroid Build Coastguard Worker sys.exit() 78*6777b538SAndroid Build Coastguard Worker 79*6777b538SAndroid Build Coastguard Worker# Filter out configs which don't match the name and vs version of the first. 80*6777b538SAndroid Build Coastguard Workername = configs[0][1] 81*6777b538SAndroid Build Coastguard Workervs_version = configs[0][2] 82*6777b538SAndroid Build Coastguard Worker 83*6777b538SAndroid Build Coastguard Workerfor config in configs: 84*6777b538SAndroid Build Coastguard Worker if config[1] != name or config[2] != vs_version: 85*6777b538SAndroid Build Coastguard Worker continue 86*6777b538SAndroid Build Coastguard Worker 87*6777b538SAndroid Build Coastguard Worker sln_lines = iter(open(os.path.join("out", config[0], config[1]))) 88*6777b538SAndroid Build Coastguard Worker for sln_line in sln_lines: 89*6777b538SAndroid Build Coastguard Worker match_obj = re.match(project_pattern, sln_line) 90*6777b538SAndroid Build Coastguard Worker if match_obj: 91*6777b538SAndroid Build Coastguard Worker proj_name = match_obj.group(1) 92*6777b538SAndroid Build Coastguard Worker if proj_name not in all_projects: 93*6777b538SAndroid Build Coastguard Worker all_projects[proj_name] = [] 94*6777b538SAndroid Build Coastguard Worker all_projects[proj_name].append((config[0], match_obj.group(2), 95*6777b538SAndroid Build Coastguard Worker match_obj.group(3))) 96*6777b538SAndroid Build Coastguard Worker 97*6777b538SAndroid Build Coastguard Worker# We need something to work with. Typically, this will fail if no GN folders 98*6777b538SAndroid Build Coastguard Worker# have IDE files 99*6777b538SAndroid Build Coastguard Workerif len(all_projects) == 0: 100*6777b538SAndroid Build Coastguard Worker print("ERROR: At least one GN directory must have been built with --ide=vs") 101*6777b538SAndroid Build Coastguard Worker sys.exit() 102*6777b538SAndroid Build Coastguard Worker 103*6777b538SAndroid Build Coastguard Worker# Create a new solution. We arbitrarily use the first config as the GUID source 104*6777b538SAndroid Build Coastguard Worker# (but we need to match that behavior later, when we copy/generate the project 105*6777b538SAndroid Build Coastguard Worker# files). 106*6777b538SAndroid Build Coastguard Workernew_sln_lines = [] 107*6777b538SAndroid Build Coastguard Workernew_sln_lines.append( 108*6777b538SAndroid Build Coastguard Worker 'Microsoft Visual Studio Solution File, Format Version 12.00\n') 109*6777b538SAndroid Build Coastguard Workernew_sln_lines.append('# Visual Studio ' + vs_version + '\n') 110*6777b538SAndroid Build Coastguard Workerfor proj_name, proj_configs in all_projects.items(): 111*6777b538SAndroid Build Coastguard Worker new_sln_lines.append('Project("{' + cpp_type_guid + '}") = "' + proj_name + 112*6777b538SAndroid Build Coastguard Worker '", "' + proj_configs[0][1] + '", "{' + 113*6777b538SAndroid Build Coastguard Worker proj_configs[0][2] + '}"\n') 114*6777b538SAndroid Build Coastguard Worker new_sln_lines.append('EndProject\n') 115*6777b538SAndroid Build Coastguard Worker 116*6777b538SAndroid Build Coastguard Workernew_sln_lines.append('Global\n') 117*6777b538SAndroid Build Coastguard Workernew_sln_lines.append( 118*6777b538SAndroid Build Coastguard Worker '\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') 119*6777b538SAndroid Build Coastguard Workerfor config in configs: 120*6777b538SAndroid Build Coastguard Worker match = config[0] + '|' + hard_coded_arch 121*6777b538SAndroid Build Coastguard Worker new_sln_lines.append('\t\t' + match + ' = ' + match + '\n') 122*6777b538SAndroid Build Coastguard Workernew_sln_lines.append('\tEndGlobalSection\n') 123*6777b538SAndroid Build Coastguard Workernew_sln_lines.append( 124*6777b538SAndroid Build Coastguard Worker '\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') 125*6777b538SAndroid Build Coastguard Workerfor proj_name, proj_configs in all_projects.items(): 126*6777b538SAndroid Build Coastguard Worker proj_guid = proj_configs[0][2] 127*6777b538SAndroid Build Coastguard Worker for config in configs: 128*6777b538SAndroid Build Coastguard Worker match = config[0] + '|' + hard_coded_arch 129*6777b538SAndroid Build Coastguard Worker new_sln_lines.append('\t\t{' + proj_guid + '}.' + match + 130*6777b538SAndroid Build Coastguard Worker '.ActiveCfg = ' + match + '\n') 131*6777b538SAndroid Build Coastguard Worker new_sln_lines.append('\t\t{' + proj_guid + '}.' + match + 132*6777b538SAndroid Build Coastguard Worker '.Build.0 = ' + match + '\n') 133*6777b538SAndroid Build Coastguard Workernew_sln_lines.append('\tEndGlobalSection\n') 134*6777b538SAndroid Build Coastguard Workernew_sln_lines.append('\tGlobalSection(SolutionProperties) = preSolution\n') 135*6777b538SAndroid Build Coastguard Workernew_sln_lines.append('\t\tHideSolutionNode = FALSE\n') 136*6777b538SAndroid Build Coastguard Workernew_sln_lines.append('\tEndGlobalSection\n') 137*6777b538SAndroid Build Coastguard Workernew_sln_lines.append('\tGlobalSection(NestedProjects) = preSolution\n') 138*6777b538SAndroid Build Coastguard Workernew_sln_lines.append('\tEndGlobalSection\n') 139*6777b538SAndroid Build Coastguard Workernew_sln_lines.append('EndGlobal\n') 140*6777b538SAndroid Build Coastguard Worker 141*6777b538SAndroid Build Coastguard Worker# Write solution file 142*6777b538SAndroid Build Coastguard WorkerWriteLinesToFile(new_sln_lines, 'out/sln/' + name) 143*6777b538SAndroid Build Coastguard Worker 144*6777b538SAndroid Build Coastguard Workeridg_hdr = "<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='" 145*6777b538SAndroid Build Coastguard Worker 146*6777b538SAndroid Build Coastguard Workerconfiguration_template = """ <ProjectConfiguration Include="{config}|{arch}"> 147*6777b538SAndroid Build Coastguard Worker <Configuration>{config}</Configuration> 148*6777b538SAndroid Build Coastguard Worker <Platform>{arch}</Platform> 149*6777b538SAndroid Build Coastguard Worker </ProjectConfiguration> 150*6777b538SAndroid Build Coastguard Worker""" 151*6777b538SAndroid Build Coastguard Worker 152*6777b538SAndroid Build Coastguard Workerdef FormatProjectConfig(config): 153*6777b538SAndroid Build Coastguard Worker return configuration_template.format( 154*6777b538SAndroid Build Coastguard Worker config = config[0], arch = hard_coded_arch) 155*6777b538SAndroid Build Coastguard Worker 156*6777b538SAndroid Build Coastguard Worker# Now, bring over the project files 157*6777b538SAndroid Build Coastguard Workerfor proj_name, proj_configs in all_projects.items(): 158*6777b538SAndroid Build Coastguard Worker # Paths to project and filter file in src and dst locations 159*6777b538SAndroid Build Coastguard Worker src_proj_path = os.path.join("out", proj_configs[0][0], proj_configs[0][1]) 160*6777b538SAndroid Build Coastguard Worker dst_proj_path = os.path.join("out", "sln", proj_configs[0][1]) 161*6777b538SAndroid Build Coastguard Worker src_filter_path = src_proj_path + ".filters" 162*6777b538SAndroid Build Coastguard Worker dst_filter_path = dst_proj_path + ".filters" 163*6777b538SAndroid Build Coastguard Worker 164*6777b538SAndroid Build Coastguard Worker # Copy the filter file unmodified 165*6777b538SAndroid Build Coastguard Worker EnsureExists(os.path.dirname(dst_proj_path)) 166*6777b538SAndroid Build Coastguard Worker copyfile(src_filter_path, dst_filter_path) 167*6777b538SAndroid Build Coastguard Worker 168*6777b538SAndroid Build Coastguard Worker preferred_tool_arch = None 169*6777b538SAndroid Build Coastguard Worker config_arch = {} 170*6777b538SAndroid Build Coastguard Worker 171*6777b538SAndroid Build Coastguard Worker # Bring over the project file, modified with extra configs 172*6777b538SAndroid Build Coastguard Worker with open(src_proj_path) as src_proj_file: 173*6777b538SAndroid Build Coastguard Worker proj_lines = iter(src_proj_file) 174*6777b538SAndroid Build Coastguard Worker new_proj_lines = [] 175*6777b538SAndroid Build Coastguard Worker for line in proj_lines: 176*6777b538SAndroid Build Coastguard Worker if "<ItemDefinitionGroup" in line: 177*6777b538SAndroid Build Coastguard Worker # This is a large group that contains many settings. We need to 178*6777b538SAndroid Build Coastguard Worker # replicate it, with conditions so it varies per configuration. 179*6777b538SAndroid Build Coastguard Worker idg_lines = [] 180*6777b538SAndroid Build Coastguard Worker while not "</ItemDefinitionGroup" in line: 181*6777b538SAndroid Build Coastguard Worker idg_lines.append(line) 182*6777b538SAndroid Build Coastguard Worker line = proj_lines.next() 183*6777b538SAndroid Build Coastguard Worker idg_lines.append(line) 184*6777b538SAndroid Build Coastguard Worker for proj_config in proj_configs: 185*6777b538SAndroid Build Coastguard Worker config_idg_lines = ExtractIdg(os.path.join("out", 186*6777b538SAndroid Build Coastguard Worker proj_config[0], 187*6777b538SAndroid Build Coastguard Worker proj_config[1])) 188*6777b538SAndroid Build Coastguard Worker match = proj_config[0] + '|' + hard_coded_arch 189*6777b538SAndroid Build Coastguard Worker new_proj_lines.append(idg_hdr + match + "'\">\n") 190*6777b538SAndroid Build Coastguard Worker for idg_line in config_idg_lines[1:]: 191*6777b538SAndroid Build Coastguard Worker new_proj_lines.append(idg_line) 192*6777b538SAndroid Build Coastguard Worker elif "ProjectConfigurations" in line: 193*6777b538SAndroid Build Coastguard Worker new_proj_lines.append(line) 194*6777b538SAndroid Build Coastguard Worker proj_lines.next() 195*6777b538SAndroid Build Coastguard Worker proj_lines.next() 196*6777b538SAndroid Build Coastguard Worker proj_lines.next() 197*6777b538SAndroid Build Coastguard Worker proj_lines.next() 198*6777b538SAndroid Build Coastguard Worker for config in configs: 199*6777b538SAndroid Build Coastguard Worker new_proj_lines.append(FormatProjectConfig(config)) 200*6777b538SAndroid Build Coastguard Worker 201*6777b538SAndroid Build Coastguard Worker elif "<OutDir" in line: 202*6777b538SAndroid Build Coastguard Worker new_proj_lines.append(line.replace(proj_configs[0][0], 203*6777b538SAndroid Build Coastguard Worker "$(Configuration)")) 204*6777b538SAndroid Build Coastguard Worker elif "<PreferredToolArchitecture" in line: 205*6777b538SAndroid Build Coastguard Worker new_proj_lines.append(" <PreferredToolArchitecture>" + 206*6777b538SAndroid Build Coastguard Worker hard_coded_arch + 207*6777b538SAndroid Build Coastguard Worker "</PreferredToolArchitecture>\n") 208*6777b538SAndroid Build Coastguard Worker else: 209*6777b538SAndroid Build Coastguard Worker new_proj_lines.append(line) 210*6777b538SAndroid Build Coastguard Worker with open(dst_proj_path, "w") as new_proj: 211*6777b538SAndroid Build Coastguard Worker new_proj.writelines(new_proj_lines) 212*6777b538SAndroid Build Coastguard Worker 213*6777b538SAndroid Build Coastguard Workerprint('Wrote meta solution to out/sln/' + name) 214