From c836a71114b0f05fe8926c52d63c7d2ebbc646e1 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Wed, 27 Aug 2025 14:44:43 +0200 Subject: [PATCH 01/10] Add OS profile sources for Windows --- .gitignore | 1 - build-aux/meson.build | 1 + build-aux/win32/app.c | 162 +++++++++++++++++++++++++++++++ build-aux/win32/app.manifest.xml | 40 ++++++++ build-aux/win32/app.rc | 6 ++ build-aux/win32/meson.build | 14 +++ 6 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 build-aux/meson.build create mode 100644 build-aux/win32/app.c create mode 100644 build-aux/win32/app.manifest.xml create mode 100644 build-aux/win32/app.rc create mode 100644 build-aux/win32/meson.build diff --git a/.gitignore b/.gitignore index eda54ed086..744d70fe90 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ tags *.pc .*.swp .sw? -*.rc *.gcno *.gcda *.gcov diff --git a/build-aux/meson.build b/build-aux/meson.build new file mode 100644 index 0000000000..7f3f2ffda3 --- /dev/null +++ b/build-aux/meson.build @@ -0,0 +1 @@ +subdir('win32') diff --git a/build-aux/win32/app.c b/build-aux/win32/app.c new file mode 100644 index 0000000000..e158c3b4d6 --- /dev/null +++ b/build-aux/win32/app.c @@ -0,0 +1,162 @@ +/* + * Copyright © 2025 Luca Bacci + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * Author: Luca Bacci + */ + +#include "config.h" + +#include + +#include +#include +#include +#include + +#ifdef _MSC_VER +#include +#endif + +static void +set_process_wide_settings (void) +{ +#if defined (_M_IX86) || defined (__i386__) + /* https://learn.microsoft.com/en-us/archive/blogs/michael_howard/faq-about-heapsetinformation-in-windows-vista-and-heap-based-buffer-overruns */ + /* https://web.archive.org/web/20080825034220/https://blogs.msdn.com/sdl/archive/2008/06/06/corrupted-heap-termination-redux.aspx */ + HeapSetInformation (NULL, HeapEnableTerminationOnCorruption, NULL, 0); + + /* https://learn.microsoft.com/en-us/archive/blogs/michael_howard/new-nx-apis-added-to-windows-vista-sp1-windows-xp-sp3-and-windows-server-2008 */ + SetProcessDEPPolicy (PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION); +#endif + + SetErrorMode (GetErrorMode () | SEM_FAILCRITICALERRORS); +} + +static void +set_crt_non_interactive (void) +{ + /* The Debug CRT may show UI dialogs even in console applications. + * Direct to stderr instead. + */ + _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR); + _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_FILE); + + _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_FILE); + + _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR); + _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_FILE); +} + +static void +set_stderr_unbuffered_mode (void) +{ + /* MSVCRT.DLL can open stderr in full-buffering mode. That depends on + * the type of output device; for example, it's fully buffered for + * named pipes but not for console devices. + * + * Having a fully buffered stderr is not a good default since we can + * loose important messages before a crash. Moreover, POSIX forbids + * full buffering on stderr. So here we set stderr to unbuffered mode. + * + * Note: line buffering mode would be good enough, but the Windows C + * RunTime library implements it the same as full buffering: + * + * "for some systems, _IOLBF provides line buffering. However, for + * Win32, the behavior is the same as _IOFBF: Full Buffering" + * + * https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setvbuf#remarks + * + * References: + * + * - https://sourceforge.net/p/mingw/mailman/message/27121137/ + */ +#if !defined (_UCRT) + int ret = setvbuf (stderr, NULL, _IONBF, 0); + assert (ret == 0); +#endif +} + +static void +early_flush_exit_handler (void) +{ + /* There are two ways to flush open streams: calling fflush with NULL + * argument and calling _flushall. The former flushes output streams + * only, the latter flushes both input and output streams. + * We should not do anything with input streams here since flushing + * means * discarding * data. + */ + fflush (NULL); +} + +static void +register_early_flush_at_exit (void) +{ + /* Implement the two-phase flushing at process exit. + * + * The C RunTime library flushes open streams within its DllMain handler. + * This goes against the rules for DllMain, as each stream is protected + * by a lock and locks must not be acquired in DllMain. + * + * So we flush from app code using an atexit handler. The handler runs when + * the application is in a fully working state and thus is completely safe. + * + * This ensures that all important data is flushed. Anything that is written + * after exit will be flushed lately by the C RunTime library (and therefore + * may be skipped). + * + * See comments in "%ProgramFiles(x86)%\Windows Kits\10\Source\\ + * \ucrt\stdio\fflush.cpp" for more informations. + * + * References: + * + * - https://devblogs.microsoft.com/oldnewthing/20070503-00/?p=27003 + * - https://devblogs.microsoft.com/oldnewthing/20100122-00/?p=15193 + * - https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices + */ + int ret = atexit (early_flush_exit_handler); + assert (ret == 0); +} + +/* Boilerplate for CRT constructor */ + +#ifdef _MSC_VER +static void startup (void); + +__pragma (section (".CRT$XCT", long, read)) + +__declspec (allocate (".CRT$XCT")) +const void (*ptr_startup) (void) = startup; + +#ifdef _M_IX86 +__pragma (comment (linker, "/INCLUDE:" "_ptr_startup")) +#else +__pragma (comment (linker, "/INCLUDE:" "ptr_startup")) +#endif +#else +static void __attribute__((constructor)) startup (void); +#endif + +static void +startup (void) +{ + set_crt_non_interactive (); + set_process_wide_settings (); + set_stderr_unbuffered_mode (); + register_early_flush_at_exit (); +} diff --git a/build-aux/win32/app.manifest.xml b/build-aux/win32/app.manifest.xml new file mode 100644 index 0000000000..57ec3d31d0 --- /dev/null +++ b/build-aux/win32/app.manifest.xml @@ -0,0 +1,40 @@ + + + + + UTF-8 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-aux/win32/app.rc b/build-aux/win32/app.rc new file mode 100644 index 0000000000..ce9785df1c --- /dev/null +++ b/build-aux/win32/app.rc @@ -0,0 +1,6 @@ +#pragma code_page(65001) + +#define WIN32_LEAN_AND_MEAN +#include + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "app.manifest.xml" diff --git a/build-aux/win32/meson.build b/build-aux/win32/meson.build new file mode 100644 index 0000000000..820b6514fe --- /dev/null +++ b/build-aux/win32/meson.build @@ -0,0 +1,14 @@ + +if host_system == 'windows' + static_lib = static_library('os-profile-app-static-lib', + sources: ['app.c'], + include_directories: [configinc]) + + resources = windows.compile_resources('app.rc', + depend_files: ['app.manifest.xml']) + + app_profile_dep = declare_dependency(link_whole: [static_lib], + sources: [resources]) +else + app_profile_dep = declare_dependency() +endif -- GitLab From 663ee11fda42b047de8be1cc0d3521d1d64e5ae4 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Thu, 28 Aug 2025 14:54:09 +0200 Subject: [PATCH 02/10] Add OS profile app to executables Fixes #3733 --- fuzzing/meson.build | 2 +- gio/meson.build | 17 +++++++++-------- gio/tests/meson.build | 6 +++--- gio/tests/static-link/meson.build | 5 ++++- girepository/compiler/meson.build | 1 + girepository/decompiler/meson.build | 1 + girepository/inspector/meson.build | 1 + girepository/meson.build | 3 ++- girepository/tests/meson.build | 2 +- glib/glib-mirroring-tab/meson.build | 2 +- glib/meson.build | 10 +++++----- glib/tests/meson.build | 4 ++-- gmodule/tests/meson.build | 2 +- gobject/meson.build | 2 +- gobject/tests/meson.build | 2 +- gobject/tests/performance/meson.build | 2 +- gthread/tests/meson.build | 2 +- meson.build | 1 + 18 files changed, 37 insertions(+), 28 deletions(-) diff --git a/fuzzing/meson.build b/fuzzing/meson.build index addbe90717..a66c2dd215 100644 --- a/fuzzing/meson.build +++ b/fuzzing/meson.build @@ -47,7 +47,7 @@ fuzz_targets = [ 'fuzz_variant_text', ] -deps = [libgmodule_dep, libgio_dep, libglib_dep, libgobject_dep] +deps = [libgmodule_dep, libgio_dep, libglib_dep, libgobject_dep, app_profile_dep] extra_sources = [] extra_c_args = cc.get_supported_arguments('-Werror=unused-function') diff --git a/gio/meson.build b/gio/meson.build index 201d2935fd..774c8f906e 100644 --- a/gio/meson.build +++ b/gio/meson.build @@ -426,6 +426,7 @@ if host_system != 'windows' endif gio_launch_desktop = executable('gio-launch-desktop', launch_desktop_sources, + dependencies: [app_profile_dep], include_directories : glibinc, install : true, install_dir : multiarch_libexecdir, @@ -1039,14 +1040,14 @@ gio_tool = executable('gio', gio_tool_sources, c_args : gio_c_args, # intl.lib is not compatible with SAFESEH link_args : noseh_link_args, - dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep]) + dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep, app_profile_dep]) executable('gresource', 'gresource-tool.c', install : true, install_tag : 'bin', # intl.lib is not compatible with SAFESEH link_args : noseh_link_args, - dependencies : [libelf, libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep]) + dependencies : [libelf, libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep, app_profile_dep]) gio_querymodules = executable('gio-querymodules', 'gio-querymodules.c', 'giomodule-priv.c', install : true, @@ -1055,7 +1056,7 @@ gio_querymodules = executable('gio-querymodules', 'gio-querymodules.c', 'giomodu c_args : gio_c_args, # intl.lib is not compatible with SAFESEH link_args : noseh_link_args, - dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep]) + dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep, app_profile_dep]) glib_compile_schemas = executable('glib-compile-schemas', ['glib-compile-schemas.c'], @@ -1064,7 +1065,7 @@ glib_compile_schemas = executable('glib-compile-schemas', install_tag : 'bin', # intl.lib is not compatible with SAFESEH link_args : noseh_link_args, - dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep, gvdb_dep]) + dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep, gvdb_dep, app_profile_dep]) glib_compile_resources = executable('glib-compile-resources', [gconstructor_as_data_h, 'glib-compile-resources.c'], @@ -1073,7 +1074,7 @@ glib_compile_resources = executable('glib-compile-resources', c_args : gio_c_args, # intl.lib is not compatible with SAFESEH link_args : noseh_link_args, - dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep, gvdb_dep]) + dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep, gvdb_dep, app_profile_dep]) install_data('gresource.dtd', install_dir: get_option('datadir') / dtds_subdir, install_tag: 'devel', @@ -1094,7 +1095,7 @@ executable('gsettings', 'gsettings-tool.c', c_args : gio_c_args, # intl.lib is not compatible with SAFESEH link_args : noseh_link_args, - dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep]) + dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep, app_profile_dep]) install_data('gschema.dtd', install_dir : get_option('datadir') / schemas_subdir, install_tag : 'devel', @@ -1111,7 +1112,7 @@ executable('gdbus', 'gdbus-tool.c', c_args : gio_c_args, # intl.lib is not compatible with SAFESEH link_args : noseh_link_args, - dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep]) + dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep, app_profile_dep]) if host_system != 'windows' and not glib_have_cocoa executable('gapplication', 'gapplication-tool.c', @@ -1120,7 +1121,7 @@ if host_system != 'windows' and not glib_have_cocoa c_args : gio_c_args, # intl.lib is not compatible with SAFESEH link_args : noseh_link_args, - dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep]) + dependencies : [libgio_dep, libgobject_dep, libgmodule_dep, libglib_dep, app_profile_dep]) endif if enable_systemtap diff --git a/gio/tests/meson.build b/gio/tests/meson.build index e719008e47..efd5e2ff79 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -635,7 +635,7 @@ if host_machine.system() != 'windows' # This test is currently unreliable executable('gdbus-overflow', 'gdbus-overflow.c', c_args : test_c_args, - dependencies : common_gio_tests_deps, + dependencies : common_gio_tests_deps + [app_profile_dep], install_dir : installed_tests_execdir, install_tag : 'tests', install : installed_tests_enabled) @@ -1129,7 +1129,7 @@ foreach program_name, extra_args : test_extra_programs program_name : executable(program_name, sources: [source, extra_sources], c_args : test_c_args, - dependencies : common_gio_tests_deps + extra_args.get('dependencies', []), + dependencies : common_gio_tests_deps + extra_args.get('dependencies', []) + [app_profile_dep], install_dir : installed_tests_execdir, install_tag : 'tests', install : install, @@ -1168,7 +1168,7 @@ foreach test_name, extra_args : gio_tests exe = executable(test_name, [source, extra_sources], c_args : test_c_args + extra_args.get('c_args', []), cpp_args : test_cpp_args + extra_args.get('cpp_args', []), - dependencies : common_gio_tests_deps + extra_args.get('dependencies', []), + dependencies : common_gio_tests_deps + extra_args.get('dependencies', []) + [app_profile_dep], install_rpath : extra_args.get('install_rpath', ''), install_dir: installed_tests_execdir, install_tag: 'tests', diff --git a/gio/tests/static-link/meson.build b/gio/tests/static-link/meson.build index c0b638fe6c..558840d0e6 100644 --- a/gio/tests/static-link/meson.build +++ b/gio/tests/static-link/meson.build @@ -3,6 +3,9 @@ project('test-static-link', 'c') # This is a dummy project that static links against installed gio. # See gio/tests/static-link.py. app = executable('test-static-link', 'app.c', - dependencies : dependency('gio-2.0', static : true) + dependencies : [ + dependency('gio-2.0', static : true), + app_profile_dep, + ] ) test('test-static-link', app) diff --git a/girepository/compiler/meson.build b/girepository/compiler/meson.build index eb704761e2..c3fce078e1 100644 --- a/girepository/compiler/meson.build +++ b/girepository/compiler/meson.build @@ -20,6 +20,7 @@ gicompilerepository = executable('gi-compile-repository', 'compiler.c', libgirepository_dep, libgirepository_internals_dep, libgio_dep, + app_profile_dep, ], install: true, c_args: custom_c_args, diff --git a/girepository/decompiler/meson.build b/girepository/decompiler/meson.build index b6090f61f6..a2fb904b48 100644 --- a/girepository/decompiler/meson.build +++ b/girepository/decompiler/meson.build @@ -20,6 +20,7 @@ gidecompiletypelib = executable('gi-decompile-typelib', 'decompiler.c', libgirepository_dep, libgirepository_internals_dep, libgio_dep, + app_profile_dep, ], install: true, c_args: custom_c_args, diff --git a/girepository/inspector/meson.build b/girepository/inspector/meson.build index 4467e6df80..81d43e3eb8 100644 --- a/girepository/inspector/meson.build +++ b/girepository/inspector/meson.build @@ -19,6 +19,7 @@ giinspecttypelib = executable('gi-inspect-typelib', 'inspector.c', dependencies: [ libgirepository_dep, libgio_dep, + app_profile_dep, ], install: true, c_args: custom_c_args, diff --git a/girepository/meson.build b/girepository/meson.build index b08801891b..3d1793e355 100644 --- a/girepository/meson.build +++ b/girepository/meson.build @@ -225,7 +225,8 @@ executable('gi-dump-types', dependencies: [ libgirepository_dep, libgiounix_dep, - libgiowin32_dep + libgiowin32_dep, + app_profile_dep, ], ) diff --git a/girepository/tests/meson.build b/girepository/tests/meson.build index b804346fb4..4d3f64a6d3 100644 --- a/girepository/tests/meson.build +++ b/girepository/tests/meson.build @@ -96,7 +96,7 @@ test_env = environment() test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) -test_deps = [libm, thread_dep, libgirepository_dep] +test_deps = [libm, thread_dep, libgirepository_dep, app_profile_dep] test_cargs = ['-DG_LOG_DOMAIN="GIRepository"', '-UG_DISABLE_ASSERT', warning_sign_conversion_args] test_cpp_args = test_cargs diff --git a/glib/glib-mirroring-tab/meson.build b/glib/glib-mirroring-tab/meson.build index 4b14ecf3be..2900be203d 100644 --- a/glib/glib-mirroring-tab/meson.build +++ b/glib/glib-mirroring-tab/meson.build @@ -1,5 +1,5 @@ gen_mirroring_tab = executable('gen-mirroring-tab', ['gen-mirroring-tab.c', 'packtab.c'], - dependencies : [libglib_dep], + dependencies : [libglib_dep, app_profile_dep], install: false, ) \ No newline at end of file diff --git a/glib/meson.build b/glib/meson.build index 209bcbfd11..19774c20cf 100644 --- a/glib/meson.build +++ b/glib/meson.build @@ -496,23 +496,23 @@ if host_system == 'windows' install : true, win_subsystem : 'windows', include_directories : configinc, - dependencies : [libglib_dep]) + dependencies : [libglib_dep, app_profile_dep]) gspawn_helpers += executable('gspawn-win32-helper-console', 'gspawn-win32-helper.c', install : true, c_args : ['-DHELPER_CONSOLE'], include_directories : configinc, - dependencies : [libglib_dep]) + dependencies : [libglib_dep, app_profile_dep]) else gspawn_helpers += executable('gspawn-win64-helper', 'gspawn-win32-helper.c', install : true, win_subsystem : 'windows', include_directories : configinc, - dependencies : [libglib_dep]) + dependencies : [libglib_dep, app_profile_dep]) gspawn_helpers += executable('gspawn-win64-helper-console', 'gspawn-win32-helper.c', install : true, c_args : ['-DHELPER_CONSOLE'], include_directories : configinc, - dependencies : [libglib_dep]) + dependencies : [libglib_dep, app_profile_dep]) endif else gtester = executable('gtester', 'gtester.c', @@ -520,7 +520,7 @@ else install_tag : 'bin-devel', c_args : ['-UG_DISABLE_ASSERT'], include_directories : configinc, - dependencies : [libglib_dep]) + dependencies : [libglib_dep, app_profile_dep]) # Provide tools for others when we're a subproject and they use the Meson GNOME module meson.override_find_program('gtester', gtester) diff --git a/glib/tests/meson.build b/glib/tests/meson.build index c4fec0c13c..301c06c9d4 100644 --- a/glib/tests/meson.build +++ b/glib/tests/meson.build @@ -400,8 +400,8 @@ test_env = environment() test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) -test_deps = [libm, thread_dep, libglib_dep] -test_deps_static = [libm, thread_dep, libglib_static_dep] +test_deps = [libm, thread_dep, libglib_dep, app_profile_dep] +test_deps_static = [libm, thread_dep, libglib_static_dep, app_profile_dep] test_cargs = ['-DG_LOG_DOMAIN="GLib"', '-UG_DISABLE_ASSERT'] test_cpp_args = test_cargs diff --git a/gmodule/tests/meson.build b/gmodule/tests/meson.build index e7d7b3dd1e..64e6040e06 100644 --- a/gmodule/tests/meson.build +++ b/gmodule/tests/meson.build @@ -74,7 +74,7 @@ test_env = environment() test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) -test_deps = [libm, thread_dep, libglib_dep, libgmodule_dep] +test_deps = [libm, thread_dep, libglib_dep, libgmodule_dep, app_profile_dep] test_cargs = ['-DG_LOG_DOMAIN="GModule"', '-UG_DISABLE_ASSERT', warning_sign_conversion_args] test_cpp_args = test_cargs diff --git a/gobject/meson.build b/gobject/meson.build index 91823af79a..421d4e311e 100644 --- a/gobject/meson.build +++ b/gobject/meson.build @@ -171,7 +171,7 @@ meson.override_dependency('gobject-2.0', libgobject_dep) gobject_query = executable('gobject-query', 'gobject-query.c', install : true, install_tag : 'bin-devel', - dependencies : [libglib_dep, libgobject_dep]) + dependencies : [libglib_dep, libgobject_dep, app_profile_dep]) install_data('gobject_gdb.py', install_dir : glib_pkgdatadir / 'gdb', diff --git a/gobject/tests/meson.build b/gobject/tests/meson.build index 214df48459..a3f4b17f8f 100644 --- a/gobject/tests/meson.build +++ b/gobject/tests/meson.build @@ -178,7 +178,7 @@ test_env = environment() test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) -test_deps = [libm, thread_dep, libglib_dep, libgobject_dep] +test_deps = [libm, thread_dep, libglib_dep, libgobject_dep, app_profile_dep] test_cargs = ['-DG_LOG_DOMAIN="GLib-GObject"', '-UG_DISABLE_ASSERT', warning_sign_conversion_args] test_cpp_args = test_cargs diff --git a/gobject/tests/performance/meson.build b/gobject/tests/performance/meson.build index 0147f7c85e..ae1a209433 100644 --- a/gobject/tests/performance/meson.build +++ b/gobject/tests/performance/meson.build @@ -11,7 +11,7 @@ test_env = environment() test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) -test_deps = [libm, thread_dep, libglib_dep, libgobject_dep] +test_deps = [libm, thread_dep, libglib_dep, libgobject_dep, app_profile_dep] test_cargs = ['-DG_LOG_DOMAIN="GLib-GObject"', '-UG_DISABLE_ASSERT'] foreach test_name, extra_args : gobject_tests diff --git a/gthread/tests/meson.build b/gthread/tests/meson.build index 66b50caf76..ebb67929c9 100644 --- a/gthread/tests/meson.build +++ b/gthread/tests/meson.build @@ -6,7 +6,7 @@ test_env = environment() test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) -test_deps = [thread_dep, libglib_dep, libgthread_dep] +test_deps = [thread_dep, libglib_dep, libgthread_dep, app_profile_dep] test_cargs = ['-DG_LOG_DOMAIN="GLib-GThread"', '-UG_DISABLE_ASSERT', warning_sign_conversion_args] test_cpp_args = test_cargs diff --git a/meson.build b/meson.build index f30ca58066..f6b9f5135d 100644 --- a/meson.build +++ b/meson.build @@ -2717,6 +2717,7 @@ pkg = import('pkgconfig') windows = import('windows') gnome = import('gnome') +subdir('build-aux') subdir('tools') subdir('glib') subdir('gobject') -- GitLab From 8e504845f2656311f6baa8ee1113964ee8ade611 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Thu, 28 Aug 2025 15:00:36 +0200 Subject: [PATCH 03/10] Revert "gobject tests: Fix running custom-dispatch on 32-bit Windows" This reverts commit cb1eb5758150de94b09bfda35db05c1ddd27abc7. We now embed a proper manifest in all executables --- gobject/tests/meson.build | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/gobject/tests/meson.build b/gobject/tests/meson.build index a3f4b17f8f..e26acf1274 100644 --- a/gobject/tests/meson.build +++ b/gobject/tests/meson.build @@ -28,35 +28,10 @@ marshalers_c = custom_target('marshalers_c', ], ) -# We must embed custom-dispatch.exe with an application -# manifest to pacify UAC in order to run on 32-bit Windows -# builds, otherwise the test will not run as UAC will kill it. -extra_custom_dispatch_objs = [] -if embed_uac_manifest - uac_exe_pkg = 'gobject' - uac_exe_name = 'custom-dispatch' - - # Well, we have to forgo the xxx.exe.manifest in the output listing, since - # compile_resources doesn't like to consume targets with multiple outputs, - # and the xxx.exe.manifest and xxx.rc are tied together - uac_rc = custom_target( - '@0@.rc'.format(uac_exe_name), - output: ['@0@.rc'.format(uac_exe_name)], - command: [gen_uac_manifest, - '-p=@0@'.format(uac_exe_pkg), - '-n=@0@'.format(uac_exe_name), - '--pkg-version=@0@'.format(meson.project_version()), - '--output-dir=@OUTDIR@'], - ) - extra_custom_dispatch_objs = import('windows').compile_resources(uac_rc) -endif - gobject_tests = { 'notify-init' : {}, 'notify-init2' : {}, - 'custom-dispatch' : { - 'extra_objs' : extra_custom_dispatch_objs, - }, + 'custom-dispatch' : {}, 'qdata' : {}, 'accumulator' : { 'source' : ['accumulator.c', marshalers_h, marshalers_c], @@ -184,7 +159,6 @@ test_cpp_args = test_cargs foreach test_name, extra_args : gobject_tests source = extra_args.get('source', test_name + '.c') - extra_objs = extra_args.get('extra_objs', []) install = installed_tests_enabled and extra_args.get('install', true) if install @@ -201,7 +175,7 @@ foreach test_name, extra_args : gobject_tests ) endif - exe = executable(test_name, source, extra_objs, + exe = executable(test_name, source, c_args : test_cargs + extra_args.get('c_args', []), cpp_args : test_cpp_args + extra_args.get('cpp_args', []), dependencies : test_deps + extra_args.get('dependencies', []), -- GitLab From 59135a3d98406c0634faf985df4e39e2da4e4659 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Thu, 28 Aug 2025 15:02:25 +0200 Subject: [PATCH 04/10] Revert "tools: Add script to create UAC manifests for Windows" This reverts commit ab732692a0b8939e95b81014bd52093227463c41. We now embed a proper manifest in all executables --- tools/generate-uac-manifest.py | 137 --------------------------------- tools/meson.build | 8 -- 2 files changed, 145 deletions(-) delete mode 100644 tools/generate-uac-manifest.py diff --git a/tools/generate-uac-manifest.py b/tools/generate-uac-manifest.py deleted file mode 100644 index a99f0a92f7..0000000000 --- a/tools/generate-uac-manifest.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright © 2021 Chun-wei Fan. -# -# SPDX-License-Identifier: LGPL-2.1-or-later -# -# Original author: Chun-wei Fan - -""" -This script generates a Windows manifest file and optionally a resource file to -determine whether a specified program requires UAC elevation -""" - -import os -import argparse - -DOMAIN_NAME = "gnome" - - -def main(): - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument( - "-p", "--package", required=True, help="package name of the executable" - ) - parser.add_argument("-n", "--name", required=True, help="name of executable") - parser.add_argument( - "--pkg-version", required=True, dest="version", help="version of package" - ) - parser.add_argument( - "--require-admin", - action="store_true", - dest="admin", - default=False, - help="require admin access to application", - ) - parser.add_argument( - "--input-resource-file", - dest="resource", - default=None, - help="existing .rc file to embed UAC manifest (do not generate a new .rc file), must have included windows.h in it", - ) - parser.add_argument( - "--output-dir", - dest="outdir", - default=None, - help="directory to output resulting files", - ) - args = parser.parse_args() - - if args.resource is not None: - if not os.path.isfile(args.resource): - raise FileNotFoundError( - "Specified resource file '%s' does not exist" % args.resource - ) - - generate_manifest(args.package, args.name, args.version, args.admin, args.outdir) - write_rc_file(args.name, args.resource, args.outdir) - - -def generate_manifest(package, name, version, admin, outdir): - if version.count(".") == 0: - manifest_package_version = version + ".0.0.0" - elif version.count(".") == 1: - manifest_package_version = version + ".0.0" - elif version.count(".") == 2: - manifest_package_version = version + ".0" - elif version.count(".") == 3: - manifest_package_version = version - else: - parts = version.split(".") - manifest_package_version = "" - for x in (0, 1, 2, 3): - if x == 0: - manifest_package_version += parts[x] - else: - manifest_package_version += "." + parts[x] - - if outdir is not None: - output_file_base_name = os.path.join(outdir, name) - else: - output_file_base_name = name - - outfile = open(output_file_base_name + ".exe.manifest", "w+") - outfile.write("\n") - outfile.write( - "\n" - ) - outfile.write(" \n") - outfile.write(" \n") - outfile.write(" \n") - outfile.write(" \n") - outfile.write(" \n") - outfile.write(" \n") - outfile.write(" \n") - outfile.write(" \n") - outfile.write("\n") - outfile.close() - - -def write_rc_file(name, resource, outdir): - if outdir is not None: - output_file_base_name = os.path.join(outdir, name) - else: - output_file_base_name = name - - if resource is None: - outfile = open(output_file_base_name + ".rc", "w+") - outfile.write("#define WIN32_LEAN_AND_MEAN\n") - outfile.write("#include \n") - else: - if resource != output_file_base_name + ".rc": - outfile = open(output_file_base_name + ".rc", "w+") - else: - outfile = open(output_file_base_name + ".final.rc", "w+") - srcfile = open(resource, "r") - outfile.write(srcfile.read()) - srcfile.close() - - outfile.write("\n") - outfile.write( - 'CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "%s.exe.manifest"' % name - ) - outfile.close() - - -if __name__ == "__main__": - main() diff --git a/tools/meson.build b/tools/meson.build index 257312ebf9..e80d4be982 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -24,11 +24,3 @@ if host_system != 'windows' endif gen_visibility_macros = find_program('gen-visibility-macros.py') - -# This is only needed for 32-bit (x86) Windows builds -if host_system == 'windows' and host_machine.cpu_family() == 'x86' - embed_uac_manifest = true - gen_uac_manifest = find_program('generate-uac-manifest.py') -else - embed_uac_manifest = false -endif -- GitLab From d1f36fefb24611b19c9dcfd36ca07314bd0d391b Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Fri, 29 Aug 2025 11:42:09 +0200 Subject: [PATCH 05/10] Add template rc for libraries --- build-aux/win32/lib.rc.in | 35 +++++++++++++++++++++++++++++++++++ build-aux/win32/meson.build | 9 +++++++++ 2 files changed, 44 insertions(+) create mode 100644 build-aux/win32/lib.rc.in diff --git a/build-aux/win32/lib.rc.in b/build-aux/win32/lib.rc.in new file mode 100644 index 0000000000..0dac990ba2 --- /dev/null +++ b/build-aux/win32/lib.rc.in @@ -0,0 +1,35 @@ +#pragma code_page(65001) + +#define WIN32_LEAN_AND_MEAN +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @MAJOR@,@MINOR@,@MICRO@,0 + PRODUCTVERSION @MAJOR@,@MINOR@,@MICRO@,0 + FILEFLAGSMASK 0 + FILEFLAGS 0 + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "The GLib developer community" + VALUE "FileDescription", "@DESCRIPTION@" + VALUE "FileVersion", "@MAJOR@.@MINOR@.@MICRO@" + VALUE "InternalName", "@NAME@" + VALUE "LegalCopyright", "© @COPYRIGHTYEAR@ the GLib developer community. © 1995-2011 Peter Mattis, Spencer Kimball, Josh MacDonald and others." + VALUE "OriginalFilename", "@FILENAME@" + VALUE "ProductName", "GLib" + VALUE "ProductVersion", "@MAJOR@.@MINOR@.@MICRO@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END + END diff --git a/build-aux/win32/meson.build b/build-aux/win32/meson.build index 820b6514fe..cf8c56ffa8 100644 --- a/build-aux/win32/meson.build +++ b/build-aux/win32/meson.build @@ -12,3 +12,12 @@ if host_system == 'windows' else app_profile_dep = declare_dependency() endif + +rc_conf_base = configuration_data({ + 'MAJOR': major_version, + 'MINOR': minor_version, + 'MICRO': micro_version, + 'COPYRIGHTYEAR': '2025', +}) + +lib_rc_in = files('lib.rc.in') -- GitLab From ef0edb01ad23b0e4117174e5c6a3119b89d6c43a Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Fri, 29 Aug 2025 12:57:51 +0200 Subject: [PATCH 06/10] Use template library RC file --- gio/gio.rc.in | 30 ------------------------------ gio/meson.build | 18 +++++++++++++----- glib/glib.rc.in | 30 ------------------------------ glib/meson.build | 19 ++++++++++++++----- gmodule/gmodule.rc.in | 30 ------------------------------ gmodule/meson.build | 18 +++++++++++++----- gobject/gobject.rc.in | 30 ------------------------------ gobject/meson.build | 18 +++++++++++++----- gthread/gthread.rc.in | 30 ------------------------------ gthread/meson.build | 18 +++++++++++++----- 10 files changed, 66 insertions(+), 175 deletions(-) delete mode 100644 gio/gio.rc.in delete mode 100644 glib/glib.rc.in delete mode 100644 gmodule/gmodule.rc.in delete mode 100644 gobject/gobject.rc.in delete mode 100644 gthread/gthread.rc.in diff --git a/gio/gio.rc.in b/gio/gio.rc.in deleted file mode 100644 index 3b19b3ee13..0000000000 --- a/gio/gio.rc.in +++ /dev/null @@ -1,30 +0,0 @@ -#include - -VS_VERSION_INFO VERSIONINFO - FILEVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0 - PRODUCTVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0 - FILEFLAGSMASK 0 - FILEFLAGS 0 - FILEOS VOS__WINDOWS32 - FILETYPE VFT_DLL - FILESUBTYPE VFT2_UNKNOWN - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904B0" - BEGIN - VALUE "CompanyName", "The GLib developer community" - VALUE "FileDescription", "Gio" - VALUE "FileVersion", "@GLIB_VERSION@.0" - VALUE "InternalName", "libgio-2.0-@LT_CURRENT_MINUS_AGE@" - VALUE "LegalCopyright", "Copyright 2006-2011 Red Hat, Inc. and others." - VALUE "OriginalFilename", "libgio-2.0-@LT_CURRENT_MINUS_AGE@.dll" - VALUE "ProductName", "GLib" - VALUE "ProductVersion", "@GLIB_VERSION@" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END - END diff --git a/gio/meson.build b/gio/meson.build index 774c8f906e..8730291b37 100644 --- a/gio/meson.build +++ b/gio/meson.build @@ -474,13 +474,21 @@ else ) if glib_build_shared - gio_win_rc = configure_file( - input: 'gio.rc.in', + gio_rc_conf = configuration_data({ + 'NAME': 'GIO', + 'FILENAME': (cc.get_argument_syntax() == 'msvc' ? '' : 'lib') + 'gio-' + glib_api_version + '-' + soversion.to_string() + '.dll', + 'DESCRIPTION': 'Gio is a library providing general purpose I/O, networking, IPC, settings, and other high level application functionality', + }) + gio_rc_conf.merge_from(rc_conf_base) + lib_rc = configure_file( + input: lib_rc_in, output: 'gio.rc', - configuration: glibconfig_conf, + configuration: gio_rc_conf, ) - gio_win_res = windows.compile_resources(gio_win_rc) - win32_sources += [gio_win_res] + win32_sources += [windows.compile_resources(lib_rc, + # Workaround for https://github.com/llvm/llvm-project/issues/63426 + args: ['-c', '65001'], + )] endif gio_win32_include_headers = files( diff --git a/glib/glib.rc.in b/glib/glib.rc.in deleted file mode 100644 index c69f644011..0000000000 --- a/glib/glib.rc.in +++ /dev/null @@ -1,30 +0,0 @@ -#include - -VS_VERSION_INFO VERSIONINFO - FILEVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0 - PRODUCTVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0 - FILEFLAGSMASK 0 - FILEFLAGS 0 - FILEOS VOS__WINDOWS32 - FILETYPE VFT_DLL - FILESUBTYPE VFT2_UNKNOWN - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904B0" - BEGIN - VALUE "CompanyName", "The GLib developer community" - VALUE "FileDescription", "GLib" - VALUE "FileVersion", "@GLIB_VERSION@.0" - VALUE "InternalName", "libglib-2.0-@LT_CURRENT_MINUS_AGE@" - VALUE "LegalCopyright", "Copyright 1995-2011 Peter Mattis, Spencer Kimball, Josh MacDonald and others." - VALUE "OriginalFilename", "libglib-2.0-@LT_CURRENT_MINUS_AGE@.dll" - VALUE "ProductName", "GLib" - VALUE "ProductVersion", "@GLIB_VERSION@" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END - END diff --git a/glib/meson.build b/glib/meson.build index 19774c20cf..8b1ce5c772 100644 --- a/glib/meson.build +++ b/glib/meson.build @@ -355,14 +355,23 @@ platform_deps = [] if host_system == 'windows' if glib_build_shared - glib_win_rc = configure_file( - input: 'glib.rc.in', + glib_rc_conf = configuration_data({ + 'NAME': 'GLib', + 'FILENAME': (cc.get_argument_syntax() == 'msvc' ? '' : 'lib') + 'glib-' + glib_api_version + '-' + soversion.to_string() + '.dll', + 'DESCRIPTION': 'GLib is a general-purpose, portable utility library', + }) + glib_rc_conf.merge_from(rc_conf_base) + lib_rc = configure_file( + input: lib_rc_in, output: 'glib.rc', - configuration: glibconfig_conf, + configuration: glib_rc_conf, ) - glib_win_res = windows.compile_resources(glib_win_rc) - glib_sources += [glib_win_res] + glib_sources += [windows.compile_resources(lib_rc, + # Workaround for https://github.com/llvm/llvm-project/issues/63426 + args: ['-c', '65001'], + )] endif + glib_sources += files('gwin32.c', 'gspawn-win32.c', 'giowin32.c') platform_deps = [winsock2, cc.find_library('winmm')] if cc.get_id() == 'msvc' or cc.get_id() == 'clang-cl' diff --git a/gmodule/gmodule.rc.in b/gmodule/gmodule.rc.in deleted file mode 100644 index c3d762da90..0000000000 --- a/gmodule/gmodule.rc.in +++ /dev/null @@ -1,30 +0,0 @@ -#include - -VS_VERSION_INFO VERSIONINFO - FILEVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0 - PRODUCTVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0 - FILEFLAGSMASK 0 - FILEFLAGS 0 - FILEOS VOS__WINDOWS32 - FILETYPE VFT_DLL - FILESUBTYPE VFT2_UNKNOWN - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904B0" - BEGIN - VALUE "CompanyName", "The GLib developer community" - VALUE "FileDescription", "GModule" - VALUE "FileVersion", "@GLIB_VERSION@.0" - VALUE "InternalName", "libgmodule-2.0-@LT_CURRENT_MINUS_AGE@" - VALUE "LegalCopyright", "Copyright 1998-2011 Tim Janik and others." - VALUE "OriginalFilename", "libgmodule-2.0-@LT_CURRENT_MINUS_AGE@.dll" - VALUE "ProductName", "GLib" - VALUE "ProductVersion", "@GLIB_VERSION@" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END - END diff --git a/gmodule/meson.build b/gmodule/meson.build index 37a8fa42d5..5a226319c0 100644 --- a/gmodule/meson.build +++ b/gmodule/meson.build @@ -79,13 +79,21 @@ gmodule_visibility_h = custom_target( gmodule_sources = [gmodule_c, gmodule_visibility_h, gmodule_deprecated_c] if host_system == 'windows' and glib_build_shared - gmodule_win_rc = configure_file( - input: 'gmodule.rc.in', + gmodule_rc_conf = configuration_data({ + 'NAME': 'GModule', + 'FILENAME': (cc.get_argument_syntax() == 'msvc' ? '' : 'lib') + 'gmodule-' + glib_api_version + '-' + soversion.to_string() + '.dll', + 'DESCRIPTION': 'Portable API for dynamically loading modules', + }) + gmodule_rc_conf.merge_from(rc_conf_base) + lib_rc = configure_file( + input: lib_rc_in, output: 'gmodule.rc', - configuration: glibconfig_conf, + configuration: gmodule_rc_conf, ) - gmodule_win_res = windows.compile_resources(gmodule_win_rc) - gmodule_sources += [gmodule_win_res] + gmodule_sources += [windows.compile_resources(lib_rc, + # Workaround for https://github.com/llvm/llvm-project/issues/63426 + args: ['-c', '65001'], + )] endif libgmodule = library('gmodule-2.0', diff --git a/gobject/gobject.rc.in b/gobject/gobject.rc.in deleted file mode 100644 index 1c73e934b2..0000000000 --- a/gobject/gobject.rc.in +++ /dev/null @@ -1,30 +0,0 @@ -#include - -VS_VERSION_INFO VERSIONINFO - FILEVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0 - PRODUCTVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0 - FILEFLAGSMASK 0 - FILEFLAGS 0 - FILEOS VOS__WINDOWS32 - FILETYPE VFT_DLL - FILESUBTYPE VFT2_UNKNOWN - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904B0" - BEGIN - VALUE "CompanyName", "The GLib developer community" - VALUE "FileDescription", "GObject" - VALUE "FileVersion", "@GLIB_VERSION@.0" - VALUE "InternalName", "libgobject-2.0-@LT_CURRENT_MINUS_AGE@" - VALUE "LegalCopyright", "Copyright 1998-2011 Tim Janik, Red Hat, Inc. and others" - VALUE "OriginalFilename", "libgobject-2.0-@LT_CURRENT_MINUS_AGE@.dll" - VALUE "ProductName", "GLib" - VALUE "ProductVersion", "@GLIB_VERSION@" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END - END diff --git a/gobject/meson.build b/gobject/meson.build index 421d4e311e..54266f32c2 100644 --- a/gobject/meson.build +++ b/gobject/meson.build @@ -61,13 +61,21 @@ gobject_sources += files( ) if host_system == 'windows' and glib_build_shared - gobject_win_rc = configure_file( - input: 'gobject.rc.in', + gobject_rc_conf = configuration_data({ + 'NAME': 'GObject', + 'FILENAME': (cc.get_argument_syntax() == 'msvc' ? '' : 'lib') + 'gobject-' + glib_api_version + '-' + soversion.to_string() + '.dll', + 'DESCRIPTION': 'The base type system and object class', + }) + gobject_rc_conf.merge_from(rc_conf_base) + lib_rc = configure_file( + input: lib_rc_in, output: 'gobject.rc', - configuration: glibconfig_conf, + configuration: gobject_rc_conf, ) - gobject_win_res = windows.compile_resources(gobject_win_rc) - gobject_sources += [gobject_win_res] + gobject_sources += [windows.compile_resources(lib_rc, + # Workaround for https://github.com/llvm/llvm-project/issues/63426 + args: ['-c', '65001'], + )] endif if enable_dtrace diff --git a/gthread/gthread.rc.in b/gthread/gthread.rc.in deleted file mode 100644 index 9e45fdff95..0000000000 --- a/gthread/gthread.rc.in +++ /dev/null @@ -1,30 +0,0 @@ -#include - -VS_VERSION_INFO VERSIONINFO - FILEVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0 - PRODUCTVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0 - FILEFLAGSMASK 0 - FILEFLAGS 0 - FILEOS VOS__WINDOWS32 - FILETYPE VFT_DLL - FILESUBTYPE VFT2_UNKNOWN - BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904B0" - BEGIN - VALUE "CompanyName", "The GLib developer community" - VALUE "FileDescription", "GThread" - VALUE "FileVersion", "@GLIB_VERSION@.0" - VALUE "InternalName", "libgthread-2.0-@LT_CURRENT_MINUS_AGE@" - VALUE "LegalCopyright", "Copyright 1995-2011 Peter Mattis, Spencer Kimball, Josh MacDonald, Sebastian Wilhelmi and others." - VALUE "OriginalFilename", "libgthread-2.0-@LT_CURRENT_MINUS_AGE@.dll" - VALUE "ProductName", "GLib" - VALUE "ProductVersion", "@GLIB_VERSION@" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END - END diff --git a/gthread/meson.build b/gthread/meson.build index 9b492eed64..62fbe47fb5 100644 --- a/gthread/meson.build +++ b/gthread/meson.build @@ -3,13 +3,21 @@ gthread_sources = ['gthread-impl.c'] if host_system == 'windows' and glib_build_shared - gthread_win_rc = configure_file( - input: 'gthread.rc.in', + gthread_rc_conf = configuration_data({ + 'NAME': 'GThread', + 'FILENAME': (cc.get_argument_syntax() == 'msvc' ? '' : 'lib') + 'gthread-' + glib_api_version + '-' + soversion.to_string() + '.dll', + 'DESCRIPTION': 'Portable threading API', + }) + gthread_rc_conf.merge_from(rc_conf_base) + lib_rc = configure_file( + input: lib_rc_in, output: 'gthread.rc', - configuration: glibconfig_conf, + configuration: gthread_rc_conf, ) - gthread_win_res = windows.compile_resources(gthread_win_rc) - gthread_sources += [gthread_win_res] + gthread_sources += [windows.compile_resources(lib_rc, + # Workaround for https://github.com/llvm/llvm-project/issues/63426 + args: ['-c', '65001'], + )] endif libgthread = library('gthread-2.0', -- GitLab From e37fb2a398ad48aaa3a3d09c31860bda453fb950 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Fri, 14 Nov 2025 18:33:42 +0100 Subject: [PATCH 07/10] gtestutils: Add new G_TEST_SUBPROCESS_INHERIT_DESCRIPTORS flag --- glib/gtestutils.c | 4 +++- glib/gtestutils.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/glib/gtestutils.c b/glib/gtestutils.c index 99d6e4c4dd..5affaddecd 100644 --- a/glib/gtestutils.c +++ b/glib/gtestutils.c @@ -205,6 +205,8 @@ * process will inherit the parent's stderr. Otherwise, the child's * stderr will not be visible, but it will be captured to allow * later tests with [func@GLib.test_trap_assert_stderr]. + * @G_TEST_SUBPROCESS_INHERIT_DESCRIPTORS: If this flag is given, the + * child process will inherit the parent’s open file descriptors. * * Flags to pass to [func@GLib.test_trap_subprocess] to control input and output. * @@ -4175,7 +4177,7 @@ g_test_trap_subprocess_with_envp (const char *test_path, g_ptr_array_add (argv, NULL); flags = G_SPAWN_DO_NOT_REAP_CHILD; - if (test_log_fd != -1) + if ((test_flags & G_TEST_SUBPROCESS_INHERIT_DESCRIPTORS) || test_log_fd != -1) flags |= G_SPAWN_LEAVE_DESCRIPTORS_OPEN; if (test_flags & G_TEST_SUBPROCESS_INHERIT_STDIN) flags |= G_SPAWN_CHILD_INHERITS_STDIN; diff --git a/glib/gtestutils.h b/glib/gtestutils.h index 6dae09cf7b..ca9782ae78 100644 --- a/glib/gtestutils.h +++ b/glib/gtestutils.h @@ -540,7 +540,8 @@ typedef enum { G_TEST_SUBPROCESS_DEFAULT GLIB_AVAILABLE_ENUMERATOR_IN_2_74 = 0, G_TEST_SUBPROCESS_INHERIT_STDIN = 1 << 0, G_TEST_SUBPROCESS_INHERIT_STDOUT = 1 << 1, - G_TEST_SUBPROCESS_INHERIT_STDERR = 1 << 2 + G_TEST_SUBPROCESS_INHERIT_STDERR = 1 << 2, + G_TEST_SUBPROCESS_INHERIT_DESCRIPTORS GLIB_AVAILABLE_ENUMERATOR_IN_2_88 = 1 << 3, } G_GNUC_FLAG_ENUM GTestSubprocessFlags; GLIB_AVAILABLE_IN_2_38 -- GitLab From 2d9fc415863caab39141e30e048b5e4e7788f0d0 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Fri, 14 Nov 2025 16:08:41 +0100 Subject: [PATCH 08/10] Win32: Add test for stderr buffering mode Verify that stderr is not fully-buffered. CRTs before the Unversal CRT can open stderr in full buffering mode (depending on the output type). That's not desirable and we have a workaround in app_profile_dep. --- glib/tests/win32.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/glib/tests/win32.c b/glib/tests/win32.c index 5cef08a2e5..a8139f0714 100644 --- a/glib/tests/win32.c +++ b/glib/tests/win32.c @@ -138,6 +138,39 @@ test_clear_com (void) CoUninitialize (); } +static void +test_subprocess_stderr_buffering_mode (void) +{ + int ret = fprintf (stderr, "hello world\n"); + g_assert_cmpint (ret, >, 0); + + /* We want to exit without flushing stdio streams. We could + * use _Exit here, but the C standard doesn't specify whether + * _Exit flushes stdio streams or not. + * The Windows C RunTime library doesn't flush streams, but + * we should not rely on implementation details which may + * change in the future. Use TerminateProcess. + */ + TerminateProcess (GetCurrentProcess (), 0); +} + +static void +test_stderr_buffering_mode (void) +{ + /* MSVCRT.DLL can open stderr in full-buffering mode. + * This can cause loss of important messages before + * a crash. Additionally, POSIX disallows full buffering + * of stderr, so this is not good for portability. + * We have a workaround in the app-profile dependency + * that we add to each executable. + */ + g_test_trap_subprocess ("/win32/subprocess/stderr-buffering-mode", + 0, + G_TEST_SUBPROCESS_DEFAULT); + g_test_trap_assert_passed (); + g_test_trap_assert_stderr ("hello world?\n"); +} + int main (int argc, char *argv[]) @@ -161,5 +194,8 @@ main (int argc, g_test_add_func ("/win32/subprocess/illegal_instruction", test_illegal_instruction); g_test_add_func ("/win32/com/clear", test_clear_com); + g_test_add_func ("/win32/subprocess/stderr-buffering-mode", test_subprocess_stderr_buffering_mode); + g_test_add_func ("/win32/stderr-buffering-mode", test_stderr_buffering_mode); + return g_test_run(); } -- GitLab From 15eeef71e3b89f2a457b5ca46f59c98afdf262ba Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Fri, 14 Nov 2025 16:12:50 +0100 Subject: [PATCH 09/10] Win32: Add test for OS compatibility section in manifest XML Check that executables() get a proper manifest resource --- glib/tests/win32.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/glib/tests/win32.c b/glib/tests/win32.c index a8139f0714..b1b49f1e02 100644 --- a/glib/tests/win32.c +++ b/glib/tests/win32.c @@ -27,6 +27,7 @@ #include #include #include +#include /* for RTL_OSVERSIONINFO */ #define COBJMACROS #include @@ -171,6 +172,83 @@ test_stderr_buffering_mode (void) g_test_trap_assert_stderr ("hello world?\n"); } +#ifndef _WIN32_WINNT_WIN10 +#define _WIN32_WINNT_WIN10 0x0A00 +#endif +#ifndef STATUS_SUCCESS +#define STATUS_SUCCESS 0 +#endif + +static void +test_manifest_os_compatibility (void) +{ + const WORD highest_known_major_minor_word = _WIN32_WINNT_WIN10; + + HMODULE module_handle = LoadLibraryEx (L"NTDLL.DLL", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (module_handle == NULL) + { + g_error ("%s (%s) failed: %s", + "LoadLibraryEx", "NTDLL.DLL", + g_win32_error_message (GetLastError ())); + } + + typedef NTSTATUS (WINAPI *ptr_RtlGetVersion_t) (RTL_OSVERSIONINFOW *); + + ptr_RtlGetVersion_t ptr_RtlGetVersion = + (ptr_RtlGetVersion_t) GetProcAddress (module_handle, "RtlGetVersion"); + if (ptr_RtlGetVersion == NULL) + { + g_error ("%s (%s, %s) failed: %s", + "GetProcAddress", "NTDLL.DLL", "RtlGetVersion", + g_win32_error_message (GetLastError ())); + } + + /* RtlGetVersion is not subject to compatibility settings + * present in the activation context, it always returns + * the real OS version. + */ + RTL_OSVERSIONINFOW rtl_os_version_info; + rtl_os_version_info.dwOSVersionInfoSize = sizeof (rtl_os_version_info); + + NTSTATUS status = ptr_RtlGetVersion (&rtl_os_version_info); + if (status != STATUS_SUCCESS) + { + g_error ("%s failed", + "RtlGetVersion"); + } + + /* Now verify if the activation context contains up-to-date + * compatibility info. + */ + OSVERSIONINFO os_version_info; + os_version_info.dwOSVersionInfoSize = sizeof (os_version_info); + + BOOL success = GetVersionEx (&os_version_info); + if (!success) + { + g_error ("%s failed: %s", + "GetVersionEx", + g_win32_error_message (GetLastError ())); + } + + if (rtl_os_version_info.dwMajorVersion != os_version_info.dwMajorVersion || + rtl_os_version_info.dwMinorVersion != os_version_info.dwMinorVersion || + rtl_os_version_info.dwBuildNumber != os_version_info.dwBuildNumber) + { + WORD rtl_major_minor_word = MAKEWORD ((BYTE)rtl_os_version_info.dwMinorVersion, + (BYTE)rtl_os_version_info.dwMajorVersion); + + if (rtl_major_minor_word > highest_known_major_minor_word) + g_error ("Please, update the manifest XML and the test's constant"); + + g_assert_cmpuint (rtl_os_version_info.dwMajorVersion, ==, os_version_info.dwMajorVersion); + g_assert_cmpuint (rtl_os_version_info.dwMinorVersion, ==, os_version_info.dwMinorVersion); + g_assert_cmpuint (rtl_os_version_info.dwBuildNumber, ==, os_version_info.dwBuildNumber); + } + + FreeLibrary (module_handle); +} + int main (int argc, char *argv[]) @@ -196,6 +274,7 @@ main (int argc, g_test_add_func ("/win32/subprocess/stderr-buffering-mode", test_subprocess_stderr_buffering_mode); g_test_add_func ("/win32/stderr-buffering-mode", test_stderr_buffering_mode); + g_test_add_func ("/win32/manifest-os-compatibility", test_manifest_os_compatibility); return g_test_run(); } -- GitLab From e1a7b54f8b0e24e0911b2f919cb5f9aacdde1df0 Mon Sep 17 00:00:00 2001 From: Luca Bacci Date: Fri, 14 Nov 2025 16:16:58 +0100 Subject: [PATCH 10/10] Win32: Add test for safe stdio flushing Test the two-phase flushing implemented in app_profile_dep. --- glib/tests/win32.c | 159 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/glib/tests/win32.c b/glib/tests/win32.c index b1b49f1e02..760bf77d59 100644 --- a/glib/tests/win32.c +++ b/glib/tests/win32.c @@ -25,7 +25,13 @@ #include #include +#include + #include +#include +#include +#include +#include #include #include /* for RTL_OSVERSIONINFO */ @@ -249,6 +255,157 @@ test_manifest_os_compatibility (void) FreeLibrary (module_handle); } +typedef struct { + FILE *stream; + bool done; + GMutex mutex; + GCond cond; +} StreamLockData_t; + +static gpointer +thread_acquire_stdio_stream_lock (gpointer user_data) +{ + StreamLockData_t *data = (StreamLockData_t *) user_data; + FILE *stream = data->stream; + + /* For the test to be effective, this thread must be holding + * the stream lock when the subprocess "main function" (i.e. + * test_subprocess_early_flush) returns. + * + * This simulates a thread doing I/O on a stream at the time + * exit invokes ExitProcess. Note that _lock_file is exactly + * what stdio functions call internally. + */ + + _lock_file (stream); + + g_mutex_lock (&data->mutex); + data->done = true; + g_cond_signal (&data->cond); + g_mutex_unlock (&data->mutex); + data = NULL; + + /* Sleep a bit to let the main thread exit. Note: if somehow + * the main thread doesn't exit in time, this test will + * succeed but won't actually verify anything. + */ + g_usleep (1 * G_USEC_PER_SEC); + + _unlock_file (stream); + + return NULL; +} + +static void +test_subprocess_early_stdio_flush (void) +{ + int64_t fd; + FILE *stream; + int ret; + + fd = g_ascii_strtoull (g_getenv ("G_TEST_PIPE_FD"), NULL, 10); + g_assert_cmpuint (fd, >=, 0); + g_assert_cmpuint (fd, <=, INT_MAX); + + stream = _fdopen ((int) fd, "w"); + g_assert_nonnull (stream); + + ret = setvbuf (stream, NULL, _IOFBF, 1024); + g_assert_cmpint (ret, ==, 0); + + ret = fprintf (stream, "hello world"); + g_assert_cmpint (ret, ==, (int) strlen ("hello world")); + g_assert_false (ferror (stream)); + + StreamLockData_t data; + memset (&data, 0, sizeof (data)); + data.stream = stream; + data.done = false; + + GThread *thread = g_thread_new ("lock stdio stream", + thread_acquire_stdio_stream_lock, + (gpointer) &data); + g_thread_unref (thread); + + /* Wait until the worker thread has acquired the stream lock. + */ + g_mutex_lock (&data.mutex); + while (!data.done) + g_cond_wait (&data.cond, &data.mutex); + g_mutex_unlock (&data.mutex); + + /* If the early flush is not implemented, the C RunTime will attempt + * to acquire the stream's internal CRITICAL_SECTION from DllMain. + * Then the process will be terminated abruptly (with the same exit + * code passed to exit) leaving stream unflushed. + */ +} + +static gpointer +thread_read_pipe (gpointer user_data) +{ + int fd = GPOINTER_TO_INT (user_data); + const char *iter = "hello world"; + int size = (int) strlen (iter); + + while (size > 0) + { + char buffer[20]; + int ret = _read (fd, buffer, sizeof (buffer)); + + g_assert_cmpint (ret, >, 0); + g_assert_cmpint (ret, <=, size); + + g_assert_cmpmem (buffer, ret, iter, ret); + + iter += ret; + size -= ret; + } + + return NULL; +} + +static void +test_early_stdio_flush (void) +{ + int pipe_fds[2]; + int pipe_read; + int pipe_write; + char buffer[10]; + GStrv envp = NULL; + GError *error = NULL; + int ret; + + ret = _pipe (pipe_fds, 1024, _O_BINARY); + if (ret < 0) + g_error ("%s failed: %s", "_pipe", g_strerror (errno)); + + pipe_read = pipe_fds[0]; + pipe_write = pipe_fds[1]; + + g_snprintf (buffer, sizeof (buffer), "%i", pipe_write); + envp = g_get_environ (); + envp = g_environ_setenv (g_steal_pointer (&envp), "G_TEST_PIPE_FD", buffer, TRUE); + + GThread *thread = g_thread_new ("read pipe", + thread_read_pipe, + GINT_TO_POINTER (pipe_read)); + + typedef const char * const * GStrvConst_t; + g_test_trap_subprocess_with_envp ("/win32/subprocess/early-stdio-flush", + (GStrvConst_t) envp, 0, + G_TEST_SUBPROCESS_INHERIT_DESCRIPTORS); + g_test_trap_assert_passed (); + + g_close (pipe_write, &error); + g_assert_no_error (error); + + g_thread_join (thread); + + g_close (pipe_read, NULL); + g_strfreev (envp); +} + int main (int argc, char *argv[]) @@ -275,6 +432,8 @@ main (int argc, g_test_add_func ("/win32/subprocess/stderr-buffering-mode", test_subprocess_stderr_buffering_mode); g_test_add_func ("/win32/stderr-buffering-mode", test_stderr_buffering_mode); g_test_add_func ("/win32/manifest-os-compatibility", test_manifest_os_compatibility); + g_test_add_func ("/win32/subprocess/early-stdio-flush", test_subprocess_early_stdio_flush); + g_test_add_func ("/win32/early-stdio-flush", test_early_stdio_flush); return g_test_run(); } -- GitLab