options.nim 34 KB

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