1 # Copyright 2014 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can b 3 # found in the LICENSE file. 4 5 """Enumerates the BoringSSL source in src/ and generates two gypi files: 6 boringssl.gypi and boringssl_tests.gypi.""" 7 8 import os 9 import subprocess 10 import sys 11 12 13 # OS_ARCH_COMBOS maps from OS and platform to the OpenSSL assembly "style" for 14 # that platform and the extension used by asm files. 15 OS_ARCH_COMBOS = [ 16 ('linux', 'arm', 'elf', [''], 'S'), 17 ('linux', 'x86', 'elf', ['-fPIC'], 'S'), 18 ('linux', 'x86_64', 'elf', [''], 'S'), 19 ('mac', 'x86', 'macosx', ['-fPIC'], 'S'), 20 ('mac', 'x86_64', 'macosx', [''], 'S'), 21 ('win', 'x86_64', 'masm', [''], 'asm'), 22 ] 23 24 # NON_PERL_FILES enumerates assembly files that are not processed by the 25 # perlasm system. 26 NON_PERL_FILES = { 27 ('linux', 'arm'): [ 28 'src/crypto/poly1305/poly1305_arm_asm.S', 29 'src/crypto/chacha/chacha_vec_arm.S', 30 ], 31 } 32 33 FILE_HEADER = """# Copyright (c) 2014 The Chromium Authors. All rights reserved. 34 # Use of this source code is governed by a BSD-style license that can be 35 # found in the LICENSE file. 36 37 # This file is created by update_gypi_and_asm.py. Do not edit manually. 38 39 """ 40 41 42 def FindCMakeFiles(directory): 43 """Returns list of all CMakeLists.txt files recursively in directory.""" 44 cmakefiles = [] 45 46 for (path, _, filenames) in os.walk(directory): 47 for filename in filenames: 48 if filename == 'CMakeLists.txt': 49 cmakefiles.append(os.path.join(path, filename)) 50 51 return cmakefiles 52 53 54 def NoTests(dent, is_dir): 55 """Filter function that can be passed to FindCFiles in order to remove test 56 sources.""" 57 if is_dir: 58 return dent != 'test' 59 return 'test.' not in dent and not dent.startswith('example_') 60 61 62 def OnlyTests(dent, is_dir): 63 """Filter function that can be passed to FindCFiles in order to remove 64 non-test sources.""" 65 if is_dir: 66 return True 67 return '_test.' in dent or dent.startswith('example_') 68 69 70 def FindCFiles(directory, filter_func): 71 """Recurses through directory and returns a list of paths to all the C source 72 files that pass filter_func.""" 73 cfiles = [] 74 75 for (path, dirnames, filenames) in os.walk(directory): 76 for filename in filenames: 77 if filename.endswith('.c') and filter_func(filename, False): 78 cfiles.append(os.path.join(path, filename)) 79 continue 80 81 for (i, dirname) in enumerate(dirnames): 82 if not filter_func(dirname, True): 83 del dirnames[i] 84 85 return cfiles 86 87 88 def ExtractPerlAsmFromCMakeFile(cmakefile): 89 """Parses the contents of the CMakeLists.txt file passed as an argument and 90 returns a list of all the perlasm() directives found in the file.""" 91 perlasms = [] 92 with open(cmakefile) as f: 93 for line in f: 94 line = line.strip() 95 if not line.startswith('perlasm('): 96 continue 97 if not line.endswith(')'): 98 raise ValueError('Bad perlasm line in %s' % cmakefile) 99 # Remove "perlasm(" from start and ")" from end 100 params = line[8:-1].split() 101 if len(params) < 2: 102 raise ValueError('Bad perlasm line in %s' % cmakefile) 103 perlasms.append({ 104 'extra_args': params[2:], 105 'input': os.path.join(os.path.dirname(cmakefile), params[1]), 106 'output': os.path.join(os.path.dirname(cmakefile), params[0]), 107 }) 108 109 return perlasms 110 111 112 def ReadPerlAsmOperations(): 113 """Returns a list of all perlasm() directives found in CMake config files in 114 src/.""" 115 perlasms = [] 116 cmakefiles = FindCMakeFiles('src') 117 118 for cmakefile in cmakefiles: 119 perlasms.extend(ExtractPerlAsmFromCMakeFile(cmakefile)) 120 121 return perlasms 122 123 124 def PerlAsm(output_filename, input_filename, perlasm_style, extra_args): 125 """Runs the a perlasm script and puts the output into output_filename.""" 126 base_dir = os.path.dirname(output_filename) 127 if not os.path.isdir(base_dir): 128 os.makedirs(base_dir) 129 output = subprocess.check_output( 130 ['perl', input_filename, perlasm_style] + extra_args) 131 with open(output_filename, 'w+') as out_file: 132 out_file.write(output) 133 134 135 def ArchForAsmFilename(filename): 136 """Returns the architecture that a given asm file should be compiled for 137 based on substrings in the filename.""" 138 139 if 'x86_64' in filename or 'avx2' in filename: 140 return 'x86_64' 141 elif ('x86' in filename and 'x86_64' not in filename) or '586' in filename: 142 return 'x86' 143 elif 'arm' in filename: 144 return 'arm' 145 else: 146 raise ValueError('Unknown arch for asm filename: ' + filename) 147 148 149 def WriteAsmFiles(perlasms): 150 """Generates asm files from perlasm directives for each supported OS x 151 platform combination.""" 152 asmfiles = {} 153 154 for osarch in OS_ARCH_COMBOS: 155 (osname, arch, perlasm_style, extra_args, asm_ext) = osarch 156 key = (osname, arch) 157 outDir = '%s-%s' % key 158 159 for perlasm in perlasms: 160 filename = os.path.basename(perlasm['input']) 161 output = perlasm['output'] 162 if not output.startswith('src'): 163 raise ValueError('output missing src: %s' % output) 164 output = os.path.join(outDir, output[4:]) 165 output = output.replace('${ASM_EXT}', asm_ext) 166 167 if arch == ArchForAsmFilename(filename): 168 PerlAsm(output, perlasm['input'], perlasm_style, 169 perlasm['extra_args'] + extra_args) 170 asmfiles.setdefault(key, []).append(output) 171 172 for (key, non_perl_asm_files) in NON_PERL_FILES.iteritems(): 173 asmfiles.setdefault(key, []).extend(non_perl_asm_files) 174 175 return asmfiles 176 177 178 def PrintVariableSection(out, name, files): 179 out.write(' \'%s\': [\n' % name) 180 for f in sorted(files): 181 out.write(' \'%s\',\n' % f) 182 out.write(' ],\n') 183 184 185 def main(): 186 crypto_c_files = FindCFiles(os.path.join('src', 'crypto'), NoTests) 187 ssl_c_files = FindCFiles(os.path.join('src', 'ssl'), NoTests) 188 189 with open('boringssl.gypi', 'w+') as gypi: 190 gypi.write(FILE_HEADER + '{\n \'variables\': {\n') 191 192 PrintVariableSection( 193 gypi, 'boringssl_lib_sources', crypto_c_files + ssl_c_files) 194 195 perlasms = ReadPerlAsmOperations() 196 197 for ((osname, arch), asm_files) in sorted( 198 WriteAsmFiles(perlasms).iteritems()): 199 PrintVariableSection(gypi, 'boringssl_%s_%s_sources' % 200 (osname, arch), asm_files) 201 202 gypi.write(' }\n}\n') 203 204 test_c_files = FindCFiles(os.path.join('src', 'crypto'), OnlyTests) 205 test_c_files += FindCFiles(os.path.join('src', 'ssl'), OnlyTests) 206 207 with open('boringssl_tests.gypi', 'w+') as test_gypi: 208 test_gypi.write(FILE_HEADER + '{\n \'targets\': [\n') 209 210 test_names = [] 211 for test in sorted(test_c_files): 212 test_name = 'boringssl_%s' % os.path.splitext(os.path.basename(test))[0] 213 test_gypi.write(""" { 214 'target_name': '%s', 215 'type': 'executable', 216 'dependencies': [ 217 'boringssl.gyp:boringssl', 218 ], 219 'sources': [ 220 '%s', 221 ], 222 },\n""" % (test_name, test)) 223 test_names.append(test_name) 224 225 test_names.sort() 226 227 test_gypi.write(""" ], 228 'variables': { 229 'boringssl_test_targets': [\n""") 230 231 for test in test_names: 232 test_gypi.write(""" '%s',\n""" % test) 233 234 test_gypi.write(' ],\n }\n}\n') 235 236 return 0 237 238 239 if __name__ == '__main__': 240 sys.exit(main()) 241