options.nim 32 KB

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