@@ -133,26 +133,130 @@ jobs:
133133 - name : Install clang for WASM C compilation
134134 run : sudo apt-get update -qq && sudo apt-get install -y clang
135135 - name : Set up C header stubs for wasm32-unknown-unknown
136- # tree-sitter 0.25+ includes stdio.h in its C source, but wasm32-unknown-unknown
137- # has no OS and therefore no standard C library headers. Provide a minimal stub
138- # so that tree-sitter's error-reporting code (fprintf to stderr) compiles as a no-op.
136+ # tree-sitter 0.25+ compiles C sources that #include standard C library headers
137+ # (stdio.h, stdlib.h, string.h, etc.). The wasm32-unknown-unknown target has no OS
138+ # and therefore no libc headers. We provide minimal stubs so the code compiles;
139+ # actual implementations (malloc, memcpy, etc.) come from the wasm runtime / Rust.
139140 run : |
140- # CLANG_RES: clang's built-in resource directory, used below to provide
141- # low-level headers (e.g. stdarg.h, stddef.h) alongside the stdio.h stub.
142141 CLANG_RES=$(clang --print-resource-dir)
143- mkdir -p /tmp/wasm-stubs
144- cat > /tmp/wasm-stubs/stdio.h << 'WASM_STDIO_STUB'
145- #pragma once
146- #include <stdarg.h>
147- typedef void FILE;
148- #define stderr ((FILE*)0)
149- #define stdout ((FILE*)0)
150- #define stdin ((FILE*)0)
151- static inline int fprintf(FILE *f, const char *fmt, ...) { (void)f; (void)fmt; return 0; }
152- static inline int vfprintf(FILE *f, const char *fmt, va_list ap) { (void)f; (void)fmt; (void)ap; return 0; }
153- static inline int printf(const char *fmt, ...) { (void)fmt; return 0; }
154- WASM_STDIO_STUB
155- echo "CFLAGS_wasm32_unknown_unknown=-I/tmp/wasm-stubs -I${CLANG_RES}/include" >> "$GITHUB_ENV"
142+ STUBS=/tmp/wasm-stubs
143+ mkdir -p "$STUBS"
144+
145+ # Use Python to write the stub files cleanly without heredoc indentation issues.
146+ python3 - << 'PYEOF'
147+ import os
148+ STUBS = '/tmp/wasm-stubs'
149+ stubs = {}
150+
151+ stubs['stdio.h'] = """\
152+ # pragma once
153+ # include <stddef.h>
154+ # include <stdarg.h>
155+ typedef void FILE;
156+ # define stderr ((FILE*)0)
157+ # define stdout ((FILE*)0)
158+ # define stdin ((FILE*)0)
159+ # define EOF (-1)
160+ static inline int fprintf(FILE *f, const char *fmt, ...) { (void)f; (void)fmt; return 0; }
161+ static inline int vfprintf(FILE *f, const char *fmt, va_list ap) { (void)f; (void)fmt; (void)ap; return 0; }
162+ static inline int printf(const char *fmt, ...) { (void)fmt; return 0; }
163+ static inline int snprintf(char *s, size_t n, const char *fmt, ...) { (void)s; (void)n; (void)fmt; return 0; }
164+ static inline int sprintf(char *s, const char *fmt, ...) { (void)s; (void)fmt; return 0; }
165+ static inline int fputc(int c, FILE *f) { (void)f; return c; }
166+ static inline int fputs(const char *s, FILE *f) { (void)s; (void)f; return 0; }
167+ static inline int fflush(FILE *f) { (void)f; return 0; }
168+ " " "
169+
170+ stubs['stdlib.h'] = " " " \
171+ # pragma once
172+ # include <stddef.h>
173+ # define EXIT_SUCCESS 0
174+ # define EXIT_FAILURE 1
175+ extern void *malloc(size_t size);
176+ extern void *calloc(size_t count, size_t size);
177+ extern void *realloc(void *ptr, size_t size);
178+ extern void free(void *ptr);
179+ extern void abort(void);
180+ extern void exit(int status);
181+ static inline int abs(int x) { return x < 0 ? -x : x; }
182+ " " "
183+
184+ stubs['string.h'] = " " " \
185+ # pragma once
186+ # include <stddef.h>
187+ extern void *memcpy(void *dest, const void *src, size_t n);
188+ extern void *memmove(void *dest, const void *src, size_t n);
189+ extern void *memset(void *s, int c, size_t n);
190+ extern int memcmp(const void *s1, const void *s2, size_t n);
191+ extern void *memchr(const void *s, int c, size_t n);
192+ extern size_t strlen(const char *s);
193+ extern int strcmp(const char *s1, const char *s2);
194+ extern int strncmp(const char *s1, const char *s2, size_t n);
195+ extern char *strcpy(char *dest, const char *src);
196+ extern char *strncpy(char *dest, const char *src, size_t n);
197+ extern char *strcat(char *dest, const char *src);
198+ extern char *strncat(char *dest, const char *src, size_t n);
199+ extern char *strchr(const char *s, int c);
200+ extern char *strrchr(const char *s, int c);
201+ extern char *strstr(const char *haystack, const char *needle);
202+ " " "
203+
204+ stubs['time.h'] = " " " \
205+ # pragma once
206+ # include <stdint.h>
207+ typedef uint64_t time_t;
208+ typedef uint64_t clock_t;
209+ # define CLOCKS_PER_SEC ((clock_t)1000000)
210+ static inline time_t time(time_t *t) { if (t) *t = 0; return 0; }
211+ static inline clock_t clock(void) { return 0; }
212+ " " "
213+
214+ stubs['ctype.h'] = " " " \
215+ # pragma once
216+ static inline int isalpha(int c) { return (c>='a'&&c<='z')||(c>='A'&&c<='Z'); }
217+ static inline int isdigit(int c) { return c>='0'&&c<='9'; }
218+ static inline int isalnum(int c) { return isalpha(c)||isdigit(c); }
219+ static inline int isspace(int c) { return c==' '||c=='\\t'||c=='\\n'||c=='\\r'||c=='\\f'||c=='\\v'; }
220+ static inline int isupper(int c) { return c>='A'&&c<='Z'; }
221+ static inline int islower(int c) { return c>='a'&&c<='z'; }
222+ static inline int toupper(int c) { return islower(c)?c-('a'-'A'):c; }
223+ static inline int tolower(int c) { return isupper(c)?c+('a'-'A'):c; }
224+ static inline int isprint(int c) { return c>=' '&&c<='~'; }
225+ static inline int iscntrl(int c) { return c<' '||c==127; }
226+ static inline int isxdigit(int c) { return isdigit(c)||(c>='a'&&c<='f')||(c>='A'&&c<='F'); }
227+ static inline int ispunct(int c) { return isprint(c)&&!isalnum(c)&&c!=' '; }
228+ " " "
229+
230+ stubs['wctype.h'] = " " " \
231+ # pragma once
232+ # include <stdint.h>
233+ typedef uint32_t wint_t;
234+ # define WEOF ((wint_t)-1)
235+ static inline int iswspace(wint_t c) { return c==' '||c=='\\t'||c=='\\n'||c=='\\r'||c=='\\f'||c=='\\v'; }
236+ static inline int iswalpha(wint_t c) { return (c>='a'&&c<='z')||(c>='A'&&c<='Z'); }
237+ static inline int iswdigit(wint_t c) { return c>='0'&&c<='9'; }
238+ static inline int iswalnum(wint_t c) { return iswalpha(c)||iswdigit(c); }
239+ static inline int iswupper(wint_t c) { return c>='A'&&c<='Z'; }
240+ static inline int iswlower(wint_t c) { return c>='a'&&c<='z'; }
241+ static inline int iswprint(wint_t c) { return c>=' '&&c<='~'; }
242+ static inline int iswpunct(wint_t c) { return iswprint(c)&&!iswalnum(c)&&c!=' '; }
243+ static inline wint_t towlower(wint_t c) { return iswupper(c)?c+('a'-'A'):c; }
244+ static inline wint_t towupper(wint_t c) { return iswlower(c)?c-('a'-'A'):c; }
245+ " " "
246+
247+ stubs['assert.h'] = " " " \
248+ # pragma once
249+ # define assert(x) ((void)(x))
250+ " " "
251+
252+ for name, content in stubs.items():
253+ path = os.path.join(STUBS, name)
254+ with open(path, 'w') as f:
255+ f.write(content)
256+ print(f' wrote {path}')
257+ PYEOF
258+
259+ echo " CFLAGS_wasm32_unknown_unknown=-I${STUBS} -I${CLANG_RES}/include" >> "$GITHUB_ENV"
156260 - name : Install wasm-pack
157261 uses : jetli/wasm-pack-action@v0.4.0
158262 - name : Build WASM (dev)
0 commit comments