···1-#!/usr/bin/env nix-shell
2-#!nix-shell -i python -p python3 swiftPackages.swift-unwrapped
3-4-"""
5-Generate a baseline frameworks.nix for a macOS SDK.
6-"""
7-8-import json
9-import os
10-import subprocess
11-import sys
12-13-ALLOWED_LIBS = ["simd"]
14-15-HEADER = """\
16-{ libs, frameworks }: with libs; with frameworks;
17-{
18-"""
19-20-FOOTER = """\
21-}
22-"""
23-24-25-def eprint(*args):
26- print(*args, file=sys.stderr)
27-28-29-def name_from_ident(ident):
30- return ident.get("swift", ident.get("clang"))
31-32-33-def scan_sdk(sdk):
34- # Find frameworks by scanning the SDK frameworks directory.
35- frameworks = [
36- framework.removesuffix(".framework")
37- for framework in os.listdir(f"{sdk}/System/Library/Frameworks")
38- if not framework.startswith("_")
39- ]
40- frameworks.sort()
41-42- # Determine the longest name for padding output.
43- width = len(max(frameworks, key=len))
44-45- output = HEADER
46-47- for framework in frameworks:
48- deps = []
49-50- # Use Swift to scan dependencies, because a module may have both Clang
51- # and Swift parts. Using Clang only imports the Clang module, whereas
52- # using Swift will usually import both Clang + Swift overlay.
53- #
54- # TODO: The above is an assumption. Not sure if it's possible a Swift
55- # module completely shadows a Clang module. (Seems unlikely)
56- #
57- # TODO: Handle "module 'Foobar' is incompatible with feature 'swift'"
58- #
59- # If there were a similar Clang invocation for scanning, we could fix
60- # the above todos, but that doesn't appear to exist.
61- eprint(f"# scanning {framework}")
62- result = subprocess.run(
63- [
64- "swiftc",
65- "-scan-dependencies",
66- # We provide a source snippet via stdin.
67- "-",
68- # Use the provided SDK.
69- "-sdk",
70- sdk,
71- # This search path is normally added automatically by the
72- # compiler based on the SDK, but we have a patch in place that
73- # removes that for SDKs in /nix/store, because our xcbuild stub
74- # SDK doesn't have the directory.
75- # (swift-prevent-sdk-dirs-warning.patch)
76- "-I",
77- f"{sdk}/usr/lib/swift",
78- # For some reason, 'lib/swift/shims' from both the SDK and
79- # Swift compiler are picked up, causing redefinition errors.
80- # This eliminates the latter.
81- "-resource-dir",
82- f"{sdk}/usr/lib/swift",
83- ],
84- input=f"import {framework}".encode(),
85- stdout=subprocess.PIPE,
86- )
87- if result.returncode != 0:
88- eprint(f"# Scanning {framework} failed (exit code {result.returncode})")
89- result.stdout = b""
90-91- # Parse JSON output.
92- if len(result.stdout) != 0:
93- data = json.loads(result.stdout)
94-95- # Entries in the modules list come in pairs. The first is an
96- # identifier (`{ swift: "foobar" }` or `{ clang: "foobar" }`), and
97- # the second metadata for that module. Here we look for the pair
98- # that matches the framework we're scanning (and ignore the rest).
99- modules = data["modules"]
100- for i in range(0, len(modules), 2):
101- ident, meta = modules[i : i + 2]
102-103- # NOTE: We may match twice, for a Swift module _and_ for a
104- # Clang module. So matching here doesn't break from the loop,
105- # and deps is appended to.
106- if name_from_ident(ident) == framework:
107- dep_idents = meta["directDependencies"]
108- deps += [name_from_ident(ident) for ident in dep_idents]
109- # List unfiltered deps in progress output.
110- eprint(ident, "->", dep_idents)
111-112- # Filter out modules that are not separate derivations.
113- # Also filter out duplicates (when a Swift overlay imports the Clang module)
114- allowed = frameworks + ALLOWED_LIBS
115- deps = set([dep for dep in deps if dep in allowed])
116-117- # Filter out self-references. (Swift overlay importing Clang module.)
118- if framework in deps:
119- deps.remove(framework)
120-121- # Generate a Nix attribute line.
122- if len(deps) != 0:
123- deps = list(deps)
124- deps.sort()
125- deps = " ".join(deps)
126- output += f" {framework.ljust(width)} = {{ inherit {deps}; }};\n"
127- else:
128- output += f" {framework.ljust(width)} = {{}};\n"
129-130- output += FOOTER
131- sys.stdout.write(output)
132-133-134-if __name__ == "__main__":
135- if len(sys.argv) != 2:
136- eprint(f"Usage: {sys.argv[0]} <path to MacOSX.sdk>")
137- sys.exit(64)
138-139- scan_sdk(sys.argv[1])
···1+#!/usr/bin/env nix-shell
2+#!nix-shell -i python -p python3 swiftPackages.swift-unwrapped
3+4+"""
5+Generate a frameworks.nix for a macOS SDK.
6+"""
7+8+import json
9+import os
10+import subprocess
11+import sys
12+13+ALLOWED_LIBS = ["simd"]
14+15+HEADER = """\
16+# This file is generated by gen-frameworks.nix.
17+# Do not edit, put overrides in apple_sdk.nix instead.
18+{ libs, frameworks }: with libs; with frameworks;
19+{
20+"""
21+22+FOOTER = """\
23+}
24+"""
25+26+27+def eprint(*args):
28+ print(*args, file=sys.stderr)
29+30+31+def name_from_ident(ident):
32+ return ident.get("swift", ident.get("clang"))
33+34+35+def scan_sdk(sdk):
36+ # Find frameworks by scanning the SDK frameworks directory.
37+ frameworks = [
38+ framework.removesuffix(".framework")
39+ for framework in os.listdir(f"{sdk}/System/Library/Frameworks")
40+ if not framework.startswith("_")
41+ ]
42+ frameworks.sort()
43+44+ # Determine the longest name for padding output.
45+ width = len(max(frameworks, key=len))
46+47+ output = HEADER
48+49+ for framework in frameworks:
50+ deps = []
51+52+ # Use Swift to scan dependencies, because a module may have both Clang
53+ # and Swift parts. Using Clang only imports the Clang module, whereas
54+ # using Swift will usually import both Clang + Swift overlay.
55+ #
56+ # TODO: The above is an assumption. Not sure if it's possible a Swift
57+ # module completely shadows a Clang module. (Seems unlikely)
58+ #
59+ # TODO: Handle "module 'Foobar' is incompatible with feature 'swift'"
60+ #
61+ # If there were a similar Clang invocation for scanning, we could fix
62+ # the above todos, but that doesn't appear to exist.
63+ eprint(f"# scanning {framework}")
64+ result = subprocess.run(
65+ [
66+ "swiftc",
67+ "-scan-dependencies",
68+ # We provide a source snippet via stdin.
69+ "-",
70+ # Use the provided SDK.
71+ "-sdk",
72+ sdk,
73+ # This search path is normally added automatically by the
74+ # compiler based on the SDK, but we have a patch in place that
75+ # removes that for SDKs in /nix/store, because our xcbuild stub
76+ # SDK doesn't have the directory.
77+ # (swift-prevent-sdk-dirs-warning.patch)
78+ "-I",
79+ f"{sdk}/usr/lib/swift",
80+ # For some reason, 'lib/swift/shims' from both the SDK and
81+ # Swift compiler are picked up, causing redefinition errors.
82+ # This eliminates the latter.
83+ "-resource-dir",
84+ f"{sdk}/usr/lib/swift",
85+ ],
86+ input=f"import {framework}".encode(),
87+ stdout=subprocess.PIPE,
88+ )
89+ if result.returncode != 0:
90+ eprint(f"# Scanning {framework} failed (exit code {result.returncode})")
91+ result.stdout = b""
92+93+ # Parse JSON output.
94+ if len(result.stdout) != 0:
95+ data = json.loads(result.stdout)
96+97+ # Entries in the modules list come in pairs. The first is an
98+ # identifier (`{ swift: "foobar" }` or `{ clang: "foobar" }`), and
99+ # the second metadata for that module. Here we look for the pair
100+ # that matches the framework we're scanning (and ignore the rest).
101+ modules = data["modules"]
102+ for i in range(0, len(modules), 2):
103+ ident, meta = modules[i : i + 2]
104+105+ # NOTE: We may match twice, for a Swift module _and_ for a
106+ # Clang module. So matching here doesn't break from the loop,
107+ # and deps is appended to.
108+ if name_from_ident(ident) == framework:
109+ dep_idents = meta["directDependencies"]
110+ deps += [name_from_ident(ident) for ident in dep_idents]
111+ # List unfiltered deps in progress output.
112+ eprint(ident, "->", dep_idents)
113+114+ # Filter out modules that are not separate derivations.
115+ # Also filter out duplicates (when a Swift overlay imports the Clang module)
116+ allowed = frameworks + ALLOWED_LIBS
117+ deps = set([dep for dep in deps if dep in allowed])
118+119+ # Filter out self-references. (Swift overlay importing Clang module.)
120+ if framework in deps:
121+ deps.remove(framework)
122+123+ # Generate a Nix attribute line.
124+ if len(deps) != 0:
125+ deps = list(deps)
126+ deps.sort()
127+ deps = " ".join(deps)
128+ output += f" {framework.ljust(width)} = {{ inherit {deps}; }};\n"
129+ else:
130+ output += f" {framework.ljust(width)} = {{}};\n"
131+132+ output += FOOTER
133+ sys.stdout.write(output)
134+135+136+if __name__ == "__main__":
137+ if len(sys.argv) != 2:
138+ eprint(f"Usage: {sys.argv[0]} <path to MacOSX.sdk>")
139+ sys.exit(64)
140+141+ scan_sdk(sys.argv[1])