From d0e3e6d7ea64781797cf94f83297d755ff892985 Mon Sep 17 00:00:00 2001 From: lmp Date: Tue, 30 Jul 2024 10:44:34 +0200 Subject: [PATCH] shy, cli: switch from `flag.FlagParser` to `flag.to_*[T]()` compile time flag parsing --- cli/cli.v | 12 +++---- cli/options.v | 78 ++++++++++++--------------------------------- cli/utils.v | 14 ++++---- cmd/export/export.v | 2 +- shy.v | 45 ++++++++++---------------- 5 files changed, 53 insertions(+), 98 deletions(-) diff --git a/cli/cli.v b/cli/cli.v index e747207..f61371a 100644 --- a/cli/cli.v +++ b/cli/cli.v @@ -10,16 +10,16 @@ pub const exe_version = version() pub const exe_name = os.file_name(os.executable()) pub const exe_short_name = os.file_name(os.executable()).replace('.exe', '') pub const exe_dir = os.dir(os.real_path(os.executable())) -pub const exe_args_description = 'input -or: shy [options] input' +pub const exe_description = 'Usage: shy [options] input +or: shy [options] input -pub const exe_description = 'shy is a module and tool made with love. +Description: shy is a module and tool made with love. It is primarily aimed at V developers roaming the creative corners of coding. shy can compile, package and deploy V apps for a wide range of platforms like: Linux, macOS, Windows, Android and HTML5 (WASM). -The following does the same as if they were passed to the "v" compiler: +The following does the same as if they were passed to the `v` command: Flags: -autofree, -gc , -g, -cg, -prod, -showcc @@ -27,13 +27,13 @@ Flags: Sub-commands: run Run the V code export Export shy based project - doctor Display useful info about your system for bug reports' + doctor Display useful info about the system' pub const exe_git_hash = shy_commit_hash() pub const work_directory = shy_tmp_work_dir() pub const cache_directory = shy_cache_dir() pub const rip_vflags = ['-autofree', '-gc', '-g', '-cg', '-prod', 'run', '-showcc'] -pub const subcmds = ['complete', 'test-cleancode', 'export'] +pub const subcmds = ['complete', 'doctor', 'export', 'test-cleancode'] pub const accepted_input_files = ['.v'] pub const shy_env_vars = [ diff --git a/cli/options.v b/cli/options.v index f03c4ca..a276bec 100644 --- a/cli/options.v +++ b/cli/options.v @@ -7,25 +7,24 @@ import flag pub struct Options { pub: // These fields would make little sense to change during a run - verbosity int - work_dir string = work_directory + verbosity int @[only: v; repeats; xdoc: 'Verbosity level 1-3'] + work_dir string = work_directory @[xdoc: 'Directory to use for temporary work files'] // - run bool - parallel bool = true // Run, what can be run, in parallel - cache bool // defaults to false in os.args/flag parsing phase - gl_version string = '3' + nocache bool @[xdoc: 'Do not use caching'] // Detected environment - dump_usage bool + run bool @[ignore] + dump_usage bool @[long: help; short: h; xdoc: 'Show this help message and exit'] pub mut: // I/O - input string - output string - additional_args []string // additional_args passed via os.args - is_prod bool - c_flags []string // flags passed to the C compiler(s) - v_flags []string // flags passed to the V compiler - assets_extra []string // list of (extra) paths to assets dirs to include - libs_extra []string + input string @[tail] + output string @[short: o; xdoc: 'Path to output (dir/file)'] + // additional_args []string // additional_args passed via os.args + is_prod bool + c_flags []string @[long: cflag; short: c; xdoc: 'Additional flags for the C compiler'] + v_flags []string @[long: flag; short: f; xdoc: 'Additional flags for the V compiler'] + assets_extra []string @[long: asset; short: a; xdoc: 'Asset dir(s) to include in build'] + libs_extra []string @[long: libs; short: l; xdoc: 'Lib dir(s) to include in build'] + version bool @[xdoc: 'Output version information and exit'] } // options_from_env returns an `Option` struct filled with flags set via @@ -35,7 +34,7 @@ pub fn options_from_env(defaults Options) !Options { if env_flags != '' { mut flags := [os.args[0]] flags << string_to_args(env_flags)! - opts, _ := args_to_options(flags, defaults)! + opts := args_to_options(flags, defaults)! return opts } return defaults @@ -85,13 +84,12 @@ pub fn (opt &Options) uses_gc() bool { // args_to_options returns an `Option` merged from (CLI/Shell) `arguments` using `defaults` as // values where no value can be obtained from `arguments`. -pub fn args_to_options(arguments []string, defaults Options) !(Options, &flag.FlagParser) { +pub fn args_to_options(arguments []string, defaults Options) !Options { mut args := arguments.clone() + // Indentify special flags in args before passing them on mut v_flags := []string{} mut cmd_flags := []string{} - // Indentify special flags in args before FlagParser ruin them. - // E.g. the -autofree flag will result in dump_usage being called for some weird reason??? for special_flag in rip_vflags { if special_flag in args { if special_flag == '-gc' { @@ -107,41 +105,7 @@ pub fn args_to_options(arguments []string, defaults Options) !(Options, &flag.Fl } } - mut fp := flag.new_flag_parser(args) - fp.application(exe_short_name) - fp.version(version_full()) - fp.description(exe_description) - fp.arguments_description(exe_args_description) - - fp.skip_executable() - - mut verbosity := fp.int_opt('verbosity', `v`, 'Verbosity level 1-3') or { defaults.verbosity } - if ('-v' in args || 'verbosity' in args) && verbosity == 0 { - verbosity = 1 - } - - mut opt := Options{ - assets_extra: fp.string_multi('assets', `a`, 'Asset dir(s) to include in build') - libs_extra: fp.string_multi('libs', `l`, 'Lib dir(s) to include in build') - v_flags: fp.string_multi('flag', `f`, 'Additional flags for the V compiler') - c_flags: fp.string_multi('cflag', `c`, 'Additional flags for the C compiler') - gl_version: fp.string('gl', 0, defaults.gl_version, 'GL(ES) version to use from any of 2,3,es2,es3') - // - run: 'run' in cmd_flags - dump_usage: fp.bool('help', `h`, defaults.dump_usage, 'Show this help message and exit') - cache: !fp.bool('nocache', 0, defaults.cache, 'Do not use build cache') - // - output: fp.string('output', `o`, defaults.output, 'Path to output (dir/file)') - // - verbosity: verbosity - parallel: !fp.bool('no-parallel', 0, false, 'Do not run tasks in parallel.') - // - work_dir: defaults.work_dir - } - - opt.additional_args = fp.finalize() or { - return error('${@FN}: flag parser failed finalizing: ${err}') - } + mut opt, _ := flag.using(defaults, args)! mut c_flags := []string{} c_flags << opt.c_flags @@ -160,7 +124,7 @@ pub fn args_to_options(arguments []string, defaults Options) !(Options, &flag.Fl } opt.v_flags = v_flags - return opt, fp + return opt } // shy_v builds `opt.input` as `v` would have done normally, except @@ -172,7 +136,7 @@ pub fn (opt &Options) shy_v() ! { mut v_cmd := [ v_exe, ] - if !opt.cache { + if opt.nocache { v_cmd << '-nocache' } if opt.is_prod { @@ -200,7 +164,7 @@ pub fn (opt &Options) shy_v() ! { if opt.run { v_compile_opt := VCompileOptions{ verbosity: opt.verbosity - cache: opt.cache + cache: !opt.nocache flags: opt.v_flags work_dir: os.join_path(opt.work_dir, 'v') input: opt.input diff --git a/cli/utils.v b/cli/utils.v index 9b33643..9a18ea8 100644 --- a/cli/utils.v +++ b/cli/utils.v @@ -44,14 +44,16 @@ fn version() string { } // run_subcommand runs any sub-command detected in `args`. -pub fn run_subcommand(args []string) ! { - nocache := args.contains('--nocache') - for subcmd in subcmds { - if subcmd in args { - // First encountered known sub-command is executed on the spot. - launch_cmd(args[args.index(subcmd)..], nocache)! +pub fn run_subcommand(args []string, opts Options) ! { + if args.len > 1 && !args[1].starts_with('-') && args[1] in subcmds { + sub_command := args[1] + if sub_command == 'doctor' { + doctor(opts) exit(0) } + // First encountered known sub-command is executed on the spot. + launch_cmd(args[args.index(sub_command)..], opts.nocache)! + exit(0) } } diff --git a/cmd/export/export.v b/cmd/export/export.v index b5a88dc..cb6e8c7 100644 --- a/cmd/export/export.v +++ b/cmd/export/export.v @@ -190,7 +190,7 @@ pub fn (opt &Options) to_export_options() export.Options { verbosity: opt.verbosity work_dir: opt.work_dir parallel: opt.parallel - cache: opt.cache + cache: !opt.nocache gl_version: gl_version format: format input: opt.input diff --git a/shy.v b/shy.v index 95c4848..1d92506 100644 --- a/shy.v +++ b/shy.v @@ -3,7 +3,6 @@ // that can be found in the LICENSE file. module main -import os import flag import term import shy.cli @@ -11,15 +10,11 @@ import shy.cli const c_embedded_shy_sixel_logo = $embed_file('assets/images/shy.six') fn main() { - // Run any sub-commands on the spot if found in args - cli.run_subcommand(os.args) or { - eprintln(err) - exit(1) - } + args := arguments() + // Collect user flags in an extended manner. // Start with defaults -> merge over SHY_FLAGS -> merge over cmdline flags -> merge .shy entries. mut opt := cli.Options{} - mut fp := &flag.FlagParser(unsafe { nil }) opt = cli.options_from_env(opt) or { eprintln('Error while parsing `SHY_FLAGS`: ${err}') @@ -27,8 +22,8 @@ fn main() { exit(1) } - opt, fp = cli.args_to_options(os.args, opt) or { - eprintln('Error while parsing `os.args`: ${err}') + opt = cli.args_to_options(args, opt) or { + eprintln('Error while parsing arguments: ${err}') eprintln('Use `${cli.exe_short_name} -h` to see all flags') exit(1) } @@ -37,36 +32,30 @@ fn main() { if term.supports_sixel() { println(c_embedded_shy_sixel_logo.to_bytes().bytestr()) } - println(fp.usage()) + println(flag.to_doc[cli.Options]( + name: cli.exe_short_name + version: '${cli.exe_version} (${cli.exe_git_hash})' + description: cli.exe_description + options: flag.DocOptions{ + compact: true + } + )!) exit(0) } // All flags after this requires an input argument - if fp.args.len == 0 { + if args.len == 1 { eprintln('No arguments given') eprintln('Use `shy -h` to see all flags') exit(1) } - // TODO - if opt.additional_args.len > 1 { - if opt.additional_args[0] == 'xxx' { - // xxx_arg := opt.additional_args[1] - exit(1) - } - } - - // Call the doctor at this point - if opt.additional_args.len > 0 { - if opt.additional_args[0] == 'doctor' { - cli.doctor(opt) - exit(0) - } + // Run any sub-commands if found on the spot if found in args + cli.run_subcommand(args, opt) or { + eprintln(err) + exit(1) } - input := fp.args.last() - opt.input = input - cli.validate_input(opt.input)! opt.extend_from_dot_shy() or {