fix honey compiler blockers
This commit is contained in:
@@ -48,9 +48,10 @@ Instead of passing these on the command line, a project can describe its build
|
||||
in Brolang itself. `brolang new <project>` creates a project with local `std`
|
||||
and `ffi` copies; `brolang init` does the same for the current directory without
|
||||
overwriting existing files. `brolang build [root]` reads a `config` constant
|
||||
from `root/build.bro` and compiles the program package it names. Without
|
||||
`root`, it searches the current directory and parents for the nearest
|
||||
`build.bro`. Build outputs are written to `root/build/<name>`.
|
||||
from exactly one of `root/build.bro` or `root/build.hon` and compiles the
|
||||
program package it names. Without `root`, it searches the current directory
|
||||
and parents for the nearest build file. Build outputs are written to
|
||||
`root/build/<name>`.
|
||||
|
||||
```bro
|
||||
b :: import "@std/build"
|
||||
@@ -67,15 +68,16 @@ config :: b.BuildConfig{
|
||||
```
|
||||
|
||||
`name` is a plain executable name, and `source` is the program package relative
|
||||
to `build.bro`. The list fields map to the matching C options (`libraries` →
|
||||
to the build file. The list fields map to the matching C options (`libraries` →
|
||||
`-l`, `lib_paths` → `-L`, `includes` → `-I`, `defines` → C defines, `links` →
|
||||
linker inputs) and, like those flags, their paths are relative to the invocation
|
||||
directory. Lists take the address of an array literal; empty lists are written
|
||||
`&[]`. See `examples/build/` for runnable projects.
|
||||
|
||||
Projects can declare tests directly and run them with `brolang test [root]`.
|
||||
The command reads the same `build.bro`, writes `build/<name>-test`, and reuses
|
||||
its C link inputs, libraries, include paths, and defines.
|
||||
The command reads the same build file—exactly one of `build.bro` or
|
||||
`build.hon`—writes `build/<name>-test`, and reuses its C link inputs,
|
||||
libraries, include paths, and defines.
|
||||
|
||||
```bro
|
||||
math :: import "../math"
|
||||
|
||||
+25
-23
@@ -66,10 +66,10 @@ load_build_config :: proc(project_root: string) -> (BuildConfig, bool) {
|
||||
return {}, false
|
||||
}
|
||||
// check needs no `main`: it synthesizes a trap main and emits one benign
|
||||
// "missing main" diagnostic, which is expected for build.bro. Suppress that
|
||||
// one but surface any real errors in build.bro (and fail on them).
|
||||
// "missing main" diagnostic, which is expected for a build config. Suppress
|
||||
// that one but surface any real errors in the build config (and fail on them).
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols, target.DEFAULT, a)
|
||||
if build_bro_has_errors(&diagnostics) {
|
||||
if build_config_has_errors(&diagnostics) {
|
||||
source.print_all(&diagnostics)
|
||||
return {}, false
|
||||
}
|
||||
@@ -82,14 +82,14 @@ project_root_for_command :: proc(root, command: string) -> (string, bool, bool)
|
||||
}
|
||||
project_root, found := find_build_root()
|
||||
if !found {
|
||||
fmt.eprintfln("brolang %s: could not find build.bro in the current directory or any parent", command)
|
||||
fmt.eprintfln("brolang %s: could not find build.bro or build.hon in the current directory or any parent", command)
|
||||
return "", false, false
|
||||
}
|
||||
return project_root, true, true
|
||||
}
|
||||
|
||||
// run_build implements `brolang build [root]`: it reads build.bro and compiles
|
||||
// the configured program package.
|
||||
// run_build implements `brolang build [root]`: it reads the build config and
|
||||
// compiles the configured program package.
|
||||
run_build :: proc(root: string) -> int {
|
||||
project_root, owned, found := project_root_for_command(root, "build")
|
||||
if !found {
|
||||
@@ -161,7 +161,7 @@ valid_output_name :: proc(name: string) -> bool {
|
||||
|
||||
build_output_path :: proc(project_root, name: string, allocator := context.allocator) -> (string, bool) {
|
||||
if !valid_output_name(name) {
|
||||
fmt.eprintfln("build.bro: config 'name' must be a plain executable name, got '%s'", name)
|
||||
fmt.eprintfln("build config: config 'name' must be a plain executable name, got '%s'", name)
|
||||
return "", false
|
||||
}
|
||||
build_dir, dir_error := filepath.join({project_root, "build"}, allocator)
|
||||
@@ -202,15 +202,17 @@ find_build_root_from :: proc(start: string, allocator := context.allocator) -> (
|
||||
current = strings.clone(start, allocator)
|
||||
}
|
||||
for {
|
||||
build_path, build_error := filepath.join({current, "build.bro"}, allocator)
|
||||
if build_error != nil {
|
||||
delete(current, allocator)
|
||||
return "", false
|
||||
}
|
||||
found := os.exists(build_path)
|
||||
delete(build_path, allocator)
|
||||
if found {
|
||||
return current, true
|
||||
for build_file in ([?]string{"build.bro", "build.hon"}) {
|
||||
build_path, build_error := filepath.join({current, build_file}, allocator)
|
||||
if build_error != nil {
|
||||
delete(current, allocator)
|
||||
return "", false
|
||||
}
|
||||
found := os.exists(build_path)
|
||||
delete(build_path, allocator)
|
||||
if found {
|
||||
return current, true
|
||||
}
|
||||
}
|
||||
if current == "/" {
|
||||
delete(current, allocator)
|
||||
@@ -227,10 +229,10 @@ find_build_root_from :: proc(start: string, allocator := context.allocator) -> (
|
||||
}
|
||||
}
|
||||
|
||||
// build_bro_has_errors reports whether checking build.bro produced any diagnostic
|
||||
// other than the benign "missing or unusable main function" (build.bro has no
|
||||
// main by design; that one is emitted with an empty span).
|
||||
build_bro_has_errors :: proc(diagnostics: ^source.Diagnostics) -> bool {
|
||||
// build_config_has_errors reports whether checking a build config produced any
|
||||
// diagnostic other than the benign "missing or unusable main function" (build
|
||||
// configs have no main by design; that one is emitted with an empty span).
|
||||
build_config_has_errors :: proc(diagnostics: ^source.Diagnostics) -> bool {
|
||||
for item in diagnostics.items {
|
||||
if item.span == (source.Span{}) && item.message == "missing or unusable main function" {
|
||||
continue
|
||||
@@ -255,12 +257,12 @@ extract_build_config :: proc(m: ^hir.Module, symbols: ^symbol.Table) -> (BuildCo
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
fmt.eprintln("build.bro: missing top-level 'config' constant")
|
||||
fmt.eprintln("build config: missing top-level 'config' constant")
|
||||
return {}, false
|
||||
}
|
||||
root := unwrap_coercions(m, config_expr)
|
||||
if root == hir.INVALID_EXPR || m.exprs[root].kind != .Struct {
|
||||
fmt.eprintln("build.bro: 'config' must be a BuildConfig{...} literal")
|
||||
fmt.eprintln("build config: 'config' must be a BuildConfig{...} literal")
|
||||
return {}, false
|
||||
}
|
||||
args := m.exprs[root].args
|
||||
@@ -322,7 +324,7 @@ extract_build_config :: proc(m: ^hir.Module, symbols: ^symbol.Table) -> (BuildCo
|
||||
cfg.c_options.defines = defines[:]
|
||||
|
||||
if len(cfg.output_name) == 0 || len(cfg.source_dir) == 0 {
|
||||
fmt.eprintln("build.bro: config requires non-empty 'name' and 'source'")
|
||||
fmt.eprintln("build config: config requires non-empty 'name' and 'source'")
|
||||
destroy_build_config(&cfg)
|
||||
return {}, false
|
||||
}
|
||||
|
||||
@@ -1677,7 +1677,7 @@ is_opaque_struct :: proc(value: Type, store: ^Store) -> bool {
|
||||
size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
#partial switch kind(value, store) {
|
||||
case .Scalar:
|
||||
return u64(bits(value, selected)/8)
|
||||
return u64((bits(value, selected)+7)/8)
|
||||
case .Pointer:
|
||||
return u64(target.pointer_bits(selected)/8)
|
||||
case .Slice:
|
||||
|
||||
+100
-3
@@ -1738,6 +1738,16 @@ layout_builtins_compile_and_run :: proc(t: ^testing.T) {
|
||||
y u8
|
||||
}
|
||||
|
||||
Bool_First :: struct {
|
||||
flag bool
|
||||
byte u8
|
||||
}
|
||||
|
||||
Bool_Last :: struct {
|
||||
byte u8
|
||||
flag bool
|
||||
}
|
||||
|
||||
Opaque :: opaque
|
||||
Color :: enum {
|
||||
red
|
||||
@@ -1755,6 +1765,20 @@ buffer func($T type) [sizeof!(T)]u8 {
|
||||
return data
|
||||
}
|
||||
|
||||
make_bool_first func() Bool_First {
|
||||
value Bool_First = undefined
|
||||
value.flag = true
|
||||
value.byte = 41
|
||||
return value
|
||||
}
|
||||
|
||||
make_bool_last func() Bool_Last {
|
||||
value Bool_Last = undefined
|
||||
value.byte = 42
|
||||
value.flag = true
|
||||
return value
|
||||
}
|
||||
|
||||
main func() i32 {
|
||||
bytes [_]u8 :: buffer(i32)
|
||||
if (needs_usize(SIZE_GLOBAL) != 4) return 1
|
||||
@@ -1771,6 +1795,15 @@ main func() i32 {
|
||||
if (alignof!(Point) != 4) return 12
|
||||
if (sizeof!(UserID) != 4) return 13
|
||||
if (alignof!(UserID) != 4) return 14
|
||||
if (sizeof!(bool) != 1) return 15
|
||||
if (sizeof!(Bool_First) != 2) return 16
|
||||
if (sizeof!(Bool_Last) != 2) return 17
|
||||
first :: make_bool_first()
|
||||
if (!first.flag) return 18
|
||||
if (first.byte != 41) return 19
|
||||
last :: make_bool_last()
|
||||
if (!last.flag) return 20
|
||||
if (last.byte != 42) return 21
|
||||
return 0
|
||||
}
|
||||
`
|
||||
@@ -5560,19 +5593,27 @@ init_project_creates_missing_layout_and_preserves_existing_files :: proc(t: ^tes
|
||||
}
|
||||
|
||||
@(test)
|
||||
build_root_search_finds_nearest_parent_build_file :: proc(t: ^testing.T) {
|
||||
build_hon_is_discovered_from_nested_directory_and_builds :: proc(t: ^testing.T) {
|
||||
root := "/tmp/brolang-test-build-search"
|
||||
nested := "/tmp/brolang-test-build-search/source/nested"
|
||||
build_bro := "/tmp/brolang-test-build-search/build.bro"
|
||||
build_hon := "/tmp/brolang-test-build-search/build.hon"
|
||||
output := "/tmp/brolang-test-build-search/build/brolang-test-build-search"
|
||||
_ = os2.remove_all(root)
|
||||
defer _ = os2.remove_all(root)
|
||||
|
||||
testing.expect(t, init_project(root))
|
||||
testing.expect(t, os.rename(build_bro, build_hon))
|
||||
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"))
|
||||
|
||||
status := compiler_core.run_build(found)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
@@ -10460,6 +10501,62 @@ cross_package_recursive_specialization_reaches_fixed_point :: proc(t: ^testing.T
|
||||
testing.expect(t, os.exists(output))
|
||||
}
|
||||
|
||||
@(test)
|
||||
qualified_concrete_call_in_runtime_for_retains_specialization :: proc(t: ^testing.T) {
|
||||
directory := "/tmp/brolang-test-qualified-concrete-call"
|
||||
ast_directory := "/tmp/brolang-test-qualified-concrete-call/ast"
|
||||
main_path := "/tmp/brolang-test-qualified-concrete-call/main.hon"
|
||||
ast_path := "/tmp/brolang-test-qualified-concrete-call/ast/ast.hon"
|
||||
output := "/tmp/brolang-test-qualified-concrete-call-output"
|
||||
ast_text := `Node :: struct { token usize }
|
||||
Token :: struct { start usize }
|
||||
|
||||
render_literal func(node @Node, tokens []Token, program []u8) ?[]u8 {
|
||||
if (node.token >= tokens.len) return null
|
||||
token :: tokens[node.token]
|
||||
if (token.start >= program.len) return null
|
||||
return program[token.start..token.start+1]
|
||||
}
|
||||
`
|
||||
main_text := `ast :: import "./ast"
|
||||
|
||||
main func() i32 {
|
||||
nodes [1]mut ast.Node = undefined
|
||||
nodes[0].token = 0
|
||||
token_storage [1]mut ast.Token = undefined
|
||||
token_storage[0].start = 0
|
||||
mutable_tokens []mut ast.Token = token_storage[..]
|
||||
program :: "x"
|
||||
for nodes |node| {
|
||||
_ = ast.render_literal(&node, mutable_tokens, program)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`
|
||||
_ = os2.remove_all(directory)
|
||||
defer _ = os2.remove_all(directory)
|
||||
defer _ = os.remove(output)
|
||||
testing.expect(t, os.make_directory(directory) == nil)
|
||||
testing.expect(t, os.make_directory(ast_directory) == nil)
|
||||
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)main_text))
|
||||
testing.expect(t, os.write_entire_file(ast_path, transmute([]byte)ast_text))
|
||||
|
||||
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)
|
||||
module, loaded := loader.load(directory, &sources, &diagnostics, &symbols)
|
||||
defer ast.destroy_module(&module)
|
||||
testing.expect(t, loaded)
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
|
||||
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
imported_main_does_not_satisfy_root_main_requirement :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-package-missing-root-main"
|
||||
|
||||
@@ -101,10 +101,10 @@ print_usage :: proc() {
|
||||
" brolang translate-c|--translate-c <header.h>... [--output-dir <dir>] [--target aarch64-macos] [--c-include-path <dir> | --c-define <name[=value]>]...",
|
||||
)
|
||||
fmt.eprintln(
|
||||
" brolang build [root] (reads root/build.bro; writes root/build/name)",
|
||||
" brolang build [root] (reads exactly one of root/build.bro or root/build.hon; writes root/build/name)",
|
||||
)
|
||||
fmt.eprintln(
|
||||
" brolang test [root] (reads root/build.bro; writes and runs root/build/name-test)",
|
||||
" brolang test [root] (reads exactly one of root/build.bro or root/build.hon; writes and runs root/build/name-test)",
|
||||
)
|
||||
fmt.eprintln(
|
||||
" brolang new <project-path>",
|
||||
|
||||
+5
-4
@@ -1,14 +1,15 @@
|
||||
# Build configuration surface for `brolang build` (v0).
|
||||
#
|
||||
# A project's `build.bro` imports this module and declares a top-level constant
|
||||
# named `config` of type `BuildConfig`. `brolang build [root]` type-checks
|
||||
# build.bro, reads the config, and writes root/build/name.
|
||||
# A project's `build.bro` or `build.hon` imports this module and declares a
|
||||
# top-level constant named `config` of type `BuildConfig`. `brolang build
|
||||
# [root]` accepts exactly one of those files, reads the config, and writes
|
||||
# root/build/name.
|
||||
#
|
||||
# Declarative and literal-only: one executable per build. List fields take an
|
||||
# address-of an array literal (`&["raylib"]`) and default to empty.
|
||||
BuildConfig :: struct {
|
||||
name []u8 # output executable name under root/build
|
||||
source []u8 # program package directory, relative to build.bro
|
||||
source []u8 # program package directory, relative to the build file
|
||||
libraries [][]u8 = &[] # library names to link (-l)
|
||||
lib_paths [][]u8 = &[] # library search directories (-L)
|
||||
includes [][]u8 = &[] # C include directories (-I)
|
||||
|
||||
Reference in New Issue
Block a user