aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/cmd/tools/check_os_api_parity.v
blob: de6242ba377d385d9671aa8089118b82fabe8c34 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
module main

import os
import v.util
import v.util.diff
import v.pref
import v.builder
import v.ast
import rand
import term

const (
	base_os      = 'linux'
	os_names     = ['linux', 'macos', 'windows']
	skip_modules = [
		'builtin.bare',
		'builtin.linux_bare.old',
		'builtin.js',
		'strconv',
		'strconv.ftoa',
		'hash',
		'strings',
		'crypto.rand',
		'os.bare',
		'os2',
		'picohttpparser',
		'picoev',
		'szip',
		'v.eval',
	]
)

struct App {
	diff_cmd   string
	is_verbose bool
	modules    []string
mut:
	api_differences map[string]int
}

fn main() {
	vexe := pref.vexe_path()
	vroot := os.dir(vexe)
	util.set_vroot_folder(vroot)
	os.chdir(vroot) ?
	cmd := diff.find_working_diff_command() or { '' }
	mut app := App{
		diff_cmd: cmd
		is_verbose: os.getenv('VERBOSE').len > 0
		modules: if os.args.len > 1 { os.args[1..] } else { all_vlib_modules() }
	}
	for mname in app.modules {
		if !app.is_verbose {
			eprintln('Checking module: $mname ...')
		}
		api_base := app.gen_api_for_module_in_os(mname, base_os)
		for oname in os_names {
			if oname == base_os {
				continue
			}
			api_os := app.gen_api_for_module_in_os(mname, oname)
			app.compare_api(api_base, api_os, mname, base_os, oname)
		}
	}
	howmany := app.api_differences.len
	if howmany > 0 {
		eprintln(term.header('Found $howmany modules with different APIs', '='))
		for m in app.api_differences.keys() {
			eprintln('Module: $m')
		}
		exit(1)
	}
}

fn all_vlib_modules() []string {
	mut vlib_v_files := os.walk_ext('vlib', '.v')
	mut vmodulesmap := map[string]int{}
	for f in vlib_v_files {
		if f.contains('/tests/') || f.ends_with('_test.v') {
			continue
		}
		vmodulename := os.dir(f).replace('/', '.').replace('vlib.', '')
		if vmodulename in skip_modules {
			continue
		}
		vmodulesmap[vmodulename] = vmodulesmap[vmodulename] + 1
	}
	mut modules := vmodulesmap.keys()
	modules.sort()
	return modules
}

fn (app App) gen_api_for_module_in_os(mod_name string, os_name string) string {
	if app.is_verbose {
		eprintln('Checking module: ${mod_name:-30} for OS: ${os_name:-10} ...')
	}
	mpath := os.join_path('vlib', mod_name.replace('.', '/'))
	tmpname := '/tmp/${mod_name}_${os_name}.c'
	prefs, _ := pref.parse_args([], ['-os', os_name, '-o', tmpname, '-shared', mpath])
	mut b := builder.new_builder(prefs)
	b.compile_c()
	mut res := []string{}
	for f in b.parsed_files {
		for s in f.stmts {
			if s is ast.FnDecl {
				if s.is_pub {
					fn_signature := s.stringify(b.table, mod_name, map[string]string{})
					fn_mod := s.modname()
					if fn_mod == mod_name {
						fline := '$fn_mod: $fn_signature'
						res << fline
					}
				}
			}
		}
	}
	res.sort()
	return res.join('\n')
}

fn (mut app App) compare_api(api_base string, api_os string, mod_name string, os_base string, os_target string) {
	res := diff.color_compare_strings(app.diff_cmd, rand.ulid(), api_base, api_os)
	if res.len > 0 {
		summary := 'Different APIs found for module: `$mod_name`, between OS base: `$os_base` and OS: `$os_target`'
		eprintln(term.header(summary, '-'))
		eprintln(res)
		eprintln(term.h_divider('-'))
		app.api_differences[mod_name] = 1
	}
}