first implementation of NimScript support

This commit is contained in:
Araq 2015-09-03 01:50:55 +02:00
commit 2cf03313fd
6 changed files with 384 additions and 223 deletions

View file

@ -10,6 +10,8 @@ import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools,
nimblepkg/download, nimblepkg/config, nimblepkg/nimbletypes, nimblepkg/download, nimblepkg/config, nimblepkg/nimbletypes,
nimblepkg/publish nimblepkg/publish
import compiler/options
when not defined(windows): when not defined(windows):
from posix import getpid from posix import getpid
else: else:
@ -40,7 +42,8 @@ type
ActionType = enum ActionType = enum
actionNil, actionUpdate, actionInit, actionPublish, actionNil, actionUpdate, actionInit, actionPublish,
actionInstall, actionSearch, actionInstall, actionSearch,
actionList, actionBuild, actionPath, actionUninstall, actionCompile actionList, actionBuild, actionPath, actionUninstall, actionCompile,
actionCustom
Action = object Action = object
case typ: ActionType case typ: ActionType
@ -99,7 +102,7 @@ Options:
For more information read the Github readme: For more information read the Github readme:
https://github.com/nim-lang/nimble#readme https://github.com/nim-lang/nimble#readme
""" """
nimbleVersion = "0.6.2" nimbleVersion = "0.7.0"
defaultPackageURL = defaultPackageURL =
"https://github.com/nim-lang/packages/raw/master/packages.json" "https://github.com/nim-lang/packages/raw/master/packages.json"
@ -112,21 +115,21 @@ proc writeVersion() =
[nimbleVersion, CompileDate, CompileTime]) [nimbleVersion, CompileDate, CompileTime])
quit(QuitSuccess) quit(QuitSuccess)
proc getNimbleDir(options: Options): string = proc getNimbleDir(opt: Options): string =
options.config.nimbleDir opt.config.nimbleDir
proc getPkgsDir(options: Options): string = proc getPkgsDir(opt: Options): string =
options.config.nimbleDir / "pkgs" opt.config.nimbleDir / "pkgs"
proc getBinDir(options: Options): string = proc getBinDir(opt: Options): string =
options.config.nimbleDir / "bin" opt.config.nimbleDir / "bin"
proc prompt(options: Options, question: string): bool = proc prompt(opt: Options, question: string): bool =
## Asks an interactive question and returns the result. ## Asks an interactive question and returns the result.
## ##
## The proc will return immediately without asking the user if the global ## The proc will return immediately without asking the user if the global
## forcePrompts has a value different than dontForcePrompt. ## forcePrompts has a value different than dontForcePrompt.
case options.forcePrompts case opt.forcePrompts
of forcePromptYes: of forcePromptYes:
echo(question & " -> [forced yes]") echo(question & " -> [forced yes]")
return true return true
@ -144,11 +147,11 @@ proc prompt(options: Options, question: string): bool =
else: else:
return false return false
proc renameBabelToNimble(options: Options) {.deprecated.} = proc renameBabelToNimble(opt: Options) {.deprecated.} =
let babelDir = getHomeDir() / ".babel" let babelDir = getHomeDir() / ".babel"
let nimbleDir = getHomeDir() / ".nimble" let nimbleDir = getHomeDir() / ".nimble"
if dirExists(babelDir): if dirExists(babelDir):
if options.prompt("Found deprecated babel package directory, would you " & if opt.prompt("Found deprecated babel package directory, would you " &
"like to rename it to nimble?"): "like to rename it to nimble?"):
copyDir(babelDir, nimbleDir) copyDir(babelDir, nimbleDir)
copyFile(babelDir / "babeldata.json", nimbleDir / "nimbledata.json") copyFile(babelDir / "babeldata.json", nimbleDir / "nimbledata.json")
@ -163,9 +166,10 @@ proc parseCmdLine(): Options =
case kind case kind
of cmdArgument: of cmdArgument:
if result.action.typ == actionNil: if result.action.typ == actionNil:
case key options.command = key
case key.normalize()
of "install", "path": of "install", "path":
case key case key.normalize()
of "install": of "install":
result.action.typ = actionInstall result.action.typ = actionInstall
of "path": of "path":
@ -197,7 +201,8 @@ proc parseCmdLine(): Options =
result.action.packages = @[] result.action.packages = @[]
of "publish": of "publish":
result.action.typ = actionPublish result.action.typ = actionPublish
else: writeHelp() else:
result.action.typ = actionCustom
else: else:
case result.action.typ case result.action.typ
of actionNil: of actionNil:
@ -222,7 +227,7 @@ proc parseCmdLine(): Options =
result.action.projName = key result.action.projName = key
of actionCompile: of actionCompile:
result.action.file = key result.action.file = key
of actionList, actionBuild, actionPublish: of actionList, actionBuild:
writeHelp() writeHelp()
else: else:
discard discard
@ -264,18 +269,18 @@ proc parseCmdLine(): Options =
else: else:
result.nimbleData = %{"reverseDeps": newJObject()} result.nimbleData = %{"reverseDeps": newJObject()}
proc update(options: Options) = proc update(opt: Options) =
## Downloads the package list from the specified URL. ## Downloads the package list from the specified URL.
## ##
## If the download is successful, the global didUpdatePackages is set to ## If the download is successful, the global didUpdatePackages is set to
## true. Otherwise an exception is raised on error. ## true. Otherwise an exception is raised on error.
let url = let url =
if options.action.typ == actionUpdate and options.action.optionalURL != "": if opt.action.typ == actionUpdate and opt.action.optionalURL != "":
options.action.optionalURL opt.action.optionalURL
else: else:
defaultPackageURL defaultPackageURL
echo("Downloading package list from " & url) echo("Downloading package list from " & url)
downloadFile(url, options.getNimbleDir() / "packages.json") downloadFile(url, opt.getNimbleDir() / "packages.json")
echo("Done.") echo("Done.")
proc checkInstallFile(pkgInfo: PackageInfo, proc checkInstallFile(pkgInfo: PackageInfo,
@ -326,7 +331,7 @@ proc copyWithExt(origDir, currentDir, dest: string,
result.add copyFileD(path, changeRoot(origDir, dest, path)) result.add copyFileD(path, changeRoot(origDir, dest, path))
proc copyFilesRec(origDir, currentDir, dest: string, proc copyFilesRec(origDir, currentDir, dest: string,
options: Options, pkgInfo: PackageInfo): HashSet[string] = opt: Options, pkgInfo: PackageInfo): HashSet[string] =
## Copies all the required files, skips files specified in the .nimble file ## Copies all the required files, skips files specified in the .nimble file
## (PackageInfo). ## (PackageInfo).
## Returns a list of filepaths to files which have been installed. ## Returns a list of filepaths to files which have been installed.
@ -339,7 +344,7 @@ proc copyFilesRec(origDir, currentDir, dest: string,
for file in pkgInfo.installFiles: for file in pkgInfo.installFiles:
let src = origDir / file let src = origDir / file
if not src.existsFile(): if not src.existsFile():
if options.prompt("Missing file " & src & ". Continue?"): if opt.prompt("Missing file " & src & ". Continue?"):
continue continue
else: else:
quit(QuitSuccess) quit(QuitSuccess)
@ -350,7 +355,7 @@ proc copyFilesRec(origDir, currentDir, dest: string,
# TODO: Allow skipping files inside dirs? # TODO: Allow skipping files inside dirs?
let src = origDir / dir let src = origDir / dir
if not src.existsDir(): if not src.existsDir():
if options.prompt("Missing directory " & src & ". Continue?"): if opt.prompt("Missing directory " & src & ". Continue?"):
continue continue
else: else:
quit(QuitSuccess) quit(QuitSuccess)
@ -366,7 +371,7 @@ proc copyFilesRec(origDir, currentDir, dest: string,
# Create the dir. # Create the dir.
createDir(changeRoot(origDir, dest, file)) createDir(changeRoot(origDir, dest, file))
result.incl copyFilesRec(origDir, file, dest, options, pkgInfo) result.incl copyFilesRec(origDir, file, dest, opt, pkgInfo)
else: else:
let skip = pkgInfo.checkInstallFile(origDir, file) let skip = pkgInfo.checkInstallFile(origDir, file)
@ -377,26 +382,26 @@ proc copyFilesRec(origDir, currentDir, dest: string,
result.incl copyFileD(pkgInfo.mypath, result.incl copyFileD(pkgInfo.mypath,
changeRoot(pkgInfo.mypath.splitFile.dir, dest, pkgInfo.mypath)) changeRoot(pkgInfo.mypath.splitFile.dir, dest, pkgInfo.mypath))
proc saveNimbleData(options: Options) = proc saveNimbleData(opt: Options) =
# TODO: This file should probably be locked. # TODO: This file should probably be locked.
writeFile(options.getNimbleDir() / "nimbledata.json", writeFile(opt.getNimbleDir() / "nimbledata.json",
pretty(options.nimbleData)) pretty(opt.nimbleData))
proc addRevDep(options: Options, dep: tuple[name, version: string], proc addRevDep(opt: Options, dep: tuple[name, version: string],
pkg: PackageInfo) = pkg: PackageInfo) =
# let depNameVer = dep.name & '-' & dep.version # let depNameVer = dep.name & '-' & dep.version
if not options.nimbleData["reverseDeps"].hasKey(dep.name): if not opt.nimbleData["reverseDeps"].hasKey(dep.name):
options.nimbleData["reverseDeps"][dep.name] = newJObject() opt.nimbleData["reverseDeps"][dep.name] = newJObject()
if not options.nimbleData["reverseDeps"][dep.name].hasKey(dep.version): if not opt.nimbleData["reverseDeps"][dep.name].hasKey(dep.version):
options.nimbleData["reverseDeps"][dep.name][dep.version] = newJArray() opt.nimbleData["reverseDeps"][dep.name][dep.version] = newJArray()
let revDep = %{ "name": %pkg.name, "version": %pkg.version} let revDep = %{ "name": %pkg.name, "version": %pkg.version}
let thisDep = options.nimbleData["reverseDeps"][dep.name][dep.version] let thisDep = opt.nimbleData["reverseDeps"][dep.name][dep.version]
if revDep notin thisDep: if revDep notin thisDep:
thisDep.add revDep thisDep.add revDep
proc removeRevDep(options: Options, pkg: PackageInfo) = proc removeRevDep(opt: Options, pkg: PackageInfo) =
## Removes ``pkg`` from the reverse dependencies of every package. ## Removes ``pkg`` from the reverse dependencies of every package.
proc remove(options: Options, pkg: PackageInfo, depTup: PkgTuple, proc remove(opt: Options, pkg: PackageInfo, depTup: PkgTuple,
thisDep: JsonNode) = thisDep: JsonNode) =
for ver, val in thisDep: for ver, val in thisDep:
if ver.newVersion in depTup.ver: if ver.newVersion in depTup.ver:
@ -410,16 +415,16 @@ proc removeRevDep(options: Options, pkg: PackageInfo) =
for depTup in pkg.requires: for depTup in pkg.requires:
if depTup.name.isURL(): if depTup.name.isURL():
# We sadly must go through everything in this case... # We sadly must go through everything in this case...
for key, val in options.nimbleData["reverseDeps"]: for key, val in opt.nimbleData["reverseDeps"]:
options.remove(pkg, depTup, val) opt.remove(pkg, depTup, val)
else: else:
let thisDep = options.nimbleData["reverseDeps"][depTup.name] let thisDep = opt.nimbleData["reverseDeps"][depTup.name]
if thisDep.isNil: continue if thisDep.isNil: continue
options.remove(pkg, depTup, thisDep) opt.remove(pkg, depTup, thisDep)
# Clean up empty objects/arrays # Clean up empty objects/arrays
var newData = newJObject() var newData = newJObject()
for key, val in options.nimbleData["reverseDeps"]: for key, val in opt.nimbleData["reverseDeps"]:
if val.len != 0: if val.len != 0:
var newVal = newJObject() var newVal = newJObject()
for ver, elem in val: for ver, elem in val:
@ -427,19 +432,19 @@ proc removeRevDep(options: Options, pkg: PackageInfo) =
newVal[ver] = elem newVal[ver] = elem
if newVal.len != 0: if newVal.len != 0:
newData[key] = newVal newData[key] = newVal
options.nimbleData["reverseDeps"] = newData opt.nimbleData["reverseDeps"] = newData
saveNimbleData(options) saveNimbleData(opt)
proc install(packages: seq[PkgTuple], proc install(packages: seq[PkgTuple],
options: Options, opt: Options,
doPrompt = true): tuple[paths: seq[string], pkg: PackageInfo] doPrompt = true): tuple[paths: seq[string], pkg: PackageInfo]
proc processDeps(pkginfo: PackageInfo, options: Options): seq[string] = proc processDeps(pkginfo: PackageInfo, opt: Options): seq[string] =
## Verifies and installs dependencies. ## Verifies and installs dependencies.
## ##
## Returns the list of paths to pass to the compiler during build phase. ## Returns the list of paths to pass to the compiler during build phase.
result = @[] result = @[]
let pkglist = getInstalledPkgs(options.getPkgsDir()) let pkglist = getInstalledPkgs(opt.getPkgsDir())
var reverseDeps: seq[tuple[name, version: string]] = @[] var reverseDeps: seq[tuple[name, version: string]] = @[]
for dep in pkginfo.requires: for dep in pkginfo.requires:
if dep.name == "nimrod" or dep.name == "nim": if dep.name == "nimrod" or dep.name == "nim":
@ -451,7 +456,7 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[string] =
var pkg: PackageInfo var pkg: PackageInfo
if not findPkg(pkglist, dep, pkg): if not findPkg(pkglist, dep, pkg):
echo("None found, installing...") echo("None found, installing...")
let (paths, installedPkg) = install(@[(dep.name, dep.ver)], options) let (paths, installedPkg) = install(@[(dep.name, dep.ver)], opt)
result.add(paths) result.add(paths)
pkg = installedPkg # For addRevDep pkg = installedPkg # For addRevDep
@ -459,7 +464,7 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[string] =
echo("Dependency already satisfied.") echo("Dependency already satisfied.")
result.add(pkg.mypath.splitFile.dir) result.add(pkg.mypath.splitFile.dir)
# Process the dependencies of this dependency. # Process the dependencies of this dependency.
result.add(processDeps(pkg, options)) result.add(processDeps(pkg, opt))
reverseDeps.add((pkg.name, pkg.version)) reverseDeps.add((pkg.name, pkg.version))
# Check if two packages of the same name (but different version) are listed # Check if two packages of the same name (but different version) are listed
@ -477,8 +482,8 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[string] =
# them added if the above errorenous condition occurs # them added if the above errorenous condition occurs
# (unsatisfiable dependendencies). # (unsatisfiable dependendencies).
for i in reverseDeps: for i in reverseDeps:
addRevDep(options, i, pkginfo) addRevDep(opt, i, pkginfo)
saveNimbleData(options) saveNimbleData(opt)
proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string], forRelease: bool) = proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string], forRelease: bool) =
## Builds a package as specified by ``pkgInfo``. ## Builds a package as specified by ``pkgInfo``.
@ -505,7 +510,7 @@ proc saveNimbleMeta(pkgDestDir, url: string, filesInstalled: HashSet[string]) =
nimblemeta["files"].add(%changeRoot(pkgDestDir, "", file)) nimblemeta["files"].add(%changeRoot(pkgDestDir, "", file))
writeFile(pkgDestDir / "nimblemeta.json", $nimblemeta) writeFile(pkgDestDir / "nimblemeta.json", $nimblemeta)
proc removePkgDir(dir: string, options: Options) = proc removePkgDir(dir: string, opt: Options) =
## Removes files belonging to the package in ``dir``. ## Removes files belonging to the package in ``dir``.
try: try:
var nimblemeta = parseFile(dir / "nimblemeta.json") var nimblemeta = parseFile(dir / "nimblemeta.json")
@ -525,12 +530,12 @@ proc removePkgDir(dir: string, options: Options) =
". Files not installed by nimble are present.") ". Files not installed by nimble are present.")
except OSError, JsonParsingError: except OSError, JsonParsingError:
echo("Error: Unable to read nimblemeta.json: ", getCurrentExceptionMsg()) echo("Error: Unable to read nimblemeta.json: ", getCurrentExceptionMsg())
if not options.prompt("Would you like to COMPLETELY remove ALL files " & if not opt.prompt("Would you like to COMPLETELY remove ALL files " &
"in " & dir & "?"): "in " & dir & "?"):
quit(QuitSuccess) quit(QuitSuccess)
removeDir(dir) removeDir(dir)
proc installFromDir(dir: string, latest: bool, options: Options, proc installFromDir(dir: string, latest: bool, opt: Options,
url: string): tuple[paths: seq[string], pkg: PackageInfo] = url: string): tuple[paths: seq[string], pkg: PackageInfo] =
## Returns where package has been installed to, together with paths ## Returns where package has been installed to, together with paths
## to the packages this package depends on. ## to the packages this package depends on.
@ -538,10 +543,10 @@ proc installFromDir(dir: string, latest: bool, options: Options,
## ``processDeps`` to gather a list of paths to pass to the nim compiler. ## ``processDeps`` to gather a list of paths to pass to the nim compiler.
var pkgInfo = getPkgInfo(dir) var pkgInfo = getPkgInfo(dir)
let realDir = pkgInfo.getRealDir() let realDir = pkgInfo.getRealDir()
let binDir = options.getBinDir() let binDir = opt.getBinDir()
let pkgsDir = options.getPkgsDir() let pkgsDir = opt.getPkgsDir()
# Dependencies need to be processed before the creation of the pkg dir. # Dependencies need to be processed before the creation of the pkg dir.
result.paths = processDeps(pkginfo, options) result.paths = processDeps(pkginfo, opt)
echo("Installing ", pkginfo.name, "-", pkginfo.version) echo("Installing ", pkginfo.name, "-", pkginfo.version)
@ -552,10 +557,10 @@ proc installFromDir(dir: string, latest: bool, options: Options,
let versionStr = (if latest: "" else: '-' & pkgInfo.version) let versionStr = (if latest: "" else: '-' & pkgInfo.version)
let pkgDestDir = pkgsDir / (pkgInfo.name & versionStr) let pkgDestDir = pkgsDir / (pkgInfo.name & versionStr)
if existsDir(pkgDestDir) and existsFile(pkgDestDir / "nimblemeta.json"): if existsDir(pkgDestDir) and existsFile(pkgDestDir / "nimblemeta.json"):
if not options.prompt(pkgInfo.name & versionStr & if not opt.prompt(pkgInfo.name & versionStr &
" already exists. Overwrite?"): " already exists. Overwrite?"):
quit(QuitSuccess) quit(QuitSuccess)
removePkgDir(pkgDestDir, options) removePkgDir(pkgDestDir, opt)
# Remove any symlinked binaries # Remove any symlinked binaries
for bin in pkgInfo.bin: for bin in pkgInfo.bin:
# TODO: Check that this binary belongs to the package being installed. # TODO: Check that this binary belongs to the package being installed.
@ -575,7 +580,7 @@ proc installFromDir(dir: string, latest: bool, options: Options,
if pkgInfo.bin.len > 0: if pkgInfo.bin.len > 0:
createDir(binDir) createDir(binDir)
# Copy all binaries and files that are not skipped # Copy all binaries and files that are not skipped
filesInstalled = copyFilesRec(realDir, realDir, pkgDestDir, options, filesInstalled = copyFilesRec(realDir, realDir, pkgDestDir, opt,
pkgInfo) pkgInfo)
# Set file permissions to +x for all binaries built, # Set file permissions to +x for all binaries built,
# and symlink them on *nix OS' to $nimbleDir/bin/ # and symlink them on *nix OS' to $nimbleDir/bin/
@ -607,7 +612,7 @@ proc installFromDir(dir: string, latest: bool, options: Options,
let dest = binDir / cleanBin.changeFileExt("cmd") let dest = binDir / cleanBin.changeFileExt("cmd")
echo("Creating stub: ", pkgDestDir / bin, " -> ", dest) echo("Creating stub: ", pkgDestDir / bin, " -> ", dest)
var contents = "@" var contents = "@"
if options.config.chcp: if opt.config.chcp:
if fixChcp: if fixChcp:
contents.add "chcp 65001 > nul && " contents.add "chcp 65001 > nul && "
else: contents.add "chcp 65001 > nul\n@" else: contents.add "chcp 65001 > nul\n@"
@ -620,7 +625,7 @@ proc installFromDir(dir: string, latest: bool, options: Options,
else: else:
{.error: "Sorry, your platform is not supported.".} {.error: "Sorry, your platform is not supported.".}
else: else:
filesInstalled = copyFilesRec(realDir, realDir, pkgDestDir, options, filesInstalled = copyFilesRec(realDir, realDir, pkgDestDir, opt,
pkgInfo) pkgInfo)
# Save a nimblemeta.json file. # Save a nimblemeta.json file.
@ -659,46 +664,46 @@ proc downloadPkg(url: string, verRange: VersionRange,
echo("Downloading ", url, " into ", downloadDir, " using ", downMethod, "...") echo("Downloading ", url, " into ", downloadDir, " using ", downMethod, "...")
result = (downloadDir, doDownload(url, downloadDir, verRange, downMethod)) result = (downloadDir, doDownload(url, downloadDir, verRange, downMethod))
proc getDownloadInfo*(pv: PkgTuple, options: Options, proc getDownloadInfo*(pv: PkgTuple, opt: Options,
doPrompt: bool): (DownloadMethod, string) = doPrompt: bool): (DownloadMethod, string) =
if pv.name.isURL: if pv.name.isURL:
return (checkUrlType(pv.name), pv.name) return (checkUrlType(pv.name), pv.name)
else: else:
var pkg: Package var pkg: Package
if getPackage(pv.name, options.getNimbleDir() / "packages.json", pkg): if getPackage(pv.name, opt.getNimbleDir() / "packages.json", pkg):
return (pkg.downloadMethod.getDownloadMethod(), pkg.url) return (pkg.downloadMethod.getDownloadMethod(), pkg.url)
else: else:
# If package is not found give the user a chance to update # If package is not found give the user a chance to update
# package.json # package.json
if doPrompt and if doPrompt and
options.prompt(pv.name & " not found in local packages.json, " & opt.prompt(pv.name & " not found in local packages.json, " &
"check internet for updated packages?"): "check internet for updated packages?"):
update(options) update(opt)
return getDownloadInfo(pv, options, doPrompt) return getDownloadInfo(pv, opt, doPrompt)
else: else:
raise newException(NimbleError, "Package not found.") raise newException(NimbleError, "Package not found.")
proc install(packages: seq[PkgTuple], proc install(packages: seq[PkgTuple],
options: Options, opt: Options,
doPrompt = true): tuple[paths: seq[string], pkg: PackageInfo] = doPrompt = true): tuple[paths: seq[string], pkg: PackageInfo] =
if packages == @[]: if packages == @[]:
result = installFromDir(getCurrentDir(), false, options, "") result = installFromDir(getCurrentDir(), false, opt, "")
else: else:
# If packages.json is not present ask the user if they want to download it. # If packages.json is not present ask the user if they want to download it.
if not existsFile(options.getNimbleDir / "packages.json"): if not existsFile(opt.getNimbleDir / "packages.json"):
if doPrompt and if doPrompt and
options.prompt("Local packages.json not found, download it from " & opt.prompt("Local packages.json not found, download it from " &
"internet?"): "internet?"):
update(options) update(opt)
else: else:
quit("Please run nimble update.", QuitFailure) quit("Please run nimble update.", QuitFailure)
# Install each package. # Install each package.
for pv in packages: for pv in packages:
let (meth, url) = getDownloadInfo(pv, options, doPrompt) let (meth, url) = getDownloadInfo(pv, opt, doPrompt)
let (downloadDir, downloadVersion) = downloadPkg(url, pv.ver, meth) let (downloadDir, downloadVersion) = downloadPkg(url, pv.ver, meth)
try: try:
result = installFromDir(downloadDir, false, options, url) result = installFromDir(downloadDir, false, opt, url)
except BuildFailed: except BuildFailed:
# The package failed to build. # The package failed to build.
# Check if we tried building a tagged version of the package. # Check if we tried building a tagged version of the package.
@ -707,37 +712,37 @@ proc install(packages: seq[PkgTuple],
# If we tried building a tagged version of the package then # If we tried building a tagged version of the package then
# ask the user whether they want to try building #head. # ask the user whether they want to try building #head.
let promptResult = doPrompt and let promptResult = doPrompt and
options.prompt(("Build failed for '$1@$2', would you" & opt.prompt(("Build failed for '$1@$2', would you" &
" like to try installing '$1@#head' (latest unstable)?") % " like to try installing '$1@#head' (latest unstable)?") %
[pv.name, $downloadVersion]) [pv.name, $downloadVersion])
if promptResult: if promptResult:
result = install(@[(pv.name, headVer)], options, doPrompt) result = install(@[(pv.name, headVer)], opt, doPrompt)
else: else:
raise newException(BuildFailed, raise newException(BuildFailed,
"Aborting installation due to build failure") "Aborting installation due to build failure")
else: else:
raise raise
proc build(options: Options) = proc build(opt: Options) =
var pkgInfo = getPkgInfo(getCurrentDir()) var pkgInfo = getPkgInfo(getCurrentDir())
let paths = processDeps(pkginfo, options) let paths = processDeps(pkginfo, opt)
buildFromDir(pkgInfo, paths, false) buildFromDir(pkgInfo, paths, false)
proc compile(options: Options) = proc compile(opt: Options) =
var pkgInfo = getPkgInfo(getCurrentDir()) var pkgInfo = getPkgInfo(getCurrentDir())
let paths = processDeps(pkginfo, options) let paths = processDeps(pkginfo, opt)
let realDir = pkgInfo.getRealDir() let realDir = pkgInfo.getRealDir()
var args = "" var args = ""
for path in paths: args.add("--path:\"" & path & "\" ") for path in paths: args.add("--path:\"" & path & "\" ")
for option in options.action.compileOptions: for option in opt.action.compileOptions:
args.add(option & " ") args.add(option & " ")
let bin = options.action.file let bin = opt.action.file
let backend = let backend =
if options.action.backend.len > 0: if opt.action.backend.len > 0:
options.action.backend opt.action.backend
else: else:
pkgInfo.backend pkgInfo.backend
@ -749,27 +754,27 @@ proc compile(options: Options) =
doCmd(getNimBin() & " $# --noBabelPath $# \"$#\"" % doCmd(getNimBin() & " $# --noBabelPath $# \"$#\"" %
[backend, args, bin]) [backend, args, bin])
proc search(options: Options) = proc search(opt: Options) =
## Searches for matches in ``options.action.search``. ## Searches for matches in ``opt.action.search``.
## ##
## Searches are done in a case insensitive way making all strings lower case. ## Searches are done in a case insensitive way making all strings lower case.
assert options.action.typ == actionSearch assert opt.action.typ == actionSearch
if options.action.search == @[]: if opt.action.search == @[]:
raise newException(NimbleError, "Please specify a search string.") raise newException(NimbleError, "Please specify a search string.")
if not existsFile(options.getNimbleDir() / "packages.json"): if not existsFile(opt.getNimbleDir() / "packages.json"):
raise newException(NimbleError, "Please run nimble update.") raise newException(NimbleError, "Please run nimble update.")
let pkgList = getPackageList(options.getNimbleDir() / "packages.json") let pkgList = getPackageList(opt.getNimbleDir() / "packages.json")
var found = false var found = false
template onFound: stmt = template onFound: stmt =
echoPackage(pkg) echoPackage(pkg)
if options.queryVersions: if opt.queryVersions:
echoPackageVersions(pkg) echoPackageVersions(pkg)
echo(" ") echo(" ")
found = true found = true
break break
for pkg in pkgList: for pkg in pkgList:
for word in options.action.search: for word in opt.action.search:
# Search by name. # Search by name.
if word.toLower() in pkg.name.toLower(): if word.toLower() in pkg.name.toLower():
onFound() onFound()
@ -781,19 +786,19 @@ proc search(options: Options) =
if not found: if not found:
echo("No package found.") echo("No package found.")
proc list(options: Options) = proc list(opt: Options) =
if not existsFile(options.getNimbleDir() / "packages.json"): if not existsFile(opt.getNimbleDir() / "packages.json"):
raise newException(NimbleError, "Please run nimble update.") raise newException(NimbleError, "Please run nimble update.")
let pkgList = getPackageList(options.getNimbleDir() / "packages.json") let pkgList = getPackageList(opt.getNimbleDir() / "packages.json")
for pkg in pkgList: for pkg in pkgList:
echoPackage(pkg) echoPackage(pkg)
if options.queryVersions: if opt.queryVersions:
echoPackageVersions(pkg) echoPackageVersions(pkg)
echo(" ") echo(" ")
proc listInstalled(options: Options) = proc listInstalled(opt: Options) =
var h = initTable[string, seq[string]]() var h = initTable[string, seq[string]]()
let pkgs = getInstalledPkgs(options.getPkgsDir()) let pkgs = getInstalledPkgs(opt.getPkgsDir())
for x in pkgs.items(): for x in pkgs.items():
let let
pName = x.pkginfo.name pName = x.pkginfo.name
@ -807,7 +812,7 @@ proc listInstalled(options: Options) =
type VersionAndPath = tuple[version: Version, path: string] type VersionAndPath = tuple[version: Version, path: string]
proc listPaths(options: Options) = proc listPaths(opt: Options) =
## Loops over installing packages displaying their installed paths. ## Loops over installing packages displaying their installed paths.
## ##
## If there are several packages installed, only the last one (the version ## If there are several packages installed, only the last one (the version
@ -816,25 +821,27 @@ proc listPaths(options: Options) =
## but at the end quits with a non zero exit error. ## but at the end quits with a non zero exit error.
## ##
## On success the proc returns normally. ## On success the proc returns normally.
assert options.action.typ == actionPath assert opt.action.typ == actionPath
assert(not options.action.packages.isNil) assert(not opt.action.packages.isNil)
var errors = 0 var errors = 0
for name, version in options.action.packages.items: for name, version in opt.action.packages.items:
var installed: seq[VersionAndPath] = @[] var installed: seq[VersionAndPath] = @[]
# There may be several, list all available ones and sort by version. # There may be several, list all available ones and sort by version.
for kind, path in walkDir(options.getPkgsDir): for kind, path in walkDir(opt.getPkgsDir):
if kind != pcDir or not path.startsWith(options.getPkgsDir / name): if kind != pcDir or not path.startsWith(opt.getPkgsDir / name):
continue continue
let let
nimScriptFile = path / name.addFileExt("nims")
babelFile = path / name.addFileExt("babel") babelFile = path / name.addFileExt("babel")
nimbleFile = path / name.addFileExt("nimble") nimbleFile = path / name.addFileExt("nimble")
hasSpec = nimbleFile.existsFile or babelFile.existsFile hasSpec = nimScriptFile.existsFile or
nimbleFile.existsFile or babelFile.existsFile
if hasSpec: if hasSpec:
var pkgInfo = getPkgInfo(path) var pkgInfo = getPkgInfo(path)
var v: VersionAndPath var v: VersionAndPath
v.version = newVersion(pkgInfo.version) v.version = newVersion(pkgInfo.version)
v.path = options.getPkgsDir / (pkgInfo.name & '-' & pkgInfo.version) v.path = opt.getPkgsDir / (pkgInfo.name & '-' & pkgInfo.version)
installed.add(v) installed.add(v)
else: else:
echo "Warning: No .nimble file found for ", path echo "Warning: No .nimble file found for ", path
@ -849,56 +856,64 @@ proc listPaths(options: Options) =
raise newException(NimbleError, raise newException(NimbleError,
"At least one of the specified packages was not found") "At least one of the specified packages was not found")
proc init(options: Options) = proc guessAuthor(): string =
if dirExists(os.getCurrentDir() / ".git"):
let (output, exitCode) = doCmdEx("git config user.name")
if exitCode == 0:
return output.string.strip
return "Anonymous"
proc init(opt: Options) =
echo("Initializing new Nimble project!") echo("Initializing new Nimble project!")
var var
pkgName, fName: string = "" pkgName, fName: string = ""
outFile: File outFile: File
if (options.action.projName != ""): if opt.action.projName != "":
pkgName = options.action.projName pkgName = opt.action.projName
fName = pkgName & ".nimble" fName = pkgName & NimsExt
if (existsFile(os.getCurrentDir() / fName)): if (existsFile(os.getCurrentDir() / fName)):
raise newException(NimbleError, "Already have a nimble file.") raise newException(NimbleError, "Already have a nimscript file.")
else: else:
echo("Enter a project name for this (blank to use working directory), " & echo("Enter a project name for this (blank to use working directory), " &
"Ctrl-C to abort:") "Ctrl-C to abort:")
pkgName = readline(stdin) pkgName = readline(stdin)
if (pkgName == ""): if pkgName == "":
pkgName = os.getCurrentDir().splitPath.tail pkgName = os.getCurrentDir().splitPath.tail
if (pkgName == ""): if pkgName == "":
raise newException(NimbleError, "Could not get default file path.") raise newException(NimbleError, "Could not get default file path.")
fName = pkgName & ".nimble" fName = pkgName & NimsExt
# Now need to write out .nimble file with projName and other details # Now need to write out .nimble file with projName and other details
if (not existsFile(os.getCurrentDir() / fName) and if (not existsFile(os.getCurrentDir() / fName) and
open(f=outFile, filename = fName, mode = fmWrite)): open(f=outFile, filename = fName, mode = fmWrite)):
outFile.writeln("[Package]") outFile.writeLine """# Package
outFile.writeln("name = \"" & pkgName & "\"")
outFile.writeln("version = \"0.1.0\"")
outFile.writeln("author = \"Anonymous\"")
outFile.writeln("description = \"New Nimble project for Nim\"")
outFile.writeln("license = \"MIT\"")
outFile.writeln("")
outFile.writeln("[Deps]")
outFile.writeln("Requires: \"nim >= 0.10.0\"")
close(outFile)
version = "1.0.0"
author = $1
description = ""
license = "MIT"
# Dependencies
requires "nim >= "0.11.2"
""" % guessAuthor().escape()
close(outFile)
else: else:
raise newException(NimbleError, "Unable to open file " & fName & raise newException(NimbleError, "Unable to open file " & fName &
" for writing: " & osErrorMsg(osLastError())) " for writing: " & osErrorMsg(osLastError()))
proc uninstall(options: Options) = proc uninstall(opt: Options) =
if options.action.packages.len == 0: if opt.action.packages.len == 0:
raise newException(NimbleError, raise newException(NimbleError,
"Please specify the package(s) to uninstall.") "Please specify the package(s) to uninstall.")
var pkgsToDelete: seq[PackageInfo] = @[] var pkgsToDelete: seq[PackageInfo] = @[]
# Do some verification. # Do some verification.
for pkgTup in options.action.packages: for pkgTup in opt.action.packages:
echo("Looking for ", pkgTup.name, " (", $pkgTup.ver, ")...") echo("Looking for ", pkgTup.name, " (", $pkgTup.ver, ")...")
let installedPkgs = getInstalledPkgs(options.getPkgsDir()) let installedPkgs = getInstalledPkgs(opt.getPkgsDir())
var pkgList = findAllPkgs(installedPkgs, pkgTup) var pkgList = findAllPkgs(installedPkgs, pkgTup)
if pkgList.len == 0: if pkgList.len == 0:
raise newException(NimbleError, "Package not found") raise newException(NimbleError, "Package not found")
@ -908,7 +923,7 @@ proc uninstall(options: Options) =
for pkg in pkgList: for pkg in pkgList:
# Check whether any packages depend on the ones the user is trying to # Check whether any packages depend on the ones the user is trying to
# uninstall. # uninstall.
let thisPkgsDep = options.nimbleData["reverseDeps"]{pkg.name}{pkg.version} let thisPkgsDep = opt.nimbleData["reverseDeps"]{pkg.name}{pkg.version}
if not thisPkgsDep.isNil: if not thisPkgsDep.isNil:
var reason = "" var reason = ""
if thisPkgsDep.len == 1: if thisPkgsDep.len == 1:
@ -936,47 +951,50 @@ proc uninstall(options: Options) =
pkgNames.add pkg.name & " (" & pkg.version & ")" pkgNames.add pkg.name & " (" & pkg.version & ")"
# Let's confirm that the user wants these packages removed. # Let's confirm that the user wants these packages removed.
if not options.prompt("The following packages will be removed:\n " & if not opt.prompt("The following packages will be removed:\n " &
pkgNames & "\nDo you wish to continue?"): pkgNames & "\nDo you wish to continue?"):
quit(QuitSuccess) quit(QuitSuccess)
for pkg in pkgsToDelete: for pkg in pkgsToDelete:
# If we reach this point then the package can be safely removed. # If we reach this point then the package can be safely removed.
removeRevDep(options, pkg) removeRevDep(opt, pkg)
removePkgDir(options.getPkgsDir / (pkg.name & '-' & pkg.version), options) removePkgDir(opt.getPkgsDir / (pkg.name & '-' & pkg.version), opt)
echo("Removed ", pkg.name, " (", $pkg.version, ")") echo("Removed ", pkg.name, " (", $pkg.version, ")")
proc doAction(options: Options) = proc getCommand: string = options.command
if not existsDir(options.getNimbleDir()):
createDir(options.getNimbleDir())
if not existsDir(options.getPkgsDir):
createDir(options.getPkgsDir)
case options.action.typ proc doAction(opt: Options) =
of actionUpdate: if not existsDir(opt.getNimbleDir()):
update(options) createDir(opt.getNimbleDir())
of actionInstall: if not existsDir(opt.getPkgsDir):
discard install(options.action.packages, options) createDir(opt.getPkgsDir)
of actionUninstall:
uninstall(options) case getCommand().normalize
of actionSearch: of "udpate":
search(options) update(opt)
of actionList: of "install":
if options.queryInstalled: listInstalled(options) discard install(opt.action.packages, opt)
else: list(options) of "uninstall", "remove", "delete", "del", "rm":
of actionPath: uninstall(opt)
listPaths(options) of "search":
of actionBuild: search(opt)
build(options) of "list":
of actionCompile: if opt.queryInstalled: listInstalled(opt)
compile(options) else: list(opt)
of actionInit: of "path":
init(options) listPaths(opt)
of actionPublish: of "build":
build(opt)
of "c", "compile", "js", "cpp", "cc":
compile(opt)
of "init":
init(opt)
of "publish":
var pkgInfo = getPkgInfo(getCurrentDir()) var pkgInfo = getPkgInfo(getCurrentDir())
publish(pkgInfo) publish(pkgInfo)
of actionNil: of "nop": discard
assert false else:
raise newException(NimbleError, "Unknown command: " & getCommand())
when isMainModule: when isMainModule:
when defined(release): when defined(release):

View file

@ -1 +1,2 @@
-d:ssl -d:ssl
--path:"$lib/packages/docutils"

View file

@ -4,9 +4,9 @@
# recursive imports # recursive imports
import version import version
export version.NimbleError
type type
NimbleError* = object of Exception
BuildFailed* = object of NimbleError BuildFailed* = object of NimbleError
PackageInfo* = object PackageInfo* = object
@ -27,4 +27,3 @@ type
binDir*: string binDir*: string
srcDir*: string srcDir*: string
backend*: string backend*: string

View file

@ -0,0 +1,114 @@
# Copyright (C) Andreas Rumpf. All rights reserved.
# BSD License. Look at license.txt for more info.
## Implements the new configuration system for Nimble. Uses Nim as a
## scripting language.
import
compiler/ast, compiler/modules, compiler/passes, compiler/passaux,
compiler/condsyms, compiler/options, compiler/sem, compiler/semdata,
compiler/llstream, compiler/vm, compiler/vmdef, compiler/commands,
compiler/msgs, compiler/magicsys, compiler/lists
from compiler/scriptconfig import setupVM
import nimbletypes, version
import os, strutils
proc raiseVariableError(ident, typ: string) {.noinline.} =
raise newException(NimbleError,
"NimScript's variable '" & ident & "' needs a value of type '" & typ & "'.")
proc isStrLit(n: PNode): bool = n.kind in {nkStrLit..nkTripleStrLit}
proc getGlobal(ident: string): string =
let n = vm.globalCtx.getGlobalValue(getSysSym ident)
if n.isStrLit:
result = n.strVal
else:
raiseVariableError(ident, "string")
proc getGlobalAsSeq(ident: string): seq[string] =
let n = vm.globalCtx.getGlobalValue(getSysSym ident)
result = @[]
if n.kind == nkBracket:
for x in n:
if x.isStrLit:
result.add n.strVal
else:
raiseVariableError(ident, "seq[string]")
else:
raiseVariableError(ident, "seq[string]")
proc extractRequires(result: var seq[PkgTuple]) =
let n = vm.globalCtx.getGlobalValue(getSysSym "requiresData")
if n.kind == nkBracket:
for x in n:
if x.kind == nkPar and x.len == 2 and x[0].isStrLit and x[1].isStrLit:
result.add(parseRequires(x[0].strVal & x[1].strVal))
elif x.isStrLit:
result.add(parseRequires(x.strVal))
else:
raiseVariableError("requiresData", "seq[(string, VersionReq)]")
else:
raiseVariableError("requiresData", "seq[(string, VersionReq)]")
proc readPackageInfoFromNims*(scriptName: string; result: var PackageInfo) =
passes.gIncludeFile = includeModule
passes.gImportModule = importModule
initDefines()
defineSymbol("nimscript")
defineSymbol("nimconfig")
defineSymbol("nimble")
registerPass(semPass)
registerPass(evalPass)
appendStr(searchPaths, options.libpath)
var m = makeModule(scriptName)
incl(m.flags, sfMainModule)
vm.globalCtx = setupVM(m, scriptName)
compileSystemModule()
processModule(m, llStreamOpen(scriptName, fmRead), nil)
template trivialField(field) =
result.field = getGlobal(astToStr field)
template trivialFieldSeq(field) =
result.field.add getGlobalAsSeq(astToStr field)
# keep reasonable default:
let name = getGlobal"packageName"
if name.len > 0: result.name = name
trivialField version
trivialField author
trivialField description
trivialField license
trivialField srcdir
trivialField bindir
trivialFieldSeq skipDirs
trivialFieldSeq skipFiles
trivialFieldSeq skipExt
trivialFieldSeq installDirs
trivialFieldSeq installFiles
trivialFieldSeq installExt
extractRequires result.requires
let binSeq = getGlobalAsSeq("bin")
for i in binSeq:
result.bin.add(i.addFileExt(ExeExt))
let backend = getGlobal("backend")
if cmpIgnoreStyle(backend, "javascript") == 0:
result.backend = "js"
else:
result.backend = backend.toLower()
# ensure everything can be called again:
resetAllModulesHard()
vm.globalCtx = nil
initDefines()

View file

@ -1,7 +1,11 @@
# Copyright (C) Dominik Picheta. All rights reserved. # Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info. # BSD License. Look at license.txt for more info.
import parsecfg, json, streams, strutils, parseutils, os import parsecfg, json, streams, strutils, parseutils, os
import version, tools, nimbletypes import version, tools, nimbletypes, nimscriptsupport
const
NimsExt* = ".nims"
type type
Package* = object Package* = object
# Required fields in a package. # Required fields in a package.
@ -11,7 +15,7 @@ type
downloadMethod*: string downloadMethod*: string
description*: string description*: string
tags*: seq[string] # Even if empty, always a valid non nil seq. \ tags*: seq[string] # Even if empty, always a valid non nil seq. \
# From here on, optional fields set to the emtpy string if not available. # From here on, optional fields set to the empty string if not available.
version*: string version*: string
dvcsTag*: string dvcsTag*: string
web*: string # Info url for humans. web*: string # Info url for humans.
@ -19,9 +23,12 @@ type
MetaData* = object MetaData* = object
url*: string url*: string
proc initPackageInfo(): PackageInfo = NimbleFile* = tuple[isNimScript: bool; file: string]
result.mypath = ""
result.name = "" proc initPackageInfo(path: string): PackageInfo =
result.mypath = path
# reasonable default:
result.name = path.splitFile.name
result.version = "" result.version = ""
result.author = "" result.author = ""
result.description = "" result.description = ""
@ -63,23 +70,6 @@ proc validatePackageInfo(pkgInfo: PackageInfo, path: string) =
"Version may only consist of numbers and the '.' character " & "Version may only consist of numbers and the '.' character " &
"but found '" & c & "'.") "but found '" & c & "'.")
proc parseRequires(req: string): PkgTuple =
try:
if ' ' in req:
var i = skipUntil(req, Whitespace)
result.name = req[0 .. i].strip
result.ver = parseVersionRange(req[i .. req.len-1])
elif '#' in req:
var i = skipUntil(req, {'#'})
result.name = req[0 .. i-1]
result.ver = parseVersionRange(req[i .. req.len-1])
else:
result.name = req.strip
result.ver = VersionRange(kind: verAny)
except ParseVersionError:
raise newException(NimbleError,
"Unable to parse dependency version range: " & getCurrentExceptionMsg())
proc multiSplit(s: string): seq[string] = proc multiSplit(s: string): seq[string] =
## Returns ``s`` split by newline and comma characters. ## Returns ``s`` split by newline and comma characters.
## ##
@ -96,9 +86,7 @@ proc multiSplit(s: string): seq[string] =
if len(result) < 1: if len(result) < 1:
return @[s] return @[s]
proc readPackageInfo*(path: string): PackageInfo = proc readPackageInfoFromNimble(path: string; result: var PackageInfo) =
result = initPackageInfo()
result.mypath = path
var fs = newFileStream(path, fmRead) var fs = newFileStream(path, fmRead)
if fs != nil: if fs != nil:
var p: CfgParser var p: CfgParser
@ -160,7 +148,35 @@ proc readPackageInfo*(path: string): PackageInfo =
close(p) close(p)
else: else:
raise newException(ValueError, "Cannot open package info: " & path) raise newException(ValueError, "Cannot open package info: " & path)
validatePackageInfo(result, path)
proc getNameVersion*(pkgpath: string): tuple[name, version: string] =
## Splits ``pkgpath`` in the format ``/home/user/.nimble/pkgs/package-0.1``
## into ``(packagea, 0.1)``
result.name = ""
result.version = ""
let tail = pkgpath.splitPath.tail
if '-' notin tail:
result.name = tail
return
for i in countdown(tail.len-1, 0):
if tail[i] == '-':
result.name = tail[0 .. i-1]
result.version = tail[i+1 .. tail.len-1]
break
proc readPackageInfo*(nf: NimbleFile; onlyMinimalInfo=false): PackageInfo =
result = initPackageInfo(nf.file)
if nf.isNimScript:
if onlyMinimalInfo:
let tmp = getNameVersion(nf.file)
result.name = tmp.name
result.version = tmp.version
else:
readPackageInfoFromNims(nf.file, result)
else:
readPackageInfoFromNimble(nf.file, result)
validatePackageInfo(result, nf.file)
proc optionalField(obj: JsonNode, name: string, default = ""): string = proc optionalField(obj: JsonNode, name: string, default = ""): string =
## Queries ``obj`` for the optional ``name`` string. ## Queries ``obj`` for the optional ``name`` string.
@ -237,21 +253,34 @@ proc getPackageList*(packagesPath: string): seq[Package] =
let pkg: Package = p.fromJson() let pkg: Package = p.fromJson()
result.add(pkg) result.add(pkg)
proc findNimbleFile*(dir: string): string = proc findNimbleFile*(dir: string; error: bool): NimbleFile =
result = "" result = (false, "")
var hits = 0
for kind, path in walkDir(dir): for kind, path in walkDir(dir):
if kind == pcFile and path.splitFile.ext in [".babel", ".nimble"]: if kind == pcFile:
if result != "": let ext = path.splitFile.ext
raise newException(NimbleError, case ext
"Only one .nimble file should be present in " & dir) of ".babel", ".nimble":
result = path result = (false, path)
inc hits
of NimsExt:
result = (true, path)
inc hits
else: discard
if hits >= 2:
raise newException(NimbleError,
"Only one .nimble file should be present in " & dir)
elif hits == 0:
if error:
raise newException(NimbleError,
"Specified directory does not contain a .nimble file.")
else:
# TODO: Abstract logging.
echo("WARNING: No .nimble file found for ", dir)
proc getPkgInfo*(dir: string): PackageInfo = proc getPkgInfo*(dir: string): PackageInfo =
## Find the .nimble file in ``dir`` and parses it, returning a PackageInfo. ## Find the .nimble file in ``dir`` and parses it, returning a PackageInfo.
let nimbleFile = findNimbleFile(dir) let nimbleFile = findNimbleFile(dir, true)
if nimbleFile == "":
raise newException(NimbleError,
"Specified directory does not contain a .nimble file.")
result = readPackageInfo(nimbleFile) result = readPackageInfo(nimbleFile)
proc getInstalledPkgs*(libsDir: string): proc getInstalledPkgs*(libsDir: string):
@ -262,13 +291,10 @@ proc getInstalledPkgs*(libsDir: string):
result = @[] result = @[]
for kind, path in walkDir(libsDir): for kind, path in walkDir(libsDir):
if kind == pcDir: if kind == pcDir:
let nimbleFile = findNimbleFile(path) let nimbleFile = findNimbleFile(path, false)
if nimbleFile != "": if nimbleFile.file != "":
let meta = readMetaData(path) let meta = readMetaData(path)
result.add((readPackageInfo(nimbleFile), meta)) result.add((readPackageInfo(nimbleFile, true), meta))
else:
# TODO: Abstract logging.
echo("WARNING: No .nimble file found for ", path)
proc findPkg*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]], proc findPkg*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]],
dep: PkgTuple, dep: PkgTuple,
@ -280,8 +306,8 @@ proc findPkg*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]],
## ##
## **Note**: dep.name here could be a URL, hence the need for pkglist.meta. ## **Note**: dep.name here could be a URL, hence the need for pkglist.meta.
for pkg in pkglist: for pkg in pkglist:
if pkg.pkginfo.name.normalize != dep.name.normalize and if cmpIgnoreStyle(pkg.pkginfo.name, dep.name) != 0 and
pkg.meta.url.normalize != dep.name.normalize: continue cmpIgnoreStyle(pkg.meta.url, dep.name) != 0: continue
if withinRange(newVersion(pkg.pkginfo.version), dep.ver): if withinRange(newVersion(pkg.pkginfo.version), dep.ver):
if not result or newVersion(r.version) < newVersion(pkg.pkginfo.version): if not result or newVersion(r.version) < newVersion(pkg.pkginfo.version):
r = pkg.pkginfo r = pkg.pkginfo
@ -294,8 +320,8 @@ proc findAllPkgs*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]],
## packages if multiple are found. ## packages if multiple are found.
result = @[] result = @[]
for pkg in pkglist: for pkg in pkglist:
if pkg.pkginfo.name.normalize != dep.name.normalize and if cmpIgnoreStyle(pkg.pkginfo.name, dep.name) != 0 and
pkg.meta.url.normalize != dep.name.normalize: continue cmpIgnoreStyle(pkg.meta.url, dep.name) != 0: continue
if withinRange(newVersion(pkg.pkginfo.version), dep.ver): if withinRange(newVersion(pkg.pkginfo.version), dep.ver):
result.add pkg.pkginfo result.add pkg.pkginfo
@ -314,22 +340,6 @@ proc getOutputDir*(pkgInfo: PackageInfo, bin: string): string =
else: else:
result = pkgInfo.mypath.splitFile.dir / bin result = pkgInfo.mypath.splitFile.dir / bin
proc getNameVersion*(pkgpath: string): tuple[name, version: string] =
## Splits ``pkgpath`` in the format ``/home/user/.nimble/pkgs/package-0.1``
## into ``(packagea, 0.1)``
result.name = ""
result.version = ""
let tail = pkgpath.splitPath.tail
if '-' notin tail:
result.name = tail
return
for i in countdown(tail.len-1, 0):
if tail[i] == '-':
result.name = tail[0 .. i-1]
result.version = tail[i+1 .. tail.len-1]
break
proc echoPackage*(pkg: Package) = proc echoPackage*(pkg: Package) =
echo(pkg.name & ":") echo(pkg.name & ":")
echo(" url: " & pkg.url & " (" & pkg.downloadMethod & ")") echo(" url: " & pkg.url & " (" & pkg.downloadMethod & ")")

