246 lines
6.4 KiB
V
246 lines
6.4 KiB
V
// 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)
|
|
}
|