better docs: osproc (#10708)

* better docs: osproc
* fix a typo in the docs, deprecate "demon"
This commit is contained in:
Miran 2019-02-19 17:25:03 +01:00 • committed by Andreas Rumpf
commit 50d1a46537
5 changed files with 164 additions and 71 deletions

View file

@ -9,6 +9,11 @@
## This module implements an advanced facility for executing OS processes ## This module implements an advanced facility for executing OS processes
## and process communication. ## and process communication.
##
## **See also:**
## * `os module <os.html>`_
## * `streams module <streams.html>`_
## * `memfiles module <memfiles.html>`_
include "system/inclrtl" include "system/inclrtl"
@ -26,22 +31,23 @@ when defined(linux):
import linux import linux
type type
ProcessOption* = enum ## options that can be passed `startProcess` ProcessOption* = enum ## Options that can be passed to `startProcess proc
poEchoCmd, ## echo the command before execution ## <#startProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_.
poUsePath, ## Asks system to search for executable using PATH environment poEchoCmd, ## Echo the command before execution.
## variable. poUsePath, ## Asks system to search for executable using PATH environment
## On Windows, this is the default. ## variable.
poEvalCommand, ## Pass `command` directly to the shell, without quoting. ## On Windows, this is the default.
## Use it only if `command` comes from trusted source. poEvalCommand, ## Pass `command` directly to the shell, without quoting.
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream ## Use it only if `command` comes from trusted source.
poParentStreams, ## use the parent's streams poStdErrToStdOut, ## Merge stdout and stderr to the stdout stream.
poInteractive, ## optimize the buffer handling for responsiveness for poParentStreams, ## Use the parent's streams.
## UI applications. Currently this only affects poInteractive, ## Optimize the buffer handling for responsiveness for
## Windows: Named pipes are used so that you can peek ## UI applications. Currently this only affects
## at the process' output streams. ## Windows: Named pipes are used so that you can peek
poDemon ## Windows: The program creates no Window. ## at the process' output streams.
## Unix: Start the program as a demon. This is still poDaemon ## Windows: The program creates no Window.
## work in progress! ## Unix: Start the program as a daemon. This is still
## work in progress!
ProcessObj = object of RootObj ProcessObj = object of RootObj
when defined(windows): when defined(windows):
@ -57,7 +63,12 @@ type
exitFlag: bool exitFlag: bool
options: set[ProcessOption] options: set[ProcessOption]
Process* = ref ProcessObj ## represents an operating system process Process* = ref ProcessObj ## Represents an operating system process.
const poDemon* {.deprecated.} = poDaemon ## Nim versions before 0.20
## used the wrong spelling ("demon").
## Now `ProcessOption` uses the correct spelling ("daemon"),
## and this is needed just for backward compatibility.
proc execProcess*(command: string, proc execProcess*(command: string,
@ -72,23 +83,41 @@ proc execProcess*(command: string,
RootEffect].} RootEffect].}
## A convenience procedure that executes ``command`` with ``startProcess`` ## A convenience procedure that executes ``command`` with ``startProcess``
## and returns its output as a string. ## and returns its output as a string.
## WARNING: this function uses poEvalCommand by default for backward compatibility. ##
## **WARNING:** This function uses `poEvalCommand` by default for backward
## compatibility.
## Make sure to pass options explicitly. ## Make sure to pass options explicitly.
## ##
## .. code-block:: Nim ## See also:
## * `startProcess proc
## <#startProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
## * `execProcesses proc <#execProcesses,openArray[string],proc(int),proc(int,Process)>`_
## * `execCmd proc <#execCmd,string>`_
## ##
## Example:
##
## .. code-block:: Nim
## let outp = execProcess("nim c -r mytestfile.nim") ## let outp = execProcess("nim c -r mytestfile.nim")
## # Note: outp may have an interleave of text from the nim compile ## # Note: outp may have an interleave of text from the nim compile
## # and any output from mytestfile when it runs ## # and any output from mytestfile when it runs
proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [ExecIOEffect, proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [ExecIOEffect,
ReadIOEffect, RootEffect].} ReadIOEffect, RootEffect].}
## Executes ``command`` and returns its error code. Standard input, output, ## Executes ``command`` and returns its error code.
## error streams are inherited from the calling process. This operation ##
## is also often called `system`:idx:. ## Standard input, output, error streams are inherited from the calling process.
## This operation is also often called `system`:idx:.
##
## See also:
## * `execCmdEx proc <#execCmdEx,string,set[ProcessOption]>`_
## * `startProcess proc
## <#startProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
## * `execProcess proc
## <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
##
## Example:
## ##
## .. code-block:: Nim ## .. code-block:: Nim
##
## let errC = execCmd("nim c -r mytestfile.nim") ## let errC = execCmd("nim c -r mytestfile.nim")
proc startProcess*(command: string, proc startProcess*(command: string,
@ -100,14 +129,16 @@ proc startProcess*(command: string,
RootEffect].} RootEffect].}
## Starts a process. `Command` is the executable file, `workingDir` is the ## Starts a process. `Command` is the executable file, `workingDir` is the
## process's working directory. If ``workingDir == ""`` the current directory ## process's working directory. If ``workingDir == ""`` the current directory
## is used. `args` are the command line arguments that are passed to the ## is used (default). `args` are the command line arguments that are passed to the
## process. On many operating systems, the first command line argument is the ## process. On many operating systems, the first command line argument is the
## name of the executable. `args` should not contain this argument! ## name of the executable. `args` should *not* contain this argument!
## `env` is the environment that will be passed to the process. ## `env` is the environment that will be passed to the process.
## If ``env == nil`` the environment is inherited of ## If ``env == nil`` (default) the environment is inherited of
## the parent process. `options` are additional flags that may be passed ## the parent process. `options` are additional flags that may be passed
## to `startProcess`. See the documentation of ``ProcessOption`` for the ## to `startProcess`. See the documentation of `ProcessOption<#ProcessOption>`_
## meaning of these flags. You need to `close` the process when done. ## for the meaning of these flags.
##
## You need to `close <#close,Process>`_ the process when done.
## ##
## Note that you can't pass any `args` if you use the option ## Note that you can't pass any `args` if you use the option
## ``poEvalCommand``, which invokes the system shell to run the specified ## ``poEvalCommand``, which invokes the system shell to run the specified
@ -119,102 +150,154 @@ proc startProcess*(command: string,
## ##
## Return value: The newly created process object. Nil is never returned, ## Return value: The newly created process object. Nil is never returned,
## but ``OSError`` is raised in case of an error. ## but ``OSError`` is raised in case of an error.
##
proc startCmd*(command: string, options: set[ProcessOption] = { ## See also:
poStdErrToStdOut, poUsePath}): Process {. ## * `execProcesses proc <#execProcesses,openArray[string],proc(int),proc(int,Process)>`_
tags: [ExecIOEffect, ReadEnvEffect, RootEffect], deprecated.} = ## * `execProcess proc
## Deprecated - use `startProcess` directly. ## <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
result = startProcess(command=command, options=options + {poEvalCommand}) ## * `execCmd proc <#execCmd,string>`_
proc close*(p: Process) {.rtl, extern: "nosp$1", tags: [WriteIOEffect].} proc close*(p: Process) {.rtl, extern: "nosp$1", tags: [WriteIOEffect].}
## When the process has finished executing, cleanup related handles. ## When the process has finished executing, cleanup related handles.
## ##
## **Warning:** If the process has not finished executing, this will forcibly ## **WARNING:** If the process has not finished executing, this will forcibly
## terminate the process. Doing so may result in zombie processes and ## terminate the process. Doing so may result in zombie processes and
## `pty leaks <http://stackoverflow.com/questions/27021641/how-to-fix-request-failed-on-channel-0>`_. ## `pty leaks <http://stackoverflow.com/questions/27021641/how-to-fix-request-failed-on-channel-0>`_.
proc suspend*(p: Process) {.rtl, extern: "nosp$1", tags: [].} proc suspend*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
## Suspends the process `p`. ## Suspends the process `p`.
##
## See also:
## * `resume proc <#resume,Process>`_
## * `terminate proc <#terminate,Process>`_
## * `kill proc <#kill,Process>`_
proc resume*(p: Process) {.rtl, extern: "nosp$1", tags: [].} proc resume*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
## Resumes the process `p`. ## Resumes the process `p`.
##
## See also:
## * `suspend proc <#suspend,Process>`_
## * `terminate proc <#terminate,Process>`_
## * `kill proc <#kill,Process>`_
proc terminate*(p: Process) {.rtl, extern: "nosp$1", tags: [].} proc terminate*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
## Stop the process `p`. On Posix OSes the procedure sends ``SIGTERM`` ## Stop the process `p`.
## to the process. On Windows the Win32 API function ``TerminateProcess()`` ##
## On Posix OSes the procedure sends ``SIGTERM`` to the process.
## On Windows the Win32 API function ``TerminateProcess()``
## is called to stop the process. ## is called to stop the process.
##
## See also:
## * `suspend proc <#suspend,Process>`_
## * `resume proc <#resume,Process>`_
## * `kill proc <#kill,Process>`_
proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].} proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
## Kill the process `p`. On Posix OSes the procedure sends ``SIGKILL`` to ## Kill the process `p`.
## the process. On Windows ``kill()`` is simply an alias for ``terminate()``. ##
## On Posix OSes the procedure sends ``SIGKILL`` to the process.
## On Windows ``kill`` is simply an alias for `terminate() <#terminate,Process>`_.
##
## See also:
## * `suspend proc <#suspend,Process>`_
## * `resume proc <#resume,Process>`_
## * `terminate proc <#terminate,Process>`_
proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].} proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].}
## Returns true iff the process `p` is still running. Returns immediately. ## Returns true iff the process `p` is still running. Returns immediately.
proc processID*(p: Process): int {.rtl, extern: "nosp$1".} = proc processID*(p: Process): int {.rtl, extern: "nosp$1".} =
## returns `p`'s process ID. See also ``os.getCurrentProcessId()``. ## Returns `p`'s process ID.
##
## See also:
## * `os.getCurrentProcessId proc <os.html#getCurrentProcessId>`_
return p.id return p.id
proc waitForExit*(p: Process, timeout: int = -1): int {.rtl, proc waitForExit*(p: Process, timeout: int = -1): int {.rtl,
extern: "nosp$1", tags: [].} extern: "nosp$1", tags: [].}
## waits for the process to finish and returns `p`'s error code. ## Waits for the process to finish and returns `p`'s error code.
## ##
## **Warning**: Be careful when using waitForExit for processes created without ## **WARNING**: Be careful when using `waitForExit` for processes created without
## poParentStreams because they may fill output buffers, causing deadlock. ## `poParentStreams` because they may fill output buffers, causing deadlock.
## ##
## On posix, if the process has exited because of a signal, 128 + signal ## On posix, if the process has exited because of a signal, 128 + signal
## number will be returned. ## number will be returned.
proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", tags: [].} proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", tags: [].}
## return -1 if the process is still running. Otherwise the process' exit code ## Return `-1` if the process is still running. Otherwise the process' exit code.
## ##
## On posix, if the process has exited because of a signal, 128 + signal ## On posix, if the process has exited because of a signal, 128 + signal
## number will be returned. ## number will be returned.
proc inputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} proc inputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
## returns ``p``'s input stream for writing to. ## Returns ``p``'s input stream for writing to.
## ##
## **Warning**: The returned `Stream` should not be closed manually as it ## **WARNING**: The returned `Stream` should not be closed manually as it
## is closed when closing the Process ``p``. ## is closed when closing the Process ``p``.
##
## See also:
## * `outputStream proc <#outputStream,Process>`_
## * `errorStream proc <#errorStream,Process>`_
proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
## returns ``p``'s output stream for reading from. ## Returns ``p``'s output stream for reading from.
## ##
## **Warning**: The returned `Stream` should not be closed manually as it ## **WARNING**: The returned `Stream` should not be closed manually as it
## is closed when closing the Process ``p``. ## is closed when closing the Process ``p``.
##
## See also:
## * `inputStream proc <#inputStream,Process>`_
## * `errorStream proc <#errorStream,Process>`_
proc errorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} proc errorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
## returns ``p``'s error stream for reading from. ## Returns ``p``'s error stream for reading from.
## ##
## **Warning**: The returned `Stream` should not be closed manually as it ## **WARNING**: The returned `Stream` should not be closed manually as it
## is closed when closing the Process ``p``. ## is closed when closing the Process ``p``.
##
## See also:
## * `inputStream proc <#inputStream,Process>`_
## * `outputStream proc <#outputStream,Process>`_
proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
tags: [].} = tags: [].} =
## returns ``p``'s input file handle for writing to. ## Returns ``p``'s input file handle for writing to.
## ##
## **Warning**: The returned `FileHandle` should not be closed manually as ## **WARNING**: The returned `FileHandle` should not be closed manually as
## it is closed when closing the Process ``p``. ## it is closed when closing the Process ``p``.
##
## See also:
## * `outputHandle proc <#outputHandle,Process>`_
## * `errorHandle proc <#errorHandle,Process>`_
result = p.inHandle result = p.inHandle
proc outputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", proc outputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
tags: [].} = tags: [].} =
## returns ``p``'s output file handle for reading from. ## Returns ``p``'s output file handle for reading from.
## ##
## **Warning**: The returned `FileHandle` should not be closed manually as ## **WARNING**: The returned `FileHandle` should not be closed manually as
## it is closed when closing the Process ``p``. ## it is closed when closing the Process ``p``.
##
## See also:
## * `inputHandle proc <#inputHandle,Process>`_
## * `errorHandle proc <#errorHandle,Process>`_
result = p.outHandle result = p.outHandle
proc errorHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", proc errorHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
tags: [].} = tags: [].} =
## returns ``p``'s error file handle for reading from. ## Returns ``p``'s error file handle for reading from.
## ##
## **Warning**: The returned `FileHandle` should not be closed manually as ## **WARNING**: The returned `FileHandle` should not be closed manually as
## it is closed when closing the Process ``p``. ## it is closed when closing the Process ``p``.
##
## See also:
## * `inputHandle proc <#inputHandle,Process>`_
## * `outputHandle proc <#outputHandle,Process>`_
result = p.errHandle result = p.errHandle
proc countProcessors*(): int {.rtl, extern: "nosp$1".} = proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
## returns the numer of the processors/cores the machine has. ## Returns the number of the processors/cores the machine has.
## Returns 0 if it cannot be detected. ## Returns 0 if it cannot be detected.
result = cpuinfo.countProcessors() result = cpuinfo.countProcessors()
@ -225,9 +308,11 @@ proc execProcesses*(cmds: openArray[string],
afterRunEvent: proc(idx: int, p: Process) = nil): int afterRunEvent: proc(idx: int, p: Process) = nil): int
{.rtl, extern: "nosp$1", {.rtl, extern: "nosp$1",
tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect].} = tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect].} =
## executes the commands `cmds` in parallel. Creates `n` processes ## Executes the commands `cmds` in parallel.
## that execute in parallel. The highest (absolute) return value of all processes ## Creates `n` processes that execute in parallel.
## is returned. Runs `beforeRunEvent` before running each command. ##
## The highest (absolute) return value of all processes is returned.
## Runs `beforeRunEvent` before running each command.
assert n > 0 assert n > 0
if n > 1: if n > 1:
@ -546,7 +631,7 @@ when defined(Windows) and not defined(useNimRtl):
else: newWideCString(e.str, e.len) else: newWideCString(e.str, e.len)
var wwd = newWideCString(wd) var wwd = newWideCString(wd)
var flags = NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT var flags = NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT
if poDemon in options: flags = flags or CREATE_NO_WINDOW if poDaemon in options: flags = flags or CREATE_NO_WINDOW
success = winlean.createProcessW(nil, tmp, nil, nil, 1, flags, success = winlean.createProcessW(nil, tmp, nil, nil, 1, flags,
ee, wwd, si, procInfo) ee, wwd, si, procInfo)
else: else:
@ -851,12 +936,12 @@ elif not defined(useNimRtl):
var mask: Sigset var mask: Sigset
chck sigemptyset(mask) chck sigemptyset(mask)
chck posix_spawnattr_setsigmask(attr, mask) chck posix_spawnattr_setsigmask(attr, mask)
if poDemon in data.options: if poDaemon in data.options:
chck posix_spawnattr_setpgroup(attr, 0'i32) chck posix_spawnattr_setpgroup(attr, 0'i32)
var flags = POSIX_SPAWN_USEVFORK or var flags = POSIX_SPAWN_USEVFORK or
POSIX_SPAWN_SETSIGMASK POSIX_SPAWN_SETSIGMASK
if poDemon in data.options: if poDaemon in data.options:
flags = flags or POSIX_SPAWN_SETPGROUP flags = flags or POSIX_SPAWN_SETPGROUP
chck posix_spawnattr_setflags(attr, flags) chck posix_spawnattr_setflags(attr, flags)
@ -1294,12 +1379,21 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = {
output: TaintedString, output: TaintedString,
exitCode: int] {.tags: exitCode: int] {.tags:
[ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} = [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} =
## a convenience proc that runs the `command`, grabs all its output and ## A convenience proc that runs the `command`, grabs all its output and
## exit code and returns both. ## exit code and returns both.
## ##
## .. code-block:: Nim ## See also:
## * `execCmd proc <#execCmd,string>`_
## * `startProcess proc
## <#startProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
## * `execProcess proc
## <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
## ##
## Example:
##
## .. code-block:: Nim
## let (outp, errC) = execCmdEx("nim c -r mytestfile.nim") ## let (outp, errC) = execCmdEx("nim c -r mytestfile.nim")
var p = startProcess(command, options=options + {poEvalCommand}) var p = startProcess(command, options=options + {poEvalCommand})
var outp = outputStream(p) var outp = outputStream(p)
@ -1318,4 +1412,3 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = {
result[1] = peekExitCode(p) result[1] = peekExitCode(p)
if result[1] != -1: break if result[1] != -1: break
close(p) close(p)

View file

@ -20,7 +20,7 @@ proc callNimsuggest() =
let cl = parseCmdLine("nimsuggest --tester temp000.nim") let cl = parseCmdLine("nimsuggest --tester temp000.nim")
var p = startProcess(command=cl[0], args=cl[1 .. ^1], var p = startProcess(command=cl[0], args=cl[1 .. ^1],
options={poStdErrToStdOut, poUsePath, options={poStdErrToStdOut, poUsePath,
poInteractive, poDemon}) poInteractive, poDaemon})
let outp = p.outputStream let outp = p.outputStream
let inp = p.inputStream let inp = p.inputStream
var report = "" var report = ""

View file

@ -251,7 +251,7 @@ proc runEpcTest(filename: string): int =
let cl = parseCmdLine(epccmd) let cl = parseCmdLine(epccmd)
var p = startProcess(command=cl[0], args=cl[1 .. ^1], var p = startProcess(command=cl[0], args=cl[1 .. ^1],
options={poStdErrToStdOut, poUsePath, options={poStdErrToStdOut, poUsePath,
poInteractive, poDemon}) poInteractive, poDaemon})
let outp = p.outputStream let outp = p.outputStream
let inp = p.inputStream let inp = p.inputStream
var report = "" var report = ""
@ -293,7 +293,7 @@ proc runTest(filename: string): int =
let cl = parseCmdLine(s.cmd) let cl = parseCmdLine(s.cmd)
var p = startProcess(command=cl[0], args=cl[1 .. ^1], var p = startProcess(command=cl[0], args=cl[1 .. ^1],
options={poStdErrToStdOut, poUsePath, options={poStdErrToStdOut, poUsePath,
poInteractive, poDemon}) poInteractive, poDaemon})
let outp = p.outputStream let outp = p.outputStream
let inp = p.inputStream let inp = p.inputStream
var report = "" var report = ""

View file

@ -98,7 +98,7 @@ when defined(upcoming):
var process = startProcess("ping.exe", "", var process = startProcess("ping.exe", "",
["127.0.0.1", "-n", "2", "-w", "100"], nil, ["127.0.0.1", "-n", "2", "-w", "100"], nil,
{poStdErrToStdOut, poUsePath, poInteractive, {poStdErrToStdOut, poUsePath, poInteractive,
poDemon}) poDaemon})
else: else:
var process = startProcess("sleep", "", ["1"], nil, var process = startProcess("sleep", "", ["1"], nil,
{poStdErrToStdOut, poUsePath}) {poStdErrToStdOut, poUsePath})

View file

@ -80,7 +80,7 @@ proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type
# Execute vcvarsall with its command-line arguments # Execute vcvarsall with its command-line arguments
# and then execute the SET command to list all environment variables # and then execute the SET command to list all environment variables
let comSpecExec = "\"$1\" /C \"$2 && SET\"" % [comSpecCmd, vcvarsExec] let comSpecExec = "\"$1\" /C \"$2 && SET\"" % [comSpecCmd, vcvarsExec]
var comSpecOpts = {poEvalCommand, poDemon, poStdErrToStdOut} var comSpecOpts = {poEvalCommand, poDaemon, poStdErrToStdOut}
if verbose: if verbose:
comSpecOpts.incl poEchoCmd comSpecOpts.incl poEchoCmd
let comSpecOut = execProcess(comSpecExec, options = comSpecOpts) let comSpecOut = execProcess(comSpecExec, options = comSpecOpts)