v.pref: allow for -os wasm32_emscripten and filtering _d_wasm32_emscripten.c.v and _notd_wasm32_emscripten.c.v files. (#23797)

This commit is contained in:
Delyan Angelov 2025-02-24 11:23:37 +02:00 committed by GitHub
parent 252df04ceb
commit f83c8bf478
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 171 additions and 43 deletions

View File

@ -2,3 +2,5 @@
2048 2048
main main
index.js
index.wasm

View File

@ -28,7 +28,8 @@ Compile & run the game with `./v run examples/2048`
## Compiling to WASM ## Compiling to WASM
1. Install Emscripten from https://emscripten.org/docs/getting_started/downloads.html 1. Install Emscripten from:
https://emscripten.org/docs/getting_started/downloads.html
2. Make sure that the environment in your shell is setup correctly, 2. Make sure that the environment in your shell is setup correctly,
i.e. that `emcc --version` works. i.e. that `emcc --version` works.
@ -39,15 +40,9 @@ Compile & run the game with `./v run examples/2048`
``` ```
3. Compile the game to WASM: 3. Compile the game to WASM:
(the JS file contains a loader for the .wasm file, without the extension):
```sh ```sh
v -skip-unused -prod -os wasm32_emscripten examples/2048/` v -prod -os wasm32_emscripten -o examples/2048/index.js examples/2048/
```
4. Copy the 2048 file to `index.js` (can be done once; this step will be removed soon):
```sh
cp examples/2048/2048 examples/2048/index.js
``` ```
5. Run/test the game: 5. Run/test the game:

View File

@ -608,7 +608,7 @@ pub fn find_abs_path_of_executable(exe_name string) !string {
$if trace_find_abs_path_of_executable ? { $if trace_find_abs_path_of_executable ? {
dump(found_abs_path) dump(found_abs_path)
} }
if exists(found_abs_path) && is_executable(found_abs_path) { if is_file(found_abs_path) && is_executable(found_abs_path) {
res = found_abs_path res = found_abs_path
break break
} }

View File

@ -108,6 +108,7 @@ pub enum CC {
icc icc
msvc msvc
clang clang
emcc
unknown unknown
} }
@ -210,6 +211,7 @@ fn (mut v Builder) setup_ccompiler_options(ccompiler string) {
cc_file_name.contains('clang') || ccoptions.guessed_compiler == 'clang' { .clang } cc_file_name.contains('clang') || ccoptions.guessed_compiler == 'clang' { .clang }
cc_file_name.contains('msvc') || ccoptions.guessed_compiler == 'msvc' { .msvc } cc_file_name.contains('msvc') || ccoptions.guessed_compiler == 'msvc' { .msvc }
cc_file_name.contains('icc') || ccoptions.guessed_compiler == 'icc' { .icc } cc_file_name.contains('icc') || ccoptions.guessed_compiler == 'icc' { .icc }
cc_file_name.contains('emcc') || ccoptions.guessed_compiler == 'emcc' { .emcc }
else { .unknown } else { .unknown }
// vfmt on // vfmt on
} }
@ -652,7 +654,7 @@ pub fn (mut v Builder) cc() {
// whether to just create a .c or .js file and exit, for example: `v -o v.c cmd.v` // whether to just create a .c or .js file and exit, for example: `v -o v.c cmd.v`
ends_with_c := v.pref.out_name.ends_with('.c') ends_with_c := v.pref.out_name.ends_with('.c')
ends_with_js := v.pref.out_name.ends_with('.js') ends_with_js := v.pref.out_name.ends_with('.js')
if ends_with_c || ends_with_js { if ends_with_c || (ends_with_js && v.pref.os != .wasm32_emscripten) {
v.pref.skip_running = true v.pref.skip_running = true
msg_mv := 'os.mv_by_cp ${os.quoted_path(v.out_name_c)} => ${os.quoted_path(v.pref.out_name)}' msg_mv := 'os.mv_by_cp ${os.quoted_path(v.out_name_c)} => ${os.quoted_path(v.pref.out_name)}'
util.timing_start(msg_mv) util.timing_start(msg_mv)

View File

@ -61,7 +61,7 @@ fn (mut p Preferences) setup_os_and_arch_when_not_explicitly_set() {
host_os := if p.backend == .wasm { OS.wasi } else { get_host_os() } host_os := if p.backend == .wasm { OS.wasi } else { get_host_os() }
if p.os == ._auto { if p.os == ._auto {
p.os = host_os p.os = host_os
p.build_options << '-os ${host_os}' p.build_options << '-os ${host_os.lower()}'
} }
if !p.output_cross_c { if !p.output_cross_c {
@ -83,6 +83,19 @@ fn (mut p Preferences) setup_os_and_arch_when_not_explicitly_set() {
} }
} }
pub fn (mut p Preferences) defines_map_unique_keys() string {
mut defines_map := map[string]bool{}
for d in p.compile_defines {
defines_map[d] = true
}
for d in p.compile_defines_all {
defines_map[d] = true
}
keys := defines_map.keys()
skeys := keys.sorted()
return skeys.join(',')
}
pub fn (mut p Preferences) fill_with_defaults() { pub fn (mut p Preferences) fill_with_defaults() {
p.setup_os_and_arch_when_not_explicitly_set() p.setup_os_and_arch_when_not_explicitly_set()
p.expand_lookup_paths() p.expand_lookup_paths()
@ -189,17 +202,20 @@ pub fn (mut p Preferences) fill_with_defaults() {
} }
} }
} }
final_os := p.os.lower()
p.parse_define(final_os)
// Prepare the cache manager. All options that can affect the generated cached .c files // Prepare the cache manager. All options that can affect the generated cached .c files
// should go into res.cache_manager.vopts, which is used as a salt for the cache hash. // should go into res.cache_manager.vopts, which is used as a salt for the cache hash.
vhash := @VHASH vhash := @VHASH
p.cache_manager = vcache.new_cache_manager([ p.cache_manager = vcache.new_cache_manager([
vhash, vhash,
// ensure that different v versions use separate build artefacts // ensure that different v versions use separate build artefacts
'${p.backend} | ${p.os} | ${p.ccompiler} | ${p.is_prod} | ${p.sanitize}', '${p.backend} | ${final_os} | ${p.ccompiler} | ${p.is_prod} | ${p.sanitize}',
p.defines_map_unique_keys(),
p.cflags.trim_space(), p.cflags.trim_space(),
p.third_party_option.trim_space(), p.third_party_option.trim_space(),
p.compile_defines_all.str(),
p.compile_defines.str(),
p.lookup_path.str(), p.lookup_path.str(),
]) ])
// eprintln('prefs.cache_manager: $p') // eprintln('prefs.cache_manager: $p')

View File

@ -131,6 +131,41 @@ pub fn os_from_string(os_str string) !OS {
} }
} }
// lower returns the name that could be used with `-os osname`, for each OS enum value
// NOTE: it is important to not change the names here, they should match 1:1, since they
// are used as part of the cache keys, when -usecache is passed.
pub fn (o OS) lower() string {
return match o {
._auto { '' }
.linux { 'linux' }
.windows { 'windows' }
.macos { 'macos' }
.ios { 'ios' }
.freebsd { 'freebsd' }
.openbsd { 'openbsd' }
.netbsd { 'netbsd' }
.dragonfly { 'dragonfly' }
.js_node { 'js' }
.js_freestanding { 'js_freestanding' }
.js_browser { 'js_browser' }
.solaris { 'solaris' }
.serenity { 'serenity' }
.qnx { 'qnx' }
.plan9 { 'plan9' }
.vinix { 'vinix' }
.android { 'android' }
.termux { 'termux' }
.haiku { 'haiku' }
.raw { 'raw' }
.wasm32 { 'wasm32' }
.wasm32_wasi { 'wasm32_wasi' }
.wasm32_emscripten { 'wasm32_emscripten' }
.browser { 'browser' }
.wasi { 'wasi' }
.all { 'all' }
}
}
pub fn (o OS) str() string { pub fn (o OS) str() string {
// TODO: check more thoroughly, why this method needs to exist at all, // TODO: check more thoroughly, why this method needs to exist at all,
// and why should it override the default autogenerated .str() method, // and why should it override the default autogenerated .str() method,

View File

@ -76,6 +76,7 @@ pub enum CompilerType {
gcc gcc
tinyc tinyc
clang clang
emcc
mingw mingw
msvc msvc
cplusplus cplusplus
@ -830,7 +831,7 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
res.build_options << '${arg}' res.build_options << '${arg}'
} }
'-os' { '-os' {
target_os := cmdline.option(args[i..], '-os', '') target_os := cmdline.option(args[i..], '-os', '').to_lower_ascii()
i++ i++
target_os_kind := os_from_string(target_os) or { target_os_kind := os_from_string(target_os) or {
if target_os == 'cross' { if target_os == 'cross' {
@ -895,12 +896,6 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
} }
'-o', '-output' { '-o', '-output' {
res.out_name = cmdline.option(args[i..], arg, '') res.out_name = cmdline.option(args[i..], arg, '')
if res.out_name.ends_with('.js') {
res.backend = .js_node
res.output_cross_c = true
} else if res.out_name.ends_with('.o') {
res.is_o = true
}
if !os.is_abs_path(res.out_name) { if !os.is_abs_path(res.out_name) {
res.out_name = os.join_path(os.getwd(), res.out_name) res.out_name = os.join_path(os.getwd(), res.out_name)
} }
@ -1053,6 +1048,18 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
res.is_run = true res.is_run = true
} }
res.show_asserts = res.show_asserts || res.is_stats || os.getenv('VTEST_SHOW_ASSERTS') != '' res.show_asserts = res.show_asserts || res.is_stats || os.getenv('VTEST_SHOW_ASSERTS') != ''
if res.os != .wasm32_emscripten {
if res.out_name.ends_with('.js') {
res.backend = .js_node
res.output_cross_c = true
}
}
if res.out_name.ends_with('.o') {
res.is_o = true
}
if command == 'run' && res.is_prod && os.is_atty(1) > 0 { if command == 'run' && res.is_prod && os.is_atty(1) > 0 {
eprintln_cond(show_output && !res.is_quiet, "Note: building an optimized binary takes much longer. It shouldn't be used with `v run`.") eprintln_cond(show_output && !res.is_quiet, "Note: building an optimized binary takes much longer. It shouldn't be used with `v run`.")
eprintln_cond(show_output && !res.is_quiet, 'Use `v run` without optimization, or build an optimized binary with -prod first, then run it separately.') eprintln_cond(show_output && !res.is_quiet, 'Use `v run` without optimization, or build an optimized binary with -prod first, then run it separately.')
@ -1226,6 +1233,7 @@ pub fn cc_from_string(s string) CompilerType {
cc.contains('tcc') || cc.contains('tinyc') { .tinyc } cc.contains('tcc') || cc.contains('tinyc') { .tinyc }
cc.contains('gcc') { .gcc } cc.contains('gcc') { .gcc }
cc.contains('clang') { .clang } cc.contains('clang') { .clang }
cc.contains('emcc') { .emcc }
cc.contains('msvc') { .msvc } cc.contains('msvc') { .msvc }
cc.contains('mingw') { .mingw } cc.contains('mingw') { .mingw }
cc.contains('++') { .cplusplus } cc.contains('++') { .cplusplus }

View File

@ -15,28 +15,9 @@ pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []s
|| file.all_before_last('.v').all_before_last('.').ends_with('_test') { || file.all_before_last('.v').all_before_last('.').ends_with('_test') {
continue continue
} }
if prefs.backend in [.c, .interpret] && !prefs.should_compile_c(file) { mut is_d_notd_file := false
continue
}
if prefs.backend.is_js() && !prefs.should_compile_js(file) {
continue
}
if prefs.backend == .native && !prefs.should_compile_native(file) {
continue
}
if !prefs.backend.is_js() && !prefs.should_compile_asm(file) {
continue
}
if file.starts_with('.#') {
continue
}
if !prefs.prealloc && !prefs.output_cross_c && file.ends_with('prealloc.c.v') {
continue
}
if prefs.nofloat && file.ends_with('float.c.v') {
continue
}
if file.contains('_d_') { if file.contains('_d_') {
is_d_notd_file = true
if prefs.compile_defines_all.len == 0 { if prefs.compile_defines_all.len == 0 {
continue continue
} }
@ -58,6 +39,7 @@ pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []s
} }
} }
if file.contains('_notd_') { if file.contains('_notd_') {
is_d_notd_file = true
mut allowed := true mut allowed := true
for cdefine in prefs.compile_defines { for cdefine in prefs.compile_defines {
file_postfixes := ['_notd_${cdefine}.v', '_notd_${cdefine}.c.v'] file_postfixes := ['_notd_${cdefine}.v', '_notd_${cdefine}.c.v']
@ -75,6 +57,27 @@ pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []s
continue continue
} }
} }
if prefs.backend in [.c, .interpret] && !is_d_notd_file && !prefs.should_compile_c(file) {
continue
}
if prefs.backend.is_js() && !prefs.should_compile_js(file) {
continue
}
if prefs.backend == .native && !prefs.should_compile_native(file) {
continue
}
if !prefs.backend.is_js() && !prefs.should_compile_asm(file) {
continue
}
if file.starts_with('.#') {
continue
}
if !prefs.prealloc && !prefs.output_cross_c && file.ends_with('prealloc.c.v') {
continue
}
if prefs.nofloat && file.ends_with('float.c.v') {
continue
}
if prefs.exclude.len > 0 { if prefs.exclude.len > 0 {
full_file_path := os.join_path(dir, file) full_file_path := os.join_path(dir, file)
for epattern in prefs.exclude { for epattern in prefs.exclude {

View File

@ -0,0 +1,5 @@
module main
fn abc() {
println('This is abc_d_wasm32_emscripten.c.v')
}

View File

@ -0,0 +1,5 @@
module main
fn abc() {
println('This is abc_notd_wasm32_emscripten.c.v')
}

View File

@ -0,0 +1,51 @@
import os
const vexe = os.quoted_path(@VEXE)
const project_folder = os.dir(@FILE)
const output_path = os.join_path(os.vtmp_dir(), 'check_wasm_works')
fn testsuite_begin() {
os.mkdir_all(output_path) or {}
os.chdir(output_path)!
dump(os.getwd())
}
fn testsuite_end() {
os.system('ls -la .')
os.chdir(os.home_dir()) or {}
os.rmdir_all(output_path) or {}
}
fn test_normal() {
if os.user_os() == 'windows' {
return
}
defer { println('done ${@FN}') }
dump(vexe)
res := os.system('${os.quoted_path(vexe)} -o normal.exe ${os.quoted_path(project_folder)}')
assert res == 0
dump(res)
assert os.exists('normal.exe')
content := os.read_file('normal.exe')!
assert content.contains('This is abc_notd_wasm32_emscripten.c.v')
}
fn test_emcc() {
if os.user_os() == 'windows' {
return
}
defer { println('done ${@FN}') }
emcc := os.find_abs_path_of_executable('emcc') or {
println('skipping ${@FN} since `emcc` is not found')
return
}
dump(emcc)
res := os.system('${os.quoted_path(vexe)} -os wasm32_emscripten -o wasm_check.html ${os.quoted_path(project_folder)}')
assert res == 0
dump(res)
assert os.exists('wasm_check.html')
assert os.exists('wasm_check.js')
assert os.exists('wasm_check.wasm')
content := os.read_file('wasm_check.wasm')!
assert content.contains('This is abc_d_wasm32_emscripten.c.v')
}

View File

@ -0,0 +1,6 @@
module main
fn main() {
abc()
println('hi from main.v')
}