feat: supply process arguments through native startup
This commit is contained in:
+179
-11
@@ -3718,7 +3718,6 @@ milestone_33_injects_explicit_io_provider_and_runs_std_io :: proc(t: ^testing.T)
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect(t, hir_module.injected_main != hir.INVALID_FUNCTION)
|
||||
testing.expect(t, hir_module.io_provider != hir.INVALID_FUNCTION)
|
||||
testing.expect_value(t, count_substring_occurrences(llvm_text, "define i32 @main()"), 1)
|
||||
testing.expect_value(t, count_substring_occurrences(llvm_text, "define internal fastcc i32 @bro__p0__main__"), 1)
|
||||
testing.expect_value(t, count_substring_occurrences(llvm_text, "declare i64 @write("), 1)
|
||||
|
||||
@@ -3935,7 +3934,6 @@ milestone_37_validates_process_init_and_hidden_system_provider :: proc(t: ^testi
|
||||
Case :: struct {
|
||||
io_source: string,
|
||||
process_source: string,
|
||||
message: string,
|
||||
}
|
||||
cases := []Case{
|
||||
{
|
||||
@@ -3943,18 +3941,54 @@ milestone_37_validates_process_init_and_hidden_system_provider :: proc(t: ^testi
|
||||
system func() Io { return Io {value = 0} }
|
||||
`,
|
||||
process_source=`io :: import "@std/io"
|
||||
Init :: struct { io io.Io }
|
||||
Args :: struct { values [][*;0]u8 }
|
||||
Init :: struct { io io.Io, args Args }
|
||||
`,
|
||||
message="does not provide the required '@hide system func() Io'",
|
||||
},
|
||||
{
|
||||
io_source=`Io :: struct { value i32 }
|
||||
@hide system func() Io { return Io {value = 0} }
|
||||
`,
|
||||
process_source=`io :: import "@std/io"
|
||||
Init :: struct { io io.Io, extra i32 }
|
||||
Args :: struct { values [][*;0]u8 }
|
||||
Init :: struct { io io.Io, args Args, extra i32 }
|
||||
`,
|
||||
},
|
||||
{
|
||||
io_source=`Io :: struct { value i32 }
|
||||
@hide system func() Io { return Io {value = 0} }
|
||||
`,
|
||||
process_source=`io :: import "@std/io"
|
||||
Args :: struct { values [][*;0]u8 }
|
||||
Init :: struct { io io.Io }
|
||||
`,
|
||||
},
|
||||
{
|
||||
io_source=`Io :: struct { value i32 }
|
||||
@hide system func() Io { return Io {value = 0} }
|
||||
`,
|
||||
process_source=`io :: import "@std/io"
|
||||
Args :: struct { values [][*]u8 }
|
||||
Init :: struct { io io.Io, args Args }
|
||||
`,
|
||||
},
|
||||
{
|
||||
io_source=`Io :: struct { value i32 }
|
||||
@hide system func() Io { return Io {value = 0} }
|
||||
`,
|
||||
process_source=`io :: import "@std/io"
|
||||
Args :: struct { values [][*;0]u8, extra i32 }
|
||||
Init :: struct { io io.Io, args Args }
|
||||
`,
|
||||
},
|
||||
{
|
||||
io_source=`Io :: struct { value i32 }
|
||||
@hide system func() Io { return Io {value = 0} }
|
||||
`,
|
||||
process_source=`io :: import "@std/io"
|
||||
Args :: struct { values [][*;0]u8 }
|
||||
Init :: struct { io io.Io, args i32 }
|
||||
`,
|
||||
message="Init must be an auto-layout record containing exactly 'io io.Io'",
|
||||
},
|
||||
}
|
||||
for test_case, index in cases {
|
||||
@@ -3985,12 +4019,8 @@ main func(init process.Init) void { _ = init }
|
||||
symbols := symbol.init_table()
|
||||
ast_module, loaded := loader.load(app, &sources, &diagnostics, &symbols, project_root_path=root)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, test_case.message)
|
||||
}
|
||||
testing.expect(t, loaded)
|
||||
testing.expect(t, found)
|
||||
testing.expect(t, len(diagnostics.items) > 0)
|
||||
hir.destroy_module(&hir_module)
|
||||
ast.destroy_module(&ast_module)
|
||||
symbol.destroy_table(&symbols)
|
||||
@@ -3999,6 +4029,144 @@ main func(init process.Init) void { _ = init }
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
process_init_supplies_native_arguments_and_helpers_borrow_explicit_storage :: proc(t: ^testing.T) {
|
||||
directory := "/tmp/brolang-test-process-args"
|
||||
main_path := "/tmp/brolang-test-process-args/main.bro"
|
||||
output := "/tmp/brolang-test-process-args/app"
|
||||
text := `process :: import "@std/process"
|
||||
io :: import "@std/io"
|
||||
mem :: import "@std/mem"
|
||||
|
||||
supplied_args_work func() bool {
|
||||
text [3;0]mut u8 := ['r', 'u', 'n']
|
||||
pointers [1][*;0]u8 :: [(&text).ptr]
|
||||
args :: process.Args { values = pointers[..] }
|
||||
if process.argument_count(args) != 1 { return false }
|
||||
if process.argument(args, 0) |value| {
|
||||
text[0] = 'f'
|
||||
if !mem.eql(value, "fun") { return false }
|
||||
} else { return false }
|
||||
if process.argument(args, 1) |_| { return false }
|
||||
empty :: process.Args { values = pointers[0..0] }
|
||||
if process.argument_count(empty) != 0 { return false }
|
||||
if process.argument(empty, 0) |_| { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
main func(init process.Init) i32 {
|
||||
count :: process.argument_count(init.args)
|
||||
if count != 1 and count != 4 { return 1 }
|
||||
if process.argument(init.args, 0) |executable| {
|
||||
if !mem.eql(executable, "/tmp/brolang-test-process-args/app") { return 2 }
|
||||
} else { return 3 }
|
||||
if process.argument(init.args, count) |_| { return 4 }
|
||||
if process.argument(init.args, maxval!(usize)) |_| { return 5 }
|
||||
if count == 4 {
|
||||
if process.argument(init.args, 1) |operation| {
|
||||
if !mem.eql(operation, "run") { return 6 }
|
||||
} else { return 7 }
|
||||
if process.argument(init.args, 2) |empty| {
|
||||
if empty.len != 0 { return 8 }
|
||||
} else { return 9 }
|
||||
if process.argument(init.args, 3) |spaced| {
|
||||
if !mem.eql(spaced, "two words") { return 10 }
|
||||
} else { return 11 }
|
||||
}
|
||||
if !supplied_args_work() { return 12 }
|
||||
io.write_all(io.stdout(init.io), "args-ok\n") catch |_| { return 13 }
|
||||
return 0
|
||||
}
|
||||
`
|
||||
_ = os2.remove_all(directory)
|
||||
defer _ = os2.remove_all(directory)
|
||||
testing.expect(t, os.make_directory(directory) == nil)
|
||||
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||
testing.expect_value(t, compiler_core.compile_package(
|
||||
directory, output, nil, target.DEFAULT, cimport.Options{}, ".",
|
||||
), 0)
|
||||
commands := [][]string{
|
||||
{output},
|
||||
{output, "run", "", "two words"},
|
||||
}
|
||||
for command in commands {
|
||||
state, stdout, stderr, error := os2.process_exec(
|
||||
os2.Process_Desc{command=command}, context.allocator,
|
||||
)
|
||||
testing.expect(t, error == nil)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
testing.expect_value(t, string(stdout), "args-ok\n")
|
||||
testing.expect_value(t, len(stderr), 0)
|
||||
delete(stdout)
|
||||
delete(stderr)
|
||||
}
|
||||
|
||||
plain_main := "main func() i32 { return 37 }\n"
|
||||
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)plain_main))
|
||||
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
|
||||
for command in commands {
|
||||
state, stdout, stderr, error := os2.process_exec(
|
||||
os2.Process_Desc{command=command}, context.allocator,
|
||||
)
|
||||
testing.expect(t, error == nil)
|
||||
testing.expect_value(t, state.exit_code, 37)
|
||||
testing.expect_value(t, len(stdout), 0)
|
||||
testing.expect_value(t, len(stderr), 0)
|
||||
delete(stdout)
|
||||
delete(stderr)
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
process_init_startup_respects_reordered_fields :: proc(t: ^testing.T) {
|
||||
root := "/tmp/brolang-test-process-reordered"
|
||||
app := "/tmp/brolang-test-process-reordered/app"
|
||||
output := "/tmp/brolang-test-process-reordered/program"
|
||||
io_source := `Io :: struct { value i32 }
|
||||
@hide system func() Io { return Io {value = 37} }
|
||||
`
|
||||
process_source := `io :: import "@std/io"
|
||||
Args :: struct { values [][*;0]u8 }
|
||||
Init :: struct { args Args, io io.Io }
|
||||
`
|
||||
main_source := `process :: import "@std/process"
|
||||
main func(init process.Init) i32 {
|
||||
if init.io.value != 37 or init.args.values.len != 3 { return 1 }
|
||||
empty :: init.args.values[1]
|
||||
if empty[0] != 0 { return 2 }
|
||||
spaced :: init.args.values[2]
|
||||
if spaced[0] != 'a' or spaced[1] != ' ' or spaced[2] != 'b' or spaced[3] != 0 { return 3 }
|
||||
return 0
|
||||
}
|
||||
`
|
||||
_ = os2.remove_all(root)
|
||||
defer _ = os2.remove_all(root)
|
||||
testing.expect(t, os2.make_directory_all(app) == nil)
|
||||
testing.expect(t, os2.make_directory_all(fmt.tprintf("%s/std/io", root)) == nil)
|
||||
testing.expect(t, os2.make_directory_all(fmt.tprintf("%s/std/process", root)) == nil)
|
||||
testing.expect(t, os.write_entire_file(
|
||||
fmt.tprintf("%s/main.bro", app), transmute([]byte)main_source,
|
||||
))
|
||||
testing.expect(t, os.write_entire_file(
|
||||
fmt.tprintf("%s/std/io/io.bro", root), transmute([]byte)io_source,
|
||||
))
|
||||
testing.expect(t, os.write_entire_file(
|
||||
fmt.tprintf("%s/std/process/process.bro", root), transmute([]byte)process_source,
|
||||
))
|
||||
testing.expect_value(t, compiler_core.compile_package(
|
||||
app, output, nil, target.DEFAULT, cimport.Options{}, root,
|
||||
), 0)
|
||||
state, stdout, stderr, error := os2.process_exec(
|
||||
os2.Process_Desc{command=[]string{output, "", "a b"}}, context.allocator,
|
||||
)
|
||||
defer delete(stdout)
|
||||
defer delete(stderr)
|
||||
testing.expect(t, error == nil)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
testing.expect_value(t, len(stdout), 0)
|
||||
testing.expect_value(t, len(stderr), 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
milestone_37_tuples_reflection_expand_for_and_debug_print_compile_and_run :: proc(t: ^testing.T) {
|
||||
sources := source.init_store()
|
||||
|
||||
Reference in New Issue
Block a user