Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix #1994 gorge, staticExec now raise Assert on exitCode !=0; simplified implementation #10345

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@
- two poorly documented and not used modules (`subexes`, `scgi`) were moved to
graveyard (they are available as Nimble packages)


- `gorge`, `staticExec` now raise AssertError if exitCode is not 0 instead of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assertions aren't catchable, I think we want something that is catchable to be raised.

Copy link
Member Author

@timotheecour timotheecour Jan 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but that's at compile time; user can always use the cleaner gorgeEx variant which returns exitCode and handle accordingly so there's 0 loss of functionality.

In any case, catching doesn't work:

static:
  try:
    let a = gorge("D20190116T211842")
  except OSError as e:
    echo "caught"

gives:

Error: cannot evaluate at compile time: currException

silently ignoring errors. See https://github.com/nim-lang/Nim/issues/1994#issuecomment-327904129
Use `gorgeEx` to get `tuple[output: string, exitCode: int]`

#### Breaking changes in the compiler

Expand Down
2 changes: 1 addition & 1 deletion compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ type
mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn,
mLow, mHigh, mSizeOf, mAlignOf, mOffsetOf, mTypeTrait,
mIs, mOf, mAddr, mType, mTypeOf,
mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic,
mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStatic,
mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
mUnaryLt, mInc, mDec, mOrd,
mNew, mNewFinalize, mNewSeq, mNewSeqOfCap,
Expand Down
2 changes: 1 addition & 1 deletion compiler/jsgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1953,7 +1953,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
of mOf: genOf(p, n, r)
of mReset: genReset(p, n)
of mEcho: genEcho(p, n, r)
of mNLen..mNError, mSlurp, mStaticExec:
of mNLen..mNError, mSlurp:
localError(p.config, n.info, errXMustBeCompileTime % n.sons[0].sym.name.s)
of mCopyStr:
binaryExpr(p, n, r, "", "($1.slice($2))")
Expand Down
12 changes: 0 additions & 12 deletions compiler/vm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1422,18 +1422,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
createStr regs[ra]
regs[ra].node.strVal = opSlurp(regs[rb].node.strVal, c.debug[pc],
c.module, c.config)
of opcGorge:
when defined(nimcore):
decodeBC(rkNode)
inc pc
let rd = c.code[pc].regA

createStr regs[ra]
regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
regs[rc].node.strVal, regs[rd].node.strVal,
c.debug[pc], c.config)[0]
else:
globalError(c.config, c.debug[pc], "VM is not built with 'gorge' support")
of opcNError, opcNWarning, opcNHint:
decodeB(rkNode)
let a = regs[ra].node
Expand Down
1 change: 0 additions & 1 deletion compiler/vmdef.nim
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ type
opcNctPut, opcNctLen, opcNctGet, opcNctHasNext, opcNctNext,

opcSlurp,
opcGorge,
opcParseExprToAst,
opcParseStmtToAst,
opcQueryErrorFlag,
Expand Down
3 changes: 2 additions & 1 deletion compiler/vmgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,8 @@ proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
c.freeTemp(tmp2)

proc genBinaryABCD(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
# was used by `of mStaticExec: genBinaryABCD(c, n, dest, opcGorge)`
# which was removed; keeping this in case of future use
let
tmp = c.genx(n.sons[1])
tmp2 = c.genx(n.sons[2])
Expand Down Expand Up @@ -1156,7 +1158,6 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
c.gABC(n, opcTypeTrait, dest, tmp)
c.freeTemp(tmp)
of mSlurp: genUnaryABC(c, n, dest, opcSlurp)
of mStaticExec: genBinaryABCD(c, n, dest, opcGorge)
of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag)
of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl)
of mGetImplTransf: genUnaryABC(c, n, dest, opcGetImplTransf)
Expand Down
74 changes: 42 additions & 32 deletions lib/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3849,38 +3849,6 @@ proc staticRead*(filename: string): string {.magic: "Slurp".}
##
## `slurp <#slurp>`_ is an alias for ``staticRead``.

proc gorge*(command: string, input = "", cache = ""): string {.
magic: "StaticExec".} = discard
## This is an alias for `staticExec <#staticExec>`_.

