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
main
index.js
index.wasm

View File

@ -28,7 +28,8 @@ Compile & run the game with `./v run examples/2048`
## 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,
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:
(the JS file contains a loader for the .wasm file, without the extension):
```sh
v -skip-unused -prod -os wasm32_emscripten 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
v -prod -os wasm32_emscripten -o examples/2048/index.js examples/2048/
```
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 ? {
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
break
}

View File

@ -108,6 +108,7 @@ pub enum CC {
icc
msvc
clang
emcc
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('msvc') || ccoptions.guessed_compiler == 'msvc' { .msvc }
cc_file_name.contains('icc') || ccoptions.guessed_compiler == 'icc' { .icc }
cc_file_name.contains('emcc') || ccoptions.guessed_compiler == 'emcc' { .emcc }
else { .unknown }
// 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`
ends_with_c := v.pref.out_name.ends_with('.c')
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
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)

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() }
if p.os == ._auto {
p.os = host_os
p.build_options << '-os ${host_os}'
p.build_options << '-os ${host_os.lower()}'
}
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() {
p.setup_os_and_arch_when_not_explicitly_set()
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
// should go into res.cache_manager.vopts, which is used as a salt for the cache hash.
vhash := @VHASH
p.cache_manager = vcache.new_cache_manager([
vhash,
// 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.third_party_option.trim_space(),
p.compile_defines_all.str(),
p.compile_defines.str(),
p.lookup_path.str(),
])
// 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 {
// TODO: check more thoroughly, why this method needs to exist at all,
// and why should it override the default autogenerated .str() method,

View File

@ -76,6 +76,7 @@ pub enum CompilerType {
gcc
tinyc
clang
emcc
mingw
msvc
cplusplus
@ -830,7 +831,7 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
res.build_options << '${arg}'
}
'-os' {
target_os := cmdline.option(args[i..], '-os', '')
target_os := cmdline.option(args[i..], '-os', '').to_lower_ascii()
i++
target_os_kind := os_from_string(target_os) or {
if target_os == 'cross' {
@ -895,12 +896,6 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
}
'-o', '-output' {
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) {
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.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 {
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.')
@ -1226,6 +1233,7 @@ pub fn cc_from_string(s string) CompilerType {
cc.contains('tcc') || cc.contains('tinyc') { .tinyc }
cc.contains('gcc') { .gcc }
cc.contains('clang') { .clang }
cc.contains('emcc') { .emcc }
cc.contains('msvc') { .msvc }
cc.contains('mingw') { .mingw }
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') {
continue
}
if prefs.backend in [.c, .interpret] && !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
}
mut is_d_notd_file := false
if file.contains('_d_') {
is_d_notd_file = true
if prefs.compile_defines_all.len == 0 {
continue
}
@ -58,6 +39,7 @@ pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []s
}
}
if file.contains('_notd_') {
is_d_notd_file = true
mut allowed := true
for cdefine in prefs.compile_defines {
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
}
}
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 {
full_file_path := os.join_path(dir, file)
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')
}