v / vlib / v2 / pref / comptime.v
245 lines · 237 sloc · 6.4 KB · b831b0eec9b5b2756784b5dabf3808d47d6a39ae
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module pref
5
6import os
7
8fn is_target_or_mode_comptime_flag(name string) bool {
9 return name in [
10 'macos',
11 'darwin',
12 'mac',
13 'linux',
14 'windows',
15 'bsd',
16 'freebsd',
17 'openbsd',
18 'netbsd',
19 'dragonfly',
20 'android',
21 'termux',
22 'ios',
23 'solaris',
24 'qnx',
25 'serenity',
26 'plan9',
27 'vinix',
28 'cross',
29 'none',
30 'freestanding',
31 'bare',
32 ]
33}
34
35pub fn define_list_contains(defines []string, name string) bool {
36 lower_name := name.to_lower()
37 return name in defines || lower_name in defines
38}
39
40// pkgconfig_result runs pkg-config with `args` and returns stdout on success.
41pub fn pkgconfig_result(args []string) ?string {
42 if args.len == 0 {
43 return none
44 }
45 mut quoted_args := []string{cap: args.len}
46 for arg in args {
47 quoted_args << os.quoted_path(arg)
48 }
49 result := os.execute('pkg-config ${quoted_args.join(' ')}')
50 if result.exit_code != 0 {
51 return none
52 }
53 return result.output.trim_space()
54}
55
56// comptime_pkgconfig_value evaluates `$pkgconfig('name')` in compile-time
57// conditions. Missing pkg-config packages are false, matching v1 behavior.
58pub fn comptime_pkgconfig_value(name string) bool {
59 if _ := pkgconfig_result(['--exists', name]) {
60 return true
61 }
62 return false
63}
64
65// comptime_flag_value evaluates a plain comptime flag identifier, as it would
66// appear in `$if name {` or `@[if name]`. Use comptime_optional_flag_value for
67// the optional `name ?` form. Shared between the parser (struct field
68// conditionals) and the transformer (statement / expression $if).
69//
70// `pref` may be `nil` for early uses (some tests construct partial state);
71// flags that depend on backend / user_defines then evaluate to `false`.
72pub fn comptime_flag_value(pref &Preferences, name string) bool {
73 match name {
74 'macos', 'darwin', 'mac' {
75 return pref.normalized_target_os() == 'macos'
76 }
77 'linux' {
78 return pref.normalized_target_os() == 'linux'
79 }
80 'windows' {
81 return pref.normalized_target_os() == 'windows'
82 }
83 'bsd' {
84 return pref.normalized_target_os() in [
85 'macos',
86 'freebsd',
87 'openbsd',
88 'netbsd',
89 'dragonfly',
90 ]
91 }
92 'freebsd' {
93 return pref.normalized_target_os() == 'freebsd'
94 }
95 'openbsd' {
96 return pref.normalized_target_os() == 'openbsd'
97 }
98 'netbsd' {
99 return pref.normalized_target_os() == 'netbsd'
100 }
101 'dragonfly' {
102 return pref.normalized_target_os() == 'dragonfly'
103 }
104 'android' {
105 return pref.normalized_target_os() == 'android'
106 }
107 'termux' {
108 return pref.normalized_target_os() == 'termux'
109 }
110 'ios' {
111 return pref.normalized_target_os() == 'ios'
112 }
113 'solaris' {
114 return pref.normalized_target_os() == 'solaris'
115 }
116 'qnx' {
117 return pref.normalized_target_os() == 'qnx'
118 }
119 'serenity' {
120 return pref.normalized_target_os() == 'serenity'
121 }
122 'plan9' {
123 return pref.normalized_target_os() == 'plan9'
124 }
125 'vinix' {
126 return pref.normalized_target_os() == 'vinix'
127 }
128 'x64', 'amd64' {
129 $if amd64 {
130 return true
131 }
132 return false
133 }
134 'arm64', 'aarch64' {
135 $if arm64 {
136 return true
137 }
138 return false
139 }
140 'little_endian' {
141 $if little_endian {
142 return true
143 }
144 return false
145 }
146 'big_endian' {
147 $if big_endian {
148 return true
149 }
150 return false
151 }
152 'debug' {
153 $if debug {
154 return true
155 }
156 return false
157 }
158 'native' {
159 return pref != unsafe { nil } && (pref.backend == .arm64 || pref.backend == .x64)
160 }
161 'v2_native_windows_pe_minimal' {
162 return pref != unsafe { nil } && pref.backend == .x64
163 && pref.get_effective_arch() == .x64 && pref.normalized_target_os() == 'windows'
164 }
165 // Native backend cannot resolve C.stdout/C.stderr data symbols through GOT,
166 // so use C.write() instead of fwrite() for I/O operations.
167 'builtin_write_buf_to_fd_should_use_c_write' {
168 return pref != unsafe { nil } && (pref.backend == .arm64 || pref.backend == .x64)
169 }
170 'tinyc' {
171 // For native backends, inline assembly from V source is not supported
172 // by the SSA builder. Pretend we're TinyCC so that `$if arm64 && !tinyc`
173 // guards select the software fallback path instead of inline asm.
174 return pref != unsafe { nil } && (pref.backend == .arm64 || pref.backend == .x64)
175 }
176 'no_backtrace' {
177 return pref != unsafe { nil } && ((pref.backend == .arm64 || pref.backend == .x64)
178 || name in pref.user_defines)
179 }
180 'prealloc' {
181 return pref != unsafe { nil } && pref.prealloc
182 }
183 'cross' {
184 return pref != unsafe { nil } && (pref.is_cross_target() || name in pref.user_defines)
185 }
186 'none' {
187 return pref != unsafe { nil } && pref.normalized_target_os() == 'none'
188 }
189 'freestanding' {
190 return pref != unsafe { nil } && (pref.is_freestanding() || name in pref.user_defines)
191 }
192 'freestanding_hooks' {
193 return pref != unsafe { nil }
194 && (pref.has_freestanding_hooks() || name in pref.user_defines)
195 }
196 'freestanding_output' {
197 return pref != unsafe { nil }
198 && (pref.has_freestanding_hook('output') || name in pref.user_defines)
199 }
200 'freestanding_panic' {
201 return pref != unsafe { nil }
202 && (pref.has_freestanding_hook('panic') || name in pref.user_defines)
203 }
204 'freestanding_alloc' {
205 return pref != unsafe { nil }
206 && (pref.has_freestanding_hook('alloc') || name in pref.user_defines)
207 }
208 'new_int', 'gcboehm', 'autofree', 'ppc64' {
209 return false
210 }
211 else {
212 if pref != unsafe { nil } && define_list_contains(pref.user_defines, name) {
213 return true
214 }
215 return false
216 }
217 }
218}
219
220pub fn comptime_optional_define_value(name string, user_defines []string, explicit_user_defines []string) bool {
221 if define_list_contains(explicit_user_defines, name) {
222 return true
223 }
224 if is_target_or_mode_comptime_flag(name.to_lower()) {
225 return false
226 }
227 return define_list_contains(user_defines, name)
228}
229
230// comptime_optional_flag_value evaluates the `name ?` form. Unlike a plain
231// `$if name`, target OS and target-mode flags must not become true implicitly.
232// Non-target compiler capability flags keep their normal value, because V's
233// builtin/runtime code uses the optional form for guarded internal fallbacks.
234pub fn comptime_optional_flag_value(pref &Preferences, name string) bool {
235 if pref == unsafe { nil } {
236 return false
237 }
238 if comptime_optional_define_value(name, pref.user_defines, pref.explicit_user_defines) {
239 return true
240 }
241 if is_target_or_mode_comptime_flag(name.to_lower()) {
242 return false
243 }
244 return comptime_flag_value(pref, name)
245}
246