options.nim 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905
  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. cmdInput*: string # input command
  228. projectIsCmd*: bool # whether we're compiling from a command input
  229. implicitCmd*: bool # whether some flag triggered an implicit `command`
  230. selectedGC*: TGCMode # the selected GC (+)
  231. exc*: ExceptionSystem
  232. verbosity*: int # how verbose the compiler is
  233. numberOfProcessors*: int # number of processors
  234. lastCmdTime*: float # when caas is enabled, we measure each command
  235. symbolFiles*: SymbolFilesOption
  236. cppDefines*: HashSet[string] # (*)
  237. headerFile*: string
  238. features*: set[Feature]
  239. legacyFeatures*: set[LegacyFeature]
  240. arguments*: string ## the arguments to be passed to the program that
  241. ## should be run
  242. ideCmd*: IdeCmd
  243. oldNewlines*: bool
  244. cCompiler*: TSystemCC
  245. modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs
  246. cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline
  247. foreignPackageNotes*: TNoteKinds
  248. notes*: TNoteKinds # notes after resolving all logic(defaults, verbosity)/cmdline/configs
  249. warningAsErrors*: TNoteKinds
  250. mainPackageNotes*: TNoteKinds
  251. mainPackageId*: int
  252. errorCounter*: int
  253. hintCounter*: int
  254. warnCounter*: int
  255. errorMax*: int
  256. maxLoopIterationsVM*: int ## VM: max iterations of all loops
  257. configVars*: StringTableRef
  258. symbols*: StringTableRef ## We need to use a StringTableRef here as defined
  259. ## symbols are always guaranteed to be style
  260. ## insensitive. Otherwise hell would break lose.
  261. packageCache*: StringTableRef
  262. nimblePaths*: seq[AbsoluteDir]
  263. searchPaths*: seq[AbsoluteDir]
  264. lazyPaths*: seq[AbsoluteDir]
  265. outFile*: RelativeFile
  266. outDir*: AbsoluteDir
  267. jsonBuildFile*: AbsoluteFile
  268. prefixDir*, libpath*, nimcacheDir*: AbsoluteDir
  269. dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef
  270. projectName*: string # holds a name like 'nim'
  271. projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
  272. projectFull*: AbsoluteFile # projectPath/projectName
  273. projectIsStdin*: bool # whether we're compiling from stdin
  274. lastMsgWasDot*: bool # the last compiler message was a single '.'
  275. projectMainIdx*: FileIndex # the canonical path id of the main module
  276. projectMainIdx2*: FileIndex # consider merging with projectMainIdx
  277. command*: string # the main command (e.g. cc, check, scan, etc)
  278. commandArgs*: seq[string] # any arguments after the main command
  279. commandLine*: string
  280. extraCmds*: seq[string] # for writeJsonBuildInstructions
  281. keepComments*: bool # whether the parser needs to keep comments
  282. implicitImports*: seq[string] # modules that are to be implicitly imported
  283. implicitIncludes*: seq[string] # modules that are to be implicitly included
  284. docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
  285. # The string uses the formatting variables `path` and `line`.
  286. docRoot*: string ## see nim --fullhelp for --docRoot
  287. docCmd*: string ## see nim --fullhelp for --docCmd
  288. # the used compiler
  289. cIncludes*: seq[AbsoluteDir] # directories to search for included files
  290. cLibs*: seq[AbsoluteDir] # directories to search for lib files
  291. cLinkedLibs*: seq[string] # libraries to link
  292. externalToLink*: seq[string] # files to link in addition to the file
  293. # we compiled (*)
  294. linkOptionsCmd*: string
  295. compileOptionsCmd*: seq[string]
  296. linkOptions*: string # (*)
  297. compileOptions*: string # (*)
  298. cCompilerPath*: string
  299. toCompile*: CfileList # (*)
  300. suggestionResultHook*: proc (result: Suggest) {.closure.}
  301. suggestVersion*: int
  302. suggestMaxResults*: int
  303. lastLineInfo*: TLineInfo
  304. writelnHook*: proc (output: string) {.closure.} # cannot make this gcsafe yet because of Nimble
  305. structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string;
  306. severity: Severity) {.closure, gcsafe.}
  307. cppCustomNamespace*: string
  308. vmProfileData*: ProfileData
  309. proc assignIfDefault*[T](result: var T, val: T, def = default(T)) =
  310. ## if `result` was already assigned to a value (that wasn't `def`), this is a noop.
  311. if result == def: result = val
  312. template setErrorMaxHighMaybe*(conf: ConfigRef) =
  313. ## do not stop after first error (but honor --errorMax if provided)
  314. assignIfDefault(conf.errorMax, high(int))
  315. proc setNoteDefaults*(conf: ConfigRef, note: TNoteKind, enabled = true) =
  316. template fun(op) =
  317. conf.notes.op note
  318. conf.mainPackageNotes.op note
  319. conf.foreignPackageNotes.op note
  320. if enabled: fun(incl) else: fun(excl)
  321. proc setNote*(conf: ConfigRef, note: TNoteKind, enabled = true) =
  322. # see also `prepareConfigNotes` which sets notes
  323. if note notin conf.cmdlineNotes:
  324. if enabled: incl(conf.notes, note) else: excl(conf.notes, note)
  325. proc hasHint*(conf: ConfigRef, note: TNoteKind): bool =
  326. if optHints notin conf.options: false
  327. elif note in {hintConf}: # could add here other special notes like hintSource
  328. note in conf.mainPackageNotes
  329. else: note in conf.notes
  330. proc hasWarn*(conf: ConfigRef, note: TNoteKind): bool =
  331. optWarns in conf.options and note in conf.notes
  332. proc hcrOn*(conf: ConfigRef): bool = return optHotCodeReloading in conf.globalOptions
  333. template depConfigFields*(fn) {.dirty.} =
  334. fn(target)
  335. fn(options)
  336. fn(globalOptions)
  337. fn(selectedGC)
  338. const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
  339. const
  340. ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
  341. optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck,
  342. optStyleCheck}
  343. DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
  344. optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck,
  345. optHints, optStackTrace, optLineTrace, # consider adding `optStackTraceMsgs`
  346. optTrMacros, optStyleCheck, optCursorInference}
  347. DefaultGlobalOptions* = {optThreadAnalysis,
  348. optExcessiveStackTrace, optListFullPaths}
  349. proc getSrcTimestamp(): DateTime =
  350. try:
  351. result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH",
  352. "not a number"))))
  353. except ValueError:
  354. # Environment variable malformed.
  355. # https://reproducible-builds.org/specs/source-date-epoch/: "If the
  356. # value is malformed, the build process SHOULD exit with a non-zero
  357. # error code", which this doesn't do. This uses local time, because
  358. # that maintains compatibility with existing usage.
  359. result = utc getTime()
  360. proc getDateStr*(): string =
  361. result = format(getSrcTimestamp(), "yyyy-MM-dd")
  362. proc getClockStr*(): string =
  363. result = format(getSrcTimestamp(), "HH:mm:ss")
  364. template newPackageCache*(): untyped =
  365. newStringTable(when FileSystemCaseSensitive:
  366. modeCaseInsensitive
  367. else:
  368. modeCaseSensitive)
  369. proc newProfileData(): ProfileData =
  370. ProfileData(data: newTable[TLineInfo, ProfileInfo]())
  371. proc newConfigRef*(): ConfigRef =
  372. result = ConfigRef(
  373. selectedGC: gcRefc,
  374. cCompiler: ccGcc,
  375. verbosity: 1,
  376. options: DefaultOptions,
  377. globalOptions: DefaultGlobalOptions,
  378. macrosToExpand: newStringTable(modeStyleInsensitive),
  379. arcToExpand: newStringTable(modeStyleInsensitive),
  380. m: initMsgConfig(),
  381. cppDefines: initHashSet[string](),
  382. headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
  383. hintQuitCalled, hintExecuting},
  384. notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1],
  385. configVars: newStringTable(modeStyleInsensitive),
  386. symbols: newStringTable(modeStyleInsensitive),
  387. packageCache: newPackageCache(),
  388. searchPaths: @[],
  389. lazyPaths: @[],
  390. outFile: RelativeFile"",
  391. outDir: AbsoluteDir"",
  392. prefixDir: AbsoluteDir"",
  393. libpath: AbsoluteDir"", nimcacheDir: AbsoluteDir"",
  394. dllOverrides: newStringTable(modeCaseInsensitive),
  395. moduleOverrides: newStringTable(modeStyleInsensitive),
  396. cfileSpecificOptions: newStringTable(modeCaseSensitive),
  397. projectName: "", # holds a name like 'nim'
  398. projectPath: AbsoluteDir"", # holds a path like /home/alice/projects/nim/compiler/
  399. projectFull: AbsoluteFile"", # projectPath/projectName
  400. projectIsStdin: false, # whether we're compiling from stdin
  401. projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
  402. command: "", # the main command (e.g. cc, check, scan, etc)
  403. commandArgs: @[], # any arguments after the main command
  404. commandLine: "",
  405. keepComments: true, # whether the parser needs to keep comments
  406. implicitImports: @[], # modules that are to be implicitly imported
  407. implicitIncludes: @[], # modules that are to be implicitly included
  408. docSeeSrcUrl: "",
  409. cIncludes: @[], # directories to search for included files
  410. cLibs: @[], # directories to search for lib files
  411. cLinkedLibs: @[], # libraries to link
  412. backend: backendInvalid,
  413. externalToLink: @[],
  414. linkOptionsCmd: "",
  415. compileOptionsCmd: @[],
  416. linkOptions: "",
  417. compileOptions: "",
  418. ccompilerpath: "",
  419. toCompile: @[],
  420. arguments: "",
  421. suggestMaxResults: 10_000,
  422. maxLoopIterationsVM: 10_000_000,
  423. vmProfileData: newProfileData(),
  424. )
  425. setTargetFromSystem(result.target)
  426. # enable colors by default on terminals
  427. if terminal.isatty(stderr):
  428. incl(result.globalOptions, optUseColors)
  429. proc newPartialConfigRef*(): ConfigRef =
  430. ## create a new ConfigRef that is only good enough for error reporting.
  431. result = ConfigRef(
  432. selectedGC: gcRefc,
  433. verbosity: 1,
  434. options: DefaultOptions,
  435. globalOptions: DefaultGlobalOptions,
  436. foreignPackageNotes: {hintProcessing, warnUnknownMagic,
  437. hintQuitCalled, hintExecuting},
  438. notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1])
  439. proc cppDefine*(c: ConfigRef; define: string) =
  440. c.cppDefines.incl define
  441. proc isDefined*(conf: ConfigRef; symbol: string): bool =
  442. if conf.symbols.hasKey(symbol):
  443. result = true
  444. elif cmpIgnoreStyle(symbol, CPU[conf.target.targetCPU].name) == 0:
  445. result = true
  446. elif cmpIgnoreStyle(symbol, platform.OS[conf.target.targetOS].name) == 0:
  447. result = true
  448. else:
  449. case symbol.normalize
  450. of "x86": result = conf.target.targetCPU == cpuI386
  451. of "itanium": result = conf.target.targetCPU == cpuIa64
  452. of "x8664": result = conf.target.targetCPU == cpuAmd64
  453. of "posix", "unix":
  454. result = conf.target.targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
  455. osQnx, osAtari, osAix,
  456. osHaiku, osVxWorks, osSolaris, osNetbsd,
  457. osFreebsd, osOpenbsd, osDragonfly, osMacosx, osIos,
  458. osAndroid, osNintendoSwitch, osFreeRTOS}
  459. of "linux":
  460. result = conf.target.targetOS in {osLinux, osAndroid}
  461. of "bsd":
  462. result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
  463. of "emulatedthreadvars":
  464. result = platform.OS[conf.target.targetOS].props.contains(ospLacksThreadVars)
  465. of "msdos": result = conf.target.targetOS == osDos
  466. of "mswindows", "win32": result = conf.target.targetOS == osWindows
  467. of "macintosh":
  468. result = conf.target.targetOS in {osMacos, osMacosx, osIos}
  469. of "osx", "macosx":
  470. result = conf.target.targetOS in {osMacosx, osIos}
  471. of "sunos": result = conf.target.targetOS == osSolaris
  472. of "nintendoswitch":
  473. result = conf.target.targetOS == osNintendoSwitch
  474. of "freertos":
  475. result = conf.target.targetOS == osFreeRTOS
  476. of "lwip":
  477. result = conf.target.targetOS in {osFreeRTOS}
  478. of "littleendian": result = CPU[conf.target.targetCPU].endian == platform.littleEndian
  479. of "bigendian": result = CPU[conf.target.targetCPU].endian == platform.bigEndian
  480. of "cpu8": result = CPU[conf.target.targetCPU].bit == 8
  481. of "cpu16": result = CPU[conf.target.targetCPU].bit == 16
  482. of "cpu32": result = CPU[conf.target.targetCPU].bit == 32
  483. of "cpu64": result = CPU[conf.target.targetCPU].bit == 64
  484. of "nimrawsetjmp":
  485. result = conf.target.targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
  486. osDragonfly, osMacosx}
  487. else: discard
  488. proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools}
  489. proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
  490. template compilationCachePresent*(conf: ConfigRef): untyped =
  491. false
  492. # conf.symbolFiles in {v2Sf, writeOnlySf}
  493. template optPreserveOrigSource*(conf: ConfigRef): untyped =
  494. optEmbedOrigSrc in conf.globalOptions
  495. const
  496. genSubDir* = RelativeDir"nimcache"
  497. NimExt* = "nim"
  498. RodExt* = "rod"
  499. HtmlExt* = "html"
  500. JsonExt* = "json"
  501. TagsExt* = "tags"
  502. TexExt* = "tex"
  503. IniExt* = "ini"
  504. DefaultConfig* = RelativeFile"nim.cfg"
  505. DefaultConfigNims* = RelativeFile"config.nims"
  506. DocConfig* = RelativeFile"nimdoc.cfg"
  507. DocTexConfig* = RelativeFile"nimdoc.tex.cfg"
  508. htmldocsDir* = htmldocsDirname.RelativeDir
  509. docRootDefault* = "@default" # using `@` instead of `$` to avoid shell quoting complications
  510. oKeepVariableNames* = true
  511. proc mainCommandArg*(conf: ConfigRef): string =
  512. ## This is intended for commands like check or parse
  513. ## which will work on the main project file unless
  514. ## explicitly given a specific file argument
  515. if conf.commandArgs.len > 0:
  516. result = conf.commandArgs[0]
  517. else:
  518. result = conf.projectName
  519. proc existsConfigVar*(conf: ConfigRef; key: string): bool =
  520. result = hasKey(conf.configVars, key)
  521. proc getConfigVar*(conf: ConfigRef; key: string, default = ""): string =
  522. result = conf.configVars.getOrDefault(key, default)
  523. proc setConfigVar*(conf: ConfigRef; key, val: string) =
  524. conf.configVars[key] = val
  525. proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): AbsoluteFile =
  526. # explains regression https://github.com/nim-lang/Nim/issues/6583#issuecomment-625711125
  527. # Yet another reason why "" should not mean "."; `""/something` should raise
  528. # instead of implying "" == "." as it's bug prone.
  529. doAssert conf.outDir.string.len > 0
  530. result = conf.outDir / changeFileExt(filename, ext)
  531. proc absOutFile*(conf: ConfigRef): AbsoluteFile =
  532. doAssert not conf.outDir.isEmpty
  533. doAssert not conf.outFile.isEmpty
  534. result = conf.outDir / conf.outFile
  535. when defined(posix):
  536. if dirExists(result.string): result.string.add ".out"
  537. proc prepareToWriteOutput*(conf: ConfigRef): AbsoluteFile =
  538. ## Create the output directory and returns a full path to the output file
  539. result = conf.absOutFile
  540. createDir result.string.parentDir
  541. proc getPrefixDir*(conf: ConfigRef): AbsoluteDir =
  542. ## Gets the prefix dir, usually the parent directory where the binary resides.
  543. ##
  544. ## This is overridden by some tools (namely nimsuggest) via the ``conf.prefixDir``
  545. ## field.
  546. ## This should resolve to root of nim sources, whether running nim from a local
  547. ## clone or using installed nim, so that these exist: `result/doc/advopt.txt`
  548. ## and `result/lib/system.nim`
  549. if not conf.prefixDir.isEmpty: result = conf.prefixDir
  550. else: result = AbsoluteDir splitPath(getAppDir()).head
  551. proc setDefaultLibpath*(conf: ConfigRef) =
  552. # set default value (can be overwritten):
  553. if conf.libpath.isEmpty:
  554. # choose default libpath:
  555. var prefix = getPrefixDir(conf)
  556. when defined(posix):
  557. if prefix == AbsoluteDir"/usr":
  558. conf.libpath = AbsoluteDir"/usr/lib/nim"
  559. elif prefix == AbsoluteDir"/usr/local":
  560. conf.libpath = AbsoluteDir"/usr/local/lib/nim"
  561. else:
  562. conf.libpath = prefix / RelativeDir"lib"
  563. else:
  564. conf.libpath = prefix / RelativeDir"lib"
  565. # Special rule to support other tools (nimble) which import the compiler
  566. # modules and make use of them.
  567. let realNimPath = findExe("nim")
  568. # Find out if $nim/../../lib/system.nim exists.
  569. let parentNimLibPath = realNimPath.parentDir.parentDir / "lib"
  570. if not fileExists(conf.libpath.string / "system.nim") and
  571. fileExists(parentNimLibPath / "system.nim"):
  572. conf.libpath = AbsoluteDir parentNimLibPath
  573. proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
  574. result = AbsoluteFile path.string.expandFilename
  575. proc removeTrailingDirSep*(path: string): string =
  576. if (path.len > 0) and (path[^1] == DirSep):
  577. result = substr(path, 0, path.len - 2)
  578. else:
  579. result = path
  580. proc disableNimblePath*(conf: ConfigRef) =
  581. incl conf.globalOptions, optNoNimblePath
  582. conf.lazyPaths.setLen(0)
  583. conf.nimblePaths.setLen(0)
  584. proc clearNimblePath*(conf: ConfigRef) =
  585. conf.lazyPaths.setLen(0)
  586. conf.nimblePaths.setLen(0)
  587. include packagehandling
  588. proc getOsCacheDir(): string =
  589. when defined(posix):
  590. result = getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") / "nim"
  591. else:
  592. result = getHomeDir() / genSubDir.string
  593. proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir =
  594. # XXX projectName should always be without a file extension!
  595. result = if not conf.nimcacheDir.isEmpty:
  596. conf.nimcacheDir
  597. elif conf.backend == backendJs:
  598. conf.projectPath / genSubDir
  599. else:
  600. AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name &
  601. (if isDefined(conf, "release") or isDefined(conf, "danger"): "_r" else: "_d"))
  602. proc pathSubs*(conf: ConfigRef; p, config: string): string =
  603. let home = removeTrailingDirSep(os.getHomeDir())
  604. result = unixToNativePath(p % [
  605. "nim", getPrefixDir(conf).string,
  606. "lib", conf.libpath.string,
  607. "home", home,
  608. "config", config,
  609. "projectname", conf.projectName,
  610. "projectpath", conf.projectPath.string,
  611. "projectdir", conf.projectPath.string,
  612. "nimcache", getNimcacheDir(conf).string]).expandTilde
  613. iterator nimbleSubs*(conf: ConfigRef; p: string): string =
  614. let pl = p.toLowerAscii
  615. if "$nimblepath" in pl or "$nimbledir" in pl:
  616. for i in countdown(conf.nimblePaths.len-1, 0):
  617. let nimblePath = removeTrailingDirSep(conf.nimblePaths[i].string)
  618. yield p % ["nimblepath", nimblePath, "nimbledir", nimblePath]
  619. else:
  620. yield p
  621. proc toGeneratedFile*(conf: ConfigRef; path: AbsoluteFile,
  622. ext: string): AbsoluteFile =
  623. ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
  624. result = getNimcacheDir(conf) / RelativeFile path.string.splitPath.tail.changeFileExt(ext)
  625. proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile,
  626. createSubDir: bool = true): AbsoluteFile =
  627. let subdir = getNimcacheDir(conf)
  628. if createSubDir:
  629. try:
  630. createDir(subdir.string)
  631. except OSError:
  632. writeLine(stdout, "cannot create directory: " & subdir.string)
  633. quit(1)
  634. result = subdir / RelativeFile f.string.splitPath.tail
  635. #echo "completeGeneratedFilePath(", f, ") = ", result
  636. proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile =
  637. for it in conf.searchPaths:
  638. if suppressStdlib and it.string.startsWith(conf.libpath.string):
  639. continue
  640. result = it / f
  641. if fileExists(result):
  642. return canonicalizePath(conf, result)
  643. result = AbsoluteFile""
  644. proc rawFindFile2(conf: ConfigRef; f: RelativeFile): AbsoluteFile =
  645. for i, it in conf.lazyPaths:
  646. result = it / f
  647. if fileExists(result):
  648. # bring to front
  649. for j in countdown(i, 1):
  650. swap(conf.lazyPaths[j], conf.lazyPaths[j-1])
  651. return canonicalizePath(conf, result)
  652. result = AbsoluteFile""
  653. template patchModule(conf: ConfigRef) {.dirty.} =
  654. if not result.isEmpty and conf.moduleOverrides.len > 0:
  655. let key = getPackageName(conf, result.string) & "_" & splitFile(result).name
  656. if conf.moduleOverrides.hasKey(key):
  657. let ov = conf.moduleOverrides[key]
  658. if ov.len > 0: result = AbsoluteFile(ov)
  659. when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo):
  660. proc isRelativeTo(path, base: string): bool =
  661. # pending #13212 use os.isRelativeTo
  662. let path = path.normalizedPath
  663. let base = base.normalizedPath
  664. let ret = relativePath(path, base)
  665. result = path.len > 0 and not ret.startsWith ".."
  666. proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile): RelativeFile =
  667. let f = $f
  668. template search(paths) =
  669. for it in paths:
  670. let it = $it
  671. if f.isRelativeTo(it):
  672. return relativePath(f, it).RelativeFile
  673. search(conf.searchPaths)
  674. search(conf.lazyPaths)
  675. proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile =
  676. if f.isAbsolute:
  677. result = if f.fileExists: AbsoluteFile(f) else: AbsoluteFile""
  678. else:
  679. result = rawFindFile(conf, RelativeFile f, suppressStdlib)
  680. if result.isEmpty:
  681. result = rawFindFile(conf, RelativeFile f.toLowerAscii, suppressStdlib)
  682. if result.isEmpty:
  683. result = rawFindFile2(conf, RelativeFile f)
  684. if result.isEmpty:
  685. result = rawFindFile2(conf, RelativeFile f.toLowerAscii)
  686. patchModule(conf)
  687. const stdlibDirs = [
  688. "pure", "core", "arch",
  689. "pure/collections",
  690. "pure/concurrency",
  691. "pure/unidecode", "impure",
  692. "wrappers", "wrappers/linenoise",
  693. "windows", "posix", "js"]
  694. proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFile =
  695. # returns path to module
  696. const pkgPrefix = "pkg/"
  697. const stdPrefix = "std/"
  698. var m = addFileExt(modulename, NimExt)
  699. if m.startsWith(pkgPrefix):
  700. result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true)
  701. else:
  702. if m.startsWith(stdPrefix):
  703. let stripped = m.substr(stdPrefix.len)
  704. for candidate in stdlibDirs:
  705. let path = (conf.libpath.string / candidate / stripped)
  706. if fileExists(path):
  707. m = path
  708. break
  709. let currentPath = currentModule.splitFile.dir
  710. result = AbsoluteFile currentPath / m
  711. if not fileExists(result):
  712. result = findFile(conf, m)
  713. patchModule(conf)
  714. proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
  715. const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
  716. var
  717. candidates: seq[string] = @[]
  718. dir = pkg
  719. prev = dir
  720. nimblepkg = ""
  721. let pkgname = pkg.lastPathPart()
  722. while true:
  723. for k, f in os.walkDir(dir, relative = true):
  724. if k == pcFile and f != "config.nims":
  725. let (_, name, ext) = splitFile(f)
  726. if ext in extensions:
  727. let x = changeFileExt(dir / name, ".nim")
  728. if fileExists(x):
  729. candidates.add x
  730. if ext == ".nimble":
  731. if nimblepkg.len == 0:
  732. nimblepkg = name
  733. # Since nimble packages can have their source in a subfolder,
  734. # check the last folder we were in for a possible match.
  735. if dir != prev:
  736. let x = prev / x.extractFilename()
  737. if fileExists(x):
  738. candidates.add x
  739. else:
  740. # If we found more than one nimble file, chances are that we
  741. # missed the real project file, or this is an invalid nimble
  742. # package. Either way, bailing is the better choice.
  743. return ""
  744. let pkgname = if nimblepkg.len > 0: nimblepkg else: pkgname
  745. for c in candidates:
  746. if pkgname in c.extractFilename(): return c
  747. if candidates.len > 0:
  748. return candidates[0]
  749. prev = dir
  750. dir = parentDir(dir)
  751. if dir == "": break
  752. return ""
  753. proc canonDynlibName(s: string): string =
  754. let start = if s.startsWith("lib"): 3 else: 0
  755. let ende = strutils.find(s, {'(', ')', '.'})
  756. if ende >= 0:
  757. result = s.substr(start, ende-1)
  758. else:
  759. result = s.substr(start)
  760. proc inclDynlibOverride*(conf: ConfigRef; lib: string) =
  761. conf.dllOverrides[lib.canonDynlibName] = "true"
  762. proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
  763. result = optDynlibOverrideAll in conf.globalOptions or
  764. conf.dllOverrides.hasKey(lib.canonDynlibName)
  765. proc parseIdeCmd*(s: string): IdeCmd =
  766. case s:
  767. of "sug": ideSug
  768. of "con": ideCon
  769. of "def": ideDef
  770. of "use": ideUse
  771. of "dus": ideDus
  772. of "chk": ideChk
  773. of "mod": ideMod
  774. of "highlight": ideHighlight
  775. of "outline": ideOutline
  776. of "known": ideKnown
  777. of "msg": ideMsg
  778. of "project": ideProject
  779. else: ideNone
  780. proc `$`*(c: IdeCmd): string =
  781. case c:
  782. of ideSug: "sug"
  783. of ideCon: "con"
  784. of ideDef: "def"
  785. of ideUse: "use"
  786. of ideDus: "dus"
  787. of ideChk: "chk"
  788. of ideMod: "mod"
  789. of ideNone: "none"
  790. of ideHighlight: "highlight"
  791. of ideOutline: "outline"
  792. of ideKnown: "known"
  793. of ideMsg: "msg"
  794. of ideProject: "project"
  795. proc floatInt64Align*(conf: ConfigRef): int16 =
  796. ## Returns either 4 or 8 depending on reasons.
  797. if conf != nil and conf.target.targetCPU == cpuI386:
  798. #on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double)
  799. if conf.target.targetOS != osWindows:
  800. # on i386 for all known POSIX systems, 64bits ints are aligned
  801. # to 4bytes (except with -malign-double)
  802. return 4
  803. return 8
  804. proc setOutFile*(conf: ConfigRef) =
  805. proc libNameTmpl(conf: ConfigRef): string {.inline.} =
  806. result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a"
  807. if conf.outFile.isEmpty:
  808. let base = conf.projectName
  809. let targetName =
  810. if optGenDynLib in conf.globalOptions:
  811. platform.OS[conf.target.targetOS].dllFrmt % base
  812. elif optGenStaticLib in conf.globalOptions:
  813. libNameTmpl(conf) % base
  814. else:
  815. base & platform.OS[conf.target.targetOS].exeExt
  816. conf.outFile = RelativeFile targetName