diff --git a/.github/scripts/common.py b/.github/scripts/common.py index 26621a14..05079d8a 100644 --- a/.github/scripts/common.py +++ b/.github/scripts/common.py @@ -121,6 +121,12 @@ def major_version(version: str) -> str: ) ), ), + "CoreKiwix.xcframework": ( + INSTALL_DIR / "lib", + ( + "CoreKiwix.xcframework/", + ) + ) } DATE = date.today().isoformat() @@ -334,8 +340,12 @@ def make_archive(project, make_release): if not platform_name: return None + if platform_name == "xcframework" and project == "libkiwix": + project_for_files = "CoreKiwix.xcframework" + else: + project_for_files = project try: - base_dir, export_files = EXPORT_FILES[project] + base_dir, export_files = EXPORT_FILES[project_for_files] except KeyError: # No binary files to export return None diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77a0d440..ee295fe8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -151,6 +151,7 @@ jobs: - macOS_arm64_static - macOS_arm64_mixed - macOS_x86_64 + - apple_all_static runs-on: macos-13 env: SSH_KEY: /tmp/id_rsa diff --git a/.github/workflows/releaseNigthly.yml b/.github/workflows/releaseNigthly.yml index 2166e663..85be5241 100644 --- a/.github/workflows/releaseNigthly.yml +++ b/.github/workflows/releaseNigthly.yml @@ -138,6 +138,7 @@ jobs: - native_mixed - macOS_arm64_static - macOS_arm64_mixed + - apple_all_static runs-on: macos-13 env: SSH_KEY: /tmp/id_rsa diff --git a/kiwixbuild/dependencies/apple_xcframework.py b/kiwixbuild/dependencies/apple_xcframework.py new file mode 100644 index 00000000..d6c19ce7 --- /dev/null +++ b/kiwixbuild/dependencies/apple_xcframework.py @@ -0,0 +1,98 @@ +import os +import shutil +from pathlib import Path + +from kiwixbuild.platforms import PlatformInfo +from kiwixbuild.utils import pj, run_command +from .base import Dependency, NoopSource, Builder as BaseBuilder + + +class AppleXCFramework(Dependency): + all_targets = ["macOS_x86_64", "macOS_arm64_static", "iOS_x86_64", "iOS_arm64"] + name = "apple_xcframework" + + Source = NoopSource + + class Builder(BaseBuilder): + @classmethod + def all_targets(cls): + return AppleXCFramework.all_targets + + @property + def macos_targets(self): + return [ + target for target in self.all_targets() if target.startswith("macOS") + ] + + @property + def ios_targets(self): + return [target for target in self.all_targets() if target.startswith("iOS")] + + @classmethod + def get_dependencies(cls, platfomInfo, alldeps): + return [(target, "libkiwix") for target in cls.all_targets()] + + @property + def final_path(self): + return pj(self.buildEnv.install_dir, "lib", "CoreKiwix.xcframework") + + def _remove_if_exists(self, context): + if not os.path.exists(self.final_path): + return + + shutil.rmtree(self.final_path) + + def _merge_libs(self, context): + """create merged.a in all targets to bundle all static archives""" + xcf_libs = [] + for target in self.all_targets(): + static_ars = [] + + plt = PlatformInfo.get_platform(target) + lib_dir = pj(plt.buildEnv.install_dir, "lib") + static_ars = [str(f) for f in Path(lib_dir).glob("*.a")] + + # create merged.a from all *.a in install_dir/lib + command = "libtool -static -o merged.a " + command += " ".join(static_ars) + run_command(command, lib_dir, context) + + # will be included in xcframework + if target in self.ios_targets: + xcf_libs.append(pj(lib_dir, "merged.a")) + + return xcf_libs + + def _make_macos_fat(self, context): + """create fat merged.a in fake macOS_fat install/lib with macOS archs""" + macos_libs = [] + for target in self.macos_targets: + plt = PlatformInfo.get_platform(target) + macos_libs.append(pj(plt.buildEnv.install_dir, "lib", "merged.a")) + + fat_dir = pj(self.buildEnv.build_dir, "macOS_fat") + os.makedirs(fat_dir, exist_ok=True) + + command = "lipo -create -output {fat_dir}/merged.a ".format(fat_dir=fat_dir) + command += " ".join(macos_libs) + run_command(command, self.buildEnv.build_dir, context) + + return [pj(fat_dir, "merged.a")] + + def _build_xcframework(self, xcf_libs, context): + # create xcframework + ref_plat = PlatformInfo.get_platform(self.macos_targets[0]) + command = "xcodebuild -create-xcframework " + for lib in xcf_libs: + command += " -library {lib} -headers {include}".format( + lib=lib, include=pj(ref_plat.buildEnv.install_dir, "include") + ) + command += " -output {dest}".format(dest=self.final_path) + run_command(command, self.buildEnv.build_dir, context) + + def build(self): + xcf_libs = [] + self.command("remove_if_exists", self._remove_if_exists) + xcf_libs += self.command("merge_libs", self._merge_libs) + xcf_libs += self.command("make_macos_fat", self._make_macos_fat) + self.command("build_xcframework", self._build_xcframework, xcf_libs) diff --git a/kiwixbuild/platforms/ios.py b/kiwixbuild/platforms/ios.py index 065a62cf..2ac948df 100644 --- a/kiwixbuild/platforms/ios.py +++ b/kiwixbuild/platforms/ios.py @@ -3,6 +3,7 @@ from kiwixbuild._global import option from kiwixbuild.utils import pj, xrun_find from .base import PlatformInfo, MetaPlatformInfo, MixedMixin +from kiwixbuild.dependencies.apple_xcframework import AppleXCFramework class ApplePlatformInfo(PlatformInfo): @@ -181,3 +182,18 @@ def add_targets(self, targetName, targets): def __str__(self): return self.name + +class AppleStaticAll(MetaPlatformInfo): + name = "apple_all_static" + compatible_hosts = ['Darwin'] + + @property + def subPlatformNames(self): + return AppleXCFramework.all_targets + + def add_targets(self, targetName, targets): + super().add_targets(targetName, targets) + return PlatformInfo.add_targets(self, 'apple_xcframework', targets) + + def __str__(self): + return self.name