vxx2 / vlib / v / tests / shared_library_system_link_test.v
108 lines · 104 sloc · 3.37 KB · ebc55063c0136d545cecdc1c2f3ed385076c236a
Raw
1import os
2import rand
3
4const vexe = @VEXE
5
6fn test_shared_library_links_with_system_cc() {
7 if os.user_os() != 'linux' {
8 return
9 }
10 workdir := os.join_path(os.vtmp_dir(), 'v_shared_link_${rand.ulid()}')
11 os.mkdir_all(workdir) or { panic(err) }
12 defer {
13 os.rmdir_all(workdir) or {}
14 }
15 lib_src := os.join_path(workdir, 'libfoo.v')
16 lib_out := os.join_path(workdir, 'libfoo')
17 lib_so := '${lib_out}.so'
18 host_src := os.join_path(workdir, 'host.c')
19 host_bin := os.join_path(workdir, 'host')
20 os.write_file(lib_src, [
21 'module libfoo',
22 '',
23 "@[export: 'libfoo_square']",
24 'pub fn square(x int) int {',
25 '\treturn x * x',
26 '}',
27 ].join('\n')) or { panic(err) }
28 os.write_file(host_src, [
29 '#include <stdio.h>',
30 '',
31 'int libfoo_square(int);',
32 '',
33 'int main(void) {',
34 '\tprintf("%d\\n", libfoo_square(2));',
35 '\treturn 0;',
36 '}',
37 ].join('\n')) or { panic(err) }
38 run_cmd('${os.quoted_path(vexe)} -shared -o ${os.quoted_path(lib_out)} ${os.quoted_path(lib_src)}') or {
39 panic(err)
40 }
41 assert os.exists(lib_so)
42 run_cmd('cc ${os.quoted_path(host_src)} -L${os.quoted_path(workdir)} -lfoo -o ${os.quoted_path(host_bin)}') or {
43 panic(err)
44 }
45 res := run_cmd('LD_LIBRARY_PATH=${os.quoted_path(workdir)} ${os.quoted_path(host_bin)}') or {
46 panic(err)
47 }
48 assert res.output.trim_space() == '4'
49}
50
51fn test_shared_library_only_exports_tagged_symbols() {
52 // Regression test for vlang/v#27167: `v -shared` used to export every
53 // V runtime/stdlib symbol, not just functions tagged with `@[export: '…']`.
54 uos := os.user_os()
55 if uos !in ['linux', 'macos'] {
56 return
57 }
58 // tcc does not honor `-fvisibility=hidden`, so the symbol-set check would be
59 // noisy there; use a system compiler for Linux and clang on macOS.
60 cc_flag := if uos == 'macos' { '-cc clang' } else { '-cc cc' }
61 workdir := os.join_path(os.vtmp_dir(), 'v_shared_visibility_${rand.ulid()}')
62 os.mkdir_all(workdir) or { panic(err) }
63 defer {
64 os.rmdir_all(workdir) or {}
65 }
66 lib_src := os.join_path(workdir, 'lib.v')
67 lib_out := os.join_path(workdir, 'liblib')
68 lib_so := if uos == 'macos' { '${lib_out}.dylib' } else { '${lib_out}.so' }
69 os.write_file(lib_src, [
70 'module main',
71 '',
72 "@[export: 'my_add']",
73 'fn my_add(a int, b int) int {',
74 '\treturn a + b',
75 '}',
76 ].join('\n')) or { panic(err) }
77 run_cmd('${os.quoted_path(vexe)} ${cc_flag} -shared -o ${os.quoted_path(lib_out)} ${os.quoted_path(lib_src)}') or {
78 panic(err)
79 }
80 assert os.exists(lib_so)
81 nm_flag := if uos == 'macos' { '-gU' } else { '-D --defined-only' }
82 res := run_cmd('nm ${nm_flag} ${os.quoted_path(lib_so)}') or { panic(err) }
83 symbol_token := if uos == 'macos' { ' _my_add' } else { ' my_add' }
84 assert res.output.contains(symbol_token), 'expected exported `my_add` in:\n${res.output}'
85 // None of the V runtime/stdlib helpers should leak into the exported ABI.
86 leaky_prefixes := ['Array_string_', 'main__', 'builtin__', 'GC_', '_vinit', '_vcleanup',
87 '_const_']
88 for line in res.output.split('\n') {
89 for prefix in leaky_prefixes {
90 needles := if uos == 'macos' { [' _${prefix}', ' T _${prefix}', ' D _${prefix}'] } else { [
91 ' ${prefix}',
92 ' T ${prefix}',
93 ' D ${prefix}',
94 ] }
95 for needle in needles {
96 assert !line.contains(needle), 'unexpected exported symbol on line: ${line}'
97 }
98 }
99 }
100}
101
102fn run_cmd(cmd string) !os.Result {
103 res := os.execute(cmd)
104 if res.exit_code != 0 {
105 return error('command failed:\n${cmd}\n${res.output}')
106 }
107 return res
108}
109