From 83dd2f394b8b0d47a6b107e6d89ef41c16a24fc0 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 21 Apr 2026 06:28:46 +0300 Subject: [PATCH] builder: fix hot code reload with graphics not working on windows 10 (fixes #3054) --- vlib/sokol/c/declaration.c.v | 13 +++++--- vlib/v/builder/cc.v | 20 ++++++++++++ vlib/v/builder/cc_test.v | 31 +++++++++++++++++++ .../v/live/live_macos_objc_duplication_test.v | 17 ++++++++++ 4 files changed, 77 insertions(+), 4 deletions(-) diff --git a/vlib/sokol/c/declaration.c.v b/vlib/sokol/c/declaration.c.v index d9dea950d..c08c1a26b 100644 --- a/vlib/sokol/c/declaration.c.v +++ b/vlib/sokol/c/declaration.c.v @@ -120,8 +120,13 @@ $if !no_sokol_app ? { // The live-reload dylib should reuse the host app's sokol_app symbols on macOS. #define SOKOL_APP_IMPL } - } $else $if windows && msvc && sharedlive ? { - // The live-reload DLL should call the host executable's sokol_app symbols. + } $else $if windows { + $if sharedlive ? { + } $else { + // On Windows, the live-reload DLL links back to the host executable's + // import library, so it should not embed its own sokol_app backend. + #define SOKOL_APP_IMPL + } } $else { #define SOKOL_APP_IMPL } @@ -130,7 +135,7 @@ $if !no_sokol_app ? { #include "sokol_app.h" } -$if windows && msvc && sharedlive ? { +$if windows && sharedlive ? { } $else { @[use_once] #define SOKOL_GFX_IMPL @@ -138,7 +143,7 @@ $if windows && msvc && sharedlive ? { #define SOKOL_NO_DEPRECATED #include "sokol_gfx.h" -$if windows && msvc && sharedlive ? { +$if windows && sharedlive ? { } $else { @[use_once] #define SOKOL_IMPL diff --git a/vlib/v/builder/cc.v b/vlib/v/builder/cc.v index a4fd68b3f..2dc8f6711 100644 --- a/vlib/v/builder/cc.v +++ b/vlib/v/builder/cc.v @@ -3,6 +3,7 @@ // that can be found in the LICENSE file. module builder +import hash.fnv1a import os import v.ast import v.cflag @@ -27,6 +28,13 @@ const missing_libatomic_markers = [ 'cannot find libatomic', ]! +fn live_windows_import_lib_path(source_path string) string { + cache_dir := os.join_path(os.cache_dir(), 'v', 'live') + os.mkdir_all(cache_dir) or {} + key := fnv1a.sum64_string(os.real_path(source_path)).str() + return os.join_path(cache_dir, 'host_symbols_${key}.a') +} + fn extract_c_struct_name(line string) string { start := line.index('struct ') or { return '' } + 'struct '.len mut end := start @@ -780,6 +788,18 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) { ccoptions.args << 'dynamic_lookup' } } + if v.pref.os == .windows && ccoptions.cc != .msvc { + host_import_lib := v.tcc_quoted_path(live_windows_import_lib_path(v.pref.path)) + if v.pref.is_livemain { + // Re-export host graphics/backend symbols so the live-reload DLL can reuse them. + ccoptions.linker_flags << '-Wl,--export-all-symbols' + ccoptions.linker_flags << '-Wl,--out-implib,${host_import_lib}' + } + if v.pref.is_liveshared { + // Link the live-reload DLL against the host executable's import library. + ccoptions.linker_flags << host_import_lib + } + } } // macOS code can include objective C TODO remove once objective C is replaced with C diff --git a/vlib/v/builder/cc_test.v b/vlib/v/builder/cc_test.v index b50a86995..6d2a5c0c8 100644 --- a/vlib/v/builder/cc_test.v +++ b/vlib/v/builder/cc_test.v @@ -287,6 +287,33 @@ fn test_thirdparty_cross_compile_config_for_freebsd_matches_target() { ] } +fn test_live_windows_main_linker_args_export_host_symbols() { + linker_args := builder_linker_args([ + '-os', + 'windows', + '-cc', + 'gcc', + '-live', + hot_reload_graph_example(), + ]) + assert linker_args.contains('-Wl,--export-all-symbols') + assert linker_args.contains('-Wl,--out-implib,') + assert linker_args.contains(live_windows_import_lib_path(hot_reload_graph_example())) +} + +fn test_live_windows_shared_linker_args_include_host_import_lib() { + linker_args := builder_linker_args([ + '-os', + 'windows', + '-cc', + 'gcc', + '-sharedlive', + '-shared', + hot_reload_graph_example(), + ]) + assert linker_args.contains(live_windows_import_lib_path(hot_reload_graph_example())) +} + fn test_should_use_rsp_for_linux_by_default() { builder := new_test_builder([hello_world_example()]) assert builder.should_use_rsp(['-o', builder.out_name_c]) @@ -363,6 +390,10 @@ fn hello_world_example() string { return os.join_path(@VEXEROOT, 'examples', 'hello_world.v') } +fn hot_reload_graph_example() string { + return os.join_path(@VEXEROOT, 'examples', 'hot_reload', 'graph.v') +} + fn test_c_output_suggests_missing_typedef_for_c_struct_with_issue_19050_output() { c_output := [ "/tmp/v_501/c_struct.6580681062929530137.tmp.c:12966:17: error: incomplete result type 'struct string_c' in function definition", diff --git a/vlib/v/live/live_macos_objc_duplication_test.v b/vlib/v/live/live_macos_objc_duplication_test.v index 8917478c0..cad3a836b 100644 --- a/vlib/v/live/live_macos_objc_duplication_test.v +++ b/vlib/v/live/live_macos_objc_duplication_test.v @@ -1,5 +1,22 @@ import os +fn test_sharedlive_windows_cgen_reuses_host_sokol_backend() { + tmp_dir := os.join_path(os.vtmp_dir(), 'live_windows_shared_graphics_cgen') + os.mkdir_all(tmp_dir)! + defer { + os.rmdir_all(tmp_dir) or {} + } + c_path := os.join_path(tmp_dir, 'graph_sharedlive_windows.c') + graph_path := os.join_path(@VEXEROOT, 'examples', 'hot_reload', 'graph.v') + build_cmd := '${os.quoted_path(@VEXE)} -nocolor -os windows -sharedlive -o ${os.quoted_path(c_path)} ${os.quoted_path(graph_path)}' + build_res := os.execute(build_cmd) + assert build_res.exit_code == 0 + c_source := os.read_file(c_path)! + assert !c_source.contains('#define SOKOL_APP_IMPL') + assert !c_source.contains('#define SOKOL_GFX_IMPL') + assert !c_source.contains('#define SOKOL_IMPL') +} + fn test_sharedlive_macos_does_not_export_sokol_objc_classes() { $if !macos { return -- 2.39.5