Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

shy: revamp shy export internals #16

Merged
merged 2 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 68 additions & 196 deletions cmd/export/export.v
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ module main

import os
import flag
// import shy.vxt
import shy.cli
import shy.export
import shy.utils

pub const exe_version = '0.0.2' // version()

Expand All @@ -17,196 +16,70 @@ pub const exe_dir = os.dir(os.real_path(os.executable()))
pub const exe_args_description = 'input
or: [options] input'

pub const exe_description = 'export exports both plain V applications and shy-based applications.
pub const exe_description = 'shy ${exe_short_name} <exporter> [options]
${exe_short_name} exports both plain V applications and shy-based applications.
The exporter is based on the `shy.export` module.

export can compile, package and deploy V apps for production use on 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:

Flags:
-autofree, -gc <type>, -g, -cg, -showcc

Sub-commands:
run Run the output package after successful export'
run Run the output package after successful export

pub const rip_vflags = ['-autofree', '-gc', '-g', '-cg', 'run', '-showcc']
pub const accepted_input_files = ['.v']
Exporters:
${export.supported_exporters}'

pub const export_env_vars = [
'SHY_FLAGS',
'SHY_EXPORT_FLAGS',
'VEXE',
'VMODULES',
]

/*
pub fn run(args []string) os.Result {
return os.execute(args.join(' '))
}*/
pub struct Options {
pub:
// These fields would make little sense to change during a run
verbosity int @[only: v; repeats; xdoc: 'Verbosity level 1-3']
work_dir string = export.work_dir() @[xdoc: 'Directory to use for temporary work files']
verbosity int @[repeats; short: v; xdoc: 'Verbosity level 1-3']
//
run bool @[ignore]
no_parallel bool @[xdoc: 'Do not run tasks in parallel'] // Run, what can be run, in parallel
nocache bool @[xdoc: 'Do not use caching'] // defaults to false in os.args/flag parsing phase
gl_version string = '3' @[only: gl; xdoc: 'GL(ES) version to use from any of 3,es3']
format string = 'zip' @[xdoc: 'Format of output (default is a .zip)']
run bool @[ignore]
nocache bool @[xdoc: 'Do not use caching'] // defaults to false in os.args/flag parsing phase
// echo and exit
dump_usage bool @[long: help; short: h; xdoc: 'Show this help message and exit']
show_version bool @[long: version; xdoc: 'Output version information and exit']
mut:
unmatched_args []string @[ignore] // args that could not be matched
pub mut:
// I/O
input string @[tail]
output string @[short: o; xdoc: 'Path to output (dir/file)']
is_prod bool = true @[ignore]
c_flags []string @[long: cflag; short: c; xdoc: 'Additional flags for the C compiler'] // flags passed to the C compiler(s)
v_flags []string @[long: flag; short: f; xdoc: 'Additional flags for the V compiler'] // flags passed to the V compiler
assets_extra []string @[long: asset; short: a; xdoc: 'Asset dir(s) to include in build'] // list of (extra) paths to assets dirs to include
libs_extra []string @[long: libs; short: l; xdoc: 'Lib dir(s) to include in build']
//
exporter []string @[ignore]
}

// options_from_env returns an `Option` struct filled with flags set via
// the `SHY_EXPORT_FLAGS` env variable otherwise it returns the `defaults` `Option` struct.
pub fn options_from_env(defaults Options) !Options {
env_flags := os.getenv('SHY_EXPORT_FLAGS')
if env_flags != '' {
mut flags := [os.args[0]]
flags << cli.string_to_args(env_flags)!
opts := args_to_options(flags, defaults)!
return opts
pub fn args_to_options(arguments []string) !Options {
if arguments.len <= 1 {
return error('no arguments given')
}
return defaults
}

// extend_from_dot_shy will merge the `Options` with any content
// found in any `.shy` config files.
pub fn (mut opt Options) extend_from_dot_shy() ! {
// Look up values in input .shy file next to input if no flags or defaults was set
// TODO use TOML format here
// dot_shy_file := dot_shy_path(opt.input)
// dot_shy := os.read_file(dot_shy_file) or { '' }
}

// validate_env ensures that `Options` meet all runtime requirements.
pub fn (opt &Options) validate_env() ! {}

// 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 {
mut args := arguments.clone()

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' {
gc_type := args[(args.index(special_flag)) + 1]
v_flags << special_flag + ' ${gc_type}'
args.delete(args.index(special_flag) + 1)
} else if special_flag.starts_with('-') {
v_flags << special_flag
} else {
cmd_flags << special_flag
}
args.delete(args.index(special_flag))
}
}

