Skip to content

Commit d59de9f

Browse files
gh-188: Move to Clang.
Update build script, interpreter (for compatibility), README.md, and ci.yml.
1 parent 6dd35e1 commit d59de9f

25 files changed

Lines changed: 215 additions & 334 deletions

File tree

.github/workflows/ci.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ jobs:
1212
- name: Checkout code
1313
uses: actions/checkout@v4
1414

15-
- name: Setup MSVC
16-
uses: ilammy/msvc-dev-cmd@v1.13.0
17-
1815
- name: Build interpreter and extensions
1916
run: .\build.ps1
2017

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
This is the canonical implementation of the Prefix programming language in C17, targeting MSVC (VS2026).
1+
This is the canonical implementation of the Prefix programming language in C17, targeting Clang on baseline x64 Windows.
22

33
The versioning system used is [SemVer 2.0](https://semver.org/).

build.ps1

Lines changed: 92 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Compiles the Prefix runtime into a shared DLL, links the interpreter EXE
44
against that DLL's import library, and compiles each discovered extension
55
against the same shared runtime.
66
7-
Requires: run from a Developer Command Prompt for Visual Studio where cl.exe is on PATH.
7+
Requires: clang.exe on PATH targeting x64.
88
Usage (from Prefix folder):
99
powershell -ExecutionPolicy Bypass -File .\build.ps1
1010
#>
@@ -30,9 +30,22 @@ $buildDir = Join-Path $env:TEMP ("prefix-build-$stamp")
3030
New-Item -ItemType Directory -Path $buildDir -Force | Out-Null
3131
Write-Host "Build dir: $buildDir"
3232

33-
$cl = Get-Command cl.exe -ErrorAction SilentlyContinue
34-
if (-not $cl) {
35-
Write-Error "cl.exe not found. Run this script from a Developer Command Prompt for Visual Studio."
33+
$clang = Get-Command clang.exe -ErrorAction SilentlyContinue
34+
if (-not $clang) {
35+
Write-Error "clang.exe not found on PATH."
36+
Remove-Item -Recurse -Force $buildDir -ErrorAction SilentlyContinue
37+
exit 1
38+
}
39+
40+
$clangTarget = (& clang.exe -dumpmachine 2>$null | Select-Object -First 1)
41+
if (-not $clangTarget) {
42+
Write-Error "Unable to determine clang.exe target triple."
43+
Remove-Item -Recurse -Force $buildDir -ErrorAction SilentlyContinue
44+
exit 1
45+
}
46+
47+
if ($clangTarget -notmatch '^x86_64-') {
48+
Write-Error "clang.exe must target baseline x64. Found target '$clangTarget'."
3649
Remove-Item -Recurse -Force $buildDir -ErrorAction SilentlyContinue
3750
exit 1
3851
}
@@ -74,22 +87,53 @@ if ($platform::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Linux))
7487

7588
Push-Location $buildDir
7689
try {
77-
$runtimeArgs = @(
78-
"/std:c17", "/Gd", "/O2", "/Gy", "/GF", "/GL", "/W4", "/WX", "/MP", "/nologo",
90+
$isWindows = $platform::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows)
91+
92+
# Use a single release profile for every artifact: portable baseline x64,
93+
# full-program optimization, and link-time dead stripping/folding.
94+
$clangArgs = @(
95+
"--driver-mode=cl",
96+
"/clang:-march=x86-64",
97+
"/clang:-fuse-ld=lld",
98+
"/clang:-flto=full",
99+
"/clang:-ffunction-sections",
100+
"/clang:-fdata-sections"
101+
)
102+
$releaseCompileArgs = @(
103+
"/std:c17", "/Gd", "/O2", "/Ot", "/Oi", "/Ob2", "/Gy", "/Gw", "/GF", "/W4", "/WX", "/nologo"
104+
)
105+
# Only pass the `--gc-sections` linker option on non-Windows platforms;
106+
# on Windows we use MSVC-style linker options (/OPT:REF /OPT:ICF).
107+
if (-not $isWindows) {
108+
$clangArgs += "/clang:-Wl,--gc-sections"
109+
}
110+
111+
# Libraries that extensions may need on Windows. Link via the build
112+
# system instead of source-level linker pragmas in the extension code.
113+
$linkLibs = @()
114+
if ($isWindows) {
115+
$linkLibs = @("ole32.lib", "ws2_32.lib", "winhttp.lib", "user32.lib", "gdi32.lib")
116+
$clangArgs += "/clang:-Wno-deprecated-declarations"
117+
}
118+
119+
$runtimeArgs = @($clangArgs + $releaseCompileArgs + @(
79120
"/LD", "/I$src",
80121
"/Fe:$runtimeDllName"
81-
)
122+
))
82123
$runtimeArgs += $runtimeSources
83-
$runtimeArgs += @(
84-
"/link",
85-
"/DEF:$runtimeDef",
86-
"/IMPLIB:$runtimeLibName"
87-
)
124+
$runtimeLinkFlags = @("/link", "/DEF:$runtimeDef", "/IMPLIB:$runtimeLibName")
125+
if ($isWindows) {
126+
$runtimeLinkFlags += "/OPT:REF"
127+
$runtimeLinkFlags += "/OPT:ICF"
128+
} else {
129+
$runtimeLinkFlags += "/clang:-Wl,--gc-sections"
130+
}
131+
$runtimeArgs += $runtimeLinkFlags
88132

89-
Write-Host "Invoking: cl.exe $($runtimeArgs -join ' ')"
90-
& cl.exe @runtimeArgs
133+
Write-Host "Invoking: clang.exe $($runtimeArgs -join ' ')"
134+
& clang.exe @runtimeArgs
91135
if ($LASTEXITCODE -ne 0) {
92-
throw "cl.exe returned exit code $LASTEXITCODE while building shared runtime"
136+
throw "clang.exe returned exit code $LASTEXITCODE while building shared runtime"
93137
}
94138

95139
$runtimeDllPath = Join-Path $buildDir $runtimeDllName
@@ -111,18 +155,26 @@ try {
111155
Write-Host "Copied runtime DLL to: $runtimeDllDest"
112156
Write-Host "Copied runtime import library to: $runtimeLibDest"
113157

114-
$exeArgs = @(
115-
"/std:c17", "/Gd", "/O2", "/Gy", "/GF", "/GL", "/W4", "/WX", "/MP", "/nologo",
158+
$exeArgs = @($clangArgs + $releaseCompileArgs + @(
116159
"/I$src",
117160
"/Fe:prefix.exe",
118161
$mainSource,
119162
$runtimeLibPath
120-
)
163+
))
164+
$exeLinkFlags = @()
165+
if ($isWindows) {
166+
$exeLinkFlags += "/link"
167+
$exeLinkFlags += "/OPT:REF"
168+
$exeLinkFlags += "/OPT:ICF"
169+
} else {
170+
$exeLinkFlags += "/clang:-Wl,--gc-sections"
171+
}
172+
$exeArgs += $exeLinkFlags
121173

122-
Write-Host "Invoking: cl.exe $($exeArgs -join ' ')"
123-
& cl.exe @exeArgs
174+
Write-Host "Invoking: clang.exe $($exeArgs -join ' ')"
175+
& clang.exe @exeArgs
124176
if ($LASTEXITCODE -ne 0) {
125-
throw "cl.exe returned exit code $LASTEXITCODE while building interpreter"
177+
throw "clang.exe returned exit code $LASTEXITCODE while building interpreter"
126178
}
127179

128180
$outExe = Join-Path $buildDir "prefix.exe"
@@ -157,18 +209,29 @@ try {
157209
New-Item -ItemType Directory -Path $extBuildDir -Force | Out-Null
158210
Push-Location $extBuildDir
159211
try {
160-
$extArgs = @(
161-
"/std:c17", "/Gd", "/O2", "/W4", "/WX", "/nologo", "/LD", "/LTCG",
212+
$extArgs = @($clangArgs + $releaseCompileArgs + @(
213+
"/LD",
162214
"/I$src",
163215
"/Fe:$extOutName",
164-
$extSourcePath,
165-
$runtimeLibPath
166-
)
216+
$extSourcePath
217+
))
218+
# Add OS-specific libraries required by some extensions.
219+
if ($linkLibs.Count -gt 0) { $extArgs += $linkLibs }
220+
$extArgs += $runtimeLibPath
221+
$extLinkFlags = @()
222+
if ($isWindows) {
223+
$extLinkFlags += "/link"
224+
$extLinkFlags += "/OPT:REF"
225+
$extLinkFlags += "/OPT:ICF"
226+
} else {
227+
$extLinkFlags += "/clang:-Wl,--gc-sections"
228+
}
229+
$extArgs += $extLinkFlags
167230

168-
Write-Host "Invoking: cl.exe $($extArgs -join ' ')"
169-
& cl.exe @extArgs
231+
Write-Host "Invoking: clang.exe $($extArgs -join ' ')"
232+
& clang.exe @extArgs
170233
if ($LASTEXITCODE -ne 0) {
171-
throw "cl.exe returned exit code $LASTEXITCODE while building extension '$extSourcePath'"
234+
throw "clang.exe returned exit code $LASTEXITCODE while building extension '$extSourcePath'"
172235
}
173236

174237
$extOutPath = Join-Path $extBuildDir $extOutName

ext/std/networking.c

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,6 @@
66
#include <stdint.h>
77
#include <stdbool.h>
88

9-
#ifdef _MSC_VER
10-
#define strdup _strdup
11-
#endif
12-
13-
#ifndef _WIN32
14-
#include <strings.h>
15-
#define _stricmp strcasecmp
16-
#endif
17-
189
#ifdef _WIN32
1910
#ifndef WIN32_LEAN_AND_MEAN
2011
#define WIN32_LEAN_AND_MEAN
@@ -23,8 +14,9 @@
2314
#include <ws2tcpip.h>
2415
#include <windows.h>
2516
#include <winhttp.h>
26-
#pragma comment(lib, "ws2_32.lib")
27-
#pragma comment(lib, "winhttp.lib")
17+
#if 0
18+
/* Linker directives removed - handled by build system instead. */
19+
#endif
2820
#else
2921
#include <unistd.h>
3022
#include <errno.h>
@@ -212,11 +204,11 @@ static int tns_to_bytes(Value v, unsigned char** out_data, size_t* out_len) {
212204

213205
static char* normalize_encoding_name(const char* coding) {
214206
if (!coding || coding[0] == '\0') return strdup("UTF-8");
215-
if (_stricmp(coding, "UTF8") == 0 || _stricmp(coding, "UTF-8") == 0) return strdup("UTF-8");
216-
if (_stricmp(coding, "UTF16") == 0 || _stricmp(coding, "UTF-16") == 0) return strdup("UTF-16");
217-
if (_stricmp(coding, "ASCII") == 0) return strdup("ASCII");
218-
if (_stricmp(coding, "LATIN1") == 0 || _stricmp(coding, "LATIN-1") == 0) return strdup("latin-1");
219-
if (_stricmp(coding, "ANSI") == 0) return strdup("cp1252");
207+
if (prefix_stricmp(coding, "UTF8") == 0 || prefix_stricmp(coding, "UTF-8") == 0) return strdup("UTF-8");
208+
if (prefix_stricmp(coding, "UTF16") == 0 || prefix_stricmp(coding, "UTF-16") == 0) return strdup("UTF-16");
209+
if (prefix_stricmp(coding, "ASCII") == 0) return strdup("ASCII");
210+
if (prefix_stricmp(coding, "LATIN1") == 0 || prefix_stricmp(coding, "LATIN-1") == 0) return strdup("latin-1");
211+
if (prefix_stricmp(coding, "ANSI") == 0) return strdup("cp1252");
220212
return strdup(coding);
221213
}
222214

@@ -369,7 +361,7 @@ static int ensure_bridge_script(char* out_path, size_t out_path_cap) {
369361
if (snprintf(script_path, sizeof(script_path), "%s%s", temp_dir, "prefix_networking_bridge.py") < 0) return -1;
370362
const char* code = py_bridge_script();
371363
if (write_text_file(script_path, code) != 0) return -1;
372-
strncpy_s(out_path, out_path_cap, script_path, out_path_cap - 1);
364+
snprintf(out_path, out_path_cap, "%s", script_path);
373365
return 0;
374366
#endif
375367
}
@@ -385,7 +377,7 @@ static int create_temp_file_path(char* out_path, size_t out_cap) {
385377
if (n == 0 || n >= sizeof(temp_dir)) return -1;
386378
char temp_file[MAX_PATH];
387379
if (GetTempFileNameA(temp_dir, "pfx", 0, temp_file) == 0) return -1;
388-
strncpy_s(out_path, out_cap, temp_file, out_cap - 1);
380+
snprintf(out_path, out_cap, "%s", temp_file);
389381
return 0;
390382
#endif
391383
}
@@ -720,7 +712,7 @@ static Value op_tcp_send(Interpreter* interp, Value* args, int argc, Expr** arg_
720712
const char* coding = (argc >= 3) ? as_cstr(args[2]) : "UTF-8";
721713
char* norm = normalize_encoding_name(coding);
722714
if (!norm) RUNTIME_ERROR(interp, "TCP_SEND failed: out of memory", line, col);
723-
if (_stricmp(norm, "UTF-8") != 0 && _stricmp(norm, "ASCII") != 0 && _stricmp(norm, "latin-1") != 0 && _stricmp(norm, "cp1252") != 0) {
715+
if (prefix_stricmp(norm, "UTF-8") != 0 && prefix_stricmp(norm, "ASCII") != 0 && prefix_stricmp(norm, "latin-1") != 0 && prefix_stricmp(norm, "cp1252") != 0) {
724716
free(norm);
725717
RUNTIME_ERROR(interp, "TCP_SEND failed: unsupported coding", line, col);
726718
}

ext/std/win32.c

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,7 @@
1313
static void set_runtime_error(Interpreter* interp, const char* msg, int line, int col) {
1414
if (!interp) return;
1515
if (interp->error) free(interp->error);
16-
#ifdef _MSC_VER
17-
interp->error = msg ? _strdup(msg) : NULL;
18-
#else
1916
interp->error = msg ? strdup(msg) : NULL;
20-
#endif
2117
interp->error_line = line;
2218
interp->error_col = col;
2319
}
@@ -112,12 +108,8 @@ static Value op_win_load_library(Interpreter* interp, Value* args, int argc, Exp
112108
if (!name) return value_null();
113109
// Accept both with and without .dll
114110
char buf[1024];
115-
if (_stricmp(name + (strlen(name) >= 4 ? strlen(name) - 4 : 0), ".dll") == 0) {
116-
#ifdef _MSC_VER
117-
strcpy_s(buf, sizeof(buf), name);
118-
#else
111+
if (prefix_stricmp(name + (strlen(name) >= 4 ? strlen(name) - 4 : 0), ".dll") == 0) {
119112
strncpy(buf, name, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0';
120-
#endif
121113
} else {
122114
snprintf(buf, sizeof(buf), "%s.dll", name);
123115
}
@@ -317,15 +309,12 @@ static Value op_win_call(Interpreter* interp, Value* args, int argc, Expr** arg_
317309
const char* func = value_as_cstr(interp, args[1], "func", line, col); if (!func) return value_null();
318310
const char* arg_types = NULL; if (args[2].type == VAL_STR) arg_types = args[2].as.s; else { set_runtime_error(interp, "arg types must be STR", line, col); return value_null(); }
319311
const char* ret_type = NULL; if (args[3].type == VAL_STR) ret_type = args[3].as.s; else { set_runtime_error(interp, "ret type must be STR", line, col); return value_null(); }
312+
(void)ret_type;
320313

321314
// Normalize library name
322315
char libbuf[1024]; if (strlen(lib) >= sizeof(libbuf)-5) { set_runtime_error(interp, "library name too long", line, col); return value_null(); }
323-
if (_stricmp(lib + (strlen(lib) >= 4 ? strlen(lib) - 4 : 0), ".dll") == 0) {
324-
#ifdef _MSC_VER
325-
strcpy_s(libbuf, sizeof(libbuf), lib);
326-
#else
316+
if (prefix_stricmp(lib + (strlen(lib) >= 4 ? strlen(lib) - 4 : 0), ".dll") == 0) {
327317
strncpy(libbuf, lib, sizeof(libbuf)-1); libbuf[sizeof(libbuf)-1] = '\0';
328-
#endif
329318
} else snprintf(libbuf, sizeof(libbuf), "%s.dll", lib);
330319
HMODULE h = LoadLibraryA(libbuf);
331320
if (!h) { char em[128]; snprintf(em, sizeof(em), "Failed to load DLL %s: %lu", libbuf, (unsigned long)GetLastError()); set_runtime_error(interp, em, line, col); return value_null(); }
@@ -337,11 +326,7 @@ static Value op_win_call(Interpreter* interp, Value* args, int argc, Expr** arg_
337326
int provided = argc - 4;
338327
int code_count = 0;
339328
char codes[64];
340-
#ifdef _MSC_VER
341-
strcpy_s(codes, sizeof(codes), arg_types);
342-
#else
343329
strncpy(codes, arg_types, sizeof(codes)-1); codes[sizeof(codes)-1] = '\0';
344-
#endif
345330
// if comma-separated, compact
346331
char compact[64]; int cc = 0;
347332
for (int i = 0; codes[i] && cc + 1 < (int)sizeof(compact); i++) if (codes[i] != ',') compact[cc++] = codes[i]; compact[cc] = '\0';

lib/std/gui/gui.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,9 @@
1212
#define WIN32_LEAN_AND_MEAN
1313
#endif
1414
#include <windows.h>
15-
#pragma comment(lib, "user32.lib")
16-
#pragma comment(lib, "gdi32.lib")
15+
#if 0
16+
/* Linker directives removed; link against OS libraries via build system when needed. */
1717
#endif
18-
19-
#ifdef _MSC_VER
20-
#define strdup _strdup
2118
#endif
2219

2320
#ifdef _WIN32

lib/std/image/image.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@
1313
#define WIN32_LEAN_AND_MEAN
1414
#endif
1515
#include <windows.h>
16-
#pragma comment(lib, "ole32.lib")
16+
#if 0
17+
/* Linker directives removed; link against OS libraries via build system when needed. */
1718
#endif
18-
19-
#ifdef _MSC_VER
20-
#define strdup _strdup
2119
#endif
2220

2321
typedef struct {
@@ -29,11 +27,7 @@ typedef struct {
2927
static void set_runtime_error(Interpreter* interp, const char* msg, int line, int col) {
3028
if (!interp) return;
3129
if (interp->error) free(interp->error);
32-
#ifdef _MSC_VER
33-
interp->error = msg ? _strdup(msg) : NULL;
34-
#else
3530
interp->error = msg ? strdup(msg) : NULL;
36-
#endif
3731
interp->error_line = line;
3832
interp->error_col = col;
3933
}

src/ast.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33
#include <string.h>
44
#include <stdio.h>
55

6-
#ifdef _MSC_VER
7-
#define strdup _strdup
8-
#endif
9-
106
static void* ast_alloc(size_t size) {
117
void* ptr = malloc(size);
128
if (!ptr) {

0 commit comments

Comments
 (0)