options.nim 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  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. const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
  236. const
  237. ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
  238. optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck,
  239. optMoveCheck}
  240. DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
  241. optBoundsCheck, optOverflowCheck, optAssert, optWarns,
  242. optHints, optStackTrace, optLineTrace,
  243. optPatterns, optNilCheck, optMoveCheck}
  244. DefaultGlobalOptions* = {optThreadAnalysis}
  245. proc getSrcTimestamp(): DateTime =
  246. try:
  247. result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH",
  248. "not a number"))))
  249. except ValueError:
  250. # Environment variable malformed.
  251. # https://reproducible-builds.org/specs/source-date-epoch/: "If the
  252. # value is malformed, the build process SHOULD exit with a non-zero
  253. # error code", which this doesn't do. This uses local time, because
  254. # that maintains compatibility with existing usage.
  255. result = utc getTime()
  256. proc getDateStr*(): string =
  257. result = format(getSrcTimestamp(), "yyyy-MM-dd")
  258. proc getClockStr*(): string =
  259. result = format(getSrcTimestamp(), "HH:mm:ss")
  260. template newPackageCache*(): untyped =
  261. newStringTable(when FileSystemCaseSensitive:
  262. modeCaseInsensitive
  263. else:
  264. modeCaseSensitive)
  265. proc newConfigRef*(): ConfigRef =
  266. result = ConfigRef(
  267. selectedGC: gcRefc,
  268. cCompiler: ccGcc,
  269. verbosity: 1,
  270. options: DefaultOptions,
  271. globalOptions: DefaultGlobalOptions,
  272. m: initMsgConfig(),
  273. evalExpr: "",
  274. cppDefines: initSet[string](),
  275. headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
  276. hintQuitCalled, hintExecuting},
  277. notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1],
  278. configVars: newStringTable(modeStyleInsensitive),
  279. symbols: newStringTable(modeStyleInsensitive),
  280. packageCache: newPackageCache(),
  281. searchPaths: @[],
  282. lazyPaths: @[],
  283. outFile: AbsoluteFile"", prefixDir: AbsoluteDir"",
  284. libpath: AbsoluteDir"", nimcacheDir: AbsoluteDir"",
  285. dllOverrides: newStringTable(modeCaseInsensitive),
  286. moduleOverrides: newStringTable(modeStyleInsensitive),
  287. projectName: "", # holds a name like 'nim'
  288. projectPath: AbsoluteDir"", # holds a path like /home/alice/projects/nim/compiler/
  289. projectFull: AbsoluteFile"", # projectPath/projectName
  290. projectIsStdin: false, # whether we're compiling from stdin
  291. projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
  292. command: "", # the main command (e.g. cc, check, scan, etc)
  293. commandArgs: @[], # any arguments after the main command
  294. keepComments: true, # whether the parser needs to keep comments
  295. implicitImports: @[], # modules that are to be implicitly imported
  296. implicitIncludes: @[], # modules that are to be implicitly included
  297. docSeeSrcUrl: "",
  298. cIncludes: @[], # directories to search for included files
  299. cLibs: @[], # directories to search for lib files
  300. cLinkedLibs: @[], # libraries to link
  301. externalToLink: @[],
  302. linkOptionsCmd: "",
  303. compileOptionsCmd: @[],
  304. linkOptions: "",
  305. compileOptions: "",
  306. ccompilerpath: "",
  307. toCompile: @[],
  308. arguments: "",
  309. suggestMaxResults: 10_000
  310. )
  311. setTargetFromSystem(result.target)
  312. # enable colors by default on terminals
  313. if terminal.isatty(stderr):
  314. incl(result.globalOptions, optUseColors)
  315. proc newPartialConfigRef*(): ConfigRef =
  316. ## create a new ConfigRef that is only good enough for error reporting.
  317. result = ConfigRef(
  318. selectedGC: gcRefc,
  319. verbosity: 1,
  320. options: DefaultOptions,
  321. globalOptions: DefaultGlobalOptions,
  322. foreignPackageNotes: {hintProcessing, warnUnknownMagic,
  323. hintQuitCalled, hintExecuting},
  324. notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1])
  325. proc cppDefine*(c: ConfigRef; define: string) =
  326. c.cppDefines.incl define
  327. proc isDefined*(conf: ConfigRef; symbol: string): bool =
  328. if conf.symbols.hasKey(symbol):
  329. result = conf.symbols[symbol] != "false"
  330. elif cmpIgnoreStyle(symbol, CPU[conf.target.targetCPU].name) == 0:
  331. result = true
  332. elif cmpIgnoreStyle(symbol, platform.OS[conf.target.targetOS].name) == 0:
  333. result = true
  334. else:
  335. case symbol.normalize
  336. of "x86": result = conf.target.targetCPU == cpuI386
  337. of "itanium": result = conf.target.targetCPU == cpuIa64
  338. of "x8664": result = conf.target.targetCPU == cpuAmd64
  339. of "posix", "unix":
  340. result = conf.target.targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
  341. osQnx, osAtari, osAix,
  342. osHaiku, osVxWorks, osSolaris, osNetbsd,
  343. osFreebsd, osOpenbsd, osDragonfly, osMacosx,
  344. osAndroid, osNintendoSwitch}
  345. of "linux":
  346. result = conf.target.targetOS in {osLinux, osAndroid}
  347. of "bsd":
  348. result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
  349. of "emulatedthreadvars":
  350. result = platform.OS[conf.target.targetOS].props.contains(ospLacksThreadVars)
  351. of "msdos": result = conf.target.targetOS == osDos
  352. of "mswindows", "win32": result = conf.target.targetOS == osWindows
  353. of "macintosh": result = conf.target.targetOS in {osMacos, osMacosx}
  354. of "osx": result = conf.target.targetOS == osMacosx
  355. of "sunos": result = conf.target.targetOS == osSolaris
  356. of "nintendoswitch":
  357. result = conf.target.targetOS == osNintendoSwitch
  358. of "littleendian": result = CPU[conf.target.targetCPU].endian == platform.littleEndian
  359. of "bigendian": result = CPU[conf.target.targetCPU].endian == platform.bigEndian
  360. of "cpu8": result = CPU[conf.target.targetCPU].bit == 8
  361. of "cpu16": result = CPU[conf.target.targetCPU].bit == 16
  362. of "cpu32": result = CPU[conf.target.targetCPU].bit == 32
  363. of "cpu64": result = CPU[conf.target.targetCPU].bit == 64
  364. of "nimrawsetjmp":
  365. result = conf.target.targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
  366. osDragonfly, osMacosx}
  367. else: discard
  368. proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools}
  369. proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
  370. template compilationCachePresent*(conf: ConfigRef): untyped =
  371. conf.symbolFiles in {v2Sf, writeOnlySf}
  372. template optPreserveOrigSource*(conf: ConfigRef): untyped =
  373. optEmbedOrigSrc in conf.globalOptions
  374. const
  375. genSubDir* = RelativeDir"nimcache"
  376. NimExt* = "nim"
  377. RodExt* = "rod"
  378. HtmlExt* = "html"
  379. JsonExt* = "json"
  380. TagsExt* = "tags"
  381. TexExt* = "tex"
  382. IniExt* = "ini"
  383. DefaultConfig* = RelativeFile"nim.cfg"
  384. DefaultConfigNims* = RelativeFile"config.nims"
  385. DocConfig* = RelativeFile"nimdoc.cfg"
  386. DocTexConfig* = RelativeFile"nimdoc.tex.cfg"
  387. const oKeepVariableNames* = true
  388. template compilingLib*(conf: ConfigRef): bool =
  389. gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}
  390. proc mainCommandArg*(conf: ConfigRef): string =
  391. ## This is intended for commands like check or parse
  392. ## which will work on the main project file unless
  393. ## explicitly given a specific file argument
  394. if conf.commandArgs.len > 0:
  395. result = conf.commandArgs[0]
  396. else:
  397. result = conf.projectName
  398. proc existsConfigVar*(conf: ConfigRef; key: string): bool =
  399. result = hasKey(conf.configVars, key)
  400. proc getConfigVar*(conf: ConfigRef; key: string, default = ""): string =
  401. result = conf.configVars.getOrDefault(key, default)
  402. proc setConfigVar*(conf: ConfigRef; key, val: string) =
  403. conf.configVars[key] = val
  404. proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): AbsoluteFile =
  405. if not conf.outFile.isEmpty: result = conf.outFile
  406. else: result = conf.projectPath / changeFileExt(filename, ext)
  407. proc getPrefixDir*(conf: ConfigRef): AbsoluteDir =
  408. ## Gets the prefix dir, usually the parent directory where the binary resides.
  409. ##
  410. ## This is overridden by some tools (namely nimsuggest) via the ``conf.prefixDir``
  411. ## field.
  412. if not conf.prefixDir.isEmpty: result = conf.prefixDir
  413. else: result = AbsoluteDir splitPath(getAppDir()).head
  414. proc setDefaultLibpath*(conf: ConfigRef) =
  415. # set default value (can be overwritten):
  416. if conf.libpath.isEmpty:
  417. # choose default libpath:
  418. var prefix = getPrefixDir(conf)
  419. when defined(posix):
  420. if prefix == AbsoluteDir"/usr":
  421. conf.libpath = AbsoluteDir"/usr/lib/nim"
  422. elif prefix == AbsoluteDir"/usr/local":
  423. conf.libpath = AbsoluteDir"/usr/local/lib/nim"
  424. else:
  425. conf.libpath = prefix / RelativeDir"lib"
  426. else:
  427. conf.libpath = prefix / RelativeDir"lib"
  428. # Special rule to support other tools (nimble) which import the compiler
  429. # modules and make use of them.
  430. let realNimPath = findExe("nim")
  431. # Find out if $nim/../../lib/system.nim exists.
  432. let parentNimLibPath = realNimPath.parentDir.parentDir / "lib"
  433. if not fileExists(conf.libpath.string / "system.nim") and
  434. fileExists(parentNimlibPath / "system.nim"):
  435. conf.libpath = AbsoluteDir parentNimLibPath
  436. proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
  437. # on Windows, 'expandFilename' calls getFullPathName which doesn't do
  438. # case corrections, so we have to use this convoluted way of retrieving
  439. # the true filename (see tests/modules and Nimble uses 'import Uri' instead
  440. # of 'import uri'):
  441. when defined(windows):
  442. result = AbsoluteFile path.string.expandFilename
  443. for x in walkFiles(result.string):
  444. return AbsoluteFile x
  445. else:
  446. try:
  447. result = AbsoluteFile path.string.expandFilename
  448. except OSError:
  449. result = AbsoluteFile""
  450. proc shortenDir*(conf: ConfigRef; dir: string): string {.
  451. deprecated: "use 'relativeTo' instead".} =
  452. ## returns the interesting part of a dir
  453. var prefix = conf.projectPath.string & DirSep
  454. if startsWith(dir, prefix):
  455. return substr(dir, len(prefix))
  456. prefix = getPrefixDir(conf).string & DirSep
  457. if startsWith(dir, prefix):
  458. return substr(dir, len(prefix))
  459. result = dir
  460. proc removeTrailingDirSep*(path: string): string =
  461. if (len(path) > 0) and (path[len(path) - 1] == DirSep):
  462. result = substr(path, 0, len(path) - 2)
  463. else:
  464. result = path
  465. proc disableNimblePath*(conf: ConfigRef) =
  466. incl conf.globalOptions, optNoNimblePath
  467. conf.lazyPaths.setLen(0)
  468. include packagehandling
  469. proc getOsCacheDir(): string =
  470. when defined(posix):
  471. result = getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") / "nim"
  472. else:
  473. result = getHomeDir() / genSubDir.string
  474. proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir =
  475. # XXX projectName should always be without a file extension!
  476. result = if not conf.nimcacheDir.isEmpty:
  477. conf.nimcacheDir
  478. elif conf.cmd == cmdCompileToJS:
  479. conf.projectPath / genSubDir
  480. else:
  481. AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name &
  482. (if isDefined(conf, "release"): "_r" else: "_d"))
  483. proc pathSubs*(conf: ConfigRef; p, config: string): string =
  484. let home = removeTrailingDirSep(os.getHomeDir())
  485. result = unixToNativePath(p % [
  486. "nim", getPrefixDir(conf).string,
  487. "lib", conf.libpath.string,
  488. "home", home,
  489. "config", config,
  490. "projectname", conf.projectName,
  491. "projectpath", conf.projectPath.string,
  492. "projectdir", conf.projectPath.string,
  493. "nimcache", getNimcacheDir(conf).string])
  494. if "~/" in result:
  495. result = result.replace("~/", home & '/')
  496. proc toGeneratedFile*(conf: ConfigRef; path: AbsoluteFile,
  497. ext: string): AbsoluteFile =
  498. ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
  499. let (head, tail) = splitPath(path.string)
  500. result = getNimcacheDir(conf) / RelativeFile changeFileExt(tail, ext)
  501. proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile,
  502. createSubDir: bool = true): AbsoluteFile =
  503. let (head, tail) = splitPath(f.string)
  504. let subdir = getNimcacheDir(conf)
  505. if createSubDir:
  506. try:
  507. createDir(subdir.string)
  508. except OSError:
  509. writeLine(stdout, "cannot create directory: " & subdir.string)
  510. quit(1)
  511. result = subdir / RelativeFile tail
  512. #echo "completeGeneratedFilePath(", f, ") = ", result
  513. proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile =
  514. for it in conf.searchPaths:
  515. if suppressStdlib and it.string.startsWith(conf.libpath.string):
  516. continue
  517. result = it / f
  518. if fileExists(result):
  519. return canonicalizePath(conf, result)
  520. result = AbsoluteFile""
  521. proc rawFindFile2(conf: ConfigRef; f: RelativeFile): AbsoluteFile =
  522. for i, it in conf.lazyPaths:
  523. result = it / f
  524. if fileExists(result):
  525. # bring to front
  526. for j in countDown(i,1):
  527. swap(conf.lazyPaths[j], conf.lazyPaths[j-1])
  528. return canonicalizePath(conf, result)
  529. result = AbsoluteFile""
  530. template patchModule(conf: ConfigRef) {.dirty.} =
  531. if not result.isEmpty and conf.moduleOverrides.len > 0:
  532. let key = getPackageName(conf, result.string) & "_" & splitFile(result).name
  533. if conf.moduleOverrides.hasKey(key):
  534. let ov = conf.moduleOverrides[key]
  535. if ov.len > 0: result = AbsoluteFile(ov)
  536. proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile {.procvar.} =
  537. if f.isAbsolute:
  538. result = if f.existsFile: AbsoluteFile(f) else: AbsoluteFile""
  539. else:
  540. result = rawFindFile(conf, RelativeFile f, suppressStdlib)
  541. if result.isEmpty:
  542. result = rawFindFile(conf, RelativeFile f.toLowerAscii, suppressStdlib)
  543. if result.isEmpty:
  544. result = rawFindFile2(conf, RelativeFile f)
  545. if result.isEmpty:
  546. result = rawFindFile2(conf, RelativeFile f.toLowerAscii)
  547. patchModule(conf)
  548. const stdlibDirs = [
  549. "pure", "core", "arch",
  550. "pure/collections",
  551. "pure/concurrency", "impure",
  552. "wrappers", "wrappers/linenoise",
  553. "windows", "posix", "js"]
  554. proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFile =
  555. # returns path to module
  556. const pkgPrefix = "pkg/"
  557. const stdPrefix = "std/"
  558. var m = addFileExt(modulename, NimExt)
  559. if m.startsWith(pkgPrefix):
  560. result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true)
  561. else:
  562. if m.startsWith(stdPrefix):
  563. let stripped = m.substr(stdPrefix.len)
  564. for candidate in stdlibDirs:
  565. let path = (conf.libpath.string / candidate / stripped)
  566. if fileExists(path):
  567. m = path
  568. break
  569. let currentPath = currentModule.splitFile.dir
  570. result = AbsoluteFile currentPath / m
  571. if not fileExists(result):
  572. result = findFile(conf, m)
  573. patchModule(conf)
  574. proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
  575. const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
  576. var candidates: seq[string] = @[]
  577. var dir = pkg
  578. while true:
  579. for k, f in os.walkDir(dir, relative=true):
  580. if k == pcFile and f != "config.nims":
  581. let (_, name, ext) = splitFile(f)
  582. if ext in extensions:
  583. let x = changeFileExt(dir / name, ".nim")
  584. if fileExists(x):
  585. candidates.add x
  586. for c in candidates:
  587. # nim-foo foo or foo nfoo
  588. if (pkg in c) or (c in pkg): return c
  589. if candidates.len >= 1:
  590. return candidates[0]
  591. dir = parentDir(dir)
  592. if dir == "": break
  593. return ""
  594. proc canonDynlibName(s: string): string =
  595. let start = if s.startsWith("lib"): 3 else: 0
  596. let ende = strutils.find(s, {'(', ')', '.'})
  597. if ende >= 0:
  598. result = s.substr(start, ende-1)
  599. else:
  600. result = s.substr(start)
  601. proc inclDynlibOverride*(conf: ConfigRef; lib: string) =
  602. conf.dllOverrides[lib.canonDynlibName] = "true"
  603. proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
  604. result = optDynlibOverrideAll in conf.globalOptions or
  605. conf.dllOverrides.hasKey(lib.canonDynlibName)
  606. proc parseIdeCmd*(s: string): IdeCmd =
  607. case s:
  608. of "sug": ideSug
  609. of "con": ideCon
  610. of "def": ideDef
  611. of "use": ideUse
  612. of "dus": ideDus
  613. of "chk": ideChk
  614. of "mod": ideMod
  615. of "highlight": ideHighlight
  616. of "outline": ideOutline
  617. of "known": ideKnown
  618. of "msg": ideMsg
  619. else: ideNone
  620. proc `$`*(c: IdeCmd): string =
  621. case c:
  622. of ideSug: "sug"
  623. of ideCon: "con"
  624. of ideDef: "def"
  625. of ideUse: "use"
  626. of ideDus: "dus"
  627. of ideChk: "chk"
  628. of ideMod: "mod"
  629. of ideNone: "none"
  630. of ideHighlight: "highlight"
  631. of ideOutline: "outline"
  632. of ideKnown: "known"
  633. of ideMsg: "msg"