options.nim 26 KB

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