From 68958fd4112bacc209c37137332fb7cbbd056f21 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 23 Apr 2026 23:37:12 +0300 Subject: [PATCH] pref: fix Bananas compile time with termux (fixes #22671) --- README.md | 4 +- vlib/v/pref/default.v | 26 ++++-- vlib/v/pref/default_tcc_compiler_test.v | 100 ++++++++++++++++++++++-- 3 files changed, 119 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 2ff713572..71f1e4ed3 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ On Termux, V needs some packages preinstalled - a working C compiler, also `libe Linux/macos: ```bash -pkg install clang libexecinfo libgc libgc-static make git +pkg install clang libexecinfo libgc libgc-static tcc make git pkg update git clone --depth=1 https://github.com/vlang/v cd v @@ -196,6 +196,8 @@ make ./v symlink ``` Note: there is *no* need for `sudo ./v symlink` on Termux (and sudo is not installed by default). +For faster development builds, keep the Termux `tcc` package installed; V will use it by default +when no compatible bundled `thirdparty/tcc` binary is available on the host. ### C compiler diff --git a/vlib/v/pref/default.v b/vlib/v/pref/default.v index d7608db0c..57db5e087 100644 --- a/vlib/v/pref/default.v +++ b/vlib/v/pref/default.v @@ -411,9 +411,9 @@ fn (mut p Preferences) find_cc_if_cross_compiling() { } fn (mut p Preferences) try_to_use_tcc_by_default() { - bundled_tcc := usable_bundled_tcc_compiler(os.dir(vexe_path())) + preferred_tcc := default_tcc_compiler() if p.ccompiler == 'tcc' { - p.ccompiler = if bundled_tcc != '' { bundled_tcc } else { 'tcc' } + p.ccompiler = if preferred_tcc != '' { preferred_tcc } else { 'tcc' } return } if p.ccompiler == '' { @@ -426,11 +426,23 @@ fn (mut p Preferences) try_to_use_tcc_by_default() { if p.is_prod { return } - p.ccompiler = bundled_tcc + p.ccompiler = preferred_tcc return } } +fn usable_system_tcc_compiler() string { + if get_host_os() != .termux { + return '' + } + system_tcc := os.find_abs_path_of_executable('tcc') or { return '' } + tcc_probe := os.execute('${os.quoted_path(system_tcc)} -v') + if tcc_probe.exit_code != 0 { + return '' + } + return system_tcc +} + fn usable_bundled_tcc_compiler(vroot string) string { vtccexe := os.join_path(vroot, 'thirdparty', 'tcc', 'tcc.exe') if !os.is_file(vtccexe) || !os.is_executable(vtccexe) { @@ -444,11 +456,15 @@ fn usable_bundled_tcc_compiler(vroot string) string { return vtccexe } -// default_tcc_compiler returns the bundled TinyCC path when it exists and works on the host. +// default_tcc_compiler returns the preferred TinyCC path when it exists and works on the host. pub fn default_tcc_compiler() string { vexe := vexe_path() vroot := os.dir(vexe) - return usable_bundled_tcc_compiler(vroot) + bundled_tcc := usable_bundled_tcc_compiler(vroot) + if bundled_tcc != '' { + return bundled_tcc + } + return usable_system_tcc_compiler() } pub fn (mut p Preferences) default_c_compiler() { diff --git a/vlib/v/pref/default_tcc_compiler_test.v b/vlib/v/pref/default_tcc_compiler_test.v index fa95b1e7f..0ae1181d3 100644 --- a/vlib/v/pref/default_tcc_compiler_test.v +++ b/vlib/v/pref/default_tcc_compiler_test.v @@ -110,11 +110,101 @@ fn test_try_to_use_tcc_by_default_skips_broken_bundled_tcc_off_musl() { assert prefs.ccompiler == '' } +fn test_usable_system_tcc_compiler_prefers_termux_tcc_from_path() { + $if windows { + return + } + test_root := os.join_path(os.vtmp_dir(), 'v_pref_default_tcc_compiler_test') + system_tcc := prepare_test_executable(test_root, 'bin/tcc', 'exit 0') + old_path := os.getenv('PATH') + old_termux_version := os.getenv('TERMUX_VERSION') + os.setenv('PATH', os.dir(system_tcc), true) + os.setenv('TERMUX_VERSION', '0.118.0', true) + defer { + os.setenv('PATH', old_path, true) + if old_termux_version == '' { + os.unsetenv('TERMUX_VERSION') + } else { + os.setenv('TERMUX_VERSION', old_termux_version, true) + } + os.rmdir_all(test_root) or {} + } + assert usable_system_tcc_compiler() == system_tcc +} + +fn test_default_tcc_compiler_uses_system_tcc_on_termux_when_bundled_is_missing() { + $if windows { + return + } + test_root := os.join_path(os.vtmp_dir(), 'v_pref_default_tcc_compiler_test') + fake_vexe := os.join_path(test_root, 'v') + system_tcc := prepare_test_executable(test_root, 'bin/tcc', 'exit 0') + old_vexe := os.getenv('VEXE') + old_path := os.getenv('PATH') + old_termux_version := os.getenv('TERMUX_VERSION') + os.setenv('VEXE', fake_vexe, true) + os.setenv('PATH', os.dir(system_tcc), true) + os.setenv('TERMUX_VERSION', '0.118.0', true) + defer { + if old_vexe == '' { + os.unsetenv('VEXE') + } else { + os.setenv('VEXE', old_vexe, true) + } + os.setenv('PATH', old_path, true) + if old_termux_version == '' { + os.unsetenv('TERMUX_VERSION') + } else { + os.setenv('TERMUX_VERSION', old_termux_version, true) + } + os.rmdir_all(test_root) or {} + } + assert default_tcc_compiler() == system_tcc +} + +fn test_try_to_use_tcc_by_default_resolves_explicit_tcc_to_system_tcc_on_termux() { + $if windows { + return + } + test_root := os.join_path(os.vtmp_dir(), 'v_pref_default_tcc_compiler_test') + fake_vexe := os.join_path(test_root, 'v') + system_tcc := prepare_test_executable(test_root, 'bin/tcc', 'exit 0') + old_vexe := os.getenv('VEXE') + old_path := os.getenv('PATH') + old_termux_version := os.getenv('TERMUX_VERSION') + os.setenv('VEXE', fake_vexe, true) + os.setenv('PATH', os.dir(system_tcc), true) + os.setenv('TERMUX_VERSION', '0.118.0', true) + defer { + if old_vexe == '' { + os.unsetenv('VEXE') + } else { + os.setenv('VEXE', old_vexe, true) + } + os.setenv('PATH', old_path, true) + if old_termux_version == '' { + os.unsetenv('TERMUX_VERSION') + } else { + os.setenv('TERMUX_VERSION', old_termux_version, true) + } + os.rmdir_all(test_root) or {} + } + mut prefs := Preferences{ + ccompiler: 'tcc' + } + prefs.try_to_use_tcc_by_default() + assert prefs.ccompiler == system_tcc +} + +fn prepare_test_executable(test_root string, relative_path string, exit_line string) string { + path := os.join_path(test_root, relative_path) + os.mkdir_all(os.dir(path)) or { panic(err) } + os.write_file(path, '#!/bin/sh\n${exit_line}\n') or { panic(err) } + os.chmod(path, 0o700) or { panic(err) } + return path +} + fn prepare_test_tcc_binary(test_root string, exit_line string) string { os.rmdir_all(test_root) or {} - tcc_path := os.join_path(test_root, 'thirdparty', 'tcc', 'tcc.exe') - os.mkdir_all(os.dir(tcc_path)) or { panic(err) } - os.write_file(tcc_path, '#!/bin/sh\n${exit_line}\n') or { panic(err) } - os.chmod(tcc_path, 0o700) or { panic(err) } - return tcc_path + return prepare_test_executable(test_root, 'thirdparty/tcc/tcc.exe', exit_line) } -- 2.39.5