mirror of
https://github.com/janet-lang/janet
synced 2025-07-05 03:22:54 +00:00
Merge branch 'master' into compile-opt
This commit is contained in:
commit
3cbdf26aa2
@ -2,7 +2,11 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## Unreleased - ???
|
||||
- Add `ev/to-stream`
|
||||
- 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.
|
||||
Add `interrupt?` argument to `ev/deadline` to use VM interruptions.
|
||||
|
||||
## 1.38.0 - 2025-03-18
|
||||
- Add `bundle/replace`
|
||||
|
15
README.md
15
README.md
@ -165,6 +165,21 @@ make install-jpm-git
|
||||
|
||||
Find out more about the available make targets by running `make help`.
|
||||
|
||||
### Alpine Linux
|
||||
|
||||
To build a statically-linked build of Janet, Alpine Linux + MUSL is a good combination. Janet can also
|
||||
be built inside a docker container or similar in this manner.
|
||||
|
||||
```sh
|
||||
docker run -it --rm alpine /bin/ash
|
||||
$ apk add make gcc musl-dev git
|
||||
$ git clone https://github.com/janet-lang/janet.git
|
||||
$ cd janet
|
||||
$ make -j10
|
||||
$ make test
|
||||
$ make install
|
||||
```
|
||||
|
||||
### 32-bit Haiku
|
||||
|
||||
32-bit Haiku build instructions are the same as the UNIX-like build instructions,
|
||||
|
@ -41,34 +41,34 @@ if not exist build\boot mkdir build\boot
|
||||
@rem Build the bootstrap interpreter
|
||||
for %%f in (src\core\*.c) do (
|
||||
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
for %%f in (src\boot\*.c) do (
|
||||
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
build\janet_boot . > build\c\janet.c
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the sources
|
||||
%JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
%JANET_COMPILE% /Fobuild\shell.obj src\mainclient\shell.c
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the resources
|
||||
rc /nologo /fobuild\janet_win.res janet_win.rc
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Link everything to main client
|
||||
%JANET_LINK% /out:janet.exe build\janet.obj build\shell.obj build\janet_win.res
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build static library (libjanet.lib)
|
||||
%JANET_LINK_STATIC% /out:build\libjanet.lib build\janet.obj
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
echo === Successfully built janet.exe for Windows ===
|
||||
echo === Run 'build_win test' to run tests. ==
|
||||
@ -102,7 +102,7 @@ exit /b 0
|
||||
:TEST
|
||||
for %%f in (test/suite*.janet) do (
|
||||
janet.exe test\%%f
|
||||
@if not errorlevel 0 goto TESTFAIL
|
||||
@if errorlevel 1 goto TESTFAIL
|
||||
)
|
||||
exit /b 0
|
||||
|
||||
|
@ -1084,16 +1084,29 @@
|
||||
(map-aggregator ,maptype ,res (,f x ;call-buffer)))))))
|
||||
|
||||
(defn map
|
||||
`Map a function over every value in a data structure and
|
||||
return an array of the results.`
|
||||
```
|
||||
Map a function `f` over every value in a data structure `ind`
|
||||
and return an array of results, but only if no `inds` are
|
||||
provided. Multiple data structures can be handled if each
|
||||
`inds` is a data structure and `f` is a function of arity
|
||||
one more than the number of `inds`. The resulting array has
|
||||
a length that is the shortest of `ind` and each of `inds`.
|
||||
```
|
||||
[f ind & inds]
|
||||
(def res @[])
|
||||
(map-template :map res f ind inds)
|
||||
res)
|
||||
|
||||
(defn mapcat
|
||||
``Map a function over every element in an array or tuple and
|
||||
use `array/concat` to concatenate the results.``
|
||||
```
|
||||
Map a function `f` over every value in a data structure `ind`
|
||||
and use `array/concat` to concatenate the results, but only if
|
||||
no `inds` are provided. Multiple data structures can be handled
|
||||
if each `inds` is a data structure and `f` is a function of
|
||||
arity one more than the number of `inds`. Note that `f` is only
|
||||
applied to values at indeces up to the largest index of the
|
||||
shortest of `ind` and each of `inds`.
|
||||
```
|
||||
[f ind & inds]
|
||||
(def res @[])
|
||||
(map-template :mapcat res f ind inds)
|
||||
@ -1110,18 +1123,30 @@
|
||||
res)
|
||||
|
||||
(defn count
|
||||
``Count the number of items in `ind` for which `(pred item)`
|
||||
is true.``
|
||||
```
|
||||
Count the number of values in a data structure `ind` for which
|
||||
applying `pred` yields a truthy value, but only if no `inds` are
|
||||
provided. Multiple data structures can be handled if each `inds`
|
||||
is a data structure and `pred` is a function of arity one more
|
||||
than the number of `inds`. Note that `pred` is only applied to
|
||||
values at indeces up to the largest index of the shortest of
|
||||
`ind` and each of `inds`.
|
||||
```
|
||||
[pred ind & inds]
|
||||
(var res 0)
|
||||
(map-template :count res pred ind inds)
|
||||
res)
|
||||
|
||||
(defn keep
|
||||
``Given a predicate `pred`, return a new array containing the truthy results
|
||||
of applying `pred` to each element in the indexed collection `ind`. This is
|
||||
different from `filter` which returns an array of the original elements where
|
||||
the predicate is truthy.``
|
||||
```
|
||||
Given a predicate `pred`, return a new array containing the
|
||||
truthy results of applying `pred` to each value in the data
|
||||
structure `ind`, but only if no `inds` are provided. Multiple
|
||||
data structures can be handled if each `inds` is a data
|
||||
structure and `pred` is a function of arity one more than the
|
||||
number of `inds`. The resulting array has a length that is no
|
||||
longer than the shortest of `ind` and each of `inds`.
|
||||
```
|
||||
[pred ind & inds]
|
||||
(def res @[])
|
||||
(map-template :keep res pred ind inds)
|
||||
@ -2203,17 +2228,32 @@
|
||||
ret)
|
||||
|
||||
(defn all
|
||||
``Returns true if `(pred item)` is truthy for every item in `ind`.
|
||||
Otherwise, returns the first falsey result encountered.
|
||||
Returns true if `ind` is empty.``
|
||||
```
|
||||
Returns true if applying `pred` to every value in a data
|
||||
structure `ind` results in only truthy values, but only if no
|
||||
`inds` are provided. Multiple data structures can be handled
|
||||
if each `inds` is a data structure and `pred` is a function
|
||||
of arity one more than the number of `inds`. Returns the first
|
||||
falsey result encountered. Note that `pred` is only called as
|
||||
many times as the length of the shortest of `ind` and each of
|
||||
`inds`. If `ind` or any of `inds` are empty, returns true.
|
||||
```
|
||||
[pred ind & inds]
|
||||
(var res true)
|
||||
(map-template :all res pred ind inds)
|
||||
res)
|
||||
|
||||
(defn some
|
||||
``Returns nil if `(pred item)` is false or nil for every item in `ind`.
|
||||
Otherwise, returns the first truthy result encountered.``
|
||||
```
|
||||
Returns nil if applying `pred` to every value in a data
|
||||
structure `ind` results in only falsey values, but only if no
|
||||
`inds` are provided. Multiple data structures can be handled
|
||||
if each `inds` is a data structure and `pred` is a function
|
||||
of arity one more than the number of `inds`. Returns the first
|
||||
truthy result encountered. Note that `pred` is only called as
|
||||
many times as the length of the shortest of `ind` and each of
|
||||
`inds`. If `ind` or any of `inds` are empty, returns nil.
|
||||
```
|
||||
[pred ind & inds]
|
||||
(var res nil)
|
||||
(map-template :some res pred ind inds)
|
||||
@ -4314,6 +4354,9 @@
|
||||
(when check
|
||||
(do-hook module bundle-name :check man)))
|
||||
(print "installed " bundle-name)
|
||||
(when (get man :has-bin-script)
|
||||
(def binpath (string (dyn *syspath*) s "bin"))
|
||||
(eprintf "executable scripts have been installed to %s" binpath))
|
||||
bundle-name)
|
||||
|
||||
(defn- bundle/pack
|
||||
@ -4424,7 +4467,7 @@
|
||||
|
||||
(defn bundle/add
|
||||
"Add files and directories during a bundle install relative to `(dyn *syspath*)`.
|
||||
Added paths will be recorded in the bundle manifest such that they are properly tracked
|
||||
Added files and directories will be recorded in the bundle manifest such that they are properly tracked
|
||||
and removed during an upgrade or uninstall."
|
||||
[manifest src &opt dest chmod-mode]
|
||||
(default dest src)
|
||||
@ -4447,6 +4490,7 @@
|
||||
(default dest (last (string/split s src)))
|
||||
(default chmod-mode 8r755)
|
||||
(os/mkdir (string (dyn *syspath*) s "bin"))
|
||||
(put manifest :has-bin-script true)
|
||||
(bundle/add-file manifest src (string "bin" s dest) chmod-mode))
|
||||
|
||||
(defn bundle/update-all
|
||||
|
@ -353,21 +353,22 @@ JanetStream *janet_stream(JanetHandle handle, uint32_t flags, const JanetMethod
|
||||
|
||||
static void janet_stream_close_impl(JanetStream *stream) {
|
||||
stream->flags |= JANET_STREAM_CLOSED;
|
||||
int canclose = !(stream->flags & JANET_STREAM_NOT_CLOSEABLE);
|
||||
#ifdef JANET_WINDOWS
|
||||
if (stream->handle != INVALID_HANDLE_VALUE) {
|
||||
#ifdef JANET_NET
|
||||
if (stream->flags & JANET_STREAM_SOCKET) {
|
||||
closesocket((SOCKET) stream->handle);
|
||||
if (canclose) closesocket((SOCKET) stream->handle);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
CloseHandle(stream->handle);
|
||||
if (canclose) CloseHandle(stream->handle);
|
||||
}
|
||||
stream->handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
#else
|
||||
if (stream->handle != -1) {
|
||||
close(stream->handle);
|
||||
if (canclose) close(stream->handle);
|
||||
stream->handle = -1;
|
||||
#ifdef JANET_EV_POLL
|
||||
uint32_t i = stream->index;
|
||||
@ -652,6 +653,12 @@ static VOID CALLBACK janet_timeout_stop(ULONG_PTR ptr) {
|
||||
UNREFERENCED_PARAMETER(ptr);
|
||||
ExitThread(0);
|
||||
}
|
||||
#elif JANET_ANDROID
|
||||
static void janet_timeout_stop(int sig_num) {
|
||||
if (sig_num == SIGUSR1) {
|
||||
pthread_exit(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void janet_timeout_cb(JanetEVGenericMessage msg) {
|
||||
@ -673,6 +680,14 @@ static DWORD WINAPI janet_timeout_body(LPVOID ptr) {
|
||||
}
|
||||
#else
|
||||
static void *janet_timeout_body(void *ptr) {
|
||||
#ifdef JANET_ANDROID
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = 0;
|
||||
action.sa_handler = &janet_timeout_stop;
|
||||
sigaction(SIGUSR1, &action, NULL);
|
||||
#endif
|
||||
JanetThreadedTimeout tto = *(JanetThreadedTimeout *)ptr;
|
||||
janet_free(ptr);
|
||||
struct timespec ts;
|
||||
@ -1489,8 +1504,12 @@ JanetFiber *janet_loop1(void) {
|
||||
QueueUserAPC(janet_timeout_stop, to.worker, 0);
|
||||
WaitForSingleObject(to.worker, INFINITE);
|
||||
CloseHandle(to.worker);
|
||||
#else
|
||||
#ifdef JANET_ANDROID
|
||||
pthread_kill(to.worker, SIGUSR1);
|
||||
#else
|
||||
pthread_cancel(to.worker);
|
||||
#endif
|
||||
void *res;
|
||||
pthread_join(to.worker, &res);
|
||||
#endif
|
||||
@ -3188,6 +3207,9 @@ JANET_CORE_FN(cfun_ev_deadline,
|
||||
to.is_error = 0;
|
||||
to.sched_id = to.fiber->sched_id;
|
||||
if (use_interrupt) {
|
||||
#ifdef JANET_ANDROID
|
||||
janet_sandbox_assert(JANET_SANDBOX_SIGNAL);
|
||||
#endif
|
||||
JanetThreadedTimeout *tto = janet_malloc(sizeof(JanetThreadedTimeout));
|
||||
if (NULL == tto) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
@ -3465,6 +3487,39 @@ JANET_CORE_FN(janet_cfun_ev_all_tasks,
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_cfun_to_stream,
|
||||
"(ev/to-stream file)",
|
||||
"Convert a core/file to a core/stream. On POSIX operating systems, this will mark "
|
||||
"the underlying open file descriptor as non-blocking.") {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t flags = 0;
|
||||
int32_t stream_flags = 0;
|
||||
FILE *file = janet_getfile(argv, 0, &flags);
|
||||
if (flags & JANET_FILE_READ) stream_flags |= JANET_STREAM_READABLE;
|
||||
if (flags & JANET_FILE_WRITE) stream_flags |= JANET_STREAM_WRITABLE;
|
||||
if (flags & JANET_FILE_NOT_CLOSEABLE) stream_flags |= JANET_STREAM_NOT_CLOSEABLE;
|
||||
if (flags & JANET_FILE_CLOSED) janet_panic("file is closed");
|
||||
#ifdef JANET_WINDOWS
|
||||
HANDLE handle = (HANDLE) _get_osfhandle(_fileno(file));
|
||||
HANDLE prochandle = GetCurrentProcess();
|
||||
HANDLE dupped_handle = INVALID_HANDLE_VALUE;
|
||||
if (!DuplicateHandle(prochandle, handle, prochandle, &dupped_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
|
||||
janet_panic("cannot duplicate handle to file");
|
||||
}
|
||||
JanetStream *stream = janet_stream(dupped_handle, stream_flags, NULL);
|
||||
#else
|
||||
int handle = fileno(file);
|
||||
int dupped_handle = 0;
|
||||
int status = 0;
|
||||
RETRY_EINTR(dupped_handle, dup(handle));
|
||||
if (status == -1) janet_panic(janet_strerror(errno));
|
||||
RETRY_EINTR(status, fcntl(dupped_handle, F_SETFL, O_NONBLOCK));
|
||||
if (status == -1) janet_panic(janet_strerror(errno));
|
||||
JanetStream *stream = janet_stream(dupped_handle, stream_flags, NULL);
|
||||
#endif
|
||||
return janet_wrap_abstract(stream);
|
||||
}
|
||||
|
||||
void janet_lib_ev(JanetTable *env) {
|
||||
JanetRegExt ev_cfuns_ext[] = {
|
||||
JANET_CORE_REG("ev/give", cfun_channel_push),
|
||||
@ -3496,6 +3551,7 @@ void janet_lib_ev(JanetTable *env) {
|
||||
JANET_CORE_REG("ev/release-rlock", janet_cfun_rwlock_read_release),
|
||||
JANET_CORE_REG("ev/release-wlock", janet_cfun_rwlock_write_release),
|
||||
JANET_CORE_REG("ev/to-file", janet_cfun_to_file),
|
||||
JANET_CORE_REG("ev/to-stream", janet_cfun_to_stream),
|
||||
JANET_CORE_REG("ev/all-tasks", janet_cfun_ev_all_tasks),
|
||||
JANET_REG_END
|
||||
};
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include <sys/utime.h>
|
||||
#include <io.h>
|
||||
#include <process.h>
|
||||
#define JANET_SPAWN_CHDIR
|
||||
#else
|
||||
#include <spawn.h>
|
||||
#include <utime.h>
|
||||
@ -73,6 +74,20 @@ extern char **environ;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Detect availability of posix_spawn_file_actions_addchdir_np. Since
|
||||
* this doesn't seem to follow any standard, just a common extension, we
|
||||
* must enumerate supported systems for availability. Define JANET_SPAWN_NO_CHDIR
|
||||
* to disable this. */
|
||||
#ifndef JANET_SPAWN_NO_CHDIR
|
||||
#ifdef __GLIBC__
|
||||
#define JANET_SPAWN_CHDIR
|
||||
#elif defined(JANET_APPLE) /* Some older versions may not work here. */
|
||||
#define JANET_SPAWN_CHDIR
|
||||
#elif defined(__FreeBSD__) /* Not all BSDs work, for example openBSD doesn't seem to support this */
|
||||
#define JANET_SPAWN_CHDIR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Not POSIX, but all Unixes but Solaris have this function. */
|
||||
#if defined(JANET_POSIX) && !defined(__sun)
|
||||
time_t timegm(struct tm *tm);
|
||||
@ -813,6 +828,19 @@ JANET_CORE_FN(os_proc_close,
|
||||
#endif
|
||||
}
|
||||
|
||||
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
|
||||
return janet_wrap_number((double) _getpid());
|
||||
#else
|
||||
return janet_wrap_number((double) getpid());
|
||||
#endif
|
||||
}
|
||||
|
||||
static void swap_handles(JanetHandle *handles) {
|
||||
JanetHandle temp = handles[0];
|
||||
handles[0] = handles[1];
|
||||
@ -1137,6 +1165,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;
|
||||
|
||||
@ -1161,11 +1190,28 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Optional working directory. Available for both os/execute and os/spawn. */
|
||||
const char *chdir_path = NULL;
|
||||
if (argc > 2) {
|
||||
JanetDictView tab = janet_getdictionary(argv, 2);
|
||||
Janet workdir = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("cd"));
|
||||
if (janet_checktype(workdir, JANET_STRING)) {
|
||||
chdir_path = (const char *) janet_unwrap_string(workdir);
|
||||
#ifndef JANET_SPAWN_CHDIR
|
||||
janet_panicf(":cd argument not supported on this system - %s", chdir_path);
|
||||
#endif
|
||||
} else if (!janet_checktype(workdir, JANET_NIL)) {
|
||||
janet_panicf("expected string for :cd argumnet, got %v", workdir);
|
||||
}
|
||||
}
|
||||
|
||||
/* Clean up if any of the pipes have any issues */
|
||||
if (pipe_errflag) {
|
||||
if (pipe_in != JANET_HANDLE_NONE) close_handle(pipe_in);
|
||||
@ -1180,6 +1226,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
SECURITY_ATTRIBUTES saAttr;
|
||||
PROCESS_INFORMATION processInfo;
|
||||
STARTUPINFO startupInfo;
|
||||
LPCSTR lpCurrentDirectory = NULL;
|
||||
memset(&saAttr, 0, sizeof(saAttr));
|
||||
memset(&processInfo, 0, sizeof(processInfo));
|
||||
memset(&startupInfo, 0, sizeof(startupInfo));
|
||||
@ -1196,6 +1243,10 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
}
|
||||
const char *path = (const char *) janet_unwrap_string(exargs.items[0]);
|
||||
|
||||
if (chdir_path != NULL) {
|
||||
lpCurrentDirectory = chdir_path;
|
||||
}
|
||||
|
||||
/* Do IO redirection */
|
||||
|
||||
if (pipe_in != JANET_HANDLE_NONE) {
|
||||
@ -1203,7 +1254,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
} else if (new_in != JANET_HANDLE_NONE) {
|
||||
startupInfo.hStdInput = new_in;
|
||||
} else {
|
||||
startupInfo.hStdInput = (HANDLE) _get_osfhandle(0);
|
||||
startupInfo.hStdInput = (HANDLE) _get_osfhandle(_fileno(stdin));
|
||||
}
|
||||
|
||||
if (pipe_out != JANET_HANDLE_NONE) {
|
||||
@ -1211,15 +1262,17 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
} else if (new_out != JANET_HANDLE_NONE) {
|
||||
startupInfo.hStdOutput = new_out;
|
||||
} else {
|
||||
startupInfo.hStdOutput = (HANDLE) _get_osfhandle(1);
|
||||
startupInfo.hStdOutput = (HANDLE) _get_osfhandle(_fileno(stdout));
|
||||
}
|
||||
|
||||
if (pipe_err != JANET_HANDLE_NONE) {
|
||||
startupInfo.hStdError = pipe_err;
|
||||
} else if (new_err != NULL) {
|
||||
startupInfo.hStdError = new_err;
|
||||
} else if (stderr_is_stdout) {
|
||||
startupInfo.hStdError = startupInfo.hStdOutput;
|
||||
} else {
|
||||
startupInfo.hStdError = (HANDLE) _get_osfhandle(2);
|
||||
startupInfo.hStdError = (HANDLE) _get_osfhandle(_fileno(stderr));
|
||||
}
|
||||
|
||||
int cp_failed = 0;
|
||||
@ -1230,7 +1283,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
TRUE, /* handle inheritance */
|
||||
0, /* flags */
|
||||
use_environ ? NULL : envp, /* pass in environment */
|
||||
NULL, /* use parents starting directory */
|
||||
lpCurrentDirectory,
|
||||
&startupInfo,
|
||||
&processInfo)) {
|
||||
cp_failed = 1;
|
||||
@ -1287,6 +1340,15 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
/* Posix spawn setup */
|
||||
posix_spawn_file_actions_t actions;
|
||||
posix_spawn_file_actions_init(&actions);
|
||||
#ifdef JANET_SPAWN_CHDIR
|
||||
if (chdir_path != NULL) {
|
||||
#ifdef JANET_SPAWN_CHDIR_NO_NP
|
||||
posix_spawn_file_actions_addchdir(&actions, chdir_path);
|
||||
#else
|
||||
posix_spawn_file_actions_addchdir_np(&actions, chdir_path);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (pipe_in != JANET_HANDLE_NONE) {
|
||||
posix_spawn_file_actions_adddup2(&actions, pipe_in, 0);
|
||||
posix_spawn_file_actions_addclose(&actions, pipe_in);
|
||||
@ -1309,6 +1371,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;
|
||||
@ -1414,7 +1478,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);
|
||||
}
|
||||
|
||||
@ -2797,6 +2862,7 @@ void janet_lib_os(JanetTable *env) {
|
||||
JANET_CORE_REG("os/proc-wait", os_proc_wait),
|
||||
JANET_CORE_REG("os/proc-kill", os_proc_kill),
|
||||
JANET_CORE_REG("os/proc-close", os_proc_close),
|
||||
JANET_CORE_REG("os/getpid", os_proc_getpid),
|
||||
#endif
|
||||
|
||||
/* high resolution timers */
|
||||
|
@ -67,6 +67,11 @@ extern "C" {
|
||||
#define JANET_LINUX 1
|
||||
#endif
|
||||
|
||||
/* Check for Android */
|
||||
#ifdef __ANDROID__
|
||||
#define JANET_ANDROID 1
|
||||
#endif
|
||||
|
||||
/* Check for Cygwin */
|
||||
#if defined(__CYGWIN__)
|
||||
#define JANET_CYGWIN 1
|
||||
@ -591,7 +596,7 @@ typedef void *JanetAbstract;
|
||||
#define JANET_STREAM_WRITABLE 0x400
|
||||
#define JANET_STREAM_ACCEPTABLE 0x800
|
||||
#define JANET_STREAM_UDPSERVER 0x1000
|
||||
#define JANET_STREAM_BUFFERED 0x2000
|
||||
#define JANET_STREAM_NOT_CLOSEABLE 0x2000
|
||||
#define JANET_STREAM_TOCLOSE 0x10000
|
||||
|
||||
typedef enum {
|
||||
|
@ -430,13 +430,7 @@
|
||||
# Now do our telnet chat
|
||||
(def bob (assert (net/connect test-host test-port :stream)))
|
||||
(expect-read bob "Whats your name?\n")
|
||||
(if (= :mingw (os/which))
|
||||
(net/write bob "bob")
|
||||
(do
|
||||
(def fbob (ev/to-file bob))
|
||||
(file/write fbob "bob")
|
||||
(file/flush fbob)
|
||||
(:close fbob)))
|
||||
(net/write bob "bob")
|
||||
(expect-read bob "Welcome bob\n")
|
||||
(def alice (assert (net/connect test-host test-port)))
|
||||
(expect-read alice "Whats your name?\n")
|
||||
@ -501,8 +495,10 @@
|
||||
# soreuseport on unix domain sockets
|
||||
(compwhen (or (= :macos (os/which)) (= :linux (os/which)))
|
||||
(assert-no-error "unix-domain socket reuseaddr"
|
||||
(let [s (net/listen :unix "./unix-domain-socket" :stream)]
|
||||
(:close s))))
|
||||
(let [uds-path "./unix-domain-socket"]
|
||||
(defer (os/rm uds-path)
|
||||
(let [s (net/listen :unix uds-path :stream)]
|
||||
(:close s))))))
|
||||
|
||||
# net/accept-loop level triggering
|
||||
(gccollect)
|
||||
@ -554,4 +550,21 @@
|
||||
(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))))
|
||||
(def data (string/replace-all "\r" "" data))
|
||||
(assert (zero? exit-code) "subprocess ran")
|
||||
(assert (= data "hi\nthere\n") "output is correct")
|
||||
|
||||
(end-suite)
|
||||
|
Loading…
x
Reference in New Issue
Block a user