cmd/tools: add check_os_api_parity.v - keep module APIs even
parent
05177b9dcb
commit
5ef9569098
|
@ -0,0 +1,126 @@
|
|||
module main
|
||||
|
||||
import os
|
||||
import v.util
|
||||
import v.pref
|
||||
import v.builder
|
||||
import v.ast
|
||||
import term
|
||||
|
||||
const (
|
||||
base_os = 'linux'
|
||||
os_names = ['linux', 'macos', 'windows']
|
||||
skip_modules = [
|
||||
'builtin.bare', 'builtin.js',
|
||||
'strconv', 'strconv.ftoa', 'hash.wyhash', '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 := util.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.size
|
||||
eprintln('NB: please, do run `git clean -xf` after this tool, or at least `find thirdparty/ |grep .o$|xargs rm`')
|
||||
eprintln('otherwise, `./v test-fixed` may show false positives, due to .o files compiled with a cross compiler.')
|
||||
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, 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 {
|
||||
fnd := s as ast.FnDecl
|
||||
if fnd.is_pub {
|
||||
fn_signature := fnd.str(b.table)
|
||||
fn_mod := fnd.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, api_os, mod_name, os_base, os_target string) {
|
||||
res := util.color_compare_strings(app.diff_cmd, 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
|
||||
}
|
||||
}
|
|
@ -163,7 +163,6 @@ fn (foptions &FormatOptions) format_file(file string) {
|
|||
fn print_compiler_options(compiler_params &pref.Preferences) {
|
||||
eprintln(' os: ' + compiler_params.os.str())
|
||||
eprintln(' ccompiler: $compiler_params.ccompiler')
|
||||
eprintln(' mod: $compiler_params.mod ')
|
||||
eprintln(' path: $compiler_params.path ')
|
||||
eprintln(' out_name: $compiler_params.out_name ')
|
||||
eprintln(' vroot: $compiler_params.vroot ')
|
||||
|
|
|
@ -210,6 +210,7 @@ pub mut:
|
|||
pub struct FnDecl {
|
||||
pub:
|
||||
name string
|
||||
mod string
|
||||
stmts []Stmt
|
||||
args []table.Arg
|
||||
is_deprecated bool
|
||||
|
|
|
@ -6,6 +6,21 @@ module ast
|
|||
import v.table
|
||||
import strings
|
||||
|
||||
pub fn (node &FnDecl) modname() string {
|
||||
if node.mod != '' {
|
||||
return node.mod
|
||||
}
|
||||
mut pamod := node.name.all_before_last('.')
|
||||
if pamod == node.name.after('.') {
|
||||
pamod = if node.is_builtin {
|
||||
'builtin'
|
||||
} else {
|
||||
'main'
|
||||
}
|
||||
}
|
||||
return pamod
|
||||
}
|
||||
|
||||
// These methods are used only by vfmt, vdoc, and for debugging.
|
||||
|
||||
pub fn (node &FnDecl) str(t &table.Table) string {
|
||||
|
|
|
@ -4382,13 +4382,6 @@ fn (mut g Gen) panic_debug_info(pos token.Position) (int, string, string, string
|
|||
paline := pos.line_nr + 1
|
||||
pafile := g.fn_decl.file.replace('\\', '/')
|
||||
pafn := g.fn_decl.name.after('.')
|
||||
mut pamod := g.fn_decl.name.all_before_last('.')
|
||||
if pamod == pafn {
|
||||
pamod = if g.fn_decl.is_builtin {
|
||||
'builtin'
|
||||
} else {
|
||||
'main'
|
||||
}
|
||||
}
|
||||
pamod := g.fn_decl.modname()
|
||||
return paline, pafile, pamod, pafn
|
||||
}
|
||||
|
|
|
@ -277,6 +277,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
p.attr_ctdefine = ''
|
||||
return ast.FnDecl{
|
||||
name: name
|
||||
mod: p.mod
|
||||
stmts: stmts
|
||||
return_type: return_type
|
||||
args: args
|
||||
|
@ -339,6 +340,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
|
|||
return ast.AnonFn{
|
||||
decl: ast.FnDecl{
|
||||
name: name
|
||||
mod: p.mod
|
||||
stmts: stmts
|
||||
return_type: return_type
|
||||
args: args
|
||||
|
|
|
@ -144,6 +144,7 @@ fn (mut p Parser) parse() ast.File {
|
|||
if p.pref.is_script && !p.pref.is_test && p.mod == 'main' && !have_fn_main(stmts) {
|
||||
stmts << ast.FnDecl{
|
||||
name: 'main'
|
||||
mod: p.mod
|
||||
file: p.file_name
|
||||
return_type: table.void_type
|
||||
}
|
||||
|
@ -458,6 +459,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
|
|||
}
|
||||
return ast.FnDecl{
|
||||
name: 'main'
|
||||
mod: p.mod
|
||||
stmts: stmts
|
||||
file: p.file_name
|
||||
return_type: table.void_type
|
||||
|
|
|
@ -319,6 +319,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
args << args2
|
||||
mut method := ast.FnDecl{
|
||||
name: name
|
||||
mod: p.mod
|
||||
args: args
|
||||
file: p.file_name
|
||||
return_type: table.void_type
|
||||
|
|
|
@ -103,7 +103,6 @@ pub mut:
|
|||
compile_defines []string // just ['vfmt']
|
||||
compile_defines_all []string // contains both: ['vfmt','another']
|
||||
|
||||
mod string
|
||||
run_args []string // `v run x.v 1 2 3` => `1 2 3`
|
||||
printfn_list []string // a list of generated function names, whose source should be shown, for debugging
|
||||
print_v_files bool // when true, just print the list of all parsed .v files then stop.
|
||||
|
|
Loading…
Reference in New Issue