View file

@ -33,6 +33,7 @@ type
PkgTuple* = tuple[name: string, ver: VersionRange] PkgTuple* = tuple[name: string, ver: VersionRange]
ParseVersionError* = object of ValueError ParseVersionError* = object of ValueError
NimbleError* = object of Exception
proc newVersion*(ver: string): Version = return Version(ver) proc newVersion*(ver: string): Version = return Version(ver)
proc newSpecial*(spe: string): Special = return Special(spe) proc newSpecial*(spe: string): Special = return Special(spe)
@ -198,6 +199,24 @@ proc parseVersionRange*(s: string): VersionRange =
"Unexpected char in version range: " & s[i]) "Unexpected char in version range: " & s[i])
inc(i) inc(i)
proc parseRequires*(req: string): PkgTuple =
try:
if ' ' in req:
var i = skipUntil(req, Whitespace)
result.name = req[0 .. i].strip
result.ver = parseVersionRange(req[i .. req.len-1])
elif '#' in req:
var i = skipUntil(req, {'#'})
result.name = req[0 .. i-1]
result.ver = parseVersionRange(req[i .. req.len-1])
else:
result.name = req.strip
result.ver = VersionRange(kind: verAny)
except ParseVersionError:
raise newException(NimbleError,
"Unable to parse dependency version range: " & getCurrentExceptionMsg())
proc `$`*(verRange: VersionRange): string = proc `$`*(verRange: VersionRange): string =
case verRange.kind case verRange.kind
of verLater: of verLater: