| #!/usr/bin/env python3 |
| # Copyright 2023 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import json |
| import os |
| import platform |
| import re |
| import shutil |
| import subprocess |
| import sys |
| |
| CMAKE_DEFAULTS = [ |
| '-DCMAKE_EXPORT_COMPILE_COMMANDS=ON', '-GNinja', |
| '-DLLVM_DEFAULT_TARGET_TRIPLE=wasm32-unknown-unknown', |
| '-DLLVM_ENABLE_ZLIB=OFF', '-DLLVM_ENABLE_TERMINFO=OFF', |
| '-DLLDB_ENABLE_CURSES=OFF', '-DLLDB_ENABLE_LIBXML2=OFF', |
| '-DLLDB_ENABLE_LZMA=OFF' |
| ] |
| |
| |
| def devtools_dir(source_dir): |
| return os.path.dirname(os.path.dirname(source_dir)) |
| |
| |
| def node_path(source_dir): |
| return os.path.join( |
| devtools_dir(source_dir), 'third_party', 'node', *{ |
| 'Darwin': ('mac', 'node-darwin-x64', 'bin'), |
| 'Linux': ('linux', 'node-linux-x64', 'bin'), |
| 'Windows': ('win', ), |
| }[platform.system()]) |
| |
| |
| def is_windows(): |
| return sys.platform == 'cygwin' or sys.platform.startswith('win') |
| |
| |
| def exec_extension(): |
| return ".exe" if is_windows() else "" |
| |
| |
| |
| def call(cmd, verbose=False, **kwargs): |
| if verbose: |
| sys.stderr.write("Running '{}' ({})\n".format( |
| ' '.join(cmd), |
| ', '.join('{}={}'.format(k, v) for k, v in kwargs.items()))) |
| subprocess.check_call(cmd, **kwargs) |
| |
| |
| def stage1(sysroot_dir, source_dir, OPTIONS): |
| sys.stdout.write('Building Stage 1.\n') |
| binary_dir = os.path.abspath( |
| os.path.join(OPTIONS.build_dir, 'DevTools_CXX_Debugging.stage1')) |
| if not os.path.exists(binary_dir): |
| os.makedirs(binary_dir) |
| emscripten_install_dir = os.path.join(devtools_dir(source_dir), |
| 'third_party', |
| 'emscripten-releases', 'install') |
| libcxx_dir = os.path.join(emscripten_install_dir, 'lib') |
| |
| cmake_settings = { |
| 'build_shared': 'OFF' if OPTIONS.static else 'ON', |
| 'libcxx_dir': libcxx_dir, |
| } |
| cmake_args = [ |
| OPTIONS.cmake, |
| OPTIONS.extension_source, |
| *CMAKE_DEFAULTS, |
| '-DBUILD_SHARED_LIBS={build_shared}'.format(**cmake_settings), |
| '-DCMAKE_BUILD_TYPE=Release', |
| '-DCMAKE_CXX_FLAGS=-stdlib=libc++ -pthread -I{libcxx_dir}/include/c++/v1' |
| .format(**cmake_settings), |
| '-DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++ -L{libcxx_dir} -fuse-ld=lld'. |
| format(**cmake_settings), |
| ] |
| if not OPTIONS.no_sysroot: |
| cmake_args.extend(('-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY', |
| '-DCMAKE_SYSROOT={}'.format(sysroot_dir))) |
| |
| if OPTIONS.cc: |
| cmake_args.append('-DCMAKE_C_COMPILER={}'.format(OPTIONS.cc)) |
| if OPTIONS.cxx: |
| cmake_args.append('-DCMAKE_CXX_COMPILER={}'.format(OPTIONS.cxx)) |
| |
| maybe_cmake(binary_dir, cmake_args, OPTIONS.verbose) |
| |
| ninja = os.path.join(devtools_dir(source_dir), 'third_party', 'ninja', |
| 'ninja') |
| call([ |
| ninja, 'lldb-tblgen', 'clang-tblgen', 'llvm-tblgen', 'llvm-dwp', |
| 'llvm-mc' |
| ], |
| verbose=OPTIONS.verbose, |
| cwd=binary_dir) |
| return binary_dir |
| |
| |
| def stage2(source_dir, stage1_dir, OPTIONS): |
| sys.stdout.write('Building Stage 2.\n') |
| llvm_tools_dir = os.path.abspath( |
| os.path.join(stage1_dir, 'third_party', 'llvm', 'src', 'llvm', 'bin')) |
| emcc = os.path.join(devtools_dir(source_dir), 'third_party', |
| 'emscripten-releases', 'install', 'emscripten', 'emcc') |
| wasm_ld_dir = os.path.join(devtools_dir(source_dir), 'third_party', |
| 'emscripten-releases', 'install', 'bin') |
| |
| |
| binary_dir = os.path.abspath( |
| os.path.join(OPTIONS.build_dir, 'DevTools_CXX_Debugging.stage2')) |
| if not os.path.exists(binary_dir): |
| os.makedirs(binary_dir) |
| |
| cmake_settings = { |
| 'toolchain_file': |
| os.path.join(os.path.dirname(emcc), 'cmake', 'Modules', 'Platform', |
| 'Emscripten.cmake'), |
| 'wasm_ld': |
| os.path.join(wasm_ld_dir, 'wasm-ld' + exec_extension()), |
| 'llvm_dwp': |
| os.path.join(llvm_tools_dir, 'llvm-dwp' + exec_extension()), |
| 'llvm_tblgen': |
| os.path.join(llvm_tools_dir, 'llvm-tblgen' + exec_extension()), |
| 'clang_tblgen': |
| os.path.join(llvm_tools_dir, 'clang-tblgen' + exec_extension()), |
| 'lldb_tblgen': |
| os.path.join(llvm_tools_dir, 'lldb-tblgen' + exec_extension()), |
| 'build_type': |
| _build_type(OPTIONS), |
| } |
| cmake_args = [ |
| OPTIONS.cmake, OPTIONS.extension_source, *CMAKE_DEFAULTS, |
| '-DCMAKE_CXX_FLAGS_RELWITHDEBINFO=-O1 -g -DNDEBUG', |
| '-DCMAKE_C_FLAGS_RELWITHDEBINFO=-O1 -g -DNDEBUG', |
| '-DCMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO=-O1 -g -DNDEBUG -gseparate-dwarf', |
| '-DCMAKE_CXX_FLAGS_DEBUG=-O0 -g -DNDEBUG', |
| '-DCMAKE_EXE_LINKER_FLAGS_DEBUG=-O0 -g -gseparate-dwarf', |
| '-DHAVE_POSIX_REGEX=0', '-Derrc_exit_code=0', |
| '-Derrc_exit_code__TRYRUN_OUTPUT=0', |
| '-DCMAKE_BUILD_TYPE={build_type}'.format(**cmake_settings), |
| '-DCMAKE_TOOLCHAIN_FILE={toolchain_file}'.format(**cmake_settings), |
| '-DLLVM_DWP={llvm_dwp}'.format(**cmake_settings), |
| '-DLLVM_TABLEGEN={llvm_tblgen}'.format(**cmake_settings), |
| '-DCLANG_TABLEGEN={clang_tblgen}'.format(**cmake_settings), |
| '-DLLDB_TABLEGEN={lldb_tblgen}'.format(**cmake_settings), |
| '-DWASM_LD={wasm_ld}'.format(**cmake_settings) |
| ] |
| if is_windows(): |
| cmake_args.append('-DLLVM_HOST_TRIPLE=x86_64') |
| |
| if OPTIONS.split_dwarf: |
| cmake_args.extend([ |
| '-DCXX_DEBUGGING_USE_SPLIT_DWARF=ON', |
| '-DLLVM_USE_SPLIT_DWARF=ON', |
| ]) |
| else: |
| cmake_args.extend([ |
| '-DCXX_DEBUGGING_USE_SPLIT_DWARF=OFF', |
| '-DLLVM_USE_SPLIT_DWARF=OFF', |
| ]) |
| |
| if OPTIONS.gdwarf_5: |
| cmake_args.extend(['-DCXX_DEBUGGING_ENABLE_DWARF5=ON']) |
| else: |
| cmake_args.extend(['-DCXX_DEBUGGING_ENABLE_DWARF5=OFF']) |
| |
| if OPTIONS.pubnames: |
| cmake_args.extend(['-DCXX_DEBUGGING_ENABLE_PUBNAMES=ON']) |
| else: |
| cmake_args.extend(['-DCXX_DEBUGGING_ENABLE_PUBNAMES=OFF']) |
| |
| if OPTIONS.skip_dwp: |
| cmake_args.extend(['-DCXX_DEBUGGING_DWO_ONLY=ON']) |
| else: |
| cmake_args.extend(['-DCXX_DEBUGGING_DWO_ONLY=OFF']) |
| |
| if OPTIONS.release_version or OPTIONS.release: |
| cmake_args.extend([ |
| '-DCXX_DEBUGGING_BUILD_REVISION={0}'.format(OPTIONS.release_version |
| or 0) |
| ]) |
| cmake_args.extend([ |
| '-DCXX_DEBUGGING_BUILD_PATCH={0}'.format(OPTIONS.patch_level or 0) |
| ]) |
| |
| if OPTIONS.sanitize: |
| cmake_args.append('-DCXX_DEBUGGING_USE_SANITIZERS=ON') |
| else: |
| cmake_args.append('-DCXX_DEBUGGING_USE_SANITIZERS=OFF') |
| |
| maybe_cmake(binary_dir, cmake_args, OPTIONS.verbose) |
| |
| num_cores = os.cpu_count() |
| env = os.environ.copy() |
| |
| ninja = os.path.join(devtools_dir(source_dir), 'third_party', 'ninja', |
| 'ninja') |
| if not OPTIONS.no_check: |
| call([ninja, '-j%d' % num_cores, 'all', 'check-extension'], |
| verbose=OPTIONS.verbose, |
| cwd=binary_dir, |
| env=env) |
| else: |
| call([ninja, '-j%d' % num_cores, 'all'], |
| verbose=OPTIONS.verbose, |
| cwd=binary_dir, |
| env=env) |
| return binary_dir |
| |
| |
| def maybe_cmake(binary_dir, cmake_args, verbose): |
| # Re-run `cmake` when the build.ninja doesn't exist, or when cmake args have changed |
| build_ninja = os.path.abspath(os.path.join(binary_dir, 'build.ninja')) |
| cmake_args_file = os.path.abspath( |
| os.path.join(binary_dir, 'cmake.args.json')) |
| if os.path.exists(build_ninja) and os.path.exists(cmake_args_file): |
| with open(cmake_args_file, 'r') as f: |
| try: |
| prev_args = json.load(f) |
| if prev_args == cmake_args: |
| # Nothing changed; cmake unnecessary |
| return |
| except json.JSONDecodeError: |
| pass # Ignore invalid json file |
| sys.stdout.write('cmake args have changed.\n') |
| call(cmake_args, verbose=verbose, cwd=binary_dir) |
| with open(cmake_args_file, 'w') as f: |
| json.dump(cmake_args, f) |
| |
| |
| def _build_type(options): |
| if options.release or options.release_version: |
| return 'Release' |
| elif options.debug: |
| return 'Debug' |
| else: |
| return 'RelWithDebInfo' |
| |
| |
| def script_main(args): |
| source_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| repo_dir = os.path.dirname(os.path.dirname(source_dir)) |
| third_party = os.path.join(repo_dir, 'third_party') |
| clang_dir = os.path.join(third_party, 'emscripten-releases', 'install', |
| 'bin') |
| cmake_dir = os.path.join(third_party, 'cmake', 'bin') |
| sysroot_dir = find_sysroot(repo_dir) |
| |
| parser = argparse.ArgumentParser( |
| formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
| parser.add_argument('-cmake', |
| default=shutil.which('cmake', path=cmake_dir), |
| help='Path to the cmake configure tool.') |
| parser.add_argument('-cc', |
| default=shutil.which('clang', path=clang_dir), |
| help='The C compiler.') |
| parser.add_argument('-cxx', |
| default=shutil.which('clang++', path=clang_dir), |
| help='The C++ compiler.') |
| parser.add_argument('-extension-source', |
| default=source_dir, |
| help='Path to alternate repo for source.') |
| parser.add_argument('-verbose', action='store_true') |
| parser.add_argument('-stage1', help='Path to a pre-built stage 1') |
| parser.add_argument('-static', |
| action='store_true', |
| default=True, |
| help='Link the first stage statically.') |
| parser.add_argument('-dynamic', |
| action='store_false', |
| default=True, |
| dest='static', |
| help='Link the first stage dynamically.') |
| parser.add_argument('-no-sysroot', |
| action='store_true', |
| help='Disable sysroot.') |
| parser.add_argument('-no-check', |
| action='store_true', |
| help='Skip running tests.') |
| parser.add_argument('-infra', |
| action='store_true', |
| help='Configure the build for the buildbots.') |
| parser.add_argument('-debug', |
| action='store_true', |
| help='Build a debug version.') |
| parser.add_argument( |
| '-release', |
| action='store_true', |
| help='Build a release instead of a debug version. (deprecated)') |
| parser.add_argument( |
| '-release-version', |
| type=int, |
| default=None, |
| help='Provide a version number for building a release,' |
| 'instead of building a debug version. (deprecates -release)') |
| parser.add_argument( |
| '-patch-level', |
| type=int, |
| default=0, |
| help='Provide a version patch level for building a release,' |
| 'instead of building a debug version. (deprecates -release)') |
| parser.add_argument('-split-dwarf', |
| action='store_true', |
| help='Build with split-dwarf support.') |
| parser.add_argument('-pubnames', |
| action='store_true', |
| help='Build with split-dwarf support.') |
| parser.add_argument('-gdwarf-5', |
| action='store_true', |
| help='Build with DWARF5 debug info.') |
| parser.add_argument( |
| '-skip-dwp', |
| action='store_true', |
| help='In combination with -split-dwarf, builds dwos but not the dwp.') |
| parser.add_argument('-sanitize', |
| action='store_true', |
| help='Enable sanitizers') |
| parser.add_argument('build_dir') |
| OPTIONS = parser.parse_args(args) |
| |
| if OPTIONS.infra: |
| OPTIONS.static = True |
| OPTIONS.verbose = True |
| if OPTIONS.no_check: |
| sys.stderr.write('-infra overrides -no-check') |
| OPTIONS.no_check = False |
| if OPTIONS.no_sysroot: |
| sys.stderr.write('-infra overrides -no-sysroot') |
| OPTIONS.no_sysroot = False |
| |
| if OPTIONS.stage1: |
| stage1_dir = OPTIONS.stage1 |
| else: |
| stage1_dir = stage1(sysroot_dir, source_dir, OPTIONS) |
| stage2(source_dir, stage1_dir, OPTIONS) |
| |
| |
| def find_sysroot(repo_dir): |
| build_linux = os.path.join(repo_dir, 'build', 'linux') |
| sysroots = [ |
| os.path.join(build_linux, f) for f in os.listdir(build_linux) |
| if os.path.isdir(os.path.join(build_linux, f)) |
| and re.match('debian_.*_amd64-sysroot', f) |
| ] |
| assert len(sysroots) >= 1, 'No sysroot found!' |
| assert len(sysroots) <= 1, 'Too many sysroots found!' |
| return sysroots[0] |
| |
| |
| if __name__ == '__main__': |
| script_main(sys.argv[1:]) |