···11-#!/usr/bin/env nix-shell
22-#!nix-shell -i python -p python3 swiftPackages.swift-unwrapped
33-44-"""
55-Generate a baseline frameworks.nix for a macOS SDK.
66-"""
77-88-import json
99-import os
1010-import subprocess
1111-import sys
1212-1313-ALLOWED_LIBS = ["simd"]
1414-1515-HEADER = """\
1616-{ libs, frameworks }: with libs; with frameworks;
1717-{
1818-"""
1919-2020-FOOTER = """\
2121-}
2222-"""
2323-2424-2525-def eprint(*args):
2626- print(*args, file=sys.stderr)
2727-2828-2929-def name_from_ident(ident):
3030- return ident.get("swift", ident.get("clang"))
3131-3232-3333-def scan_sdk(sdk):
3434- # Find frameworks by scanning the SDK frameworks directory.
3535- frameworks = [
3636- framework.removesuffix(".framework")
3737- for framework in os.listdir(f"{sdk}/System/Library/Frameworks")
3838- if not framework.startswith("_")
3939- ]
4040- frameworks.sort()
4141-4242- # Determine the longest name for padding output.
4343- width = len(max(frameworks, key=len))
4444-4545- output = HEADER
4646-4747- for framework in frameworks:
4848- deps = []
4949-5050- # Use Swift to scan dependencies, because a module may have both Clang
5151- # and Swift parts. Using Clang only imports the Clang module, whereas
5252- # using Swift will usually import both Clang + Swift overlay.
5353- #
5454- # TODO: The above is an assumption. Not sure if it's possible a Swift
5555- # module completely shadows a Clang module. (Seems unlikely)
5656- #
5757- # TODO: Handle "module 'Foobar' is incompatible with feature 'swift'"
5858- #
5959- # If there were a similar Clang invocation for scanning, we could fix
6060- # the above todos, but that doesn't appear to exist.
6161- eprint(f"# scanning {framework}")
6262- result = subprocess.run(
6363- [
6464- "swiftc",
6565- "-scan-dependencies",
6666- # We provide a source snippet via stdin.
6767- "-",
6868- # Use the provided SDK.
6969- "-sdk",
7070- sdk,
7171- # This search path is normally added automatically by the
7272- # compiler based on the SDK, but we have a patch in place that
7373- # removes that for SDKs in /nix/store, because our xcbuild stub
7474- # SDK doesn't have the directory.
7575- # (swift-prevent-sdk-dirs-warning.patch)
7676- "-I",
7777- f"{sdk}/usr/lib/swift",
7878- # For some reason, 'lib/swift/shims' from both the SDK and
7979- # Swift compiler are picked up, causing redefinition errors.
8080- # This eliminates the latter.
8181- "-resource-dir",
8282- f"{sdk}/usr/lib/swift",
8383- ],
8484- input=f"import {framework}".encode(),
8585- stdout=subprocess.PIPE,
8686- )
8787- if result.returncode != 0:
8888- eprint(f"# Scanning {framework} failed (exit code {result.returncode})")
8989- result.stdout = b""
9090-9191- # Parse JSON output.
9292- if len(result.stdout) != 0:
9393- data = json.loads(result.stdout)
9494-9595- # Entries in the modules list come in pairs. The first is an
9696- # identifier (`{ swift: "foobar" }` or `{ clang: "foobar" }`), and
9797- # the second metadata for that module. Here we look for the pair
9898- # that matches the framework we're scanning (and ignore the rest).
9999- modules = data["modules"]
100100- for i in range(0, len(modules), 2):
101101- ident, meta = modules[i : i + 2]
102102-103103- # NOTE: We may match twice, for a Swift module _and_ for a
104104- # Clang module. So matching here doesn't break from the loop,
105105- # and deps is appended to.
106106- if name_from_ident(ident) == framework:
107107- dep_idents = meta["directDependencies"]
108108- deps += [name_from_ident(ident) for ident in dep_idents]
109109- # List unfiltered deps in progress output.
110110- eprint(ident, "->", dep_idents)
111111-112112- # Filter out modules that are not separate derivations.
113113- # Also filter out duplicates (when a Swift overlay imports the Clang module)
114114- allowed = frameworks + ALLOWED_LIBS
115115- deps = set([dep for dep in deps if dep in allowed])
116116-117117- # Filter out self-references. (Swift overlay importing Clang module.)
118118- if framework in deps:
119119- deps.remove(framework)
120120-121121- # Generate a Nix attribute line.
122122- if len(deps) != 0:
123123- deps = list(deps)
124124- deps.sort()
125125- deps = " ".join(deps)
126126- output += f" {framework.ljust(width)} = {{ inherit {deps}; }};\n"
127127- else:
128128- output += f" {framework.ljust(width)} = {{}};\n"
129129-130130- output += FOOTER
131131- sys.stdout.write(output)
132132-133133-134134-if __name__ == "__main__":
135135- if len(sys.argv) != 2:
136136- eprint(f"Usage: {sys.argv[0]} <path to MacOSX.sdk>")
137137- sys.exit(64)
138138-139139- scan_sdk(sys.argv[1])
+141
pkgs/os-specific/darwin/gen-frameworks.py
···11+#!/usr/bin/env nix-shell
22+#!nix-shell -i python -p python3 swiftPackages.swift-unwrapped
33+44+"""
55+Generate a frameworks.nix for a macOS SDK.
66+"""
77+88+import json
99+import os
1010+import subprocess
1111+import sys
1212+1313+ALLOWED_LIBS = ["simd"]
1414+1515+HEADER = """\
1616+# This file is generated by gen-frameworks.nix.
1717+# Do not edit, put overrides in apple_sdk.nix instead.
1818+{ libs, frameworks }: with libs; with frameworks;
1919+{
2020+"""
2121+2222+FOOTER = """\
2323+}
2424+"""
2525+2626+2727+def eprint(*args):
2828+ print(*args, file=sys.stderr)
2929+3030+3131+def name_from_ident(ident):
3232+ return ident.get("swift", ident.get("clang"))
3333+3434+3535+def scan_sdk(sdk):
3636+ # Find frameworks by scanning the SDK frameworks directory.
3737+ frameworks = [
3838+ framework.removesuffix(".framework")
3939+ for framework in os.listdir(f"{sdk}/System/Library/Frameworks")
4040+ if not framework.startswith("_")
4141+ ]
4242+ frameworks.sort()
4343+4444+ # Determine the longest name for padding output.
4545+ width = len(max(frameworks, key=len))
4646+4747+ output = HEADER
4848+4949+ for framework in frameworks:
5050+ deps = []
5151+5252+ # Use Swift to scan dependencies, because a module may have both Clang
5353+ # and Swift parts. Using Clang only imports the Clang module, whereas
5454+ # using Swift will usually import both Clang + Swift overlay.
5555+ #
5656+ # TODO: The above is an assumption. Not sure if it's possible a Swift
5757+ # module completely shadows a Clang module. (Seems unlikely)
5858+ #
5959+ # TODO: Handle "module 'Foobar' is incompatible with feature 'swift'"
6060+ #
6161+ # If there were a similar Clang invocation for scanning, we could fix
6262+ # the above todos, but that doesn't appear to exist.
6363+ eprint(f"# scanning {framework}")
6464+ result = subprocess.run(
6565+ [
6666+ "swiftc",
6767+ "-scan-dependencies",
6868+ # We provide a source snippet via stdin.
6969+ "-",
7070+ # Use the provided SDK.
7171+ "-sdk",
7272+ sdk,
7373+ # This search path is normally added automatically by the
7474+ # compiler based on the SDK, but we have a patch in place that
7575+ # removes that for SDKs in /nix/store, because our xcbuild stub
7676+ # SDK doesn't have the directory.
7777+ # (swift-prevent-sdk-dirs-warning.patch)
7878+ "-I",
7979+ f"{sdk}/usr/lib/swift",
8080+ # For some reason, 'lib/swift/shims' from both the SDK and
8181+ # Swift compiler are picked up, causing redefinition errors.
8282+ # This eliminates the latter.
8383+ "-resource-dir",
8484+ f"{sdk}/usr/lib/swift",
8585+ ],
8686+ input=f"import {framework}".encode(),
8787+ stdout=subprocess.PIPE,
8888+ )
8989+ if result.returncode != 0:
9090+ eprint(f"# Scanning {framework} failed (exit code {result.returncode})")
9191+ result.stdout = b""
9292+9393+ # Parse JSON output.
9494+ if len(result.stdout) != 0:
9595+ data = json.loads(result.stdout)
9696+9797+ # Entries in the modules list come in pairs. The first is an
9898+ # identifier (`{ swift: "foobar" }` or `{ clang: "foobar" }`), and
9999+ # the second metadata for that module. Here we look for the pair
100100+ # that matches the framework we're scanning (and ignore the rest).
101101+ modules = data["modules"]
102102+ for i in range(0, len(modules), 2):
103103+ ident, meta = modules[i : i + 2]
104104+105105+ # NOTE: We may match twice, for a Swift module _and_ for a
106106+ # Clang module. So matching here doesn't break from the loop,
107107+ # and deps is appended to.
108108+ if name_from_ident(ident) == framework:
109109+ dep_idents = meta["directDependencies"]
110110+ deps += [name_from_ident(ident) for ident in dep_idents]
111111+ # List unfiltered deps in progress output.
112112+ eprint(ident, "->", dep_idents)
113113+114114+ # Filter out modules that are not separate derivations.
115115+ # Also filter out duplicates (when a Swift overlay imports the Clang module)
116116+ allowed = frameworks + ALLOWED_LIBS
117117+ deps = set([dep for dep in deps if dep in allowed])
118118+119119+ # Filter out self-references. (Swift overlay importing Clang module.)
120120+ if framework in deps:
121121+ deps.remove(framework)
122122+123123+ # Generate a Nix attribute line.
124124+ if len(deps) != 0:
125125+ deps = list(deps)
126126+ deps.sort()
127127+ deps = " ".join(deps)
128128+ output += f" {framework.ljust(width)} = {{ inherit {deps}; }};\n"
129129+ else:
130130+ output += f" {framework.ljust(width)} = {{}};\n"
131131+132132+ output += FOOTER
133133+ sys.stdout.write(output)
134134+135135+136136+if __name__ == "__main__":
137137+ if len(sys.argv) != 2:
138138+ eprint(f"Usage: {sys.argv[0]} <path to MacOSX.sdk>")
139139+ sys.exit(64)
140140+141141+ scan_sdk(sys.argv[1])