mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
Merge remote-tracking branch 'origin/master' into d3d12-binding-fixes
This commit is contained in:
@@ -39,7 +39,9 @@ jobs:
|
|||||||
make
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Odin issues tests
|
- name: Odin issues tests
|
||||||
run: tests/issues/run.sh
|
run: |
|
||||||
|
cd tests/issues
|
||||||
|
./run.sh
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Odin check examples/all for Linux i386
|
- name: Odin check examples/all for Linux i386
|
||||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||||
@@ -91,7 +93,9 @@ jobs:
|
|||||||
make
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Odin issues tests
|
- name: Odin issues tests
|
||||||
run: tests/issues/run.sh
|
run: |
|
||||||
|
cd tests/issues
|
||||||
|
./run.sh
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Odin check examples/all for Darwin arm64
|
- name: Odin check examples/all for Darwin arm64
|
||||||
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
||||||
@@ -163,7 +167,8 @@ jobs:
|
|||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
call tests\issues\run.bat
|
cd tests\issues
|
||||||
|
call run.bat
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Odin check examples/all for Windows 32bits
|
- name: Odin check examples/all for Windows 32bits
|
||||||
shell: cmd
|
shell: cmd
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2016-2021 Ginger Bill. All rights reserved.
|
Copyright (c) 2016-2022 Ginger Bill. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
all: debug demo
|
all: debug
|
||||||
|
|
||||||
demo:
|
demo:
|
||||||
./odin run examples/demo/demo.odin
|
./odin run examples/demo/demo.odin -file
|
||||||
|
|
||||||
report:
|
report:
|
||||||
./odin report
|
./odin report
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
package container_intrusive_list
|
||||||
|
|
||||||
|
import "core:intrinsics"
|
||||||
|
|
||||||
|
// An intrusive doubly-linked list
|
||||||
|
//
|
||||||
|
// As this is an intrusive container, a `Node` must be embedded in your own
|
||||||
|
// structure which is conventionally called a "link". The use of `push_front`
|
||||||
|
// and `push_back` take the address of this node. Retrieving the data
|
||||||
|
// associated with the node requires finding the relative offset of the node
|
||||||
|
// of the parent structure. The parent type and field name are given to
|
||||||
|
// `iterator_*` procedures, or to the built-in `container_of` procedure.
|
||||||
|
//
|
||||||
|
// This data structure is two-pointers in size:
|
||||||
|
// 8 bytes on 32-bit platforms and 16 bytes on 64-bit platforms
|
||||||
|
List :: struct {
|
||||||
|
head: ^Node,
|
||||||
|
tail: ^Node,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Node :: struct {
|
||||||
|
next, prev: ^Node,
|
||||||
|
}
|
||||||
|
|
||||||
|
push_front :: proc(list: ^List, node: ^Node) {
|
||||||
|
if list.head != nil {
|
||||||
|
list.head.prev = node
|
||||||
|
node.prev, node.next = nil, list.head
|
||||||
|
list.head = node
|
||||||
|
} else {
|
||||||
|
list.head, list.tail = node, node
|
||||||
|
node.prev, node.next = nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
push_back :: proc(list: ^List, node: ^Node) {
|
||||||
|
if list.tail != nil {
|
||||||
|
list.tail.next = node
|
||||||
|
node.prev, node.next = list.tail, nil
|
||||||
|
list.tail = node
|
||||||
|
} else {
|
||||||
|
list.head, list.tail = node, node
|
||||||
|
node.prev, node.next = nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove :: proc(list: ^List, node: ^Node) {
|
||||||
|
if node != nil {
|
||||||
|
if node.next != nil {
|
||||||
|
node.next.prev = node.prev
|
||||||
|
}
|
||||||
|
if node.prev != nil {
|
||||||
|
node.prev.next = node.next
|
||||||
|
}
|
||||||
|
if list.head == node {
|
||||||
|
list.head = node.next
|
||||||
|
}
|
||||||
|
if list.tail == node {
|
||||||
|
list.tail = node.prev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_by_proc :: proc(list: ^List, to_erase: proc(^Node) -> bool) {
|
||||||
|
for node := list.head; node != nil; {
|
||||||
|
next := node.next
|
||||||
|
if to_erase(node) {
|
||||||
|
if node.next != nil {
|
||||||
|
node.next.prev = node.prev
|
||||||
|
}
|
||||||
|
if node.prev != nil {
|
||||||
|
node.prev.next = node.next
|
||||||
|
}
|
||||||
|
if list.head == node {
|
||||||
|
list.head = node.next
|
||||||
|
}
|
||||||
|
if list.tail == node {
|
||||||
|
list.tail = node.prev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
is_empty :: proc(list: ^List) -> bool {
|
||||||
|
return list.head == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pop_front :: proc(list: ^List) -> ^Node {
|
||||||
|
link := list.head
|
||||||
|
if link == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if link.next != nil {
|
||||||
|
link.next.prev = link.prev
|
||||||
|
}
|
||||||
|
if link.prev != nil {
|
||||||
|
link.prev.next = link.next
|
||||||
|
}
|
||||||
|
if link == list.head {
|
||||||
|
list.head = link.next
|
||||||
|
}
|
||||||
|
if link == list.tail {
|
||||||
|
list.tail = link.prev
|
||||||
|
}
|
||||||
|
return link
|
||||||
|
|
||||||
|
}
|
||||||
|
pop_back :: proc(list: ^List) -> ^Node {
|
||||||
|
link := list.tail
|
||||||
|
if link == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if link.next != nil {
|
||||||
|
link.next.prev = link.prev
|
||||||
|
}
|
||||||
|
if link.prev != nil {
|
||||||
|
link.prev.next = link.next
|
||||||
|
}
|
||||||
|
if link == list.head {
|
||||||
|
list.head = link.next
|
||||||
|
}
|
||||||
|
if link == list.tail {
|
||||||
|
list.tail = link.prev
|
||||||
|
}
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Iterator :: struct($T: typeid) {
|
||||||
|
curr: ^Node,
|
||||||
|
offset: uintptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator_head :: proc(list: List, $T: typeid, $field_name: string) -> Iterator(T)
|
||||||
|
where intrinsics.type_has_field(T, field_name),
|
||||||
|
intrinsics.type_field_type(T, field_name) == Node {
|
||||||
|
return {list.head, offset_of_by_string(T, field_name)}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator_tail :: proc(list: List, $T: typeid, $field_name: string) -> Iterator(T)
|
||||||
|
where intrinsics.type_has_field(T, field_name),
|
||||||
|
intrinsics.type_field_type(T, field_name) == Node {
|
||||||
|
return {list.tail, offset_of_by_string(T, field_name)}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator_from_node :: proc(node: ^Node, $T: typeid, $field_name: string) -> Iterator(T)
|
||||||
|
where intrinsics.type_has_field(T, field_name),
|
||||||
|
intrinsics.type_field_type(T, field_name) == Node {
|
||||||
|
return {node, offset_of_by_string(T, field_name)}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterate_next :: proc(it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
||||||
|
node := it.curr
|
||||||
|
if node == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
it.curr = node.next
|
||||||
|
|
||||||
|
return (^T)(uintptr(node) - it.offset), true
|
||||||
|
}
|
||||||
|
|
||||||
|
iterate_prev :: proc(it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
||||||
|
node := it.curr
|
||||||
|
if node == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
it.curr = node.prev
|
||||||
|
|
||||||
|
return (^T)(uintptr(node) - it.offset), true
|
||||||
|
}
|
||||||
@@ -41,6 +41,10 @@ mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
|
|||||||
mem_zero :: proc(ptr: rawptr, len: int) ---
|
mem_zero :: proc(ptr: rawptr, len: int) ---
|
||||||
mem_zero_volatile :: proc(ptr: rawptr, len: int) ---
|
mem_zero_volatile :: proc(ptr: rawptr, len: int) ---
|
||||||
|
|
||||||
|
// prefer [^]T operations if possible
|
||||||
|
ptr_offset :: proc(ptr: ^$T, offset: int) -> ^T ---
|
||||||
|
ptr_sub :: proc(a, b: ^$T) -> int ---
|
||||||
|
|
||||||
unaligned_load :: proc(src: ^$T) -> T ---
|
unaligned_load :: proc(src: ^$T) -> T ---
|
||||||
unaligned_store :: proc(dst: ^$T, val: T) -> T ---
|
unaligned_store :: proc(dst: ^$T, val: T) -> T ---
|
||||||
|
|
||||||
@@ -82,6 +86,7 @@ atomic_store_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) ---
|
|||||||
atomic_load :: proc(dst: ^$T) -> T ---
|
atomic_load :: proc(dst: ^$T) -> T ---
|
||||||
atomic_load_explicit :: proc(dst: ^$T, order: Atomic_Memory_Order) -> T ---
|
atomic_load_explicit :: proc(dst: ^$T, order: Atomic_Memory_Order) -> T ---
|
||||||
|
|
||||||
|
// fetch then operator
|
||||||
atomic_add :: proc(dst; ^$T, val: T) -> T ---
|
atomic_add :: proc(dst; ^$T, val: T) -> T ---
|
||||||
atomic_add_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
atomic_add_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||||
atomic_sub :: proc(dst; ^$T, val: T) -> T ---
|
atomic_sub :: proc(dst; ^$T, val: T) -> T ---
|
||||||
@@ -119,19 +124,20 @@ type_is_string :: proc($T: typeid) -> bool ---
|
|||||||
type_is_typeid :: proc($T: typeid) -> bool ---
|
type_is_typeid :: proc($T: typeid) -> bool ---
|
||||||
type_is_any :: proc($T: typeid) -> bool ---
|
type_is_any :: proc($T: typeid) -> bool ---
|
||||||
|
|
||||||
type_is_endian_platform :: proc($T: typeid) -> bool ---
|
type_is_endian_platform :: proc($T: typeid) -> bool ---
|
||||||
type_is_endian_little :: proc($T: typeid) -> bool ---
|
type_is_endian_little :: proc($T: typeid) -> bool ---
|
||||||
type_is_endian_big :: proc($T: typeid) -> bool ---
|
type_is_endian_big :: proc($T: typeid) -> bool ---
|
||||||
type_is_unsigned :: proc($T: typeid) -> bool ---
|
type_is_unsigned :: proc($T: typeid) -> bool ---
|
||||||
type_is_numeric :: proc($T: typeid) -> bool ---
|
type_is_numeric :: proc($T: typeid) -> bool ---
|
||||||
type_is_ordered :: proc($T: typeid) -> bool ---
|
type_is_ordered :: proc($T: typeid) -> bool ---
|
||||||
type_is_ordered_numeric :: proc($T: typeid) -> bool ---
|
type_is_ordered_numeric :: proc($T: typeid) -> bool ---
|
||||||
type_is_indexable :: proc($T: typeid) -> bool ---
|
type_is_indexable :: proc($T: typeid) -> bool ---
|
||||||
type_is_sliceable :: proc($T: typeid) -> bool ---
|
type_is_sliceable :: proc($T: typeid) -> bool ---
|
||||||
type_is_comparable :: proc($T: typeid) -> bool ---
|
type_is_comparable :: proc($T: typeid) -> bool ---
|
||||||
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
|
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
|
||||||
type_is_dereferenceable :: proc($T: typeid) -> bool ---
|
type_is_dereferenceable :: proc($T: typeid) -> bool ---
|
||||||
type_is_valid_map_key :: proc($T: typeid) -> bool ---
|
type_is_valid_map_key :: proc($T: typeid) -> bool ---
|
||||||
|
type_is_valid_matrix_elements :: proc($T: typeid) -> bool ---
|
||||||
|
|
||||||
type_is_named :: proc($T: typeid) -> bool ---
|
type_is_named :: proc($T: typeid) -> bool ---
|
||||||
type_is_pointer :: proc($T: typeid) -> bool ---
|
type_is_pointer :: proc($T: typeid) -> bool ---
|
||||||
@@ -146,6 +152,7 @@ type_is_enum :: proc($T: typeid) -> bool ---
|
|||||||
type_is_proc :: proc($T: typeid) -> bool ---
|
type_is_proc :: proc($T: typeid) -> bool ---
|
||||||
type_is_bit_set :: proc($T: typeid) -> bool ---
|
type_is_bit_set :: proc($T: typeid) -> bool ---
|
||||||
type_is_simd_vector :: proc($T: typeid) -> bool ---
|
type_is_simd_vector :: proc($T: typeid) -> bool ---
|
||||||
|
type_is_matrix :: proc($T: typeid) -> bool ---
|
||||||
|
|
||||||
type_has_nil :: proc($T: typeid) -> bool ---
|
type_has_nil :: proc($T: typeid) -> bool ---
|
||||||
|
|
||||||
@@ -153,6 +160,7 @@ type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
|||||||
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
|
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
|
||||||
|
|
||||||
type_has_field :: proc($T: typeid, $name: string) -> bool ---
|
type_has_field :: proc($T: typeid, $name: string) -> bool ---
|
||||||
|
type_field_type :: proc($T: typeid, $name: string) -> typeid ---
|
||||||
|
|
||||||
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
||||||
type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
||||||
@@ -160,20 +168,41 @@ type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
|||||||
type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
||||||
type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
||||||
|
|
||||||
|
type_struct_field_count :: proc($T: typeid) -> int where type_is_struct(T) ---
|
||||||
|
|
||||||
type_polymorphic_record_parameter_count :: proc($T: typeid) -> typeid ---
|
type_polymorphic_record_parameter_count :: proc($T: typeid) -> typeid ---
|
||||||
type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V ---
|
type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V ---
|
||||||
|
|
||||||
|
type_is_specialized_polymorphic_record :: proc($T: typeid) -> bool ---
|
||||||
|
type_is_unspecialized_polymorphic_record :: proc($T: typeid) -> bool ---
|
||||||
|
|
||||||
|
type_is_subtype_of :: proc($T, $U: typeid) -> bool ---
|
||||||
|
|
||||||
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
||||||
|
|
||||||
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
|
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
|
||||||
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
|
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
|
||||||
|
|
||||||
|
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||||
|
|
||||||
// WASM targets only
|
// WASM targets only
|
||||||
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
|
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
|
||||||
wasm_memory_size :: proc(index: uintptr) -> int ---
|
wasm_memory_size :: proc(index: uintptr) -> int ---
|
||||||
|
|
||||||
|
|
||||||
|
// Darwin targets only
|
||||||
|
objc_object :: struct{}
|
||||||
|
objc_selector :: struct{}
|
||||||
|
objc_class :: struct{}
|
||||||
|
objc_id :: ^objc_object
|
||||||
|
objc_SEL :: ^objc_selector
|
||||||
|
objc_Class :: ^objc_class
|
||||||
|
|
||||||
|
objc_find_selector :: proc($name: string) -> objc_SEL ---
|
||||||
|
objc_register_selector :: proc($name: string) -> objc_SEL ---
|
||||||
|
objc_find_class :: proc($name: string) -> objc_Class ---
|
||||||
|
objc_register_class :: proc($name: string) -> objc_Class ---
|
||||||
|
|
||||||
// Internal compiler use only
|
// Internal compiler use only
|
||||||
|
|
||||||
__entry_point :: proc() ---
|
__entry_point :: proc() ---
|
||||||
@@ -479,21 +479,21 @@ angle_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
|
|||||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
return math.cos(q.x) * 2
|
return math.acos(q.w) * 2
|
||||||
}
|
}
|
||||||
angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
|
angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
|
||||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
return math.cos(q.x) * 2
|
return math.acos(q.w) * 2
|
||||||
}
|
}
|
||||||
angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
|
angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
|
||||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
return math.cos(q.x) * 2
|
return math.acos(q.w) * 2
|
||||||
}
|
}
|
||||||
angle_from_quaternion :: proc{
|
angle_from_quaternion :: proc{
|
||||||
angle_from_quaternion_f16,
|
angle_from_quaternion_f16,
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
|||||||
do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint) -> (err: Allocator_Error) {
|
do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint) -> (err: Allocator_Error) {
|
||||||
if block.committed - block.used < size {
|
if block.committed - block.used < size {
|
||||||
pmblock := (^Platform_Memory_Block)(block)
|
pmblock := (^Platform_Memory_Block)(block)
|
||||||
base_offset := uint(uintptr(block) - uintptr(pmblock))
|
base_offset := uint(uintptr(pmblock.block.base) - uintptr(pmblock))
|
||||||
platform_total_commit := base_offset + block.used + size
|
platform_total_commit := base_offset + block.used + size
|
||||||
|
|
||||||
assert(pmblock.committed <= pmblock.reserved)
|
assert(pmblock.committed <= pmblock.reserved)
|
||||||
|
|||||||
@@ -5,6 +5,16 @@ import "core:intrinsics"
|
|||||||
@builtin
|
@builtin
|
||||||
Maybe :: union($T: typeid) #maybe {T}
|
Maybe :: union($T: typeid) #maybe {T}
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
|
||||||
|
where intrinsics.type_has_field(T, field_name),
|
||||||
|
intrinsics.type_field_type(T, field_name) == Field_Type {
|
||||||
|
offset :: offset_of_by_string(T, field_name)
|
||||||
|
return (^T)(uintptr(ptr) - offset) if ptr != nil else nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@thread_local global_default_temp_allocator_data: Default_Temp_Allocator
|
@thread_local global_default_temp_allocator_data: Default_Temp_Allocator
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
|
|||||||
@@ -400,30 +400,28 @@ atomic_cond_broadcast :: proc(c: ^Atomic_Cond) {
|
|||||||
//
|
//
|
||||||
// An Atomic_Sema must not be copied after first use
|
// An Atomic_Sema must not be copied after first use
|
||||||
Atomic_Sema :: struct {
|
Atomic_Sema :: struct {
|
||||||
mutex: Atomic_Mutex,
|
count: Futex,
|
||||||
cond: Atomic_Cond,
|
|
||||||
count: int,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_sema_post :: proc(s: ^Atomic_Sema, count := 1) {
|
atomic_sema_post :: proc(s: ^Atomic_Sema, count := 1) {
|
||||||
atomic_mutex_lock(&s.mutex)
|
atomic_add_explicit(&s.count, Futex(count), .Release)
|
||||||
defer atomic_mutex_unlock(&s.mutex)
|
if count == 1 {
|
||||||
|
futex_signal(&s.count)
|
||||||
s.count += count
|
} else {
|
||||||
atomic_cond_signal(&s.cond)
|
futex_broadcast(&s.count)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_sema_wait :: proc(s: ^Atomic_Sema) {
|
atomic_sema_wait :: proc(s: ^Atomic_Sema) {
|
||||||
atomic_mutex_lock(&s.mutex)
|
for {
|
||||||
defer atomic_mutex_unlock(&s.mutex)
|
original_count := atomic_load_explicit(&s.count, .Relaxed)
|
||||||
|
for original_count == 0 {
|
||||||
for s.count == 0 {
|
futex_wait(&s.count, u32(original_count))
|
||||||
atomic_cond_wait(&s.cond, &s.mutex)
|
original_count = s.count
|
||||||
}
|
}
|
||||||
|
if original_count == atomic_compare_exchange_strong_explicit(&s.count, original_count, original_count-1, .Acquire, .Acquire) {
|
||||||
s.count -= 1
|
return
|
||||||
if s.count > 0 {
|
}
|
||||||
atomic_cond_signal(&s.cond)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,25 +429,22 @@ atomic_sema_wait_with_timeout :: proc(s: ^Atomic_Sema, duration: time.Duration)
|
|||||||
if duration <= 0 {
|
if duration <= 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
atomic_mutex_lock(&s.mutex)
|
for {
|
||||||
defer atomic_mutex_unlock(&s.mutex)
|
|
||||||
|
|
||||||
start := time.tick_now()
|
original_count := atomic_load_explicit(&s.count, .Relaxed)
|
||||||
|
for start := time.tick_now(); original_count == 0; /**/ {
|
||||||
|
remaining := duration - time.tick_since(start)
|
||||||
|
if remaining < 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
for s.count == 0 {
|
if !futex_wait_with_timeout(&s.count, u32(original_count), remaining) {
|
||||||
remaining := duration - time.tick_since(start)
|
return false
|
||||||
if remaining < 0 {
|
}
|
||||||
return false
|
original_count = s.count
|
||||||
}
|
}
|
||||||
|
if original_count == atomic_compare_exchange_strong_explicit(&s.count, original_count, original_count-1, .Acquire, .Acquire) {
|
||||||
if !atomic_cond_wait_with_timeout(&s.cond, &s.mutex, remaining) {
|
return true
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.count -= 1
|
|
||||||
if s.count > 0 {
|
|
||||||
atomic_cond_signal(&s.cond)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,53 +6,19 @@ import "core:time"
|
|||||||
|
|
||||||
when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) {
|
when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) {
|
||||||
_Sema :: struct {
|
_Sema :: struct {
|
||||||
count: Futex,
|
atomic: Atomic_Sema,
|
||||||
}
|
}
|
||||||
|
|
||||||
_sema_post :: proc(s: ^Sema, count := 1) {
|
_sema_post :: proc(s: ^Sema, count := 1) {
|
||||||
atomic_add_explicit(&s.impl.count, Futex(count), .Release)
|
atomic_sema_post(&s.impl.atomic, count)
|
||||||
if count == 1 {
|
|
||||||
futex_signal(&s.impl.count)
|
|
||||||
} else {
|
|
||||||
futex_broadcast(&s.impl.count)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_sema_wait :: proc(s: ^Sema) {
|
_sema_wait :: proc(s: ^Sema) {
|
||||||
for {
|
atomic_sema_wait(&s.impl.atomic)
|
||||||
original_count := atomic_load_explicit(&s.impl.count, .Relaxed)
|
|
||||||
for original_count == 0 {
|
|
||||||
futex_wait(&s.impl.count, u32(original_count))
|
|
||||||
original_count = s.impl.count
|
|
||||||
}
|
|
||||||
if original_count == atomic_compare_exchange_strong_explicit(&s.impl.count, original_count, original_count-1, .Acquire, .Acquire) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
|
_sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
|
||||||
if duration <= 0 {
|
return atomic_sema_wait_with_timeout(&s.impl.atomic, duration)
|
||||||
return false
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
|
|
||||||
original_count := atomic_load_explicit(&s.impl.count, .Relaxed)
|
|
||||||
for start := time.tick_now(); original_count == 0; /**/ {
|
|
||||||
remaining := duration - time.tick_since(start)
|
|
||||||
if remaining < 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !futex_wait_with_timeout(&s.impl.count, u32(original_count), remaining) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
original_count = s.impl.count
|
|
||||||
}
|
|
||||||
if original_count == atomic_compare_exchange_strong_explicit(&s.impl.count, original_count, original_count-1, .Acquire, .Acquire) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_Sema :: struct {
|
_Sema :: struct {
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ Pool :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Once initialized, the pool's memory address is not allowed to change until
|
// Once initialized, the pool's memory address is not allowed to change until
|
||||||
// it is destroyed. If thread_count < 1, thread count 1 will be used.
|
// it is destroyed.
|
||||||
//
|
//
|
||||||
// The thread pool requires an allocator which it either owns, or which is thread safe.
|
// The thread pool requires an allocator which it either owns, or which is thread safe.
|
||||||
pool_init :: proc(pool: ^Pool, thread_count: int, allocator: mem.Allocator) {
|
pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
pool.allocator = allocator
|
pool.allocator = allocator
|
||||||
pool.tasks = make([dynamic]Task)
|
pool.tasks = make([dynamic]Task)
|
||||||
@@ -113,9 +113,8 @@ pool_join :: proc(pool: ^Pool) {
|
|||||||
// the thread pool. You can even add tasks from inside other tasks.
|
// the thread pool. You can even add tasks from inside other tasks.
|
||||||
//
|
//
|
||||||
// Each task also needs an allocator which it either owns, or which is thread
|
// Each task also needs an allocator which it either owns, or which is thread
|
||||||
// safe. By default, allocations in the task are disabled by use of the
|
// safe.
|
||||||
// nil_allocator.
|
pool_add_task :: proc(pool: ^Pool, allocator: mem.Allocator, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
|
||||||
pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0, allocator := context.allocator) {
|
|
||||||
sync.guard(&pool.mutex)
|
sync.guard(&pool.mutex)
|
||||||
|
|
||||||
append(&pool.tasks, Task{
|
append(&pool.tasks, Task{
|
||||||
|
|||||||
@@ -1166,7 +1166,8 @@ threading_example :: proc() {
|
|||||||
|
|
||||||
|
|
||||||
for i in 0..<30 {
|
for i in 0..<30 {
|
||||||
thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i)
|
// be mindful of the allocator used for tasks. The allocator needs to be thread safe, or be owned by the task for exclusive use
|
||||||
|
thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i, allocator=context.allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
thread.pool_start(&pool)
|
thread.pool_start(&pool)
|
||||||
|
|||||||
+208
-28
@@ -3,7 +3,6 @@
|
|||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// #if defined(GB_SYSTEM_WINDOWS)
|
// #if defined(GB_SYSTEM_WINDOWS)
|
||||||
// #define DEFAULT_TO_THREADED_CHECKER
|
// #define DEFAULT_TO_THREADED_CHECKER
|
||||||
// #endif
|
// #endif
|
||||||
@@ -198,6 +197,22 @@ enum RelocMode : u8 {
|
|||||||
RelocMode_DynamicNoPIC,
|
RelocMode_DynamicNoPIC,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum BuildPath : u8 {
|
||||||
|
BuildPath_Main_Package, // Input Path to the package directory (or file) we're building.
|
||||||
|
BuildPath_RC, // Input Path for .rc file, can be set with `-resource:`.
|
||||||
|
BuildPath_RES, // Output Path for .res file, generated from previous.
|
||||||
|
BuildPath_Win_SDK_Root, // windows_sdk_root
|
||||||
|
BuildPath_Win_SDK_UM_Lib, // windows_sdk_um_library_path
|
||||||
|
BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path
|
||||||
|
BuildPath_VS_EXE, // vs_exe_path
|
||||||
|
BuildPath_VS_LIB, // vs_library_path
|
||||||
|
|
||||||
|
BuildPath_Output, // Output Path for .exe, .dll, .so, etc. Can be overridden with `-out:`.
|
||||||
|
BuildPath_PDB, // Output Path for .pdb file, can be overridden with `-pdb-name:`.
|
||||||
|
|
||||||
|
BuildPathCOUNT,
|
||||||
|
};
|
||||||
|
|
||||||
// This stores the information for the specify architecture of this build
|
// This stores the information for the specify architecture of this build
|
||||||
struct BuildContext {
|
struct BuildContext {
|
||||||
// Constants
|
// Constants
|
||||||
@@ -226,9 +241,13 @@ struct BuildContext {
|
|||||||
|
|
||||||
bool show_help;
|
bool show_help;
|
||||||
|
|
||||||
|
Array<Path> build_paths; // Contains `Path` objects to output filename, pdb, resource and intermediate files.
|
||||||
|
// BuildPath enum contains the indices of paths we know *before* the work starts.
|
||||||
|
|
||||||
String out_filepath;
|
String out_filepath;
|
||||||
String resource_filepath;
|
String resource_filepath;
|
||||||
String pdb_filepath;
|
String pdb_filepath;
|
||||||
|
|
||||||
bool has_resource;
|
bool has_resource;
|
||||||
String link_flags;
|
String link_flags;
|
||||||
String extra_linker_flags;
|
String extra_linker_flags;
|
||||||
@@ -300,8 +319,6 @@ struct BuildContext {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
gb_global BuildContext build_context = {0};
|
gb_global BuildContext build_context = {0};
|
||||||
|
|
||||||
bool global_warnings_as_errors(void) {
|
bool global_warnings_as_errors(void) {
|
||||||
@@ -605,28 +622,6 @@ bool allow_check_foreign_filepath(void) {
|
|||||||
// is_abs_path
|
// is_abs_path
|
||||||
// has_subdir
|
// has_subdir
|
||||||
|
|
||||||
enum TargetFileValidity : u8 {
|
|
||||||
TargetFileValidity_Invalid,
|
|
||||||
|
|
||||||
TargetFileValidity_Writable_File,
|
|
||||||
TargetFileValidity_No_Write_Permission,
|
|
||||||
TargetFileValidity_Directory,
|
|
||||||
|
|
||||||
TargetTargetFileValidity_COUNT,
|
|
||||||
};
|
|
||||||
|
|
||||||
TargetFileValidity set_output_filename(void) {
|
|
||||||
// Assembles the output filename from build_context information.
|
|
||||||
// Returns `true` if it doesn't exist or is a file.
|
|
||||||
// Returns `false` if a directory or write-protected file.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return TargetFileValidity_Writable_File;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
|
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
|
||||||
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
|
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
|
||||||
|
|
||||||
@@ -973,7 +968,6 @@ char *token_pos_to_string(TokenPos const &pos) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void init_build_context(TargetMetrics *cross_target) {
|
void init_build_context(TargetMetrics *cross_target) {
|
||||||
BuildContext *bc = &build_context;
|
BuildContext *bc = &build_context;
|
||||||
|
|
||||||
@@ -1152,8 +1146,194 @@ void init_build_context(TargetMetrics *cross_target) {
|
|||||||
|
|
||||||
bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
|
bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#undef LINK_FLAG_X64
|
#undef LINK_FLAG_X64
|
||||||
#undef LINK_FLAG_386
|
#undef LINK_FLAG_386
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
|
||||||
|
// NOTE(Jeroen): No longer needed in `main.cpp -> linker_stage`. We now resolve those paths in `init_build_paths`.
|
||||||
|
#include "microsoft_craziness.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate.
|
||||||
|
// We've previously called `parse_build_flags`, so `out_filepath` should be set.
|
||||||
|
bool init_build_paths(String init_filename) {
|
||||||
|
gbAllocator ha = heap_allocator();
|
||||||
|
BuildContext *bc = &build_context;
|
||||||
|
|
||||||
|
// NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index.
|
||||||
|
array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT);
|
||||||
|
|
||||||
|
// [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path.
|
||||||
|
bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename);
|
||||||
|
|
||||||
|
bool produces_output_file = false;
|
||||||
|
if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
|
||||||
|
produces_output_file = true;
|
||||||
|
} else if (bc->command_kind & Command__does_build) {
|
||||||
|
produces_output_file = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!produces_output_file) {
|
||||||
|
// Command doesn't produce output files. We're done.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
if (bc->resource_filepath.len > 0) {
|
||||||
|
bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath);
|
||||||
|
bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath);
|
||||||
|
bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc"));
|
||||||
|
bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bc->pdb_filepath.len > 0) {
|
||||||
|
bc->build_paths[BuildPath_PDB] = path_from_string(ha, bc->pdb_filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) {
|
||||||
|
// NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
|
||||||
|
Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
|
||||||
|
|
||||||
|
if (find_result.windows_sdk_version == 0) {
|
||||||
|
gb_printf_err("Windows SDK not found.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
|
||||||
|
GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
|
||||||
|
|
||||||
|
if (find_result.windows_sdk_root.len > 0) {
|
||||||
|
bc->build_paths[BuildPath_Win_SDK_Root] = path_from_string(ha, find_result.windows_sdk_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (find_result.windows_sdk_um_library_path.len > 0) {
|
||||||
|
bc->build_paths[BuildPath_Win_SDK_UM_Lib] = path_from_string(ha, find_result.windows_sdk_um_library_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (find_result.windows_sdk_ucrt_library_path.len > 0) {
|
||||||
|
bc->build_paths[BuildPath_Win_SDK_UCRT_Lib] = path_from_string(ha, find_result.windows_sdk_ucrt_library_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (find_result.vs_exe_path.len > 0) {
|
||||||
|
bc->build_paths[BuildPath_VS_EXE] = path_from_string(ha, find_result.vs_exe_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (find_result.vs_library_path.len > 0) {
|
||||||
|
bc->build_paths[BuildPath_VS_LIB] = path_from_string(ha, find_result.vs_library_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_free(ha, find_result.windows_sdk_root.text);
|
||||||
|
gb_free(ha, find_result.windows_sdk_um_library_path.text);
|
||||||
|
gb_free(ha, find_result.windows_sdk_ucrt_library_path.text);
|
||||||
|
gb_free(ha, find_result.vs_exe_path.text);
|
||||||
|
gb_free(ha, find_result.vs_library_path.text);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// All the build targets and OSes.
|
||||||
|
String output_extension;
|
||||||
|
|
||||||
|
if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
|
||||||
|
output_extension = STR_LIT("odin-doc");
|
||||||
|
} else if (is_arch_wasm()) {
|
||||||
|
output_extension = STR_LIT("wasm");
|
||||||
|
} else if (build_context.build_mode == BuildMode_Executable) {
|
||||||
|
// By default use a .bin executable extension.
|
||||||
|
output_extension = STR_LIT("bin");
|
||||||
|
|
||||||
|
if (build_context.metrics.os == TargetOs_windows) {
|
||||||
|
output_extension = STR_LIT("exe");
|
||||||
|
} else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
|
||||||
|
output_extension = make_string(nullptr, 0);
|
||||||
|
}
|
||||||
|
} else if (build_context.build_mode == BuildMode_DynamicLibrary) {
|
||||||
|
// By default use a .so shared library extension.
|
||||||
|
output_extension = STR_LIT("so");
|
||||||
|
|
||||||
|
if (build_context.metrics.os == TargetOs_windows) {
|
||||||
|
output_extension = STR_LIT("dll");
|
||||||
|
} else if (build_context.metrics.os == TargetOs_darwin) {
|
||||||
|
output_extension = STR_LIT("dylib");
|
||||||
|
}
|
||||||
|
} else if (build_context.build_mode == BuildMode_Object) {
|
||||||
|
// By default use a .o object extension.
|
||||||
|
output_extension = STR_LIT("o");
|
||||||
|
|
||||||
|
if (build_context.metrics.os == TargetOs_windows) {
|
||||||
|
output_extension = STR_LIT("obj");
|
||||||
|
}
|
||||||
|
} else if (build_context.build_mode == BuildMode_Assembly) {
|
||||||
|
// By default use a .S asm extension.
|
||||||
|
output_extension = STR_LIT("S");
|
||||||
|
} else if (build_context.build_mode == BuildMode_LLVM_IR) {
|
||||||
|
output_extension = STR_LIT("ll");
|
||||||
|
} else {
|
||||||
|
GB_PANIC("Unhandled build mode/target combination.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bc->out_filepath.len > 0) {
|
||||||
|
bc->build_paths[BuildPath_Output] = path_from_string(ha, bc->out_filepath);
|
||||||
|
if (build_context.metrics.os == TargetOs_windows) {
|
||||||
|
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
|
||||||
|
defer (gb_free(ha, output_file.text));
|
||||||
|
if (path_is_directory(bc->build_paths[BuildPath_Output])) {
|
||||||
|
gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
|
||||||
|
return false;
|
||||||
|
} else if (bc->build_paths[BuildPath_Output].ext.len == 0) {
|
||||||
|
gb_printf_err("Output path %.*s must have an appropriate extension.\n", LIT(output_file));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Path output_path;
|
||||||
|
|
||||||
|
if (str_eq(init_filename, str_lit("."))) {
|
||||||
|
// We must name the output file after the current directory.
|
||||||
|
debugf("Output name will be created from current base name %.*s.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
|
||||||
|
String last_element = last_path_element(bc->build_paths[BuildPath_Main_Package].basename);
|
||||||
|
|
||||||
|
if (last_element.len == 0) {
|
||||||
|
gb_printf_err("The output name is created from the last path element. `%.*s` has none. Use `-out:output_name.ext` to set it.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
output_path.basename = copy_string(ha, bc->build_paths[BuildPath_Main_Package].basename);
|
||||||
|
output_path.name = copy_string(ha, last_element);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Init filename was not 'current path'.
|
||||||
|
// Contruct the output name from the path elements as usual.
|
||||||
|
String output_name = remove_directory_from_path(init_filename);
|
||||||
|
output_name = remove_extension_from_path(output_name);
|
||||||
|
output_name = copy_string(ha, string_trim_whitespace(output_name));
|
||||||
|
output_path = path_from_string(ha, output_name);
|
||||||
|
|
||||||
|
// Replace extension.
|
||||||
|
if (output_path.ext.len > 0) {
|
||||||
|
gb_free(ha, output_path.ext.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output_path.ext = copy_string(ha, output_extension);
|
||||||
|
|
||||||
|
bc->build_paths[BuildPath_Output] = output_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have an extension? We might not if the output filename was supplied.
|
||||||
|
if (bc->build_paths[BuildPath_Output].ext.len == 0) {
|
||||||
|
if (build_context.metrics.os == TargetOs_windows || build_context.build_mode != BuildMode_Executable) {
|
||||||
|
bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if output path is a directory.
|
||||||
|
if (path_is_directory(bc->build_paths[BuildPath_Output])) {
|
||||||
|
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
|
||||||
|
defer (gb_free(ha, output_file.text));
|
||||||
|
gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end -
|
|||||||
|
|
||||||
is_type_named,
|
is_type_named,
|
||||||
is_type_pointer,
|
is_type_pointer,
|
||||||
|
is_type_multi_pointer,
|
||||||
is_type_array,
|
is_type_array,
|
||||||
is_type_enumerated_array,
|
is_type_enumerated_array,
|
||||||
is_type_slice,
|
is_type_slice,
|
||||||
@@ -3866,6 +3867,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
case BuiltinProc_type_is_valid_matrix_elements:
|
case BuiltinProc_type_is_valid_matrix_elements:
|
||||||
case BuiltinProc_type_is_named:
|
case BuiltinProc_type_is_named:
|
||||||
case BuiltinProc_type_is_pointer:
|
case BuiltinProc_type_is_pointer:
|
||||||
|
case BuiltinProc_type_is_multi_pointer:
|
||||||
case BuiltinProc_type_is_array:
|
case BuiltinProc_type_is_array:
|
||||||
case BuiltinProc_type_is_enumerated_array:
|
case BuiltinProc_type_is_enumerated_array:
|
||||||
case BuiltinProc_type_is_slice:
|
case BuiltinProc_type_is_slice:
|
||||||
@@ -3926,6 +3928,37 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case BuiltinProc_type_field_type:
|
||||||
|
{
|
||||||
|
Operand op = {};
|
||||||
|
Type *bt = check_type(c, ce->args[0]);
|
||||||
|
Type *type = base_type(bt);
|
||||||
|
if (type == nullptr || type == t_invalid) {
|
||||||
|
error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Operand x = {};
|
||||||
|
check_expr(c, &x, ce->args[1]);
|
||||||
|
|
||||||
|
if (!is_type_string(x.type) || x.mode != Addressing_Constant || x.value.kind != ExactValue_String) {
|
||||||
|
error(ce->args[1], "Expected a const string for field argument");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String field_name = x.value.value_string;
|
||||||
|
|
||||||
|
Selection sel = lookup_field(type, field_name, false);
|
||||||
|
if (sel.index.count == 0) {
|
||||||
|
gbString t = type_to_string(type);
|
||||||
|
error(ce->args[1], "'%.*s' is not a field of type %s", LIT(field_name), t);
|
||||||
|
gb_string_free(t);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
operand->mode = Addressing_Type;
|
||||||
|
operand->type = sel.entity->type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case BuiltinProc_type_is_specialization_of:
|
case BuiltinProc_type_is_specialization_of:
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ BuiltinProc__type_simple_boolean_begin,
|
|||||||
|
|
||||||
BuiltinProc_type_is_named,
|
BuiltinProc_type_is_named,
|
||||||
BuiltinProc_type_is_pointer,
|
BuiltinProc_type_is_pointer,
|
||||||
|
BuiltinProc_type_is_multi_pointer,
|
||||||
BuiltinProc_type_is_array,
|
BuiltinProc_type_is_array,
|
||||||
BuiltinProc_type_is_enumerated_array,
|
BuiltinProc_type_is_enumerated_array,
|
||||||
BuiltinProc_type_is_slice,
|
BuiltinProc_type_is_slice,
|
||||||
@@ -179,6 +180,7 @@ BuiltinProc__type_simple_boolean_begin,
|
|||||||
BuiltinProc__type_simple_boolean_end,
|
BuiltinProc__type_simple_boolean_end,
|
||||||
|
|
||||||
BuiltinProc_type_has_field,
|
BuiltinProc_type_has_field,
|
||||||
|
BuiltinProc_type_field_type,
|
||||||
|
|
||||||
BuiltinProc_type_is_specialization_of,
|
BuiltinProc_type_is_specialization_of,
|
||||||
|
|
||||||
@@ -375,6 +377,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
|||||||
|
|
||||||
{STR_LIT("type_is_named"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("type_is_named"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
{STR_LIT("type_is_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("type_is_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
|
{STR_LIT("type_is_multi_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
{STR_LIT("type_is_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("type_is_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
{STR_LIT("type_is_enumerated_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("type_is_enumerated_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
{STR_LIT("type_is_slice"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("type_is_slice"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
@@ -395,6 +398,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
|||||||
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||||
|
|
||||||
{STR_LIT("type_has_field"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("type_has_field"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
|
{STR_LIT("type_field_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
|
|
||||||
{STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
{STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
|
|
||||||
|
|||||||
+1
-256
@@ -675,262 +675,7 @@ wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "path.cpp"
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
|
||||||
bool path_is_directory(String path) {
|
|
||||||
gbAllocator a = heap_allocator();
|
|
||||||
String16 wstr = string_to_string16(a, path);
|
|
||||||
defer (gb_free(a, wstr.text));
|
|
||||||
|
|
||||||
i32 attribs = GetFileAttributesW(wstr.text);
|
|
||||||
if (attribs < 0) return false;
|
|
||||||
|
|
||||||
return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
bool path_is_directory(String path) {
|
|
||||||
gbAllocator a = heap_allocator();
|
|
||||||
char *copy = cast(char *)copy_string(a, path).text;
|
|
||||||
defer (gb_free(a, copy));
|
|
||||||
|
|
||||||
struct stat s;
|
|
||||||
if (stat(copy, &s) == 0) {
|
|
||||||
return (s.st_mode & S_IFDIR) != 0;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
String path_to_full_path(gbAllocator a, String path) {
|
|
||||||
gbAllocator ha = heap_allocator();
|
|
||||||
char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
|
|
||||||
defer (gb_free(ha, path_c));
|
|
||||||
|
|
||||||
char *fullpath = gb_path_get_full_name(a, path_c);
|
|
||||||
String res = string_trim_whitespace(make_string_c(fullpath));
|
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
|
||||||
for (isize i = 0; i < res.len; i++) {
|
|
||||||
if (res.text[i] == '\\') {
|
|
||||||
res.text[i] = '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct FileInfo {
|
|
||||||
String name;
|
|
||||||
String fullpath;
|
|
||||||
i64 size;
|
|
||||||
bool is_dir;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ReadDirectoryError {
|
|
||||||
ReadDirectory_None,
|
|
||||||
|
|
||||||
ReadDirectory_InvalidPath,
|
|
||||||
ReadDirectory_NotExists,
|
|
||||||
ReadDirectory_Permission,
|
|
||||||
ReadDirectory_NotDir,
|
|
||||||
ReadDirectory_Empty,
|
|
||||||
ReadDirectory_Unknown,
|
|
||||||
|
|
||||||
ReadDirectory_COUNT,
|
|
||||||
};
|
|
||||||
|
|
||||||
i64 get_file_size(String path) {
|
|
||||||
char *c_str = alloc_cstring(heap_allocator(), path);
|
|
||||||
defer (gb_free(heap_allocator(), c_str));
|
|
||||||
|
|
||||||
gbFile f = {};
|
|
||||||
gbFileError err = gb_file_open(&f, c_str);
|
|
||||||
defer (gb_file_close(&f));
|
|
||||||
if (err != gbFileError_None) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return gb_file_size(&f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
|
||||||
ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
|
||||||
GB_ASSERT(fi != nullptr);
|
|
||||||
|
|
||||||
gbAllocator a = heap_allocator();
|
|
||||||
|
|
||||||
while (path.len > 0) {
|
|
||||||
Rune end = path[path.len-1];
|
|
||||||
if (end == '/') {
|
|
||||||
path.len -= 1;
|
|
||||||
} else if (end == '\\') {
|
|
||||||
path.len -= 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.len == 0) {
|
|
||||||
return ReadDirectory_InvalidPath;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
char *c_str = alloc_cstring(a, path);
|
|
||||||
defer (gb_free(a, c_str));
|
|
||||||
|
|
||||||
gbFile f = {};
|
|
||||||
gbFileError file_err = gb_file_open(&f, c_str);
|
|
||||||
defer (gb_file_close(&f));
|
|
||||||
|
|
||||||
switch (file_err) {
|
|
||||||
case gbFileError_Invalid: return ReadDirectory_InvalidPath;
|
|
||||||
case gbFileError_NotExists: return ReadDirectory_NotExists;
|
|
||||||
// case gbFileError_Permission: return ReadDirectory_Permission;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!path_is_directory(path)) {
|
|
||||||
return ReadDirectory_NotDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char *new_path = gb_alloc_array(a, char, path.len+3);
|
|
||||||
defer (gb_free(a, new_path));
|
|
||||||
|
|
||||||
gb_memmove(new_path, path.text, path.len);
|
|
||||||
gb_memmove(new_path+path.len, "/*", 2);
|
|
||||||
new_path[path.len+2] = 0;
|
|
||||||
|
|
||||||
String np = make_string(cast(u8 *)new_path, path.len+2);
|
|
||||||
String16 wstr = string_to_string16(a, np);
|
|
||||||
defer (gb_free(a, wstr.text));
|
|
||||||
|
|
||||||
WIN32_FIND_DATAW file_data = {};
|
|
||||||
HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
|
|
||||||
if (find_file == INVALID_HANDLE_VALUE) {
|
|
||||||
return ReadDirectory_Unknown;
|
|
||||||
}
|
|
||||||
defer (FindClose(find_file));
|
|
||||||
|
|
||||||
array_init(fi, a, 0, 100);
|
|
||||||
|
|
||||||
do {
|
|
||||||
wchar_t *filename_w = file_data.cFileName;
|
|
||||||
i64 size = cast(i64)file_data.nFileSizeLow;
|
|
||||||
size |= (cast(i64)file_data.nFileSizeHigh) << 32;
|
|
||||||
String name = string16_to_string(a, make_string16_c(filename_w));
|
|
||||||
if (name == "." || name == "..") {
|
|
||||||
gb_free(a, name.text);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String filepath = {};
|
|
||||||
filepath.len = path.len+1+name.len;
|
|
||||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
|
||||||
defer (gb_free(a, filepath.text));
|
|
||||||
gb_memmove(filepath.text, path.text, path.len);
|
|
||||||
gb_memmove(filepath.text+path.len, "/", 1);
|
|
||||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
|
||||||
|
|
||||||
FileInfo info = {};
|
|
||||||
info.name = name;
|
|
||||||
info.fullpath = path_to_full_path(a, filepath);
|
|
||||||
info.size = size;
|
|
||||||
info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
||||||
array_add(fi, info);
|
|
||||||
} while (FindNextFileW(find_file, &file_data));
|
|
||||||
|
|
||||||
if (fi->count == 0) {
|
|
||||||
return ReadDirectory_Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ReadDirectory_None;
|
|
||||||
}
|
|
||||||
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
|
|
||||||
|
|
||||||
#include <dirent.h>
|
|
||||||
|
|
||||||
ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
|
||||||
GB_ASSERT(fi != nullptr);
|
|
||||||
|
|
||||||
gbAllocator a = heap_allocator();
|
|
||||||
|
|
||||||
char *c_path = alloc_cstring(a, path);
|
|
||||||
defer (gb_free(a, c_path));
|
|
||||||
|
|
||||||
DIR *dir = opendir(c_path);
|
|
||||||
if (!dir) {
|
|
||||||
switch (errno) {
|
|
||||||
case ENOENT:
|
|
||||||
return ReadDirectory_NotExists;
|
|
||||||
case EACCES:
|
|
||||||
return ReadDirectory_Permission;
|
|
||||||
case ENOTDIR:
|
|
||||||
return ReadDirectory_NotDir;
|
|
||||||
default:
|
|
||||||
// ENOMEM: out of memory
|
|
||||||
// EMFILE: per-process limit on open fds reached
|
|
||||||
// ENFILE: system-wide limit on total open files reached
|
|
||||||
return ReadDirectory_Unknown;
|
|
||||||
}
|
|
||||||
GB_PANIC("unreachable");
|
|
||||||
}
|
|
||||||
|
|
||||||
array_init(fi, a, 0, 100);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
struct dirent *entry = readdir(dir);
|
|
||||||
if (entry == nullptr) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = make_string_c(entry->d_name);
|
|
||||||
if (name == "." || name == "..") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String filepath = {};
|
|
||||||
filepath.len = path.len+1+name.len;
|
|
||||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
|
||||||
defer (gb_free(a, filepath.text));
|
|
||||||
gb_memmove(filepath.text, path.text, path.len);
|
|
||||||
gb_memmove(filepath.text+path.len, "/", 1);
|
|
||||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
|
||||||
filepath.text[filepath.len] = 0;
|
|
||||||
|
|
||||||
|
|
||||||
struct stat dir_stat = {};
|
|
||||||
|
|
||||||
if (stat((char *)filepath.text, &dir_stat)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (S_ISDIR(dir_stat.st_mode)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
i64 size = dir_stat.st_size;
|
|
||||||
|
|
||||||
FileInfo info = {};
|
|
||||||
info.name = name;
|
|
||||||
info.fullpath = path_to_full_path(a, filepath);
|
|
||||||
info.size = size;
|
|
||||||
array_add(fi, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fi->count == 0) {
|
|
||||||
return ReadDirectory_Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ReadDirectory_None;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#error Implement read_directory
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct LoadedFile {
|
struct LoadedFile {
|
||||||
void *handle;
|
void *handle;
|
||||||
|
|||||||
+37
-13
@@ -6273,20 +6273,44 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) {
|
|||||||
#else
|
#else
|
||||||
char *p, *result, *fullpath = NULL;
|
char *p, *result, *fullpath = NULL;
|
||||||
isize len;
|
isize len;
|
||||||
p = realpath(path, NULL);
|
fullpath = realpath(path, NULL);
|
||||||
fullpath = p;
|
|
||||||
if (p == NULL) {
|
if (fullpath == NULL) {
|
||||||
// NOTE(bill): File does not exist
|
// NOTE(Jeroen): Path doesn't exist.
|
||||||
fullpath = cast(char *)path;
|
if (gb_strlen(path) > 0 && path[0] == '/') {
|
||||||
|
// But it is an absolute path, so return as-is.
|
||||||
|
|
||||||
|
fullpath = (char *)path;
|
||||||
|
len = gb_strlen(fullpath) + 1;
|
||||||
|
result = gb_alloc_array(a, char, len + 1);
|
||||||
|
|
||||||
|
gb_memmove(result, fullpath, len);
|
||||||
|
result[len] = 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Appears to be a relative path, so construct an absolute one relative to <cwd>.
|
||||||
|
char cwd[4096];
|
||||||
|
getcwd(&cwd[0], 4096);
|
||||||
|
|
||||||
|
isize path_len = gb_strlen(path);
|
||||||
|
isize cwd_len = gb_strlen(cwd);
|
||||||
|
len = cwd_len + 1 + path_len + 1;
|
||||||
|
result = gb_alloc_array(a, char, len);
|
||||||
|
|
||||||
|
gb_memmove(result, (void *)cwd, cwd_len);
|
||||||
|
result[cwd_len] = '/';
|
||||||
|
|
||||||
|
gb_memmove(result + cwd_len + 1, (void *)path, gb_strlen(path));
|
||||||
|
result[len] = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
len = gb_strlen(fullpath) + 1;
|
||||||
|
result = gb_alloc_array(a, char, len + 1);
|
||||||
|
gb_memmove(result, fullpath, len);
|
||||||
|
result[len] = 0;
|
||||||
|
free(fullpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
len = gb_strlen(fullpath);
|
|
||||||
|
|
||||||
result = gb_alloc_array(a, char, len + 1);
|
|
||||||
gb_memmove(result, fullpath, len);
|
|
||||||
result[len] = 0;
|
|
||||||
free(p);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -967,7 +967,12 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
|
|||||||
}
|
}
|
||||||
|
|
||||||
String lb_filepath_ll_for_module(lbModule *m) {
|
String lb_filepath_ll_for_module(lbModule *m) {
|
||||||
String path = m->gen->output_base;
|
String path = concatenate3_strings(permanent_allocator(),
|
||||||
|
build_context.build_paths[BuildPath_Output].basename,
|
||||||
|
STR_LIT("/"),
|
||||||
|
build_context.build_paths[BuildPath_Output].name
|
||||||
|
);
|
||||||
|
|
||||||
if (m->pkg) {
|
if (m->pkg) {
|
||||||
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
|
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
|
||||||
} else if (USE_SEPARATE_MODULES) {
|
} else if (USE_SEPARATE_MODULES) {
|
||||||
@@ -978,7 +983,12 @@ String lb_filepath_ll_for_module(lbModule *m) {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
String lb_filepath_obj_for_module(lbModule *m) {
|
String lb_filepath_obj_for_module(lbModule *m) {
|
||||||
String path = m->gen->output_base;
|
String path = concatenate3_strings(permanent_allocator(),
|
||||||
|
build_context.build_paths[BuildPath_Output].basename,
|
||||||
|
STR_LIT("/"),
|
||||||
|
build_context.build_paths[BuildPath_Output].name
|
||||||
|
);
|
||||||
|
|
||||||
if (m->pkg) {
|
if (m->pkg) {
|
||||||
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
|
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String init_fullpath = c->parser->init_fullpath;
|
String init_fullpath = c->parser->init_fullpath;
|
||||||
|
|
||||||
if (build_context.out_filepath.len == 0) {
|
if (build_context.out_filepath.len == 0) {
|
||||||
|
|||||||
+72
-80
@@ -46,7 +46,6 @@ gb_global Timings global_timings = {0};
|
|||||||
#include "checker.cpp"
|
#include "checker.cpp"
|
||||||
#include "docs.cpp"
|
#include "docs.cpp"
|
||||||
|
|
||||||
|
|
||||||
#include "llvm_backend.cpp"
|
#include "llvm_backend.cpp"
|
||||||
|
|
||||||
#if defined(GB_SYSTEM_OSX)
|
#if defined(GB_SYSTEM_OSX)
|
||||||
@@ -57,16 +56,8 @@ gb_global Timings global_timings = {0};
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "query_data.cpp"
|
#include "query_data.cpp"
|
||||||
|
|
||||||
|
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
|
||||||
// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
|
|
||||||
#include "microsoft_craziness.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "bug_report.cpp"
|
#include "bug_report.cpp"
|
||||||
|
|
||||||
|
|
||||||
// NOTE(bill): 'name' is used in debugging and profiling modes
|
// NOTE(bill): 'name' is used in debugging and profiling modes
|
||||||
i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
|
i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
|
||||||
isize const cmd_cap = 64<<20; // 64 MiB should be more than enough
|
isize const cmd_cap = 64<<20; // 64 MiB should be more than enough
|
||||||
@@ -130,34 +121,35 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
i32 linker_stage(lbGenerator *gen) {
|
i32 linker_stage(lbGenerator *gen) {
|
||||||
i32 result = 0;
|
i32 result = 0;
|
||||||
Timings *timings = &global_timings;
|
Timings *timings = &global_timings;
|
||||||
|
|
||||||
String output_base = gen->output_base;
|
String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
|
||||||
|
debugf("Linking %.*s\n", LIT(output_filename));
|
||||||
|
|
||||||
|
// TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`.
|
||||||
|
|
||||||
if (is_arch_wasm()) {
|
if (is_arch_wasm()) {
|
||||||
timings_start_section(timings, str_lit("wasm-ld"));
|
timings_start_section(timings, str_lit("wasm-ld"));
|
||||||
|
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
result = system_exec_command_line_app("wasm-ld",
|
result = system_exec_command_line_app("wasm-ld",
|
||||||
"\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
|
"\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
|
||||||
LIT(build_context.ODIN_ROOT),
|
LIT(build_context.ODIN_ROOT),
|
||||||
LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
||||||
#else
|
#else
|
||||||
result = system_exec_command_line_app("wasm-ld",
|
result = system_exec_command_line_app("wasm-ld",
|
||||||
"wasm-ld \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
|
"wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
|
||||||
LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
||||||
#endif
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
|
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
|
||||||
#ifdef GB_SYSTEM_UNIX
|
#if defined(GB_SYSTEM_UNIX)
|
||||||
result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
|
result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
|
||||||
LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
|
||||||
#else
|
#else
|
||||||
gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
|
gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
|
||||||
LIT(target_os_names[build_context.metrics.os]),
|
LIT(target_os_names[build_context.metrics.os]),
|
||||||
@@ -181,28 +173,11 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
gbString lib_str = gb_string_make(heap_allocator(), "");
|
gbString lib_str = gb_string_make(heap_allocator(), "");
|
||||||
defer (gb_string_free(lib_str));
|
defer (gb_string_free(lib_str));
|
||||||
|
|
||||||
char const *output_ext = "exe";
|
|
||||||
gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
|
gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
|
||||||
defer (gb_string_free(link_settings));
|
defer (gb_string_free(link_settings));
|
||||||
|
|
||||||
|
|
||||||
// NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
|
|
||||||
Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
|
|
||||||
|
|
||||||
if (find_result.windows_sdk_version == 0) {
|
|
||||||
gb_printf_err("Windows SDK not found.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (build_context.ignore_microsoft_magic) {
|
|
||||||
find_result = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add library search paths.
|
// Add library search paths.
|
||||||
if (find_result.vs_library_path.len > 0) {
|
if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
|
||||||
GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
|
|
||||||
GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
|
|
||||||
|
|
||||||
String path = {};
|
String path = {};
|
||||||
auto add_path = [&](String path) {
|
auto add_path = [&](String path) {
|
||||||
if (path[path.len-1] == '\\') {
|
if (path[path.len-1] == '\\') {
|
||||||
@@ -210,9 +185,9 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
}
|
}
|
||||||
link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
|
link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
|
||||||
};
|
};
|
||||||
add_path(find_result.windows_sdk_um_library_path);
|
add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
|
||||||
add_path(find_result.windows_sdk_ucrt_library_path);
|
add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
|
||||||
add_path(find_result.vs_library_path);
|
add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -252,14 +227,14 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
|
|
||||||
|
|
||||||
if (build_context.build_mode == BuildMode_DynamicLibrary) {
|
if (build_context.build_mode == BuildMode_DynamicLibrary) {
|
||||||
output_ext = "dll";
|
|
||||||
link_settings = gb_string_append_fmt(link_settings, " /DLL");
|
link_settings = gb_string_append_fmt(link_settings, " /DLL");
|
||||||
} else {
|
} else {
|
||||||
link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
|
link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (build_context.pdb_filepath != "") {
|
if (build_context.pdb_filepath != "") {
|
||||||
link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
|
String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
|
||||||
|
link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (build_context.no_crt) {
|
if (build_context.no_crt) {
|
||||||
@@ -300,13 +275,21 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
|
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
|
||||||
|
defer (gb_free(heap_allocator(), vs_exe_path.text));
|
||||||
|
|
||||||
char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
|
char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
|
||||||
if (!build_context.use_lld) { // msvc
|
if (!build_context.use_lld) { // msvc
|
||||||
if (build_context.has_resource) {
|
if (build_context.has_resource) {
|
||||||
|
String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
|
||||||
|
String res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
|
||||||
|
defer (gb_free(heap_allocator(), rc_path.text));
|
||||||
|
defer (gb_free(heap_allocator(), res_path.text));
|
||||||
|
|
||||||
result = system_exec_command_line_app("msvc-link",
|
result = system_exec_command_line_app("msvc-link",
|
||||||
"\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
|
"\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
|
||||||
LIT(output_base),
|
LIT(res_path),
|
||||||
LIT(build_context.resource_filepath)
|
LIT(rc_path)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
@@ -314,13 +297,13 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result = system_exec_command_line_app("msvc-link",
|
result = system_exec_command_line_app("msvc-link",
|
||||||
"\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
|
"\"%.*slink.exe\" %s \"%.*s\" -OUT:\"%.*s\" %s "
|
||||||
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
||||||
" %.*s "
|
" %.*s "
|
||||||
" %.*s "
|
" %.*s "
|
||||||
" %s "
|
" %s "
|
||||||
"",
|
"",
|
||||||
LIT(find_result.vs_exe_path), object_files, LIT(output_base), LIT(output_base), output_ext,
|
LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
|
||||||
link_settings,
|
link_settings,
|
||||||
subsystem_str,
|
subsystem_str,
|
||||||
LIT(build_context.link_flags),
|
LIT(build_context.link_flags),
|
||||||
@@ -329,13 +312,13 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
result = system_exec_command_line_app("msvc-link",
|
result = system_exec_command_line_app("msvc-link",
|
||||||
"\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s "
|
"\"%.*slink.exe\" %s -OUT:\"%.*s\" %s "
|
||||||
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
||||||
" %.*s "
|
" %.*s "
|
||||||
" %.*s "
|
" %.*s "
|
||||||
" %s "
|
" %s "
|
||||||
"",
|
"",
|
||||||
LIT(find_result.vs_exe_path), object_files, LIT(output_base), output_ext,
|
LIT(vs_exe_path), object_files, LIT(output_filename),
|
||||||
link_settings,
|
link_settings,
|
||||||
subsystem_str,
|
subsystem_str,
|
||||||
LIT(build_context.link_flags),
|
LIT(build_context.link_flags),
|
||||||
@@ -350,13 +333,13 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
|
|
||||||
} else { // lld
|
} else { // lld
|
||||||
result = system_exec_command_line_app("msvc-lld-link",
|
result = system_exec_command_line_app("msvc-lld-link",
|
||||||
"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s "
|
"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
|
||||||
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
||||||
" %.*s "
|
" %.*s "
|
||||||
" %.*s "
|
" %.*s "
|
||||||
" %s "
|
" %s "
|
||||||
"",
|
"",
|
||||||
LIT(build_context.ODIN_ROOT), object_files, LIT(output_base),output_ext,
|
LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
|
||||||
link_settings,
|
link_settings,
|
||||||
subsystem_str,
|
subsystem_str,
|
||||||
LIT(build_context.link_flags),
|
LIT(build_context.link_flags),
|
||||||
@@ -415,7 +398,7 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
} else if (string_ends_with(lib, str_lit(".so"))) {
|
} else if (string_ends_with(lib, str_lit(".so"))) {
|
||||||
// dynamic lib, relative path to executable
|
// dynamic lib, relative path to executable
|
||||||
// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
|
// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
|
||||||
// at runtimeto the executable
|
// at runtime to the executable
|
||||||
lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
|
lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
|
||||||
} else {
|
} else {
|
||||||
// dynamic or static system lib, just link regularly searching system library paths
|
// dynamic or static system lib, just link regularly searching system library paths
|
||||||
@@ -431,9 +414,6 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
|
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlike the Win32 linker code, the output_ext includes the dot, because
|
|
||||||
// typically executable files on *NIX systems don't have extensions.
|
|
||||||
String output_ext = {};
|
|
||||||
gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
|
gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
|
||||||
|
|
||||||
if (build_context.no_crt) {
|
if (build_context.no_crt) {
|
||||||
@@ -461,26 +441,12 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
// correctly this way since all the other dependencies provided implicitly
|
// correctly this way since all the other dependencies provided implicitly
|
||||||
// by the compiler frontend are still needed and most of the command
|
// by the compiler frontend are still needed and most of the command
|
||||||
// line arguments prepared previously are incompatible with ld.
|
// line arguments prepared previously are incompatible with ld.
|
||||||
//
|
|
||||||
// Shared libraries are .dylib on MacOS and .so on Linux.
|
|
||||||
if (build_context.metrics.os == TargetOs_darwin) {
|
|
||||||
output_ext = STR_LIT(".dylib");
|
|
||||||
} else {
|
|
||||||
output_ext = STR_LIT(".so");
|
|
||||||
}
|
|
||||||
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
|
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
|
||||||
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
|
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
|
||||||
} else if (build_context.metrics.os != TargetOs_openbsd) {
|
} else if (build_context.metrics.os != TargetOs_openbsd) {
|
||||||
// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
|
// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
|
||||||
link_settings = gb_string_appendc(link_settings, "-no-pie ");
|
link_settings = gb_string_appendc(link_settings, "-no-pie ");
|
||||||
}
|
}
|
||||||
if (build_context.out_filepath.len > 0) {
|
|
||||||
//NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
|
|
||||||
isize pos = string_extension_position(build_context.out_filepath);
|
|
||||||
if (pos > 0) {
|
|
||||||
output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
|
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
|
||||||
defer (gb_string_free(platform_lib_str));
|
defer (gb_string_free(platform_lib_str));
|
||||||
@@ -507,7 +473,7 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
defer (gb_string_free(link_command_line));
|
defer (gb_string_free(link_command_line));
|
||||||
|
|
||||||
link_command_line = gb_string_appendc(link_command_line, object_files);
|
link_command_line = gb_string_appendc(link_command_line, object_files);
|
||||||
link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s%.*s\" ", LIT(output_base), LIT(output_ext));
|
link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
|
||||||
link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
|
link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
|
||||||
link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
|
link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
|
||||||
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
|
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
|
||||||
@@ -524,9 +490,7 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
if (build_context.ODIN_DEBUG) {
|
if (build_context.ODIN_DEBUG) {
|
||||||
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
|
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
|
||||||
// to the symbols in the object file
|
// to the symbols in the object file
|
||||||
result = system_exec_command_line_app("dsymutil",
|
result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename));
|
||||||
"dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
@@ -1526,6 +1490,10 @@ bool parse_build_flags(Array<String> args) {
|
|||||||
gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
|
gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
|
||||||
bad_flags = true;
|
bad_flags = true;
|
||||||
break;
|
break;
|
||||||
|
} else if (!gb_file_exists((const char *)path.text)) {
|
||||||
|
gb_printf_err("Invalid -resource path %.*s, file does not exist.\n", LIT(path));
|
||||||
|
bad_flags = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
build_context.resource_filepath = substring(path, 0, string_extension_position(path));
|
build_context.resource_filepath = substring(path, 0, string_extension_position(path));
|
||||||
build_context.has_resource = true;
|
build_context.has_resource = true;
|
||||||
@@ -1540,6 +1508,11 @@ bool parse_build_flags(Array<String> args) {
|
|||||||
String path = value.value_string;
|
String path = value.value_string;
|
||||||
path = string_trim_whitespace(path);
|
path = string_trim_whitespace(path);
|
||||||
if (is_build_flag_path_valid(path)) {
|
if (is_build_flag_path_valid(path)) {
|
||||||
|
if (path_is_directory(path)) {
|
||||||
|
gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path));
|
||||||
|
bad_flags = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
// #if defined(GB_SYSTEM_WINDOWS)
|
// #if defined(GB_SYSTEM_WINDOWS)
|
||||||
// String ext = path_extension(path);
|
// String ext = path_extension(path);
|
||||||
// if (ext != ".pdb") {
|
// if (ext != ".pdb") {
|
||||||
@@ -2666,6 +2639,8 @@ int main(int arg_count, char const **arg_ptr) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init_filename = copy_string(permanent_allocator(), init_filename);
|
||||||
|
|
||||||
if (init_filename == "-help" ||
|
if (init_filename == "-help" ||
|
||||||
init_filename == "--help") {
|
init_filename == "--help") {
|
||||||
build_context.show_help = true;
|
build_context.show_help = true;
|
||||||
@@ -2688,6 +2663,12 @@ int main(int arg_count, char const **arg_ptr) {
|
|||||||
gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename));
|
gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename));
|
||||||
gb_printf_err("The `-file` flag tells it to treat a file as a self-contained package.\n");
|
gb_printf_err("The `-file` flag tells it to treat a file as a self-contained package.\n");
|
||||||
return 1;
|
return 1;
|
||||||
|
} else {
|
||||||
|
String const ext = str_lit(".odin");
|
||||||
|
if (!string_ends_with(init_filename, ext)) {
|
||||||
|
gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2709,13 +2690,24 @@ int main(int arg_count, char const **arg_ptr) {
|
|||||||
get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
|
get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr);
|
init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr);
|
||||||
// if (build_context.word_size == 4 && build_context.metrics.os != TargetOs_js) {
|
// if (build_context.word_size == 4 && build_context.metrics.os != TargetOs_js) {
|
||||||
// print_usage_line(0, "%.*s 32-bit is not yet supported for this platform", LIT(args[0]));
|
// print_usage_line(0, "%.*s 32-bit is not yet supported for this platform", LIT(args[0]));
|
||||||
// return 1;
|
// return 1;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// Set and check build paths...
|
||||||
|
if (!init_build_paths(init_filename)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (build_context.show_debug_messages) {
|
||||||
|
for_array(i, build_context.build_paths) {
|
||||||
|
String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
|
||||||
|
debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init_global_thread_pool();
|
init_global_thread_pool();
|
||||||
defer (thread_pool_destroy(&global_thread_pool));
|
defer (thread_pool_destroy(&global_thread_pool));
|
||||||
|
|
||||||
@@ -2732,6 +2724,8 @@ int main(int arg_count, char const **arg_ptr) {
|
|||||||
}
|
}
|
||||||
defer (destroy_parser(parser));
|
defer (destroy_parser(parser));
|
||||||
|
|
||||||
|
// TODO(jeroen): Remove the `init_filename` param.
|
||||||
|
// Let's put that on `build_context.build_paths[0]` instead.
|
||||||
if (parse_packages(parser, init_filename) != ParseFile_None) {
|
if (parse_packages(parser, init_filename) != ParseFile_None) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -2810,16 +2804,14 @@ int main(int arg_count, char const **arg_ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (run_output) {
|
if (run_output) {
|
||||||
|
String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
|
||||||
|
defer (gb_free(heap_allocator(), exe_name.text));
|
||||||
|
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(gen->output_base), LIT(run_args_string));
|
return system_exec_command_line_app("odin run", "%.*s %.*s", LIT(exe_name), LIT(run_args_string));
|
||||||
#else
|
#else
|
||||||
//NOTE(thebirk): This whole thing is a little leaky
|
return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
|
||||||
String output_ext = {};
|
|
||||||
String complete_path = concatenate_strings(permanent_allocator(), gen->output_base, output_ext);
|
|
||||||
complete_path = path_to_full_path(permanent_allocator(), complete_path);
|
|
||||||
return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+394
@@ -0,0 +1,394 @@
|
|||||||
|
/*
|
||||||
|
Path handling utilities.
|
||||||
|
*/
|
||||||
|
String remove_extension_from_path(String const &s) {
|
||||||
|
for (isize i = s.len-1; i >= 0; i--) {
|
||||||
|
if (s[i] == '.') {
|
||||||
|
return substring(s, 0, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
String remove_directory_from_path(String const &s) {
|
||||||
|
isize len = 0;
|
||||||
|
for (isize i = s.len-1; i >= 0; i--) {
|
||||||
|
if (s[i] == '/' ||
|
||||||
|
s[i] == '\\') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
return substring(s, s.len-len, s.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool path_is_directory(String path);
|
||||||
|
|
||||||
|
String directory_from_path(String const &s) {
|
||||||
|
if (path_is_directory(s)) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
isize i = s.len-1;
|
||||||
|
for (; i >= 0; i--) {
|
||||||
|
if (s[i] == '/' ||
|
||||||
|
s[i] == '\\') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i >= 0) {
|
||||||
|
return substring(s, 0, i);
|
||||||
|
}
|
||||||
|
return substring(s, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
bool path_is_directory(String path) {
|
||||||
|
gbAllocator a = heap_allocator();
|
||||||
|
String16 wstr = string_to_string16(a, path);
|
||||||
|
defer (gb_free(a, wstr.text));
|
||||||
|
|
||||||
|
i32 attribs = GetFileAttributesW(wstr.text);
|
||||||
|
if (attribs < 0) return false;
|
||||||
|
|
||||||
|
return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
bool path_is_directory(String path) {
|
||||||
|
gbAllocator a = heap_allocator();
|
||||||
|
char *copy = cast(char *)copy_string(a, path).text;
|
||||||
|
defer (gb_free(a, copy));
|
||||||
|
|
||||||
|
struct stat s;
|
||||||
|
if (stat(copy, &s) == 0) {
|
||||||
|
return (s.st_mode & S_IFDIR) != 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
String path_to_full_path(gbAllocator a, String path) {
|
||||||
|
gbAllocator ha = heap_allocator();
|
||||||
|
char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
|
||||||
|
defer (gb_free(ha, path_c));
|
||||||
|
|
||||||
|
char *fullpath = gb_path_get_full_name(a, path_c);
|
||||||
|
String res = string_trim_whitespace(make_string_c(fullpath));
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
for (isize i = 0; i < res.len; i++) {
|
||||||
|
if (res.text[i] == '\\') {
|
||||||
|
res.text[i] = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return copy_string(a, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Path {
|
||||||
|
String basename;
|
||||||
|
String name;
|
||||||
|
String ext;
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE(Jeroen): Naively turns a Path into a string.
|
||||||
|
String path_to_string(gbAllocator a, Path path) {
|
||||||
|
if (path.basename.len + path.name.len + path.ext.len == 0) {
|
||||||
|
return make_string(nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
isize len = path.basename.len + 1 + path.name.len + 1;
|
||||||
|
if (path.ext.len > 0) {
|
||||||
|
len += path.ext.len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 *str = gb_alloc_array(a, u8, len);
|
||||||
|
|
||||||
|
isize i = 0;
|
||||||
|
gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
|
||||||
|
gb_memmove(str+i, "/", 1); i += 1;
|
||||||
|
gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
|
||||||
|
if (path.ext.len > 0) {
|
||||||
|
gb_memmove(str+i, ".", 1); i += 1;
|
||||||
|
gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
|
||||||
|
}
|
||||||
|
str[i] = 0;
|
||||||
|
|
||||||
|
String res = make_string(str, i);
|
||||||
|
res = string_trim_whitespace(res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
|
||||||
|
String path_to_full_path(gbAllocator a, Path path) {
|
||||||
|
String temp = path_to_string(heap_allocator(), path);
|
||||||
|
defer (gb_free(heap_allocator(), temp.text));
|
||||||
|
|
||||||
|
return path_to_full_path(a, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
|
||||||
|
// and then breaks it into its components to make a Path.
|
||||||
|
Path path_from_string(gbAllocator a, String const &path) {
|
||||||
|
Path res = {};
|
||||||
|
|
||||||
|
if (path.len == 0) return res;
|
||||||
|
|
||||||
|
String fullpath = path_to_full_path(a, path);
|
||||||
|
defer (gb_free(heap_allocator(), fullpath.text));
|
||||||
|
|
||||||
|
res.basename = directory_from_path(fullpath);
|
||||||
|
res.basename = copy_string(a, res.basename);
|
||||||
|
|
||||||
|
if (path_is_directory(fullpath)) {
|
||||||
|
// It's a directory. We don't need to tinker with the name and extension.
|
||||||
|
// It could have a superfluous trailing `/`. Remove it if so.
|
||||||
|
if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
|
||||||
|
res.basename.len--;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
|
||||||
|
res.name = substring(fullpath, name_start, fullpath.len);
|
||||||
|
res.name = remove_extension_from_path(res.name);
|
||||||
|
res.name = copy_string(a, res.name);
|
||||||
|
|
||||||
|
res.ext = path_extension(fullpath, false); // false says not to include the dot.
|
||||||
|
res.ext = copy_string(a, res.ext);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(Jeroen): Takes a path String and returns the last path element.
|
||||||
|
String last_path_element(String const &path) {
|
||||||
|
isize count = 0;
|
||||||
|
u8 * start = (u8 *)(&path.text[path.len - 1]);
|
||||||
|
for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
|
||||||
|
count++;
|
||||||
|
start--;
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
start++; // Advance past the `/` and return the substring.
|
||||||
|
String res = make_string(start, count);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// Must be a root path like `/` or `C:/`, return empty String.
|
||||||
|
return STR_LIT("");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool path_is_directory(Path path) {
|
||||||
|
String path_string = path_to_full_path(heap_allocator(), path);
|
||||||
|
defer (gb_free(heap_allocator(), path_string.text));
|
||||||
|
|
||||||
|
return path_is_directory(path_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileInfo {
|
||||||
|
String name;
|
||||||
|
String fullpath;
|
||||||
|
i64 size;
|
||||||
|
bool is_dir;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ReadDirectoryError {
|
||||||
|
ReadDirectory_None,
|
||||||
|
|
||||||
|
ReadDirectory_InvalidPath,
|
||||||
|
ReadDirectory_NotExists,
|
||||||
|
ReadDirectory_Permission,
|
||||||
|
ReadDirectory_NotDir,
|
||||||
|
ReadDirectory_Empty,
|
||||||
|
ReadDirectory_Unknown,
|
||||||
|
|
||||||
|
ReadDirectory_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
i64 get_file_size(String path) {
|
||||||
|
char *c_str = alloc_cstring(heap_allocator(), path);
|
||||||
|
defer (gb_free(heap_allocator(), c_str));
|
||||||
|
|
||||||
|
gbFile f = {};
|
||||||
|
gbFileError err = gb_file_open(&f, c_str);
|
||||||
|
defer (gb_file_close(&f));
|
||||||
|
if (err != gbFileError_None) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return gb_file_size(&f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||||
|
GB_ASSERT(fi != nullptr);
|
||||||
|
|
||||||
|
gbAllocator a = heap_allocator();
|
||||||
|
|
||||||
|
while (path.len > 0) {
|
||||||
|
Rune end = path[path.len-1];
|
||||||
|
if (end == '/') {
|
||||||
|
path.len -= 1;
|
||||||
|
} else if (end == '\\') {
|
||||||
|
path.len -= 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.len == 0) {
|
||||||
|
return ReadDirectory_InvalidPath;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char *c_str = alloc_cstring(a, path);
|
||||||
|
defer (gb_free(a, c_str));
|
||||||
|
|
||||||
|
gbFile f = {};
|
||||||
|
gbFileError file_err = gb_file_open(&f, c_str);
|
||||||
|
defer (gb_file_close(&f));
|
||||||
|
|
||||||
|
switch (file_err) {
|
||||||
|
case gbFileError_Invalid: return ReadDirectory_InvalidPath;
|
||||||
|
case gbFileError_NotExists: return ReadDirectory_NotExists;
|
||||||
|
// case gbFileError_Permission: return ReadDirectory_Permission;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!path_is_directory(path)) {
|
||||||
|
return ReadDirectory_NotDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *new_path = gb_alloc_array(a, char, path.len+3);
|
||||||
|
defer (gb_free(a, new_path));
|
||||||
|
|
||||||
|
gb_memmove(new_path, path.text, path.len);
|
||||||
|
gb_memmove(new_path+path.len, "/*", 2);
|
||||||
|
new_path[path.len+2] = 0;
|
||||||
|
|
||||||
|
String np = make_string(cast(u8 *)new_path, path.len+2);
|
||||||
|
String16 wstr = string_to_string16(a, np);
|
||||||
|
defer (gb_free(a, wstr.text));
|
||||||
|
|
||||||
|
WIN32_FIND_DATAW file_data = {};
|
||||||
|
HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
|
||||||
|
if (find_file == INVALID_HANDLE_VALUE) {
|
||||||
|
return ReadDirectory_Unknown;
|
||||||
|
}
|
||||||
|
defer (FindClose(find_file));
|
||||||
|
|
||||||
|
array_init(fi, a, 0, 100);
|
||||||
|
|
||||||
|
do {
|
||||||
|
wchar_t *filename_w = file_data.cFileName;
|
||||||
|
i64 size = cast(i64)file_data.nFileSizeLow;
|
||||||
|
size |= (cast(i64)file_data.nFileSizeHigh) << 32;
|
||||||
|
String name = string16_to_string(a, make_string16_c(filename_w));
|
||||||
|
if (name == "." || name == "..") {
|
||||||
|
gb_free(a, name.text);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String filepath = {};
|
||||||
|
filepath.len = path.len+1+name.len;
|
||||||
|
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||||
|
defer (gb_free(a, filepath.text));
|
||||||
|
gb_memmove(filepath.text, path.text, path.len);
|
||||||
|
gb_memmove(filepath.text+path.len, "/", 1);
|
||||||
|
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||||
|
|
||||||
|
FileInfo info = {};
|
||||||
|
info.name = name;
|
||||||
|
info.fullpath = path_to_full_path(a, filepath);
|
||||||
|
info.size = size;
|
||||||
|
info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||||
|
array_add(fi, info);
|
||||||
|
} while (FindNextFileW(find_file, &file_data));
|
||||||
|
|
||||||
|
if (fi->count == 0) {
|
||||||
|
return ReadDirectory_Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReadDirectory_None;
|
||||||
|
}
|
||||||
|
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||||
|
GB_ASSERT(fi != nullptr);
|
||||||
|
|
||||||
|
gbAllocator a = heap_allocator();
|
||||||
|
|
||||||
|
char *c_path = alloc_cstring(a, path);
|
||||||
|
defer (gb_free(a, c_path));
|
||||||
|
|
||||||
|
DIR *dir = opendir(c_path);
|
||||||
|
if (!dir) {
|
||||||
|
switch (errno) {
|
||||||
|
case ENOENT:
|
||||||
|
return ReadDirectory_NotExists;
|
||||||
|
case EACCES:
|
||||||
|
return ReadDirectory_Permission;
|
||||||
|
case ENOTDIR:
|
||||||
|
return ReadDirectory_NotDir;
|
||||||
|
default:
|
||||||
|
// ENOMEM: out of memory
|
||||||
|
// EMFILE: per-process limit on open fds reached
|
||||||
|
// ENFILE: system-wide limit on total open files reached
|
||||||
|
return ReadDirectory_Unknown;
|
||||||
|
}
|
||||||
|
GB_PANIC("unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
array_init(fi, a, 0, 100);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
struct dirent *entry = readdir(dir);
|
||||||
|
if (entry == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = make_string_c(entry->d_name);
|
||||||
|
if (name == "." || name == "..") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String filepath = {};
|
||||||
|
filepath.len = path.len+1+name.len;
|
||||||
|
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||||
|
defer (gb_free(a, filepath.text));
|
||||||
|
gb_memmove(filepath.text, path.text, path.len);
|
||||||
|
gb_memmove(filepath.text+path.len, "/", 1);
|
||||||
|
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||||
|
filepath.text[filepath.len] = 0;
|
||||||
|
|
||||||
|
|
||||||
|
struct stat dir_stat = {};
|
||||||
|
|
||||||
|
if (stat((char *)filepath.text, &dir_stat)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISDIR(dir_stat.st_mode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 size = dir_stat.st_size;
|
||||||
|
|
||||||
|
FileInfo info = {};
|
||||||
|
info.name = name;
|
||||||
|
info.fullpath = path_to_full_path(a, filepath);
|
||||||
|
info.size = size;
|
||||||
|
array_add(fi, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fi->count == 0) {
|
||||||
|
return ReadDirectory_Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReadDirectory_None;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error Implement read_directory
|
||||||
|
#endif
|
||||||
|
|
||||||
+2
-35
@@ -245,15 +245,14 @@ gb_inline isize string_extension_position(String const &str) {
|
|||||||
return dot_pos;
|
return dot_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
String path_extension(String const &str) {
|
String path_extension(String const &str, bool include_dot = true) {
|
||||||
isize pos = string_extension_position(str);
|
isize pos = string_extension_position(str);
|
||||||
if (pos < 0) {
|
if (pos < 0) {
|
||||||
return make_string(nullptr, 0);
|
return make_string(nullptr, 0);
|
||||||
}
|
}
|
||||||
return substring(str, pos, str.len);
|
return substring(str, include_dot ? pos : pos + 1, str.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String string_trim_whitespace(String str) {
|
String string_trim_whitespace(String str) {
|
||||||
while (str.len > 0 && rune_is_whitespace(str[str.len-1])) {
|
while (str.len > 0 && rune_is_whitespace(str[str.len-1])) {
|
||||||
str.len--;
|
str.len--;
|
||||||
@@ -299,38 +298,6 @@ String filename_from_path(String s) {
|
|||||||
return make_string(nullptr, 0);
|
return make_string(nullptr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
String remove_extension_from_path(String const &s) {
|
|
||||||
for (isize i = s.len-1; i >= 0; i--) {
|
|
||||||
if (s[i] == '.') {
|
|
||||||
return substring(s, 0, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
String remove_directory_from_path(String const &s) {
|
|
||||||
isize len = 0;
|
|
||||||
for (isize i = s.len-1; i >= 0; i--) {
|
|
||||||
if (s[i] == '/' ||
|
|
||||||
s[i] == '\\') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
len += 1;
|
|
||||||
}
|
|
||||||
return substring(s, s.len-len, s.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
String directory_from_path(String const &s) {
|
|
||||||
isize i = s.len-1;
|
|
||||||
for (; i >= 0; i--) {
|
|
||||||
if (s[i] == '/' ||
|
|
||||||
s[i] == '\\') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return substring(s, 0, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
String concatenate_strings(gbAllocator a, String const &x, String const &y) {
|
String concatenate_strings(gbAllocator a, String const &x, String const &y) {
|
||||||
isize len = x.len+y.len;
|
isize len = x.len+y.len;
|
||||||
u8 *data = gb_alloc_array(a, u8, len+1);
|
u8 *data = gb_alloc_array(a, u8, len+1);
|
||||||
|
|||||||
+14
-14
@@ -8,39 +8,39 @@ download_test_assets:
|
|||||||
$(PYTHON) download_assets.py
|
$(PYTHON) download_assets.py
|
||||||
|
|
||||||
image_test:
|
image_test:
|
||||||
$(ODIN) run image/test_core_image.odin -file
|
$(ODIN) run image/test_core_image.odin -file -out:test_core_image
|
||||||
|
|
||||||
compress_test:
|
compress_test:
|
||||||
$(ODIN) run compress/test_core_compress.odin -file
|
$(ODIN) run compress/test_core_compress.odin -file -out:test_core_compress
|
||||||
|
|
||||||
strings_test:
|
strings_test:
|
||||||
$(ODIN) run strings/test_core_strings.odin -file
|
$(ODIN) run strings/test_core_strings.odin -file -out:test_core_strings
|
||||||
|
|
||||||
hash_test:
|
hash_test:
|
||||||
$(ODIN) run hash -out=test_hash -o:speed -no-bounds-check
|
$(ODIN) run hash -o:speed -no-bounds-check -out:test_hash
|
||||||
|
|
||||||
crypto_test:
|
crypto_test:
|
||||||
$(ODIN) run crypto -out=test_crypto_hash -o:speed -no-bounds-check
|
$(ODIN) run crypto -o:speed -no-bounds-check -out:test_crypto_hash
|
||||||
|
|
||||||
noise_test:
|
noise_test:
|
||||||
$(ODIN) run math/noise -out=test_noise
|
$(ODIN) run math/noise -out:test_noise
|
||||||
|
|
||||||
encoding_test:
|
encoding_test:
|
||||||
$(ODIN) run encoding/hxa -out=test_hxa -collection:tests=..
|
$(ODIN) run encoding/hxa -collection:tests=.. -out:test_hxa
|
||||||
$(ODIN) run encoding/json -out=test_json
|
$(ODIN) run encoding/json -out:test_json
|
||||||
$(ODIN) run encoding/varint -out=test_varint
|
$(ODIN) run encoding/varint -out:test_varint
|
||||||
|
|
||||||
math_test:
|
math_test:
|
||||||
$(ODIN) run math/test_core_math.odin -out=test_core_math -file -collection:tests=..
|
$(ODIN) run math/test_core_math.odin -file -collection:tests=.. -out:test_core_math
|
||||||
|
|
||||||
linalg_glsl_math_test:
|
linalg_glsl_math_test:
|
||||||
$(ODIN) run math/linalg/glsl/test_linalg_glsl_math.odin -file -out=test_linalg_glsl_math -collection:tests=..
|
$(ODIN) run math/linalg/glsl/test_linalg_glsl_math.odin -file -collection:tests=.. -out:test_linalg_glsl_math
|
||||||
|
|
||||||
filepath_test:
|
filepath_test:
|
||||||
$(ODIN) run path/filepath/test_core_filepath.odin -file -out=test_core_filepath -collection:tests=..
|
$(ODIN) run path/filepath/test_core_filepath.odin -file -collection:tests=.. -out:test_core_filepath
|
||||||
|
|
||||||
reflect_test:
|
reflect_test:
|
||||||
$(ODIN) run reflect/test_core_reflect.odin -file -out=test_core_reflect -collection:tests=..
|
$(ODIN) run reflect/test_core_reflect.odin -file -collection:tests=.. -out:test_core_reflect
|
||||||
|
|
||||||
os_exit_test:
|
os_exit_test:
|
||||||
$(ODIN) run os/test_core_os_exit.odin -file -out=test_core_os_exit && exit 1 || exit 0
|
$(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0
|
||||||
+14
-14
@@ -5,61 +5,61 @@ python3 download_assets.py
|
|||||||
echo ---
|
echo ---
|
||||||
echo Running core:image tests
|
echo Running core:image tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run image %COMMON%
|
%PATH_TO_ODIN% run image %COMMON% -out:test_core_image.exe
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:compress tests
|
echo Running core:compress tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run compress %COMMON%
|
%PATH_TO_ODIN% run compress %COMMON% -out:test_core_compress.exe
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:strings tests
|
echo Running core:strings tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run strings %COMMON%
|
%PATH_TO_ODIN% run strings %COMMON% -out:test_core_strings.exe
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:hash tests
|
echo Running core:hash tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run hash %COMMON% -o:size
|
%PATH_TO_ODIN% run hash %COMMON% -o:size -out:test_core_hash.exe
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:odin tests
|
echo Running core:odin tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run odin %COMMON% -o:size
|
%PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_core_odin.exe
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:crypto hash tests
|
echo Running core:crypto hash tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run crypto %COMMON%
|
%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto_hash.exe
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:encoding tests
|
echo Running core:encoding tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run encoding/hxa %COMMON%
|
%PATH_TO_ODIN% run encoding/hxa %COMMON% -out:test_hxa.exe
|
||||||
%PATH_TO_ODIN% run encoding/json %COMMON%
|
%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json.exe
|
||||||
%PATH_TO_ODIN% run encoding/varint %COMMON%
|
%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint.exe
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:math/noise tests
|
echo Running core:math/noise tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run math/noise %COMMON%
|
%PATH_TO_ODIN% run math/noise %COMMON% -out:test_noise.exe
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:math tests
|
echo Running core:math tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run math %COMMON%
|
%PATH_TO_ODIN% run math %COMMON% -out:test_core_math.exe
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:math/linalg/glsl tests
|
echo Running core:math/linalg/glsl tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run math/linalg/glsl %COMMON%
|
%PATH_TO_ODIN% run math/linalg/glsl %COMMON% -out:test_linalg_glsl.exe
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:path/filepath tests
|
echo Running core:path/filepath tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run path/filepath %COMMON%
|
%PATH_TO_ODIN% run path/filepath %COMMON% -out:test_core_filepath.exe
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:reflect tests
|
echo Running core:reflect tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run reflect %COMMON%
|
%PATH_TO_ODIN% run reflect %COMMON% -out:test_core_reflect.exe
|
||||||
@@ -5,8 +5,9 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
|
TEST_SUITES = ['PNG', 'XML']
|
||||||
DOWNLOAD_BASE_PATH = "assets/{}"
|
DOWNLOAD_BASE_PATH = "assets/{}"
|
||||||
ASSETS_BASE_URL = "https://raw.githubusercontent.com/Kelimion/compress-odin/master/tests/assets/{}/{}"
|
ASSETS_BASE_URL = "https://raw.githubusercontent.com/odin-lang/test-assets/master/{}/{}"
|
||||||
PNG_IMAGES = [
|
PNG_IMAGES = [
|
||||||
"basi0g01.png", "basi0g02.png", "basi0g04.png", "basi0g08.png", "basi0g16.png", "basi2c08.png",
|
"basi0g01.png", "basi0g02.png", "basi0g04.png", "basi0g08.png", "basi0g16.png", "basi2c08.png",
|
||||||
"basi2c16.png", "basi3p01.png", "basi3p02.png", "basi3p04.png", "basi3p08.png", "basi4a08.png",
|
"basi2c16.png", "basi3p01.png", "basi3p02.png", "basi3p04.png", "basi3p08.png", "basi4a08.png",
|
||||||
@@ -73,25 +74,27 @@ def try_download_and_unpack_zip(suite):
|
|||||||
print("Could not extract ZIP file")
|
print("Could not extract ZIP file")
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("Downloading PNG assets")
|
for suite in TEST_SUITES:
|
||||||
|
print("Downloading {} assets".format(suite))
|
||||||
|
|
||||||
# Make PNG assets path
|
# Make assets path
|
||||||
try:
|
try:
|
||||||
path = DOWNLOAD_BASE_PATH.format("PNG")
|
path = DOWNLOAD_BASE_PATH.format(suite)
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
except FileExistsError:
|
except FileExistsError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Try downloading and unpacking the assets
|
||||||
|
r = try_download_and_unpack_zip(suite)
|
||||||
|
if r is not None:
|
||||||
|
return r
|
||||||
|
|
||||||
|
# We could fall back on downloading the PNG files individually, but it's slow
|
||||||
|
print("Done downloading {} assets.".format(suite))
|
||||||
|
|
||||||
# Try downloading and unpacking the PNG assets
|
|
||||||
r = try_download_and_unpack_zip("PNG")
|
|
||||||
if r is not None:
|
|
||||||
return r
|
|
||||||
|
|
||||||
# We could fall back on downloading the PNG files individually, but it's slow
|
|
||||||
|
|
||||||
print("Done downloading PNG assets")
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ set PATH_TO_ODIN==..\..\..\..\odin
|
|||||||
set TEST_ARGS=-fast-tests
|
set TEST_ARGS=-fast-tests
|
||||||
set TEST_ARGS=-no-random
|
set TEST_ARGS=-no-random
|
||||||
set TEST_ARGS=
|
set TEST_ARGS=
|
||||||
set OUT_NAME=math_big_test_library
|
set OUT_NAME=math_big_test_library.dll
|
||||||
set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style
|
set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style
|
||||||
echo ---
|
echo ---
|
||||||
echo Running core:math/big tests
|
echo Running core:math/big tests
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
if not exist "tests\issues\build\" mkdir tests\issues\build
|
if not exist "build\" mkdir build
|
||||||
|
|
||||||
set COMMON=-collection:tests=tests -out:tests\issues\build\test_issue
|
set COMMON=-collection:tests=.. -out:build\test_issue.exe
|
||||||
|
|
||||||
@echo on
|
@echo on
|
||||||
|
|
||||||
.\odin build tests\issues\test_issue_829.odin %COMMON% -file
|
..\..\odin build test_issue_829.odin %COMMON% -file
|
||||||
tests\issues\build\test_issue
|
build\test_issue
|
||||||
|
|
||||||
.\odin build tests\issues\test_issue_1592.odin %COMMON% -file
|
..\..\odin build test_issue_1592.odin %COMMON% -file
|
||||||
tests\issues\build\test_issue
|
build\test_issue
|
||||||
|
|
||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
rmdir /S /Q tests\issues\build
|
rmdir /S /Q build
|
||||||
|
|||||||
+8
-8
@@ -1,18 +1,18 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
mkdir -p tests/issues/build
|
mkdir -p build
|
||||||
|
ODIN=../../odin
|
||||||
COMMON="-collection:tests=tests -out:tests/issues/build/test_issue"
|
COMMON="-collection:tests=.. -out:build/test_issue"
|
||||||
|
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
./odin build tests/issues/test_issue_829.odin $COMMON -file
|
$ODIN build test_issue_829.odin $COMMON -file
|
||||||
tests/issues/build/test_issue
|
./build/test_issue
|
||||||
|
|
||||||
./odin build tests/issues/test_issue_1592.odin $COMMON -file
|
$ODIN build test_issue_1592.odin $COMMON -file
|
||||||
tests/issues/build/test_issue
|
./build/test_issue
|
||||||
|
|
||||||
set +x
|
set +x
|
||||||
|
|
||||||
rm -rf tests/issues/build
|
rm -rf build
|
||||||
|
|||||||
Vendored
+1
-1
@@ -10,4 +10,4 @@ endif
|
|||||||
all: botan_test
|
all: botan_test
|
||||||
|
|
||||||
botan_test:
|
botan_test:
|
||||||
$(ODIN) run botan -out=botan_hash -o:speed -no-bounds-check $(ODINFLAGS)
|
$(ODIN) run botan -o:speed -no-bounds-check $(ODINFLAGS) -out=vendor_botan
|
||||||
|
|||||||
Vendored
+2
-2
@@ -5,9 +5,9 @@ set PATH_TO_ODIN==..\..\odin
|
|||||||
echo ---
|
echo ---
|
||||||
echo Running vendor:botan tests
|
echo Running vendor:botan tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run botan %COMMON%
|
%PATH_TO_ODIN% run botan %COMMON% -out:vendor_botan.exe
|
||||||
|
|
||||||
echo ---
|
echo ---
|
||||||
echo Running vendor:glfw tests
|
echo Running vendor:glfw tests
|
||||||
echo ---
|
echo ---
|
||||||
%PATH_TO_ODIN% run glfw %COMMON%
|
%PATH_TO_ODIN% run glfw %COMMON% -out:vendor_glfw.exe
|
||||||
@@ -626,6 +626,9 @@ with open("../core.odin", 'w', encoding='utf-8') as f:
|
|||||||
f.write(BASE)
|
f.write(BASE)
|
||||||
f.write("""
|
f.write("""
|
||||||
API_VERSION_1_0 :: (1<<22) | (0<<12) | (0)
|
API_VERSION_1_0 :: (1<<22) | (0<<12) | (0)
|
||||||
|
API_VERSION_1_1 :: (1<<22) | (1<<12) | (0)
|
||||||
|
API_VERSION_1_2 :: (1<<22) | (2<<12) | (0)
|
||||||
|
API_VERSION_1_3 :: (1<<22) | (3<<12) | (0)
|
||||||
|
|
||||||
MAKE_VERSION :: proc(major, minor, patch: u32) -> u32 {
|
MAKE_VERSION :: proc(major, minor, patch: u32) -> u32 {
|
||||||
return (major<<22) | (minor<<12) | (patch)
|
return (major<<22) | (minor<<12) | (patch)
|
||||||
|
|||||||
Vendored
+3
@@ -3,6 +3,9 @@
|
|||||||
//
|
//
|
||||||
package vulkan
|
package vulkan
|
||||||
API_VERSION_1_0 :: (1<<22) | (0<<12) | (0)
|
API_VERSION_1_0 :: (1<<22) | (0<<12) | (0)
|
||||||
|
API_VERSION_1_1 :: (1<<22) | (1<<12) | (0)
|
||||||
|
API_VERSION_1_2 :: (1<<22) | (2<<12) | (0)
|
||||||
|
API_VERSION_1_3 :: (1<<22) | (3<<12) | (0)
|
||||||
|
|
||||||
MAKE_VERSION :: proc(major, minor, patch: u32) -> u32 {
|
MAKE_VERSION :: proc(major, minor, patch: u32) -> u32 {
|
||||||
return (major<<22) | (minor<<12) | (patch)
|
return (major<<22) | (minor<<12) | (patch)
|
||||||
|
|||||||
Reference in New Issue
Block a user