Skip to content

Commit accc667

Browse files
committed
ci: fix remaining V3 platform failures
1 parent 587731b commit accc667

7 files changed

Lines changed: 333 additions & 32 deletions

File tree

cmd/v/v.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ fn launch_v3_ownership_compiler(is_verbose bool, args []string) {
341341
exit(1)
342342
}
343343
if util.should_recompile_tool(vexe, v3_src_dir, tool_name, v3_exe) {
344-
compilation_command := '${os.quoted_path(vexe)} -nocache -gc none -d ownership -o ${os.quoted_path(v3_exe)} ${os.quoted_path(v3_main_source)}'
344+
compilation_command := '${os.quoted_path(vexe)} -no-parallel -nocache -gc none -d ownership -o ${os.quoted_path(v3_exe)} ${os.quoted_path(v3_main_source)}'
345345
if is_verbose {
346346
println('Compiling ${tool_name} with: "${compilation_command}"')
347347
}

vlib/v3/driver/c_compiler_flags_test.v

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,17 @@ fn test_add_v3_tcc_compat_defines() {
3333
add_v3_tcc_compat_defines(mut other_compiler, 'macos', 'arm64', false, false)
3434
assert other_compiler.len == 0
3535
}
36+
37+
fn test_v3_default_linker_flags() {
38+
assert v3_default_linker_flags('windows', false) == ['-lm']
39+
assert v3_default_linker_flags('linux', false) == ['-lm', '-lpthread']
40+
assert v3_default_linker_flags('freebsd', false) == ['-lm', '-lpthread', '-lexecinfo', '-lelf']
41+
assert v3_default_linker_flags('netbsd', false) == ['-lm', '-lpthread', '-lexecinfo', '-lelf']
42+
assert v3_default_linker_flags('linux', true) == []
43+
}
44+
45+
fn test_v3_default_linker_flags_do_not_duplicate_existing_flags() {
46+
mut flags := ['-lpthread', '-lm']
47+
add_v3_default_linker_flags(mut flags, 'linux', false)
48+
assert flags == ['-lpthread', '-lm']
49+
}

vlib/v3/driver/driver.v

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,7 +1685,7 @@ fn input_is_legacy_diagnostic_fixture(input_file string) bool {
16851685
fn default_bin_file_for_input(input_file string) string {
16861686
if os.is_dir(input_file) {
16871687
real_input := os.real_path(input_file)
1688-
return os.base(real_input)
1688+
return os.join_path_single(real_input, os.file_name(real_input))
16891689
}
16901690
resolved_input := if os.exists(input_file) { os.real_path(input_file) } else { input_file }
16911691
if !resolved_input.ends_with('.v') && !resolved_input.ends_with('.vv')
@@ -1809,6 +1809,28 @@ fn add_v3_tcc_compat_defines(mut user_defines []string, target_os string, target
18091809
}
18101810
}
18111811

1812+
fn v3_default_linker_flags(target_os string, is_o bool) []string {
1813+
if is_o {
1814+
return []
1815+
}
1816+
mut flags := ['-lm']
1817+
if target_os in ['linux', 'freebsd', 'openbsd', 'netbsd', 'dragonfly', 'solaris', 'haiku'] {
1818+
flags << '-lpthread'
1819+
}
1820+
if target_os in ['freebsd', 'netbsd'] {
1821+
flags << ['-lexecinfo', '-lelf']
1822+
}
1823+
return flags
1824+
}
1825+
1826+
fn add_v3_default_linker_flags(mut flags []string, target_os string, is_o bool) {
1827+
for flag in v3_default_linker_flags(target_os, is_o) {
1828+
if flag !in flags {
1829+
flags << flag
1830+
}
1831+
}
1832+
}
1833+
18121834
fn v3_c_compiler_flag_plan(options V3CCompilerFlagOptions) V3CCompilerFlagPlan {
18131835
mut before_inputs := options.environment_c_flags.clone()
18141836
before_inputs << options.target_args
@@ -1850,7 +1872,7 @@ fn v3_c_compiler_flag_plan(options V3CCompilerFlagOptions) V3CCompilerFlagPlan {
18501872
before_inputs << ['-flat_namespace', '-undefined', 'dynamic_lookup']
18511873
}
18521874
mut after_inputs := options.dependencies.clone()
1853-
after_inputs << '-lm'
1875+
add_v3_default_linker_flags(mut after_inputs, options.target_os, options.is_o)
18541876
if !options.is_o {
18551877
after_inputs << options.environment_ld_flags
18561878
}
@@ -7321,6 +7343,19 @@ pub fn run(args []string) {
73217343
target.default_thread_stack_size()
73227344
}
73237345
prefs.backend = backend
7346+
prefs.vroot = if pref.has_macos_v3_caller_environment() && prefs.vexe.len > 0 {
7347+
// The macOS dispatcher sets VEXE to the invoking compiler. Preserve that
7348+
// checkout instead of selecting another V checkout around the input.
7349+
os.real_path(os.dir(prefs.vexe))
7350+
} else {
7351+
resolve_vroot_for_input(prefs.vroot, input_file)
7352+
}
7353+
if !c_compiler_explicit && os.user_os() == 'windows' && target.os == 'windows' {
7354+
bundled_tcc := os.join_path(prefs.vroot, 'thirdparty', 'tcc', 'tcc.exe')
7355+
if os.is_executable(bundled_tcc) {
7356+
c_compiler = bundled_tcc
7357+
}
7358+
}
73247359
effective_c_compiler := if backend == 'arm64' {
73257360
'tinyc'
73267361
} else {
@@ -7333,13 +7368,6 @@ pub fn run(args []string) {
73337368
prefs.force_bounds_checking = force_bounds_checking
73347369
prefs.user_defines = user_defines
73357370
prefs.compile_values = compile_values.clone()
7336-
prefs.vroot = if pref.has_macos_v3_caller_environment() && prefs.vexe.len > 0 {
7337-
// The macOS dispatcher sets VEXE to the invoking compiler. Preserve that
7338-
// checkout instead of selecting another V checkout around the input.
7339-
os.real_path(os.dir(prefs.vexe))
7340-
} else {
7341-
resolve_vroot_for_input(prefs.vroot, input_file)
7342-
}
73437371
prefs.module_search_paths = expand_v3_module_search_paths(module_search_path_spec, prefs.vroot)
73447372
if explicit_tcc && c_compiler in ['tcc', 'tinyc'] {
73457373
bundled_tcc := os.join_path(prefs.vroot, 'thirdparty', 'tcc', 'tcc.exe')
@@ -7378,7 +7406,7 @@ pub fn run(args []string) {
73787406
// builtin source set, which likewise must remain a monolithic translation unit.
73797407
cache_enabled := backend == 'c' && !c_only && !no_cache && !no_skip_unused && !no_builtin
73807408
&& !keep_c && !backend_explicit && !c_compiler_explicit && !minimal_literal_output
7381-
&& target.os == host_target.os && target.arch == host_target.arch
7409+
&& c_compiler == 'cc' && target.os == host_target.os && target.arch == host_target.arch
73827410
&& !input_owns_builtin_bundle_module(input_file, prefs.vroot)
73837411
cc_identity := if cache_enabled { default_cc_identity() } else { '' }
73847412
compiler_signature := if cache_enabled { v3_cache_compiler_signature(prefs.vroot) } else { '' }
@@ -9776,9 +9804,7 @@ pub fn run(args []string) {
97769804
tcc_args << tcc_native_c_source_flags(resolved_c_flags)
97779805
tcc_args << cached_dev_dylib
97789806
tcc_args << tcc_dynamic_link_flags(resolved_c_flags)
9779-
if '-lm' !in tcc_args {
9780-
tcc_args << '-lm'
9781-
}
9807+
add_v3_default_linker_flags(mut tcc_args, prefs.normalized_target_os(), is_o)
97829808
program_source_identity := '${prefix_source_identity}\n${modulecache.file_signature(tcc_main_file)}\n${if cached_program_body_source.len > 0 {
97839809
modulecache.file_signature(cached_program_body_source)
97849810
} else {
@@ -9862,7 +9888,7 @@ pub fn run(args []string) {
98629888
tcc_args << atomic_s
98639889
}
98649890
tcc_args << resolved_c_flags
9865-
tcc_args << '-lm'
9891+
add_v3_default_linker_flags(mut tcc_args, prefs.normalized_target_os(), is_o)
98669892
if !is_o {
98679893
tcc_args << environment_ld_flags
98689894
}

vlib/v3/driver/implicit_import_test.v

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@ fn test_default_bin_file_strips_backend_source_extension() {
1313
assert default_bin_file_for_input('foo.vv') == 'foo'
1414
}
1515

16+
fn test_default_bin_file_for_directory_is_source_adjacent() {
17+
root := os.join_path(os.temp_dir(), 'v3_default_bin_directory_${os.getpid()}')
18+
source_dir := os.join_path(root, 'app')
19+
os.rmdir_all(root) or {}
20+
os.mkdir_all(source_dir)!
21+
defer {
22+
os.rmdir_all(root) or {}
23+
}
24+
real_source_dir := os.real_path(source_dir)
25+
assert default_bin_file_for_input(source_dir) == os.join_path_single(real_source_dir, 'app')
26+
}
27+
1628
fn test_profile_optional_arg_recognizes_vv_source() {
1729
value, consumed := v3_profile_optional_arg_value(['-profile', 'fixture.vv', '-o', 'out'], 0,
1830
false)

vlib/v3/gen/c/fn.v

Lines changed: 144 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8110,6 +8110,24 @@ fn (mut g FlatGen) preintern_json_encode_value_strings(typ types.Type, seen []st
81108110
g.preintern_json_encode_value_strings(clean.value_type, seen)
81118111
return
81128112
}
8113+
if clean is types.SumType {
8114+
sum_name := g.resolve_sum_name(clean.name)
8115+
if sum_name in seen {
8116+
return
8117+
}
8118+
mut next_seen := seen.clone()
8119+
next_seen << sum_name
8120+
g.intern_string('null')
8121+
for variant in g.tc.sum_types[sum_name] or { []string{} } {
8122+
variant_type := g.json_sum_variant_type(variant)
8123+
if variant_type is types.Pointer {
8124+
g.preintern_json_encode_value_strings(variant_type.base_type, next_seen)
8125+
} else {
8126+
g.preintern_json_encode_value_strings(variant_type, next_seen)
8127+
}
8128+
}
8129+
return
8130+
}
81138131
if clean is types.Primitive {
81148132
if clean.props.has(.boolean) {
81158133
g.intern_string('true')
@@ -8247,6 +8265,26 @@ fn (mut g FlatGen) json_encode_value_c_expr(typ types.Type, expr string) ?string
82478265
}
82488266
return none
82498267
}
8268+
if clean is types.SumType {
8269+
sum_name := g.resolve_sum_name(clean.name)
8270+
variants := g.tc.sum_types[sum_name] or { return none }
8271+
null_sid := g.intern_string('null')
8272+
mut result := '_str_${null_sid}'
8273+
for i := variants.len - 1; i >= 0; i-- {
8274+
variant := variants[i]
8275+
variant_type := g.json_sum_variant_type(variant)
8276+
if variant_type is types.Pointer {
8277+
return none
8278+
}
8279+
field := g.sum_field_name(variant)
8280+
encoded := g.json_encode_value_c_expr(variant_type, '(*(${expr}).${field})') or {
8281+
return none
8282+
}
8283+
index := g.sum_type_index(sum_name, variant)
8284+
result = '((${expr}).typ == ${index} ? ${encoded} : ${result})'
8285+
}
8286+
return result
8287+
}
82508288
if clean is types.Struct {
82518289
fields := g.json_encode_struct_field_exprs(clean.name, expr, []string{}) or { return none }
82528290
open := g.intern_string('{')
@@ -8391,7 +8429,8 @@ fn (g &FlatGen) json_struct_field_is_embedded(field types.StructField, type_name
83918429

83928430
fn (g &FlatGen) json_encode_omitempty_supported(typ types.Type) bool {
83938431
clean := if typ is types.Alias { typ.base_type } else { typ }
8394-
if clean is types.String || clean is types.Enum {
8432+
if clean is types.String || clean is types.Enum || clean is types.Array || clean is types.Map
8433+
|| clean is types.Struct || clean is types.SumType {
83958434
return true
83968435
}
83978436
if clean is types.Primitive {
@@ -8401,25 +8440,119 @@ fn (g &FlatGen) json_encode_omitempty_supported(typ types.Type) bool {
84018440
}
84028441

84038442
fn (mut g FlatGen) json_encode_omitempty_expr(typ types.Type, expr string) ?string {
8443+
clean := if typ is types.Alias { typ.base_type } else { typ }
8444+
ct := g.value_c_type(typ)
8445+
value_name := g.tmp_name()
8446+
default_name := g.tmp_name()
8447+
equal := g.json_encode_equal_c_expr(clean, value_name, default_name, []string{}) or {
8448+
return none
8449+
}
8450+
default_value := g.default_value_to_string(typ)
8451+
return '({ ${ct} ${value_name} = ${expr}; ${ct} ${default_name} = ${default_value}; ${equal}; })'
8452+
}
8453+
8454+
fn (mut g FlatGen) json_encode_equal_c_expr(typ types.Type, left string, right string, seen []string) ?string {
84048455
clean := if typ is types.Alias { typ.base_type } else { typ }
84058456
if clean is types.String {
8406-
return '((${expr}).len == 0)'
8457+
return '((${left}).len == (${right}).len && ((${left}).len == 0 || memcmp((${left}).str, (${right}).str, (${left}).len) == 0))'
84078458
}
84088459
if clean is types.Enum {
8409-
default_value := g.enum_default_value_expr_for_type(clean.name) or { '0' }
8410-
return '((${expr}) == ${default_value})'
8460+
return '((${left}) == (${right}))'
84118461
}
84128462
if clean is types.Primitive {
8413-
if clean.props.has(.boolean) {
8414-
return '(!((bool)(${expr})))'
8463+
if clean.props.has(.boolean) || clean.props.has(.integer) || clean.props.has(.float) {
8464+
return '((${left}) == (${right}))'
8465+
}
8466+
return none
8467+
}
8468+
if clean is types.Array {
8469+
elem_ct := g.value_c_type(clean.elem_type)
8470+
left_name := g.tmp_name()
8471+
right_name := g.tmp_name()
8472+
equal_name := g.tmp_name()
8473+
index_name := g.tmp_name()
8474+
left_elem := g.tmp_name()
8475+
right_elem := g.tmp_name()
8476+
elem_equal := g.json_encode_equal_c_expr(clean.elem_type, '(*${left_elem})',
8477+
'(*${right_elem})', seen) or { return none }
8478+
return '({ Array ${left_name} = ${left}; Array ${right_name} = ${right}; bool ${equal_name} = ${left_name}.len == ${right_name}.len; for (int ${index_name} = 0; ${equal_name} && ${index_name} < ${left_name}.len; ++${index_name}) { ${elem_ct}* ${left_elem} = (${elem_ct}*)array_get(${left_name}, ${index_name}); ${elem_ct}* ${right_elem} = (${elem_ct}*)array_get(${right_name}, ${index_name}); if (!(${elem_equal})) ${equal_name} = false; } ${equal_name}; })'
8479+
}
8480+
if clean is types.Map {
8481+
key_clean := if clean.key_type is types.Alias {
8482+
clean.key_type.base_type
8483+
} else {
8484+
clean.key_type
84158485
}
8416-
if clean.props.has(.integer) || clean.props.has(.float) {
8417-
return '((${expr}) == 0)'
8486+
if key_clean !is types.String {
8487+
return none
84188488
}
8489+
value_ct := g.value_c_type(clean.value_type)
8490+
left_name := g.tmp_name()
8491+
right_name := g.tmp_name()
8492+
equal_name := g.tmp_name()
8493+
index_name := g.tmp_name()
8494+
key_name := g.tmp_name()
8495+
left_value := g.tmp_name()
8496+
right_value := g.tmp_name()
8497+
value_equal := g.json_encode_equal_c_expr(clean.value_type, '(*${left_value})',
8498+
'(*${right_value})', seen) or { return none }
8499+
return '({ map ${left_name} = ${left}; map ${right_name} = ${right}; bool ${equal_name} = ${left_name}.len == ${right_name}.len; for (int ${index_name} = 0; ${equal_name} && ${index_name} < ${left_name}.key_values.len; ++${index_name}) { if (${left_name}.key_values.deletes != 0 && ${left_name}.key_values.all_deleted != 0 && ${left_name}.key_values.all_deleted[${index_name}] != 0) continue; string* ${key_name} = (string*)(${left_name}.key_values.keys + ${index_name} * ${left_name}.key_values.key_bytes); ${value_ct}* ${left_value} = (${value_ct}*)(${left_name}.key_values.values + ${index_name} * ${left_name}.key_values.value_bytes); if (!map__exists(&${right_name}, ${key_name})) { ${equal_name} = false; break; } ${value_ct}* ${right_value} = (${value_ct}*)map__get(&${right_name}, ${key_name}, ${left_value}); if (!(${value_equal})) ${equal_name} = false; } ${equal_name}; })'
8500+
}
8501+
if clean is types.Struct {
8502+
if clean.name in seen {
8503+
return none
8504+
}
8505+
mut next_seen := seen.clone()
8506+
next_seen << clean.name
8507+
fields := g.tc.structs[clean.name] or { return none }
8508+
mut conditions := []string{cap: fields.len}
8509+
for field in fields {
8510+
field_name := g.cname(field.name)
8511+
condition := g.json_encode_equal_c_expr(field.typ, '(${left}).${field_name}',
8512+
'(${right}).${field_name}', next_seen) or { return none }
8513+
conditions << condition
8514+
}
8515+
return if conditions.len == 0 { 'true' } else { conditions.join(' && ') }
8516+
}
8517+
if clean is types.SumType {
8518+
sum_name := g.resolve_sum_name(clean.name)
8519+
if sum_name in seen {
8520+
return none
8521+
}
8522+
variants := g.tc.sum_types[sum_name] or { return none }
8523+
mut next_seen := seen.clone()
8524+
next_seen << sum_name
8525+
mut active_equal := 'true'
8526+
for i := variants.len - 1; i >= 0; i-- {
8527+
variant := variants[i]
8528+
variant_type := g.json_sum_variant_type(variant)
8529+
if variant_type is types.Pointer {
8530+
return none
8531+
}
8532+
field := g.sum_field_name(variant)
8533+
variant_equal := g.json_encode_equal_c_expr(variant_type, '(*(${left}).${field})',
8534+
'(*(${right}).${field})', next_seen) or { return none }
8535+
index := g.sum_type_index(sum_name, variant)
8536+
active_equal = '((${left}).typ == ${index} ? ${variant_equal} : ${active_equal})'
8537+
}
8538+
return '((${left}).typ == (${right}).typ && ${active_equal})'
84198539
}
84208540
return none
84218541
}
84228542

8543+
fn (g &FlatGen) json_sum_variant_type(raw_type string) types.Type {
8544+
clean := raw_type.trim_space()
8545+
if types.is_builtin_type_name(clean) {
8546+
return types.builtin_type_value(clean)
8547+
}
8548+
if clean.starts_with('[]') {
8549+
return types.Type(types.Array{
8550+
elem_type: g.json_sum_variant_type(clean[2..])
8551+
})
8552+
}
8553+
return select_receive_unalias_type(g.tc.parse_canonical_type(clean))
8554+
}
8555+
84238556
fn (mut g FlatGen) gen_json_decode_call(node flat.Node) bool {
84248557
if node.children_count < 3 {
84258558
return false
@@ -8860,7 +8993,9 @@ fn (g &FlatGen) json_struct_has_decode_field_attrs(struct_name string) bool {
88608993
}
88618994

88628995
fn (g &FlatGen) json_struct_has_encode_field_attrs(struct_name string) bool {
8863-
return g.json_struct_has_disallowed_field_attrs(struct_name, ['skip', 'json', 'omitempty'])
8996+
// `required` only constrains decoding. It does not change the encoded value.
8997+
return g.json_struct_has_disallowed_field_attrs(struct_name, ['skip', 'json', 'omitempty',
8998+
'required'])
88648999
}
88659000

88669001
fn (g &FlatGen) json_struct_has_disallowed_field_attrs(struct_name string, allowed []string) bool {

0 commit comments

Comments
 (0)