options.nim 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  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. import
  10. os, strutils, strtabs, osproc, sets
  11. const
  12. hasTinyCBackend* = defined(tinyc)
  13. useEffectSystem* = true
  14. useWriteTracking* = false
  15. hasFFI* = defined(useFFI)
  16. newScopeForIf* = true
  17. useCaas* = not defined(noCaas)
  18. noTimeMachine* = defined(avoidTimeMachine) and defined(macosx)
  19. type # please make sure we have under 32 options
  20. # (improves code efficiency a lot!)
  21. TOption* = enum # **keep binary compatible**
  22. optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck,
  23. optOverflowCheck, optNilCheck,
  24. optNaNCheck, optInfCheck,
  25. optAssert, optLineDir, optWarns, optHints,
  26. optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support
  27. optLineTrace, # line tracing support (includes stack tracing)
  28. optEndb, # embedded debugger
  29. optByRef, # use pass by ref for objects
  30. # (for interfacing with C)
  31. optProfiler, # profiler turned on
  32. optImplicitStatic, # optimization: implicit at compile time
  33. # evaluation
  34. optPatterns, # en/disable pattern matching
  35. optMemTracker
  36. TOptions* = set[TOption]
  37. TGlobalOption* = enum # **keep binary compatible**
  38. gloptNone, optForceFullMake, optDeadCodeElim,
  39. optListCmd, optCompileOnly, optNoLinking,
  40. optCDebug, # turn on debugging information
  41. optGenDynLib, # generate a dynamic library
  42. optGenStaticLib, # generate a static library
  43. optGenGuiApp, # generate a GUI application
  44. optGenScript, # generate a script file to compile the *.c files
  45. optGenMapping, # generate a mapping file
  46. optRun, # run the compiled project
  47. optSymbolFiles, # use symbol files for speeding up compilation
  48. optCaasEnabled # compiler-as-a-service is running
  49. optSkipConfigFile, # skip the general config file
  50. optSkipProjConfigFile, # skip the project's config file
  51. optSkipUserConfigFile, # skip the users's config file
  52. optSkipParentConfigFiles, # skip parent dir's config files
  53. optNoMain, # do not generate a "main" proc
  54. optUseColors, # use colors for hints, warnings, and errors
  55. optThreads, # support for multi-threading
  56. optStdout, # output to stdout
  57. optThreadAnalysis, # thread analysis pass
  58. optTaintMode, # taint mode turned on
  59. optTlsEmulation, # thread var emulation turned on
  60. optGenIndex # generate index file for documentation;
  61. optEmbedOrigSrc # embed the original source in the generated code
  62. # also: generate header file
  63. optIdeDebug # idetools: debug mode
  64. optIdeTerse # idetools: use terse descriptions
  65. optNoCppExceptions # use C exception handling even with CPP
  66. optExcessiveStackTrace # fully qualified module filenames
  67. TGlobalOptions* = set[TGlobalOption]
  68. const
  69. harmlessOptions* = {optForceFullMake, optNoLinking, optRun,
  70. optUseColors, optStdout}
  71. type
  72. TCommands* = enum # Nim's commands
  73. # **keep binary compatible**
  74. cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
  75. cmdCompileToJS,
  76. cmdCompileToPHP,
  77. cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc,
  78. cmdGenDepend, cmdDump,
  79. cmdCheck, # semantic checking for whole project
  80. cmdParse, # parse a single file (for debugging)
  81. cmdScan, # scan a single file (for debugging)
  82. cmdIdeTools, # ide tools
  83. cmdDef, # def feature (find definition for IDEs)
  84. cmdRst2html, # convert a reStructuredText file to HTML
  85. cmdRst2tex, # convert a reStructuredText file to TeX
  86. cmdInteractive, # start interactive session
  87. cmdRun, # run the project via TCC backend
  88. cmdJsonScript # compile a .json build file
  89. TStringSeq* = seq[string]
  90. TGCMode* = enum # the selected GC
  91. gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc,
  92. gcV2, gcGenerational
  93. IdeCmd* = enum
  94. ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
  95. ideHighlight, ideOutline, ideKnown, ideMsg
  96. ConfigRef* = ref object ## eventually all global configuration should be moved here
  97. cppDefines*: HashSet[string]
  98. headerFile*: string
  99. proc newConfigRef*(): ConfigRef =
  100. result = ConfigRef(cppDefines: initSet[string](),
  101. headerFile: "")
  102. proc cppDefine*(c: ConfigRef; define: string) =
  103. c.cppDefines.incl define
  104. var
  105. gIdeCmd*: IdeCmd
  106. const
  107. ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
  108. optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck}
  109. var
  110. gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck,
  111. optBoundsCheck, optOverflowCheck, optAssert, optWarns,
  112. optHints, optStackTrace, optLineTrace,
  113. optPatterns, optNilCheck}
  114. gGlobalOptions*: TGlobalOptions = {optThreadAnalysis}
  115. gExitcode*: int8
  116. gCmd*: TCommands = cmdNone # the command
  117. gSelectedGC* = gcRefc # the selected GC
  118. searchPaths*: seq[string] = @[]
  119. lazyPaths*: seq[string] = @[]
  120. outFile*: string = ""
  121. docSeeSrcUrl*: string = "" # if empty, no seeSrc will be generated. \
  122. # The string uses the formatting variables `path` and `line`.
  123. #headerFile*: string = ""
  124. gVerbosity* = 1 # how verbose the compiler is
  125. gNumberOfProcessors*: int # number of processors
  126. gWholeProject*: bool # for 'doc2': output any dependency
  127. gEvalExpr* = "" # expression for idetools --eval
  128. gLastCmdTime*: float # when caas is enabled, we measure each command
  129. gListFullPaths*: bool
  130. isServing*: bool = false
  131. gNoNimblePath* = false
  132. gExperimentalMode*: bool
  133. newDestructors*: bool
  134. proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
  135. proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
  136. template compilationCachePresent*: untyped =
  137. {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
  138. template optPreserveOrigSource*: untyped =
  139. optEmbedOrigSrc in gGlobalOptions
  140. const
  141. genSubDir* = "nimcache"
  142. NimExt* = "nim"
  143. RodExt* = "rod"
  144. HtmlExt* = "html"
  145. JsonExt* = "json"
  146. TexExt* = "tex"
  147. IniExt* = "ini"
  148. DefaultConfig* = "nim.cfg"
  149. DocConfig* = "nimdoc.cfg"
  150. DocTexConfig* = "nimdoc.tex.cfg"
  151. # additional configuration variables:
  152. var
  153. gConfigVars* = newStringTable(modeStyleInsensitive)
  154. gDllOverrides = newStringTable(modeCaseInsensitive)
  155. gModuleOverrides* = newStringTable(modeStyleInsensitive)
  156. gPrefixDir* = "" # Overrides the default prefix dir in getPrefixDir proc.
  157. libpath* = ""
  158. gProjectName* = "" # holds a name like 'nim'
  159. gProjectPath* = "" # holds a path like /home/alice/projects/nim/compiler/
  160. gProjectFull* = "" # projectPath/projectName
  161. gProjectIsStdin* = false # whether we're compiling from stdin
  162. gProjectMainIdx*: int32 # the canonical path id of the main module
  163. nimcacheDir* = ""
  164. command* = "" # the main command (e.g. cc, check, scan, etc)
  165. commandArgs*: seq[string] = @[] # any arguments after the main command
  166. gKeepComments*: bool = true # whether the parser needs to keep comments
  167. implicitImports*: seq[string] = @[] # modules that are to be implicitly imported
  168. implicitIncludes*: seq[string] = @[] # modules that are to be implicitly included
  169. const oKeepVariableNames* = true
  170. template compilingLib*: bool =
  171. gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}
  172. proc mainCommandArg*: string =
  173. ## This is intended for commands like check or parse
  174. ## which will work on the main project file unless
  175. ## explicitly given a specific file argument
  176. if commandArgs.len > 0:
  177. result = commandArgs[0]
  178. else:
  179. result = gProjectName
  180. proc existsConfigVar*(key: string): bool =
  181. result = hasKey(gConfigVars, key)
  182. proc getConfigVar*(key: string): string =
  183. result = gConfigVars.getOrDefault key
  184. proc setConfigVar*(key, val: string) =
  185. gConfigVars[key] = val
  186. proc getOutFile*(filename, ext: string): string =
  187. if options.outFile != "": result = options.outFile
  188. else: result = changeFileExt(filename, ext)
  189. proc getPrefixDir*(): string =
  190. ## Gets the prefix dir, usually the parent directory where the binary resides.
  191. ##
  192. ## This is overridden by some tools (namely nimsuggest) via the ``gPrefixDir``
  193. ## global.
  194. if gPrefixDir != "": result = gPrefixDir
  195. else:
  196. result = splitPath(getAppDir()).head
  197. proc setDefaultLibpath*() =
  198. # set default value (can be overwritten):
  199. if libpath == "":
  200. # choose default libpath:
  201. var prefix = getPrefixDir()
  202. when defined(posix):
  203. if prefix == "/usr": libpath = "/usr/lib/nim"
  204. elif prefix == "/usr/local": libpath = "/usr/local/lib/nim"
  205. else: libpath = joinPath(prefix, "lib")
  206. else: libpath = joinPath(prefix, "lib")
  207. # Special rule to support other tools (nimble) which import the compiler
  208. # modules and make use of them.
  209. let realNimPath = findExe("nim")
  210. # Find out if $nim/../../lib/system.nim exists.
  211. let parentNimLibPath = realNimPath.parentDir().parentDir() / "lib"
  212. if not fileExists(libpath / "system.nim") and
  213. fileExists(parentNimlibPath / "system.nim"):
  214. libpath = parentNimLibPath
  215. proc canonicalizePath*(path: string): string =
  216. # on Windows, 'expandFilename' calls getFullPathName which doesn't do
  217. # case corrections, so we have to use this convoluted way of retrieving
  218. # the true filename (see tests/modules and Nimble uses 'import Uri' instead
  219. # of 'import uri'):
  220. when defined(windows):
  221. result = path.expandFilename
  222. for x in walkFiles(result):
  223. return x
  224. else:
  225. result = path.expandFilename
  226. proc shortenDir*(dir: string): string =
  227. ## returns the interesting part of a dir
  228. var prefix = gProjectPath & DirSep
  229. if startsWith(dir, prefix):
  230. return substr(dir, len(prefix))
  231. prefix = getPrefixDir() & DirSep
  232. if startsWith(dir, prefix):
  233. return substr(dir, len(prefix))
  234. result = dir
  235. proc removeTrailingDirSep*(path: string): string =
  236. if (len(path) > 0) and (path[len(path) - 1] == DirSep):
  237. result = substr(path, 0, len(path) - 2)
  238. else:
  239. result = path
  240. proc disableNimblePath*() =
  241. gNoNimblePath = true
  242. lazyPaths.setLen(0)
  243. include packagehandling
  244. proc getNimcacheDir*: string =
  245. result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
  246. genSubDir
  247. proc pathSubs*(p, config: string): string =
  248. let home = removeTrailingDirSep(os.getHomeDir())
  249. result = unixToNativePath(p % [
  250. "nim", getPrefixDir(),
  251. "lib", libpath,
  252. "home", home,
  253. "config", config,
  254. "projectname", options.gProjectName,
  255. "projectpath", options.gProjectPath,
  256. "projectdir", options.gProjectPath,
  257. "nimcache", getNimcacheDir()])
  258. if "~/" in result:
  259. result = result.replace("~/", home & '/')
  260. proc toGeneratedFile*(path, ext: string): string =
  261. ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
  262. var (head, tail) = splitPath(path)
  263. #if len(head) > 0: head = shortenDir(head & dirSep)
  264. result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)])
  265. #echo "toGeneratedFile(", path, ", ", ext, ") = ", result
  266. when noTimeMachine:
  267. var alreadyExcludedDirs = initSet[string]()
  268. proc excludeDirFromTimeMachine(dir: string) {.raises: [].} =
  269. ## Calls a macosx command on the directory to exclude it from backups.
  270. ##
  271. ## The macosx tmutil command is invoked to mark the specified path as an
  272. ## item to be excluded from time machine backups. If a path already exists
  273. ## with files before excluding it, newer files won't be added to the
  274. ## directory, but previous files won't be removed from the backup until the
  275. ## user deletes that directory.
  276. ##
  277. ## The whole proc is optional and will ignore all kinds of errors. The only
  278. ## way to be sure that it works is to call ``tmutil isexcluded path``.
  279. if alreadyExcludedDirs.contains(dir): return
  280. alreadyExcludedDirs.incl(dir)
  281. try:
  282. var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir])
  283. discard p.waitForExit
  284. p.close
  285. except Exception:
  286. discard
  287. proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
  288. var (head, tail) = splitPath(f)
  289. #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
  290. var subdir = getNimcacheDir() # / head
  291. if createSubDir:
  292. try:
  293. createDir(subdir)
  294. when noTimeMachine:
  295. excludeDirFromTimeMachine(subdir)
  296. except OSError:
  297. writeLine(stdout, "cannot create directory: " & subdir)
  298. quit(1)
  299. result = joinPath(subdir, tail)
  300. #echo "completeGeneratedFilePath(", f, ") = ", result
  301. proc rawFindFile(f: string): string =
  302. for it in searchPaths:
  303. result = joinPath(it, f)
  304. if existsFile(result):
  305. return result.canonicalizePath
  306. result = ""
  307. proc rawFindFile2(f: string): string =
  308. for i, it in lazyPaths:
  309. result = joinPath(it, f)
  310. if existsFile(result):
  311. # bring to front
  312. for j in countDown(i,1):
  313. swap(lazyPaths[j], lazyPaths[j-1])
  314. return result.canonicalizePath
  315. result = ""
  316. template patchModule() {.dirty.} =
  317. if result.len > 0 and gModuleOverrides.len > 0:
  318. let key = getPackageName(result) & "_" & splitFile(result).name
  319. if gModuleOverrides.hasKey(key):
  320. let ov = gModuleOverrides[key]
  321. if ov.len > 0: result = ov
  322. proc findFile*(f: string): string {.procvar.} =
  323. if f.isAbsolute:
  324. result = if f.existsFile: f else: ""
  325. else:
  326. result = f.rawFindFile
  327. if result.len == 0:
  328. result = f.toLowerAscii.rawFindFile
  329. if result.len == 0:
  330. result = f.rawFindFile2
  331. if result.len == 0:
  332. result = f.toLowerAscii.rawFindFile2
  333. patchModule()
  334. proc findModule*(modulename, currentModule: string): string =
  335. # returns path to module
  336. when defined(nimfix):
  337. # '.nimfix' modules are preferred over '.nim' modules so that specialized
  338. # versions can be kept for 'nimfix'.
  339. block:
  340. let m = addFileExt(modulename, "nimfix")
  341. let currentPath = currentModule.splitFile.dir
  342. result = currentPath / m
  343. if not existsFile(result):
  344. result = findFile(m)
  345. if existsFile(result): return result
  346. let m = addFileExt(modulename, NimExt)
  347. let currentPath = currentModule.splitFile.dir
  348. result = currentPath / m
  349. if not existsFile(result):
  350. result = findFile(m)
  351. patchModule()
  352. proc findProjectNimFile*(pkg: string): string =
  353. const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
  354. var candidates: seq[string] = @[]
  355. for k, f in os.walkDir(pkg, relative=true):
  356. if k == pcFile and f != "config.nims":
  357. let (_, name, ext) = splitFile(f)
  358. if ext in extensions:
  359. let x = changeFileExt(pkg / name, ".nim")
  360. if fileExists(x):
  361. candidates.add x
  362. for c in candidates:
  363. # nim-foo foo or foo nfoo
  364. if (pkg in c) or (c in pkg): return c
  365. if candidates.len >= 1:
  366. return candidates[0]
  367. return ""
  368. proc canonDynlibName(s: string): string =
  369. let start = if s.startsWith("lib"): 3 else: 0
  370. let ende = strutils.find(s, {'(', ')', '.'})
  371. if ende >= 0:
  372. result = s.substr(start, ende-1)
  373. else:
  374. result = s.substr(start)
  375. proc inclDynlibOverride*(lib: string) =
  376. gDllOverrides[lib.canonDynlibName] = "true"
  377. proc isDynlibOverride*(lib: string): bool =
  378. result = gDllOverrides.hasKey(lib.canonDynlibName)
  379. proc binaryStrSearch*(x: openArray[string], y: string): int =
  380. var a = 0
  381. var b = len(x) - 1
  382. while a <= b:
  383. var mid = (a + b) div 2
  384. var c = cmpIgnoreCase(x[mid], y)
  385. if c < 0:
  386. a = mid + 1
  387. elif c > 0:
  388. b = mid - 1
  389. else:
  390. return mid
  391. result = - 1
  392. proc parseIdeCmd*(s: string): IdeCmd =
  393. case s:
  394. of "sug": ideSug
  395. of "con": ideCon
  396. of "def": ideDef
  397. of "use": ideUse
  398. of "dus": ideDus
  399. of "chk": ideChk
  400. of "mod": ideMod
  401. of "highlight": ideHighlight
  402. of "outline": ideOutline
  403. of "known": ideKnown
  404. of "msg": ideMsg
  405. else: ideNone
  406. proc `$`*(c: IdeCmd): string =
  407. case c:
  408. of ideSug: "sug"
  409. of ideCon: "con"
  410. of ideDef: "def"
  411. of ideUse: "use"
  412. of ideDus: "dus"
  413. of ideChk: "chk"
  414. of ideMod: "mod"
  415. of ideNone: "none"
  416. of ideHighlight: "highlight"
  417. of ideOutline: "outline"
  418. of ideKnown: "known"
  419. of ideMsg: "msg"