diff options
author | leftmike <mikemon@gmail.com> | 2020-02-02 08:23:11 -0800 |
---|---|---|
committer | leftmike <mikemon@gmail.com> | 2020-03-07 11:04:16 -0800 |
commit | ebcf4cab52e244992356167a97ab27d0191ab190 (patch) | |
tree | e2ae7bd2b0c765e7ceb45211967d3838b620133f | |
parent | 1ed918c42da20276bfde13c83b3be30203b054da (diff) |
add subprocess, inspired by the Racket APIprocesses
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | src/base.scm | 134 | ||||
-rw-r--r-- | src/charset.cpp | 4 | ||||
-rw-r--r-- | src/foment.cpp | 10 | ||||
-rw-r--r-- | src/foment.hpp | 28 | ||||
-rw-r--r-- | src/io.cpp | 240 | ||||
-rw-r--r-- | src/io.hpp | 14 | ||||
-rw-r--r-- | src/main.cpp | 12 | ||||
-rw-r--r-- | src/process.cpp | 558 | ||||
-rw-r--r-- | src/syncthrd.cpp | 4 | ||||
-rw-r--r-- | test/exitcode.cpp | 14 | ||||
-rw-r--r-- | test/hang.cpp | 15 | ||||
-rw-r--r-- | test/process.scm | 311 | ||||
-rw-r--r-- | test/stdread.cpp | 30 | ||||
-rw-r--r-- | test/stdwrite.cpp | 30 | ||||
-rw-r--r-- | unix/makefile | 31 | ||||
-rw-r--r-- | windows/makefile | 45 |
17 files changed, 1387 insertions, 100 deletions
@@ -3,6 +3,8 @@ * Full R7RS. * Libraries and programs work. * Native threads and some synchronization primitives. +* [Proccess](Processes) is a subset of the [Racket](https://racket-lang.org/) +[Processes API](https://docs.racket-lang.org/reference/subprocess.html). * Memory management featuring guardians and trackers. Guardians protect objects from being collected and trackers follow objects as they get moved by the copying part of the collector. * Full Unicode including reading and writing unicode characters to the console. Files in UTF-8 and UTF-16 encoding can be read and written. * The system is built around a compiler and VM. There is support for prompts and continuation marks. @@ -25,11 +27,6 @@ See [Foment](https://github.com/leftmike/foment/wiki/Foment) for more details. -Future plans include -* Providing line numbers and stack traces on errors. -* R7RS large SRFIs. -* composable continuations - Please note that this is very much a work in progress. Please let me know if you find bugs and omissions. I will do my best to fix them. diff --git a/src/base.scm b/src/base.scm index 55f421d..afe5575 100644 --- a/src/base.scm +++ b/src/base.scm @@ -596,6 +596,20 @@ lookup-type-tags comparator-context comparator-context-set! + subprocess + subprocess-wait + subprocess-status + subprocess-kill + subprocess-pid + subprocess? + system + system* + system/exit-code + system*/exit-code + process + process* + process/ports + process*/ports current-milliseconds time-apply time @@ -944,6 +958,126 @@ (lambda () expr1 expr2 ...) (lambda () (leave-exclusive exclusive)))))) + (define (subprocess stdout stdin stderr command . args) + (apply values (apply %subprocess #f stdout stdin stderr command args))) + + (define (shell-cmd/args cmd) + (cond-expand + (windows (list (get-environment-variable "ComSpec") (string-append "/c " cmd))) + (else (list "/bin/sh" "-c" cmd)))) + + (define (system cmd) + (apply system* (shell-cmd/args cmd))) + + (define (system* cmd . args) + (= (apply system*/exit-code cmd args) 0)) + + (define (system/exit-code cmd) + (apply system*/exit-code (shell-cmd/args cmd))) + + (define (system*/exit-code cmd . args) + (let* ((lst (apply process*/ports (current-output-port) (current-input-port) + (current-error-port) cmd args)) + (ctrl (cadddr (cdr lst)))) + (ctrl 'wait) + (ctrl 'exit-code))) + + (define (process cmd) + (apply process*/ports (current-output-port) (current-input-port) (current-error-port) + (shell-cmd/args cmd))) + + (define (process* cmd . args) + (apply process*/ports (current-output-port) (current-input-port) (current-error-port) + cmd args)) + + (define (process/ports out in err cmd) + (apply process*/ports out in err (shell-cmd/args cmd))) + + (define (process-pipes out chldin in chldout err chlderr) + (if (or + (and (output-port? out) (input-port? chldin)) + (and (input-port? in) (output-port? chldout)) + (and (output-port? err) (input-port? chlderr))) + (let ((lock (make-exclusive)) + (done (make-condition)) + (cnt 0)) + (if (and (output-port? out) (input-port? chldin)) + (run-thread + (lambda () + (with-exclusive lock (set! cnt (+ cnt 1))) + (%copy-port chldin out) + (close-port chldin) + (with-exclusive lock + (set! cnt (- cnt 1)) + (if (= cnt 0) + (condition-wake done)))))) + (if (and (input-port? in) (output-port? chldout)) + (run-thread + (lambda () + (with-exclusive lock (set! cnt (+ cnt 1))) + (%copy-port in chldout) + (close-port chldout) + (with-exclusive lock + (set! cnt (- cnt 1)) + (if (= cnt 0) + (condition-wake done)))))) + (if (and (output-port? err) (input-port? chlderr)) + (run-thread + (lambda () + (with-exclusive lock (set! cnt (+ cnt 1))) + (%copy-port chlderr err) + (close-port chlderr) + (with-exclusive lock + (set! cnt (- cnt 1)) + (if (= cnt 0) + (condition-wake done)))))) + (lambda () + (define (wait) + (if (= cnt 0) + (leave-exclusive lock) + (begin + (condition-wait done lock) + (wait)))) + (enter-exclusive lock) + (wait))) + (lambda () #t))) + + (define (process*/ports out in err cmd . args) + (let* ((ret (apply %subprocess #t out in err cmd args)) + (sub (car ret)) + (chldout (cadr ret)) + (chldin (caddr ret)) + (chlderr (cadddr ret)) + (pipes-wait (process-pipes out chldout in chldin err chlderr))) + (list + (if out #f chldout) + (if in #f chldin) + (subprocess-pid sub) + (if err #f chlderr) + (lambda (what) + (case what + ((status) + (let ((ret (subprocess-status sub))) + (if (integer? ret) + (if (zero? ret) + 'done-ok + 'done-error) + ret))) + ((exit-code) + (let ((ret (subprocess-status sub))) + (and (integer? ret) ret))) + ((wait) + (subprocess-wait sub) + (pipes-wait)) + ((interrupt) + (subprocess-kill sub #f)) + ((kill) + (subprocess-kill sub #t)) + (else + (full-error 'assertion-violation '<control-process> #f + "<control-process>: expected status, exit-code, wait, interrupt, or kill" + what))))))) + (define push-parameter (cons #f #f)) (define pop-parameter (cons #f #f)) diff --git a/src/charset.cpp b/src/charset.cpp index 36079fa..746e46a 100644 --- a/src/charset.cpp +++ b/src/charset.cpp @@ -495,8 +495,8 @@ Define("ucs-range->char-set", UCSRangeToCharSetPrimitive)(long_t argc, FObject a FixnumArgCheck("ucs-range->char-set", argv[0]); FixnumArgCheck("ucs-range->char-set", argv[1]); - FCh lower = AsFixnum(argv[0]); - FCh upper = AsFixnum(argv[1]); + FCh lower = (FCh) AsFixnum(argv[0]); + FCh upper = (FCh) AsFixnum(argv[1]); FObject base = EmptyCharSet; if (argc > 2) diff --git a/src/foment.cpp b/src/foment.cpp index b0a1428..8847d0c 100644 --- a/src/foment.cpp +++ b/src/foment.cpp @@ -99,7 +99,7 @@ void ErrorExitFoment(const char * what, const char * msg) { if (CheckHeapFlag || VerboseFlag) fprintf(stderr, "RandomSeed: " ULONG_FMT "\n", RandomSeed); - ExitFoment(); + FlushStandardPorts(); } #ifdef FOMENT_UNIX @@ -1163,6 +1163,8 @@ FObject ThreadPPrimitiveFn(long_t argc, FObject argv[]); FObject ExclusivePPrimitiveFn(long_t argc, FObject argv[]); FObject ConditionPPrimitiveFn(long_t argc, FObject argv[]); FObject EphemeronPPrimitiveFn(long_t argc, FObject argv[]); +FObject CharSetPPrimitiveFn(long_t argc, FObject argv[]); +FObject SubprocessPPrimitiveFn(long_t argc, FObject argv[]); static FObject LookupTypeTags(FObject ttp) { @@ -1208,6 +1210,10 @@ static FObject LookupTypeTags(FObject ttp) return(List(MakeFixnum(ConditionTag + INDIRECT_TAG_OFFSET))); else if (AsPrimitive(ttp)->PrimitiveFn == EphemeronPPrimitiveFn) return(List(MakeFixnum(EphemeronTag + INDIRECT_TAG_OFFSET))); + else if (AsPrimitive(ttp)->PrimitiveFn == CharSetPPrimitiveFn) + return(List(MakeFixnum(CharSetTag + INDIRECT_TAG_OFFSET))); + else if (AsPrimitive(ttp)->PrimitiveFn == SubprocessPPrimitiveFn) + return(List(MakeFixnum(SubprocessTag + INDIRECT_TAG_OFFSET))); } return(EmptyListObject); @@ -1406,6 +1412,7 @@ FIndirectType IndirectTypes[] = {"builtin-type", WriteBuiltinType}, {"builtin", WriteBuiltin}, {"char-set", WriteCharSet}, + {"subprocess", WriteSubprocess}, {"free", 0} }; @@ -1511,6 +1518,7 @@ long_t SetupFoment(FThreadState * ts) SetupStrings(); SetupVectors(); SetupCharSets(); + SetupProcess(); SetupIO(); SetupFileSys(); SetupCompile(); diff --git a/src/foment.hpp b/src/foment.hpp index be0ada4..fdcc8a0 100644 --- a/src/foment.hpp +++ b/src/foment.hpp @@ -225,6 +225,7 @@ typedef enum BuiltinTypeTag, BuiltinTag, CharSetTag, + SubprocessTag, FreeTag, // Only on Adult Generation BadDogTag // Invalid Tag } FIndirectTag; @@ -543,11 +544,22 @@ typedef enum FromEnd } FPositionFrom; +#ifdef FOMENT_WINDOWS +typedef void * FFileHandle; +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX +typedef long_t FFileHandle; +#endif // FOMENT_UNIX + +#define INVALID_FILE_HANDLE ((FFileHandle) -1) + typedef void (*FCloseInputFn)(FObject port); typedef void (*FCloseOutputFn)(FObject port); typedef void (*FFlushOutputFn)(FObject port); typedef int64_t (*FGetPositionFn)(FObject port); typedef void (*FSetPositionFn)(FObject port, int64_t pos, FPositionFrom frm); +typedef FFileHandle (*FGetFileHandleFn)(FObject port); typedef struct { @@ -560,6 +572,7 @@ typedef struct FFlushOutputFn FlushOutputFn; FGetPositionFn GetPositionFn; FSetPositionFn SetPositionFn; + FGetFileHandleFn GetFileHandleFn; } FGenericPort; #define AsGenericPort(obj) ((FGenericPort *) obj) @@ -648,6 +661,8 @@ inline long_t PositioningPortP(FObject obj) return(AsGenericPort(obj)->Flags & PORT_FLAG_POSITIONING); } +void FlushStandardPorts(); + // Binary and textual ports FObject HandOffPort(FObject port); @@ -656,6 +671,7 @@ void CloseOutput(FObject port); void FlushOutput(FObject port); int64_t GetPosition(FObject port); void SetPosition(FObject port, int64_t pos, FPositionFrom frm); +FFileHandle GetFileHandle(FObject port); // Binary ports @@ -730,6 +746,10 @@ void WriteSimple(FObject port, FObject obj, long_t df); void WritePortObject(FWriteContext * wctx, FObject obj); +// ---- Subprocesses ---- + +void WriteSubprocess(FWriteContext * wctx, FObject obj); + // ---- Pairs ---- #define PairP(obj) (IndirectTag(obj) == PairTag) @@ -1598,6 +1618,12 @@ inline void AtLeastFourArgsCheck(const char * who, long_t argc) RaiseExceptionC(Assertion, who, "expected at least four arguments", EmptyListObject); } +inline void AtLeastFiveArgsCheck(const char * who, long_t argc) +{ + if (argc < 5) + RaiseExceptionC(Assertion, who, "expected at least five arguments", EmptyListObject); +} + inline void ZeroOrOneArgsCheck(const char * who, long_t argc) { if (argc > 1) @@ -1923,7 +1949,6 @@ FObject ExecuteProc(FObject op); long_t SetupFoment(FThreadState * ts); extern ulong_t SetupComplete; -void ExitFoment(); void ErrorExitFoment(const char * what, const char * msg); // ---- Do Not Call Directly ---- @@ -1935,6 +1960,7 @@ void SetupCharacters(); void SetupStrings(); void SetupVectors(); void SetupCharSets(); +void SetupProcess(); void SetupHashTables(); void SetupCompare(); void SetupIO(); @@ -66,9 +66,22 @@ FObject StandardError = NoValueObject; // ---- Binary Ports ---- +static FFileHandle InvalidFileHandle(FObject port) +{ + return(INVALID_FILE_HANDLE); +} + +static FFileHandle GetObjectFileHandle(FObject port) +{ + FAssert(BinaryPortP(port) || TextualPortP(port)); + + return(GetFileHandle(AsGenericPort(port)->Object)); +} + FObject MakeBinaryPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn, FCloseOutputFn cofn, FFlushOutputFn fofn, FReadBytesFn rbfn, FByteReadyPFn brpfn, - FWriteBytesFn wbfn, FGetPositionFn gpfn, FSetPositionFn spfn) + FWriteBytesFn wbfn, FGetPositionFn gpfn, FSetPositionFn spfn, + FGetFileHandleFn gfhfn, ulong_t flgs) { FAssert((cifn == 0 && rbfn == 0 && brpfn == 0) || (cifn != 0 && rbfn != 0 && brpfn != 0)); FAssert((cofn == 0 && wbfn == 0 && fofn == 0) || (cofn != 0 && wbfn != 0 && fofn != 0)); @@ -79,7 +92,8 @@ FObject MakeBinaryPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn, "%make-binary-port"); port->Generic.Flags = (cifn != 0 ? (PORT_FLAG_INPUT | PORT_FLAG_INPUT_OPEN) : 0) | (cofn != 0 ? (PORT_FLAG_OUTPUT | PORT_FLAG_OUTPUT_OPEN) : 0) - | (gpfn != 0 ? PORT_FLAG_POSITIONING : 0); + | (gpfn != 0 ? PORT_FLAG_POSITIONING : 0) + | flgs; port->Generic.Name = nam; port->Generic.Object = obj; port->Generic.Context = ctx; @@ -88,6 +102,7 @@ FObject MakeBinaryPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn, port->Generic.FlushOutputFn = fofn; port->Generic.GetPositionFn = gpfn; port->Generic.SetPositionFn = spfn; + port->Generic.GetFileHandleFn = gfhfn == 0 ? InvalidFileHandle : gfhfn; port->ReadBytesFn = rbfn; port->ByteReadyPFn = brpfn; port->WriteBytesFn = wbfn; @@ -403,7 +418,7 @@ static FObject MakeBufferedPort(FObject port) } port = HandOffPort(port); - FObject nport = MakeBinaryPort(AsGenericPort(port)->Name, port, bc, + return(MakeBinaryPort(AsGenericPort(port)->Name, port, bc, InputPortOpenP(port) ? BufferedCloseInput : 0, OutputPortOpenP(port) ? BufferedCloseOutput : 0, OutputPortOpenP(port) ? BufferedFlushOutput : 0, @@ -411,10 +426,9 @@ static FObject MakeBufferedPort(FObject port) InputPortOpenP(port) ? BufferedByteReadyP : 0, OutputPortOpenP(port) ? BufferedWriteBytes : 0, PositioningPortP(port) ? BufferedGetPosition : 0, - PositioningPortP(port) ? BufferedSetPosition : 0); - AsGenericPort(nport)->Flags |= PORT_FLAG_BUFFERED; - - return(nport); + PositioningPortP(port) ? BufferedSetPosition : 0, + GetObjectFileHandle, + PORT_FLAG_BUFFERED)); } static void SocketCloseInput(FObject port) @@ -494,12 +508,9 @@ static void SocketWriteBytes(FObject port, void * b, ulong_t bl) static FObject MakeSocketPort(SOCKET s) { - FObject port = MakeBinaryPort(NoValueObject, NoValueObject, (void *) s, SocketCloseInput, + return(MakeBinaryPort(NoValueObject, NoValueObject, (void *) s, SocketCloseInput, SocketCloseOutput, SocketFlushOutput, SocketReadBytes, SocketByteReadyP, - SocketWriteBytes, 0, 0); - AsGenericPort(port)->Flags |= PORT_FLAG_SOCKET; - - return(port); + SocketWriteBytes, 0, 0, 0, PORT_FLAG_SOCKET)); } #ifdef FOMENT_WINDOWS @@ -592,24 +603,31 @@ static void HandleSetPosition(FObject port, int64_t pos, FPositionFrom frm) frm == FromBegin ? FILE_BEGIN : (frm == FromCurrent ? FILE_CURRENT : FILE_END)); } +static FFileHandle HandleGetFileHandle(FObject port) +{ + FAssert(BinaryPortP(port) || TextualPortP(port)); + + return(AsGenericPort(port)->Context); +} + static FObject MakeHandleInputPort(FObject nam, HANDLE h) { if (GetFileType(h) == FILE_TYPE_PIPE) return(MakeBinaryPort(nam, NoValueObject, h, HandleCloseInput, 0, 0, HandleReadBytes, - PipeByteReadyP, 0, 0, 0)); + PipeByteReadyP, 0, 0, 0, HandleGetFileHandle, 0)); return(MakeBinaryPort(nam, NoValueObject, h, HandleCloseInput, 0, 0, HandleReadBytes, - FileByteReadyP, 0, HandleGetPosition, HandleSetPosition)); + FileByteReadyP, 0, HandleGetPosition, HandleSetPosition, HandleGetFileHandle, 0)); } static FObject MakeHandleOutputPort(FObject nam, HANDLE h) { if (GetFileType(h) == FILE_TYPE_PIPE) return(MakeBinaryPort(nam, NoValueObject, h, 0, HandleCloseOutput, HandleFlushOutput, 0, 0, - HandleWriteBytes, 0, 0)); + HandleWriteBytes, 0, 0, HandleGetFileHandle, 0)); return(MakeBinaryPort(nam, NoValueObject, h, 0, HandleCloseOutput, HandleFlushOutput, 0, 0, - HandleWriteBytes, HandleGetPosition, HandleSetPosition)); + HandleWriteBytes, HandleGetPosition, HandleSetPosition, HandleGetFileHandle, 0)); } #endif // FOMENT_WINDOWS @@ -680,26 +698,34 @@ static int64_t FileDescGetPosition(FObject port) return(lseek((long_t) AsGenericPort(port)->Context, 0, SEEK_CUR)); } +static FFileHandle FileDescGetFileHandle(FObject port) +{ + FAssert(BinaryPortP(port) || TextualPortP(port)); + + return((FFileHandle) (AsGenericPort(port)->Context)); +} + static void FileDescSetPosition(FObject port, int64_t pos, FPositionFrom frm) { FAssert(BinaryPortP(port) && PortOpenP(port) && PositioningPortP(port)); FAssert(frm == FromBegin || frm == FromCurrent || frm == FromEnd); - lseek((long_t) AsGenericPort(port)->Context, pos, + lseek((long_t) AsGenericPort(port)->Context, pos, frm == FromBegin ? SEEK_SET : (frm == FromCurrent ? SEEK_CUR : SEEK_END)); } static FObject MakeFileDescInputPort(FObject nam, long_t fd) { return(MakeBinaryPort(nam, NoValueObject, (void *) fd, FileDescCloseInput, 0, 0, - FileDescReadBytes, FileDescByteReadyP, 0, FileDescGetPosition, FileDescSetPosition)); + FileDescReadBytes, FileDescByteReadyP, 0, FileDescGetPosition, FileDescSetPosition, + FileDescGetFileHandle, 0)); } static FObject MakeFileDescOutputPort(FObject nam, long_t fd) { return(MakeBinaryPort(nam, NoValueObject, (void *) fd, 0, FileDescCloseOutput, FileDescFlushOutput, 0, 0, FileDescWriteBytes, FileDescGetPosition, - FileDescSetPosition)); + FileDescSetPosition, FileDescGetFileHandle, 0)); } #endif // FOMENT_UNIX @@ -744,7 +770,7 @@ static FObject MakeBytevectorInputPort(FObject bv) FAssert(BytevectorP(bv)); return(MakeBinaryPort(NoValueObject, bv, 0, BvinCloseInput, 0, 0, BvinReadBytes, - BvinByteReadyP, 0, 0, 0)); + BvinByteReadyP, 0, 0, 0, 0, 0)); } static void BvoutCloseOutput(FObject port) @@ -799,17 +825,16 @@ static FObject GetOutputBytevector(FObject port) static FObject MakeBytevectorOutputPort() { - FObject port = MakeBinaryPort(NoValueObject, EmptyListObject, 0, 0, BvoutCloseOutput, - BvoutFlushOutput, 0, 0, BvoutWriteBytes, 0, 0); - AsGenericPort(port)->Flags |= PORT_FLAG_BYTEVECTOR_OUTPUT; - return(port); + return(MakeBinaryPort(NoValueObject, EmptyListObject, 0, 0, BvoutCloseOutput, + BvoutFlushOutput, 0, 0, BvoutWriteBytes, 0, 0, 0, PORT_FLAG_BYTEVECTOR_OUTPUT)); } // ---- Textual Ports ---- FObject MakeTextualPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn, FCloseOutputFn cofn, FFlushOutputFn fofn, FReadChFn rcfn, FCharReadyPFn crpfn, - FWriteStringFn wsfn, FGetPositionFn gpfn, FSetPositionFn spfn) + FWriteStringFn wsfn, FGetPositionFn gpfn, FSetPositionFn spfn, FGetFileHandleFn gfhfn, + ulong_t flgs) { FAssert((cifn == 0 && rcfn == 0 && crpfn == 0) || (cifn != 0 && rcfn != 0 && crpfn != 0)); FAssert((cofn == 0 && wsfn == 0 && fofn == 0) || (cofn != 0 && wsfn != 0 && fofn != 0)); @@ -820,7 +845,8 @@ FObject MakeTextualPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn "%make-textual-port"); port->Generic.Flags = (cifn != 0 ? (PORT_FLAG_INPUT | PORT_FLAG_INPUT_OPEN) : 0) | (cofn != 0 ? (PORT_FLAG_OUTPUT | PORT_FLAG_OUTPUT_OPEN) : 0) - | (gpfn != 0 ? PORT_FLAG_POSITIONING : 0); + | (gpfn != 0 ? PORT_FLAG_POSITIONING : 0) + | flgs; port->Generic.Name = nam; port->Generic.Object = obj; port->Generic.Context = ctx; @@ -829,6 +855,7 @@ FObject MakeTextualPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn port->Generic.FlushOutputFn = fofn; port->Generic.GetPositionFn = gpfn; port->Generic.SetPositionFn = spfn; + port->Generic.GetFileHandleFn = gfhfn == 0 ? InvalidFileHandle : gfhfn; port->ReadChFn = rcfn; port->CharReadyPFn = crpfn; port->WriteStringFn = wsfn; @@ -1099,6 +1126,13 @@ void SetPosition(FObject port, int64_t pos, FPositionFrom frm) AsGenericPort(port)->SetPositionFn(port, pos, frm); } +FFileHandle GetFileHandle(FObject port) +{ + FAssert(BinaryPortP(port) || TextualPortP(port)); + + return(AsGenericPort(port)->GetFileHandleFn(port)); +} + static void TranslatorCloseInput(FObject port) { FAssert(BinaryPortP(AsGenericPort(port)->Object)); @@ -1132,7 +1166,7 @@ static FObject MakeTranslatorPort(FObject port, FReadChFn rcfn, FCharReadyPFn cr OutputPortP(port) ? TranslatorFlushOutput : 0, InputPortP(port) ? rcfn : 0, InputPortP(port) ? crpfn : 0, - OutputPortP(port) ? wsfn : 0, 0, 0)); + OutputPortP(port) ? wsfn : 0, 0, 0, GetObjectFileHandle, 0)); } static ulong_t AsciiReadCh(FObject port, FCh * ch) @@ -1497,6 +1531,32 @@ FObject OpenOutputFile(FObject fn) return(port); } +FObject OpenInputPipe(FFileHandle fh) +{ +#ifdef FOMENT_WINDOWS + return(MakeLatin1Port(MakeBufferedPort( + MakeHandleInputPort(MakeStringC("input-pipe"), fh)))); +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX + return(MakeUtf8Port(MakeBufferedPort( + MakeFileDescInputPort(MakeStringC("input-pipe"), fh)))); +#endif // FOMENT_UNIX +} + +FObject OpenOutputPipe(FFileHandle fh) +{ +#ifdef FOMENT_WINDOWS + return(MakeLatin1Port(MakeBufferedPort( + MakeHandleOutputPort(MakeStringC("output-pipe"), fh)))); +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX + return(MakeUtf8Port(MakeBufferedPort( + MakeFileDescOutputPort(MakeStringC("output-pipe"), fh)))); +#endif // FOMENT_UNIX +} + static void SinCloseInput(FObject port) { FAssert(TextualPortP(port)); @@ -1533,7 +1593,7 @@ FObject MakeStringInputPort(FObject s) FAssert(StringP(s)); return(MakeTextualPort(NoValueObject, s, 0, SinCloseInput, 0, 0, SinReadCh, SinCharReadyP, - 0, 0, 0)); + 0, 0, 0, 0, 0)); } static void SoutCloseOutput(FObject port) @@ -1588,10 +1648,8 @@ FObject GetOutputString(FObject port) FObject MakeStringOutputPort() { - FObject port = MakeTextualPort(NoValueObject, EmptyListObject, 0, 0, SoutCloseOutput, - SoutFlushOutput, 0, 0, SoutWriteString, 0, 0); - AsGenericPort(port)->Flags |= PORT_FLAG_STRING_OUTPUT; - return(port); + return(MakeTextualPort(NoValueObject, EmptyListObject, 0, 0, SoutCloseOutput, + SoutFlushOutput, 0, 0, SoutWriteString, 0, 0, 0, PORT_FLAG_STRING_OUTPUT)); } static void CinCloseInput(FObject port) @@ -1622,7 +1680,7 @@ static long_t CinCharReadyP(FObject port) FObject MakeStringCInputPort(const char * s) { return(MakeTextualPort(NoValueObject, NoValueObject, (void *) s, CinCloseInput, 0, 0, - CinReadCh, CinCharReadyP, 0, 0, 0)); + CinReadCh, CinCharReadyP, 0, 0, 0, 0, 0)); } // ---- Console Input and Output ---- @@ -2761,6 +2819,13 @@ static long_t ConCharReadyP(FObject port) } #ifdef FOMENT_WINDOWS +static FFileHandle ConGetFileHandle(FObject port) +{ + FAssert(InputPortP(port) && ConsolePortP(port)); + + return(AsConsoleInput(port)->InputHandle); +} + static FObject MakeConsoleInputPort(FObject nam, HANDLE hin, HANDLE hout) { FConsoleInput * ci = MakeConsoleInput(); @@ -2771,8 +2836,7 @@ static FObject MakeConsoleInputPort(FObject nam, HANDLE hin, HANDLE hout) ci->OutputHandle = hout; FObject port = MakeTextualPort(nam, NoValueObject, ci, ConCloseInput, 0, 0, ConReadCh, - ConCharReadyP, 0, 0, 0); - AsGenericPort(port)->Flags |= PORT_FLAG_CONSOLE | PORT_FLAG_INTERACTIVE; + ConCharReadyP, 0, 0, 0, ConGetFileHandle, PORT_FLAG_CONSOLE | PORT_FLAG_INTERACTIVE); AsConsoleInput(port)->Mode = CONSOLE_INPUT_ECHO; return(port); @@ -2806,15 +2870,19 @@ static void ConWriteString(FObject port, FCh * s, ulong_t sl) static FObject MakeConsoleOutputPort(FObject nam, HANDLE h) { - FObject port = MakeTextualPort(nam, NoValueObject, h, 0, ConCloseOutput, ConFlushOutput, 0, - 0, ConWriteString, 0, 0); - AsGenericPort(port)->Flags |= PORT_FLAG_CONSOLE; - - return(port); + return(MakeTextualPort(nam, NoValueObject, h, 0, ConCloseOutput, ConFlushOutput, 0, + 0, ConWriteString, 0, 0, HandleGetFileHandle, PORT_FLAG_CONSOLE)); } #endif // FOMENT_WINDOWS #ifdef FOMENT_UNIX +static FFileHandle ConGetFileHandle(FObject port) +{ + FAssert(InputPortP(port) && ConsolePortP(port)); + + return(AsConsoleInput(port)->InputFd); +} + static FObject MakeConsoleInputPort(FObject nam, long_t ifd, long_t ofd, int fi) { FConsoleInput * ci = MakeConsoleInput(); @@ -2825,8 +2893,7 @@ static FObject MakeConsoleInputPort(FObject nam, long_t ifd, long_t ofd, int fi) ci->OutputFd = ofd; FObject port = MakeTextualPort(nam, NoValueObject, ci, ConCloseInput, 0, 0, ConReadCh, - ConCharReadyP, 0, 0, 0); - AsGenericPort(port)->Flags |= PORT_FLAG_CONSOLE; + ConCharReadyP, 0, 0, 0, ConGetFileHandle, PORT_FLAG_CONSOLE); if (fi) { AsGenericPort(port)->Flags |= PORT_FLAG_INTERACTIVE; @@ -2862,11 +2929,8 @@ static void ConWriteString(FObject port, FCh * s, ulong_t sl) static FObject MakeConsoleOutputPort(FObject nam, long_t ofd) { - FObject port = MakeTextualPort(nam, NoValueObject, (void *) ofd, 0, ConCloseOutput, - ConFlushOutput, 0, 0, ConWriteString, 0, 0); - AsGenericPort(port)->Flags |= PORT_FLAG_CONSOLE; - - return(port); + return(MakeTextualPort(nam, NoValueObject, (void *) ofd, 0, ConCloseOutput, + ConFlushOutput, 0, 0, ConWriteString, 0, 0, FileDescGetFileHandle, PORT_FLAG_CONSOLE)); } #endif // FOMENT_UNIX @@ -3189,6 +3253,44 @@ Define("set-port-position!", SetPortPositionPrimitive)(long_t argc, FObject argv return(NoValueObject); } +Define("%copy-port", CopyPortPrimitive)(long_t argc, FObject argv[]) +{ + TwoArgsCheck("%copy-port", argc); + InputPortArgCheck("%copy-port", argv[0]); + if (TextualPortP(argv[0])) + { + TextualInputPortArgCheck("%copy-port", argv[0]); + TextualOutputPortArgCheck("%copy-port", argv[1]); + + for (;;) + { + FCh ch; + + if (ReadCh(argv[0], &ch) == 0) + break; + WriteCh(argv[1], ch); + } + } + else + { + FAssert(BinaryPortP(argv[0])); + + BinaryInputPortArgCheck("%copy-port", argv[0]); + BinaryOutputPortArgCheck("%copy-port", argv[1]); + + for (;;) + { + FByte buf[1024]; + ulong_t bl = ReadBytes(argv[0], buf, sizeof(buf)); + if (bl == 0) + break; + WriteBytes(argv[1], buf, bl); + } + } + + return(NoValueObject); +} + Define("socket-merge-flags", SocketMergeFlagsPrimitive)(long_t argc, FObject argv[]) { long_t ret = 0; @@ -3583,6 +3685,7 @@ static FObject Primitives[] = PositioningPortPPrimitive, PortPositionPrimitive, SetPortPositionPrimitive, + CopyPortPrimitive, SocketMergeFlagsPrimitive, SocketPurgeFlagsPrimitive, SocketPPrimitive, @@ -3599,25 +3702,43 @@ static FObject Primitives[] = #ifdef FOMENT_UNIX static struct termios OriginalTios; +static int InteractiveConsole = 0; -static void FomentAtExit(void) -{ - tcsetattr(0, TCSANOW, &OriginalTios); -} - -static long_t SetupConsole() +static void SetupTios(void) { struct termios tios; - tcgetattr(0, &OriginalTios); - atexit(FomentAtExit); - tcgetattr(0, &tios); tios.c_iflag = BRKINT; tios.c_lflag = ISIG; tios.c_cc[VMIN] = 1; tios.c_cc[VTIME] = 1; tcsetattr(0, TCSANOW, &tios); +} + +static void RestoreTios(void) +{ + tcsetattr(0, TCSANOW, &OriginalTios); +} + +void SetupConsoleAgain() +{ + if (InteractiveConsole) + SetupTios(); +} + +void RestoreConsole() +{ + if (InteractiveConsole) + RestoreTios(); +} + +static long_t SetupConsole() +{ + tcgetattr(0, &OriginalTios); + atexit(RestoreTios); + + SetupTios(); long_t x; long_t y; @@ -3634,7 +3755,7 @@ static void DefineConstant(FObject env, FObject lib, const char * nam, FObject v LibraryExport(lib, EnvironmentSetC(env, nam, val)); } -void ExitFoment() +void FlushStandardPorts() { if (TextualPortP(StandardOutput) || BinaryPortP(StandardOutput)) FlushOutput(StandardOutput); @@ -3693,8 +3814,9 @@ void SetupIO() if (isatty(0) && isatty(1)) { char * term = getenv("TERM"); - int fi = (term != 0 && strcasecmp(term, "dumb") != 0 && SetupConsole()); - StandardInput = MakeConsoleInputPort(MakeStringC("console-input"), 0, 1, fi); + InteractiveConsole = (term != 0 && strcasecmp(term, "dumb") != 0 && SetupConsole()); + StandardInput = MakeConsoleInputPort(MakeStringC("console-input"), 0, 1, + InteractiveConsole); StandardOutput = MakeConsoleOutputPort(MakeStringC("console-output"), 1); } else @@ -27,7 +27,8 @@ typedef struct FObject MakeBinaryPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn, FCloseOutputFn cofn, FFlushOutputFn fofn, FReadBytesFn rbfn, FByteReadyPFn brpfn, - FWriteBytesFn wbfn, FGetPositionFn gpfn, FSetPositionFn spfn); + FWriteBytesFn wbfn, FGetPositionFn gpfn, FSetPositionFn spfn, + FGetFileHandleFn gfhfn, ulong_t flgs); // ---- Textual Ports ---- @@ -50,7 +51,8 @@ typedef struct FObject MakeTextualPort(FObject nam, FObject obj, void * ctx, FCloseInputFn cifn, FCloseOutputFn cofn, FFlushOutputFn fofn, FReadChFn rcfn, FCharReadyPFn crpfn, - FWriteStringFn wsfn, FGetPositionFn gpfn, FSetPositionFn spfn); + FWriteStringFn wsfn, FGetPositionFn gpfn, FSetPositionFn spfn, FGetFileHandleFn gfhfn, + ulong_t flgs); inline FObject CurrentInputPort() { @@ -74,10 +76,18 @@ inline FObject CurrentOutputPort() return(port); } +FObject OpenInputPipe(FFileHandle fh); +FObject OpenOutputPipe(FFileHandle fh); + // ---------------- void SetupWrite(); void SetupRead(); long_t IdentifierSubsequentP(FCh ch); +#ifdef FOMENT_UNIX +void SetupConsoleAgain(); +void RestoreConsole(); +#endif // FOMENT_UNIX + #endif // __IO_HPP__ diff --git a/src/main.cpp b/src/main.cpp index 5a63693..c1a6e24 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -155,7 +155,7 @@ static int RunProgram(FChS * arg) ReadLine(port); ExecuteProc(CompileProgram(nam, port)); - ExitFoment(); + FlushStandardPorts(); return(0); } @@ -769,7 +769,7 @@ int main(int argc, FChS * argv[]) if ((BinaryPortP(StandardOutput) || TextualPortP(StandardOutput)) && OutputPortOpenP(StandardOutput)) WriteSimple(StandardOutput, obj, 0); - ExitFoment(); + FlushStandardPorts(); return(1); } @@ -779,7 +779,7 @@ int main(int argc, FChS * argv[]) { if (ProcessOptions(LateConfig, argc, argv, &pdx) == 0) { - ExitFoment(); + FlushStandardPorts(); return(1); } @@ -811,12 +811,12 @@ int main(int argc, FChS * argv[]) return(1); } ExecuteProc(CompileProgram(AsGenericPort(StandardInput)->Name, StandardInput)); - ExitFoment(); + FlushStandardPorts(); break; case InteractiveMode: ExecuteProc(InteractiveThunk); - ExitFoment(); + FlushStandardPorts(); break; default: @@ -831,7 +831,7 @@ int main(int argc, FChS * argv[]) WriteStringC(StandardOutput, "exception: "); WriteSimple(StandardOutput, obj, 0); WriteCh(StandardOutput, '\n'); - ExitFoment(); + FlushStandardPorts(); return(1); } } diff --git a/src/process.cpp b/src/process.cpp new file mode 100644 index 0000000..f67ff57 --- /dev/null +++ b/src/process.cpp @@ -0,0 +1,558 @@ +/* + +Foment + +- %subprocess: textual vs binary port when a pipe is created +*/ + +#ifdef FOMENT_WINDOWS +#include <windows.h> +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX +#include <string.h> +#include <pthread.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#endif // FOMENT_UNIX + +#include <stdio.h> +#include "foment.hpp" +#include "syncthrd.hpp" +#include "io.hpp" +#include "unicode.hpp" + +#if defined(FOMENT_BSD) || defined(FOMENT_OSX) +extern char ** environ; +#else +#include <malloc.h> +#endif + +// ---------------- + +EternalSymbol(StdoutSymbol, "stdout"); + +// ---- Subprocesses ---- + +typedef struct +{ +#ifdef FOMENT_WINDOWS + DWORD ProcessID; + HANDLE ProcessHandle; + DWORD Status; +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX + pid_t ProcessID; + int Status; + int Done; +#endif // FOMENT_UNIX +} FSubprocess; + +#define AsSubprocess(obj) ((FSubprocess *) (obj)) +#define SubprocessP(obj) (IndirectTag(obj) == SubprocessTag) + +#ifdef FOMENT_WINDOWS +static FObject MakeSubprocess(DWORD pid, HANDLE ph, const char * who) +{ + FSubprocess * sub = (FSubprocess *) MakeObject(SubprocessTag, sizeof(FSubprocess), 0, who); + sub->ProcessID = pid; + sub->ProcessHandle = ph; + return(sub); +} +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX +static FObject MakeSubprocess(pid_t pid, const char * who) +{ + FSubprocess * sub = (FSubprocess *) MakeObject(SubprocessTag, sizeof(FSubprocess), 0, who); + sub->ProcessID = pid; + sub->Status = 0; + sub->Done = 0; + return(sub); +} +#endif // FOMENT_UNIX + +void WriteSubprocess(FWriteContext * wctx, FObject obj) +{ + FCh s[16]; + long_t sl = FixnumAsString((long_t) obj, s, 16); + + wctx->WriteStringC("#<subprocess: "); + wctx->WriteString(s, sl); + wctx->WriteStringC(" pid: "); + + FAssert(SubprocessP(obj)); + + sl = FixnumAsString((long_t) AsSubprocess(obj)->ProcessID, s, 10); + wctx->WriteString(s, sl); + wctx->WriteCh('>'); +} + +inline void SubprocessArgCheck(const char * who, FObject obj) +{ + if (SubprocessP(obj) == 0) + RaiseExceptionC(Assertion, who, "expected a subprocess", List(obj)); +} + +static void MakePipe(FFileHandle * ifh, FFileHandle * ofh, const char * who) +{ +#ifdef FOMENT_WINDOWS + if (CreatePipe(ifh, ofh, 0, 0) == 0) + RaiseExceptionC(Assertion, who, "create pipe failed", List(MakeFixnum(GetLastError()))); +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX + int pipefd[2]; + + if (pipe(pipefd) != 0) + RaiseExceptionC(Assertion, who, "pipe system call failed", List(MakeFixnum(errno))); + *ifh = pipefd[0]; + *ofh = pipefd[1]; +#endif // FOMENT_UNIX +} + +static FChS * ConvertStringToSystem(FObject s) +{ + FAssert(StringP(s)); + +#ifdef FOMENT_WINDOWS + FObject bv = ConvertStringToUtf16(s); +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX + FObject bv = ConvertStringToUtf8(s); +#endif // FOMENT_UNIX + + FAssert(BytevectorP(bv)); + + return((FChS *) AsBytevector(bv)->Vector); +} + +typedef struct +{ + // Optional file handles to use for stdout, stdin, and stderr in the subprocess. + FFileHandle ChildStdout; // Output file handle. + FFileHandle ChildStdin; // Input file handle. + FFileHandle ChildStderr; // Output file handle. + int UseStdout; // Use stdout for stderr. + + // These are only used if the corresponding Child* above is not specified. + FFileHandle PipeStdout; // Input file handle of a pipe connected to stdout in the subprocess. + FFileHandle PipeStdin; // Output file handle of a pipe connected to stdin in the subprocess. + FFileHandle PipeStderr; // Input file handle of a pipe connected to stderr in the subprocess. +} FSubprocessFileHandles; + +#ifdef FOMENT_WINDOWS +static FChS * CopyArg(FChS * cmd, FChS * arg) +{ + *cmd = '"'; + cmd += 1; + + while (*arg != 0) + { + if (*arg == '"') // || *arg == '\\') + { + *cmd = '\\'; + cmd += 1; + } + *cmd = *arg; + cmd += 1; + arg += 1; + } + + *cmd = '"'; + cmd += 1; + return(cmd); +} + +static void ChildArgs(long_t argc, FObject argv[], FChS ** app, FChS ** cmdline) +{ + long_t cmdlen = 1; + long_t adx; + + for (adx = 0; adx < argc; adx++) + cmdlen += StringLength(argv[adx]) * 2 + 3; + + FObject bv = MakeBytevector(cmdlen * sizeof(FChS)); + FAssert(BytevectorP(bv)); + FChS * cmd = (FChS *) AsBytevector(bv)->Vector; + *cmdline = cmd; + + for (adx = 0; adx < argc; adx++) + { + FChS * arg = ConvertStringToSystem(argv[adx]); + if (adx == 0) + { + *app = arg; + FChS * s = wcsrchr(arg, '\\'); + if (s != 0) + arg = s + 1; + cmd = CopyArg(cmd, arg); + } + else + { + *cmd = ' '; + cmd += 1; + cmd = CopyArg(cmd, arg); + } + } + *cmd = 0; +} +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX +static void ChildFailed(char * cmd, const char * call) +{ + fprintf(stderr, "error: %s: %s: %d\n", cmd, call, errno); + fflush(stderr); + exit(1); +} + +static void ChildProcess(FSubprocessFileHandles * sfh, FFileHandle chldin, FFileHandle chldout, + FFileHandle chlderr, long_t argc, FObject argv[]) +{ + FAssert(argc > 0); + + char ** args = (char **) malloc(sizeof(char *) * (argc + 1)); + char * cmd = ConvertStringToSystem(argv[0]); + char * s = strrchr(cmd, '/'); + if (s == 0) + args[0] = cmd; + else + args[0] = strdup(s + 1); + + for (long_t adx = 1; adx < argc; adx += 1) + args[adx] = ConvertStringToSystem(argv[adx]); + args[argc] = 0; + + if (sfh->PipeStdout != INVALID_FILE_HANDLE) + close(sfh->PipeStdout); + if (sfh->PipeStdin != INVALID_FILE_HANDLE) + close(sfh->PipeStdin); + if (sfh->UseStdout == 0 && sfh->PipeStderr != INVALID_FILE_HANDLE) + close(sfh->PipeStderr); + + while (chldin < 3) + chldin = dup(chldin); + while (chldout < 3) + chldout = dup(chldout); + while (chlderr < 3) + chlderr = dup(chlderr); + + close(0); + if (dup2(chldin, 0) != 0) + ChildFailed(cmd, "dup2"); + close(1); + if (dup2(chldout, 1) != 1) + ChildFailed(cmd, "dup2"); + close(2); + if (dup2(chlderr, 2) != 2) + ChildFailed(cmd, "dup2"); + close(chldin); + close(chldout); + close(chlderr); + + execve(cmd, args, environ); + ChildFailed(cmd, "execve"); +} +#endif // FOMENT_UNIX + +static FObject Subprocess(FSubprocessFileHandles * sfh, long_t argc, FObject argv[], + const char * who) +{ + FFileHandle chldout, chldin, chlderr; + + if (sfh->ChildStdout == INVALID_FILE_HANDLE) + { + MakePipe(&(sfh->PipeStdout), &chldout, who); +#ifdef FOMENT_WINDOWS + SetHandleInformation(chldout, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); +#endif // FOMENT_WINDOWS + } + else + chldout = sfh->ChildStdout; + + if (sfh->ChildStdin == INVALID_FILE_HANDLE) + { + MakePipe(&chldin, &(sfh->PipeStdin), who); +#ifdef FOMENT_WINDOWS + SetHandleInformation(chldin, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); +#endif // FOMENT_WINDOWS + } + else + chldin = sfh->ChildStdin; + + if (sfh->UseStdout) + chlderr = chldout; + else if (sfh->ChildStderr == INVALID_FILE_HANDLE) + { + MakePipe(&(sfh->PipeStderr), &chlderr, who); +#ifdef FOMENT_WINDOWS + SetHandleInformation(chlderr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); +#endif // FOMENT_WINDOWS + } + else + chlderr = sfh->ChildStderr; + + FlushStandardPorts(); + +#ifdef FOMENT_WINDOWS + FChS *app; + FChS *cmdline; + ChildArgs(argc, argv, &app, &cmdline); + + STARTUPINFOW si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = chldin; + si.hStdOutput = chldout; + si.hStdError = chlderr; + + PROCESS_INFORMATION pi; + BOOL ret = CreateProcessW(app, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); + if (ret == 0) + RaiseExceptionC(Assertion, who, "create process system call failed", + List(MakeIntegerFromUInt64(GetLastError()))); + + CloseHandle(pi.hThread); + + if (sfh->PipeStdout != INVALID_FILE_HANDLE) + CloseHandle(chldout); + + if (sfh->PipeStdin != INVALID_FILE_HANDLE) + CloseHandle(chldin); + + if (sfh->UseStdout == 0 && sfh->PipeStderr != INVALID_FILE_HANDLE) + CloseHandle(chlderr); + + return(MakeSubprocess(pi.dwProcessId, pi.hProcess, who)); +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX + RestoreConsole(); + + pid_t pid = fork(); + if (pid < 0) + RaiseExceptionC(Assertion, who, "fork system call failed", List(MakeFixnum(errno))); + + if (pid == 0) + { + ChildProcess(sfh, chldin, chldout, chlderr, argc, argv); + + // Never returns. + FAssert(0); + } + + // Parent. + + SetupConsoleAgain(); + + if (sfh->PipeStdout != INVALID_FILE_HANDLE) + close(chldout); + + if (sfh->PipeStdin != INVALID_FILE_HANDLE) + close(chldin); + + if (sfh->UseStdout == 0 && sfh->PipeStderr != INVALID_FILE_HANDLE) + close(chlderr); + + return(MakeSubprocess(pid, who)); +#endif // FOMENT_UNIX +} + +Define("%subprocess", SubprocessPrimitive)(long_t argc, FObject argv[]) +{ + AtLeastFiveArgsCheck("%subprocess", argc); + BooleanArgCheck("%subprocess", argv[0]); + + for (long_t adx = 4; adx < argc; adx += 1) + StringArgCheck("%subprocess", argv[adx]); + + FSubprocessFileHandles sfh; + + if (argv[1] == FalseObject) + sfh.ChildStdout = INVALID_FILE_HANDLE; + else + { + OutputPortArgCheck("%subprocess", argv[1]); + + sfh.ChildStdout = GetFileHandle(argv[1]); + if (argv[0] == FalseObject && sfh.ChildStdout == INVALID_FILE_HANDLE) + RaiseExceptionC(Assertion, "%subprocess", "expected an output file handle port", + List(argv[1])); + } + + if (argv[2] == FalseObject) + sfh.ChildStdin = INVALID_FILE_HANDLE; + else + { + InputPortArgCheck("%subprocess", argv[2]); + + sfh.ChildStdin = GetFileHandle(argv[2]); + if (argv[0] == FalseObject && sfh.ChildStdin == INVALID_FILE_HANDLE) + RaiseExceptionC(Assertion, "%subprocess", "expected an input file handle port", + List(argv[2])); + } + + sfh.ChildStderr = INVALID_FILE_HANDLE; + sfh.UseStdout = 0; + + if (argv[3] == StdoutSymbol) + sfh.UseStdout = 1; + else if (argv[3] != FalseObject) + { + OutputPortArgCheck("%subprocess", argv[3]); + + sfh.ChildStderr = GetFileHandle(argv[3]); + if (argv[0] == FalseObject && sfh.ChildStderr == INVALID_FILE_HANDLE) + RaiseExceptionC(Assertion, "%subprocess", "expected an output file handle port", + List(argv[3])); + } + + sfh.PipeStdout = INVALID_FILE_HANDLE; + sfh.PipeStdin = INVALID_FILE_HANDLE; + sfh.PipeStderr = INVALID_FILE_HANDLE; + + FObject sub = Subprocess(&sfh, argc - 4, argv + 4, "%subprocess"); + + FAssert(SubprocessP(sub)); + + FObject out = (sfh.ChildStdout == INVALID_FILE_HANDLE) ? OpenInputPipe(sfh.PipeStdout) : + FalseObject; + + FObject in = (sfh.ChildStdin == INVALID_FILE_HANDLE) ? OpenOutputPipe(sfh.PipeStdin) : + FalseObject; + + FObject err = (sfh.UseStdout == 0 && sfh.ChildStderr == INVALID_FILE_HANDLE) ? + OpenInputPipe(sfh.PipeStderr) : FalseObject; + + return(List(sub, out, in, err)); +} + +Define("subprocess?", SubprocessPPrimitive)(long_t argc, FObject argv[]) +{ + OneArgCheck("subprocess?", argc); + + return(SubprocessP(argv[0]) ? TrueObject : FalseObject); +} + +Define("subprocess-wait", SubprocessWaitPrimitive)(long_t argc, FObject argv[]) +{ + OneArgCheck("subprocess-wait", argc); + SubprocessArgCheck("subprocess-wait", argv[0]); + + FSubprocess * sub = AsSubprocess(argv[0]); + +#ifdef FOMENT_WINDOWS + if (sub->ProcessHandle != INVALID_HANDLE_VALUE) + { + if (WaitForSingleObject(sub->ProcessHandle, INFINITE) == WAIT_FAILED) + RaiseExceptionC(Assertion, "subprocess-wait", + "wait for single object system call failed", + List(MakeIntegerFromUInt64(GetLastError()))); + + DWORD status; + GetExitCodeProcess(sub->ProcessHandle, &status); + sub->Status = status; + + CloseHandle(sub->ProcessHandle); + sub->ProcessHandle = INVALID_HANDLE_VALUE; + } +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX + if (sub->Done == 0) + { + int status; + if (waitpid(sub->ProcessID, &status, 0) < 0) + RaiseExceptionC(Assertion, "subprocess-wait", "waitpid failed", + List(MakeFixnum(errno))); + + sub->Done = 1; + sub->Status = status; + } +#endif // FOMENT_UNIX + + return(NoValueObject); +} + +Define("subprocess-status", SubprocessStatusPrimitive)(long_t argc, FObject argv[]) +{ + OneArgCheck("subprocess-status", argc); + SubprocessArgCheck("subprocess-status", argv[0]); + + FSubprocess * sub = AsSubprocess(argv[0]); + +#ifdef FOMENT_WINDOWS + if (sub->ProcessHandle != INVALID_HANDLE_VALUE) + return(StringCToSymbol("running")); + + return(MakeIntegerFromUInt64(sub->Status)); +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX + if (sub->Done == 0) + return(StringCToSymbol("running")); + + if (WIFEXITED(sub->Status)) + return(MakeFixnum(WEXITSTATUS(sub->Status))); + + FAssert(WIFSIGNALED(sub->Status)); + + return(MakeFixnum(WTERMSIG(sub->Status))); +#endif // FOMENT_UNIX +} + +Define("subprocess-kill", SubprocessKillPrimitive)(long_t argc, FObject argv[]) +{ + TwoArgsCheck("subprocess-kill", argc); + SubprocessArgCheck("subprocess-kill", argv[0]); + + FSubprocess * sub = AsSubprocess(argv[0]); + +#ifdef FOMENT_WINDOWS + if (sub->ProcessHandle != INVALID_HANDLE_VALUE && argv[1] != FalseObject) + TerminateProcess(sub->ProcessHandle, 0); +#endif // FOMENT_WINDOWS + +#ifdef FOMENT_UNIX + if (sub->Done == 0) + kill(sub->ProcessID, argv[1] == FalseObject ? SIGINT : SIGTERM); +#endif // FOMENT_UNIX + + return(NoValueObject); +} + +Define("subprocess-pid", SubprocessPIDPrimitive)(long_t argc, FObject argv[]) +{ + OneArgCheck("subprocess-pid", argc); + SubprocessArgCheck("subprocess-pid", argv[0]); + + return(MakeFixnum(AsSubprocess(argv[0])->ProcessID)); +} + +static FObject Primitives[] = +{ + SubprocessPrimitive, + SubprocessPPrimitive, + SubprocessWaitPrimitive, + SubprocessStatusPrimitive, + SubprocessKillPrimitive, + SubprocessPIDPrimitive +}; + +void SetupProcess() +{ + StdoutSymbol = InternSymbol(StdoutSymbol); + + FAssert(StdoutSymbol == StringCToSymbol("stdout")); + + for (ulong_t idx = 0; idx < sizeof(Primitives) / sizeof(FPrimitive *); idx++) + DefinePrimitive(Bedrock, BedrockLibrary, Primitives[idx]); +} diff --git a/src/syncthrd.cpp b/src/syncthrd.cpp index 384e021..108e68e 100644 --- a/src/syncthrd.cpp +++ b/src/syncthrd.cpp @@ -253,7 +253,7 @@ void ThreadExit(FObject obj) if (LeaveThread(ts) == 0) { - ExitFoment(); + FlushStandardPorts(); exit(0); } @@ -281,7 +281,7 @@ Define("%exit", ExitPrimitive)(long_t argc, FObject argv[]) { ZeroOrOneArgsCheck("exit", argc); - ExitFoment(); + FlushStandardPorts(); if (argc == 0 || argv[0] == TrueObject) exit(0); if (FixnumP(argv[0])) diff --git a/test/exitcode.cpp b/test/exitcode.cpp new file mode 100644 index 0000000..a178ee4 --- /dev/null +++ b/test/exitcode.cpp @@ -0,0 +1,14 @@ +/* + +Exit with the code specified as an argument. + +*/ + +#include <stdlib.h> + +int main(int argc, char * argv[]) +{ + if (argc != 2) + return(1); + return(atoi(argv[1])); +} diff --git a/test/hang.cpp b/test/hang.cpp new file mode 100644 index 0000000..a09ecde --- /dev/null +++ b/test/hang.cpp @@ -0,0 +1,15 @@ +/* + +Hang. + +*/ + +int main(int argc, char * argv[]) +{ + int cnt = 0; + + for (;;) + cnt += 1; + + return(0); +} diff --git a/test/process.scm b/test/process.scm new file mode 100644 index 0000000..62a9383 --- /dev/null +++ b/test/process.scm @@ -0,0 +1,311 @@ +;;; +;;; Processes +;;; + +(import (foment base)) + +(check-equal #t #t) + +(define (read-lines port) + (let ((s (read-line port))) + (if (eof-object? s) + '() + (cons s (read-lines port))))) + +(define stdread + (cond-expand + (windows "..\\windows\\debug\\stdread.exe") + (else "../unix/debug/stdread"))) + +(define stdwrite + (cond-expand + (windows "..\\windows\\debug\\stdwrite.exe") + (else "../unix/debug/stdwrite"))) + +(define exitcode + (cond-expand + (windows "..\\windows\\debug\\exitcode.exe") + (else "../unix/debug/exitcode"))) + +(define hang + (cond-expand + (windows "..\\windows\\debug\\hang.exe") + (else "../unix/debug/hang"))) + +;; subprocess + +(check-equal 0 + (call-with-values + (lambda () + (subprocess (current-output-port) #f (current-error-port) + stdread "ABC" "DEF GHI" "JKLMNOPQR")) + (lambda (sub in out err) + (for-each + (lambda (line) + (display line out) + (newline out)) + '("ABC" "DEF GHI" "JKLMNOPQR")) + (close-port out) + (subprocess-wait sub) + (subprocess-status sub)))) + +(check-equal ("abc" "def\\ghi" "jklmn\"opqr") + (call-with-values + (lambda () + (subprocess #f (current-input-port) (current-error-port) + stdwrite "--stdout" "abc" "def\\ghi" "jklmn\"opqr")) + (lambda (sub in out err) + (let ((lines (read-lines in))) + (close-port in) + (subprocess-wait sub) + (if (zero? (subprocess-status sub)) + lines + #f))))) + +(check-equal ("abc" "defghi" "jklmnopqr") + (call-with-values + (lambda () + (subprocess #f (current-input-port) 'stdout + stdwrite "--stderr" "abc" "defghi" "jklmnopqr")) + (lambda (sub in out err) + (let ((lines (read-lines in))) + (close-port in) + (subprocess-wait sub) + (if (zero? (subprocess-status sub)) + lines + #f))))) + +(check-equal ("abc" "defghi" "jklmnopqr") + (call-with-values + (lambda () + (subprocess (current-output-port) (current-input-port) #f + stdwrite "--stderr" "abc" "defghi" "jklmnopqr")) + (lambda (sub in out err) + (let ((lines (read-lines err))) + (close-port err) + (subprocess-wait sub) + (if (zero? (subprocess-status sub)) + lines + #f))))) + +(check-equal 234 + (call-with-values + (lambda () + (subprocess (current-output-port) (current-input-port) (current-error-port) + exitcode "234")) + (lambda (sub in out err) + (subprocess-wait sub) + (subprocess-status sub)))) + +;; subprocess-wait +;; subprocess-status +;; subprocess-kill +;; subprocess-pid +;; subprocess? + +(define-values (sub1 in1 out1 err1) (subprocess #f #f 'stdout hang)) +(check-equal #t (subprocess? sub1)) +(check-equal #f (subprocess? in1)) +(check-equal #f err1) +(check-equal #t (input-port-open? in1)) +(check-equal #t (output-port-open? out1)) + +(check-equal running (subprocess-status sub1)) +(check-equal #t (and (integer? (subprocess-pid sub1)) (> (subprocess-pid sub1) 0))) + +(subprocess-kill sub1 #t) +(subprocess-wait sub1) +(check-equal #t (integer? (subprocess-status sub1))) + +(close-port in1) +(close-port out1) + +;; process/ports +;; process*/ports + +(check-equal 0 + (let* ((lst (process*/ports (current-output-port) #f (current-error-port) + stdread "ABC" "DEFGHI" "JKLMNOPQR")) + (out (cadr lst)) + (ctrl (cadddr (cdr lst)))) + (for-each + (lambda (line) + (display line out) + (newline out)) + '("ABC" "DEFGHI" "JKLMNOPQR")) + (close-port out) + (ctrl 'wait) + (ctrl 'exit-code))) + +(check-equal ("abc" "defghi" "jklmnopqr") + (let* ((lst (process*/ports #f (current-input-port) (current-error-port) + stdwrite "--stdout" "abc" "defghi" "jklmnopqr")) + (in (car lst)) + (ctrl (cadddr (cdr lst)))) + (let ((lines (read-lines in))) + (close-port in) + (ctrl 'wait) + (if (zero? (ctrl 'exit-code)) + lines + #f)))) + +(check-equal ("abc" "defghi" "jklmnopqr") + (let* ((lst (process/ports #f (current-input-port) 'stdout + "stdwrite --stderr abc defghi jklmnopqr")) + (in (car lst)) + (ctrl (cadddr (cdr lst)))) + (let ((lines (read-lines in))) + (close-port in) + (ctrl 'wait) + (if (zero? (ctrl 'exit-code)) + lines + #f)))) + +(check-equal ("abc" "defghi" "jklmnopqr") + (let* ((lst (process*/ports (current-output-port) (current-input-port) #f + stdwrite "--stderr" "abc" "defghi" "jklmnopqr")) + (err (cadddr lst)) + (ctrl (cadddr (cdr lst)))) + (let ((lines (read-lines err))) + (close-port err) + (ctrl 'wait) + (if (zero? (ctrl 'exit-code)) + lines + #f)))) + +(check-equal 123 + (let* ((lst (process*/ports (current-output-port) (current-input-port) (current-error-port) + exitcode "123")) + (ctrl (cadddr (cdr lst)))) + (ctrl 'wait) + (ctrl 'exit-code))) + +;; (<ctrl> 'status) +;; (<ctrl> 'exit-code) +;; (<ctrl> 'wait) +;; (<ctrl> 'kill) + +(define p2 (process*/ports #f #f 'stdout hang)) +(define in2 (car p2)) +(define out2 (cadr p2)) +(check-equal #t + (let ((pid (caddr p2))) + (and (integer? pid) (> pid 0)))) +(define err2 (cadddr p2)) +(define ctrl2 (cadddr (cdr p2))) + +(check-equal #f err2) +(check-equal #t (input-port-open? in2)) +(check-equal #t (output-port-open? out2)) + +(check-equal running (ctrl2 'status)) +(check-equal #f (ctrl2 'exit-code)) + +(ctrl2 'kill) +(ctrl2 'wait) +(check-equal #t + (eq? (cond-expand (windows 'done-ok) (else 'done-error)) + (ctrl2 'status))) + +(check-equal #t (integer? (ctrl2 'exit-code))) + +;; system +;; system* + +(define (with-output-to-string proc) + (let ((port (open-output-string))) + (parameterize ((current-output-port port)) (proc)) + (get-output-string port))) + +(check-equal #t + (string=? (substring "abcdefghijklmnopqr" 0 18) + (substring + (with-output-to-string + (lambda () + (system "stdwrite --stdout abcdefghijklmnopqr"))) + 0 18))) + +(define (with-input-from-string s proc) + (let ((port (open-input-string s))) + (parameterize ((current-input-port port)) (proc)))) + +(check-equal #t + (with-input-from-string "abcdef\n" + (lambda () + (system* stdread "abcdef")))) + +(check-equal #f + (with-input-from-string "abcdef" + (lambda () + (system* stdread "abc" "def")))) + +;; system/exit-code +;; system*/exit-code + +(check-equal 88 + (system/exit-code "exitcode 88")) + +(check-equal 88 + (system*/exit-code exitcode "88")) + +;; chicken process module + +(define (call-with-input-pipe cmdline proc) + (let* ((lst (process/ports #f (current-input-port) (current-error-port) cmdline)) + (in (car lst)) + (ctrl (cadddr (cdr lst)))) + (let-values ((results (proc in))) + (close-port in) + (ctrl 'wait) + (if (= (ctrl 'exit-code) 0) + (apply values results) + #f)))) + +(check-equal ("aaa" "bbb" "ccc" "ddd") + (call-with-input-pipe "stdwrite --stdout aaa bbb ccc ddd" + (lambda (in) (read-lines in)))) + +(define (call-with-output-pipe cmdline proc) + (let* ((lst (process/ports (current-output-port) #f (current-error-port) cmdline)) + (out (cadr lst)) + (ctrl (cadddr (cdr lst)))) + (let-values ((results (proc out))) + (close-port out) + (ctrl 'wait) + (if (= (ctrl 'exit-code) 0) + (apply values results) + #f)))) + +(check-equal #t + (call-with-output-pipe "stdread 1 22 333 4444 55555" + (lambda (out) + (for-each + (lambda (line) + (display line out) + (newline out)) + '("1" "22" "333" "4444" "55555")) + #t))) + +(define (with-input-from-pipe cmdline proc) + (call-with-input-pipe cmdline + (lambda (port) + (parameterize ((current-input-port port)) (proc))))) + +(check-equal ("aaa" "bbb" "ccc" "ddd") + (with-input-from-pipe "stdwrite --stdout aaa bbb ccc ddd" + (lambda () (read-lines (current-input-port))))) + +(define (with-output-to-pipe cmdline proc) + (call-with-output-pipe cmdline + (lambda (port) + (parameterize ((current-output-port port)) (proc))))) + +(check-equal #t + (with-output-to-pipe "stdread 1 22 333 4444 55555" + (lambda () + (for-each + (lambda (line) + (display line) + (newline)) + '("1" "22" "333" "4444" "55555")) + #t))) diff --git a/test/stdread.cpp b/test/stdread.cpp new file mode 100644 index 0000000..fccadae --- /dev/null +++ b/test/stdread.cpp @@ -0,0 +1,30 @@ +/* + +Read lines from standard input; each line should match one argument. + +*/ + +#include <string.h> +#include <stdio.h> + +int main(int argc, char * argv[]) +{ + char s[1024]; + int adx = 1; + + for (;;) + { + if (fgets(s, sizeof(s), stdin) == 0) + break; + if (adx == argc) + return(1); + size_t sl = strlen(s); + if (sl > 0) + s[sl - 1] = 0; + if (strcmp(s, argv[adx]) != 0) + return(2); + adx += 1; + } + + return(0); +} diff --git a/test/stdwrite.cpp b/test/stdwrite.cpp new file mode 100644 index 0000000..dbcb818 --- /dev/null +++ b/test/stdwrite.cpp @@ -0,0 +1,30 @@ +/* + +Write lines to standard output or error; each line is an argument. + +*/ + +#include <string.h> +#include <stdio.h> + +int main(int argc, char * argv[]) +{ + FILE * fp; + + if (argc < 2) + return(1); + if (strcmp(argv[1], "--stdout") == 0) + fp = stdout; + else if (strcmp(argv[1], "--stderr") == 0) + fp = stderr; + else + return(2); + + for (int adx = 2; adx < argc; adx += 1) + { + fputs(argv[adx], fp); + fputc('\n', fp); + } + + return(0); +} diff --git a/unix/makefile b/unix/makefile index a048ed9..a45488a 100644 --- a/unix/makefile +++ b/unix/makefile @@ -2,7 +2,6 @@ # Foment # -CC ?= gcc CXX ?= g++ BUILD_CXX ?= g++ @@ -39,7 +38,7 @@ clean: debug release profile -rm profile/* .PHONY: test -test: all foment-test stress-test chibi-test +test: all foment-test stress-test chibi-test process-test .PHONY: test-all test-all: all @@ -72,6 +71,10 @@ stress-test: all chibi-test: all cd ../test ; ../unix/$(TEST_BUILD)/foment $(TEST_OPTIONS) r7rs-tests.scm +.PHONY: process-test +process-test: all debug/stdread debug/stdwrite debug/exitcode debug/hang + PATH=../unix/debug:$(PATH) ; cd ../test ; ../unix/$(TEST_BUILD)/foment $(TEST_OPTIONS) runtests.scm process.scm + debug: -mkdir debug @@ -93,7 +96,7 @@ debug/foment: debug/foment.o debug/gc.o debug/syncthrd.o debug/compile.o debug/i debug/pairs.o debug/unicode.o debug/chars.o debug/strings.o debug/vectors.o\ debug/library.o debug/execute.o debug/numbers.o debug/write.o\ debug/read.o debug/filesys.o debug/compare.o debug/main.o debug/hashtbl.o\ - debug/bignums.o debug/charset.o debug/base.o + debug/bignums.o debug/charset.o debug/process.o debug/base.o $(CXX) $(LDFLAGS) -o debug/foment $^ -lpthread release/foment: release/foment.o release/gc.o release/syncthrd.o release/compile.o release/io.o\ @@ -102,7 +105,7 @@ release/foment: release/foment.o release/gc.o release/syncthrd.o release/compile release/vectors.o release/library.o release/execute.o release/numbers.o\ release/write.o release/read.o release/filesys.o\ release/compare.o release/main.o release/hashtbl.o release/bignums.o release/charset.o\ - release/base.o + release/process.o release/base.o $(CXX) $(LDFLAGS) -o release/foment $^ -lpthread profile/foment: profile/foment.o profile/gc.o profile/syncthrd.o profile/compile.o profile/io.o\ @@ -111,7 +114,7 @@ profile/foment: profile/foment.o profile/gc.o profile/syncthrd.o profile/compile profile/vectors.o profile/library.o profile/execute.o profile/numbers.o\ profile/write.o profile/read.o profile/filesys.o\ profile/compare.o profile/main.o profile/hashtbl.o profile/bignums.o profile/charset.o\ - profile/base.o + profile/process.o profile/base.o $(CXX) $(LDFLAGS) -pg -o profile/foment $^ -lpthread debug/foment.o: ../src/foment.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/unicode.hpp @@ -132,6 +135,8 @@ debug/execute.o: ../src/execute.cpp ../src/foment.hpp ../src/execute.hpp ../src/ debug/numbers.o: ../src/numbers.cpp ../src/foment.hpp ../src/unicode.hpp ../src/bignums.hpp debug/bignums.o: ../src/bignums.cpp ../src/foment.hpp ../src/unicode.hpp ../src/bignums.hpp debug/charset.o: ../src/charset.cpp ../src/foment.hpp ../src/unicode.hpp ../src/unicrng.hpp +debug/process.o: ../src/process.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/io.hpp\ + ../src/unicode.hpp debug/io.o: ../src/io.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/io.hpp\ ../src/unicode.hpp debug/write.o: ../src/write.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/io.hpp\ @@ -162,6 +167,8 @@ release/execute.o: ../src/execute.cpp ../src/foment.hpp ../src/execute.hpp ../sr release/numbers.o: ../src/numbers.cpp ../src/foment.hpp ../src/unicode.hpp ../src/bignums.hpp release/bignums.o: ../src/bignums.cpp ../src/foment.hpp ../src/unicode.hpp ../src/bignums.hpp release/charset.o: ../src/charset.cpp ../src/foment.hpp ../src/unicode.hpp ../src/unicrng.hpp +release/process.o: ../src/process.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/io.hpp\ + ../src/unicode.hpp release/io.o: ../src/io.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/io.hpp\ ../src/unicode.hpp release/write.o: ../src/write.cpp ../src/foment.hpp ../src/syncthrd.hpp ../src/io.hpp\ @@ -180,9 +187,6 @@ debug/%.o: %.cpp debug/%.o: ../src/%.cpp $(CXX) $(CCDEBUG) -I ../src -o $@ $< -debug/%.o: ../src/%.c - $(CC) $(CCDEBUG) -I ../src -o $@ $< - debug/%.o: debug/%.cpp $(CXX) $(CCDEBUG) -I ../src -o $@ $< @@ -192,9 +196,6 @@ release/%.o: %.cpp release/%.o: ../src/%.cpp $(CXX) $(CCRELEASE) -I ../src -o $@ $< -release/%.o: ../src/%.c - $(CC) $(CCRELEASE) -I ../src -o $@ $< - release/%.o: debug/%.cpp $(CXX) $(CCRELEASE) -I ../src -o $@ $< @@ -204,12 +205,14 @@ profile/%.o: %.cpp profile/%.o: ../src/%.cpp $(CXX) $(CCPROFILE) -I ../src -o $@ $< -profile/%.o: ../src/%.c - $(CC) $(CCPROFILE) -I ../src -o $@ $< - profile/%.o: debug/%.cpp $(CXX) $(CCPROFILE) -I ../src -o $@ $< +debug/%: ../test/%.cpp + $(BUILD_CXX) $(CCDEBUG) -o debug/$*.o $< + $(BUILD_CXX) debug/$*.o -o $@ + debug/txt2cpp: ../src/txt2cpp.cpp $(BUILD_CXX) $(CCDEBUG) ../src/txt2cpp.cpp -o debug/txt2cpp.o $(BUILD_CXX) debug/txt2cpp.o -o debug/txt2cpp + diff --git a/windows/makefile b/windows/makefile index 0579f7a..e8205f9 100644 --- a/windows/makefile +++ b/windows/makefile @@ -2,22 +2,25 @@ # Foment # +PATH = $(PATH);../windows/debug + TEST_OPTIONS = --check-heap TEST_BUILD = debug -CLDEBUG = /nologo /MD /W3 /Gm /EHsc /Zi /Od /c /Fodebug/ /DFOMENT_DEBUG /DFOMENT_WINDOWS -CLRELEASE = /nologo /MD /W3 /Ox /Zi /GA /EHsc /Forelease/ /c /DFOMENT_WINDOWS +CLDEBUG = /nologo /MD /W3 /EHsc /Zi /Od /c /Fodebug\ /DFOMENT_DEBUG /DFOMENT_WINDOWS +CLRELEASE = /nologo /MD /W3 /Ox /Zi /GA /EHsc /Forelease\ /c /DFOMENT_WINDOWS LIBS = ws2_32.lib iphlpapi.lib -all: debug release debug\foment.exe release\foment.exe +all: debug release debug\foment.exe release\foment.exe debug\stdread.exe debug\stdwrite.exe\ + debug\exitcode.exe debug\hang.exe clean: debug release manual del /Q debug\* del /Q release\* del /Q manual\* -test: all foment-test stress-test chibi-test +test: all foment-test stress-test chibi-test process-test test-all: all test-no-collector test-mark-sweep @@ -53,6 +56,10 @@ chibi-test: all cd ..\test ..\windows\$(TEST_BUILD)\foment $(TEST_OPTIONS) runtests.scm stress.scm r7rs-tests.scm +process-test: all + cd ..\test + ..\windows\$(TEST_BUILD)\foment $(TEST_OPTIONS) runtests.scm process.scm + debug: -mkdir debug @@ -74,7 +81,7 @@ debug\foment.exe: debug\foment.obj debug\gc.obj debug\syncthrd.obj debug\compile debug\pairs.obj debug\unicode.obj debug\chars.obj debug\strings.obj debug\vectors.obj\ debug\library.obj debug\execute.obj debug\numbers.obj debug\write.obj\ debug\read.obj debug\filesys.obj debug\compare.obj debug\main.obj debug\hashtbl.obj\ - debug\bignums.obj debug\charset.obj debug\base.obj + debug\bignums.obj debug\charset.obj debug\process.obj debug\base.obj link /nologo /subsystem:console /out:debug\foment.exe /debug /pdb:debug\foment.pdb\ /largeaddressaware $** $(LIBS) @@ -84,14 +91,25 @@ release\foment.exe: release\foment.obj release\gc.obj release\syncthrd.obj relea release\strings.obj release\vectors.obj release\library.obj release\execute.obj\ release\numbers.obj release\write.obj release\read.obj\ release\filesys.obj release\compare.obj release\main.obj release\hashtbl.obj\ - release\bignums.obj release\charset.obj release\base.obj + release\bignums.obj release\charset.obj release\process.obj release\base.obj # link /nologo /subsystem:console /out:release\foment.exe $** link /nologo /subsystem:console /out:release\foment.exe /debug /pdb:release\foment.pdb\ /largeaddressaware $** $(LIBS) debug\txt2cpp.exe: debug\txt2cpp.obj - link /nologo /subsystem:console /out:debug\txt2cpp.exe\ - debug\txt2cpp.obj + link /nologo /subsystem:console /out:debug\txt2cpp.exe debug\txt2cpp.obj + +debug\stdread.exe: debug\stdread.obj + link /nologo /subsystem:console /out:debug\stdread.exe debug\stdread.obj + +debug\stdwrite.exe: debug\stdwrite.obj + link /nologo /subsystem:console /out:debug\stdwrite.exe debug\stdwrite.obj + +debug\exitcode.exe: debug\exitcode.obj + link /nologo /subsystem:console /out:debug\exitcode.exe debug\exitcode.obj + +debug\hang.exe: debug\hang.obj + link /nologo /subsystem:console /out:debug\hang.exe debug\hang.obj debug\foment.obj: ..\src\foment.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\unicode.hpp debug\gc.obj: ..\src\gc.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp @@ -111,6 +129,8 @@ debug\execute.obj: ..\src\execute.cpp ..\src\foment.hpp ..\src\execute.hpp ..\sr debug\numbers.obj: ..\src\numbers.cpp ..\src\foment.hpp ..\src\unicode.hpp ..\src\bignums.hpp debug\bignums.obj: ..\src\bignums.cpp ..\src\foment.hpp ..\src\unicode.hpp ..\src\bignums.hpp debug\charset.obj: ..\src\charset.cpp ..\src\foment.hpp ..\src\unicode.hpp ..\src\unicrng.hpp +debug\process.obj: ..\src\process.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp\ + ..\src\unicode.hpp debug\io.obj: ..\src\io.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp ..\src\unicode.hpp debug\write.obj: ..\src\write.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp\ ..\src\compile.hpp @@ -140,6 +160,8 @@ release\execute.obj: ..\src\execute.cpp ..\src\foment.hpp ..\src\execute.hpp ..\ release\numbers.obj: ..\src\numbers.cpp ..\src\foment.hpp ..\src\unicode.hpp ..\src\bignums.hpp release\bignums.obj: ..\src\bignums.cpp ..\src\foment.hpp ..\src\unicode.hpp ..\src\bignums.hpp release\charset.obj: ..\src\charset.cpp ..\src\foment.hpp ..\src\unicode.hpp ..\src\unicrng.hpp +release\process.obj: ..\src\process.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp\ + ..\src\unicode.hpp release\io.obj: ..\src\io.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp\ ..\src\unicode.hpp release\write.obj: ..\src\write.cpp ..\src\foment.hpp ..\src\syncthrd.hpp ..\src\io.hpp\ @@ -154,6 +176,11 @@ release\base.obj: debug\base.cpp debug\txt2cpp.obj: ..\src\txt2cpp.cpp +debug\stdread.obj: ..\test\stdread.cpp +debug\stdwrite.obj: ..\test\stdwrite.cpp +debug\exitcode.obj: ..\test\exitcode.cpp +debug\hang.obj: ..\test\hang.cpp + {.}.cpp.{debug}.obj: cl $(CLDEBUG) $(*B).cpp @@ -178,3 +205,5 @@ debug\txt2cpp.obj: ..\src\txt2cpp.cpp {debug\}.cpp.{release}.obj: cl $(CLRELEASE) debug\$(*B).cpp +{..\test\}.cpp.{debug}.obj: + cl $(CLDEBUG) ..\test\$(*B).cpp |