Skip to content

Commit 18d117f

Browse files
committed
cgen: msvc fixes
1 parent a193277 commit 18d117f

4 files changed

Lines changed: 139 additions & 92 deletions

File tree

vlib/v/builder/msvc_windows.v

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,10 @@ pub fn (mut v Builder) cc_msvc() {
319319
if v.pref.sanitize {
320320
eprintln('Sanitize not supported on msvc.')
321321
}
322-
a << '/TC'
323322
// The C file we are compiling
324-
// a << '"$TmpPath/${v.out_name_c}"'
325-
a << '"' + os.real_path(v.out_name_c) + '"'
323+
// Use /Tc<file> instead of /TC, otherwise .lib/.obj linker inputs are also
324+
// treated as C sources.
325+
a << '/Tc' + os.quoted_path(os.real_path(v.out_name_c))
326326
if !v.ccoptions.debug_mode {
327327
v.pref.cleanup_files << os.real_path(v.out_name_c)
328328
}

vlib/v/gen/c/cheaders.v

Lines changed: 83 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -479,75 +479,89 @@ typedef __builtin_va_list va_list;
479479
#define va_copy(dest, src) __builtin_va_copy(dest, src)
480480
#endif
481481
#endif
482-
int vfprintf(FILE *stream, const char *format, va_list ap);
483-
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
484-
int fprintf(FILE *stream, const char *format, ...);
485-
int printf(const char *format, ...);
486-
int snprintf(char *str, size_t size, const char *format, ...);
487-
int sprintf(char *str, const char *format, ...);
488-
int sscanf(const char *str, const char *format, ...);
489-
int scanf(const char *format, ...);
490-
int puts(const char *str);
491-
void perror(const char *str);
492-
int fputs(const char *str, FILE *stream);
493-
int getchar(void);
494-
int putchar(int ch);
495-
int getc(FILE *stream);
496-
int fgetc(FILE *stream);
497-
int ungetc(int ch, FILE *stream);
498-
int fflush(FILE *stream);
499-
int feof(FILE *stream);
500-
int ferror(FILE *stream);
501-
void clearerr(FILE *stream);
502-
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
503-
long ftell(FILE *stream);
504-
void rewind(FILE *stream);
505-
FILE *fopen(const char *filename, const char *mode);
506-
FILE *fdopen(int fd, const char *mode);
507-
FILE *freopen(const char *filename, const char *mode, FILE *stream);
508-
int fileno(FILE *stream);
509-
size_t fread(void *ptr, size_t size, size_t items, FILE *stream);
510-
size_t fwrite(const void *ptr, size_t size, size_t items, FILE *stream);
511-
char *fgets(char *str, int size, FILE *stream);
512-
int fclose(FILE *stream);
513-
FILE *popen(const char *command, const char *mode);
514-
int pclose(FILE *stream);
515-
void *malloc(size_t size);
516-
void *calloc(size_t nitems, size_t size);
517-
void *realloc(void *ptr, size_t size);
518-
void *aligned_alloc(size_t alignment, size_t size);
519-
void free(void *ptr);
520-
int rand(void);
521-
void srand(unsigned int seed);
522-
int atexit(void (*cb)(void));
523-
void exit(int status);
524-
int atoi(const char *str);
525-
double atof(const char *str);
526-
char *getenv(const char *name);
527-
int setenv(const char *name, const char *value, int overwrite);
528-
int unsetenv(const char *name);
529-
int system(const char *command);
530-
int remove(const char *path);
531-
int rename(const char *old_path, const char *new_path);
532-
char *realpath(const char *path, char *resolved_path);
533-
int mkstemp(char *stemplate);
534-
void qsort(void *base, size_t items, size_t item_size, qsort_callback_func cb);
535-
int strcmp(const char *left, const char *right);
536-
size_t strlen(const char *str);
537-
char *strerror(int errnum);
538-
void *memcpy(void *dest, const void *src, size_t n);
539-
void *memmove(void *dest, const void *src, size_t n);
540-
void *memset(void *dest, int ch, size_t n);
541-
int memcmp(const void *left, const void *right, size_t n);
542-
void *memchr(const void *str, int c, size_t n);
543-
char *strchr(const char *str, int c);
544-
char *strrchr(const char *str, int c);
545-
int fseek(FILE *stream, long offset, int whence);
546-
isize getline(char **lineptr, size_t *n, FILE *stream);
482+
#if defined(_MSC_VER) && !defined(__clang__)
483+
#define V_CRT_CALL VCALLCONV(cdecl)
484+
#else
485+
#define V_CRT_CALL
486+
#endif
487+
int V_CRT_CALL vfprintf(FILE *stream, const char *format, va_list ap);
488+
int V_CRT_CALL vsnprintf(char *str, size_t size, const char *format, va_list ap);
489+
int V_CRT_CALL fprintf(FILE *stream, const char *format, ...);
490+
int V_CRT_CALL printf(const char *format, ...);
491+
int V_CRT_CALL snprintf(char *str, size_t size, const char *format, ...);
492+
int V_CRT_CALL sprintf(char *str, const char *format, ...);
493+
int V_CRT_CALL sscanf(const char *str, const char *format, ...);
494+
int V_CRT_CALL scanf(const char *format, ...);
495+
int V_CRT_CALL puts(const char *str);
496+
void V_CRT_CALL perror(const char *str);
497+
int V_CRT_CALL fputs(const char *str, FILE *stream);
498+
int V_CRT_CALL getchar(void);
499+
int V_CRT_CALL putchar(int ch);
500+
int V_CRT_CALL getc(FILE *stream);
501+
int V_CRT_CALL fgetc(FILE *stream);
502+
int V_CRT_CALL ungetc(int ch, FILE *stream);
503+
int V_CRT_CALL fflush(FILE *stream);
504+
int V_CRT_CALL feof(FILE *stream);
505+
int V_CRT_CALL ferror(FILE *stream);
506+
void V_CRT_CALL clearerr(FILE *stream);
507+
int V_CRT_CALL setvbuf(FILE *stream, char *buf, int mode, size_t size);
508+
long V_CRT_CALL ftell(FILE *stream);
509+
void V_CRT_CALL rewind(FILE *stream);
510+
FILE * V_CRT_CALL fopen(const char *filename, const char *mode);
511+
FILE * V_CRT_CALL fdopen(int fd, const char *mode);
512+
FILE * V_CRT_CALL freopen(const char *filename, const char *mode, FILE *stream);
513+
int V_CRT_CALL fileno(FILE *stream);
514+
size_t V_CRT_CALL fread(void *ptr, size_t size, size_t items, FILE *stream);
515+
size_t V_CRT_CALL fwrite(const void *ptr, size_t size, size_t items, FILE *stream);
516+
char * V_CRT_CALL fgets(char *str, int size, FILE *stream);
517+
int V_CRT_CALL fclose(FILE *stream);
518+
FILE * V_CRT_CALL popen(const char *command, const char *mode);
519+
int V_CRT_CALL pclose(FILE *stream);
520+
void * V_CRT_CALL malloc(size_t size);
521+
void * V_CRT_CALL calloc(size_t nitems, size_t size);
522+
void * V_CRT_CALL realloc(void *ptr, size_t size);
523+
void * V_CRT_CALL aligned_alloc(size_t alignment, size_t size);
524+
void V_CRT_CALL free(void *ptr);
525+
int V_CRT_CALL rand(void);
526+
void V_CRT_CALL srand(unsigned int seed);
527+
int V_CRT_CALL atexit(void (*cb)(void));
528+
void V_CRT_CALL exit(int status);
529+
int V_CRT_CALL atoi(const char *str);
530+
double V_CRT_CALL atof(const char *str);
531+
char * V_CRT_CALL getenv(const char *name);
532+
int V_CRT_CALL setenv(const char *name, const char *value, int overwrite);
533+
int V_CRT_CALL unsetenv(const char *name);
534+
int V_CRT_CALL system(const char *command);
535+
int V_CRT_CALL remove(const char *path);
536+
int V_CRT_CALL rename(const char *old_path, const char *new_path);
537+
char * V_CRT_CALL realpath(const char *path, char *resolved_path);
538+
int V_CRT_CALL mkstemp(char *stemplate);
539+
void V_CRT_CALL qsort(void *base, size_t items, size_t item_size, qsort_callback_func cb);
540+
int V_CRT_CALL strcmp(const char *left, const char *right);
541+
size_t V_CRT_CALL strlen(const char *str);
542+
char * V_CRT_CALL strerror(int errnum);
543+
void * V_CRT_CALL memcpy(void *dest, const void *src, size_t n);
544+
void * V_CRT_CALL memmove(void *dest, const void *src, size_t n);
545+
void * V_CRT_CALL memset(void *dest, int ch, size_t n);
546+
int V_CRT_CALL memcmp(const void *left, const void *right, size_t n);
547+
void * V_CRT_CALL memchr(const void *str, int c, size_t n);
548+
char * V_CRT_CALL strchr(const char *str, int c);
549+
char * V_CRT_CALL strrchr(const char *str, int c);
550+
int V_CRT_CALL fseek(FILE *stream, long offset, int whence);
551+
isize V_CRT_CALL getline(char **lineptr, size_t *n, FILE *stream);
547552
#if defined(_WIN32) || defined(_WIN64)
548-
int _fileno(FILE *stream);
549-
FILE *_wfopen(const unsigned short *filename, const unsigned short *mode);
550-
int _wremove(const unsigned short *path);
553+
int V_CRT_CALL _fileno(FILE *stream);
554+
FILE * V_CRT_CALL _wfopen(const unsigned short *filename, const unsigned short *mode);
555+
int V_CRT_CALL _wremove(const unsigned short *path);
556+
#endif
557+
#if defined(_MSC_VER) && !defined(__clang__)
558+
#ifndef _TRUNCATE
559+
#define _TRUNCATE ((size_t)-1)
560+
#endif
561+
int V_CRT_CALL _vscprintf(const char *format, va_list ap);
562+
int V_CRT_CALL _vsnprintf_s(char *buffer, size_t size, size_t count, const char *format, va_list ap);
563+
unsigned short * V_CRT_CALL _wgetenv(const unsigned short *varname);
564+
int V_CRT_CALL _wputenv(const unsigned short *envstring);
551565
#endif
552566
#ifndef _IOFBF
553567
#define _IOFBF 0
@@ -579,6 +593,7 @@ enum {
579593
#endif
580594
};
581595
#endif
596+
#undef V_CRT_CALL
582597
#if defined(__TINYC__)
583598
// https://lists.nongnu.org/archive/html/tinycc-devel/2025-10/msg00007.html
584599
// gnu headers use to #define __attribute__ to empty for non-gcc compilers

vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,32 @@ fn test_default_c_prelude_uses_manual_stdio_stdlib_string_and_stdarg_decls() {
1616
assert res.exit_code == 0, '${cmd}\n${res.output}'
1717
generated_c := res.output.replace('\r\n', '\n')
1818
assert generated_c.contains('typedef struct _iobuf FILE;'), generated_c
19+
assert generated_c.contains('typedef char* va_list;'), generated_c
1920
assert generated_c.contains('typedef struct __sFILE FILE;'), generated_c
2021
assert generated_c.contains('typedef struct _IO_FILE FILE;'), generated_c
2122
assert generated_c.contains('typedef __builtin_va_list va_list;'), generated_c
22-
assert generated_c.contains('typedef char* va_list;'), generated_c
23-
assert generated_c.contains('int vfprintf(FILE *stream, const char *format, va_list ap);'), generated_c
23+
assert generated_c.contains('int V_CRT_CALL vfprintf(FILE *stream, const char *format, va_list ap);'), generated_c
2424

25-
assert generated_c.contains('int vsnprintf(char *str, size_t size, const char *format, va_list ap);'), generated_c
26-
assert generated_c.contains('#if defined(_WIN32) || defined(_WIN64)\nint _fileno(FILE *stream);\nFILE *_wfopen(const unsigned short *filename, const unsigned short *mode);\nint _wremove(const unsigned short *path);\n#endif'), generated_c
25+
assert generated_c.contains('int V_CRT_CALL vsnprintf(char *str, size_t size, const char *format, va_list ap);'), generated_c
26+
assert generated_c.contains('#if defined(_WIN32) || defined(_WIN64)\nint V_CRT_CALL _fileno(FILE *stream);\nFILE * V_CRT_CALL _wfopen(const unsigned short *filename, const unsigned short *mode);\nint V_CRT_CALL _wremove(const unsigned short *path);\n#endif'), generated_c
2727

28-
assert generated_c.contains('void perror(const char *str);'), generated_c
29-
assert generated_c.contains('int mkstemp(char *stemplate);'), generated_c
30-
assert generated_c.contains('int strcmp(const char *left, const char *right);'), generated_c
31-
assert generated_c.contains('int rand(void);'), generated_c
32-
assert generated_c.contains('void srand(unsigned int seed);'), generated_c
28+
assert generated_c.contains('void V_CRT_CALL perror(const char *str);'), generated_c
29+
assert generated_c.contains('int V_CRT_CALL mkstemp(char *stemplate);'), generated_c
30+
assert generated_c.contains('int V_CRT_CALL strcmp(const char *left, const char *right);'), generated_c
31+
assert generated_c.contains('int V_CRT_CALL rand(void);'), generated_c
32+
assert generated_c.contains('void V_CRT_CALL srand(unsigned int seed);'), generated_c
3333
assert generated_c.contains('RAND_MAX = 2147483647'), generated_c
34-
assert generated_c.contains('double atof(const char *str);'), generated_c
34+
assert generated_c.contains('double V_CRT_CALL atof(const char *str);'), generated_c
3535
assert generated_c.contains('extern FILE* stdout;'), generated_c
3636
assert generated_c.contains('#define stdout (__acrt_iob_func(1))'), generated_c
3737
assert generated_c.contains('#if defined(_MSC_VER) && !defined(__clang__)\ntypedef struct _iobuf FILE;\ntypedef char* va_list;'), generated_c
38+
assert generated_c.contains('#define _INTSIZEOF(n) \\'), generated_c
39+
assert generated_c.contains('#define va_arg(ap, t) \\'), generated_c
40+
assert generated_c.contains('#if defined(_MSC_VER) && !defined(__clang__)\n\t#define V_CRT_CALL VCALLCONV(cdecl)\n#else\n\t#define V_CRT_CALL\n#endif'), generated_c
41+
assert generated_c.contains('int V_CRT_CALL _vscprintf(const char *format, va_list ap);'), generated_c
42+
assert generated_c.contains('int V_CRT_CALL _vsnprintf_s(char *buffer, size_t size, size_t count, const char *format, va_list ap);'), generated_c
43+
assert generated_c.contains('unsigned short * V_CRT_CALL _wgetenv(const unsigned short *varname);'), generated_c
44+
assert generated_c.contains('int V_CRT_CALL _wputenv(const unsigned short *envstring);'), generated_c
3845
assert generated_c.contains('#elif defined(__MINGW32__) || defined(__MINGW64__) || (defined(__clang__) && (defined(_WIN32) || defined(_WIN64)))\ntypedef struct _iobuf FILE;\nFILE* __cdecl __acrt_iob_func(unsigned index);\n#define stdin (__acrt_iob_func(0))\n#define stdout (__acrt_iob_func(1))\n#define stderr (__acrt_iob_func(2))'), generated_c
3946
assert generated_c.contains('#elif defined(__TINYC__) && (defined(_WIN32) || defined(_WIN64))'), generated_c
4047
assert generated_c.contains('#ifndef _FILE_DEFINED\nstruct _iobuf {\n\tchar *_ptr;\n\tint _cnt;\n\tchar *_base;\n\tint _flag;\n\tint _file;\n\tint _charbuf;\n\tint _bufsiz;\n\tchar *_tmpfname;\n};\ntypedef struct _iobuf FILE;\n#define _FILE_DEFINED'), generated_c
@@ -47,6 +54,31 @@ fn test_default_c_prelude_uses_manual_stdio_stdlib_string_and_stdarg_decls() {
4754
assert generated_c.contains('#elif defined(__linux__) && !defined(__GLIBC__) && !defined(__GNU_LIBRARY__) && !defined(__BIONIC__) && !defined(__UCLIBC__)\ntypedef struct _IO_FILE FILE;\n// musl exposes the stdio streams as `FILE *const`, so match that to stay\n// compatible with later <stdio.h> includes from headers like miniz.h.\nextern FILE* const stdin;\nextern FILE* const stdout;\nextern FILE* const stderr'), generated_c
4855
}
4956

57+
fn test_msvc_windows_prelude_keeps_manual_crt_decls() {
58+
tmp_dir := os.join_path(os.vtmp_dir(), 'cheaders_msvc_windows_${os.getpid()}')
59+
os.mkdir_all(tmp_dir)!
60+
defer {
61+
os.rmdir_all(tmp_dir) or {}
62+
}
63+
source_path := os.join_path(tmp_dir, 'hello.v')
64+
output_path := os.join_path(tmp_dir, 'hello.c')
65+
os.write_file(source_path, 'fn main() {\n\tprintln("hi")\n}\n')!
66+
cmd := '${os.quoted_path(cheaders_manual_stdlib_vexe)} -cc msvc -os windows -o ${os.quoted_path(output_path)} ${os.quoted_path(source_path)}'
67+
res := os.execute(cmd)
68+
assert res.exit_code == 0, '${cmd}\n${res.output}'
69+
generated_c := os.read_file(output_path)!.replace('\r\n', '\n')
70+
assert !generated_c.contains('#include <stdarg.h>'), generated_c
71+
assert !generated_c.contains('#include <stdio.h>'), generated_c
72+
assert !generated_c.contains('#include <stdlib.h>'), generated_c
73+
assert !generated_c.contains('#include <string.h>'), generated_c
74+
assert generated_c.contains('#if defined(_MSC_VER) && !defined(__clang__)\ntypedef struct _iobuf FILE;\ntypedef char* va_list;'), generated_c
75+
assert generated_c.contains('#ifndef va_copy\n\t#define va_copy(dest, src) ((dest) = (src))\n#endif\nFILE* __cdecl __acrt_iob_func(unsigned index);'), generated_c
76+
assert generated_c.contains('#if defined(_MSC_VER) && !defined(__clang__)\n\t#define V_CRT_CALL VCALLCONV(cdecl)\n#else\n\t#define V_CRT_CALL\n#endif'), generated_c
77+
assert generated_c.contains('int V_CRT_CALL _vscprintf(const char *format, va_list ap);'), generated_c
78+
assert generated_c.contains('int V_CRT_CALL _vsnprintf_s(char *buffer, size_t size, size_t count, const char *format, va_list ap);'), generated_c
79+
assert generated_c.contains('#include <windows.h>'), generated_c
80+
}
81+
5082
fn test_manual_stdio_decls_do_not_conflict_with_later_stdio_includes() {
5183
tmp_dir := os.join_path(os.vtmp_dir(), 'cheaders_manual_stdlib_${os.getpid()}')
5284
os.mkdir_all(tmp_dir)!

vlib/v/gen/c/testdata/windows_clang_bootstrap_regression.c.must_have

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
#if defined(_MSC_VER) && !defined(__clang__)
2-
#define _INTSIZEOF(n) \
3-
((sizeof(n) + \
4-
sizeof(int) - 1) & \
5-
~(sizeof(int) - 1))
6-
#define va_arg(ap, t) \
7-
(*(t*)((ap += \
8-
_INTSIZEOF(t)) - \
9-
_INTSIZEOF(t)))
2+
typedef struct _iobuf FILE;
3+
typedef char* va_list;
4+
FILE* __cdecl __acrt_iob_func(unsigned index);
5+
#define stdout (__acrt_iob_func(1))
106
#elif defined(__MINGW32__) || defined(__MINGW64__) || (defined(__clang__) && (defined(_WIN32) || defined(_WIN64)))
117
FILE* __cdecl __acrt_iob_func(unsigned index);
12-
int _fileno(FILE *stream);
13-
FILE *_wfopen(const unsigned short *filename, const unsigned short *mode);
14-
int _wremove(const unsigned short *path);
8+
int V_CRT_CALL _fileno(FILE *stream);
9+
FILE * V_CRT_CALL _wfopen(const unsigned short *filename, const unsigned short *mode);
10+
int V_CRT_CALL _vscprintf(const char *format, va_list ap);
11+
int V_CRT_CALL _vsnprintf_s(char *buffer, size_t size, size_t count, const char *format, va_list ap);
12+
unsigned short * V_CRT_CALL _wgetenv(const unsigned short *varname);
13+
int V_CRT_CALL _wputenv(const unsigned short *envstring);
14+
int V_CRT_CALL _wremove(const unsigned short *path);
1515
#if defined(_MSC_VER) && !defined(__clang__)
1616
#define C__LONG LONG
1717
#define v_set_unhandled_exception_filter(handler) SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)(handler))

0 commit comments

Comments
 (0)