• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# coding=utf8
2
3# Copyright (c) 2015, Google Inc.
4#
5# Permission to use, copy, modify, and/or distribute this software for any
6# purpose with or without fee is hereby granted, provided that the above
7# copyright notice and this permission notice appear in all copies.
8#
9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
12# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
14# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
15# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
17"""Enumerates source files for consumption by various build systems."""
18
19import optparse
20import os
21import subprocess
22import sys
23import json
24
25
26# OS_ARCH_COMBOS maps from OS and platform to the OpenSSL assembly "style" for
27# that platform and the extension used by asm files.
28#
29# TODO(https://crbug.com/boringssl/542): This probably should be a map, but some
30# downstream scripts import this to find what folders to add/remove from git.
31OS_ARCH_COMBOS = [
32    ('apple', 'arm', 'ios32', [], 'S'),
33    ('apple', 'aarch64', 'ios64', [], 'S'),
34    ('apple', 'x86', 'macosx', ['-fPIC', '-DOPENSSL_IA32_SSE2'], 'S'),
35    ('apple', 'x86_64', 'macosx', [], 'S'),
36    ('linux', 'arm', 'linux32', [], 'S'),
37    ('linux', 'aarch64', 'linux64', [], 'S'),
38    ('linux', 'x86', 'elf', ['-fPIC', '-DOPENSSL_IA32_SSE2'], 'S'),
39    ('linux', 'x86_64', 'elf', [], 'S'),
40    ('win', 'x86', 'win32n', ['-DOPENSSL_IA32_SSE2'], 'asm'),
41    ('win', 'x86_64', 'nasm', [], 'asm'),
42    ('win', 'aarch64', 'win64', [], 'S'),
43]
44
45# NON_PERL_FILES enumerates assembly files that are not processed by the
46# perlasm system.
47NON_PERL_FILES = {
48    ('apple', 'x86_64'): [
49        'src/third_party/fiat/asm/fiat_curve25519_adx_mul.S',
50        'src/third_party/fiat/asm/fiat_curve25519_adx_square.S',
51        'src/third_party/fiat/asm/fiat_p256_adx_mul.S',
52        'src/third_party/fiat/asm/fiat_p256_adx_sqr.S',
53    ],
54    ('linux', 'arm'): [
55        'src/crypto/curve25519/asm/x25519-asm-arm.S',
56        'src/crypto/poly1305/poly1305_arm_asm.S',
57    ],
58    ('linux', 'x86_64'): [
59        'src/crypto/hrss/asm/poly_rq_mul.S',
60        'src/third_party/fiat/asm/fiat_curve25519_adx_mul.S',
61        'src/third_party/fiat/asm/fiat_curve25519_adx_square.S',
62        'src/third_party/fiat/asm/fiat_p256_adx_mul.S',
63        'src/third_party/fiat/asm/fiat_p256_adx_sqr.S',
64    ],
65}
66
67PREFIX = None
68EMBED_TEST_DATA = True
69
70
71def PathOf(x):
72  return x if not PREFIX else os.path.join(PREFIX, x)
73
74
75LICENSE_TEMPLATE = """Copyright (c) 2015, Google Inc.
76
77Permission to use, copy, modify, and/or distribute this software for any
78purpose with or without fee is hereby granted, provided that the above
79copyright notice and this permission notice appear in all copies.
80
81THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
82WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
83MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
84SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
85WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
86OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
87CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.""".split("\n")
88
89def LicenseHeader(comment):
90  lines = []
91  for line in LICENSE_TEMPLATE:
92    if not line:
93      lines.append(comment)
94    else:
95      lines.append("%s %s" % (comment, line))
96  lines.append("")
97  return "\n".join(lines)
98
99
100class Android(object):
101
102  def __init__(self):
103    self.header = LicenseHeader("#") + """
104# This file is created by generate_build_files.py. Do not edit manually.
105"""
106
107  def PrintVariableSection(self, out, name, files):
108    out.write('%s := \\\n' % name)
109    for f in sorted(files):
110      out.write('  %s\\\n' % f)
111    out.write('\n')
112
113  def WriteFiles(self, files):
114    # New Android.bp format
115    with open('sources.bp', 'w+') as blueprint:
116      blueprint.write(self.header.replace('#', '//'))
117
118      #  Separate out BCM files to allow different compilation rules (specific to Android FIPS)
119      bcm_c_files = files['bcm_crypto']
120      non_bcm_c_files = [file for file in files['crypto'] if file not in bcm_c_files]
121      non_bcm_asm = self.FilterBcmAsm(files['crypto_asm'], False)
122      bcm_asm = self.FilterBcmAsm(files['crypto_asm'], True)
123
124      self.PrintDefaults(blueprint, 'libcrypto_sources', non_bcm_c_files, non_bcm_asm)
125      self.PrintDefaults(blueprint, 'libcrypto_bcm_sources', bcm_c_files, bcm_asm)
126      self.PrintDefaults(blueprint, 'libssl_sources', files['ssl'])
127      self.PrintDefaults(blueprint, 'bssl_sources', files['tool'])
128      self.PrintDefaults(blueprint, 'boringssl_test_support_sources', files['test_support'])
129      self.PrintDefaults(blueprint, 'boringssl_crypto_test_sources', files['crypto_test'])
130      self.PrintDefaults(blueprint, 'boringssl_ssl_test_sources', files['ssl_test'])
131      self.PrintDefaults(blueprint, 'libpki_sources', files['pki'])
132
133    # Legacy Android.mk format, only used by Trusty in new branches
134    with open('sources.mk', 'w+') as makefile:
135      makefile.write(self.header)
136      makefile.write('\n')
137      self.PrintVariableSection(makefile, 'crypto_sources', files['crypto'])
138      self.PrintVariableSection(makefile, 'crypto_sources_asm',
139                                files['crypto_asm'])
140
141  def PrintDefaults(self, blueprint, name, files, asm_files=[]):
142    """Print a cc_defaults section from a list of C files and optionally assembly outputs"""
143    if asm_files:
144      blueprint.write('\n')
145      blueprint.write('%s_asm = [\n' % name)
146      for f in sorted(asm_files):
147        blueprint.write('    "%s",\n' % f)
148      blueprint.write(']\n')
149
150    blueprint.write('\n')
151    blueprint.write('cc_defaults {\n')
152    blueprint.write('    name: "%s",\n' % name)
153    blueprint.write('    srcs: [\n')
154    for f in sorted(files):
155      blueprint.write('        "%s",\n' % f)
156    blueprint.write('    ],\n')
157
158    if asm_files:
159      blueprint.write('    target: {\n')
160      # Only emit asm for Linux. On Windows, BoringSSL requires NASM, which is
161      # not available in AOSP. On Darwin, the assembly works fine, but it
162      # conflicts with Android's FIPS build. See b/294399371.
163      blueprint.write('        linux: {\n')
164      blueprint.write('            srcs: %s_asm,\n' % name)
165      blueprint.write('        },\n')
166      blueprint.write('        darwin: {\n')
167      blueprint.write('            cflags: ["-DOPENSSL_NO_ASM"],\n')
168      blueprint.write('        },\n')
169      blueprint.write('        windows: {\n')
170      blueprint.write('            cflags: ["-DOPENSSL_NO_ASM"],\n')
171      blueprint.write('        },\n')
172      blueprint.write('    },\n')
173
174    blueprint.write('}\n')
175
176  def FilterBcmAsm(self, asm, want_bcm):
177    """Filter a list of assembly outputs based on whether they belong in BCM
178
179    Args:
180      asm: Assembly file list to filter
181      want_bcm: If true then include BCM files, otherwise do not
182
183    Returns:
184      A copy of |asm| with files filtered according to |want_bcm|
185    """
186    # TODO(https://crbug.com/boringssl/542): Rather than filtering by filename,
187    # use the variable listed in the CMake perlasm line, available in
188    # ExtractPerlAsmFromCMakeFile.
189    return filter(lambda p: ("/crypto/fipsmodule/" in p) == want_bcm, asm)
190
191
192class AndroidCMake(object):
193
194  def __init__(self):
195    self.header = LicenseHeader("#") + """
196# This file is created by generate_build_files.py. Do not edit manually.
197# To specify a custom path prefix, set BORINGSSL_ROOT before including this
198# file, or use list(TRANSFORM ... PREPEND) from CMake 3.12.
199
200"""
201
202  def PrintVariableSection(self, out, name, files):
203    out.write('set(%s\n' % name)
204    for f in sorted(files):
205      # Ideally adding the prefix would be the caller's job, but
206      # list(TRANSFORM ... PREPEND) is only available starting CMake 3.12. When
207      # sources.cmake is the source of truth, we can ask Android to either write
208      # a CMake function or update to 3.12.
209      out.write('  ${BORINGSSL_ROOT}%s\n' % f)
210    out.write(')\n')
211
212  def WriteFiles(self, files):
213    # The Android emulator uses a custom CMake buildsystem.
214    #
215    # TODO(crbug.com/boringssl/542): Move our various source lists into
216    # sources.cmake and have Android consume that directly.
217    with open('android-sources.cmake', 'w+') as out:
218      out.write(self.header)
219
220      self.PrintVariableSection(out, 'crypto_sources', files['crypto'])
221      self.PrintVariableSection(out, 'crypto_sources_asm', files['crypto_asm'])
222      self.PrintVariableSection(out, 'crypto_sources_nasm',
223                                files['crypto_nasm'])
224      self.PrintVariableSection(out, 'ssl_sources', files['ssl'])
225      self.PrintVariableSection(out, 'tool_sources', files['tool'])
226      self.PrintVariableSection(out, 'test_support_sources',
227                                files['test_support'])
228      self.PrintVariableSection(out, 'crypto_test_sources',
229                                files['crypto_test'])
230      self.PrintVariableSection(out, 'ssl_test_sources', files['ssl_test'])
231
232
233class Bazel(object):
234  """Bazel outputs files suitable for including in Bazel files."""
235
236  def __init__(self):
237    self.firstSection = True
238    self.header = \
239"""# This file is created by generate_build_files.py. Do not edit manually.
240
241"""
242
243  def PrintVariableSection(self, out, name, files):
244    if not self.firstSection:
245      out.write('\n')
246    self.firstSection = False
247
248    out.write('%s = [\n' % name)
249    for f in sorted(files):
250      out.write('    "%s",\n' % PathOf(f))
251    out.write(']\n')
252
253  def WriteFiles(self, files):
254    with open('BUILD.generated.bzl', 'w+') as out:
255      out.write(self.header)
256
257      self.PrintVariableSection(out, 'ssl_headers', files['ssl_headers'])
258      self.PrintVariableSection(out, 'fips_fragments', files['fips_fragments'])
259      self.PrintVariableSection(
260          out, 'ssl_internal_headers', files['ssl_internal_headers'])
261      self.PrintVariableSection(out, 'ssl_sources', files['ssl'])
262      self.PrintVariableSection(out, 'crypto_headers', files['crypto_headers'])
263      self.PrintVariableSection(
264          out, 'crypto_internal_headers', files['crypto_internal_headers'])
265      self.PrintVariableSection(out, 'crypto_sources', files['crypto'])
266      self.PrintVariableSection(out, 'crypto_sources_asm', files['crypto_asm'])
267      self.PrintVariableSection(out, 'crypto_sources_nasm', files['crypto_nasm'])
268      self.PrintVariableSection(
269          out, 'pki_internal_headers', files['pki_internal_headers'])
270      self.PrintVariableSection(out, 'pki_sources', files['pki'])
271      self.PrintVariableSection(out, 'tool_sources', files['tool'])
272      self.PrintVariableSection(out, 'tool_headers', files['tool_headers'])
273
274    with open('BUILD.generated_tests.bzl', 'w+') as out:
275      out.write(self.header)
276
277      out.write('test_support_sources = [\n')
278      for filename in sorted(files['test_support'] +
279                             files['test_support_headers'] +
280                             files['crypto_internal_headers'] +
281                             files['pki_internal_headers'] +
282                             files['ssl_internal_headers']):
283        out.write('    "%s",\n' % PathOf(filename))
284
285      out.write(']\n')
286
287      self.PrintVariableSection(out, 'crypto_test_sources',
288                                files['crypto_test'])
289      self.PrintVariableSection(out, 'ssl_test_sources', files['ssl_test'])
290      self.PrintVariableSection(out, 'pki_test_sources',
291                                files['pki_test'])
292      self.PrintVariableSection(out, 'crypto_test_data',
293                                files['crypto_test_data'])
294      self.PrintVariableSection(out, 'pki_test_data',
295                                files['pki_test_data'])
296      self.PrintVariableSection(out, 'urandom_test_sources',
297                                files['urandom_test'])
298
299
300class Eureka(object):
301
302  def __init__(self):
303    self.header = LicenseHeader("#") + """
304# This file is created by generate_build_files.py. Do not edit manually.
305
306"""
307
308  def PrintVariableSection(self, out, name, files):
309    out.write('%s := \\\n' % name)
310    for f in sorted(files):
311      out.write('  %s\\\n' % f)
312    out.write('\n')
313
314  def WriteFiles(self, files):
315    # Legacy Android.mk format
316    with open('eureka.mk', 'w+') as makefile:
317      makefile.write(self.header)
318
319      self.PrintVariableSection(makefile, 'crypto_sources', files['crypto'])
320      self.PrintVariableSection(makefile, 'crypto_sources_asm',
321                                files['crypto_asm'])
322      self.PrintVariableSection(makefile, 'crypto_sources_nasm',
323                                files['crypto_nasm'])
324      self.PrintVariableSection(makefile, 'ssl_sources', files['ssl'])
325      self.PrintVariableSection(makefile, 'tool_sources', files['tool'])
326
327
328class GN(object):
329
330  def __init__(self):
331    self.firstSection = True
332    self.header = LicenseHeader("#") + """
333# This file is created by generate_build_files.py. Do not edit manually.
334
335"""
336
337  def PrintVariableSection(self, out, name, files):
338    if not self.firstSection:
339      out.write('\n')
340    self.firstSection = False
341
342    out.write('%s = [\n' % name)
343    for f in sorted(files):
344      out.write('  "%s",\n' % f)
345    out.write(']\n')
346
347  def WriteFiles(self, files):
348    with open('BUILD.generated.gni', 'w+') as out:
349      out.write(self.header)
350
351      self.PrintVariableSection(out, 'crypto_sources',
352                                files['crypto'] +
353                                files['crypto_internal_headers'])
354      self.PrintVariableSection(out, 'crypto_sources_asm', files['crypto_asm'])
355      self.PrintVariableSection(out, 'crypto_sources_nasm',
356                                files['crypto_nasm'])
357      self.PrintVariableSection(out, 'crypto_headers',
358                                files['crypto_headers'])
359      self.PrintVariableSection(out, 'ssl_sources',
360                                files['ssl'] + files['ssl_internal_headers'])
361      self.PrintVariableSection(out, 'ssl_headers', files['ssl_headers'])
362      self.PrintVariableSection(out, 'pki_sources',
363                                files['pki'])
364      self.PrintVariableSection(out, 'pki_internal_headers',
365                                files['pki_internal_headers'])
366      self.PrintVariableSection(out, 'tool_sources',
367                                files['tool'] + files['tool_headers'])
368
369      fuzzers = [os.path.splitext(os.path.basename(fuzzer))[0]
370                 for fuzzer in files['fuzz']]
371      self.PrintVariableSection(out, 'fuzzers', fuzzers)
372
373    with open('BUILD.generated_tests.gni', 'w+') as out:
374      self.firstSection = True
375      out.write(self.header)
376
377      self.PrintVariableSection(out, 'test_support_sources',
378                                files['test_support'] +
379                                files['test_support_headers'])
380      self.PrintVariableSection(out, 'crypto_test_sources',
381                                files['crypto_test'])
382      self.PrintVariableSection(out, 'crypto_test_data',
383                                files['crypto_test_data'])
384      self.PrintVariableSection(out, 'pki_test_data',
385                                files['pki_test_data'])
386      self.PrintVariableSection(out, 'ssl_test_sources', files['ssl_test'])
387      self.PrintVariableSection(out, 'pki_test_sources', files['pki_test'])
388
389
390class GYP(object):
391
392  def __init__(self):
393    self.header = LicenseHeader("#") + """
394# This file is created by generate_build_files.py. Do not edit manually.
395
396"""
397
398  def PrintVariableSection(self, out, name, files):
399    out.write('    \'%s\': [\n' % name)
400    for f in sorted(files):
401      out.write('      \'%s\',\n' % f)
402    out.write('    ],\n')
403
404  def WriteFiles(self, files):
405    with open('boringssl.gypi', 'w+') as gypi:
406      gypi.write(self.header + '{\n  \'variables\': {\n')
407
408      self.PrintVariableSection(gypi, 'boringssl_ssl_sources',
409                                files['ssl'] + files['ssl_headers'] +
410                                files['ssl_internal_headers'])
411      self.PrintVariableSection(gypi, 'boringssl_crypto_sources',
412                                files['crypto'] + files['crypto_headers'] +
413                                files['crypto_internal_headers'])
414      self.PrintVariableSection(gypi, 'boringssl_crypto_asm_sources',
415                                files['crypto_asm'])
416      self.PrintVariableSection(gypi, 'boringssl_crypto_nasm_sources',
417                                files['crypto_nasm'])
418
419      gypi.write('  }\n}\n')
420
421class CMake(object):
422
423  def __init__(self):
424    self.header = LicenseHeader("#") + R'''
425# This file is created by generate_build_files.py. Do not edit manually.
426
427cmake_minimum_required(VERSION 3.12)
428
429project(BoringSSL LANGUAGES C CXX)
430
431set(CMAKE_CXX_STANDARD 14)
432set(CMAKE_CXX_STANDARD_REQUIRED ON)
433set(CMAKE_C_STANDARD 11)
434set(CMAKE_C_STANDARD_REQUIRED ON)
435if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
436  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fno-common -fno-exceptions -fno-rtti")
437  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -fno-common")
438endif()
439
440# pthread_rwlock_t requires a feature flag on glibc.
441if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
442  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_XOPEN_SOURCE=700")
443endif()
444
445if(WIN32)
446  add_definitions(-D_HAS_EXCEPTIONS=0)
447  add_definitions(-DWIN32_LEAN_AND_MEAN)
448  add_definitions(-DNOMINMAX)
449  # Allow use of fopen.
450  add_definitions(-D_CRT_SECURE_NO_WARNINGS)
451endif()
452
453add_definitions(-DBORINGSSL_IMPLEMENTATION)
454
455if(OPENSSL_NO_ASM)
456  add_definitions(-DOPENSSL_NO_ASM)
457else()
458  # On x86 and x86_64 Windows, we use the NASM output.
459  if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|x86_64|amd64|x86|i[3-6]86")
460    enable_language(ASM_NASM)
461    set(OPENSSL_NASM TRUE)
462    set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -gcv8")
463  else()
464    enable_language(ASM)
465    set(OPENSSL_ASM TRUE)
466    # Work around https://gitlab.kitware.com/cmake/cmake/-/issues/20771 in older
467    # CMake versions.
468    if(APPLE AND CMAKE_VERSION VERSION_LESS 3.19)
469      if(CMAKE_OSX_SYSROOT)
470        set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -isysroot \"${CMAKE_OSX_SYSROOT}\"")
471      endif()
472      foreach(arch ${CMAKE_OSX_ARCHITECTURES})
473        set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -arch ${arch}")
474      endforeach()
475    endif()
476    if(NOT WIN32)
477      set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wa,--noexecstack")
478    endif()
479    # Clang's integerated assembler does not support debug symbols.
480    if(NOT CMAKE_ASM_COMPILER_ID MATCHES "Clang")
481      set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wa,-g")
482    endif()
483  endif()
484endif()
485
486if(BUILD_SHARED_LIBS)
487  add_definitions(-DBORINGSSL_SHARED_LIBRARY)
488  # Enable position-independent code globally. This is needed because
489  # some library targets are OBJECT libraries.
490  set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
491endif()
492
493'''
494
495  def PrintLibrary(self, out, name, files, libs=[]):
496    out.write('add_library(\n')
497    out.write('  %s\n\n' % name)
498
499    for f in sorted(files):
500      out.write('  %s\n' % PathOf(f))
501
502    out.write(')\n\n')
503    if libs:
504      out.write('target_link_libraries(%s %s)\n\n' % (name, ' '.join(libs)))
505
506  def PrintExe(self, out, name, files, libs):
507    out.write('add_executable(\n')
508    out.write('  %s\n\n' % name)
509
510    for f in sorted(files):
511      out.write('  %s\n' % PathOf(f))
512
513    out.write(')\n\n')
514    out.write('target_link_libraries(%s %s)\n\n' % (name, ' '.join(libs)))
515
516  def PrintVariable(self, out, name, files):
517    out.write('set(\n')
518    out.write('  %s\n\n' % name)
519    for f in sorted(files):
520      out.write('  %s\n' % PathOf(f))
521    out.write(')\n\n')
522
523  def WriteFiles(self, files):
524    with open('CMakeLists.txt', 'w+') as cmake:
525      cmake.write(self.header)
526
527      self.PrintVariable(cmake, 'CRYPTO_SOURCES_ASM', files['crypto_asm'])
528      self.PrintVariable(cmake, 'CRYPTO_SOURCES_NASM', files['crypto_nasm'])
529
530      cmake.write(
531R'''if(OPENSSL_ASM)
532  list(APPEND CRYPTO_SOURCES_ASM_USED ${CRYPTO_SOURCES_ASM})
533endif()
534if(OPENSSL_NASM)
535  list(APPEND CRYPTO_SOURCES_ASM_USED ${CRYPTO_SOURCES_NASM})
536endif()
537
538''')
539
540      self.PrintLibrary(cmake, 'crypto',
541          files['crypto'] + ['${CRYPTO_SOURCES_ASM_USED}'])
542      cmake.write('target_include_directories(crypto PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/include>)\n\n')
543      self.PrintLibrary(cmake, 'ssl', files['ssl'], ['crypto'])
544      self.PrintExe(cmake, 'bssl', files['tool'], ['ssl', 'crypto'])
545
546      cmake.write(
547R'''if(NOT ANDROID)
548  find_package(Threads REQUIRED)
549  target_link_libraries(crypto Threads::Threads)
550endif()
551
552if(WIN32)
553  target_link_libraries(crypto ws2_32)
554endif()
555
556''')
557
558class JSON(object):
559  def WriteFiles(self, files):
560    with open('sources.json', 'w+') as f:
561      json.dump(files, f, sort_keys=True, indent=2)
562
563def FindCMakeFiles(directory):
564  """Returns list of all CMakeLists.txt files recursively in directory."""
565  cmakefiles = []
566
567  for (path, _, filenames) in os.walk(directory):
568    for filename in filenames:
569      if filename == 'CMakeLists.txt':
570        cmakefiles.append(os.path.join(path, filename))
571
572  return cmakefiles
573
574def OnlyFIPSFragments(path, dent, is_dir):
575  return is_dir or (path.startswith(
576      os.path.join('src', 'crypto', 'fipsmodule', '')) and
577      NoTests(path, dent, is_dir))
578
579def NoTestsNorFIPSFragments(path, dent, is_dir):
580  return (NoTests(path, dent, is_dir) and
581      (is_dir or not OnlyFIPSFragments(path, dent, is_dir)))
582
583def NoTests(path, dent, is_dir):
584  """Filter function that can be passed to FindCFiles in order to remove test
585  sources."""
586  if is_dir:
587    return dent != 'test'
588  return 'test.' not in dent
589
590
591def AllFiles(path, dent, is_dir):
592  """Filter function that can be passed to FindCFiles in order to include all
593  sources."""
594  return True
595
596
597def NoTestRunnerFiles(path, dent, is_dir):
598  """Filter function that can be passed to FindCFiles or FindHeaderFiles in
599  order to exclude test runner files."""
600  # NOTE(martinkr): This prevents .h/.cc files in src/ssl/test/runner, which
601  # are in their own subpackage, from being included in boringssl/BUILD files.
602  return not is_dir or dent != 'runner'
603
604
605def SSLHeaderFiles(path, dent, is_dir):
606  return dent in ['ssl.h', 'tls1.h', 'ssl23.h', 'ssl3.h', 'dtls1.h', 'srtp.h']
607
608
609def FindCFiles(directory, filter_func):
610  """Recurses through directory and returns a list of paths to all the C source
611  files that pass filter_func."""
612  cfiles = []
613
614  for (path, dirnames, filenames) in os.walk(directory):
615    for filename in filenames:
616      if not filename.endswith('.c') and not filename.endswith('.cc'):
617        continue
618      if not filter_func(path, filename, False):
619        continue
620      cfiles.append(os.path.join(path, filename))
621
622    for (i, dirname) in enumerate(dirnames):
623      if not filter_func(path, dirname, True):
624        del dirnames[i]
625
626  cfiles.sort()
627  return cfiles
628
629
630def FindHeaderFiles(directory, filter_func):
631  """Recurses through directory and returns a list of paths to all the header files that pass filter_func."""
632  hfiles = []
633
634  for (path, dirnames, filenames) in os.walk(directory):
635    for filename in filenames:
636      if not filename.endswith('.h'):
637        continue
638      if not filter_func(path, filename, False):
639        continue
640      hfiles.append(os.path.join(path, filename))
641
642      for (i, dirname) in enumerate(dirnames):
643        if not filter_func(path, dirname, True):
644          del dirnames[i]
645
646  hfiles.sort()
647  return hfiles
648
649
650def ExtractPerlAsmFromCMakeFile(cmakefile):
651  """Parses the contents of the CMakeLists.txt file passed as an argument and
652  returns a list of all the perlasm() directives found in the file."""
653  perlasms = []
654  with open(cmakefile) as f:
655    for line in f:
656      line = line.strip()
657      if not line.startswith('perlasm('):
658        continue
659      if not line.endswith(')'):
660        raise ValueError('Bad perlasm line in %s' % cmakefile)
661      # Remove "perlasm(" from start and ")" from end
662      params = line[8:-1].split()
663      if len(params) < 4:
664        raise ValueError('Bad perlasm line in %s' % cmakefile)
665      perlasms.append({
666          'arch': params[1],
667          'output': os.path.join(os.path.dirname(cmakefile), params[2]),
668          'input': os.path.join(os.path.dirname(cmakefile), params[3]),
669          'extra_args': params[4:],
670      })
671
672  return perlasms
673
674
675def ReadPerlAsmOperations():
676  """Returns a list of all perlasm() directives found in CMake config files in
677  src/."""
678  perlasms = []
679  cmakefiles = FindCMakeFiles('src')
680
681  for cmakefile in cmakefiles:
682    perlasms.extend(ExtractPerlAsmFromCMakeFile(cmakefile))
683
684  return perlasms
685
686
687def PerlAsm(output_filename, input_filename, perlasm_style, extra_args):
688  """Runs the a perlasm script and puts the output into output_filename."""
689  base_dir = os.path.dirname(output_filename)
690  if not os.path.isdir(base_dir):
691    os.makedirs(base_dir)
692  subprocess.check_call(
693      ['perl', input_filename, perlasm_style] + extra_args + [output_filename])
694
695
696def WriteAsmFiles(perlasms):
697  """Generates asm files from perlasm directives for each supported OS x
698  platform combination."""
699  asmfiles = {}
700
701  for perlasm in perlasms:
702    for (osname, arch, perlasm_style, extra_args, asm_ext) in OS_ARCH_COMBOS:
703      if arch != perlasm['arch']:
704        continue
705      # TODO(https://crbug.com/boringssl/542): Now that we incorporate osname in
706      # the output filename, the asm files can just go in a single directory.
707      # For now, we keep them in target-specific directories to avoid breaking
708      # downstream scripts.
709      key = (osname, arch)
710      outDir = '%s-%s' % key
711      output = perlasm['output']
712      if not output.startswith('src'):
713        raise ValueError('output missing src: %s' % output)
714      output = os.path.join(outDir, output[4:])
715      output = '%s-%s.%s' % (output, osname, asm_ext)
716      PerlAsm(output, perlasm['input'], perlasm_style,
717              extra_args + perlasm['extra_args'])
718      asmfiles.setdefault(key, []).append(output)
719
720  for (key, non_perl_asm_files) in NON_PERL_FILES.items():
721    asmfiles.setdefault(key, []).extend(non_perl_asm_files)
722
723  for files in asmfiles.values():
724    files.sort()
725
726  return asmfiles
727
728
729def ExtractVariablesFromCMakeFile(cmakefile):
730  """Parses the contents of the CMakeLists.txt file passed as an argument and
731  returns a dictionary of exported source lists."""
732  variables = {}
733  in_set_command = False
734  set_command = []
735  with open(cmakefile) as f:
736    for line in f:
737      if '#' in line:
738        line = line[:line.index('#')]
739      line = line.strip()
740
741      if not in_set_command:
742        if line.startswith('set('):
743          in_set_command = True
744          set_command = []
745      elif line == ')':
746        in_set_command = False
747        if not set_command:
748          raise ValueError('Empty set command')
749        variables[set_command[0]] = set_command[1:]
750      else:
751        set_command.extend([c for c in line.split(' ') if c])
752
753  if in_set_command:
754    raise ValueError('Unfinished set command')
755  return variables
756
757
758def PrefixWithSrc(files):
759  return ['src/' + x for x in files]
760
761
762def main(platforms):
763  cmake = ExtractVariablesFromCMakeFile(os.path.join('src', 'sources.cmake'))
764  crypto_c_files = (FindCFiles(os.path.join('src', 'crypto'), NoTestsNorFIPSFragments) +
765                    FindCFiles(os.path.join('src', 'third_party', 'fiat'), NoTestsNorFIPSFragments))
766  fips_fragments = FindCFiles(os.path.join('src', 'crypto', 'fipsmodule'), OnlyFIPSFragments)
767  ssl_source_files = FindCFiles(os.path.join('src', 'ssl'), NoTests)
768  tool_h_files = FindHeaderFiles(os.path.join('src', 'tool'), AllFiles)
769
770  # BCM shared library C files
771  bcm_crypto_c_files = [
772      os.path.join('src', 'crypto', 'fipsmodule', 'bcm.c')
773  ]
774
775  # Generate err_data.c
776  with open('err_data.c', 'w+') as err_data:
777    subprocess.check_call(['go', 'run', 'err_data_generate.go'],
778                          cwd=os.path.join('src', 'crypto', 'err'),
779                          stdout=err_data)
780  crypto_c_files.append('err_data.c')
781  crypto_c_files.sort()
782
783  test_support_h_files = (
784      FindHeaderFiles(os.path.join('src', 'crypto', 'test'), AllFiles) +
785      FindHeaderFiles(os.path.join('src', 'ssl', 'test'), NoTestRunnerFiles))
786
787  crypto_test_files = []
788  if EMBED_TEST_DATA:
789    # Generate crypto_test_data.cc
790    with open('crypto_test_data.cc', 'w+') as out:
791      subprocess.check_call(
792          ['go', 'run', 'util/embed_test_data.go'] + cmake['CRYPTO_TEST_DATA'],
793          cwd='src',
794          stdout=out)
795    crypto_test_files.append('crypto_test_data.cc')
796
797  crypto_test_files += PrefixWithSrc(cmake['CRYPTO_TEST_SOURCES'])
798  crypto_test_files.sort()
799
800  fuzz_c_files = FindCFiles(os.path.join('src', 'fuzz'), NoTests)
801
802  ssl_h_files = FindHeaderFiles(os.path.join('src', 'include', 'openssl'),
803                                SSLHeaderFiles)
804
805  pki_internal_h_files = FindHeaderFiles(os.path.join('src', 'pki'), AllFiles);
806
807  def NotSSLHeaderFiles(path, filename, is_dir):
808    return not SSLHeaderFiles(path, filename, is_dir)
809  crypto_h_files = FindHeaderFiles(os.path.join('src', 'include', 'openssl'),
810                                   NotSSLHeaderFiles)
811
812  ssl_internal_h_files = FindHeaderFiles(os.path.join('src', 'ssl'), NoTests)
813  crypto_internal_h_files = (
814      FindHeaderFiles(os.path.join('src', 'crypto'), NoTests) +
815      FindHeaderFiles(os.path.join('src', 'third_party', 'fiat'), NoTests))
816
817  asm_outputs = sorted(WriteAsmFiles(ReadPerlAsmOperations()).items())
818
819  # Generate combined source lists for gas and nasm. Some files appear in
820  # multiple per-platform lists, so we de-duplicate.
821  #
822  # TODO(https://crbug.com/boringssl/542): It would be simpler to build the
823  # combined source lists directly. This is a remnant of the previous assembly
824  # strategy. When we move to pre-generated assembly files, this will be
825  # removed.
826  asm_sources = set()
827  nasm_sources = set()
828  for ((osname, arch), asm_files) in asm_outputs:
829    if (osname, arch) in (('win', 'x86'), ('win', 'x86_64')):
830      nasm_sources.update(asm_files)
831    else:
832      asm_sources.update(asm_files)
833
834  files = {
835      'bcm_crypto': bcm_crypto_c_files,
836      'crypto': crypto_c_files,
837      'crypto_asm': sorted(list(asm_sources)),
838      'crypto_nasm': sorted(list(nasm_sources)),
839      'crypto_headers': crypto_h_files,
840      'crypto_internal_headers': crypto_internal_h_files,
841      'crypto_test': crypto_test_files,
842      'crypto_test_data': sorted(PrefixWithSrc(cmake['CRYPTO_TEST_DATA'])),
843      'fips_fragments': fips_fragments,
844      'fuzz': fuzz_c_files,
845      'pki': PrefixWithSrc(cmake['PKI_SOURCES']),
846      'pki_internal_headers': sorted(list(pki_internal_h_files)),
847      'pki_test': PrefixWithSrc(cmake['PKI_TEST_SOURCES']),
848      'pki_test_data': PrefixWithSrc(cmake['PKI_TEST_DATA']),
849      'ssl': ssl_source_files,
850      'ssl_headers': ssl_h_files,
851      'ssl_internal_headers': ssl_internal_h_files,
852      'ssl_test': PrefixWithSrc(cmake['SSL_TEST_SOURCES']),
853      'tool': PrefixWithSrc(cmake['BSSL_SOURCES']),
854      'tool_headers': tool_h_files,
855      'test_support': PrefixWithSrc(cmake['TEST_SUPPORT_SOURCES']),
856      'test_support_headers': test_support_h_files,
857      'urandom_test': PrefixWithSrc(cmake['URANDOM_TEST_SOURCES']),
858  }
859
860  for platform in platforms:
861    platform.WriteFiles(files)
862
863  return 0
864
865ALL_PLATFORMS = {
866    'android': Android,
867    'android-cmake': AndroidCMake,
868    'bazel': Bazel,
869    'cmake': CMake,
870    'eureka': Eureka,
871    'gn': GN,
872    'gyp': GYP,
873    'json': JSON,
874}
875
876if __name__ == '__main__':
877  parser = optparse.OptionParser(
878      usage='Usage: %%prog [--prefix=<path>] [all|%s]' %
879      '|'.join(sorted(ALL_PLATFORMS.keys())))
880  parser.add_option('--prefix', dest='prefix',
881      help='For Bazel, prepend argument to all source files')
882  parser.add_option(
883      '--embed_test_data', type='choice', dest='embed_test_data',
884      action='store', default="true", choices=["true", "false"],
885      help='For Bazel or GN, don\'t embed data files in crypto_test_data.cc')
886  options, args = parser.parse_args(sys.argv[1:])
887  PREFIX = options.prefix
888  EMBED_TEST_DATA = (options.embed_test_data == "true")
889
890  if not args:
891    parser.print_help()
892    sys.exit(1)
893
894  if 'all' in args:
895    platforms = [platform() for platform in ALL_PLATFORMS.values()]
896  else:
897    platforms = []
898    for s in args:
899      platform = ALL_PLATFORMS.get(s)
900      if platform is None:
901        parser.print_help()
902        sys.exit(1)
903      platforms.append(platform())
904
905  sys.exit(main(platforms))
906