From 2a66c6aa63251e941ccd415a511821c15fae6bb1 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 21 Apr 2026 17:11:37 +0300 Subject: [PATCH] builder: fix cannot cross compile to freebsd while windows works (fixes #22333) --- vlib/v/builder/cc.v | 80 +++++++++++++++++++++++++++---------- vlib/v/builder/cc_test.v | 36 +++++++++++++++++ vlib/v/vcache/vcache.v | 1 + vlib/v/vcache/vcache_test.v | 8 ++++ 4 files changed, 105 insertions(+), 20 deletions(-) diff --git a/vlib/v/builder/cc.v b/vlib/v/builder/cc.v index 8998b7a5c..5184a1bf6 100644 --- a/vlib/v/builder/cc.v +++ b/vlib/v/builder/cc.v @@ -808,6 +808,13 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) { if v.pref.os == .windows { ccoptions.post_args << v.get_subsystem_flag() } + ccoptions.env_cflags = os.getenv('CFLAGS').replace('\n', ' ') + ccoptions.env_ldflags = os.getenv('LDFLAGS').replace('\n', ' ') + // Set the cache salt before resolving cached thirdparty object paths, + // so object building and final compilation agree on the same cache entry. + v.pref.cache_manager.set_temporary_options(v.thirdparty_object_args(ccoptions, [ + ccoptions.guessed_compiler, + ], false)) cflags := v.get_os_cflags() if v.pref.build_mode != .build_module { @@ -839,8 +846,6 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) { ccoptions.linker_flags << '-lelf' } } - ccoptions.env_cflags = os.getenv('CFLAGS').replace('\n', ' ') - ccoptions.env_ldflags = os.getenv('LDFLAGS').replace('\n', ' ') if v.pref.os == .macos { if v.pref.use_cache { ccoptions.source_args << '-x none' @@ -873,10 +878,6 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) { println('>>> setup_ccompiler_options ccoptions: ${ccoptions}') } v.ccoptions = ccoptions - // setup the cache too, so that different compilers/options do not interfere: - v.pref.cache_manager.set_temporary_options(v.thirdparty_object_args(v.ccoptions, [ - ccoptions.guessed_compiler, - ], false)) } fn (v &Builder) all_args(ccoptions CcompilerOptions) []string { @@ -937,6 +938,53 @@ fn (v &Builder) only_linker_args(ccoptions CcompilerOptions) []string { return all } +struct ThirdpartyCrossCompileConfig { + target_args []string + trailing_include_args []string + sysroot string +} + +fn (v &Builder) thirdparty_cross_compile_config() ThirdpartyCrossCompileConfig { + if v.pref.os == .linux && current_os != 'linux' { + sysroot := os.join_path(os.vmodules_dir(), 'linuxroot') + return ThirdpartyCrossCompileConfig{ + target_args: ['-target x86_64-linux-gnu'] + trailing_include_args: [ + '-I', + os.quoted_path('${sysroot}/include'), + ] + sysroot: sysroot + } + } + if v.pref.os == .freebsd && current_os != 'freebsd' { + sysroot := os.join_path(os.vmodules_dir(), 'freebsdroot') + return ThirdpartyCrossCompileConfig{ + target_args: ['-target x86_64-unknown-freebsd14.0'] + trailing_include_args: [ + '-I', + os.quoted_path('${sysroot}/include'), + '-I', + os.quoted_path('${sysroot}/usr/include'), + ] + sysroot: sysroot + } + } + return ThirdpartyCrossCompileConfig{} +} + +fn (mut v Builder) ensure_thirdparty_cross_compile_sysroot(cfg ThirdpartyCrossCompileConfig) { + if cfg.sysroot == '' { + return + } + if v.pref.os == .linux { + v.ensure_linuxroot_exists(cfg.sysroot) + return + } + if v.pref.os == .freebsd { + v.ensure_freebsdroot_exists(cfg.sysroot) + } +} + fn (mut v Builder) thirdparty_object_args(ccoptions CcompilerOptions, middle []string, cpp_file bool) []string { mut all := []string{} @@ -949,17 +997,10 @@ fn (mut v Builder) thirdparty_object_args(ccoptions CcompilerOptions, middle []s all << '-D_DEFAULT_SOURCE' } - sysroot := os.join_path(os.vmodules_dir(), 'linuxroot') - mut cross_compiling_from_macos_to_linux := false - if v.pref.os == .linux && v.pref.arch == .amd64 { - $if macos { - cross_compiling_from_macos_to_linux = true - } - } - - if cross_compiling_from_macos_to_linux { - v.ensure_linuxroot_exists(sysroot) - all << '-target x86_64-linux-gnu' + cross_cfg := v.thirdparty_cross_compile_config() + if cross_cfg.sysroot != '' { + v.ensure_thirdparty_cross_compile_sysroot(cross_cfg) + all << cross_cfg.target_args } all << ccoptions.env_cflags @@ -969,12 +1010,11 @@ fn (mut v Builder) thirdparty_object_args(ccoptions CcompilerOptions, middle []s // compilers are inconsistent about how they handle: // all << ccoptions.env_ldflags // all << ccoptions.ldflags - if cross_compiling_from_macos_to_linux { + if cross_cfg.sysroot != '' { // add the system include/ folder after everything else, // so that local folders like thirdparty/mbedtls have a // chance to supply their own headers - all << '-I' - all << os.quoted_path('${sysroot}/include') + all << cross_cfg.trailing_include_args } return all } diff --git a/vlib/v/builder/cc_test.v b/vlib/v/builder/cc_test.v index abd136a5d..762bb8c9e 100644 --- a/vlib/v/builder/cc_test.v +++ b/vlib/v/builder/cc_test.v @@ -217,6 +217,42 @@ fn test_live_termux_linker_args_include_rdynamic_without_debug() { assert linker_args.contains('-rdynamic') } +fn test_thirdparty_cross_compile_config_for_linux_matches_target() { + builder := new_builder_for_args(['-os', 'linux', hello_world_example()]) + cfg := builder.thirdparty_cross_compile_config() + if current_os == 'linux' { + assert cfg.sysroot == '' + assert cfg.target_args == []string{} + assert cfg.trailing_include_args == []string{} + return + } + assert cfg.target_args == ['-target x86_64-linux-gnu'] + assert cfg.sysroot.ends_with('/linuxroot') + assert cfg.trailing_include_args == [ + '-I', + os.quoted_path('${cfg.sysroot}/include'), + ] +} + +fn test_thirdparty_cross_compile_config_for_freebsd_matches_target() { + builder := new_builder_for_args(['-os', 'freebsd', hello_world_example()]) + cfg := builder.thirdparty_cross_compile_config() + if current_os == 'freebsd' { + assert cfg.sysroot == '' + assert cfg.target_args == []string{} + assert cfg.trailing_include_args == []string{} + return + } + assert cfg.target_args == ['-target x86_64-unknown-freebsd14.0'] + assert cfg.sysroot.ends_with('/freebsdroot') + assert cfg.trailing_include_args == [ + '-I', + os.quoted_path('${cfg.sysroot}/include'), + '-I', + os.quoted_path('${cfg.sysroot}/usr/include'), + ] +} + 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]) diff --git a/vlib/v/vcache/vcache.v b/vlib/v/vcache/vcache.v index 144d2a8c0..d80524afe 100644 --- a/vlib/v/vcache/vcache.v +++ b/vlib/v/vcache/vcache.v @@ -87,6 +87,7 @@ pub fn new_cache_manager(opts []string) CacheManager { // without affecting the .original_vopts pub fn (mut cm CacheManager) set_temporary_options(new_opts []string) { cm.vopts = cm.original_vopts + '#' + new_opts.join('|') + cm.k2cpath = map[string]string{} dlog(@FN, 'cm.vopts:\n ${cm.vopts}') } diff --git a/vlib/v/vcache/vcache_test.v b/vlib/v/vcache/vcache_test.v index 2b8657cde..55ffb192c 100644 --- a/vlib/v/vcache/vcache_test.v +++ b/vlib/v/vcache/vcache_test.v @@ -80,6 +80,14 @@ fn test_exists() { assert y != z } +fn test_temporary_options_reset_cached_key_paths() { + mut cm := vcache.new_cache_manager(['-os freebsd']) + path_before := cm.mod_postfix_with_key2cpath('builtin', '.o', '/tmp/gc.o') + cm.set_temporary_options(['-target x86_64-unknown-freebsd14.0']) + path_after := cm.mod_postfix_with_key2cpath('builtin', '.o', '/tmp/gc.o') + assert path_before != path_after +} + fn test_readme_exists_and_is_readable() { vcache.new_cache_manager([]) freadme := os.join_path(vcache_folder, 'README.md') -- 2.39.5