From d8a10457e21654272e9de67048182d845e87eb3a Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 28 Sep 2015 23:04:17 +0100 Subject: [PATCH 01/15] Fixes #3207. --- lib/pure/uri.nim | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 492de3b460..abb1a462d8 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -142,6 +142,7 @@ proc parseUri*(uri: string): Uri = parseUri(uri, result) proc removeDotSegments(path: string): string = + if path.len == 0: return "" var collection: seq[string] = @[] let endsWithSlash = path[path.len-1] == '/' var i = 0 @@ -432,3 +433,12 @@ when isMainModule: block: let test = parseUri("http://example.com/foo/") / "/bar/asd" doAssert test.path == "/foo/bar/asd" + + # removeDotSegments tests + block: + # empty test + doAssert removeDotSegments("") == "" + + # bug #3207 + block: + doAssert parseUri("http://qq/1").combine(parseUri("https://qqq")).`$` == "https://qqq" From 900ea8103014dcd8e7f72c774689743df2d3075c Mon Sep 17 00:00:00 2001 From: Adam Strzelecki Date: Tue, 29 Sep 2015 19:23:16 +0200 Subject: [PATCH 02/15] lib/posix: OS X & Free/Open/NetBSD kqueue API --- lib/posix/kqueue.nim | 71 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 lib/posix/kqueue.nim diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim new file mode 100644 index 0000000000..511ada9ace --- /dev/null +++ b/lib/posix/kqueue.nim @@ -0,0 +1,71 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Adam Strzelecki +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +{.deadCodeElim:on.} + +from posix import Timespec + +# Filters: +const + EVFILT_READ* = -1 + EVFILT_WRITE* = -2 + EVFILT_AIO* = -3 + EVFILT_VNODE* = -4 + EVFILT_PROC* = -5 + EVFILT_SIGNAL* = -6 + EVFILT_TIMER* = -7 + EVFILT_MACHPORT* = -8 + EVFILT_FS* = -9 + EVFILT_USER* = -10 + # -11 is unused + EVFILT_VM* = -12 + +# Actions: +const + EV_ADD* = 0x0001 ## Add event to queue (implies enable). + ## Re-adding an existing element modifies it. + EV_DELETE* = 0x0002 ## Delete event from queue. + EV_ENABLE* = 0x0004 ## Enable event. + EV_DISABLE* = 0x0008 ## Disable event (not reported). + +# Flags: +const + EV_ONESHOT* = 0x0010 ## Only report one occurrence. + EV_CLEAR* = 0x0020 ## Clear event state after reporting. + EV_RECEIPT* = 0x0040 ## Force EV_ERROR on success, data == 0 + EV_DISPATCH* = 0x0080 ## Disable event after reporting. + +# Return values: +const + EV_EOF* = 0x8000 ## EOF detected + EV_ERROR* = 0x4000 ## Error, data contains errno + +type + KEvent* {.importc: "struct kevent", + header: "", pure, final.} = object + ident*: cuint ## identifier for this event (uintptr_t) + filter*: cshort ## filter for event + flags*: cushort ## general flags + fflags*: cuint ## filter-specific flags + data*: cuint ## filter-specific data (intptr_t) + #udata*: ptr void ## opaque user data identifier + +proc kqueue*(): cint {.importc: "kqueue", header: "".} + ## Creates new queue and returns its descriptor. + +proc kevent*(kqFD: cint, + changelist: ptr KEvent, nchanges: cint, + eventlist: ptr KEvent, nevents: cint, timeout: ptr Timespec): cint + {.importc: "kevent", header: "".} + ## Manipulates queue for given ``kqFD`` descriptor. + +proc EV_SET*(event: ptr KEvent, ident: cuint, filter: cshort, flags: cushort, + fflags: cuint, data: cuint, udata: ptr void) + {.importc: "EV_SET", header: "".} + ## Fills event with given data. From 248f52fea7f22beac1ac75022b6b4d49964f6071 Mon Sep 17 00:00:00 2001 From: Adam Strzelecki Date: Tue, 29 Sep 2015 19:27:41 +0200 Subject: [PATCH 03/15] Selectors using OS X & Free/Open/NetBSD kqueue API --- lib/pure/selectors.nim | 82 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index bfc393a964..2ee8f11807 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -13,6 +13,8 @@ import os, unsigned, hashes when defined(linux): import posix, epoll +elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd): + import posix, kqueue, times elif defined(windows): import winlean else: @@ -204,6 +206,86 @@ elif defined(linux): ## Retrieves the selector key for ``fd``. return s.fds[fd] +elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd): + type + Selector* = object + kqFD: cint + events: array[64, KEvent] + when MultiThreaded: + fds: SharedTable[SocketHandle, SelectorKey] + else: + fds: Table[SocketHandle, SelectorKey] + + template modifyKQueue(kqFD: cint, fd: SocketHandle, event: Event, + op: cushort) = + var kev = KEvent(ident: fd.cuint, + filter: if event == EvRead: EVFILT_READ else: EVFILT_WRITE, + flags: op) + if kevent(kqFD, addr kev, 1, nil, 0, nil) == -1: + raiseOSError(osLastError()) + + proc register*(s: var Selector, fd: SocketHandle, events: set[Event], + data: SelectorData) = + for event in events: + modifyKQueue(s.kqFD, fd, event, EV_ADD) + s.fds[fd] = SelectorKey(fd: fd, events: events, data: data) + + proc update*(s: var Selector, fd: SocketHandle, events: set[Event]) = + let previousEvents = s.fds[fd].events + if previousEvents != events: + for event in events-previousEvents: + modifyKQueue(s.kqFD, fd, event, EV_ADD) + for event in previousEvents-events: + modifyKQueue(s.kqFD, fd, event, EV_DELETE) + s.fds.mget(fd).events = events + + proc unregister*(s: var Selector, fd: SocketHandle) = + for event in s.fds[fd].events: + modifyKQueue(s.kqFD, fd, event, EV_DELETE) + s.fds.del(fd) + + proc close*(s: var Selector) = + when MultiThreaded: deinitSharedTable(s.fds) + if s.kqFD.close() != 0: raiseOSError(osLastError()) + + proc select*(s: var Selector, timeout: int): seq[ReadyInfo] = + result = @[] + var tv = Timespec(tv_sec: timeout.Time, tv_nsec: 0) + let evNum = kevent(s.kqFD, nil, 0, addr s.events[0], 64.cint, addr tv) + if evNum < 0: + let err = osLastError() + if err.cint == EINTR: + return @[] + raiseOSError(err) + if evNum == 0: return @[] + for i in 0 .. Date: Tue, 29 Sep 2015 19:28:10 +0200 Subject: [PATCH 04/15] selectors: Cleanup a bit epoll flavor 1. Remove select documentation that is duplicate of nimdoc section below 2. Simplify a bit register proc code --- lib/pure/selectors.nim | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 2ee8f11807..ca969c761b 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -81,7 +81,6 @@ when defined(nimdoc): proc `[]`*(s: Selector, fd: SocketHandle): SelectorKey = ## Retrieves the selector key for ``fd``. - elif defined(linux): type Selector* = object @@ -101,15 +100,13 @@ elif defined(linux): result.data.fd = fd.cint proc register*(s: var Selector, fd: SocketHandle, events: set[Event], - data: SelectorData) = + data: SelectorData) = var event = createEventStruct(events, fd) if events != {}: if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: raiseOSError(osLastError()) - var key = SelectorKey(fd: fd, events: events, data: data) - - s.fds[fd] = key + s.fds[fd] = SelectorKey(fd: fd, events: events, data: data) proc update*(s: var Selector, fd: SocketHandle, events: set[Event]) = if s.fds[fd].events != events: @@ -156,11 +153,6 @@ elif defined(linux): raiseOSError(err) proc select*(s: var Selector, timeout: int): seq[ReadyInfo] = - ## - ## The ``events`` field of the returned ``key`` contains the original events - ## for which the ``fd`` was bound. This is contrary to the ``events`` field - ## of the ``TReadyInfo`` tuple which determines which events are ready - ## on the ``fd``. result = @[] let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint) if evNum < 0: From 985594cbb8274e21c65f88485f3c4d83d6ff60d2 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 28 Sep 2015 16:39:52 +0200 Subject: [PATCH 05/15] added streams.readAll proc --- lib/pure/streams.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 406a0ec6e8..68f31e9fea 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -101,6 +101,18 @@ proc readData*(s: Stream, buffer: pointer, bufLen: int): int = ## low level proc that reads data into an untyped `buffer` of `bufLen` size. result = s.readDataImpl(s, buffer, bufLen) +proc readAll*(s: Stream): string = + ## Reads all available data. + result = newString(1000) + var r = 0 + while true: + let readBytes = readData(s, addr(result[r]), 1000) + if readBytes < 1000: + setLen(result, r+readBytes) + break + inc r, 1000 + setLen(result, r+1000) + proc readData*(s, unused: Stream, buffer: pointer, bufLen: int): int {.deprecated.} = ## low level proc that reads data into an untyped `buffer` of `bufLen` size. From ab6f8f6e5b8aa365d7725d6866904c3fa2e0e553 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 28 Sep 2015 16:41:03 +0200 Subject: [PATCH 06/15] fixesunicode.lastRune --- lib/pure/unicode.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index d3dc77909b..b059a7315f 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -1340,10 +1340,9 @@ proc lastRune*(s: string; last: int): (Rune, int) = else: var L = 0 while last-L >= 0 and ord(s[last-L]) shr 6 == 0b10: inc(L) - inc(L) var r: Rune fastRuneAt(s, last-L, r, false) - result = (r, L) + result = (r, L+1) when isMainModule: let From c852143f3a45ed42f03d4d70225c92079bd475ed Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 28 Sep 2015 16:41:36 +0200 Subject: [PATCH 07/15] os.walkDir supports yielding relative paths --- lib/pure/os.nim | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index f413371cbc..c012285630 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -810,11 +810,12 @@ type {.deprecated: [TPathComponent: PathComponent].} -iterator walkDir*(dir: string): tuple[kind: PathComponent, path: string] {. +iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] {. tags: [ReadDirEffect].} = ## walks over the directory `dir` and yields for each directory or file in ## `dir`. The component type and full path for each item is returned. - ## Walking is not recursive. + ## Walking is not recursive. If ``relative`` is true the resulting path is + ## shortened to be relative to ``dir``. ## Example: This directory structure:: ## dirA / dirB / fileB1.txt ## / dirC @@ -843,7 +844,9 @@ iterator walkDir*(dir: string): tuple[kind: PathComponent, path: string] {. k = pcDir if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32: k = succ(k) - yield (k, dir / extractFilename(getFilename(f))) + let xx = if relative: extractFilename(getFilename(f)) + else: dir / extractFilename(getFilename(f)) + yield (k, xx) if findNextFile(h, f) == 0'i32: break findClose(h) else: @@ -855,7 +858,8 @@ iterator walkDir*(dir: string): tuple[kind: PathComponent, path: string] {. var y = $x.d_name if y != "." and y != "..": var s: Stat - y = dir / y + if not relative: + y = dir / y var k = pcFile when defined(linux) or defined(macosx) or defined(bsd): From 4e44ded2be609aa3e36ce5bd59301d2ab0bd60bd Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 28 Sep 2015 16:42:40 +0200 Subject: [PATCH 08/15] winlean additions; preparing for osproc patch to use named pipes --- lib/windows/winlean.nim | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 84dac6d798..e6a119e15f 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -108,6 +108,13 @@ const CREATE_UNICODE_ENVIRONMENT* = 1024'i32 + PIPE_ACCESS_DUPLEX* = 0x00000003'i32 + PIPE_ACCESS_INBOUND* = 1'i32 + PIPE_ACCESS_OUTBOUND* = 2'i32 + PIPE_NOWAIT* = 0x00000001'i32 + SYNCHRONIZE* = 0x00100000'i32 + FILE_FLAG_WRITE_THROUGH* = 0x80000000'i32 + proc closeHandle*(hObject: Handle): WINBOOL {.stdcall, dynlib: "kernel32", importc: "CloseHandle".} @@ -125,6 +132,19 @@ proc createPipe*(hReadPipe, hWritePipe: var Handle, nSize: int32): WINBOOL{. stdcall, dynlib: "kernel32", importc: "CreatePipe".} +proc createNamedPipe*(lpName: WideCString, + dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize, + nInBufferSize, nDefaultTimeOut: int32, + lpSecurityAttributes: ptr SECURITY_ATTRIBUTES): Handle {. + stdcall, dynlib: "kernel32", importc: "CreateNamedPipeW".} + +proc peekNamedPipe*(hNamedPipe: Handle, lpBuffer: pointer=nil, + nBufferSize: int32 = 0, + lpBytesRead: ptr int32 = nil, + lpTotalBytesAvail: ptr int32 = nil, + lpBytesLeftThisMessage: ptr int32 = nil): bool {. + stdcall, dynlib: "kernel32", importc: "PeekNamedPipe".} + when useWinUnicode: proc createProcessW*(lpApplicationName, lpCommandLine: WideCString, lpProcessAttributes: ptr SECURITY_ATTRIBUTES, @@ -615,12 +635,24 @@ const FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32 FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32 + DUPLICATE_SAME_ACCESS* = 2 + FILE_READ_DATA* = 0x00000001 # file & pipe + FILE_WRITE_DATA* = 0x00000002 # file & pipe # Error Constants const ERROR_ACCESS_DENIED* = 5 ERROR_HANDLE_EOF* = 38 +proc duplicateHandle*(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE, + hTargetProcessHandle: HANDLE, + lpTargetHandle: ptr HANDLE, + dwDesiredAccess: DWORD, bInheritHandle: WINBOOL, + dwOptions: DWORD): WINBOOL{.stdcall, dynlib: "kernel32", + importc: "DuplicateHandle".} +proc getCurrentProcess*(): HANDLE{.stdcall, dynlib: "kernel32", + importc: "GetCurrentProcess".} + when useWinUnicode: proc createFileW*(lpFileName: WideCString, dwDesiredAccess, dwShareMode: DWORD, lpSecurityAttributes: pointer, From 6268bf33a232d4060ebdfb58eb436f8b3fef8ddb Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 29 Sep 2015 17:42:00 +0200 Subject: [PATCH 09/15] fixes #3387 --- compiler/vmgen.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 95fa43b484..0559acc880 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1383,7 +1383,6 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = elif arrayType == tyTypeDesc: c.genTypeLit(n.typ, dest) else: - echo renderTree(n) genArrAccess2(c, n, dest, opcLdArr, flags) proc getNullValueAux(obj: PNode, result: PNode) = From 5f7ad9131f7e02416a6c9aff7a2caf7d6096697b Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 29 Sep 2015 19:29:45 +0200 Subject: [PATCH 10/15] added osproc.poInteractive and osproc.hasData; both experimental --- lib/pure/osproc.nim | 130 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 105 insertions(+), 25 deletions(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 7431be702f..454f9eda0c 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -24,6 +24,20 @@ when defined(linux): import linux type + ProcessOption* = enum ## options that can be passed `startProcess` + poEchoCmd, ## echo the command before execution + poUsePath, ## Asks system to search for executable using PATH environment + ## variable. + ## On Windows, this is the default. + poEvalCommand, ## Pass `command` directly to the shell, without quoting. + ## Use it only if `command` comes from trused source. + poStdErrToStdOut, ## merge stdout and stderr to the stdout stream + poParentStreams, ## use the parent's streams + poInteractive ## optimize the buffer handling for responsiveness for + ## UI applications. Currently this only affects + ## Windows: Named pipes are used so that you can peek + ## at the process' output streams. + ProcessObj = object of RootObj when defined(windows): fProcessHandle: Handle @@ -34,18 +48,10 @@ type inStream, outStream, errStream: Stream id: Pid exitCode: cint + options: set[ProcessOption] Process* = ref ProcessObj ## represents an operating system process - ProcessOption* = enum ## options that can be passed `startProcess` - poEchoCmd, ## echo the command before execution - poUsePath, ## Asks system to search for executable using PATH environment - ## variable. - ## On Windows, this is the default. - poEvalCommand, ## Pass `command` directly to the shell, without quoting. - ## Use it only if `command` comes from trused source. - poStdErrToStdOut, ## merge stdout and stderr to the stdout stream - poParentStreams ## use the parent's streams {.deprecated: [TProcess: ProcessObj, PProcess: Process, TProcessOption: ProcessOption].} @@ -302,7 +308,7 @@ proc execProcesses*(cmds: openArray[string], result = max(waitForExit(p), result) close(p) -proc select*(readfds: var seq[Process], timeout = 500): int +proc select*(readfds: var seq[Process], timeout = 500): int {.benign.} ## `select` with a sensible Nim interface. `timeout` is in milliseconds. ## Specify -1 for no timeout. Returns the number of processes that are ## ready to read from. The processes that are ready to be read from are @@ -394,13 +400,68 @@ when defined(Windows) and not defined(useNimRtl): #var # O_WRONLY {.importc: "_O_WRONLY", header: "".}: int # O_RDONLY {.importc: "_O_RDONLY", header: "".}: int + proc myDup(h: Handle; inherit: WinBool=1): Handle = + let thisProc = getCurrentProcess() + if duplicateHandle(thisProc, h, + thisProc, addr result,0,inherit, + DUPLICATE_SAME_ACCESS) == 0: + raiseOSError(osLastError()) + + proc createAllPipeHandles(si: var STARTUPINFO; + stdin, stdout, stderr: var Handle) = + var sa: SECURITY_ATTRIBUTES + sa.nLength = sizeof(SECURITY_ATTRIBUTES).cint + sa.lpSecurityDescriptor = nil + sa.bInheritHandle = 1 + let pipeOutName = newWideCString(r"\\.\pipe\stdout") + let pipeInName = newWideCString(r"\\.\pipe\stdin") + let pipeOut = createNamedPipe(pipeOutName, + dwOpenMode=PIPE_ACCESS_INBOUND or FILE_FLAG_WRITE_THROUGH, + dwPipeMode=PIPE_NOWAIT, + nMaxInstances=1, + nOutBufferSize=1024, nInBufferSize=1024, + nDefaultTimeOut=0,addr sa) + if pipeOut == INVALID_HANDLE_VALUE: + raiseOSError(osLastError()) + let pipeIn = createNamedPipe(pipeInName, + dwOpenMode=PIPE_ACCESS_OUTBOUND or FILE_FLAG_WRITE_THROUGH, + dwPipeMode=PIPE_NOWAIT, + nMaxInstances=1, + nOutBufferSize=1024, nInBufferSize=1024, + nDefaultTimeOut=0,addr sa) + if pipeIn == INVALID_HANDLE_VALUE: + raiseOSError(osLastError()) + + si.hStdOutput = createFileW(pipeOutName, + FILE_WRITE_DATA or SYNCHRONIZE, 0, addr sa, + OPEN_EXISTING, # very important flag! + FILE_ATTRIBUTE_NORMAL, + 0 # no template file for OPEN_EXISTING + ) + if si.hStdOutput == INVALID_HANDLE_VALUE: + raiseOSError(osLastError()) + si.hStdError = myDup(si.hStdOutput) + si.hStdInput = createFileW(pipeInName, + FILE_READ_DATA or SYNCHRONIZE, 0, addr sa, + OPEN_EXISTING, # very important flag! + FILE_ATTRIBUTE_NORMAL, + 0 # no template file for OPEN_EXISTING + ) + if si.hStdOutput == INVALID_HANDLE_VALUE: + raiseOSError(osLastError()) + + stdin = myDup(pipeIn, 0) + stdout = myDup(pipeOut, 0) + discard closeHandle(pipeIn) + discard closeHandle(pipeOut) + stderr = stdout proc createPipeHandles(rdHandle, wrHandle: var Handle) = - var piInheritablePipe: SECURITY_ATTRIBUTES - piInheritablePipe.nLength = sizeof(SECURITY_ATTRIBUTES).cint - piInheritablePipe.lpSecurityDescriptor = nil - piInheritablePipe.bInheritHandle = 1 - if createPipe(rdHandle, wrHandle, piInheritablePipe, 1024) == 0'i32: + var sa: SECURITY_ATTRIBUTES + sa.nLength = sizeof(SECURITY_ATTRIBUTES).cint + sa.lpSecurityDescriptor = nil + sa.bInheritHandle = 1 + if createPipe(rdHandle, wrHandle, sa, 1024) == 0'i32: raiseOSError(osLastError()) proc fileClose(h: Handle) {.inline.} = @@ -417,16 +478,20 @@ when defined(Windows) and not defined(useNimRtl): success: int hi, ho, he: Handle new(result) + result.options = options si.cb = sizeof(si).cint if poParentStreams notin options: si.dwFlags = STARTF_USESTDHANDLES # STARTF_USESHOWWINDOW or - createPipeHandles(si.hStdInput, hi) - createPipeHandles(ho, si.hStdOutput) - if poStdErrToStdOut in options: - si.hStdError = si.hStdOutput - he = ho + if poInteractive notin options: + createPipeHandles(si.hStdInput, hi) + createPipeHandles(ho, si.hStdOutput) + if poStdErrToStdOut in options: + si.hStdError = si.hStdOutput + he = ho + else: + createPipeHandles(he, si.hStdError) else: - createPipeHandles(he, si.hStdError) + createAllPipeHandles(si, hi, ho, he) result.inHandle = FileHandle(hi) result.outHandle = FileHandle(ho) result.errHandle = FileHandle(he) @@ -482,12 +547,12 @@ when defined(Windows) and not defined(useNimRtl): result.id = procInfo.dwProcessId proc close(p: Process) = - when false: - # somehow this does not work on Windows: + if poInteractive in p.options: + # somehow this is not always required on Windows: discard closeHandle(p.inHandle) discard closeHandle(p.outHandle) discard closeHandle(p.errHandle) - discard closeHandle(p.FProcessHandle) + #discard closeHandle(p.FProcessHandle) proc suspend(p: Process) = discard suspendThread(p.fProcessHandle) @@ -564,7 +629,7 @@ when defined(Windows) and not defined(useNimRtl): assert readfds.len <= MAXIMUM_WAIT_OBJECTS var rfds: WOHandleArray for i in 0..readfds.len()-1: - rfds[i] = readfds[i].fProcessHandle + rfds[i] = readfds[i].outHandle #fProcessHandle var ret = waitForMultipleObjects(readfds.len.int32, addr(rfds), 0'i32, timeout.int32) @@ -578,6 +643,11 @@ when defined(Windows) and not defined(useNimRtl): readfds.del(i) return 1 + proc hasData*(p: Process): bool = + var x: int32 + if peekNamedPipe(p.outHandle, lpTotalBytesAvail=addr x): + result = x > 0 + elif not defined(useNimRtl): const readIdx = 0 @@ -635,6 +705,7 @@ elif not defined(useNimRtl): var pStdin, pStdout, pStderr: array [0..1, cint] new(result) + result.options = options result.exitCode = -3 # for ``waitForExit`` if poParentStreams notin options: if pipe(pStdin) != 0'i32 or pipe(pStdout) != 0'i32 or @@ -960,6 +1031,15 @@ elif not defined(useNimRtl): pruneProcessSet(readfds, (rd)) + proc hasData*(p: Process): bool = + var rd: TFdSet + + FD_ZERO(rd) + let m = max(0, int(p.outHandle)) + FD_SET(cint(p.outHandle), rd) + + result = int(select(cint(m+1), addr(rd), nil, nil, nil)) == 1 + proc execCmdEx*(command: string, options: set[ProcessOption] = { poStdErrToStdOut, poUsePath}): tuple[ From 27bdf5c45c7850f62ae573fdcd86e8490a2bec15 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 29 Sep 2015 20:00:28 +0200 Subject: [PATCH 11/15] osproc: free resources properly for the new poInteractive flag --- lib/pure/osproc.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 454f9eda0c..bc73f7119d 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -534,6 +534,7 @@ when defined(Windows) and not defined(useNimRtl): if e != nil: dealloc(e) if success == 0: + if poInteractive in result.options: close(result) const errInvalidParameter = 87.int const errFileNotFound = 2.int if lastError.int in {errInvalidParameter, errFileNotFound}: From 1b7d8246c13c48ba8037321a000d5688a8bdb5fe Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 29 Sep 2015 20:44:12 +0200 Subject: [PATCH 12/15] NimScript: setCommand takes an optional project filename --- compiler/nim.nim | 2 +- compiler/scriptconfig.nim | 7 +++++++ lib/system/nimscript.nim | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/nim.nim b/compiler/nim.nim index 51f4cae92f..64c4e2026f 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -48,7 +48,7 @@ proc handleCmdLine() = gProjectFull = canonicalizePath(gProjectName) except OSError: gProjectFull = gProjectName - var p = splitFile(gProjectFull) + let p = splitFile(gProjectFull) gProjectPath = p.dir gProjectName = p.name else: diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 8499ebd985..aaa2486e5d 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -105,6 +105,13 @@ proc setupVM*(module: PSym; scriptName: string): PEvalContext = setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1)) cbconf setCommand: options.command = a.getString 0 + let arg = a.getString 1 + if arg.len > 0: + gProjectName = arg + try: + gProjectFull = canonicalizePath(gProjectPath / gProjectName) + except OSError: + gProjectFull = gProjectName cbconf getCommand: setResult(a, options.command) cbconf switch: diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 4841749a94..a93c65dd4f 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -56,7 +56,7 @@ proc getCommand*(): string = ## "c", "js", "build", "help". builtin -proc setCommand*(cmd: string) = +proc setCommand*(cmd: string; project="") = ## Sets the Nim command that should be continued with after this Nimscript ## has finished. builtin From 29dac5ed60807c80d64987a0e0651e02917965f3 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 29 Sep 2015 21:01:26 +0200 Subject: [PATCH 13/15] Nimscript: the compiler supports a directory wide config.nims file --- compiler/nim.nim | 3 +++ doc/nims.txt | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/nim.nim b/compiler/nim.nim index 64c4e2026f..1293ec922b 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -59,6 +59,9 @@ proc handleCmdLine() = runNimScript(scriptFile) # 'nim foo.nims' means to just run the NimScript file and do nothing more: if scriptFile == gProjectFull: return + elif fileExists(gProjectPath / "config.nims"): + # directory wide NimScript file + runNimScript(gProjectPath / "config.nims") # now process command line arguments again, because some options in the # command line can overwite the config file's settings extccomp.initVars() diff --git a/doc/nims.txt b/doc/nims.txt index 2b9df4a87f..7c76efe423 100644 --- a/doc/nims.txt +++ b/doc/nims.txt @@ -9,7 +9,8 @@ system. So instead of a ``myproject.nim.cfg`` configuration file, you can use a ``myproject.nims`` file that simply contains Nim code controlling the -compilation process. +compilation process. For a directory wide configuration, use ``config.nims`` +instead of ``nim.cfg``. The VM cannot deal with ``importc``, the FFI is not available, so there are not many stdlib modules that you can use with Nim's VM. However, at least the From 3af310eb555673a8ad870f0ec061016e2a5f6742 Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Tue, 29 Sep 2015 14:58:10 -0700 Subject: [PATCH 14/15] add basic travis config --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..ab2639347d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: c +os: linux +script: + - sh build.sh +after_script: + - ./koch tests From fbe7bf3c8b2ac6fe48a0cf1c6b2cd9d7ae51c9b6 Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Tue, 29 Sep 2015 15:14:54 -0700 Subject: [PATCH 15/15] no more build.sh, bootstrap release too --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ab2639347d..2509c0b97d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,10 @@ language: c os: linux script: - - sh build.sh + - git clone --depth 1 https://github.com/nim-lang/csources.git + - cd csources && sh build.sh + - ./bin/nim c koch + - ./koch boot + - ./koch boot -d:release after_script: - - ./koch tests + - ./koch test