| 1 | module main |
| 2 | |
| 3 | import os |
| 4 | import v.util |
| 5 | import v.util.diff |
| 6 | import v.pref |
| 7 | import v.builder |
| 8 | import v.builder.cbuilder |
| 9 | import v.ast |
| 10 | import term |
| 11 | |
| 12 | const base_os = pref.get_host_os() |
| 13 | const os_list = [pref.OS.linux, .macos, .windows, .freebsd, .openbsd, .solaris, .termux] |
| 14 | const skip_modules = [ |
| 15 | 'builtin.bare', |
| 16 | 'builtin.linux_bare.old', |
| 17 | 'builtin.js', |
| 18 | 'builtin.wasm', |
| 19 | 'strconv', |
| 20 | 'strconv.ftoa', |
| 21 | 'hash', |
| 22 | 'strings', |
| 23 | 'crypto.rand', |
| 24 | 'os.bare', |
| 25 | 'os2', |
| 26 | 'szip', |
| 27 | ] |
| 28 | const is_verbose = os.getenv('VERBOSE') != '' |
| 29 | |
| 30 | fn main() { |
| 31 | vexe := os.real_path(os.getenv_opt('VEXE') or { @VEXE }) |
| 32 | vroot := os.dir(vexe) |
| 33 | util.set_vroot_folder(vroot) |
| 34 | os.chdir(vroot)! |
| 35 | modules := if os.args.len > 1 { os.args[1..] } else { all_vlib_modules() } |
| 36 | mut diff_modules := map[string]bool{} |
| 37 | other_os_list := os_list.filter(it != base_os) |
| 38 | for m in modules { |
| 39 | if !is_verbose { |
| 40 | eprintln('Checking module: ${m} ...') |
| 41 | } |
| 42 | api_base := gen_api_for_module_in_os(m, base_os) |
| 43 | for other_os in other_os_list { |
| 44 | api_os := gen_api_for_module_in_os(m, other_os) |
| 45 | if api_base == api_os { |
| 46 | continue |
| 47 | } |
| 48 | diff_modules[m] = true |
| 49 | summary := 'Different APIs found for module: `${m}`, between OS base: `${base_os}` and OS: `${other_os}`' |
| 50 | eprintln(term.header(summary, '-')) |
| 51 | diff_ := diff.compare_text(api_base, api_os) or { continue } |
| 52 | println(diff_) |
| 53 | eprintln(term.h_divider('-')) |
| 54 | } |
| 55 | } |
| 56 | if diff_modules.len > 0 { |
| 57 | eprintln(term.header('Found ${diff_modules.len} modules with different APIs', '=')) |
| 58 | for m in diff_modules.keys() { |
| 59 | eprintln('Module: ${m}') |
| 60 | } |
| 61 | exit(1) |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | fn all_vlib_modules() []string { |
| 66 | mut vlib_v_files := os.walk_ext('vlib', '.v') |
| 67 | mut vmodulesmap := map[string]int{} |
| 68 | for f in vlib_v_files { |
| 69 | if f.contains('/tests/') || f.ends_with('_test.v') { |
| 70 | continue |
| 71 | } |
| 72 | vmodulename := os.dir(f).replace('/', '.').replace('vlib.', '') |
| 73 | if vmodulename in skip_modules { |
| 74 | continue |
| 75 | } |
| 76 | vmodulesmap[vmodulename] = vmodulesmap[vmodulename] + 1 |
| 77 | } |
| 78 | mut modules := vmodulesmap.keys() |
| 79 | modules.sort() |
| 80 | return modules |
| 81 | } |
| 82 | |
| 83 | fn gen_api_for_module_in_os(mod_name string, os_ pref.OS) string { |
| 84 | if is_verbose { |
| 85 | eprintln('Checking module: ${mod_name:-30} for OS: ${os_:-10} ...') |
| 86 | } |
| 87 | os_name := os_.str().to_lower() |
| 88 | mpath := os.join_path('vlib', mod_name.replace('.', '/')) |
| 89 | tmpname := '/tmp/${mod_name}_${os_name}.c' |
| 90 | prefs, _ := pref.parse_args([], ['-os', os_name, '-o', tmpname, '-shared', mpath]) |
| 91 | mut b := builder.new_builder(prefs) |
| 92 | cbuilder.compile_c(mut b) |
| 93 | mut res := []string{} |
| 94 | for f in b.parsed_files { |
| 95 | for s in f.stmts { |
| 96 | if s is ast.FnDecl && s.is_pub { |
| 97 | fn_mod := s.modname() |
| 98 | if fn_mod == mod_name { |
| 99 | fn_signature := b.table.stringify_fn_decl(&s, mod_name, map[string]string{}, |
| 100 | false) |
| 101 | fline := '${fn_mod}: ${fn_signature}' |
| 102 | res << fline |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | } |
| 107 | res.sort() |
| 108 | return res.join('\n') |
| 109 | } |
| 110 | |