2022-08-11 00:39:11 +04:00

944 lines
33 KiB
Python

#!/bin/python3
import argparse
import os
import shlex
import sys
import tempfile
import subprocess
import shutil
import glob
from BuildEnvironment import resolve_executable, call_executable, run_executable_with_output, BuildEnvironment
from ProjectGeneration import generate
from BazelLocation import locate_bazel
from BuildConfiguration import CodesigningSource, GitCodesigningSource, DirectoryCodesigningSource, BuildConfiguration, build_configuration_from_json
import RemoteBuild
class BazelCommandLine:
def __init__(self, bazel, override_bazel_version, override_xcode_version, bazel_user_root):
self.build_environment = BuildEnvironment(
base_path=os.getcwd(),
bazel_path=bazel,
override_bazel_version=override_bazel_version,
override_xcode_version=override_xcode_version
)
self.bazel_user_root = bazel_user_root
self.remote_cache = None
self.cache_dir = None
self.additional_args = None
self.build_number = None
self.configuration_args = None
self.configuration_path = None
self.split_submodules = False
self.custom_target = None
self.continue_on_error = False
self.enable_sandbox = False
self.common_args = [
# https://docs.bazel.build/versions/master/command-line-reference.html
# Ask bazel to print the actual resolved command line options.
'--announce_rc',
# https://github.com/bazelbuild/rules_swift
# If enabled, Swift compilation actions will use the same global Clang module
# cache used by Objective-C compilation actions. This is disabled by default
# because under some circumstances Clang module cache corruption can cause the
# Swift compiler to crash (sometimes when switching configurations or syncing a
# repository), but disabling it also causes a noticeable build time regression
# so it can be explicitly re-enabled by users who are not affected by those
# crashes.
'--features=swift.use_global_module_cache',
# https://docs.bazel.build/versions/master/command-line-reference.html
# Print the subcommand details in case of failure.
'--verbose_failures',
]
self.common_build_args = [
# https://github.com/bazelbuild/rules_swift
# If enabled the skip function bodies frontend flag is passed when using derived
# files generation.
'--features=swift.skip_function_bodies_for_derived_files',
# Set the number of parallel processes to match the available CPU core count.
'--jobs={}'.format(os.cpu_count()),
]
self.common_debug_args = [
# https://github.com/bazelbuild/rules_swift
# If enabled, Swift compilation actions will use batch mode by passing
# `-enable-batch-mode` to `swiftc`. This is a new compilation mode as of
# Swift 4.2 that is intended to speed up non-incremental non-WMO builds by
# invoking a smaller number of frontend processes and passing them batches of
# source files.
'--features=swift.enable_batch_mode',
# https://docs.bazel.build/versions/master/command-line-reference.html
# Set the number of parallel jobs per module to saturate the available CPU resources.
'--swiftcopt=-j{}'.format(os.cpu_count() - 1),
]
self.common_release_args = [
# https://github.com/bazelbuild/rules_swift
# Enable whole module optimization.
'--features=swift.opt_uses_wmo',
# https://github.com/bazelbuild/rules_swift
# Use -Osize instead of -O when building swift modules.
'--features=swift.opt_uses_osize',
# --num-threads 0 forces swiftc to generate one object file per module; it:
# 1. resolves issues with the linker caused by the swift-objc mixing.
# 2. makes the resulting binaries significantly smaller (up to 9% for this project).
'--swiftcopt=-num-threads', '--swiftcopt=0',
# Strip unsused code.
'--features=dead_strip',
'--objc_enable_binary_stripping',
# Always embed bitcode into Watch binaries. This is required by the App Store.
'--apple_bitcode=watchos=embedded',
]
def add_remote_cache(self, host):
self.remote_cache = host
def add_cache_dir(self, path):
self.cache_dir = path
def add_additional_args(self, additional_args):
self.additional_args = additional_args
def set_build_number(self, build_number):
self.build_number = build_number
def set_custom_target(self, target_name):
self.custom_target = target_name
def set_continue_on_error(self, continue_on_error):
self.continue_on_error = continue_on_error
def set_enable_sandbox(self, enable_sandbox):
self.enable_sandbox = enable_sandbox
def set_split_swiftmodules(self, value):
self.split_submodules = value
def set_configuration_path(self, path):
self.configuration_path = path
def set_configuration(self, configuration):
if configuration == 'debug_universal':
self.configuration_args = [
# bazel debug build configuration
'-c', 'dbg',
# Build universal binaries.
'--ios_multi_cpus=armv7,arm64',
# Always build universal Watch binaries.
'--watchos_cpus=armv7k,arm64_32'
] + self.common_debug_args
elif configuration == 'debug_arm64':
self.configuration_args = [
# bazel debug build configuration
'-c', 'dbg',
# Build single-architecture binaries. It is almost 2 times faster is 32-bit support is not required.
'--ios_multi_cpus=arm64',
# Always build universal Watch binaries.
'--watchos_cpus=armv7k,arm64_32'
] + self.common_debug_args
elif configuration == 'debug_sim_arm64':
self.configuration_args = [
# bazel debug build configuration
'-c', 'dbg',
# Build single-architecture binaries. It is almost 2 times faster is 32-bit support is not required.
'--ios_multi_cpus=sim_arm64',
# Always build universal Watch binaries.
'--watchos_cpus=armv7k,arm64_32'
] + self.common_debug_args
elif configuration == 'debug_armv7':
self.configuration_args = [
# bazel debug build configuration
'-c', 'dbg',
'--ios_multi_cpus=armv7',
# Always build universal Watch binaries.
'--watchos_cpus=armv7k,arm64_32'
] + self.common_debug_args
elif configuration == 'release_arm64':
self.configuration_args = [
# bazel optimized build configuration
'-c', 'opt',
# Build single-architecture binaries. It is almost 2 times faster is 32-bit support is not required.
'--ios_multi_cpus=arm64',
# Always build universal Watch binaries.
'--watchos_cpus=armv7k,arm64_32',
# Generate DSYM files when building.
'--apple_generate_dsym',
# Require DSYM files as build output.
'--output_groups=+dsyms'
] + self.common_release_args
elif configuration == 'release_armv7':
self.configuration_args = [
# bazel optimized build configuration
'-c', 'opt',
# Build single-architecture binaries. It is almost 2 times faster is 32-bit support is not required.
'--ios_multi_cpus=armv7',
# Always build universal Watch binaries.
'--watchos_cpus=armv7k,arm64_32',
# Generate DSYM files when building.
'--apple_generate_dsym',
# Require DSYM files as build output.
'--output_groups=+dsyms'
] + self.common_release_args
elif configuration == 'release_universal':
self.configuration_args = [
# bazel optimized build configuration
'-c', 'opt',
# Build universal binaries.
'--ios_multi_cpus=armv7,arm64',
# Always build universal Watch binaries.
'--watchos_cpus=armv7k,arm64_32',
# Generate DSYM files when building.
'--apple_generate_dsym',
# Require DSYM files as build output.
'--output_groups=+dsyms'
] + self.common_release_args
else:
raise Exception('Unknown configuration {}'.format(configuration))
def get_startup_bazel_arguments(self):
combined_arguments = []
if self.bazel_user_root is not None:
combined_arguments += ['--output_user_root={}'.format(self.bazel_user_root)]
return combined_arguments
def invoke_clean(self):
combined_arguments = [
self.build_environment.bazel_path
]
combined_arguments += self.get_startup_bazel_arguments()
combined_arguments += [
'clean',
'--expunge'
]
print('TelegramBuild: running {}'.format(combined_arguments))
call_executable(combined_arguments)
def get_define_arguments(self):
return [
'--define=buildNumber={}'.format(self.build_number),
'--define=telegramVersion={}'.format(self.build_environment.app_version)
]
def get_project_generation_arguments(self):
combined_arguments = []
combined_arguments += self.common_args
combined_arguments += self.common_debug_args
combined_arguments += self.get_define_arguments()
if self.remote_cache is not None:
combined_arguments += [
'--remote_cache={}'.format(self.remote_cache),
'--experimental_remote_downloader={}'.format(self.remote_cache)
]
elif self.cache_dir is not None:
combined_arguments += [
'--disk_cache={path}'.format(path=self.cache_dir)
]
if self.continue_on_error:
combined_arguments += ['--keep_going']
return combined_arguments
def get_additional_build_arguments(self):
combined_arguments = []
if self.split_submodules:
combined_arguments += [
# https://github.com/bazelbuild/rules_swift
# If enabled and whole module optimisation is being used, the `*.swiftdoc`,
# `*.swiftmodule` and `*-Swift.h` are generated with a separate action
# rather than as part of the compilation.
'--features=swift.split_derived_files_generation',
]
return combined_arguments
def invoke_build(self):
combined_arguments = [
self.build_environment.bazel_path
]
combined_arguments += self.get_startup_bazel_arguments()
combined_arguments += ['build']
if self.custom_target is not None:
combined_arguments += [self.custom_target]
else:
combined_arguments += ['Telegram/Telegram']
if self.continue_on_error:
combined_arguments += ['--keep_going']
if self.enable_sandbox:
combined_arguments += ['--spawn_strategy=sandboxed']
if self.configuration_path is None:
raise Exception('configuration_path is not defined')
combined_arguments += [
'--override_repository=build_configuration={}'.format(self.configuration_path)
]
combined_arguments += self.common_args
combined_arguments += self.common_build_args
combined_arguments += self.get_define_arguments()
combined_arguments += self.get_additional_build_arguments()
if self.remote_cache is not None:
combined_arguments += [
'--remote_cache={}'.format(self.remote_cache),
'--experimental_remote_downloader={}'.format(self.remote_cache)
]
elif self.cache_dir is not None:
combined_arguments += [
'--disk_cache={path}'.format(path=self.cache_dir)
]
combined_arguments += self.configuration_args
print('TelegramBuild: running')
print(subprocess.list2cmdline(combined_arguments))
call_executable(combined_arguments)
def invoke_test(self):
combined_arguments = [
self.build_environment.bazel_path
]
combined_arguments += self.get_startup_bazel_arguments()
combined_arguments += ['test']
combined_arguments += ['--cache_test_results=no']
combined_arguments += ['--test_output=errors']
combined_arguments += ['Tests/AllTests']
if self.configuration_path is None:
raise Exception('configuration_path is not defined')
combined_arguments += [
'--override_repository=build_configuration={}'.format(self.configuration_path)
]
combined_arguments += self.common_args
combined_arguments += self.common_build_args
combined_arguments += self.get_define_arguments()
combined_arguments += self.get_additional_build_arguments()
if self.remote_cache is not None:
combined_arguments += [
'--remote_cache={}'.format(self.remote_cache),
'--experimental_remote_downloader={}'.format(self.remote_cache)
]
elif self.cache_dir is not None:
combined_arguments += [
'--disk_cache={path}'.format(path=self.cache_dir)
]
combined_arguments += self.configuration_args
print('TelegramBuild: running')
print(subprocess.list2cmdline(combined_arguments))
call_executable(combined_arguments)
def clean(bazel, arguments):
bazel_command_line = BazelCommandLine(
bazel=bazel,
override_bazel_version=arguments.overrideBazelVersion,
override_xcode_version=arguments.overrideXcodeVersion,
bazel_user_root=arguments.bazelUserRoot
)
bazel_command_line.invoke_clean()
def resolve_codesigning(arguments, base_path, build_configuration, provisioning_profiles_path, additional_codesigning_output_path):
profile_source = None
if arguments.gitCodesigningRepository is not None:
password = os.getenv('TELEGRAM_CODESIGNING_GIT_PASSWORD')
if password is None:
print('TELEGRAM_CODESIGNING_GIT_PASSWORD environment variable is not set')
sys.exit(1)
if arguments.gitCodesigningType is None:
print('--gitCodesigningType is required if --gitCodesigningRepository is set')
sys.exit(1)
profile_source = GitCodesigningSource(
repo_url=arguments.gitCodesigningRepository,
team_id=build_configuration.team_id,
bundle_id=build_configuration.bundle_id,
codesigning_type=arguments.gitCodesigningType,
password=password,
always_fetch=arguments.gitCodesigningAlwaysFetch
)
elif arguments.codesigningInformationPath is not None:
profile_source = DirectoryCodesigningSource(
directory_path=arguments.codesigningInformationPath,
team_id=build_configuration.team_id,
bundle_id=build_configuration.bundle_id
)
else:
raise Exception('Neither gitCodesigningRepository nor codesigningInformationPath are set')
workdir_path = '{}/build-input/configuration-repository-workdir'.format(base_path)
os.makedirs(workdir_path, exist_ok=True)
profile_source.load_data(working_dir=workdir_path)
if provisioning_profiles_path is not None:
profile_source.copy_profiles_to_destination(destination_path=provisioning_profiles_path)
if additional_codesigning_output_path is not None:
profile_source.copy_profiles_to_destination(destination_path=additional_codesigning_output_path + '/profiles')
profile_source.copy_certificates_to_destination(destination_path=additional_codesigning_output_path + '/certs')
def resolve_configuration(base_path, bazel_command_line: BazelCommandLine, arguments, aps_environment, additional_codesigning_output_path):
configuration_repository_path = '{}/build-input/configuration-repository'.format(base_path)
os.makedirs(configuration_repository_path, exist_ok=True)
build_configuration = build_configuration_from_json(path=arguments.configurationPath)
with open(configuration_repository_path + '/WORKSPACE', 'w+') as file:
pass
with open(configuration_repository_path + '/BUILD', 'w+') as file:
pass
build_configuration.write_to_variables_file(aps_environment=aps_environment, path=configuration_repository_path + '/variables.bzl')
provisioning_path = '{}/provisioning'.format(configuration_repository_path)
if os.path.exists(provisioning_path):
shutil.rmtree(provisioning_path)
os.makedirs(provisioning_path, exist_ok=True)
resolve_codesigning(
arguments=arguments,
base_path=base_path,
build_configuration=build_configuration,
provisioning_profiles_path=provisioning_path,
additional_codesigning_output_path=additional_codesigning_output_path
)
provisioning_profile_files = []
for file_name in os.listdir(provisioning_path):
if file_name.endswith('.mobileprovision'):
provisioning_profile_files.append(file_name)
with open(provisioning_path + '/BUILD', 'w+') as file:
file.write('exports_files([\n')
for file_name in provisioning_profile_files:
file.write(' "{}",\n'.format(file_name))
file.write('])\n')
if bazel_command_line is not None:
bazel_command_line.set_configuration_path(configuration_repository_path)
def generate_project(bazel, arguments):
bazel_command_line = BazelCommandLine(
bazel=bazel,
override_bazel_version=arguments.overrideBazelVersion,
override_xcode_version=arguments.overrideXcodeVersion,
bazel_user_root=arguments.bazelUserRoot
)
if arguments.cacheDir is not None:
bazel_command_line.add_cache_dir(arguments.cacheDir)
elif arguments.cacheHost is not None:
bazel_command_line.add_remote_cache(arguments.cacheHost)
bazel_command_line.set_continue_on_error(arguments.continueOnError)
resolve_configuration(
base_path=os.getcwd(),
bazel_command_line=bazel_command_line,
arguments=arguments,
aps_environment=arguments.apsEnvironment,
additional_codesigning_output_path=None
)
bazel_command_line.set_build_number(arguments.buildNumber)
disable_extensions = False
disable_provisioning_profiles = False
generate_dsym = False
target_name = "Telegram"
if arguments.disableExtensions is not None:
disable_extensions = arguments.disableExtensions
if arguments.disableProvisioningProfiles is not None:
disable_provisioning_profiles = arguments.disableProvisioningProfiles
if arguments.generateDsym is not None:
generate_dsym = arguments.generateDsym
if arguments.target is not None:
target_name = arguments.target
call_executable(['killall', 'Xcode'], check_result=False)
generate(
build_environment=bazel_command_line.build_environment,
disable_extensions=disable_extensions,
disable_provisioning_profiles=disable_provisioning_profiles,
generate_dsym=generate_dsym,
configuration_path=bazel_command_line.configuration_path,
bazel_app_arguments=bazel_command_line.get_project_generation_arguments(),
target_name=target_name
)
def build(bazel, arguments):
bazel_command_line = BazelCommandLine(
bazel=bazel,
override_bazel_version=arguments.overrideBazelVersion,
override_xcode_version=arguments.overrideXcodeVersion,
bazel_user_root=arguments.bazelUserRoot
)
if arguments.cacheDir is not None:
bazel_command_line.add_cache_dir(arguments.cacheDir)
elif arguments.cacheHost is not None:
bazel_command_line.add_remote_cache(arguments.cacheHost)
resolve_configuration(
base_path=os.getcwd(),
bazel_command_line=bazel_command_line,
arguments=arguments,
aps_environment=arguments.apsEnvironment,
additional_codesigning_output_path=None
)
bazel_command_line.set_configuration(arguments.configuration)
bazel_command_line.set_build_number(arguments.buildNumber)
bazel_command_line.set_custom_target(arguments.target)
bazel_command_line.set_continue_on_error(arguments.continueOnError)
bazel_command_line.set_enable_sandbox(arguments.sandbox)
bazel_command_line.set_split_swiftmodules(not arguments.disableParallelSwiftmoduleGeneration)
bazel_command_line.invoke_build()
if arguments.outputBuildArtifactsPath is not None:
artifacts_path = arguments.outputBuildArtifactsPath
if os.path.exists(artifacts_path + '/Telegram.ipa'):
os.remove(path)
if os.path.exists(artifacts_path + '/DSYMs'):
shutil.rmtree(artifacts_path + '/DSYMs')
os.makedirs(artifacts_path, exist_ok=True)
os.makedirs(artifacts_path + '/DSYMs', exist_ok=True)
ipa_paths = glob.glob('bazel-out/applebin_ios-ios_arm*-opt-ST-*/bin/Telegram/Telegram.ipa')
if len(ipa_paths) == 0:
print('Could not find the IPA at bazel-out/applebin_ios-ios_arm*-opt-ST-*/bin/Telegram/Telegram.ipa')
sys.exit(1)
elif len(ipa_paths) > 1:
print('Multiple matching IPA files found: {}'.format(ipa_paths))
sys.exit(1)
shutil.copyfile(ipa_paths[0], artifacts_path + '/Telegram.ipa')
dsym_paths = glob.glob('bazel-out/applebin_ios-ios_arm*-opt-ST-*/bin/Telegram/*.dSYM')
for dsym_path in dsym_paths:
file_name = os.path.basename(dsym_path)
shutil.copyfile(ipa_paths[0], artifacts_path + '/DSYMs/{}'.format(file_name))
run_executable_with_output('zip', arguments=[
'-9',
'-r',
artifacts_path + '/Telegram.DSYMs.zip',
artifacts_path + '/DSYMs'
], check_result=True)
shutil.rmtree(artifacts_path + '/DSYMs')
def test(bazel, arguments):
bazel_command_line = BazelCommandLine(
bazel=bazel,
override_bazel_version=arguments.overrideBazelVersion,
override_xcode_version=arguments.overrideXcodeVersion,
bazel_user_root=arguments.bazelUserRoot
)
if arguments.cacheDir is not None:
bazel_command_line.add_cache_dir(arguments.cacheDir)
elif arguments.cacheHost is not None:
bazel_command_line.add_remote_cache(arguments.cacheHost)
resolve_configuration(
base_path=os.getcwd(),
bazel_command_line=bazel_command_line,
arguments=arguments,
aps_environment=arguments.apsEnvironment,
additional_codesigning_output_path=None
)
bazel_command_line.set_configuration('debug_sim_arm64')
bazel_command_line.set_build_number('10000')
bazel_command_line.invoke_test()
def add_codesigning_common_arguments(current_parser: argparse.ArgumentParser):
configuration_group = current_parser.add_mutually_exclusive_group(required=True)
configuration_group.add_argument(
'--configurationPath',
help='''
Path to a json containing build configuration.
See build-system/appstore-configuration.json for an example.
''',
metavar='path'
)
codesigning_group = current_parser.add_mutually_exclusive_group(required=True)
codesigning_group.add_argument(
'--gitCodesigningRepository',
help='''
If specified, certificates and provisioning profiles will be loaded from git.
TELEGRAM_CODESIGNING_GIT_PASSWORD environment variable must be set.
''',
metavar='path'
)
codesigning_group.add_argument(
'--codesigningInformationPath',
help='''
Use signing certificates and provisioning profiles from a local directory.
''',
metavar='command'
)
current_parser.add_argument(
'--gitCodesigningType',
choices=[
'development',
'adhoc',
'appstore',
'enterprise'
],
required=False,
help='''
The name of the folder to use inside "profiles" folder in the git repository.
Required if gitCodesigningRepository is specified.
''',
metavar='type'
)
current_parser.add_argument(
'--gitCodesigningAlwaysFetch',
action='store_true',
required=False,
default=True,
help='''
Always refresh codesigning repository.
'''
)
def add_project_and_build_common_arguments(current_parser: argparse.ArgumentParser):
current_parser.add_argument(
'--apsEnvironment',
choices=[
'development',
'production'
],
required=True,
help='APNS environment',
)
add_codesigning_common_arguments(current_parser=current_parser)
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog='Make')
parser.add_argument(
'--verbose',
action='store_true',
default=False,
help='Print debug info'
)
parser.add_argument(
'--bazel',
required=False,
help='Use custom bazel binary',
metavar='path'
)
parser.add_argument(
'--bazelUserRoot',
required=False,
help='Use custom bazel user root (useful when reproducing a build)',
metavar='path'
)
parser.add_argument(
'--overrideBazelVersion',
action='store_true',
help='Override bazel version with the actual version reported by the bazel binary'
)
parser.add_argument(
'--overrideXcodeVersion',
action='store_true',
help='Override xcode version with the actual version reported by \'xcode-select -p\''
)
parser.add_argument(
'--bazelArguments',
required=False,
help='Add additional arguments to all bazel invocations.',
metavar='arguments'
)
cacheTypeGroup = parser.add_mutually_exclusive_group()
cacheTypeGroup.add_argument(
'--cacheHost',
required=False,
help='Use remote build artifact cache to speed up rebuilds (See https://github.com/buchgr/bazel-remote).',
metavar='grpc://host:9092'
)
cacheTypeGroup.add_argument(
'--cacheDir',
required=False,
help='Cache build artifacts in a local directory to speed up rebuilds.',
metavar='path'
)
subparsers = parser.add_subparsers(dest='commandName', help='Commands')
cleanParser = subparsers.add_parser(
'clean', help='''
Clean local bazel cache. Does not affect files cached remotely (via --cacheHost=...) or
locally in an external directory ('--cacheDir=...')
'''
)
testParser = subparsers.add_parser(
'test', help='''
Run all tests.
'''
)
add_project_and_build_common_arguments(testParser)
generateProjectParser = subparsers.add_parser('generateProject', help='Generate Xcode project')
generateProjectParser.add_argument(
'--buildNumber',
required=False,
type=int,
default=10000,
help='Build number.',
metavar='number'
)
add_project_and_build_common_arguments(generateProjectParser)
generateProjectParser.add_argument(
'--disableExtensions',
action='store_true',
default=False,
help='''
The generated project will not include app extensions.
This allows Xcode to properly index the source code.
'''
)
generateProjectParser.add_argument(
'--continueOnError',
action='store_true',
default=False,
help='Continue build process after an error.',
)
generateProjectParser.add_argument(
'--disableProvisioningProfiles',
action='store_true',
default=False,
help='''
This allows to build the project for simulator without having any codesigning identities installed.
Building for an actual device will fail.
'''
)
generateProjectParser.add_argument(
'--generateDsym',
action='store_true',
default=False,
help='''
This improves profiling experinence by generating DSYM files. Keep disabled for better build performance.
'''
)
generateProjectParser.add_argument(
'--target',
type=str,
help='A custom bazel target name to build.',
metavar='target_name'
)
buildParser = subparsers.add_parser('build', help='Build the app')
buildParser.add_argument(
'--buildNumber',
required=True,
type=int,
help='Build number.',
metavar='number'
)
add_project_and_build_common_arguments(buildParser)
buildParser.add_argument(
'--configuration',
choices=[
'debug_universal',
'debug_arm64',
'debug_armv7',
'release_arm64',
'release_armv7',
'release_universal'
],
required=True,
help='Build configuration'
)
buildParser.add_argument(
'--disableParallelSwiftmoduleGeneration',
action='store_true',
default=False,
help='Generate .swiftmodule files in parallel to building modules, can speed up compilation on multi-core '
'systems. '
)
buildParser.add_argument(
'--target',
type=str,
help='A custom bazel target name to build.',
metavar='target_name'
)
buildParser.add_argument(
'--continueOnError',
action='store_true',
default=False,
help='Continue build process after an error.',
)
buildParser.add_argument(
'--sandbox',
action='store_true',
default=False,
help='Enable sandbox.',
)
buildParser.add_argument(
'--outputBuildArtifactsPath',
required=False,
help='Store IPA and DSYM at the specified path after a successful build.',
metavar='arguments'
)
remote_build_parser = subparsers.add_parser('remote-build', help='Build the app using a remote environment.')
add_codesigning_common_arguments(remote_build_parser)
remote_build_parser.add_argument(
'--darwinContainersHost',
required=True,
type=str,
help='DarwinContainers host address.'
)
remote_build_parser.add_argument(
'--configuration',
choices=[
'debug_universal',
'debug_arm64',
'debug_armv7',
'release_arm64',
'release_armv7',
'release_universal'
],
required=True,
help='Build configuration'
)
remote_build_parser.add_argument(
'--cacheHost',
required=False,
type=str,
help='Bazel remote cache host address.'
)
if len(sys.argv) < 2:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
if args.verbose:
print(args)
if args.commandName is None:
exit(0)
bazel_path = None
if args.bazel is None:
bazel_path = locate_bazel(base_path=os.getcwd())
else:
bazel_path = args.bazel
try:
if args.commandName == 'clean':
clean(bazel=bazel_path, arguments=args)
elif args.commandName == 'generateProject':
generate_project(bazel=bazel_path, arguments=args)
elif args.commandName == 'build':
build(bazel=bazel_path, arguments=args)
elif args.commandName == 'remote-build':
base_path = os.getcwd()
remote_input_path = '{}/build-input/remote-input'.format(base_path)
if os.path.exists(remote_input_path):
shutil.rmtree(remote_input_path)
os.makedirs(remote_input_path)
os.makedirs(remote_input_path + '/certs')
os.makedirs(remote_input_path + '/profiles')
resolve_configuration(
base_path=os.getcwd(),
bazel_command_line=None,
arguments=args,
aps_environment='production',
additional_codesigning_output_path=remote_input_path
)
shutil.copyfile(args.configurationPath, remote_input_path + '/configuration.json')
RemoteBuild.remote_build(
darwin_containers_host=args.darwinContainersHost,
bazel_cache_host=args.cacheHost,
configuration=args.configuration,
build_input_data_path=remote_input_path
)
elif args.commandName == 'test':
test(bazel=bazel_path, arguments=args)
else:
raise Exception('Unknown command')
except KeyboardInterrupt:
pass