diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index dbeff51d4..3dad2fdbc 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -383,12 +383,23 @@ _make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, ali return } -// `make_map` allocates and initializes a map. Like `new`, the first argument is a type, not a value. +// `make_map` initializes a map with an allocator. Like `new`, the first argument is a type, not a value. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. // // Note: Prefer using the procedure group `make`. @(builtin, require_results) -make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1< (m: T, err: Allocator_Error) #optional_allocator_error { +make_map :: proc($T: typeid/map[$K]$E, allocator := context.allocator) -> (m: T) { + m.allocator = allocator + return m +} + +// `make_map_cap` initializes a map with an allocator and allocates space using `capacity`. +// Like `new`, the first argument is a type, not a value. +// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. +// +// Note: Prefer using the procedure group `make`. +@(builtin, require_results) +make_map_cap :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1< (m: T, err: Allocator_Error) #optional_allocator_error { make_map_expr_error_loc(loc, capacity) context.allocator = allocator @@ -425,6 +436,7 @@ make :: proc{ make_dynamic_array_len, make_dynamic_array_len_cap, make_map, + make_map_cap, make_multi_pointer, make_soa_slice, diff --git a/build.bat b/build.bat index 33dd53cb8..55c71ca9f 100644 --- a/build.bat +++ b/build.bat @@ -19,8 +19,8 @@ if "%VSCMD_ARG_TGT_ARCH%" neq "x64" ( ) ) -for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime ( - set CURR_DATE_TIME=%%j +for /f %%i in ('powershell get-date -format "{yyyyMMdd}"') do ( + set CURR_DATE_TIME=%%i ) set curr_year=%CURR_DATE_TIME:~0,4% set curr_month=%CURR_DATE_TIME:~4,2% @@ -70,6 +70,7 @@ set rc_flags=-nologo ^ -DV1=%V1% -DV2=%V2% -DV3=%V3% -DV4=%V4% ^ -DVF=%odin_version_full% -DNIGHTLY=%nightly% +where /Q git.exe || goto skip_git_hash if not exist .git\ goto skip_git_hash for /f "tokens=1,2" %%i IN ('git show "--pretty=%%cd %%h" "--date=format:%%Y-%%m" --no-patch --no-notes HEAD') do ( set odin_version_raw=dev-%%i diff --git a/build_odin.sh b/build_odin.sh index f283dc441..c06004ea8 100755 --- a/build_odin.sh +++ b/build_odin.sh @@ -130,7 +130,7 @@ build_odin() { EXTRAFLAGS="-O3" ;; release-native) - if [ "$OS_ARCH" = "arm64" ]; then + if [ "$OS_ARCH" = "arm64" ] || [ "$OS_ARCH" = "aarch64" ]; then # Use preferred flag for Arm (ie arm64 / aarch64 / etc) EXTRAFLAGS="-O3 -mcpu=native" else diff --git a/core/os/os2/allocators.odin b/core/os/os2/allocators.odin index a205cae48..864532850 100644 --- a/core/os/os2/allocators.odin +++ b/core/os/os2/allocators.odin @@ -62,8 +62,8 @@ TEMP_ALLOCATOR_GUARD_END :: proc(temp: runtime.Arena_Temp, loc := #caller_locati @(deferred_out=TEMP_ALLOCATOR_GUARD_END) TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) { - tmp := temp_allocator_temp_begin(loc) global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT + tmp := temp_allocator_temp_begin(loc) return tmp, loc } diff --git a/core/os/os2/errors_linux.odin b/core/os/os2/errors_linux.odin index 09492110d..a7556c306 100644 --- a/core/os/os2/errors_linux.odin +++ b/core/os/os2/errors_linux.odin @@ -162,6 +162,8 @@ _get_platform_error :: proc(errno: linux.Errno) -> Error { return .Invalid_File case .ENOMEM: return .Out_Of_Memory + case .ENOSYS: + return .Unsupported } return Platform_Error(i32(errno)) diff --git a/core/os/os2/errors_posix.odin b/core/os/os2/errors_posix.odin index 59f0ba5f1..0b5876c0b 100644 --- a/core/os/os2/errors_posix.odin +++ b/core/os/os2/errors_posix.odin @@ -26,6 +26,8 @@ _get_platform_error :: proc() -> Error { return .Invalid_File case .ENOMEM: return .Out_Of_Memory + case .ENOSYS: + return .Unsupported case: return Platform_Error(errno) } diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 963544985..8af46fab3 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -164,7 +164,7 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d } @(require_results) -write_entire_file :: proc(name: string, data: []byte, perm: int, truncate := true) -> Error { +write_entire_file :: proc(name: string, data: []byte, perm: int = 0o644, truncate := true) -> Error { flags := O_WRONLY|O_CREATE if truncate { flags |= O_TRUNC diff --git a/core/os/os2/path_posix.odin b/core/os/os2/path_posix.odin index 6f358c58d..5ffdac28e 100644 --- a/core/os/os2/path_posix.odin +++ b/core/os/os2/path_posix.odin @@ -40,7 +40,7 @@ _mkdir_all :: proc(path: string, perm: int) -> Error { internal_mkdir_all :: proc(path: string, perm: int) -> Error { dir, file := filepath.split(path) - if file != path { + if file != path && dir != "/" { if len(dir) > 1 && dir[len(dir) - 1] == '/' { dir = dir[:len(dir) - 1] } diff --git a/core/os/os2/pipe_posix.odin b/core/os/os2/pipe_posix.odin index 463f29f01..df9425339 100644 --- a/core/os/os2/pipe_posix.odin +++ b/core/os/os2/pipe_posix.odin @@ -49,7 +49,7 @@ _pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) { if r == nil || r.impl == nil { return false, nil } - fd := posix.FD((^File_Impl)(r.impl).fd) + fd := __fd(r) poll_fds := []posix.pollfd { posix.pollfd { fd = fd, @@ -57,8 +57,10 @@ _pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) { }, } n := posix.poll(raw_data(poll_fds), u32(len(poll_fds)), 0) - if n != 1 { + if n < 0 { return false, _get_platform_error() + } else if n != 1 { + return false, nil } pipe_events := poll_fds[0].revents if pipe_events >= {.IN} { @@ -68,4 +70,4 @@ _pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) { return false, .Broken_Pipe } return false, nil -} \ No newline at end of file +} diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin index fd06dca74..5b5a6e844 100644 --- a/core/os/os2/process.odin +++ b/core/os/os2/process.odin @@ -1,7 +1,7 @@ package os2 import "base:runtime" -import "core:strings" + import "core:time" /* @@ -371,16 +371,18 @@ process_exec :: proc( loc := #caller_location, ) -> ( state: Process_State, - stdout: []u8, - stderr: []u8, + stdout: []byte, + stderr: []byte, err: Error, ) { assert(desc.stdout == nil, "Cannot redirect stdout when it's being captured", loc) assert(desc.stderr == nil, "Cannot redirect stderr when it's being captured", loc) + stdout_r, stdout_w := pipe() or_return defer close(stdout_r) stderr_r, stderr_w := pipe() or_return - defer close(stdout_w) + defer close(stderr_r) + process: Process { // NOTE(flysand): Make sure the write-ends are closed, regardless @@ -392,45 +394,65 @@ process_exec :: proc( desc.stderr = stderr_w process = process_start(desc) or_return } - stdout_builder := strings.builder_make(allocator) or_return - stderr_builder := strings.builder_make(allocator) or_return - read_data: for { - buf: [1024]u8 - n: int - has_data: bool - hangup := false - has_data, err = pipe_has_data(stdout_r) - if has_data { - n, err = read(stdout_r, buf[:]) - strings.write_bytes(&stdout_builder, buf[:n]) - } - switch err { - case nil: // nothing - case .Broken_Pipe: - hangup = true - case: - return - } - has_data, err = pipe_has_data(stderr_r) - if has_data { - n, err = read(stderr_r, buf[:]) - strings.write_bytes(&stderr_builder, buf[:n]) - } - switch err { - case nil: // nothing - case .Broken_Pipe: - hangup = true - case: - return - } - if hangup { - break read_data + + { + stdout_b: [dynamic]byte + stdout_b.allocator = allocator + defer stdout = stdout_b[:] + + stderr_b: [dynamic]byte + stderr_b.allocator = allocator + defer stderr = stderr_b[:] + + buf: [1024]u8 = --- + + stdout_done, stderr_done, has_data: bool + for err == nil && (!stdout_done || !stderr_done) { + n := 0 + + if !stdout_done { + has_data, err = pipe_has_data(stdout_r) + if has_data { + n, err = read(stdout_r, buf[:]) + } + + switch err { + case nil: + _, err = append(&stdout_b, ..buf[:n]) + case .EOF, .Broken_Pipe: + stdout_done = true + err = nil + } + } + + if err == nil && !stderr_done { + n = 0 + has_data, err = pipe_has_data(stderr_r) + if has_data { + n, err = read(stderr_r, buf[:]) + } + + switch err { + case nil: + _, err = append(&stderr_b, ..buf[:n]) + case .EOF, .Broken_Pipe: + stderr_done = true + err = nil + } + } } } - err = nil - stdout = transmute([]u8) strings.to_string(stdout_builder) - stderr = transmute([]u8) strings.to_string(stderr_builder) - state = process_wait(process) or_return + + if err != nil { + state, _ = process_wait(process, timeout=0) + if !state.exited { + _ = process_kill(process) + state, _ = process_wait(process) + } + return + } + + state, err = process_wait(process) return } diff --git a/core/os/os2/process_linux.odin b/core/os/os2/process_linux.odin index ea5ee41b1..7eb4dfa44 100644 --- a/core/os/os2/process_linux.odin +++ b/core/os/os2/process_linux.odin @@ -523,7 +523,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) { write_errno_to_parent_and_abort :: proc(parent_fd: linux.Fd, errno: linux.Errno) -> ! { error_byte: [1]u8 = { u8(errno) } linux.write(parent_fd, error_byte[:]) - intrinsics.trap() + linux.exit(126) } stdin_fd: linux.Fd diff --git a/core/os/os2/process_posix.odin b/core/os/os2/process_posix.odin index 5ac6babc1..b54374cec 100644 --- a/core/os/os2/process_posix.odin +++ b/core/os/os2/process_posix.odin @@ -163,7 +163,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) { #assert(len(posix.Errno) < max(u8)) errno := u8(posix.errno()) posix.write(parent_fd, &errno, 1) - runtime.trap() + posix.exit(126) } null := posix.open("/dev/null", {.RDWR}) @@ -223,7 +223,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) { return } - process.pid = int(pid) process, _ = _process_open(int(pid), {}) return } diff --git a/core/os/os2/process_posix_other.odin b/core/os/os2/process_posix_other.odin index 77dbfa7eb..65da3e9e2 100644 --- a/core/os/os2/process_posix_other.odin +++ b/core/os/os2/process_posix_other.odin @@ -15,6 +15,7 @@ _process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error) } _process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) { + process.pid = pid err = .Unsupported return } diff --git a/core/os/os2/process_windows.odin b/core/os/os2/process_windows.odin index 0c32373f3..201fa28e7 100644 --- a/core/os/os2/process_windows.odin +++ b/core/os/os2/process_windows.odin @@ -650,26 +650,30 @@ _build_command_line :: proc(command: []string, allocator: runtime.Allocator) -> strings.write_byte(&builder, ' ') } j := 0 - strings.write_byte(&builder, '"') - for j < len(arg) { - backslashes := 0 - for j < len(arg) && arg[j] == '\\' { - backslashes += 1 + if strings.contains_any(arg, "()[]{}^=;!'+,`~\" ") { + strings.write_byte(&builder, '"') + for j < len(arg) { + backslashes := 0 + for j < len(arg) && arg[j] == '\\' { + backslashes += 1 + j += 1 + } + if j == len(arg) { + _write_byte_n_times(&builder, '\\', 2*backslashes) + break + } else if arg[j] == '"' { + _write_byte_n_times(&builder, '\\', 2*backslashes+1) + strings.write_byte(&builder, arg[j]) + } else { + _write_byte_n_times(&builder, '\\', backslashes) + strings.write_byte(&builder, arg[j]) + } j += 1 } - if j == len(arg) { - _write_byte_n_times(&builder, '\\', 2*backslashes) - break - } else if arg[j] == '"' { - _write_byte_n_times(&builder, '\\', 2*backslashes+1) - strings.write_byte(&builder, '"') - } else { - _write_byte_n_times(&builder, '\\', backslashes) - strings.write_byte(&builder, arg[j]) - } - j += 1 + strings.write_byte(&builder, '"') + } else { + strings.write_string(&builder, arg) } - strings.write_byte(&builder, '"') } return strings.to_string(builder) } diff --git a/core/sys/posix/sys_wait.odin b/core/sys/posix/sys_wait.odin index 8421c8bfd..e0e2ae21b 100644 --- a/core/sys/posix/sys_wait.odin +++ b/core/sys/posix/sys_wait.odin @@ -124,11 +124,11 @@ WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool { idtype_t :: enum c.int { // Wait for any children and `id` is ignored. - P_ALL, + P_ALL = _P_ALL, // Wait for any child wiith a process group ID equal to `id`. - P_PID, + P_PID = _P_PID, // Wait for any child with a process group ID equal to `id`. - P_PGID, + P_PGID = _P_PGID, } Wait_Flag_Bits :: enum c.int { @@ -166,6 +166,10 @@ when ODIN_OS == .Darwin { WNOWAIT :: 0x00000020 WSTOPPED :: 0x00000008 + _P_ALL :: 0 + _P_PID :: 1 + _P_PGID :: 2 + @(private) _WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { return x & 0o177 @@ -221,6 +225,10 @@ when ODIN_OS == .Darwin { WNOWAIT :: 8 WSTOPPED :: 2 + _P_ALL :: 7 + _P_PID :: 0 + _P_PGID :: 2 + @(private) _WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { return x & 0o177 @@ -275,6 +283,10 @@ when ODIN_OS == .Darwin { WNOWAIT :: 0x00010000 WSTOPPED :: 0x00000002 + _P_ALL :: 0 + _P_PID :: 1 + _P_PGID :: 2 + @(private) _WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { return x & 0o177 @@ -330,6 +342,10 @@ when ODIN_OS == .Darwin { WNOWAIT :: 0x00010000 WSTOPPED :: 0x00000002 + _P_ALL :: 0 + _P_PID :: 2 + _P_PGID :: 1 + @(private) _WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { return x & 0o177 diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 934d103e5..e1ace4133 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -253,8 +253,6 @@ FILE_GENERIC_WRITE: DWORD : STANDARD_RIGHTS_WRITE | FILE_APPEND_DATA | SYNCHRONIZE -FILE_FLAG_OPEN_REPARSE_POINT: DWORD : 0x00200000 -FILE_FLAG_BACKUP_SEMANTICS: DWORD : 0x02000000 SECURITY_SQOS_PRESENT: DWORD : 0x00100000 FIONBIO: c_ulong : 0x8004667e @@ -2222,11 +2220,22 @@ WAIT_OBJECT_0: DWORD : 0x00000000 WAIT_TIMEOUT: DWORD : 258 WAIT_FAILED: DWORD : 0xFFFFFFFF +FILE_FLAG_WRITE_THROUGH: DWORD : 0x80000000 +FILE_FLAG_OVERLAPPED: DWORD : 0x40000000 +FILE_FLAG_NO_BUFFERING: DWORD : 0x20000000 +FILE_FLAG_RANDOM_ACCESS: DWORD : 0x10000000 +FILE_FLAG_SEQUENTIAL_SCAN: DWORD : 0x08000000 +FILE_FLAG_DELETE_ON_CLOSE: DWORD : 0x04000000 +FILE_FLAG_BACKUP_SEMANTICS: DWORD : 0x02000000 +FILE_FLAG_POSIX_SEMANTICS: DWORD : 0x01000000 +FILE_FLAG_SESSION_AWARE: DWORD : 0x00800000 +FILE_FLAG_OPEN_REPARSE_POINT: DWORD : 0x00200000 +FILE_FLAG_OPEN_NO_RECALL: DWORD : 0x00100000 +FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD : 0x00080000 + PIPE_ACCESS_INBOUND: DWORD : 0x00000001 PIPE_ACCESS_OUTBOUND: DWORD : 0x00000002 PIPE_ACCESS_DUPLEX: DWORD : 0x00000003 -FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD : 0x00080000 -FILE_FLAG_OVERLAPPED: DWORD : 0x40000000 PIPE_WAIT: DWORD : 0x00000000 PIPE_TYPE_BYTE: DWORD : 0x00000000 PIPE_TYPE_MESSAGE: DWORD : 0x00000004 diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fc1aa62e6..3b61e049c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8795,11 +8795,6 @@ gb_internal ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *n return kind; } - if (x.type == nullptr || x.type == t_invalid || - y.type == nullptr || y.type == t_invalid) { - return kind; - } - bool use_type_hint = type_hint != nullptr && (is_operand_nil(x) || is_operand_nil(y)); convert_to_typed(c, &x, use_type_hint ? type_hint : y.type); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 328237e25..8967a4e03 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1225,10 +1225,10 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV lbValue d3 = lb_emit_struct_ep(p, res.addr, 3); if (immediate_type != ft) { - d0 = lb_emit_conv(p, d0, ft); - d1 = lb_emit_conv(p, d1, ft); - d2 = lb_emit_conv(p, d2, ft); - d3 = lb_emit_conv(p, d3, ft); + z0 = lb_emit_conv(p, z0, ft); + z1 = lb_emit_conv(p, z1, ft); + z2 = lb_emit_conv(p, z2, ft); + z3 = lb_emit_conv(p, z3, ft); } lb_emit_store(p, d0, z0); diff --git a/tests/core/normal.odin b/tests/core/normal.odin index 0670842ac..5bc73bd24 100644 --- a/tests/core/normal.odin +++ b/tests/core/normal.odin @@ -33,6 +33,7 @@ download_assets :: proc() { @(require) import "net" @(require) import "odin" @(require) import "os" +@(require) import "os/os2" @(require) import "path/filepath" @(require) import "reflect" @(require) import "runtime" diff --git a/tests/core/os/os2/process.odin b/tests/core/os/os2/process.odin new file mode 100644 index 000000000..d7700d201 --- /dev/null +++ b/tests/core/os/os2/process.odin @@ -0,0 +1,25 @@ +package tests_core_os_os2 + +import os "core:os/os2" +import "core:log" +import "core:testing" + +@(test) +test_process_exec :: proc(t: ^testing.T) { + state, stdout, stderr, err := os.process_exec({ + command = {"echo", "hellope"}, + }, context.allocator) + defer delete(stdout) + defer delete(stderr) + + if err == .Unsupported { + log.warn("process_exec unsupported") + return + } + + testing.expect_value(t, state.exited, true) + testing.expect_value(t, state.success, true) + testing.expect_value(t, err, nil) + testing.expect_value(t, string(stdout), "hellope\n") + testing.expect_value(t, string(stderr), "") +} diff --git a/tests/core/sys/posix/posix.odin b/tests/core/sys/posix/posix.odin index 760ddc1fb..d73e49ffb 100644 --- a/tests/core/sys/posix/posix.odin +++ b/tests/core/sys/posix/posix.odin @@ -1,8 +1,6 @@ #+build darwin, freebsd, openbsd, netbsd package tests_core_posix -import "base:runtime" - import "core:log" import "core:path/filepath" import "core:strings" @@ -217,52 +215,6 @@ test_termios :: proc(t: ^testing.T) { testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._FFDLY), posix.FFDLY) } -@(test) -test_signal :: proc(t: ^testing.T) { - @static tt: ^testing.T - tt = t - - @static ctx: runtime.Context - ctx = context - - act: posix.sigaction_t - act.sa_flags = {.SIGINFO, .RESETHAND} - act.sa_sigaction = handler - testing.expect_value(t, posix.sigaction(.SIGCHLD, &act, nil), posix.result.OK) - - handler :: proc "c" (sig: posix.Signal, info: ^posix.siginfo_t, address: rawptr) { - context = ctx - testing.expect_value(tt, sig, posix.Signal.SIGCHLD) - testing.expect_value(tt, info.si_signo, posix.Signal.SIGCHLD) - testing.expect_value(tt, info.si_status, 69) - testing.expect_value(tt, info.si_code.chld, posix.CLD_Code.EXITED) - } - - switch pid := posix.fork(); pid { - case -1: - log.errorf("fork() failure: %v", posix.strerror()) - case 0: - posix.exit(69) - case: - for { - status: i32 - res := posix.waitpid(pid, &status, {}) - if res == -1 { - if !testing.expect_value(t, posix.errno(), posix.Errno.EINTR) { - break - } - continue - } - - if posix.WIFEXITED(status) || posix.WIFSIGNALED(status) { - testing.expect(t, posix.WIFEXITED(status)) - testing.expect(t, posix.WEXITSTATUS(status) == 69) - break - } - } - } -} - @(test) test_pthreads :: proc(t: ^testing.T) { testing.set_fail_timeout(t, time.Second)