Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,245 @@
|
||||
// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module pref
|
||||
|
||||
import os
|
||||
|
||||
fn is_target_or_mode_comptime_flag(name string) bool {
|
||||
return name in [
|
||||
'macos',
|
||||
'darwin',
|
||||
'mac',
|
||||
'linux',
|
||||
'windows',
|
||||
'bsd',
|
||||
'freebsd',
|
||||
'openbsd',
|
||||
'netbsd',
|
||||
'dragonfly',
|
||||
'android',
|
||||
'termux',
|
||||
'ios',
|
||||
'solaris',
|
||||
'qnx',
|
||||
'serenity',
|
||||
'plan9',
|
||||
'vinix',
|
||||
'cross',
|
||||
'none',
|
||||
'freestanding',
|
||||
'bare',
|
||||
]
|
||||
}
|
||||
|
||||
pub fn define_list_contains(defines []string, name string) bool {
|
||||
lower_name := name.to_lower()
|
||||
return name in defines || lower_name in defines
|
||||
}
|
||||
|
||||
// pkgconfig_result runs pkg-config with `args` and returns stdout on success.
|
||||
pub fn pkgconfig_result(args []string) ?string {
|
||||
if args.len == 0 {
|
||||
return none
|
||||
}
|
||||
mut quoted_args := []string{cap: args.len}
|
||||
for arg in args {
|
||||
quoted_args << os.quoted_path(arg)
|
||||
}
|
||||
result := os.execute('pkg-config ${quoted_args.join(' ')}')
|
||||
if result.exit_code != 0 {
|
||||
return none
|
||||
}
|
||||
return result.output.trim_space()
|
||||
}
|
||||
|
||||
// comptime_pkgconfig_value evaluates `$pkgconfig('name')` in compile-time
|
||||
// conditions. Missing pkg-config packages are false, matching v1 behavior.
|
||||
pub fn comptime_pkgconfig_value(name string) bool {
|
||||
if _ := pkgconfig_result(['--exists', name]) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// comptime_flag_value evaluates a plain comptime flag identifier, as it would
|
||||
// appear in `$if name {` or `@[if name]`. Use comptime_optional_flag_value for
|
||||
// the optional `name ?` form. Shared between the parser (struct field
|
||||
// conditionals) and the transformer (statement / expression $if).
|
||||
//
|
||||
// `pref` may be `nil` for early uses (some tests construct partial state);
|
||||
// flags that depend on backend / user_defines then evaluate to `false`.
|
||||
pub fn comptime_flag_value(pref &Preferences, name string) bool {
|
||||
match name {
|
||||
'macos', 'darwin', 'mac' {
|
||||
return pref.normalized_target_os() == 'macos'
|
||||
}
|
||||
'linux' {
|
||||
return pref.normalized_target_os() == 'linux'
|
||||
}
|
||||
'windows' {
|
||||
return pref.normalized_target_os() == 'windows'
|
||||
}
|
||||
'bsd' {
|
||||
return pref.normalized_target_os() in [
|
||||
'macos',
|
||||
'freebsd',
|
||||
'openbsd',
|
||||
'netbsd',
|
||||
'dragonfly',
|
||||
]
|
||||
}
|
||||
'freebsd' {
|
||||
return pref.normalized_target_os() == 'freebsd'
|
||||
}
|
||||
'openbsd' {
|
||||
return pref.normalized_target_os() == 'openbsd'
|
||||
}
|
||||
'netbsd' {
|
||||
return pref.normalized_target_os() == 'netbsd'
|
||||
}
|
||||
'dragonfly' {
|
||||
return pref.normalized_target_os() == 'dragonfly'
|
||||
}
|
||||
'android' {
|
||||
return pref.normalized_target_os() == 'android'
|
||||
}
|
||||
'termux' {
|
||||
return pref.normalized_target_os() == 'termux'
|
||||
}
|
||||
'ios' {
|
||||
return pref.normalized_target_os() == 'ios'
|
||||
}
|
||||
'solaris' {
|
||||
return pref.normalized_target_os() == 'solaris'
|
||||
}
|
||||
'qnx' {
|
||||
return pref.normalized_target_os() == 'qnx'
|
||||
}
|
||||
'serenity' {
|
||||
return pref.normalized_target_os() == 'serenity'
|
||||
}
|
||||
'plan9' {
|
||||
return pref.normalized_target_os() == 'plan9'
|
||||
}
|
||||
'vinix' {
|
||||
return pref.normalized_target_os() == 'vinix'
|
||||
}
|
||||
'x64', 'amd64' {
|
||||
$if amd64 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
'arm64', 'aarch64' {
|
||||
$if arm64 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
'little_endian' {
|
||||
$if little_endian {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
'big_endian' {
|
||||
$if big_endian {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
'debug' {
|
||||
$if debug {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
'native' {
|
||||
return pref != unsafe { nil } && (pref.backend == .arm64 || pref.backend == .x64)
|
||||
}
|
||||
'v2_native_windows_pe_minimal' {
|
||||
return pref != unsafe { nil } && pref.backend == .x64
|
||||
&& pref.get_effective_arch() == .x64 && pref.normalized_target_os() == 'windows'
|
||||
}
|
||||
// Native backend cannot resolve C.stdout/C.stderr data symbols through GOT,
|
||||
// so use C.write() instead of fwrite() for I/O operations.
|
||||
'builtin_write_buf_to_fd_should_use_c_write' {
|
||||
return pref != unsafe { nil } && (pref.backend == .arm64 || pref.backend == .x64)
|
||||
}
|
||||
'tinyc' {
|
||||
// For native backends, inline assembly from V source is not supported
|
||||
// by the SSA builder. Pretend we're TinyCC so that `$if arm64 && !tinyc`
|
||||
// guards select the software fallback path instead of inline asm.
|
||||
return pref != unsafe { nil } && (pref.backend == .arm64 || pref.backend == .x64)
|
||||
}
|
||||
'no_backtrace' {
|
||||
return pref != unsafe { nil } && ((pref.backend == .arm64 || pref.backend == .x64)
|
||||
|| name in pref.user_defines)
|
||||
}
|
||||
'prealloc' {
|
||||
return pref != unsafe { nil } && pref.prealloc
|
||||
}
|
||||
'cross' {
|
||||
return pref != unsafe { nil } && (pref.is_cross_target() || name in pref.user_defines)
|
||||
}
|
||||
'none' {
|
||||
return pref != unsafe { nil } && pref.normalized_target_os() == 'none'
|
||||
}
|
||||
'freestanding' {
|
||||
return pref != unsafe { nil } && (pref.is_freestanding() || name in pref.user_defines)
|
||||
}
|
||||
'freestanding_hooks' {
|
||||
return pref != unsafe { nil }
|
||||
&& (pref.has_freestanding_hooks() || name in pref.user_defines)
|
||||
}
|
||||
'freestanding_output' {
|
||||
return pref != unsafe { nil }
|
||||
&& (pref.has_freestanding_hook('output') || name in pref.user_defines)
|
||||
}
|
||||
'freestanding_panic' {
|
||||
return pref != unsafe { nil }
|
||||
&& (pref.has_freestanding_hook('panic') || name in pref.user_defines)
|
||||
}
|
||||
'freestanding_alloc' {
|
||||
return pref != unsafe { nil }
|
||||
&& (pref.has_freestanding_hook('alloc') || name in pref.user_defines)
|
||||
}
|
||||
'new_int', 'gcboehm', 'autofree', 'ppc64' {
|
||||
return false
|
||||
}
|
||||
else {
|
||||
if pref != unsafe { nil } && define_list_contains(pref.user_defines, name) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn comptime_optional_define_value(name string, user_defines []string, explicit_user_defines []string) bool {
|
||||
if define_list_contains(explicit_user_defines, name) {
|
||||
return true
|
||||
}
|
||||
if is_target_or_mode_comptime_flag(name.to_lower()) {
|
||||
return false
|
||||
}
|
||||
return define_list_contains(user_defines, name)
|
||||
}
|
||||
|
||||
// comptime_optional_flag_value evaluates the `name ?` form. Unlike a plain
|
||||
// `$if name`, target OS and target-mode flags must not become true implicitly.
|
||||
// Non-target compiler capability flags keep their normal value, because V's
|
||||
// builtin/runtime code uses the optional form for guarded internal fallbacks.
|
||||
pub fn comptime_optional_flag_value(pref &Preferences, name string) bool {
|
||||
if pref == unsafe { nil } {
|
||||
return false
|
||||
}
|
||||
if comptime_optional_define_value(name, pref.user_defines, pref.explicit_user_defines) {
|
||||
return true
|
||||
}
|
||||
if is_target_or_mode_comptime_flag(name.to_lower()) {
|
||||
return false
|
||||
}
|
||||
return comptime_flag_value(pref, name)
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
module pref
|
||||
|
||||
fn test_comptime_flag_value_uses_target_os_preference() {
|
||||
mut prefs := new_preferences()
|
||||
prefs.target_os = 'windows'
|
||||
assert comptime_flag_value(&prefs, 'windows')
|
||||
assert !comptime_flag_value(&prefs, 'linux')
|
||||
assert !comptime_flag_value(&prefs, 'macos')
|
||||
|
||||
prefs.target_os = 'darwin'
|
||||
assert comptime_flag_value(&prefs, 'macos')
|
||||
assert comptime_flag_value(&prefs, 'darwin')
|
||||
assert comptime_flag_value(&prefs, 'bsd')
|
||||
assert !comptime_flag_value(&prefs, 'windows')
|
||||
|
||||
prefs.target_os = 'termux'
|
||||
assert comptime_flag_value(&prefs, 'termux')
|
||||
assert !comptime_flag_value(&prefs, 'android')
|
||||
assert !comptime_flag_value(&prefs, 'linux')
|
||||
}
|
||||
|
||||
fn test_comptime_flag_value_allows_nil_preferences() {
|
||||
prefs := unsafe { &Preferences(nil) }
|
||||
assert comptime_flag_value(prefs, 'linux') == (prefs.target_os_or_host() == 'linux')
|
||||
assert !comptime_flag_value(prefs, 'definitely_missing_flag')
|
||||
}
|
||||
|
||||
fn test_comptime_pkgconfig_value_reports_missing_packages_as_false() {
|
||||
assert !comptime_pkgconfig_value('__v2_definitely_missing_pkgconfig_test_package__')
|
||||
}
|
||||
|
||||
fn test_comptime_optional_flag_value_uses_user_defines_only() {
|
||||
mut prefs := new_preferences()
|
||||
prefs.target_os = 'linux'
|
||||
assert comptime_flag_value(&prefs, 'linux')
|
||||
assert !comptime_optional_flag_value(&prefs, 'linux')
|
||||
|
||||
prefs.user_defines = ['linux']
|
||||
prefs.explicit_user_defines = ['linux']
|
||||
assert comptime_optional_flag_value(&prefs, 'linux')
|
||||
|
||||
prefs.target_os = 'windows'
|
||||
assert !comptime_flag_value(&prefs, 'linux')
|
||||
assert comptime_optional_flag_value(&prefs, 'linux')
|
||||
}
|
||||
|
||||
fn test_comptime_optional_target_modes_ignore_synthesized_defines() {
|
||||
mut cross_prefs := new_preferences()
|
||||
cross_prefs.target_os = 'cross'
|
||||
cross_prefs.output_cross_c = true
|
||||
cross_prefs.user_defines = ['cross']
|
||||
assert comptime_flag_value(&cross_prefs, 'cross')
|
||||
assert !comptime_optional_flag_value(&cross_prefs, 'cross')
|
||||
cross_prefs.explicit_user_defines = ['cross']
|
||||
assert comptime_optional_flag_value(&cross_prefs, 'cross')
|
||||
|
||||
mut free_prefs := new_preferences()
|
||||
free_prefs.freestanding = true
|
||||
free_prefs.user_defines = ['freestanding']
|
||||
assert comptime_flag_value(&free_prefs, 'freestanding')
|
||||
assert !comptime_optional_flag_value(&free_prefs, 'freestanding')
|
||||
free_prefs.explicit_user_defines = ['freestanding']
|
||||
assert comptime_optional_flag_value(&free_prefs, 'freestanding')
|
||||
|
||||
mut none_prefs := new_preferences()
|
||||
none_prefs.freestanding = true
|
||||
none_prefs.target_os = 'none'
|
||||
none_prefs.user_defines = ['freestanding']
|
||||
assert comptime_flag_value(&none_prefs, 'none')
|
||||
assert !comptime_optional_flag_value(&none_prefs, 'none')
|
||||
none_prefs.explicit_user_defines = ['none']
|
||||
assert comptime_optional_flag_value(&none_prefs, 'none')
|
||||
}
|
||||
|
||||
fn test_comptime_optional_bare_preserves_explicit_user_define() {
|
||||
mut prefs := new_preferences()
|
||||
prefs.user_defines = ['bare']
|
||||
assert !comptime_optional_flag_value(&prefs, 'bare')
|
||||
|
||||
prefs.explicit_user_defines = ['bare']
|
||||
assert comptime_optional_flag_value(&prefs, 'bare')
|
||||
}
|
||||
|
||||
fn test_comptime_optional_flag_value_keeps_internal_capability_flags() {
|
||||
mut prefs := new_preferences()
|
||||
prefs.backend = .x64
|
||||
prefs.arch = .x64
|
||||
prefs.target_os = 'windows'
|
||||
assert comptime_flag_value(&prefs, 'v2_native_windows_pe_minimal')
|
||||
assert comptime_optional_flag_value(&prefs, 'v2_native_windows_pe_minimal')
|
||||
assert comptime_flag_value(&prefs, 'builtin_write_buf_to_fd_should_use_c_write')
|
||||
assert comptime_optional_flag_value(&prefs, 'builtin_write_buf_to_fd_should_use_c_write')
|
||||
}
|
||||
|
||||
fn test_comptime_flag_value_native_backend_backtrace_guards() {
|
||||
mut prefs := new_preferences()
|
||||
prefs.backend = .x64
|
||||
assert comptime_flag_value(&prefs, 'no_backtrace')
|
||||
assert comptime_flag_value(&prefs, 'tinyc')
|
||||
|
||||
prefs.backend = .arm64
|
||||
assert comptime_flag_value(&prefs, 'no_backtrace')
|
||||
assert comptime_flag_value(&prefs, 'tinyc')
|
||||
|
||||
prefs.backend = .cleanc
|
||||
assert !comptime_flag_value(&prefs, 'no_backtrace')
|
||||
assert !comptime_flag_value(&prefs, 'tinyc')
|
||||
|
||||
prefs.user_defines = ['no_backtrace']
|
||||
assert comptime_flag_value(&prefs, 'no_backtrace')
|
||||
assert !comptime_flag_value(&prefs, 'tinyc')
|
||||
}
|
||||
|
||||
fn test_effective_arch_uses_target_os_preference() {
|
||||
mut prefs := new_preferences()
|
||||
prefs.arch = .auto
|
||||
prefs.target_os = 'macos'
|
||||
assert prefs.get_effective_arch() == .arm64
|
||||
|
||||
prefs.target_os = 'windows'
|
||||
assert prefs.get_effective_arch() == .x64
|
||||
}
|
||||
|
||||
fn test_v2_native_windows_pe_minimal_flag() {
|
||||
mut prefs := new_preferences()
|
||||
prefs.backend = .x64
|
||||
prefs.arch = .x64
|
||||
prefs.target_os = 'windows'
|
||||
assert comptime_flag_value(&prefs, 'v2_native_windows_pe_minimal')
|
||||
|
||||
prefs.target_os = 'linux'
|
||||
assert !comptime_flag_value(&prefs, 'v2_native_windows_pe_minimal')
|
||||
prefs.target_os = 'windows'
|
||||
prefs.backend = .arm64
|
||||
assert !comptime_flag_value(&prefs, 'v2_native_windows_pe_minimal')
|
||||
}
|
||||
|
||||
fn test_freestanding_hook_comptime_flags() {
|
||||
mut prefs := new_preferences()
|
||||
prefs.freestanding = true
|
||||
prefs.freestanding_hooks = ['output', 'alloc']
|
||||
assert comptime_flag_value(&prefs, 'freestanding')
|
||||
assert comptime_flag_value(&prefs, 'freestanding_hooks')
|
||||
assert comptime_flag_value(&prefs, 'freestanding_output')
|
||||
assert comptime_flag_value(&prefs, 'freestanding_alloc')
|
||||
assert !comptime_flag_value(&prefs, 'freestanding_panic')
|
||||
}
|
||||
|
||||
fn test_freestanding_none_has_no_concrete_os_comptime_flags() {
|
||||
mut prefs := new_preferences()
|
||||
prefs.freestanding = true
|
||||
prefs.target_os = 'none'
|
||||
assert comptime_flag_value(&prefs, 'freestanding')
|
||||
assert !comptime_flag_value(&prefs, 'cross')
|
||||
assert !comptime_flag_value(&prefs, 'linux')
|
||||
assert !comptime_flag_value(&prefs, 'macos')
|
||||
assert !comptime_flag_value(&prefs, 'darwin')
|
||||
assert !comptime_flag_value(&prefs, 'windows')
|
||||
assert !comptime_flag_value(&prefs, 'bsd')
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright (c) 2020-2024 Joe Conigliaro. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module pref
|
||||
|
||||
import os
|
||||
|
||||
fn (p &Preferences) effective_vroot() string {
|
||||
if p.vroot.len > 0 {
|
||||
return p.vroot
|
||||
}
|
||||
return os.getwd()
|
||||
}
|
||||
|
||||
pub fn (p &Preferences) get_vlib_module_path(mod string) string {
|
||||
mod_path := mod.replace('.', os.path_separator)
|
||||
return module_path_join(module_path_join(p.effective_vroot(), 'vlib'), mod_path)
|
||||
}
|
||||
|
||||
fn module_path_join(base string, name string) string {
|
||||
if base.len == 0 {
|
||||
return name
|
||||
}
|
||||
if name.len == 0 {
|
||||
return base
|
||||
}
|
||||
last := base[base.len - 1]
|
||||
if last == `/` || last == `\\` {
|
||||
return base + name
|
||||
}
|
||||
return base + os.path_separator + name
|
||||
}
|
||||
|
||||
fn module_root_for_file(path string) string {
|
||||
mut dir := os.dir(path)
|
||||
for dir.len > 0 {
|
||||
if os.exists(module_path_join(dir, 'v.mod')) {
|
||||
return dir
|
||||
}
|
||||
parent := os.dir(dir)
|
||||
if parent == dir {
|
||||
break
|
||||
}
|
||||
dir = parent
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn find_module_path_in_parent_dirs(mod_path string, importing_file_path string) ?string {
|
||||
mut dir := os.dir(importing_file_path)
|
||||
for dir.len > 0 {
|
||||
parent_relative_path := module_path_join(dir, mod_path)
|
||||
if dir_exists(parent_relative_path) {
|
||||
return parent_relative_path
|
||||
}
|
||||
parent := os.dir(dir)
|
||||
if parent == dir {
|
||||
break
|
||||
}
|
||||
dir = parent
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// check for relative and then vlib
|
||||
pub fn (p &Preferences) get_module_path(mod string, importing_file_path string) string {
|
||||
mod_path := mod.replace('.', os.path_separator)
|
||||
vroot := p.effective_vroot()
|
||||
// relative to file importing it
|
||||
relative_path := module_path_join(os.dir(importing_file_path), mod_path)
|
||||
if dir_exists(relative_path) {
|
||||
return relative_path
|
||||
}
|
||||
module_root := module_root_for_file(importing_file_path)
|
||||
if module_root != '' {
|
||||
root_relative_path := module_path_join(module_root, mod_path)
|
||||
if dir_exists(root_relative_path) {
|
||||
return root_relative_path
|
||||
}
|
||||
}
|
||||
if parent_relative_path := find_module_path_in_parent_dirs(mod_path, importing_file_path) {
|
||||
return parent_relative_path
|
||||
}
|
||||
cwd_relative_path := module_path_join(os.getwd(), mod_path)
|
||||
if dir_exists(cwd_relative_path) {
|
||||
return cwd_relative_path
|
||||
}
|
||||
// TODO: is this the best order?
|
||||
// vlib
|
||||
vlib_path := module_path_join(module_path_join(vroot, 'vlib'), mod_path)
|
||||
if dir_exists(vlib_path) {
|
||||
return vlib_path
|
||||
}
|
||||
// V1 compiler modules under vlib/v use legacy bare sibling imports
|
||||
// (`import token` from vlib/v/checker, with sources in vlib/v/token).
|
||||
vlib_v_path := module_path_join(module_path_join(vroot, 'vlib'), 'v')
|
||||
normalized_importer := importing_file_path.replace('\\', '/')
|
||||
normalized_vlib_v := vlib_v_path.replace('\\', '/')
|
||||
if normalized_importer.starts_with(normalized_vlib_v + '/')
|
||||
|| normalized_importer.starts_with('vlib/v/') {
|
||||
legacy_v_path := module_path_join(vlib_v_path, mod_path)
|
||||
if dir_exists(legacy_v_path) {
|
||||
return legacy_v_path
|
||||
}
|
||||
}
|
||||
// ~/.vmodules
|
||||
vmodules_path := module_path_join(p.vmodules_path, mod_path)
|
||||
if dir_exists(vmodules_path) {
|
||||
// V convention: if a module dir has a src/ subdirectory, use that
|
||||
src_path := module_path_join(vmodules_path, 'src')
|
||||
if dir_exists(src_path) {
|
||||
return src_path
|
||||
}
|
||||
return vmodules_path
|
||||
}
|
||||
panic('Preferences.get_module_path: cannot find module path for `${mod}`')
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
module pref
|
||||
|
||||
import os
|
||||
|
||||
fn test_get_module_path_finds_sibling_module_from_importing_file_ancestor() {
|
||||
tmp_dir := os.join_path(os.vtmp_dir(), 'v2_pref_sibling_module_${os.getpid()}')
|
||||
project_dir := os.join_path(tmp_dir, 'app')
|
||||
project_subdir := os.join_path(project_dir, 'ui')
|
||||
sibling_module_dir := os.join_path(tmp_dir, 'viper')
|
||||
os.mkdir_all(project_subdir) or { panic(err) }
|
||||
os.mkdir_all(sibling_module_dir) or { panic(err) }
|
||||
defer {
|
||||
os.rmdir_all(tmp_dir) or {}
|
||||
}
|
||||
os.write_file(os.join_path(project_dir, 'v.mod'), "Module { name: 'app' }") or { panic(err) }
|
||||
os.write_file(os.join_path(sibling_module_dir, 'v.mod'), "Module { name: 'viper' }") or {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
prefs := Preferences{
|
||||
vroot: tmp_dir
|
||||
vmodules_path: os.join_path(tmp_dir, '.vmodules')
|
||||
}
|
||||
importing_file := os.join_path(project_subdir, 'text.v')
|
||||
assert prefs.get_module_path('viper', importing_file) == sibling_module_dir
|
||||
}
|
||||
@@ -0,0 +1,728 @@
|
||||
// Copyright (c) 2020-2024 Joe Conigliaro. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module pref
|
||||
|
||||
import os
|
||||
import os.cmdline
|
||||
|
||||
pub enum Backend {
|
||||
v // V source output (default)
|
||||
eval // AST interpreter
|
||||
cleanc // Clean C backend (AST -> C)
|
||||
c // SSA -> C backend
|
||||
x64 // Native x64/AMD64 backend
|
||||
arm64 // Native ARM64 backend
|
||||
}
|
||||
|
||||
pub enum Arch {
|
||||
auto // Auto-detect based on OS
|
||||
x64
|
||||
arm64
|
||||
}
|
||||
|
||||
// GarbageCollectionMode controls which garbage collector is used.
|
||||
// Translated from Go's runtime GC, the `vgc` mode provides a concurrent
|
||||
// tri-color mark-and-sweep collector written in pure V.
|
||||
pub enum GarbageCollectionMode {
|
||||
no_gc // no garbage collection
|
||||
vgc // V GC: concurrent tri-color mark-and-sweep (translated from Go's runtime GC)
|
||||
boehm // Boehm-Demers-Weiser conservative GC (legacy)
|
||||
}
|
||||
|
||||
pub struct Preferences {
|
||||
pub mut:
|
||||
debug bool
|
||||
verbose bool
|
||||
ownership bool // -ownership: enable ownership checking for strings
|
||||
skip_genv bool
|
||||
skip_builtin bool
|
||||
skip_imports bool
|
||||
skip_type_check bool // Skip type checking phase (for backends that don't need it yet)
|
||||
no_parallel bool // when true, run type check sequentially (default: parallel)
|
||||
no_parallel_transform bool // when true, run transform sequentially (default: parallel)
|
||||
no_cache bool // Disable build cache
|
||||
no_markused bool // Disable markused stage and dead-function pruning
|
||||
show_cc bool // Print C compiler command(s)
|
||||
stats bool // Print extended statistics
|
||||
print_parsed_files bool // Print all parsed files grouped by full/.vh parse mode
|
||||
keep_c bool // Keep generated C file after compilation
|
||||
use_context_allocator bool // Use context allocator for heap allocations (enables profiling)
|
||||
is_shared_lib bool // Compile to shared library (.dylib/.so) for live reload
|
||||
no_optimize bool = true // skip SSA optimization (mem2reg, phi elimination); cleared by -prod
|
||||
is_prod bool // -prod: enable SSA optimization + -O3 -flto for C compiler
|
||||
prealloc bool // -prealloc: use arena allocation (bump-pointer, not thread-safe)
|
||||
gc_mode GarbageCollectionMode // Garbage collection mode (-gc flag)
|
||||
backend Backend
|
||||
arch Arch = .auto
|
||||
target_os string = os.user_os()
|
||||
output_cross_c bool // -os cross: keep generated C portable
|
||||
freestanding bool // -freestanding: target a platform contract without an OS runtime
|
||||
freestanding_hooks []string // -fhooks: explicit freestanding platform capabilities
|
||||
macos_tiny bool = true // -no-mos-tiny: disable the automatic macOS x64 tiny object path
|
||||
output_file string
|
||||
printfn_list []string // List of function names whose generated C source should be printed
|
||||
user_defines []string // All active comptime flags, including compiler-synthesized flags.
|
||||
explicit_user_defines []string // User-defined comptime flags from explicit -d <name> only.
|
||||
hot_fn string // Extract raw machine code for this function only (hot reload)
|
||||
single_backend bool // Only include the selected backend (strip other backends from binary)
|
||||
eval_runtime_args []string // Program argv exposed to the eval backend
|
||||
ccompiler string // C compiler override (-cc flag)
|
||||
pub:
|
||||
vroot string = detect_vroot()
|
||||
vmodules_path string = os.vmodules_dir()
|
||||
}
|
||||
|
||||
fn dir_exists(path string) bool {
|
||||
if path.len == 0 {
|
||||
return false
|
||||
}
|
||||
if os.is_dir(path) {
|
||||
return true
|
||||
}
|
||||
_ := os.ls(path) or { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
fn detect_vroot_from(start string) string {
|
||||
if start.len == 0 {
|
||||
return ''
|
||||
}
|
||||
mut dir := start
|
||||
// Avoid os.abs_path during bootstrap; normalize relative paths manually.
|
||||
if !os.is_abs_path(dir) {
|
||||
cwd := os.getwd()
|
||||
if cwd.len > 0 {
|
||||
dir = os.join_path(cwd, dir)
|
||||
}
|
||||
}
|
||||
if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
|
||||
return dir
|
||||
}
|
||||
dir = os.dir(dir)
|
||||
if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
|
||||
return dir
|
||||
}
|
||||
dir = os.dir(dir)
|
||||
if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
|
||||
return dir
|
||||
}
|
||||
dir = os.dir(dir)
|
||||
if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
|
||||
return dir
|
||||
}
|
||||
dir = os.dir(dir)
|
||||
if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
|
||||
return dir
|
||||
}
|
||||
dir = os.dir(dir)
|
||||
if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
|
||||
return dir
|
||||
}
|
||||
dir = os.dir(dir)
|
||||
if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
|
||||
return dir
|
||||
}
|
||||
dir = os.dir(dir)
|
||||
if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
|
||||
return dir
|
||||
}
|
||||
dir = os.dir(dir)
|
||||
if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
|
||||
return dir
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn detect_vroot() string {
|
||||
// First prefer the compile-time module root when it points to a valid
|
||||
// V source tree. This keeps self-host binaries stable during bootstrapping.
|
||||
compile_time_root := @VMODROOT
|
||||
if compile_time_root.len > 0 {
|
||||
// Trust @VMODROOT without runtime validation. During bootstrapping
|
||||
// (v2 → v3 via ARM64), os.join_path/os.is_dir may not work correctly
|
||||
// in the native binary, but the compile-time root is always correct.
|
||||
return compile_time_root
|
||||
}
|
||||
// Prefer deriving from executable path: <vroot>/cmd/v2/v3.
|
||||
if os.args.len > 0 && os.args[0].len > 0 {
|
||||
vroot := detect_vroot_from(os.args[0])
|
||||
if vroot.len > 0 {
|
||||
return vroot
|
||||
}
|
||||
}
|
||||
// Fallback to current directory ancestry.
|
||||
cwd := os.getwd()
|
||||
vroot := detect_vroot_from(cwd)
|
||||
if vroot.len > 0 {
|
||||
return vroot
|
||||
}
|
||||
// Final fallback: keep old behavior for unusual bootstrap contexts.
|
||||
if os.args.len > 0 && os.args[0].len > 0 {
|
||||
if os.is_abs_path(os.args[0]) {
|
||||
return os.dir(os.args[0])
|
||||
}
|
||||
return cwd
|
||||
}
|
||||
return cwd
|
||||
}
|
||||
|
||||
pub fn new_preferences() Preferences {
|
||||
return Preferences{
|
||||
backend: .cleanc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (p &Preferences) target_os_or_host() string {
|
||||
if p == unsafe { nil } || p.target_os == '' {
|
||||
return os.user_os()
|
||||
}
|
||||
return p.target_os
|
||||
}
|
||||
|
||||
pub fn (p &Preferences) normalized_target_os() string {
|
||||
return normalize_target_os_name(p.target_os_or_host())
|
||||
}
|
||||
|
||||
pub fn (p &Preferences) is_cross_target() bool {
|
||||
return p != unsafe { nil } && (p.output_cross_c || p.normalized_target_os() == 'cross')
|
||||
}
|
||||
|
||||
pub fn (p &Preferences) is_freestanding() bool {
|
||||
return p != unsafe { nil } && p.freestanding
|
||||
}
|
||||
|
||||
pub fn (p &Preferences) can_compile_cleanc_locally() bool {
|
||||
if p == unsafe { nil } {
|
||||
return true
|
||||
}
|
||||
if p.is_freestanding() {
|
||||
return false
|
||||
}
|
||||
if p.is_cross_target() {
|
||||
return true
|
||||
}
|
||||
return p.normalized_target_os() == normalize_target_os_name(os.user_os())
|
||||
}
|
||||
|
||||
pub fn (p &Preferences) can_run_target_binary_locally() bool {
|
||||
if p == unsafe { nil } {
|
||||
return true
|
||||
}
|
||||
if p.is_freestanding() {
|
||||
return false
|
||||
}
|
||||
if p.backend == .cleanc && p.is_cross_target() {
|
||||
return true
|
||||
}
|
||||
if p.normalized_target_os() != normalize_target_os_name(os.user_os()) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
pub fn (p &Preferences) source_filter_target_os() string {
|
||||
if p == unsafe { nil } {
|
||||
return normalize_target_os_name(os.user_os())
|
||||
}
|
||||
if p.is_cross_target() {
|
||||
return normalize_target_os_name(os.user_os())
|
||||
}
|
||||
return p.target_os_or_host()
|
||||
}
|
||||
|
||||
pub fn (p &Preferences) freestanding_hook_list() []string {
|
||||
if p == unsafe { nil } {
|
||||
return []string{}
|
||||
}
|
||||
return p.freestanding_hooks.clone()
|
||||
}
|
||||
|
||||
pub fn (p &Preferences) has_freestanding_hooks() bool {
|
||||
return p != unsafe { nil } && p.freestanding_hooks.len > 0
|
||||
}
|
||||
|
||||
pub fn (p &Preferences) has_freestanding_hook(name string) bool {
|
||||
if p == unsafe { nil } {
|
||||
return false
|
||||
}
|
||||
normalized := normalize_freestanding_hook_name(name) or { return false }
|
||||
return normalized in p.freestanding_hooks
|
||||
}
|
||||
|
||||
fn normalize_cli_target_os(target_os string) string {
|
||||
normalized := normalize_target_os_name(target_os)
|
||||
match normalized {
|
||||
'linux', 'macos', 'windows', 'cross', 'none', 'freebsd', 'openbsd', 'netbsd', 'dragonfly',
|
||||
'android', 'termux', 'ios', 'solaris', 'qnx', 'serenity', 'plan9', 'vinix' {
|
||||
return normalized
|
||||
}
|
||||
else {
|
||||
eprintln('error: unknown target OS `${target_os}`. Valid targets include: linux, macos, windows, cross, none, freebsd, openbsd, netbsd, dragonfly, android, termux, ios, solaris, qnx, serenity, plan9, vinix')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_target_contract(target_os string, freestanding bool) {
|
||||
if target_os == 'none' && !freestanding {
|
||||
eprintln('error: -os none requires -freestanding')
|
||||
exit(1)
|
||||
}
|
||||
if target_os == 'cross' && freestanding {
|
||||
eprintln('error: -freestanding -os cross is not supported; use -os none for pure freestanding targets')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_macos_tiny_flag_contract(options []string, target_os string) ! {
|
||||
if '-no-mos-tiny' in options && target_os != 'macos' {
|
||||
return error('-no-mos-tiny requires a macOS target; use -os macos or run on a macOS host')
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_freestanding_hook_name(name string) ?string {
|
||||
normalized := name.trim_space().to_lower()
|
||||
return match normalized {
|
||||
'output', 'panic', 'alloc' {
|
||||
normalized
|
||||
}
|
||||
else {
|
||||
none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn append_freestanding_hook(mut hooks []string, hook string) {
|
||||
normalized := normalize_freestanding_hook_name(hook) or {
|
||||
eprintln('error: unknown freestanding hook `${hook}`. Valid hooks: output, panic, alloc, minimal')
|
||||
exit(1)
|
||||
}
|
||||
if normalized !in hooks {
|
||||
hooks << normalized
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_freestanding_hooks(value string) []string {
|
||||
mut hooks := []string{}
|
||||
for raw_part in value.split(',') {
|
||||
part := raw_part.trim_space()
|
||||
if part == '' {
|
||||
continue
|
||||
}
|
||||
if part == 'minimal' {
|
||||
for hook in ['output', 'panic', 'alloc'] {
|
||||
append_freestanding_hook(mut hooks, hook)
|
||||
}
|
||||
continue
|
||||
}
|
||||
append_freestanding_hook(mut hooks, part)
|
||||
}
|
||||
return hooks
|
||||
}
|
||||
|
||||
fn add_freestanding_hook_defines(mut defines []string, hooks []string) {
|
||||
if hooks.len == 0 {
|
||||
return
|
||||
}
|
||||
if 'freestanding_hooks' !in defines {
|
||||
defines << 'freestanding_hooks'
|
||||
}
|
||||
for hook in hooks {
|
||||
for define in ['freestanding_${hook}', 'freestanding_hooks_${hook}'] {
|
||||
if define !in defines {
|
||||
defines << define
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_files_from_args(args []string) []string {
|
||||
options_with_values := ['-backend', '-b', '-o', '-output', '-arch', '-printfn', '-gc', '-d',
|
||||
'-hot-fn', '-cc', '-os', '-fhooks']
|
||||
mut files := []string{}
|
||||
mut skip_next := false
|
||||
for arg in args {
|
||||
if arg == '--' {
|
||||
break
|
||||
}
|
||||
if skip_next {
|
||||
skip_next = false
|
||||
continue
|
||||
}
|
||||
if arg.starts_with('-') {
|
||||
if arg in options_with_values {
|
||||
skip_next = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
files << arg
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
// new_preferences_from_args parses full args list including option values
|
||||
pub fn new_preferences_from_args(args []string) Preferences {
|
||||
// Default backend is cleanc
|
||||
default_backend := 'cleanc'
|
||||
mut backend_str_long := cmdline.option(args, '-backend', '')
|
||||
if backend_str_long.len == 0 {
|
||||
backend_str_long = ''
|
||||
}
|
||||
mut backend_str_short := cmdline.option(args, '-b', '')
|
||||
if backend_str_short.len == 0 {
|
||||
backend_str_short = ''
|
||||
}
|
||||
backend_str := if backend_str_long.len > 0 {
|
||||
backend_str_long
|
||||
} else if backend_str_short.len > 0 {
|
||||
backend_str_short
|
||||
} else {
|
||||
default_backend
|
||||
}
|
||||
mut backend := Backend.cleanc
|
||||
match backend_str {
|
||||
'eval' {
|
||||
backend = .eval
|
||||
}
|
||||
'cleanc' {
|
||||
backend = .cleanc
|
||||
}
|
||||
'c' {
|
||||
backend = .c
|
||||
}
|
||||
'v' {
|
||||
backend = .v
|
||||
}
|
||||
'arm64' {
|
||||
backend = .arm64
|
||||
}
|
||||
'x64' {
|
||||
backend = .x64
|
||||
}
|
||||
else {
|
||||
eprintln('error: unknown backend `${backend_str}`. Valid backends: eval, cleanc, c, v, arm64, x64')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
mut arch_str := cmdline.option(args, '-arch', 'auto')
|
||||
if arch_str.len == 0 {
|
||||
arch_str = 'auto'
|
||||
}
|
||||
mut arch := Arch.auto
|
||||
match arch_str {
|
||||
'auto' {
|
||||
arch = .auto
|
||||
}
|
||||
'x64' {
|
||||
arch = .x64
|
||||
}
|
||||
'arm64' {
|
||||
arch = .arm64
|
||||
}
|
||||
else {
|
||||
eprintln('error: unknown architecture `${arch_str}`. Valid architectures: auto, x64, arm64')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
output_file := cmdline.option(args, '-o', cmdline.option(args, '-output', ''))
|
||||
ccompiler := cmdline.option(args, '-cc', '')
|
||||
target_os_arg := cmdline.option(args, '-os', '')
|
||||
freestanding_hooks_arg := cmdline.option(args, '-fhooks', '')
|
||||
// Without -os, V2 targets the host OS. -os is an explicit target override;
|
||||
// -os cross and -freestanding express separate portable/platform contracts.
|
||||
normalized_target_os := if target_os_arg.len > 0 {
|
||||
normalize_cli_target_os(target_os_arg)
|
||||
} else {
|
||||
normalize_target_os_name(os.user_os())
|
||||
}
|
||||
output_cross_c := normalized_target_os == 'cross'
|
||||
|
||||
// Parse -printfn option (comma-separated list of function names to print)
|
||||
mut printfn_str := cmdline.option(args, '-printfn', '')
|
||||
if printfn_str.len == 0 {
|
||||
printfn_str = ''
|
||||
}
|
||||
printfn_list := if printfn_str.len > 0 { printfn_str.split(',') } else { []string{} }
|
||||
|
||||
// Parse -d <name> comptime defines (can be specified multiple times)
|
||||
user_defines := cmdline.options(args, '-d')
|
||||
|
||||
// Parse -hot-fn <name> for hot code reloading
|
||||
mut hot_fn_str := cmdline.option(args, '-hot-fn', '')
|
||||
if hot_fn_str.len == 0 {
|
||||
hot_fn_str = ''
|
||||
}
|
||||
|
||||
// Parse -gc <mode> for garbage collection
|
||||
gc_mode_str := cmdline.option(args, '-gc', '')
|
||||
mut gc_mode := GarbageCollectionMode.no_gc
|
||||
mut gc_defines := []string{}
|
||||
match gc_mode_str {
|
||||
'', 'none' {
|
||||
gc_mode = .no_gc
|
||||
}
|
||||
'vgc' {
|
||||
gc_mode = .vgc
|
||||
gc_defines << 'vgc'
|
||||
}
|
||||
'boehm' {
|
||||
gc_mode = .boehm
|
||||
gc_defines << 'gcboehm'
|
||||
gc_defines << 'gcboehm_full'
|
||||
gc_defines << 'gcboehm_opt'
|
||||
}
|
||||
else {
|
||||
eprintln('error: unknown garbage collection mode `-gc ${gc_mode_str}`')
|
||||
eprintln(' `-gc vgc` .............. V GC: concurrent tri-color mark-and-sweep (translated from Go)')
|
||||
eprintln(' `-gc boehm` ............ Boehm-Demers-Weiser conservative GC')
|
||||
eprintln(' `-gc none` ............. no garbage collection (default)')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
mut all_defines := user_defines.clone()
|
||||
all_defines << gc_defines
|
||||
if output_cross_c && 'cross' !in all_defines {
|
||||
all_defines << 'cross'
|
||||
}
|
||||
if '-prealloc' in args {
|
||||
all_defines << 'prealloc'
|
||||
}
|
||||
freestanding := '-freestanding' in args || '--freestanding' in args
|
||||
validate_target_contract(normalized_target_os, freestanding)
|
||||
freestanding_hooks := parse_freestanding_hooks(freestanding_hooks_arg)
|
||||
if freestanding_hooks.len > 0 && !freestanding {
|
||||
eprintln('error: -fhooks requires -freestanding')
|
||||
exit(1)
|
||||
}
|
||||
if freestanding && 'freestanding' !in all_defines {
|
||||
all_defines << 'freestanding'
|
||||
}
|
||||
add_freestanding_hook_defines(mut all_defines, freestanding_hooks)
|
||||
|
||||
options := cmdline.only_options(args)
|
||||
validate_macos_tiny_flag_contract(options, normalized_target_os) or {
|
||||
eprintln('error: ${err.msg()}')
|
||||
exit(1)
|
||||
}
|
||||
macos_tiny := '-no-mos-tiny' !in options
|
||||
|
||||
// Parse -ownership flag (must be after options is declared)
|
||||
mut ownership := false
|
||||
$if ownership ? {
|
||||
ownership = '-ownership' in options
|
||||
if ownership {
|
||||
all_defines << 'ownership'
|
||||
}
|
||||
}
|
||||
|
||||
// Validate flags: error on unknown options
|
||||
known_flags_with_values := ['-backend', '-b', '-o', '-output', '-arch', '-printfn', '-gc',
|
||||
'-d', '-hot-fn', '-cc', '-os', '-fhooks']
|
||||
mut known_boolean_flags := ['--debug', '--verbose', '-v', '--skip-genv', '--skip-builtin',
|
||||
'--skip-imports', '--skip-type-check', '-no-parallel', '--no-parallel', '-nocache',
|
||||
'--nocache', '-nomarkused', '--nomarkused', '-showcc', '--showcc', '-stats', '--stats',
|
||||
'-print-parsed-files', '--print-parsed-files', '-keepc', '--profile-alloc', '-profile-alloc',
|
||||
'-enable-globals', '--enable-globals', '-shared', '--shared', '-O0', '--single-backend',
|
||||
'-single-backend', '-prod', '-prealloc', '-freestanding', '--freestanding', '-no-mos-tiny']
|
||||
$if ownership ? {
|
||||
known_boolean_flags << '-ownership'
|
||||
}
|
||||
for opt in options {
|
||||
if opt !in known_flags_with_values && opt !in known_boolean_flags {
|
||||
eprintln('error: unknown flag `${opt}`')
|
||||
eprintln('')
|
||||
eprintln('Usage: v2 [options] <file.v>')
|
||||
eprintln('')
|
||||
eprintln('Options:')
|
||||
eprintln(' -o <file> Output file name')
|
||||
eprintln(' -b <name> Backend: eval, cleanc, c, v, arm64, x64 (default: cleanc; omit for cleanc)')
|
||||
eprintln(' -backend <name> is accepted as a compatibility alias')
|
||||
eprintln(' -arch <name> Architecture: auto, x64, arm64 (default: auto)')
|
||||
eprintln(' -os <target> Override target OS (default: host OS): linux, macos, windows, termux, cross, none')
|
||||
eprintln(' -printfn <names> Print generated C for functions (comma-separated)')
|
||||
eprintln(' -stats, --stats Print compilation statistics')
|
||||
eprintln(' -nocache, --nocache Disable build cache')
|
||||
eprintln(' -d <name> Define a comptime flag')
|
||||
eprintln(' -enable-globals Accepted for v1 compatibility')
|
||||
eprintln(' -prod Production build: enable SSA optimization + -O3 -flto')
|
||||
eprintln(' -prealloc Use arena allocation (faster, not thread-safe)')
|
||||
eprintln(' -freestanding Generate for a freestanding platform contract')
|
||||
eprintln(' -fhooks <values> Advanced freestanding hooks for --skip-builtin --skip-type-check stubs')
|
||||
eprintln(' Values: output, panic, alloc, minimal')
|
||||
eprintln(' -no-mos-tiny Disable macOS x64 tiny object output')
|
||||
eprintln(' -O0 Skip SSA optimization (default; use -prod to enable)')
|
||||
$if ownership ? {
|
||||
eprintln(' -ownership Enable ownership checking for strings')
|
||||
}
|
||||
eprintln(' --debug Enable debug mode')
|
||||
eprintln(' -v, --verbose Enable verbose output')
|
||||
eprintln(' -cc <compiler> C compiler to use (default: tcc, fallback: cc)')
|
||||
eprintln(' -showcc, --showcc Print C compiler command')
|
||||
eprintln(' -keepc Keep generated C file')
|
||||
eprintln(' -no-parallel, --no-parallel')
|
||||
eprintln(' Disable parallel type check and transform')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
implicit_skip_builtin := freestanding && freestanding_hooks.len == 0
|
||||
return Preferences{
|
||||
debug: '--debug' in options
|
||||
verbose: '--verbose' in options || '-v' in options
|
||||
skip_genv: '--skip-genv' in options
|
||||
skip_builtin: '--skip-builtin' in options || implicit_skip_builtin
|
||||
skip_imports: '--skip-imports' in options
|
||||
skip_type_check: '--skip-type-check' in options
|
||||
no_parallel: '-no-parallel' in options || '--no-parallel' in options
|
||||
no_parallel_transform: '-no-parallel' in options || '--no-parallel' in options
|
||||
no_cache: '-nocache' in options || '--nocache' in options
|
||||
no_markused: '-nomarkused' in options || '--nomarkused' in options
|
||||
show_cc: '-showcc' in options || '--showcc' in options
|
||||
stats: '-stats' in options || '--stats' in options
|
||||
print_parsed_files: '-print-parsed-files' in options || '--print-parsed-files' in options
|
||||
keep_c: '-keepc' in options
|
||||
use_context_allocator: '--profile-alloc' in options || '-profile-alloc' in options
|
||||
is_shared_lib: '-shared' in options || '--shared' in options
|
||||
no_optimize: '-O0' in options || '-prod' !in options
|
||||
is_prod: '-prod' in options
|
||||
prealloc: '-prealloc' in options
|
||||
single_backend: '--single-backend' in options || '-single-backend' in options
|
||||
ownership: ownership
|
||||
gc_mode: gc_mode
|
||||
backend: backend
|
||||
arch: arch
|
||||
target_os: normalized_target_os
|
||||
output_cross_c: output_cross_c
|
||||
freestanding: freestanding
|
||||
freestanding_hooks: freestanding_hooks
|
||||
macos_tiny: macos_tiny
|
||||
output_file: output_file
|
||||
printfn_list: printfn_list
|
||||
user_defines: all_defines
|
||||
explicit_user_defines: user_defines.clone()
|
||||
hot_fn: hot_fn_str
|
||||
ccompiler: ccompiler
|
||||
vroot: detect_vroot()
|
||||
vmodules_path: os.vmodules_dir()
|
||||
}
|
||||
}
|
||||
|
||||
fn target_os_from_option_tokens(options []string) string {
|
||||
for opt in options {
|
||||
if opt.starts_with('--os-') {
|
||||
return normalize_cli_target_os(opt.all_after('--os-'))
|
||||
}
|
||||
if opt.starts_with('os-') {
|
||||
return normalize_cli_target_os(opt.all_after('os-'))
|
||||
}
|
||||
}
|
||||
return normalize_target_os_name(os.user_os())
|
||||
}
|
||||
|
||||
fn freestanding_hooks_from_option_tokens(options []string) []string {
|
||||
mut hooks := []string{}
|
||||
for opt in options {
|
||||
if opt.starts_with('--fhooks-') {
|
||||
for hook in parse_freestanding_hooks(opt.all_after('--fhooks-')) {
|
||||
append_freestanding_hook(mut hooks, hook)
|
||||
}
|
||||
} else if opt.starts_with('fhooks-') {
|
||||
for hook in parse_freestanding_hooks(opt.all_after('fhooks-')) {
|
||||
append_freestanding_hook(mut hooks, hook)
|
||||
}
|
||||
}
|
||||
}
|
||||
return hooks
|
||||
}
|
||||
|
||||
pub fn new_preferences_using_options(options []string) Preferences {
|
||||
// Default backend based on OS: macOS defaults to arm64, others to x64
|
||||
mut backend := if os.user_os() == 'macos' { Backend.arm64 } else { Backend.x64 }
|
||||
if '--eval' in options || 'eval' in options {
|
||||
backend = .eval
|
||||
} else if '--cleanc' in options || 'cleanc' in options {
|
||||
backend = .cleanc
|
||||
} else if '--c' in options || 'c' in options {
|
||||
backend = .c
|
||||
} else if '--v' in options || 'v' in options {
|
||||
backend = .v
|
||||
} else if '--arm64' in options || 'arm64' in options {
|
||||
backend = .arm64
|
||||
} else if '--x64' in options || 'x64' in options {
|
||||
backend = .x64
|
||||
}
|
||||
|
||||
mut arch := Arch.auto
|
||||
if '--arch-x64' in options {
|
||||
arch = .x64
|
||||
} else if '--arch-arm64' in options {
|
||||
arch = .arm64
|
||||
}
|
||||
target_os := target_os_from_option_tokens(options)
|
||||
output_cross_c := target_os == 'cross'
|
||||
freestanding := '--freestanding' in options || '-freestanding' in options
|
||||
|| 'freestanding' in options
|
||||
validate_target_contract(target_os, freestanding)
|
||||
freestanding_hooks := freestanding_hooks_from_option_tokens(options)
|
||||
if freestanding_hooks.len > 0 && !freestanding {
|
||||
eprintln('error: freestanding hooks require freestanding target mode')
|
||||
exit(1)
|
||||
}
|
||||
mut user_defines := []string{}
|
||||
if output_cross_c {
|
||||
user_defines << 'cross'
|
||||
}
|
||||
if freestanding {
|
||||
user_defines << 'freestanding'
|
||||
}
|
||||
add_freestanding_hook_defines(mut user_defines, freestanding_hooks)
|
||||
|
||||
implicit_skip_builtin := freestanding && freestanding_hooks.len == 0
|
||||
return Preferences{
|
||||
// config flags
|
||||
debug: '--debug' in options
|
||||
verbose: '--verbose' in options || '-v' in options
|
||||
skip_genv: '--skip-genv' in options
|
||||
skip_builtin: '--skip-builtin' in options || implicit_skip_builtin
|
||||
skip_imports: '--skip-imports' in options
|
||||
skip_type_check: '--skip-type-check' in options
|
||||
no_parallel: '-no-parallel' in options || '--no-parallel' in options
|
||||
no_parallel_transform: '-no-parallel' in options || '--no-parallel' in options
|
||||
no_cache: '-nocache' in options || '--nocache' in options
|
||||
no_markused: '-nomarkused' in options || '--nomarkused' in options
|
||||
show_cc: '-showcc' in options || '--showcc' in options
|
||||
stats: '-stats' in options || '--stats' in options
|
||||
print_parsed_files: '-print-parsed-files' in options || '--print-parsed-files' in options
|
||||
use_context_allocator: '--profile-alloc' in options || '-profile-alloc' in options
|
||||
is_shared_lib: '-shared' in options || '--shared' in options
|
||||
no_optimize: '-O0' in options || '-prod' !in options
|
||||
is_prod: '-prod' in options
|
||||
prealloc: '-prealloc' in options
|
||||
single_backend: '--single-backend' in options || '-single-backend' in options
|
||||
backend: backend
|
||||
arch: arch
|
||||
target_os: target_os
|
||||
output_cross_c: output_cross_c
|
||||
freestanding: freestanding
|
||||
freestanding_hooks: freestanding_hooks
|
||||
user_defines: user_defines
|
||||
explicit_user_defines: []string{}
|
||||
}
|
||||
}
|
||||
|
||||
// get_effective_arch returns the architecture to use based on preferences and OS
|
||||
pub fn (p &Preferences) get_effective_arch() Arch {
|
||||
if p.arch != .auto {
|
||||
return p.arch
|
||||
}
|
||||
// Auto-detect: macOS defaults to arm64, others to x64
|
||||
return if normalize_current_os_name(p.target_os_or_host()) == 'macos' {
|
||||
Arch.arm64
|
||||
} else {
|
||||
Arch.x64
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
module pref
|
||||
|
||||
import os
|
||||
|
||||
fn test_source_files_from_args_skips_os_option_value() {
|
||||
files := source_files_from_args(['-b', 'x64', '-os', 'windows', '-o', 'app', 'main.v'])
|
||||
assert files == ['main.v']
|
||||
}
|
||||
|
||||
fn test_source_files_from_args_skips_fhooks_option_value() {
|
||||
files := source_files_from_args(['-freestanding', '-fhooks', 'output,panic', '-o', 'app',
|
||||
'main.v'])
|
||||
assert files == ['main.v']
|
||||
}
|
||||
|
||||
fn test_new_preferences_from_args_defaults_to_host_target() {
|
||||
prefs := new_preferences_from_args(['main.v'])
|
||||
assert prefs.target_os == normalize_target_os_name(os.user_os())
|
||||
assert prefs.normalized_target_os() == normalize_target_os_name(os.user_os())
|
||||
assert !prefs.is_cross_target()
|
||||
assert !prefs.is_freestanding()
|
||||
assert 'cross' !in prefs.user_defines
|
||||
assert 'freestanding' !in prefs.user_defines
|
||||
assert prefs.explicit_user_defines.len == 0
|
||||
}
|
||||
|
||||
fn test_new_preferences_from_args_parses_macos_tiny_opt_out() {
|
||||
default_prefs := new_preferences_from_args(['-b', 'x64', '-os', 'macos', 'main.v'])
|
||||
assert default_prefs.macos_tiny
|
||||
|
||||
opt_out_prefs :=
|
||||
new_preferences_from_args(['-b', 'x64', '-no-mos-tiny', '-os', 'macos', 'main.v'])
|
||||
assert !opt_out_prefs.macos_tiny
|
||||
}
|
||||
|
||||
fn test_macos_tiny_opt_out_requires_macos_target() {
|
||||
validate_macos_tiny_flag_contract(['-no-mos-tiny'], 'macos')!
|
||||
|
||||
for target in ['linux', 'windows', 'cross', 'none'] {
|
||||
mut got_error := false
|
||||
validate_macos_tiny_flag_contract(['-no-mos-tiny'], target) or {
|
||||
got_error = true
|
||||
assert err.msg().contains('-no-mos-tiny requires a macOS target')
|
||||
}
|
||||
assert got_error, '-no-mos-tiny was accepted for target ${target}'
|
||||
}
|
||||
}
|
||||
|
||||
fn test_macos_tiny_opt_out_rejection_has_cli_error_prefix() {
|
||||
tmp_dir := os.join_path(os.vtmp_dir(), 'v2_pref_macos_tiny_error_${os.getpid()}')
|
||||
os.rmdir_all(tmp_dir) or {}
|
||||
os.mkdir_all(tmp_dir) or { panic(err) }
|
||||
defer {
|
||||
os.rmdir_all(tmp_dir) or {}
|
||||
}
|
||||
source_path := os.join_path(tmp_dir, 'main.v')
|
||||
os.write_file(source_path,
|
||||
"import v2.pref\n\nfn main() {\n\tpref.new_preferences_from_args(['-no-mos-tiny', '-os', 'linux', 'main.v'])\n}\n") or {
|
||||
panic(err)
|
||||
}
|
||||
vlib_path := os.join_path(os.dir(@VEXE), 'vlib')
|
||||
res :=
|
||||
os.execute('${os.quoted_path(@VEXE)} -path "${vlib_path}|@vlib|@vmodules" run ${os.quoted_path(source_path)}')
|
||||
assert res.exit_code == 1, res.output
|
||||
assert res.output.contains('error: -no-mos-tiny requires a macOS target'), res.output
|
||||
}
|
||||
|
||||
fn test_new_preferences_from_args_parses_target_os() {
|
||||
linux_prefs := new_preferences_from_args(['-os', 'linux', 'main.v'])
|
||||
assert linux_prefs.target_os == 'linux'
|
||||
assert linux_prefs.normalized_target_os() == 'linux'
|
||||
assert !linux_prefs.is_cross_target()
|
||||
|
||||
windows_prefs := new_preferences_from_args(['-os', 'windows', 'main.v'])
|
||||
assert windows_prefs.target_os == 'windows'
|
||||
assert windows_prefs.normalized_target_os() == 'windows'
|
||||
|
||||
termux_prefs := new_preferences_from_args(['-os', 'termux', 'main.v'])
|
||||
assert termux_prefs.target_os == 'termux'
|
||||
assert termux_prefs.normalized_target_os() == 'termux'
|
||||
assert comptime_flag_value(&termux_prefs, 'termux')
|
||||
}
|
||||
|
||||
fn test_new_preferences_from_args_normalizes_macos_target_aliases() {
|
||||
macos_prefs := new_preferences_from_args(['-os', 'macos', 'main.v'])
|
||||
assert macos_prefs.target_os == 'macos'
|
||||
assert macos_prefs.normalized_target_os() == 'macos'
|
||||
|
||||
darwin_prefs := new_preferences_from_args(['-os', 'darwin', 'main.v'])
|
||||
assert darwin_prefs.target_os == 'macos'
|
||||
assert darwin_prefs.normalized_target_os() == 'macos'
|
||||
|
||||
mac_prefs := new_preferences_from_args(['-os', 'mac', 'main.v'])
|
||||
assert mac_prefs.target_os == 'macos'
|
||||
assert mac_prefs.normalized_target_os() == 'macos'
|
||||
}
|
||||
|
||||
fn test_new_preferences_from_args_cross_is_not_concrete_os() {
|
||||
prefs := new_preferences_from_args(['-os', 'cross', 'main.v'])
|
||||
assert prefs.target_os == 'cross'
|
||||
assert prefs.normalized_target_os() == 'cross'
|
||||
assert prefs.is_cross_target()
|
||||
assert 'cross' in prefs.user_defines
|
||||
assert 'cross' !in prefs.explicit_user_defines
|
||||
assert comptime_flag_value(&prefs, 'cross')
|
||||
assert !comptime_optional_flag_value(&prefs, 'cross')
|
||||
assert !comptime_flag_value(&prefs, 'linux')
|
||||
assert !comptime_flag_value(&prefs, 'macos')
|
||||
assert !comptime_flag_value(&prefs, 'darwin')
|
||||
assert !comptime_flag_value(&prefs, 'windows')
|
||||
}
|
||||
|
||||
fn test_new_preferences_from_args_freestanding_is_distinct_from_cross() {
|
||||
prefs := new_preferences_from_args(['-freestanding', '-os', 'linux', 'main.v'])
|
||||
assert prefs.is_freestanding()
|
||||
assert !prefs.is_cross_target()
|
||||
assert prefs.normalized_target_os() == 'linux'
|
||||
assert comptime_flag_value(&prefs, 'freestanding')
|
||||
assert comptime_flag_value(&prefs, 'linux')
|
||||
assert !comptime_flag_value(&prefs, 'cross')
|
||||
assert 'freestanding' in prefs.user_defines
|
||||
assert 'freestanding' !in prefs.explicit_user_defines
|
||||
assert !comptime_optional_flag_value(&prefs, 'freestanding')
|
||||
}
|
||||
|
||||
fn test_new_preferences_from_args_accepts_freestanding_none_target() {
|
||||
prefs := new_preferences_from_args(['-freestanding', '-os', 'none', 'main.v'])
|
||||
assert prefs.is_freestanding()
|
||||
assert !prefs.is_cross_target()
|
||||
assert prefs.target_os == 'none'
|
||||
assert prefs.normalized_target_os() == 'none'
|
||||
assert prefs.source_filter_target_os() == 'none'
|
||||
assert !prefs.can_compile_cleanc_locally()
|
||||
assert comptime_flag_value(&prefs, 'freestanding')
|
||||
assert comptime_flag_value(&prefs, 'none')
|
||||
assert !comptime_optional_flag_value(&prefs, 'none')
|
||||
assert !comptime_flag_value(&prefs, 'cross')
|
||||
assert !comptime_flag_value(&prefs, 'linux')
|
||||
assert !comptime_flag_value(&prefs, 'macos')
|
||||
assert !comptime_flag_value(&prefs, 'windows')
|
||||
}
|
||||
|
||||
fn test_can_compile_cleanc_locally_follows_target_contract() {
|
||||
host := normalize_target_os_name(os.user_os())
|
||||
host_prefs := new_preferences_from_args(['-os', host, 'main.v'])
|
||||
assert host_prefs.can_compile_cleanc_locally()
|
||||
|
||||
cross_prefs := new_preferences_from_args(['-os', 'cross', 'main.v'])
|
||||
assert cross_prefs.can_compile_cleanc_locally()
|
||||
assert cross_prefs.source_filter_target_os() == host
|
||||
|
||||
freestanding_prefs := new_preferences_from_args(['-freestanding', '-os', host, 'main.v'])
|
||||
assert !freestanding_prefs.can_compile_cleanc_locally()
|
||||
assert freestanding_prefs.source_filter_target_os() == host
|
||||
|
||||
non_host := if host == 'windows' { 'linux' } else { 'windows' }
|
||||
non_host_prefs := new_preferences_from_args(['-os', non_host, 'main.v'])
|
||||
assert !non_host_prefs.can_compile_cleanc_locally()
|
||||
assert non_host_prefs.source_filter_target_os() == non_host
|
||||
}
|
||||
|
||||
fn test_can_run_target_binary_locally_follows_effective_target() {
|
||||
host := normalize_target_os_name(os.user_os())
|
||||
host_prefs := new_preferences_from_args(['-os', host, 'main.v'])
|
||||
assert host_prefs.can_run_target_binary_locally()
|
||||
|
||||
cross_prefs := new_preferences_from_args(['-os', 'cross', 'main.v'])
|
||||
assert cross_prefs.can_run_target_binary_locally()
|
||||
|
||||
native_cross_prefs := new_preferences_from_args(['-b', 'x64', '-os', 'cross', 'main.v'])
|
||||
assert native_cross_prefs.backend == .x64
|
||||
assert !native_cross_prefs.can_run_target_binary_locally()
|
||||
|
||||
freestanding_prefs := new_preferences_from_args(['-freestanding', '-os', host, 'main.v'])
|
||||
assert !freestanding_prefs.can_run_target_binary_locally()
|
||||
|
||||
mut native_freestanding_prefs := new_preferences_from_args(['-b', 'x64', '-freestanding', '-os',
|
||||
host, 'main.v'])
|
||||
native_freestanding_prefs.backend = .x64
|
||||
assert !native_freestanding_prefs.can_run_target_binary_locally()
|
||||
|
||||
non_host := if host == 'windows' { 'linux' } else { 'windows' }
|
||||
non_host_prefs := new_preferences_from_args(['-os', non_host, 'main.v'])
|
||||
assert !non_host_prefs.can_run_target_binary_locally()
|
||||
|
||||
mut native_non_host_prefs := new_preferences_from_args(['-b', 'x64', '-os', non_host, 'main.v'])
|
||||
native_non_host_prefs.backend = .x64
|
||||
assert !native_non_host_prefs.can_run_target_binary_locally()
|
||||
}
|
||||
|
||||
fn test_new_preferences_from_args_parses_freestanding_hooks() {
|
||||
prefs := new_preferences_from_args(['-freestanding', '-fhooks', 'output,panic', '-os', 'linux',
|
||||
'main.v'])
|
||||
assert prefs.is_freestanding()
|
||||
assert !prefs.skip_builtin
|
||||
assert prefs.freestanding_hook_list() == ['output', 'panic']
|
||||
assert prefs.has_freestanding_hooks()
|
||||
assert prefs.has_freestanding_hook('output')
|
||||
assert prefs.has_freestanding_hook('panic')
|
||||
assert !prefs.has_freestanding_hook('alloc')
|
||||
assert 'freestanding_hooks' in prefs.user_defines
|
||||
assert 'freestanding_output' in prefs.user_defines
|
||||
assert 'freestanding_hooks_output' in prefs.user_defines
|
||||
assert 'freestanding_panic' in prefs.user_defines
|
||||
assert 'freestanding_hooks_panic' in prefs.user_defines
|
||||
assert 'freestanding_alloc' !in prefs.user_defines
|
||||
assert 'freestanding_hooks_alloc' !in prefs.user_defines
|
||||
assert prefs.explicit_user_defines.len == 0
|
||||
assert comptime_flag_value(&prefs, 'freestanding_hooks')
|
||||
assert comptime_flag_value(&prefs, 'freestanding_output')
|
||||
assert comptime_flag_value(&prefs, 'freestanding_panic')
|
||||
assert !comptime_flag_value(&prefs, 'freestanding_alloc')
|
||||
}
|
||||
|
||||
fn test_new_preferences_from_args_uses_implicit_skip_builtin_only_without_freestanding_hooks() {
|
||||
prefs := new_preferences_from_args(['-freestanding', '-os', 'linux', 'main.v'])
|
||||
assert prefs.is_freestanding()
|
||||
assert prefs.skip_builtin
|
||||
assert !prefs.has_freestanding_hooks()
|
||||
|
||||
hook_prefs := new_preferences_from_args(['-freestanding', '-fhooks', 'output', '-os', 'linux',
|
||||
'main.v'])
|
||||
assert hook_prefs.is_freestanding()
|
||||
assert hook_prefs.has_freestanding_hook('output')
|
||||
assert !hook_prefs.skip_builtin
|
||||
|
||||
explicit_hook_prefs := new_preferences_from_args(['-freestanding', '-fhooks', 'output',
|
||||
'--skip-builtin', '-os', 'linux', 'main.v'])
|
||||
assert explicit_hook_prefs.has_freestanding_hook('output')
|
||||
assert explicit_hook_prefs.skip_builtin
|
||||
}
|
||||
|
||||
fn test_freestanding_hook_defines_do_not_grant_hook_capabilities() {
|
||||
prefs := new_preferences_from_args(['-freestanding', '-d', 'freestanding_output', '-d',
|
||||
'freestanding_panic', '-d', 'freestanding_alloc', 'main.v'])
|
||||
assert prefs.is_freestanding()
|
||||
assert !prefs.has_freestanding_hooks()
|
||||
assert !prefs.has_freestanding_hook('output')
|
||||
assert !prefs.has_freestanding_hook('panic')
|
||||
assert !prefs.has_freestanding_hook('alloc')
|
||||
assert 'freestanding_output' in prefs.user_defines
|
||||
assert 'freestanding_panic' in prefs.user_defines
|
||||
assert 'freestanding_alloc' in prefs.user_defines
|
||||
assert prefs.explicit_user_defines == ['freestanding_output', 'freestanding_panic',
|
||||
'freestanding_alloc']
|
||||
assert comptime_flag_value(&prefs, 'freestanding_output')
|
||||
assert comptime_flag_value(&prefs, 'freestanding_panic')
|
||||
assert comptime_flag_value(&prefs, 'freestanding_alloc')
|
||||
}
|
||||
|
||||
fn test_new_preferences_from_args_expands_minimal_freestanding_hooks() {
|
||||
prefs := new_preferences_from_args(['-freestanding', '-fhooks', 'minimal', 'main.v'])
|
||||
assert prefs.freestanding_hook_list() == ['output', 'panic', 'alloc']
|
||||
assert prefs.has_freestanding_hook('output')
|
||||
assert prefs.has_freestanding_hook('panic')
|
||||
assert prefs.has_freestanding_hook('alloc')
|
||||
assert comptime_flag_value(&prefs, 'freestanding_output')
|
||||
assert comptime_flag_value(&prefs, 'freestanding_panic')
|
||||
assert comptime_flag_value(&prefs, 'freestanding_alloc')
|
||||
}
|
||||
|
||||
fn test_new_preferences_using_options_accepts_target_os_and_freestanding() {
|
||||
prefs := new_preferences_using_options(['--cleanc', '--os-windows', '--freestanding'])
|
||||
assert prefs.backend == .cleanc
|
||||
assert prefs.target_os == 'windows'
|
||||
assert prefs.normalized_target_os() == 'windows'
|
||||
assert !prefs.is_cross_target()
|
||||
assert prefs.is_freestanding()
|
||||
assert comptime_flag_value(&prefs, 'windows')
|
||||
assert comptime_flag_value(&prefs, 'freestanding')
|
||||
assert 'freestanding' in prefs.user_defines
|
||||
assert 'freestanding' !in prefs.explicit_user_defines
|
||||
|
||||
termux_prefs := new_preferences_using_options(['--cleanc', '--os-termux'])
|
||||
assert termux_prefs.backend == .cleanc
|
||||
assert termux_prefs.target_os == 'termux'
|
||||
assert termux_prefs.normalized_target_os() == 'termux'
|
||||
assert comptime_flag_value(&termux_prefs, 'termux')
|
||||
assert !comptime_flag_value(&termux_prefs, 'android')
|
||||
}
|
||||
|
||||
fn test_new_preferences_using_options_accepts_freestanding_hooks() {
|
||||
prefs := new_preferences_using_options(['--cleanc', '--os-linux', '--freestanding',
|
||||
'--fhooks-output,alloc'])
|
||||
assert prefs.is_freestanding()
|
||||
assert prefs.freestanding_hook_list() == ['output', 'alloc']
|
||||
assert prefs.has_freestanding_hook('output')
|
||||
assert prefs.has_freestanding_hook('alloc')
|
||||
assert !prefs.has_freestanding_hook('panic')
|
||||
assert comptime_flag_value(&prefs, 'freestanding_output')
|
||||
assert comptime_flag_value(&prefs, 'freestanding_alloc')
|
||||
assert !comptime_flag_value(&prefs, 'freestanding_panic')
|
||||
}
|
||||
|
||||
fn test_new_preferences_using_options_accepts_freestanding_none_target() {
|
||||
prefs := new_preferences_using_options(['--cleanc', '--freestanding', '--os-none'])
|
||||
assert prefs.is_freestanding()
|
||||
assert !prefs.is_cross_target()
|
||||
assert prefs.target_os == 'none'
|
||||
assert prefs.source_filter_target_os() == 'none'
|
||||
assert !prefs.can_compile_cleanc_locally()
|
||||
}
|
||||
|
||||
fn test_new_preferences_using_options_accepts_cross_target() {
|
||||
prefs := new_preferences_using_options(['--cleanc', '--os-cross'])
|
||||
assert prefs.target_os == 'cross'
|
||||
assert prefs.is_cross_target()
|
||||
assert 'cross' in prefs.user_defines
|
||||
assert 'cross' !in prefs.explicit_user_defines
|
||||
assert comptime_flag_value(&prefs, 'cross')
|
||||
assert !comptime_optional_flag_value(&prefs, 'cross')
|
||||
assert !comptime_flag_value(&prefs, 'linux')
|
||||
assert !comptime_flag_value(&prefs, 'macos')
|
||||
assert !comptime_flag_value(&prefs, 'windows')
|
||||
}
|
||||
|
||||
fn test_new_preferences_from_args_accepted_targets_match_comptime_flags() {
|
||||
for target in ['linux', 'macos', 'darwin', 'mac', 'windows', 'freebsd', 'openbsd', 'netbsd',
|
||||
'dragonfly', 'android', 'termux', 'ios', 'solaris', 'qnx', 'serenity', 'plan9', 'vinix'] {
|
||||
prefs := new_preferences_from_args(['-os', target, 'main.v'])
|
||||
flag_name := match target {
|
||||
'darwin', 'mac' { 'macos' }
|
||||
else { prefs.normalized_target_os() }
|
||||
}
|
||||
|
||||
assert comptime_flag_value(&prefs, flag_name), '${target} should match ${flag_name}'
|
||||
}
|
||||
}
|
||||
|
||||
fn test_cross_and_freestanding_still_allow_user_defines() {
|
||||
mut prefs := new_preferences()
|
||||
prefs.user_defines = ['cross', 'freestanding']
|
||||
prefs.explicit_user_defines = ['cross', 'freestanding']
|
||||
assert comptime_flag_value(&prefs, 'cross')
|
||||
assert comptime_flag_value(&prefs, 'freestanding')
|
||||
assert comptime_optional_flag_value(&prefs, 'cross')
|
||||
assert comptime_optional_flag_value(&prefs, 'freestanding')
|
||||
}
|
||||
|
||||
fn test_explicit_user_defines_are_separate_from_synthesized_defines() {
|
||||
cross_prefs := new_preferences_from_args(['-os', 'cross', '-d', 'cross', 'main.v'])
|
||||
assert 'cross' in cross_prefs.user_defines
|
||||
assert 'cross' in cross_prefs.explicit_user_defines
|
||||
assert comptime_optional_flag_value(&cross_prefs, 'cross')
|
||||
|
||||
free_prefs := new_preferences_from_args(['-freestanding', '-d', 'freestanding', 'main.v'])
|
||||
assert 'freestanding' in free_prefs.user_defines
|
||||
assert 'freestanding' in free_prefs.explicit_user_defines
|
||||
assert comptime_optional_flag_value(&free_prefs, 'freestanding')
|
||||
|
||||
none_prefs :=
|
||||
new_preferences_from_args(['-freestanding', '-os', 'none', '-d', 'none', 'main.v'])
|
||||
assert none_prefs.target_os == 'none'
|
||||
assert 'none' in none_prefs.explicit_user_defines
|
||||
assert comptime_optional_flag_value(&none_prefs, 'none')
|
||||
|
||||
bare_prefs := new_preferences_from_args(['-d', 'bare', 'main.v'])
|
||||
assert 'bare' in bare_prefs.explicit_user_defines
|
||||
assert comptime_optional_flag_value(&bare_prefs, 'bare')
|
||||
}
|
||||
|
||||
fn test_file_suffix_filter_cross_target_contract() {
|
||||
assert !file_has_incompatible_os_suffix('common.v', 'cross')
|
||||
assert file_has_incompatible_os_suffix('platform_linux.v', 'cross')
|
||||
assert file_has_incompatible_os_suffix('platform_macos.v', 'cross')
|
||||
assert file_has_incompatible_os_suffix('platform_darwin.v', 'cross')
|
||||
assert file_has_incompatible_os_suffix('platform_windows.v', 'cross')
|
||||
assert file_has_incompatible_os_suffix('platform_termux.c.v', 'cross')
|
||||
assert !file_has_incompatible_os_suffix('platform_nix.v', 'cross')
|
||||
}
|
||||
|
||||
fn test_file_suffix_filter_termux_is_selected_only_for_termux_target() {
|
||||
for target in ['linux', 'macos', 'windows', 'android', 'ios', 'freebsd', 'openbsd', 'netbsd',
|
||||
'dragonfly', 'solaris', 'qnx', 'serenity', 'plan9', 'vinix'] {
|
||||
assert file_has_incompatible_os_suffix('platform_termux.c.v', target)
|
||||
}
|
||||
assert !file_has_incompatible_os_suffix('platform_termux.c.v', 'termux')
|
||||
assert !file_has_incompatible_os_suffix('platform_android.c.v', 'termux')
|
||||
assert !file_has_incompatible_os_suffix('platform_android_outside_termux.c.v', 'android')
|
||||
assert file_has_incompatible_os_suffix('platform_android_outside_termux.c.v', 'termux')
|
||||
}
|
||||
|
||||
fn test_file_suffix_filter_none_target_excludes_all_os_variants() {
|
||||
assert !file_has_incompatible_os_suffix('common.v', 'none')
|
||||
assert file_has_incompatible_os_suffix('platform_linux.v', 'none')
|
||||
assert file_has_incompatible_os_suffix('platform_macos.v', 'none')
|
||||
assert file_has_incompatible_os_suffix('platform_darwin.v', 'none')
|
||||
assert file_has_incompatible_os_suffix('platform_windows.v', 'none')
|
||||
assert file_has_incompatible_os_suffix('platform_nix.v', 'none')
|
||||
assert file_has_incompatible_os_suffix('platform_termux.c.v', 'none')
|
||||
assert file_has_incompatible_os_suffix('platform_bsd.v', 'none')
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
module pref
|
||||
|
||||
pub fn normalize_target_os_name(target_os string) string {
|
||||
return match target_os.to_lower() {
|
||||
'darwin', 'mac' { 'macos' }
|
||||
else { target_os.to_lower() }
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_current_os_name(current_os string) string {
|
||||
return normalize_target_os_name(current_os)
|
||||
}
|
||||
|
||||
fn file_has_termux_os_suffix(file string) bool {
|
||||
return file.contains('_termux.') && !file.contains('_android_outside_termux.')
|
||||
}
|
||||
|
||||
fn file_has_known_os_suffix(file string) bool {
|
||||
return file.contains('_nix.') || file.contains('_windows.') || file.contains('_linux.')
|
||||
|| file.contains('_macos.') || file.contains('_darwin.') || file.contains('_bsd.')
|
||||
|| file.contains('_android') || file_has_termux_os_suffix(file) || file.contains('_ios.')
|
||||
|| file.contains('_freebsd.') || file.contains('_openbsd.') || file.contains('_netbsd.')
|
||||
|| file.contains('_dragonfly.') || file.contains('_solaris.') || file.contains('_qnx.')
|
||||
|| file.contains('_serenity.') || file.contains('_plan9.') || file.contains('_vinix.')
|
||||
}
|
||||
|
||||
// file_has_incompatible_os_suffix reports whether file is specialized for a different OS.
|
||||
pub fn file_has_incompatible_os_suffix(file string, current_os string) bool {
|
||||
os_name := normalize_current_os_name(current_os)
|
||||
if os_name == 'none' {
|
||||
return file_has_known_os_suffix(file)
|
||||
}
|
||||
if os_name == 'windows' && file.contains('_nix.') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'windows' && file.contains('_windows.') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'linux' && file.contains('_linux.') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'macos' && (file.contains('_macos.') || file.contains('_darwin.')) {
|
||||
return true
|
||||
}
|
||||
if os_name !in ['macos', 'freebsd', 'openbsd', 'netbsd', 'dragonfly'] && file.contains('_bsd.') {
|
||||
return true
|
||||
}
|
||||
if file.contains('_android_outside_termux.') {
|
||||
return os_name != 'android'
|
||||
}
|
||||
if os_name !in ['android', 'termux'] && file.contains('_android') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'termux' && file_has_termux_os_suffix(file) {
|
||||
return true
|
||||
}
|
||||
if os_name != 'ios' && file.contains('_ios.') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'freebsd' && file.contains('_freebsd.') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'openbsd' && file.contains('_openbsd.') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'netbsd' && file.contains('_netbsd.') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'dragonfly' && file.contains('_dragonfly.') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'solaris' && file.contains('_solaris.') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'qnx' && file.contains('_qnx.') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'serenity' && file.contains('_serenity.') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'plan9' && file.contains('_plan9.') {
|
||||
return true
|
||||
}
|
||||
if os_name != 'vinix' && file.contains('_vinix.') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in new issue
Block a user