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
parent e4a76c6ebf
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
## and process communication.
##
## **See also:**
## * `os module <os.html>`_
## * `streams module <streams.html>`_
## * `memfiles module <memfiles.html>`_
include "system/inclrtl"
@@ -26,22 +31,23 @@ 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 trusted 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.
poDemon ## Windows: The program creates no Window.
## Unix: Start the program as a demon. This is still
## work in progress!
ProcessOption* = enum ## Options that can be passed to `startProcess proc
## <#startProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_.
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 trusted 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.
poDaemon ## Windows: The program creates no Window.
## Unix: Start the program as a daemon. This is still
## work in progress!
ProcessObj = object of RootObj
when defined(windows):
@@ -57,7 +63,12 @@ type
exitFlag: bool
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,
@@ -72,23 +83,41 @@ proc execProcess*(command: string,
RootEffect].}
## A convenience procedure that executes ``command`` with ``startProcess``
## 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.
##
## .. 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")
## # Note: outp may have an interleave of text from the nim compile
## # and any output from mytestfile when it runs
proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [ExecIOEffect,
ReadIOEffect, RootEffect].}
## Executes ``command`` and returns its error code. Standard input, output,
## error streams are inherited from the calling process. This operation
## is also often called `system`:idx:.
## Executes ``command`` and returns its error code.
##
## 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
##
## let errC = execCmd("nim c -r mytestfile.nim")
proc startProcess*(command: string,
@@ -100,14 +129,16 @@ proc startProcess*(command: string,
RootEffect].}
## Starts a process. `Command` is the executable file, `workingDir` is the
## 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
## 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.
## 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
## to `startProcess`. See the documentation of ``ProcessOption`` for the
## meaning of these flags. You need to `close` the process when done.
## to `startProcess`. See the documentation of `ProcessOption<#ProcessOption>`_
## 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
## ``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,
## but ``OSError`` is raised in case of an error.
proc startCmd*(command: string, options: set[ProcessOption] = {
poStdErrToStdOut, poUsePath}): Process {.
tags: [ExecIOEffect, ReadEnvEffect, RootEffect], deprecated.} =
## Deprecated - use `startProcess` directly.
result = startProcess(command=command, options=options + {poEvalCommand})
##
## See also:
## * `execProcesses proc <#execProcesses,openArray[string],proc(int),proc(int,Process)>`_
## * `execProcess proc
## <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
## * `execCmd proc <#execCmd,string>`_
proc close*(p: Process) {.rtl, extern: "nosp$1", tags: [WriteIOEffect].}
## 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
## `pty leaks <http://stackoverflow.com/questions/27021641/how-to-fix-request-failed-on-channel-0>`_.
proc suspend*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
## 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: [].}
## 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: [].}
## Stop the process `p`. On Posix OSes the procedure sends ``SIGTERM``
## to the process. On Windows the Win32 API function ``TerminateProcess()``
## Stop the process `p`.
##
## On Posix OSes the procedure sends ``SIGTERM`` to the process.
## On Windows the Win32 API function ``TerminateProcess()``
## 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: [].}
## Kill the process `p`. On Posix OSes the procedure sends ``SIGKILL`` to
## the process. On Windows ``kill()`` is simply an alias for ``terminate()``.
## Kill the process `p`.
##
## 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: [].}
## Returns true iff the process `p` is still running. Returns immediately.
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
proc waitForExit*(p: Process, timeout: int = -1): int {.rtl,
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
## poParentStreams because they may fill output buffers, causing deadlock.
## **WARNING**: Be careful when using `waitForExit` for processes created without
## `poParentStreams` because they may fill output buffers, causing deadlock.
##
## On posix, if the process has exited because of a signal, 128 + signal
## number will be returned.
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
## number will be returned.
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``.
##
## See also:
## * `outputStream proc <#outputStream,Process>`_
## * `errorStream proc <#errorStream,Process>`_
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``.
##
## See also:
## * `inputStream proc <#inputStream,Process>`_
## * `errorStream proc <#errorStream,Process>`_
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``.
##
## See also:
## * `inputStream proc <#inputStream,Process>`_
## * `outputStream proc <#outputStream,Process>`_
proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
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``.
##
## See also:
## * `outputHandle proc <#outputHandle,Process>`_
## * `errorHandle proc <#errorHandle,Process>`_
result = p.inHandle
proc outputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
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``.
##
## See also:
## * `inputHandle proc <#inputHandle,Process>`_
## * `errorHandle proc <#errorHandle,Process>`_
result = p.outHandle
proc errorHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
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``.
##
## See also:
## * `inputHandle proc <#inputHandle,Process>`_
## * `outputHandle proc <#outputHandle,Process>`_
result = p.errHandle
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.
result = cpuinfo.countProcessors()
@@ -225,9 +308,11 @@ proc execProcesses*(cmds: openArray[string],
afterRunEvent: proc(idx: int, p: Process) = nil): int
{.rtl, extern: "nosp$1",
tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect].} =
## executes the commands `cmds` in parallel. Creates `n` processes
## that execute in parallel. The highest (absolute) return value of all processes
## is returned. Runs `beforeRunEvent` before running each command.
## Executes the commands `cmds` in parallel.
## Creates `n` processes that execute in parallel.
##
## The highest (absolute) return value of all processes is returned.
## Runs `beforeRunEvent` before running each command.
assert n > 0
if n > 1:
@@ -546,7 +631,7 @@ when defined(Windows) and not defined(useNimRtl):
else: newWideCString(e.str, e.len)
var wwd = newWideCString(wd)
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,
ee, wwd, si, procInfo)
else:
@@ -851,12 +936,12 @@ elif not defined(useNimRtl):
var mask: Sigset
chck sigemptyset(mask)
chck posix_spawnattr_setsigmask(attr, mask)
if poDemon in data.options:
if poDaemon in data.options:
chck posix_spawnattr_setpgroup(attr, 0'i32)
var flags = POSIX_SPAWN_USEVFORK or
POSIX_SPAWN_SETSIGMASK
if poDemon in data.options:
if poDaemon in data.options:
flags = flags or POSIX_SPAWN_SETPGROUP
chck posix_spawnattr_setflags(attr, flags)
@@ -1294,12 +1379,21 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = {
output: TaintedString,
exitCode: int] {.tags:
[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.
##
## .. 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")
var p = startProcess(command, options=options + {poEvalCommand})
var outp = outputStream(p)
@@ -1318,4 +1412,3 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = {
result[1] = peekExitCode(p)
if result[1] != -1: break
close(p)

View File

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

View File

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

View File

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

View File

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