gelang 0.1.0
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.
- package/CHANGELOG.md +65 -0
- package/LICENSE +21 -0
- package/README.md +535 -0
- package/bin/ge.js +112 -0
- package/package.json +62 -0
- package/python/pyeffic/__init__.py +9 -0
- package/python/pyeffic/__main__.py +6 -0
- package/python/pyeffic/analyzer.py +464 -0
- package/python/pyeffic/apisurface.py +238 -0
- package/python/pyeffic/autoselect.py +327 -0
- package/python/pyeffic/backends.py +87 -0
- package/python/pyeffic/bench.py +233 -0
- package/python/pyeffic/cli.py +184 -0
- package/python/pyeffic/compiler.py +421 -0
- package/python/pyeffic/config.py +383 -0
- package/python/pyeffic/dartgen.py +441 -0
- package/python/pyeffic/deploy.py +586 -0
- package/python/pyeffic/diagnostics.py +194 -0
- package/python/pyeffic/difftest.py +424 -0
- package/python/pyeffic/downloader.py +307 -0
- package/python/pyeffic/emitters/__init__.py +11 -0
- package/python/pyeffic/emitters/base.py +2359 -0
- package/python/pyeffic/emitters/cpp.py +266 -0
- package/python/pyeffic/emitters/csharp.py +342 -0
- package/python/pyeffic/emitters/dart.py +349 -0
- package/python/pyeffic/emitters/go.py +388 -0
- package/python/pyeffic/emitters/kotlin.py +314 -0
- package/python/pyeffic/emitters/rust.py +314 -0
- package/python/pyeffic/emitters/zig.py +411 -0
- package/python/pyeffic/ffi.py +49 -0
- package/python/pyeffic/frontends/__init__.py +94 -0
- package/python/pyeffic/frontends/hybrid.py +709 -0
- package/python/pyeffic/frontends/typescript.py +965 -0
- package/python/pyeffic/ge_cli.py +1148 -0
- package/python/pyeffic/golden.py +348 -0
- package/python/pyeffic/idents.py +206 -0
- package/python/pyeffic/modules.py +220 -0
- package/python/pyeffic/packer.py +222 -0
- package/python/pyeffic/pipeline.py +797 -0
- package/python/pyeffic/reactgen.py +966 -0
- package/python/pyeffic/researcher.py +177 -0
- package/python/pyeffic/scaffold.py +397 -0
- package/python/pyeffic/stdlib.py +246 -0
- package/python/pyeffic/styling.py +220 -0
- package/python/pyeffic/templates/desktop_gui/README.md +106 -0
- package/python/pyeffic/templates/desktop_gui/app/__init__.py +0 -0
- package/python/pyeffic/templates/desktop_gui/app/core/__init__.py +0 -0
- package/python/pyeffic/templates/desktop_gui/app/core/add.ge.py +13 -0
- package/python/pyeffic/templates/desktop_gui/app/core/factorial.ge.py +20 -0
- package/python/pyeffic/templates/desktop_gui/app/core/fibonacci.ge.py +25 -0
- package/python/pyeffic/templates/desktop_gui/app/core/gcd.ge.py +19 -0
- package/python/pyeffic/templates/desktop_gui/app/core/is_prime.ge.py +24 -0
- package/python/pyeffic/templates/desktop_gui/app/core/multiply.ge.py +13 -0
- package/python/pyeffic/templates/desktop_gui/app/core/power.ge.py +25 -0
- package/python/pyeffic/templates/desktop_gui/app/main.ge.py +49 -0
- package/python/pyeffic/templates/desktop_gui/app/memory/__init__.py +0 -0
- package/python/pyeffic/templates/desktop_gui/app/memory/buffer.ge.py +26 -0
- package/python/pyeffic/templates/desktop_gui/app/memory/limits.ge.py +47 -0
- package/python/pyeffic/templates/desktop_gui/app/memory/state.ge.py +44 -0
- package/python/pyeffic/templates/desktop_gui/app/ui/__init__.py +0 -0
- package/python/pyeffic/templates/desktop_gui/app/ui/layout.ge.py +64 -0
- package/python/pyeffic/templates/desktop_gui/app/ui/render.ge.py +87 -0
- package/python/pyeffic/templates/desktop_gui/app/ui/theme.ge.py +147 -0
- package/python/pyeffic/templates/desktop_gui/app/ui/widgets.ge.py +105 -0
- package/python/pyeffic/templates/desktop_gui/desktop/__init__.py +1 -0
- package/python/pyeffic/templates/desktop_gui/desktop/main.ge.py +258 -0
- package/python/pyeffic/templates/desktop_gui/ge.toml +16 -0
- package/python/pyeffic/templates/desktop_gui/tests/__init__.py +0 -0
- package/python/pyeffic/templates/desktop_gui/tests/ge_loader.py +76 -0
- package/python/pyeffic/templates/desktop_gui/tests/test_app.py +173 -0
- package/python/pyeffic/templates/web_react/README.md +115 -0
- package/python/pyeffic/templates/web_react/app/__init__.py +0 -0
- package/python/pyeffic/templates/web_react/app/core/__init__.py +0 -0
- package/python/pyeffic/templates/web_react/app/core/add.ge.py +9 -0
- package/python/pyeffic/templates/web_react/app/core/factorial.ge.py +16 -0
- package/python/pyeffic/templates/web_react/app/core/fibonacci.ge.py +21 -0
- package/python/pyeffic/templates/web_react/app/core/is_prime.ge.py +20 -0
- package/python/pyeffic/templates/web_react/app/core/multiply.ge.py +9 -0
- package/python/pyeffic/templates/web_react/app/main.ge.py +25 -0
- package/python/pyeffic/templates/web_react/app/memory/__init__.py +0 -0
- package/python/pyeffic/templates/web_react/app/memory/buffer.ge.py +25 -0
- package/python/pyeffic/templates/web_react/app/memory/limits.ge.py +51 -0
- package/python/pyeffic/templates/web_react/ge.toml +23 -0
- package/python/pyeffic/templates/web_react/tests/__init__.py +0 -0
- package/python/pyeffic/templates/web_react/tests/ge_loader.py +68 -0
- package/python/pyeffic/templates/web_react/tests/test_app.py +105 -0
- package/python/pyeffic/templates/web_react/ui/main.ge.ui +33 -0
- package/python/pyeffic/templates/web_react/web/__init__.py +0 -0
- package/python/pyeffic/templates/web_react/web/server.ge.py +78 -0
- package/python/pyeffic/ts2py.py +657 -0
- package/python/pyeffic/typecheck.py +232 -0
- package/python/pyeffic/ui.py +154 -0
- package/python/pyeffic/ui_dsl.py +618 -0
- package/python/pyeffic/widgets.py +87 -0
- package/scripts/README.md +42 -0
- package/scripts/check-toolchains.py +85 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
"""Configuration and compiler detection for pyeffic.
|
|
2
|
+
|
|
3
|
+
Toolchain detection is version-adaptive: it finds compilers on PATH or in
|
|
4
|
+
the local tools/ directory regardless of their version. Minimum supported
|
|
5
|
+
versions are checked and warnings are issued if a toolchain is too old,
|
|
6
|
+
but detection itself never fails due to a version mismatch.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
import shutil
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ---- Minimum supported versions per toolchain ----
|
|
20
|
+
# These are minimums — any version >= these will work.
|
|
21
|
+
# Updated toolchains should be backward-compatible per each language's semver policy.
|
|
22
|
+
MIN_VERSIONS: dict[str, str] = {
|
|
23
|
+
"rustc": "1.70.0", # Rust 1.70+ (stabilized many features we use)
|
|
24
|
+
"cpp": "10.0.0", # clang 10+ / GCC 10+ / MSVC 2019+
|
|
25
|
+
"dotnet": "8.0.0", # .NET 8+ for NativeAOT
|
|
26
|
+
"zig": "0.13.0", # Zig 0.13+ (API stabilized enough for our use)
|
|
27
|
+
"go": "1.21.0", # Go 1.21+ (toolchain management, go.mod required)
|
|
28
|
+
"kotlinc": "2.0.0", # Kotlin 2.0+ (stable Kotlin/Native @CName)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class ToolchainInfo:
|
|
34
|
+
"""Information about a detected toolchain."""
|
|
35
|
+
path: str
|
|
36
|
+
version: str = "unknown"
|
|
37
|
+
is_supported: bool = True
|
|
38
|
+
warning: str = ""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class CompilerInfo:
|
|
43
|
+
rustc: str | None = None
|
|
44
|
+
cpp: str | None = None # one of g++/clang++/cl
|
|
45
|
+
cpp_kind: str | None = None # "gcc" | "clang" | "msvc"
|
|
46
|
+
dotnet: str | None = None # .NET SDK for C# NativeAOT
|
|
47
|
+
zig: str | None = None # Zig compiler
|
|
48
|
+
go: str | None = None # Go compiler
|
|
49
|
+
kotlinc: str | None = None # Kotlin compiler (kotlinc-native)
|
|
50
|
+
# version info for each toolchain
|
|
51
|
+
versions: dict[str, str] = field(default_factory=dict)
|
|
52
|
+
warnings: list[str] = field(default_factory=list)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class Config:
|
|
57
|
+
out_dir: Path = field(default_factory=lambda: Path("build"))
|
|
58
|
+
#: platform bucket: "desktop" | "web" | "mobile"
|
|
59
|
+
#: artifacts land in <out_dir>/<target>/ so a multi-target project stays
|
|
60
|
+
#: readable:
|
|
61
|
+
#: build/desktop/backend/ generated native sources
|
|
62
|
+
#: build/desktop/obj/ object files
|
|
63
|
+
#: build/desktop/bin/ executables
|
|
64
|
+
#: build/web/... web build
|
|
65
|
+
#: build/mobile/... Flutter project
|
|
66
|
+
target: str = "desktop"
|
|
67
|
+
keep_sources: bool = True
|
|
68
|
+
run_after_compile: bool = False
|
|
69
|
+
force_backend: str | None = None # "rust"|"cpp"|"csharp"|"zig"|"go"|"kotlin"|None (auto)
|
|
70
|
+
do_research: bool = True
|
|
71
|
+
research_timeout: float = 8.0
|
|
72
|
+
opt_level: str = "3" # -O3 / -C opt-level=3
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def target_dir(self) -> Path:
|
|
76
|
+
"""Root of this target's artifacts: <out_dir>/<target>."""
|
|
77
|
+
return self.out_dir / self.target
|
|
78
|
+
|
|
79
|
+
def out(self, *parts: str) -> Path:
|
|
80
|
+
"""A path under the target directory, with parents created."""
|
|
81
|
+
p = self.target_dir.joinpath(*parts)
|
|
82
|
+
p.parent.mkdir(parents=True, exist_ok=True)
|
|
83
|
+
return p
|
|
84
|
+
|
|
85
|
+
def bin_dir(self) -> Path:
|
|
86
|
+
"""Where executables go: <out_dir>/<target>/bin."""
|
|
87
|
+
d = self.target_dir / "bin"
|
|
88
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
89
|
+
return d
|
|
90
|
+
|
|
91
|
+
def obj_dir(self) -> Path:
|
|
92
|
+
"""Where object files go: <out_dir>/<target>/obj."""
|
|
93
|
+
d = self.target_dir / "obj"
|
|
94
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
95
|
+
return d
|
|
96
|
+
|
|
97
|
+
def backend_dir(self) -> Path:
|
|
98
|
+
"""Where generated sources go: <out_dir>/<target>/backend."""
|
|
99
|
+
d = self.target_dir / "backend"
|
|
100
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
101
|
+
return d
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ---- Tool finding ----
|
|
105
|
+
|
|
106
|
+
def _find_tool(name: str, subdir: str = "") -> str | None:
|
|
107
|
+
"""Find a tool on PATH or in the local tools directory.
|
|
108
|
+
|
|
109
|
+
Searches in this order:
|
|
110
|
+
1. System PATH (shutil.which)
|
|
111
|
+
2. tools/ directory relative to the package (development mode)
|
|
112
|
+
3. tools/ directory relative to the executable (standalone binary mode)
|
|
113
|
+
4. tools/ directory relative to the current working directory
|
|
114
|
+
|
|
115
|
+
This is version-adaptive: it finds any version of the tool, not a
|
|
116
|
+
specific pinned version. Updated toolchains are detected automatically.
|
|
117
|
+
"""
|
|
118
|
+
# 1) check PATH
|
|
119
|
+
found = shutil.which(name)
|
|
120
|
+
if found:
|
|
121
|
+
return found
|
|
122
|
+
|
|
123
|
+
# 2) check local tools directory relative to this file (development mode)
|
|
124
|
+
tools_dir = Path(__file__).resolve().parent.parent / "tools"
|
|
125
|
+
result = _search_tools_dir(tools_dir, name, subdir)
|
|
126
|
+
if result:
|
|
127
|
+
return result
|
|
128
|
+
|
|
129
|
+
# 3) check tools/ relative to the executable (standalone binary mode)
|
|
130
|
+
if hasattr(sys, "_MEIPASS") or getattr(sys, "frozen", False):
|
|
131
|
+
# PyInstaller: executable is in dist/ge/ge.exe, tools/ should be next to it
|
|
132
|
+
exe_dir = Path(sys.executable).resolve().parent
|
|
133
|
+
tools_dir = exe_dir / "tools"
|
|
134
|
+
result = _search_tools_dir(tools_dir, name, subdir)
|
|
135
|
+
if result:
|
|
136
|
+
return result
|
|
137
|
+
# also check parent of exe dir
|
|
138
|
+
tools_dir = exe_dir.parent / "tools"
|
|
139
|
+
result = _search_tools_dir(tools_dir, name, subdir)
|
|
140
|
+
if result:
|
|
141
|
+
return result
|
|
142
|
+
|
|
143
|
+
# 4) check tools/ relative to current working directory
|
|
144
|
+
tools_dir = Path.cwd() / "tools"
|
|
145
|
+
result = _search_tools_dir(tools_dir, name, subdir)
|
|
146
|
+
if result:
|
|
147
|
+
return result
|
|
148
|
+
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _search_tools_dir(tools_dir: Path, name: str, subdir: str = "") -> str | None:
|
|
153
|
+
"""Search a tools directory for a tool by name."""
|
|
154
|
+
if not tools_dir.exists():
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
# direct path with subdir
|
|
158
|
+
if subdir:
|
|
159
|
+
p = tools_dir / subdir / f"{name}.exe"
|
|
160
|
+
if p.exists():
|
|
161
|
+
return str(p)
|
|
162
|
+
p = tools_dir / subdir / name
|
|
163
|
+
if p.exists():
|
|
164
|
+
return str(p)
|
|
165
|
+
|
|
166
|
+
# version-agnostic search: find any directory matching a prefix
|
|
167
|
+
name_lower = name.lower()
|
|
168
|
+
for child in sorted(tools_dir.iterdir(), reverse=True):
|
|
169
|
+
if not child.is_dir():
|
|
170
|
+
continue
|
|
171
|
+
child_lower = child.name.lower()
|
|
172
|
+
if child_lower.startswith(name_lower) or name_lower in child_lower:
|
|
173
|
+
p = child / f"{name}.exe"
|
|
174
|
+
if p.exists():
|
|
175
|
+
return str(p)
|
|
176
|
+
p = child / name
|
|
177
|
+
if p.exists():
|
|
178
|
+
return str(p)
|
|
179
|
+
p = child / "bin" / f"{name}.exe"
|
|
180
|
+
if p.exists():
|
|
181
|
+
return str(p)
|
|
182
|
+
p = child / "bin" / f"{name}.bat"
|
|
183
|
+
if p.exists():
|
|
184
|
+
return str(p)
|
|
185
|
+
p = child / "bin" / name
|
|
186
|
+
if p.exists():
|
|
187
|
+
return str(p)
|
|
188
|
+
|
|
189
|
+
# search one level deep
|
|
190
|
+
for child in tools_dir.iterdir():
|
|
191
|
+
if not child.is_dir():
|
|
192
|
+
continue
|
|
193
|
+
for sub in child.iterdir():
|
|
194
|
+
if sub.is_dir() and sub.name.lower() in ("bin", "lib", "cmd"):
|
|
195
|
+
p = sub / f"{name}.exe"
|
|
196
|
+
if p.exists():
|
|
197
|
+
return str(p)
|
|
198
|
+
p = sub / f"{name}.bat"
|
|
199
|
+
if p.exists():
|
|
200
|
+
return str(p)
|
|
201
|
+
|
|
202
|
+
return None
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# ---- Version parsing ----
|
|
206
|
+
|
|
207
|
+
def _parse_version(output: str) -> str:
|
|
208
|
+
"""Extract a version string from compiler --version output.
|
|
209
|
+
|
|
210
|
+
Handles formats like:
|
|
211
|
+
- "rustc 1.92.0 (ded5c06cf 2025-12-08)"
|
|
212
|
+
- "clang version 20.1.8"
|
|
213
|
+
- "go version go1.25.5 windows/amd64"
|
|
214
|
+
- "0.14.1" (Zig)
|
|
215
|
+
- "info: kotlinc-native 2.4.10 (JRE 25.0.1+8-LTS-27)"
|
|
216
|
+
- "10.0.101" (.NET)
|
|
217
|
+
"""
|
|
218
|
+
# try common version patterns
|
|
219
|
+
patterns = [
|
|
220
|
+
r'(\d+\.\d+\.\d+(?:\.\d+)?)', # x.y.z or x.y.z.w
|
|
221
|
+
r'(\d+\.\d+)', # x.y (fallback)
|
|
222
|
+
]
|
|
223
|
+
for pattern in patterns:
|
|
224
|
+
match = re.search(pattern, output)
|
|
225
|
+
if match:
|
|
226
|
+
return match.group(1)
|
|
227
|
+
return "unknown"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _get_tool_version(tool_path: str, tool_name: str) -> str:
|
|
231
|
+
"""Get the version of a toolchain by running it with the right version flag."""
|
|
232
|
+
try:
|
|
233
|
+
# different tools use different version flags
|
|
234
|
+
if tool_name == "zig":
|
|
235
|
+
cmd = [tool_path, "version"]
|
|
236
|
+
elif tool_name == "kotlinc":
|
|
237
|
+
cmd = [tool_path, "-version"]
|
|
238
|
+
elif tool_name == "dotnet":
|
|
239
|
+
cmd = [tool_path, "--version"]
|
|
240
|
+
elif tool_name == "go":
|
|
241
|
+
cmd = [tool_path, "version"] # Go uses "go version" not "go --version"
|
|
242
|
+
else:
|
|
243
|
+
cmd = [tool_path, "--version"]
|
|
244
|
+
|
|
245
|
+
r = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
|
246
|
+
output = (r.stdout or "") + (r.stderr or "")
|
|
247
|
+
return _parse_version(output)
|
|
248
|
+
except Exception:
|
|
249
|
+
return "unknown"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _compare_versions(v1: str, v2: str) -> int:
|
|
253
|
+
"""Compare two version strings. Returns -1, 0, or 1."""
|
|
254
|
+
parts1 = [int(x) for x in v1.split(".") if x.isdigit()]
|
|
255
|
+
parts2 = [int(x) for x in v2.split(".") if x.isdigit()]
|
|
256
|
+
# pad with zeros
|
|
257
|
+
while len(parts1) < len(parts2):
|
|
258
|
+
parts1.append(0)
|
|
259
|
+
while len(parts2) < len(parts1):
|
|
260
|
+
parts2.append(0)
|
|
261
|
+
for a, b in zip(parts1, parts2):
|
|
262
|
+
if a < b:
|
|
263
|
+
return -1
|
|
264
|
+
if a > b:
|
|
265
|
+
return 1
|
|
266
|
+
return 0
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _check_version(tool_name: str, found_version: str, min_version: str) -> str:
|
|
270
|
+
"""Check if a tool version meets the minimum. Returns warning string or empty."""
|
|
271
|
+
if found_version == "unknown":
|
|
272
|
+
return f"Warning: could not determine {tool_name} version (expected >={min_version})"
|
|
273
|
+
if _compare_versions(found_version, min_version) < 0:
|
|
274
|
+
return f"Warning: {tool_name} {found_version} is older than recommended {min_version} — some features may not work"
|
|
275
|
+
return ""
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# ---- Compiler detection ----
|
|
279
|
+
|
|
280
|
+
def detect_compilers() -> CompilerInfo:
|
|
281
|
+
"""Detect all available compilers and their versions.
|
|
282
|
+
|
|
283
|
+
This function is version-adaptive: it finds any version of each toolchain
|
|
284
|
+
on PATH or in the local tools/ directory. It records versions and issues
|
|
285
|
+
warnings for outdated toolchains, but never fails to detect a tool.
|
|
286
|
+
"""
|
|
287
|
+
info = CompilerInfo()
|
|
288
|
+
|
|
289
|
+
# Rust
|
|
290
|
+
info.rustc = _find_tool("rustc")
|
|
291
|
+
if info.rustc:
|
|
292
|
+
v = _get_tool_version(info.rustc, "rustc")
|
|
293
|
+
info.versions["rustc"] = v
|
|
294
|
+
w = _check_version("rustc", v, MIN_VERSIONS["rustc"])
|
|
295
|
+
if w:
|
|
296
|
+
info.warnings.append(w)
|
|
297
|
+
|
|
298
|
+
# C++ (try g++, clang++, cl in order)
|
|
299
|
+
for name, kind in (("g++", "gcc"), ("clang++", "clang"), ("cl", "msvc")):
|
|
300
|
+
found = _find_tool(name)
|
|
301
|
+
if found:
|
|
302
|
+
info.cpp = found
|
|
303
|
+
info.cpp_kind = kind
|
|
304
|
+
v = _get_tool_version(found, "cpp")
|
|
305
|
+
info.versions["cpp"] = v
|
|
306
|
+
w = _check_version("cpp", v, MIN_VERSIONS["cpp"])
|
|
307
|
+
if w:
|
|
308
|
+
info.warnings.append(w)
|
|
309
|
+
break
|
|
310
|
+
|
|
311
|
+
# C# (.NET)
|
|
312
|
+
info.dotnet = _find_tool("dotnet")
|
|
313
|
+
if info.dotnet:
|
|
314
|
+
v = _get_tool_version(info.dotnet, "dotnet")
|
|
315
|
+
info.versions["dotnet"] = v
|
|
316
|
+
w = _check_version("dotnet", v, MIN_VERSIONS["dotnet"])
|
|
317
|
+
if w:
|
|
318
|
+
info.warnings.append(w)
|
|
319
|
+
|
|
320
|
+
# Zig
|
|
321
|
+
info.zig = _find_tool("zig")
|
|
322
|
+
if info.zig:
|
|
323
|
+
v = _get_tool_version(info.zig, "zig")
|
|
324
|
+
info.versions["zig"] = v
|
|
325
|
+
w = _check_version("zig", v, MIN_VERSIONS["zig"])
|
|
326
|
+
if w:
|
|
327
|
+
info.warnings.append(w)
|
|
328
|
+
|
|
329
|
+
# Go
|
|
330
|
+
info.go = _find_tool("go")
|
|
331
|
+
if info.go:
|
|
332
|
+
v = _get_tool_version(info.go, "go")
|
|
333
|
+
info.versions["go"] = v
|
|
334
|
+
w = _check_version("go", v, MIN_VERSIONS["go"])
|
|
335
|
+
if w:
|
|
336
|
+
info.warnings.append(w)
|
|
337
|
+
|
|
338
|
+
# Kotlin (kotlinc-native for Kotlin/Native compilation)
|
|
339
|
+
info.kotlinc = _find_tool("kotlinc-native")
|
|
340
|
+
if info.kotlinc:
|
|
341
|
+
v = _get_tool_version(info.kotlinc, "kotlinc")
|
|
342
|
+
info.versions["kotlinc"] = v
|
|
343
|
+
w = _check_version("kotlinc", v, MIN_VERSIONS["kotlinc"])
|
|
344
|
+
if w:
|
|
345
|
+
info.warnings.append(w)
|
|
346
|
+
|
|
347
|
+
return info
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def compiler_version(name: str) -> str:
|
|
351
|
+
"""Get the version string of a compiler by name."""
|
|
352
|
+
try:
|
|
353
|
+
if name == "zig":
|
|
354
|
+
cmd = [name, "version"]
|
|
355
|
+
elif name == "kotlinc-native":
|
|
356
|
+
cmd = [name, "-version"]
|
|
357
|
+
else:
|
|
358
|
+
cmd = [name, "--version"]
|
|
359
|
+
r = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
|
|
360
|
+
return (r.stdout or r.stderr).strip().splitlines()[0] if (r.stdout or r.stderr) else "unknown"
|
|
361
|
+
except Exception:
|
|
362
|
+
return "unknown"
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def print_toolchain_report(info: CompilerInfo) -> None:
|
|
366
|
+
"""Print a human-readable report of detected toolchains."""
|
|
367
|
+
print("=== GE Toolchain Report ===")
|
|
368
|
+
tools = [
|
|
369
|
+
("Rust", info.rustc, info.versions.get("rustc", "—")),
|
|
370
|
+
("C++", info.cpp, info.versions.get("cpp", "—")),
|
|
371
|
+
("C# (.NET)", info.dotnet, info.versions.get("dotnet", "—")),
|
|
372
|
+
("Zig", info.zig, info.versions.get("zig", "—")),
|
|
373
|
+
("Go", info.go, info.versions.get("go", "—")),
|
|
374
|
+
("Kotlin", info.kotlinc, info.versions.get("kotlinc", "—")),
|
|
375
|
+
]
|
|
376
|
+
for name, path, version in tools:
|
|
377
|
+
status = "[OK]" if path else "[--]"
|
|
378
|
+
ver_str = f"v{version}" if version != "-" else ""
|
|
379
|
+
print(f" {status} {name:12} {ver_str:12} {path or 'not found'}")
|
|
380
|
+
if info.warnings:
|
|
381
|
+
print("\nWarnings:")
|
|
382
|
+
for w in info.warnings:
|
|
383
|
+
print(f" [!] {w}")
|