From 00daf2084335c764f16c8edac658f7ca3f121f87 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Wed, 28 Apr 2021 15:30:27 -0400 Subject: [PATCH 01/13] adds Process.exec(cmd, [args]) --- src/cli/modules.c | 2 ++ src/module/os.c | 56 ++++++++++++++++++++++++++++++++++++++++++ src/module/os.h | 7 ++++++ src/module/os.wren | 9 +++++++ src/module/os.wren.inc | 9 +++++++ 5 files changed, 83 insertions(+) diff --git a/src/cli/modules.c b/src/cli/modules.c index a3c7f6c1..4e20cada 100644 --- a/src/cli/modules.c +++ b/src/cli/modules.c @@ -32,6 +32,7 @@ extern void processCwd(WrenVM* vm); extern void processPid(WrenVM* vm); extern void processPpid(WrenVM* vm); extern void processVersion(WrenVM* vm); +extern void processExec(WrenVM* vm); extern void statPath(WrenVM* vm); extern void statBlockCount(WrenVM* vm); extern void statBlockSize(WrenVM* vm); @@ -180,6 +181,7 @@ static ModuleRegistry modules[] = STATIC_METHOD("pid", processPid) STATIC_METHOD("ppid", processPpid) STATIC_METHOD("version", processVersion) + STATIC_METHOD("exec_(_,_,_)", processExec) END_CLASS END_MODULE MODULE(repl) diff --git a/src/module/os.c b/src/module/os.c index c13cd33f..14d9144d 100644 --- a/src/module/os.c +++ b/src/module/os.c @@ -1,6 +1,9 @@ #include "os.h" #include "uv.h" #include "wren.h" +#include "vm.h" +#include "scheduler.h" +#include #if __APPLE__ #include "TargetConditionals.h" @@ -128,3 +131,56 @@ void processVersion(WrenVM* vm) { wrenEnsureSlots(vm, 1); wrenSetSlotString(vm, 0, WREN_VERSION_STRING); } + +void proccesOnExit(uv_process_t *req, int64_t exit_status, int term_signal) { + uv_close((uv_handle_t*) req, NULL); + schedulerResume(((processData*)req->data)->fiber, true); + wrenSetSlotDouble(getVM(), 2, exit_status); + schedulerFinishResume(); + + free((processData*)(req->data)); + free(req); +} + +void processExec(WrenVM* vm) { + uv_process_t *child_req = malloc(sizeof(uv_process_t)); + processData* data = malloc(sizeof(processData)); + memset(data, 0, sizeof(processData)); + + // 2 extra slots in args for: + // - the program to execute (head) + // - NULL terminator (tail) + const char* args[PROCESS_MAX_EXEC_ARGUMENTS + 2]; + uv_loop_t *loop = getLoop(); + + args[0] = wrenGetSlotString(vm,1); + int argCount = wrenGetListCount(vm,2); + WrenHandle *fiber = wrenGetSlotHandle(vm,3); + + // TODO: allocate args dynamically to remove this limitation? + if (argCount > PROCESS_MAX_EXEC_ARGUMENTS) { + wrenSetSlotString(vm, 0, "Too many process arguments."); + wrenAbortFiber(vm, 0); + } + + wrenEnsureSlots(vm,4); + for (int i = 0; i < argCount; i++) { + wrenGetListElement(vm, 2, i, 4); + args[i+1] = wrenGetSlotString(vm,4); + } + args[argCount+1] = NULL; + + data->options.exit_cb = proccesOnExit; + data->options.file = args[0]; + data->options.args = args; + + data->fiber = fiber; + child_req->data = data; + + int r; + if ((r = uv_spawn(loop, child_req, &data->options))) { + fprintf(stderr, "could not launch %s, %s\n", args[0], uv_strerror(r)); + wrenSetSlotString(vm, 0, "Could not spawn process."); + wrenAbortFiber(vm, 0); + } +} diff --git a/src/module/os.h b/src/module/os.h index b9988f80..2f306d75 100644 --- a/src/module/os.h +++ b/src/module/os.h @@ -1,9 +1,16 @@ #ifndef process_h #define process_h +#include "uv.h" #include "wren.h" #define WREN_PATH_MAX 4096 +#define PROCESS_MAX_EXEC_ARGUMENTS 20 + +typedef struct { + WrenHandle* fiber; + uv_process_options_t options; +} processData; // Stores the command line arguments passed to the CLI. void osSetArguments(int argc, const char* argv[]); diff --git a/src/module/os.wren b/src/module/os.wren index 4d9ec25a..3c5c3ae7 100644 --- a/src/module/os.wren +++ b/src/module/os.wren @@ -1,3 +1,5 @@ +import "scheduler" for Scheduler + class Platform { foreign static homePath foreign static isPosix @@ -10,6 +12,13 @@ class Process { // TODO: This will need to be smarter when wren supports CLI options. static arguments { allArguments.count >= 2 ? allArguments[2..-1] : [] } + // executes a command passing it the specified arguments (as a list) + static exec(cmd, argList) { + exec_(cmd, argList, Fiber.current) + return Scheduler.runNextScheduled_() + } + + foreign static exec_(cmd, args, fiber) foreign static allArguments foreign static cwd foreign static pid diff --git a/src/module/os.wren.inc b/src/module/os.wren.inc index 8c7c8522..17d5b55d 100644 --- a/src/module/os.wren.inc +++ b/src/module/os.wren.inc @@ -2,6 +2,8 @@ // from `src/module/os.wren` using `util/wren_to_c_string.py` static const char* osModuleSource = +"import \"scheduler\" for Scheduler\n" +"\n" "class Platform {\n" " foreign static homePath\n" " foreign static isPosix\n" @@ -14,6 +16,13 @@ static const char* osModuleSource = " // TODO: This will need to be smarter when wren supports CLI options.\n" " static arguments { allArguments.count >= 2 ? allArguments[2..-1] : [] }\n" "\n" +" // executes a command passing it the specified arguments (as a list)\n" +" static exec(cmd, argList) {\n" +" exec_(cmd, argList, Fiber.current)\n" +" return Scheduler.runNextScheduled_()\n" +" }\n" +"\n" +" foreign static exec_(cmd, args, fiber)\n" " foreign static allArguments\n" " foreign static cwd\n" " foreign static pid\n" From c2a105f29eccf1922e2cdd176bc48ac09bcdf1fd Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Wed, 28 Apr 2021 15:53:33 -0400 Subject: [PATCH 02/13] cannot spell --- src/module/os.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/module/os.c b/src/module/os.c index 14d9144d..765ad76b 100644 --- a/src/module/os.c +++ b/src/module/os.c @@ -132,7 +132,7 @@ void processVersion(WrenVM* vm) { wrenSetSlotString(vm, 0, WREN_VERSION_STRING); } -void proccesOnExit(uv_process_t *req, int64_t exit_status, int term_signal) { +void processOnExit(uv_process_t *req, int64_t exit_status, int term_signal) { uv_close((uv_handle_t*) req, NULL); schedulerResume(((processData*)req->data)->fiber, true); wrenSetSlotDouble(getVM(), 2, exit_status); @@ -170,7 +170,7 @@ void processExec(WrenVM* vm) { } args[argCount+1] = NULL; - data->options.exit_cb = proccesOnExit; + data->options.exit_cb = processOnExit; data->options.file = args[0]; data->options.args = args; From ca045135436ec8346c9e7568f59cc472deb49136 Mon Sep 17 00:00:00 2001 From: ruby0x1 Date: Sun, 2 May 2021 14:33:27 -0700 Subject: [PATCH 03/13] refactor; fix ownership of strings, clean up the uv handle safely, add some todos --- src/cli/cli_common.h | 24 ++++++++ src/module/os.c | 121 ++++++++++++++++++++++++----------------- src/module/os.h | 3 +- src/module/os.wren | 5 +- src/module/os.wren.inc | 5 +- 5 files changed, 101 insertions(+), 57 deletions(-) create mode 100644 src/cli/cli_common.h diff --git a/src/cli/cli_common.h b/src/cli/cli_common.h new file mode 100644 index 00000000..1e7fe855 --- /dev/null +++ b/src/cli/cli_common.h @@ -0,0 +1,24 @@ +#ifndef cli_common_h +#define cli_common_h + +#include +#include + +inline char* cli_strdup(const char* s) { + size_t len = strlen(s) + 1; + char* m = (char*)malloc(len); + if (m == NULL) return NULL; + return memcpy(m, s, len); +} + +inline char* cli_strndup(const char* s, size_t n) { + char* m; + size_t len = strlen(s); + if (n < len) len = n; + m = (char*)malloc(len + 1); + if (m == NULL) return NULL; + m[len] = '\0'; + return memcpy(m, s, len); +} + +#endif \ No newline at end of file diff --git a/src/module/os.c b/src/module/os.c index 765ad76b..f8f9b386 100644 --- a/src/module/os.c +++ b/src/module/os.c @@ -3,7 +3,9 @@ #include "wren.h" #include "vm.h" #include "scheduler.h" -#include +#include "cli_common.h" + +#include #if __APPLE__ #include "TargetConditionals.h" @@ -132,55 +134,76 @@ void processVersion(WrenVM* vm) { wrenSetSlotString(vm, 0, WREN_VERSION_STRING); } -void processOnExit(uv_process_t *req, int64_t exit_status, int term_signal) { - uv_close((uv_handle_t*) req, NULL); - schedulerResume(((processData*)req->data)->fiber, true); - wrenSetSlotDouble(getVM(), 2, exit_status); - schedulerFinishResume(); +// Called when the UV handle for a process is done, so we can free it +static void processOnClose(uv_handle_t* req) +{ + free((void*)req); +} + +// Called when a process is finished running +static void processOnExit(uv_process_t* req, int64_t exit_status, int term_signal) +{ + ProcessData* data = (ProcessData*)req->data; + WrenHandle* fiber = data->fiber; + + uv_close((uv_handle_t*)req, processOnClose); - free((processData*)(req->data)); - free(req); + int index = 0; + char* arg = data->options.args[index]; + while (arg != NULL) + { + free(arg); + index += 1; + arg = data->options.args[index]; + } + + free((void*)data); + + schedulerResume(fiber, true); + wrenSetSlotDouble(getVM(), 2, (double)exit_status); + schedulerFinishResume(); } -void processExec(WrenVM* vm) { - uv_process_t *child_req = malloc(sizeof(uv_process_t)); - processData* data = malloc(sizeof(processData)); - memset(data, 0, sizeof(processData)); - - // 2 extra slots in args for: - // - the program to execute (head) - // - NULL terminator (tail) - const char* args[PROCESS_MAX_EXEC_ARGUMENTS + 2]; - uv_loop_t *loop = getLoop(); - - args[0] = wrenGetSlotString(vm,1); - int argCount = wrenGetListCount(vm,2); - WrenHandle *fiber = wrenGetSlotHandle(vm,3); - - // TODO: allocate args dynamically to remove this limitation? - if (argCount > PROCESS_MAX_EXEC_ARGUMENTS) { - wrenSetSlotString(vm, 0, "Too many process arguments."); - wrenAbortFiber(vm, 0); - } - - wrenEnsureSlots(vm,4); - for (int i = 0; i < argCount; i++) { - wrenGetListElement(vm, 2, i, 4); - args[i+1] = wrenGetSlotString(vm,4); - } - args[argCount+1] = NULL; - - data->options.exit_cb = processOnExit; - data->options.file = args[0]; - data->options.args = args; - - data->fiber = fiber; - child_req->data = data; - - int r; - if ((r = uv_spawn(loop, child_req, &data->options))) { - fprintf(stderr, "could not launch %s, %s\n", args[0], uv_strerror(r)); - wrenSetSlotString(vm, 0, "Could not spawn process."); - wrenAbortFiber(vm, 0); - } +void processExec(WrenVM* vm) +{ + ProcessData* data = (ProcessData*)malloc(sizeof(ProcessData)); + memset(data, 0, sizeof(ProcessData)); + + //:todo: add env + cwd + flags args + + char* cmd = cli_strdup(wrenGetSlotString(vm, 1)); + + data->options.file = cmd; + data->options.exit_cb = processOnExit; + data->fiber = wrenGetSlotHandle(vm, 3); + + int argCount = wrenGetListCount(vm, 2); + int argsSize = sizeof(char*) * (argCount + 2); + + // First argument is the cmd, last+1 is NULL + data->options.args = (char**)malloc(argsSize); + data->options.args[0] = cmd; + data->options.args[argCount + 1] = NULL; + + wrenEnsureSlots(vm, 3); + for (int i = 0; i < argCount; i++) + { + wrenGetListElement(vm, 2, i, 3); + //:todo: ensure this is a string, and report an error if not + char* arg = cli_strdup(wrenGetSlotString(vm, 3)); + data->options.args[i + 1] = arg; + } + + uv_process_t* child_req = (uv_process_t*)malloc(sizeof(uv_process_t)); + memset(child_req, 0, sizeof(uv_process_t)); + + child_req->data = data; + + int r; + if ((r = uv_spawn(getLoop(), child_req, &data->options))) + { + fprintf(stderr, "Could not launch %s, reason: %s\n", cmd, uv_strerror(r)); + wrenSetSlotString(vm, 0, "Could not spawn process."); + wrenAbortFiber(vm, 0); + } } diff --git a/src/module/os.h b/src/module/os.h index 2f306d75..55c8beed 100644 --- a/src/module/os.h +++ b/src/module/os.h @@ -5,12 +5,11 @@ #include "wren.h" #define WREN_PATH_MAX 4096 -#define PROCESS_MAX_EXEC_ARGUMENTS 20 typedef struct { WrenHandle* fiber; uv_process_options_t options; -} processData; +} ProcessData; // Stores the command line arguments passed to the CLI. void osSetArguments(int argc, const char* argv[]); diff --git a/src/module/os.wren b/src/module/os.wren index 3c5c3ae7..e076dda7 100644 --- a/src/module/os.wren +++ b/src/module/os.wren @@ -12,9 +12,8 @@ class Process { // TODO: This will need to be smarter when wren supports CLI options. static arguments { allArguments.count >= 2 ? allArguments[2..-1] : [] } - // executes a command passing it the specified arguments (as a list) - static exec(cmd, argList) { - exec_(cmd, argList, Fiber.current) + static exec(cmd, args) { + exec_(cmd, args, Fiber.current) return Scheduler.runNextScheduled_() } diff --git a/src/module/os.wren.inc b/src/module/os.wren.inc index 17d5b55d..17ef658e 100644 --- a/src/module/os.wren.inc +++ b/src/module/os.wren.inc @@ -16,9 +16,8 @@ static const char* osModuleSource = " // TODO: This will need to be smarter when wren supports CLI options.\n" " static arguments { allArguments.count >= 2 ? allArguments[2..-1] : [] }\n" "\n" -" // executes a command passing it the specified arguments (as a list)\n" -" static exec(cmd, argList) {\n" -" exec_(cmd, argList, Fiber.current)\n" +" static exec(cmd, args) {\n" +" exec_(cmd, args, Fiber.current)\n" " return Scheduler.runNextScheduled_()\n" " }\n" "\n" From 2b74cd6769d19ce70ce32d024e8d595e38d8e9dc Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 2 May 2021 18:43:20 -0400 Subject: [PATCH 04/13] more api --- src/cli/modules.c | 2 +- src/module/os.c | 2 +- src/module/os.wren | 12 ++++++++++-- src/module/os.wren.inc | 12 ++++++++++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/cli/modules.c b/src/cli/modules.c index 4e20cada..f87a501c 100644 --- a/src/cli/modules.c +++ b/src/cli/modules.c @@ -181,7 +181,7 @@ static ModuleRegistry modules[] = STATIC_METHOD("pid", processPid) STATIC_METHOD("ppid", processPpid) STATIC_METHOD("version", processVersion) - STATIC_METHOD("exec_(_,_,_)", processExec) + STATIC_METHOD("exec_(_,_,_,_,_)", processExec) END_CLASS END_MODULE MODULE(repl) diff --git a/src/module/os.c b/src/module/os.c index f8f9b386..bd00be2a 100644 --- a/src/module/os.c +++ b/src/module/os.c @@ -175,7 +175,7 @@ void processExec(WrenVM* vm) data->options.file = cmd; data->options.exit_cb = processOnExit; - data->fiber = wrenGetSlotHandle(vm, 3); + data->fiber = wrenGetSlotHandle(vm, 5); int argCount = wrenGetListCount(vm, 2); int argsSize = sizeof(char*) * (argCount + 2); diff --git a/src/module/os.wren b/src/module/os.wren index e076dda7..8f0165e9 100644 --- a/src/module/os.wren +++ b/src/module/os.wren @@ -13,11 +13,19 @@ class Process { static arguments { allArguments.count >= 2 ? allArguments[2..-1] : [] } static exec(cmd, args) { - exec_(cmd, args, Fiber.current) + return exec(cmd, args, null, null) + } + + static exec(cmd, args, cwd) { + return exec(cmd, args, cwd, null) + } + + static exec(cmd, args, cwd, env) { + exec_(cmd, args, cwd, env, Fiber.current) return Scheduler.runNextScheduled_() } - foreign static exec_(cmd, args, fiber) + foreign static exec_(cmd, args, cwd, env, fiber) foreign static allArguments foreign static cwd foreign static pid diff --git a/src/module/os.wren.inc b/src/module/os.wren.inc index 17ef658e..2c658f89 100644 --- a/src/module/os.wren.inc +++ b/src/module/os.wren.inc @@ -17,11 +17,19 @@ static const char* osModuleSource = " static arguments { allArguments.count >= 2 ? allArguments[2..-1] : [] }\n" "\n" " static exec(cmd, args) {\n" -" exec_(cmd, args, Fiber.current)\n" +" return exec(cmd, args, null, null)\n" +" }\n" +"\n" +" static exec(cmd, args, cwd) { \n" +" return exec(cmd, args, cwd, null) \n" +" }\n" +" \n" +" static exec(cmd, args, cwd, env) { \n" +" exec_(cmd, args, cwd, env, Fiber.current)\n" " return Scheduler.runNextScheduled_()\n" " }\n" "\n" -" foreign static exec_(cmd, args, fiber)\n" +" foreign static exec_(cmd, args, cwd, env, fiber)\n" " foreign static allArguments\n" " foreign static cwd\n" " foreign static pid\n" From aa2033682fd35a585f470dfe23884255ec0aef76 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 2 May 2021 18:50:27 -0400 Subject: [PATCH 05/13] single exec --- src/module/os.wren | 4 ++++ src/module/os.wren.inc | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/module/os.wren b/src/module/os.wren index 8f0165e9..942380cb 100644 --- a/src/module/os.wren +++ b/src/module/os.wren @@ -12,6 +12,10 @@ class Process { // TODO: This will need to be smarter when wren supports CLI options. static arguments { allArguments.count >= 2 ? allArguments[2..-1] : [] } + static exec(cmd) { + return exec(cmd, [], null, null) + } + static exec(cmd, args) { return exec(cmd, args, null, null) } diff --git a/src/module/os.wren.inc b/src/module/os.wren.inc index 2c658f89..bee24871 100644 --- a/src/module/os.wren.inc +++ b/src/module/os.wren.inc @@ -16,6 +16,10 @@ static const char* osModuleSource = " // TODO: This will need to be smarter when wren supports CLI options.\n" " static arguments { allArguments.count >= 2 ? allArguments[2..-1] : [] }\n" "\n" +" static exec(cmd) {\n" +" return exec(cmd, [], null, null)\n" +" }\n" +"\n" " static exec(cmd, args) {\n" " return exec(cmd, args, null, null)\n" " }\n" From 58c4a8ba90e58b1292f80fe8331ee2d36645a620 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 2 May 2021 18:50:35 -0400 Subject: [PATCH 06/13] exec tests --- test/os/process/exec.wren | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test/os/process/exec.wren diff --git a/test/os/process/exec.wren b/test/os/process/exec.wren new file mode 100644 index 00000000..03624769 --- /dev/null +++ b/test/os/process/exec.wren @@ -0,0 +1,22 @@ +import "os" for Platform, Process + +var result +if(Platform.name == "Windows") { + result = Process.exec("cmd.exe", []) +} else { + // works on Mac + result = Process.exec("/usr/bin/true", []) +} +System.print(result) // expect: 0 + +if (Platform.isPosix) { + // known output of success/fail based on only command name + System.print(Process.exec("/usr/bin/true")) // expect: 0 + System.print(Process.exec("/usr/bin/false")) // expect: 1 + // these test that our arguments are being passed as it proves + // they effect the result code returned + System.print(Process.exec("/bin/test", ["2", "-eq", "2"])) // expect: 0 + System.print(Process.exec("/bin/test", ["2", "-eq", "3"])) // expect: 1 +} else if (Platform.name == "Windows") { + // TODO: more windows argument specific tests +} From 5ca236733a92d3fabec23a42305e8faaabe07e06 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 2 May 2021 18:57:08 -0400 Subject: [PATCH 07/13] cwd --- src/module/os.c | 7 +++++++ test/os/process/exec.wren | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/module/os.c b/src/module/os.c index bd00be2a..34c28d13 100644 --- a/src/module/os.c +++ b/src/module/os.c @@ -164,6 +164,8 @@ static void processOnExit(uv_process_t* req, int64_t exit_status, int term_signa schedulerFinishResume(); } +// 1 2 3 4 5 +// exec_(cmd, args, cwd, env, fiber) void processExec(WrenVM* vm) { ProcessData* data = (ProcessData*)malloc(sizeof(ProcessData)); @@ -173,6 +175,11 @@ void processExec(WrenVM* vm) char* cmd = cli_strdup(wrenGetSlotString(vm, 1)); + if (wrenGetSlotType(vm, 3) != WREN_TYPE_NULL) { + const char* cwd = wrenGetSlotString(vm, 3); + data->options.cwd = cwd; + } + data->options.file = cmd; data->options.exit_cb = processOnExit; data->fiber = wrenGetSlotHandle(vm, 5); diff --git a/test/os/process/exec.wren b/test/os/process/exec.wren index 03624769..2956d6cf 100644 --- a/test/os/process/exec.wren +++ b/test/os/process/exec.wren @@ -9,6 +9,8 @@ if(Platform.name == "Windows") { } System.print(result) // expect: 0 +// basics + if (Platform.isPosix) { // known output of success/fail based on only command name System.print(Process.exec("/usr/bin/true")) // expect: 0 @@ -20,3 +22,14 @@ if (Platform.isPosix) { } else if (Platform.name == "Windows") { // TODO: more windows argument specific tests } + +// cwd + +if (Platform.isPosix) { + // tests exists in our root + System.print(Process.exec("ls", ["test"])) // expect: 0 + // but not in our `src` folder + System.print(Process.exec("ls", ["test"], "./src/")) // expect: 1 +} else if (Platform.name == "Windows") { + // TODO: can this be done with dir on windows? +} \ No newline at end of file From 3384115bcb42684a3446993ff2c354c6404ff690 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 2 May 2021 20:09:16 -0400 Subject: [PATCH 08/13] env --- src/module/os.c | 50 +++++++++++++++++++++++++++++++++++++-- src/module/os.wren | 12 +++++++++- src/module/os.wren.inc | 12 +++++++++- test/os/process/exec.wren | 16 +++++++++++++ 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/module/os.c b/src/module/os.c index 34c28d13..2f45a749 100644 --- a/src/module/os.c +++ b/src/module/os.c @@ -157,6 +157,17 @@ static void processOnExit(uv_process_t* req, int64_t exit_status, int term_signa arg = data->options.args[index]; } + index = 0; + if (data->options.env) { + char* env = data->options.env[index]; + while (env != NULL) + { + free(env); + index += 1; + env = data->options.args[index]; + } + } + free((void*)data); schedulerResume(fiber, true); @@ -184,6 +195,41 @@ void processExec(WrenVM* vm) data->options.exit_cb = processOnExit; data->fiber = wrenGetSlotHandle(vm, 5); + wrenEnsureSlots(vm, 6); + + if (wrenGetSlotType(vm, 4) == WREN_TYPE_NULL) { + // no environment specified + } else if (wrenGetSlotType(vm, 4) == WREN_TYPE_LIST) { + // fprintf(stderr,"got list\n"); + int envCount = wrenGetListCount(vm, 4); + int envSize = sizeof(char*) * (envCount + 1); + + data->options.env = (char**)malloc(envSize); + data->options.env[envCount] = NULL; + + // fprintf(stderr,"envsize %d\n", envCount); + for (int i = 0; i < envCount ; i++) + { + + wrenGetListElement(vm, 4, i, 6); + //:todo: ensure this is a string, and report an error if not + + if (wrenGetSlotType(vm, 6) != WREN_TYPE_STRING) { + wrenSetSlotString(vm, 0, "aruments to params are suppose to be string"); + wrenAbortFiber(vm, 0); + } + char* envKeyPlusValue = cli_strdup(wrenGetSlotString(vm, 6)); + // fprintf(stderr,"key: %s\n", envKeyPlusValue); + // fprintf(stderr,"setting %s\n", envKeyPlusValue); + // char* equalSplit = strchr(envKeyPlusValue, '='); + // *equalSplit = '\0'; + // char* key = envKeyPlusValue; + // char* value = equalSplit + 1; + + data->options.env[i] = envKeyPlusValue; + } + } + int argCount = wrenGetListCount(vm, 2); int argsSize = sizeof(char*) * (argCount + 2); @@ -192,7 +238,6 @@ void processExec(WrenVM* vm) data->options.args[0] = cmd; data->options.args[argCount + 1] = NULL; - wrenEnsureSlots(vm, 3); for (int i = 0; i < argCount; i++) { wrenGetListElement(vm, 2, i, 3); @@ -209,7 +254,8 @@ void processExec(WrenVM* vm) int r; if ((r = uv_spawn(getLoop(), child_req, &data->options))) { - fprintf(stderr, "Could not launch %s, reason: %s\n", cmd, uv_strerror(r)); + // should be stderr??? but no idea how to make tests work/pass with that + fprintf(stdout, "Could not launch %s, reason: %s\n", cmd, uv_strerror(r)); wrenSetSlotString(vm, 0, "Could not spawn process."); wrenAbortFiber(vm, 0); } diff --git a/src/module/os.wren b/src/module/os.wren index 942380cb..eec8d92a 100644 --- a/src/module/os.wren +++ b/src/module/os.wren @@ -24,7 +24,17 @@ class Process { return exec(cmd, args, cwd, null) } - static exec(cmd, args, cwd, env) { + static exec(cmd, args, cwd, envMap) { + var env = [] + if (envMap is Map) { + for (entry in envMap) { + env.add([entry.key, entry.value].join("=")) + } + } else if (envMap == null) { + env = null + } else { + Fiber.abort("environment vars must be passed as a Map") + } exec_(cmd, args, cwd, env, Fiber.current) return Scheduler.runNextScheduled_() } diff --git a/src/module/os.wren.inc b/src/module/os.wren.inc index bee24871..42f2955d 100644 --- a/src/module/os.wren.inc +++ b/src/module/os.wren.inc @@ -28,7 +28,17 @@ static const char* osModuleSource = " return exec(cmd, args, cwd, null) \n" " }\n" " \n" -" static exec(cmd, args, cwd, env) { \n" +" static exec(cmd, args, cwd, envMap) { \n" +" var env = []\n" +" if (envMap is Map) {\n" +" for (entry in envMap) {\n" +" env.add([entry.key, entry.value].join(\"=\"))\n" +" }\n" +" } else if (envMap == null) {\n" +" env = null\n" +" } else {\n" +" Fiber.abort(\"environment vars must be passed as a Map\")\n" +" }\n" " exec_(cmd, args, cwd, env, Fiber.current)\n" " return Scheduler.runNextScheduled_()\n" " }\n" diff --git a/test/os/process/exec.wren b/test/os/process/exec.wren index 2956d6cf..620e80f8 100644 --- a/test/os/process/exec.wren +++ b/test/os/process/exec.wren @@ -32,4 +32,20 @@ if (Platform.isPosix) { System.print(Process.exec("ls", ["test"], "./src/")) // expect: 1 } else if (Platform.name == "Windows") { // TODO: can this be done with dir on windows? +} + +// env + +if (Platform.isPosix) { + System.print(Process.exec("/usr/bin/true",[],null,{})) // expect: 0 + + var fiber = Fiber.new { + Process.exec("ls",[],null,{"PATH": "/binx/"}) + } + var r = fiber.try() + System.print(r) + // expect: Could not launch ls, reason: no such file or directory + // expect: Could not spawn process. +} else if (Platform.name == "Windows") { + } \ No newline at end of file From df6e8baaeef32dab5991b28b3c89a3cd2a58176e Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 2 May 2021 20:14:59 -0400 Subject: [PATCH 09/13] questions --- test/os/process/exec.wren | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/os/process/exec.wren b/test/os/process/exec.wren index 620e80f8..eaf63a33 100644 --- a/test/os/process/exec.wren +++ b/test/os/process/exec.wren @@ -1,5 +1,12 @@ import "os" for Platform, Process +var TRY = Fn.new { |fn| + var fiber = Fiber.new { + fn.call() + } + return fiber.try() +} + var result if(Platform.name == "Windows") { result = Process.exec("cmd.exe", []) @@ -38,13 +45,13 @@ if (Platform.isPosix) { if (Platform.isPosix) { System.print(Process.exec("/usr/bin/true",[],null,{})) // expect: 0 - - var fiber = Fiber.new { - Process.exec("ls",[],null,{"PATH": "/binx/"}) + var r = TRY.call { + Process.exec("ls",[],null,{"PATH": "/binx/"}) } - var r = fiber.try() System.print(r) + // TODO: should be on stderr // expect: Could not launch ls, reason: no such file or directory + // TODO: should this be a runtime error????? // expect: Could not spawn process. } else if (Platform.name == "Windows") { From 4842cd574e32dc6a99e51cff765440820ee79f4e Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 2 May 2021 20:21:54 -0400 Subject: [PATCH 10/13] windows is the outlier --- src/module/os.c | 4 +--- test/os/process/exec.wren | 43 +++++++++++++++++++-------------------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/module/os.c b/src/module/os.c index 2f45a749..5a3e6020 100644 --- a/src/module/os.c +++ b/src/module/os.c @@ -212,10 +212,8 @@ void processExec(WrenVM* vm) { wrenGetListElement(vm, 4, i, 6); - //:todo: ensure this is a string, and report an error if not - if (wrenGetSlotType(vm, 6) != WREN_TYPE_STRING) { - wrenSetSlotString(vm, 0, "aruments to params are suppose to be string"); + wrenSetSlotString(vm, 0, "arguments to env are supposed to be string"); wrenAbortFiber(vm, 0); } char* envKeyPlusValue = cli_strdup(wrenGetSlotString(vm, 6)); diff --git a/test/os/process/exec.wren b/test/os/process/exec.wren index eaf63a33..c2db18da 100644 --- a/test/os/process/exec.wren +++ b/test/os/process/exec.wren @@ -9,50 +9,49 @@ var TRY = Fn.new { |fn| var result if(Platform.name == "Windows") { - result = Process.exec("cmd.exe", []) + result = Process.exec("cmd.exe") } else { - // works on Mac - result = Process.exec("/usr/bin/true", []) + result = Process.exec("true") } System.print(result) // expect: 0 // basics -if (Platform.isPosix) { +if (Platform.isWindows) { + // TODO: more windows argument specific tests +} else { // known output of success/fail based on only command name - System.print(Process.exec("/usr/bin/true")) // expect: 0 - System.print(Process.exec("/usr/bin/false")) // expect: 1 + System.print(Process.exec("true")) // expect: 0 + System.print(Process.exec("false")) // expect: 1 // these test that our arguments are being passed as it proves // they effect the result code returned - System.print(Process.exec("/bin/test", ["2", "-eq", "2"])) // expect: 0 - System.print(Process.exec("/bin/test", ["2", "-eq", "3"])) // expect: 1 -} else if (Platform.name == "Windows") { - // TODO: more windows argument specific tests + System.print(Process.exec("test", ["2", "-eq", "2"])) // expect: 0 + System.print(Process.exec("test", ["2", "-eq", "3"])) // expect: 1 } // cwd -if (Platform.isPosix) { - // tests exists in our root +if (Platform.isWindows) { + // TODO: can this be done with dir on windows? +} else { + // tests exists in our project folder System.print(Process.exec("ls", ["test"])) // expect: 0 - // but not in our `src` folder + // but does not in our `src` folder System.print(Process.exec("ls", ["test"], "./src/")) // expect: 1 -} else if (Platform.name == "Windows") { - // TODO: can this be done with dir on windows? } // env -if (Platform.isPosix) { - System.print(Process.exec("/usr/bin/true",[],null,{})) // expect: 0 - var r = TRY.call { - Process.exec("ls",[],null,{"PATH": "/binx/"}) +if (Platform.name == "Windows") { + // TODO: how? +} else { + System.print(Process.exec("true",[],null,{})) // expect: 0 + var result = TRY.call { + Process.exec("ls",[],null,{"PATH": "/whereiscarmen/"}) } - System.print(r) + System.print(result) // TODO: should be on stderr // expect: Could not launch ls, reason: no such file or directory // TODO: should this be a runtime error????? // expect: Could not spawn process. -} else if (Platform.name == "Windows") { - } \ No newline at end of file From 0791b4c09a4adadaeeb8b349bd4869e373ed7e0e Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 2 May 2021 20:26:22 -0400 Subject: [PATCH 11/13] clean ups --- src/module/os.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/module/os.c b/src/module/os.c index 5a3e6020..3eb483e7 100644 --- a/src/module/os.c +++ b/src/module/os.c @@ -164,7 +164,7 @@ static void processOnExit(uv_process_t* req, int64_t exit_status, int term_signa { free(env); index += 1; - env = data->options.args[index]; + env = data->options.env[index]; } } @@ -200,30 +200,20 @@ void processExec(WrenVM* vm) if (wrenGetSlotType(vm, 4) == WREN_TYPE_NULL) { // no environment specified } else if (wrenGetSlotType(vm, 4) == WREN_TYPE_LIST) { - // fprintf(stderr,"got list\n"); int envCount = wrenGetListCount(vm, 4); int envSize = sizeof(char*) * (envCount + 1); data->options.env = (char**)malloc(envSize); data->options.env[envCount] = NULL; - // fprintf(stderr,"envsize %d\n", envCount); for (int i = 0; i < envCount ; i++) { - wrenGetListElement(vm, 4, i, 6); if (wrenGetSlotType(vm, 6) != WREN_TYPE_STRING) { - wrenSetSlotString(vm, 0, "arguments to env are supposed to be string"); + wrenSetSlotString(vm, 0, "arguments to env are supposed to be strings"); wrenAbortFiber(vm, 0); } char* envKeyPlusValue = cli_strdup(wrenGetSlotString(vm, 6)); - // fprintf(stderr,"key: %s\n", envKeyPlusValue); - // fprintf(stderr,"setting %s\n", envKeyPlusValue); - // char* equalSplit = strchr(envKeyPlusValue, '='); - // *equalSplit = '\0'; - // char* key = envKeyPlusValue; - // char* value = equalSplit + 1; - data->options.env[i] = envKeyPlusValue; } } @@ -239,7 +229,10 @@ void processExec(WrenVM* vm) for (int i = 0; i < argCount; i++) { wrenGetListElement(vm, 2, i, 3); - //:todo: ensure this is a string, and report an error if not + if (wrenGetSlotType(vm, 3) != WREN_TYPE_STRING) { + wrenSetSlotString(vm, 0, "arguments to args are supposed to be strings"); + wrenAbortFiber(vm, 0); + } char* arg = cli_strdup(wrenGetSlotString(vm, 3)); data->options.args[i + 1] = arg; } From 6c3be41e98be6abf7e0fa31328185818239859ac Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 2 May 2021 21:11:56 -0400 Subject: [PATCH 12/13] Scheduler.await_ --- src/module/os.wren | 3 +-- src/module/os.wren.inc | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/module/os.wren b/src/module/os.wren index eec8d92a..b397149e 100644 --- a/src/module/os.wren +++ b/src/module/os.wren @@ -35,8 +35,7 @@ class Process { } else { Fiber.abort("environment vars must be passed as a Map") } - exec_(cmd, args, cwd, env, Fiber.current) - return Scheduler.runNextScheduled_() + return Scheduler.await_ { exec_(cmd, args, cwd, env, Fiber.current) } } foreign static exec_(cmd, args, cwd, env, fiber) diff --git a/src/module/os.wren.inc b/src/module/os.wren.inc index 42f2955d..a33ef650 100644 --- a/src/module/os.wren.inc +++ b/src/module/os.wren.inc @@ -39,8 +39,7 @@ static const char* osModuleSource = " } else {\n" " Fiber.abort(\"environment vars must be passed as a Map\")\n" " }\n" -" exec_(cmd, args, cwd, env, Fiber.current)\n" -" return Scheduler.runNextScheduled_()\n" +" return Scheduler.await_ { exec_(cmd, args, cwd, env, Fiber.current) }\n" " }\n" "\n" " foreign static exec_(cmd, args, cwd, env, fiber)\n" From 854a5fcc6df818445c365e3a536a638e2e0073a2 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Sun, 2 May 2021 21:13:41 -0400 Subject: [PATCH 13/13] consistency --- src/module/os.wren | 3 ++- src/module/os.wren.inc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/module/os.wren b/src/module/os.wren index b397149e..80a4c8f6 100644 --- a/src/module/os.wren +++ b/src/module/os.wren @@ -13,7 +13,7 @@ class Process { static arguments { allArguments.count >= 2 ? allArguments[2..-1] : [] } static exec(cmd) { - return exec(cmd, [], null, null) + return exec(cmd, null, null, null) } static exec(cmd, args) { @@ -26,6 +26,7 @@ class Process { static exec(cmd, args, cwd, envMap) { var env = [] + args = args || [] if (envMap is Map) { for (entry in envMap) { env.add([entry.key, entry.value].join("=")) diff --git a/src/module/os.wren.inc b/src/module/os.wren.inc index a33ef650..8c12009b 100644 --- a/src/module/os.wren.inc +++ b/src/module/os.wren.inc @@ -17,7 +17,7 @@ static const char* osModuleSource = " static arguments { allArguments.count >= 2 ? allArguments[2..-1] : [] }\n" "\n" " static exec(cmd) {\n" -" return exec(cmd, [], null, null)\n" +" return exec(cmd, null, null, null)\n" " }\n" "\n" " static exec(cmd, args) {\n" @@ -30,6 +30,7 @@ static const char* osModuleSource = " \n" " static exec(cmd, args, cwd, envMap) { \n" " var env = []\n" +" args = args || []\n" " if (envMap is Map) {\n" " for (entry in envMap) {\n" " env.add([entry.key, entry.value].join(\"=\"))\n"