From dc6a9583d758528db99b49e3654ebc6282b503ea Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Sat, 5 Oct 2024 01:29:13 +0800 Subject: [PATCH] v.builder, builtin: use ANSI encoding for the .rsp file under Windows; add builtin string_to_ansi_not_null_terminated + tests (#22409) --- vlib/builtin/utf8.c.v | 25 +++++++++++++++++++++++++ vlib/builtin/utf8.v | 10 ++++++++++ vlib/builtin/utf8_test.v | 9 +++++++++ vlib/v/builder/cc.v | 10 ++++++++-- vlib/v/builder/msvc_windows.v | 4 ++-- 5 files changed, 54 insertions(+), 4 deletions(-) diff --git a/vlib/builtin/utf8.c.v b/vlib/builtin/utf8.c.v index 335ce008e1..687ff62cc1 100644 --- a/vlib/builtin/utf8.c.v +++ b/vlib/builtin/utf8.c.v @@ -2,6 +2,7 @@ module builtin import strings +const cp_acp = 0 const cp_utf8 = 65001 // to_wide returns a pointer to an UTF-16 version of the string receiver. @@ -92,3 +93,27 @@ pub fn string_from_wide2(_wstr &u16, len int) string { return res } } + +// wide_to_ansi create an ANSI string, given a windows +// style string, encoded in UTF-16. +// It use CP_ACP, which is ANSI code page identifier, as dest encoding. +// NOTE: It return a vstring(encoded in UTF-8) []u8 under Linux. +pub fn wide_to_ansi(_wstr &u16) []u8 { + $if windows { + num_bytes := C.WideCharToMultiByte(cp_acp, 0, _wstr, -1, 0, 0, 0, 0) + if num_bytes != 0 { + mut str_to := []u8{len: num_bytes} + C.WideCharToMultiByte(cp_acp, 0, _wstr, -1, &char(str_to.data), str_to.len, + 0, 0) + return str_to + } else { + return []u8{} + } + } $else { + s := unsafe { string_from_wide(_wstr) } + mut str_to := []u8{len: s.len + 1} + unsafe { vmemcpy(str_to.data, s.str, s.len) } + return str_to + } + return []u8{} // TODO: remove this, bug? +} diff --git a/vlib/builtin/utf8.v b/vlib/builtin/utf8.v index f585fe4297..7128e56db6 100644 --- a/vlib/builtin/utf8.v +++ b/vlib/builtin/utf8.v @@ -184,3 +184,13 @@ pub fn utf8_str_visible_length(s string) int { } return l } + +// string_to_ansi_not_null_terminated returns an ANSI version of the string `_str`. +// NOTE: This is most useful for converting a vstring to an ANSI string under Windows. +// NOTE: The ANSI string return is not null-terminated, then you can use `os.write_file_array` write an ANSI file. +pub fn string_to_ansi_not_null_terminated(_str string) []u8 { + wstr := _str.to_wide() + mut ansi := wide_to_ansi(wstr) + ansi.pop() // remove tailing zero + return ansi +} diff --git a/vlib/builtin/utf8_test.v b/vlib/builtin/utf8_test.v index 275ea14b3a..fc790a196f 100644 --- a/vlib/builtin/utf8_test.v +++ b/vlib/builtin/utf8_test.v @@ -76,3 +76,12 @@ fn test_reverse_cyrillic_with_string_from_wide() { z := unsafe { string_from_wide(ws) } assert z == s } + +fn test_wide_to_ansi() { + ws := 'abc'.to_wide() + assert wide_to_ansi(ws) == [u8(97), 98, 99, 0] +} + +fn test_string_to_ansi_not_null_terminated() { + assert string_to_ansi_not_null_terminated('abc') == [u8(97), 98, 99] +} diff --git a/vlib/v/builder/cc.v b/vlib/v/builder/cc.v index 7100ed7e76..074a4289e9 100644 --- a/vlib/v/builder/cc.v +++ b/vlib/v/builder/cc.v @@ -666,8 +666,14 @@ pub fn (mut v Builder) cc() { response_file_content = str_args.replace('\\', '\\\\') rspexpr := '@${response_file}' cmd = '${v.quote_compiler_name(ccompiler)} ${os.quoted_path(rspexpr)}' - os.write_file(response_file, response_file_content) or { - verror('Unable to write to C response file "${response_file}"') + $if windows { + os.write_file_array(response_file, string_to_ansi_not_null_terminated(response_file_content)) or { + verror('Unable to write to C response file "${response_file}"') + } + } $else { + os.write_file(response_file, response_file_content) or { + verror('Unable to write to C response file "${response_file}"') + } } } if !v.ccoptions.debug_mode { diff --git a/vlib/v/builder/msvc_windows.v b/vlib/v/builder/msvc_windows.v index 1e82c8e1e8..926bf5f337 100644 --- a/vlib/v/builder/msvc_windows.v +++ b/vlib/v/builder/msvc_windows.v @@ -355,9 +355,9 @@ pub fn (mut v Builder) cc_msvc() { a << env_ldflags } v.dump_c_options(a) - args := '\xEF\xBB\xBF' + a.join(' ') + args := a.join(' ') // write args to a file so that we dont smash createprocess - os.write_file(out_name_cmd_line, args) or { + os.write_file_array(out_name_cmd_line, string_to_ansi_not_null_terminated(args)) or { verror('Unable to write response file to "${out_name_cmd_line}"') } cmd := '"${r.full_cl_exe_path}" "@${out_name_cmd_line}"'