options.nim 35 KB

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