Build system

This commit is contained in:
Isaac 2025-12-26 19:42:31 +08:00
parent 25d13682ba
commit 7ed08c0141
15 changed files with 173 additions and 18 deletions

View file

@ -22,6 +22,15 @@ build --spawn_strategy=standalone
build --strategy=SwiftCompile=standalone
build --define RULES_SWIFT_BUILD_DUMMY_WORKER=1
build:swift_profile --jobs=1
build:swift_profile --local_cpu_resources=1
build:swift_profile --features=-swift.enable_batch_mode
build:swift_profile --experimental_ui_max_stdouterr_bytes=104857600
build:swift_profile --@build_bazel_rules_swift//swift:copt=-Xfrontend
build:swift_profile --@build_bazel_rules_swift//swift:copt=-debug-time-function-bodies
build:swift_profile --@build_bazel_rules_swift//swift:copt=-Xfrontend
build:swift_profile --@build_bazel_rules_swift//swift:copt=-debug-time-expression-type-checking
common:index_build --experimental_convenience_symlinks=ignore
common:index_build --bes_backend= --bes_results_url=
common:index_build --nolegacy_important_outputs

1
.gitignore vendored
View file

@ -77,3 +77,4 @@ spm-files
xcode-files
.bsp/**
/.claude/
/buildbox/*

View file

@ -5,40 +5,165 @@ import shutil
import tempfile
import plistlib
import argparse
import subprocess
import base64
from BuildEnvironment import run_executable_with_output, check_run_system
def get_certificate_base64():
certificate_data = run_executable_with_output('security', arguments=['find-certificate', '-c', 'Apple Distribution: Telegram FZ-LLC (C67CF9S4VU)', '-p'])
certificate_data = certificate_data.replace('-----BEGIN CERTIFICATE-----', '')
certificate_data = certificate_data.replace('-----END CERTIFICATE-----', '')
certificate_data = certificate_data.replace('\n', '')
return certificate_data
def setup_temp_keychain(p12_path, p12_password=''):
"""Create a temporary keychain and import the p12 certificate."""
keychain_name = 'generate-profiles-temp.keychain'
keychain_password = 'temp123'
# Delete if exists
run_executable_with_output('security', arguments=['delete-keychain', keychain_name], check_result=False)
# Create keychain
run_executable_with_output('security', arguments=[
'create-keychain', '-p', keychain_password, keychain_name
], check_result=True)
# Add to search list
existing = run_executable_with_output('security', arguments=['list-keychains', '-d', 'user'])
run_executable_with_output('security', arguments=[
'list-keychains', '-d', 'user', '-s', keychain_name, existing.replace('"', '')
], check_result=True)
# Unlock and set settings
run_executable_with_output('security', arguments=['set-keychain-settings', keychain_name])
run_executable_with_output('security', arguments=[
'unlock-keychain', '-p', keychain_password, keychain_name
])
# Import p12
run_executable_with_output('security', arguments=[
'import', p12_path, '-k', keychain_name, '-P', p12_password,
'-T', '/usr/bin/codesign', '-T', '/usr/bin/security'
], check_result=True)
# Set partition list for access
run_executable_with_output('security', arguments=[
'set-key-partition-list', '-S', 'apple-tool:,apple:', '-k', keychain_password, keychain_name
], check_result=True)
return keychain_name
def process_provisioning_profile(source, destination, certificate_data):
def cleanup_temp_keychain(keychain_name):
"""Remove the temporary keychain."""
run_executable_with_output('security', arguments=['delete-keychain', keychain_name], check_result=False)
def get_signing_identity_from_p12(p12_path, p12_password=''):
"""Extract the common name (signing identity) from the p12 certificate."""
proc = subprocess.Popen(
['openssl', 'pkcs12', '-in', p12_path, '-passin', 'pass:' + p12_password, '-nokeys', '-legacy'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
cert_pem, _ = proc.communicate()
proc2 = subprocess.Popen(
['openssl', 'x509', '-noout', '-subject', '-nameopt', 'oneline,-esc_msb'],
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
subject, _ = proc2.communicate(cert_pem)
subject = subject.decode('utf-8').strip()
# Parse CN from subject line like: subject= C = AE, O = ..., CN = Some Name
if 'CN = ' in subject:
cn = subject.split('CN = ')[-1].split(',')[0].strip()
return cn
return None
def get_certificate_base64_from_p12(p12_path, p12_password=''):
"""Extract the certificate as base64 from p12 file."""
# Extract certificate in PEM format
proc = subprocess.Popen(
['openssl', 'pkcs12', '-in', p12_path, '-passin', 'pass:' + p12_password, '-nokeys', '-legacy'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
cert_pem, _ = proc.communicate()
# Convert to DER format
proc2 = subprocess.Popen(
['openssl', 'x509', '-outform', 'DER'],
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
cert_der, _ = proc2.communicate(cert_pem)
return base64.b64encode(cert_der).decode('utf-8')
def process_provisioning_profile(source, destination, certificate_data, signing_identity, keychain_name):
parsed_plist = run_executable_with_output('security', arguments=['cms', '-D', '-i', source], check_result=True)
parsed_plist_file = tempfile.mktemp()
with open(parsed_plist_file, 'w+') as file:
file.write(parsed_plist)
run_executable_with_output('plutil', arguments=['-remove', 'DeveloperCertificates.0', parsed_plist_file])
# Remove all existing developer certificates
while True:
result = run_executable_with_output('plutil', arguments=['-remove', 'DeveloperCertificates.0', parsed_plist_file], check_result=False)
if result is None or 'Could not' in str(result) or result == '':
# Check if the removal actually failed by trying to extract
check = run_executable_with_output('plutil', arguments=['-extract', 'DeveloperCertificates.0', 'raw', parsed_plist_file], check_result=False)
if check is None or 'Could not' in str(check):
break
# Insert the new certificate
run_executable_with_output('plutil', arguments=['-insert', 'DeveloperCertificates.0', '-data', certificate_data, parsed_plist_file])
# Remove the DER-Encoded-Profile (signature)
run_executable_with_output('plutil', arguments=['-remove', 'DER-Encoded-Profile', parsed_plist_file])
run_executable_with_output('security', arguments=['cms', '-S', '-N', 'Apple Distribution: Telegram FZ-LLC (C67CF9S4VU)', '-i', parsed_plist_file, '-o', destination])
# Sign with the certificate from the temporary keychain
run_executable_with_output('security', arguments=[
'cms', '-S', '-k', keychain_name, '-N', signing_identity, '-i', parsed_plist_file, '-o', destination
], check_result=True)
os.unlink(parsed_plist_file)
def generate_provisioning_profiles(source_path, destination_path):
certificate_data = get_certificate_base64()
def generate_provisioning_profiles(source_path, destination_path, certs_path):
p12_path = os.path.join(certs_path, 'SelfSigned.p12')
if not os.path.exists(destination_path):
print('{} does not exits'.format(destination_path))
if not os.path.exists(p12_path):
print('{} does not exist'.format(p12_path))
sys.exit(1)
for file_name in os.listdir(source_path):
if file_name.endswith('.mobileprovision'):
process_provisioning_profile(source=source_path + '/' + file_name, destination=destination_path + '/' + file_name, certificate_data=certificate_data)
if not os.path.exists(destination_path):
print('{} does not exist'.format(destination_path))
sys.exit(1)
# Extract certificate info from p12
p12_password = '' # fake-codesigning uses empty password
certificate_data = get_certificate_base64_from_p12(p12_path, p12_password)
signing_identity = get_signing_identity_from_p12(p12_path, p12_password)
if not signing_identity:
print('Could not extract signing identity from {}'.format(p12_path))
sys.exit(1)
print('Using signing identity: {}'.format(signing_identity))
# Setup temporary keychain with the certificate
keychain_name = setup_temp_keychain(p12_path, p12_password)
try:
for file_name in os.listdir(source_path):
if file_name.endswith('.mobileprovision'):
print('Processing {}'.format(file_name))
process_provisioning_profile(
source=os.path.join(source_path, file_name),
destination=os.path.join(destination_path, file_name),
certificate_data=certificate_data,
signing_identity=signing_identity,
keychain_name=keychain_name
)
print('Done. Generated {} profiles.'.format(
len([f for f in os.listdir(destination_path) if f.endswith('.mobileprovision')])
))
finally:
cleanup_temp_keychain(keychain_name)

View file

@ -46,6 +46,7 @@ class BazelCommandLine:
self.show_actions = False
self.enable_sandbox = False
self.disable_provisioning_profiles = False
self.profile_swift = False
self.common_args = [
# https://docs.bazel.build/versions/master/command-line-reference.html
@ -143,6 +144,9 @@ class BazelCommandLine:
def set_disable_provisioning_profiles(self):
self.disable_provisioning_profiles = True
def set_profile_swift(self, value):
self.profile_swift = value
def set_configuration(self, configuration):
if configuration == 'debug_arm64':
self.configuration_args = [
@ -300,6 +304,8 @@ class BazelCommandLine:
]
combined_arguments += self.configuration_args
if self.profile_swift:
combined_arguments += ['--config=swift_profile']
print('TelegramBuild: running')
print(subprocess.list2cmdline(combined_arguments))
@ -620,6 +626,7 @@ def build(bazel, arguments):
bazel_command_line.set_continue_on_error(arguments.continueOnError)
bazel_command_line.set_show_actions(arguments.showActions)
bazel_command_line.set_enable_sandbox(arguments.sandbox)
bazel_command_line.set_profile_swift(arguments.profileSwift)
bazel_command_line.set_split_swiftmodules(arguments.enableParallelSwiftmoduleGeneration)
@ -972,6 +979,12 @@ if __name__ == '__main__':
help='Generate .swiftmodule files in parallel to building modules, can speed up compilation on multi-core '
'systems. '
)
buildParser.add_argument(
'--profileSwift',
action='store_true',
default=False,
help='Enable single-core Swift compile profiling flags.'
)
buildParser.add_argument(
'--target',
type=str,
@ -1063,6 +1076,13 @@ if __name__ == '__main__':
type=str,
help='Path to the destination directory.'
)
generate_profiles_build_parser.add_argument(
'--certsPath',
required=False,
type=str,
default='build-system/fake-codesigning/certs',
help='Path to the directory containing SelfSigned.p12 certificate.'
)
remote_upload_testflight_parser = subparsers.add_parser('remote-deploy-testflight', help='Build the app using a remote environment.')
remote_upload_testflight_parser.add_argument(
@ -1304,7 +1324,7 @@ if __name__ == '__main__':
additional_codesigning_output_path=remote_input_path
)
GenerateProfiles.generate_provisioning_profiles(source_path=remote_input_path + '/profiles', destination_path=args.destination)
GenerateProfiles.generate_provisioning_profiles(source_path=remote_input_path + '/profiles', destination_path=args.destination, certs_path=args.certsPath)
elif args.commandName == 'remote-deploy-testflight':
env = os.environ
if 'APPSTORE_CONNECT_USERNAME' not in env:

@ -1 +1 @@
Subproject commit 21edd49ba4db756cc54b104b6b7eb7137801e8c4
Subproject commit fb08c7e64a421df645dad8963f7f52a9af3f2ecb