cg-code-graph 0.10.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cg_code_graph-0.10.1.dist-info/METADATA +678 -0
- cg_code_graph-0.10.1.dist-info/RECORD +174 -0
- cg_code_graph-0.10.1.dist-info/WHEEL +5 -0
- cg_code_graph-0.10.1.dist-info/entry_points.txt +3 -0
- cg_code_graph-0.10.1.dist-info/licenses/LICENSE +21 -0
- cg_code_graph-0.10.1.dist-info/top_level.txt +1 -0
- codegraph/__init__.py +2 -0
- codegraph/aitools.py +129 -0
- codegraph/apps.py +76 -0
- codegraph/blindspots.py +428 -0
- codegraph/bridges.py +1701 -0
- codegraph/cli.py +725 -0
- codegraph/concepts.py +362 -0
- codegraph/config.py +559 -0
- codegraph/core/__init__.py +0 -0
- codegraph/core/cache.py +375 -0
- codegraph/core/detect.py +80 -0
- codegraph/core/extractors.py +187 -0
- codegraph/core/fsutil.py +61 -0
- codegraph/core/generated.py +575 -0
- codegraph/core/model.py +174 -0
- codegraph/core/paths.py +175 -0
- codegraph/core/plugin.py +160 -0
- codegraph/core/store.py +80 -0
- codegraph/core/syntax_errors.py +132 -0
- codegraph/coverage.py +928 -0
- codegraph/doctor.py +453 -0
- codegraph/external.py +613 -0
- codegraph/indexer.py +336 -0
- codegraph/link.py +434 -0
- codegraph/lint_async.py +524 -0
- codegraph/mcp_server.py +1303 -0
- codegraph/parity.py +473 -0
- codegraph/parity_structure.py +307 -0
- codegraph/payload.py +321 -0
- codegraph/plans.py +1285 -0
- codegraph/platform_scan.py +643 -0
- codegraph/platforms.py +1369 -0
- codegraph/plugins/__init__.py +0 -0
- codegraph/plugins/cfamily/__init__.py +0 -0
- codegraph/plugins/cfamily/plugin.py +930 -0
- codegraph/plugins/cfamily/syntax.py +881 -0
- codegraph/plugins/dart/__init__.py +0 -0
- codegraph/plugins/dart/bridges.py +345 -0
- codegraph/plugins/dart/extractor/bin/extract.dart +717 -0
- codegraph/plugins/dart/extractor/pubspec.lock +149 -0
- codegraph/plugins/dart/extractor/pubspec.yaml +7 -0
- codegraph/plugins/dart/http.py +904 -0
- codegraph/plugins/dart/models.py +308 -0
- codegraph/plugins/dart/plugin.py +625 -0
- codegraph/plugins/dart/program.py +907 -0
- codegraph/plugins/django/__init__.py +0 -0
- codegraph/plugins/django/extras.py +378 -0
- codegraph/plugins/django/models.py +508 -0
- codegraph/plugins/django/plugin.py +728 -0
- codegraph/plugins/django/schemas.py +339 -0
- codegraph/plugins/django/shapes.py +216 -0
- codegraph/plugins/django/urls.py +603 -0
- codegraph/plugins/express/__init__.py +0 -0
- codegraph/plugins/express/plugin.py +428 -0
- codegraph/plugins/flutter/__init__.py +0 -0
- codegraph/plugins/flutter/plugin.py +538 -0
- codegraph/plugins/kotlin/__init__.py +0 -0
- codegraph/plugins/kotlin/exact.py +457 -0
- codegraph/plugins/kotlin/plugin.py +1961 -0
- codegraph/plugins/kotlin/reparse.py +234 -0
- codegraph/plugins/laravel/__init__.py +0 -0
- codegraph/plugins/laravel/broadcast.py +351 -0
- codegraph/plugins/laravel/plugin.py +863 -0
- codegraph/plugins/laravel/tests.py +262 -0
- codegraph/plugins/laravel/values.py +728 -0
- codegraph/plugins/native/__init__.py +0 -0
- codegraph/plugins/native/gates.py +286 -0
- codegraph/plugins/native/runner.py +183 -0
- codegraph/plugins/native/scipread.py +194 -0
- codegraph/plugins/native/ts.py +54 -0
- codegraph/plugins/nest/__init__.py +0 -0
- codegraph/plugins/nest/plugin.py +654 -0
- codegraph/plugins/nextjs/__init__.py +0 -0
- codegraph/plugins/nextjs/plugin.py +336 -0
- codegraph/plugins/nuxt/__init__.py +0 -0
- codegraph/plugins/nuxt/plugin.py +308 -0
- codegraph/plugins/php/__init__.py +0 -0
- codegraph/plugins/php/extractor/composer.json +5 -0
- codegraph/plugins/php/extractor/composer.lock +76 -0
- codegraph/plugins/php/extractor/extract.php +743 -0
- codegraph/plugins/php/gating.py +573 -0
- codegraph/plugins/php/plugin.py +668 -0
- codegraph/plugins/php/strings.py +197 -0
- codegraph/plugins/python/__init__.py +0 -0
- codegraph/plugins/python/aitools.py +664 -0
- codegraph/plugins/python/external.py +245 -0
- codegraph/plugins/python/fields.py +107 -0
- codegraph/plugins/python/plugin.py +1733 -0
- codegraph/plugins/python/refs.py +485 -0
- codegraph/plugins/python/roots.py +412 -0
- codegraph/plugins/python/socketio.py +210 -0
- codegraph/plugins/python/subproc.py +864 -0
- codegraph/plugins/python/tests.py +1040 -0
- codegraph/plugins/python/values.py +179 -0
- codegraph/plugins/pyweb/__init__.py +0 -0
- codegraph/plugins/pyweb/plugin.py +1334 -0
- codegraph/plugins/pyweb/values.py +68 -0
- codegraph/plugins/rust/__init__.py +0 -0
- codegraph/plugins/rust/cargo.py +226 -0
- codegraph/plugins/rust/plugin.py +980 -0
- codegraph/plugins/rust/syntax.py +678 -0
- codegraph/plugins/scip/__init__.py +0 -0
- codegraph/plugins/scip/importer.py +129 -0
- codegraph/plugins/scip/scip.proto +962 -0
- codegraph/plugins/scip/scip_pb2.py +97 -0
- codegraph/plugins/stubs/__init__.py +0 -0
- codegraph/plugins/stubs/plugins.py +38 -0
- codegraph/plugins/swift/__init__.py +0 -0
- codegraph/plugins/swift/baseurl.py +109 -0
- codegraph/plugins/swift/exact.py +415 -0
- codegraph/plugins/swift/indexstore.py +209 -0
- codegraph/plugins/swift/packages.py +174 -0
- codegraph/plugins/swift/plugin.py +2890 -0
- codegraph/plugins/ts/__init__.py +0 -0
- codegraph/plugins/ts/baseurl.py +185 -0
- codegraph/plugins/ts/extractor/extract.mjs +2652 -0
- codegraph/plugins/ts/extractor/fw.mjs +685 -0
- codegraph/plugins/ts/extractor/package-lock.json +205 -0
- codegraph/plugins/ts/extractor/package.json +9 -0
- codegraph/plugins/ts/plugin.py +480 -0
- codegraph/plugins/tsweb/__init__.py +0 -0
- codegraph/plugins/tsweb/common.py +290 -0
- codegraph/plugins/tsweb/data.py +276 -0
- codegraph/presets/__init__.py +146 -0
- codegraph/presets/c_cpp.yaml +9 -0
- codegraph/presets/common.yaml +66 -0
- codegraph/presets/dart.yaml +9 -0
- codegraph/presets/django-ninja.yaml +15 -0
- codegraph/presets/django.yaml +25 -0
- codegraph/presets/djangorestframework.yaml +17 -0
- codegraph/presets/express.yaml +17 -0
- codegraph/presets/kotlin.yaml +11 -0
- codegraph/presets/laravel.yaml +40 -0
- codegraph/presets/nest.yaml +11 -0
- codegraph/presets/nextjs.yaml +15 -0
- codegraph/presets/nuxt.yaml +9 -0
- codegraph/presets/php.yaml +5 -0
- codegraph/presets/python.yaml +10 -0
- codegraph/presets/rust.yaml +5 -0
- codegraph/presets/swift.yaml +10 -0
- codegraph/presets/typescript.yaml +13 -0
- codegraph/process_runs.py +328 -0
- codegraph/protocols/__init__.py +299 -0
- codegraph/protocols/builtin.py +67 -0
- codegraph/protocols/matchers.py +144 -0
- codegraph/protocols/view.py +334 -0
- codegraph/query.py +2089 -0
- codegraph/realtime.py +260 -0
- codegraph/roundtrip.py +346 -0
- codegraph/routes.py +442 -0
- codegraph/starters.py +218 -0
- codegraph/tests_index.py +117 -0
- codegraph/viz/__init__.py +0 -0
- codegraph/viz/graph.py +369 -0
- codegraph/viz/server.py +198 -0
- codegraph/viz/static/app.css +148 -0
- codegraph/viz/static/app.js +1082 -0
- codegraph/viz/static/index.html +81 -0
- codegraph/viz/static/layered.js +237 -0
- codegraph/viz/static/vendor/VERSIONS.txt +4 -0
- codegraph/viz/static/vendor/cose-base.js +3214 -0
- codegraph/viz/static/vendor/cytoscape-fcose.js +1549 -0
- codegraph/viz/static/vendor/cytoscape.min.js +31 -0
- codegraph/viz/static/vendor/layout-base.js +5230 -0
- codegraph/viz/tools/package-lock.json +303 -0
- codegraph/viz/tools/package.json +7 -0
- codegraph/viz/tools/shoot.mjs +165 -0
- codegraph/xcode.py +251 -0
codegraph/xcode.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""Apple build files: the platforms an Xcode project (`*.xcodeproj/project.pbxproj`) and SwiftPM manifests
|
|
2
|
+
(`Package.swift`) build for, and which Xcode target each source file belongs to (#74).
|
|
3
|
+
|
|
4
|
+
`apple_build(root)` returns
|
|
5
|
+
targets {platform: where it came from}: ios / macos / tvos / watchos / visionos
|
|
6
|
+
mac None | "native" (an AppKit macOS target) | "catalyst" (the iOS app built for Mac) | "both"
|
|
7
|
+
membership {file relative to root: {platforms, targets}}: source files of Xcode targets (build phases and Xcode 16
|
|
8
|
+
synchronized folders with their membership exceptions), with the platforms of the targets that
|
|
9
|
+
compile them
|
|
10
|
+
projects the .pbxproj files read
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import re
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
from . import presets
|
|
18
|
+
|
|
19
|
+
SKIP = presets.skip_dirs("common") | {"Pods", "Carthage", ".build", "checkouts", "DerivedData", "SourcePackages"}
|
|
20
|
+
SDK_PLATFORM = {"iphoneos": "ios", "macosx": "macos", "appletvos": "tvos", "watchos": "watchos", "xros": "visionos"}
|
|
21
|
+
SPM_PLATFORM = {"iOS": "ios", "macOS": "macos", "tvOS": "tvos", "watchOS": "watchos", "visionOS": "visionos",
|
|
22
|
+
"macCatalyst": "macos", "driverKit": None}
|
|
23
|
+
APPLE = ("macos", "ios", "tvos", "watchos", "visionos")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ------------------------------------------------------------------ OpenStep plist (project.pbxproj)
|
|
27
|
+
_TOKEN = re.compile(r'\s+|//[^\n]*|/\*.*?\*/|"((?:[^"\\]|\\.)*)"|([^\s{}()=;,"]+)|([{}()=;,])', re.S)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def parse_pbxproj(text: str):
|
|
31
|
+
"""The project.pbxproj dictionary (nested dicts, lists and strings); ValueError on a malformed file."""
|
|
32
|
+
toks = []
|
|
33
|
+
pos = 0
|
|
34
|
+
while pos < len(text):
|
|
35
|
+
m = _TOKEN.match(text, pos)
|
|
36
|
+
if not m:
|
|
37
|
+
raise ValueError(f"pbxproj: unexpected {text[pos:pos + 20]!r}")
|
|
38
|
+
pos = m.end()
|
|
39
|
+
if m.group(1) is not None:
|
|
40
|
+
toks.append(("s", m.group(1).encode().decode("unicode_escape", "replace") if "\\" in m.group(1) else m.group(1)))
|
|
41
|
+
elif m.group(2) is not None:
|
|
42
|
+
toks.append(("s", m.group(2)))
|
|
43
|
+
elif m.group(3) is not None:
|
|
44
|
+
toks.append(("p", m.group(3)))
|
|
45
|
+
i = 0
|
|
46
|
+
|
|
47
|
+
def value():
|
|
48
|
+
nonlocal i
|
|
49
|
+
kind, v = toks[i]
|
|
50
|
+
i += 1
|
|
51
|
+
if kind == "s":
|
|
52
|
+
return v
|
|
53
|
+
if v == "{":
|
|
54
|
+
d = {}
|
|
55
|
+
while toks[i] != ("p", "}"):
|
|
56
|
+
k = value()
|
|
57
|
+
if toks[i] != ("p", "="):
|
|
58
|
+
raise ValueError("pbxproj: '=' expected")
|
|
59
|
+
i += 1
|
|
60
|
+
d[k] = value()
|
|
61
|
+
if toks[i] == ("p", ";"):
|
|
62
|
+
i += 1
|
|
63
|
+
i += 1
|
|
64
|
+
return d
|
|
65
|
+
if v == "(":
|
|
66
|
+
out = []
|
|
67
|
+
while toks[i] != ("p", ")"):
|
|
68
|
+
out.append(value())
|
|
69
|
+
if toks[i] == ("p", ","):
|
|
70
|
+
i += 1
|
|
71
|
+
i += 1
|
|
72
|
+
return out
|
|
73
|
+
raise ValueError(f"pbxproj: unexpected {v!r}")
|
|
74
|
+
try:
|
|
75
|
+
return value()
|
|
76
|
+
except IndexError as e:
|
|
77
|
+
raise ValueError("pbxproj: truncated") from e
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _settings_platforms(bs: dict) -> tuple[set[str], bool]:
|
|
81
|
+
"""Platforms of one build configuration: SUPPORTED_PLATFORMS (else SDKROOT), and SUPPORTS_MACCATALYST."""
|
|
82
|
+
out = set()
|
|
83
|
+
sup = bs.get("SUPPORTED_PLATFORMS")
|
|
84
|
+
for tok in (sup.split() if isinstance(sup, str) else []):
|
|
85
|
+
if tok in SDK_PLATFORM:
|
|
86
|
+
out.add(SDK_PLATFORM[tok])
|
|
87
|
+
if not out:
|
|
88
|
+
sdk = bs.get("SDKROOT")
|
|
89
|
+
if isinstance(sdk, str) and sdk in SDK_PLATFORM:
|
|
90
|
+
out.add(SDK_PLATFORM[sdk])
|
|
91
|
+
cat = str(bs.get("SUPPORTS_MACCATALYST", "")).upper() == "YES"
|
|
92
|
+
return out, cat
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _read_project(pbx: Path, root: Path) -> dict | None:
|
|
96
|
+
try:
|
|
97
|
+
d = parse_pbxproj(pbx.read_text(encoding="utf-8", errors="replace"))
|
|
98
|
+
objs = d["objects"]
|
|
99
|
+
proj = objs[d["rootObject"]]
|
|
100
|
+
except (OSError, ValueError, KeyError, TypeError):
|
|
101
|
+
return None
|
|
102
|
+
base = pbx.parent.parent # the directory holding Foo.xcodeproj
|
|
103
|
+
if proj.get("projectDirPath"):
|
|
104
|
+
base = base / proj["projectDirPath"]
|
|
105
|
+
|
|
106
|
+
def configs(lst_id) -> list[dict]:
|
|
107
|
+
lst = objs.get(lst_id) or {}
|
|
108
|
+
return [(objs.get(c) or {}).get("buildSettings") or {} for c in lst.get("buildConfigurations") or ()]
|
|
109
|
+
proj_bs = configs(proj.get("buildConfigurationList"))
|
|
110
|
+
# file paths: walk the group tree from the main group
|
|
111
|
+
paths: dict[str, Path] = {}
|
|
112
|
+
|
|
113
|
+
def walk(gid, parent: Path):
|
|
114
|
+
g = objs.get(gid) or {}
|
|
115
|
+
st = g.get("sourceTree", "<group>")
|
|
116
|
+
p = g.get("path")
|
|
117
|
+
here = parent if not p else (base / p if st == "SOURCE_ROOT" else Path(p) if st == "<absolute>" else parent / p)
|
|
118
|
+
if st not in ("<group>", "SOURCE_ROOT", "<absolute>"):
|
|
119
|
+
return
|
|
120
|
+
paths[gid] = here
|
|
121
|
+
for c in g.get("children") or ():
|
|
122
|
+
walk(c, here)
|
|
123
|
+
walk(proj.get("mainGroup"), base)
|
|
124
|
+
targets = []
|
|
125
|
+
for tid in proj.get("targets") or ():
|
|
126
|
+
t = objs.get(tid) or {}
|
|
127
|
+
if t.get("isa") != "PBXNativeTarget":
|
|
128
|
+
continue
|
|
129
|
+
plats, cat = set(), False
|
|
130
|
+
for bs in configs(t.get("buildConfigurationList")) or [{}]:
|
|
131
|
+
merged = {**(proj_bs[0] if proj_bs else {}), **bs}
|
|
132
|
+
p, c = _settings_platforms(merged)
|
|
133
|
+
plats |= p
|
|
134
|
+
cat |= c
|
|
135
|
+
files = set()
|
|
136
|
+
for ph in t.get("buildPhases") or ():
|
|
137
|
+
phase = objs.get(ph) or {}
|
|
138
|
+
if phase.get("isa") != "PBXSourcesBuildPhase":
|
|
139
|
+
continue
|
|
140
|
+
for bf in phase.get("files") or ():
|
|
141
|
+
ref = (objs.get(bf) or {}).get("fileRef")
|
|
142
|
+
if ref in paths:
|
|
143
|
+
files.add(paths[ref])
|
|
144
|
+
targets.append({"id": tid, "name": t.get("name") or tid, "platforms": plats, "catalyst": cat, "files": files,
|
|
145
|
+
"groups": set(t.get("fileSystemSynchronizedGroups") or ())})
|
|
146
|
+
# Xcode 16 synchronized folders: every file under the folder belongs to the targets that list the folder; a
|
|
147
|
+
# membership exception for such a target takes the file out, one for another target adds it there
|
|
148
|
+
by_id = {t["id"]: t for t in targets}
|
|
149
|
+
for gid, g in objs.items():
|
|
150
|
+
if not isinstance(g, dict) or g.get("isa") != "PBXFileSystemSynchronizedRootGroup" or gid not in paths:
|
|
151
|
+
continue
|
|
152
|
+
folder = paths[gid]
|
|
153
|
+
owners = [t for t in targets if gid in t["groups"]]
|
|
154
|
+
excl: dict[str, set] = {}
|
|
155
|
+
for ex in g.get("exceptions") or ():
|
|
156
|
+
e = objs.get(ex) or {}
|
|
157
|
+
if e.get("isa") == "PBXFileSystemSynchronizedBuildFileExceptionSet" and e.get("target") in by_id:
|
|
158
|
+
excl.setdefault(e["target"], set()).update(e.get("membershipExceptions") or ())
|
|
159
|
+
try:
|
|
160
|
+
rel_files = [f.relative_to(folder).as_posix() for f in folder.rglob("*")
|
|
161
|
+
if f.is_file() and f.suffix in (".swift", ".m", ".mm", ".c", ".cpp")]
|
|
162
|
+
except OSError:
|
|
163
|
+
rel_files = []
|
|
164
|
+
for t in targets:
|
|
165
|
+
ex = excl.get(t["id"], set())
|
|
166
|
+
if t in owners:
|
|
167
|
+
t["files"] |= {folder / r for r in rel_files if r not in ex}
|
|
168
|
+
elif ex:
|
|
169
|
+
t["files"] |= {folder / r for r in rel_files if r in ex}
|
|
170
|
+
try:
|
|
171
|
+
where = pbx.parent.relative_to(root).as_posix()
|
|
172
|
+
except ValueError:
|
|
173
|
+
where = pbx.parent.as_posix()
|
|
174
|
+
return {"where": where, "targets": targets}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _projects(root: Path) -> list[Path]:
|
|
178
|
+
out = []
|
|
179
|
+
for pat in ("*.xcodeproj/project.pbxproj", "*/*.xcodeproj/project.pbxproj", "*/*/*.xcodeproj/project.pbxproj"):
|
|
180
|
+
for p in root.glob(pat):
|
|
181
|
+
if SKIP.isdisjoint(p.relative_to(root).parts[:-2]):
|
|
182
|
+
out.append(p)
|
|
183
|
+
return sorted(out)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _manifests(root: Path, depth: int = 4) -> list[Path]:
|
|
187
|
+
out = []
|
|
188
|
+
for lvl in range(depth):
|
|
189
|
+
for p in root.glob("/".join(["*"] * lvl + ["Package.swift"])):
|
|
190
|
+
if SKIP.isdisjoint(p.relative_to(root).parts[:-1]):
|
|
191
|
+
out.append(p)
|
|
192
|
+
return out
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def package_platforms(pk: Path) -> list[str]:
|
|
196
|
+
"""`platforms: [.iOS(.v15), .macOS("12.0"), .visionOS(.v1)]` of one Package.swift, as SwiftPM names."""
|
|
197
|
+
try:
|
|
198
|
+
txt = pk.read_text(encoding="utf-8", errors="replace")
|
|
199
|
+
except OSError:
|
|
200
|
+
return []
|
|
201
|
+
m = re.search(r"\bplatforms\s*:\s*\[([^\]]*)\]", txt)
|
|
202
|
+
if not m:
|
|
203
|
+
return []
|
|
204
|
+
names = re.findall(r"\.(\w+)\s*\(", m.group(1)) + re.findall(r"\.(\w+)\s*(?=[,\]]|$)", m.group(1))
|
|
205
|
+
return [n for n in dict.fromkeys(names) if n in SPM_PLATFORM]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def apple_build(root: Path | str) -> dict:
|
|
209
|
+
root = Path(root)
|
|
210
|
+
out = {"targets": {}, "mac": None, "membership": {}, "projects": []}
|
|
211
|
+
native = catalyst = False
|
|
212
|
+
for pbx in _projects(root):
|
|
213
|
+
pr = _read_project(pbx, root)
|
|
214
|
+
if pr is None:
|
|
215
|
+
continue
|
|
216
|
+
out["projects"].append(pr["where"])
|
|
217
|
+
for t in pr["targets"]:
|
|
218
|
+
for p in sorted(t["platforms"], key=APPLE.index):
|
|
219
|
+
out["targets"].setdefault(p, f"{pr['where']} target {t['name']}: SDKROOT / SUPPORTED_PLATFORMS")
|
|
220
|
+
if t["catalyst"]:
|
|
221
|
+
out["targets"].setdefault("macos", f"{pr['where']} target {t['name']}: SUPPORTS_MACCATALYST")
|
|
222
|
+
native |= "macos" in t["platforms"]
|
|
223
|
+
catalyst |= t["catalyst"]
|
|
224
|
+
plats = set(t["platforms"]) | ({"macos"} if t["catalyst"] else set())
|
|
225
|
+
for f in t["files"]:
|
|
226
|
+
try:
|
|
227
|
+
rel = f.resolve().relative_to(root.resolve()).as_posix()
|
|
228
|
+
except (ValueError, OSError):
|
|
229
|
+
continue
|
|
230
|
+
m = out["membership"].setdefault(rel, {"platforms": set(), "targets": []})
|
|
231
|
+
m["platforms"] |= plats
|
|
232
|
+
if t["name"] not in m["targets"]:
|
|
233
|
+
m["targets"].append(t["name"])
|
|
234
|
+
# SwiftPM: the root manifest (one level down for a repo holding the package in a folder); local packages only
|
|
235
|
+
# when no Xcode project or root manifest names a platform
|
|
236
|
+
pks = [p for p in [root / "Package.swift", *root.glob("*/Package.swift")] if p.is_file()]
|
|
237
|
+
for pass_ in (pks, [] if out["targets"] or any(package_platforms(p) for p in pks) else _manifests(root)):
|
|
238
|
+
for pk in pass_:
|
|
239
|
+
names = package_platforms(pk)
|
|
240
|
+
where = pk.relative_to(root).as_posix()
|
|
241
|
+
for n in names:
|
|
242
|
+
p = SPM_PLATFORM[n]
|
|
243
|
+
if p:
|
|
244
|
+
out["targets"].setdefault(p, f"{where} platforms: .{n}")
|
|
245
|
+
native |= "macOS" in names
|
|
246
|
+
catalyst |= "macCatalyst" in names and "macOS" not in names
|
|
247
|
+
if "macos" in out["targets"]:
|
|
248
|
+
out["mac"] = "both" if native and catalyst else "catalyst" if catalyst else "native"
|
|
249
|
+
for m in out["membership"].values():
|
|
250
|
+
m["platforms"] = sorted(m["platforms"], key=APPLE.index)
|
|
251
|
+
return out
|