nimscript.nim 12 KB

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