| 1 | // Copyright (c) 2019-2024 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. |
| 4 | module pref |
| 5 | |
| 6 | // import v.ast // TODO: this results in a compiler bug |
| 7 | import os.cmdline |
| 8 | import os |
| 9 | import v.vcache |
| 10 | import rand |
| 11 | |
| 12 | pub enum BuildMode { |
| 13 | // `v program.v' |
| 14 | // Build user code only, and add pre-compiled vlib (`cc program.o builtin.o os.o...`) |
| 15 | default_mode // `v -lib ~/v/os` |
| 16 | // build any module (generate os.o + os.vh) |
| 17 | build_module |
| 18 | } |
| 19 | |
| 20 | pub enum AssertFailureMode { |
| 21 | default |
| 22 | aborts |
| 23 | backtraces |
| 24 | continues |
| 25 | } |
| 26 | |
| 27 | pub enum GarbageCollectionMode { |
| 28 | unknown |
| 29 | no_gc |
| 30 | boehm_full // full garbage collection mode |
| 31 | boehm_incr // incremental garbage collection mode |
| 32 | boehm_full_opt // full garbage collection mode |
| 33 | boehm_incr_opt // incremental garbage collection mode |
| 34 | boehm_leak // leak detection mode (makes `gc_check_leaks()` work) |
| 35 | vgc // V GC: concurrent tri-color mark-and-sweep (translated from Go's runtime GC) |
| 36 | } |
| 37 | |
| 38 | pub enum OutputMode { |
| 39 | stdout |
| 40 | silent |
| 41 | } |
| 42 | |
| 43 | pub enum ColorOutput { |
| 44 | auto |
| 45 | always |
| 46 | never |
| 47 | } |
| 48 | |
| 49 | // Subsystem is needed for modeling passing an explicit `/subsystem:windows` or `/subsystem:console` on Windows. |
| 50 | // By default it is `auto`. It has no effect on platforms != windows. |
| 51 | pub enum Subsystem { |
| 52 | auto |
| 53 | console |
| 54 | windows |
| 55 | } |
| 56 | |
| 57 | pub enum Backend { |
| 58 | c // The (default) C backend |
| 59 | interpret // Removed V1 interpreter backend; kept for compatibility diagnostics. |
| 60 | js_node // The JavaScript NodeJS backend |
| 61 | js_browser // The JavaScript browser backend |
| 62 | js_freestanding // The JavaScript freestanding backend |
| 63 | wasm // The WebAssembly backend |
| 64 | } |
| 65 | |
| 66 | pub fn (b Backend) is_js() bool { |
| 67 | return b in [ |
| 68 | .js_node, |
| 69 | .js_browser, |
| 70 | .js_freestanding, |
| 71 | ] |
| 72 | } |
| 73 | |
| 74 | pub enum CompilerType { |
| 75 | gcc |
| 76 | tinyc |
| 77 | clang |
| 78 | emcc |
| 79 | mingw |
| 80 | msvc |
| 81 | cplusplus |
| 82 | } |
| 83 | |
| 84 | pub const supported_test_runners = ['normal', 'simple', 'tap', 'dump', 'teamcity'] |
| 85 | |
| 86 | @[heap; minify] |
| 87 | pub struct Preferences { |
| 88 | pub mut: |
| 89 | os OS // the OS to compile for |
| 90 | backend Backend |
| 91 | backend_set_by_flag bool // true when the compiler receives `-b`/`-backend` |
| 92 | build_mode BuildMode |
| 93 | arch Arch |
| 94 | output_mode OutputMode = .stdout |
| 95 | // verbosity VerboseLevel |
| 96 | is_verbose bool |
| 97 | use_v2 bool |
| 98 | // nofmt bool // disable vfmt |
| 99 | is_glibc bool // if GLIBC will be linked |
| 100 | is_musl bool // if MUSL will be linked |
| 101 | is_test bool // `v test string_test.v` |
| 102 | is_script bool // single file mode (`v program.v`), main function can be skipped |
| 103 | is_vsh bool // v script (`file.vsh`) file, the `os` module should be made global |
| 104 | raw_vsh_tmp_prefix string // The prefix used for executables, when a script lacks the .vsh extension |
| 105 | is_livemain bool // main program that contains live/hot code |
| 106 | is_liveshared bool // a shared library, that will be used in a -live main program |
| 107 | is_shared bool // an ordinary shared library, -shared, no matter if it is live or not |
| 108 | is_o bool // building an .o file |
| 109 | is_prof bool // benchmark every function |
| 110 | is_prod bool // use "-O3" |
| 111 | no_prod_options bool // `-no-prod-options`, means do not pass any optimization flags to the C compilation, while still allowing the user to use for example `-cflags -Os` to pass custom ones |
| 112 | is_repl bool |
| 113 | is_eval_argument bool // true for `v -e 'println(2+2)'`. `println(2+2)` will be in pref.eval_argument . |
| 114 | is_run bool // compile and run a v program, passing arguments to it, and deleting the executable afterwards |
| 115 | is_crun bool // similar to run, but does not recompile the executable, if there were no changes to the sources |
| 116 | is_debug bool // turned on by -g/-debug or -cg/-cdebug, it tells v to pass -g to the C backend compiler. |
| 117 | is_vlines bool // turned on by -g (it slows down .tmp.c generation slightly). |
| 118 | is_stats bool // `v -stats file.v` will produce more detailed statistics for the file that is compiled |
| 119 | show_asserts bool // `VTEST_SHOW_ASSERTS=1 v file_test.v` will show details about the asserts done by a test file. Also activated for `-stats` and `-show-asserts`. |
| 120 | show_timings bool // show how much time each compiler stage took |
| 121 | is_fmt bool |
| 122 | is_vdoc bool |
| 123 | is_vet bool |
| 124 | is_template bool // skip _ var warning in templates |
| 125 | is_ios_simulator bool |
| 126 | is_apk bool // build as Android .apk format |
| 127 | is_help bool // -h, -help or --help was passed |
| 128 | is_quiet bool // do not show the repetitive explanatory messages like the one for `v -prod run file.v` . |
| 129 | is_cstrict bool // turn on more C warnings; slightly slower |
| 130 | is_callstack bool // turn on callstack registers on each call when v.debug is imported |
| 131 | is_trace bool // turn on possibility to trace fn call where v.debug is imported |
| 132 | is_coverage bool // turn on code coverage stats |
| 133 | is_check_return bool // -check-return, will make V produce notices about *all* call expressions with unused results. NOTE: experimental! |
| 134 | is_check_overflow bool // -check-overflow, will panic on integer overflow |
| 135 | eval_argument string // `println(2+2)` on `v -e "println(2+2)"`. Note that this source code, will be evaluated in vsh mode, so 'v -e 'println(ls(".")!)' is valid. |
| 136 | test_runner string // can be 'simple' (fastest, but much less detailed), 'tap', 'normal' |
| 137 | profile_file string // the profile results will be stored inside profile_file |
| 138 | coverage_dir string // the coverage files will be stored inside coverage_dir |
| 139 | profile_no_inline bool // when true, @[inline] functions would not be profiled |
| 140 | profile_fns []string // when set, profiling will be off by default, but inside these functions (and what they call) it will be on. |
| 141 | translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc |
| 142 | translated_go bool = true // Are we running V code translated from Go? Allow err shadowing |
| 143 | obfuscate_removed bool // `v -obf program.v`, renames functions to "f_XXX". REMOVED. Use `strip` instead |
| 144 | hide_auto_str bool // `v -hide-auto-str program.v`, doesn't generate str() with struct data |
| 145 | // Note: passing -cg instead of -g will set is_vlines to false and is_debug to true, thus making v generate cleaner C files, |
| 146 | // which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks). |
| 147 | sanitize bool // use Clang's new "-fsanitize" option |
| 148 | sourcemap bool // JS Backend: -sourcemap will create a source map - default false |
| 149 | sourcemap_inline bool = true // JS Backend: -sourcemap-inline will embed the source map in the generated JaaScript file - currently default true only implemented |
| 150 | sourcemap_src_included bool // JS Backend: -sourcemap-src-included includes V source code in source map - default false |
| 151 | show_cc bool // -showcc, print cc command |
| 152 | show_c_output bool // -show-c-output, print all cc output even if the code was compiled correctly |
| 153 | show_callgraph bool // -show-callgraph, print the program callgraph, in a Graphviz DOT format to stdout |
| 154 | show_depgraph bool // -show-depgraph, print the program module dependency graph, in a Graphviz DOT format to stdout |
| 155 | show_unused_params bool = true // regular function params should report as unused by default. |
| 156 | dump_c_flags string // `-dump-c-flags file.txt` - let V store all C flags, passed to the backend C compiler in `file.txt`, one C flag/value per line. |
| 157 | dump_modules string // `-dump-modules modules.txt` - let V store all V modules, that were used by the compiled program in `modules.txt`, one module per line. |
| 158 | dump_files string // `-dump-files files.txt` - let V store all V or .template file paths, that were used by the compiled program in `files.txt`, one path per line. |
| 159 | dump_defines string // `-dump-defines defines.txt` - let V store all the defines that affect the current program and their values, one define per line + `,` + its value. |
| 160 | generate_c_project string // `-generate-c-project path` - generate a portable C project folder with the generated C file and build scripts. |
| 161 | use_cache bool // when set, use cached modules to speed up subsequent compilations, at the cost of slower initial ones (while the modules are cached) |
| 162 | retry_compilation bool = true // retry the compilation with another C compiler, if tcc fails. |
| 163 | use_os_system_to_run bool // when set, use os.system() to run the produced executable, instead of os.new_process; works around segfaults on macos, that may happen when xcode is updated |
| 164 | macosx_version_min string = '0' // relevant only for macos and ios targets |
| 165 | // TODO: Convert this into a []string |
| 166 | cflags string // Additional options which will be passed to the C compiler *before* other options. |
| 167 | ldflags string // Additional options which will be passed to the C compiler *after* everything else. |
| 168 | // For example, passing -cflags -Os will cause the C compiler to optimize the generated binaries for size. |
| 169 | // You could pass several -cflags XXX arguments. They will be merged with each other. |
| 170 | // You can also quote several options at the same time: -cflags '-Os -fno-inline-small-functions'. |
| 171 | m64 bool // true = generate 64-bit code, defaults to x64 |
| 172 | ccompiler string // the name of the C compiler used |
| 173 | ccompiler_set_by_flag bool // true when the compiler receives `-cc` |
| 174 | ccompiler_type CompilerType // the type of the C compiler used |
| 175 | cppcompiler string // the name of the CPP compiler used |
| 176 | third_party_option string |
| 177 | building_v bool |
| 178 | no_bounds_checking bool // `-no-bounds-checking` turns off *all* bounds checks for all functions at runtime, as if they all had been tagged with `@[direct_array_access]` |
| 179 | force_bounds_checking bool // `-force-bounds-checking` turns ON *all* bounds checks, even for functions that *were* tagged with `@[direct_array_access]` |
| 180 | autofree bool // `v -manualfree` => false, `v -autofree` => true; false by default for now. |
| 181 | print_autofree_vars bool // print vars that are not freed by autofree |
| 182 | print_autofree_vars_in_fn string // same as above, but only for a single fn |
| 183 | // Disabling `free()` insertion results in better performance in some applications (e.g. compilers) |
| 184 | trace_calls bool // -trace-calls true = the transformer stage will generate and inject print calls for tracing function calls |
| 185 | trace_fns []string // when set, tracing will be done only for functions, whose names match the listed patterns. |
| 186 | compress bool // when set, use `upx` to compress the generated executable |
| 187 | // generating_vh bool |
| 188 | no_builtin bool // Skip adding the `builtin` module implicitly. The generated C code may not compile. |
| 189 | enable_globals bool // allow __global for low level code |
| 190 | disable_explicit_mutability bool // allow ordinary variables to be mutated without explicit `mut` annotations |
| 191 | is_bare bool // set by -freestanding |
| 192 | bare_builtin_dir string // Set by -bare-builtin-dir xyz/ . The xyz/ module should contain implementations of malloc, memset, etc, that are used by the rest of V's `builtin` module. That option is only useful with -freestanding (i.e. when is_bare is true). |
| 193 | no_preludes bool // Prevents V from generating preludes in resulting .c files |
| 194 | custom_prelude string // Contents of custom V prelude that will be prepended before code in resulting .c files |
| 195 | no_closures bool // Produce a compile time error, if a closure was generated for any reason (an implicit receiver method was stored, or an explicit `fn [captured]()`). |
| 196 | cmain string // The name of the generated C main function. Useful with framework like code, that uses macros to re-define `main`, like SDL2 does. When set, V will always generate `int THE_NAME(int ___argc, char** ___argv){`, *no matter* the platform. |
| 197 | lookup_path []string |
| 198 | output_cross_c bool // true, when the user passed `-os cross` or `-cross` |
| 199 | output_es5 bool |
| 200 | prealloc bool |
| 201 | vroot string |
| 202 | vlib string // absolute path to the vlib/ folder |
| 203 | vmodules_paths []string // absolute paths to the vmodules folders, by default ['/home/user/.vmodules'], can be overridden by setting VMODULES |
| 204 | out_name_c string // full os.real_path to the generated .tmp.c file; set by builder. |
| 205 | out_name string |
| 206 | out_name_is_dir bool // true when `-o`/`-output` was passed with a trailing path separator |
| 207 | path string // Path to file/folder to compile |
| 208 | line_info string // `-line-info="file.v:28"`: for "mini VLS" (shows information about objects on provided line) |
| 209 | linfo LineInfo |
| 210 | |
| 211 | run_only []string // VTEST_ONLY_FN and -run-only accept comma separated glob patterns. |
| 212 | exclude []string // glob patterns for excluding .v files from the list of .v files that otherwise would have been used for a compilation, example: `-exclude @vlib/math/*.c.v` |
| 213 | file_list []string // A list of .v files or directories. All .v files found recursively in directories will be included in the compilation. |
| 214 | // Only test_ functions that match these patterns will be run. -run-only is valid only for _test.v files. |
| 215 | // -d vfmt and -d another=0 for `$if vfmt { will execute }` and `$if another ? { will NOT get here }` |
| 216 | compile_defines []string // just ['vfmt'] |
| 217 | compile_defines_all []string // contains both: ['vfmt','another'] |
| 218 | compile_values map[string]string // the map will contain for `-d key=value`: compile_values['key'] = 'value', and for `-d ident`, it will be: compile_values['ident'] = 'true' |
| 219 | |
| 220 | run_args []string // `v run x.v 1 2 3` => `1 2 3` |
| 221 | printfn_list []string // a list of generated function names, whose source should be shown, for debugging |
| 222 | |
| 223 | print_v_files bool // when true, just print the list of all parsed .v files then stop. |
| 224 | print_watched_files bool // when true, just print the list of all parsed .v files + all the compiled $tmpl files, then stop. Used by `v watch run webserver.v` |
| 225 | |
| 226 | skip_running bool // when true, do no try to run the produced file (set by b.cc(), when -o x.c or -o x.js) |
| 227 | skip_warnings bool // like C's "-w", forces warnings to be ignored. |
| 228 | skip_notes bool // force notices to be ignored/not shown. |
| 229 | warn_impure_v bool // -Wimpure-v, force a warning for JS.fn()/C.fn(), outside of .js.v/.c.v files. TODO: turn to an error by default |
| 230 | warns_are_errors bool // -W, like C's "-Werror", treat *every* warning is an error |
| 231 | notes_are_errors bool // -N, treat *every* notice as an error |
| 232 | fatal_errors bool // unconditionally exit after the first error with exit(1) |
| 233 | reuse_tmpc bool // do not use random names for .tmp.c and .tmp.c.rsp files, and do not remove them |
| 234 | no_rsp bool // when true, pass C backend options directly on the CLI (do not use `.rsp` files for them, some older C compilers do not support them) |
| 235 | no_std bool // when true, do not pass -std=c99 to the C backend |
| 236 | |
| 237 | no_parallel bool // do not use threads when compiling; slower, but more portable and sometimes less buggy |
| 238 | parallel_cc bool // whether to split the resulting .c file into many .c files + a common .h file, that are then compiled in parallel, then linked together. |
| 239 | only_check_syntax bool // when true, just parse the files, then stop, before running checker |
| 240 | check_only bool // same as only_check_syntax, but also runs the checker |
| 241 | experimental bool // enable experimental features |
| 242 | skip_unused bool // skip generating C code for functions, that are not used |
| 243 | |
| 244 | use_color ColorOutput // whether the warnings/errors should use ANSI color escapes. |
| 245 | cleanup_files []string // list of temporary *.tmp.c and *.tmp.c.rsp files. Cleaned up on successful builds. |
| 246 | build_options []string // list of options, that should be passed down to `build-module`, if needed for -usecache |
| 247 | cache_manager vcache.CacheManager |
| 248 | gc_mode GarbageCollectionMode = .unknown // .no_gc, .boehm, .boehm_leak, ... |
| 249 | gc_set_by_flag bool // true when the compiler receives `-gc` |
| 250 | assert_failure_mode AssertFailureMode // whether to call abort() or print_backtrace() after an assertion failure |
| 251 | message_limit int = 200 // the maximum amount of warnings/errors/notices that will be accumulated |
| 252 | nofloat bool // for low level code, like kernels: replaces f32 with u32 and f64 with u64 |
| 253 | use_coroutines bool // experimental coroutines |
| 254 | fast_math bool // -fast-math will pass either -ffast-math or /fp:fast (for msvc) to the C backend |
| 255 | // checker settings: |
| 256 | checker_match_exhaustive_cutoff_limit int = 12 |
| 257 | thread_stack_size int = 8388608 // Change with `-thread-stack-size 4194304`. The final default is adjusted in fill_with_defaults() based on the target architecture. |
| 258 | thread_stack_size_set_by_flag bool |
| 259 | // wasm settings: |
| 260 | wasm_stack_top int = 1024 + (16 * 1024) // stack size for webassembly backend |
| 261 | wasm_validate bool // validate webassembly code, by calling `wasm-validate` |
| 262 | warn_about_allocs bool // -warn-about-allocs warngs about every single allocation, e.g. 'hi ${name}'. Mostly for low level development where manual memory management is used. |
| 263 | // game prototyping flags: |
| 264 | div_by_zero_is_zero bool // -div-by-zero-is-zero makes so `x / 0 == 0`, i.e. eliminates the division by zero panics/segfaults |
| 265 | // forwards compatibility settings: |
| 266 | relaxed_gcc14 bool = true // turn on the generated pragmas, that make gcc versions > 14 a lot less pedantic. The default is to have those pragmas in the generated C output, so that gcc-14 can be used on Arch etc. |
| 267 | // |
| 268 | subsystem Subsystem // the type of the window app, that is going to be generated; has no effect on !windows |
| 269 | icon_path string // Windows executable icon file (.ico or .png) |
| 270 | is_vls bool |
| 271 | json_errors bool // -json-errors, for VLS and other tools |
| 272 | new_transform bool // temporary for the new transformer |
| 273 | new_generic_solver bool |
| 274 | } |
| 275 | |
| 276 | // ensure_coroutines_runtime downloads and exposes the photon runtime used by `import coroutines`. |
| 277 | pub fn ensure_coroutines_runtime() ! { |
| 278 | $if macos || linux { |
| 279 | arch := $if arm64 { 'arm64' } $else { 'amd64' } |
| 280 | vexe := vexe_path() |
| 281 | vroot := os.dir(vexe) |
| 282 | so_path := os.join_path(vroot, 'thirdparty', 'photon', 'photonwrapper.so') |
| 283 | so_url := 'https://raw.githubusercontent.com/vlang/photonbin/master/photonwrapper_${os.user_os()}_${arch}.so' |
| 284 | if !os.exists(so_path) { |
| 285 | println('coroutines .so not found, downloading...') |
| 286 | res := os.execute('${os.quoted_path(vexe)} download -o "${so_path}" "${so_url}"') |
| 287 | if res.exit_code != 0 || !os.exists(so_path) { |
| 288 | return error('coroutines .so could not be downloaded with `v download`. Download ${so_url}, place it in ${so_path} then try again.') |
| 289 | } |
| 290 | println('done!') |
| 291 | } |
| 292 | $if macos { |
| 293 | dyld_fallback_paths := os.getenv('DYLD_FALLBACK_LIBRARY_PATH') |
| 294 | so_dir := os.dir(so_path) |
| 295 | if !dyld_fallback_paths.contains(so_dir) { |
| 296 | env := [dyld_fallback_paths, so_dir].filter(it.len != 0).join(':') |
| 297 | os.setenv('DYLD_FALLBACK_LIBRARY_PATH', env, true) |
| 298 | } |
| 299 | } |
| 300 | } $else { |
| 301 | return error('coroutines only work on macOS & Linux for now') |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | pub fn parse_args(known_external_commands []string, args []string) (&Preferences, string) { |
| 306 | return parse_args_and_show_errors(known_external_commands, args, false) |
| 307 | } |
| 308 | |
| 309 | @[if linux] |
| 310 | fn detect_musl(mut res Preferences) { |
| 311 | res.is_glibc = true |
| 312 | res.is_musl = false |
| 313 | musl := os.execute('ldd --version').output.contains('musl') |
| 314 | if musl { |
| 315 | res.is_musl = true |
| 316 | res.is_glibc = false |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | @[noreturn] |
| 321 | fn run_code_in_tmp_vfile_and_exit(args []string, mut res Preferences, option_name string, extension string, |
| 322 | content string) { |
| 323 | tmp_file_path := os.join_path(os.vtmp_dir(), rand.ulid()) |
| 324 | mut tmp_exe_file_path := res.out_name |
| 325 | mut output_option := '' |
| 326 | if tmp_exe_file_path == '' { |
| 327 | tmp_exe_file_path = '${tmp_file_path}.exe' |
| 328 | output_option = '-o ${os.quoted_path(tmp_exe_file_path)} ' |
| 329 | } |
| 330 | tmp_v_file_path := '${tmp_file_path}.${extension}' |
| 331 | os.write_file(tmp_v_file_path, content) or { |
| 332 | panic('Failed to create temporary file ${tmp_v_file_path}') |
| 333 | } |
| 334 | run_options := cmdline.options_before(args, [option_name]).join(' ') |
| 335 | command_options := cmdline.options_after(args, [option_name])#[1..].join(' ') |
| 336 | vexe := vexe_path() |
| 337 | tmp_cmd := '${os.quoted_path(vexe)} ${output_option} ${run_options} run ${os.quoted_path(tmp_v_file_path)} ${command_options}' |
| 338 | |
| 339 | res.vrun_elog('tmp_cmd: ${tmp_cmd}') |
| 340 | tmp_result := os.system(tmp_cmd) |
| 341 | res.vrun_elog('exit code: ${tmp_result}') |
| 342 | |
| 343 | if output_option != '' { |
| 344 | res.vrun_elog('remove tmp exe file: ${tmp_exe_file_path}') |
| 345 | os.rm(tmp_exe_file_path) or {} |
| 346 | } |
| 347 | res.vrun_elog('remove tmp v file: ${tmp_v_file_path}') |
| 348 | os.rm(tmp_v_file_path) or {} |
| 349 | exit(tmp_result) |
| 350 | } |
| 351 | |
| 352 | fn inline_icon_option_value(arg string) ?string { |
| 353 | for prefix in ['-icon=', '--icon=', '-seticon=', '--seticon='] { |
| 354 | if arg.starts_with(prefix) { |
| 355 | return arg[prefix.len..] |
| 356 | } |
| 357 | } |
| 358 | return none |
| 359 | } |
| 360 | |
| 361 | fn set_icon_path(mut res Preferences, raw_path string, option_name string) { |
| 362 | if raw_path == '' { |
| 363 | eprintln_exit('missing value for `${option_name}`') |
| 364 | } |
| 365 | res.icon_path = os.real_path(raw_path) |
| 366 | res.build_options << '-icon "${res.icon_path}"' |
| 367 | } |
| 368 | |
| 369 | const internal_v_commands = [ |
| 370 | 'run', |
| 371 | 'crun', |
| 372 | 'build', |
| 373 | 'build-module', |
| 374 | 'help', |
| 375 | 'version', |
| 376 | 'new', |
| 377 | 'init', |
| 378 | 'install', |
| 379 | 'link', |
| 380 | 'list', |
| 381 | 'outdated', |
| 382 | 'remove', |
| 383 | 'search', |
| 384 | 'show', |
| 385 | 'unlink', |
| 386 | 'update', |
| 387 | 'upgrade', |
| 388 | 'vlib-docs', |
| 389 | 'translate', |
| 390 | ] |
| 391 | |
| 392 | fn has_following_positional_arg(args []string, start int) bool { |
| 393 | for idx := start; idx < args.len; idx++ { |
| 394 | if !args[idx].starts_with('-') { |
| 395 | return true |
| 396 | } |
| 397 | } |
| 398 | return false |
| 399 | } |
| 400 | |
| 401 | fn optional_arg_value(args []string, idx int, command string, known_external_commands []string, def string) (string, bool) { |
| 402 | next := args[idx + 1] or { return def, false } |
| 403 | if next == '-' { |
| 404 | return next, true |
| 405 | } |
| 406 | if next.starts_with('-') { |
| 407 | return def, false |
| 408 | } |
| 409 | if command == '' |
| 410 | && (next in known_external_commands || next in internal_v_commands || next.ends_with('.v') |
| 411 | || next.ends_with('.vsh') || os.is_dir(next) |
| 412 | || !has_following_positional_arg(args, idx + 2)) { |
| 413 | return def, false |
| 414 | } |
| 415 | return next, true |
| 416 | } |
| 417 | |
| 418 | pub fn parse_args_and_show_errors(known_external_commands []string, args []string, show_output bool) (&Preferences, string) { |
| 419 | mut res := &Preferences{} |
| 420 | use_v2_requested := '-v2' in args |
| 421 | detect_musl(mut res) |
| 422 | $if x64 { |
| 423 | res.m64 = true // follow V model by default |
| 424 | } |
| 425 | res.run_only = os.getenv('VTEST_ONLY_FN').split_any(',') |
| 426 | if os.getenv('VQUIET') != '' { |
| 427 | res.is_quiet = true |
| 428 | } |
| 429 | if os.getenv('VNORUN') != '' { |
| 430 | res.skip_running = true |
| 431 | } |
| 432 | coverage_dir_from_env := os.getenv('VCOVDIR') |
| 433 | if coverage_dir_from_env != '' { |
| 434 | res.coverage_dir = coverage_dir_from_env |
| 435 | } |
| 436 | |
| 437 | mut no_skip_unused := false |
| 438 | mut command, mut command_idx := '', 0 |
| 439 | mut build_vsh_source := false |
| 440 | for i := 0; i < args.len; i++ { |
| 441 | arg := args[i] |
| 442 | if inline_icon_path := inline_icon_option_value(arg) { |
| 443 | set_icon_path(mut res, inline_icon_path, arg.all_before('=')) |
| 444 | continue |
| 445 | } |
| 446 | match arg { |
| 447 | '--' { |
| 448 | break |
| 449 | } |
| 450 | '-wasm-validate' { |
| 451 | res.wasm_validate = true |
| 452 | } |
| 453 | '-wasm-stack-top' { |
| 454 | res.wasm_stack_top = cmdline.option(args[i..], arg, res.wasm_stack_top.str()).int() |
| 455 | i++ |
| 456 | } |
| 457 | '-apk' { |
| 458 | res.is_apk = true |
| 459 | res.build_options << arg |
| 460 | } |
| 461 | '-arch' { |
| 462 | target_arch := cmdline.option(args[i..], '-arch', '') |
| 463 | i++ |
| 464 | target_arch_kind := arch_from_string(target_arch) or { |
| 465 | eprintln_exit('unknown architecture target `${target_arch}`') |
| 466 | } |
| 467 | res.arch = target_arch_kind |
| 468 | res.build_options << '${arg} ${target_arch}' |
| 469 | } |
| 470 | '-assert' { |
| 471 | assert_mode := cmdline.option(args[i..], '-assert', '') |
| 472 | match assert_mode { |
| 473 | 'aborts' { |
| 474 | res.assert_failure_mode = .aborts |
| 475 | } |
| 476 | 'backtraces' { |
| 477 | res.assert_failure_mode = .backtraces |
| 478 | } |
| 479 | 'continues' { |
| 480 | res.assert_failure_mode = .continues |
| 481 | } |
| 482 | else { |
| 483 | eprintln('unknown assert mode `-gc ${assert_mode}`, supported modes are:`') |
| 484 | eprintln(' `-assert aborts` .... calls abort() after assertion failure') |
| 485 | eprintln(' `-assert backtraces` .... calls print_backtrace() after assertion failure') |
| 486 | eprintln(' `-assert continues` .... does not call anything, just continue after an assertion failure') |
| 487 | exit(1) |
| 488 | } |
| 489 | } |
| 490 | |
| 491 | i++ |
| 492 | } |
| 493 | '-show-timings' { |
| 494 | res.show_timings = true |
| 495 | } |
| 496 | '-show-asserts' { |
| 497 | res.show_asserts = true |
| 498 | } |
| 499 | '-check-syntax' { |
| 500 | res.only_check_syntax = true |
| 501 | } |
| 502 | '-check' { |
| 503 | res.check_only = true |
| 504 | } |
| 505 | '-vls-mode' { |
| 506 | res.is_vls = true |
| 507 | } |
| 508 | '-?', '-h', '-help', '--help' { |
| 509 | // Note: help is *very important*, just respond to all variations: |
| 510 | res.is_help = true |
| 511 | } |
| 512 | '-q' { |
| 513 | res.is_quiet = true |
| 514 | } |
| 515 | '-v', '-V', '--version', '-version' { |
| 516 | if command != '' { |
| 517 | // Version flags after a command are intended for the command, not for V itself. |
| 518 | continue |
| 519 | } |
| 520 | if args[i..].len > 1 && arg == '-v' { |
| 521 | // With additional args after the `-v` flag, it toggles verbosity, like Clang. |
| 522 | // E.g.: `v -v` VS `v -v run examples/hello_world.v`. |
| 523 | res.is_verbose = true |
| 524 | } else { |
| 525 | command = 'version' |
| 526 | } |
| 527 | } |
| 528 | '-v2' { |
| 529 | res.use_v2 = true |
| 530 | } |
| 531 | '-eval', '--eval' { |
| 532 | if !use_v2_requested { |
| 533 | eprintln_exit('use v -v2 -eval file.v') |
| 534 | } |
| 535 | } |
| 536 | '-ownership' { |
| 537 | // Passed through to v2 compiler for ownership checking |
| 538 | } |
| 539 | '-progress' { |
| 540 | // processed by testing tools in cmd/tools/modules/testing/common.v |
| 541 | } |
| 542 | '-Wimpure-v' { |
| 543 | res.warn_impure_v = true |
| 544 | } |
| 545 | '-Wfatal-errors' { |
| 546 | res.fatal_errors = true |
| 547 | } |
| 548 | '-silent' { |
| 549 | res.output_mode = .silent |
| 550 | res.compile_defines_all << 'silent' // enable `$if silent? {` |
| 551 | res.compile_defines << 'silent' |
| 552 | } |
| 553 | '-skip-running' { |
| 554 | res.skip_running = true |
| 555 | } |
| 556 | '-cstrict' { |
| 557 | res.is_cstrict = true |
| 558 | } |
| 559 | '-nofloat' { |
| 560 | res.nofloat = true |
| 561 | res.compile_defines_all << 'nofloat' // so that `$if nofloat? {` works |
| 562 | res.compile_defines << 'nofloat' |
| 563 | } |
| 564 | '-fast-math' { |
| 565 | res.fast_math = true |
| 566 | } |
| 567 | '-e' { |
| 568 | res.is_eval_argument = true |
| 569 | res.eval_argument = cmdline.option(args[i..], '-e', '') |
| 570 | i++ |
| 571 | } |
| 572 | '-subsystem' { |
| 573 | subsystem := cmdline.option(args[i..], '-subsystem', '') |
| 574 | res.subsystem = Subsystem.from_string(subsystem) or { |
| 575 | mut valid := []string{} |
| 576 | $for x in Subsystem.values { |
| 577 | valid << x.name |
| 578 | } |
| 579 | eprintln('invalid subsystem: ${subsystem}') |
| 580 | eprintln('valid values are: ${valid}') |
| 581 | exit(1) |
| 582 | } |
| 583 | i++ |
| 584 | } |
| 585 | '-icon', '--icon', '-seticon', '--seticon' { |
| 586 | set_icon_path(mut res, cmdline.option(args[i..], arg, ''), arg) |
| 587 | i++ |
| 588 | } |
| 589 | '-gc' { |
| 590 | gc_mode := cmdline.option(args[i..], '-gc', '') |
| 591 | res.gc_set_by_flag = true |
| 592 | match gc_mode { |
| 593 | 'none' { |
| 594 | res.gc_mode = .no_gc |
| 595 | } |
| 596 | '', 'boehm' { |
| 597 | res.gc_mode = .boehm_full_opt // default mode |
| 598 | res.parse_define('gcboehm') |
| 599 | res.parse_define('gcboehm_full') |
| 600 | res.parse_define('gcboehm_opt') |
| 601 | } |
| 602 | 'boehm_full' { |
| 603 | res.gc_mode = .boehm_full |
| 604 | res.parse_define('gcboehm') |
| 605 | res.parse_define('gcboehm_full') |
| 606 | } |
| 607 | 'boehm_incr' { |
| 608 | res.gc_mode = .boehm_incr |
| 609 | res.parse_define('gcboehm') |
| 610 | res.parse_define('gcboehm_incr') |
| 611 | } |
| 612 | 'boehm_full_opt' { |
| 613 | res.gc_mode = .boehm_full_opt |
| 614 | res.parse_define('gcboehm') |
| 615 | res.parse_define('gcboehm_full') |
| 616 | res.parse_define('gcboehm_opt') |
| 617 | } |
| 618 | 'boehm_incr_opt' { |
| 619 | res.gc_mode = .boehm_incr_opt |
| 620 | res.parse_define('gcboehm') |
| 621 | res.parse_define('gcboehm_incr') |
| 622 | res.parse_define('gcboehm_opt') |
| 623 | } |
| 624 | 'boehm_leak' { |
| 625 | res.gc_mode = .boehm_leak |
| 626 | res.parse_define('gcboehm') |
| 627 | res.parse_define('gcboehm_leak') |
| 628 | } |
| 629 | 'vgc' { |
| 630 | res.gc_mode = .vgc |
| 631 | res.parse_define('vgc') |
| 632 | } |
| 633 | else { |
| 634 | eprintln('unknown garbage collection mode `-gc ${gc_mode}`, supported modes are:`') |
| 635 | eprintln(' `-gc boehm` ............ select default Boehm mode (currently `boehm_full_opt`)') |
| 636 | eprintln(' `-gc boehm_full` ....... classic full collection') |
| 637 | eprintln(' `-gc boehm_incr` ....... incremental collection') |
| 638 | eprintln(' `-gc boehm_full_opt` ... optimized classic full collection') |
| 639 | eprintln(' `-gc boehm_incr_opt` ... optimized incremental collection') |
| 640 | eprintln(' `-gc boehm_leak` ....... leak detection (for debugging)') |
| 641 | eprintln(' `-gc vgc` .............. V GC (concurrent tri-color mark-and-sweep)') |
| 642 | eprintln(' `-gc none` ............. no garbage collection') |
| 643 | exit(1) |
| 644 | } |
| 645 | } |
| 646 | |
| 647 | effective_gc_mode := if gc_mode == '' { 'boehm' } else { gc_mode } |
| 648 | res.build_options << '${arg} ${effective_gc_mode}' |
| 649 | i++ |
| 650 | } |
| 651 | '-g', '-debug' { |
| 652 | res.is_debug = true |
| 653 | res.is_vlines = true |
| 654 | res.build_options << arg |
| 655 | } |
| 656 | '-cg', '-cdebug' { |
| 657 | res.is_debug = true |
| 658 | res.is_vlines = false |
| 659 | res.build_options << arg |
| 660 | } |
| 661 | '-debug-tcc' { |
| 662 | res.ccompiler = 'tcc' |
| 663 | res.build_options << '${arg} "${res.ccompiler}"' |
| 664 | res.retry_compilation = false |
| 665 | res.show_cc = true |
| 666 | res.show_c_output = true |
| 667 | } |
| 668 | '-sourcemap' { |
| 669 | res.sourcemap = true |
| 670 | } |
| 671 | '-warn-about-allocs' { |
| 672 | res.warn_about_allocs = true |
| 673 | } |
| 674 | '-div-by-zero-is-zero' { |
| 675 | res.div_by_zero_is_zero = true |
| 676 | } |
| 677 | '-sourcemap-src-included' { |
| 678 | res.sourcemap_src_included = true |
| 679 | } |
| 680 | '-sourcemap-inline' { |
| 681 | res.sourcemap_inline = true |
| 682 | } |
| 683 | '-repl' { |
| 684 | res.is_repl = true |
| 685 | } |
| 686 | '-json-errors' { |
| 687 | res.json_errors = true |
| 688 | } |
| 689 | '-live' { |
| 690 | res.is_livemain = true |
| 691 | res.compile_defines << 'livemain' |
| 692 | res.compile_defines_all << 'livemain' |
| 693 | } |
| 694 | '-sharedlive' { |
| 695 | res.is_liveshared = true |
| 696 | res.is_shared = true |
| 697 | res.compile_defines << 'sharedlive' |
| 698 | res.compile_defines_all << 'sharedlive' |
| 699 | } |
| 700 | '-shared' { |
| 701 | res.is_shared = true |
| 702 | } |
| 703 | '--enable-globals' { |
| 704 | eprintln_cond(show_output && !res.is_quiet, |
| 705 | '`--enable-globals` flag is deprecated, please use `-enable-globals` instead') |
| 706 | res.enable_globals = true |
| 707 | } |
| 708 | '-enable-globals' { |
| 709 | res.enable_globals = true |
| 710 | } |
| 711 | '--disable-explicit-mutability', '-disable-explicit-mutability' { |
| 712 | res.disable_explicit_mutability = true |
| 713 | res.build_options << arg |
| 714 | } |
| 715 | '-autofree' { |
| 716 | res.autofree = true |
| 717 | res.gc_mode = .no_gc |
| 718 | res.build_options << arg |
| 719 | } |
| 720 | '-print_autofree_vars' { |
| 721 | res.print_autofree_vars = true |
| 722 | res.build_options << arg |
| 723 | } |
| 724 | '-print_autofree_vars_in_fn' { |
| 725 | res.print_autofree_vars = true |
| 726 | value := cmdline.option(args[i..], arg, '') |
| 727 | res.build_options << arg |
| 728 | res.build_options << value |
| 729 | res.print_autofree_vars_in_fn = value |
| 730 | i++ |
| 731 | } |
| 732 | '-trace-calls' { |
| 733 | res.build_options << arg |
| 734 | res.trace_calls = true |
| 735 | } |
| 736 | '-trace-fns' { |
| 737 | value := cmdline.option(args[i..], arg, '') |
| 738 | res.build_options << arg |
| 739 | res.build_options << value |
| 740 | trace_fns := value.split(',') |
| 741 | if trace_fns.len > 0 { |
| 742 | res.trace_fns << trace_fns |
| 743 | } |
| 744 | i++ |
| 745 | } |
| 746 | '-manualfree' { |
| 747 | res.autofree = false |
| 748 | res.build_options << arg |
| 749 | } |
| 750 | '-skip-unused' { |
| 751 | res.skip_unused = true |
| 752 | } |
| 753 | '-no-skip-unused' { |
| 754 | no_skip_unused = true |
| 755 | res.skip_unused = false |
| 756 | } |
| 757 | '-compress' { |
| 758 | res.compress = true |
| 759 | } |
| 760 | '-freestanding' { |
| 761 | res.is_bare = true |
| 762 | res.build_options << arg |
| 763 | } |
| 764 | '-no-retry-compilation' { |
| 765 | res.retry_compilation = false |
| 766 | res.build_options << arg |
| 767 | } |
| 768 | '-musl' { |
| 769 | res.is_musl = true |
| 770 | res.is_glibc = false |
| 771 | res.build_options << arg |
| 772 | } |
| 773 | '-glibc' { |
| 774 | res.is_musl = false |
| 775 | res.is_glibc = true |
| 776 | res.build_options << arg |
| 777 | } |
| 778 | '-no-bounds-checking' { |
| 779 | res.no_bounds_checking = true |
| 780 | res.compile_defines << 'no_bounds_checking' |
| 781 | res.compile_defines_all << 'no_bounds_checking' |
| 782 | res.build_options << arg |
| 783 | } |
| 784 | '-force-bounds-checking' { |
| 785 | res.force_bounds_checking = true |
| 786 | } |
| 787 | '-no-builtin' { |
| 788 | res.no_builtin = true |
| 789 | res.build_options << arg |
| 790 | } |
| 791 | '-no-preludes' { |
| 792 | res.no_preludes = true |
| 793 | res.build_options << arg |
| 794 | } |
| 795 | '-no-relaxed-gcc14' { |
| 796 | res.relaxed_gcc14 = false |
| 797 | } |
| 798 | '-prof', '-profile' { |
| 799 | profile_file, profile_file_consumed := optional_arg_value(args, i, command, |
| 800 | known_external_commands, '-') |
| 801 | res.profile_file = profile_file |
| 802 | res.is_prof = true |
| 803 | res.build_options << '${arg} ${res.profile_file}' |
| 804 | if profile_file_consumed { |
| 805 | i++ |
| 806 | } |
| 807 | } |
| 808 | '-cov', '-coverage' { |
| 809 | res.coverage_dir = cmdline.option(args[i..], arg, '-') |
| 810 | i++ |
| 811 | } |
| 812 | '-profile-fns' { |
| 813 | profile_fns := cmdline.option(args[i..], arg, '').split(',') |
| 814 | if profile_fns.len > 0 { |
| 815 | res.profile_fns << profile_fns |
| 816 | } |
| 817 | i++ |
| 818 | } |
| 819 | '-profile-no-inline' { |
| 820 | res.profile_no_inline = true |
| 821 | } |
| 822 | '-prod' { |
| 823 | res.is_prod = true |
| 824 | res.build_options << arg |
| 825 | } |
| 826 | '-no-prod-options' { |
| 827 | res.no_prod_options = true |
| 828 | res.build_options << arg |
| 829 | } |
| 830 | '-sanitize' { |
| 831 | res.sanitize = true |
| 832 | res.build_options << arg |
| 833 | } |
| 834 | '-simulator' { |
| 835 | res.is_ios_simulator = true |
| 836 | } |
| 837 | '-stats' { |
| 838 | res.is_stats = true |
| 839 | } |
| 840 | '-obf', '-obfuscate' { |
| 841 | println('obfuscation has been removed; use `strip` on the resulting binary instead') |
| 842 | res.obfuscate_removed = true |
| 843 | } |
| 844 | '-hide-auto-str' { |
| 845 | res.hide_auto_str = true |
| 846 | } |
| 847 | '-translated' { |
| 848 | res.translated = true |
| 849 | res.gc_mode = .no_gc // no gc in c2v'ed code, at least for now |
| 850 | } |
| 851 | '-translated-go' { |
| 852 | println('got -translated-go') |
| 853 | res.translated_go = true |
| 854 | } |
| 855 | '-m32', '-m64' { |
| 856 | res.m64 = arg[2] == `6` |
| 857 | res.cflags += ' ${arg}' |
| 858 | res.build_options << arg |
| 859 | if arg == '-m32' && res.arch == ._auto { |
| 860 | res.arch = .i386 |
| 861 | } |
| 862 | } |
| 863 | '-color' { |
| 864 | res.use_color = .always |
| 865 | } |
| 866 | '-nocolor' { |
| 867 | res.use_color = .never |
| 868 | } |
| 869 | '-showcc' { |
| 870 | res.show_cc = true |
| 871 | } |
| 872 | '-show-c-output' { |
| 873 | res.show_c_output = true |
| 874 | } |
| 875 | '-show-callgraph' { |
| 876 | res.show_callgraph = true |
| 877 | } |
| 878 | '-show-depgraph' { |
| 879 | res.show_depgraph = true |
| 880 | } |
| 881 | '-run-only' { |
| 882 | res.run_only = |
| 883 | cmdline.option(args[i..], arg, os.getenv('VTEST_ONLY_FN')).split_any(',') |
| 884 | i++ |
| 885 | } |
| 886 | '-exclude' { |
| 887 | patterns := cmdline.option(args[i..], arg, '').split_any(',') |
| 888 | res.exclude << patterns |
| 889 | i++ |
| 890 | } |
| 891 | '-file-list' { |
| 892 | res.file_list = cmdline.option(args[i..], arg, '').split_any(',') |
| 893 | i++ |
| 894 | } |
| 895 | '-test-runner' { |
| 896 | res.test_runner = cmdline.option(args[i..], arg, res.test_runner) |
| 897 | i++ |
| 898 | } |
| 899 | '-dump-c-flags' { |
| 900 | res.dump_c_flags = cmdline.option(args[i..], arg, '-') |
| 901 | i++ |
| 902 | } |
| 903 | '-dump-modules' { |
| 904 | res.dump_modules = cmdline.option(args[i..], arg, '-') |
| 905 | i++ |
| 906 | } |
| 907 | '-dump-files' { |
| 908 | res.dump_files = cmdline.option(args[i..], arg, '-') |
| 909 | i++ |
| 910 | } |
| 911 | '-dump-defines' { |
| 912 | res.dump_defines = cmdline.option(args[i..], arg, '-') |
| 913 | i++ |
| 914 | } |
| 915 | '-generate-c-project' { |
| 916 | res.generate_c_project = cmdline.option(args[i..], arg, '') |
| 917 | if res.generate_c_project == '' { |
| 918 | eprintln_exit('Missing output directory after `-generate-c-project`.') |
| 919 | } |
| 920 | i++ |
| 921 | } |
| 922 | '-experimental' { |
| 923 | res.experimental = true |
| 924 | } |
| 925 | '-new-transformer' { |
| 926 | res.new_transform = true |
| 927 | } |
| 928 | '-usecache' { |
| 929 | res.use_cache = true |
| 930 | res.parallel_cc = false |
| 931 | res.no_parallel = true |
| 932 | } |
| 933 | '-use-os-system-to-run' { |
| 934 | res.use_os_system_to_run = true |
| 935 | } |
| 936 | '-macosx-version-min' { |
| 937 | res.macosx_version_min = cmdline.option(args[i..], arg, res.macosx_version_min) |
| 938 | res.build_options << '${arg} ${res.macosx_version_min}' |
| 939 | i++ |
| 940 | } |
| 941 | '-nocache' { |
| 942 | res.use_cache = false |
| 943 | } |
| 944 | '-prealloc' { |
| 945 | res.prealloc = true |
| 946 | if !res.gc_set_by_flag { |
| 947 | res.gc_mode = .no_gc |
| 948 | } |
| 949 | res.build_options << arg |
| 950 | } |
| 951 | '-no-parallel' { |
| 952 | res.no_parallel = true |
| 953 | res.build_options << arg |
| 954 | } |
| 955 | '-parallel-cc' { |
| 956 | res.parallel_cc = true |
| 957 | res.no_parallel = true // TODO: see how to make both work |
| 958 | res.build_options << arg |
| 959 | } |
| 960 | '-native' { |
| 961 | eprintln_exit('The native backend has been removed. Use `v -v2 -b arm64` or `v -v2 -b x64` instead.') |
| 962 | } |
| 963 | '-interpret' { |
| 964 | eprintln_exit('use v -v2 -eval file.v') |
| 965 | } |
| 966 | '-W' { |
| 967 | res.warns_are_errors = true |
| 968 | } |
| 969 | '-w' { |
| 970 | res.skip_warnings = true |
| 971 | res.warns_are_errors = false |
| 972 | } |
| 973 | '-N' { |
| 974 | res.notes_are_errors = true |
| 975 | } |
| 976 | '-n' { |
| 977 | res.skip_notes = true |
| 978 | res.notes_are_errors = false |
| 979 | } |
| 980 | '-no-closures' { |
| 981 | res.no_closures = true |
| 982 | } |
| 983 | '-no-rsp' { |
| 984 | res.no_rsp = true |
| 985 | } |
| 986 | '-no-std' { |
| 987 | res.no_std = true |
| 988 | } |
| 989 | '-keepc' { |
| 990 | res.reuse_tmpc = true |
| 991 | } |
| 992 | '-watch' { |
| 993 | eprintln_exit('The -watch option is deprecated. Please use the watch command `v watch file.v` instead.') |
| 994 | } |
| 995 | '-print-v-files' { |
| 996 | res.print_v_files = true |
| 997 | } |
| 998 | '-print-watched-files' { |
| 999 | res.print_watched_files = true |
| 1000 | } |
| 1001 | '-http' { |
| 1002 | run_http_argument := 'import net.http.file; file.serve()' |
| 1003 | mut new_args := args.filter(it != '-http') |
| 1004 | new_args << ['-e', run_http_argument] |
| 1005 | eprintln_cond(show_output && !res.is_quiet, |
| 1006 | "Note: use `v -e '${run_http_argument}'`, if you want to customise the http server options.") |
| 1007 | run_code_in_tmp_vfile_and_exit(new_args, mut res, '-e', 'vsh', run_http_argument) |
| 1008 | } |
| 1009 | '-cross' { |
| 1010 | res.output_cross_c = true |
| 1011 | res.build_options << '${arg}' |
| 1012 | } |
| 1013 | '-os' { |
| 1014 | target_os := cmdline.option(args[i..], '-os', '').to_lower_ascii() |
| 1015 | i++ |
| 1016 | target_os_kind := os_from_string(target_os) or { |
| 1017 | if target_os == 'cross' { |
| 1018 | res.output_cross_c = true |
| 1019 | continue |
| 1020 | } |
| 1021 | eprintln_exit('unknown operating system target `${target_os}`') |
| 1022 | } |
| 1023 | if target_os_kind == .wasm32 { |
| 1024 | res.is_bare = true |
| 1025 | } |
| 1026 | if target_os_kind in [.wasm32, .wasm32_emscripten, .wasm32_wasi] { |
| 1027 | res.arch = .wasm32 |
| 1028 | } |
| 1029 | if target_os_kind == .wasm32_emscripten { |
| 1030 | res.gc_mode = .no_gc // TODO: enable gc (turn off threads etc, in builtin_d_gcboehm.c.v, once `$if wasm32_emscripten {` works) |
| 1031 | } |
| 1032 | res.os = target_os_kind |
| 1033 | res.build_options << '${arg} ${target_os}' |
| 1034 | } |
| 1035 | '-printfn' { |
| 1036 | res.printfn_list << cmdline.option(args[i..], '-printfn', '').split(',') |
| 1037 | i++ |
| 1038 | } |
| 1039 | '-cflags' { |
| 1040 | res.cflags += ' ' + cmdline.option(args[i..], '-cflags', '') |
| 1041 | res.build_options << '${arg} "${res.cflags.trim_space()}"' |
| 1042 | i++ |
| 1043 | } |
| 1044 | '-ldflags' { |
| 1045 | res.ldflags += ' ' + cmdline.option(args[i..], '-ldflags', '') |
| 1046 | res.build_options << '${arg} "${res.ldflags.trim_space()}"' |
| 1047 | i++ |
| 1048 | } |
| 1049 | '-d', '-define' { |
| 1050 | if define := args[i..][1] { |
| 1051 | res.parse_define(define) |
| 1052 | } |
| 1053 | i++ |
| 1054 | } |
| 1055 | '-message-limit' { |
| 1056 | res.message_limit = cmdline.option(args[i..], arg, '5').int() |
| 1057 | i++ |
| 1058 | } |
| 1059 | '-thread-stack-size' { |
| 1060 | res.thread_stack_size = |
| 1061 | cmdline.option(args[i..], arg, res.thread_stack_size.str()).int() |
| 1062 | res.thread_stack_size_set_by_flag = true |
| 1063 | i++ |
| 1064 | } |
| 1065 | '-cc' { |
| 1066 | res.ccompiler = cmdline.option(args[i..], '-cc', 'cc') |
| 1067 | res.ccompiler_set_by_flag = true |
| 1068 | res.build_options << '${arg} "${res.ccompiler}"' |
| 1069 | i++ |
| 1070 | } |
| 1071 | '-c++' { |
| 1072 | res.cppcompiler = cmdline.option(args[i..], '-c++', 'c++') |
| 1073 | i++ |
| 1074 | } |
| 1075 | '-checker-match-exhaustive-cutoff-limit' { |
| 1076 | res.checker_match_exhaustive_cutoff_limit = |
| 1077 | cmdline.option(args[i..], arg, '10').int() |
| 1078 | i++ |
| 1079 | } |
| 1080 | '-o', '-output' { |
| 1081 | raw_out_name := cmdline.option(args[i..], arg, '') |
| 1082 | res.out_name_is_dir = raw_out_name.ends_with('/') || raw_out_name.ends_with('\\') |
| 1083 | res.out_name = raw_out_name |
| 1084 | if !os.is_abs_path(res.out_name) { |
| 1085 | res.out_name = os.join_path(os.getwd(), res.out_name) |
| 1086 | } |
| 1087 | i++ |
| 1088 | } |
| 1089 | '-is_o' { |
| 1090 | res.is_o = true |
| 1091 | } |
| 1092 | '-b', '-backend' { |
| 1093 | sbackend := cmdline.option(args[i..], arg, 'c') |
| 1094 | res.build_options << '${arg} ${sbackend}' |
| 1095 | if use_v2_requested && sbackend in ['eval', 'cleanc', 'c', 'v', 'arm64', 'x64'] { |
| 1096 | res.backend_set_by_flag = true |
| 1097 | i++ |
| 1098 | continue |
| 1099 | } |
| 1100 | b := backend_from_string(sbackend) or { |
| 1101 | eprintln_exit('Unknown V backend: ${sbackend}\nValid -backend choices are: c, js, js_node, js_browser, js_freestanding, wasm, arm64, x64') |
| 1102 | } |
| 1103 | if b == .wasm { |
| 1104 | res.compile_defines << 'wasm' |
| 1105 | res.compile_defines_all << 'wasm' |
| 1106 | res.arch = .wasm32 |
| 1107 | } else if b.is_js() { |
| 1108 | res.output_cross_c = true |
| 1109 | } |
| 1110 | res.backend = b |
| 1111 | res.backend_set_by_flag = true |
| 1112 | i++ |
| 1113 | } |
| 1114 | '-es5' { |
| 1115 | res.output_es5 = true |
| 1116 | } |
| 1117 | '-path' { |
| 1118 | path := cmdline.option(args[i..], '-path', '') |
| 1119 | res.build_options << '${arg} "${path}"' |
| 1120 | res.lookup_path = path.replace('|', os.path_delimiter).split(os.path_delimiter) |
| 1121 | i++ |
| 1122 | } |
| 1123 | '-bare-builtin-dir' { |
| 1124 | bare_builtin_dir := cmdline.option(args[i..], arg, '') |
| 1125 | res.build_options << '${arg} "${bare_builtin_dir}"' |
| 1126 | res.bare_builtin_dir = bare_builtin_dir |
| 1127 | i++ |
| 1128 | } |
| 1129 | '-custom-prelude' { |
| 1130 | path := cmdline.option(args[i..], '-custom-prelude', '') |
| 1131 | res.build_options << '${arg} ${path}' |
| 1132 | prelude := os.read_file(path) or { |
| 1133 | eprintln_exit('cannot open custom prelude file: ${err}') |
| 1134 | } |
| 1135 | res.custom_prelude = prelude |
| 1136 | i++ |
| 1137 | } |
| 1138 | '-raw-vsh-tmp-prefix' { |
| 1139 | res.raw_vsh_tmp_prefix = cmdline.option(args[i..], arg, '') |
| 1140 | i++ |
| 1141 | } |
| 1142 | '-cmain' { |
| 1143 | res.cmain = cmdline.option(args[i..], '-cmain', '') |
| 1144 | i++ |
| 1145 | } |
| 1146 | '-line-info' { |
| 1147 | res.line_info = cmdline.option(args[i..], arg, '') |
| 1148 | res.parse_line_info(res.line_info) |
| 1149 | i++ |
| 1150 | } |
| 1151 | '-check-unused-fn-args' { |
| 1152 | res.show_unused_params = true |
| 1153 | } |
| 1154 | '-check-return' { |
| 1155 | res.is_check_return = true |
| 1156 | } |
| 1157 | '-check-overflow' { |
| 1158 | res.is_check_overflow = true |
| 1159 | } |
| 1160 | '-use-coroutines' { |
| 1161 | res.use_coroutines = true |
| 1162 | ensure_coroutines_runtime() or { eprintln_exit(err.msg()) } |
| 1163 | res.compile_defines << 'is_coroutine' |
| 1164 | res.compile_defines_all << 'is_coroutine' |
| 1165 | } |
| 1166 | '-new-generic-solver' { |
| 1167 | res.new_generic_solver = true |
| 1168 | } |
| 1169 | else { |
| 1170 | if command == 'build' && is_source_file(arg) { |
| 1171 | if arg.ends_with('.vsh') { |
| 1172 | command, command_idx = arg, i |
| 1173 | build_vsh_source = true |
| 1174 | res.skip_running = true |
| 1175 | continue |
| 1176 | } |
| 1177 | eprintln_exit('Use `v ${arg}` instead.') |
| 1178 | } |
| 1179 | if is_source_file(arg) && arg.ends_with('.vsh') { |
| 1180 | // store for future iterations |
| 1181 | res.is_vsh = true |
| 1182 | } |
| 1183 | if !arg.starts_with('-') { |
| 1184 | if command == '' { |
| 1185 | command, command_idx = arg, i |
| 1186 | if res.is_eval_argument || command in ['run', 'crun', 'watch'] { |
| 1187 | break |
| 1188 | } |
| 1189 | } else if is_source_file(command) && is_source_file(arg) && !res.is_vsh |
| 1190 | && command !in known_external_commands && res.raw_vsh_tmp_prefix == '' { |
| 1191 | eprintln_exit('Too many targets. Specify just one target: <target.v|target_directory>.') |
| 1192 | } |
| 1193 | continue |
| 1194 | } |
| 1195 | if command !in ['', 'build-module'] && !is_source_file(command) { |
| 1196 | // arguments for e.g. fmt should be checked elsewhere |
| 1197 | continue |
| 1198 | } |
| 1199 | if command_idx < i && (res.is_vsh || (is_source_file(command) |
| 1200 | && command in known_external_commands)) { |
| 1201 | // When running programs, let them be responsible for the arguments passed to them. |
| 1202 | // E.g.: `script.vsh cmd -opt` or `v run hello_world.v -opt`. |
| 1203 | // But detect unknown arguments when building them. E.g.: `v hello_world.v -opt`. |
| 1204 | continue |
| 1205 | } |
| 1206 | err_detail := if command == '' { '' } else { ' for command `${command}`' } |
| 1207 | eprintln_exit('Unknown argument `${arg}`${err_detail}') |
| 1208 | } |
| 1209 | } |
| 1210 | } |
| 1211 | if res.force_bounds_checking { |
| 1212 | res.no_bounds_checking = false |
| 1213 | res.compile_defines = res.compile_defines.filter(it == 'no_bounds_checking') |
| 1214 | res.compile_defines_all = res.compile_defines_all.filter(it == 'no_bounds_checking') |
| 1215 | } |
| 1216 | if res.trace_calls { |
| 1217 | if res.trace_fns.len == 0 { |
| 1218 | res.trace_fns << '*' |
| 1219 | } |
| 1220 | for i, fpattern in res.trace_fns { |
| 1221 | if fpattern.contains('*') { |
| 1222 | continue |
| 1223 | } |
| 1224 | res.trace_fns[i] = '*${fpattern}*' |
| 1225 | } |
| 1226 | } |
| 1227 | if command == 'crun' { |
| 1228 | res.is_crun = true |
| 1229 | } |
| 1230 | if command == 'run' { |
| 1231 | res.is_run = true |
| 1232 | } |
| 1233 | res.show_asserts = res.show_asserts || res.is_stats || os.getenv('VTEST_SHOW_ASSERTS') != '' |
| 1234 | |
| 1235 | if res.os != .wasm32_emscripten { |
| 1236 | if res.out_name.ends_with('.js') && !res.backend_set_by_flag { |
| 1237 | res.backend = .js_node |
| 1238 | res.output_cross_c = true |
| 1239 | } |
| 1240 | } |
| 1241 | |
| 1242 | // Disable parallel checker on arm64 windows and linux for now |
| 1243 | $if linux || windows { |
| 1244 | $if arm64 { |
| 1245 | res.no_parallel = true |
| 1246 | } |
| 1247 | } |
| 1248 | |
| 1249 | if res.out_name.ends_with('.o') { |
| 1250 | res.is_o = true |
| 1251 | } |
| 1252 | |
| 1253 | if command == 'run' && res.is_prod && os.is_atty(1) > 0 { |
| 1254 | eprintln_cond(show_output && !res.is_quiet, |
| 1255 | "Note: building an optimized binary takes much longer. It shouldn't be used with `v run`.") |
| 1256 | eprintln_cond(show_output && !res.is_quiet, |
| 1257 | 'Use `v run` without optimization, or build an optimized binary with -prod first, then run it separately.') |
| 1258 | } |
| 1259 | if res.os in [.browser, .wasi] && res.backend != .wasm { |
| 1260 | eprintln_exit('OS `${res.os}` forbidden for backends other than wasm') |
| 1261 | } |
| 1262 | if res.backend == .wasm && res.os !in [.browser, .wasi, ._auto] { |
| 1263 | eprintln_exit('Native WebAssembly backend OS must be `browser` or `wasi`') |
| 1264 | } |
| 1265 | |
| 1266 | if command != 'doc' && res.out_name.ends_with('.v') { |
| 1267 | eprintln_exit('Cannot save output binary in a .v file.') |
| 1268 | } |
| 1269 | if res.fast_math { |
| 1270 | if res.ccompiler_type == .msvc { |
| 1271 | res.cflags += ' /fp:fast' |
| 1272 | } else { |
| 1273 | res.cflags += ' -ffast-math' |
| 1274 | } |
| 1275 | } |
| 1276 | if res.is_eval_argument { |
| 1277 | // `v -e "println(2+5)"` |
| 1278 | run_code_in_tmp_vfile_and_exit(args, mut res, '-e', 'vsh', res.eval_argument) |
| 1279 | } |
| 1280 | |
| 1281 | command_args := args#[command_idx + 1..] |
| 1282 | if res.is_run || res.is_crun { |
| 1283 | res.path = command_args[0] or { eprintln_exit('v run: no v files listed') } |
| 1284 | res.run_args = command_args[1..] |
| 1285 | if res.path == '-' { |
| 1286 | // `echo "println(2+5)" | v -` |
| 1287 | contents := os.get_raw_lines_joined() |
| 1288 | run_code_in_tmp_vfile_and_exit(args, mut res, 'run', 'v', contents) |
| 1289 | } |
| 1290 | must_exist(res.path) |
| 1291 | if !res.path.ends_with('.v') && os.is_executable(res.path) && os.is_file(res.path) |
| 1292 | && os.is_file(res.path + '.v') { |
| 1293 | eprintln_cond(show_output && !res.is_quiet, |
| 1294 | 'It looks like you wanted to run "${res.path}.v", so we went ahead and did that since "${res.path}" is an executable.') |
| 1295 | res.path += '.v' |
| 1296 | } |
| 1297 | } else if is_source_file(command) { |
| 1298 | res.path = command |
| 1299 | } |
| 1300 | if !res.is_bare && res.bare_builtin_dir != '' { |
| 1301 | eprintln_cond(show_output && !res.is_quiet, |
| 1302 | '`-bare-builtin-dir` must be used with `-freestanding`') |
| 1303 | } |
| 1304 | if !build_vsh_source |
| 1305 | && (command.ends_with('.vsh') || (res.raw_vsh_tmp_prefix != '' && !res.is_run)) { |
| 1306 | // `v build.vsh gcc` is the same as `v run build.vsh gcc`, |
| 1307 | // i.e. compiling, then running the script, passing the args |
| 1308 | // after it to the script: |
| 1309 | res.is_crun = true |
| 1310 | res.path = command |
| 1311 | res.run_args = command_args |
| 1312 | } else if command == 'interpret' { |
| 1313 | eprintln_exit('use v -v2 -eval file.v') |
| 1314 | } |
| 1315 | if command == 'build-module' { |
| 1316 | res.build_mode = .build_module |
| 1317 | res.no_parallel = true |
| 1318 | res.parallel_cc = false |
| 1319 | res.path = command_args[0] or { eprintln_exit('v build-module: no module specified') } |
| 1320 | } |
| 1321 | if res.ccompiler == 'musl-gcc' { |
| 1322 | res.is_musl = true |
| 1323 | res.is_glibc = false |
| 1324 | } |
| 1325 | if res.is_musl { |
| 1326 | // make `$if musl? {` work: |
| 1327 | res.compile_defines << 'musl' |
| 1328 | res.compile_defines_all << 'musl' |
| 1329 | } |
| 1330 | if res.is_bare { |
| 1331 | // make `$if freestanding? {` + file_freestanding.v + file_notd_freestanding.v work: |
| 1332 | res.compile_defines << 'freestanding' |
| 1333 | res.compile_defines_all << 'freestanding' |
| 1334 | } |
| 1335 | if 'callstack' in res.compile_defines_all { |
| 1336 | res.is_callstack = true |
| 1337 | } |
| 1338 | if 'trace' in res.compile_defines_all { |
| 1339 | res.is_trace = true |
| 1340 | } |
| 1341 | if res.coverage_dir != '' { |
| 1342 | res.is_coverage = true |
| 1343 | res.build_options << '-coverage ${res.coverage_dir}' |
| 1344 | } |
| 1345 | // keep only the unique res.build_options: |
| 1346 | mut m := map[string]string{} |
| 1347 | for x in res.build_options { |
| 1348 | m[x] = '' |
| 1349 | } |
| 1350 | res.build_options = m.keys() |
| 1351 | // eprintln('>> res.build_options: ${res.build_options}') |
| 1352 | res.fill_with_defaults() |
| 1353 | if res.generate_c_project != '' { |
| 1354 | // The generated C project should not depend on cached V module objects. |
| 1355 | res.use_cache = false |
| 1356 | } |
| 1357 | if res.backend == .c { |
| 1358 | res.skip_unused = res.build_mode != .build_module |
| 1359 | if no_skip_unused { |
| 1360 | res.skip_unused = false |
| 1361 | } |
| 1362 | } |
| 1363 | |
| 1364 | return res, command |
| 1365 | } |
| 1366 | |
| 1367 | @[noreturn] |
| 1368 | pub fn eprintln_exit(s string) { |
| 1369 | eprintln(s) |
| 1370 | exit(1) |
| 1371 | } |
| 1372 | |
| 1373 | pub fn eprintln_cond(condition bool, s string) { |
| 1374 | if !condition { |
| 1375 | return |
| 1376 | } |
| 1377 | eprintln(s) |
| 1378 | } |
| 1379 | |
| 1380 | pub fn (pref &Preferences) vrun_elog(s string) { |
| 1381 | if pref.is_verbose { |
| 1382 | eprintln('> v run -, ${s}') |
| 1383 | } |
| 1384 | } |
| 1385 | |
| 1386 | pub fn (pref &Preferences) should_output_to_stdout() bool { |
| 1387 | return pref.out_name.ends_with('/-') || pref.out_name.ends_with(r'\-') |
| 1388 | } |
| 1389 | |
| 1390 | fn must_exist(path string) { |
| 1391 | if !os.exists(path) { |
| 1392 | eprintln_exit('v expects that `${path}` exists, but it does not') |
| 1393 | } |
| 1394 | } |
| 1395 | |
| 1396 | @[inline] |
| 1397 | fn is_source_file(path string) bool { |
| 1398 | return path.ends_with('.v') || os.exists(path) |
| 1399 | } |
| 1400 | |
| 1401 | pub fn backend_from_string(s string) !Backend { |
| 1402 | // TODO: unify the "different js backend" options into a single `-b js` |
| 1403 | // + a separate option, to choose the wanted JS output. |
| 1404 | return match s { |
| 1405 | 'c' { .c } |
| 1406 | 'eval', 'interpret' { eprintln_exit('use v -v2 -eval file.v') } |
| 1407 | 'js', 'js_node' { .js_node } |
| 1408 | 'js_browser' { .js_browser } |
| 1409 | 'js_freestanding' { .js_freestanding } |
| 1410 | 'wasm' { .wasm } |
| 1411 | 'native' { eprintln_exit('The native backend has been removed. Use `v -v2 -b arm64` or `v -v2 -b x64` instead.') } |
| 1412 | 'go', 'golang' { eprintln_exit('The Go backend has been removed. Use `v -v2 -b golang` instead.') } |
| 1413 | else { error('Unknown backend type ${s}') } |
| 1414 | } |
| 1415 | } |
| 1416 | |
| 1417 | // Helper function to convert string names to CC enum |
| 1418 | pub fn cc_from_string(s string) CompilerType { |
| 1419 | if s == '' { |
| 1420 | return .gcc |
| 1421 | } |
| 1422 | cc := os.file_name(s).to_lower_ascii() |
| 1423 | return match true { |
| 1424 | cc.contains('tcc') || cc.contains('tinyc') || cc.contains('tinygcc') |
| 1425 | || cc.contains('tiny_gcc') || cc.contains('tiny-gcc') { |
| 1426 | .tinyc |
| 1427 | } |
| 1428 | cc.contains('gcc') { |
| 1429 | .gcc |
| 1430 | } |
| 1431 | cc.contains('clang') { |
| 1432 | .clang |
| 1433 | } |
| 1434 | cc.contains('emcc') { |
| 1435 | .emcc |
| 1436 | } |
| 1437 | cc == 'cl' || cc == 'cl.exe' || cc.contains('msvc') { |
| 1438 | .msvc |
| 1439 | } |
| 1440 | cc.contains('mingw') { |
| 1441 | .mingw |
| 1442 | } |
| 1443 | cc.contains('++') { |
| 1444 | .cplusplus |
| 1445 | } |
| 1446 | else { |
| 1447 | .gcc |
| 1448 | } |
| 1449 | } |
| 1450 | } |
| 1451 | |
| 1452 | fn (mut prefs Preferences) parse_compile_value(define string) { |
| 1453 | if !define.contains('=') { |
| 1454 | eprintln_exit('V error: Define argument value missing for ${define}.') |
| 1455 | return |
| 1456 | } |
| 1457 | name := define.all_before('=') |
| 1458 | value := define.all_after_first('=') |
| 1459 | prefs.compile_values[name] = value |
| 1460 | } |
| 1461 | |
| 1462 | fn (mut prefs Preferences) parse_define(define string) { |
| 1463 | if !(prefs.is_debug && define == 'debug') { |
| 1464 | prefs.build_options << '-d ${define}' |
| 1465 | } |
| 1466 | if !define.contains('=') { |
| 1467 | prefs.compile_values[define] = 'true' |
| 1468 | prefs.compile_defines << define |
| 1469 | prefs.compile_defines_all << define |
| 1470 | return |
| 1471 | } |
| 1472 | dname := define.all_before('=') |
| 1473 | dvalue := define.all_after_first('=') |
| 1474 | prefs.compile_values[dname] = dvalue |
| 1475 | prefs.compile_defines_all << dname |
| 1476 | match dvalue { |
| 1477 | '' {} |
| 1478 | else { |
| 1479 | prefs.compile_defines << dname |
| 1480 | } |
| 1481 | } |
| 1482 | } |
| 1483 | |
| 1484 | pub fn supported_test_runners_list() string { |
| 1485 | return supported_test_runners.map('`${it}`').join(', ') |
| 1486 | } |
| 1487 | |
| 1488 | pub fn (pref &Preferences) should_trace_fn_name(fname string) bool { |
| 1489 | return pref.trace_fns.any(fname.match_glob(it)) |
| 1490 | } |
| 1491 | |
| 1492 | pub fn (pref &Preferences) should_use_segfault_handler() bool { |
| 1493 | return !('no_segfault_handler' in pref.compile_defines |
| 1494 | || pref.os in [.wasm32, .wasm32_emscripten]) |
| 1495 | } |
| 1496 | |