From 32bf70571a64185a575a1ca262f4148bdf6cbd60 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 13 Sep 2020 20:49:38 -0500 Subject: [PATCH 1/8] Fix os/spawn piping on windows and free handles on errors. --- .gitattributes | 9 ++++ src/core/os.c | 125 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 100 insertions(+), 34 deletions(-) diff --git a/.gitattributes b/.gitattributes index c0b7fe0e..144d3aa5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,10 @@ *.janet linguist-language=Clojure + +*.janet text eol=lf +*.c text eol=lf +*.h text eol=lf +*.md text eol=lf +*.yml text eol=lf +*.build text eol=lf +*.txt text eol=lf +*.sh text eol=lf diff --git a/src/core/os.c b/src/core/os.c index 7d83ebb9..a4827ca4 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -221,7 +221,8 @@ static char **os_execute_env(int32_t argc, const Janet *argv) { return envp; } -/* Free memory from os_execute */ +/* Free memory from os_execute. Not actually needed, but doesn't pressure the GC + in the happy path. */ static void os_execute_cleanup(char **envp, const char **child_argv) { #ifdef JANET_WINDOWS (void) child_argv; @@ -414,33 +415,66 @@ static Janet os_proc_kill(int32_t argc, Janet *argv) { } } -/* Create piped file for os/execute and os/spawn. */ -static JanetFile *make_pipes(JanetHandle *handle, int reverse) { +static void swap_handles(JanetHandle *handles) { + JanetHandle temp = handles[0]; + handles[0] = handles[1]; + handles[1] = temp; +} + +static void close_handle(JanetHandle handle) { +#ifdef JANET_WINDOWS + CloseHandle(handle); +#else + close(handle); +#endif +} + +/* Create piped file for os/execute and os/spawn. Need to be careful that we mark + the error flag if we can't create pipe and don't leak handles. *handle will be cleaned + up by the calling function. If everything goes well, *handle is owned by the calling function, + (if it is set) and the returned JanetFile owns the other end of the pipe, which will be closed + on GC or fclose. */ +static JanetFile *make_pipes(JanetHandle *handle, int reverse, int *errflag) { JanetHandle handles[2]; #ifdef JANET_WINDOWS - if (!CreatePipe(handles, handles + 1, NULL, 0)) janet_panic("failed to create pipe"); - if (reverse) { - JanetHandle temp = handles[0]; - handles[0] = handles[1]; - handles[1] = temp; - } + SECURITY_ATTRIBUTES saAttr; + memset(&saAttr, 0, sizeof(saAttr)); + saAttr.nLength = sizeof(saAttr); + saAttr.bInheritHandle = TRUE; + if (!CreatePipe(handles, handles + 1, &saAttr, 0)) goto error_pipe; + if (reverse) swap_handles(handles); + /* Don't inherit the side of the pipe owned by this process */ + if (!SetHandleInformation(handles[0], HANDLE_FLAG_INHERIT, 0)) goto error_set_handle_info; *handle = handles[1]; int fd = _open_osfhandle((intptr_t) handles[0], reverse ? _O_WRONLY : _O_RDONLY); - if (fd == -1) janet_panic("could not create file for piping"); + if (fd == -1) goto error_open_osfhandle; FILE *f = _fdopen(fd, reverse ? "w" : "r"); - if (NULL == f) janet_panic(strerror(errno)); + if (NULL == f) goto error_fdopen; return janet_makejfile(f, reverse ? JANET_FILE_WRITE : JANET_FILE_READ); +error_fdopen: + _close(fd); /* we need to close the fake file descriptor instead of the handle, as ownership has been transfered. */ + *errflag = 1; + return NULL; +error_set_handle_info: +error_open_osfhandle: + close_handle(handles[0]); + /* fallthrough */ +error_pipe: + *errflag = 1; + return NULL; #else - if (pipe(handles)) janet_panic(strerror(errno)); - if (reverse) { - JanetHandle temp = handles[0]; - handles[0] = handles[1]; - handles[1] = temp; - } + if (pipe(handles)) goto error_pipe; + if (reverse) swap_handles(handles); *handle = handles[1]; FILE *f = fdopen(handles[0], reverse ? "w" : "r"); - if (NULL == f) janet_panic(strerror(errno)); + if (NULL == f) goto error_fdopen; return janet_makejfile(f, reverse ? JANET_FILE_WRITE : JANET_FILE_READ); +error_fdopen: + close_handle(handles[0]); + /* fallthrough */ +error_pipe: + *errflag = 1; + return NULL; #endif } @@ -490,6 +524,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) { } /* Get environment */ + int use_environ = !janet_flag_at(flags, 0); char **envp = os_execute_env(argc, argv); /* Get arguments */ @@ -501,6 +536,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) { /* Optional stdio redirections */ JanetFile *new_in = NULL, *new_out = NULL, *new_err = NULL; JanetHandle pipe_in = JANET_HANDLE_NONE, pipe_out = JANET_HANDLE_NONE, pipe_err = JANET_HANDLE_NONE; + int pipe_errflag = 0; /* Track errors setting up pipes */ /* Get optional redirections */ if (argc > 2) { @@ -509,34 +545,46 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) { Janet maybe_stdout = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("out")); Janet maybe_stderr = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("err")); if (janet_keyeq(maybe_stdin, "pipe")) { - new_in = make_pipes(&pipe_in, 1); + new_in = make_pipes(&pipe_in, 1, &pipe_errflag); } else if (!janet_checktype(maybe_stdin, JANET_NIL)) { new_in = janet_getjfile(&maybe_stdin, 0); } if (janet_keyeq(maybe_stdout, "pipe")) { - new_out = make_pipes(&pipe_out, 0); + new_out = make_pipes(&pipe_out, 0, &pipe_errflag); } else if (!janet_checktype(maybe_stdout, JANET_NIL)) { new_out = janet_getjfile(&maybe_stdout, 0); } if (janet_keyeq(maybe_stderr, "err")) { - new_err = make_pipes(&pipe_err, 0); + new_err = make_pipes(&pipe_err, 0, &pipe_errflag); } else if (!janet_checktype(maybe_stderr, JANET_NIL)) { new_err = janet_getjfile(&maybe_stderr, 0); } } + /* Clean up if any of the pipes have any issues */ + if (pipe_errflag) { + if (pipe_in != JANET_HANDLE_NONE) close_handle(pipe_in); + if (pipe_out != JANET_HANDLE_NONE) close_handle(pipe_out); + if (pipe_err != JANET_HANDLE_NONE) close_handle(pipe_err); + janet_panic("failed to create pipes"); + } + /* Result */ int status = 0; #ifdef JANET_WINDOWS HANDLE pHandle, tHandle; + SECURITY_ATTRIBUTES saAttr; PROCESS_INFORMATION processInfo; STARTUPINFO startupInfo; + memset(&saAttr, 0, sizeof(saAttr)); memset(&processInfo, 0, sizeof(processInfo)); memset(&startupInfo, 0, sizeof(startupInfo)); startupInfo.cb = sizeof(startupInfo); startupInfo.dwFlags |= STARTF_USESTDHANDLES; + saAttr.nLength = sizeof(saAttr); + saAttr.bInheritHandle = TRUE; JanetBuffer *buf = os_exec_escape(exargs); if (buf->count > 8191) { @@ -550,47 +598,58 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) { startupInfo.hStdInput = pipe_in; } else if (new_in != NULL) { startupInfo.hStdInput = (HANDLE) _get_osfhandle(_fileno(new_in->file)); + } else { + startupInfo.hStdInput = (HANDLE) _get_osfhandle(0); } + if (pipe_out != JANET_HANDLE_NONE) { - startupInfo.hStdInput = pipe_out; + startupInfo.hStdOutput = pipe_out; } else if (new_out != NULL) { startupInfo.hStdOutput = (HANDLE) _get_osfhandle(_fileno(new_out->file)); + } else { + startupInfo.hStdOutput = (HANDLE) _get_osfhandle(1); } if (pipe_err != JANET_HANDLE_NONE) { - startupInfo.hStdInput = pipe_err; + startupInfo.hStdError = pipe_err; } else if (new_err != NULL) { startupInfo.hStdError = (HANDLE) _get_osfhandle(_fileno(new_err->file)); + } else { + startupInfo.hStdError = (HANDLE) _get_osfhandle(2); } /* Use _spawn family of functions. */ /* Windows docs say do this before any spawns. */ _flushall(); - /* TODO - redirection, :p flag */ - if (!CreateProcess(janet_flag_at(flags, 1) ? NULL : path, /* NULL? */ + int cp_failed = 0; + if (!CreateProcess(janet_flag_at(flags, 1) ? NULL : path, (char *) buf->data, /* Single CLI argument */ - NULL, /* no proc inheritance */ - NULL, /* no thread inheritance */ + &saAttr, /* no proc inheritance */ + &saAttr, /* no thread inheritance */ TRUE, /* handle inheritance */ 0, /* flags */ - envp, /* pass in environment */ + use_environ ? NULL : envp, /* pass in environment */ NULL, /* use parents starting directory */ &startupInfo, - &processInfo)) { - janet_panic("failed to create process"); + &processInfo)) { + cp_failed = 1; } if (pipe_in != JANET_HANDLE_NONE) CloseHandle(pipe_in); if (pipe_out != JANET_HANDLE_NONE) CloseHandle(pipe_out); if (pipe_err != JANET_HANDLE_NONE) CloseHandle(pipe_err); + os_execute_cleanup(envp, NULL); + + if (cp_failed) { + janet_panic("failed to create process"); + } + pHandle = processInfo.hProcess; tHandle = processInfo.hThread; - os_execute_cleanup(envp, NULL); - /* Wait and cleanup immedaitely */ if (!is_async) { DWORD code; @@ -612,8 +671,6 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) { /* Use posix_spawn to spawn new process */ - int use_environ = !janet_flag_at(flags, 0); - if (use_environ) { janet_lock_environ(); } From 468e13501c09802cf05ac4a00aeaa4594f01b998 Mon Sep 17 00:00:00 2001 From: John Gabriele Date: Tue, 15 Sep 2020 00:06:09 -0400 Subject: [PATCH 2/8] Update jpm.1 Fix some typos. --- jpm.1 | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/jpm.1 b/jpm.1 index 53204195..3bf4dc1c 100644 --- a/jpm.1 +++ b/jpm.1 @@ -74,6 +74,7 @@ $JANET_LIBPATH, or a reasonable default. See JANET_LIBPATH for more. Sets the C compiler used for compiling native modules and standalone executables. Defaults to cc. +.TP .BR \-\-cpp\-compiler=$CXX Sets the C++ compiler used for compiling native modules and standalone executables. Defaults to c++.. @@ -105,7 +106,6 @@ be created in the ./build/ directory. .TP .BR install\ [\fBrepo...\fR] - When run with no arguments, installs all installable artifacts in the current project to the current JANET_MODPATH for modules and JANET_BINPATH for executables and scripts. Can also take an optional git repository URL and will install all artifacts in that repository instead. @@ -115,7 +115,7 @@ install multiple dependencies in one command. .TP .BR uninstall\ [\fBname...\fR] Uninstall a project installed with install. uninstall expects the name of the project, not the -repository url, path to installed file or executable name. The name of the project must be specified +repository url, path to installed file, or executable name. The name of the project must be specified at the top of the project.janet file in the declare-project form. If no name is given, uninstalls the current project if installed. Will also uninstall multiple packages in one command. @@ -148,7 +148,7 @@ required. List all installed packages in the current syspath. .TP -.BR list-pkgs [\fBsearch\fR] +.BR list-pkgs\ [\fBsearch\fR] List all package aliases in the current package listing that contain the given search string. If no search string is given, prints the entire listing. @@ -172,7 +172,7 @@ like make. run will run a single rule or build a single file. List all rules that can be run via run. This is useful for exploring rules in the project. .TP -.BR rule-tree\ [\fBroot\fR] [\fdepth\fR] +.BR rule-tree\ [\fBroot\fR]\ [\fBdepth\fR] Show rule dependency tree in a pretty format. Optionally provide a rule to use as the tree root, as well as a max depth to print. By default, prints the full tree for all rules. This can be quite long, so it is recommended to give a root rule. @@ -186,7 +186,7 @@ Show all of the paths used when installing and building artifacts. Update the package listing by installing the 'pkgs' package. Same as jpm install pkgs .TP -.BR quickbin [\fBentry\fR] [\fBexecutable\fR] +.BR quickbin\ [\fBentry\fR]\ [\fBexecutable\fR] Create a standalone, statically linked executable from a Janet source file that contains a main function. The main function is the entry point of the program and will receive command line arguments as function arguments. The entry file can import other modules, including native C modules, and @@ -222,7 +222,7 @@ the default location set at compile time, which can be determined with (dyn :sys .RS The location that jpm will use to install libraries to. Defaults to JANET_PATH, but you could set this to a different directory if you want to. Doing so would let you import Janet modules -on the normal system path (JANET_PATH or (dyn :syspath)), but install to a different directory. It is also a more reliable way to install +on the normal system path (JANET_PATH or (dyn :syspath)), but install to a different directory. It is also a more reliable way to install. This variable is overwritten by the --modpath=/some/path if it is provided. .RE @@ -238,7 +238,7 @@ variable. .B JANET_LIBPATH .RS Similar to JANET_HEADERPATH, this path is where jpm will look for -libjanet.a for creating standalong executables. This does not need to be +libjanet.a for creating standalone executables. This does not need to be set on a normal install. If not provided, this will default to /../lib. The --libpath=/some/path option will override this variable. @@ -257,11 +257,13 @@ The --binpath=/some/path will override this variable. The git repository URL that contains a listing of packages. This allows installing packages with shortnames, which is mostly a convenience. However, package dependencies can use short names, package listings can be used to choose a particular set of dependency versions for a whole project. +.RE .B JANET_GIT .RS An optional path to a git executable to use to clone git dependencies. By default, uses "git" on the current $PATH. You shouldn't need to set this if you have a normal install of git. +.RE .SH AUTHOR Written by Calvin Rose From cc0035b1d71a36b4b63cf4ecf3a7c9006eaa9443 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 19 Sep 2020 16:35:32 -0500 Subject: [PATCH 3/8] Add help text to repl line. --- src/boot/boot.janet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index a8a2ff0c..72c047f8 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -2868,7 +2868,7 @@ (when (and (not *compile-only*) (or *should-repl* *no-file*)) (if-not *quiet* - (print "Janet " janet/version "-" janet/build " " (os/which) "/" (os/arch))) + (print "Janet " janet/version "-" janet/build " " (os/which) "/" (os/arch) " - '(doc)' for help")) (flush) (defn getprompt [p] (def [line] (parser/where p)) From 603791c0bab668ffce9af1930e1d9c94b0875c0e Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 19 Sep 2020 16:40:07 -0500 Subject: [PATCH 4/8] Add more help text to doc macro and default repl. --- src/boot/boot.janet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index 72c047f8..a31785bb 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -1702,7 +1702,7 @@ (print (doc-format (string "Bindings:\n\n" (string/join bindings " ")))) (print) (print (doc-format (string "Dynamics:\n\n" (string/join dynamics " ")))) - (print)) + (print "\n Use (doc sym) for more information on a binding.\n")) (defn doc* "Get the documentation for a symbol in a given environment. Function form of doc." From bc1ef813c2baa6d534a1cabf50d34a10f7b0d571 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 19 Sep 2020 16:53:35 -0500 Subject: [PATCH 5/8] Fix whitespace. --- .gitattributes | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitattributes b/.gitattributes index 144d3aa5..7ceffe52 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,10 +1,10 @@ *.janet linguist-language=Clojure -*.janet text eol=lf -*.c text eol=lf -*.h text eol=lf -*.md text eol=lf -*.yml text eol=lf -*.build text eol=lf -*.txt text eol=lf -*.sh text eol=lf +*.janet text eol=lf +*.c text eol=lf +*.h text eol=lf +*.md text eol=lf +*.yml text eol=lf +*.build text eol=lf +*.txt text eol=lf +*.sh text eol=lf From c455bdad1109b24c062833419c91596fe5a9f2e0 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 19 Sep 2020 16:54:56 -0500 Subject: [PATCH 6/8] Get ready for 1.12.2 release. --- CHANGELOG.md | 3 ++- src/conf/janetconf.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6970c62..636ad7fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # Changelog All notable changes to this project will be documented in this file. -## Unreleased - ??? +## 1.12.2 - 2020-09-19 +- Fix `os/execute` regression on windows. - Add :pipe option to `os/spawn`. - Fix docstring typos. diff --git a/src/conf/janetconf.h b/src/conf/janetconf.h index 2a3cdfba..d3dd9ff0 100644 --- a/src/conf/janetconf.h +++ b/src/conf/janetconf.h @@ -28,9 +28,9 @@ #define JANET_VERSION_MAJOR 1 #define JANET_VERSION_MINOR 12 -#define JANET_VERSION_PATCH 1 +#define JANET_VERSION_PATCH 2 #define JANET_VERSION_EXTRA "" -#define JANET_VERSION "1.12.1" +#define JANET_VERSION "1.12.2" /* #define JANET_BUILD "local" */ From d1f0a13ddce5b3ff3e3c2c9ea23a5f7b1eb0f7a9 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 19 Sep 2020 18:47:47 -0500 Subject: [PATCH 7/8] janet_try macro and janet_restore function. This allows catching panics without using pcall. --- src/core/vm.c | 65 +++++++++++++++++++++------------------------ src/include/janet.h | 20 ++++++++++++++ test/helper.janet | 13 ++------- 3 files changed, 52 insertions(+), 46 deletions(-) diff --git a/src/core/vm.c b/src/core/vm.c index 1cbe47ca..326d91a6 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1315,8 +1315,25 @@ static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out) { return JANET_SIGNAL_OK; } +void janet_try_init(JanetTryState *state) { + state->stackn = janet_vm_stackn++; + state->gc_handle = janet_vm_gc_suspend; + state->vm_fiber = janet_vm_fiber; + state->vm_jmp_buf = janet_vm_jmp_buf; + state->vm_return_reg = janet_vm_return_reg; + janet_vm_return_reg = &(state->payload); + janet_vm_jmp_buf = &(state->buf); +} + +void janet_restore(JanetTryState *state) { + janet_vm_stackn = state->stackn; + janet_vm_gc_suspend = state->gc_handle; + janet_vm_fiber = state->vm_fiber; + janet_vm_jmp_buf = state->vm_jmp_buf; + janet_vm_return_reg = state->vm_return_reg; +} + static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *out) { - jmp_buf buf; JanetFiberStatus old_status = janet_fiber_status(fiber); @@ -1349,45 +1366,23 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o } /* Save global state */ - int32_t oldn = janet_vm_stackn++; - int handle = janet_vm_gc_suspend; - JanetFiber *old_vm_fiber = janet_vm_fiber; - jmp_buf *old_vm_jmp_buf = janet_vm_jmp_buf; - Janet *old_vm_return_reg = janet_vm_return_reg; - - /* Setup fiber */ - if (janet_vm_root_fiber == NULL) janet_vm_root_fiber = fiber; - janet_vm_fiber = fiber; - janet_gcroot(janet_wrap_fiber(fiber)); - janet_fiber_set_status(fiber, JANET_STATUS_ALIVE); - janet_vm_return_reg = out; - janet_vm_jmp_buf = &buf; - - /* Run loop */ - JanetSignal signal; - int jmpsig; -#if defined(JANET_BSD) || defined(JANET_APPLE) - jmpsig = _setjmp(buf); -#else - jmpsig = setjmp(buf); -#endif - if (jmpsig) { - signal = (JanetSignal) jmpsig; - } else { + JanetTryState tstate; + JanetSignal signal = janet_try(&tstate); + if (!signal) { + /* Normal setup */ + if (janet_vm_root_fiber == NULL) janet_vm_root_fiber = fiber; + janet_vm_fiber = fiber; + janet_gcroot(janet_wrap_fiber(fiber)); + janet_fiber_set_status(fiber, JANET_STATUS_ALIVE); signal = run_vm(fiber, in); } - /* Tear down fiber */ + /* Restore */ + if (janet_vm_root_fiber == fiber) janet_vm_root_fiber = NULL; janet_fiber_set_status(fiber, signal); janet_gcunroot(janet_wrap_fiber(fiber)); - - /* Restore global state */ - if (janet_vm_root_fiber == fiber) janet_vm_root_fiber = NULL; - janet_vm_gc_suspend = handle; - janet_vm_fiber = old_vm_fiber; - janet_vm_stackn = oldn; - janet_vm_return_reg = old_vm_return_reg; - janet_vm_jmp_buf = old_vm_jmp_buf; + janet_restore(&tstate); + *out = tstate.payload; return signal; } diff --git a/src/include/janet.h b/src/include/janet.h index a3bf38cf..76699d02 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -1030,6 +1030,19 @@ struct JanetFile { int32_t flags; }; +/* For janet_try and janet_restore */ +typedef struct { + /* old state */ + int32_t stackn; + int gc_handle; + JanetFiber *vm_fiber; + jmp_buf *vm_jmp_buf; + Janet *vm_return_reg; + /* new state */ + jmp_buf buf; + Janet payload; +} JanetTryState; + /* Thread types */ #ifdef JANET_THREADS typedef struct JanetThread JanetThread; @@ -1411,6 +1424,13 @@ JANET_API JanetBuffer *janet_pretty(JanetBuffer *buffer, int depth, int flags, J #define JANET_HASH_KEY_SIZE 16 JANET_API void janet_init_hash_key(uint8_t key[JANET_HASH_KEY_SIZE]); #endif +JANET_API void janet_try_init(JanetTryState *state); +#if defined(JANET_BSD) || defined(JANET_APPLE) +#define janet_try(state) (janet_try_init(state), (JanetSignal) _setjmp((state)->buf)) +#else +#define janet_try(state) (janet_try_init(state), (JanetSignal) setjmp((state)->buf)) +#endif +JANET_API void janet_restore(JanetTryState *state); JANET_API int janet_equals(Janet x, Janet y); JANET_API int32_t janet_hash(Janet x); JANET_API int janet_compare(Janet x, Janet y); diff --git a/test/helper.janet b/test/helper.janet index 373eb816..60985478 100644 --- a/test/helper.janet +++ b/test/helper.janet @@ -3,7 +3,6 @@ (var num-tests-passed 0) (var num-tests-run 0) (var suite-num 0) -(var numchecks 0) (var start-time 0) (defn assert @@ -13,16 +12,8 @@ (++ num-tests-run) (when x (++ num-tests-passed)) (if x - (do - (when (= numchecks 25) - (set numchecks 0) - (print)) - (++ numchecks) - (file/write stdout "\e[32m✔\e[0m")) - (do - (file/write stdout "\n\e[31m✘\e[0m ") - (set numchecks 0) - (print e))) + (xprintf stdout "\e[32m✔\e[0m %s: %v" (string e) x) + (xprintf stdout "\n\e[31m✘\e[0m %s: %v" (string e) x)) x) (defmacro assert-error From b91fe8be5ac4fb5bd7f1580cea53b8b1637f71c2 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 20 Sep 2020 12:03:59 -0500 Subject: [PATCH 8/8] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 636ad7fa..06340de6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # Changelog All notable changes to this project will be documented in this file. -## 1.12.2 - 2020-09-19 +## 1.12.2 - 2020-09-20 +- Add janet\_try and janet\_restore to C API. - Fix `os/execute` regression on windows. - Add :pipe option to `os/spawn`. - Fix docstring typos.