options.nim 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  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* = "2019"
  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, optStyleCheck,
  26. optAssert, optLineDir, optWarns, optHints,
  27. optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support
  28. optLineTrace, # line tracing support (includes stack tracing)
  29. optEndb, # embedded debugger
  30. optByRef, # use pass by ref for objects
  31. # (for interfacing with C)
  32. optProfiler, # profiler turned on
  33. optImplicitStatic, # optimization: implicit at compile time
  34. # evaluation
  35. optTrMacros, # en/disable pattern matching
  36. optMemTracker,
  37. optLaxStrings,
  38. optNilSeqs,
  39. optOldAst
  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. optStyleHint, # check that the names adhere to NEP-1
  53. optStyleError, # enforce that the names adhere to NEP-1
  54. optSkipSystemConfigFile, # skip the system's cfg/nims config file
  55. optSkipProjConfigFile, # skip the project's cfg/nims config file
  56. optSkipUserConfigFile, # skip the users's cfg/nims config file
  57. optSkipParentConfigFiles, # skip parent dir's cfg/nims config files
  58. optNoMain, # do not generate a "main" proc
  59. optUseColors, # use colors for hints, warnings, and errors
  60. optThreads, # support for multi-threading
  61. optStdout, # output to stdout
  62. optThreadAnalysis, # thread analysis pass
  63. optTaintMode, # taint mode turned on
  64. optTlsEmulation, # thread var emulation turned on
  65. optGenIndex # generate index file for documentation;
  66. optEmbedOrigSrc # embed the original source in the generated code
  67. # also: generate header file
  68. optIdeDebug # idetools: debug mode
  69. optIdeTerse # idetools: use terse descriptions
  70. optNoCppExceptions # use C exception handling even with CPP
  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. optNimV2
  81. optMultiMethods
  82. TGlobalOptions* = set[TGlobalOption]
  83. const
  84. harmlessOptions* = {optForceFullMake, optNoLinking, optRun,
  85. optUseColors, optStdout}
  86. type
  87. TCommands* = enum # Nim's commands
  88. # **keep binary compatible**
  89. cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
  90. cmdCompileToJS,
  91. cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc,
  92. cmdGenDepend, cmdDump,
  93. cmdCheck, # semantic checking for whole project
  94. cmdParse, # parse a single file (for debugging)
  95. cmdScan, # scan a single file (for debugging)
  96. cmdIdeTools, # ide tools
  97. cmdDef, # def feature (find definition for IDEs)
  98. cmdRst2html, # convert a reStructuredText file to HTML
  99. cmdRst2tex, # convert a reStructuredText file to TeX
  100. cmdInteractive, # start interactive session
  101. cmdRun, # run the project via TCC backend
  102. cmdJsonScript # compile a .json build file
  103. TStringSeq* = seq[string]
  104. TGCMode* = enum # the selected GC
  105. gcNone, gcBoehm, gcRegions, gcMarkAndSweep, gcDestructors,
  106. gcRefc, gcV2, gcGo
  107. # gcRefc and the GCs that follow it use a write barrier,
  108. # as far as usesWriteBarrier() is concerned
  109. IdeCmd* = enum
  110. ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
  111. ideHighlight, ideOutline, ideKnown, ideMsg
  112. Feature* = enum ## experimental features; DO NOT RENAME THESE!
  113. implicitDeref,
  114. dotOperators,
  115. callOperator,
  116. parallel,
  117. destructor,
  118. notnil,
  119. dynamicBindSym,
  120. forLoopMacros,
  121. caseStmtMacros,
  122. codeReordering,
  123. compiletimeFFI,
  124. ## This requires building nim with `-d:nimHasLibFFI`
  125. ## which itself requires `nimble install libffi`, see #10150
  126. ## Note: this feature can't be localized with {.push.}
  127. LegacyFeature* = enum
  128. allowSemcheckedAstModification,
  129. ## Allows to modify a NimNode where the type has already been
  130. ## flaged with nfSem. If you actually do this, it will cause
  131. ## bugs.
  132. SymbolFilesOption* = enum
  133. disabledSf, writeOnlySf, readOnlySf, v2Sf
  134. TSystemCC* = enum
  135. ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
  136. ccTcc, ccPcc, ccUcc, ccIcl, ccIcc, ccClangCl
  137. CfileFlag* {.pure.} = enum
  138. Cached, ## no need to recompile this time
  139. External ## file was introduced via .compile pragma
  140. Cfile* = object
  141. nimname*: string
  142. cname*, obj*: AbsoluteFile
  143. flags*: set[CfileFlag]
  144. CfileList* = seq[Cfile]
  145. Suggest* = ref object
  146. section*: IdeCmd
  147. qualifiedPath*: seq[string]
  148. name*: ptr string # not used beyond sorting purposes; name is also
  149. # part of 'qualifiedPath'
  150. filePath*: string
  151. line*: int # Starts at 1
  152. column*: int # Starts at 0
  153. doc*: string # Not escaped (yet)
  154. forth*: string # type
  155. quality*: range[0..100] # matching quality
  156. isGlobal*: bool # is a global variable
  157. contextFits*: bool # type/non-type context matches
  158. prefix*: PrefixMatch
  159. symkind*: byte
  160. scope*, localUsages*, globalUsages*: int # more usages is better
  161. tokenLen*: int
  162. version*: int
  163. Suggestions* = seq[Suggest]
  164. ConfigRef* = ref object ## every global configuration
  165. ## fields marked with '*' are subject to
  166. ## the incremental compilation mechanisms
  167. ## (+) means "part of the dependency"
  168. target*: Target # (+)
  169. linesCompiled*: int # all lines that have been compiled
  170. options*: TOptions # (+)
  171. globalOptions*: TGlobalOptions # (+)
  172. macrosToExpand*: StringTableRef
  173. m*: MsgConfig
  174. evalTemplateCounter*: int
  175. evalMacroCounter*: int
  176. exitcode*: int8
  177. cmd*: TCommands # the command
  178. selectedGC*: TGCMode # the selected GC (+)
  179. verbosity*: int # how verbose the compiler is
  180. numberOfProcessors*: int # number of processors
  181. evalExpr*: string # expression for idetools --eval
  182. lastCmdTime*: float # when caas is enabled, we measure each command
  183. symbolFiles*: SymbolFilesOption
  184. cppDefines*: HashSet[string] # (*)
  185. headerFile*: string
  186. features*: set[Feature]
  187. legacyFeatures*: set[LegacyFeature]
  188. arguments*: string ## the arguments to be passed to the program that
  189. ## should be run
  190. ideCmd*: IdeCmd
  191. oldNewlines*: bool
  192. cCompiler*: TSystemCC
  193. enableNotes*: TNoteKinds
  194. disableNotes*: TNoteKinds
  195. foreignPackageNotes*: TNoteKinds
  196. notes*: TNoteKinds
  197. mainPackageNotes*: TNoteKinds
  198. mainPackageId*: int
  199. errorCounter*: int
  200. hintCounter*: int
  201. warnCounter*: int
  202. errorMax*: int
  203. configVars*: StringTableRef
  204. symbols*: StringTableRef ## We need to use a StringTableRef here as defined
  205. ## symbols are always guaranteed to be style
  206. ## insensitive. Otherwise hell would break lose.
  207. packageCache*: StringTableRef
  208. searchPaths*: seq[AbsoluteDir]
  209. lazyPaths*: seq[AbsoluteDir]
  210. outFile*: RelativeFile
  211. outDir*: AbsoluteDir
  212. prefixDir*, libpath*, nimcacheDir*: AbsoluteDir
  213. dllOverrides, moduleOverrides*: StringTableRef
  214. projectName*: string # holds a name like 'nim'
  215. projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
  216. projectFull*: AbsoluteFile # projectPath/projectName
  217. projectIsStdin*: bool # whether we're compiling from stdin
  218. projectMainIdx*: FileIndex # the canonical path id of the main module
  219. command*: string # the main command (e.g. cc, check, scan, etc)
  220. commandArgs*: seq[string] # any arguments after the main command
  221. keepComments*: bool # whether the parser needs to keep comments
  222. implicitImports*: seq[string] # modules that are to be implicitly imported
  223. implicitIncludes*: seq[string] # modules that are to be implicitly included
  224. docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
  225. # The string uses the formatting variables `path` and `line`.
  226. # the used compiler
  227. cIncludes*: seq[AbsoluteDir] # directories to search for included files
  228. cLibs*: seq[AbsoluteDir] # directories to search for lib files
  229. cLinkedLibs*: seq[string] # libraries to link
  230. externalToLink*: seq[string] # files to link in addition to the file
  231. # we compiled (*)
  232. linkOptionsCmd*: string
  233. compileOptionsCmd*: seq[string]
  234. linkOptions*: string # (*)
  235. compileOptions*: string # (*)
  236. cCompilerPath*: string
  237. toCompile*: CfileList # (*)
  238. suggestionResultHook*: proc (result: Suggest) {.closure.}
  239. suggestVersion*: int
  240. suggestMaxResults*: int
  241. lastLineInfo*: TLineInfo
  242. writelnHook*: proc (output: string) {.closure.} # cannot make this gcsafe yet because of Nimble
  243. structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string;
  244. severity: Severity) {.closure, gcsafe.}
  245. cppCustomNamespace*: string
  246. proc hcrOn*(conf: ConfigRef): bool = return optHotCodeReloading in conf.globalOptions
  247. template depConfigFields*(fn) {.dirty.} =
  248. fn(target)
  249. fn(options)
  250. fn(globalOptions)
  251. fn(selectedGC)
  252. const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
  253. const
  254. ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
  255. optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck,
  256. optStyleCheck, optRefCheck}
  257. DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
  258. optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck,
  259. optHints, optStackTrace, optLineTrace,
  260. optTrMacros, optNilCheck, optStyleCheck}
  261. DefaultGlobalOptions* = {optThreadAnalysis,
  262. optExcessiveStackTrace, optListFullPaths}
  263. proc getSrcTimestamp(): DateTime =
  264. try:
  265. result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH",
  266. "not a number"))))
  267. except ValueError:
  268. # Environment variable malformed.
  269. # https://reproducible-builds.org/specs/source-date-epoch/: "If the
  270. # value is malformed, the build process SHOULD exit with a non-zero
  271. # error code", which this doesn't do. This uses local time, because
  272. # that maintains compatibility with existing usage.
  273. result = utc getTime()
  274. proc getDateStr*(): string =
  275. result = format(getSrcTimestamp(), "yyyy-MM-dd")
  276. proc getClockStr*(): string =
  277. result = format(getSrcTimestamp(), "HH:mm:ss")
  278. template newPackageCache*(): untyped =
  279. newStringTable(when FileSystemCaseSensitive:
  280. modeCaseInsensitive
  281. else:
  282. modeCaseSensitive)
  283. proc newConfigRef*(): ConfigRef =
  284. result = ConfigRef(
  285. selectedGC: gcRefc,
  286. cCompiler: ccGcc,
  287. verbosity: 1,
  288. options: DefaultOptions,
  289. globalOptions: DefaultGlobalOptions,
  290. macrosToExpand: newStringTable(modeStyleInsensitive),
  291. m: initMsgConfig(),
  292. evalExpr: "",
  293. cppDefines: initHashSet[string](),
  294. headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
  295. hintQuitCalled, hintExecuting},
  296. notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1],
  297. configVars: newStringTable(modeStyleInsensitive),
  298. symbols: newStringTable(modeStyleInsensitive),
  299. packageCache: newPackageCache(),
  300. searchPaths: @[],
  301. lazyPaths: @[],
  302. outFile: RelativeFile"",
  303. outDir: AbsoluteDir"",
  304. prefixDir: AbsoluteDir"",
  305. libpath: AbsoluteDir"", nimcacheDir: AbsoluteDir"",
  306. dllOverrides: newStringTable(modeCaseInsensitive),
  307. moduleOverrides: newStringTable(modeStyleInsensitive),
  308. projectName: "", # holds a name like 'nim'
  309. projectPath: AbsoluteDir"", # holds a path like /home/alice/projects/nim/compiler/
  310. projectFull: AbsoluteFile"", # projectPath/projectName
  311. projectIsStdin: false, # whether we're compiling from stdin
  312. projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
  313. command: "", # the main command (e.g. cc, check, scan, etc)
  314. commandArgs: @[], # any arguments after the main command
  315. keepComments: true, # whether the parser needs to keep comments
  316. implicitImports: @[], # modules that are to be implicitly imported
  317. implicitIncludes: @[], # modules that are to be implicitly included
  318. docSeeSrcUrl: "",
  319. cIncludes: @[], # directories to search for included files
  320. cLibs: @[], # directories to search for lib files
  321. cLinkedLibs: @[], # libraries to link
  322. externalToLink: @[],
  323. linkOptionsCmd: "",
  324. compileOptionsCmd: @[],
  325. linkOptions: "",
  326. compileOptions: "",
  327. ccompilerpath: "",
  328. toCompile: @[],
  329. arguments: "",
  330. suggestMaxResults: 10_000
  331. )
  332. setTargetFromSystem(result.target)
  333. # enable colors by default on terminals
  334. if terminal.isatty(stderr):
  335. incl(result.globalOptions, optUseColors)
  336. proc newPartialConfigRef*(): ConfigRef =
  337. ## create a new ConfigRef that is only good enough for error reporting.
  338. result = ConfigRef(
  339. selectedGC: gcRefc,
  340. verbosity: 1,
  341. options: DefaultOptions,
  342. globalOptions: DefaultGlobalOptions,
  343. foreignPackageNotes: {hintProcessing, warnUnknownMagic,
  344. hintQuitCalled, hintExecuting},
  345. notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1])
  346. proc cppDefine*(c: ConfigRef; define: string) =
  347. c.cppDefines.incl define
  348. proc isDefined*(conf: ConfigRef; symbol: string): bool =
  349. if conf.symbols.hasKey(symbol):
  350. result = true
  351. elif cmpIgnoreStyle(symbol, CPU[conf.target.targetCPU].name) == 0:
  352. result = true
  353. elif cmpIgnoreStyle(symbol, platform.OS[conf.target.targetOS].name) == 0:
  354. result = true
  355. else:
  356. case symbol.normalize
  357. of "x86": result = conf.target.targetCPU == cpuI386
  358. of "itanium": result = conf.target.targetCPU == cpuIa64
  359. of "x8664": result = conf.target.targetCPU == cpuAmd64
  360. of "posix", "unix":
  361. result = conf.target.targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
  362. osQnx, osAtari, osAix,
  363. osHaiku, osVxWorks, osSolaris, osNetbsd,
  364. osFreebsd, osOpenbsd, osDragonfly, osMacosx,
  365. osAndroid, osNintendoSwitch}
  366. of "linux":
  367. result = conf.target.targetOS in {osLinux, osAndroid}
  368. of "bsd":
  369. result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
  370. of "emulatedthreadvars":
  371. result = platform.OS[conf.target.targetOS].props.contains(ospLacksThreadVars)
  372. of "msdos": result = conf.target.targetOS == osDos
  373. of "mswindows", "win32": result = conf.target.targetOS == osWindows
  374. of "macintosh": result = conf.target.targetOS in {osMacos, osMacosx}
  375. of "osx": result = conf.target.targetOS == osMacosx
  376. of "sunos": result = conf.target.targetOS == osSolaris
  377. of "nintendoswitch":
  378. result = conf.target.targetOS == osNintendoSwitch
  379. of "littleendian": result = CPU[conf.target.targetCPU].endian == platform.littleEndian
  380. of "bigendian": result = CPU[conf.target.targetCPU].endian == platform.bigEndian
  381. of "cpu8": result = CPU[conf.target.targetCPU].bit == 8
  382. of "cpu16": result = CPU[conf.target.targetCPU].bit == 16
  383. of "cpu32": result = CPU[conf.target.targetCPU].bit == 32
  384. of "cpu64": result = CPU[conf.target.targetCPU].bit == 64
  385. of "nimrawsetjmp":
  386. result = conf.target.targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
  387. osDragonfly, osMacosx}
  388. else: discard
  389. proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools}
  390. proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
  391. template compilationCachePresent*(conf: ConfigRef): untyped =
  392. false
  393. # conf.symbolFiles in {v2Sf, writeOnlySf}
  394. template optPreserveOrigSource*(conf: ConfigRef): untyped =
  395. optEmbedOrigSrc in conf.globalOptions
  396. const
  397. genSubDir* = RelativeDir"nimcache"
  398. NimExt* = "nim"
  399. RodExt* = "rod"
  400. HtmlExt* = "html"
  401. JsonExt* = "json"
  402. TagsExt* = "tags"
  403. TexExt* = "tex"
  404. IniExt* = "ini"
  405. DefaultConfig* = RelativeFile"nim.cfg"
  406. DefaultConfigNims* = RelativeFile"config.nims"
  407. DocConfig* = RelativeFile"nimdoc.cfg"
  408. DocTexConfig* = RelativeFile"nimdoc.tex.cfg"
  409. const oKeepVariableNames* = true
  410. proc mainCommandArg*(conf: ConfigRef): string =
  411. ## This is intended for commands like check or parse
  412. ## which will work on the main project file unless
  413. ## explicitly given a specific file argument
  414. if conf.commandArgs.len > 0:
  415. result = conf.commandArgs[0]
  416. else:
  417. result = conf.projectName
  418. proc existsConfigVar*(conf: ConfigRef; key: string): bool =
  419. result = hasKey(conf.configVars, key)
  420. proc getConfigVar*(conf: ConfigRef; key: string, default = ""): string =
  421. result = conf.configVars.getOrDefault(key, default)
  422. proc setConfigVar*(conf: ConfigRef; key, val: string) =
  423. conf.configVars[key] = val
  424. proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): AbsoluteFile =
  425. conf.outDir / changeFileExt(filename, ext)
  426. proc absOutFile*(conf: ConfigRef): AbsoluteFile =
  427. conf.outDir / conf.outFile
  428. proc prepareToWriteOutput*(conf: ConfigRef): AbsoluteFile =
  429. ## Create the output directory and returns a full path to the output file
  430. createDir conf.outDir
  431. return conf.outDir / conf.outFile
  432. proc getPrefixDir*(conf: ConfigRef): AbsoluteDir =
  433. ## Gets the prefix dir, usually the parent directory where the binary resides.
  434. ##
  435. ## This is overridden by some tools (namely nimsuggest) via the ``conf.prefixDir``
  436. ## field.
  437. if not conf.prefixDir.isEmpty: result = conf.prefixDir
  438. else: result = AbsoluteDir splitPath(getAppDir()).head
  439. proc setDefaultLibpath*(conf: ConfigRef) =
  440. # set default value (can be overwritten):
  441. if conf.libpath.isEmpty:
  442. # choose default libpath:
  443. var prefix = getPrefixDir(conf)
  444. when defined(posix):
  445. if prefix == AbsoluteDir"/usr":
  446. conf.libpath = AbsoluteDir"/usr/lib/nim"
  447. elif prefix == AbsoluteDir"/usr/local":
  448. conf.libpath = AbsoluteDir"/usr/local/lib/nim"
  449. else:
  450. conf.libpath = prefix / RelativeDir"lib"
  451. else:
  452. conf.libpath = prefix / RelativeDir"lib"
  453. # Special rule to support other tools (nimble) which import the compiler
  454. # modules and make use of them.
  455. let realNimPath = findExe("nim")
  456. # Find out if $nim/../../lib/system.nim exists.
  457. let parentNimLibPath = realNimPath.parentDir.parentDir / "lib"
  458. if not fileExists(conf.libpath.string / "system.nim") and
  459. fileExists(parentNimLibPath / "system.nim"):
  460. conf.libpath = AbsoluteDir parentNimLibPath
  461. proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
  462. result = AbsoluteFile path.string.expandFilename
  463. proc shortenDir*(conf: ConfigRef; dir: string): string {.
  464. deprecated: "use 'relativeTo' instead".} =
  465. ## returns the interesting part of a dir
  466. var prefix = conf.projectPath.string & DirSep
  467. if startsWith(dir, prefix):
  468. return substr(dir, len(prefix))
  469. prefix = getPrefixDir(conf).string & DirSep
  470. if startsWith(dir, prefix):
  471. return substr(dir, len(prefix))
  472. result = dir
  473. proc removeTrailingDirSep*(path: string): string =
  474. if (len(path) > 0) and (path[len(path) - 1] == DirSep):
  475. result = substr(path, 0, len(path) - 2)
  476. else:
  477. result = path
  478. proc disableNimblePath*(conf: ConfigRef) =
  479. incl conf.globalOptions, optNoNimblePath
  480. conf.lazyPaths.setLen(0)
  481. include packagehandling
  482. proc getOsCacheDir(): string =
  483. when defined(posix):
  484. result = getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") / "nim"
  485. else:
  486. result = getHomeDir() / genSubDir.string
  487. proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir =
  488. # XXX projectName should always be without a file extension!
  489. result = if not conf.nimcacheDir.isEmpty:
  490. conf.nimcacheDir
  491. elif conf.cmd == cmdCompileToJS:
  492. conf.projectPath / genSubDir
  493. else:
  494. AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name &
  495. (if isDefined(conf, "release") or isDefined(conf, "danger"): "_r" else: "_d"))
  496. proc pathSubs*(conf: ConfigRef; p, config: string): string =
  497. let home = removeTrailingDirSep(os.getHomeDir())
  498. result = unixToNativePath(p % [
  499. "nim", getPrefixDir(conf).string,
  500. "lib", conf.libpath.string,
  501. "home", home,
  502. "config", config,
  503. "projectname", conf.projectName,
  504. "projectpath", conf.projectPath.string,
  505. "projectdir", conf.projectPath.string,
  506. "nimcache", getNimcacheDir(conf).string])
  507. if "~/" in result:
  508. result = result.replace("~/", home & '/')
  509. proc toGeneratedFile*(conf: ConfigRef; path: AbsoluteFile,
  510. ext: string): AbsoluteFile =
  511. ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
  512. let (head, tail) = splitPath(path.string)
  513. result = getNimcacheDir(conf) / RelativeFile changeFileExt(tail, ext)
  514. proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile,
  515. createSubDir: bool = true): AbsoluteFile =
  516. let (head, tail) = splitPath(f.string)
  517. let subdir = getNimcacheDir(conf)
  518. if createSubDir:
  519. try:
  520. createDir(subdir.string)
  521. except OSError:
  522. writeLine(stdout, "cannot create directory: " & subdir.string)
  523. quit(1)
  524. result = subdir / RelativeFile tail
  525. #echo "completeGeneratedFilePath(", f, ") = ", result
  526. proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile =
  527. for it in conf.searchPaths:
  528. if suppressStdlib and it.string.startsWith(conf.libpath.string):
  529. continue
  530. result = it / f
  531. if fileExists(result):
  532. return canonicalizePath(conf, result)
  533. result = AbsoluteFile""
  534. proc rawFindFile2(conf: ConfigRef; f: RelativeFile): AbsoluteFile =
  535. for i, it in conf.lazyPaths:
  536. result = it / f
  537. if fileExists(result):
  538. # bring to front
  539. for j in countdown(i, 1):
  540. swap(conf.lazyPaths[j], conf.lazyPaths[j-1])
  541. return canonicalizePath(conf, result)
  542. result = AbsoluteFile""
  543. template patchModule(conf: ConfigRef) {.dirty.} =
  544. if not result.isEmpty and conf.moduleOverrides.len > 0:
  545. let key = getPackageName(conf, result.string) & "_" & splitFile(result).name
  546. if conf.moduleOverrides.hasKey(key):
  547. let ov = conf.moduleOverrides[key]
  548. if ov.len > 0: result = AbsoluteFile(ov)
  549. proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile {.procvar.} =
  550. if f.isAbsolute:
  551. result = if f.existsFile: AbsoluteFile(f) else: AbsoluteFile""
  552. else:
  553. result = rawFindFile(conf, RelativeFile f, suppressStdlib)
  554. if result.isEmpty:
  555. result = rawFindFile(conf, RelativeFile f.toLowerAscii, suppressStdlib)
  556. if result.isEmpty:
  557. result = rawFindFile2(conf, RelativeFile f)
  558. if result.isEmpty:
  559. result = rawFindFile2(conf, RelativeFile f.toLowerAscii)
  560. patchModule(conf)
  561. const stdlibDirs = [
  562. "pure", "core", "arch",
  563. "pure/collections",
  564. "pure/concurrency", "impure",
  565. "wrappers", "wrappers/linenoise",
  566. "windows", "posix", "js"]
  567. proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFile =
  568. # returns path to module
  569. const pkgPrefix = "pkg/"
  570. const stdPrefix = "std/"
  571. var m = addFileExt(modulename, NimExt)
  572. if m.startsWith(pkgPrefix):
  573. result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true)
  574. else:
  575. if m.startsWith(stdPrefix):
  576. let stripped = m.substr(stdPrefix.len)
  577. for candidate in stdlibDirs:
  578. let path = (conf.libpath.string / candidate / stripped)
  579. if fileExists(path):
  580. m = path
  581. break
  582. let currentPath = currentModule.splitFile.dir
  583. result = AbsoluteFile currentPath / m
  584. if not fileExists(result):
  585. result = findFile(conf, m)
  586. patchModule(conf)
  587. proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
  588. const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
  589. var candidates: seq[string] = @[]
  590. var dir = pkg
  591. while true:
  592. for k, f in os.walkDir(dir, relative=true):
  593. if k == pcFile and f != "config.nims":
  594. let (_, name, ext) = splitFile(f)
  595. if ext in extensions:
  596. let x = changeFileExt(dir / name, ".nim")
  597. if fileExists(x):
  598. candidates.add x
  599. for c in candidates:
  600. # nim-foo foo or foo nfoo
  601. if (pkg in c) or (c in pkg): return c
  602. if candidates.len >= 1:
  603. return candidates[0]
  604. dir = parentDir(dir)
  605. if dir == "": break
  606. return ""
  607. proc canonDynlibName(s: string): string =
  608. let start = if s.startsWith("lib"): 3 else: 0
  609. let ende = strutils.find(s, {'(', ')', '.'})
  610. if ende >= 0:
  611. result = s.substr(start, ende-1)
  612. else:
  613. result = s.substr(start)
  614. proc inclDynlibOverride*(conf: ConfigRef; lib: string) =
  615. conf.dllOverrides[lib.canonDynlibName] = "true"
  616. proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
  617. result = optDynlibOverrideAll in conf.globalOptions or
  618. conf.dllOverrides.hasKey(lib.canonDynlibName)
  619. proc parseIdeCmd*(s: string): IdeCmd =
  620. case s:
  621. of "sug": ideSug
  622. of "con": ideCon
  623. of "def": ideDef
  624. of "use": ideUse
  625. of "dus": ideDus
  626. of "chk": ideChk
  627. of "mod": ideMod
  628. of "highlight": ideHighlight
  629. of "outline": ideOutline
  630. of "known": ideKnown
  631. of "msg": ideMsg
  632. else: ideNone
  633. proc `$`*(c: IdeCmd): string =
  634. case c:
  635. of ideSug: "sug"
  636. of ideCon: "con"
  637. of ideDef: "def"
  638. of ideUse: "use"
  639. of ideDus: "dus"
  640. of ideChk: "chk"
  641. of ideMod: "mod"
  642. of ideNone: "none"
  643. of ideHighlight: "highlight"
  644. of ideOutline: "outline"
  645. of ideKnown: "known"
  646. of ideMsg: "msg"