proc staticExec*(command: string, input = "", cache = ""): string {.
magic: "StaticExec".} = discard
## Executes an external process at compile-time.
## if `input` is not an empty string, it will be passed as a standard input
## to the executed program.
##
## .. code-block:: nim
## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
## "\nCompiled on " & staticExec("uname -v")
##
## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use
## this proc inside a pragma like `passC <nimc.html#passc-pragma>`_ or `passL
## <nimc.html#passl-pragma>`_.
##
## If ``cache`` is not empty, the results of ``staticExec`` are cached within
## the ``nimcache`` directory. Use ``--forceBuild`` to get rid of this caching
## behaviour then. ``command & input & cache`` (the concatenated string) is
## used to determine whether the entry in the cache is still valid. You can
## use versioning information for ``cache``:
##
## .. code-block:: nim
## const stateMachine = staticExec("dfaoptimizer", "input", "0.8.0")

proc gorgeEx*(command: string, input = "", cache = ""): tuple[output: string,
exitCode: int] =
## Same as `gorge` but also returns the precious exit code.
discard

proc `+=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.
magic: "Inc", noSideEffect.}
## Increments an ordinal
Expand Down Expand Up @@ -4005,6 +3973,48 @@ template doAssert*(cond: untyped, msg = "") =
const expr = astToStr(cond)
assertImpl(cond, msg, expr, true)


proc gorgeEx*(command: string, input = "", cache = ""):
tuple[output: string, exitCode: int] {.compileTime.} =
## Executes an external process at compile-time.
## if `input` is not an empty string, it will be passed as a standard input
## to the executed program.
##
## .. code-block:: nim
## const buildInfo = "Revision " & gorgeEx("git rev-parse HEAD") &
## "\nCompiled on " & gorgeEx("uname -v")
##
## If ``cache`` is not empty, the results of ``gorgeEx`` are cached within
## the ``nimcache`` directory. Use ``--forceBuild`` to get rid of this caching
## behaviour then. ``command & input & cache`` (the concatenated string) is
## used to determine whether the entry in the cache is still valid. You can
## use versioning information for ``cache``:
##
## .. code-block:: nim
## const (stateMachine, exitCode) = gorgeEx("dfaoptimizer", "input", "0.8.0")
##
runnableExamples:
import os, strutils
const ret = gorgeEx(getCurrentCompilerExe() & " --version")
doAssert ret.exitCode == 0
doAssert ret.output.startsWith("Nim Compiler Version")
discard

proc gorge*(command: string, input = "", cache = ""): string {.compileTime.} =
## Convenience wrapper around `gorgeEx` that returns the output after
## checking `exitCode`.
## Note that you can use this proc inside a pragma like
## `passC <nimc.html#passc-pragma>`_ or
## `passL <nimc.html#passl-pragma>`_,
## eg: `{.passL: gorge("pkg-config --libs sdl").}`
let ret = gorgeEx(command, input, cache)
doAssert ret.exitCode == 0, "gorgeEx failed: cmd: `" & command & "` input: `" & input & "` cache: `" & cache & "`"
result = ret.output

proc staticExec*(command: string, input = "", cache = ""): string {.compileTime.} =
## alias for `gorge`
result = gorge(command, input, cache)

iterator items*[T](a: seq[T]): T {.inline.} =
## iterates over each item of `a`.
var i = 0
Expand Down
10 changes: 9 additions & 1 deletion tests/vm/tvmops.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import os
import math
import strutils

const unexistant = "D20190116T211842"
timotheecour marked this conversation as resolved.
Show resolved Hide resolved

template forceConst(a: untyped): untyped =
## Force evaluation at CT, useful for example here:
## `callFoo(forceConst(getBar1()), getBar2())`
Expand Down Expand Up @@ -44,4 +46,10 @@ block:
# Check against bugs like #9176
doAssert getCurrentCompilerExe() == forceConst(getCurrentCompilerExe())
if false: #pending #9176
doAssert gorgeEx("unexistant") == forceConst(gorgeEx("unexistant"))
doAssert gorgeEx(unexistant) == forceConst(gorgeEx(unexistant))
echo gorge("pwd")

block: # issue #1994
static:
doAssertRaises(AssertionError):
let a = gorge(unexistant)