nimscript.nim 15 KB

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