a0537d633f
The SDK was missing SDKSettings files. This is usually not a problem for Nix builds, because we generate our own fake SDK structure when necessary (in xcbuild), but not having these files blocks using the upstream Apple SDK in tooling such as gen-frameworks.py.
148 lines
5.0 KiB
Python
Executable File
148 lines
5.0 KiB
Python
Executable File
#!/usr/bin/env nix-shell
|
|
#!nix-shell -i python -p python3 swiftPackages.swift-unwrapped
|
|
|
|
"""
|
|
Generate a frameworks.nix for a macOS SDK.
|
|
|
|
You may point this tool at an Xcode bundled SDK, but more ideal is using the
|
|
SDK from Nixpkgs. For example:
|
|
|
|
SDK_PATH="$(nix-build --no-link -A darwin.apple_sdk_11_0.MacOSX-SDK)"
|
|
./gen-frameworks.py "$SDK_PATH" > ./new-frameworks.nix
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
ALLOWED_LIBS = ["simd"]
|
|
|
|
HEADER = """\
|
|
# This file is generated by gen-frameworks.nix.
|
|
# Do not edit, put overrides in apple_sdk.nix instead.
|
|
{ libs, frameworks }: with libs; with frameworks;
|
|
{
|
|
"""
|
|
|
|
FOOTER = """\
|
|
}
|
|
"""
|
|
|
|
|
|
def eprint(*args):
|
|
print(*args, file=sys.stderr)
|
|
|
|
|
|
def name_from_ident(ident):
|
|
return ident.get("swift", ident.get("clang"))
|
|
|
|
|
|
def scan_sdk(sdk):
|
|
# Find frameworks by scanning the SDK frameworks directory.
|
|
frameworks = [
|
|
framework.removesuffix(".framework")
|
|
for framework in os.listdir(f"{sdk}/System/Library/Frameworks")
|
|
if not framework.startswith("_")
|
|
]
|
|
frameworks.sort()
|
|
|
|
# Determine the longest name for padding output.
|
|
width = len(max(frameworks, key=len))
|
|
|
|
output = HEADER
|
|
|
|
for framework in frameworks:
|
|
deps = []
|
|
|
|
# Use Swift to scan dependencies, because a module may have both Clang
|
|
# and Swift parts. Using Clang only imports the Clang module, whereas
|
|
# using Swift will usually import both Clang + Swift overlay.
|
|
#
|
|
# TODO: The above is an assumption. Not sure if it's possible a Swift
|
|
# module completely shadows a Clang module. (Seems unlikely)
|
|
#
|
|
# TODO: Handle "module 'Foobar' is incompatible with feature 'swift'"
|
|
#
|
|
# If there were a similar Clang invocation for scanning, we could fix
|
|
# the above todos, but that doesn't appear to exist.
|
|
eprint(f"# scanning {framework}")
|
|
result = subprocess.run(
|
|
[
|
|
"swiftc",
|
|
"-scan-dependencies",
|
|
# We provide a source snippet via stdin.
|
|
"-",
|
|
# Use the provided SDK.
|
|
"-sdk",
|
|
sdk,
|
|
# This search path is normally added automatically by the
|
|
# compiler based on the SDK, but we have a patch in place that
|
|
# removes that for SDKs in /nix/store, because our xcbuild stub
|
|
# SDK doesn't have the directory.
|
|
# (swift-prevent-sdk-dirs-warning.patch)
|
|
"-I",
|
|
f"{sdk}/usr/lib/swift",
|
|
# For some reason, 'lib/swift/shims' from both the SDK and
|
|
# Swift compiler are picked up, causing redefinition errors.
|
|
# This eliminates the latter.
|
|
"-resource-dir",
|
|
f"{sdk}/usr/lib/swift",
|
|
],
|
|
input=f"import {framework}".encode(),
|
|
stdout=subprocess.PIPE,
|
|
)
|
|
if result.returncode != 0:
|
|
eprint(f"# Scanning {framework} failed (exit code {result.returncode})")
|
|
result.stdout = b""
|
|
|
|
# Parse JSON output.
|
|
if len(result.stdout) != 0:
|
|
data = json.loads(result.stdout)
|
|
|
|
# Entries in the modules list come in pairs. The first is an
|
|
# identifier (`{ swift: "foobar" }` or `{ clang: "foobar" }`), and
|
|
# the second metadata for that module. Here we look for the pair
|
|
# that matches the framework we're scanning (and ignore the rest).
|
|
modules = data["modules"]
|
|
for i in range(0, len(modules), 2):
|
|
ident, meta = modules[i : i + 2]
|
|
|
|
# NOTE: We may match twice, for a Swift module _and_ for a
|
|
# Clang module. So matching here doesn't break from the loop,
|
|
# and deps is appended to.
|
|
if name_from_ident(ident) == framework:
|
|
dep_idents = meta["directDependencies"]
|
|
deps += [name_from_ident(ident) for ident in dep_idents]
|
|
# List unfiltered deps in progress output.
|
|
eprint(ident, "->", dep_idents)
|
|
|
|
# Filter out modules that are not separate derivations.
|
|
# Also filter out duplicates (when a Swift overlay imports the Clang module)
|
|
allowed = frameworks + ALLOWED_LIBS
|
|
deps = set([dep for dep in deps if dep in allowed])
|
|
|
|
# Filter out self-references. (Swift overlay importing Clang module.)
|
|
if framework in deps:
|
|
deps.remove(framework)
|
|
|
|
# Generate a Nix attribute line.
|
|
if len(deps) != 0:
|
|
deps = list(deps)
|
|
deps.sort()
|
|
deps = " ".join(deps)
|
|
output += f" {framework.ljust(width)} = {{ inherit {deps}; }};\n"
|
|
else:
|
|
output += f" {framework.ljust(width)} = {{}};\n"
|
|
|
|
output += FOOTER
|
|
sys.stdout.write(output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) != 2:
|
|
eprint(f"Usage: {sys.argv[0]} <path to MacOSX.sdk>")
|
|
sys.exit(64)
|
|
|
|
scan_sdk(sys.argv[1])
|