v / cmd / tools / check_os_api_parity.v
109 lines · 104 sloc · 2.77 KB · 6dd9033de29819f9ada4011f783b1d8b9d47d7e8
Raw
1module main
2
3import os
4import v.util
5import v.util.diff
6import v.pref
7import v.builder
8import v.builder.cbuilder
9import v.ast
10import term
11
12const base_os = pref.get_host_os()
13const os_list = [pref.OS.linux, .macos, .windows, .freebsd, .openbsd, .solaris, .termux]
14const 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]
28const is_verbose = os.getenv('VERBOSE') != ''
29
30fn 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
65fn 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
83fn 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