openvibers/source.openvibe.games Commits
Build QuickJS with clang-cl on Windows
Changes
nnew file mode 100644@@@ -0,0 +1,5 @@++# Windows QuickJS build uses clang-cl++++QuickJS uses GNU/C99 compiler features that plain `cl.exe` does not parse cleanly, including GNU attributes, GCC/Clang builtins, and C initializer patterns. The Windows workflow now prefers `clang-cl.exe` for QuickJS only. `clang-cl` emits MSVC-compatible COFF `.obj` files, and `lib.exe` packs them into `libquickjs_openvibe.lib` for the regular Source SDK MSVC link.++++The rest of the Source SDK build still uses the Visual Studio/MSBuild toolchain.@@@ -9,13 +9,23 @@ $Out = Join-Path $SdkQjs 'build'$LogDir = Join-Path $Root 'artifacts/windows-build-debug'function Say($m) { Write-Host "[openvibe-qjs-win] $m" }++function FirstExistingCommand([string[]]$names) {++ foreach ($name in $names) {++ $cmd = Get-Command $name -ErrorAction SilentlyContinue++ if ($cmd) { return $cmd.Source }++ }++ return $null++}++function FirstExistingPath([string[]]$paths) {++ foreach ($p in $paths) {++ if ($p -and (Test-Path $p)) { return (Resolve-Path $p).Path }++ }++ return $null++}if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."}--if (!(Get-Command cl.exe -ErrorAction SilentlyContinue)) {-- throw "cl.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."--}if (!(Get-Command lib.exe -ErrorAction SilentlyContinue)) {throw "lib.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."}@@@ -27,93 +37,28 @@ $CompatInclude = Join-Path $Out 'compat/include'$CompatSys = Join-Path $CompatInclude 'sys'New-Item -ItemType Directory -Force -Path $CompatInclude, $CompatSys | Out-Null--# Upstream QuickJS is mostly GCC/clang oriented. MSVC needs a forced include for:--# - GCC __attribute__((...)) syntax used in cutils.h--# - GCC __builtin_* helpers used in cutils.h--# - a few libc aliases--$MsvcCompat = Join-Path $CompatInclude 'openvibe_qjs_msvc_compat.h'--@'--#ifndef OPENVIBE_QJS_MSVC_COMPAT_H--#define OPENVIBE_QJS_MSVC_COMPAT_H----#if defined(_MSC_VER)--#ifndef WIN32_LEAN_AND_MEAN--#define WIN32_LEAN_AND_MEAN--#endif--#ifndef NOMINMAX--#define NOMINMAX--#endif--#include <stdint.h>--#include <stdlib.h>--#include <intrin.h>----#ifndef __attribute__--#define __attribute__(x)--#endif--#ifndef __builtin_expect--#define __builtin_expect(x, y) (x)--#endif----static __inline int openvibe_qjs_clz32(unsigned int v)--{-- unsigned long idx;-- if (_BitScanReverse(&idx, v)) return 31 - (int)idx;-- return 32;--}----static __inline int openvibe_qjs_ctz32(unsigned int v)--{-- unsigned long idx;-- if (_BitScanForward(&idx, v)) return (int)idx;-- return 32;--}----static __inline int openvibe_qjs_clz64(uint64_t v)--{-- uint32_t hi = (uint32_t)(v >> 32);-- uint32_t lo = (uint32_t)v;-- if (hi) return openvibe_qjs_clz32(hi);-- return 32 + openvibe_qjs_clz32(lo);--}----static __inline int openvibe_qjs_ctz64(uint64_t v)--{-- uint32_t hi = (uint32_t)(v >> 32);-- uint32_t lo = (uint32_t)v;-- if (lo) return openvibe_qjs_ctz32(lo);-- return 32 + openvibe_qjs_ctz32(hi);--}----#ifndef __builtin_clz--#define __builtin_clz(x) openvibe_qjs_clz32((unsigned int)(x))--#endif--#ifndef __builtin_ctz--#define __builtin_ctz(x) openvibe_qjs_ctz32((unsigned int)(x))--#endif--#ifndef __builtin_clzll--#define __builtin_clzll(x) openvibe_qjs_clz64((uint64_t)(x))--#endif--#ifndef __builtin_ctzll--#define __builtin_ctzll(x) openvibe_qjs_ctz64((uint64_t)(x))--#endif----#ifndef alloca--#define alloca _alloca--#endif--#ifndef strdup--#define strdup _strdup--#endif----#endif /* _MSC_VER */--#endif /* OPENVIBE_QJS_MSVC_COMPAT_H */--'@ | Set-Content -Encoding ascii $MsvcCompat++# QuickJS is written for GCC/clang C, not plain MSVC C. Prefer clang-cl because it++# emits MSVC-compatible COFF .obj files while accepting GNU/C99 constructs that++# QuickJS uses heavily. Keep cl.exe only as a last-resort fallback.++$clangCandidates = @(++ (FirstExistingCommand @('clang-cl.exe','clang-cl')),++ (Join-Path ${env:ProgramFiles} 'LLVM/bin/clang-cl.exe'),++ (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm/x86/bin/clang-cl.exe'),++ (Join-Path ${env:ProgramFiles} 'Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm/x86/bin/clang-cl.exe'),++ (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/2022/BuildTools/VC/Tools/Llvm/x86/bin/clang-cl.exe'),++ (Join-Path ${env:ProgramFiles} 'Microsoft Visual Studio/2022/BuildTools/VC/Tools/Llvm/x86/bin/clang-cl.exe')++)++$ClangCl = FirstExistingPath $clangCandidates++$ClExe = FirstExistingCommand @('cl.exe')++if ($ClangCl) { Say "using clang-cl=$ClangCl" }++elseif ($ClExe) { Say "clang-cl not found; falling back to cl.exe=$ClExe" }++else { throw "Neither clang-cl.exe nor cl.exe was found." }--# Upstream QuickJS includes <sys/time.h>. MSVC does not provide it.++# Windows sys/time.h shim for upstream QuickJS.$TimeHeader = Join-Path $CompatSys 'time.h'@'#ifndef OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H#define OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H--#if defined(_WIN32)#ifndef WIN32_LEAN_AND_MEAN#define WIN32_LEAN_AND_MEAN@@@ -122,11 +67,9 @@ $TimeHeader = Join-Path $CompatSys 'time.h'#define NOMINMAX#endif#include <windows.h>--#ifdef __cplusplusextern "C" {#endif--#ifndef _TIMEVAL_DEFINED#define _TIMEVAL_DEFINEDtypedef long suseconds_t;@@@ -135,7 +78,6 @@ struct timeval {long tv_usec;};#endif--static __inline int gettimeofday(struct timeval *tv, void *tz){FILETIME ft;@@@ -152,19 +94,17 @@ static __inline int gettimeofday(struct timeval *tv, void *tz)tv->tv_usec = (long)(usec % 1000000ULL);return 0;}--#ifdef __cplusplus}#endif#else#include_next <sys/time.h>#endif----#endif /* OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H */++#endif'@ | Set-Content -Encoding ascii $TimeHeader--# Stub enough pthread API for accidental includes. CONFIG_ATOMICS is disabled below,--# so this should not be exercised, but the header keeps MSVC moving if upstream includes it.++# pthread.h shim. We also disable CONFIG_ATOMICS below, so this is only to satisfy++# accidental includes from vendored QuickJS variants.$PThreadHeader = Join-Path $CompatInclude 'pthread.h'@'#ifndef OPENVIBE_QUICKJS_COMPAT_PTHREAD_H@@@ -183,15 +123,91 @@ static __inline int pthread_join(pthread_t t, void **ret) { (void)t; if (ret) *r#else#include_next <pthread.h>#endif--#endif /* OPENVIBE_QUICKJS_COMPAT_PTHREAD_H */++#endif'@ | Set-Content -Encoding ascii $PThreadHeader--# Disable QuickJS atomics/workers for this embedded game runtime on MSVC.--# That avoids needing a real pthread implementation and is fine for our scripts.++# Forced include for clang-cl. Do NOT redefine __attribute__ or __builtin_* here;++# clang handles those and QuickJS needs them.++$ClangCompat = Join-Path $CompatInclude 'openvibe_qjs_clangcl_compat.h'++@'++#ifndef OPENVIBE_QJS_CLANGCL_COMPAT_H++#define OPENVIBE_QJS_CLANGCL_COMPAT_H++#if defined(_WIN32)++#ifndef WIN32_LEAN_AND_MEAN++#define WIN32_LEAN_AND_MEAN++#endif++#ifndef NOMINMAX++#define NOMINMAX++#endif++#include <stdint.h>++#include <stdlib.h>++#include <malloc.h>++#ifndef alloca++#define alloca _alloca++#endif++#ifndef strdup++#define strdup _strdup++#endif++#endif++#endif++'@ | Set-Content -Encoding ascii $ClangCompat++++# Forced include for emergency cl.exe fallback only.++$MsvcCompat = Join-Path $CompatInclude 'openvibe_qjs_msvc_compat.h'++@'++#ifndef OPENVIBE_QJS_MSVC_COMPAT_H++#define OPENVIBE_QJS_MSVC_COMPAT_H++#if defined(_MSC_VER) && !defined(__clang__)++#ifndef WIN32_LEAN_AND_MEAN++#define WIN32_LEAN_AND_MEAN++#endif++#ifndef NOMINMAX++#define NOMINMAX++#endif++#include <stdint.h>++#include <stdlib.h>++#include <malloc.h>++#include <intrin.h>++#ifndef __attribute__++#define __attribute__(x)++#endif++#ifndef __builtin_expect++#define __builtin_expect(x, y) (x)++#endif++#ifndef __builtin_frame_address++#define __builtin_frame_address(x) ((void*)0)++#endif++static __inline int openvibe_qjs_clz32(unsigned int v) { unsigned long idx; if (_BitScanReverse(&idx, v)) return 31 - (int)idx; return 32; }++static __inline int openvibe_qjs_ctz32(unsigned int v) { unsigned long idx; if (_BitScanForward(&idx, v)) return (int)idx; return 32; }++static __inline int openvibe_qjs_clz64(uint64_t v) { uint32_t hi = (uint32_t)(v >> 32); uint32_t lo = (uint32_t)v; if (hi) return openvibe_qjs_clz32(hi); return 32 + openvibe_qjs_clz32(lo); }++static __inline int openvibe_qjs_ctz64(uint64_t v) { uint32_t hi = (uint32_t)(v >> 32); uint32_t lo = (uint32_t)v; if (lo) return openvibe_qjs_ctz32(lo); return 32 + openvibe_qjs_ctz32(hi); }++#ifndef __builtin_clz++#define __builtin_clz(x) openvibe_qjs_clz32((unsigned int)(x))++#endif++#ifndef __builtin_ctz++#define __builtin_ctz(x) openvibe_qjs_ctz32((unsigned int)(x))++#endif++#ifndef __builtin_clzll++#define __builtin_clzll(x) openvibe_qjs_clz64((uint64_t)(x))++#endif++#ifndef __builtin_ctzll++#define __builtin_ctzll(x) openvibe_qjs_ctz64((uint64_t)(x))++#endif++#ifndef alloca++#define alloca _alloca++#endif++#ifndef strdup++#define strdup _strdup++#endif++#endif++#endif++'@ | Set-Content -Encoding ascii $MsvcCompat++++# Disable QuickJS atomics/workers for the embedded game runtime.$QuickJsC = Join-Path $SdkQjs 'quickjs.c'if (Test-Path $QuickJsC) {$q = Get-Content -Raw $QuickJsC-- $q2 = [regex]::Replace($q, '(?m)^\s*#\s*define\s+CONFIG_ATOMICS\s*$', '/* OpenVibe MSVC build: CONFIG_ATOMICS disabled */')++ $q2 = [regex]::Replace($q, '(?m)^\s*#\s*define\s+CONFIG_ATOMICS\s*$', '/* OpenVibe Windows embedded build: CONFIG_ATOMICS disabled */')if ($q2 -ne $q) {Set-Content -Encoding ascii $QuickJsC $q2Say 'disabled CONFIG_ATOMICS in SDK quickjs.c copy'@@@ -201,9 +217,10 @@ if (Test-Path $QuickJsC) {}Say "compat include=$CompatInclude"--Say "compat forced include=$MsvcCompat"Say "compat sys/time.h=$TimeHeader"Say "compat pthread.h=$PThreadHeader"++Say "clang forced include=$ClangCompat"++Say "msvc forced include=$MsvcCompat"$sources = @('quickjs.c','libregexp.c','libunicode.c','cutils.c')if (Test-Path (Join-Path $SdkQjs 'dtoa.c')) { $sources += 'dtoa.c' }@@@ -212,32 +229,56 @@ if (Test-Path (Join-Path $SdkQjs 'libbf.c')) { $sources += 'libbf.c' }Remove-Item (Join-Path $Out '*.obj') -Force -ErrorAction SilentlyContinueRemove-Item (Join-Path $Out 'libquickjs_openvibe.lib') -Force -ErrorAction SilentlyContinue--$common = @(-- '/nologo',-- '/TC',-- '/O2',-- '/MT',-- '/W3',-- '/D_CRT_SECURE_NO_WARNINGS',-- '/D_CRT_NONSTDC_NO_WARNINGS',-- '/DWIN32',-- '/D_WINDOWS',-- '/DNOMINMAX',-- '/DWIN32_LEAN_AND_MEAN',-- "/DCONFIG_VERSION=`\`"openvibe`\`"",-- "/FI$MsvcCompat",-- "/I$CompatInclude",-- "/I$SdkQjs"--)++if ($ClangCl) {++ $Compiler = $ClangCl++ $Common = @(++ '/nologo',++ '/TC',++ '/O2',++ '/MT',++ '/W3',++ '/D_CRT_SECURE_NO_WARNINGS',++ '/D_CRT_NONSTDC_NO_WARNINGS',++ '/DWIN32',++ '/D_WINDOWS',++ '/DNOMINMAX',++ '/DWIN32_LEAN_AND_MEAN',++ "/DCONFIG_VERSION=`\`"openvibe`\`"",++ "/FI$ClangCompat",++ "/I$CompatInclude",++ "/I$SdkQjs",++ '/clang:-std=gnu11',++ '/clang:-Wno-everything'++ )++} else {++ $Compiler = $ClExe++ $Common = @(++ '/nologo',++ '/TC',++ '/O2',++ '/MT',++ '/W3',++ '/D_CRT_SECURE_NO_WARNINGS',++ '/D_CRT_NONSTDC_NO_WARNINGS',++ '/DWIN32',++ '/D_WINDOWS',++ '/DNOMINMAX',++ '/DWIN32_LEAN_AND_MEAN',++ "/DCONFIG_VERSION=`\`"openvibe`\`"",++ "/FI$MsvcCompat",++ "/I$CompatInclude",++ "/I$SdkQjs"++ )++}$objects = @()foreach ($src in $sources) {$srcPath = Join-Path $SdkQjs $src$obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')-- Say "cl $src -> $obj"-- & cl.exe @common /c "$srcPath" "/Fo$obj"-- if ($LASTEXITCODE -ne 0) { throw "cl failed for $src" }-- if (!(Test-Path $obj)) { throw "cl reported success but did not produce $obj" }++ Say "$([IO.Path]::GetFileName($Compiler)) $src -> $obj"++ & $Compiler @Common /c "$srcPath" "/Fo$obj"++ if ($LASTEXITCODE -ne 0) { throw "$Compiler failed for $src" }++ if (!(Test-Path $obj)) { throw "$Compiler reported success but did not produce $obj" }$objects += $obj}@@@ -246,5 +287,4 @@ Say "lib -> $outLib"& lib.exe /nologo "/OUT:$outLib" $objectsif ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }if (!(Test-Path $outLib)) { throw "lib.exe did not produce $outLib" }--Say "built $outLib"nnew file mode 100755@@@ -0,0 +1,342 @@++#!/usr/bin/env bash++set -euo pipefail++++ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"++cd "$ROOT"++BRANCH="$(git branch --show-current)"++++say() { echo "[openvibe] $*"; }++++say "fix QuickJS Windows build: use clang-cl for GNU/C99 QuickJS objects"++say "root=$ROOT"++say "branch=$BRANCH"++++mkdir -p tools docs++++cat > tools/build-quickjs-lib-windows.ps1 <<'PS1'++$ErrorActionPreference = 'Stop'++++$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }++$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }++++$SrcQjs = Join-Path $Root 'sdk/openvibe/third_party/quickjs'++$SdkQjs = Join-Path $Sdk 'src/game/shared/openvibe/third_party/quickjs'++$Out = Join-Path $SdkQjs 'build'++$LogDir = Join-Path $Root 'artifacts/windows-build-debug'++++function Say($m) { Write-Host "[openvibe-qjs-win] $m" }++function FirstExistingCommand([string[]]$names) {++ foreach ($name in $names) {++ $cmd = Get-Command $name -ErrorAction SilentlyContinue++ if ($cmd) { return $cmd.Source }++ }++ return $null++}++function FirstExistingPath([string[]]$paths) {++ foreach ($p in $paths) {++ if ($p -and (Test-Path $p)) { return (Resolve-Path $p).Path }++ }++ return $null++}++++if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {++ throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."++}++if (!(Get-Command lib.exe -ErrorAction SilentlyContinue)) {++ throw "lib.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."++}++++New-Item -ItemType Directory -Force -Path $SdkQjs, $Out, $LogDir | Out-Null++Copy-Item -Path (Join-Path $SrcQjs '*') -Destination $SdkQjs -Recurse -Force++++$CompatInclude = Join-Path $Out 'compat/include'++$CompatSys = Join-Path $CompatInclude 'sys'++New-Item -ItemType Directory -Force -Path $CompatInclude, $CompatSys | Out-Null++++# QuickJS is written for GCC/clang C, not plain MSVC C. Prefer clang-cl because it++# emits MSVC-compatible COFF .obj files while accepting GNU/C99 constructs that++# QuickJS uses heavily. Keep cl.exe only as a last-resort fallback.++$clangCandidates = @(++ (FirstExistingCommand @('clang-cl.exe','clang-cl')),++ (Join-Path ${env:ProgramFiles} 'LLVM/bin/clang-cl.exe'),++ (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm/x86/bin/clang-cl.exe'),++ (Join-Path ${env:ProgramFiles} 'Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm/x86/bin/clang-cl.exe'),++ (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/2022/BuildTools/VC/Tools/Llvm/x86/bin/clang-cl.exe'),++ (Join-Path ${env:ProgramFiles} 'Microsoft Visual Studio/2022/BuildTools/VC/Tools/Llvm/x86/bin/clang-cl.exe')++)++$ClangCl = FirstExistingPath $clangCandidates++$ClExe = FirstExistingCommand @('cl.exe')++if ($ClangCl) { Say "using clang-cl=$ClangCl" }++elseif ($ClExe) { Say "clang-cl not found; falling back to cl.exe=$ClExe" }++else { throw "Neither clang-cl.exe nor cl.exe was found." }++++# Windows sys/time.h shim for upstream QuickJS.++$TimeHeader = Join-Path $CompatSys 'time.h'++@'++#ifndef OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H++#define OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H++#if defined(_WIN32)++#ifndef WIN32_LEAN_AND_MEAN++#define WIN32_LEAN_AND_MEAN++#endif++#ifndef NOMINMAX++#define NOMINMAX++#endif++#include <windows.h>++#ifdef __cplusplus++extern "C" {++#endif++#ifndef _TIMEVAL_DEFINED++#define _TIMEVAL_DEFINED++typedef long suseconds_t;++struct timeval {++ long tv_sec;++ long tv_usec;++};++#endif++static __inline int gettimeofday(struct timeval *tv, void *tz)++{++ FILETIME ft;++ ULARGE_INTEGER uli;++ unsigned long long usec;++ const unsigned long long unix_epoch = 116444736000000000ULL;++ (void)tz;++ if (!tv) return -1;++ GetSystemTimeAsFileTime(&ft);++ uli.LowPart = ft.dwLowDateTime;++ uli.HighPart = ft.dwHighDateTime;++ usec = (uli.QuadPart - unix_epoch) / 10ULL;++ tv->tv_sec = (long)(usec / 1000000ULL);++ tv->tv_usec = (long)(usec % 1000000ULL);++ return 0;++}++#ifdef __cplusplus++}++#endif++#else++#include_next <sys/time.h>++#endif++#endif++'@ | Set-Content -Encoding ascii $TimeHeader++++# pthread.h shim. We also disable CONFIG_ATOMICS below, so this is only to satisfy++# accidental includes from vendored QuickJS variants.++$PThreadHeader = Join-Path $CompatInclude 'pthread.h'++@'++#ifndef OPENVIBE_QUICKJS_COMPAT_PTHREAD_H++#define OPENVIBE_QUICKJS_COMPAT_PTHREAD_H++#if defined(_WIN32)++typedef int pthread_mutex_t;++typedef int pthread_t;++typedef int pthread_attr_t;++#define PTHREAD_MUTEX_INITIALIZER 0++static __inline int pthread_mutex_init(pthread_mutex_t *m, const void *a) { (void)a; if (m) *m = 0; return 0; }++static __inline int pthread_mutex_destroy(pthread_mutex_t *m) { (void)m; return 0; }++static __inline int pthread_mutex_lock(pthread_mutex_t *m) { (void)m; return 0; }++static __inline int pthread_mutex_unlock(pthread_mutex_t *m) { (void)m; return 0; }++static __inline int pthread_create(pthread_t *t, const pthread_attr_t *a, void *(*fn)(void *), void *arg) { (void)t; (void)a; (void)fn; (void)arg; return -1; }++static __inline int pthread_join(pthread_t t, void **ret) { (void)t; if (ret) *ret = 0; return 0; }++#else++#include_next <pthread.h>++#endif++#endif++'@ | Set-Content -Encoding ascii $PThreadHeader++++# Forced include for clang-cl. Do NOT redefine __attribute__ or __builtin_* here;++# clang handles those and QuickJS needs them.++$ClangCompat = Join-Path $CompatInclude 'openvibe_qjs_clangcl_compat.h'++@'++#ifndef OPENVIBE_QJS_CLANGCL_COMPAT_H++#define OPENVIBE_QJS_CLANGCL_COMPAT_H++#if defined(_WIN32)++#ifndef WIN32_LEAN_AND_MEAN++#define WIN32_LEAN_AND_MEAN++#endif++#ifndef NOMINMAX++#define NOMINMAX++#endif++#include <stdint.h>++#include <stdlib.h>++#include <malloc.h>++#ifndef alloca++#define alloca _alloca++#endif++#ifndef strdup++#define strdup _strdup++#endif++#endif++#endif++'@ | Set-Content -Encoding ascii $ClangCompat++++# Forced include for emergency cl.exe fallback only.++$MsvcCompat = Join-Path $CompatInclude 'openvibe_qjs_msvc_compat.h'++@'++#ifndef OPENVIBE_QJS_MSVC_COMPAT_H++#define OPENVIBE_QJS_MSVC_COMPAT_H++#if defined(_MSC_VER) && !defined(__clang__)++#ifndef WIN32_LEAN_AND_MEAN++#define WIN32_LEAN_AND_MEAN++#endif++#ifndef NOMINMAX++#define NOMINMAX++#endif++#include <stdint.h>++#include <stdlib.h>++#include <malloc.h>++#include <intrin.h>++#ifndef __attribute__++#define __attribute__(x)++#endif++#ifndef __builtin_expect++#define __builtin_expect(x, y) (x)++#endif++#ifndef __builtin_frame_address++#define __builtin_frame_address(x) ((void*)0)++#endif++static __inline int openvibe_qjs_clz32(unsigned int v) { unsigned long idx; if (_BitScanReverse(&idx, v)) return 31 - (int)idx; return 32; }++static __inline int openvibe_qjs_ctz32(unsigned int v) { unsigned long idx; if (_BitScanForward(&idx, v)) return (int)idx; return 32; }++static __inline int openvibe_qjs_clz64(uint64_t v) { uint32_t hi = (uint32_t)(v >> 32); uint32_t lo = (uint32_t)v; if (hi) return openvibe_qjs_clz32(hi); return 32 + openvibe_qjs_clz32(lo); }++static __inline int openvibe_qjs_ctz64(uint64_t v) { uint32_t hi = (uint32_t)(v >> 32); uint32_t lo = (uint32_t)v; if (lo) return openvibe_qjs_ctz32(lo); return 32 + openvibe_qjs_ctz32(hi); }++#ifndef __builtin_clz++#define __builtin_clz(x) openvibe_qjs_clz32((unsigned int)(x))++#endif++#ifndef __builtin_ctz++#define __builtin_ctz(x) openvibe_qjs_ctz32((unsigned int)(x))++#endif++#ifndef __builtin_clzll++#define __builtin_clzll(x) openvibe_qjs_clz64((uint64_t)(x))++#endif++#ifndef __builtin_ctzll++#define __builtin_ctzll(x) openvibe_qjs_ctz64((uint64_t)(x))++#endif++#ifndef alloca++#define alloca _alloca++#endif++#ifndef strdup++#define strdup _strdup++#endif++#endif++#endif++'@ | Set-Content -Encoding ascii $MsvcCompat++++# Disable QuickJS atomics/workers for the embedded game runtime.++$QuickJsC = Join-Path $SdkQjs 'quickjs.c'++if (Test-Path $QuickJsC) {++ $q = Get-Content -Raw $QuickJsC++ $q2 = [regex]::Replace($q, '(?m)^\s*#\s*define\s+CONFIG_ATOMICS\s*$', '/* OpenVibe Windows embedded build: CONFIG_ATOMICS disabled */')++ if ($q2 -ne $q) {++ Set-Content -Encoding ascii $QuickJsC $q2++ Say 'disabled CONFIG_ATOMICS in SDK quickjs.c copy'++ } else {++ Say 'CONFIG_ATOMICS define not found in quickjs.c copy; pthread shim remains available'++ }++}++++Say "compat include=$CompatInclude"++Say "compat sys/time.h=$TimeHeader"++Say "compat pthread.h=$PThreadHeader"++Say "clang forced include=$ClangCompat"++Say "msvc forced include=$MsvcCompat"++++$sources = @('quickjs.c','libregexp.c','libunicode.c','cutils.c')++if (Test-Path (Join-Path $SdkQjs 'dtoa.c')) { $sources += 'dtoa.c' }++if (Test-Path (Join-Path $SdkQjs 'libbf.c')) { $sources += 'libbf.c' }++++Remove-Item (Join-Path $Out '*.obj') -Force -ErrorAction SilentlyContinue++Remove-Item (Join-Path $Out 'libquickjs_openvibe.lib') -Force -ErrorAction SilentlyContinue++++if ($ClangCl) {++ $Compiler = $ClangCl++ $Common = @(++ '/nologo',++ '/TC',++ '/O2',++ '/MT',++ '/W3',++ '/D_CRT_SECURE_NO_WARNINGS',++ '/D_CRT_NONSTDC_NO_WARNINGS',++ '/DWIN32',++ '/D_WINDOWS',++ '/DNOMINMAX',++ '/DWIN32_LEAN_AND_MEAN',++ "/DCONFIG_VERSION=`\`"openvibe`\`"",++ "/FI$ClangCompat",++ "/I$CompatInclude",++ "/I$SdkQjs",++ '/clang:-std=gnu11',++ '/clang:-Wno-everything'++ )++} else {++ $Compiler = $ClExe++ $Common = @(++ '/nologo',++ '/TC',++ '/O2',++ '/MT',++ '/W3',++ '/D_CRT_SECURE_NO_WARNINGS',++ '/D_CRT_NONSTDC_NO_WARNINGS',++ '/DWIN32',++ '/D_WINDOWS',++ '/DNOMINMAX',++ '/DWIN32_LEAN_AND_MEAN',++ "/DCONFIG_VERSION=`\`"openvibe`\`"",++ "/FI$MsvcCompat",++ "/I$CompatInclude",++ "/I$SdkQjs"++ )++}++++$objects = @()++foreach ($src in $sources) {++ $srcPath = Join-Path $SdkQjs $src++ $obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')++ Say "$([IO.Path]::GetFileName($Compiler)) $src -> $obj"++ & $Compiler @Common /c "$srcPath" "/Fo$obj"++ if ($LASTEXITCODE -ne 0) { throw "$Compiler failed for $src" }++ if (!(Test-Path $obj)) { throw "$Compiler reported success but did not produce $obj" }++ $objects += $obj++}++++$outLib = Join-Path $Out 'libquickjs_openvibe.lib'++Say "lib -> $outLib"++& lib.exe /nologo "/OUT:$outLib" $objects++if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }++if (!(Test-Path $outLib)) { throw "lib.exe did not produce $outLib" }++Say "built $outLib"++PS1++++cat > docs/WINDOWS_QUICKJS_CLANGCL.md <<'MD'++# Windows QuickJS build uses clang-cl++++QuickJS uses GNU/C99 compiler features that plain `cl.exe` does not parse cleanly, including GNU attributes, GCC/Clang builtins, and C initializer patterns. The Windows workflow now prefers `clang-cl.exe` for QuickJS only. `clang-cl` emits MSVC-compatible COFF `.obj` files, and `lib.exe` packs them into `libquickjs_openvibe.lib` for the regular Source SDK MSVC link.++++The rest of the Source SDK build still uses the Visual Studio/MSBuild toolchain.++MD++++chmod +x tools/trigger-windows-dll-build-clean.sh 2>/dev/null || true++++git add tools/build-quickjs-lib-windows.ps1 docs/WINDOWS_QUICKJS_CLANGCL.md "$0" 2>/dev/null || git add tools/build-quickjs-lib-windows.ps1 docs/WINDOWS_QUICKJS_CLANGCL.md++++say "git diff summary"++git diff --cached --stat || true++++if git diff --cached --quiet; then++ say "no changes to commit"++else++ git commit -m "Build QuickJS with clang-cl on Windows"++ git push++fi++++say "triggering clean Windows DLL build"++if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then++ tools/trigger-windows-dll-build-clean.sh++else++ gh workflow run windows-source-sdk-dlls.yml --ref "$BRANCH"++ sleep 10++ RUN_ID="$(gh run list --workflow windows-source-sdk-dlls.yml --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"++ gh run watch "$RUN_ID" --exit-status || true++ tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true++fi++++say "done"
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+# Windows QuickJS build uses clang-cl
+
+QuickJS uses GNU/C99 compiler features that plain `cl.exe` does not parse cleanly, including GNU attributes, GCC/Clang builtins, and C initializer patterns. The Windows workflow now prefers `clang-cl.exe` for QuickJS only. `clang-cl` emits MSVC-compatible COFF `.obj` files, and `lib.exe` packs them into `libquickjs_openvibe.lib` for the regular Source SDK MSVC link.
+
+The rest of the Source SDK build still uses the Visual Studio/MSBuild toolchain.
@@ -9,13 +9,23 @@ $Out = Join-Path $SdkQjs 'build'
@@ -9,13 +9,23 @@ $Out = Join-Path $SdkQjs 'build'
$LogDir = Join-Path $Root 'artifacts/windows-build-debug'
$LogDir = Join-Path $Root 'artifacts/windows-build-debug'
function Say($m) { Write-Host "[openvibe-qjs-win] $m" }
function Say($m) { Write-Host "[openvibe-qjs-win] $m" }
+function FirstExistingCommand([string[]]$names) {
+ foreach ($name in $names) {
+ $cmd = Get-Command $name -ErrorAction SilentlyContinue
+ if ($cmd) { return $cmd.Source }
+ }
+ return $null
+}
+function FirstExistingPath([string[]]$paths) {
+ foreach ($p in $paths) {
+ if ($p -and (Test-Path $p)) { return (Resolve-Path $p).Path }
+ }
+ return $null
+}
if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {
if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {
throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."
throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."
}
}
-if (!(Get-Command cl.exe -ErrorAction SilentlyContinue)) {
- throw "cl.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."
-}
if (!(Get-Command lib.exe -ErrorAction SilentlyContinue)) {
if (!(Get-Command lib.exe -ErrorAction SilentlyContinue)) {
throw "lib.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."
throw "lib.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."
}
}
@@ -27,93 +37,28 @@ $CompatInclude = Join-Path $Out 'compat/include'
@@ -27,93 +37,28 @@ $CompatInclude = Join-Path $Out 'compat/include'
$CompatSys = Join-Path $CompatInclude 'sys'
$CompatSys = Join-Path $CompatInclude 'sys'
New-Item -ItemType Directory -Force -Path $CompatInclude, $CompatSys | Out-Null
New-Item -ItemType Directory -Force -Path $CompatInclude, $CompatSys | Out-Null
-# Upstream QuickJS is mostly GCC/clang oriented. MSVC needs a forced include for:
-# - GCC __attribute__((...)) syntax used in cutils.h
-# - GCC __builtin_* helpers used in cutils.h
-# - a few libc aliases
-$MsvcCompat = Join-Path $CompatInclude 'openvibe_qjs_msvc_compat.h'
-@'
-#ifndef OPENVIBE_QJS_MSVC_COMPAT_H
-#define OPENVIBE_QJS_MSVC_COMPAT_H
-
-#if defined(_MSC_VER)
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
-#endif
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-#include <stdint.h>
-#include <stdlib.h>
-#include <intrin.h>
-
-#ifndef __attribute__
-#define __attribute__(x)
-#endif
-#ifndef __builtin_expect
-#define __builtin_expect(x, y) (x)
-#endif
-
-static __inline int openvibe_qjs_clz32(unsigned int v)
-{
- unsigned long idx;
- if (_BitScanReverse(&idx, v)) return 31 - (int)idx;
- return 32;
-}
-
-static __inline int openvibe_qjs_ctz32(unsigned int v)
-{
- unsigned long idx;
- if (_BitScanForward(&idx, v)) return (int)idx;
- return 32;
-}
-
-static __inline int openvibe_qjs_clz64(uint64_t v)
-{
- uint32_t hi = (uint32_t)(v >> 32);
- uint32_t lo = (uint32_t)v;
- if (hi) return openvibe_qjs_clz32(hi);
- return 32 + openvibe_qjs_clz32(lo);
-}
-
-static __inline int openvibe_qjs_ctz64(uint64_t v)
-{
- uint32_t hi = (uint32_t)(v >> 32);
- uint32_t lo = (uint32_t)v;
- if (lo) return openvibe_qjs_ctz32(lo);
- return 32 + openvibe_qjs_ctz32(hi);
-}
-
-#ifndef __builtin_clz
-#define __builtin_clz(x) openvibe_qjs_clz32((unsigned int)(x))
-#endif
-#ifndef __builtin_ctz
-#define __builtin_ctz(x) openvibe_qjs_ctz32((unsigned int)(x))
-#endif
-#ifndef __builtin_clzll
-#define __builtin_clzll(x) openvibe_qjs_clz64((uint64_t)(x))
-#endif
-#ifndef __builtin_ctzll
-#define __builtin_ctzll(x) openvibe_qjs_ctz64((uint64_t)(x))
-#endif
-
-#ifndef alloca
-#define alloca _alloca
-#endif
-#ifndef strdup
-#define strdup _strdup
-#endif
-
-#endif /* _MSC_VER */
-#endif /* OPENVIBE_QJS_MSVC_COMPAT_H */
-'@ | Set-Content -Encoding ascii $MsvcCompat
+# QuickJS is written for GCC/clang C, not plain MSVC C. Prefer clang-cl because it
+# emits MSVC-compatible COFF .obj files while accepting GNU/C99 constructs that
+# QuickJS uses heavily. Keep cl.exe only as a last-resort fallback.
+$clangCandidates = @(
+ (FirstExistingCommand @('clang-cl.exe','clang-cl')),
+ (Join-Path ${env:ProgramFiles} 'LLVM/bin/clang-cl.exe'),
+ (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm/x86/bin/clang-cl.exe'),
+ (Join-Path ${env:ProgramFiles} 'Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm/x86/bin/clang-cl.exe'),
+ (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/2022/BuildTools/VC/Tools/Llvm/x86/bin/clang-cl.exe'),
+ (Join-Path ${env:ProgramFiles} 'Microsoft Visual Studio/2022/BuildTools/VC/Tools/Llvm/x86/bin/clang-cl.exe')
+)
+$ClangCl = FirstExistingPath $clangCandidates
+$ClExe = FirstExistingCommand @('cl.exe')
+if ($ClangCl) { Say "using clang-cl=$ClangCl" }
+elseif ($ClExe) { Say "clang-cl not found; falling back to cl.exe=$ClExe" }
+else { throw "Neither clang-cl.exe nor cl.exe was found." }
-# Upstream QuickJS includes <sys/time.h>. MSVC does not provide it.
+# Windows sys/time.h shim for upstream QuickJS.
$TimeHeader = Join-Path $CompatSys 'time.h'
$TimeHeader = Join-Path $CompatSys 'time.h'
@'
@'
#ifndef OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H
#ifndef OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H
#define OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H
#define OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H
-
#if defined(_WIN32)
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
@@ -122,11 +67,9 @@ $TimeHeader = Join-Path $CompatSys 'time.h'
@@ -122,11 +67,9 @@ $TimeHeader = Join-Path $CompatSys 'time.h'
#define NOMINMAX
#define NOMINMAX
#endif
#endif
#include <windows.h>
#include <windows.h>
-
#ifdef __cplusplus
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
#endif
-
#ifndef _TIMEVAL_DEFINED
#ifndef _TIMEVAL_DEFINED
#define _TIMEVAL_DEFINED
#define _TIMEVAL_DEFINED
typedef long suseconds_t;
typedef long suseconds_t;
@@ -135,7 +78,6 @@ struct timeval {
@@ -135,7 +78,6 @@ struct timeval {
long tv_usec;
long tv_usec;
};
};
#endif
#endif
-
static __inline int gettimeofday(struct timeval *tv, void *tz)
static __inline int gettimeofday(struct timeval *tv, void *tz)
{
{
FILETIME ft;
FILETIME ft;
@@ -152,19 +94,17 @@ static __inline int gettimeofday(struct timeval *tv, void *tz)
@@ -152,19 +94,17 @@ static __inline int gettimeofday(struct timeval *tv, void *tz)
tv->tv_usec = (long)(usec % 1000000ULL);
tv->tv_usec = (long)(usec % 1000000ULL);
return 0;
return 0;
}
}
-
#ifdef __cplusplus
#ifdef __cplusplus
}
}
#endif
#endif
#else
#else
#include_next <sys/time.h>
#include_next <sys/time.h>
#endif
#endif
-
-#endif /* OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H */
+#endif
'@ | Set-Content -Encoding ascii $TimeHeader
'@ | Set-Content -Encoding ascii $TimeHeader
-# Stub enough pthread API for accidental includes. CONFIG_ATOMICS is disabled below,
-# so this should not be exercised, but the header keeps MSVC moving if upstream includes it.
+# pthread.h shim. We also disable CONFIG_ATOMICS below, so this is only to satisfy
+# accidental includes from vendored QuickJS variants.
$PThreadHeader = Join-Path $CompatInclude 'pthread.h'
$PThreadHeader = Join-Path $CompatInclude 'pthread.h'
@'
@'
#ifndef OPENVIBE_QUICKJS_COMPAT_PTHREAD_H
#ifndef OPENVIBE_QUICKJS_COMPAT_PTHREAD_H
@@ -183,15 +123,91 @@ static __inline int pthread_join(pthread_t t, void **ret) { (void)t; if (ret) *r
@@ -183,15 +123,91 @@ static __inline int pthread_join(pthread_t t, void **ret) { (void)t; if (ret) *r
#else
#else
#include_next <pthread.h>
#include_next <pthread.h>
#endif
#endif
-#endif /* OPENVIBE_QUICKJS_COMPAT_PTHREAD_H */
+#endif
'@ | Set-Content -Encoding ascii $PThreadHeader
'@ | Set-Content -Encoding ascii $PThreadHeader
-# Disable QuickJS atomics/workers for this embedded game runtime on MSVC.
-# That avoids needing a real pthread implementation and is fine for our scripts.
+# Forced include for clang-cl. Do NOT redefine __attribute__ or __builtin_* here;
+# clang handles those and QuickJS needs them.
+$ClangCompat = Join-Path $CompatInclude 'openvibe_qjs_clangcl_compat.h'
+@'
+#ifndef OPENVIBE_QJS_CLANGCL_COMPAT_H
+#define OPENVIBE_QJS_CLANGCL_COMPAT_H
+#if defined(_WIN32)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <stdint.h>
+#include <stdlib.h>
+#include <malloc.h>
+#ifndef alloca
+#define alloca _alloca
+#endif
+#ifndef strdup
+#define strdup _strdup
+#endif
+#endif
+#endif
+'@ | Set-Content -Encoding ascii $ClangCompat
+
+# Forced include for emergency cl.exe fallback only.
+$MsvcCompat = Join-Path $CompatInclude 'openvibe_qjs_msvc_compat.h'
+@'
+#ifndef OPENVIBE_QJS_MSVC_COMPAT_H
+#define OPENVIBE_QJS_MSVC_COMPAT_H
+#if defined(_MSC_VER) && !defined(__clang__)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <stdint.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <intrin.h>
+#ifndef __attribute__
+#define __attribute__(x)
+#endif
+#ifndef __builtin_expect
+#define __builtin_expect(x, y) (x)
+#endif
+#ifndef __builtin_frame_address
+#define __builtin_frame_address(x) ((void*)0)
+#endif
+static __inline int openvibe_qjs_clz32(unsigned int v) { unsigned long idx; if (_BitScanReverse(&idx, v)) return 31 - (int)idx; return 32; }
+static __inline int openvibe_qjs_ctz32(unsigned int v) { unsigned long idx; if (_BitScanForward(&idx, v)) return (int)idx; return 32; }
+static __inline int openvibe_qjs_clz64(uint64_t v) { uint32_t hi = (uint32_t)(v >> 32); uint32_t lo = (uint32_t)v; if (hi) return openvibe_qjs_clz32(hi); return 32 + openvibe_qjs_clz32(lo); }
+static __inline int openvibe_qjs_ctz64(uint64_t v) { uint32_t hi = (uint32_t)(v >> 32); uint32_t lo = (uint32_t)v; if (lo) return openvibe_qjs_ctz32(lo); return 32 + openvibe_qjs_ctz32(hi); }
+#ifndef __builtin_clz
+#define __builtin_clz(x) openvibe_qjs_clz32((unsigned int)(x))
+#endif
+#ifndef __builtin_ctz
+#define __builtin_ctz(x) openvibe_qjs_ctz32((unsigned int)(x))
+#endif
+#ifndef __builtin_clzll
+#define __builtin_clzll(x) openvibe_qjs_clz64((uint64_t)(x))
+#endif
+#ifndef __builtin_ctzll
+#define __builtin_ctzll(x) openvibe_qjs_ctz64((uint64_t)(x))
+#endif
+#ifndef alloca
+#define alloca _alloca
+#endif
+#ifndef strdup
+#define strdup _strdup
+#endif
+#endif
+#endif
+'@ | Set-Content -Encoding ascii $MsvcCompat
+
+# Disable QuickJS atomics/workers for the embedded game runtime.
$QuickJsC = Join-Path $SdkQjs 'quickjs.c'
$QuickJsC = Join-Path $SdkQjs 'quickjs.c'
if (Test-Path $QuickJsC) {
if (Test-Path $QuickJsC) {
$q = Get-Content -Raw $QuickJsC
$q = Get-Content -Raw $QuickJsC
- $q2 = [regex]::Replace($q, '(?m)^\s*#\s*define\s+CONFIG_ATOMICS\s*$', '/* OpenVibe MSVC build: CONFIG_ATOMICS disabled */')
+ $q2 = [regex]::Replace($q, '(?m)^\s*#\s*define\s+CONFIG_ATOMICS\s*$', '/* OpenVibe Windows embedded build: CONFIG_ATOMICS disabled */')
if ($q2 -ne $q) {
if ($q2 -ne $q) {
Set-Content -Encoding ascii $QuickJsC $q2
Set-Content -Encoding ascii $QuickJsC $q2
Say 'disabled CONFIG_ATOMICS in SDK quickjs.c copy'
Say 'disabled CONFIG_ATOMICS in SDK quickjs.c copy'
@@ -201,9 +217,10 @@ if (Test-Path $QuickJsC) {
@@ -201,9 +217,10 @@ if (Test-Path $QuickJsC) {
}
}
Say "compat include=$CompatInclude"
Say "compat include=$CompatInclude"
-Say "compat forced include=$MsvcCompat"
Say "compat sys/time.h=$TimeHeader"
Say "compat sys/time.h=$TimeHeader"
Say "compat pthread.h=$PThreadHeader"
Say "compat pthread.h=$PThreadHeader"
+Say "clang forced include=$ClangCompat"
+Say "msvc forced include=$MsvcCompat"
$sources = @('quickjs.c','libregexp.c','libunicode.c','cutils.c')
$sources = @('quickjs.c','libregexp.c','libunicode.c','cutils.c')
if (Test-Path (Join-Path $SdkQjs 'dtoa.c')) { $sources += 'dtoa.c' }
if (Test-Path (Join-Path $SdkQjs 'dtoa.c')) { $sources += 'dtoa.c' }
@@ -212,32 +229,56 @@ if (Test-Path (Join-Path $SdkQjs 'libbf.c')) { $sources += 'libbf.c' }
@@ -212,32 +229,56 @@ if (Test-Path (Join-Path $SdkQjs 'libbf.c')) { $sources += 'libbf.c' }
Remove-Item (Join-Path $Out '*.obj') -Force -ErrorAction SilentlyContinue
Remove-Item (Join-Path $Out '*.obj') -Force -ErrorAction SilentlyContinue
Remove-Item (Join-Path $Out 'libquickjs_openvibe.lib') -Force -ErrorAction SilentlyContinue
Remove-Item (Join-Path $Out 'libquickjs_openvibe.lib') -Force -ErrorAction SilentlyContinue
-$common = @(
- '/nologo',
- '/TC',
- '/O2',
- '/MT',
- '/W3',
- '/D_CRT_SECURE_NO_WARNINGS',
- '/D_CRT_NONSTDC_NO_WARNINGS',
- '/DWIN32',
- '/D_WINDOWS',
- '/DNOMINMAX',
- '/DWIN32_LEAN_AND_MEAN',
- "/DCONFIG_VERSION=`\`"openvibe`\`"",
- "/FI$MsvcCompat",
- "/I$CompatInclude",
- "/I$SdkQjs"
-)
+if ($ClangCl) {
+ $Compiler = $ClangCl
+ $Common = @(
+ '/nologo',
+ '/TC',
+ '/O2',
+ '/MT',
+ '/W3',
+ '/D_CRT_SECURE_NO_WARNINGS',
+ '/D_CRT_NONSTDC_NO_WARNINGS',
+ '/DWIN32',
+ '/D_WINDOWS',
+ '/DNOMINMAX',
+ '/DWIN32_LEAN_AND_MEAN',
+ "/DCONFIG_VERSION=`\`"openvibe`\`"",
+ "/FI$ClangCompat",
+ "/I$CompatInclude",
+ "/I$SdkQjs",
+ '/clang:-std=gnu11',
+ '/clang:-Wno-everything'
+ )
+} else {
+ $Compiler = $ClExe
+ $Common = @(
+ '/nologo',
+ '/TC',
+ '/O2',
+ '/MT',
+ '/W3',
+ '/D_CRT_SECURE_NO_WARNINGS',
+ '/D_CRT_NONSTDC_NO_WARNINGS',
+ '/DWIN32',
+ '/D_WINDOWS',
+ '/DNOMINMAX',
+ '/DWIN32_LEAN_AND_MEAN',
+ "/DCONFIG_VERSION=`\`"openvibe`\`"",
+ "/FI$MsvcCompat",
+ "/I$CompatInclude",
+ "/I$SdkQjs"
+ )
+}
$objects = @()
$objects = @()
foreach ($src in $sources) {
foreach ($src in $sources) {
$srcPath = Join-Path $SdkQjs $src
$srcPath = Join-Path $SdkQjs $src
$obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')
$obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')
- Say "cl $src -> $obj"
- & cl.exe @common /c "$srcPath" "/Fo$obj"
- if ($LASTEXITCODE -ne 0) { throw "cl failed for $src" }
- if (!(Test-Path $obj)) { throw "cl reported success but did not produce $obj" }
+ Say "$([IO.Path]::GetFileName($Compiler)) $src -> $obj"
+ & $Compiler @Common /c "$srcPath" "/Fo$obj"
+ if ($LASTEXITCODE -ne 0) { throw "$Compiler failed for $src" }
+ if (!(Test-Path $obj)) { throw "$Compiler reported success but did not produce $obj" }
$objects += $obj
$objects += $obj
}
}
@@ -246,5 +287,4 @@ Say "lib -> $outLib"
@@ -246,5 +287,4 @@ Say "lib -> $outLib"
& lib.exe /nologo "/OUT:$outLib" $objects
& lib.exe /nologo "/OUT:$outLib" $objects
if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }
if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }
if (!(Test-Path $outLib)) { throw "lib.exe did not produce $outLib" }
if (!(Test-Path $outLib)) { throw "lib.exe did not produce $outLib" }
-
Say "built $outLib"
Say "built $outLib"
new file mode 100755
new file mode 100755
@@ -0,0 +1,342 @@
@@ -0,0 +1,342 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
+cd "$ROOT"
+BRANCH="$(git branch --show-current)"
+
+say() { echo "[openvibe] $*"; }
+
+say "fix QuickJS Windows build: use clang-cl for GNU/C99 QuickJS objects"
+say "root=$ROOT"
+say "branch=$BRANCH"
+
+mkdir -p tools docs
+
+cat > tools/build-quickjs-lib-windows.ps1 <<'PS1'
+$ErrorActionPreference = 'Stop'
+
+$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }
+$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }
+
+$SrcQjs = Join-Path $Root 'sdk/openvibe/third_party/quickjs'
+$SdkQjs = Join-Path $Sdk 'src/game/shared/openvibe/third_party/quickjs'
+$Out = Join-Path $SdkQjs 'build'
+$LogDir = Join-Path $Root 'artifacts/windows-build-debug'
+
+function Say($m) { Write-Host "[openvibe-qjs-win] $m" }
+function FirstExistingCommand([string[]]$names) {
+ foreach ($name in $names) {
+ $cmd = Get-Command $name -ErrorAction SilentlyContinue
+ if ($cmd) { return $cmd.Source }
+ }
+ return $null
+}
+function FirstExistingPath([string[]]$paths) {
+ foreach ($p in $paths) {
+ if ($p -and (Test-Path $p)) { return (Resolve-Path $p).Path }
+ }
+ return $null
+}
+
+if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {
+ throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."
+}
+if (!(Get-Command lib.exe -ErrorAction SilentlyContinue)) {
+ throw "lib.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."
+}
+
+New-Item -ItemType Directory -Force -Path $SdkQjs, $Out, $LogDir | Out-Null
+Copy-Item -Path (Join-Path $SrcQjs '*') -Destination $SdkQjs -Recurse -Force
+
+$CompatInclude = Join-Path $Out 'compat/include'
+$CompatSys = Join-Path $CompatInclude 'sys'
+New-Item -ItemType Directory -Force -Path $CompatInclude, $CompatSys | Out-Null
+
+# QuickJS is written for GCC/clang C, not plain MSVC C. Prefer clang-cl because it
+# emits MSVC-compatible COFF .obj files while accepting GNU/C99 constructs that
+# QuickJS uses heavily. Keep cl.exe only as a last-resort fallback.
+$clangCandidates = @(
+ (FirstExistingCommand @('clang-cl.exe','clang-cl')),
+ (Join-Path ${env:ProgramFiles} 'LLVM/bin/clang-cl.exe'),
+ (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm/x86/bin/clang-cl.exe'),
+ (Join-Path ${env:ProgramFiles} 'Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm/x86/bin/clang-cl.exe'),
+ (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/2022/BuildTools/VC/Tools/Llvm/x86/bin/clang-cl.exe'),
+ (Join-Path ${env:ProgramFiles} 'Microsoft Visual Studio/2022/BuildTools/VC/Tools/Llvm/x86/bin/clang-cl.exe')
+)
+$ClangCl = FirstExistingPath $clangCandidates
+$ClExe = FirstExistingCommand @('cl.exe')
+if ($ClangCl) { Say "using clang-cl=$ClangCl" }
+elseif ($ClExe) { Say "clang-cl not found; falling back to cl.exe=$ClExe" }
+else { throw "Neither clang-cl.exe nor cl.exe was found." }
+
+# Windows sys/time.h shim for upstream QuickJS.
+$TimeHeader = Join-Path $CompatSys 'time.h'
+@'
+#ifndef OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H
+#define OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H
+#if defined(_WIN32)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <windows.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifndef _TIMEVAL_DEFINED
+#define _TIMEVAL_DEFINED
+typedef long suseconds_t;
+struct timeval {
+ long tv_sec;
+ long tv_usec;
+};
+#endif
+static __inline int gettimeofday(struct timeval *tv, void *tz)
+{
+ FILETIME ft;
+ ULARGE_INTEGER uli;
+ unsigned long long usec;
+ const unsigned long long unix_epoch = 116444736000000000ULL;
+ (void)tz;
+ if (!tv) return -1;
+ GetSystemTimeAsFileTime(&ft);
+ uli.LowPart = ft.dwLowDateTime;
+ uli.HighPart = ft.dwHighDateTime;
+ usec = (uli.QuadPart - unix_epoch) / 10ULL;
+ tv->tv_sec = (long)(usec / 1000000ULL);
+ tv->tv_usec = (long)(usec % 1000000ULL);
+ return 0;
+}
+#ifdef __cplusplus
+}
+#endif
+#else
+#include_next <sys/time.h>
+#endif
+#endif
+'@ | Set-Content -Encoding ascii $TimeHeader
+
+# pthread.h shim. We also disable CONFIG_ATOMICS below, so this is only to satisfy
+# accidental includes from vendored QuickJS variants.
+$PThreadHeader = Join-Path $CompatInclude 'pthread.h'
+@'
+#ifndef OPENVIBE_QUICKJS_COMPAT_PTHREAD_H
+#define OPENVIBE_QUICKJS_COMPAT_PTHREAD_H
+#if defined(_WIN32)
+typedef int pthread_mutex_t;
+typedef int pthread_t;
+typedef int pthread_attr_t;
+#define PTHREAD_MUTEX_INITIALIZER 0
+static __inline int pthread_mutex_init(pthread_mutex_t *m, const void *a) { (void)a; if (m) *m = 0; return 0; }
+static __inline int pthread_mutex_destroy(pthread_mutex_t *m) { (void)m; return 0; }
+static __inline int pthread_mutex_lock(pthread_mutex_t *m) { (void)m; return 0; }
+static __inline int pthread_mutex_unlock(pthread_mutex_t *m) { (void)m; return 0; }
+static __inline int pthread_create(pthread_t *t, const pthread_attr_t *a, void *(*fn)(void *), void *arg) { (void)t; (void)a; (void)fn; (void)arg; return -1; }
+static __inline int pthread_join(pthread_t t, void **ret) { (void)t; if (ret) *ret = 0; return 0; }
+#else
+#include_next <pthread.h>
+#endif
+#endif
+'@ | Set-Content -Encoding ascii $PThreadHeader
+
+# Forced include for clang-cl. Do NOT redefine __attribute__ or __builtin_* here;
+# clang handles those and QuickJS needs them.
+$ClangCompat = Join-Path $CompatInclude 'openvibe_qjs_clangcl_compat.h'
+@'
+#ifndef OPENVIBE_QJS_CLANGCL_COMPAT_H
+#define OPENVIBE_QJS_CLANGCL_COMPAT_H
+#if defined(_WIN32)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <stdint.h>
+#include <stdlib.h>
+#include <malloc.h>
+#ifndef alloca
+#define alloca _alloca
+#endif
+#ifndef strdup
+#define strdup _strdup
+#endif
+#endif
+#endif
+'@ | Set-Content -Encoding ascii $ClangCompat
+
+# Forced include for emergency cl.exe fallback only.
+$MsvcCompat = Join-Path $CompatInclude 'openvibe_qjs_msvc_compat.h'
+@'
+#ifndef OPENVIBE_QJS_MSVC_COMPAT_H
+#define OPENVIBE_QJS_MSVC_COMPAT_H
+#if defined(_MSC_VER) && !defined(__clang__)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <stdint.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <intrin.h>
+#ifndef __attribute__
+#define __attribute__(x)
+#endif
+#ifndef __builtin_expect
+#define __builtin_expect(x, y) (x)
+#endif
+#ifndef __builtin_frame_address
+#define __builtin_frame_address(x) ((void*)0)
+#endif
+static __inline int openvibe_qjs_clz32(unsigned int v) { unsigned long idx; if (_BitScanReverse(&idx, v)) return 31 - (int)idx; return 32; }
+static __inline int openvibe_qjs_ctz32(unsigned int v) { unsigned long idx; if (_BitScanForward(&idx, v)) return (int)idx; return 32; }
+static __inline int openvibe_qjs_clz64(uint64_t v) { uint32_t hi = (uint32_t)(v >> 32); uint32_t lo = (uint32_t)v; if (hi) return openvibe_qjs_clz32(hi); return 32 + openvibe_qjs_clz32(lo); }
+static __inline int openvibe_qjs_ctz64(uint64_t v) { uint32_t hi = (uint32_t)(v >> 32); uint32_t lo = (uint32_t)v; if (lo) return openvibe_qjs_ctz32(lo); return 32 + openvibe_qjs_ctz32(hi); }
+#ifndef __builtin_clz
+#define __builtin_clz(x) openvibe_qjs_clz32((unsigned int)(x))
+#endif
+#ifndef __builtin_ctz
+#define __builtin_ctz(x) openvibe_qjs_ctz32((unsigned int)(x))
+#endif
+#ifndef __builtin_clzll
+#define __builtin_clzll(x) openvibe_qjs_clz64((uint64_t)(x))
+#endif
+#ifndef __builtin_ctzll
+#define __builtin_ctzll(x) openvibe_qjs_ctz64((uint64_t)(x))
+#endif
+#ifndef alloca
+#define alloca _alloca
+#endif
+#ifndef strdup
+#define strdup _strdup
+#endif
+#endif
+#endif
+'@ | Set-Content -Encoding ascii $MsvcCompat
+
+# Disable QuickJS atomics/workers for the embedded game runtime.
+$QuickJsC = Join-Path $SdkQjs 'quickjs.c'
+if (Test-Path $QuickJsC) {
+ $q = Get-Content -Raw $QuickJsC
+ $q2 = [regex]::Replace($q, '(?m)^\s*#\s*define\s+CONFIG_ATOMICS\s*$', '/* OpenVibe Windows embedded build: CONFIG_ATOMICS disabled */')
+ if ($q2 -ne $q) {
+ Set-Content -Encoding ascii $QuickJsC $q2
+ Say 'disabled CONFIG_ATOMICS in SDK quickjs.c copy'
+ } else {
+ Say 'CONFIG_ATOMICS define not found in quickjs.c copy; pthread shim remains available'
+ }
+}
+
+Say "compat include=$CompatInclude"
+Say "compat sys/time.h=$TimeHeader"
+Say "compat pthread.h=$PThreadHeader"
+Say "clang forced include=$ClangCompat"
+Say "msvc forced include=$MsvcCompat"
+
+$sources = @('quickjs.c','libregexp.c','libunicode.c','cutils.c')
+if (Test-Path (Join-Path $SdkQjs 'dtoa.c')) { $sources += 'dtoa.c' }
+if (Test-Path (Join-Path $SdkQjs 'libbf.c')) { $sources += 'libbf.c' }
+
+Remove-Item (Join-Path $Out '*.obj') -Force -ErrorAction SilentlyContinue
+Remove-Item (Join-Path $Out 'libquickjs_openvibe.lib') -Force -ErrorAction SilentlyContinue
+
+if ($ClangCl) {
+ $Compiler = $ClangCl
+ $Common = @(
+ '/nologo',
+ '/TC',
+ '/O2',
+ '/MT',
+ '/W3',
+ '/D_CRT_SECURE_NO_WARNINGS',
+ '/D_CRT_NONSTDC_NO_WARNINGS',
+ '/DWIN32',
+ '/D_WINDOWS',
+ '/DNOMINMAX',
+ '/DWIN32_LEAN_AND_MEAN',
+ "/DCONFIG_VERSION=`\`"openvibe`\`"",
+ "/FI$ClangCompat",
+ "/I$CompatInclude",
+ "/I$SdkQjs",
+ '/clang:-std=gnu11',
+ '/clang:-Wno-everything'
+ )
+} else {
+ $Compiler = $ClExe
+ $Common = @(
+ '/nologo',
+ '/TC',
+ '/O2',
+ '/MT',
+ '/W3',
+ '/D_CRT_SECURE_NO_WARNINGS',
+ '/D_CRT_NONSTDC_NO_WARNINGS',
+ '/DWIN32',
+ '/D_WINDOWS',
+ '/DNOMINMAX',
+ '/DWIN32_LEAN_AND_MEAN',
+ "/DCONFIG_VERSION=`\`"openvibe`\`"",
+ "/FI$MsvcCompat",
+ "/I$CompatInclude",
+ "/I$SdkQjs"
+ )
+}
+
+$objects = @()
+foreach ($src in $sources) {
+ $srcPath = Join-Path $SdkQjs $src
+ $obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')
+ Say "$([IO.Path]::GetFileName($Compiler)) $src -> $obj"
+ & $Compiler @Common /c "$srcPath" "/Fo$obj"
+ if ($LASTEXITCODE -ne 0) { throw "$Compiler failed for $src" }
+ if (!(Test-Path $obj)) { throw "$Compiler reported success but did not produce $obj" }
+ $objects += $obj
+}
+
+$outLib = Join-Path $Out 'libquickjs_openvibe.lib'
+Say "lib -> $outLib"
+& lib.exe /nologo "/OUT:$outLib" $objects
+if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }
+if (!(Test-Path $outLib)) { throw "lib.exe did not produce $outLib" }
+Say "built $outLib"
+PS1
+
+cat > docs/WINDOWS_QUICKJS_CLANGCL.md <<'MD'
+# Windows QuickJS build uses clang-cl
+
+QuickJS uses GNU/C99 compiler features that plain `cl.exe` does not parse cleanly, including GNU attributes, GCC/Clang builtins, and C initializer patterns. The Windows workflow now prefers `clang-cl.exe` for QuickJS only. `clang-cl` emits MSVC-compatible COFF `.obj` files, and `lib.exe` packs them into `libquickjs_openvibe.lib` for the regular Source SDK MSVC link.
+
+The rest of the Source SDK build still uses the Visual Studio/MSBuild toolchain.
+MD
+
+chmod +x tools/trigger-windows-dll-build-clean.sh 2>/dev/null || true
+
+git add tools/build-quickjs-lib-windows.ps1 docs/WINDOWS_QUICKJS_CLANGCL.md "$0" 2>/dev/null || git add tools/build-quickjs-lib-windows.ps1 docs/WINDOWS_QUICKJS_CLANGCL.md
+
+say "git diff summary"
+git diff --cached --stat || true
+
+if git diff --cached --quiet; then
+ say "no changes to commit"
+else
+ git commit -m "Build QuickJS with clang-cl on Windows"
+ git push
+fi
+
+say "triggering clean Windows DLL build"
+if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then
+ tools/trigger-windows-dll-build-clean.sh
+else
+ gh workflow run windows-source-sdk-dlls.yml --ref "$BRANCH"
+ sleep 10
+ RUN_ID="$(gh run list --workflow windows-source-sdk-dlls.yml --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
+ gh run watch "$RUN_ID" --exit-status || true
+ tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true
+fi
+
+say "done"