From a937b1f0e5d451eea99065a4a134849a82e6b057 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 25 Apr 2026 15:54:33 +0300 Subject: [PATCH] cgen: msvc fixes --- vlib/v/gen/c/cgen.v | 74 +++++++- vlib/v/gen/c/cheaders.v | 159 +++++++++--------- .../gen/c/cheaders_manual_stdlib_decls_test.v | 61 +++++-- vlib/v/gen/c/consts_and_globals.v | 4 +- vlib/v/gen/c/str.v | 6 +- 5 files changed, 201 insertions(+), 103 deletions(-) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index b4d28bf2f..d2ac61892 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -32,6 +32,7 @@ const cmp_str = ['eq', 'ne', 'gt', 'lt', 'ge', 'le'] const cmp_rev = ['eq', 'ne', 'lt', 'gt', 'le', 'ge'] const result_name = ast.result_name const option_name = ast.option_name +const max_c_string_literal_segment_len = 12000 pub struct Gen { pref &pref.Preferences = unsafe { nil } @@ -4914,7 +4915,78 @@ fn cestring(s string) string { // ctoslit returns a '_S("${s}")' call, where s is properly escaped. fn ctoslit(s string) string { - return '_S("' + cescape_nonascii(cestring(s)) + '")' + return '_S(' + cescaped_string_literal(cescape_nonascii(cestring(s))) + ')' +} + +fn cescaped_string_literal(s string) string { + if s.len <= max_c_string_literal_segment_len { + return '"${s}"' + } + mut b := strings.new_builder(s.len + (s.len / max_c_string_literal_segment_len + 1) * 3) + mut start := 0 + for start < s.len { + end := cescaped_string_literal_segment_end(s, start, max_c_string_literal_segment_len) + if start > 0 { + b.write_u8(` `) + } + b.write_u8(`"`) + b.write_string(s[start..end]) + b.write_u8(`"`) + start = end + } + return b.str() +} + +fn cescaped_string_literal_segment_end(s string, start int, max_len int) int { + limit := if start + max_len < s.len { start + max_len } else { s.len } + mut i := start + mut last_safe := start + for i < limit { + if s[i] == `\\` { + escape_end := cescaped_string_escape_end(s, i) + if escape_end > limit { + break + } + i = escape_end + last_safe = i + continue + } + i++ + last_safe = i + } + if last_safe > start { + return last_safe + } + return limit +} + +fn cescaped_string_escape_end(s string, start int) int { + if start + 1 >= s.len { + return s.len + } + next := s[start + 1] + if next >= `0` && next <= `7` { + mut end := start + 2 + for end < s.len && end < start + 4 && s[end] >= `0` && s[end] <= `7` { + end++ + } + return end + } + if next == `x` { + mut end := start + 2 + for end < s.len && end < start + 4 && ((s[end] >= `0` && s[end] <= `9`) + || (s[end] >= `a` && s[end] <= `f`) || (s[end] >= `A` && s[end] <= `F`)) { + end++ + } + return end + } + if next == `u` { + return if start + 6 < s.len { start + 6 } else { s.len } + } + if next == `U` { + return if start + 10 < s.len { start + 10 } else { s.len } + } + return start + 2 } fn (mut g Gen) gen_attrs(attrs []ast.Attr) { diff --git a/vlib/v/gen/c/cheaders.v b/vlib/v/gen/c/cheaders.v index 4ce950a70..65237e626 100644 --- a/vlib/v/gen/c/cheaders.v +++ b/vlib/v/gen/c/cheaders.v @@ -366,6 +366,13 @@ const c_headers = c_helper_macros + c_common_macros + c_common_callconv_attr + r' // c_headers typedef int (*qsort_callback_func)(const void*, const void*); +#if defined(_MSC_VER) && !defined(__clang__) + #define V_CRT_LINKAGE __declspec(dllimport) + #define V_CRT_CALL VCALLCONV(cdecl) +#else + #define V_CRT_LINKAGE + #define V_CRT_CALL +#endif #if defined(_MSC_VER) && !defined(__clang__) typedef struct _iobuf FILE; typedef char* va_list; @@ -383,7 +390,7 @@ typedef char* va_list; #ifndef va_copy #define va_copy(dest, src) ((dest) = (src)) #endif -FILE* __cdecl __acrt_iob_func(unsigned index); +V_CRT_LINKAGE FILE* V_CRT_CALL __acrt_iob_func(unsigned index); #define stdin (__acrt_iob_func(0)) #define stdout (__acrt_iob_func(1)) #define stderr (__acrt_iob_func(2)) @@ -479,89 +486,84 @@ typedef __builtin_va_list va_list; #define va_copy(dest, src) __builtin_va_copy(dest, src) #endif #endif -#if defined(_MSC_VER) && !defined(__clang__) - #define V_CRT_CALL VCALLCONV(cdecl) -#else - #define V_CRT_CALL -#endif -int V_CRT_CALL vfprintf(FILE *stream, const char *format, va_list ap); -int V_CRT_CALL vsnprintf(char *str, size_t size, const char *format, va_list ap); -int V_CRT_CALL fprintf(FILE *stream, const char *format, ...); -int V_CRT_CALL printf(const char *format, ...); -int V_CRT_CALL snprintf(char *str, size_t size, const char *format, ...); -int V_CRT_CALL sprintf(char *str, const char *format, ...); -int V_CRT_CALL sscanf(const char *str, const char *format, ...); -int V_CRT_CALL scanf(const char *format, ...); -int V_CRT_CALL puts(const char *str); -void V_CRT_CALL perror(const char *str); -int V_CRT_CALL fputs(const char *str, FILE *stream); -int V_CRT_CALL getchar(void); -int V_CRT_CALL putchar(int ch); -int V_CRT_CALL getc(FILE *stream); -int V_CRT_CALL fgetc(FILE *stream); -int V_CRT_CALL ungetc(int ch, FILE *stream); -int V_CRT_CALL fflush(FILE *stream); -int V_CRT_CALL feof(FILE *stream); -int V_CRT_CALL ferror(FILE *stream); -void V_CRT_CALL clearerr(FILE *stream); -int V_CRT_CALL setvbuf(FILE *stream, char *buf, int mode, size_t size); -long V_CRT_CALL ftell(FILE *stream); -void V_CRT_CALL rewind(FILE *stream); -FILE * V_CRT_CALL fopen(const char *filename, const char *mode); -FILE * V_CRT_CALL fdopen(int fd, const char *mode); -FILE * V_CRT_CALL freopen(const char *filename, const char *mode, FILE *stream); -int V_CRT_CALL fileno(FILE *stream); -size_t V_CRT_CALL fread(void *ptr, size_t size, size_t items, FILE *stream); -size_t V_CRT_CALL fwrite(const void *ptr, size_t size, size_t items, FILE *stream); -char * V_CRT_CALL fgets(char *str, int size, FILE *stream); -int V_CRT_CALL fclose(FILE *stream); -FILE * V_CRT_CALL popen(const char *command, const char *mode); -int V_CRT_CALL pclose(FILE *stream); -void * V_CRT_CALL malloc(size_t size); -void * V_CRT_CALL calloc(size_t nitems, size_t size); -void * V_CRT_CALL realloc(void *ptr, size_t size); -void * V_CRT_CALL aligned_alloc(size_t alignment, size_t size); -void V_CRT_CALL free(void *ptr); -int V_CRT_CALL rand(void); -void V_CRT_CALL srand(unsigned int seed); -int V_CRT_CALL atexit(void (*cb)(void)); -void V_CRT_CALL exit(int status); -int V_CRT_CALL atoi(const char *str); -double V_CRT_CALL atof(const char *str); -char * V_CRT_CALL getenv(const char *name); -int V_CRT_CALL setenv(const char *name, const char *value, int overwrite); -int V_CRT_CALL unsetenv(const char *name); -int V_CRT_CALL system(const char *command); -int V_CRT_CALL remove(const char *path); -int V_CRT_CALL rename(const char *old_path, const char *new_path); -char * V_CRT_CALL realpath(const char *path, char *resolved_path); -int V_CRT_CALL mkstemp(char *stemplate); -void V_CRT_CALL qsort(void *base, size_t items, size_t item_size, qsort_callback_func cb); -int V_CRT_CALL strcmp(const char *left, const char *right); -size_t V_CRT_CALL strlen(const char *str); -char * V_CRT_CALL strerror(int errnum); -void * V_CRT_CALL memcpy(void *dest, const void *src, size_t n); -void * V_CRT_CALL memmove(void *dest, const void *src, size_t n); -void * V_CRT_CALL memset(void *dest, int ch, size_t n); -int V_CRT_CALL memcmp(const void *left, const void *right, size_t n); -void * V_CRT_CALL memchr(const void *str, int c, size_t n); -char * V_CRT_CALL strchr(const char *str, int c); -char * V_CRT_CALL strrchr(const char *str, int c); -int V_CRT_CALL fseek(FILE *stream, long offset, int whence); -isize V_CRT_CALL getline(char **lineptr, size_t *n, FILE *stream); +V_CRT_LINKAGE int V_CRT_CALL vfprintf(FILE *stream, const char *format, va_list ap); +V_CRT_LINKAGE int V_CRT_CALL vsnprintf(char *str, size_t size, const char *format, va_list ap); +V_CRT_LINKAGE int V_CRT_CALL fprintf(FILE *stream, const char *format, ...); +V_CRT_LINKAGE int V_CRT_CALL printf(const char *format, ...); +V_CRT_LINKAGE int V_CRT_CALL snprintf(char *str, size_t size, const char *format, ...); +V_CRT_LINKAGE int V_CRT_CALL sprintf(char *str, const char *format, ...); +V_CRT_LINKAGE int V_CRT_CALL sscanf(const char *str, const char *format, ...); +V_CRT_LINKAGE int V_CRT_CALL scanf(const char *format, ...); +V_CRT_LINKAGE int V_CRT_CALL puts(const char *str); +V_CRT_LINKAGE void V_CRT_CALL perror(const char *str); +V_CRT_LINKAGE int V_CRT_CALL fputs(const char *str, FILE *stream); +V_CRT_LINKAGE int V_CRT_CALL getchar(void); +V_CRT_LINKAGE int V_CRT_CALL putchar(int ch); +V_CRT_LINKAGE int V_CRT_CALL getc(FILE *stream); +V_CRT_LINKAGE int V_CRT_CALL fgetc(FILE *stream); +V_CRT_LINKAGE int V_CRT_CALL ungetc(int ch, FILE *stream); +V_CRT_LINKAGE int V_CRT_CALL fflush(FILE *stream); +V_CRT_LINKAGE int V_CRT_CALL feof(FILE *stream); +V_CRT_LINKAGE int V_CRT_CALL ferror(FILE *stream); +V_CRT_LINKAGE void V_CRT_CALL clearerr(FILE *stream); +V_CRT_LINKAGE int V_CRT_CALL setvbuf(FILE *stream, char *buf, int mode, size_t size); +V_CRT_LINKAGE long V_CRT_CALL ftell(FILE *stream); +V_CRT_LINKAGE void V_CRT_CALL rewind(FILE *stream); +V_CRT_LINKAGE FILE * V_CRT_CALL fopen(const char *filename, const char *mode); +V_CRT_LINKAGE FILE * V_CRT_CALL fdopen(int fd, const char *mode); +V_CRT_LINKAGE FILE * V_CRT_CALL freopen(const char *filename, const char *mode, FILE *stream); +V_CRT_LINKAGE int V_CRT_CALL fileno(FILE *stream); +V_CRT_LINKAGE size_t V_CRT_CALL fread(void *ptr, size_t size, size_t items, FILE *stream); +V_CRT_LINKAGE size_t V_CRT_CALL fwrite(const void *ptr, size_t size, size_t items, FILE *stream); +V_CRT_LINKAGE char * V_CRT_CALL fgets(char *str, int size, FILE *stream); +V_CRT_LINKAGE int V_CRT_CALL fclose(FILE *stream); +V_CRT_LINKAGE FILE * V_CRT_CALL popen(const char *command, const char *mode); +V_CRT_LINKAGE int V_CRT_CALL pclose(FILE *stream); +V_CRT_LINKAGE void * V_CRT_CALL malloc(size_t size); +V_CRT_LINKAGE void * V_CRT_CALL calloc(size_t nitems, size_t size); +V_CRT_LINKAGE void * V_CRT_CALL realloc(void *ptr, size_t size); +V_CRT_LINKAGE void * V_CRT_CALL aligned_alloc(size_t alignment, size_t size); +V_CRT_LINKAGE void V_CRT_CALL free(void *ptr); +V_CRT_LINKAGE int V_CRT_CALL rand(void); +V_CRT_LINKAGE void V_CRT_CALL srand(unsigned int seed); +V_CRT_LINKAGE int V_CRT_CALL atexit(void (*cb)(void)); +V_CRT_LINKAGE void V_CRT_CALL exit(int status); +V_CRT_LINKAGE int V_CRT_CALL atoi(const char *str); +V_CRT_LINKAGE double V_CRT_CALL atof(const char *str); +V_CRT_LINKAGE char * V_CRT_CALL getenv(const char *name); +V_CRT_LINKAGE int V_CRT_CALL setenv(const char *name, const char *value, int overwrite); +V_CRT_LINKAGE int V_CRT_CALL unsetenv(const char *name); +V_CRT_LINKAGE int V_CRT_CALL system(const char *command); +V_CRT_LINKAGE int V_CRT_CALL remove(const char *path); +V_CRT_LINKAGE int V_CRT_CALL rename(const char *old_path, const char *new_path); +V_CRT_LINKAGE char * V_CRT_CALL realpath(const char *path, char *resolved_path); +V_CRT_LINKAGE int V_CRT_CALL mkstemp(char *stemplate); +V_CRT_LINKAGE void V_CRT_CALL qsort(void *base, size_t items, size_t item_size, qsort_callback_func cb); +V_CRT_LINKAGE int V_CRT_CALL strcmp(const char *left, const char *right); +V_CRT_LINKAGE size_t V_CRT_CALL strlen(const char *str); +V_CRT_LINKAGE char * V_CRT_CALL strerror(int errnum); +V_CRT_LINKAGE void * V_CRT_CALL memcpy(void *dest, const void *src, size_t n); +V_CRT_LINKAGE void * V_CRT_CALL memmove(void *dest, const void *src, size_t n); +V_CRT_LINKAGE void * V_CRT_CALL memset(void *dest, int ch, size_t n); +V_CRT_LINKAGE int V_CRT_CALL memcmp(const void *left, const void *right, size_t n); +V_CRT_LINKAGE void * V_CRT_CALL memchr(const void *str, int c, size_t n); +V_CRT_LINKAGE char * V_CRT_CALL strchr(const char *str, int c); +V_CRT_LINKAGE char * V_CRT_CALL strrchr(const char *str, int c); +V_CRT_LINKAGE int V_CRT_CALL fseek(FILE *stream, long offset, int whence); +V_CRT_LINKAGE isize V_CRT_CALL getline(char **lineptr, size_t *n, FILE *stream); #if defined(_WIN32) || defined(_WIN64) -int V_CRT_CALL _fileno(FILE *stream); -FILE * V_CRT_CALL _wfopen(const unsigned short *filename, const unsigned short *mode); -int V_CRT_CALL _wremove(const unsigned short *path); +V_CRT_LINKAGE int V_CRT_CALL _fileno(FILE *stream); +V_CRT_LINKAGE FILE * V_CRT_CALL _wfopen(const unsigned short *filename, const unsigned short *mode); +V_CRT_LINKAGE int V_CRT_CALL _wremove(const unsigned short *path); #endif #if defined(_MSC_VER) && !defined(__clang__) #ifndef _TRUNCATE #define _TRUNCATE ((size_t)-1) #endif -int V_CRT_CALL _vscprintf(const char *format, va_list ap); -int V_CRT_CALL _vsnprintf_s(char *buffer, size_t size, size_t count, const char *format, va_list ap); -unsigned short * V_CRT_CALL _wgetenv(const unsigned short *varname); -int V_CRT_CALL _wputenv(const unsigned short *envstring); +V_CRT_LINKAGE int V_CRT_CALL _vscprintf(const char *format, va_list ap); +V_CRT_LINKAGE int V_CRT_CALL _vsnprintf_s(char *buffer, size_t size, size_t count, const char *format, va_list ap); +V_CRT_LINKAGE unsigned short * V_CRT_CALL _wgetenv(const unsigned short *varname); +V_CRT_LINKAGE int V_CRT_CALL _wputenv(const unsigned short *envstring); #endif #ifndef _IOFBF #define _IOFBF 0 @@ -593,6 +595,7 @@ enum { #endif }; #endif +#undef V_CRT_LINKAGE #undef V_CRT_CALL #if defined(__TINYC__) // https://lists.nongnu.org/archive/html/tinycc-devel/2025-10/msg00007.html diff --git a/vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v b/vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v index 6e6a8abac..e409ca3ff 100644 --- a/vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v +++ b/vlib/v/gen/c/cheaders_manual_stdlib_decls_test.v @@ -20,28 +20,28 @@ fn test_default_c_prelude_uses_manual_stdio_stdlib_string_and_stdarg_decls() { assert generated_c.contains('typedef struct __sFILE FILE;'), generated_c assert generated_c.contains('typedef struct _IO_FILE FILE;'), generated_c assert generated_c.contains('typedef __builtin_va_list va_list;'), generated_c - assert generated_c.contains('int V_CRT_CALL vfprintf(FILE *stream, const char *format, va_list ap);'), generated_c + assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL vfprintf(FILE *stream, const char *format, va_list ap);'), generated_c - assert generated_c.contains('int V_CRT_CALL vsnprintf(char *str, size_t size, const char *format, va_list ap);'), generated_c - 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 + assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL vsnprintf(char *str, size_t size, const char *format, va_list ap);'), generated_c + assert generated_c.contains('#if defined(_WIN32) || defined(_WIN64)\nV_CRT_LINKAGE int V_CRT_CALL _fileno(FILE *stream);\nV_CRT_LINKAGE FILE * V_CRT_CALL _wfopen(const unsigned short *filename, const unsigned short *mode);\nV_CRT_LINKAGE int V_CRT_CALL _wremove(const unsigned short *path);\n#endif'), generated_c - assert generated_c.contains('void V_CRT_CALL perror(const char *str);'), generated_c - assert generated_c.contains('int V_CRT_CALL mkstemp(char *stemplate);'), generated_c - assert generated_c.contains('int V_CRT_CALL strcmp(const char *left, const char *right);'), generated_c - assert generated_c.contains('int V_CRT_CALL rand(void);'), generated_c - assert generated_c.contains('void V_CRT_CALL srand(unsigned int seed);'), generated_c + assert generated_c.contains('V_CRT_LINKAGE void V_CRT_CALL perror(const char *str);'), generated_c + assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL mkstemp(char *stemplate);'), generated_c + assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL strcmp(const char *left, const char *right);'), generated_c + assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL rand(void);'), generated_c + assert generated_c.contains('V_CRT_LINKAGE void V_CRT_CALL srand(unsigned int seed);'), generated_c assert generated_c.contains('RAND_MAX = 2147483647'), generated_c - assert generated_c.contains('double V_CRT_CALL atof(const char *str);'), generated_c + assert generated_c.contains('V_CRT_LINKAGE double V_CRT_CALL atof(const char *str);'), generated_c assert generated_c.contains('extern FILE* stdout;'), generated_c assert generated_c.contains('#define stdout (__acrt_iob_func(1))'), generated_c assert generated_c.contains('#if defined(_MSC_VER) && !defined(__clang__)\ntypedef struct _iobuf FILE;\ntypedef char* va_list;'), generated_c assert generated_c.contains('#define _INTSIZEOF(n) \\'), generated_c assert generated_c.contains('#define va_arg(ap, t) \\'), generated_c - 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 - assert generated_c.contains('int V_CRT_CALL _vscprintf(const char *format, va_list ap);'), generated_c - 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 - assert generated_c.contains('unsigned short * V_CRT_CALL _wgetenv(const unsigned short *varname);'), generated_c - assert generated_c.contains('int V_CRT_CALL _wputenv(const unsigned short *envstring);'), generated_c + assert generated_c.contains('#if defined(_MSC_VER) && !defined(__clang__)\n\t#define V_CRT_LINKAGE __declspec(dllimport)\n\t#define V_CRT_CALL VCALLCONV(cdecl)\n#else\n\t#define V_CRT_LINKAGE\n\t#define V_CRT_CALL\n#endif'), generated_c + assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL _vscprintf(const char *format, va_list ap);'), generated_c + assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL _vsnprintf_s(char *buffer, size_t size, size_t count, const char *format, va_list ap);'), generated_c + assert generated_c.contains('V_CRT_LINKAGE unsigned short * V_CRT_CALL _wgetenv(const unsigned short *varname);'), generated_c + assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL _wputenv(const unsigned short *envstring);'), generated_c 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 assert generated_c.contains('#elif defined(__TINYC__) && (defined(_WIN32) || defined(_WIN64))'), generated_c 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 @@ -72,13 +72,38 @@ fn test_msvc_windows_prelude_keeps_manual_crt_decls() { assert !generated_c.contains('#include '), generated_c assert !generated_c.contains('#include '), generated_c assert generated_c.contains('#if defined(_MSC_VER) && !defined(__clang__)\ntypedef struct _iobuf FILE;\ntypedef char* va_list;'), generated_c - 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 - 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 - assert generated_c.contains('int V_CRT_CALL _vscprintf(const char *format, va_list ap);'), generated_c - 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 + assert generated_c.contains('#ifndef va_copy\n\t#define va_copy(dest, src) ((dest) = (src))\n#endif\nV_CRT_LINKAGE FILE* V_CRT_CALL __acrt_iob_func(unsigned index);'), generated_c + assert generated_c.contains('#if defined(_MSC_VER) && !defined(__clang__)\n\t#define V_CRT_LINKAGE __declspec(dllimport)\n\t#define V_CRT_CALL VCALLCONV(cdecl)\n#else\n\t#define V_CRT_LINKAGE\n\t#define V_CRT_CALL\n#endif'), generated_c + assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL _vscprintf(const char *format, va_list ap);'), generated_c + assert generated_c.contains('V_CRT_LINKAGE int V_CRT_CALL _vsnprintf_s(char *buffer, size_t size, size_t count, const char *format, va_list ap);'), generated_c assert generated_c.contains('#include '), generated_c } +fn test_msvc_windows_splits_large_string_const_literals() { + tmp_dir := os.join_path(os.vtmp_dir(), 'cheaders_msvc_long_string_${os.getpid()}') + os.mkdir_all(tmp_dir)! + defer { + os.rmdir_all(tmp_dir) or {} + } + long_text := 'a'.repeat(25000) + source_path := os.join_path(tmp_dir, 'long_string.v') + output_path := os.join_path(tmp_dir, 'long_string.c') + os.write_file(source_path, + "const long_literal = '${long_text}'\n\nfn main() {\n\tassert long_literal.len == ${long_text.len}\n}\n")! + cmd := '${os.quoted_path(cheaders_manual_stdlib_vexe)} -cc msvc -os windows -o ${os.quoted_path(output_path)} ${os.quoted_path(source_path)}' + res := os.execute(cmd) + assert res.exit_code == 0, '${cmd}\n${res.output}' + generated_c := os.read_file(output_path)!.replace('\r\n', '\n') + marker := 'long_literal = _S(' + start := generated_c.index(marker) or { + assert false, generated_c + return + } + line := generated_c[start..].all_before(');') + assert line.contains('" "'), line + assert !line.contains('a'.repeat(20000)), line +} + fn test_manual_stdio_decls_do_not_conflict_with_later_stdio_includes() { tmp_dir := os.join_path(os.vtmp_dir(), 'cheaders_manual_stdlib_${os.getpid()}') os.mkdir_all(tmp_dir)! diff --git a/vlib/v/gen/c/consts_and_globals.v b/vlib/v/gen/c/consts_and_globals.v index b9b0c09de..de5b6b2eb 100644 --- a/vlib/v/gen/c/consts_and_globals.v +++ b/vlib/v/gen/c/consts_and_globals.v @@ -235,9 +235,9 @@ fn (mut g Gen) const_decl_precomputed(mod string, name string, cname string, fie // `error C2099: initializer is not a constant` errors in MSVC, // so fall back to the delayed initialisation scheme: init := if typ == ast.string_type { - '_S("${escaped_val}")' + '_S(${cescaped_string_literal(escaped_val)})' } else { - '(${styp})"${escaped_val}"' + '(${styp})${cescaped_string_literal(escaped_val)}' } g.global_const_defs[util.no_dots(field_name)] = GlobalConstDef{ mod: mod diff --git a/vlib/v/gen/c/str.v b/vlib/v/gen/c/str.v index fbdc9fff1..cfa37a868 100644 --- a/vlib/v/gen/c/str.v +++ b/vlib/v/gen/c/str.v @@ -8,11 +8,9 @@ import v.util fn (mut g Gen) string_literal(node ast.StringLiteral) { escaped_val := cescape_nonascii(util.smart_quote(node.val, node.is_raw)) if node.language == .c { - g.write2('"', escaped_val) - g.write('"') + g.write(cescaped_string_literal(escaped_val)) } else { - g.write2('_S("', escaped_val) - g.write('")') + g.write('_S(${cescaped_string_literal(escaped_val)})') } } -- 2.39.5