• 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.
28OS_ARCH_COMBOS = [
29    ('apple', 'arm', 'ios32', [], 'S'),
30    ('apple', 'aarch64', 'ios64', [], 'S'),
31    ('apple', 'x86', 'macosx', ['-fPIC', '-DOPENSSL_IA32_SSE2'], 'S'),
32    ('apple', 'x86_64', 'macosx', [], 'S'),
33    ('linux', 'arm', 'linux32', [], 'S'),
34    ('linux', 'aarch64', 'linux64', [], 'S'),
35    ('linux', 'ppc64le', 'linux64le', [], 'S'),
36    ('linux', 'x86', 'elf', ['-fPIC', '-DOPENSSL_IA32_SSE2'], 'S'),
37    ('linux', 'x86_64', 'elf', [], '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.5)
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++14 -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 -std=c11")
446endif()
447
448# pthread_rwlock_t requires a feature flag.
449if(NOT WIN32)
450  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_XOPEN_SOURCE=700")
451endif()
452
453if(WIN32)
454  add_definitions(-D_HAS_EXCEPTIONS=0)
455  add_definitions(-DWIN32_LEAN_AND_MEAN)
456  add_definitions(-DNOMINMAX)
457  # Allow use of fopen.
458  add_definitions(-D_CRT_SECURE_NO_WARNINGS)
459  # VS 2017 and higher supports STL-only warning suppressions.
460  # A bug in CMake < 3.13.0 may cause the space in this value to
461  # cause issues when building with NASM. In that case, update CMake.
462  add_definitions("-D_STL_EXTRA_DISABLED_WARNINGS=4774 4987")
463endif()
464
465add_definitions(-DBORINGSSL_IMPLEMENTATION)
466
467# CMake's iOS support uses Apple's multiple-architecture toolchain. It takes an
468# architecture list from CMAKE_OSX_ARCHITECTURES, leaves CMAKE_SYSTEM_PROCESSOR
469# alone, and expects all architecture-specific logic to be conditioned within
470# the source files rather than the build. This does not work for our assembly
471# files, so we fix CMAKE_SYSTEM_PROCESSOR and only support single-architecture
472# builds.
473if(NOT OPENSSL_NO_ASM AND CMAKE_OSX_ARCHITECTURES)
474  list(LENGTH CMAKE_OSX_ARCHITECTURES NUM_ARCHES)
475  if(NOT NUM_ARCHES EQUAL 1)
476    message(FATAL_ERROR "Universal binaries not supported.")
477  endif()
478  list(GET CMAKE_OSX_ARCHITECTURES 0 CMAKE_SYSTEM_PROCESSOR)
479endif()
480
481if(OPENSSL_NO_ASM)
482  add_definitions(-DOPENSSL_NO_ASM)
483  set(ARCH "generic")
484elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
485  set(ARCH "x86_64")
486elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64")
487  set(ARCH "x86_64")
488elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")
489  # cmake reports AMD64 on Windows, but we might be building for 32-bit.
490  if(CMAKE_SIZEOF_VOID_P EQUAL 8)
491    set(ARCH "x86_64")
492  else()
493    set(ARCH "x86")
494  endif()
495elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86")
496  set(ARCH "x86")
497elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i386")
498  set(ARCH "x86")
499elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i686")
500  set(ARCH "x86")
501elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
502  set(ARCH "aarch64")
503elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
504  set(ARCH "aarch64")
505# Apple A12 Bionic chipset which is added in iPhone XS/XS Max/XR uses arm64e architecture.
506elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64e")
507  set(ARCH "aarch64")
508elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm*")
509  set(ARCH "arm")
510elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "mips")
511  # Just to avoid the “unknown processor” error.
512  set(ARCH "generic")
513elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le")
514  set(ARCH "ppc64le")
515else()
516  message(FATAL_ERROR "Unknown processor:" ${CMAKE_SYSTEM_PROCESSOR})
517endif()
518
519if(NOT OPENSSL_NO_ASM)
520  if(UNIX)
521    enable_language(ASM)
522
523    # Clang's integerated assembler does not support debug symbols.
524    if(NOT CMAKE_ASM_COMPILER_ID MATCHES "Clang")
525      set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wa,-g")
526    endif()
527
528    # CMake does not add -isysroot and -arch flags to assembly.
529    if(APPLE)
530      if(CMAKE_OSX_SYSROOT)
531        set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -isysroot \"${CMAKE_OSX_SYSROOT}\"")
532      endif()
533      foreach(arch ${CMAKE_OSX_ARCHITECTURES})
534        set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -arch ${arch}")
535      endforeach()
536    endif()
537  else()
538    set(CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS} -gcv8")
539    enable_language(ASM_NASM)
540  endif()
541endif()
542
543if(BUILD_SHARED_LIBS)
544  add_definitions(-DBORINGSSL_SHARED_LIBRARY)
545  # Enable position-independent code globally. This is needed because
546  # some library targets are OBJECT libraries.
547  set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
548endif()
549
550include_directories(src/include)
551
552'''
553
554  def PrintLibrary(self, out, name, files):
555    out.write('add_library(\n')
556    out.write('  %s\n\n' % name)
557
558    for f in sorted(files):
559      out.write('  %s\n' % PathOf(f))
560
561    out.write(')\n\n')
562
563  def PrintExe(self, out, name, files, libs):
564    out.write('add_executable(\n')
565    out.write('  %s\n\n' % name)
566
567    for f in sorted(files):
568      out.write('  %s\n' % PathOf(f))
569
570    out.write(')\n\n')
571    out.write('target_link_libraries(%s %s)\n\n' % (name, ' '.join(libs)))
572
573  def PrintSection(self, out, name, files):
574    out.write('set(\n')
575    out.write('  %s\n\n' % name)
576    for f in sorted(files):
577      out.write('  %s\n' % PathOf(f))
578    out.write(')\n\n')
579
580  def WriteFiles(self, files, asm_outputs):
581    with open('CMakeLists.txt', 'w+') as cmake:
582      cmake.write(self.header)
583
584      for ((osname, arch), asm_files) in asm_outputs:
585        self.PrintSection(cmake, 'CRYPTO_%s_%s_SOURCES' % (osname, arch),
586            asm_files)
587
588      cmake.write(
589R'''if(APPLE)
590  set(CRYPTO_ARCH_SOURCES ${CRYPTO_apple_${ARCH}_SOURCES})
591elseif(UNIX)
592  set(CRYPTO_ARCH_SOURCES ${CRYPTO_linux_${ARCH}_SOURCES})
593elseif(WIN32)
594  set(CRYPTO_ARCH_SOURCES ${CRYPTO_win_${ARCH}_SOURCES})
595endif()
596
597''')
598
599      self.PrintLibrary(cmake, 'crypto',
600          files['crypto'] + ['${CRYPTO_ARCH_SOURCES}'])
601      self.PrintLibrary(cmake, 'ssl', files['ssl'])
602      self.PrintExe(cmake, 'bssl', files['tool'], ['ssl', 'crypto'])
603
604      cmake.write(
605R'''if(NOT WIN32 AND NOT ANDROID)
606  target_link_libraries(crypto pthread)
607endif()
608
609if(WIN32)
610  target_link_libraries(bssl ws2_32)
611endif()
612
613''')
614
615class JSON(object):
616  def WriteFiles(self, files, asm_outputs):
617    sources = dict(files)
618    for ((osname, arch), asm_files) in asm_outputs:
619      sources['crypto_%s_%s' % (osname, arch)] = asm_files
620    with open('sources.json', 'w+') as f:
621      json.dump(sources, f, sort_keys=True, indent=2)
622
623def FindCMakeFiles(directory):
624  """Returns list of all CMakeLists.txt files recursively in directory."""
625  cmakefiles = []
626
627  for (path, _, filenames) in os.walk(directory):
628    for filename in filenames:
629      if filename == 'CMakeLists.txt':
630        cmakefiles.append(os.path.join(path, filename))
631
632  return cmakefiles
633
634def OnlyFIPSFragments(path, dent, is_dir):
635  return is_dir or (path.startswith(
636      os.path.join('src', 'crypto', 'fipsmodule', '')) and
637      NoTests(path, dent, is_dir))
638
639def NoTestsNorFIPSFragments(path, dent, is_dir):
640  return (NoTests(path, dent, is_dir) and
641      (is_dir or not OnlyFIPSFragments(path, dent, is_dir)))
642
643def NoTests(path, dent, is_dir):
644  """Filter function that can be passed to FindCFiles in order to remove test
645  sources."""
646  if is_dir:
647    return dent != 'test'
648  return 'test.' not in dent
649
650
651def OnlyTests(path, dent, is_dir):
652  """Filter function that can be passed to FindCFiles in order to remove
653  non-test sources."""
654  if is_dir:
655    return dent != 'test'
656  return '_test.' in dent
657
658
659def AllFiles(path, dent, is_dir):
660  """Filter function that can be passed to FindCFiles in order to include all
661  sources."""
662  return True
663
664
665def NoTestRunnerFiles(path, dent, is_dir):
666  """Filter function that can be passed to FindCFiles or FindHeaderFiles in
667  order to exclude test runner files."""
668  # NOTE(martinkr): This prevents .h/.cc files in src/ssl/test/runner, which
669  # are in their own subpackage, from being included in boringssl/BUILD files.
670  return not is_dir or dent != 'runner'
671
672
673def NotGTestSupport(path, dent, is_dir):
674  return 'gtest' not in dent and 'abi_test' not in dent
675
676
677def SSLHeaderFiles(path, dent, is_dir):
678  return dent in ['ssl.h', 'tls1.h', 'ssl23.h', 'ssl3.h', 'dtls1.h', 'srtp.h']
679
680
681def FindCFiles(directory, filter_func):
682  """Recurses through directory and returns a list of paths to all the C source
683  files that pass filter_func."""
684  cfiles = []
685
686  for (path, dirnames, filenames) in os.walk(directory):
687    for filename in filenames:
688      if not filename.endswith('.c') and not filename.endswith('.cc'):
689        continue
690      if not filter_func(path, filename, False):
691        continue
692      cfiles.append(os.path.join(path, filename))
693
694    for (i, dirname) in enumerate(dirnames):
695      if not filter_func(path, dirname, True):
696        del dirnames[i]
697
698  cfiles.sort()
699  return cfiles
700
701
702def FindHeaderFiles(directory, filter_func):
703  """Recurses through directory and returns a list of paths to all the header files that pass filter_func."""
704  hfiles = []
705
706  for (path, dirnames, filenames) in os.walk(directory):
707    for filename in filenames:
708      if not filename.endswith('.h'):
709        continue
710      if not filter_func(path, filename, False):
711        continue
712      hfiles.append(os.path.join(path, filename))
713
714      for (i, dirname) in enumerate(dirnames):
715        if not filter_func(path, dirname, True):
716          del dirnames[i]
717
718  hfiles.sort()
719  return hfiles
720
721
722def ExtractPerlAsmFromCMakeFile(cmakefile):
723  """Parses the contents of the CMakeLists.txt file passed as an argument and
724  returns a list of all the perlasm() directives found in the file."""
725  perlasms = []
726  with open(cmakefile) as f:
727    for line in f:
728      line = line.strip()
729      if not line.startswith('perlasm('):
730        continue
731      if not line.endswith(')'):
732        raise ValueError('Bad perlasm line in %s' % cmakefile)
733      # Remove "perlasm(" from start and ")" from end
734      params = line[8:-1].split()
735      if len(params) < 2:
736        raise ValueError('Bad perlasm line in %s' % cmakefile)
737      perlasms.append({
738          'extra_args': params[2:],
739          'input': os.path.join(os.path.dirname(cmakefile), params[1]),
740          'output': os.path.join(os.path.dirname(cmakefile), params[0]),
741      })
742
743  return perlasms
744
745
746def ReadPerlAsmOperations():
747  """Returns a list of all perlasm() directives found in CMake config files in
748  src/."""
749  perlasms = []
750  cmakefiles = FindCMakeFiles('src')
751
752  for cmakefile in cmakefiles:
753    perlasms.extend(ExtractPerlAsmFromCMakeFile(cmakefile))
754
755  return perlasms
756
757
758def PerlAsm(output_filename, input_filename, perlasm_style, extra_args):
759  """Runs the a perlasm script and puts the output into output_filename."""
760  base_dir = os.path.dirname(output_filename)
761  if not os.path.isdir(base_dir):
762    os.makedirs(base_dir)
763  subprocess.check_call(
764      ['perl', input_filename, perlasm_style] + extra_args + [output_filename])
765
766
767def ArchForAsmFilename(filename):
768  """Returns the architectures that a given asm file should be compiled for
769  based on substrings in the filename."""
770
771  if 'x86_64' in filename or 'avx2' in filename:
772    return ['x86_64']
773  elif ('x86' in filename and 'x86_64' not in filename) or '586' in filename:
774    return ['x86']
775  elif 'armx' in filename:
776    return ['arm', 'aarch64']
777  elif 'armv8' in filename:
778    return ['aarch64']
779  elif 'arm' in filename:
780    return ['arm']
781  elif 'ppc' in filename:
782    return ['ppc64le']
783  else:
784    raise ValueError('Unknown arch for asm filename: ' + filename)
785
786
787def WriteAsmFiles(perlasms):
788  """Generates asm files from perlasm directives for each supported OS x
789  platform combination."""
790  asmfiles = {}
791
792  for osarch in OS_ARCH_COMBOS:
793    (osname, arch, perlasm_style, extra_args, asm_ext) = osarch
794    key = (osname, arch)
795    outDir = '%s-%s' % key
796
797    for perlasm in perlasms:
798      filename = os.path.basename(perlasm['input'])
799      output = perlasm['output']
800      if not output.startswith('src'):
801        raise ValueError('output missing src: %s' % output)
802      output = os.path.join(outDir, output[4:])
803      if output.endswith('-armx.${ASM_EXT}'):
804        output = output.replace('-armx',
805                                '-armx64' if arch == 'aarch64' else '-armx32')
806      output = output.replace('${ASM_EXT}', asm_ext)
807
808      if arch in ArchForAsmFilename(filename):
809        PerlAsm(output, perlasm['input'], perlasm_style,
810                perlasm['extra_args'] + extra_args)
811        asmfiles.setdefault(key, []).append(output)
812
813  for (key, non_perl_asm_files) in NON_PERL_FILES.items():
814    asmfiles.setdefault(key, []).extend(non_perl_asm_files)
815
816  for files in asmfiles.values():
817    files.sort()
818
819  return asmfiles
820
821
822def ExtractVariablesFromCMakeFile(cmakefile):
823  """Parses the contents of the CMakeLists.txt file passed as an argument and
824  returns a dictionary of exported source lists."""
825  variables = {}
826  in_set_command = False
827  set_command = []
828  with open(cmakefile) as f:
829    for line in f:
830      if '#' in line:
831        line = line[:line.index('#')]
832      line = line.strip()
833
834      if not in_set_command:
835        if line.startswith('set('):
836          in_set_command = True
837          set_command = []
838      elif line == ')':
839        in_set_command = False
840        if not set_command:
841          raise ValueError('Empty set command')
842        variables[set_command[0]] = set_command[1:]
843      else:
844        set_command.extend([c for c in line.split(' ') if c])
845
846  if in_set_command:
847    raise ValueError('Unfinished set command')
848  return variables
849
850
851def main(platforms):
852  cmake = ExtractVariablesFromCMakeFile(os.path.join('src', 'sources.cmake'))
853  crypto_c_files = (FindCFiles(os.path.join('src', 'crypto'), NoTestsNorFIPSFragments) +
854                    FindCFiles(os.path.join('src', 'third_party', 'fiat'), NoTestsNorFIPSFragments))
855  fips_fragments = FindCFiles(os.path.join('src', 'crypto', 'fipsmodule'), OnlyFIPSFragments)
856  ssl_source_files = FindCFiles(os.path.join('src', 'ssl'), NoTests)
857  tool_c_files = FindCFiles(os.path.join('src', 'tool'), NoTests)
858  tool_h_files = FindHeaderFiles(os.path.join('src', 'tool'), AllFiles)
859
860  # BCM shared library C files
861  bcm_crypto_c_files = [
862      os.path.join('src', 'crypto', 'fipsmodule', 'bcm.c')
863  ]
864
865  # Generate err_data.c
866  with open('err_data.c', 'w+') as err_data:
867    subprocess.check_call(['go', 'run', 'err_data_generate.go'],
868                          cwd=os.path.join('src', 'crypto', 'err'),
869                          stdout=err_data)
870  crypto_c_files.append('err_data.c')
871  crypto_c_files.sort()
872
873  test_support_c_files = FindCFiles(os.path.join('src', 'crypto', 'test'),
874                                    NotGTestSupport)
875  test_support_h_files = (
876      FindHeaderFiles(os.path.join('src', 'crypto', 'test'), AllFiles) +
877      FindHeaderFiles(os.path.join('src', 'ssl', 'test'), NoTestRunnerFiles))
878
879  crypto_test_files = []
880  if EMBED_TEST_DATA:
881    # Generate crypto_test_data.cc
882    with open('crypto_test_data.cc', 'w+') as out:
883      subprocess.check_call(
884          ['go', 'run', 'util/embed_test_data.go'] + cmake['CRYPTO_TEST_DATA'],
885          cwd='src',
886          stdout=out)
887    crypto_test_files += ['crypto_test_data.cc']
888
889  crypto_test_files += FindCFiles(os.path.join('src', 'crypto'), OnlyTests)
890  crypto_test_files += [
891      'src/crypto/test/abi_test.cc',
892      'src/crypto/test/file_test_gtest.cc',
893      'src/crypto/test/gtest_main.cc',
894  ]
895  # urandom_test.cc is in a separate binary so that it can be test PRNG
896  # initialisation.
897  crypto_test_files = [
898      file for file in crypto_test_files
899      if not file.endswith('/urandom_test.cc')
900  ]
901  crypto_test_files.sort()
902
903  ssl_test_files = FindCFiles(os.path.join('src', 'ssl'), OnlyTests)
904  ssl_test_files += [
905      'src/crypto/test/abi_test.cc',
906      'src/crypto/test/gtest_main.cc',
907  ]
908  ssl_test_files.sort()
909
910  urandom_test_files = [
911      'src/crypto/fipsmodule/rand/urandom_test.cc',
912  ]
913
914  fuzz_c_files = FindCFiles(os.path.join('src', 'fuzz'), NoTests)
915
916  ssl_h_files = FindHeaderFiles(os.path.join('src', 'include', 'openssl'),
917                                SSLHeaderFiles)
918
919  def NotSSLHeaderFiles(path, filename, is_dir):
920    return not SSLHeaderFiles(path, filename, is_dir)
921  crypto_h_files = FindHeaderFiles(os.path.join('src', 'include', 'openssl'),
922                                   NotSSLHeaderFiles)
923
924  ssl_internal_h_files = FindHeaderFiles(os.path.join('src', 'ssl'), NoTests)
925  crypto_internal_h_files = (
926      FindHeaderFiles(os.path.join('src', 'crypto'), NoTests) +
927      FindHeaderFiles(os.path.join('src', 'third_party', 'fiat'), NoTests))
928
929  files = {
930      'bcm_crypto': bcm_crypto_c_files,
931      'crypto': crypto_c_files,
932      'crypto_headers': crypto_h_files,
933      'crypto_internal_headers': crypto_internal_h_files,
934      'crypto_test': crypto_test_files,
935      'crypto_test_data': sorted('src/' + x for x in cmake['CRYPTO_TEST_DATA']),
936      'fips_fragments': fips_fragments,
937      'fuzz': fuzz_c_files,
938      'ssl': ssl_source_files,
939      'ssl_headers': ssl_h_files,
940      'ssl_internal_headers': ssl_internal_h_files,
941      'ssl_test': ssl_test_files,
942      'tool': tool_c_files,
943      'tool_headers': tool_h_files,
944      'test_support': test_support_c_files,
945      'test_support_headers': test_support_h_files,
946      'urandom_test': urandom_test_files,
947  }
948
949  asm_outputs = sorted(WriteAsmFiles(ReadPerlAsmOperations()).items())
950
951  for platform in platforms:
952    platform.WriteFiles(files, asm_outputs)
953
954  return 0
955
956ALL_PLATFORMS = {
957    'android': Android,
958    'android-cmake': AndroidCMake,
959    'bazel': Bazel,
960    'cmake': CMake,
961    'eureka': Eureka,
962    'gn': GN,
963    'gyp': GYP,
964    'json': JSON,
965}
966
967if __name__ == '__main__':
968  parser = optparse.OptionParser(usage='Usage: %%prog [--prefix=<path>] [%s]' %
969                                 '|'.join(sorted(ALL_PLATFORMS.keys())))
970  parser.add_option('--prefix', dest='prefix',
971      help='For Bazel, prepend argument to all source files')
972  parser.add_option(
973      '--embed_test_data', type='choice', dest='embed_test_data',
974      action='store', default="true", choices=["true", "false"],
975      help='For Bazel or GN, don\'t embed data files in crypto_test_data.cc')
976  options, args = parser.parse_args(sys.argv[1:])
977  PREFIX = options.prefix
978  EMBED_TEST_DATA = (options.embed_test_data == "true")
979
980  if not args:
981    parser.print_help()
982    sys.exit(1)
983
984  platforms = []
985  for s in args:
986    platform = ALL_PLATFORMS.get(s)
987    if platform is None:
988      parser.print_help()
989      sys.exit(1)
990    platforms.append(platform())
991
992  sys.exit(main(platforms))
993