mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-25 20:57:40 +00:00 
			
		
		
		
	Add {:err :out} option to os/spawn.
				
					
				
			This special case in the os/spawn interface allows easily redirecting both stderr and stdout to the same pipe.
This commit is contained in:
		| @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. | ||||
|  | ||||
| ## Unreleased - ??? | ||||
| - Make `ffi/write` append to a buffer instead of insert at 0 by default. | ||||
| - Add `os/getpid` to get the current process id. | ||||
| - Add `:out` option to `os/spawn` to be able to redirect stderr to stdout with pipes. | ||||
|  | ||||
| ## 1.38.0 - 2025-03-18 | ||||
| - Add `bundle/replace` | ||||
|   | ||||
| @@ -816,6 +816,7 @@ JANET_CORE_FN(os_proc_close, | ||||
| JANET_CORE_FN(os_proc_getpid, | ||||
|         "(os/getpid)", | ||||
|         "Get the process ID of the current process.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS); | ||||
|     janet_fixarity(argc, 0); | ||||
|     (void) argv; | ||||
| #ifdef JANET_WINDOWS | ||||
| @@ -1149,6 +1150,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) { | ||||
|     JanetAbstract orig_in = NULL, orig_out = NULL, orig_err = NULL; | ||||
|     JanetHandle new_in = JANET_HANDLE_NONE, new_out = JANET_HANDLE_NONE, new_err = JANET_HANDLE_NONE; | ||||
|     JanetHandle pipe_in = JANET_HANDLE_NONE, pipe_out = JANET_HANDLE_NONE, pipe_err = JANET_HANDLE_NONE; | ||||
|     int stderr_is_stdout = 0; | ||||
|     int pipe_errflag = 0; /* Track errors setting up pipes */ | ||||
|     int pipe_owner_flags = (is_spawn && (flags & 0x8)) ? JANET_PROC_ALLOW_ZOMBIE : 0; | ||||
|  | ||||
| @@ -1173,6 +1175,8 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) { | ||||
|         if (is_spawn && janet_keyeq(maybe_stderr, "pipe")) { | ||||
|             new_err = make_pipes(&pipe_err, 0, &pipe_errflag); | ||||
|             pipe_owner_flags |= JANET_PROC_OWNS_STDERR; | ||||
|         } else if (is_spawn && janet_keyeq(maybe_stderr, "out")) { | ||||
|             stderr_is_stdout = 1; | ||||
|         } else if (!janet_checktype(maybe_stderr, JANET_NIL)) { | ||||
|             new_err = janet_getjstream(&maybe_stderr, 0, &orig_err); | ||||
|         } | ||||
| @@ -1230,6 +1234,8 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) { | ||||
|         startupInfo.hStdError = pipe_err; | ||||
|     } else if (new_err != NULL) { | ||||
|         startupInfo.hStdError = new_err; | ||||
|     } else if (stderr_is_stdout) { | ||||
|         startupInfo.hStdError = startupInfo.hStdOut; | ||||
|     } else { | ||||
|         startupInfo.hStdError = (HANDLE) _get_osfhandle(2); | ||||
|     } | ||||
| @@ -1321,6 +1327,8 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) { | ||||
|     } else if (new_err != JANET_HANDLE_NONE && new_err != 2) { | ||||
|         posix_spawn_file_actions_adddup2(&actions, new_err, 2); | ||||
|         posix_spawn_file_actions_addclose(&actions, new_err); | ||||
|     } else if (stderr_is_stdout) { | ||||
|         posix_spawn_file_actions_adddup2(&actions, 1, 2); | ||||
|     } | ||||
|  | ||||
|     pid_t pid; | ||||
| @@ -1426,7 +1434,8 @@ JANET_CORE_FN(os_spawn, | ||||
|               "`:pipe` may fail if there are too many active file descriptors. The caller is " | ||||
|               "responsible for closing pipes created by `:pipe` (either individually or using " | ||||
|               "`os/proc-close`). Similar to `os/execute`, the caller is responsible for ensuring " | ||||
|               "pipes do not cause the program to block and deadlock.") { | ||||
|               "pipes do not cause the program to block and deadlock. As a special case, the stream passed to `:err` " | ||||
|               "can be the keyword `:out` to redirect stderr to stdout in the subprocess.") { | ||||
|     return os_execute_impl(argc, argv, JANET_EXECUTE_SPAWN); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -556,4 +556,20 @@ | ||||
|   (ev/deadline 0.01 nil f true) | ||||
|   (assert-error "deadline expired" (resume f))) | ||||
|  | ||||
| # Use :err :stdout | ||||
| (def- subproc-code '(do (eprint "hi") (eflush) (print "there") (flush))) | ||||
| (defn ev/slurp | ||||
|   [f &opt buf] | ||||
|   (default buf @"") | ||||
|   (if (ev/read f 0x10000 buf) | ||||
|     (ev/slurp f buf) | ||||
|     buf)) | ||||
| (def p (os/spawn [;run janet "-e" (string/format "%j" subproc-code)] :px {:out :pipe :err :out})) | ||||
| (def [exit-code data] | ||||
|   (ev/gather | ||||
|     (os/proc-wait p) | ||||
|     (ev/slurp (p :out)))) | ||||
| (assert (zero? exit-code) "subprocess ran") | ||||
| (assert (deep= data @"hi\nthere\n") "output is correct") | ||||
|  | ||||
| (end-suite) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose