options.nim 28 KB

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