restricted c header imports
This commit is contained in:
+169
-5
@@ -3,6 +3,7 @@ package main
|
||||
import compiler_core "./compiler"
|
||||
import "./compiler/ast"
|
||||
import "./compiler/backend"
|
||||
import "./compiler/cimport"
|
||||
import "./compiler/checker"
|
||||
import "./compiler/hir"
|
||||
import "./compiler/ir"
|
||||
@@ -18,6 +19,7 @@ import "./compiler/target"
|
||||
import "./compiler/token"
|
||||
import "./compiler/types"
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:os/os2"
|
||||
import "core:strings"
|
||||
@@ -395,20 +397,26 @@ cli_parses_ordered_link_options_and_rejects_invalid_forms :: proc(t: ^testing.T)
|
||||
options, valid := parse_cli_args([]string{
|
||||
"brolang",
|
||||
"app",
|
||||
"--link",
|
||||
"--c-link",
|
||||
"native.c",
|
||||
"-o",
|
||||
"app.out",
|
||||
"--library-path",
|
||||
"--c-library-path",
|
||||
"vendor/lib",
|
||||
"--library",
|
||||
"--c-library",
|
||||
"thing",
|
||||
"--link",
|
||||
"--c-link",
|
||||
"helper.o",
|
||||
"--c-include-path",
|
||||
"vendor/include",
|
||||
"--c-define",
|
||||
"FEATURE=1",
|
||||
"--target",
|
||||
"aarch64-macos",
|
||||
})
|
||||
defer delete(options.link_arguments)
|
||||
defer delete(options.c_options.include_paths)
|
||||
defer delete(options.c_options.defines)
|
||||
|
||||
testing.expect(t, valid)
|
||||
testing.expect_value(t, options.input_path, "app")
|
||||
@@ -419,6 +427,8 @@ cli_parses_ordered_link_options_and_rejects_invalid_forms :: proc(t: ^testing.T)
|
||||
testing.expect_value(t, options.link_arguments[1].kind, linker.Kind.Library_Path)
|
||||
testing.expect_value(t, options.link_arguments[2].kind, linker.Kind.Library)
|
||||
testing.expect_value(t, options.link_arguments[3].value, "helper.o")
|
||||
testing.expect_value(t, options.c_options.include_paths[0], "vendor/include")
|
||||
testing.expect_value(t, options.c_options.defines[0], "FEATURE=1")
|
||||
testing.expect_value(t, target.name(options.target), "aarch64-macos")
|
||||
|
||||
_, unknown_valid := parse_cli_args([]string{"brolang", "app", "-o", "out", "--unknown", "value"})
|
||||
@@ -426,11 +436,17 @@ cli_parses_ordered_link_options_and_rejects_invalid_forms :: proc(t: ^testing.T)
|
||||
_, duplicate_output_valid := parse_cli_args([]string{"brolang", "app", "-o", "one", "-o", "two"})
|
||||
_, 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"})
|
||||
_, legacy_library_path := parse_cli_args([]string{"brolang", "app", "-o", "out", "--library-path", "vendor/lib"})
|
||||
_, legacy_library := parse_cli_args([]string{"brolang", "app", "-o", "out", "--library", "thing"})
|
||||
testing.expect(t, !unknown_valid)
|
||||
testing.expect(t, !incomplete_valid)
|
||||
testing.expect(t, !duplicate_output_valid)
|
||||
testing.expect(t, !duplicate_empty_output_valid)
|
||||
testing.expect(t, !invalid_target)
|
||||
testing.expect(t, !legacy_link)
|
||||
testing.expect(t, !legacy_library_path)
|
||||
testing.expect(t, !legacy_library)
|
||||
}
|
||||
|
||||
@(test)
|
||||
@@ -1319,6 +1335,44 @@ foreign_function_links_from_c_source :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, state.exit_code, 42)
|
||||
}
|
||||
|
||||
@(test)
|
||||
restricted_c_header_imports_compile_and_link :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-header-import"
|
||||
defer _ = os.remove(output)
|
||||
arguments := []linker.Argument{{kind=.Input, value="examples/interop/header/native.c"}}
|
||||
c_options := cimport.Options{
|
||||
include_paths=[]string{"examples/interop/header/include"},
|
||||
defines=[]string{"BROLANG_FEATURE"},
|
||||
}
|
||||
status := compiler_core.compile_package("examples/interop/header/app", output, arguments, target.DEFAULT, c_options)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
unsupported_c_header_members_diagnose_only_when_referenced :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-header-unsupported"
|
||||
defer _ = os.remove(output)
|
||||
c_options := cimport.Options{include_paths=[]string{"examples/interop/header/include"}}
|
||||
status := compiler_core.compile_package("examples/interop/header_unsupported", output, nil, target.DEFAULT, c_options)
|
||||
testing.expect_value(t, status, 1)
|
||||
state := run_executable(output)
|
||||
testing.expect(t, !state.success)
|
||||
}
|
||||
|
||||
@(test)
|
||||
compatible_c_header_redeclarations_share_one_llvm_declaration :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-header-duplicate"
|
||||
defer _ = os.remove(output)
|
||||
arguments := []linker.Argument{{kind=.Input, value="examples/interop/header/native.c"}}
|
||||
c_options := cimport.Options{include_paths=[]string{"examples/interop/header/include"}}
|
||||
status := compiler_core.compile_package("examples/interop/header_duplicate", output, arguments, target.DEFAULT, c_options)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
interop_foundation_matches_zig_compiled_apple_silicon_c_fixture :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-interop-foundation"
|
||||
@@ -1570,7 +1624,11 @@ backend_translates_link_arguments_without_reordering_them :: proc(t: ^testing.T)
|
||||
{kind=.Library, value="thing"},
|
||||
{kind=.Input, value="helper.o"},
|
||||
}
|
||||
command := backend.build_command("module.ll", "program", arguments)
|
||||
c_options := cimport.Options{
|
||||
include_paths=[]string{"vendor/include"},
|
||||
defines=[]string{"FEATURE=1"},
|
||||
}
|
||||
command := backend.build_command("module.ll", "program", arguments, target.DEFAULT, c_options)
|
||||
defer backend.destroy_command(command)
|
||||
|
||||
expected := []string{
|
||||
@@ -1580,7 +1638,10 @@ backend_translates_link_arguments_without_reordering_them :: proc(t: ^testing.T)
|
||||
"-target",
|
||||
"aarch64-macos",
|
||||
"-Wno-override-module",
|
||||
"-Wno-unused-command-line-argument",
|
||||
"module.ll",
|
||||
"-Ivendor/include",
|
||||
"-DFEATURE=1",
|
||||
"native.c",
|
||||
"-Lvendor/lib",
|
||||
"-lthing",
|
||||
@@ -1594,6 +1655,109 @@ backend_translates_link_arguments_without_reordering_them :: proc(t: ^testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
Fake_Cimport_State :: struct {
|
||||
calls: int,
|
||||
available: bool,
|
||||
infrastructure: bool,
|
||||
saw_options: bool,
|
||||
}
|
||||
|
||||
fake_cimport_backend :: proc(user_data: rawptr, request: cimport.Request, allocator: mem.Allocator) -> cimport.Result {
|
||||
state := (^Fake_Cimport_State)(user_data)
|
||||
state.calls += 1
|
||||
state.saw_options =
|
||||
len(request.include_paths) == 2 &&
|
||||
request.include_paths[0] == "first/include" &&
|
||||
request.include_paths[1] == "second/include" &&
|
||||
len(request.defines) == 2 &&
|
||||
request.defines[0] == "FIRST=1" &&
|
||||
request.defines[1] == "SECOND" &&
|
||||
request.target == target.DEFAULT
|
||||
result := cimport.init_result(allocator)
|
||||
if !state.available {
|
||||
result.infrastructure = state.infrastructure
|
||||
result.error_message = fmt.aprintf("fake importer unavailable", allocator=allocator)
|
||||
return result
|
||||
}
|
||||
append(&result.types, cimport.Type{kind=.C_Int, child=cimport.INVALID_TYPE})
|
||||
append(&result.functions, cimport.Function{
|
||||
name=fmt.aprintf("fake_value", allocator=allocator),
|
||||
result=cimport.Type_Id(0),
|
||||
reason=fmt.aprintf("", allocator=allocator),
|
||||
})
|
||||
result.available = true
|
||||
return result
|
||||
}
|
||||
|
||||
@(test)
|
||||
cimport_backend_is_replaceable :: proc(t: ^testing.T) {
|
||||
state := Fake_Cimport_State{available=true}
|
||||
options := cimport.Options{backend={import_header=fake_cimport_backend, user_data=&state}}
|
||||
result := cimport.import_header(options, "fake.h")
|
||||
defer cimport.destroy_result(&result)
|
||||
|
||||
testing.expect(t, result.available)
|
||||
testing.expect_value(t, state.calls, 1)
|
||||
testing.expect_value(t, len(result.functions), 1)
|
||||
testing.expect_value(t, result.functions[0].name, "fake_value")
|
||||
}
|
||||
|
||||
@(test)
|
||||
loader_injects_and_caches_cimport_backend_per_compilation :: 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)
|
||||
state := Fake_Cimport_State{available=true}
|
||||
options := cimport.Options{
|
||||
include_paths=[]string{"first/include", "second/include"},
|
||||
defines=[]string{"FIRST=1", "SECOND"},
|
||||
backend={import_header=fake_cimport_backend, user_data=&state},
|
||||
}
|
||||
module, loaded := loader.load(
|
||||
"examples/interop/header_cache",
|
||||
&sources,
|
||||
&diagnostics,
|
||||
&symbols,
|
||||
c_options=options,
|
||||
)
|
||||
defer ast.destroy_module(&module)
|
||||
|
||||
testing.expect(t, loaded)
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect_value(t, state.calls, 1)
|
||||
testing.expect(t, state.saw_options)
|
||||
testing.expect_value(t, len(module.imports), 2)
|
||||
testing.expect_value(t, module.imports[0].target, module.imports[1].target)
|
||||
testing.expect_value(t, len(module.functions), 2)
|
||||
}
|
||||
|
||||
@(test)
|
||||
cimport_infrastructure_failure_makes_compilation_unavailable :: 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)
|
||||
state := Fake_Cimport_State{infrastructure=true}
|
||||
options := cimport.Options{backend={import_header=fake_cimport_backend, user_data=&state}}
|
||||
module, loaded := loader.load(
|
||||
"examples/interop/header_cache",
|
||||
&sources,
|
||||
&diagnostics,
|
||||
&symbols,
|
||||
c_options=options,
|
||||
)
|
||||
defer ast.destroy_module(&module)
|
||||
|
||||
testing.expect(t, !loaded)
|
||||
testing.expect_value(t, state.calls, 1)
|
||||
testing.expect(t, len(diagnostics.items) > 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
source_store_owns_buffers_indexes_lines_and_deduplicates_diagnostics :: proc(t: ^testing.T) {
|
||||
store := source.init_store()
|
||||
|
||||
Reference in New Issue
Block a user