mut opt, unmatched_args := flag.using(defaults, args, skip: 1)!

// Validate format
if opt.format != '' {
export.string_to_export_format(opt.format)!
}

mut unmatched := unmatched_args.clone()
for unmatched_arg in defaults.unmatched_args {
if unmatched_arg !in unmatched {
unmatched << unmatched_arg
mut run := false
for i, arg in args {
if arg == 'run' {
run = true
args.delete(i)
break
}
}
opt.unmatched_args = unmatched

mut c_flags := []string{}
c_flags << opt.c_flags
for c_flag in defaults.c_flags {
if c_flag !in c_flags {
c_flags << c_flag
mut exporter := []string{cap: 0}
for i, arg in args {
if arg in export.supported_exporters {
exporter = args[i..].clone()
args = args[..i].clone()
break
}
}
opt.c_flags = c_flags
opt, _ := flag.to_struct[Options](args, skip: 1)!

v_flags << opt.v_flags
for v_flag in defaults.v_flags {
if v_flag !in v_flags {
v_flags << v_flag
}
return Options{
...opt
run: run
exporter: exporter
}
opt.v_flags = v_flags

return opt
}

pub fn (opt &Options) to_export_options() export.Options {
format := export.string_to_export_format(opt.format) or { export.Format.zip }
mut gl_version := opt.gl_version

opts := export.Options{
verbosity: opt.verbosity
work_dir: opt.work_dir
parallel: !opt.no_parallel
cache: !opt.nocache
gl_version: gl_version
format: format
input: opt.input
output: opt.output
is_prod: opt.is_prod
c_flags: opt.c_flags
v_flags: opt.v_flags
assets: opt.assets_extra
}
return opts
}

fn main() {
args := os.args[1..]

// Collect user flags in an extended manner.
mut opt := Options{}

/* TODO: (lmp) fix this .shy should be used first, then from env then flags... right?
opt = extend_from_dot_shy() or {
eprintln('Error while parsing `.shy`: ${err}')
eprintln('Use `${cli.exe_short_name} -h` to see all flags')
exit(1)
}
*/

opt = options_from_env(opt) or {
eprintln('Error while parsing `SHY_EXPORT_FLAGS`: ${err}')
eprintln('Use `${exe_short_name} -h` to see all flags')
exit(1)
}

opt = args_to_options(args, opt) or {
eprintln('Error while parsing `os.args`: ${err}')
eprintln('Use `${exe_short_name} -h` to see all flags')
exit(1)
}

if args.len == 1 {
eprintln('No arguments given')
eprintln('Use `shy export -h` to see all flags')
opt := args_to_options(os.args) or {
utils.shy_error('Error while parsing arguments: ${err}',
details: 'Use `${exe_short_name} -h` to see all flags'
)
exit(1)
}

Expand All @@ -227,43 +100,42 @@ fn main() {
exit(0)
}

// All flags after this requires an input argument
if opt.input == '' {
dump(opt)
eprintln('No input given')
eprintln('See `shy export -h` for help')
exit(1)
}

// Validate environment after options and input has been resolved
opt.validate_env() or { panic(err) }

// input_ext := os.file_ext(opt.input)
if opt.verbosity > 2 {
dump(opt)
}

// TODO
if opt.unmatched_args.len > 1 {
eprintln('Unknown args: ${opt.unmatched_args}')
exit(1)
}

// Validate environment after options and input has been resolved
opt.validate_env() or {
eprintln('${err}')
if opt.exporter.len == 0 {
utils.shy_error('No exporter defined', details: 'See `shy export -h` for help')
exit(1)
}

// input_ext := os.file_ext(opt.input)
if opt.verbosity > 3 {
eprintln('--- ${exe_short_name} ---')
eprintln(opt)
mut export_result := export.Result{}
match opt.exporter[0] {
'appimage' {
appimage_export_options := export.args_to_appimage_options(opt.exporter) or {
utils.shy_error('Error getting AppImage options', details: '${err}')
exit(1)
}
export_result = export.appimage(appimage_export_options) or {
utils.shy_error('Error while exporting to AppImage', details: '${err}')
exit(1)
}
}
'wasm' {
wasm_export_options := export.args_to_wasm_options(opt.exporter) or {
utils.shy_error('Error getting wasm options', details: '${err}')
exit(1)
}
export_result = export.wasm(wasm_export_options) or {
utils.shy_error('Error while exporting to wasm', details: '${err}')
exit(1)
}
}
else {
utils.shy_error('Unknown exporter "${opt.exporter[0]}"',
details: 'Valid exporters: ${export.supported_exporters}'
)
exit(1)
}
}

export_opts := opt.to_export_options()
export.export(export_opts) or {
eprintln('Error while exporting `${export_opts.input}`: ${err}')
exit(1)
if export_result.output != '' {
println(export_result.output)
}
exit(0)
}
3 changes: 3 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#### Notable changes

* Add support for `wasm32_emscripten` build target making it possible to target the Web via `emscripten`/`emcc`
* **WIP** Rewrite `shy export` internals
- Exporting to an `AppImage` on Linux now works via `shy export appimage ...`
- Exporting to the Web (via `emcc`) now works via `shy export wasm ...`

#### Breaking changes

Expand Down
3 changes: 1 addition & 2 deletions docs/dev/emscripten_howto.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ static char __CLOSURE_GET_DATA_BYTES[] = {

-gsource-map -sSTACK_SIZE=8mb
# -sTOTAL_MEMORY=300mb -sINITIAL_MEMORY=200mb

-sEMSCRIPTEN_KEEPALIVE?? test this instead of -sEXPORTED_FUNCTIONS...
# Puzzle Vibes
shy_root="$(pwd)"; pro="$HOME/Projects/puzzle_vibes"; v -skip-unused -gc none -d wasm32_emscripten -os wasm32_emscripten -o /tmp/shyem/vc_src.c $pro && emcc -flto -fPIC -fvisibility=hidden --preload-file $shy_root/assets@/ --preload-file $pro/assets@/ -sEXPORTED_FUNCTIONS="['_malloc', '_main']" -sSTACK_SIZE=1mb -sERROR_ON_UNDEFINED_SYMBOLS=0 -sASSERTIONS=1 -sUSE_WEBGL2=1 -sUSE_SDL=2 -sNO_EXIT_RUNTIME=1 -sALLOW_MEMORY_GROWTH=1 -O0 -g -D_DEBUG_ -D_DEBUG -D SOKOL_GLES3 -D SOKOL_NO_ENTRY -D MINIAUDIO_IMPLEMENTATION -D _REENTRANT -I "$shy_root/thirdparty/stb" -I "$shy_root/thirdparty/fontstash" -I "$shy_root/thirdparty/sokol" -I "$shy_root/thirdparty/sokol/util" -I "$shy_root/wraps/miniaudio/c/miniaudio" -I "$shy_root/shy" -Wno-enum-conversion -Wno-unused-value $shy_root/thirdparty/stb/stbi.c /tmp/shyem/vc_src.c -lm -lpthread -ldl -o /tmp/shyem/vc_src.html


Loading
Loading