module pref import os pub struct Preferences { pub mut: verbose bool output_file string target_os string = os.user_os() user_defines []string backend string = 'c' vroot string = detect_vroot() } pub fn new_preferences() &Preferences { return &Preferences{} } fn detect_vroot() string { baked_root := @VMODROOT if baked_root.len > 0 { return baked_root } if os.args.len > 0 && os.args[0].len > 0 { vroot := detect_vroot_from(os.args[0]) if vroot.len > 0 { return vroot } } return detect_vroot_from(os.getwd()) } fn detect_vroot_from(start string) string { if start.len == 0 { return '' } mut dir := start if !os.is_abs_path(dir) { cwd := os.getwd() if cwd.len > 0 { dir = os.join_path_single(cwd, dir) } } if !os.is_dir(dir) { dir = os.dir(dir) } for _ in 0 .. 8 { if os.is_dir(os.join_path_single(os.join_path_single(dir, 'vlib'), 'builtin')) { return dir } parent := os.dir(dir) if parent == dir { break } dir = parent } return '' } pub fn (p &Preferences) get_vlib_module_path(mod string) string { mod_path := mod.replace('.', os.path_separator) return os.join_path_single(os.join_path_single(p.vroot, 'vlib'), mod_path) } pub fn (p &Preferences) get_module_path(mod string, importing_file_path string) string { mod_path := mod.replace('.', os.path_separator) relative_path := os.join_path_single(os.dir(importing_file_path), mod_path) if os.is_dir(relative_path) { return relative_path } vlib_path := os.join_path_single(os.join_path_single(p.vroot, 'vlib'), mod_path) if os.is_dir(vlib_path) { return vlib_path } return '' } pub fn file_has_incompatible_os_suffix(file string, current_os string) bool { os_name := normalized_os(current_os) 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 != 'macos' && os_name != 'freebsd' && os_name != 'openbsd' && os_name != 'netbsd' && os_name != 'dragonfly' && file.contains('_bsd.') { return true } if os_name != 'android' && file.contains('_android') { 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 file.contains('.amd64.') || file.contains('_amd64.') || file.contains('.arm64.') || file.contains('_arm64.') { return true } return false } pub fn get_v_files_from_dir(dir string, user_defines []string, target_os string) []string { if dir == '' || !os.is_dir(dir) { return []string{} } all_files := os.ls(dir) or { return []string{} } mut has_os_specific := map[string]bool{} for file in all_files { if !file.ends_with('.v') || file.ends_with('.js.v') || file.contains('_test.') { continue } if file_has_incompatible_os_suffix(file, target_os) { continue } if base := os_specific_base(file, target_os) { has_os_specific[base] = true } } mut v_files := []string{} for file in all_files { if !file.ends_with('.v') || file.ends_with('.js.v') || file.contains('_test.') { continue } if file_has_incompatible_os_suffix(file, target_os) { continue } if base := default_file_base(file) { if has_os_specific[base] { continue } } if file.contains('_notd_') { feature := extract_define_feature(file, '_notd_') if feature.len > 0 && feature in user_defines { continue } } else if file.contains('_d_') { feature := extract_define_feature(file, '_d_') if feature.len == 0 || feature !in user_defines { continue } } v_files << os.join_path_single(dir, file) } return v_files } fn default_file_base(file string) ?string { for marker in ['_default.c.v', '_default.v'] { if file.ends_with(marker) { return file[..file.len - marker.len] } } return none } fn os_specific_base(file string, target_os string) ?string { if _ := default_file_base(file) { return none } mut suffixes := []string{} os_name := normalized_os(target_os) if os_name != 'windows' { suffixes << '_nix' } match os_name { 'windows' { suffixes << '_windows' } 'macos' { suffixes << '_macos' suffixes << '_darwin' } 'linux' { suffixes << '_linux' } 'android' { suffixes << '_android' } 'ios' { suffixes << '_ios' } 'freebsd' { suffixes << '_freebsd' suffixes << '_bsd' } 'openbsd' { suffixes << '_openbsd' suffixes << '_bsd' } 'netbsd' { suffixes << '_netbsd' suffixes << '_bsd' } 'dragonfly' { suffixes << '_dragonfly' suffixes << '_bsd' } 'solaris' { suffixes << '_solaris' } else {} } for suffix in suffixes { for ext in ['.c.v', '.v'] { marker := suffix + ext if file.ends_with(marker) { return file[..file.len - marker.len] } } } return none } fn extract_define_feature(file string, marker string) string { idx := file.index(marker) or { return '' } rest := file[idx + marker.len..] if rest.ends_with('.c.v') { return rest[..rest.len - 4] } if rest.ends_with('.v') { return rest[..rest.len - 2] } return rest } pub fn normalized_os(target_os string) string { return match target_os { 'darwin' { 'macos' } 'mac' { 'macos' } else { target_os } } } pub fn (p &Preferences) normalized_target_os() string { return normalized_os(p.target_os) } pub fn (p &Preferences) is_cross_target() bool { return p.normalized_target_os() != normalized_os(os.user_os()) } pub fn comptime_flag_value(p &Preferences, name string) bool { match name { 'macos', 'darwin', 'mac' { return p.normalized_target_os() == 'macos' } 'linux' { return p.normalized_target_os() == 'linux' } 'windows' { return p.normalized_target_os() == 'windows' } 'freebsd' { return p.normalized_target_os() == 'freebsd' } 'openbsd' { return p.normalized_target_os() == 'openbsd' } 'netbsd' { return p.normalized_target_os() == 'netbsd' } 'dragonfly' { return p.normalized_target_os() == 'dragonfly' } 'android' { return p.normalized_target_os() == 'android' } 'posix', 'unix' { return p.normalized_target_os() != 'windows' } 'bsd' { tos := p.normalized_target_os() return tos == 'macos' || tos == 'freebsd' || tos == 'openbsd' || tos == 'netbsd' || tos == 'dragonfly' } '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 p.backend == 'arm64' } 'builtin_write_buf_to_fd_should_use_c_write' { return p.backend == 'arm64' } 'tinyc' { return p.backend == 'arm64' } 'no_backtrace' { return p.backend == 'arm64' || name in p.user_defines } 'gcboehm', 'gcboehm_opt', 'prealloc', 'autofree', 'no_bounds_checking', 'freestanding', 'nofloat' { return name in p.user_defines } else { return name in p.user_defines } } } pub fn comptime_optional_flag_value(p &Preferences, name string) bool { if name in p.user_defines { return true } return comptime_flag_value(p, name) } pub fn comptime_pkgconfig_value(name string) bool { result := os.execute('pkg-config --exists ${name}') return result.exit_code == 0 }