options.nim 26 KB

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