options.nim 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035
  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, sets, lineinfos, platform,
  11. prefixmatches, pathutils, nimpaths, tables
  12. from terminal import isatty
  13. from times import utc, fromUnix, local, getTime, format, DateTime
  14. from std/private/globs import nativeToUnixPath
  15. const
  16. hasTinyCBackend* = defined(tinyc)
  17. useEffectSystem* = true
  18. useWriteTracking* = false
  19. hasFFI* = defined(nimHasLibFFI)
  20. copyrightYear* = "2021"
  21. nimEnableCovariance* = defined(nimEnableCovariance)
  22. type # please make sure we have under 32 options
  23. # (improves code efficiency a lot!)
  24. TOption* = enum # **keep binary compatible**
  25. optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck,
  26. optOverflowCheck, optRefCheck,
  27. optNaNCheck, optInfCheck, optStaticBoundsCheck, optStyleCheck,
  28. optAssert, optLineDir, optWarns, optHints,
  29. optOptimizeSpeed, optOptimizeSize,
  30. optStackTrace, # stack tracing support
  31. optStackTraceMsgs, # enable custom runtime msgs via `setFrameMsg`
  32. optLineTrace, # line tracing support (includes stack tracing)
  33. optByRef, # use pass by ref for objects
  34. # (for interfacing with C)
  35. optProfiler, # profiler turned on
  36. optImplicitStatic, # optimization: implicit at compile time
  37. # evaluation
  38. optTrMacros, # en/disable pattern matching
  39. optMemTracker,
  40. optSinkInference # 'sink T' inference
  41. optCursorInference
  42. optImportHidden
  43. TOptions* = set[TOption]
  44. TGlobalOption* = enum
  45. gloptNone, optForceFullMake,
  46. optWasNimscript, # redundant with `cmdNimscript`, could be removed
  47. optListCmd, optCompileOnly, optNoLinking,
  48. optCDebug, # turn on debugging information
  49. optGenDynLib, # generate a dynamic library
  50. optGenStaticLib, # generate a static library
  51. optGenGuiApp, # generate a GUI application
  52. optGenScript, # generate a script file to compile the *.c files
  53. optGenMapping, # generate a mapping file
  54. optRun, # run the compiled project
  55. optUseNimcache, # save artifacts (including binary) in $nimcache
  56. optStyleHint, # check that the names adhere to NEP-1
  57. optStyleError, # enforce that the names adhere to NEP-1
  58. optStyleUsages, # only enforce consistent **usages** of the symbol
  59. optSkipSystemConfigFile, # skip the system's cfg/nims config file
  60. optSkipProjConfigFile, # skip the project's cfg/nims config file
  61. optSkipUserConfigFile, # skip the users's cfg/nims config file
  62. optSkipParentConfigFiles, # skip parent dir's cfg/nims config files
  63. optNoMain, # do not generate a "main" proc
  64. optUseColors, # use colors for hints, warnings, and errors
  65. optThreads, # support for multi-threading
  66. optStdout, # output to stdout
  67. optThreadAnalysis, # thread analysis pass
  68. optTlsEmulation, # thread var emulation turned on
  69. optGenIndex # generate index file for documentation;
  70. optEmbedOrigSrc # embed the original source in the generated code
  71. # also: generate header file
  72. optIdeDebug # idetools: debug mode
  73. optIdeTerse # idetools: use terse descriptions
  74. optExcessiveStackTrace # fully qualified module filenames
  75. optShowAllMismatches # show all overloading resolution candidates
  76. optWholeProject # for 'doc': output any dependency
  77. optDocInternal # generate documentation for non-exported symbols
  78. optMixedMode # true if some module triggered C++ codegen
  79. optDeclaredLocs # show declaration locations in messages
  80. optNoNimblePath
  81. optHotCodeReloading
  82. optDynlibOverrideAll
  83. optSeqDestructors # active if the implementation uses the new
  84. # string/seq implementation based on destructors
  85. optTinyRtti # active if we use the new "tiny RTTI"
  86. # implementation
  87. optOwnedRefs # active if the Nim compiler knows about 'owned'.
  88. optMultiMethods
  89. optBenchmarkVM # Enables cpuTime() in the VM
  90. optProduceAsm # produce assembler code
  91. optPanics # turn panics (sysFatal) into a process termination
  92. optNimV1Emulation # emulate Nim v1.0
  93. optNimV12Emulation # emulate Nim v1.2
  94. optSourcemap
  95. optProfileVM # enable VM profiler
  96. optEnableDeepCopy # ORC specific: enable 'deepcopy' for all types.
  97. TGlobalOptions* = set[TGlobalOption]
  98. const
  99. harmlessOptions* = {optForceFullMake, optNoLinking, optRun, optUseColors, optStdout}
  100. genSubDir* = RelativeDir"nimcache"
  101. NimExt* = "nim"
  102. RodExt* = "rod"
  103. HtmlExt* = "html"
  104. JsonExt* = "json"
  105. TagsExt* = "tags"
  106. TexExt* = "tex"
  107. IniExt* = "ini"
  108. DefaultConfig* = RelativeFile"nim.cfg"
  109. DefaultConfigNims* = RelativeFile"config.nims"
  110. DocConfig* = RelativeFile"nimdoc.cfg"
  111. DocTexConfig* = RelativeFile"nimdoc.tex.cfg"
  112. htmldocsDir* = htmldocsDirname.RelativeDir
  113. docRootDefault* = "@default" # using `@` instead of `$` to avoid shell quoting complications
  114. oKeepVariableNames* = true
  115. spellSuggestSecretSauce* = -1
  116. type
  117. TBackend* = enum
  118. backendInvalid = "" # for parseEnum
  119. backendC = "c"
  120. backendCpp = "cpp"
  121. backendJs = "js"
  122. backendObjc = "objc"
  123. # backendNimscript = "nimscript" # this could actually work
  124. # backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM
  125. Command* = enum ## Nim's commands
  126. cmdNone # not yet processed command
  127. cmdUnknown # command unmapped
  128. cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS
  129. cmdCrun # compile and run in nimache
  130. cmdTcc # run the project via TCC backend
  131. cmdCheck # semantic checking for whole project
  132. cmdParse # parse a single file (for debugging)
  133. cmdRod # .rod to some text representation (for debugging)
  134. cmdIdeTools # ide tools (e.g. nimsuggest)
  135. cmdNimscript # evaluate nimscript
  136. cmdDoc0
  137. cmdDoc # convert .nim doc comments to HTML
  138. cmdDoc2tex # convert .nim doc comments to LaTeX
  139. cmdRst2html # convert a reStructuredText file to HTML
  140. cmdRst2tex # convert a reStructuredText file to TeX
  141. cmdJsondoc0
  142. cmdJsondoc
  143. cmdCtags
  144. cmdBuildindex
  145. cmdGendepend
  146. cmdDump
  147. cmdInteractive # start interactive session
  148. cmdNop
  149. cmdJsonscript # compile a .json build file
  150. cmdNimfix
  151. # old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
  152. const
  153. cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, cmdCrun}
  154. cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc,
  155. cmdCtags, cmdBuildindex}
  156. type
  157. NimVer* = tuple[major: int, minor: int, patch: int]
  158. TStringSeq* = seq[string]
  159. TGCMode* = enum # the selected GC
  160. gcUnselected = "unselected"
  161. gcNone = "none"
  162. gcBoehm = "boehm"
  163. gcRegions = "regions"
  164. gcArc = "arc"
  165. gcOrc = "orc"
  166. gcMarkAndSweep = "markAndSweep"
  167. gcHooks = "hooks"
  168. gcRefc = "refc"
  169. gcV2 = "v2"
  170. gcGo = "go"
  171. # gcRefc and the GCs that follow it use a write barrier,
  172. # as far as usesWriteBarrier() is concerned
  173. IdeCmd* = enum
  174. ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideChkFile, ideMod,
  175. ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols,
  176. ideRecompile, ideChanged, ideType
  177. Feature* = enum ## experimental features; DO NOT RENAME THESE!
  178. implicitDeref,
  179. dotOperators,
  180. callOperator,
  181. parallel,
  182. destructor,
  183. notnil,
  184. dynamicBindSym,
  185. forLoopMacros, # not experimental anymore; remains here for backwards compatibility
  186. caseStmtMacros,
  187. codeReordering,
  188. compiletimeFFI,
  189. ## This requires building nim with `-d:nimHasLibFFI`
  190. ## which itself requires `nimble install libffi`, see #10150
  191. ## Note: this feature can't be localized with {.push.}
  192. vmopsDanger,
  193. strictFuncs,
  194. views,
  195. strictNotNil,
  196. overloadableEnums,
  197. strictEffects,
  198. unicodeOperators,
  199. flexibleOptionalParams
  200. LegacyFeature* = enum
  201. allowSemcheckedAstModification,
  202. ## Allows to modify a NimNode where the type has already been
  203. ## flagged with nfSem. If you actually do this, it will cause
  204. ## bugs.
  205. checkUnsignedConversions
  206. ## Historically and especially in version 1.0.0 of the language
  207. ## conversions to unsigned numbers were checked. In 1.0.4 they
  208. ## are not anymore.
  209. SymbolFilesOption* = enum
  210. disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
  211. TSystemCC* = enum
  212. ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc,
  213. ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl
  214. ExceptionSystem* = enum
  215. excNone, # no exception system selected yet
  216. excSetjmp, # setjmp based exception handling
  217. excCpp, # use C++'s native exception handling
  218. excGoto, # exception handling based on goto (should become the new default for C)
  219. excQuirky # quirky exception handling
  220. CfileFlag* {.pure.} = enum
  221. Cached, ## no need to recompile this time
  222. External ## file was introduced via .compile pragma
  223. Cfile* = object
  224. nimname*: string
  225. cname*, obj*: AbsoluteFile
  226. flags*: set[CfileFlag]
  227. customArgs*: string
  228. CfileList* = seq[Cfile]
  229. Suggest* = ref object
  230. section*: IdeCmd
  231. qualifiedPath*: seq[string]
  232. name*: ptr string # not used beyond sorting purposes; name is also
  233. # part of 'qualifiedPath'
  234. filePath*: string
  235. line*: int # Starts at 1
  236. column*: int # Starts at 0
  237. doc*: string # Not escaped (yet)
  238. forth*: string # type
  239. quality*: range[0..100] # matching quality
  240. isGlobal*: bool # is a global variable
  241. contextFits*: bool # type/non-type context matches
  242. prefix*: PrefixMatch
  243. symkind*: byte
  244. scope*, localUsages*, globalUsages*: int # more usages is better
  245. tokenLen*: int
  246. version*: int
  247. Suggestions* = seq[Suggest]
  248. ProfileInfo* = object
  249. time*: float
  250. count*: int
  251. ProfileData* = ref object
  252. data*: TableRef[TLineInfo, ProfileInfo]
  253. StdOrrKind* = enum
  254. stdOrrStdout
  255. stdOrrStderr
  256. FilenameOption* = enum
  257. foAbs # absolute path, e.g.: /pathto/bar/foo.nim
  258. foRelProject # relative to project path, e.g.: ../foo.nim
  259. foCanonical # canonical module name
  260. foLegacyRelProj # legacy, shortest of (foAbs, foRelProject)
  261. foName # lastPathPart, e.g.: foo.nim
  262. foStacktrace # if optExcessiveStackTrace: foAbs else: foName
  263. ConfigRef* {.acyclic.} = ref object ## every global configuration
  264. ## fields marked with '*' are subject to
  265. ## the incremental compilation mechanisms
  266. ## (+) means "part of the dependency"
  267. backend*: TBackend # set via `nim x` or `nim --backend:x`
  268. target*: Target # (+)
  269. linesCompiled*: int # all lines that have been compiled
  270. options*: TOptions # (+)
  271. globalOptions*: TGlobalOptions # (+)
  272. macrosToExpand*: StringTableRef
  273. arcToExpand*: StringTableRef
  274. m*: MsgConfig
  275. filenameOption*: FilenameOption # how to render paths in compiler messages
  276. unitSep*: string
  277. evalTemplateCounter*: int
  278. evalMacroCounter*: int
  279. exitcode*: int8
  280. cmd*: Command # raw command parsed as enum
  281. cmdInput*: string # input command
  282. projectIsCmd*: bool # whether we're compiling from a command input
  283. implicitCmd*: bool # whether some flag triggered an implicit `command`
  284. selectedGC*: TGCMode # the selected GC (+)
  285. exc*: ExceptionSystem
  286. hintProcessingDots*: bool # true for dots, false for filenames
  287. verbosity*: int # how verbose the compiler is
  288. numberOfProcessors*: int # number of processors
  289. lastCmdTime*: float # when caas is enabled, we measure each command
  290. symbolFiles*: SymbolFilesOption
  291. spellSuggestMax*: int # max number of spelling suggestions for typos
  292. cppDefines*: HashSet[string] # (*)
  293. headerFile*: string
  294. features*: set[Feature]
  295. legacyFeatures*: set[LegacyFeature]
  296. arguments*: string ## the arguments to be passed to the program that
  297. ## should be run
  298. ideCmd*: IdeCmd
  299. oldNewlines*: bool
  300. cCompiler*: TSystemCC # the used compiler
  301. modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs
  302. cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline
  303. foreignPackageNotes*: TNoteKinds
  304. notes*: TNoteKinds # notes after resolving all logic(defaults, verbosity)/cmdline/configs
  305. warningAsErrors*: TNoteKinds
  306. mainPackageNotes*: TNoteKinds
  307. mainPackageId*: int
  308. errorCounter*: int
  309. hintCounter*: int
  310. warnCounter*: int
  311. errorMax*: int
  312. maxLoopIterationsVM*: int ## VM: max iterations of all loops
  313. isVmTrace*: bool
  314. configVars*: StringTableRef
  315. symbols*: StringTableRef ## We need to use a StringTableRef here as defined
  316. ## symbols are always guaranteed to be style
  317. ## insensitive. Otherwise hell would break lose.
  318. packageCache*: StringTableRef
  319. nimblePaths*: seq[AbsoluteDir]
  320. searchPaths*: seq[AbsoluteDir]
  321. lazyPaths*: seq[AbsoluteDir]
  322. outFile*: RelativeFile
  323. outDir*: AbsoluteDir
  324. jsonBuildFile*: AbsoluteFile
  325. prefixDir*, libpath*, nimcacheDir*: AbsoluteDir
  326. nimStdlibVersion*: NimVer
  327. dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef
  328. projectName*: string # holds a name like 'nim'
  329. projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
  330. projectFull*: AbsoluteFile # projectPath/projectName
  331. projectIsStdin*: bool # whether we're compiling from stdin
  332. lastMsgWasDot*: set[StdOrrKind] # the last compiler message was a single '.'
  333. projectMainIdx*: FileIndex # the canonical path id of the main module
  334. projectMainIdx2*: FileIndex # consider merging with projectMainIdx
  335. command*: string # the main command (e.g. cc, check, scan, etc)
  336. commandArgs*: seq[string] # any arguments after the main command
  337. commandLine*: string
  338. extraCmds*: seq[string] # for writeJsonBuildInstructions
  339. keepComments*: bool # whether the parser needs to keep comments
  340. implicitImports*: seq[string] # modules that are to be implicitly imported
  341. implicitIncludes*: seq[string] # modules that are to be implicitly included
  342. docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
  343. # The string uses the formatting variables `path` and `line`.
  344. docRoot*: string ## see nim --fullhelp for --docRoot
  345. docCmd*: string ## see nim --fullhelp for --docCmd
  346. configFiles*: seq[AbsoluteFile] # config files (cfg,nims)
  347. cIncludes*: seq[AbsoluteDir] # directories to search for included files
  348. cLibs*: seq[AbsoluteDir] # directories to search for lib files
  349. cLinkedLibs*: seq[string] # libraries to link
  350. externalToLink*: seq[string] # files to link in addition to the file
  351. # we compiled (*)
  352. linkOptionsCmd*: string
  353. compileOptionsCmd*: seq[string]
  354. linkOptions*: string # (*)
  355. compileOptions*: string # (*)
  356. cCompilerPath*: string
  357. toCompile*: CfileList # (*)
  358. suggestionResultHook*: proc (result: Suggest) {.closure.}
  359. suggestVersion*: int
  360. suggestMaxResults*: int
  361. lastLineInfo*: TLineInfo
  362. writelnHook*: proc (output: string) {.closure.} # cannot make this gcsafe yet because of Nimble
  363. structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string;
  364. severity: Severity) {.closure, gcsafe.}
  365. cppCustomNamespace*: string
  366. nimMainPrefix*: string
  367. vmProfileData*: ProfileData
  368. proc parseNimVersion*(a: string): NimVer =
  369. # could be moved somewhere reusable
  370. if a.len > 0:
  371. let b = a.split(".")
  372. assert b.len == 3, a
  373. template fn(i) = result[i] = b[i].parseInt # could be optimized if needed
  374. fn(0)
  375. fn(1)
  376. fn(2)
  377. proc assignIfDefault*[T](result: var T, val: T, def = default(T)) =
  378. ## if `result` was already assigned to a value (that wasn't `def`), this is a noop.
  379. if result == def: result = val
  380. template setErrorMaxHighMaybe*(conf: ConfigRef) =
  381. ## do not stop after first error (but honor --errorMax if provided)
  382. assignIfDefault(conf.errorMax, high(int))
  383. proc setNoteDefaults*(conf: ConfigRef, note: TNoteKind, enabled = true) =
  384. template fun(op) =
  385. conf.notes.op note
  386. conf.mainPackageNotes.op note
  387. conf.foreignPackageNotes.op note
  388. if enabled: fun(incl) else: fun(excl)
  389. proc setNote*(conf: ConfigRef, note: TNoteKind, enabled = true) =
  390. # see also `prepareConfigNotes` which sets notes
  391. if note notin conf.cmdlineNotes:
  392. if enabled: incl(conf.notes, note) else: excl(conf.notes, note)
  393. proc hasHint*(conf: ConfigRef, note: TNoteKind): bool =
  394. # ternary states instead of binary states would simplify logic
  395. if optHints notin conf.options: false
  396. elif note in {hintConf, hintProcessing}:
  397. # could add here other special notes like hintSource
  398. # these notes apply globally.
  399. note in conf.mainPackageNotes
  400. else: note in conf.notes
  401. proc hasWarn*(conf: ConfigRef, note: TNoteKind): bool {.inline.} =
  402. optWarns in conf.options and note in conf.notes
  403. proc hcrOn*(conf: ConfigRef): bool = return optHotCodeReloading in conf.globalOptions
  404. when false:
  405. template depConfigFields*(fn) {.dirty.} = # deadcode
  406. fn(target)
  407. fn(options)
  408. fn(globalOptions)
  409. fn(selectedGC)
  410. const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
  411. const
  412. ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
  413. optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck,
  414. optStyleCheck}
  415. DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
  416. optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck,
  417. optHints, optStackTrace, optLineTrace, # consider adding `optStackTraceMsgs`
  418. optTrMacros, optStyleCheck, optCursorInference}
  419. DefaultGlobalOptions* = {optThreadAnalysis, optExcessiveStackTrace}
  420. proc getSrcTimestamp(): DateTime =
  421. try:
  422. result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH",
  423. "not a number"))))
  424. except ValueError:
  425. # Environment variable malformed.
  426. # https://reproducible-builds.org/specs/source-date-epoch/: "If the
  427. # value is malformed, the build process SHOULD exit with a non-zero
  428. # error code", which this doesn't do. This uses local time, because
  429. # that maintains compatibility with existing usage.
  430. result = utc getTime()
  431. proc getDateStr*(): string =
  432. result = format(getSrcTimestamp(), "yyyy-MM-dd")
  433. proc getClockStr*(): string =
  434. result = format(getSrcTimestamp(), "HH:mm:ss")
  435. template newPackageCache*(): untyped =
  436. newStringTable(when FileSystemCaseSensitive:
  437. modeCaseInsensitive
  438. else:
  439. modeCaseSensitive)
  440. proc newProfileData(): ProfileData =
  441. ProfileData(data: newTable[TLineInfo, ProfileInfo]())
  442. const foreignPackageNotesDefault* = {
  443. hintProcessing, warnUnknownMagic, hintQuitCalled, hintExecuting, hintUser, warnUser}
  444. proc isDefined*(conf: ConfigRef; symbol: string): bool
  445. when defined(nimDebugUtils):
  446. # this allows inserting debugging utilties in all modules that import `options`
  447. # with a single switch, which is useful when debugging compiler.
  448. import debugutils
  449. export debugutils
  450. proc initConfigRefCommon(conf: ConfigRef) =
  451. conf.selectedGC = gcRefc
  452. conf.verbosity = 1
  453. conf.hintProcessingDots = true
  454. conf.options = DefaultOptions
  455. conf.globalOptions = DefaultGlobalOptions
  456. conf.filenameOption = foAbs
  457. conf.foreignPackageNotes = foreignPackageNotesDefault
  458. conf.notes = NotesVerbosity[1]
  459. conf.mainPackageNotes = NotesVerbosity[1]
  460. proc newConfigRef*(): ConfigRef =
  461. result = ConfigRef(
  462. cCompiler: ccGcc,
  463. macrosToExpand: newStringTable(modeStyleInsensitive),
  464. arcToExpand: newStringTable(modeStyleInsensitive),
  465. m: initMsgConfig(),
  466. cppDefines: initHashSet[string](),
  467. headerFile: "", features: {}, legacyFeatures: {},
  468. configVars: newStringTable(modeStyleInsensitive),
  469. symbols: newStringTable(modeStyleInsensitive),
  470. packageCache: newPackageCache(),
  471. searchPaths: @[],
  472. lazyPaths: @[],
  473. outFile: RelativeFile"",
  474. outDir: AbsoluteDir"",
  475. prefixDir: AbsoluteDir"",
  476. libpath: AbsoluteDir"", nimcacheDir: AbsoluteDir"",
  477. dllOverrides: newStringTable(modeCaseInsensitive),
  478. moduleOverrides: newStringTable(modeStyleInsensitive),
  479. cfileSpecificOptions: newStringTable(modeCaseSensitive),
  480. projectName: "", # holds a name like 'nim'
  481. projectPath: AbsoluteDir"", # holds a path like /home/alice/projects/nim/compiler/
  482. projectFull: AbsoluteFile"", # projectPath/projectName
  483. projectIsStdin: false, # whether we're compiling from stdin
  484. projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
  485. command: "", # the main command (e.g. cc, check, scan, etc)
  486. commandArgs: @[], # any arguments after the main command
  487. commandLine: "",
  488. keepComments: true, # whether the parser needs to keep comments
  489. implicitImports: @[], # modules that are to be implicitly imported
  490. implicitIncludes: @[], # modules that are to be implicitly included
  491. docSeeSrcUrl: "",
  492. cIncludes: @[], # directories to search for included files
  493. cLibs: @[], # directories to search for lib files
  494. cLinkedLibs: @[], # libraries to link
  495. backend: backendInvalid,
  496. externalToLink: @[],
  497. linkOptionsCmd: "",
  498. compileOptionsCmd: @[],
  499. linkOptions: "",
  500. compileOptions: "",
  501. ccompilerpath: "",
  502. toCompile: @[],
  503. arguments: "",
  504. suggestMaxResults: 10_000,
  505. maxLoopIterationsVM: 10_000_000,
  506. vmProfileData: newProfileData(),
  507. spellSuggestMax: spellSuggestSecretSauce,
  508. )
  509. initConfigRefCommon(result)
  510. setTargetFromSystem(result.target)
  511. # enable colors by default on terminals
  512. if terminal.isatty(stderr):
  513. incl(result.globalOptions, optUseColors)
  514. when defined(nimDebugUtils):
  515. onNewConfigRef(result)
  516. proc newPartialConfigRef*(): ConfigRef =
  517. ## create a new ConfigRef that is only good enough for error reporting.
  518. when defined(nimDebugUtils):
  519. result = getConfigRef()
  520. else:
  521. result = ConfigRef()
  522. initConfigRefCommon(result)
  523. proc cppDefine*(c: ConfigRef; define: string) =
  524. c.cppDefines.incl define
  525. proc getStdlibVersion*(conf: ConfigRef): NimVer =
  526. if conf.nimStdlibVersion == (0,0,0):
  527. let s = conf.symbols.getOrDefault("nimVersion", "")
  528. conf.nimStdlibVersion = s.parseNimVersion
  529. result = conf.nimStdlibVersion
  530. proc isDefined*(conf: ConfigRef; symbol: string): bool =
  531. if conf.symbols.hasKey(symbol):
  532. result = true
  533. elif cmpIgnoreStyle(symbol, CPU[conf.target.targetCPU].name) == 0:
  534. result = true
  535. elif cmpIgnoreStyle(symbol, platform.OS[conf.target.targetOS].name) == 0:
  536. result = true
  537. else:
  538. case symbol.normalize
  539. of "x86": result = conf.target.targetCPU == cpuI386
  540. of "itanium": result = conf.target.targetCPU == cpuIa64
  541. of "x8664": result = conf.target.targetCPU == cpuAmd64
  542. of "posix", "unix":
  543. result = conf.target.targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
  544. osQnx, osAtari, osAix,
  545. osHaiku, osVxWorks, osSolaris, osNetbsd,
  546. osFreebsd, osOpenbsd, osDragonfly, osMacosx, osIos,
  547. osAndroid, osNintendoSwitch, osFreeRTOS, osCrossos, osZephyr}
  548. of "linux":
  549. result = conf.target.targetOS in {osLinux, osAndroid}
  550. of "bsd":
  551. result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osCrossos}
  552. of "freebsd":
  553. result = conf.target.targetOS in {osFreebsd, osCrossos}
  554. of "emulatedthreadvars":
  555. result = platform.OS[conf.target.targetOS].props.contains(ospLacksThreadVars)
  556. of "msdos": result = conf.target.targetOS == osDos
  557. of "mswindows", "win32": result = conf.target.targetOS == osWindows
  558. of "macintosh":
  559. result = conf.target.targetOS in {osMacos, osMacosx, osIos}
  560. of "osx", "macosx":
  561. result = conf.target.targetOS in {osMacosx, osIos}
  562. of "sunos": result = conf.target.targetOS == osSolaris
  563. of "nintendoswitch":
  564. result = conf.target.targetOS == osNintendoSwitch
  565. of "freertos", "lwip":
  566. result = conf.target.targetOS == osFreeRTOS
  567. of "zephyr":
  568. result = conf.target.targetOS == osZephyr
  569. of "littleendian": result = CPU[conf.target.targetCPU].endian == littleEndian
  570. of "bigendian": result = CPU[conf.target.targetCPU].endian == bigEndian
  571. of "cpu8": result = CPU[conf.target.targetCPU].bit == 8
  572. of "cpu16": result = CPU[conf.target.targetCPU].bit == 16
  573. of "cpu32": result = CPU[conf.target.targetCPU].bit == 32
  574. of "cpu64": result = CPU[conf.target.targetCPU].bit == 64
  575. of "nimrawsetjmp":
  576. result = conf.target.targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
  577. osDragonfly, osMacosx}
  578. else: discard
  579. template quitOrRaise*(conf: ConfigRef, msg = "") =
  580. # xxx in future work, consider whether to also intercept `msgQuit` calls
  581. if conf.isDefined("nimDebug"):
  582. doAssert false, msg
  583. else:
  584. quit(msg) # quits with QuitFailure
  585. proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in cmdDocLike + {cmdIdeTools}
  586. proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
  587. template compilationCachePresent*(conf: ConfigRef): untyped =
  588. false
  589. # conf.symbolFiles in {v2Sf, writeOnlySf}
  590. template optPreserveOrigSource*(conf: ConfigRef): untyped =
  591. optEmbedOrigSrc in conf.globalOptions
  592. proc mainCommandArg*(conf: ConfigRef): string =
  593. ## This is intended for commands like check or parse
  594. ## which will work on the main project file unless
  595. ## explicitly given a specific file argument
  596. if conf.commandArgs.len > 0:
  597. result = conf.commandArgs[0]
  598. else:
  599. result = conf.projectName
  600. proc existsConfigVar*(conf: ConfigRef; key: string): bool =
  601. result = hasKey(conf.configVars, key)
  602. proc getConfigVar*(conf: ConfigRef; key: string, default = ""): string =
  603. result = conf.configVars.getOrDefault(key, default)
  604. proc setConfigVar*(conf: ConfigRef; key, val: string) =
  605. conf.configVars[key] = val
  606. proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): AbsoluteFile =
  607. # explains regression https://github.com/nim-lang/Nim/issues/6583#issuecomment-625711125
  608. # Yet another reason why "" should not mean "."; `""/something` should raise
  609. # instead of implying "" == "." as it's bug prone.
  610. doAssert conf.outDir.string.len > 0
  611. result = conf.outDir / changeFileExt(filename, ext)
  612. proc absOutFile*(conf: ConfigRef): AbsoluteFile =
  613. doAssert not conf.outDir.isEmpty
  614. doAssert not conf.outFile.isEmpty
  615. result = conf.outDir / conf.outFile
  616. when defined(posix):
  617. if dirExists(result.string): result.string.add ".out"
  618. proc prepareToWriteOutput*(conf: ConfigRef): AbsoluteFile =
  619. ## Create the output directory and returns a full path to the output file
  620. result = conf.absOutFile
  621. createDir result.string.parentDir
  622. proc getPrefixDir*(conf: ConfigRef): AbsoluteDir =
  623. ## Gets the prefix dir, usually the parent directory where the binary resides.
  624. ##
  625. ## This is overridden by some tools (namely nimsuggest) via the ``conf.prefixDir``
  626. ## field.
  627. ## This should resolve to root of nim sources, whether running nim from a local
  628. ## clone or using installed nim, so that these exist: `result/doc/advopt.txt`
  629. ## and `result/lib/system.nim`
  630. if not conf.prefixDir.isEmpty: result = conf.prefixDir
  631. else: result = AbsoluteDir splitPath(getAppDir()).head
  632. proc setDefaultLibpath*(conf: ConfigRef) =
  633. # set default value (can be overwritten):
  634. if conf.libpath.isEmpty:
  635. # choose default libpath:
  636. var prefix = getPrefixDir(conf)
  637. when defined(posix):
  638. if prefix == AbsoluteDir"/usr":
  639. conf.libpath = AbsoluteDir"/usr/lib/nim"
  640. elif prefix == AbsoluteDir"/usr/local":
  641. conf.libpath = AbsoluteDir"/usr/local/lib/nim"
  642. else:
  643. conf.libpath = prefix / RelativeDir"lib"
  644. else:
  645. conf.libpath = prefix / RelativeDir"lib"
  646. # Special rule to support other tools (nimble) which import the compiler
  647. # modules and make use of them.
  648. let realNimPath = findExe("nim")
  649. # Find out if $nim/../../lib/system.nim exists.
  650. let parentNimLibPath = realNimPath.parentDir.parentDir / "lib"
  651. if not fileExists(conf.libpath.string / "system.nim") and
  652. fileExists(parentNimLibPath / "system.nim"):
  653. conf.libpath = AbsoluteDir parentNimLibPath
  654. proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
  655. result = AbsoluteFile path.string.expandFilename
  656. proc setFromProjectName*(conf: ConfigRef; projectName: string) =
  657. try:
  658. conf.projectFull = canonicalizePath(conf, AbsoluteFile projectName)
  659. except OSError:
  660. conf.projectFull = AbsoluteFile projectName
  661. let p = splitFile(conf.projectFull)
  662. let dir = if p.dir.isEmpty: AbsoluteDir getCurrentDir() else: p.dir
  663. conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile dir)
  664. conf.projectName = p.name
  665. proc removeTrailingDirSep*(path: string): string =
  666. if (path.len > 0) and (path[^1] == DirSep):
  667. result = substr(path, 0, path.len - 2)
  668. else:
  669. result = path
  670. proc disableNimblePath*(conf: ConfigRef) =
  671. incl conf.globalOptions, optNoNimblePath
  672. conf.lazyPaths.setLen(0)
  673. conf.nimblePaths.setLen(0)
  674. proc clearNimblePath*(conf: ConfigRef) =
  675. conf.lazyPaths.setLen(0)
  676. conf.nimblePaths.setLen(0)
  677. include packagehandling
  678. proc getOsCacheDir(): string =
  679. when defined(posix):
  680. result = getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") / "nim"
  681. else:
  682. result = getHomeDir() / genSubDir.string
  683. proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir =
  684. proc nimcacheSuffix(conf: ConfigRef): string =
  685. if conf.cmd == cmdCheck: "_check"
  686. elif isDefined(conf, "release") or isDefined(conf, "danger"): "_r"
  687. else: "_d"
  688. # XXX projectName should always be without a file extension!
  689. result = if not conf.nimcacheDir.isEmpty:
  690. conf.nimcacheDir
  691. elif conf.backend == backendJs:
  692. if conf.outDir.isEmpty:
  693. conf.projectPath / genSubDir
  694. else:
  695. conf.outDir / genSubDir
  696. else:
  697. AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name &
  698. nimcacheSuffix(conf))
  699. proc pathSubs*(conf: ConfigRef; p, config: string): string =
  700. let home = removeTrailingDirSep(os.getHomeDir())
  701. result = unixToNativePath(p % [
  702. "nim", getPrefixDir(conf).string,
  703. "lib", conf.libpath.string,
  704. "home", home,
  705. "config", config,
  706. "projectname", conf.projectName,
  707. "projectpath", conf.projectPath.string,
  708. "projectdir", conf.projectPath.string,
  709. "nimcache", getNimcacheDir(conf).string]).expandTilde
  710. iterator nimbleSubs*(conf: ConfigRef; p: string): string =
  711. let pl = p.toLowerAscii
  712. if "$nimblepath" in pl or "$nimbledir" in pl:
  713. for i in countdown(conf.nimblePaths.len-1, 0):
  714. let nimblePath = removeTrailingDirSep(conf.nimblePaths[i].string)
  715. yield p % ["nimblepath", nimblePath, "nimbledir", nimblePath]
  716. else:
  717. yield p
  718. proc toGeneratedFile*(conf: ConfigRef; path: AbsoluteFile,
  719. ext: string): AbsoluteFile =
  720. ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
  721. result = getNimcacheDir(conf) / RelativeFile path.string.splitPath.tail.changeFileExt(ext)
  722. proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile,
  723. createSubDir: bool = true): AbsoluteFile =
  724. ## Return an absolute path of a generated intermediary file.
  725. ## Optionally creates the cache directory if `createSubDir` is `true`.
  726. let subdir = getNimcacheDir(conf)
  727. if createSubDir:
  728. try:
  729. createDir(subdir.string)
  730. except OSError:
  731. conf.quitOrRaise "cannot create directory: " & subdir.string
  732. result = subdir / RelativeFile f.string.splitPath.tail
  733. proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile =
  734. for it in conf.searchPaths:
  735. if suppressStdlib and it.string.startsWith(conf.libpath.string):
  736. continue
  737. result = it / f
  738. if fileExists(result):
  739. return canonicalizePath(conf, result)
  740. result = AbsoluteFile""
  741. proc rawFindFile2(conf: ConfigRef; f: RelativeFile): AbsoluteFile =
  742. for i, it in conf.lazyPaths:
  743. result = it / f
  744. if fileExists(result):
  745. # bring to front
  746. for j in countdown(i, 1):
  747. swap(conf.lazyPaths[j], conf.lazyPaths[j-1])
  748. return canonicalizePath(conf, result)
  749. result = AbsoluteFile""
  750. template patchModule(conf: ConfigRef) {.dirty.} =
  751. if not result.isEmpty and conf.moduleOverrides.len > 0:
  752. let key = getPackageName(conf, result.string) & "_" & splitFile(result).name
  753. if conf.moduleOverrides.hasKey(key):
  754. let ov = conf.moduleOverrides[key]
  755. if ov.len > 0: result = AbsoluteFile(ov)
  756. when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo):
  757. proc isRelativeTo(path, base: string): bool =
  758. # pending #13212 use os.isRelativeTo
  759. let path = path.normalizedPath
  760. let base = base.normalizedPath
  761. let ret = relativePath(path, base)
  762. result = path.len > 0 and not ret.startsWith ".."
  763. const stdlibDirs* = [
  764. "pure", "core", "arch",
  765. "pure/collections",
  766. "pure/concurrency",
  767. "pure/unidecode", "impure",
  768. "wrappers", "wrappers/linenoise",
  769. "windows", "posix", "js"]
  770. const
  771. pkgPrefix = "pkg/"
  772. stdPrefix = "std/"
  773. proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile, isTitle = false): RelativeFile =
  774. let f = $f
  775. if isTitle:
  776. for dir in stdlibDirs:
  777. let path = conf.libpath.string / dir / f.lastPathPart
  778. if path.cmpPaths(f) == 0:
  779. return RelativeFile(stdPrefix & f.splitFile.name)
  780. template search(paths) =
  781. for it in paths:
  782. let it = $it
  783. if f.isRelativeTo(it):
  784. return relativePath(f, it).RelativeFile
  785. search(conf.searchPaths)
  786. search(conf.lazyPaths)
  787. proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile =
  788. if f.isAbsolute:
  789. result = if f.fileExists: AbsoluteFile(f) else: AbsoluteFile""
  790. else:
  791. result = rawFindFile(conf, RelativeFile f, suppressStdlib)
  792. if result.isEmpty:
  793. result = rawFindFile(conf, RelativeFile f.toLowerAscii, suppressStdlib)
  794. if result.isEmpty:
  795. result = rawFindFile2(conf, RelativeFile f)
  796. if result.isEmpty:
  797. result = rawFindFile2(conf, RelativeFile f.toLowerAscii)
  798. patchModule(conf)
  799. proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFile =
  800. # returns path to module
  801. var m = addFileExt(modulename, NimExt)
  802. if m.startsWith(pkgPrefix):
  803. result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true)
  804. else:
  805. if m.startsWith(stdPrefix):
  806. let stripped = m.substr(stdPrefix.len)
  807. for candidate in stdlibDirs:
  808. let path = (conf.libpath.string / candidate / stripped)
  809. if fileExists(path):
  810. result = AbsoluteFile path
  811. break
  812. else: # If prefixed with std/ why would we add the current module path!
  813. let currentPath = currentModule.splitFile.dir
  814. result = AbsoluteFile currentPath / m
  815. if not fileExists(result):
  816. result = findFile(conf, m)
  817. patchModule(conf)
  818. proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
  819. const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
  820. var
  821. candidates: seq[string] = @[]
  822. dir = pkg
  823. prev = dir
  824. nimblepkg = ""
  825. let pkgname = pkg.lastPathPart()
  826. while true:
  827. for k, f in os.walkDir(dir, relative = true):
  828. if k == pcFile and f != "config.nims":
  829. let (_, name, ext) = splitFile(f)
  830. if ext in extensions:
  831. let x = changeFileExt(dir / name, ".nim")
  832. if fileExists(x):
  833. candidates.add x
  834. if ext == ".nimble":
  835. if nimblepkg.len == 0:
  836. nimblepkg = name
  837. # Since nimble packages can have their source in a subfolder,
  838. # check the last folder we were in for a possible match.
  839. if dir != prev:
  840. let x = prev / x.extractFilename()
  841. if fileExists(x):
  842. candidates.add x
  843. else:
  844. # If we found more than one nimble file, chances are that we
  845. # missed the real project file, or this is an invalid nimble
  846. # package. Either way, bailing is the better choice.
  847. return ""
  848. let pkgname = if nimblepkg.len > 0: nimblepkg else: pkgname
  849. for c in candidates:
  850. if pkgname in c.extractFilename(): return c
  851. if candidates.len > 0:
  852. return candidates[0]
  853. prev = dir
  854. dir = parentDir(dir)
  855. if dir == "": break
  856. return ""
  857. proc canonicalImportAux*(conf: ConfigRef, file: AbsoluteFile): string =
  858. ##[
  859. Shows the canonical module import, e.g.:
  860. system, std/tables, fusion/pointers, system/assertions, std/private/asciitables
  861. ]##
  862. var ret = getRelativePathFromConfigPath(conf, file, isTitle = true)
  863. let dir = getNimbleFile(conf, $file).parentDir.AbsoluteDir
  864. if not dir.isEmpty:
  865. let relPath = relativeTo(file, dir)
  866. if not relPath.isEmpty and (ret.isEmpty or relPath.string.len < ret.string.len):
  867. ret = relPath
  868. if ret.isEmpty:
  869. ret = relativeTo(file, conf.projectPath)
  870. result = ret.string
  871. proc canonicalImport*(conf: ConfigRef, file: AbsoluteFile): string =
  872. let ret = canonicalImportAux(conf, file)
  873. result = ret.nativeToUnixPath.changeFileExt("")
  874. proc canonDynlibName(s: string): string =
  875. let start = if s.startsWith("lib"): 3 else: 0
  876. let ende = strutils.find(s, {'(', ')', '.'})
  877. if ende >= 0:
  878. result = s.substr(start, ende-1)
  879. else:
  880. result = s.substr(start)
  881. proc inclDynlibOverride*(conf: ConfigRef; lib: string) =
  882. conf.dllOverrides[lib.canonDynlibName] = "true"
  883. proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
  884. result = optDynlibOverrideAll in conf.globalOptions or
  885. conf.dllOverrides.hasKey(lib.canonDynlibName)
  886. proc parseIdeCmd*(s: string): IdeCmd =
  887. case s:
  888. of "sug": ideSug
  889. of "con": ideCon
  890. of "def": ideDef
  891. of "use": ideUse
  892. of "dus": ideDus
  893. of "chk": ideChk
  894. of "chkFile": ideChkFile
  895. of "mod": ideMod
  896. of "highlight": ideHighlight
  897. of "outline": ideOutline
  898. of "known": ideKnown
  899. of "msg": ideMsg
  900. of "project": ideProject
  901. of "globalSymbols": ideGlobalSymbols
  902. of "recompile": ideRecompile
  903. of "changed": ideChanged
  904. of "type": ideType
  905. else: ideNone
  906. proc `$`*(c: IdeCmd): string =
  907. case c:
  908. of ideSug: "sug"
  909. of ideCon: "con"
  910. of ideDef: "def"
  911. of ideUse: "use"
  912. of ideDus: "dus"
  913. of ideChk: "chk"
  914. of ideChkFile: "chkFile"
  915. of ideMod: "mod"
  916. of ideNone: "none"
  917. of ideHighlight: "highlight"
  918. of ideOutline: "outline"
  919. of ideKnown: "known"
  920. of ideMsg: "msg"
  921. of ideProject: "project"
  922. of ideGlobalSymbols: "globalSymbols"
  923. of ideRecompile: "recompile"
  924. of ideChanged: "changed"
  925. of ideType: "type"
  926. proc floatInt64Align*(conf: ConfigRef): int16 =
  927. ## Returns either 4 or 8 depending on reasons.
  928. if conf != nil and conf.target.targetCPU == cpuI386:
  929. #on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double)
  930. if conf.target.targetOS != osWindows:
  931. # on i386 for all known POSIX systems, 64bits ints are aligned
  932. # to 4bytes (except with -malign-double)
  933. return 4
  934. return 8