From 343f94ca915b34f80bd05e77a95354a3898ae9a2 Mon Sep 17 00:00:00 2001 From: Richard Wheeler Date: Thu, 19 Feb 2026 21:28:42 -0500 Subject: [PATCH] builder: fix Windows pkgconfig paths and MSVC flags (#26631) --- vlib/v/builder/msvc_windows.v | 136 ++++++++++++++++++++++++++++-- vlib/v/pkgconfig/pkgconfig.v | 1 + vlib/v/pkgconfig/pkgconfig_test.v | 2 + 3 files changed, 134 insertions(+), 5 deletions(-) diff --git a/vlib/v/builder/msvc_windows.v b/vlib/v/builder/msvc_windows.v index d96f325d4..b4573bde9 100644 --- a/vlib/v/builder/msvc_windows.v +++ b/vlib/v/builder/msvc_windows.v @@ -523,6 +523,99 @@ mut: other_flags []string } +fn strip_quotes(value string) string { + if value.len >= 2 && value[0] == `"` && value[value.len - 1] == `"` { + return value[1..value.len - 1] + } + return value +} + +fn apply_gnu_flag_to_msvc(value string, mut inc_paths []string, mut lib_paths []string, mut real_libs []string) bool { + v := strip_quotes(value) + if v.starts_with('-I') && v.len > 2 { + path := v[2..] + inc_paths << '/I"${os.real_path(path)}"' + return true + } + if v.starts_with('-L') && v.len > 2 { + path := v[2..] + lib_paths << path + lib_paths << path + os.path_separator + 'msvc' + return true + } + if v.starts_with('-l') && v.len > 2 { + mut lib := v[2..] + if lib.starts_with(':') && lib.len > 1 { + lib = lib[1..] + } + if !lib.ends_with('.lib') { + lib += '.lib' + } + real_libs << lib + return true + } + if v.starts_with('-Wl,') { + mut consumed := false + for part in v[4..].split(',') { + if apply_gnu_flag_to_msvc(part, mut inc_paths, mut lib_paths, mut real_libs) { + consumed = true + } + } + return consumed + } + return false +} + +fn split_and_apply_gnu_flags(value string, mut inc_paths []string, mut lib_paths []string, mut real_libs []string) (bool, string) { + if !value.contains(' ') { + if apply_gnu_flag_to_msvc(value, mut inc_paths, mut lib_paths, mut real_libs) { + return true, '' + } + return false, value + } + parts := split_quoted_flags(value) + mut consumed := false + mut leftovers := []string{} + for part in parts { + if part == '' { + continue + } + if apply_gnu_flag_to_msvc(part, mut inc_paths, mut lib_paths, mut real_libs) { + consumed = true + } else { + leftovers << strip_quotes(part) + } + } + if consumed { + return true, leftovers.join(' ') + } + return false, value +} + +fn split_quoted_flags(value string) []string { + mut parts := []string{} + mut buf := []u8{} + mut in_quote := false + for ch in value { + if ch == `"` { + in_quote = !in_quote + continue + } + if !in_quote && ch == ` ` { + if buf.len > 0 { + parts << buf.bytestr() + buf = []u8{} + } + continue + } + buf << ch + } + if buf.len > 0 { + parts << buf.bytestr() + } + return parts +} + pub fn (mut v Builder) msvc_string_flags(cflags []cflag.CFlag) MsvcStringFlags { mut real_libs := []string{} mut inc_paths := []string{} @@ -530,6 +623,16 @@ pub fn (mut v Builder) msvc_string_flags(cflags []cflag.CFlag) MsvcStringFlags { mut defines := []string{} mut other_flags := []string{} for flag in cflags { + if flag.name == '' { + consumed, leftover := split_and_apply_gnu_flags(flag.value, mut inc_paths, mut + lib_paths, mut real_libs) + if consumed { + if leftover != '' { + other_flags << strip_quotes(leftover) + } + continue + } + } // println('fl: ${flag.name} | flag arg: ${flag.value}') // We need to see if the flag contains -l // -l isnt recognised and these libs will be passed straight to the linker @@ -540,14 +643,29 @@ pub fn (mut v Builder) msvc_string_flags(cflags []cflag.CFlag) MsvcStringFlags { } // MSVC has no method of linking against a .dll // TODO: we should look for .defs aswell - lib_lib := flag.value + '.lib' - real_libs << lib_lib + parts := split_quoted_flags(flag.value) + if parts.len > 0 { + mut lib := strip_quotes(parts[0]) + if lib.starts_with(':') && lib.len > 1 { + lib = lib[1..] + } + if !lib.ends_with('.lib') { + lib += '.lib' + } + real_libs << lib + if parts.len > 1 { + _, leftover := split_and_apply_gnu_flags(parts[1..].join(' '), mut + inc_paths, mut lib_paths, mut real_libs) + if leftover != '' { + other_flags << strip_quotes(leftover) + } + } + } } else if flag.name == '-I' { - inc_paths << flag.format() or { continue } + inc_paths << '/I"${os.real_path(flag.value)}"' } else if flag.name == '-D' { defines << '/D${flag.value}' } else if flag.name == '-L' { - // TODO: use flag.format() here as well; `#flag -L$when_first_existing(...)` is a more explicit way to achieve the same lib_paths << flag.value lib_paths << flag.value + os.path_separator + 'msvc' // The above allows putting msvc specific .lib files in a subfolder msvc/ , @@ -567,7 +685,15 @@ pub fn (mut v Builder) msvc_string_flags(cflags []cflag.CFlag) MsvcStringFlags { } else if flag.value.starts_with('-D') { defines << '/D${flag.value[2..]}' } else { - other_flags << flag.value + consumed, leftover := split_and_apply_gnu_flags(flag.value, mut inc_paths, mut + lib_paths, mut real_libs) + if consumed { + if leftover != '' { + other_flags << strip_quotes(leftover) + } + continue + } + other_flags << strip_quotes(flag.value) } } mut lpaths := []string{} diff --git a/vlib/v/pkgconfig/pkgconfig.v b/vlib/v/pkgconfig/pkgconfig.v index 90f162c73..e79e3ba69 100644 --- a/vlib/v/pkgconfig/pkgconfig.v +++ b/vlib/v/pkgconfig/pkgconfig.v @@ -100,6 +100,7 @@ fn (mut pc PkgConfig) setvar(line string) { fn (mut pc PkgConfig) parse(file string) bool { pc.file_path = file + pc.vars['pcfiledir'] = os.real_path(os.dir(file)) data := os.read_file(file) or { return false } if pc.options.debug { eprintln(data) diff --git a/vlib/v/pkgconfig/pkgconfig_test.v b/vlib/v/pkgconfig/pkgconfig_test.v index 9cea08691..4c0b4c894 100644 --- a/vlib/v/pkgconfig/pkgconfig_test.v +++ b/vlib/v/pkgconfig/pkgconfig_test.v @@ -57,6 +57,7 @@ fn test_samples() { assert x.cflags == ['-I/usr/include', '-pthread', '-I/usr/include/glib-2.0', '-I/usr/lib/x86_64-linux-gnu/glib-2.0/include'] assert x.vars == { + 'pcfiledir': samples_dir 'prefix': '/usr' 'libdir': '/usr/lib/x86_64-linux-gnu' 'includedir': '/usr/include' @@ -76,6 +77,7 @@ fn test_samples() { assert x.cflags == ['-I/usr/include/glib-2.0', '-I/usr/lib/x86_64-linux-gnu/glib-2.0/include', '-I/usr/include'] assert x.vars == { + 'pcfiledir': samples_dir 'prefix': '/usr' 'libdir': '/usr/lib/x86_64-linux-gnu' 'includedir': '/usr/include' -- 2.39.5