options.nim 35 KB

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