commands.nim 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. #
  2. #
  3. # The Nim Compiler
  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. # This module handles the parsing of command line arguments.
  10. # We do this here before the 'import' statement so 'defined' does not get
  11. # confused with 'TGCMode.gcGenerational' etc.
  12. template bootSwitch(name, expr, userString) =
  13. # Helper to build boot constants, for debugging you can 'echo' the else part.
  14. const name = if expr: " " & userString else: ""
  15. bootSwitch(usedRelease, defined(release), "-d:release")
  16. bootSwitch(usedGnuReadline, defined(useLinenoise), "-d:useLinenoise")
  17. bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm")
  18. bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
  19. bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational")
  20. bootSwitch(usedGoGC, defined(gogc), "--gc:go")
  21. bootSwitch(usedNoGC, defined(nogc), "--gc:none")
  22. import
  23. os, msgs, options, nversion, condsyms, strutils, extccomp, platform,
  24. wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, lineinfos,
  25. pathutils, strtabs
  26. # but some have deps to imported modules. Yay.
  27. bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
  28. bootSwitch(usedNativeStacktrace,
  29. defined(nativeStackTrace) and nativeStackTraceSupported,
  30. "-d:nativeStackTrace")
  31. bootSwitch(usedFFI, hasFFI, "-d:nimHasLibFFI")
  32. type
  33. TCmdLinePass* = enum
  34. passCmd1, # first pass over the command line
  35. passCmd2, # second pass over the command line
  36. passPP # preprocessor called processCommand()
  37. const
  38. HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" &
  39. "Compiled at $4\n" &
  40. "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
  41. const
  42. Usage = slurp"../doc/basicopt.txt".replace(" //", " ")
  43. FeatureDesc = block:
  44. var x = ""
  45. for f in low(Feature)..high(Feature):
  46. if x.len > 0: x.add "|"
  47. x.add $f
  48. x
  49. AdvancedUsage = slurp"../doc/advopt.txt".replace(" //", " ") % FeatureDesc
  50. proc getCommandLineDesc(conf: ConfigRef): string =
  51. result = (HelpMessage % [VersionAsString, platform.OS[conf.target.hostOS].name,
  52. CPU[conf.target.hostCPU].name, CompileDate]) &
  53. Usage
  54. proc helpOnError(conf: ConfigRef; pass: TCmdLinePass) =
  55. if pass == passCmd1:
  56. msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
  57. msgQuit(0)
  58. proc writeAdvancedUsage(conf: ConfigRef; pass: TCmdLinePass) =
  59. if pass == passCmd1:
  60. msgWriteln(conf, (HelpMessage % [VersionAsString,
  61. platform.OS[conf.target.hostOS].name,
  62. CPU[conf.target.hostCPU].name, CompileDate]) &
  63. AdvancedUsage,
  64. {msgStdout})
  65. msgQuit(0)
  66. proc writeFullhelp(conf: ConfigRef; pass: TCmdLinePass) =
  67. if pass == passCmd1:
  68. msgWriteln(conf, `%`(HelpMessage, [VersionAsString,
  69. platform.OS[conf.target.hostOS].name,
  70. CPU[conf.target.hostCPU].name, CompileDate]) &
  71. Usage & AdvancedUsage,
  72. {msgStdout})
  73. msgQuit(0)
  74. proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) =
  75. if pass == passCmd1:
  76. msgWriteln(conf, `%`(HelpMessage, [VersionAsString,
  77. platform.OS[conf.target.hostOS].name,
  78. CPU[conf.target.hostCPU].name, CompileDate]),
  79. {msgStdout})
  80. const gitHash = gorge("git log -n 1 --format=%H").strip
  81. when gitHash.len == 40:
  82. msgWriteln(conf, "git hash: " & gitHash, {msgStdout})
  83. msgWriteln(conf, "active boot switches:" & usedRelease &
  84. usedTinyC & usedGnuReadline & usedNativeStacktrace &
  85. usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC,
  86. {msgStdout})
  87. msgQuit(0)
  88. proc writeCommandLineUsage*(conf: ConfigRef) =
  89. msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
  90. proc addPrefix(switch: string): string =
  91. if len(switch) <= 1: result = "-" & switch
  92. else: result = "--" & switch
  93. const
  94. errInvalidCmdLineOption = "invalid command line option: '$1'"
  95. errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
  96. errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
  97. errOffHintsError = "'off', 'hint' or 'error' expected, but '$1' found"
  98. proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
  99. if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
  100. else: localError(conf, info, errInvalidCmdLineOption % addPrefix(switch))
  101. proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TCmdLinePass,
  102. info: TLineInfo) =
  103. cmd = ""
  104. var i = 0
  105. if i < len(switch) and switch[i] == '-': inc(i)
  106. if i < len(switch) and switch[i] == '-': inc(i)
  107. while i < len(switch):
  108. case switch[i]
  109. of 'a'..'z', 'A'..'Z', '0'..'9', '_', '.': add(cmd, switch[i])
  110. else: break
  111. inc(i)
  112. if i >= len(switch): arg = ""
  113. # cmd:arg => (cmd,arg)
  114. elif switch[i] in {':', '='}: arg = substr(switch, i + 1)
  115. # cmd[sub]:rest => (cmd,[sub]:rest)
  116. elif switch[i] == '[': arg = substr(switch, i)
  117. else: invalidCmdLineOption(conf, pass, switch, info)
  118. proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
  119. info: TLineInfo) =
  120. case arg.normalize
  121. of "","on": conf.options = conf.options + op
  122. of "off": conf.options = conf.options - op
  123. else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
  124. proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
  125. info: TLineInfo): bool =
  126. result = false
  127. case arg.normalize
  128. of "on": conf.options = conf.options + op
  129. of "off": conf.options = conf.options - op
  130. of "list": result = true
  131. else: localError(conf, info, errOnOffOrListExpectedButXFound % arg)
  132. proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass,
  133. info: TLineInfo) =
  134. case arg.normalize
  135. of "", "on": conf.globalOptions = conf.globalOptions + op
  136. of "off": conf.globalOptions = conf.globalOptions - op
  137. else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
  138. proc expectArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
  139. if arg == "":
  140. localError(conf, info, "argument for command line option expected: '$1'" % addPrefix(switch))
  141. proc expectNoArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
  142. if arg != "":
  143. localError(conf, info, "invalid argument for command line option: '$1'" % addPrefix(switch))
  144. proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
  145. info: TLineInfo; orig: string; conf: ConfigRef) =
  146. var id = "" # arg = key:val or [key]:val; with val=on|off
  147. var i = 0
  148. var n = hintMin
  149. var isBracket = false
  150. if i < len(arg) and arg[i] == '[':
  151. isBracket = true
  152. inc(i)
  153. while i < len(arg) and (arg[i] notin {':', '=', ']'}):
  154. add(id, arg[i])
  155. inc(i)
  156. if isBracket:
  157. if i < len(arg) and arg[i] == ']': inc(i)
  158. else: invalidCmdLineOption(conf, pass, orig, info)
  159. if i < len(arg) and (arg[i] in {':', '='}): inc(i)
  160. else: invalidCmdLineOption(conf, pass, orig, info)
  161. if state == wHint:
  162. let x = findStr(lineinfos.HintsToStr, id)
  163. if x >= 0: n = TNoteKind(x + ord(hintMin))
  164. else: localError(conf, info, "unknown hint: " & id)
  165. else:
  166. let x = findStr(lineinfos.WarningsToStr, id)
  167. if x >= 0: n = TNoteKind(x + ord(warnMin))
  168. else: localError(conf, info, "unknown warning: " & id)
  169. case substr(arg, i).normalize
  170. of "on":
  171. incl(conf.notes, n)
  172. incl(conf.mainPackageNotes, n)
  173. incl(conf.enableNotes, n)
  174. of "off":
  175. excl(conf.notes, n)
  176. excl(conf.mainPackageNotes, n)
  177. incl(conf.disableNotes, n)
  178. excl(conf.foreignPackageNotes, n)
  179. else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
  180. proc processCompile(conf: ConfigRef; filename: string) =
  181. var found = findFile(conf, filename)
  182. if found.isEmpty: found = AbsoluteFile filename
  183. extccomp.addExternalFileToCompile(conf, found)
  184. const
  185. errNoneBoehmRefcExpectedButXFound = "'none', 'boehm' or 'refc' expected, but '$1' found"
  186. errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
  187. errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
  188. proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
  189. case switch.normalize
  190. of "gc":
  191. case arg.normalize
  192. of "boehm": result = conf.selectedGC == gcBoehm
  193. of "refc": result = conf.selectedGC == gcRefc
  194. of "v2": result = false
  195. of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
  196. of "generational": result = false
  197. of "destructors": result = conf.selectedGC == gcDestructors
  198. of "go": result = conf.selectedGC == gcGo
  199. of "none": result = conf.selectedGC == gcNone
  200. of "stack", "regions": result = conf.selectedGC == gcRegions
  201. else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
  202. of "opt":
  203. case arg.normalize
  204. of "speed": result = contains(conf.options, optOptimizeSpeed)
  205. of "size": result = contains(conf.options, optOptimizeSize)
  206. of "none": result = conf.options * {optOptimizeSpeed, optOptimizeSize} == {}
  207. else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
  208. of "verbosity": result = $conf.verbosity == arg
  209. of "app":
  210. case arg.normalize
  211. of "gui": result = contains(conf.globalOptions, optGenGuiApp)
  212. of "console": result = not contains(conf.globalOptions, optGenGuiApp)
  213. of "lib": result = contains(conf.globalOptions, optGenDynLib) and
  214. not contains(conf.globalOptions, optGenGuiApp)
  215. of "staticlib": result = contains(conf.globalOptions, optGenStaticLib) and
  216. not contains(conf.globalOptions, optGenGuiApp)
  217. else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
  218. of "dynliboverride":
  219. result = isDynlibOverride(conf, arg)
  220. else: invalidCmdLineOption(conf, passCmd1, switch, info)
  221. proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool =
  222. case switch.normalize
  223. of "debuginfo": result = contains(conf.globalOptions, optCDebug)
  224. of "compileonly", "c": result = contains(conf.globalOptions, optCompileOnly)
  225. of "nolinking": result = contains(conf.globalOptions, optNoLinking)
  226. of "nomain": result = contains(conf.globalOptions, optNoMain)
  227. of "forcebuild", "f": result = contains(conf.globalOptions, optForceFullMake)
  228. of "warnings", "w": result = contains(conf.options, optWarns)
  229. of "hints": result = contains(conf.options, optHints)
  230. of "threadanalysis": result = contains(conf.globalOptions, optThreadAnalysis)
  231. of "stacktrace": result = contains(conf.options, optStackTrace)
  232. of "linetrace": result = contains(conf.options, optLineTrace)
  233. of "debugger": result = contains(conf.options, optEndb)
  234. of "profiler": result = contains(conf.options, optProfiler)
  235. of "memtracker": result = contains(conf.options, optMemTracker)
  236. of "checks", "x": result = conf.options * ChecksOptions == ChecksOptions
  237. of "floatchecks":
  238. result = conf.options * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck}
  239. of "infchecks": result = contains(conf.options, optInfCheck)
  240. of "nanchecks": result = contains(conf.options, optNaNCheck)
  241. of "nilchecks": result = contains(conf.options, optNilCheck)
  242. of "objchecks": result = contains(conf.options, optObjCheck)
  243. of "fieldchecks": result = contains(conf.options, optFieldCheck)
  244. of "rangechecks": result = contains(conf.options, optRangeCheck)
  245. of "boundchecks": result = contains(conf.options, optBoundsCheck)
  246. of "overflowchecks": result = contains(conf.options, optOverflowCheck)
  247. of "movechecks": result = contains(conf.options, optMoveCheck)
  248. of "linedir": result = contains(conf.options, optLineDir)
  249. of "assertions", "a": result = contains(conf.options, optAssert)
  250. of "run", "r": result = contains(conf.globalOptions, optRun)
  251. of "symbolfiles": result = conf.symbolFiles != disabledSf
  252. of "genscript": result = contains(conf.globalOptions, optGenScript)
  253. of "threads": result = contains(conf.globalOptions, optThreads)
  254. of "taintmode": result = contains(conf.globalOptions, optTaintMode)
  255. of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
  256. of "implicitstatic": result = contains(conf.options, optImplicitStatic)
  257. of "patterns", "trmacros": result = contains(conf.options, optTrMacros)
  258. of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
  259. of "nilseqs": result = contains(conf.options, optNilSeqs)
  260. of "oldast": result = contains(conf.options, optOldAst)
  261. else: invalidCmdLineOption(conf, passCmd1, switch, info)
  262. proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
  263. notRelativeToProj = false): AbsoluteDir =
  264. let p = if os.isAbsolute(path) or '$' in path:
  265. path
  266. elif notRelativeToProj:
  267. getCurrentDir() / path
  268. else:
  269. conf.projectPath.string / path
  270. try:
  271. result = AbsoluteDir pathSubs(conf, p, toFullPath(conf, info).splitFile().dir)
  272. except ValueError:
  273. localError(conf, info, "invalid path: " & p)
  274. result = AbsoluteDir p
  275. proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): AbsoluteDir =
  276. let path = if path.len > 0 and path[0] == '"': strutils.unescape(path)
  277. else: path
  278. let basedir = toFullPath(conf, info).splitFile().dir
  279. let p = if os.isAbsolute(path) or '$' in path:
  280. path
  281. else:
  282. basedir / path
  283. try:
  284. result = AbsoluteDir pathSubs(conf, p, basedir)
  285. except ValueError:
  286. localError(conf, info, "invalid path: " & p)
  287. result = AbsoluteDir p
  288. const
  289. errInvalidNumber = "$1 is not a valid number"
  290. proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) =
  291. var a = arg.split(',')
  292. if a.len != 4: localError(conf, info,
  293. "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN expected")
  294. var line, column: int
  295. if parseUtils.parseInt(a[2], line) <= 0:
  296. localError(conf, info, errInvalidNumber % a[1])
  297. if parseUtils.parseInt(a[3], column) <= 0:
  298. localError(conf, info, errInvalidNumber % a[2])
  299. let dirtyOriginalIdx = fileInfoIdx(conf, AbsoluteFile a[1])
  300. if dirtyOriginalIdx.int32 >= 0:
  301. msgs.setDirtyFile(conf, dirtyOriginalIdx, AbsoluteFile a[0])
  302. conf.m.trackPos = newLineInfo(dirtyOriginalIdx, line, column)
  303. proc track(conf: ConfigRef; arg: string, info: TLineInfo) =
  304. var a = arg.split(',')
  305. if a.len != 3: localError(conf, info, "FILE,LINE,COLUMN expected")
  306. var line, column: int
  307. if parseUtils.parseInt(a[1], line) <= 0:
  308. localError(conf, info, errInvalidNumber % a[1])
  309. if parseUtils.parseInt(a[2], column) <= 0:
  310. localError(conf, info, errInvalidNumber % a[2])
  311. conf.m.trackPos = newLineInfo(conf, AbsoluteFile a[0], line, column)
  312. proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
  313. if pass in {passCmd2, passPP}:
  314. expectArg(conf, switch, arg, pass, info)
  315. options.inclDynlibOverride(conf, arg)
  316. proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
  317. conf: ConfigRef) =
  318. var
  319. key, val: string
  320. case switch.normalize
  321. of "path", "p":
  322. expectArg(conf, switch, arg, pass, info)
  323. addPath(conf, if pass == passPP: processCfgPath(conf, arg, info)
  324. else: processPath(conf, arg, info), info)
  325. of "nimblepath", "babelpath":
  326. # keep the old name for compat
  327. if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions:
  328. expectArg(conf, switch, arg, pass, info)
  329. var path = processPath(conf, arg, info, notRelativeToProj=true)
  330. let nimbleDir = AbsoluteDir getEnv("NIMBLE_DIR")
  331. if not nimbleDir.isEmpty and pass == passPP:
  332. path = nimbleDir / RelativeDir"pkgs"
  333. nimblePath(conf, path, info)
  334. of "nonimblepath", "nobabelpath":
  335. expectNoArg(conf, switch, arg, pass, info)
  336. disableNimblePath(conf)
  337. of "excludepath":
  338. expectArg(conf, switch, arg, pass, info)
  339. let path = processPath(conf, arg, info)
  340. conf.searchPaths.keepItIf(it != path)
  341. conf.lazyPaths.keepItIf(it != path)
  342. of "nimcache":
  343. expectArg(conf, switch, arg, pass, info)
  344. conf.nimcacheDir = processPath(conf, arg, info, true)
  345. of "out", "o":
  346. expectArg(conf, switch, arg, pass, info)
  347. let f = splitFile(arg.expandTilde)
  348. conf.outFile = RelativeFile f.name & f.ext
  349. conf.outDir = toAbsoluteDir f.dir
  350. of "outdir":
  351. expectArg(conf, switch, arg, pass, info)
  352. conf.outDir = toAbsoluteDir arg.expandTilde
  353. of "docseesrcurl":
  354. expectArg(conf, switch, arg, pass, info)
  355. conf.docSeeSrcUrl = arg
  356. of "mainmodule", "m":
  357. discard "allow for backwards compatibility, but don't do anything"
  358. of "define", "d":
  359. expectArg(conf, switch, arg, pass, info)
  360. if {':', '='} in arg:
  361. splitSwitch(conf, arg, key, val, pass, info)
  362. defineSymbol(conf.symbols, key, val)
  363. else:
  364. defineSymbol(conf.symbols, arg)
  365. of "undef", "u":
  366. expectArg(conf, switch, arg, pass, info)
  367. undefSymbol(conf.symbols, arg)
  368. of "symbol":
  369. expectArg(conf, switch, arg, pass, info)
  370. # deprecated, do nothing
  371. of "compile":
  372. expectArg(conf, switch, arg, pass, info)
  373. if pass in {passCmd2, passPP}: processCompile(conf, arg)
  374. of "link":
  375. expectArg(conf, switch, arg, pass, info)
  376. if pass in {passCmd2, passPP}:
  377. addExternalFileToLink(conf, AbsoluteFile arg)
  378. of "debuginfo":
  379. processOnOffSwitchG(conf, {optCDebug}, arg, pass, info)
  380. of "embedsrc":
  381. processOnOffSwitchG(conf, {optEmbedOrigSrc}, arg, pass, info)
  382. of "compileonly", "c":
  383. processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
  384. of "nolinking":
  385. processOnOffSwitchG(conf, {optNoLinking}, arg, pass, info)
  386. of "nomain":
  387. processOnOffSwitchG(conf, {optNoMain}, arg, pass, info)
  388. of "forcebuild", "f":
  389. processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info)
  390. of "project":
  391. processOnOffSwitchG(conf, {optWholeProject}, arg, pass, info)
  392. of "gc":
  393. expectArg(conf, switch, arg, pass, info)
  394. case arg.normalize
  395. of "boehm":
  396. conf.selectedGC = gcBoehm
  397. defineSymbol(conf.symbols, "boehmgc")
  398. of "refc":
  399. conf.selectedGC = gcRefc
  400. of "v2":
  401. message(conf, info, warnDeprecated, "--gc:v2 is deprecated; using default gc")
  402. of "markandsweep":
  403. conf.selectedGC = gcMarkAndSweep
  404. defineSymbol(conf.symbols, "gcmarkandsweep")
  405. of "destructors":
  406. conf.selectedGC = gcDestructors
  407. defineSymbol(conf.symbols, "gcdestructors")
  408. of "go":
  409. conf.selectedGC = gcGo
  410. defineSymbol(conf.symbols, "gogc")
  411. of "none":
  412. conf.selectedGC = gcNone
  413. defineSymbol(conf.symbols, "nogc")
  414. of "stack", "regions":
  415. conf.selectedGC= gcRegions
  416. defineSymbol(conf.symbols, "gcregions")
  417. else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
  418. of "warnings", "w":
  419. if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
  420. of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
  421. of "hint": processSpecificNote(arg, wHint, pass, info, switch, conf)
  422. of "hints":
  423. if processOnOffSwitchOrList(conf, {optHints}, arg, pass, info): listHints(conf)
  424. of "threadanalysis": processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info)
  425. of "stacktrace": processOnOffSwitch(conf, {optStackTrace}, arg, pass, info)
  426. of "excessivestacktrace": processOnOffSwitchG(conf, {optExcessiveStackTrace}, arg, pass, info)
  427. of "linetrace": processOnOffSwitch(conf, {optLineTrace}, arg, pass, info)
  428. of "debugger":
  429. case arg.normalize
  430. of "on", "endb":
  431. conf.options.incl optEndb
  432. defineSymbol(conf.symbols, "endb")
  433. of "off":
  434. conf.options.excl optEndb
  435. undefSymbol(conf.symbols, "endb")
  436. of "native", "gdb":
  437. incl(conf.globalOptions, optCDebug)
  438. conf.options = conf.options + {optLineDir} - {optEndb}
  439. #defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
  440. undefSymbol(conf.symbols, "endb")
  441. else:
  442. localError(conf, info, "expected endb|gdb but found " & arg)
  443. of "g": # alias for --debugger:native
  444. incl(conf.globalOptions, optCDebug)
  445. conf.options = conf.options + {optLineDir} - {optEndb}
  446. #defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
  447. undefSymbol(conf.symbols, "endb")
  448. of "profiler":
  449. processOnOffSwitch(conf, {optProfiler}, arg, pass, info)
  450. if optProfiler in conf.options: defineSymbol(conf.symbols, "profiler")
  451. else: undefSymbol(conf.symbols, "profiler")
  452. of "memtracker":
  453. processOnOffSwitch(conf, {optMemTracker}, arg, pass, info)
  454. if optMemTracker in conf.options: defineSymbol(conf.symbols, "memtracker")
  455. else: undefSymbol(conf.symbols, "memtracker")
  456. of "hotcodereloading":
  457. processOnOffSwitchG(conf, {optHotCodeReloading}, arg, pass, info)
  458. if conf.hcrOn:
  459. defineSymbol(conf.symbols, "hotcodereloading")
  460. defineSymbol(conf.symbols, "useNimRtl")
  461. # hardcoded linking with dynamic runtime for MSVC for smaller binaries
  462. # should do the same for all compilers (wherever applicable)
  463. if isVSCompatible(conf):
  464. extccomp.addCompileOptionCmd(conf, "/MD")
  465. else:
  466. undefSymbol(conf.symbols, "hotcodereloading")
  467. undefSymbol(conf.symbols, "useNimRtl")
  468. of "oldnewlines":
  469. case arg.normalize
  470. of "","on":
  471. conf.oldNewlines = true
  472. defineSymbol(conf.symbols, "nimOldNewlines")
  473. of "off":
  474. conf.oldNewlines = false
  475. undefSymbol(conf.symbols, "nimOldNewlines")
  476. else:
  477. localError(conf, info, errOnOrOffExpectedButXFound % arg)
  478. of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
  479. of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info)
  480. of "oldast": processOnOffSwitch(conf, {optOldAst}, arg, pass, info)
  481. of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
  482. of "floatchecks":
  483. processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)
  484. of "infchecks": processOnOffSwitch(conf, {optInfCheck}, arg, pass, info)
  485. of "nanchecks": processOnOffSwitch(conf, {optNaNCheck}, arg, pass, info)
  486. of "nilchecks": processOnOffSwitch(conf, {optNilCheck}, arg, pass, info)
  487. of "objchecks": processOnOffSwitch(conf, {optObjCheck}, arg, pass, info)
  488. of "fieldchecks": processOnOffSwitch(conf, {optFieldCheck}, arg, pass, info)
  489. of "rangechecks": processOnOffSwitch(conf, {optRangeCheck}, arg, pass, info)
  490. of "boundchecks": processOnOffSwitch(conf, {optBoundsCheck}, arg, pass, info)
  491. of "overflowchecks": processOnOffSwitch(conf, {optOverflowCheck}, arg, pass, info)
  492. of "movechecks": processOnOffSwitch(conf, {optMoveCheck}, arg, pass, info)
  493. of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info)
  494. of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info)
  495. of "deadcodeelim": discard # deprecated, dead code elim always on
  496. of "threads":
  497. processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
  498. #if optThreads in conf.globalOptions: incl(conf.notes, warnGcUnsafe)
  499. of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
  500. of "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info)
  501. of "implicitstatic":
  502. processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
  503. of "patterns", "trmacros":
  504. processOnOffSwitch(conf, {optTrMacros}, arg, pass, info)
  505. of "opt":
  506. expectArg(conf, switch, arg, pass, info)
  507. case arg.normalize
  508. of "speed":
  509. incl(conf.options, optOptimizeSpeed)
  510. excl(conf.options, optOptimizeSize)
  511. of "size":
  512. excl(conf.options, optOptimizeSpeed)
  513. incl(conf.options, optOptimizeSize)
  514. of "none":
  515. excl(conf.options, optOptimizeSpeed)
  516. excl(conf.options, optOptimizeSize)
  517. else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
  518. of "app":
  519. expectArg(conf, switch, arg, pass, info)
  520. case arg.normalize
  521. of "gui":
  522. incl(conf.globalOptions, optGenGuiApp)
  523. defineSymbol(conf.symbols, "executable")
  524. defineSymbol(conf.symbols, "guiapp")
  525. of "console":
  526. excl(conf.globalOptions, optGenGuiApp)
  527. defineSymbol(conf.symbols, "executable")
  528. defineSymbol(conf.symbols, "consoleapp")
  529. of "lib":
  530. incl(conf.globalOptions, optGenDynLib)
  531. excl(conf.globalOptions, optGenGuiApp)
  532. defineSymbol(conf.symbols, "library")
  533. defineSymbol(conf.symbols, "dll")
  534. of "staticlib":
  535. incl(conf.globalOptions, optGenStaticLib)
  536. excl(conf.globalOptions, optGenGuiApp)
  537. defineSymbol(conf.symbols, "library")
  538. defineSymbol(conf.symbols, "staticlib")
  539. else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
  540. of "passc", "t":
  541. expectArg(conf, switch, arg, pass, info)
  542. if pass in {passCmd2, passPP}: extccomp.addCompileOptionCmd(conf, arg)
  543. of "passl", "l":
  544. expectArg(conf, switch, arg, pass, info)
  545. if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(conf, arg)
  546. of "cincludes":
  547. expectArg(conf, switch, arg, pass, info)
  548. if pass in {passCmd2, passPP}: conf.cIncludes.add processPath(conf, arg, info)
  549. of "clibdir":
  550. expectArg(conf, switch, arg, pass, info)
  551. if pass in {passCmd2, passPP}: conf.cLibs.add processPath(conf, arg, info)
  552. of "clib":
  553. expectArg(conf, switch, arg, pass, info)
  554. if pass in {passCmd2, passPP}:
  555. conf.cLinkedLibs.add processPath(conf, arg, info).string
  556. of "header":
  557. if conf != nil: conf.headerFile = arg
  558. incl(conf.globalOptions, optGenIndex)
  559. of "index":
  560. processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
  561. of "import":
  562. expectArg(conf, switch, arg, pass, info)
  563. if pass in {passCmd2, passPP}:
  564. conf.implicitImports.add findModule(conf, arg, toFullPath(conf, info)).string
  565. of "include":
  566. expectArg(conf, switch, arg, pass, info)
  567. if pass in {passCmd2, passPP}:
  568. conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string
  569. of "listcmd":
  570. processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
  571. of "genmapping":
  572. processOnOffSwitchG(conf, {optGenMapping}, arg, pass, info)
  573. of "os":
  574. expectArg(conf, switch, arg, pass, info)
  575. if pass in {passCmd1, passPP}:
  576. let theOS = platform.nameToOS(arg)
  577. if theOS == osNone:
  578. let osList = platform.listOSnames().join(", ")
  579. localError(conf, info, "unknown OS: '$1'. Available options are: $2" % [arg, $osList])
  580. elif theOS != conf.target.hostOS:
  581. setTarget(conf.target, theOS, conf.target.targetCPU)
  582. of "cpu":
  583. expectArg(conf, switch, arg, pass, info)
  584. if pass in {passCmd1, passPP}:
  585. let cpu = platform.nameToCPU(arg)
  586. if cpu == cpuNone:
  587. let cpuList = platform.listCPUnames().join(", ")
  588. localError(conf, info, "unknown CPU: '$1'. Available options are: $2" % [ arg, cpuList])
  589. elif cpu != conf.target.hostCPU:
  590. setTarget(conf.target, conf.target.targetOS, cpu)
  591. of "run", "r":
  592. processOnOffSwitchG(conf, {optRun}, arg, pass, info)
  593. of "errormax":
  594. expectArg(conf, switch, arg, pass, info)
  595. # Note: `nim check` (etc) can overwrite this.
  596. # `0` is meaningless, give it a useful meaning as in clang's -ferror-limit
  597. # If user doesn't set this flag and the code doesn't either, it'd
  598. # have the same effect as errorMax = 1
  599. let ret = parseInt(arg)
  600. conf.errorMax = if ret == 0: high(int) else: ret
  601. of "verbosity":
  602. expectArg(conf, switch, arg, pass, info)
  603. let verbosity = parseInt(arg)
  604. if verbosity notin {0..3}:
  605. localError(conf, info, "invalid verbosity level: '$1'" % arg)
  606. conf.verbosity = verbosity
  607. conf.notes = NotesVerbosity[conf.verbosity]
  608. incl(conf.notes, conf.enableNotes)
  609. excl(conf.notes, conf.disableNotes)
  610. conf.mainPackageNotes = conf.notes
  611. of "parallelbuild":
  612. expectArg(conf, switch, arg, pass, info)
  613. conf.numberOfProcessors = parseInt(arg)
  614. of "version", "v":
  615. expectNoArg(conf, switch, arg, pass, info)
  616. writeVersionInfo(conf, pass)
  617. of "advanced":
  618. expectNoArg(conf, switch, arg, pass, info)
  619. writeAdvancedUsage(conf, pass)
  620. of "fullhelp":
  621. expectNoArg(conf, switch, arg, pass, info)
  622. writeFullhelp(conf, pass)
  623. of "help", "h":
  624. expectNoArg(conf, switch, arg, pass, info)
  625. helpOnError(conf, pass)
  626. of "symbolfiles": discard "ignore for backwards compat"
  627. of "incremental":
  628. when not defined(nimIncremental):
  629. localError(conf, info, "the compiler was not built with " &
  630. "incremental compilation features; bootstrap with " &
  631. "-d:nimIncremental to enable")
  632. case arg.normalize
  633. of "on": conf.symbolFiles = v2Sf
  634. of "off": conf.symbolFiles = disabledSf
  635. of "writeonly": conf.symbolFiles = writeOnlySf
  636. of "readonly": conf.symbolFiles = readOnlySf
  637. of "v2": conf.symbolFiles = v2Sf
  638. else: localError(conf, info, "invalid option for --incremental: " & arg)
  639. of "skipcfg":
  640. processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info)
  641. of "skipprojcfg":
  642. processOnOffSwitchG(conf, {optSkipProjConfigFile}, arg, pass, info)
  643. of "skipusercfg":
  644. processOnOffSwitchG(conf, {optSkipUserConfigFile}, arg, pass, info)
  645. of "skipparentcfg":
  646. processOnOffSwitchG(conf, {optSkipParentConfigFiles}, arg, pass, info)
  647. of "genscript", "gendeps":
  648. processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
  649. processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
  650. of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
  651. of "lib":
  652. expectArg(conf, switch, arg, pass, info)
  653. conf.libpath = processPath(conf, arg, info, notRelativeToProj=true)
  654. of "putenv":
  655. expectArg(conf, switch, arg, pass, info)
  656. splitSwitch(conf, arg, key, val, pass, info)
  657. os.putEnv(key, val)
  658. of "cc":
  659. expectArg(conf, switch, arg, pass, info)
  660. setCC(conf, arg, info)
  661. of "track":
  662. expectArg(conf, switch, arg, pass, info)
  663. track(conf, arg, info)
  664. of "trackdirty":
  665. expectArg(conf, switch, arg, pass, info)
  666. trackDirty(conf, arg, info)
  667. of "suggest":
  668. expectNoArg(conf, switch, arg, pass, info)
  669. conf.ideCmd = ideSug
  670. of "def":
  671. expectNoArg(conf, switch, arg, pass, info)
  672. conf.ideCmd = ideDef
  673. of "eval":
  674. expectArg(conf, switch, arg, pass, info)
  675. conf.evalExpr = arg
  676. of "context":
  677. expectNoArg(conf, switch, arg, pass, info)
  678. conf.ideCmd = ideCon
  679. of "usages":
  680. expectNoArg(conf, switch, arg, pass, info)
  681. conf.ideCmd = ideUse
  682. of "stdout":
  683. processOnOffSwitchG(conf, {optStdout}, arg, pass, info)
  684. of "listfullpaths":
  685. processOnOffSwitchG(conf, {optListFullPaths}, arg, pass, info)
  686. of "dynliboverride":
  687. dynlibOverride(conf, switch, arg, pass, info)
  688. of "dynliboverrideall":
  689. processOnOffSwitchG(conf, {optDynlibOverrideAll}, arg, pass, info)
  690. of "cs":
  691. # only supported for compatibility. Does nothing.
  692. expectArg(conf, switch, arg, pass, info)
  693. of "experimental":
  694. if arg.len == 0:
  695. conf.features.incl oldExperimentalFeatures
  696. else:
  697. try:
  698. conf.features.incl parseEnum[Feature](arg)
  699. except ValueError:
  700. localError(conf, info, "unknown experimental feature")
  701. of "nocppexceptions":
  702. expectNoArg(conf, switch, arg, pass, info)
  703. incl(conf.globalOptions, optNoCppExceptions)
  704. defineSymbol(conf.symbols, "noCppExceptions")
  705. of "cppdefine":
  706. expectArg(conf, switch, arg, pass, info)
  707. if conf != nil:
  708. conf.cppDefine(arg)
  709. of "newruntime":
  710. expectNoArg(conf, switch, arg, pass, info)
  711. if pass in {passCmd2, passPP}:
  712. doAssert(conf != nil)
  713. incl(conf.features, destructor)
  714. incl(conf.globalOptions, optNimV2)
  715. defineSymbol(conf.symbols, "nimV2")
  716. conf.selectedGC = gcDestructors
  717. defineSymbol(conf.symbols, "gcdestructors")
  718. of "stylecheck":
  719. case arg.normalize
  720. of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}
  721. of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint}
  722. of "error": conf.globalOptions = conf.globalOptions + {optStyleError}
  723. else: localError(conf, info, errOffHintsError % arg)
  724. of "showallmismatches":
  725. processOnOffSwitchG(conf, {optShowAllMismatches}, arg, pass, info)
  726. of "cppcompiletonamespace":
  727. if arg.len > 0:
  728. conf.cppCustomNamespace = arg
  729. else:
  730. conf.cppCustomNamespace = "Nim"
  731. defineSymbol(conf.symbols, "cppCompileToNamespace", conf.cppCustomNamespace)
  732. of "docinternal":
  733. processOnOffSwitchG(conf, {optDocInternal}, arg, pass, info)
  734. of "multimethods":
  735. processOnOffSwitchG(conf, {optMultiMethods}, arg, pass, info)
  736. of "expandmacro":
  737. expectArg(conf, switch, arg, pass, info)
  738. conf.macrosToExpand[arg] = "T"
  739. of "":
  740. conf.projectName = "-"
  741. else:
  742. if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)
  743. else: invalidCmdLineOption(conf, pass, switch, info)
  744. template gCmdLineInfo*(): untyped = newLineInfo(config, AbsoluteFile"command line", 1, 1)
  745. proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) =
  746. var cmd, arg: string
  747. splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo)
  748. processSwitch(cmd, arg, pass, gCmdLineInfo, config)
  749. proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) =
  750. # hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off")
  751. # we transform it to (key = hint, val = [X]:off)
  752. var bracketLe = strutils.find(p.key, '[')
  753. if bracketLe >= 0:
  754. var key = substr(p.key, 0, bracketLe - 1)
  755. var val = substr(p.key, bracketLe) & ':' & p.val
  756. processSwitch(key, val, pass, gCmdLineInfo, config)
  757. else:
  758. processSwitch(p.key, p.val, pass, gCmdLineInfo, config)
  759. proc processArgument*(pass: TCmdLinePass; p: OptParser;
  760. argsCount: var int; config: ConfigRef): bool =
  761. if argsCount == 0:
  762. # nim filename.nims is the same as "nim e filename.nims":
  763. if p.key.endswith(".nims"):
  764. config.command = "e"
  765. incl(config.globalOptions, optWasNimscript)
  766. config.projectName = unixToNativePath(p.key)
  767. config.arguments = cmdLineRest(p)
  768. result = true
  769. elif pass != passCmd2:
  770. config.command = p.key
  771. else:
  772. if pass == passCmd1: config.commandArgs.add p.key
  773. if argsCount == 1:
  774. # support UNIX style filenames everywhere for portable build scripts:
  775. if config.projectName.len == 0:
  776. config.projectName = unixToNativePath(p.key)
  777. config.arguments = cmdLineRest(p)
  778. result = true
  779. inc argsCount