add new and init to toolchain

This commit is contained in:
2026-07-04 17:38:45 +02:00
parent cdde49e68b
commit e7f69ffb5a
17 changed files with 711 additions and 63 deletions
+234 -10
View File
@@ -736,6 +736,8 @@ cli_parses_ordered_link_options_and_rejects_invalid_forms :: proc(t: ^testing.T)
"vendor/include",
"--c-define",
"FEATURE=1",
"--root",
".",
"--target",
"aarch64-macos",
})
@@ -746,6 +748,7 @@ cli_parses_ordered_link_options_and_rejects_invalid_forms :: proc(t: ^testing.T)
testing.expect(t, valid)
testing.expect_value(t, options.input_path, "app")
testing.expect_value(t, options.output_path, "app.out")
testing.expect_value(t, options.project_root, ".")
testing.expect_value(t, len(options.link_arguments), 4)
testing.expect_value(t, options.link_arguments[0].kind, linker.Kind.Input)
testing.expect_value(t, options.link_arguments[0].value, "native.c")
@@ -759,6 +762,7 @@ cli_parses_ordered_link_options_and_rejects_invalid_forms :: proc(t: ^testing.T)
_, unknown_valid := parse_cli_args([]string{"brolang", "app", "-o", "out", "--unknown", "value"})
_, incomplete_valid := parse_cli_args([]string{"brolang", "app", "-o"})
_, duplicate_output_valid := parse_cli_args([]string{"brolang", "app", "-o", "one", "-o", "two"})
_, duplicate_root_valid := parse_cli_args([]string{"brolang", "app", "-o", "out", "--root", ".", "--root", "other"})
_, duplicate_empty_output_valid := parse_cli_args([]string{"brolang", "app", "-o", "", "-o", "two"})
_, invalid_target := parse_cli_args([]string{"brolang", "app", "-o", "out", "--target", "x86_64-linux"})
_, legacy_link := parse_cli_args([]string{"brolang", "app", "-o", "out", "--link", "native.c"})
@@ -767,6 +771,7 @@ cli_parses_ordered_link_options_and_rejects_invalid_forms :: proc(t: ^testing.T)
testing.expect(t, !unknown_valid)
testing.expect(t, !incomplete_valid)
testing.expect(t, !duplicate_output_valid)
testing.expect(t, !duplicate_root_valid)
testing.expect(t, !duplicate_empty_output_valid)
testing.expect(t, !invalid_target)
testing.expect(t, !legacy_link)
@@ -2560,22 +2565,194 @@ build_command_is_recognized :: proc(t: ^testing.T) {
@(test)
build_subcommand_compiles_and_runs :: proc(t: ^testing.T) {
defer _ = os.remove("hello")
output := "examples/build/hello/build/hello"
defer _ = os2.remove_all("examples/build/hello/build")
status := compiler_core.run_build("examples/build/hello")
testing.expect_value(t, status, 0)
state := run_executable("./hello")
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
build_subcommand_links_c_source_via_list_field :: proc(t: ^testing.T) {
defer _ = os.remove("manual_build")
output := "examples/build/manual/build/manual_build"
defer _ = os2.remove_all("examples/build/manual/build")
status := compiler_core.run_build("examples/build/manual")
testing.expect_value(t, status, 0)
state := run_executable("./manual_build")
state := run_executable(output)
testing.expect_value(t, state.exit_code, 42)
}
@(test)
new_project_creates_layout_and_builds :: proc(t: ^testing.T) {
root := "/tmp/brolang-test-new-project"
output := "/tmp/brolang-test-new-project/build/brolang-test-new-project"
_ = os2.remove_all(root)
defer _ = os2.remove_all(root)
testing.expect_value(t, run_new_project(root), 0)
testing.expect(t, os.is_dir("/tmp/brolang-test-new-project/source"))
testing.expect(t, os.is_dir("/tmp/brolang-test-new-project/std"))
testing.expect(t, os.is_dir("/tmp/brolang-test-new-project/ffi"))
testing.expect(t, os.is_dir("/tmp/brolang-test-new-project/vendor"))
testing.expect(t, os.exists("/tmp/brolang-test-new-project/build.bro"))
testing.expect(t, os.exists("/tmp/brolang-test-new-project/source/main.bro"))
status := compiler_core.run_build(root)
testing.expect_value(t, status, 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
init_project_creates_missing_layout_and_preserves_existing_files :: proc(t: ^testing.T) {
root := "/tmp/brolang-test-init-project"
output := "/tmp/brolang-test-init-project/build/brolang-test-init-project"
_ = os2.remove_all(root)
defer _ = os2.remove_all(root)
testing.expect(t, os2.make_directory_all(root) == nil)
testing.expect(t, init_project(root))
testing.expect(t, os.is_dir("/tmp/brolang-test-init-project/source"))
testing.expect(t, os.is_dir("/tmp/brolang-test-init-project/std"))
testing.expect(t, os.is_dir("/tmp/brolang-test-init-project/ffi"))
testing.expect(t, os.is_dir("/tmp/brolang-test-init-project/vendor"))
status := compiler_core.run_build(root)
testing.expect_value(t, status, 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
custom_build := "custom build"
custom_main := "custom main"
testing.expect(t, os.write_entire_file("/tmp/brolang-test-init-project/build.bro", transmute([]byte)custom_build))
testing.expect(t, os.write_entire_file("/tmp/brolang-test-init-project/source/main.bro", transmute([]byte)custom_main))
testing.expect(t, init_project(root))
build_data, build_ok := os.read_entire_file("/tmp/brolang-test-init-project/build.bro")
defer delete(build_data)
main_data, main_ok := os.read_entire_file("/tmp/brolang-test-init-project/source/main.bro")
defer delete(main_data)
testing.expect(t, build_ok && string(build_data) == custom_build)
testing.expect(t, main_ok && string(main_data) == custom_main)
}
@(test)
build_root_search_finds_nearest_parent_build_file :: proc(t: ^testing.T) {
root := "/tmp/brolang-test-build-search"
nested := "/tmp/brolang-test-build-search/source/nested"
_ = os2.remove_all(root)
defer _ = os2.remove_all(root)
testing.expect(t, os2.make_directory_all(nested) == nil)
build_text := "# root\n"
testing.expect(t, os.write_entire_file("/tmp/brolang-test-build-search/build.bro", transmute([]byte)build_text))
found, ok := compiler_core.find_build_root_from(nested)
defer delete(found)
testing.expect(t, ok)
testing.expect(t, strings.has_suffix(found, "/brolang-test-build-search"))
}
@(test)
build_subcommand_discovered_root_writes_to_root_build_dir :: proc(t: ^testing.T) {
root := "/tmp/brolang-test-build-nested"
nested := "/tmp/brolang-test-build-nested/source/nested"
output := "/tmp/brolang-test-build-nested/build/brolang-test-build-nested"
_ = os2.remove_all(root)
defer _ = os2.remove_all(root)
testing.expect(t, init_project(root))
testing.expect(t, os2.make_directory_all(nested) == nil)
found, ok := compiler_core.find_build_root_from(nested)
defer delete(found)
testing.expect(t, ok)
status := compiler_core.run_build(found)
testing.expect_value(t, status, 0)
testing.expect(t, os.exists(output))
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
build_subcommand_rejects_path_like_output_name :: proc(t: ^testing.T) {
root := "/tmp/brolang-test-build-invalid-name"
_ = os2.remove_all(root)
defer _ = os2.remove_all(root)
testing.expect(t, init_project(root))
text := `b :: import "@std/build"
config :: b.BuildConfig{
name = "bad/name",
source = "source",
libraries = &[],
lib_paths = &[],
includes = &[],
defines = &[],
links = &[],
}
`
testing.expect(t, os.write_entire_file("/tmp/brolang-test-build-invalid-name/build.bro", transmute([]byte)text))
status := compiler_core.run_build(root)
testing.expect_value(t, status, 2)
testing.expect(t, !os.exists("/tmp/brolang-test-build-invalid-name/build/bad/name"))
}
@(test)
project_root_imports_resolve_under_passed_root :: proc(t: ^testing.T) {
sources := source.init_store()
defer source.destroy_store(&sources)
diagnostics := source.init_store_diagnostics(&sources)
defer source.destroy_diagnostics(&diagnostics)
symbols := symbol.init_table()
defer symbol.destroy_table(&symbols)
ast_module, loaded := loader.load(
"examples/build/raylib/source",
&sources,
&diagnostics,
&symbols,
context.allocator,
context.allocator,
cimport.Options{},
target.DEFAULT,
"examples/build/raylib",
)
defer ast.destroy_module(&ast_module)
testing.expect(t, loaded)
found_vendor := false
for pkg in ast_module.packages {
found_vendor = found_vendor || strings.has_suffix(pkg.path, "/examples/build/raylib/vendor/raylib")
}
testing.expect(t, found_vendor)
}
@(test)
direct_compile_defaults_project_root_to_input_package_and_allows_override :: proc(t: ^testing.T) {
root := "/tmp/brolang-test-direct-root"
app := "/tmp/brolang-test-direct-root/app"
dep := "/tmp/brolang-test-direct-root/dep"
output := "/tmp/brolang-test-direct-root-out"
_ = os2.remove_all(root)
_ = os.remove(output)
defer _ = os2.remove_all(root)
defer _ = os.remove(output)
testing.expect(t, os2.make_directory_all(app) == nil)
testing.expect(t, os2.make_directory_all(dep) == nil)
main_text := "dep :: import \"@dep\"\nmain func() i32 { return dep.value }\n"
dep_text := "value i32 :: 7\n"
testing.expect(t, os.write_entire_file("/tmp/brolang-test-direct-root/app/main.bro", transmute([]byte)main_text))
testing.expect(t, os.write_entire_file("/tmp/brolang-test-direct-root/dep/dep.bro", transmute([]byte)dep_text))
testing.expect_value(t, compiler_core.compile_package(app, output), 1)
status := compiler_core.compile_package(app, output, nil, target.DEFAULT, cimport.Options{}, root)
testing.expect_value(t, status, 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 7)
}
@(test)
c_printf_accepts_a_string_literal :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-printf"
@@ -2629,7 +2806,7 @@ milestone_24_rejects_invalid_forms :: proc(t: ^testing.T) {
milestone_25_heap_compiles_and_runs :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-heap"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/programs/heap", output)
status := compiler_core.compile_package("examples/programs/heap", output, nil, target.DEFAULT, cimport.Options{}, ".")
testing.expect_value(t, status, 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
@@ -2643,7 +2820,7 @@ milestone_25_heap_emits_libc_alloc_declarations :: proc(t: ^testing.T) {
defer source.destroy_diagnostics(&diagnostics)
symbols := symbol.init_table()
defer symbol.destroy_table(&symbols)
ast_module, loaded := loader.load("examples/programs/heap", &sources, &diagnostics, &symbols)
ast_module, loaded := loader.load("examples/programs/heap", &sources, &diagnostics, &symbols, context.allocator, context.allocator, cimport.Options{}, target.DEFAULT, ".")
defer ast.destroy_module(&ast_module)
testing.expect(t, loaded)
@@ -2818,6 +2995,35 @@ yield_compiles_and_runs :: proc(t: ^testing.T) {
testing.expect_value(t, state.exit_code, 42)
}
@(test)
value_loop_label_does_not_shadow_own_yield_target :: proc(t: ^testing.T) {
text := `main func() i32 {
idx :: for 0..10 |i| hit: {
if (i == 3) yield :hit i
yield none
}
if idx |found| {
if (found == 3) return 0
return 1
}
return 2
}
`
source_file := source.Source{path="test.bro", text=text}
diagnostics := source.init_diagnostics(&source_file)
defer source.destroy_diagnostics(&diagnostics)
symbols := symbol.init_table()
defer symbol.destroy_table(&symbols)
stream := lexer.lex(&source_file, &diagnostics, &symbols)
defer delete(stream.items)
ast_module := parser.parse(&stream, &source_file, &diagnostics)
defer ast.destroy_module(&ast_module)
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
defer hir.destroy_module(&hir_module)
testing.expect_value(t, len(diagnostics.items), 0)
}
@(test)
native_union_compiles_and_runs :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-unions"
@@ -4306,7 +4512,7 @@ backend_translates_link_arguments_without_reordering_them :: proc(t: ^testing.T)
command := backend.build_command("module.ll", "program", arguments, target.DEFAULT, c_options)
defer backend.destroy_command(command)
expected := []string{
expected_prefix := []string{
"/usr/bin/env",
"zig",
"cc",
@@ -4314,6 +4520,24 @@ backend_translates_link_arguments_without_reordering_them :: proc(t: ^testing.T)
"aarch64-macos",
"-Wno-override-module",
"-Wno-unused-command-line-argument",
}
testing.expect(t, len(command) >= len(expected_prefix))
for value, index in expected_prefix {
testing.expect_value(t, command[index], value)
}
tail_index := len(expected_prefix)
if sdk, ok := backend.macos_sdk_root(); ok {
defer delete(sdk)
frameworks := fmt.tprintf("-F%s/System/Library/Frameworks", sdk)
lib_dir := fmt.tprintf("-L%s/usr/lib", sdk)
testing.expect(t, len(command) >= tail_index + 2)
testing.expect_value(t, command[tail_index], frameworks)
testing.expect_value(t, command[tail_index + 1], lib_dir)
tail_index += 2
}
expected_tail := []string{
"module.ll",
"-Ivendor/include",
"-DFEATURE=1",
@@ -4324,9 +4548,9 @@ backend_translates_link_arguments_without_reordering_them :: proc(t: ^testing.T)
"-o",
"program",
}
testing.expect_value(t, len(command), len(expected))
for value, index in expected {
testing.expect_value(t, command[index], value)
testing.expect_value(t, len(command), tail_index + len(expected_tail))
for value, index in expected_tail {
testing.expect_value(t, command[tail_index + index], value)
}
}