| 1 | module builder |
| 2 | |
| 3 | import os |
| 4 | import v2.abi |
| 5 | import v2.ast |
| 6 | import v2.gen.x64 |
| 7 | import v2.pref |
| 8 | |
| 9 | fn test_native_x64_windows_selects_coff_and_windows_abi() { |
| 10 | assert is_windows_x64_native_target(.x64, 'windows') |
| 11 | assert native_x64_object_format_for_os('windows') == x64.ObjectFormat.coff |
| 12 | assert native_x64_codegen_abi_for_os('windows') == x64.X64Abi.windows |
| 13 | assert native_x64_lowering_abi_for_os('windows') == abi.X64Abi.windows |
| 14 | } |
| 15 | |
| 16 | fn test_native_x64_non_windows_keeps_existing_object_formats_and_sysv() { |
| 17 | assert !is_windows_x64_native_target(.x64, 'linux') |
| 18 | assert native_x64_object_format_for_os('linux') == x64.ObjectFormat.elf |
| 19 | assert native_x64_codegen_abi_for_os('linux') == x64.X64Abi.sysv |
| 20 | assert native_x64_lowering_abi_for_os('linux') == abi.X64Abi.sysv |
| 21 | |
| 22 | assert !is_windows_x64_native_target(.x64, 'macos') |
| 23 | assert native_x64_object_format_for_os('macos') == x64.ObjectFormat.macho |
| 24 | assert native_x64_codegen_abi_for_os('macos') == x64.X64Abi.sysv |
| 25 | assert native_x64_lowering_abi_for_os('macos') == abi.X64Abi.sysv |
| 26 | assert is_macos_native_target('macos') |
| 27 | assert is_macos_native_target('darwin') |
| 28 | assert native_x64_object_format_for_os('darwin') == x64.ObjectFormat.macho |
| 29 | assert native_x64_codegen_abi_for_os('darwin') == x64.X64Abi.sysv |
| 30 | assert native_x64_lowering_abi_for_os('darwin') == abi.X64Abi.sysv |
| 31 | } |
| 32 | |
| 33 | fn test_minimal_x64_runtime_and_macos_tiny_object_targets() { |
| 34 | old_linux_tiny := os.getenv_opt('V2_X64_LINUX_TINY') |
| 35 | os.unsetenv('V2_X64_LINUX_TINY') |
| 36 | defer { |
| 37 | if old := old_linux_tiny { |
| 38 | os.setenv('V2_X64_LINUX_TINY', old, true) |
| 39 | } else { |
| 40 | os.unsetenv('V2_X64_LINUX_TINY') |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | mut prefs := pref.new_preferences() |
| 45 | prefs.backend = .x64 |
| 46 | prefs.arch = .x64 |
| 47 | |
| 48 | prefs.target_os = 'linux' |
| 49 | linux_builder := new_builder(&prefs) |
| 50 | assert !linux_builder.uses_minimal_windows_x64_runtime() |
| 51 | assert linux_builder.uses_minimal_linux_x64_runtime() |
| 52 | assert linux_builder.uses_minimal_x64_runtime() |
| 53 | assert !linux_builder.uses_minimal_linux_x64_runtime_roots() |
| 54 | assert !linux_builder.uses_minimal_x64_runtime_roots() |
| 55 | |
| 56 | os.setenv('V2_X64_LINUX_TINY', '1', true) |
| 57 | linux_tiny_builder := new_builder(&prefs) |
| 58 | assert linux_tiny_builder.uses_minimal_linux_x64_runtime() |
| 59 | assert linux_tiny_builder.uses_minimal_linux_x64_runtime_roots() |
| 60 | assert linux_tiny_builder.uses_minimal_x64_runtime_roots() |
| 61 | os.unsetenv('V2_X64_LINUX_TINY') |
| 62 | |
| 63 | prefs.target_os = 'macos' |
| 64 | macos_builder := new_builder(&prefs) |
| 65 | assert !macos_builder.uses_minimal_windows_x64_runtime() |
| 66 | assert !macos_builder.uses_minimal_linux_x64_runtime() |
| 67 | assert !macos_builder.uses_minimal_x64_runtime() |
| 68 | assert !macos_builder.uses_minimal_x64_runtime_roots() |
| 69 | assert macos_builder.uses_macos_x64_tiny_object(.x64) |
| 70 | assert !macos_builder.uses_macos_x64_tiny_object(.arm64) |
| 71 | |
| 72 | macos_tiny_builder := new_builder(&prefs) |
| 73 | assert !macos_tiny_builder.uses_minimal_x64_runtime() |
| 74 | assert !macos_tiny_builder.uses_minimal_x64_runtime_roots() |
| 75 | assert macos_tiny_builder.uses_macos_x64_tiny_object(.x64) |
| 76 | assert !macos_tiny_builder.uses_macos_x64_tiny_object(.arm64) |
| 77 | |
| 78 | prefs.macos_tiny = false |
| 79 | macos_no_tiny_builder := new_builder(&prefs) |
| 80 | assert !macos_no_tiny_builder.uses_macos_x64_tiny_object(.x64) |
| 81 | |
| 82 | prefs.macos_tiny = true |
| 83 | prefs.arch = .auto |
| 84 | macos_auto_arch_builder := new_builder(&prefs) |
| 85 | assert !macos_auto_arch_builder.uses_minimal_x64_runtime_roots() |
| 86 | assert macos_auto_arch_builder.uses_macos_x64_tiny_object(.x64) |
| 87 | assert !macos_auto_arch_builder.uses_macos_x64_tiny_object(.arm64) |
| 88 | prefs.arch = .x64 |
| 89 | |
| 90 | prefs.target_os = 'darwin' |
| 91 | darwin_builder := new_builder(&prefs) |
| 92 | assert !darwin_builder.uses_minimal_windows_x64_runtime() |
| 93 | assert !darwin_builder.uses_minimal_linux_x64_runtime() |
| 94 | assert !darwin_builder.uses_minimal_x64_runtime() |
| 95 | assert !darwin_builder.uses_minimal_x64_runtime_roots() |
| 96 | assert darwin_builder.uses_macos_x64_tiny_object(.x64) |
| 97 | |
| 98 | prefs.target_os = 'windows' |
| 99 | windows_builder := new_builder(&prefs) |
| 100 | assert windows_builder.uses_minimal_windows_x64_runtime() |
| 101 | assert !windows_builder.uses_minimal_linux_x64_runtime() |
| 102 | assert windows_builder.uses_minimal_x64_runtime() |
| 103 | assert windows_builder.uses_minimal_x64_runtime_roots() |
| 104 | assert !windows_builder.uses_macos_x64_tiny_object(.x64) |
| 105 | } |
| 106 | |
| 107 | fn test_macos_tiny_candidate_source_snapshot_is_auto_by_default_with_opt_out() { |
| 108 | mut prefs := pref.new_preferences() |
| 109 | prefs.backend = .x64 |
| 110 | prefs.arch = .auto |
| 111 | prefs.target_os = 'macos' |
| 112 | |
| 113 | mut hosted_builder := new_builder(&prefs) |
| 114 | hosted_builder.files = [ |
| 115 | ast.File{ |
| 116 | mod: 'main' |
| 117 | name: 'main.v' |
| 118 | }, |
| 119 | ] |
| 120 | hosted_builder.prepare_macos_tiny_candidate_source_files() |
| 121 | assert hosted_builder.macos_tiny_candidate_source_files.len == 1 |
| 122 | assert hosted_builder.macos_tiny_candidate_source_files[0].name == 'main.v' |
| 123 | |
| 124 | prefs.macos_tiny = false |
| 125 | mut opt_out_builder := new_builder(&prefs) |
| 126 | opt_out_builder.files = [ |
| 127 | ast.File{ |
| 128 | mod: 'main' |
| 129 | name: 'main.v' |
| 130 | }, |
| 131 | ] |
| 132 | opt_out_builder.prepare_macos_tiny_candidate_source_files() |
| 133 | assert opt_out_builder.macos_tiny_candidate_source_files.len == 0 |
| 134 | assert !opt_out_builder.uses_minimal_x64_runtime_roots() |
| 135 | } |
| 136 | |
| 137 | fn test_macos_tiny_candidate_source_snapshot_uses_flat_when_enabled() { |
| 138 | mut prefs := pref.new_preferences() |
| 139 | prefs.backend = .x64 |
| 140 | prefs.arch = .auto |
| 141 | prefs.target_os = 'macos' |
| 142 | |
| 143 | source_files := [ |
| 144 | ast.File{ |
| 145 | mod: 'main' |
| 146 | name: 'flat_main.v' |
| 147 | stmts: [ |
| 148 | ast.Stmt(ast.FnDecl{ |
| 149 | name: 'main' |
| 150 | }), |
| 151 | ] |
| 152 | }, |
| 153 | ] |
| 154 | mut tiny_builder := new_builder(&prefs) |
| 155 | tiny_builder.files = [ |
| 156 | ast.File{ |
| 157 | mod: 'stale' |
| 158 | name: 'stale.v' |
| 159 | }, |
| 160 | ] |
| 161 | tiny_builder.flat = ast.flatten_files(source_files) |
| 162 | tiny_builder.prepare_macos_tiny_candidate_source_files() |
| 163 | assert tiny_builder.macos_tiny_candidate_source_files.len == 0 |
| 164 | assert tiny_builder.macos_tiny_candidate_source_flat.files.len == source_files.len |
| 165 | assert tiny_builder.macos_tiny_candidate_source_flat.file_name(tiny_builder.macos_tiny_candidate_source_flat.files[0]) == 'flat_main.v' |
| 166 | assert tiny_builder.macos_tiny_candidate_source_flat.string_at(tiny_builder.macos_tiny_candidate_source_flat.files[0].mod_idx) == 'main' |
| 167 | } |
| 168 | |
| 169 | fn test_macos_tiny_candidate_native_mir_build_policy_is_sequential_only_for_candidate() { |
| 170 | mut prefs := pref.new_preferences() |
| 171 | prefs.backend = .x64 |
| 172 | prefs.arch = .x64 |
| 173 | prefs.target_os = 'macos' |
| 174 | prefs.no_parallel = false |
| 175 | prefs.hot_fn = '' |
| 176 | builder := new_builder(&prefs) |
| 177 | |
| 178 | assert !builder.native_mir_build_sequential('') |
| 179 | assert builder.native_mir_build_sequential(macos_tiny_candidate_graph_label) |
| 180 | |
| 181 | prefs.no_parallel = true |
| 182 | no_parallel_builder := new_builder(&prefs) |
| 183 | assert no_parallel_builder.native_mir_build_sequential('') |
| 184 | } |
| 185 | |
| 186 | fn test_macos_tiny_link_command_adds_hygiene_only_for_tiny_object() { |
| 187 | normal := macos_native_link_command('/tmp/out', 'main.o', '/SDK Path', 'x86_64', false, '') |
| 188 | tiny := macos_native_link_command('/tmp/out', 'main.o', '/SDK Path', 'x86_64', true, '') |
| 189 | |
| 190 | assert normal == 'ld -o ${os.quoted_path('/tmp/out')} ${os.quoted_path('main.o')} -lSystem -syslibroot ${os.quoted_path('/SDK Path')} -e _main -arch x86_64 -platform_version macos 11.0.0 11.0.0' |
| 191 | assert tiny == '${normal} -dead_strip -x -S' |
| 192 | assert !normal.contains('-dead_strip') |
| 193 | assert !normal.contains(' -x') |
| 194 | assert !normal.contains(' -S') |
| 195 | } |
| 196 | |
| 197 | fn test_macos_sdk_path_from_xcrun_output_accepts_simple_sdk_path() { |
| 198 | output := '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk\n' |
| 199 | assert macos_sdk_path_from_xcrun_output(output)! == '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk' |
| 200 | } |
| 201 | |
| 202 | fn test_macos_sdk_path_from_xcrun_output_ignores_diagnostics_before_sdk_path() { |
| 203 | output := |
| 204 | '2026-06-07 16:19:11.428 xcodebuild[1002:7567] Requested but did not find extension point\n' + |
| 205 | '2026-06-07 16:19:14.880 xcodebuild[1004:7681] Requested but did not find extension point\n' + |
| 206 | '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk\n' |
| 207 | assert macos_sdk_path_from_xcrun_output(output)! == '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk' |
| 208 | } |
| 209 | |
| 210 | fn test_macos_sdk_path_from_xcrun_output_handles_empty_lines_spaces_and_crlf() { |
| 211 | output := ' \r\n\t\r\n /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk \r\n' |
| 212 | assert macos_sdk_path_from_xcrun_output(output)! == '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk' |
| 213 | } |
| 214 | |
| 215 | fn test_macos_sdk_path_from_xcrun_output_rejects_missing_sdk_path() { |
| 216 | path := macos_sdk_path_from_xcrun_output('xcodebuild diagnostic only\nnot a path\n') or { |
| 217 | assert err.msg().contains('could not find a clean macOS SDK path') |
| 218 | return |
| 219 | } |
| 220 | assert false, 'expected missing SDK path error, got ${path}' |
| 221 | } |
| 222 | |
| 223 | fn test_macos_sdk_path_from_xcrun_output_preserves_spaces_inside_path() { |
| 224 | output := '/Applications/Xcode With Spaces.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk\n' |
| 225 | assert macos_sdk_path_from_xcrun_output(output)! == '/Applications/Xcode With Spaces.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk' |
| 226 | } |
| 227 | |
| 228 | fn test_macos_sdk_path_from_xcrun_output_rejects_relative_or_non_macos_sdk_lines() { |
| 229 | output := 'relative/MacOSX12.3.sdk\n/Applications/Xcode.app/Contents/Developer/SDKs/iPhoneOS.sdk\n/Applications/Xcode.app/Contents/Developer/SDKs/NotMacOSX.sdk\n' |
| 230 | path := macos_sdk_path_from_xcrun_output(output) or { |
| 231 | assert err.msg().contains('could not find a clean macOS SDK path') |
| 232 | return |
| 233 | } |
| 234 | assert false, 'expected invalid SDK path lines to be rejected, got ${path}' |
| 235 | } |
| 236 | |
| 237 | fn test_macos_sdk_path_from_xcrun_output_uses_last_valid_sdk_line() { |
| 238 | output := '/Applications/Old.app/Contents/Developer/SDKs/MacOSX11.0.sdk\n' + |
| 239 | '/Applications/New.app/Contents/Developer/SDKs/MacOSX12.3.sdk\n' |
| 240 | assert macos_sdk_path_from_xcrun_output(output)! == '/Applications/New.app/Contents/Developer/SDKs/MacOSX12.3.sdk' |
| 241 | } |
| 242 | |
| 243 | fn test_validate_macos_sdk_path_for_native_link_accepts_libsystem_tbd() { |
| 244 | tmp_dir := os.join_path(os.vtmp_dir(), 'v2_macos_sdk_tbd_${os.getpid()}') |
| 245 | sdk_path := os.join_path(tmp_dir, 'MacOSX.test.sdk') |
| 246 | lib_dir := os.join_path(sdk_path, 'usr', 'lib') |
| 247 | os.mkdir_all(lib_dir) or { panic(err) } |
| 248 | defer { |
| 249 | os.rmdir_all(tmp_dir) or {} |
| 250 | } |
| 251 | os.write_file(os.join_path(lib_dir, 'libSystem.tbd'), '') or { panic(err) } |
| 252 | validate_macos_sdk_path_for_native_link(sdk_path)! |
| 253 | } |
| 254 | |
| 255 | fn test_validate_macos_sdk_path_for_native_link_accepts_libsystem_dylib() { |
| 256 | tmp_dir := os.join_path(os.vtmp_dir(), 'v2_macos_sdk_dylib_${os.getpid()}') |
| 257 | sdk_path := os.join_path(tmp_dir, 'MacOSX.test.sdk') |
| 258 | lib_dir := os.join_path(sdk_path, 'usr', 'lib') |
| 259 | os.mkdir_all(lib_dir) or { panic(err) } |
| 260 | defer { |
| 261 | os.rmdir_all(tmp_dir) or {} |
| 262 | } |
| 263 | os.write_file(os.join_path(lib_dir, 'libSystem.dylib'), '') or { panic(err) } |
| 264 | validate_macos_sdk_path_for_native_link(sdk_path)! |
| 265 | } |
| 266 | |
| 267 | fn test_validate_macos_sdk_path_for_native_link_rejects_sdk_without_libsystem() { |
| 268 | tmp_dir := os.join_path(os.vtmp_dir(), 'v2_macos_sdk_missing_libsystem_${os.getpid()}') |
| 269 | sdk_path := os.join_path(tmp_dir, 'MacOSX.test.sdk') |
| 270 | os.mkdir_all(os.join_path(sdk_path, 'usr', 'lib')) or { panic(err) } |
| 271 | defer { |
| 272 | os.rmdir_all(tmp_dir) or {} |
| 273 | } |
| 274 | validate_macos_sdk_path_for_native_link(sdk_path) or { |
| 275 | assert err.msg().contains('missing usr/lib/libSystem') |
| 276 | return |
| 277 | } |
| 278 | assert false, 'expected SDK without libSystem to be rejected' |
| 279 | } |
| 280 | |
| 281 | fn test_validate_macos_sdk_path_for_native_link_rejects_missing_sdk_dir() { |
| 282 | missing_sdk := os.join_path(os.vtmp_dir(), 'v2_missing_macos_sdk_${os.getpid()}', |
| 283 | 'MacOSX.test.sdk') |
| 284 | validate_macos_sdk_path_for_native_link(missing_sdk) or { |
| 285 | assert err.msg().contains('does not exist') |
| 286 | return |
| 287 | } |
| 288 | assert false, 'expected missing SDK directory to be rejected' |
| 289 | } |
| 290 | |
| 291 | fn test_native_link_commands_append_directive_link_flags() { |
| 292 | linux := linux_native_link_command('cc', '/tmp/out', 'main.o', '-lm') |
| 293 | macos := macos_native_link_command('/tmp/out', 'main.o', '/SDK Path', 'x86_64', false, '-lm') |
| 294 | |
| 295 | assert linux == 'cc ${os.quoted_path('main.o')} -o ${os.quoted_path('/tmp/out')} -no-pie -lm' |
| 296 | assert macos == 'ld -o ${os.quoted_path('/tmp/out')} ${os.quoted_path('main.o')} -lm -lSystem -syslibroot ${os.quoted_path('/SDK Path')} -e _main -arch x86_64 -platform_version macos 11.0.0 11.0.0' |
| 297 | } |
| 298 | |
| 299 | fn test_linux_native_link_command_uses_configured_driver_and_link_flags() { |
| 300 | cmd := linux_native_link_command('custom-cc', '/tmp/out', 'main.o', '-fopenmp') |
| 301 | |
| 302 | assert cmd.starts_with('custom-cc ${os.quoted_path('main.o')}'), cmd |
| 303 | assert cmd.contains('-fopenmp'), cmd |
| 304 | } |
| 305 | |
| 306 | fn test_native_link_commands_keep_wl_for_linux_and_translate_for_macos_ld() { |
| 307 | linux := linux_native_link_command('cc', '/tmp/out', 'main.o', '-Wl,-rpath,/tmp/lib') |
| 308 | macos := macos_native_link_command('/tmp/out', 'main.o', '/SDK Path', 'x86_64', false, |
| 309 | '-Wl,-rpath,@loader_path/lib') |
| 310 | |
| 311 | assert linux.contains('-Wl,-rpath,/tmp/lib'), linux |
| 312 | assert macos.contains(' -rpath @loader_path/lib '), macos |
| 313 | assert !macos.contains('-Wl,'), macos |
| 314 | } |
| 315 | |
| 316 | fn test_native_link_commands_translate_xlinker_for_macos_ld() { |
| 317 | macos := macos_native_link_command('/tmp/out', 'main.o', '/SDK Path', 'x86_64', false, |
| 318 | '-Xlinker -rpath -Xlinker @loader_path/lib') |
| 319 | |
| 320 | assert macos.contains(' -rpath @loader_path/lib '), macos |
| 321 | assert !macos.contains('-Xlinker'), macos |
| 322 | } |
| 323 | |
| 324 | fn test_macos_native_link_command_keeps_framework_search_paths() { |
| 325 | macos := macos_native_link_command('/tmp/out', 'main.o', '/SDK Path', 'x86_64', false, |
| 326 | '-F /tmp/Fw -F/opt/Fw -framework Foo') |
| 327 | |
| 328 | assert macos.contains(' -F /tmp/Fw -F/opt/Fw -framework Foo '), macos |
| 329 | } |
| 330 | |
| 331 | fn test_split_compile_and_link_flags_duplicates_dual_use_driver_flags() { |
| 332 | compile_flags, link_flags := |
| 333 | split_compile_and_link_flags('-I include -fopenmp -fopenmp=libomp -pthread helper.c') |
| 334 | |
| 335 | assert compile_flags == '-I include -fopenmp -fopenmp=libomp -pthread' |
| 336 | assert link_flags == '-fopenmp -fopenmp=libomp -pthread helper.c' |
| 337 | } |
| 338 | |
| 339 | fn test_split_compile_and_link_flags_keeps_framework_search_paths_dual_use() { |
| 340 | compile_flags, link_flags := |
| 341 | split_compile_and_link_flags('-F /tmp/Fw -F/opt/Fw -framework Foo helper.m') |
| 342 | |
| 343 | assert compile_flags == '-F /tmp/Fw -F/opt/Fw' |
| 344 | assert link_flags == '-F /tmp/Fw -F/opt/Fw -framework Foo helper.m' |
| 345 | } |
| 346 | |
| 347 | fn test_macos_native_ld_rejects_dual_use_driver_link_flags() { |
| 348 | validate_macos_native_ld_link_flags('-lm')! |
| 349 | validate_macos_native_ld_link_flags('-fopenmp') or { |
| 350 | assert err.msg().contains('macOS native ld cannot consume driver linker flags'), err.msg() |
| 351 | return |
| 352 | } |
| 353 | assert false, 'expected macOS native ld driver flag diagnostic' |
| 354 | } |
| 355 | |
| 356 | fn test_native_linux_tiny_link_allows_runtime_system_libs_when_user_flags_are_empty() { |
| 357 | assert native_link_flags_allow_builtin_linux_tiny('', '') |
| 358 | assert native_link_flags_allow_builtin_linux_tiny(' ', '') |
| 359 | assert native_link_flags_allow_builtin_linux_tiny('-lpthread -lm -ldl -lc', '') |
| 360 | assert native_link_flags_allow_builtin_linux_tiny('-l pthread -l m -l dl -l c', '') |
| 361 | } |
| 362 | |
| 363 | fn test_native_linux_tiny_link_blocks_user_external_link_flags() { |
| 364 | for user_link_flags in ['-lm', '-Wl,-rpath,/tmp/lib', '/tmp/helper.c', '-fopenmp', '-pthread', |
| 365 | '-F/tmp/Frameworks'] { |
| 366 | assert !native_link_flags_allow_builtin_linux_tiny('-lpthread -lm -ldl', user_link_flags), user_link_flags |
| 367 | } |
| 368 | } |
| 369 | |
| 370 | fn test_native_linux_tiny_link_blocks_external_link_inputs_in_global_flags() { |
| 371 | assert !native_link_flags_allow_builtin_linux_tiny('-Xlinker -rpath', '') |
| 372 | assert !native_link_flags_allow_builtin_linux_tiny('-L/tmp', '') |
| 373 | assert !native_link_flags_allow_builtin_linux_tiny('/tmp/helper.o', '') |
| 374 | assert !native_link_flags_allow_builtin_linux_tiny('/tmp/libhelper.a', '') |
| 375 | assert !native_link_flags_allow_builtin_linux_tiny('-framework Foundation', '') |
| 376 | assert !native_link_flags_allow_builtin_linux_tiny('-lfoo', '') |
| 377 | } |
| 378 | |
| 379 | fn test_native_external_link_inputs_replace_sources_with_objects_in_order() { |
| 380 | inputs := native_external_link_inputs('-L/tmp/lib /tmp/foo.c -lfoo /tmp/bar.m /tmp/baz.o', |
| 381 | '/tmp/out')! |
| 382 | |
| 383 | assert inputs.source_files == ['/tmp/foo.c', '/tmp/bar.m'] |
| 384 | assert inputs.object_files.len == 2 |
| 385 | assert inputs.link_flags == '-L/tmp/lib ${os.quoted_path(inputs.object_files[0])} -lfoo ${os.quoted_path(inputs.object_files[1])} /tmp/baz.o' |
| 386 | assert !inputs.link_flags.contains('/tmp/foo.c') |
| 387 | assert !inputs.link_flags.contains('/tmp/bar.m') |
| 388 | } |
| 389 | |
| 390 | fn test_native_external_link_inputs_reject_unsupported_c_family_sources() { |
| 391 | for source in ['/tmp/foo.cc', '/tmp/foo.cpp', '/tmp/foo.cxx', '/tmp/foo.mm'] { |
| 392 | native_external_link_inputs(source, '/tmp/out') or { |
| 393 | assert err.msg().starts_with('x64: unsupported backend feature: '), err.msg() |
| 394 | continue |
| 395 | } |
| 396 | assert false, 'expected ${source} source to be rejected' |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | fn test_native_external_link_inputs_reject_quoted_source_tokens() { |
| 401 | native_external_link_inputs('"foo.c"', '/tmp/out') or { |
| 402 | assert err.msg().starts_with('x64: unsupported backend feature: quoted #flag source path'), err.msg() |
| 403 | |
| 404 | return |
| 405 | } |
| 406 | assert false, 'expected quoted source token to be rejected' |
| 407 | } |
| 408 | |
| 409 | fn test_macos_native_link_command_uses_rewritten_external_objects() { |
| 410 | tmp_dir := os.real_path(os.vtmp_dir()) |
| 411 | foo_c := os.join_path(tmp_dir, 'foo.c') |
| 412 | bar_m := os.join_path(tmp_dir, 'bar.m') |
| 413 | out_path := os.join_path(tmp_dir, 'out') |
| 414 | inputs := native_external_link_inputs('${foo_c} -framework Foundation ${bar_m}', out_path)! |
| 415 | cmd := macos_native_link_command(out_path, 'main.o', '/SDK Path', 'x86_64', false, |
| 416 | inputs.link_flags) |
| 417 | |
| 418 | assert cmd.contains(os.quoted_path(inputs.object_files[0])), cmd |
| 419 | assert cmd.contains(os.quoted_path(inputs.object_files[1])), cmd |
| 420 | assert cmd.contains('-framework Foundation'), cmd |
| 421 | assert !cmd.contains(foo_c), cmd |
| 422 | assert !cmd.contains(bar_m), cmd |
| 423 | } |
| 424 | |
| 425 | fn test_native_external_source_compile_command_adds_macos_sdk_and_arch() { |
| 426 | cmd := native_external_source_compile_command('cc', '/tmp/foo.c', '/tmp/foo.o', |
| 427 | '-I /tmp/include -DHELPER', '/SDK Path', 'x86_64', 'macos') |
| 428 | |
| 429 | assert cmd.starts_with('cc -c '), cmd |
| 430 | assert cmd.contains('-isysroot ${os.quoted_path('/SDK Path')}'), cmd |
| 431 | assert cmd.contains('-arch x86_64'), cmd |
| 432 | assert cmd.contains('-I /tmp/include -DHELPER'), cmd |
| 433 | assert cmd.contains(os.quoted_path('/tmp/foo.c')), cmd |
| 434 | assert cmd.contains('-o ${os.quoted_path('/tmp/foo.o')}'), cmd |
| 435 | } |
| 436 | |
| 437 | fn test_native_external_source_compile_command_keeps_linux_compile_flags() { |
| 438 | cmd := native_external_source_compile_command('cc', '/tmp/foo.c', '/tmp/foo.o', |
| 439 | '-I /tmp/include -DHELPER', '', '', 'linux') |
| 440 | |
| 441 | assert cmd.starts_with('cc -c '), cmd |
| 442 | assert !cmd.contains('-isysroot'), cmd |
| 443 | assert !cmd.contains('-arch'), cmd |
| 444 | assert cmd.contains('-I /tmp/include -DHELPER'), cmd |
| 445 | assert cmd.contains(os.quoted_path('/tmp/foo.c')), cmd |
| 446 | assert cmd.contains('-o ${os.quoted_path('/tmp/foo.o')}'), cmd |
| 447 | } |
| 448 | |
| 449 | fn test_native_external_source_compiler_uses_pref_ccompiler() { |
| 450 | mut prefs := pref.new_preferences() |
| 451 | prefs.ccompiler = 'custom-native-cc' |
| 452 | mut b := new_builder(&prefs) |
| 453 | |
| 454 | assert b.native_external_source_compiler('macos') == 'custom-native-cc' |
| 455 | } |
| 456 | |
| 457 | fn test_native_external_source_compiler_uses_v2cc_when_pref_is_empty() { |
| 458 | old_v2cc := os.getenv_opt('V2CC') |
| 459 | os.setenv('V2CC', 'env-native-cc', true) |
| 460 | defer { |
| 461 | if old := old_v2cc { |
| 462 | os.setenv('V2CC', old, true) |
| 463 | } else { |
| 464 | os.unsetenv('V2CC') |
| 465 | } |
| 466 | } |
| 467 | |
| 468 | mut prefs := pref.new_preferences() |
| 469 | prefs.ccompiler = '' |
| 470 | mut b := new_builder(&prefs) |
| 471 | |
| 472 | assert b.native_external_source_compiler('macos') == 'env-native-cc' |
| 473 | } |
| 474 | |
| 475 | fn test_native_external_source_compiler_defaults_to_cc_for_macos() { |
| 476 | old_v2cc := os.getenv_opt('V2CC') |
| 477 | os.unsetenv('V2CC') |
| 478 | tmp_dir := os.join_path(os.vtmp_dir(), 'v2_builder_native_external_cc_${os.getpid()}') |
| 479 | tcc_dir := os.join_path(tmp_dir, 'thirdparty', 'tcc') |
| 480 | os.mkdir_all(tcc_dir) or { panic(err) } |
| 481 | os.write_file(os.join_path(tcc_dir, 'tcc.exe'), '') or { panic(err) } |
| 482 | defer { |
| 483 | os.rmdir_all(tmp_dir) or {} |
| 484 | if old := old_v2cc { |
| 485 | os.setenv('V2CC', old, true) |
| 486 | } else { |
| 487 | os.unsetenv('V2CC') |
| 488 | } |
| 489 | } |
| 490 | |
| 491 | mut prefs := pref.Preferences{ |
| 492 | vroot: tmp_dir |
| 493 | } |
| 494 | mut b := new_builder(&prefs) |
| 495 | |
| 496 | assert b.native_external_source_compiler('macos') == 'cc' |
| 497 | assert b.native_external_source_compiler('darwin') == 'cc' |
| 498 | } |
| 499 | |
| 500 | fn test_native_linux_hosted_link_compiler_pref_v2cc_default_order() { |
| 501 | old_v2cc := os.getenv_opt('V2CC') |
| 502 | tmp_dir := os.join_path(os.vtmp_dir(), 'v2_builder_native_linux_hosted_cc_${os.getpid()}') |
| 503 | tcc_dir := os.join_path(tmp_dir, 'thirdparty', 'tcc') |
| 504 | os.mkdir_all(tcc_dir) or { panic(err) } |
| 505 | os.write_file(os.join_path(tcc_dir, 'tcc.exe'), '') or { panic(err) } |
| 506 | defer { |
| 507 | os.rmdir_all(tmp_dir) or {} |
| 508 | if old := old_v2cc { |
| 509 | os.setenv('V2CC', old, true) |
| 510 | } else { |
| 511 | os.unsetenv('V2CC') |
| 512 | } |
| 513 | } |
| 514 | |
| 515 | os.setenv('V2CC', 'env-hosted-cc', true) |
| 516 | mut pref_prefs := pref.new_preferences() |
| 517 | pref_prefs.ccompiler = 'pref-hosted-cc' |
| 518 | pref_builder := new_builder(&pref_prefs) |
| 519 | assert pref_builder.native_linux_hosted_link_compiler() == 'pref-hosted-cc' |
| 520 | |
| 521 | mut env_prefs := pref.new_preferences() |
| 522 | env_prefs.ccompiler = '' |
| 523 | env_builder := new_builder(&env_prefs) |
| 524 | assert env_builder.native_linux_hosted_link_compiler() == 'env-hosted-cc' |
| 525 | |
| 526 | os.unsetenv('V2CC') |
| 527 | mut default_prefs := pref.Preferences{ |
| 528 | vroot: tmp_dir |
| 529 | } |
| 530 | default_builder := new_builder(&default_prefs) |
| 531 | assert default_builder.native_linux_hosted_link_compiler() == 'cc' |
| 532 | } |
| 533 | |
| 534 | fn test_native_link_flags_from_sources_are_not_global() { |
| 535 | tmp_dir := os.join_path(os.vtmp_dir(), 'v2_builder_native_link_flags_${os.getpid()}') |
| 536 | os.mkdir_all(tmp_dir) or { panic(err) } |
| 537 | defer { |
| 538 | os.rmdir_all(tmp_dir) or {} |
| 539 | } |
| 540 | no_flag_path := os.join_path(tmp_dir, 'no_flag.v') |
| 541 | math_flag_path := os.join_path(tmp_dir, 'math_flag.v') |
| 542 | os.write_file(no_flag_path, 'module main\n') or { panic(err) } |
| 543 | os.write_file(math_flag_path, 'module main\n#flag -lm\n') or { panic(err) } |
| 544 | |
| 545 | mut prefs := pref.new_preferences() |
| 546 | prefs.backend = .x64 |
| 547 | prefs.arch = .x64 |
| 548 | prefs.target_os = 'linux' |
| 549 | prefs.skip_builtin = true |
| 550 | |
| 551 | mut no_flag_builder := new_builder(&prefs) |
| 552 | no_flag_builder.files = [ |
| 553 | ast.File{ |
| 554 | name: no_flag_path |
| 555 | }, |
| 556 | ] |
| 557 | assert no_flag_builder.native_link_flags_from_sources() == '' |
| 558 | |
| 559 | mut math_flag_builder := new_builder(&prefs) |
| 560 | math_flag_builder.files = [ |
| 561 | ast.File{ |
| 562 | name: math_flag_path |
| 563 | }, |
| 564 | ] |
| 565 | assert math_flag_builder.native_link_flags_from_sources() == '-lm' |
| 566 | } |
| 567 | |
| 568 | fn test_native_user_link_flags_ignore_internal_vlib_runtime_flags() { |
| 569 | tmp_dir := os.join_path(os.vtmp_dir(), 'v2_builder_native_user_link_flags_${os.getpid()}') |
| 570 | vlib_os_dir := os.join_path(tmp_dir, 'vlib', 'os') |
| 571 | os.mkdir_all(vlib_os_dir) or { panic(err) } |
| 572 | defer { |
| 573 | os.rmdir_all(tmp_dir) or {} |
| 574 | } |
| 575 | main_path := os.join_path(tmp_dir, 'main.v') |
| 576 | internal_path := os.join_path(vlib_os_dir, 'signal_linux.c.v') |
| 577 | os.write_file(main_path, 'module main\n#flag -lm\n') or { panic(err) } |
| 578 | os.write_file(internal_path, 'module os\n#flag -lpthread\n') or { panic(err) } |
| 579 | |
| 580 | mut prefs := pref.Preferences{ |
| 581 | backend: .x64 |
| 582 | arch: .x64 |
| 583 | target_os: 'linux' |
| 584 | vroot: tmp_dir |
| 585 | skip_builtin: true |
| 586 | } |
| 587 | |
| 588 | mut b := new_builder(&prefs) |
| 589 | b.user_files = [main_path] |
| 590 | b.files = [ |
| 591 | ast.File{ |
| 592 | name: main_path |
| 593 | }, |
| 594 | ast.File{ |
| 595 | name: internal_path |
| 596 | }, |
| 597 | ] |
| 598 | _, all_link_flags := b.native_compile_and_link_flags_from_sources() |
| 599 | _, user_link_flags := b.native_user_compile_and_link_flags_from_sources() |
| 600 | |
| 601 | assert all_link_flags.fields().sorted() == ['-lm', '-lpthread'] |
| 602 | assert user_link_flags == '-lm' |
| 603 | assert !native_link_flags_allow_builtin_linux_tiny(all_link_flags, user_link_flags) |
| 604 | |
| 605 | b.user_files = [] |
| 606 | b.files = [ |
| 607 | ast.File{ |
| 608 | name: internal_path |
| 609 | }, |
| 610 | ] |
| 611 | _, internal_only_link_flags := b.native_compile_and_link_flags_from_sources() |
| 612 | _, no_user_link_flags := b.native_user_compile_and_link_flags_from_sources() |
| 613 | |
| 614 | assert internal_only_link_flags == '-lpthread' |
| 615 | assert no_user_link_flags == '' |
| 616 | assert native_link_flags_allow_builtin_linux_tiny(internal_only_link_flags, no_user_link_flags) |
| 617 | } |
| 618 | |
| 619 | fn test_native_compile_and_link_flags_from_sources_keep_compile_flags_for_source_inputs() { |
| 620 | tmp_dir := os.join_path(os.vtmp_dir(), 'v2_builder_native_source_flags_${os.getpid()}') |
| 621 | include_dir := os.join_path(tmp_dir, 'include') |
| 622 | os.mkdir_all(include_dir) or { panic(err) } |
| 623 | defer { |
| 624 | os.rmdir_all(tmp_dir) or {} |
| 625 | } |
| 626 | main_path := os.join_path(tmp_dir, 'main.v') |
| 627 | c_path := os.join_path(tmp_dir, 'helper.c') |
| 628 | os.write_file(c_path, 'int helper(void) { return 7; }\n') or { panic(err) } |
| 629 | os.write_file(main_path, 'module main |
| 630 | #flag -I include -DHELPER -fopenmp -pthread helper.c -Wl,-rpath,/tmp/native-lib -lm |
| 631 | ') or { |
| 632 | panic(err) |
| 633 | } |
| 634 | |
| 635 | mut prefs := pref.new_preferences() |
| 636 | prefs.backend = .x64 |
| 637 | prefs.arch = .x64 |
| 638 | prefs.target_os = 'linux' |
| 639 | prefs.skip_builtin = true |
| 640 | |
| 641 | mut b := new_builder(&prefs) |
| 642 | b.files = [ |
| 643 | ast.File{ |
| 644 | name: main_path |
| 645 | }, |
| 646 | ] |
| 647 | compile_flags, link_flags := b.native_compile_and_link_flags_from_sources() |
| 648 | expected_include_dir := os.real_path(include_dir) |
| 649 | expected_c_path := os.real_path(c_path) |
| 650 | |
| 651 | assert compile_flags == '-I ${expected_include_dir} -DHELPER -fopenmp -pthread' |
| 652 | assert link_flags == '-fopenmp -pthread ${expected_c_path} -Wl,-rpath,/tmp/native-lib -lm' |
| 653 | } |
| 654 | |
| 655 | fn test_native_x64_requires_ssa_optimization() { |
| 656 | mut prefs := pref.new_preferences() |
| 657 | assert prefs.no_optimize |
| 658 | builder := new_builder(&prefs) |
| 659 | assert builder.native_backend_requires_ssa_optimization(.x64) |
| 660 | assert !builder.native_backend_requires_ssa_optimization(.arm64) |
| 661 | } |
| 662 | |