v / vlib / v2 / pref / pref.v
728 lines · 683 sloc · 23.81 KB · e8810a671233f1d2072d70ab8e17c3b1dad5e1e5
Raw
1// Copyright (c) 2020-2024 Joe Conigliaro. 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
7import os.cmdline
8
9pub enum Backend {
10 v // V source output (default)
11 eval // AST interpreter
12 cleanc // Clean C backend (AST -> C)
13 c // SSA -> C backend
14 x64 // Native x64/AMD64 backend
15 arm64 // Native ARM64 backend
16}
17
18pub enum Arch {
19 auto // Auto-detect based on OS
20 x64
21 arm64
22}
23
24// GarbageCollectionMode controls which garbage collector is used.
25// Translated from Go's runtime GC, the `vgc` mode provides a concurrent
26// tri-color mark-and-sweep collector written in pure V.
27pub enum GarbageCollectionMode {
28 no_gc // no garbage collection
29 vgc // V GC: concurrent tri-color mark-and-sweep (translated from Go's runtime GC)
30 boehm // Boehm-Demers-Weiser conservative GC (legacy)
31}
32
33pub struct Preferences {
34pub mut:
35 debug bool
36 verbose bool
37 ownership bool // -ownership: enable ownership checking for strings
38 skip_genv bool
39 skip_builtin bool
40 skip_imports bool
41 skip_type_check bool // Skip type checking phase (for backends that don't need it yet)
42 no_parallel bool // when true, run type check sequentially (default: parallel)
43 no_parallel_transform bool // when true, run transform sequentially (default: parallel)
44 no_cache bool // Disable build cache
45 no_markused bool // Disable markused stage and dead-function pruning
46 show_cc bool // Print C compiler command(s)
47 stats bool // Print extended statistics
48 print_parsed_files bool // Print all parsed files grouped by full/.vh parse mode
49 keep_c bool // Keep generated C file after compilation
50 use_context_allocator bool // Use context allocator for heap allocations (enables profiling)
51 is_shared_lib bool // Compile to shared library (.dylib/.so) for live reload
52 no_optimize bool = true // skip SSA optimization (mem2reg, phi elimination); cleared by -prod
53 is_prod bool // -prod: enable SSA optimization + -O3 -flto for C compiler
54 prealloc bool // -prealloc: use arena allocation (bump-pointer, not thread-safe)
55 gc_mode GarbageCollectionMode // Garbage collection mode (-gc flag)
56 backend Backend
57 arch Arch = .auto
58 target_os string = os.user_os()
59 output_cross_c bool // -os cross: keep generated C portable
60 freestanding bool // -freestanding: target a platform contract without an OS runtime
61 freestanding_hooks []string // -fhooks: explicit freestanding platform capabilities
62 macos_tiny bool = true // -no-mos-tiny: disable the automatic macOS x64 tiny object path
63 output_file string
64 printfn_list []string // List of function names whose generated C source should be printed
65 user_defines []string // All active comptime flags, including compiler-synthesized flags.
66 explicit_user_defines []string // User-defined comptime flags from explicit -d <name> only.
67 hot_fn string // Extract raw machine code for this function only (hot reload)
68 single_backend bool // Only include the selected backend (strip other backends from binary)
69 eval_runtime_args []string // Program argv exposed to the eval backend
70 ccompiler string // C compiler override (-cc flag)
71pub:
72 vroot string = detect_vroot()
73 vmodules_path string = os.vmodules_dir()
74}
75
76fn dir_exists(path string) bool {
77 if path.len == 0 {
78 return false
79 }
80 if os.is_dir(path) {
81 return true
82 }
83 _ := os.ls(path) or { return false }
84 return true
85}
86
87fn detect_vroot_from(start string) string {
88 if start.len == 0 {
89 return ''
90 }
91 mut dir := start
92 // Avoid os.abs_path during bootstrap; normalize relative paths manually.
93 if !os.is_abs_path(dir) {
94 cwd := os.getwd()
95 if cwd.len > 0 {
96 dir = os.join_path(cwd, dir)
97 }
98 }
99 if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
100 return dir
101 }
102 dir = os.dir(dir)
103 if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
104 return dir
105 }
106 dir = os.dir(dir)
107 if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
108 return dir
109 }
110 dir = os.dir(dir)
111 if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
112 return dir
113 }
114 dir = os.dir(dir)
115 if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
116 return dir
117 }
118 dir = os.dir(dir)
119 if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
120 return dir
121 }
122 dir = os.dir(dir)
123 if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
124 return dir
125 }
126 dir = os.dir(dir)
127 if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
128 return dir
129 }
130 dir = os.dir(dir)
131 if dir_exists(os.join_path(dir, 'vlib', 'builtin')) {
132 return dir
133 }
134 return ''
135}
136
137fn detect_vroot() string {
138 // First prefer the compile-time module root when it points to a valid
139 // V source tree. This keeps self-host binaries stable during bootstrapping.
140 compile_time_root := @VMODROOT
141 if compile_time_root.len > 0 {
142 // Trust @VMODROOT without runtime validation. During bootstrapping
143 // (v2 → v3 via ARM64), os.join_path/os.is_dir may not work correctly
144 // in the native binary, but the compile-time root is always correct.
145 return compile_time_root
146 }
147 // Prefer deriving from executable path: <vroot>/cmd/v2/v3.
148 if os.args.len > 0 && os.args[0].len > 0 {
149 vroot := detect_vroot_from(os.args[0])
150 if vroot.len > 0 {
151 return vroot
152 }
153 }
154 // Fallback to current directory ancestry.
155 cwd := os.getwd()
156 vroot := detect_vroot_from(cwd)
157 if vroot.len > 0 {
158 return vroot
159 }
160 // Final fallback: keep old behavior for unusual bootstrap contexts.
161 if os.args.len > 0 && os.args[0].len > 0 {
162 if os.is_abs_path(os.args[0]) {
163 return os.dir(os.args[0])
164 }
165 return cwd
166 }
167 return cwd
168}
169
170pub fn new_preferences() Preferences {
171 return Preferences{
172 backend: .cleanc
173 }
174}
175
176pub fn (p &Preferences) target_os_or_host() string {
177 if p == unsafe { nil } || p.target_os == '' {
178 return os.user_os()
179 }
180 return p.target_os
181}
182
183pub fn (p &Preferences) normalized_target_os() string {
184 return normalize_target_os_name(p.target_os_or_host())
185}
186
187pub fn (p &Preferences) is_cross_target() bool {
188 return p != unsafe { nil } && (p.output_cross_c || p.normalized_target_os() == 'cross')
189}
190
191pub fn (p &Preferences) is_freestanding() bool {
192 return p != unsafe { nil } && p.freestanding
193}
194
195pub fn (p &Preferences) can_compile_cleanc_locally() bool {
196 if p == unsafe { nil } {
197 return true
198 }
199 if p.is_freestanding() {
200 return false
201 }
202 if p.is_cross_target() {
203 return true
204 }
205 return p.normalized_target_os() == normalize_target_os_name(os.user_os())
206}
207
208pub fn (p &Preferences) can_run_target_binary_locally() bool {
209 if p == unsafe { nil } {
210 return true
211 }
212 if p.is_freestanding() {
213 return false
214 }
215 if p.backend == .cleanc && p.is_cross_target() {
216 return true
217 }
218 if p.normalized_target_os() != normalize_target_os_name(os.user_os()) {
219 return false
220 }
221 return true
222}
223
224pub fn (p &Preferences) source_filter_target_os() string {
225 if p == unsafe { nil } {
226 return normalize_target_os_name(os.user_os())
227 }
228 if p.is_cross_target() {
229 return normalize_target_os_name(os.user_os())
230 }
231 return p.target_os_or_host()
232}
233
234pub fn (p &Preferences) freestanding_hook_list() []string {
235 if p == unsafe { nil } {
236 return []string{}
237 }
238 return p.freestanding_hooks.clone()
239}
240
241pub fn (p &Preferences) has_freestanding_hooks() bool {
242 return p != unsafe { nil } && p.freestanding_hooks.len > 0
243}
244
245pub fn (p &Preferences) has_freestanding_hook(name string) bool {
246 if p == unsafe { nil } {
247 return false
248 }
249 normalized := normalize_freestanding_hook_name(name) or { return false }
250 return normalized in p.freestanding_hooks
251}
252
253fn normalize_cli_target_os(target_os string) string {
254 normalized := normalize_target_os_name(target_os)
255 match normalized {
256 'linux', 'macos', 'windows', 'cross', 'none', 'freebsd', 'openbsd', 'netbsd', 'dragonfly',
257 'android', 'termux', 'ios', 'solaris', 'qnx', 'serenity', 'plan9', 'vinix' {
258 return normalized
259 }
260 else {
261 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')
262 exit(1)
263 }
264 }
265}
266
267fn validate_target_contract(target_os string, freestanding bool) {
268 if target_os == 'none' && !freestanding {
269 eprintln('error: -os none requires -freestanding')
270 exit(1)
271 }
272 if target_os == 'cross' && freestanding {
273 eprintln('error: -freestanding -os cross is not supported; use -os none for pure freestanding targets')
274 exit(1)
275 }
276}
277
278fn validate_macos_tiny_flag_contract(options []string, target_os string) ! {
279 if '-no-mos-tiny' in options && target_os != 'macos' {
280 return error('-no-mos-tiny requires a macOS target; use -os macos or run on a macOS host')
281 }
282}
283
284fn normalize_freestanding_hook_name(name string) ?string {
285 normalized := name.trim_space().to_lower()
286 return match normalized {
287 'output', 'panic', 'alloc' {
288 normalized
289 }
290 else {
291 none
292 }
293 }
294}
295
296fn append_freestanding_hook(mut hooks []string, hook string) {
297 normalized := normalize_freestanding_hook_name(hook) or {
298 eprintln('error: unknown freestanding hook `${hook}`. Valid hooks: output, panic, alloc, minimal')
299 exit(1)
300 }
301 if normalized !in hooks {
302 hooks << normalized
303 }
304}
305
306fn parse_freestanding_hooks(value string) []string {
307 mut hooks := []string{}
308 for raw_part in value.split(',') {
309 part := raw_part.trim_space()
310 if part == '' {
311 continue
312 }
313 if part == 'minimal' {
314 for hook in ['output', 'panic', 'alloc'] {
315 append_freestanding_hook(mut hooks, hook)
316 }
317 continue
318 }
319 append_freestanding_hook(mut hooks, part)
320 }
321 return hooks
322}
323
324fn add_freestanding_hook_defines(mut defines []string, hooks []string) {
325 if hooks.len == 0 {
326 return
327 }
328 if 'freestanding_hooks' !in defines {
329 defines << 'freestanding_hooks'
330 }
331 for hook in hooks {
332 for define in ['freestanding_${hook}', 'freestanding_hooks_${hook}'] {
333 if define !in defines {
334 defines << define
335 }
336 }
337 }
338}
339
340pub fn source_files_from_args(args []string) []string {
341 options_with_values := ['-backend', '-b', '-o', '-output', '-arch', '-printfn', '-gc', '-d',
342 '-hot-fn', '-cc', '-os', '-fhooks']
343 mut files := []string{}
344 mut skip_next := false
345 for arg in args {
346 if arg == '--' {
347 break
348 }
349 if skip_next {
350 skip_next = false
351 continue
352 }
353 if arg.starts_with('-') {
354 if arg in options_with_values {
355 skip_next = true
356 }
357 continue
358 }
359 files << arg
360 }
361 return files
362}
363
364// new_preferences_from_args parses full args list including option values
365pub fn new_preferences_from_args(args []string) Preferences {
366 // Default backend is cleanc
367 default_backend := 'cleanc'
368 mut backend_str_long := cmdline.option(args, '-backend', '')
369 if backend_str_long.len == 0 {
370 backend_str_long = ''
371 }
372 mut backend_str_short := cmdline.option(args, '-b', '')
373 if backend_str_short.len == 0 {
374 backend_str_short = ''
375 }
376 backend_str := if backend_str_long.len > 0 {
377 backend_str_long
378 } else if backend_str_short.len > 0 {
379 backend_str_short
380 } else {
381 default_backend
382 }
383 mut backend := Backend.cleanc
384 match backend_str {
385 'eval' {
386 backend = .eval
387 }
388 'cleanc' {
389 backend = .cleanc
390 }
391 'c' {
392 backend = .c
393 }
394 'v' {
395 backend = .v
396 }
397 'arm64' {
398 backend = .arm64
399 }
400 'x64' {
401 backend = .x64
402 }
403 else {
404 eprintln('error: unknown backend `${backend_str}`. Valid backends: eval, cleanc, c, v, arm64, x64')
405 exit(1)
406 }
407 }
408
409 mut arch_str := cmdline.option(args, '-arch', 'auto')
410 if arch_str.len == 0 {
411 arch_str = 'auto'
412 }
413 mut arch := Arch.auto
414 match arch_str {
415 'auto' {
416 arch = .auto
417 }
418 'x64' {
419 arch = .x64
420 }
421 'arm64' {
422 arch = .arm64
423 }
424 else {
425 eprintln('error: unknown architecture `${arch_str}`. Valid architectures: auto, x64, arm64')
426 exit(1)
427 }
428 }
429
430 output_file := cmdline.option(args, '-o', cmdline.option(args, '-output', ''))
431 ccompiler := cmdline.option(args, '-cc', '')
432 target_os_arg := cmdline.option(args, '-os', '')
433 freestanding_hooks_arg := cmdline.option(args, '-fhooks', '')
434 // Without -os, V2 targets the host OS. -os is an explicit target override;
435 // -os cross and -freestanding express separate portable/platform contracts.
436 normalized_target_os := if target_os_arg.len > 0 {
437 normalize_cli_target_os(target_os_arg)
438 } else {
439 normalize_target_os_name(os.user_os())
440 }
441 output_cross_c := normalized_target_os == 'cross'
442
443 // Parse -printfn option (comma-separated list of function names to print)
444 mut printfn_str := cmdline.option(args, '-printfn', '')
445 if printfn_str.len == 0 {
446 printfn_str = ''
447 }
448 printfn_list := if printfn_str.len > 0 { printfn_str.split(',') } else { []string{} }
449
450 // Parse -d <name> comptime defines (can be specified multiple times)
451 user_defines := cmdline.options(args, '-d')
452
453 // Parse -hot-fn <name> for hot code reloading
454 mut hot_fn_str := cmdline.option(args, '-hot-fn', '')
455 if hot_fn_str.len == 0 {
456 hot_fn_str = ''
457 }
458
459 // Parse -gc <mode> for garbage collection
460 gc_mode_str := cmdline.option(args, '-gc', '')
461 mut gc_mode := GarbageCollectionMode.no_gc
462 mut gc_defines := []string{}
463 match gc_mode_str {
464 '', 'none' {
465 gc_mode = .no_gc
466 }
467 'vgc' {
468 gc_mode = .vgc
469 gc_defines << 'vgc'
470 }
471 'boehm' {
472 gc_mode = .boehm
473 gc_defines << 'gcboehm'
474 gc_defines << 'gcboehm_full'
475 gc_defines << 'gcboehm_opt'
476 }
477 else {
478 eprintln('error: unknown garbage collection mode `-gc ${gc_mode_str}`')
479 eprintln(' `-gc vgc` .............. V GC: concurrent tri-color mark-and-sweep (translated from Go)')
480 eprintln(' `-gc boehm` ............ Boehm-Demers-Weiser conservative GC')
481 eprintln(' `-gc none` ............. no garbage collection (default)')
482 exit(1)
483 }
484 }
485
486 mut all_defines := user_defines.clone()
487 all_defines << gc_defines
488 if output_cross_c && 'cross' !in all_defines {
489 all_defines << 'cross'
490 }
491 if '-prealloc' in args {
492 all_defines << 'prealloc'
493 }
494 freestanding := '-freestanding' in args || '--freestanding' in args
495 validate_target_contract(normalized_target_os, freestanding)
496 freestanding_hooks := parse_freestanding_hooks(freestanding_hooks_arg)
497 if freestanding_hooks.len > 0 && !freestanding {
498 eprintln('error: -fhooks requires -freestanding')
499 exit(1)
500 }
501 if freestanding && 'freestanding' !in all_defines {
502 all_defines << 'freestanding'
503 }
504 add_freestanding_hook_defines(mut all_defines, freestanding_hooks)
505
506 options := cmdline.only_options(args)
507 validate_macos_tiny_flag_contract(options, normalized_target_os) or {
508 eprintln('error: ${err.msg()}')
509 exit(1)
510 }
511 macos_tiny := '-no-mos-tiny' !in options
512
513 // Parse -ownership flag (must be after options is declared)
514 mut ownership := false
515 $if ownership ? {
516 ownership = '-ownership' in options
517 if ownership {
518 all_defines << 'ownership'
519 }
520 }
521
522 // Validate flags: error on unknown options
523 known_flags_with_values := ['-backend', '-b', '-o', '-output', '-arch', '-printfn', '-gc',
524 '-d', '-hot-fn', '-cc', '-os', '-fhooks']
525 mut known_boolean_flags := ['--debug', '--verbose', '-v', '--skip-genv', '--skip-builtin',
526 '--skip-imports', '--skip-type-check', '-no-parallel', '--no-parallel', '-nocache',
527 '--nocache', '-nomarkused', '--nomarkused', '-showcc', '--showcc', '-stats', '--stats',
528 '-print-parsed-files', '--print-parsed-files', '-keepc', '--profile-alloc', '-profile-alloc',
529 '-enable-globals', '--enable-globals', '-shared', '--shared', '-O0', '--single-backend',
530 '-single-backend', '-prod', '-prealloc', '-freestanding', '--freestanding', '-no-mos-tiny']
531 $if ownership ? {
532 known_boolean_flags << '-ownership'
533 }
534 for opt in options {
535 if opt !in known_flags_with_values && opt !in known_boolean_flags {
536 eprintln('error: unknown flag `${opt}`')
537 eprintln('')
538 eprintln('Usage: v2 [options] <file.v>')
539 eprintln('')
540 eprintln('Options:')
541 eprintln(' -o <file> Output file name')
542 eprintln(' -b <name> Backend: eval, cleanc, c, v, arm64, x64 (default: cleanc; omit for cleanc)')
543 eprintln(' -backend <name> is accepted as a compatibility alias')
544 eprintln(' -arch <name> Architecture: auto, x64, arm64 (default: auto)')
545 eprintln(' -os <target> Override target OS (default: host OS): linux, macos, windows, termux, cross, none')
546 eprintln(' -printfn <names> Print generated C for functions (comma-separated)')
547 eprintln(' -stats, --stats Print compilation statistics')
548 eprintln(' -nocache, --nocache Disable build cache')
549 eprintln(' -d <name> Define a comptime flag')
550 eprintln(' -enable-globals Accepted for v1 compatibility')
551 eprintln(' -prod Production build: enable SSA optimization + -O3 -flto')
552 eprintln(' -prealloc Use arena allocation (faster, not thread-safe)')
553 eprintln(' -freestanding Generate for a freestanding platform contract')
554 eprintln(' -fhooks <values> Advanced freestanding hooks for --skip-builtin --skip-type-check stubs')
555 eprintln(' Values: output, panic, alloc, minimal')
556 eprintln(' -no-mos-tiny Disable macOS x64 tiny object output')
557 eprintln(' -O0 Skip SSA optimization (default; use -prod to enable)')
558 $if ownership ? {
559 eprintln(' -ownership Enable ownership checking for strings')
560 }
561 eprintln(' --debug Enable debug mode')
562 eprintln(' -v, --verbose Enable verbose output')
563 eprintln(' -cc <compiler> C compiler to use (default: tcc, fallback: cc)')
564 eprintln(' -showcc, --showcc Print C compiler command')
565 eprintln(' -keepc Keep generated C file')
566 eprintln(' -no-parallel, --no-parallel')
567 eprintln(' Disable parallel type check and transform')
568 exit(1)
569 }
570 }
571
572 implicit_skip_builtin := freestanding && freestanding_hooks.len == 0
573 return Preferences{
574 debug: '--debug' in options
575 verbose: '--verbose' in options || '-v' in options
576 skip_genv: '--skip-genv' in options
577 skip_builtin: '--skip-builtin' in options || implicit_skip_builtin
578 skip_imports: '--skip-imports' in options
579 skip_type_check: '--skip-type-check' in options
580 no_parallel: '-no-parallel' in options || '--no-parallel' in options
581 no_parallel_transform: '-no-parallel' in options || '--no-parallel' in options
582 no_cache: '-nocache' in options || '--nocache' in options
583 no_markused: '-nomarkused' in options || '--nomarkused' in options
584 show_cc: '-showcc' in options || '--showcc' in options
585 stats: '-stats' in options || '--stats' in options
586 print_parsed_files: '-print-parsed-files' in options || '--print-parsed-files' in options
587 keep_c: '-keepc' in options
588 use_context_allocator: '--profile-alloc' in options || '-profile-alloc' in options
589 is_shared_lib: '-shared' in options || '--shared' in options
590 no_optimize: '-O0' in options || '-prod' !in options
591 is_prod: '-prod' in options
592 prealloc: '-prealloc' in options
593 single_backend: '--single-backend' in options || '-single-backend' in options
594 ownership: ownership
595 gc_mode: gc_mode
596 backend: backend
597 arch: arch
598 target_os: normalized_target_os
599 output_cross_c: output_cross_c
600 freestanding: freestanding
601 freestanding_hooks: freestanding_hooks
602 macos_tiny: macos_tiny
603 output_file: output_file
604 printfn_list: printfn_list
605 user_defines: all_defines
606 explicit_user_defines: user_defines.clone()
607 hot_fn: hot_fn_str
608 ccompiler: ccompiler
609 vroot: detect_vroot()
610 vmodules_path: os.vmodules_dir()
611 }
612}
613
614fn target_os_from_option_tokens(options []string) string {
615 for opt in options {
616 if opt.starts_with('--os-') {
617 return normalize_cli_target_os(opt.all_after('--os-'))
618 }
619 if opt.starts_with('os-') {
620 return normalize_cli_target_os(opt.all_after('os-'))
621 }
622 }
623 return normalize_target_os_name(os.user_os())
624}
625
626fn freestanding_hooks_from_option_tokens(options []string) []string {
627 mut hooks := []string{}
628 for opt in options {
629 if opt.starts_with('--fhooks-') {
630 for hook in parse_freestanding_hooks(opt.all_after('--fhooks-')) {
631 append_freestanding_hook(mut hooks, hook)
632 }
633 } else if opt.starts_with('fhooks-') {
634 for hook in parse_freestanding_hooks(opt.all_after('fhooks-')) {
635 append_freestanding_hook(mut hooks, hook)
636 }
637 }
638 }
639 return hooks
640}
641
642pub fn new_preferences_using_options(options []string) Preferences {
643 // Default backend based on OS: macOS defaults to arm64, others to x64
644 mut backend := if os.user_os() == 'macos' { Backend.arm64 } else { Backend.x64 }
645 if '--eval' in options || 'eval' in options {
646 backend = .eval
647 } else if '--cleanc' in options || 'cleanc' in options {
648 backend = .cleanc
649 } else if '--c' in options || 'c' in options {
650 backend = .c
651 } else if '--v' in options || 'v' in options {
652 backend = .v
653 } else if '--arm64' in options || 'arm64' in options {
654 backend = .arm64
655 } else if '--x64' in options || 'x64' in options {
656 backend = .x64
657 }
658
659 mut arch := Arch.auto
660 if '--arch-x64' in options {
661 arch = .x64
662 } else if '--arch-arm64' in options {
663 arch = .arm64
664 }
665 target_os := target_os_from_option_tokens(options)
666 output_cross_c := target_os == 'cross'
667 freestanding := '--freestanding' in options || '-freestanding' in options
668 || 'freestanding' in options
669 validate_target_contract(target_os, freestanding)
670 freestanding_hooks := freestanding_hooks_from_option_tokens(options)
671 if freestanding_hooks.len > 0 && !freestanding {
672 eprintln('error: freestanding hooks require freestanding target mode')
673 exit(1)
674 }
675 mut user_defines := []string{}
676 if output_cross_c {
677 user_defines << 'cross'
678 }
679 if freestanding {
680 user_defines << 'freestanding'
681 }
682 add_freestanding_hook_defines(mut user_defines, freestanding_hooks)
683
684 implicit_skip_builtin := freestanding && freestanding_hooks.len == 0
685 return Preferences{
686 // config flags
687 debug: '--debug' in options
688 verbose: '--verbose' in options || '-v' in options
689 skip_genv: '--skip-genv' in options
690 skip_builtin: '--skip-builtin' in options || implicit_skip_builtin
691 skip_imports: '--skip-imports' in options
692 skip_type_check: '--skip-type-check' in options
693 no_parallel: '-no-parallel' in options || '--no-parallel' in options
694 no_parallel_transform: '-no-parallel' in options || '--no-parallel' in options
695 no_cache: '-nocache' in options || '--nocache' in options
696 no_markused: '-nomarkused' in options || '--nomarkused' in options
697 show_cc: '-showcc' in options || '--showcc' in options
698 stats: '-stats' in options || '--stats' in options
699 print_parsed_files: '-print-parsed-files' in options || '--print-parsed-files' in options
700 use_context_allocator: '--profile-alloc' in options || '-profile-alloc' in options
701 is_shared_lib: '-shared' in options || '--shared' in options
702 no_optimize: '-O0' in options || '-prod' !in options
703 is_prod: '-prod' in options
704 prealloc: '-prealloc' in options
705 single_backend: '--single-backend' in options || '-single-backend' in options
706 backend: backend
707 arch: arch
708 target_os: target_os
709 output_cross_c: output_cross_c
710 freestanding: freestanding
711 freestanding_hooks: freestanding_hooks
712 user_defines: user_defines
713 explicit_user_defines: []string{}
714 }
715}
716
717// get_effective_arch returns the architecture to use based on preferences and OS
718pub fn (p &Preferences) get_effective_arch() Arch {
719 if p.arch != .auto {
720 return p.arch
721 }
722 // Auto-detect: macOS defaults to arm64, others to x64
723 return if normalize_current_os_name(p.target_os_or_host()) == 'macos' {
724 Arch.arm64
725 } else {
726 Arch.x64
727 }
728}
729