nimscript.nim 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## To learn about scripting in Nim see `NimScript<nims.html>`_
  10. # Nim's configuration system now uses Nim for scripting. This module provides
  11. # a few things that are required for this to work.
  12. const
  13. buildOS* {.magic: "BuildOS".}: string = ""
  14. ## The OS this build is running on. Can be different from ``system.hostOS``
  15. ## for cross compilations.
  16. buildCPU* {.magic: "BuildCPU".}: string = ""
  17. ## The CPU this build is running on. Can be different from ``system.hostCPU``
  18. ## for cross compilations.
  19. template builtin = discard
  20. # We know the effects better than the compiler:
  21. {.push hint[XDeclaredButNotUsed]: off.}
  22. proc listDirs*(dir: string): seq[string] =
  23. ## Lists all the subdirectories (non-recursively) in the directory `dir`.
  24. builtin
  25. proc listFiles*(dir: string): seq[string] =
  26. ## Lists all the files (non-recursively) in the directory `dir`.
  27. builtin
  28. proc removeDir(dir: string) {.
  29. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
  30. proc removeFile(dir: string) {.
  31. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
  32. proc moveFile(src, dest: string) {.
  33. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
  34. proc moveDir(src, dest: string) {.
  35. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
  36. proc copyFile(src, dest: string) {.
  37. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
  38. proc copyDir(src, dest: string) {.
  39. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
  40. proc createDir(dir: string) {.tags: [WriteIOEffect], raises: [OSError].} =
  41. builtin
  42. proc getError: string = builtin
  43. proc setCurrentDir(dir: string) = builtin
  44. proc getCurrentDir*(): string =
  45. ## Retrieves the current working directory.
  46. builtin
  47. proc rawExec(cmd: string): int {.tags: [ExecIOEffect], raises: [OSError].} =
  48. builtin
  49. proc warningImpl(arg, orig: string) = discard
  50. proc hintImpl(arg, orig: string) = discard
  51. proc paramStr*(i: int): string =
  52. ## Retrieves the ``i``'th command line parameter.
  53. builtin
  54. proc paramCount*(): int =
  55. ## Retrieves the number of command line parameters.
  56. builtin
  57. proc switch*(key: string, val="") =
  58. ## Sets a Nim compiler command line switch, for
  59. ## example ``switch("checks", "on")``.
  60. builtin
  61. proc warning*(name: string; val: bool) =
  62. ## Disables or enables a specific warning.
  63. let v = if val: "on" else: "off"
  64. warningImpl(name & ":" & v, "warning:" & name & ":" & v)
  65. proc hint*(name: string; val: bool) =
  66. ## Disables or enables a specific hint.
  67. let v = if val: "on" else: "off"
  68. hintImpl(name & ":" & v, "hint:" & name & ":" & v)
  69. proc patchFile*(package, filename, replacement: string) =
  70. ## Overrides the location of a given file belonging to the
  71. ## passed package.
  72. ## If the ``replacement`` is not an absolute path, the path
  73. ## is interpreted to be local to the Nimscript file that contains
  74. ## the call to ``patchFile``, Nim's ``--path`` is not used at all
  75. ## to resolve the filename!
  76. ##
  77. ## Example:
  78. ##
  79. ## .. code-block:: nim
  80. ##
  81. ## patchFile("stdlib", "asyncdispatch", "patches/replacement")
  82. discard
  83. proc getCommand*(): string =
  84. ## Gets the Nim command that the compiler has been invoked with, for example
  85. ## "c", "js", "build", "help".
  86. builtin
  87. proc setCommand*(cmd: string; project="") =
  88. ## Sets the Nim command that should be continued with after this Nimscript
  89. ## has finished.
  90. builtin
  91. proc cmpIgnoreStyle(a, b: string): int = builtin
  92. proc cmpIgnoreCase(a, b: string): int = builtin
  93. proc cmpic*(a, b: string): int =
  94. ## Compares `a` and `b` ignoring case.
  95. cmpIgnoreCase(a, b)
  96. proc getEnv*(key: string; default = ""): string {.tags: [ReadIOEffect].} =
  97. ## Retrieves the environment variable of name ``key``.
  98. builtin
  99. proc existsEnv*(key: string): bool {.tags: [ReadIOEffect].} =
  100. ## Checks for the existence of an environment variable named ``key``.
  101. builtin
  102. proc putEnv*(key, val: string) {.tags: [WriteIOEffect].} =
  103. ## Sets the value of the environment variable named ``key`` to ``val``.
  104. builtin
  105. proc delEnv*(key: string) {.tags: [WriteIOEffect].} =
  106. ## Deletes the environment variable named ``key``.
  107. builtin
  108. proc fileExists*(filename: string): bool {.tags: [ReadIOEffect].} =
  109. ## Checks if the file exists.
  110. builtin
  111. proc dirExists*(dir: string): bool {.
  112. tags: [ReadIOEffect].} =
  113. ## Checks if the directory `dir` exists.
  114. builtin
  115. proc existsFile*(filename: string): bool =
  116. ## An alias for ``fileExists``.
  117. fileExists(filename)
  118. proc existsDir*(dir: string): bool =
  119. ## An alias for ``dirExists``.
  120. dirExists(dir)
  121. proc selfExe*(): string =
  122. ## Returns the currently running nim or nimble executable.
  123. # TODO: consider making this as deprecated alias of `getCurrentCompilerExe`
  124. builtin
  125. proc toExe*(filename: string): string =
  126. ## On Windows adds ".exe" to `filename`, else returns `filename` unmodified.
  127. (when defined(windows): filename & ".exe" else: filename)
  128. proc toDll*(filename: string): string =
  129. ## On Windows adds ".dll" to `filename`, on Posix produces "lib$filename.so".
  130. (when defined(windows): filename & ".dll" else: "lib" & filename & ".so")
  131. proc strip(s: string): string =
  132. var i = 0
  133. while s[i] in {' ', '\c', '\L'}: inc i
  134. result = s.substr(i)
  135. template `--`*(key, val: untyped) =
  136. ## A shortcut for ``switch(astToStr(key), astToStr(val))``.
  137. switch(astToStr(key), strip astToStr(val))
  138. template `--`*(key: untyped) =
  139. ## A shortcut for ``switch(astToStr(key)``.
  140. switch(astToStr(key), "")
  141. type
  142. ScriptMode* {.pure.} = enum ## Controls the behaviour of the script.
  143. Silent, ## Be silent.
  144. Verbose, ## Be verbose.
  145. Whatif ## Do not run commands, instead just echo what
  146. ## would have been done.
  147. var
  148. mode*: ScriptMode ## Set this to influence how mkDir, rmDir, rmFile etc.
  149. ## behave
  150. template checkError(exc: untyped): untyped =
  151. let err = getError()
  152. if err.len > 0: raise newException(exc, err)
  153. template checkOsError =
  154. checkError(OSError)
  155. template log(msg: string, body: untyped) =
  156. if mode in {ScriptMode.Verbose, ScriptMode.Whatif}:
  157. echo "[NimScript] ", msg
  158. if mode != ScriptMode.WhatIf:
  159. body
  160. proc rmDir*(dir: string) {.raises: [OSError].} =
  161. ## Removes the directory `dir`.
  162. log "rmDir: " & dir:
  163. removeDir dir
  164. checkOsError()
  165. proc rmFile*(file: string) {.raises: [OSError].} =
  166. ## Removes the `file`.
  167. log "rmFile: " & file:
  168. removeFile file
  169. checkOsError()
  170. proc mkDir*(dir: string) {.raises: [OSError].} =
  171. ## Creates the directory `dir` including all necessary subdirectories. If
  172. ## the directory already exists, no error is raised.
  173. log "mkDir: " & dir:
  174. createDir dir
  175. checkOsError()
  176. proc mvFile*(`from`, to: string) {.raises: [OSError].} =
  177. ## Moves the file `from` to `to`.
  178. log "mvFile: " & `from` & ", " & to:
  179. moveFile `from`, to
  180. checkOsError()
  181. proc mvDir*(`from`, to: string) {.raises: [OSError].} =
  182. ## Moves the dir `from` to `to`.
  183. log "mvDir: " & `from` & ", " & to:
  184. moveDir `from`, to
  185. checkOsError()
  186. proc cpFile*(`from`, to: string) {.raises: [OSError].} =
  187. ## Copies the file `from` to `to`.
  188. log "cpFile: " & `from` & ", " & to:
  189. copyFile `from`, to
  190. checkOsError()
  191. proc cpDir*(`from`, to: string) {.raises: [OSError].} =
  192. ## Copies the dir `from` to `to`.
  193. log "cpDir: " & `from` & ", " & to:
  194. copyDir `from`, to
  195. checkOsError()
  196. proc exec*(command: string) {.
  197. raises: [OSError], tags: [ExecIOEffect].} =
  198. ## Executes an external process. If the external process terminates with
  199. ## a non-zero exit code, an OSError exception is raised.
  200. ##
  201. ## **Note:** If you need a version of ``exec`` that returns the exit code
  202. ## and text output of the command, you can use `system.gorgeEx
  203. ## <system.html#gorgeEx,string,string,string>`_.
  204. log "exec: " & command:
  205. if rawExec(command) != 0:
  206. raise newException(OSError, "FAILED: " & command)
  207. checkOsError()
  208. proc exec*(command: string, input: string, cache = "") {.
  209. raises: [OSError], tags: [ExecIOEffect].} =
  210. ## Executes an external process. If the external process terminates with
  211. ## a non-zero exit code, an OSError exception is raised.
  212. log "exec: " & command:
  213. let (output, exitCode) = gorgeEx(command, input, cache)
  214. if exitCode != 0:
  215. raise newException(OSError, "FAILED: " & command)
  216. echo output
  217. proc selfExec*(command: string) {.
  218. raises: [OSError], tags: [ExecIOEffect].} =
  219. ## Executes an external command with the current nim/nimble executable.
  220. ## ``Command`` must not contain the "nim " part.
  221. let c = selfExe() & " " & command
  222. log "exec: " & c:
  223. if rawExec(c) != 0:
  224. raise newException(OSError, "FAILED: " & c)
  225. checkOsError()
  226. proc put*(key, value: string) =
  227. ## Sets a configuration 'key' like 'gcc.options.always' to its value.
  228. builtin
  229. proc get*(key: string): string =
  230. ## Retrieves a configuration 'key' like 'gcc.options.always'.
  231. builtin
  232. proc exists*(key: string): bool =
  233. ## Checks for the existence of a configuration 'key'
  234. ## like 'gcc.options.always'.
  235. builtin
  236. proc nimcacheDir*(): string =
  237. ## Retrieves the location of 'nimcache'.
  238. builtin
  239. proc projectName*(): string =
  240. ## Retrieves the name of the current project
  241. builtin
  242. proc projectDir*(): string =
  243. ## Retrieves the absolute directory of the current project
  244. builtin
  245. proc projectPath*(): string =
  246. ## Retrieves the absolute path of the current project
  247. builtin
  248. proc thisDir*(): string =
  249. ## Retrieves the directory of the current ``nims`` script file. Its path is
  250. ## obtained via ``currentSourcePath`` (although, currently,
  251. ## ``currentSourcePath`` resolves symlinks, unlike ``thisDir``).
  252. builtin
  253. proc cd*(dir: string) {.raises: [OSError].} =
  254. ## Changes the current directory.
  255. ##
  256. ## The change is permanent for the rest of the execution, since this is just
  257. ## a shortcut for `os.setCurrentDir() <os.html#setCurrentDir,string>`_ . Use
  258. ## the `withDir() <#withDir.t,string,untyped>`_ template if you want to
  259. ## perform a temporary change only.
  260. setCurrentDir(dir)
  261. checkOsError()
  262. proc findExe*(bin: string): string =
  263. ## Searches for bin in the current working directory and then in directories
  264. ## listed in the PATH environment variable. Returns "" if the exe cannot be
  265. ## found.
  266. builtin
  267. template withDir*(dir: string; body: untyped): untyped =
  268. ## Changes the current directory temporarily.
  269. ##
  270. ## If you need a permanent change, use the `cd() <#cd,string>`_ proc.
  271. ## Usage example:
  272. ##
  273. ## .. code-block:: nim
  274. ## withDir "foo":
  275. ## # inside foo
  276. ## #back to last dir
  277. var curDir = getCurrentDir()
  278. try:
  279. cd(dir)
  280. body
  281. finally:
  282. cd(curDir)
  283. proc writeTask(name, desc: string) =
  284. if desc.len > 0:
  285. var spaces = " "
  286. for i in 0 ..< 20 - name.len: spaces.add ' '
  287. echo name, spaces, desc
  288. proc cppDefine*(define: string) =
  289. ## tell Nim that ``define`` is a C preprocessor ``#define`` and so always
  290. ## needs to be mangled.
  291. builtin
  292. proc stdinReadLine(): TaintedString {.
  293. tags: [ReadIOEffect], raises: [IOError].} =
  294. builtin
  295. proc stdinReadAll(): TaintedString {.
  296. tags: [ReadIOEffect], raises: [IOError].} =
  297. builtin
  298. proc readLineFromStdin*(): TaintedString {.raises: [IOError].} =
  299. ## Reads a line of data from stdin - blocks until \n or EOF which happens when stdin is closed
  300. log "readLineFromStdin":
  301. result = stdinReadLine()
  302. checkError(EOFError)
  303. proc readAllFromStdin*(): TaintedString {.raises: [IOError].} =
  304. ## Reads all data from stdin - blocks until EOF which happens when stdin is closed
  305. log "readAllFromStdin":
  306. result = stdinReadAll()
  307. checkError(EOFError)
  308. when not defined(nimble):
  309. template `==?`(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
  310. template task*(name: untyped; description: string; body: untyped): untyped =
  311. ## Defines a task. Hidden tasks are supported via an empty description.
  312. ## Example:
  313. ##
  314. ## .. code-block:: nim
  315. ## task build, "default build is via the C backend":
  316. ## setCommand "c"
  317. proc `name Task`*() =
  318. setCommand "nop"
  319. body
  320. let cmd = getCommand()
  321. if cmd.len == 0 or cmd ==? "help":
  322. setCommand "help"
  323. writeTask(astToStr(name), description)
  324. elif cmd ==? astToStr(name):
  325. `name Task`()
  326. # nimble has its own implementation for these things.
  327. var
  328. packageName* = "" ## Nimble support: Set this to the package name. It
  329. ## is usually not required to do that, nims' filename is
  330. ## the default.
  331. version*: string ## Nimble support: The package's version.
  332. author*: string ## Nimble support: The package's author.
  333. description*: string ## Nimble support: The package's description.
  334. license*: string ## Nimble support: The package's license.
  335. srcDir*: string ## Nimble support: The package's source directory.
  336. binDir*: string ## Nimble support: The package's binary directory.
  337. backend*: string ## Nimble support: The package's backend.
  338. skipDirs*, skipFiles*, skipExt*, installDirs*, installFiles*,
  339. installExt*, bin*: seq[string] = @[] ## Nimble metadata.
  340. requiresData*: seq[string] = @[] ## Exposes the list of requirements for read
  341. ## and write accesses.
  342. proc requires*(deps: varargs[string]) =
  343. ## Nimble support: Call this to set the list of requirements of your Nimble
  344. ## package.
  345. for d in deps: requiresData.add(d)
  346. {.pop.}