Skip to content

Commit

Permalink
binding.gyp with build script (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
1Conan authored Mar 16, 2022
1 parent 7ee1de8 commit b157a69
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = ["rumblefrog <[email protected]>"]
edition = "2018"

[lib]
crate-type = ["cdylib"]
crate-type = ["cdylib", "staticlib"]

[dependencies]
log = "0.4"
Expand Down
31 changes: 31 additions & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
'variables': {
'simulator%': '0',
},
'targets': [
{
'target_name': 'rf.node',
'type': 'none',
'actions': [
{
'action_name': 'build.py',
'action': [
'python3',
'build.py',
'--os=<(OS)',
'--arch=<(target_arch)',
'--simulator=<(simulator)',
'--out-dir', '<(PRODUCT_DIR)/',
'--cargo-build-dir', '<(INTERMEDIATE_DIR)/rust'
],
'inputs': [],
'outputs': [
'<(PRODUCT_DIR)/<(_target_name)',
# This really needs to be environment-variable-sensitive, but node-gyp doesn't support that. Cargo will still save work if possible.
'<(PRODUCT_DIR)/nonexistent-file-to-force-rebuild'
]
}
]
}
]
}
165 changes: 165 additions & 0 deletions build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#!/usr/bin/env python3
import os
import sys
import optparse
import shlex
import subprocess
import shutil
import re

lib_name = 'rust_fetch'

cargo_archs = {
'x64': 'x86_64',
'ia32': 'i686',
'arm64': 'aarch64'
}

cargo_target_suffix = {
'mac': '-apple-darwin',
'ios': '-apple-ios',
'win': '-pc-windows-msvc',
'linux': '-unknown-linux-gnu'
}

def main(args=None):
parser = optparse.OptionParser()
parser.add_option('--os', default=None, metavar='OS', help='Specify OS')
parser.add_option('--arch', default=None, help='specify arch')
parser.add_option('--simulator', default='0', help='specify if target is simulator (iOS only)')
parser.add_option('--out-dir', '-o', default=None, metavar='DIR', help='specify destination dir')
parser.add_option('--cargo-build-dir', default='target', metavar='PATH', help='specify cargo build dir')

if args is None:
args = sys.argv
if sys.platform == 'win32':
args = shlex.split(' '.join(args), posix=0)

(options, args) = parser.parse_args(args)

arch = options.arch
os_name = options.os
simulator = options.simulator
out_dir = options.out_dir.strip('"') or os.path.join('build', 'Release')

if arch not in cargo_archs:
print('arch must be one of ', list(cargo_archs.keys()))
return 1
if os_name not in cargo_target_suffix:
print('os must be one of ', list(cargo_target_suffix.keys()))
return 1

cargo_triple = cargo_archs[arch] + cargo_target_suffix[os_name]

if os_name == 'ios' and arch == 'arm64' and simulator == '1':
cargo_triple += '-sim'


cargo_env = os.environ.copy()

if 'CARGO_BUILD_TARGET' in cargo_env:
cargo_triple = cargo_env['CARGO_BUILD_TARGET']

cargo_env['CARGO_BUILD_TARGET_DIR'] = options.cargo_build_dir
# on linux cdylib don't include public symbols from their deps
# using lto fixes this issue
# source: libsignal
cargo_env['CARGO_PROFILE_RELEASE_LTO'] = 'thin'

if os == 'win':
cargo_env['RUSTFLAGS'] += '-C target-feature=+crt-static'

cmdline = ['cargo', 'build', '--release', '--target', cargo_triple]

print('cmd:', ' '.join(cmdline))

cmd = subprocess.Popen(cmdline, env=cargo_env)
cmd.wait()

if cmd.returncode != 0:
print('ERROR: cargo failed to run: %s' % cmd.returncode)
return 1

rust_dir = os.path.join(options.cargo_build_dir, cargo_triple, 'release')
print("rust_dir", rust_dir)

found_a_lib = False
for lib_format in ['%s.dll', 'lib%s.so', 'lib%s.dylib', 'lib%s.a']:
src_path = os.path.join(rust_dir, lib_format % lib_name)
print("src_path", src_path)
if os.path.exists(src_path):
found_a_lib = True
break

if not found_a_lib:
print("ERROR: could not find built library")
return 1

dst_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'rf.node')

# iOS hax
# convert static lib to dylib since rust doesn't support cdylib for iOS
if 'apple-ios' in cargo_triple:
sdktype = 'iphoneos'
if simulator == '1':
sdktype = 'iphonesimulator'

cc = subprocess.check_output(['xcrun', '--sdk', sdktype, '--find', 'clang'], encoding='utf-8').replace('\n', '')
sysroot = subprocess.check_output(['xcrun', '--sdk', sdktype, '--show-sdk-path'], encoding='utf-8').replace('\n', '')

dylib_arch = cargo_archs[arch]
if dylib_arch == 'aarch64':
dylib_arch = 'arm64'

cc_cmdline = [
cc,
'-arch', dylib_arch,
'-isysroot', sysroot,
'-miphoneos-version-min=12.0',
'-fPIC',
'-fvisibility=default',
'-shared',
'-framework', 'CoreFoundation',
'-framework', 'Security',
'-Wl,-all_load',
src_path,
'-o', dst_path
]

print(' '.join(cc_cmdline))

# run it first to get all the files with duplicated symbols
try:
results = subprocess.check_output(cc_cmdline, encoding='utf-8', stderr=subprocess.STDOUT)
except Exception as e:
results = str(e.output)

lines = results.split('\n')
for idx, line in enumerate(lines):
if not line.startswith('duplicate symbol'):
continue
filename = re.search(r"\.a\((.+\.o)\)$", lines[idx + 1])[1]

# hacky solution to missing files error
# a lot of times there are multiple objects with the same filename in the .a
# just blindly try to delete each one. it's slow though
try:
subprocess.check_output(['ar', 'd', src_path, filename])
except Exception as e:
continue

cmd = subprocess.Popen(cc_cmdline)
cmd.wait()

if cmd.returncode != 0 or not os.path.exists(dst_path):
print('ERROR: failed converting to dylib')
print('cmd:', ' '.join(cc_cmdline))
return

else:
shutil.copy(src_path, dst_path)

return 0

if __name__ == '__main__':
sys.exit(main())

0 comments on commit b157a69

Please sign in to comment.