Skip to content

Commit

Permalink
Build CoreKiwix.xcframework
Browse files Browse the repository at this point in the history
CoreKiwix.xcframework is the packaging format required by Kiwix apple (macOS/iOS) reader for libkiwix.
Naming will hopefully be revisited later (libkiwix.xcframework?)

This folder is a combination of all static archive (*.a): dependencies and libkiwix itself
for all apple-supported platforms: macOS x86_64, macOS arm64, iOS arm64 and iOS x86_64.
It also includes the headers.
Note: while iOS archs are separated in the framework, the macOS archs are bundled as a fat binary.

This thus adds:

- A new `apple_all_static` target that lists mentioned sub-targets. Is name OK?
- A new `AppleXCFramework` virtual dependency and builder
  -  requires `libkiwix`
  - merges the archives into one per target
  - creates the fat binary for macOS
  - creates the xcframework
- Special handling in github/common to produce the archive from diff. files archive
  - it's a libkiwix export afterall
  - requires a new `xcframework` (is name appropriate?) platform_name in builddef

---

https://developer.apple.com/documentation/xcode/creating-a-multi-platform-binary-framework-bundle
  • Loading branch information
rgaudin committed Nov 9, 2023
1 parent 69dc2b7 commit cebcdbf
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 1 deletion.
12 changes: 11 additions & 1 deletion .github/scripts/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ def major_version(version: str) -> str:
)
),
),
"CoreKiwix.xcframework": (
INSTALL_DIR / "lib",
(
"CoreKiwix.xcframework/",
)
)
}

DATE = date.today().isoformat()
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/releaseNigthly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
98 changes: 98 additions & 0 deletions kiwixbuild/dependencies/apple_xcframework.py
Original file line number Diff line number Diff line change
@@ -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)
16 changes: 16 additions & 0 deletions kiwixbuild/platforms/ios.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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

0 comments on commit cebcdbf

Please sign in to comment.