1# Copyright (c) 2015, Google Inc.
2#
3# Permission to use, copy, modify, and/or distribute this software for any
4# purpose with or without fee is hereby granted, provided that the above
5# copyright notice and this permission notice appear in all copies.
6#
7# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15"""Enumerates source files for consumption by various build systems."""
16
17import optparse
18import os
19import subprocess
20import sys
21import json
22
23
24# OS_ARCH_COMBOS maps from OS and platform to the OpenSSL assembly "style" for
25# that platform and the extension used by asm files.
26OS_ARCH_COMBOS = [
27    ('linux', 'arm', 'linux32', [], 'S'),
28    ('linux', 'aarch64', 'linux64', [], 'S'),
29    ('linux', 'ppc64le', 'ppc64le', [], 'S'),
30    ('linux', 'x86', 'elf', ['-fPIC', '-DOPENSSL_IA32_SSE2'], 'S'),
31    ('linux', 'x86_64', 'elf', [], 'S'),
32    ('mac', 'x86', 'macosx', ['-fPIC', '-DOPENSSL_IA32_SSE2'], 'S'),
33    ('mac', 'x86_64', 'macosx', [], 'S'),
34    ('win', 'x86', 'win32n', ['-DOPENSSL_IA32_SSE2'], 'asm'),
35    ('win', 'x86_64', 'nasm', [], 'asm'),
36]
37
38# NON_PERL_FILES enumerates assembly files that are not processed by the
39# perlasm system.
40NON_PERL_FILES = {
41    ('linux', 'arm'): [
42        'src/crypto/curve25519/asm/x25519-asm-arm.S',
43        'src/crypto/poly1305/poly1305_arm_asm.S',
44    ],
45    ('linux', 'x86_64'): [
46        'src/crypto/curve25519/asm/x25519-asm-x86_64.S',
47    ],
48    ('mac', 'x86_64'): [
49        'src/crypto/curve25519/asm/x25519-asm-x86_64.S',
50    ],
51}
52
53PREFIX = None
54
55
56def PathOf(x):
57  return x if not PREFIX else os.path.join(PREFIX, x)
58
59
60class Android(object):
61
62  def __init__(self):
63    self.header = \
64"""# Copyright (C) 2015 The Android Open Source Project
65#
66# Licensed under the Apache License, Version 2.0 (the "License");
67# you may not use this file except in compliance with the License.
68# You may obtain a copy of the License at
69#
70#      http://www.apache.org/licenses/LICENSE-2.0
71#
72# Unless required by applicable law or agreed to in writing, software
73# distributed under the License is distributed on an "AS IS" BASIS,
74# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
75# See the License for the specific language governing permissions and
76# limitations under the License.
77
78# This file is created by generate_build_files.py. Do not edit manually.
79
80"""
81
82  def PrintVariableSection(self, out, name, files):
83    out.write('%s := \\\n' % name)
84    for f in sorted(files):
85      out.write('  %s\\\n' % f)
86    out.write('\n')
87
88  def WriteFiles(self, files, asm_outputs):
89    # New Android.bp format
90    with open('sources.bp', 'w+') as blueprint:
91      blueprint.write(self.header.replace('#', '//'))
92
93      blueprint.write('cc_defaults {\n')
94      blueprint.write('    name: "libcrypto_sources",\n')
95      blueprint.write('    srcs: [\n')
96      for f in sorted(files['crypto']):
97        blueprint.write('        "%s",\n' % f)
98      blueprint.write('    ],\n')
99      blueprint.write('    target: {\n')
100
101      for ((osname, arch), asm_files) in asm_outputs:
102        if osname != 'linux' or arch == 'ppc64le':
103          continue
104        if arch == 'aarch64':
105          arch = 'arm64'
106
107        blueprint.write('        android_%s: {\n' % arch)
108        blueprint.write('            srcs: [\n')
109        for f in sorted(asm_files):
110          blueprint.write('                "%s",\n' % f)
111        blueprint.write('            ],\n')
112        blueprint.write('        },\n')
113
114        if arch == 'x86' or arch == 'x86_64':
115          blueprint.write('        linux_%s: {\n' % arch)
116          blueprint.write('            srcs: [\n')
117          for f in sorted(asm_files):
118            blueprint.write('                "%s",\n' % f)
119          blueprint.write('            ],\n')
120          blueprint.write('        },\n')
121
122      blueprint.write('    },\n')
123      blueprint.write('}\n\n')
124
125      blueprint.write('cc_defaults {\n')
126      blueprint.write('    name: "libssl_sources",\n')
127      blueprint.write('    srcs: [\n')
128      for f in sorted(files['ssl']):
129        blueprint.write('        "%s",\n' % f)
130      blueprint.write('    ],\n')
131      blueprint.write('}\n\n')
132
133      blueprint.write('cc_defaults {\n')
134      blueprint.write('    name: "bssl_sources",\n')
135      blueprint.write('    srcs: [\n')
136      for f in sorted(files['tool']):
137        blueprint.write('        "%s",\n' % f)
138      blueprint.write('    ],\n')
139      blueprint.write('}\n\n')
140
141      blueprint.write('cc_defaults {\n')
142      blueprint.write('    name: "boringssl_test_support_sources",\n')
143      blueprint.write('    srcs: [\n')
144      for f in sorted(files['test_support']):
145        blueprint.write('        "%s",\n' % f)
146      blueprint.write('    ],\n')
147      blueprint.write('}\n\n')
148
149      blueprint.write('cc_defaults {\n')
150      blueprint.write('    name: "boringssl_crypto_test_sources",\n')
151      blueprint.write('    srcs: [\n')
152      for f in sorted(files['crypto_test']):
153        blueprint.write('        "%s",\n' % f)
154      blueprint.write('    ],\n')
155      blueprint.write('}\n\n')
156
157      blueprint.write('cc_defaults {\n')
158      blueprint.write('    name: "boringssl_ssl_test_sources",\n')
159      blueprint.write('    srcs: [\n')
160      for f in sorted(files['ssl_test']):
161        blueprint.write('        "%s",\n' % f)
162      blueprint.write('    ],\n')
163      blueprint.write('}\n\n')
164
165      blueprint.write('cc_defaults {\n')
166      blueprint.write('    name: "boringssl_tests_sources",\n')
167      blueprint.write('    srcs: [\n')
168      for f in sorted(files['test']):
169        blueprint.write('        "%s",\n' % f)
170      blueprint.write('    ],\n')
171      blueprint.write('}\n')
172
173    # Legacy Android.mk format, only used by Trusty in new branches
174    with open('sources.mk', 'w+') as makefile:
175      makefile.write(self.header)
176
177      self.PrintVariableSection(makefile, 'crypto_sources', files['crypto'])
178
179      for ((osname, arch), asm_files) in asm_outputs:
180        if osname != 'linux':
181          continue
182        self.PrintVariableSection(
183            makefile, '%s_%s_sources' % (osname, arch), asm_files)
184
185
186class Bazel(object):
187  """Bazel outputs files suitable for including in Bazel files."""
188
189  def __init__(self):
190    self.firstSection = True
191    self.header = \
192"""# This file is created by generate_build_files.py. Do not edit manually.
193
194"""
195
196  def PrintVariableSection(self, out, name, files):
197    if not self.firstSection:
198      out.write('\n')
199    self.firstSection = False
200
201    out.write('%s = [\n' % name)
202    for f in sorted(files):
203      out.write('    "%s",\n' % PathOf(f))
204    out.write(']\n')
205
206  def WriteFiles(self, files, asm_outputs):
207    with open('BUILD.generated.bzl', 'w+') as out:
208      out.write(self.header)
209
210      self.PrintVariableSection(out, 'ssl_headers', files['ssl_headers'])
211      self.PrintVariableSection(
212          out, 'ssl_internal_headers', files['ssl_internal_headers'])
213      self.PrintVariableSection(out, 'ssl_sources', files['ssl'])
214      self.PrintVariableSection(out, 'ssl_c_sources', files['ssl_c'])
215      self.PrintVariableSection(out, 'ssl_cc_sources', files['ssl_cc'])
216      self.PrintVariableSection(out, 'crypto_headers', files['crypto_headers'])
217      self.PrintVariableSection(
218          out, 'crypto_internal_headers', files['crypto_internal_headers'])
219      self.PrintVariableSection(out, 'crypto_sources', files['crypto'])
220      self.PrintVariableSection(out, 'tool_sources', files['tool'])
221      self.PrintVariableSection(out, 'tool_headers', files['tool_headers'])
222
223      for ((osname, arch), asm_files) in asm_outputs:
224        self.PrintVariableSection(
225            out, 'crypto_sources_%s_%s' % (osname, arch), asm_files)
226
227    with open('BUILD.generated_tests.bzl', 'w+') as out:
228      out.write(self.header)
229
230      out.write('test_support_sources = [\n')
231      for filename in sorted(files['test_support'] +
232                             files['test_support_headers'] +
233                             files['crypto_internal_headers'] +
234                             files['ssl_internal_headers']):
235        if os.path.basename(filename) == 'malloc.cc':
236          continue
237        out.write('    "%s",\n' % PathOf(filename))
238
239      out.write(']\n\n')
240
241      self.PrintVariableSection(out, 'crypto_test_sources',
242                                files['crypto_test'])
243      self.PrintVariableSection(out, 'ssl_test_sources', files['ssl_test'])
244
245      out.write('def create_tests(copts, crypto, ssl):\n')
246      name_counts = {}
247      for test in files['tests']:
248        name = os.path.basename(test[0])
249        name_counts[name] = name_counts.get(name, 0) + 1
250
251      first = True
252      for test in files['tests']:
253        name = os.path.basename(test[0])
254        if name_counts[name] > 1:
255          if '/' in test[1]:
256            name += '_' + os.path.splitext(os.path.basename(test[1]))[0]
257          else:
258            name += '_' + test[1].replace('-', '_')
259
260        if not first:
261          out.write('\n')
262        first = False
263
264        src_prefix = 'src/' + test[0]
265        for src in files['test']:
266          if src.startswith(src_prefix):
267            src = src
268            break
269        else:
270          raise ValueError("Can't find source for %s" % test[0])
271
272        out.write('  native.cc_test(\n')
273        out.write('      name = "%s",\n' % name)
274        out.write('      size = "small",\n')
275        out.write('      srcs = ["%s"] + test_support_sources,\n' %
276            PathOf(src))
277
278        data_files = []
279        if len(test) > 1:
280
281          out.write('      args = [\n')
282          for arg in test[1:]:
283            if '/' in arg:
284              out.write('          "$(location %s)",\n' %
285                  PathOf(os.path.join('src', arg)))
286              data_files.append('src/%s' % arg)
287            else:
288              out.write('          "%s",\n' % arg)
289          out.write('      ],\n')
290
291        out.write('      copts = copts + ["-DBORINGSSL_SHARED_LIBRARY"],\n')
292
293        if len(data_files) > 0:
294          out.write('      data = [\n')
295          for filename in data_files:
296            out.write('          "%s",\n' % PathOf(filename))
297          out.write('      ],\n')
298
299        if 'ssl/' in test[0]:
300          out.write('      deps = [\n')
301          out.write('          crypto,\n')
302          out.write('          ssl,\n')
303          out.write('      ],\n')
304        else:
305          out.write('      deps = [crypto],\n')
306        out.write('  )\n')
307
308
309class GN(object):
310
311  def __init__(self):
312    self.firstSection = True
313    self.header = \
314"""# Copyright (c) 2016 The Chromium Authors. All rights reserved.
315# Use of this source code is governed by a BSD-style license that can be
316# found in the LICENSE file.
317
318# This file is created by generate_build_files.py. Do not edit manually.
319
320"""
321
322  def PrintVariableSection(self, out, name, files):
323    if not self.firstSection:
324      out.write('\n')
325    self.firstSection = False
326
327    out.write('%s = [\n' % name)
328    for f in sorted(files):
329      out.write('  "%s",\n' % f)
330    out.write(']\n')
331
332  def WriteFiles(self, files, asm_outputs):
333    with open('BUILD.generated.gni', 'w+') as out:
334      out.write(self.header)
335
336      self.PrintVariableSection(out, 'crypto_sources',
337                                files['crypto'] + files['crypto_headers'] +
338                                files['crypto_internal_headers'])
339      self.PrintVariableSection(out, 'ssl_sources',
340                                files['ssl'] + files['ssl_headers'] +
341                                files['ssl_internal_headers'])
342
343      for ((osname, arch), asm_files) in asm_outputs:
344        self.PrintVariableSection(
345            out, 'crypto_sources_%s_%s' % (osname, arch), asm_files)
346
347      fuzzers = [os.path.splitext(os.path.basename(fuzzer))[0]
348                 for fuzzer in files['fuzz']]
349      self.PrintVariableSection(out, 'fuzzers', fuzzers)
350
351    with open('BUILD.generated_tests.gni', 'w+') as out:
352      self.firstSection = True
353      out.write(self.header)
354
355      self.PrintVariableSection(out, 'test_support_sources',
356                                files['test_support'] +
357                                files['test_support_headers'])
358      self.PrintVariableSection(out, 'crypto_test_sources',
359                                files['crypto_test'])
360      self.PrintVariableSection(out, 'ssl_test_sources', files['ssl_test'])
361      out.write('\n')
362
363      out.write('template("create_tests") {\n')
364
365      all_tests = []
366      for test in sorted(files['test']):
367        test_name = 'boringssl_%s' % os.path.splitext(os.path.basename(test))[0]
368        all_tests.append(test_name)
369
370        out.write('  executable("%s") {\n' % test_name)
371        out.write('    sources = [\n')
372        out.write('      "%s",\n' % test)
373        out.write('    ]\n')
374        out.write('    sources += test_support_sources\n')
375        out.write('    if (defined(invoker.configs_exclude)) {\n')
376        out.write('      configs -= invoker.configs_exclude\n')
377        out.write('    }\n')
378        out.write('    configs += invoker.configs\n')
379        out.write('    deps = invoker.deps\n')
380        out.write('  }\n')
381        out.write('\n')
382
383      out.write('  group(target_name) {\n')
384      out.write('    deps = [\n')
385      for test_name in sorted(all_tests):
386        out.write('      ":%s",\n' % test_name)
387      out.write('    ]\n')
388      out.write('  }\n')
389      out.write('}\n')
390
391
392class GYP(object):
393
394  def __init__(self):
395    self.header = \
396"""# Copyright (c) 2016 The Chromium Authors. All rights reserved.
397# Use of this source code is governed by a BSD-style license that can be
398# found in the LICENSE file.
399
400# This file is created by generate_build_files.py. Do not edit manually.
401
402"""
403
404  def PrintVariableSection(self, out, name, files):
405    out.write('    \'%s\': [\n' % name)
406    for f in sorted(files):
407      out.write('      \'%s\',\n' % f)
408    out.write('    ],\n')
409
410  def WriteFiles(self, files, asm_outputs):
411    with open('boringssl.gypi', 'w+') as gypi:
412      gypi.write(self.header + '{\n  \'variables\': {\n')
413
414      self.PrintVariableSection(gypi, 'boringssl_ssl_sources',
415                                files['ssl'] + files['ssl_headers'] +
416                                files['ssl_internal_headers'])
417      self.PrintVariableSection(gypi, 'boringssl_crypto_sources',
418                                files['crypto'] + files['crypto_headers'] +
419                                files['crypto_internal_headers'])
420
421      for ((osname, arch), asm_files) in asm_outputs:
422        self.PrintVariableSection(gypi, 'boringssl_%s_%s_sources' %
423                                  (osname, arch), asm_files)
424
425      gypi.write('  }\n}\n')
426
427
428def FindCMakeFiles(directory):
429  """Returns list of all CMakeLists.txt files recursively in directory."""
430  cmakefiles = []
431
432  for (path, _, filenames) in os.walk(directory):
433    for filename in filenames:
434      if filename == 'CMakeLists.txt':
435        cmakefiles.append(os.path.join(path, filename))
436
437  return cmakefiles
438
439
440def NoTests(dent, is_dir):
441  """Filter function that can be passed to FindCFiles in order to remove test
442  sources."""
443  if is_dir:
444    return dent != 'test'
445  return 'test.' not in dent and not dent.startswith('example_')
446
447
448def OnlyTests(dent, is_dir):
449  """Filter function that can be passed to FindCFiles in order to remove
450  non-test sources."""
451  if is_dir:
452    return dent != 'test'
453  return '_test.' in dent or dent.startswith('example_')
454
455
456def AllFiles(dent, is_dir):
457  """Filter function that can be passed to FindCFiles in order to include all
458  sources."""
459  return True
460
461
462def NotGTestMain(dent, is_dir):
463  return dent != 'gtest_main.cc'
464
465
466def SSLHeaderFiles(dent, is_dir):
467  return dent in ['ssl.h', 'tls1.h', 'ssl23.h', 'ssl3.h', 'dtls1.h']
468
469
470def FindCFiles(directory, filter_func):
471  """Recurses through directory and returns a list of paths to all the C source
472  files that pass filter_func."""
473  cfiles = []
474
475  for (path, dirnames, filenames) in os.walk(directory):
476    for filename in filenames:
477      if not filename.endswith('.c') and not filename.endswith('.cc'):
478        continue
479      if not filter_func(filename, False):
480        continue
481      cfiles.append(os.path.join(path, filename))
482
483    for (i, dirname) in enumerate(dirnames):
484      if not filter_func(dirname, True):
485        del dirnames[i]
486
487  return cfiles
488
489
490def FindHeaderFiles(directory, filter_func):
491  """Recurses through directory and returns a list of paths to all the header files that pass filter_func."""
492  hfiles = []
493
494  for (path, dirnames, filenames) in os.walk(directory):
495    for filename in filenames:
496      if not filename.endswith('.h'):
497        continue
498      if not filter_func(filename, False):
499        continue
500      hfiles.append(os.path.join(path, filename))
501
502      for (i, dirname) in enumerate(dirnames):
503        if not filter_func(dirname, True):
504          del dirnames[i]
505
506  return hfiles
507
508
509def ExtractPerlAsmFromCMakeFile(cmakefile):
510  """Parses the contents of the CMakeLists.txt file passed as an argument and
511  returns a list of all the perlasm() directives found in the file."""
512  perlasms = []
513  with open(cmakefile) as f:
514    for line in f:
515      line = line.strip()
516      if not line.startswith('perlasm('):
517        continue
518      if not line.endswith(')'):
519        raise ValueError('Bad perlasm line in %s' % cmakefile)
520      # Remove "perlasm(" from start and ")" from end
521      params = line[8:-1].split()
522      if len(params) < 2:
523        raise ValueError('Bad perlasm line in %s' % cmakefile)
524      perlasms.append({
525          'extra_args': params[2:],
526          'input': os.path.join(os.path.dirname(cmakefile), params[1]),
527          'output': os.path.join(os.path.dirname(cmakefile), params[0]),
528      })
529
530  return perlasms
531
532
533def ReadPerlAsmOperations():
534  """Returns a list of all perlasm() directives found in CMake config files in
535  src/."""
536  perlasms = []
537  cmakefiles = FindCMakeFiles('src')
538
539  for cmakefile in cmakefiles:
540    perlasms.extend(ExtractPerlAsmFromCMakeFile(cmakefile))
541
542  return perlasms
543
544
545def PerlAsm(output_filename, input_filename, perlasm_style, extra_args):
546  """Runs the a perlasm script and puts the output into output_filename."""
547  base_dir = os.path.dirname(output_filename)
548  if not os.path.isdir(base_dir):
549    os.makedirs(base_dir)
550  subprocess.check_call(
551      ['perl', input_filename, perlasm_style] + extra_args + [output_filename])
552
553
554def ArchForAsmFilename(filename):
555  """Returns the architectures that a given asm file should be compiled for
556  based on substrings in the filename."""
557
558  if 'x86_64' in filename or 'avx2' in filename:
559    return ['x86_64']
560  elif ('x86' in filename and 'x86_64' not in filename) or '586' in filename:
561    return ['x86']
562  elif 'armx' in filename:
563    return ['arm', 'aarch64']
564  elif 'armv8' in filename:
565    return ['aarch64']
566  elif 'arm' in filename:
567    return ['arm']
568  elif 'ppc' in filename:
569    return ['ppc64le']
570  else:
571    raise ValueError('Unknown arch for asm filename: ' + filename)
572
573
574def WriteAsmFiles(perlasms):
575  """Generates asm files from perlasm directives for each supported OS x
576  platform combination."""
577  asmfiles = {}
578
579  for osarch in OS_ARCH_COMBOS:
580    (osname, arch, perlasm_style, extra_args, asm_ext) = osarch
581    key = (osname, arch)
582    outDir = '%s-%s' % key
583
584    for perlasm in perlasms:
585      filename = os.path.basename(perlasm['input'])
586      output = perlasm['output']
587      if not output.startswith('src'):
588        raise ValueError('output missing src: %s' % output)
589      output = os.path.join(outDir, output[4:])
590      if output.endswith('-armx.${ASM_EXT}'):
591        output = output.replace('-armx',
592                                '-armx64' if arch == 'aarch64' else '-armx32')
593      output = output.replace('${ASM_EXT}', asm_ext)
594
595      if arch in ArchForAsmFilename(filename):
596        PerlAsm(output, perlasm['input'], perlasm_style,
597                perlasm['extra_args'] + extra_args)
598        asmfiles.setdefault(key, []).append(output)
599
600  for (key, non_perl_asm_files) in NON_PERL_FILES.iteritems():
601    asmfiles.setdefault(key, []).extend(non_perl_asm_files)
602
603  return asmfiles
604
605
606def IsGTest(path):
607  with open(path) as f:
608    return "#include <gtest/gtest.h>" in f.read()
609
610
611def main(platforms):
612  crypto_c_files = FindCFiles(os.path.join('src', 'crypto'), NoTests)
613  ssl_source_files = FindCFiles(os.path.join('src', 'ssl'), NoTests)
614  tool_c_files = FindCFiles(os.path.join('src', 'tool'), NoTests)
615  tool_h_files = FindHeaderFiles(os.path.join('src', 'tool'), AllFiles)
616
617  # Generate err_data.c
618  with open('err_data.c', 'w+') as err_data:
619    subprocess.check_call(['go', 'run', 'err_data_generate.go'],
620                          cwd=os.path.join('src', 'crypto', 'err'),
621                          stdout=err_data)
622  crypto_c_files.append('err_data.c')
623
624  test_support_c_files = FindCFiles(os.path.join('src', 'crypto', 'test'),
625                                    NotGTestMain)
626  test_support_h_files = (
627      FindHeaderFiles(os.path.join('src', 'crypto', 'test'), AllFiles) +
628      FindHeaderFiles(os.path.join('src', 'ssl', 'test'), AllFiles))
629
630  test_c_files = []
631  crypto_test_files = ['src/crypto/test/gtest_main.cc']
632  # TODO(davidben): Remove this loop once all tests are converted.
633  for path in FindCFiles(os.path.join('src', 'crypto'), OnlyTests):
634    if IsGTest(path):
635      crypto_test_files.append(path)
636    else:
637      test_c_files.append(path)
638
639  ssl_test_files = FindCFiles(os.path.join('src', 'ssl'), OnlyTests)
640  ssl_test_files.append('src/crypto/test/gtest_main.cc')
641
642  fuzz_c_files = FindCFiles(os.path.join('src', 'fuzz'), NoTests)
643
644  ssl_h_files = (
645      FindHeaderFiles(
646          os.path.join('src', 'include', 'openssl'),
647          SSLHeaderFiles))
648
649  def NotSSLHeaderFiles(filename, is_dir):
650    return not SSLHeaderFiles(filename, is_dir)
651  crypto_h_files = (
652      FindHeaderFiles(
653          os.path.join('src', 'include', 'openssl'),
654          NotSSLHeaderFiles))
655
656  ssl_internal_h_files = FindHeaderFiles(os.path.join('src', 'ssl'), NoTests)
657  crypto_internal_h_files = FindHeaderFiles(
658      os.path.join('src', 'crypto'), NoTests)
659
660  with open('src/util/all_tests.json', 'r') as f:
661    tests = json.load(f)
662  # For now, GTest-based tests are specified manually.
663  tests = [test for test in tests if test[0] not in ['crypto/crypto_test',
664                                                     'decrepit/decrepit_test',
665                                                     'ssl/ssl_test']]
666  test_binaries = set([test[0] for test in tests])
667  test_sources = set([
668      test.replace('.cc', '').replace('.c', '').replace(
669          'src/',
670          '')
671      for test in test_c_files])
672  if test_binaries != test_sources:
673    print 'Test sources and configured tests do not match'
674    a = test_binaries.difference(test_sources)
675    if len(a) > 0:
676      print 'These tests are configured without sources: ' + str(a)
677    b = test_sources.difference(test_binaries)
678    if len(b) > 0:
679      print 'These test sources are not configured: ' + str(b)
680
681  files = {
682      'crypto': crypto_c_files,
683      'crypto_headers': crypto_h_files,
684      'crypto_internal_headers': crypto_internal_h_files,
685      'crypto_test': sorted(crypto_test_files),
686      'fuzz': fuzz_c_files,
687      'ssl': ssl_source_files,
688      'ssl_c': [s for s in ssl_source_files if s.endswith('.c')],
689      'ssl_cc': [s for s in ssl_source_files if s.endswith('.cc')],
690      'ssl_headers': ssl_h_files,
691      'ssl_internal_headers': ssl_internal_h_files,
692      'ssl_test': sorted(ssl_test_files),
693      'tool': tool_c_files,
694      'tool_headers': tool_h_files,
695      'test': test_c_files,
696      'test_support': test_support_c_files,
697      'test_support_headers': test_support_h_files,
698      'tests': tests,
699  }
700
701  asm_outputs = sorted(WriteAsmFiles(ReadPerlAsmOperations()).iteritems())
702
703  for platform in platforms:
704    platform.WriteFiles(files, asm_outputs)
705
706  return 0
707
708
709if __name__ == '__main__':
710  parser = optparse.OptionParser(usage='Usage: %prog [--prefix=<path>]'
711      ' [android|bazel|gn|gyp]')
712  parser.add_option('--prefix', dest='prefix',
713      help='For Bazel, prepend argument to all source files')
714  options, args = parser.parse_args(sys.argv[1:])
715  PREFIX = options.prefix
716
717  if not args:
718    parser.print_help()
719    sys.exit(1)
720
721  platforms = []
722  for s in args:
723    if s == 'android':
724      platforms.append(Android())
725    elif s == 'bazel':
726      platforms.append(Bazel())
727    elif s == 'gn':
728      platforms.append(GN())
729    elif s == 'gyp':
730      platforms.append(GYP())
731    else:
732      parser.print_help()
733      sys.exit(1)
734
735  sys.exit(main(platforms))
736