mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 05:25:20 +00:00
Build system
This commit is contained in:
parent
058a5297ea
commit
65a0b41071
@ -1 +1,2 @@
|
||||
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
|
||||
spm-files
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -73,3 +73,4 @@ buildServer.json
|
||||
.build/**
|
||||
Telegram.LSP.json
|
||||
**/.build/**
|
||||
spm-files
|
||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -11,5 +11,8 @@
|
||||
},
|
||||
"search.exclude": {
|
||||
".git/**": true
|
||||
},
|
||||
"files.associations": {
|
||||
"memory": "cpp"
|
||||
}
|
||||
}
|
||||
|
@ -393,6 +393,58 @@ class BazelCommandLine:
|
||||
print(subprocess.list2cmdline(combined_arguments))
|
||||
call_executable(combined_arguments)
|
||||
|
||||
def get_spm_aspect_invocation(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.show_actions:
|
||||
combined_arguments += ['--subcommands']
|
||||
|
||||
if self.enable_sandbox:
|
||||
combined_arguments += ['--spawn_strategy=sandboxed']
|
||||
|
||||
if self.disable_provisioning_profiles:
|
||||
combined_arguments += ['--//Telegram:disableProvisioningProfiles']
|
||||
|
||||
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
|
||||
|
||||
combined_arguments += ['--aspects', '//build-system/bazel-utils:spm.bzl%spm_text_aspect']
|
||||
|
||||
print(subprocess.list2cmdline(combined_arguments))
|
||||
call_executable(combined_arguments)
|
||||
|
||||
|
||||
def clean(bazel, arguments):
|
||||
bazel_command_line = BazelCommandLine(
|
||||
@ -696,6 +748,36 @@ def query(bazel, arguments):
|
||||
bazel_command_line.invoke_query(query_args)
|
||||
|
||||
|
||||
def get_spm_aspect_invocation(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,
|
||||
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(False)
|
||||
bazel_command_line.set_show_actions(False)
|
||||
bazel_command_line.set_enable_sandbox(False)
|
||||
bazel_command_line.set_split_swiftmodules(False)
|
||||
|
||||
bazel_command_line.get_spm_aspect_invocation()
|
||||
|
||||
def add_codesigning_common_arguments(current_parser: argparse.ArgumentParser):
|
||||
configuration_group = current_parser.add_mutually_exclusive_group(required=True)
|
||||
configuration_group.add_argument(
|
||||
@ -1121,6 +1203,38 @@ if __name__ == '__main__':
|
||||
metavar='query_string'
|
||||
)
|
||||
|
||||
spm_parser = subparsers.add_parser('spm', help='Generate SPM package')
|
||||
spm_parser.add_argument(
|
||||
'--target',
|
||||
type=str,
|
||||
help='A custom bazel target name to build.',
|
||||
metavar='target_name'
|
||||
)
|
||||
spm_parser.add_argument(
|
||||
'--buildNumber',
|
||||
required=False,
|
||||
type=int,
|
||||
default=10000,
|
||||
help='Build number.',
|
||||
metavar='number'
|
||||
)
|
||||
spm_parser.add_argument(
|
||||
'--configuration',
|
||||
choices=[
|
||||
'debug_universal',
|
||||
'debug_arm64',
|
||||
'debug_armv7',
|
||||
'debug_sim_arm64',
|
||||
'release_sim_arm64',
|
||||
'release_arm64',
|
||||
'release_armv7',
|
||||
'release_universal'
|
||||
],
|
||||
required=True,
|
||||
help='Build configuration'
|
||||
)
|
||||
add_codesigning_common_arguments(spm_parser)
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
@ -1229,6 +1343,8 @@ if __name__ == '__main__':
|
||||
test(bazel=bazel_path, arguments=args)
|
||||
elif args.commandName == 'query':
|
||||
query(bazel=bazel_path, arguments=args)
|
||||
elif args.commandName == 'spm':
|
||||
get_spm_aspect_invocation(bazel=bazel_path, arguments=args)
|
||||
else:
|
||||
raise Exception('Unknown command')
|
||||
except KeyboardInterrupt:
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 44b6f046d95b84933c1149fbf7f9d81fd4e32020
|
||||
Subproject commit 41929acc4c7c1da973c77871d0375207b9d0806f
|
447
build-system/bazel-utils/spm.bzl
Normal file
447
build-system/bazel-utils/spm.bzl
Normal file
@ -0,0 +1,447 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "SwiftInfo")
|
||||
load("@bazel_skylib//lib:paths.bzl", "paths")
|
||||
load("@bazel_skylib//lib:dicts.bzl", "dicts")
|
||||
|
||||
# Define provider to propagate data
|
||||
SPMModulesInfo = provider(
|
||||
fields = {
|
||||
"modules": "Dictionary of module information",
|
||||
"transitive_sources": "Depset of all transitive source files",
|
||||
}
|
||||
)
|
||||
|
||||
_IGNORE_CC_LIBRARY_ATTRS = [
|
||||
"data",
|
||||
"applicable_licenses",
|
||||
"alwayslink",
|
||||
"aspect_hints",
|
||||
"compatible_with",
|
||||
"deprecation",
|
||||
"exec_compatible_with",
|
||||
"exec_properties",
|
||||
"expect_failure",
|
||||
"features",
|
||||
"generator_function",
|
||||
"generator_location",
|
||||
"generator_name",
|
||||
"generator_platform",
|
||||
"generator_script",
|
||||
"generator_tool",
|
||||
"generator_toolchain",
|
||||
"generator_toolchain_type",
|
||||
"licenses",
|
||||
"linkstamp",
|
||||
"linkstatic",
|
||||
"name",
|
||||
"restricted_to",
|
||||
"tags",
|
||||
"target_compatible_with",
|
||||
"testonly",
|
||||
"to_json",
|
||||
"to_proto",
|
||||
"toolchains",
|
||||
"transitive_configs",
|
||||
"visibility",
|
||||
"win_def_file",
|
||||
"linkopts",
|
||||
]
|
||||
|
||||
_IGNORE_CC_LIBRARY_EMPTY_ATTRS = [
|
||||
"additional_compiler_inputs",
|
||||
"additional_linker_inputs",
|
||||
"hdrs_check",
|
||||
"implementation_deps",
|
||||
"include_prefix",
|
||||
"strip_include_prefix",
|
||||
"local_defines",
|
||||
]
|
||||
|
||||
_CC_LIBRARY_ATTRS = {
|
||||
"copts": [],
|
||||
"defines": [],
|
||||
"deps": [],
|
||||
"hdrs": [],
|
||||
"includes": [],
|
||||
"srcs": [],
|
||||
"textual_hdrs": [],
|
||||
}
|
||||
|
||||
_CC_LIBRARY_REQUIRED_ATTRS = {
|
||||
}
|
||||
|
||||
_IGNORE_OBJC_LIBRARY_ATTRS = [
|
||||
"data",
|
||||
"alwayslink",
|
||||
"applicable_licenses",
|
||||
"aspect_hints",
|
||||
"compatible_with",
|
||||
"enable_modules",
|
||||
"exec_compatible_with",
|
||||
"exec_properties",
|
||||
"expect_failure",
|
||||
"features",
|
||||
"generator_function",
|
||||
"generator_location",
|
||||
"generator_name",
|
||||
"deprecation",
|
||||
"module_name",
|
||||
"name",
|
||||
"stamp",
|
||||
"tags",
|
||||
"target_compatible_with",
|
||||
"testonly",
|
||||
"to_json",
|
||||
"to_proto",
|
||||
"toolchains",
|
||||
"transitive_configs",
|
||||
"visibility",
|
||||
]
|
||||
|
||||
_IGNORE_OBJC_LIBRARY_EMPTY_ATTRS = [
|
||||
"implementation_deps",
|
||||
"linkopts",
|
||||
"module_map",
|
||||
"non_arc_srcs",
|
||||
"pch",
|
||||
"restricted_to",
|
||||
"textual_hdrs",
|
||||
"sdk_includes",
|
||||
]
|
||||
|
||||
_OBJC_LIBRARY_ATTRS = {
|
||||
"copts": [],
|
||||
"defines": [],
|
||||
"deps": [],
|
||||
"hdrs": [],
|
||||
"srcs": [],
|
||||
"sdk_dylibs": [],
|
||||
"sdk_frameworks": [],
|
||||
"weak_sdk_frameworks": [],
|
||||
"includes": [],
|
||||
}
|
||||
|
||||
_OBJC_LIBRARY_REQUIRED_ATTRS = [
|
||||
"module_name",
|
||||
]
|
||||
|
||||
_IGNORE_SWIFT_LIBRARY_ATTRS = [
|
||||
"data",
|
||||
"always_include_developer_search_paths",
|
||||
"alwayslink",
|
||||
"applicable_licenses",
|
||||
"aspect_hints",
|
||||
"compatible_with",
|
||||
"deprecation",
|
||||
"exec_compatible_with",
|
||||
"exec_properties",
|
||||
"expect_failure",
|
||||
"features",
|
||||
"generated_header_name",
|
||||
"generates_header",
|
||||
"generator_function",
|
||||
"generator_location",
|
||||
"generator_name",
|
||||
"linkstatic",
|
||||
"module_name",
|
||||
"name",
|
||||
"package_name",
|
||||
"restricted_to",
|
||||
"tags",
|
||||
"target_compatible_with",
|
||||
"testonly",
|
||||
"to_json",
|
||||
"to_proto",
|
||||
"toolchains",
|
||||
"transitive_configs",
|
||||
"visibility",
|
||||
]
|
||||
|
||||
_IGNORE_SWIFT_LIBRARY_EMPTY_ATTRS = [
|
||||
"plugins",
|
||||
"private_deps",
|
||||
"swiftc_inputs",
|
||||
]
|
||||
|
||||
_SWIFT_LIBRARY_ATTRS = {
|
||||
"copts": [],
|
||||
"defines": [],
|
||||
"deps": [],
|
||||
"linkopts": [],
|
||||
"srcs": [],
|
||||
}
|
||||
|
||||
_SWIFT_LIBRARY_REQUIRED_ATTRS = [
|
||||
"module_name",
|
||||
]
|
||||
|
||||
_LIBRARY_CONFIGS = {
|
||||
"cc_library": {
|
||||
"ignore_attrs": _IGNORE_CC_LIBRARY_ATTRS,
|
||||
"ignore_empty_attrs": _IGNORE_CC_LIBRARY_EMPTY_ATTRS,
|
||||
"handled_attrs": _CC_LIBRARY_ATTRS,
|
||||
"required_attrs": _CC_LIBRARY_REQUIRED_ATTRS,
|
||||
},
|
||||
"objc_library": {
|
||||
"ignore_attrs": _IGNORE_OBJC_LIBRARY_ATTRS,
|
||||
"ignore_empty_attrs": _IGNORE_OBJC_LIBRARY_EMPTY_ATTRS,
|
||||
"handled_attrs": _OBJC_LIBRARY_ATTRS,
|
||||
"required_attrs": _OBJC_LIBRARY_REQUIRED_ATTRS,
|
||||
},
|
||||
"swift_library": {
|
||||
"ignore_attrs": _IGNORE_SWIFT_LIBRARY_ATTRS,
|
||||
"ignore_empty_attrs": _IGNORE_SWIFT_LIBRARY_EMPTY_ATTRS,
|
||||
"handled_attrs": _SWIFT_LIBRARY_ATTRS,
|
||||
"required_attrs": _SWIFT_LIBRARY_REQUIRED_ATTRS,
|
||||
},
|
||||
}
|
||||
|
||||
def get_rule_atts(rule):
|
||||
if rule.kind in _LIBRARY_CONFIGS:
|
||||
config = _LIBRARY_CONFIGS[rule.kind]
|
||||
ignore_attrs = config["ignore_attrs"]
|
||||
ignore_empty_attrs = config["ignore_empty_attrs"]
|
||||
handled_attrs = config["handled_attrs"]
|
||||
required_attrs = config["required_attrs"]
|
||||
|
||||
for attr_name in dir(rule.attr):
|
||||
if attr_name.startswith("_"):
|
||||
continue
|
||||
if attr_name in ignore_attrs:
|
||||
continue
|
||||
if attr_name in ignore_empty_attrs:
|
||||
attr_value = getattr(rule.attr, attr_name)
|
||||
if attr_value == [] or attr_value == None or attr_value == "":
|
||||
continue
|
||||
else:
|
||||
fail("Attribute {} is not empty: {}".format(attr_name, attr_value))
|
||||
if attr_name in handled_attrs:
|
||||
continue
|
||||
fail("Unknown attribute: {}".format(attr_name))
|
||||
|
||||
result = dict()
|
||||
result["type"] = rule.kind
|
||||
for attr_name in handled_attrs:
|
||||
if hasattr(rule.attr, attr_name):
|
||||
result[attr_name] = getattr(rule.attr, attr_name)
|
||||
else:
|
||||
result[attr_name] = handled_attrs[attr_name] # Use default value
|
||||
for attr_name in required_attrs:
|
||||
if not hasattr(rule.attr, attr_name):
|
||||
if rule.kind == "objc_library" and attr_name == "module_name":
|
||||
result[attr_name] = getattr(rule.attr, "name")
|
||||
else:
|
||||
fail("Required attribute {} is missing".format(attr_name))
|
||||
else:
|
||||
result[attr_name] = getattr(rule.attr, attr_name)
|
||||
result["name"] = getattr(rule.attr, "name")
|
||||
return result
|
||||
elif rule.kind == "ios_application":
|
||||
result = dict()
|
||||
result["type"] = "ios_application"
|
||||
return result
|
||||
elif rule.kind == "generate_spm":
|
||||
result = dict()
|
||||
result["type"] = "root"
|
||||
return result
|
||||
elif rule.kind == "apple_static_xcframework_import":
|
||||
result = dict()
|
||||
result["type"] = "apple_static_xcframework_import"
|
||||
return result
|
||||
else:
|
||||
fail("Unknown rule kind: {}".format(rule.kind))
|
||||
|
||||
def _collect_spm_modules_impl(target, ctx):
|
||||
# Skip targets without DefaultInfo
|
||||
if not DefaultInfo in target:
|
||||
return []
|
||||
|
||||
# Get module name
|
||||
module_name = ctx.label.name
|
||||
if hasattr(ctx.rule.attr, "module_name"):
|
||||
module_name = ctx.rule.attr.module_name or ctx.label.name
|
||||
|
||||
# Collect all modules and transitive sources from dependencies first
|
||||
all_modules = {}
|
||||
dep_transitive_sources_list = []
|
||||
|
||||
if hasattr(ctx.rule.attr, "deps"):
|
||||
for dep in ctx.rule.attr.deps:
|
||||
if SPMModulesInfo in dep:
|
||||
# Merge the modules dictionaries
|
||||
for label, info in dep[SPMModulesInfo].modules.items():
|
||||
all_modules[label] = info
|
||||
# Add transitive sources depset from dependency to the list
|
||||
dep_transitive_sources_list.append(dep[SPMModulesInfo].transitive_sources)
|
||||
|
||||
# Merge all transitive sources from dependencies
|
||||
transitive_sources_from_deps = depset(transitive = dep_transitive_sources_list)
|
||||
|
||||
# Keep this for debugging later
|
||||
# if result_attrs["type"] == "swift_library":
|
||||
# print("Processing rule {}".format(ctx.label.name))
|
||||
# print("ctx.rule.kind = {}".format(ctx.rule.kind))
|
||||
# for attr_name in dir(ctx.rule.attr):
|
||||
# print(" attr1: {}".format(attr_name))
|
||||
|
||||
result_attrs = get_rule_atts(ctx.rule)
|
||||
|
||||
sources = []
|
||||
current_target_src_files = []
|
||||
if "srcs" in result_attrs:
|
||||
for src_target in result_attrs["srcs"]:
|
||||
src_files = src_target.files.to_list()
|
||||
for f in src_files:
|
||||
if f.extension in ["swift", "cc", "cpp", "h", "m", "mm", "s", "S"]:
|
||||
current_target_src_files.append(f)
|
||||
for src_file in src_files:
|
||||
sources.append(src_file.path)
|
||||
current_target_sources = depset(current_target_src_files)
|
||||
|
||||
headers = []
|
||||
current_target_hdr_files = []
|
||||
if "hdrs" in result_attrs:
|
||||
for hdr_target in result_attrs["hdrs"]:
|
||||
hdr_files = hdr_target.files.to_list()
|
||||
for f in hdr_files:
|
||||
current_target_hdr_files.append(f)
|
||||
for hdr_file in hdr_files:
|
||||
headers.append(hdr_file.path)
|
||||
current_target_headers = depset(current_target_hdr_files)
|
||||
|
||||
module_type = result_attrs["type"]
|
||||
|
||||
if module_type == "root":
|
||||
pass
|
||||
elif module_type == "apple_static_xcframework_import":
|
||||
pass
|
||||
elif module_type == "objc_library" or module_type == "swift_library" or module_type == "cc_library":
|
||||
# Collect dependency labels
|
||||
dep_names = []
|
||||
if "deps" in result_attrs:
|
||||
for dep in result_attrs["deps"]:
|
||||
if hasattr(dep, "label"):
|
||||
dep_label = str(dep.label)
|
||||
dep_name = dep_label.split(":")[-1]
|
||||
dep_names.append(dep_name)
|
||||
else:
|
||||
fail("Missing dependency label")
|
||||
|
||||
if module_type == "objc_library" or module_type == "swift_library":
|
||||
if result_attrs["module_name"] != result_attrs["name"]:
|
||||
fail("Module name mismatch: {} != {}".format(result_attrs["module_name"], result_attrs["name"]))
|
||||
|
||||
# Extract the path from the label
|
||||
# Example: @//path/ModuleName:ModuleSubname -> path/ModuleName
|
||||
if not str(ctx.label).startswith("@//"):
|
||||
fail("Invalid label: {}".format(ctx.label))
|
||||
module_path = str(ctx.label).split(":")[0].split("@//")[1]
|
||||
|
||||
if module_type == "objc_library":
|
||||
module_info = {
|
||||
"name": result_attrs["name"],
|
||||
"type": module_type,
|
||||
"path": module_path,
|
||||
"defines": result_attrs["defines"],
|
||||
"deps": dep_names,
|
||||
"sources": sorted(sources + headers),
|
||||
"module_name": module_name,
|
||||
"copts": result_attrs["copts"],
|
||||
"sdk_frameworks": result_attrs["sdk_frameworks"],
|
||||
"sdk_dylibs": result_attrs["sdk_dylibs"],
|
||||
"weak_sdk_frameworks": result_attrs["weak_sdk_frameworks"],
|
||||
"includes": result_attrs["includes"],
|
||||
}
|
||||
elif module_type == "cc_library":
|
||||
module_info = {
|
||||
"name": result_attrs["name"],
|
||||
"type": module_type,
|
||||
"path": module_path,
|
||||
"defines": result_attrs["defines"],
|
||||
"deps": dep_names,
|
||||
"sources": sorted(sources + headers),
|
||||
"module_name": module_name,
|
||||
"copts": result_attrs["copts"],
|
||||
"includes": result_attrs["includes"],
|
||||
}
|
||||
elif module_type == "swift_library":
|
||||
module_info = {
|
||||
"name": result_attrs["name"],
|
||||
"type": module_type,
|
||||
"path": module_path,
|
||||
"deps": dep_names,
|
||||
"sources": sorted(sources),
|
||||
"module_name": module_name,
|
||||
"copts": result_attrs["copts"],
|
||||
}
|
||||
else:
|
||||
fail("Unknown module type: {}".format(module_type))
|
||||
|
||||
if result_attrs["name"] in all_modules:
|
||||
fail("Duplicate module name: {}".format(result_attrs["name"]))
|
||||
all_modules[result_attrs["name"]] = module_info
|
||||
elif result_attrs["type"] == "ios_application":
|
||||
pass
|
||||
else:
|
||||
fail("Unknown rule type: {}".format(ctx.rule.kind))
|
||||
|
||||
# Add current target's sources and headers to the transitive set
|
||||
final_transitive_sources = depset(transitive = [
|
||||
transitive_sources_from_deps,
|
||||
current_target_sources,
|
||||
current_target_headers,
|
||||
])
|
||||
|
||||
# Return both the SPM output files and the provider with modules data and sources
|
||||
return [
|
||||
SPMModulesInfo(
|
||||
modules = all_modules,
|
||||
transitive_sources = final_transitive_sources,
|
||||
),
|
||||
]
|
||||
|
||||
spm_modules_aspect = aspect(
|
||||
implementation = _collect_spm_modules_impl,
|
||||
attr_aspects = ["deps"],
|
||||
)
|
||||
|
||||
def _generate_spm_impl(ctx):
|
||||
outputs = []
|
||||
dep_transitive_sources_list = []
|
||||
|
||||
if len(ctx.attr.deps) != 1:
|
||||
fail("generate_spm must have exactly one dependency")
|
||||
if SPMModulesInfo not in ctx.attr.deps[0]:
|
||||
fail("generate_spm must have a dependency with SPMModulesInfo provider")
|
||||
|
||||
spm_info = ctx.attr.deps[0][SPMModulesInfo]
|
||||
modules = spm_info.modules
|
||||
|
||||
# Declare and write the modules JSON file
|
||||
modules_json_out = ctx.actions.declare_file("%s_modules.json" % ctx.label.name)
|
||||
ctx.actions.write(
|
||||
output = modules_json_out,
|
||||
content = json.encode_indent(modules, indent = " "), # Use encode_indent for readability
|
||||
)
|
||||
outputs.append(modules_json_out)
|
||||
|
||||
for dep in ctx.attr.deps:
|
||||
if SPMModulesInfo in dep:
|
||||
# Add transitive sources depset from dependency
|
||||
dep_transitive_sources_list.append(dep[SPMModulesInfo].transitive_sources)
|
||||
|
||||
# Merge all transitive sources from dependencies
|
||||
transitive_sources_from_deps = depset(transitive = dep_transitive_sources_list)
|
||||
|
||||
# Return DefaultInfo containing only the output files in the 'files' field,
|
||||
# but include the transitive sources in 'runfiles' to enforce the dependency.
|
||||
return [DefaultInfo(
|
||||
files = depset(outputs),
|
||||
runfiles = ctx.runfiles(transitive_files = transitive_sources_from_deps),
|
||||
)]
|
||||
|
||||
generate_spm = rule(
|
||||
implementation = _generate_spm_impl,
|
||||
attrs = {
|
||||
'deps' : attr.label_list(aspects = [spm_modules_aspect]),
|
||||
},
|
||||
)
|
193
build-system/generate_spm.py
Normal file
193
build-system/generate_spm.py
Normal file
@ -0,0 +1,193 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
|
||||
# Read the modules JSON file
|
||||
modules_json_path = "bazel-bin/Telegram/spm_build_root_modules.json"
|
||||
|
||||
with open(modules_json_path, 'r') as f:
|
||||
modules = json.load(f)
|
||||
|
||||
# Clean spm-files
|
||||
spm_files_dir = "spm-files"
|
||||
if os.path.exists(spm_files_dir):
|
||||
shutil.rmtree(spm_files_dir)
|
||||
if not os.path.exists(spm_files_dir):
|
||||
os.makedirs(spm_files_dir)
|
||||
|
||||
combined_lines = []
|
||||
combined_lines.append("// swift-tools-version: 6.0")
|
||||
combined_lines.append("// The swift-tools-version declares the minimum version of Swift required to build this package.")
|
||||
combined_lines.append("")
|
||||
combined_lines.append("import PackageDescription")
|
||||
combined_lines.append("")
|
||||
combined_lines.append("let package = Package(")
|
||||
combined_lines.append(" name: \"Telegram\",")
|
||||
combined_lines.append(" platforms: [")
|
||||
combined_lines.append(" .iOS(.v13)")
|
||||
combined_lines.append(" ],")
|
||||
combined_lines.append(" products: [")
|
||||
|
||||
for name, module in sorted(modules.items()):
|
||||
if module["type"] == "objc_library" or module["type"] == "swift_library" or module["type"] == "cc_library":
|
||||
combined_lines.append(" .library(name: \"%s\", targets: [\"%s\"])," % (module["name"], module["name"]))
|
||||
|
||||
combined_lines.append(" ],")
|
||||
combined_lines.append(" targets: [")
|
||||
|
||||
for name, module in sorted(modules.items()):
|
||||
module_type = module["type"]
|
||||
if module_type == "objc_library" or module_type == "cc_library" or module_type == "swift_library":
|
||||
combined_lines.append(" .target(")
|
||||
combined_lines.append(" name: \"%s\"," % name)
|
||||
|
||||
linked_directory = None
|
||||
has_non_linked_sources = False
|
||||
for source in module["sources"]:
|
||||
if source.startswith("bazel-out/"):
|
||||
linked_directory = "spm-files/" + name
|
||||
else:
|
||||
has_non_linked_sources = True
|
||||
if linked_directory and has_non_linked_sources:
|
||||
print("Module {} has both regular and generated sources".format(name))
|
||||
sys.exit(1)
|
||||
if linked_directory:
|
||||
os.makedirs(linked_directory)
|
||||
|
||||
combined_lines.append(" dependencies: [")
|
||||
for dep in module["deps"]:
|
||||
combined_lines.append(" .target(name: \"%s\")," % dep)
|
||||
combined_lines.append(" ],")
|
||||
|
||||
if linked_directory:
|
||||
combined_lines.append(" path: \"%s\"," % linked_directory)
|
||||
else:
|
||||
combined_lines.append(" path: \"%s\"," % module["path"])
|
||||
|
||||
combined_lines.append(" exclude: [")
|
||||
exclude_files_and_dirs = []
|
||||
if not linked_directory:
|
||||
for root, dirs, files in os.walk(module["path"]):
|
||||
rel_path = os.path.relpath(root, module["path"])
|
||||
if rel_path == ".":
|
||||
rel_path = ""
|
||||
else:
|
||||
rel_path += "/"
|
||||
|
||||
# Add directories that should be excluded
|
||||
for d in dirs:
|
||||
dir_path = os.path.join(rel_path, d)
|
||||
if any(component.startswith('.') for component in dir_path.split('/')):
|
||||
continue
|
||||
# Check if any source file is under this directory
|
||||
has_source = False
|
||||
for source in module["sources"]:
|
||||
rel_source = source[len(module["path"]) + 1:]
|
||||
if rel_source.startswith(dir_path + "/"):
|
||||
has_source = True
|
||||
break
|
||||
if not has_source:
|
||||
exclude_files_and_dirs.append(dir_path)
|
||||
|
||||
# Add files that should be excluded
|
||||
for f in files:
|
||||
file_path = os.path.join(rel_path, f)
|
||||
if any(component.startswith('.') for component in file_path.split('/')):
|
||||
continue
|
||||
if file_path not in [source[len(module["path"]) + 1:] for source in module["sources"]]:
|
||||
exclude_files_and_dirs.append(file_path)
|
||||
for item in exclude_files_and_dirs:
|
||||
combined_lines.append(" \"%s\"," % item)
|
||||
combined_lines.append(" ],")
|
||||
|
||||
combined_lines.append(" sources: [")
|
||||
for source in module["sources"]:
|
||||
if source.endswith(('.h', '.hpp')):
|
||||
continue
|
||||
linked_source_file_names = []
|
||||
if not source.startswith(module["path"]):
|
||||
if source.startswith("bazel-out/"):
|
||||
if not linked_directory:
|
||||
print("Source {} is a generated file, but module {} has no linked directory".format(source, name))
|
||||
sys.exit(1)
|
||||
if module["path"] in source:
|
||||
source_file_name = source[source.index(module["path"]) + len(module["path"]) + 1:]
|
||||
else:
|
||||
print("Source {} is not inside module path {}".format(source, module["path"]))
|
||||
sys.exit(1)
|
||||
if source_file_name in linked_source_file_names:
|
||||
print("Source {} is a duplicate".format(source))
|
||||
sys.exit(1)
|
||||
|
||||
linked_source_file_names.append(source_file_name)
|
||||
|
||||
# Create any parent directories needed for the source file
|
||||
symlink_location = os.path.join(linked_directory, source_file_name)
|
||||
source_dir = os.path.dirname(symlink_location)
|
||||
if not os.path.exists(source_dir):
|
||||
os.makedirs(source_dir)
|
||||
|
||||
# Calculate the relative path from the symlink location back to the workspace root
|
||||
num_parent_dirs = 2 + source_file_name.count(os.path.sep)
|
||||
relative_prefix = "".join(["../"] * num_parent_dirs)
|
||||
symlink_target = relative_prefix + source
|
||||
|
||||
os.symlink(symlink_target, symlink_location)
|
||||
relative_source = source_file_name
|
||||
combined_lines.append(" \"%s\"," % relative_source)
|
||||
else:
|
||||
print("Source {} is not inside module path {}".format(source, module["path"]))
|
||||
sys.exit(1)
|
||||
else:
|
||||
relative_source = source[len(module["path"]) + 1:]
|
||||
combined_lines.append(" \"%s\"," % relative_source)
|
||||
combined_lines.append(" ],")
|
||||
if module_type == "objc_library" or module_type == "cc_library":
|
||||
if len(module["includes"]) == 0:
|
||||
combined_lines.append(" publicHeadersPath: \"\",")
|
||||
elif len(module["includes"]) == 1:
|
||||
combined_lines.append(" publicHeadersPath: \"%s\"," % module["includes"][0])
|
||||
else:
|
||||
print("Multiple includes are not supported yet: {}".format(module["includes"]))
|
||||
sys.exit(1)
|
||||
combined_lines.append(" cSettings: [")
|
||||
combined_lines.append(" .unsafeFlags([")
|
||||
for flag in module["copts"]:
|
||||
# Escape C-string entities in flag
|
||||
escaped_flag = flag.replace('\\', '\\\\').replace('"', '\\"')
|
||||
combined_lines.append(" \"%s\"," % escaped_flag)
|
||||
combined_lines.append(" ])")
|
||||
combined_lines.append(" ],")
|
||||
combined_lines.append(" linkerSettings: [")
|
||||
if module_type == "objc_library":
|
||||
for framework in module["sdk_frameworks"]:
|
||||
combined_lines.append(" .linkedFramework(\"%s\")," % framework)
|
||||
for dylib in module["sdk_dylibs"]:
|
||||
combined_lines.append(" .linkedLibrary(\"%s\")," % dylib)
|
||||
combined_lines.append(" ]")
|
||||
|
||||
elif module_type == "swift_library":
|
||||
combined_lines.append(" swiftSettings: [")
|
||||
combined_lines.append(" .swiftLanguageMode(.v5),")
|
||||
combined_lines.append(" .unsafeFlags([")
|
||||
for flag in module["copts"]:
|
||||
combined_lines.append(" \"%s\"," % flag)
|
||||
combined_lines.append(" ])")
|
||||
combined_lines.append(" ]")
|
||||
combined_lines.append(" ),")
|
||||
elif module["type"] == "root":
|
||||
pass
|
||||
else:
|
||||
print("Unknown module type: {}".format(module["type"]))
|
||||
sys.exit(1)
|
||||
|
||||
combined_lines.append(" ]")
|
||||
combined_lines.append(")")
|
||||
combined_lines.append("")
|
||||
|
||||
with open("Package.swift", "w") as f:
|
||||
f.write("\n".join(combined_lines))
|
Loading…
x
Reference in New Issue
Block a user