nimblecmd.nim 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Implements some helper procs for Nimble (Nim's package manager) support.
  10. import options, msgs, lineinfos, pathutils
  11. import std/[parseutils, strutils, os, tables, sequtils]
  12. when defined(nimPreviewSlimSystem):
  13. import std/[syncio, assertions]
  14. import ../dist/checksums/src/checksums/sha1
  15. proc addPath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) =
  16. if not conf.searchPaths.contains(path):
  17. conf.searchPaths.insert(path, 0)
  18. type
  19. Version* = distinct string
  20. PackageInfo = Table[string, tuple[version, checksum: string]]
  21. proc `$`*(ver: Version): string {.borrow.}
  22. proc newVersion*(ver: string): Version =
  23. doAssert(ver.len == 0 or ver[0] in {'#', '\0'} + Digits,
  24. "Wrong version: " & ver)
  25. return Version(ver)
  26. proc isSpecial(ver: Version): bool =
  27. return ($ver).len > 0 and ($ver)[0] == '#'
  28. proc isValidVersion(v: string): bool =
  29. if v.len > 0:
  30. if v[0] in {'#'} + Digits:
  31. result = true
  32. else:
  33. result = false
  34. else:
  35. result = false
  36. proc `<`*(ver: Version, ver2: Version): bool =
  37. ## This is synced from Nimble's version module.
  38. result = false
  39. # Handling for special versions such as "#head" or "#branch".
  40. if ver.isSpecial or ver2.isSpecial:
  41. if ver2.isSpecial and ($ver2).normalize == "#head":
  42. return ($ver).normalize != "#head"
  43. if not ver2.isSpecial:
  44. # `#aa111 < 1.1`
  45. return ($ver).normalize != "#head"
  46. # Handling for normal versions such as "0.1.0" or "1.0".
  47. var sVer = string(ver).split('.')
  48. var sVer2 = string(ver2).split('.')
  49. for i in 0..<max(sVer.len, sVer2.len):
  50. var sVerI = 0
  51. if i < sVer.len:
  52. discard parseInt(sVer[i], sVerI)
  53. var sVerI2 = 0
  54. if i < sVer2.len:
  55. discard parseInt(sVer2[i], sVerI2)
  56. if sVerI < sVerI2:
  57. return true
  58. elif sVerI == sVerI2:
  59. discard
  60. else:
  61. return false
  62. proc getPathVersionChecksum*(p: string): tuple[name, version, checksum: string] =
  63. ## Splits path ``p`` in the format
  64. ## ``/home/user/.nimble/pkgs/package-0.1-febadeaea2345e777f0f6f8433f7f0a52edd5d1b`` into
  65. ## ``("/home/user/.nimble/pkgs/package", "0.1", "febadeaea2345e777f0f6f8433f7f0a52edd5d1b")``
  66. result = ("", "", "")
  67. const checksumSeparator = '-'
  68. const versionSeparator = '-'
  69. const specialVersionSepartator = "-#"
  70. const separatorNotFound = -1
  71. var checksumSeparatorIndex = p.rfind(checksumSeparator)
  72. if checksumSeparatorIndex != separatorNotFound:
  73. result.checksum = p.substr(checksumSeparatorIndex + 1)
  74. if not result.checksum.isValidSha1Hash():
  75. result.checksum = ""
  76. checksumSeparatorIndex = p.len()
  77. else:
  78. checksumSeparatorIndex = p.len()
  79. var versionSeparatorIndex = p.rfind(
  80. specialVersionSepartator, 0, checksumSeparatorIndex - 1)
  81. if versionSeparatorIndex != separatorNotFound:
  82. result.version = p.substr(
  83. versionSeparatorIndex + 1, checksumSeparatorIndex - 1)
  84. else:
  85. versionSeparatorIndex = p.rfind(
  86. versionSeparator, 0, checksumSeparatorIndex - 1)
  87. if versionSeparatorIndex != separatorNotFound:
  88. result.version = p.substr(
  89. versionSeparatorIndex + 1, checksumSeparatorIndex - 1)
  90. else:
  91. versionSeparatorIndex = checksumSeparatorIndex
  92. result.name = p[0..<versionSeparatorIndex]
  93. proc addPackage*(conf: ConfigRef; packages: var PackageInfo, p: string;
  94. info: TLineInfo) =
  95. let (name, ver, checksum) = getPathVersionChecksum(p)
  96. if isValidVersion(ver):
  97. let version = newVersion(ver)
  98. if packages.getOrDefault(name).version.newVersion < version or
  99. (not packages.hasKey(name)):
  100. if checksum.isValidSha1Hash():
  101. packages[name] = ($version, checksum)
  102. else:
  103. packages[name] = ($version, "")
  104. else:
  105. localError(conf, info, "invalid package name: " & p)
  106. iterator chosen(packages: PackageInfo): string =
  107. for key, val in pairs(packages):
  108. var res = key
  109. if val.version.len != 0:
  110. res &= '-'
  111. res &= val.version
  112. if val.checksum.len != 0:
  113. res &= '-'
  114. res &= val.checksum
  115. yield res
  116. proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) =
  117. var path = p
  118. let nimbleLinks = toSeq(walkPattern(p / "*.nimble-link"))
  119. if nimbleLinks.len > 0:
  120. # If the user has more than one .nimble-link file then... we just ignore it.
  121. # Spec for these files is available in Nimble's readme:
  122. # https://github.com/nim-lang/nimble#nimble-link
  123. let nimbleLinkLines = readFile(nimbleLinks[0]).splitLines()
  124. path = nimbleLinkLines[1]
  125. if not path.isAbsolute():
  126. path = p / path
  127. if not contains(conf.searchPaths, AbsoluteDir path):
  128. message(conf, info, hintPath, path)
  129. conf.lazyPaths.insert(AbsoluteDir path, 0)
  130. proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) =
  131. var packages: PackageInfo = initTable[string, tuple[version, checksum: string]]()
  132. var pos = dir.len-1
  133. if dir[pos] in {DirSep, AltSep}: inc(pos)
  134. for k,p in os.walkDir(dir):
  135. if k == pcDir and p[pos] != '.':
  136. addPackage(conf, packages, p, info)
  137. for p in packages.chosen:
  138. addNimblePath(conf, p, info)
  139. proc nimblePath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) =
  140. addPathRec(conf, path.string, info)
  141. addNimblePath(conf, path.string, info)
  142. let i = conf.nimblePaths.find(path)
  143. if i != -1:
  144. conf.nimblePaths.delete(i)
  145. conf.nimblePaths.insert(path, 0)