From 540e4ddaf5b371297d171a19328752fd8d47f2f4 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 29 Mar 2024 17:32:16 +0100 Subject: [PATCH] Back to wide strings on Windows + magic encantations The main fix here is the switch to `wmain` from `main` on Windows. The difference is that we're now passed parameters encoded as Unicode wide strings, which makes them actually keep the correct characters as specified on the application's command line by the caller. Also, in order to properly output wide strings to output streams, applications need to set the correct mode for the output stream's file handle (see `platform_setup`) --- prepare-release.sh | 9 +----- src/gas/command_line.cc | 13 +++----- src/gas/command_line.hh | 4 +-- src/gas/gas.cc | 6 ++-- src/gas/gas.hh | 4 ++- src/gas/gas.posix.cc | 6 ++++ src/gas/gas.windows.cc | 63 ++++++++++++++++++++++++++++++++++++++ src/gas/llvm_mc_runner.cc | 6 ++-- src/gas/llvm_mc_runner.hh | 4 +-- src/gas/main.cc | 6 +++- src/gas/platform.hh | 16 +++++----- src/gas/process.windows.cc | 31 +++++++++---------- 12 files changed, 116 insertions(+), 52 deletions(-) diff --git a/prepare-release.sh b/prepare-release.sh index dcb4295..4d193ad 100755 --- a/prepare-release.sh +++ b/prepare-release.sh @@ -6,8 +6,7 @@ MY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" source common.sh WORK_DIR="${MY_DIR}/prep" -ARTIFACT_TARBALL="${DIST_PACKAGE_NAME_BASE}.tar.bz2" -ARTIFACTS_DIR="${WORK_DIR}/artifacts" +ARTIFACTS_DIR="${WORK_DIR}" ARTIFACT_ZIP="${1}" XA_TAG_COMPONENT="${2}" @@ -38,12 +37,6 @@ function prepare() echo Unpacking artifact ZIP unzip "${ARTIFACT_ZIP}" - if [ ! -f "${ARTIFACT_TARBALL}" ]; then - die Build artifact tarball $(pwd)/${ARTIFACT_TARBALL} not found - fi - - echo Unpacking binaries tarball - tar xf "${ARTIFACT_TARBALL}" if [ ! -d "${ARTIFACTS_DIR}" ]; then die Artifacts directory ${ARTIFACTS_DIR} does not exist fi diff --git a/src/gas/command_line.cc b/src/gas/command_line.cc index bf59ebe..9801418 100644 --- a/src/gas/command_line.cc +++ b/src/gas/command_line.cc @@ -5,7 +5,6 @@ #include #include "command_line.hh" -#include "exceptions.hh" #include "platform.hh" using namespace xamarin::android::gas; @@ -34,7 +33,8 @@ bool CommandLine::parse (std::span options, std::vector if (last_opt.has_value ()) { option_cb (last_opt.value (), option); } else { - throw invalid_argument_error { "Option '" + option + "' requires an argument." }; + STDERR << "Option '" << option << "' requires an argument."; + return false; } next_arg_is_value = false; @@ -83,7 +83,7 @@ bool CommandLine::parse (std::span options, std::vector auto match = std::find_if (options.begin (), options.end (), matching_option); #endif if (match == options.end ()) { - STDERR << "Uncrecognized option '" << option << "'\n"; + STDERR << "Unrecognized option '" << option << Constants::newline; continue; } @@ -98,11 +98,8 @@ bool CommandLine::parse (std::span options, std::vector } if (last_opt.has_value ()) { - platform::string message { "Option '" }; - message - .append (last_opt.value ().name) - .append ("' requires an argument."); - throw invalid_operation_error {message}; + STDERR << "Option '" << last_opt.value().name << "' requires an argument." << std::endl; + return false; } return true; diff --git a/src/gas/command_line.hh b/src/gas/command_line.hh index a4f57e6..23623d9 100644 --- a/src/gas/command_line.hh +++ b/src/gas/command_line.hh @@ -113,8 +113,8 @@ namespace xamarin::android::gas #define CLISTR(_str_lit_) PSTR((_str_lit_)) -#if !defined (_WIN32) -#define CLIPARAM(_str_lit_) std::string_view { _str_lit_ } +#if defined (_WIN32) +#define CLIPARAM(_str_lit_) std::wstring_view { L ## _str_lit_ } #else #define CLIPARAM(_str_lit_) std::string_view { _str_lit_ } #endif diff --git a/src/gas/gas.cc b/src/gas/gas.cc index 2b0759a..f806990 100644 --- a/src/gas/gas.cc +++ b/src/gas/gas.cc @@ -58,7 +58,7 @@ int Gas::usage (bool is_error, platform::string const message) return is_error ? 1 : 0; } -std::vector Gas::get_command_line (int &argc, char **&argv) +std::vector Gas::get_command_line (int argc, argv_char **argv) { std::vector ret; @@ -173,11 +173,11 @@ int Gas::run (std::vector args) ld_path /= ld_name; auto ld = std::make_unique (ld_path); ld->append_program_argument (PSTR("-o")); - ld->append_program_argument (_gas_output_file.empty () ? platform::string (Constants::default_output_name) : _gas_output_file.string ()); + ld->append_program_argument (_gas_output_file.empty () ? platform::string (Constants::default_output_name) : _gas_output_file.native ()); ld->append_program_argument (PSTR("--relocatable")); for (fs::path const& output : output_files) { - ld->append_program_argument (output.string ()); + ld->append_program_argument (output.native ()); } return ld->run (); diff --git a/src/gas/gas.hh b/src/gas/gas.hh index 5c6c6d4..a3b9115 100644 --- a/src/gas/gas.hh +++ b/src/gas/gas.hh @@ -108,7 +108,9 @@ namespace xamarin::android::gas ~Gas () {} - std::vector get_command_line (int &argc, char **&argv); + void dump_command_line_args (int argc, argv_char **argv); + static void platform_setup (); + std::vector get_command_line (int argc, argv_char **argv); int run (std::vector args); diff --git a/src/gas/gas.posix.cc b/src/gas/gas.posix.cc index 84b5010..fd1e292 100644 --- a/src/gas/gas.posix.cc +++ b/src/gas/gas.posix.cc @@ -10,6 +10,12 @@ using namespace xamarin::android::gas; +void Gas::dump_command_line_args ([[maybe_unused]] int argc, [[maybe_unused]] char **argv) +{} + +void Gas::platform_setup() +{} + void Gas::determine_program_dir (std::vector args) { fs::path program_path { args[0] }; diff --git a/src/gas/gas.windows.cc b/src/gas/gas.windows.cc index e421539..3b72424 100644 --- a/src/gas/gas.windows.cc +++ b/src/gas/gas.windows.cc @@ -2,9 +2,13 @@ #include #include +#include +#include #include #include #include +#include +#include #include "exceptions.hh" #include "gas.hh" @@ -12,6 +16,65 @@ using namespace xamarin::android::gas; +namespace { + void log_cp_info(std::wstring label, UINT cp) + { + std::wcout << L" " << label << L" code page:" << std::endl; + std::wcout << L" ID: " << cp << std::endl; + + CPINFOEX cpinfo; + BOOL result = GetCPInfoEx(cp, 0, &cpinfo); + if (!result) { + std::wcout << L" failed to obtain more information about the code page" << std::endl; + return; + } + + std::wcout + << L" Maximum character size: " << cpinfo.MaxCharSize << std::endl + << L" Localized name: " << cpinfo.CodePageName << std::endl; + } + + void log_cli_arg(int index, const wchar_t* arg) + { + std::wcout << L" [" << index << L"] " << std::endl; + std::wcout << std::endl; + std::wstring ws(arg); + std::wcout << L" As C string (direct): " << ws << std::endl; + std::wcout << L" As hex bytes: " << std::hex << std::setw(4) << std::setfill(L'0'); + + const wchar_t* p = arg; + while (p != nullptr && *p != 0) { + auto ch = static_cast(*p); + std::wcout << ch << " "; + p++; + } + std::wcout << std::endl; + } +} + +void Gas::dump_command_line_args (int argc, wchar_t **argv) +{ + std::wcout << L"Active code pages information" << std::endl; + log_cp_info(L"OS", GetACP()); + std::wcout << std::endl; + log_cp_info(L"OEM", GetOEMCP()); + std::wcout << std::endl; + + std::wcout << L"Command line arguments (" << argc << "):" << std::endl; + for (int i = 0; i < argc; i++) { + log_cli_arg(i, argv[i]); + } + std::wcout << L"================================" << std::endl << std::endl; +} + +void Gas::platform_setup() +{ + // Windows needs that magic to make stdout work with wchar_t and friends + constexpr char cp_utf16le[] = ".1200"; // UTF-16 little-endian locale. + setlocale(LC_ALL, cp_utf16le); + _setmode(_fileno(stdout), _O_WTEXT); +} + void Gas::determine_program_dir (std::vector args) { TCHAR buffer[MAX_PATH + 1]{}; diff --git a/src/gas/llvm_mc_runner.cc b/src/gas/llvm_mc_runner.cc index 965fc94..49f75cf 100644 --- a/src/gas/llvm_mc_runner.cc +++ b/src/gas/llvm_mc_runner.cc @@ -26,7 +26,7 @@ std::unordered_map LlvmMcRunner::known_options { int LlvmMcRunner::run (fs::path const& executable_path) { if (!fs::exists (executable_path)) { - STDERR << "Executable '" << executable_path.string () << "' does not exist." << Constants::newline; + STDERR << "Executable '" << executable_path.native () << "' does not exist." << Constants::newline; return Constants::wrapper_exec_failed_error_code; } @@ -59,8 +59,8 @@ int LlvmMcRunner::run (fs::path const& executable_path) process->append_program_argument (PSTR("-o"), opt->second); } - platform::string input_file { PSTR("\"") + input_file_path.make_preferred ().string () + PSTR("\"") }; - process->append_program_argument (input_file_path.make_preferred ().string ()); + platform::string input_file { PSTR("\"") + input_file_path.make_preferred ().native () + PSTR("\"") }; + process->append_program_argument (input_file_path.make_preferred ().native ()); return process->run (); } diff --git a/src/gas/llvm_mc_runner.hh b/src/gas/llvm_mc_runner.hh index afb7cd5..f83ece9 100644 --- a/src/gas/llvm_mc_runner.hh +++ b/src/gas/llvm_mc_runner.hh @@ -68,7 +68,7 @@ namespace xamarin::android::gas void set_output_file_path (fs::path const& file_path) { - set_option (LlvmMcArgument::Output, file_path.string ()); + set_option (LlvmMcArgument::Output, file_path.native ()); } void add_include_path (fs::path const& include_path) @@ -77,7 +77,7 @@ namespace xamarin::android::gas return; } - set_option (LlvmMcArgument::IncludeDir, include_path.string ()); + set_option (LlvmMcArgument::IncludeDir, include_path.native ()); } void generate_debug_info () diff --git a/src/gas/main.cc b/src/gas/main.cc index aca2ea8..f41c6dd 100644 --- a/src/gas/main.cc +++ b/src/gas/main.cc @@ -1,12 +1,16 @@ // SPDX-License-Identifier: MIT -#include #include #include "gas.hh" #include "platform.hh" +#if defined(_WIN32) +int wmain (int argc, wchar_t **argv) +#else int main (int argc, char **argv) +#endif { + xamarin::android::gas::Gas::platform_setup (); xamarin::android::gas::Gas app; std::vector args = app.get_command_line (argc, argv); diff --git a/src/gas/platform.hh b/src/gas/platform.hh index 80d241f..211c476 100644 --- a/src/gas/platform.hh +++ b/src/gas/platform.hh @@ -6,21 +6,23 @@ #include #if defined(_WIN32) -#define STDOUT std::cout -#define STDERR std::cerr -#define PSTR(_str_lit_) (_str_lit_) -#define PCHAR(_ch_) (_ch_) +#define STDOUT std::wcout +#define STDERR std::wcerr +#define PSTR(_str_lit_) (L ## _str_lit_) +#define PCHAR(_ch_) (L ## _ch_) +using argv_char = wchar_t; #else #define STDOUT std::cout #define STDERR std::cerr #define PSTR(_str_lit_) (_str_lit_) #define PCHAR(_ch_) (_ch_) +using argv_char = char; #endif namespace platform { -#if !defined (_WIN32) - using string = std::string; - using string_view = std::string_view; +#if defined (_WIN32) + using string = std::wstring; + using string_view = std::wstring_view; #else using string = std::string; using string_view = std::string_view; diff --git a/src/gas/process.windows.cc b/src/gas/process.windows.cc index 7edb57b..3953ad9 100644 --- a/src/gas/process.windows.cc +++ b/src/gas/process.windows.cc @@ -49,7 +49,7 @@ int Process::run (bool print_command_line) print_process_command_line (); } - platform::string binary = executable_path.string (); + platform::string binary = executable_path.native (); platform::string args { escape_argument (binary) }; for (platform::string const& a : _args) { if (a.empty ()) { @@ -59,21 +59,14 @@ int Process::run (bool print_command_line) args.append (escape_argument (a)); } - int size = MultiByteToWideChar (CP_UTF8, 0, args.c_str (), -1, NULL , 0); - wchar_t* wargs = new wchar_t [size]; - MultiByteToWideChar (CP_UTF8, 0, args.c_str (), -1, wargs, size); - - size = MultiByteToWideChar (CP_UTF8, 0, binary.c_str (), -1, NULL , 0); - wchar_t* wbinary = new wchar_t [size]; - MultiByteToWideChar (CP_UTF8, 0, binary.c_str (), -1, wbinary, size); - PROCESS_INFORMATION pi {}; STARTUPINFOW si {}; si.cb = sizeof(si); DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT; + wchar_t* wargs = _wcsdup(args.c_str()); BOOL success = CreateProcessW ( - wbinary, + binary.c_str (), wargs, nullptr, // process security attributes nullptr, // primary thread security attributes @@ -84,24 +77,28 @@ int Process::run (bool print_command_line) &si, &pi ); - - delete[] wargs; - delete[] wbinary; + free (wargs); if (!success) { return Constants::wrapper_exec_failed_error_code; } // TODO: error handling below + int ret = 0; DWORD result = WaitForSingleObject (pi.hProcess, INFINITE); if (result == 0) { DWORD retcode = 0; if (GetExitCodeProcess (pi.hProcess, &retcode)) { - return retcode; + ret = retcode; + } else { + ret = 128; } - - return 128; + } else { + ret = 1; } - return 1; + CloseHandle (pi.hProcess); + CloseHandle (pi.hThread); + + return ret; }