0
0
mirror of https://github.com/signalapp/libsignal.git synced 2024-09-19 19:42:19 +02:00
libsignal/node/build_node_bridge.py
Jordan Rose a8814d16f8 Move all "testing-fns" APIs into libsignal-bridge-testing
This means they'll be shipped to clients, but still filtered out of
the device builds on Android (via having two separate libraries on
disk) and iOS (by not being included in non-simulator builds). The
biggest benefit of this is dropping the :android:makeTestJniLibraries
step for running libsignal's Android tests.
2024-08-27 08:29:06 -07:00

212 lines
8.4 KiB
Python
Executable File

#!/usr/bin/env python3
#
# Copyright (C) 2021 Signal Messenger, LLC.
# SPDX-License-Identifier: AGPL-3.0-only
#
import hashlib
import optparse
import os
import shlex
import shutil
import subprocess
import sys
from typing import List, Optional
def maybe_dump_debug_symbols(*, src_path: str, src_checksum_path: str, dst_path: str, dst_checksum_path: str) -> None:
dump_syms = shutil.which('dump_syms')
if not dump_syms:
print("note: dump_syms not installed; skipping debug info processing")
return
with open(src_checksum_path, 'rb') as f:
digest = hashlib.sha256()
# Use read1 to use the file object's buffering.
# We don't want to load the entire input in memory if we can help it.
while next := f.read1():
digest.update(next)
checksum = digest.hexdigest()
if os.path.exists(dst_checksum_path):
with open(dst_checksum_path, 'r') as f:
if f.read() == checksum:
print("Debug info did not change")
return
with open(dst_checksum_path, 'w') as f:
f.write(checksum)
print("Dumping debug symbols to %s" % dst_path)
subprocess.check_call([dump_syms, src_path, '-o', dst_path])
def main(args: Optional[List[str]] = None) -> int:
if args is None:
args = sys.argv
if sys.platform == 'win32':
args = shlex.split(' '.join(args), posix=0)
print("Invoked with '%s'" % (' '.join(args)))
parser = optparse.OptionParser()
parser.add_option('--out-dir', '-o', default=None, metavar='DIR',
help='specify destination dir (default build/$CONFIGURATION_NAME)')
parser.add_option('--configuration', default='Release', metavar='C',
help='specify build configuration (Release or Debug)')
parser.add_option('--os-name', default=None, metavar='OS',
help='specify Node OS name')
parser.add_option('--cargo-build-dir', default='target', metavar='PATH',
help='specify cargo build dir (default %default)')
parser.add_option('--cargo-target', default=None,
help='specify cargo target')
parser.add_option('--node-arch', default=None,
help='specify node arch (x64, ia32, arm64)')
(options, args) = parser.parse_args(args)
configuration_name = options.configuration.strip('"')
if configuration_name is None:
print('ERROR: --configuration is required')
return 1
elif configuration_name not in ['Release', 'Debug']:
print("ERROR: Unknown value for --configuration '%s'" % (configuration_name))
return 1
node_os_name = options.os_name
if node_os_name is None:
print('ERROR: --os-name is required')
return 1
cargo_target = options.cargo_target
if cargo_target is None:
print('ERROR: --cargo-target is required')
return 1
node_arch = options.node_arch
if node_arch is None:
print('ERROR: --node_arch is required')
return 1
out_dir = options.out_dir.strip('"') or os.path.join('build', configuration_name)
features = []
if 'npm_config_libsignal_debug_level_logs' not in os.environ:
features.append('log/release_max_level_info')
cmdline = ['cargo', 'build', '--target', cargo_target, '-p', 'libsignal-node', '--features', ','.join(features)]
if configuration_name == 'Release':
cmdline.append('--release')
print("Running '%s'" % (' '.join(cmdline)))
cargo_env = os.environ.copy()
cargo_env['RUSTFLAGS'] = cargo_env.get('RUSTFLAGS') or ''
cargo_env['CARGO_BUILD_TARGET_DIR'] = options.cargo_build_dir
cargo_env['MACOSX_DEPLOYMENT_TARGET'] = '10.13'
# Build with debug line tables, but not full debug info.
cargo_env['CARGO_PROFILE_RELEASE_DEBUG'] = '1'
# On Linux, cdylibs don't include public symbols from their dependencies,
# even if those symbols have been re-exported in the Rust source.
# Using LTO works around this at the cost of a slightly slower build.
# https://github.com/rust-lang/rfcs/issues/2771
cargo_env['CARGO_PROFILE_RELEASE_LTO'] = 'thin'
# Enable ARMv8 cryptography acceleration when available
cargo_env['RUSTFLAGS'] += ' --cfg aes_armv8 --cfg polyval_armv8'
# If set (below), will post-process the build library using this instead of just `cp`-ing it.
objcopy = None
if node_os_name == 'win32':
# By default, Rust on Windows depends on an MSVC component for the C runtime.
# Link it statically to avoid propagating that dependency.
cargo_env['RUSTFLAGS'] += ' -C target-feature=+crt-static'
# Save the debug info in PDB format...
cargo_env['CARGO_PROFILE_RELEASE_SPLIT_DEBUGINFO'] = 'packed'
# ...and DLLs don't have anything to strip.
# (If you turn on stripping the PDB doesn't get generated at all.)
lib_format = '{}.dll'
debug_format = '{}.pdb'
debug_format_for_checksum = debug_format
abs_build_dir = os.path.abspath(options.cargo_build_dir)
if 'GITHUB_WORKSPACE' in cargo_env:
# Avoid long build directory paths on GitHub Actions,
# breaking the old Win32 limit of 260 characters.
# (https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation)
# We don't do this everywhere because it breaks cleaning.
#
# In the long run, Visual Studio's CLI tools will become long-path aware and this should
# become unnecessary.
# It would be nice if using extended-length path syntax `\\?\` was sufficient,
# but that also isn't accepted by all of Visual Studio's CLI tools.
tmpdir = cargo_env['RUNNER_TEMP']
if len(tmpdir) < len(abs_build_dir):
cargo_env['CARGO_BUILD_TARGET_DIR'] = os.path.join(tmpdir, "libsignal")
elif node_os_name == 'darwin':
# Save the debug info in dSYM format...
cargo_env['CARGO_PROFILE_RELEASE_SPLIT_DEBUGINFO'] = 'packed'
# ...then have Rust strip the library.
cargo_env['CARGO_PROFILE_RELEASE_STRIP'] = 'symbols'
lib_format = 'lib{0}.dylib'
debug_format = 'lib{0}.dylib.dSYM'
# The dSYM format is a directory, not a single file.
# We use the single file that contains the DWARF information for our checksum,
# since our primary purpose for debug info is symbolicating crashdumps,
# which uses the line tables stored as DWARF.
debug_format_for_checksum = os.path.join(debug_format, 'Contents', 'Resources', 'DWARF', lib_format)
# macOS has a nice place for us to stash our version number.
if 'npm_package_version' in cargo_env:
cargo_env['RUSTFLAGS'] += ' -Clink-arg=-Wl,-current_version,%s' % cargo_env['npm_package_version']
else:
# Assume Linux-like.
# DWP files don't seem ready for everyday use.
# We'll just save the whole unstripped binary.
lib_format = 'lib{}.so'
debug_format = 'lib{}.so'
debug_format_for_checksum = debug_format
objcopy = shutil.which('%s-linux-gnu-objcopy' % cargo_target.split('-')[0]) or 'objcopy'
print("with environment: %s" % (' '.join("%s=%s" % (k, v) for (k, v) in cargo_env.items())))
subprocess.check_call(cmdline, env=cargo_env)
libs_in = os.path.join(cargo_env['CARGO_BUILD_TARGET_DIR'],
cargo_target,
configuration_name.lower())
src_path = os.path.join(libs_in, lib_format.format('signal_node'))
if os.access(src_path, os.R_OK):
dst_base = 'libsignal_client_%s_%s' % (node_os_name, node_arch)
dst_path = os.path.join(out_dir, dst_base + '.node')
print("Copying %s to %s" % (src_path, dst_path))
if not os.path.exists(out_dir):
os.makedirs(out_dir)
if objcopy:
subprocess.check_call([objcopy, '-S', src_path, dst_path])
else:
shutil.copyfile(src_path, dst_path)
maybe_dump_debug_symbols(
src_path=os.path.join(libs_in, debug_format.format('signal_node')),
src_checksum_path=os.path.join(libs_in, debug_format_for_checksum.format('signal_node')),
dst_path=os.path.join(out_dir, dst_base + '-debuginfo.sym'),
dst_checksum_path=os.path.join(out_dir, dst_base + '-debuginfo.sha256'),
)
else:
print("ERROR: did not find generated library")
return 1
return 0
if __name__ == '__main__':
sys.exit(main())