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.
28OS_ARCH_COMBOS = [
29    ('ios', 'arm', 'ios32', [], 'S'),
30    ('ios', 'aarch64', 'ios64', [], 'S'),
31    ('linux', 'arm', 'linux32', [], 'S'),
32    ('linux', 'aarch64', 'linux64', [], 'S'),
33    ('linux', 'ppc64le', 'linux64le', [], 'S'),
34    ('linux', 'x86', 'elf', ['-fPIC', '-DOPENSSL_IA32_SSE2'], 'S'),
35    ('linux', 'x86_64', 'elf', [], 'S'),
36    ('mac', 'x86', 'macosx', ['-fPIC', '-DOPENSSL_IA32_SSE2'], 'S'),
37    ('mac', 'x86_64', 'macosx', [], 'S'),
38    ('win', 'x86', 'win32n', ['-DOPENSSL_IA32_SSE2'], 'asm'),
39    ('win', 'x86_64', 'nasm', [], 'asm'),
40    ('win', 'aarch64', 'win64', [], 'S'),
41]
42
43# NON_PERL_FILES enumerates assembly files that are not processed by the
44# perlasm system.
45NON_PERL_FILES = {
46    ('linux', 'arm'): [
47        'src/crypto/curve25519/asm/x25519-asm-arm.S',
48        'src/crypto/poly1305/poly1305_arm_asm.S',
49    ],
50    ('linux', 'x86_64'): [
51        'src/crypto/hrss/asm/poly_rq_mul.S',
52    ],
53}
54
55PREFIX = None
56EMBED_TEST_DATA = True
57
58
59def PathOf(x):
60  return x if not PREFIX else os.path.join(PREFIX, x)
61
62
63class Android(object):
64
65  def __init__(self):
66    self.header = \
67"""# Copyright (C) 2015 The Android Open Source Project
68#
69# Licensed under the Apache License, Version 2.0 (the "License");
70# you may not use this file except in compliance with the License.
71# You may obtain a copy of the License at
72#
73#      http://www.apache.org/licenses/LICENSE-2.0
74#
75# Unless required by applicable law or agreed to in writing, software
76# distributed under the License is distributed on an "AS IS" BASIS,
77# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
78# See the License for the specific language governing permissions and
79# limitations under the License.
80
81# This file is created by generate_build_files.py. Do not edit manually.
82"""
83
84  def PrintVariableSection(self, out, name, files):
85    out.write('%s := \\\n' % name)
86    for f in sorted(files):
87      out.write('  %s\\\n' % f)
88    out.write('\n')
89
90  def WriteFiles(self, files, asm_outputs):
91    # New Android.bp format
92    with open('sources.bp', 'w+') as blueprint:
93      blueprint.write(self.header.replace('#', '//'))
94
95      #  Separate out BCM files to allow different compilation rules (specific to Android FIPS)
96      bcm_c_files = files['bcm_crypto']
97      non_bcm_c_files = [file for file in files['crypto'] if file not in bcm_c_files]
98      non_bcm_asm = self.FilterBcmAsm(asm_outputs, False)
99      bcm_asm = self.FilterBcmAsm(asm_outputs, True)
100
101      self.PrintDefaults(blueprint, 'libcrypto_sources', non_bcm_c_files, non_bcm_asm)
102      self.PrintDefaults(blueprint, 'libcrypto_bcm_sources', bcm_c_files, bcm_asm)
103      self.PrintDefaults(blueprint, 'libssl_sources', files['ssl'])
104      self.PrintDefaults(blueprint, 'bssl_sources', files['tool'])
105      self.PrintDefaults(blueprint, 'boringssl_test_support_sources', files['test_support'])
106      self.PrintDefaults(blueprint, 'boringssl_crypto_test_sources', files['crypto_test'])
107      self.PrintDefaults(blueprint, 'boringssl_ssl_test_sources', files['ssl_test'])
108
109    # Legacy Android.mk format, only used by Trusty in new branches
110    with open('sources.mk', 'w+') as makefile:
111      makefile.write(self.header)
112      makefile.write('\n')
113      self.PrintVariableSection(makefile, 'crypto_sources', files['crypto'])
114
115      for ((osname, arch), asm_files) in asm_outputs:
116        if osname != 'linux':
117          continue
118        self.PrintVariableSection(
119            makefile, '%s_%s_sources' % (osname, arch), asm_files)
120
121  def PrintDefaults(self, blueprint, name, files, asm_outputs={}):
122    """Print a cc_defaults section from a list of C files and optionally assembly outputs"""
123    blueprint.write('\n')
124    blueprint.write('cc_defaults {\n')
125    blueprint.write('    name: "%s",\n' % name)
126    blueprint.write('    srcs: [\n')
127    for f in sorted(files):
128      blueprint.write('        "%s",\n' % f)
129    blueprint.write('    ],\n')
130
131    if asm_outputs:
132      blueprint.write('    target: {\n')
133      for ((osname, arch), asm_files) in asm_outputs:
134        if osname != 'linux' or arch == 'ppc64le':
135          continue
136        if arch == 'aarch64':
137          arch = 'arm64'
138
139        blueprint.write('        linux_%s: {\n' % arch)
140        blueprint.write('            srcs: [\n')
141        for f in sorted(asm_files):
142          blueprint.write('                "%s",\n' % f)
143        blueprint.write('            ],\n')
144        blueprint.write('        },\n')
145      blueprint.write('    },\n')
146
147    blueprint.write('}\n')
148
149  def FilterBcmAsm(self, asm, want_bcm):
150    """Filter a list of assembly outputs based on whether they belong in BCM
151
152    Args:
153      asm: Assembly file lists to filter
154      want_bcm: If true then include BCM files, otherwise do not
155
156    Returns:
157      A copy of |asm| with files filtered according to |want_bcm|
158    """
159    return [(archinfo, filter(lambda p: ("/crypto/fipsmodule/" in p) == want_bcm, files))
160            for (archinfo, files) in asm]
161
162
163class AndroidCMake(object):
164
165  def __init__(self):
166    self.header = \
167"""# Copyright (C) 2019 The Android Open Source Project
168#
169# Licensed under the Apache License, Version 2.0 (the "License");
170# you may not use this file except in compliance with the License.
171# You may obtain a copy of the License at
172#
173#      http://www.apache.org/licenses/LICENSE-2.0
174#
175# Unless required by applicable law or agreed to in writing, software
176# distributed under the License is distributed on an "AS IS" BASIS,
177# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
178# See the License for the specific language governing permissions and
179# limitations under the License.
180
181# This file is created by generate_build_files.py. Do not edit manually.
182# To specify a custom path prefix, set BORINGSSL_ROOT before including this
183# file, or use list(TRANSFORM ... PREPEND) from CMake 3.12.
184
185"""
186
187  def PrintVariableSection(self, out, name, files):
188    out.write('set(%s\n' % name)
189    for f in sorted(files):
190      # Ideally adding the prefix would be the caller's job, but
191      # list(TRANSFORM ... PREPEND) is only available starting CMake 3.12. When
192      # sources.cmake is the source of truth, we can ask Android to either write
193      # a CMake function or update to 3.12.
194      out.write('  ${BORINGSSL_ROOT}%s\n' % f)
195    out.write(')\n')
196
197  def WriteFiles(self, files, asm_outputs):
198    # The Android emulator uses a custom CMake buildsystem.
199    #
200    # TODO(davidben): Move our various source lists into sources.cmake and have
201    # Android consume that directly.
202    with open('android-sources.cmake', 'w+') as out:
203      out.write(self.header)
204
205      self.PrintVariableSection(out, 'crypto_sources', files['crypto'])
206      self.PrintVariableSection(out, 'ssl_sources', files['ssl'])
207      self.PrintVariableSection(out, 'tool_sources', files['tool'])
208      self.PrintVariableSection(out, 'test_support_sources',
209                                files['test_support'])
210      self.PrintVariableSection(out, 'crypto_test_sources',
211                                files['crypto_test'])
212      self.PrintVariableSection(out, 'ssl_test_sources', files['ssl_test'])
213
214      for ((osname, arch), asm_files) in asm_outputs:
215        self.PrintVariableSection(
216            out, 'crypto_sources_%s_%s' % (osname, arch), asm_files)
217
218
219class Bazel(object):
220  """Bazel outputs files suitable for including in Bazel files."""
221
222  def __init__(self):
223    self.firstSection = True
224    self.header = \
225"""# This file is created by generate_build_files.py. Do not edit manually.
226
227"""
228
229  def PrintVariableSection(self, out, name, files):
230    if not self.firstSection:
231      out.write('\n')
232    self.firstSection = False
233
234    out.write('%s = [\n' % name)
235    for f in sorted(files):
236      out.write('    "%s",\n' % PathOf(f))
237    out.write(']\n')
238
239  def WriteFiles(self, files, asm_outputs):
240    with open('BUILD.generated.bzl', 'w+') as out:
241      out.write(self.header)
242
243      self.PrintVariableSection(out, 'ssl_headers', files['ssl_headers'])
244      self.PrintVariableSection(out, 'fips_fragments', files['fips_fragments'])
245      self.PrintVariableSection(
246          out, 'ssl_internal_headers', files['ssl_internal_headers'])
247      self.PrintVariableSection(out, 'ssl_sources', files['ssl'])
248      self.PrintVariableSection(out, 'crypto_headers', files['crypto_headers'])
249      self.PrintVariableSection(
250          out, 'crypto_internal_headers', files['crypto_internal_headers'])
251      self.PrintVariableSection(out, 'crypto_sources', files['crypto'])
252      self.PrintVariableSection(out, 'tool_sources', files['tool'])
253      self.PrintVariableSection(out, 'tool_headers', files['tool_headers'])
254
255      for ((osname, arch), asm_files) in asm_outputs:
256        self.PrintVariableSection(
257            out, 'crypto_sources_%s_%s' % (osname, arch), asm_files)
258
259    with open('BUILD.generated_tests.bzl', 'w+') as out:
260      out.write(self.header)
261
262      out.write('test_support_sources = [\n')
263      for filename in sorted(files['test_support'] +
264                             files['test_support_headers'] +
265                             files['crypto_internal_headers'] +
266                             files['ssl_internal_headers']):
267        if os.path.basename(filename) == 'malloc.cc':
268          continue
269        out.write('    "%s",\n' % PathOf(filename))
270
271      out.write(']\n')
272
273      self.PrintVariableSection(out, 'crypto_test_sources',
274                                files['crypto_test'])
275      self.PrintVariableSection(out, 'ssl_test_sources', files['ssl_test'])
276      self.PrintVariableSection(out, 'crypto_test_data',
277                                files['crypto_test_data'])
278      self.PrintVariableSection(out, 'urandom_test_sources',
279                                files['urandom_test'])
280
281
282class Eureka(object):
283
284  def __init__(self):
285    self.header = \
286"""# Copyright (C) 2017 The Android Open Source Project
287#
288# Licensed under the Apache License, Version 2.0 (the "License");
289# you may not use this file except in compliance with the License.
290# You may obtain a copy of the License at
291#
292#      http://www.apache.org/licenses/LICENSE-2.0
293#
294# Unless required by applicable law or agreed to in writing, software
295# distributed under the License is distributed on an "AS IS" BASIS,
296# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
297# See the License for the specific language governing permissions and
298# limitations under the License.
299
300# This file is created by generate_build_files.py. Do not edit manually.
301
302"""
303
304  def PrintVariableSection(self, out, name, files):
305    out.write('%s := \\\n' % name)
306    for f in sorted(files):
307      out.write('  %s\\\n' % f)
308    out.write('\n')
309
310  def WriteFiles(self, files, asm_outputs):
311    # Legacy Android.mk format
312    with open('eureka.mk', 'w+') as makefile:
313      makefile.write(self.header)
314
315      self.PrintVariableSection(makefile, 'crypto_sources', files['crypto'])
316      self.PrintVariableSection(makefile, 'ssl_sources', files['ssl'])
317      self.PrintVariableSection(makefile, 'tool_sources', files['tool'])
318
319      for ((osname, arch), asm_files) in asm_outputs:
320        if osname != 'linux':
321          continue
322        self.PrintVariableSection(
323            makefile, '%s_%s_sources' % (osname, arch), asm_files)
324
325
326class GN(object):
327
328  def __init__(self):
329    self.firstSection = True
330    self.header = \
331"""# Copyright (c) 2016 The Chromium Authors. All rights reserved.
332# Use of this source code is governed by a BSD-style license that can be
333# found in the LICENSE file.
334
335# This file is created by generate_build_files.py. Do not edit manually.
336
337"""
338
339  def PrintVariableSection(self, out, name, files):
340    if not self.firstSection:
341      out.write('\n')
342    self.firstSection = False
343
344    out.write('%s = [\n' % name)
345    for f in sorted(files):
346      out.write('  "%s",\n' % f)
347    out.write(']\n')
348
349  def WriteFiles(self, files, asm_outputs):
350    with open('BUILD.generated.gni', 'w+') as out:
351      out.write(self.header)
352
353      self.PrintVariableSection(out, 'crypto_sources',
354                                files['crypto'] +
355                                files['crypto_internal_headers'])
356      self.PrintVariableSection(out, 'crypto_headers',
357                                files['crypto_headers'])
358      self.PrintVariableSection(out, 'ssl_sources',
359                                files['ssl'] + files['ssl_internal_headers'])
360      self.PrintVariableSection(out, 'ssl_headers', files['ssl_headers'])
361      self.PrintVariableSection(out, 'tool_sources',
362                                files['tool'] + files['tool_headers'])
363
364      for ((osname, arch), asm_files) in asm_outputs:
365        self.PrintVariableSection(
366            out, 'crypto_sources_%s_%s' % (osname, arch), asm_files)
367
368      fuzzers = [os.path.splitext(os.path.basename(fuzzer))[0]
369                 for fuzzer in files['fuzz']]
370      self.PrintVariableSection(out, 'fuzzers', fuzzers)
371
372    with open('BUILD.generated_tests.gni', 'w+') as out:
373      self.firstSection = True
374      out.write(self.header)
375
376      self.PrintVariableSection(out, 'test_support_sources',
377                                files['test_support'] +
378                                files['test_support_headers'])
379      self.PrintVariableSection(out, 'crypto_test_sources',
380                                files['crypto_test'])
381      self.PrintVariableSection(out, 'crypto_test_data',
382                                files['crypto_test_data'])
383      self.PrintVariableSection(out, 'ssl_test_sources', files['ssl_test'])
384
385
386class GYP(object):
387
388  def __init__(self):
389    self.header = \
390"""# Copyright (c) 2016 The Chromium Authors. All rights reserved.
391# Use of this source code is governed by a BSD-style license that can be
392# found in the LICENSE file.
393
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, asm_outputs):
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
415      for ((osname, arch), asm_files) in asm_outputs:
416        self.PrintVariableSection(gypi, 'boringssl_%s_%s_sources' %
417                                  (osname, arch), asm_files)
418
419      gypi.write('  }\n}\n')
420
421class CMake(object):
422
423  def __init__(self):
424    self.header = \
425R'''# Copyright (c) 2019 The Chromium Authors. All rights reserved.
426# Use of this source code is governed by a BSD-style license that can be
427# found in the LICENSE file.
428
429# This file is created by generate_build_files.py. Do not edit manually.
430
431cmake_minimum_required(VERSION 3.0)
432
433project(BoringSSL LANGUAGES C CXX)
434
435if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
436  set(CLANG 1)
437endif()
438
439if(CMAKE_COMPILER_IS_GNUCXX OR CLANG)
440  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fvisibility=hidden -fno-common -fno-exceptions -fno-rtti")
441  if(APPLE)
442    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
443  endif()
444
445  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -fno-common")
446  if((CMAKE_C_COMPILER_VERSION VERSION_GREATER "4.8.99") OR CLANG)
447    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
448  else()
449    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
450  endif()
451endif()
452
453# pthread_rwlock_t requires a feature flag.
454if(NOT WIN32)
455  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_XOPEN_SOURCE=700")
456endif()
457
458if(WIN32)
459  add_definitions(-D_HAS_EXCEPTIONS=0)
460  add_definitions(-DWIN32_LEAN_AND_MEAN)
461  add_definitions(-DNOMINMAX)
462  # Allow use of fopen.
463  add_definitions(-D_CRT_SECURE_NO_WARNINGS)
464  # VS 2017 and higher supports STL-only warning suppressions.
465  # A bug in CMake < 3.13.0 may cause the space in this value to
466  # cause issues when building with NASM. In that case, update CMake.
467  add_definitions("-D_STL_EXTRA_DISABLED_WARNINGS=4774 4987")
468endif()
469
470add_definitions(-DBORINGSSL_IMPLEMENTATION)
471
472# CMake's iOS support uses Apple's multiple-architecture toolchain. It takes an
473# architecture list from CMAKE_OSX_ARCHITECTURES, leaves CMAKE_SYSTEM_PROCESSOR
474# alone, and expects all architecture-specific logic to be conditioned within
475# the source files rather than the build. This does not work for our assembly
476# files, so we fix CMAKE_SYSTEM_PROCESSOR and only support single-architecture
477# builds.
478if(NOT OPENSSL_NO_ASM AND CMAKE_OSX_ARCHITECTURES)
479  list(LENGTH CMAKE_OSX_ARCHITECTURES NUM_ARCHES)
480  if(NOT ${NUM_ARCHES} EQUAL 1)
481    message(FATAL_ERROR "Universal binaries not supported.")
482  endif()
483  list(GET CMAKE_OSX_ARCHITECTURES 0 CMAKE_SYSTEM_PROCESSOR)
484endif()
485
486if(OPENSSL_NO_ASM)
487  add_definitions(-DOPENSSL_NO_ASM)
488  set(ARCH "generic")
489elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
490  set(ARCH "x86_64")
491elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "amd64")
492  set(ARCH "x86_64")
493elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64")
494  # cmake reports AMD64 on Windows, but we might be building for 32-bit.
495  if(CMAKE_SIZEOF_VOID_P EQUAL 8)
496    set(ARCH "x86_64")
497  else()
498    set(ARCH "x86")
499  endif()
500elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86")
501  set(ARCH "x86")
502elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i386")
503  set(ARCH "x86")
504elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i686")
505  set(ARCH "x86")
506elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
507  set(ARCH "aarch64")
508elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64")
509  set(ARCH "aarch64")
510# Apple A12 Bionic chipset which is added in iPhone XS/XS Max/XR uses arm64e architecture.
511elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64e")
512  set(ARCH "aarch64")
513elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm*")
514  set(ARCH "arm")
515elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "mips")
516  # Just to avoid the “unknown processor” error.
517  set(ARCH "generic")
518elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ppc64le")
519  set(ARCH "ppc64le")
520else()
521  message(FATAL_ERROR "Unknown processor:" ${CMAKE_SYSTEM_PROCESSOR})
522endif()
523
524if(NOT OPENSSL_NO_ASM)
525  if(UNIX)
526    enable_language(ASM)
527
528    # Clang's integerated assembler does not support debug symbols.
529    if(NOT CMAKE_ASM_COMPILER_ID MATCHES "Clang")
530      set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wa,-g")
531    endif()
532
533    # CMake does not add -isysroot and -arch flags to assembly.
534    if(APPLE)
535      if(CMAKE_OSX_SYSROOT)
536        set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -isysroot \"${CMAKE_OSX_SYSROOT}\"")
537      endif()
538      foreach(arch ${CMAKE_OSX_ARCHITECTURES})
539        set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -arch ${arch}")
540      endforeach()
541    endif()
542  else()
543    set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -gcv8")
544    enable_language(ASM_NASM)
545  endif()
546endif()
547
548if(BUILD_SHARED_LIBS)
549  add_definitions(-DBORINGSSL_SHARED_LIBRARY)
550  # Enable position-independent code globally. This is needed because
551  # some library targets are OBJECT libraries.
552  set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
553endif()
554
555include_directories(src/include)
556
557'''
558
559  def PrintLibrary(self, out, name, files):
560    out.write('add_library(\n')
561    out.write('  %s\n\n' % name)
562
563    for f in sorted(files):
564      out.write('  %s\n' % PathOf(f))
565
566    out.write(')\n\n')
567
568  def PrintExe(self, out, name, files, libs):
569    out.write('add_executable(\n')
570    out.write('  %s\n\n' % name)
571
572    for f in sorted(files):
573      out.write('  %s\n' % PathOf(f))
574
575    out.write(')\n\n')
576    out.write('target_link_libraries(%s %s)\n\n' % (name, ' '.join(libs)))
577
578  def PrintSection(self, out, name, files):
579    out.write('set(\n')
580    out.write('  %s\n\n' % name)
581    for f in sorted(files):
582      out.write('  %s\n' % PathOf(f))
583    out.write(')\n\n')
584
585  def WriteFiles(self, files, asm_outputs):
586    with open('CMakeLists.txt', 'w+') as cmake:
587      cmake.write(self.header)
588
589      for ((osname, arch), asm_files) in asm_outputs:
590        self.PrintSection(cmake, 'CRYPTO_%s_%s_SOURCES' % (osname, arch),
591            asm_files)
592
593      cmake.write(
594R'''if(APPLE AND ${ARCH} STREQUAL "aarch64")
595  set(CRYPTO_ARCH_SOURCES ${CRYPTO_ios_aarch64_SOURCES})
596elseif(APPLE AND ${ARCH} STREQUAL "arm")
597  set(CRYPTO_ARCH_SOURCES ${CRYPTO_ios_arm_SOURCES})
598elseif(APPLE)
599  set(CRYPTO_ARCH_SOURCES ${CRYPTO_mac_${ARCH}_SOURCES})
600elseif(UNIX)
601  set(CRYPTO_ARCH_SOURCES ${CRYPTO_linux_${ARCH}_SOURCES})
602elseif(WIN32)
603  set(CRYPTO_ARCH_SOURCES ${CRYPTO_win_${ARCH}_SOURCES})
604endif()
605
606''')
607
608      self.PrintLibrary(cmake, 'crypto',
609          files['crypto'] + ['${CRYPTO_ARCH_SOURCES}'])
610      self.PrintLibrary(cmake, 'ssl', files['ssl'])
611      self.PrintExe(cmake, 'bssl', files['tool'], ['ssl', 'crypto'])
612
613      cmake.write(
614R'''if(NOT WIN32 AND NOT ANDROID)
615  target_link_libraries(crypto pthread)
616endif()
617
618if(WIN32)
619  target_link_libraries(bssl ws2_32)
620endif()
621
622''')
623
624class JSON(object):
625  def WriteFiles(self, files, asm_outputs):
626    sources = dict(files)
627    for ((osname, arch), asm_files) in asm_outputs:
628      sources['crypto_%s_%s' % (osname, arch)] = asm_files
629    with open('sources.json', 'w+') as f:
630      json.dump(sources, f, sort_keys=True, indent=2)
631
632def FindCMakeFiles(directory):
633  """Returns list of all CMakeLists.txt files recursively in directory."""
634  cmakefiles = []
635
636  for (path, _, filenames) in os.walk(directory):
637    for filename in filenames:
638      if filename == 'CMakeLists.txt':
639        cmakefiles.append(os.path.join(path, filename))
640
641  return cmakefiles
642
643def OnlyFIPSFragments(path, dent, is_dir):
644  return is_dir or (path.startswith(
645      os.path.join('src', 'crypto', 'fipsmodule', '')) and
646      NoTests(path, dent, is_dir))
647
648def NoTestsNorFIPSFragments(path, dent, is_dir):
649  return (NoTests(path, dent, is_dir) and
650      (is_dir or not OnlyFIPSFragments(path, dent, is_dir)))
651
652def NoTests(path, dent, is_dir):
653  """Filter function that can be passed to FindCFiles in order to remove test
654  sources."""
655  if is_dir:
656    return dent != 'test'
657  return 'test.' not in dent
658
659
660def OnlyTests(path, dent, is_dir):
661  """Filter function that can be passed to FindCFiles in order to remove
662  non-test sources."""
663  if is_dir:
664    return dent != 'test'
665  return '_test.' in dent
666
667
668def AllFiles(path, dent, is_dir):
669  """Filter function that can be passed to FindCFiles in order to include all
670  sources."""
671  return True
672
673
674def NoTestRunnerFiles(path, dent, is_dir):
675  """Filter function that can be passed to FindCFiles or FindHeaderFiles in
676  order to exclude test runner files."""
677  # NOTE(martinkr): This prevents .h/.cc files in src/ssl/test/runner, which
678  # are in their own subpackage, from being included in boringssl/BUILD files.
679  return not is_dir or dent != 'runner'
680
681
682def NotGTestSupport(path, dent, is_dir):
683  return 'gtest' not in dent and 'abi_test' not in dent
684
685
686def SSLHeaderFiles(path, dent, is_dir):
687  return dent in ['ssl.h', 'tls1.h', 'ssl23.h', 'ssl3.h', 'dtls1.h', 'srtp.h']
688
689
690def FindCFiles(directory, filter_func):
691  """Recurses through directory and returns a list of paths to all the C source
692  files that pass filter_func."""
693  cfiles = []
694
695  for (path, dirnames, filenames) in os.walk(directory):
696    for filename in filenames:
697      if not filename.endswith('.c') and not filename.endswith('.cc'):
698        continue
699      if not filter_func(path, filename, False):
700        continue
701      cfiles.append(os.path.join(path, filename))
702
703    for (i, dirname) in enumerate(dirnames):
704      if not filter_func(path, dirname, True):
705        del dirnames[i]
706
707  cfiles.sort()
708  return cfiles
709
710
711def FindHeaderFiles(directory, filter_func):
712  """Recurses through directory and returns a list of paths to all the header files that pass filter_func."""
713  hfiles = []
714
715  for (path, dirnames, filenames) in os.walk(directory):
716    for filename in filenames:
717      if not filename.endswith('.h'):
718        continue
719      if not filter_func(path, filename, False):
720        continue
721      hfiles.append(os.path.join(path, filename))
722
723      for (i, dirname) in enumerate(dirnames):
724        if not filter_func(path, dirname, True):
725          del dirnames[i]
726
727  hfiles.sort()
728  return hfiles
729
730
731def ExtractPerlAsmFromCMakeFile(cmakefile):
732  """Parses the contents of the CMakeLists.txt file passed as an argument and
733  returns a list of all the perlasm() directives found in the file."""
734  perlasms = []
735  with open(cmakefile) as f:
736    for line in f:
737      line = line.strip()
738      if not line.startswith('perlasm('):
739        continue
740      if not line.endswith(')'):
741        raise ValueError('Bad perlasm line in %s' % cmakefile)
742      # Remove "perlasm(" from start and ")" from end
743      params = line[8:-1].split()
744      if len(params) < 2:
745        raise ValueError('Bad perlasm line in %s' % cmakefile)
746      perlasms.append({
747          'extra_args': params[2:],
748          'input': os.path.join(os.path.dirname(cmakefile), params[1]),
749          'output': os.path.join(os.path.dirname(cmakefile), params[0]),
750      })
751
752  return perlasms
753
754
755def ReadPerlAsmOperations():
756  """Returns a list of all perlasm() directives found in CMake config files in
757  src/."""
758  perlasms = []
759  cmakefiles = FindCMakeFiles('src')
760
761  for cmakefile in cmakefiles:
762    perlasms.extend(ExtractPerlAsmFromCMakeFile(cmakefile))
763
764  return perlasms
765
766
767def PerlAsm(output_filename, input_filename, perlasm_style, extra_args):
768  """Runs the a perlasm script and puts the output into output_filename."""
769  base_dir = os.path.dirname(output_filename)
770  if not os.path.isdir(base_dir):
771    os.makedirs(base_dir)
772  subprocess.check_call(
773      ['perl', input_filename, perlasm_style] + extra_args + [output_filename])
774
775
776def ArchForAsmFilename(filename):
777  """Returns the architectures that a given asm file should be compiled for
778  based on substrings in the filename."""
779
780  if 'x86_64' in filename or 'avx2' in filename:
781    return ['x86_64']
782  elif ('x86' in filename and 'x86_64' not in filename) or '586' in filename:
783    return ['x86']
784  elif 'armx' in filename:
785    return ['arm', 'aarch64']
786  elif 'armv8' in filename:
787    return ['aarch64']
788  elif 'arm' in filename:
789    return ['arm']
790  elif 'ppc' in filename:
791    return ['ppc64le']
792  else:
793    raise ValueError('Unknown arch for asm filename: ' + filename)
794
795
796def WriteAsmFiles(perlasms):
797  """Generates asm files from perlasm directives for each supported OS x
798  platform combination."""
799  asmfiles = {}
800
801  for osarch in OS_ARCH_COMBOS:
802    (osname, arch, perlasm_style, extra_args, asm_ext) = osarch
803    key = (osname, arch)
804    outDir = '%s-%s' % key
805
806    for perlasm in perlasms:
807      filename = os.path.basename(perlasm['input'])
808      output = perlasm['output']
809      if not output.startswith('src'):
810        raise ValueError('output missing src: %s' % output)
811      output = os.path.join(outDir, output[4:])
812      if output.endswith('-armx.${ASM_EXT}'):
813        output = output.replace('-armx',
814                                '-armx64' if arch == 'aarch64' else '-armx32')
815      output = output.replace('${ASM_EXT}', asm_ext)
816
817      if arch in ArchForAsmFilename(filename):
818        PerlAsm(output, perlasm['input'], perlasm_style,
819                perlasm['extra_args'] + extra_args)
820        asmfiles.setdefault(key, []).append(output)
821
822  for (key, non_perl_asm_files) in NON_PERL_FILES.iteritems():
823    asmfiles.setdefault(key, []).extend(non_perl_asm_files)
824
825  for files in asmfiles.itervalues():
826    files.sort()
827
828  return asmfiles
829
830
831def ExtractVariablesFromCMakeFile(cmakefile):
832  """Parses the contents of the CMakeLists.txt file passed as an argument and
833  returns a dictionary of exported source lists."""
834  variables = {}
835  in_set_command = False
836  set_command = []
837  with open(cmakefile) as f:
838    for line in f:
839      if '#' in line:
840        line = line[:line.index('#')]
841      line = line.strip()
842
843      if not in_set_command:
844        if line.startswith('set('):
845          in_set_command = True
846          set_command = []
847      elif line == ')':
848        in_set_command = False
849        if not set_command:
850          raise ValueError('Empty set command')
851        variables[set_command[0]] = set_command[1:]
852      else:
853        set_command.extend([c for c in line.split(' ') if c])
854
855  if in_set_command:
856    raise ValueError('Unfinished set command')
857  return variables
858
859
860def main(platforms):
861  cmake = ExtractVariablesFromCMakeFile(os.path.join('src', 'sources.cmake'))
862  crypto_c_files = (FindCFiles(os.path.join('src', 'crypto'), NoTestsNorFIPSFragments) +
863                    FindCFiles(os.path.join('src', 'third_party', 'fiat'), NoTestsNorFIPSFragments))
864  fips_fragments = FindCFiles(os.path.join('src', 'crypto', 'fipsmodule'), OnlyFIPSFragments)
865  ssl_source_files = FindCFiles(os.path.join('src', 'ssl'), NoTests)
866  tool_c_files = FindCFiles(os.path.join('src', 'tool'), NoTests)
867  tool_h_files = FindHeaderFiles(os.path.join('src', 'tool'), AllFiles)
868
869  # BCM shared library C files
870  bcm_crypto_c_files = [
871      os.path.join('src', 'crypto', 'fipsmodule', 'bcm.c')
872  ]
873
874  # Generate err_data.c
875  with open('err_data.c', 'w+') as err_data:
876    subprocess.check_call(['go', 'run', 'err_data_generate.go'],
877                          cwd=os.path.join('src', 'crypto', 'err'),
878                          stdout=err_data)
879  crypto_c_files.append('err_data.c')
880  crypto_c_files.sort()
881
882  test_support_c_files = FindCFiles(os.path.join('src', 'crypto', 'test'),
883                                    NotGTestSupport)
884  test_support_h_files = (
885      FindHeaderFiles(os.path.join('src', 'crypto', 'test'), AllFiles) +
886      FindHeaderFiles(os.path.join('src', 'ssl', 'test'), NoTestRunnerFiles))
887
888  crypto_test_files = []
889  if EMBED_TEST_DATA:
890    # Generate crypto_test_data.cc
891    with open('crypto_test_data.cc', 'w+') as out:
892      subprocess.check_call(
893          ['go', 'run', 'util/embed_test_data.go'] + cmake['CRYPTO_TEST_DATA'],
894          cwd='src',
895          stdout=out)
896    crypto_test_files += ['crypto_test_data.cc']
897
898  crypto_test_files += FindCFiles(os.path.join('src', 'crypto'), OnlyTests)
899  crypto_test_files += [
900      'src/crypto/test/abi_test.cc',
901      'src/crypto/test/file_test_gtest.cc',
902      'src/crypto/test/gtest_main.cc',
903  ]
904  # urandom_test.cc is in a separate binary so that it can be test PRNG
905  # initialisation.
906  crypto_test_files = [
907      file for file in crypto_test_files
908      if not file.endswith('/urandom_test.cc')
909  ]
910  crypto_test_files.sort()
911
912  ssl_test_files = FindCFiles(os.path.join('src', 'ssl'), OnlyTests)
913  ssl_test_files += [
914      'src/crypto/test/abi_test.cc',
915      'src/crypto/test/gtest_main.cc',
916  ]
917  ssl_test_files.sort()
918
919  urandom_test_files = [
920      'src/crypto/fipsmodule/rand/urandom_test.cc',
921  ]
922
923  fuzz_c_files = FindCFiles(os.path.join('src', 'fuzz'), NoTests)
924
925  ssl_h_files = FindHeaderFiles(os.path.join('src', 'include', 'openssl'),
926                                SSLHeaderFiles)
927
928  def NotSSLHeaderFiles(path, filename, is_dir):
929    return not SSLHeaderFiles(path, filename, is_dir)
930  crypto_h_files = FindHeaderFiles(os.path.join('src', 'include', 'openssl'),
931                                   NotSSLHeaderFiles)
932
933  ssl_internal_h_files = FindHeaderFiles(os.path.join('src', 'ssl'), NoTests)
934  crypto_internal_h_files = (
935      FindHeaderFiles(os.path.join('src', 'crypto'), NoTests) +
936      FindHeaderFiles(os.path.join('src', 'third_party', 'fiat'), NoTests))
937
938  files = {
939      'bcm_crypto': bcm_crypto_c_files,
940      'crypto': crypto_c_files,
941      'crypto_headers': crypto_h_files,
942      'crypto_internal_headers': crypto_internal_h_files,
943      'crypto_test': crypto_test_files,
944      'crypto_test_data': sorted('src/' + x for x in cmake['CRYPTO_TEST_DATA']),
945      'fips_fragments': fips_fragments,
946      'fuzz': fuzz_c_files,
947      'ssl': ssl_source_files,
948      'ssl_headers': ssl_h_files,
949      'ssl_internal_headers': ssl_internal_h_files,
950      'ssl_test': ssl_test_files,
951      'tool': tool_c_files,
952      'tool_headers': tool_h_files,
953      'test_support': test_support_c_files,
954      'test_support_headers': test_support_h_files,
955      'urandom_test': urandom_test_files,
956  }
957
958  asm_outputs = sorted(WriteAsmFiles(ReadPerlAsmOperations()).iteritems())
959
960  for platform in platforms:
961    platform.WriteFiles(files, asm_outputs)
962
963  return 0
964
965ALL_PLATFORMS = {
966    'android': Android,
967    'android-cmake': AndroidCMake,
968    'bazel': Bazel,
969    'cmake': CMake,
970    'eureka': Eureka,
971    'gn': GN,
972    'gyp': GYP,
973    'json': JSON,
974}
975
976if __name__ == '__main__':
977  parser = optparse.OptionParser(usage='Usage: %%prog [--prefix=<path>] [%s]' %
978                                 '|'.join(sorted(ALL_PLATFORMS.keys())))
979  parser.add_option('--prefix', dest='prefix',
980      help='For Bazel, prepend argument to all source files')
981  parser.add_option(
982      '--embed_test_data', type='choice', dest='embed_test_data',
983      action='store', default="true", choices=["true", "false"],
984      help='For Bazel or GN, don\'t embed data files in crypto_test_data.cc')
985  options, args = parser.parse_args(sys.argv[1:])
986  PREFIX = options.prefix
987  EMBED_TEST_DATA = (options.embed_test_data == "true")
988
989  if not args:
990    parser.print_help()
991    sys.exit(1)
992
993  platforms = []
994  for s in args:
995    platform = ALL_PLATFORMS.get(s)
996    if platform is None:
997      parser.print_help()
998      sys.exit(1)
999    platforms.append(platform())
1000
1001  sys.exit(main(platforms))
1002