Squashed commit of the following:
commit e86a376f2faf9d26109405a3a9f73f986185f62d
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Sun Apr 28 15:37:22 2019 -0500
Fix caching issue
commit 640ce3f2e464e52668b5350fdc5a8fe506e79d38
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Thu Apr 25 18:38:48 2019 -0500
Clean up per feedback
commit ae3ef9f7a0cbad574b725d1bc7a83bd6115e19cc
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Thu Apr 25 16:39:26 2019 -0500
Fix for 0.19.4
commit 915d6b2be43e33bc51327585193b1899386ee250
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Thu Apr 25 16:13:42 2019 -0500
Keep nimscript separate, pin devel
commit c278bd6ba09771dc079029a87e3a375998f0b447
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Mon Apr 22 14:57:44 2019 -0500
Hardcode version, json{}, code width 80, isScriptResultCached, no blank paramStr check
commit 64e5489e256d5fc5abbfe3345789f65edf5980b7
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Wed Apr 17 21:07:03 2019 -0500
Remove compiler dependency
commit a031fffd70c118c16eb3e16d3b1ed10472baf5d7
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Wed Apr 17 16:49:09 2019 -0500
Add devel to travis
commit d49916e2a05b6bd7716f45bd8f74253fc8037827
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Wed Apr 17 16:43:14 2019 -0500
Interactive live, json to file
commit 24131deea4693199922f9a5697aa3d072cceaee1
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Wed Apr 17 12:40:27 2019 -0500
Fix empty param, json echo
commit b22fe37d47fd03367d49129ea4d2d56a779a6f26
Merge: 5cf0240 2942f11
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Tue Apr 16 22:23:17 2019 -0500
Merge branch 'nocompiler' of https://github.com/genotrance/nimble into nocompiler
commit 5cf0240b728ab6ff4a39ddf629ba5833eb8985f5
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Tue Apr 16 22:23:06 2019 -0500
No hints, live output
commit 2942f116c7774e0fa91f770cebde32bc431923a5
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Tue Apr 16 21:02:28 2019 -0500
Remove osx, test with stable
commit 85f3865ef195c7b813f0b9e30b5cc8c9b2756518
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Tue Apr 16 18:19:42 2019 -0500
Remove ospaths, fix tests for Windows
commit 74201bcfe4de00bdece5b31715618975f9ce8e6e
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Tue Apr 16 14:00:14 2019 -0500
No success for missing task
commit 8c2e65e223d32366b03004d9711364504c5d7916
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Tue Apr 16 13:44:32 2019 -0500
Fix packageName to name
commit b05d9480281ebae7a0f5fd0331c8627bbf2a77d5
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Tue Apr 16 13:29:37 2019 -0500
Add switch support
commit deecd903102a9baa5d4674cb9871cd9dbb658a04
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Tue Apr 16 12:24:01 2019 -0500
API cleanup, json setCommand fix
commit 1e95fd4104ec3ffb69fe67b9c2fac23f991e163a
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Tue Apr 16 10:45:12 2019 -0500
getParams once, hash nimscriptapi, fix loop in setcommand
commit 51d03b3845cd562796bb32d41d5ad17cd09a91e7
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Tue Apr 16 07:21:32 2019 -0500
getPkgDir impl
commit 7d0a40aa286d114d7557b229852f3c314795dc5d
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Mon Apr 15 14:24:02 2019 -0500
Before/after hook info
commit cbb3af3e970b20322030331d4849436b821f25ca
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Mon Apr 15 13:44:56 2019 -0500
Remove nims from package dir after exec
commit 0ed53d60bcdc8bb11beddb965590ed3ee63349d4
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Sat Apr 13 00:44:26 2019 -0500
Return bool from hooks
commit ab38b81b81e68cfccf3ca84fd854422cd3733c84
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Fri Apr 12 23:20:13 2019 -0500
Initial version
commit b9ef88b9f79b48435e7b4beeff959b4223f4b8ba
Merge: 220ebae c8d79fc
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Tue Mar 26 20:16:21 2019 -0500
Merge remote-tracking branch 'upstream/master' into nocompiler
commit 220ebae355c945963591b002a43b262a70640aa5
Merge: 3d7227c 119be48
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Wed Dec 12 18:02:10 2018 -0600
Merge remote-tracking branch 'upstream/master'
commit 3d7227c8900c205aada488d60565c90e17759639
Merge: cf7263d 66d79bf
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Wed Oct 17 13:39:51 2018 -0500
Merge remote-tracking branch 'upstream/master'
commit cf7263d6caf27ca4930ed54b05d4aa4f36e1dff1
Merge: 2fc3106 ee4c0ae
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Thu Sep 13 23:03:41 2018 -0500
Merge remote-tracking branch 'upstream/master'
commit 2fc310623b9f49ea012fc04fa09713fda140a7a3
Merge: e9a8850 c249f9b
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Thu Apr 26 16:27:31 2018 -0500
Merge remote-tracking branch 'upstream/master'
commit e9a885099b0b97bf3e0cddcde27e8c6b0bd51b10
Merge: 7adfd7b 75b7a21
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Thu Mar 8 14:26:46 2018 -0600
Merge remote-tracking branch 'upstream/master'
commit 7adfd7be2b38a52886640579845de378139ca0cc
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Mon Jan 15 00:35:55 2018 -0600
Updated fix for #398
commit de18319159b76a9da6765f35ea4d2e2c963d688a
Merge: 93ba4a0 3dae264
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Sun Jan 14 22:01:20 2018 -0600
Merge remote-tracking branch 'upstream/master'
commit 93ba4a00820ccb9a5362f0398cf3b5b4782bbefe
Author: Ganesh Viswanathan <dev@genotrance.com>
Date: Sat Jan 13 19:52:34 2018 -0600
Fix for #398
494 lines
18 KiB
Nim
Executable file
494 lines
18 KiB
Nim
Executable file
# Copyright (C) Dominik Picheta. All rights reserved.
|
|
# BSD License. Look at license.txt for more info.
|
|
import parsecfg, json, sets, streams, strutils, parseutils, os, tables, sugar
|
|
from sequtils import apply, map
|
|
|
|
import version, tools, common, nimscriptwrapper, options, packageinfo, cli
|
|
|
|
## Contains procedures for parsing .nimble files. Moved here from ``packageinfo``
|
|
## because it depends on ``nimscriptwrapper`` (``nimscriptwrapper`` also
|
|
## depends on other procedures in ``packageinfo``.
|
|
|
|
type
|
|
NimbleFile* = string
|
|
|
|
ValidationError* = object of NimbleError
|
|
warnInstalled*: bool # Determines whether to show a warning for installed pkgs
|
|
warnAll*: bool
|
|
|
|
const reservedNames = [
|
|
"CON",
|
|
"PRN",
|
|
"AUX",
|
|
"NUL",
|
|
"COM1",
|
|
"COM2",
|
|
"COM3",
|
|
"COM4",
|
|
"COM5",
|
|
"COM6",
|
|
"COM7",
|
|
"COM8",
|
|
"COM9",
|
|
"LPT1",
|
|
"LPT2",
|
|
"LPT3",
|
|
"LPT4",
|
|
"LPT5",
|
|
"LPT6",
|
|
"LPT7",
|
|
"LPT8",
|
|
"LPT9",
|
|
]
|
|
|
|
proc newValidationError(msg: string, warnInstalled: bool,
|
|
hint: string, warnAll: bool): ref ValidationError =
|
|
result = newException(ValidationError, msg)
|
|
result.warnInstalled = warnInstalled
|
|
result.warnAll = warnAll
|
|
result.hint = hint
|
|
|
|
proc raiseNewValidationError(msg: string, warnInstalled: bool,
|
|
hint: string = "", warnAll = false) =
|
|
raise newValidationError(msg, warnInstalled, hint, warnAll)
|
|
|
|
proc validatePackageName*(name: string) =
|
|
## Raises an error if specified package name contains invalid characters.
|
|
##
|
|
## A valid package name is one which is a valid nim module name. So only
|
|
## underscores, letters and numbers allowed.
|
|
if name.len == 0: return
|
|
|
|
if name[0] in {'0'..'9'}:
|
|
raiseNewValidationError(name &
|
|
"\"$1\" is an invalid package name: cannot begin with $2" %
|
|
[name, $name[0]], true)
|
|
|
|
var prevWasUnderscore = false
|
|
for c in name:
|
|
case c
|
|
of '_':
|
|
if prevWasUnderscore:
|
|
raiseNewValidationError(
|
|
"$1 is an invalid package name: cannot contain \"__\"" % name, true)
|
|
prevWasUnderscore = true
|
|
of AllChars - IdentChars:
|
|
raiseNewValidationError(
|
|
"$1 is an invalid package name: cannot contain '$2'" % [name, $c],
|
|
true)
|
|
else:
|
|
prevWasUnderscore = false
|
|
|
|
if name.endsWith("pkg"):
|
|
raiseNewValidationError("\"$1\" is an invalid package name: cannot end" &
|
|
" with \"pkg\"" % name, false)
|
|
if name.toUpperAscii() in reservedNames:
|
|
raiseNewValidationError(
|
|
"\"$1\" is an invalid package name: reserved name" % name, false)
|
|
|
|
proc validateVersion*(ver: string) =
|
|
for c in ver:
|
|
if c notin ({'.'} + Digits):
|
|
raiseNewValidationError(
|
|
"Version may only consist of numbers and the '.' character " &
|
|
"but found '" & c & "'.", false)
|
|
|
|
proc validatePackageStructure(pkgInfo: PackageInfo, options: Options) =
|
|
## This ensures that a package's source code does not leak into
|
|
## another package's namespace.
|
|
## https://github.com/nim-lang/nimble/issues/144
|
|
let
|
|
realDir = pkgInfo.getRealDir()
|
|
normalizedBinNames = pkgInfo.bin.map(
|
|
(x) => x.changeFileExt("").toLowerAscii()
|
|
)
|
|
correctDir =
|
|
if pkgInfo.name.toLowerAscii() in normalizedBinNames:
|
|
pkgInfo.name & "pkg"
|
|
else:
|
|
pkgInfo.name
|
|
|
|
proc onFile(path: string) =
|
|
# Remove the root to leave only the package subdirectories.
|
|
# ~/package-0.1/package/utils.nim -> package/utils.nim.
|
|
var trailPath = changeRoot(realDir, "", path)
|
|
if trailPath.startsWith(DirSep): trailPath = trailPath[1 .. ^1]
|
|
let (dir, file, ext) = trailPath.splitFile
|
|
# We're only interested in nim files, because only they can pollute our
|
|
# namespace.
|
|
if ext != (ExtSep & "nim"):
|
|
return
|
|
|
|
if dir.len == 0:
|
|
if file != pkgInfo.name:
|
|
# A source file was found in the top level of srcDir that doesn't share
|
|
# a name with the package.
|
|
let
|
|
msg = ("Package '$1' has an incorrect structure. " &
|
|
"The top level of the package source directory " &
|
|
"should contain at most one module, " &
|
|
"named '$2', but a file named '$3' was found. This " &
|
|
"will be an error in the future.") %
|
|
[pkgInfo.name, pkgInfo.name & ext, file & ext]
|
|
hint = ("If this is the primary source file in the package, " &
|
|
"rename it to '$1'. If it's a source file required by " &
|
|
"the main module, or if it is one of several " &
|
|
"modules exposed by '$4', then move it into a '$2' subdirectory. " &
|
|
"If it's a test file or otherwise not required " &
|
|
"to build the the package '$1', prevent its installation " &
|
|
"by adding `skipFiles = @[\"$3\"]` to the .nimble file. See " &
|
|
"https://github.com/nim-lang/nimble#libraries for more info.") %
|
|
[pkgInfo.name & ext, correctDir & DirSep, file & ext, pkgInfo.name]
|
|
raiseNewValidationError(msg, true, hint, true)
|
|
else:
|
|
assert(not pkgInfo.isMinimal)
|
|
# On Windows `pkgInfo.bin` has a .exe extension, so we need to normalize.
|
|
if not (dir.startsWith(correctDir & DirSep) or dir == correctDir):
|
|
let
|
|
msg = ("Package '$2' has an incorrect structure. " &
|
|
"It should contain a single directory hierarchy " &
|
|
"for source files, named '$3', but file '$1' " &
|
|
"is in a directory named '$4' instead. " &
|
|
"This will be an error in the future.") %
|
|
[file & ext, pkgInfo.name, correctDir, dir]
|
|
hint = ("If '$1' contains source files for building '$2', rename it " &
|
|
"to '$3'. Otherwise, prevent its installation " &
|
|
"by adding `skipDirs = @[\"$1\"]` to the .nimble file.") %
|
|
[dir, pkgInfo.name, correctDir]
|
|
raiseNewValidationError(msg, true, hint, true)
|
|
|
|
iterInstallFiles(realDir, pkgInfo, options, onFile)
|
|
|
|
proc validatePackageInfo(pkgInfo: PackageInfo, options: Options) =
|
|
let path = pkgInfo.myPath
|
|
if pkgInfo.name == "":
|
|
raiseNewValidationError("Incorrect .nimble file: " & path &
|
|
" does not contain a name field.", false)
|
|
|
|
if pkgInfo.name.normalize != path.splitFile.name.normalize:
|
|
raiseNewValidationError(
|
|
"The .nimble file name must match name specified inside " & path, true)
|
|
|
|
if pkgInfo.version == "":
|
|
raiseNewValidationError("Incorrect .nimble file: " & path &
|
|
" does not contain a version field.", false)
|
|
|
|
if not pkgInfo.isMinimal:
|
|
if pkgInfo.author == "":
|
|
raiseNewValidationError("Incorrect .nimble file: " & path &
|
|
" does not contain an author field.", false)
|
|
if pkgInfo.description == "":
|
|
raiseNewValidationError("Incorrect .nimble file: " & path &
|
|
" does not contain a description field.", false)
|
|
if pkgInfo.license == "":
|
|
raiseNewValidationError("Incorrect .nimble file: " & path &
|
|
" does not contain a license field.", false)
|
|
if pkgInfo.backend notin ["c", "cc", "objc", "cpp", "js"]:
|
|
raiseNewValidationError("'" & pkgInfo.backend &
|
|
"' is an invalid backend.", false)
|
|
|
|
validatePackageStructure(pkginfo, options)
|
|
|
|
|
|
proc nimScriptHint*(pkgInfo: PackageInfo) =
|
|
if not pkgInfo.isNimScript:
|
|
display("Warning:", "The .nimble file for this project could make use of " &
|
|
"additional features, if converted into the new NimScript format." &
|
|
"\nFor more details see:" &
|
|
"https://github.com/nim-lang/nimble#creating-packages",
|
|
Warning, HighPriority)
|
|
|
|
proc multiSplit(s: string): seq[string] =
|
|
## Returns ``s`` split by newline and comma characters.
|
|
##
|
|
## Before returning, all individual entries are stripped of whitespace and
|
|
## also empty entries are purged from the list. If after all the cleanups are
|
|
## done no entries are found in the list, the proc returns a sequence with
|
|
## the original string as the only entry.
|
|
result = split(s, {char(0x0A), char(0x0D), ','})
|
|
apply(result, proc(x: var string) = x = x.strip())
|
|
for i in countdown(result.len()-1, 0):
|
|
if len(result[i]) < 1:
|
|
result.del(i)
|
|
# Huh, nothing to return? Return given input.
|
|
if len(result) < 1:
|
|
return @[s]
|
|
|
|
proc readPackageInfoFromNimble(path: string; result: var PackageInfo) =
|
|
var fs = newFileStream(path, fmRead)
|
|
if fs != nil:
|
|
var p: CfgParser
|
|
open(p, fs, path)
|
|
defer: close(p)
|
|
var currentSection = ""
|
|
while true:
|
|
var ev = next(p)
|
|
case ev.kind
|
|
of cfgEof:
|
|
break
|
|
of cfgSectionStart:
|
|
currentSection = ev.section
|
|
of cfgKeyValuePair:
|
|
case currentSection.normalize
|
|
of "package":
|
|
case ev.key.normalize
|
|
of "name": result.name = ev.value
|
|
of "version": result.version = ev.value
|
|
of "author": result.author = ev.value
|
|
of "description": result.description = ev.value
|
|
of "license": result.license = ev.value
|
|
of "srcdir": result.srcDir = ev.value
|
|
of "bindir": result.binDir = ev.value
|
|
of "skipdirs":
|
|
result.skipDirs.add(ev.value.multiSplit)
|
|
of "skipfiles":
|
|
result.skipFiles.add(ev.value.multiSplit)
|
|
of "skipext":
|
|
result.skipExt.add(ev.value.multiSplit)
|
|
of "installdirs":
|
|
result.installDirs.add(ev.value.multiSplit)
|
|
of "installfiles":
|
|
result.installFiles.add(ev.value.multiSplit)
|
|
of "installext":
|
|
result.installExt.add(ev.value.multiSplit)
|
|
of "bin":
|
|
for i in ev.value.multiSplit:
|
|
result.bin.add(i.addFileExt(ExeExt))
|
|
of "backend":
|
|
result.backend = ev.value.toLowerAscii()
|
|
case result.backend.normalize
|
|
of "javascript": result.backend = "js"
|
|
else: discard
|
|
of "beforehooks":
|
|
for i in ev.value.multiSplit:
|
|
result.preHooks.incl(i.normalize)
|
|
of "afterhooks":
|
|
for i in ev.value.multiSplit:
|
|
result.postHooks.incl(i.normalize)
|
|
else:
|
|
raise newException(NimbleError, "Invalid field: " & ev.key)
|
|
of "deps", "dependencies":
|
|
case ev.key.normalize
|
|
of "requires":
|
|
for v in ev.value.multiSplit:
|
|
result.requires.add(parseRequires(v.strip))
|
|
else:
|
|
raise newException(NimbleError, "Invalid field: " & ev.key)
|
|
else: raise newException(NimbleError,
|
|
"Invalid section: " & currentSection)
|
|
of cfgOption: raise newException(NimbleError,
|
|
"Invalid package info, should not contain --" & ev.value)
|
|
of cfgError:
|
|
raise newException(NimbleError, "Error parsing .nimble file: " & ev.msg)
|
|
else:
|
|
raise newException(ValueError, "Cannot open package info: " & path)
|
|
|
|
proc readPackageInfoFromNims(scriptName: string, options: Options,
|
|
result: var PackageInfo) =
|
|
let
|
|
iniFile = getIniFile(scriptName, options)
|
|
|
|
if iniFile.fileExists():
|
|
readPackageInfoFromNimble(iniFile, result)
|
|
|
|
proc inferInstallRules(pkgInfo: var PackageInfo, options: Options) =
|
|
# Binary packages shouldn't install .nim files by default.
|
|
# (As long as the package info doesn't explicitly specify what should be
|
|
# installed.)
|
|
let installInstructions =
|
|
pkgInfo.installDirs.len + pkgInfo.installExt.len + pkgInfo.installFiles.len
|
|
if installInstructions == 0 and pkgInfo.bin.len > 0:
|
|
pkgInfo.skipExt.add("nim")
|
|
|
|
# When a package doesn't specify a `srcDir` it's fair to assume that
|
|
# the .nim files are in the root of the package. So we can explicitly select
|
|
# them and prevent the installation of anything else. The user can always
|
|
# override this with `installFiles`.
|
|
if pkgInfo.srcDir == "":
|
|
if dirExists(pkgInfo.getRealDir() / pkgInfo.name):
|
|
pkgInfo.installDirs.add(pkgInfo.name)
|
|
if fileExists(pkgInfo.getRealDir() / pkgInfo.name.addFileExt("nim")):
|
|
pkgInfo.installFiles.add(pkgInfo.name.addFileExt("nim"))
|
|
|
|
proc readPackageInfo(nf: NimbleFile, options: Options,
|
|
onlyMinimalInfo=false): PackageInfo =
|
|
## Reads package info from the specified Nimble file.
|
|
##
|
|
## Attempts to read it using the "old" Nimble ini format first, if that
|
|
## fails attempts to evaluate it as a nimscript file.
|
|
##
|
|
## If both fail then returns an error.
|
|
##
|
|
## When ``onlyMinimalInfo`` is true, only the `name` and `version` fields are
|
|
## populated. The ``isNimScript`` field can also be relied on.
|
|
##
|
|
## This version uses a cache stored in ``options``, so calling it multiple
|
|
## times on the same ``nf`` shouldn't require re-evaluation of the Nimble
|
|
## file.
|
|
|
|
assert fileExists(nf)
|
|
|
|
# Check the cache.
|
|
if options.pkgInfoCache.hasKey(nf):
|
|
return options.pkgInfoCache[nf]
|
|
|
|
result = initPackageInfo(nf)
|
|
let minimalInfo = getNameVersion(nf)
|
|
|
|
validatePackageName(nf.splitFile.name)
|
|
|
|
var success = false
|
|
var iniError: ref NimbleError
|
|
# Attempt ini-format first.
|
|
try:
|
|
readPackageInfoFromNimble(nf, result)
|
|
success = true
|
|
result.isNimScript = false
|
|
except NimbleError:
|
|
iniError = (ref NimbleError)(getCurrentException())
|
|
|
|
if not success:
|
|
if onlyMinimalInfo:
|
|
result.name = minimalInfo.name
|
|
result.version = minimalInfo.version
|
|
result.isNimScript = true
|
|
result.isMinimal = true
|
|
|
|
# It's possible this proc will receive a .nimble-link file eventually,
|
|
# I added this assert to hopefully make this error clear for everyone.
|
|
let msg = "No version detected. Received nimble-link?"
|
|
assert result.version.len > 0, msg
|
|
else:
|
|
try:
|
|
readPackageInfoFromNims(nf, options, result)
|
|
result.isNimScript = true
|
|
except NimbleError as exc:
|
|
if exc.hint.len > 0:
|
|
raise
|
|
let msg = "Could not read package info file in " & nf & ";\n" &
|
|
" Reading as ini file failed with: \n" &
|
|
" " & iniError.msg & ".\n" &
|
|
" Evaluating as NimScript file failed with: \n" &
|
|
" " & exc.msg & "."
|
|
raise newException(NimbleError, msg)
|
|
|
|
# By default specialVersion is the same as version.
|
|
result.specialVersion = result.version
|
|
|
|
# Only attempt to read a special version if `nf` is inside the $nimbleDir.
|
|
if nf.startsWith(options.getNimbleDir()):
|
|
# The package directory name may include a "special" version
|
|
# (example #head). If so, it is given higher priority and therefore
|
|
# overwrites the .nimble file's version.
|
|
let version = parseVersionRange(minimalInfo.version)
|
|
if version.kind == verSpecial:
|
|
result.specialVersion = minimalInfo.version
|
|
|
|
# Apply rules to infer which files should/shouldn't be installed. See #469.
|
|
inferInstallRules(result, options)
|
|
|
|
if not result.isMinimal:
|
|
options.pkgInfoCache[nf] = result
|
|
|
|
# Validate the rest of the package info last.
|
|
if not options.disableValidation:
|
|
validateVersion(result.version)
|
|
validatePackageInfo(result, options)
|
|
|
|
proc validate*(file: NimbleFile, options: Options,
|
|
error: var ValidationError, pkgInfo: var PackageInfo): bool =
|
|
try:
|
|
pkgInfo = readPackageInfo(file, options)
|
|
except ValidationError as exc:
|
|
error = exc[]
|
|
return false
|
|
|
|
return true
|
|
|
|
proc getPkgInfoFromFile*(file: NimbleFile, options: Options): PackageInfo =
|
|
## Reads the specified .nimble file and returns its data as a PackageInfo
|
|
## object. Any validation errors are handled and displayed as warnings.
|
|
try:
|
|
result = readPackageInfo(file, options)
|
|
except ValidationError:
|
|
let exc = (ref ValidationError)(getCurrentException())
|
|
if exc.warnAll:
|
|
display("Warning:", exc.msg, Warning, HighPriority)
|
|
display("Hint:", exc.hint, Warning, HighPriority)
|
|
else:
|
|
raise
|
|
|
|
proc getPkgInfo*(dir: string, options: Options): PackageInfo =
|
|
## Find the .nimble file in ``dir`` and parses it, returning a PackageInfo.
|
|
let nimbleFile = findNimbleFile(dir, true)
|
|
return getPkgInfoFromFile(nimbleFile, options)
|
|
|
|
proc getInstalledPkgs*(libsDir: string, options: Options):
|
|
seq[tuple[pkginfo: PackageInfo, meta: MetaData]] =
|
|
## Gets a list of installed packages.
|
|
##
|
|
## ``libsDir`` is in most cases: ~/.nimble/pkgs/
|
|
const
|
|
readErrorMsg = "Installed package '$1@$2' is outdated or corrupt."
|
|
validationErrorMsg = readErrorMsg & "\nPackage did not pass validation: $3"
|
|
hintMsg = "The corrupted package will need to be removed manually. To fix" &
|
|
" this error message, remove $1."
|
|
|
|
proc createErrorMsg(tmplt, path, msg: string): string =
|
|
let (name, version) = getNameVersion(path)
|
|
return tmplt % [name, version, msg]
|
|
|
|
display("Loading", "list of installed packages", priority = MediumPriority)
|
|
|
|
result = @[]
|
|
for kind, path in walkDir(libsDir):
|
|
if kind == pcDir:
|
|
let nimbleFile = findNimbleFile(path, false)
|
|
if nimbleFile != "":
|
|
let meta = readMetaData(path)
|
|
var pkg: PackageInfo
|
|
try:
|
|
pkg = readPackageInfo(nimbleFile, options, onlyMinimalInfo=false)
|
|
except ValidationError:
|
|
let exc = (ref ValidationError)(getCurrentException())
|
|
exc.msg = createErrorMsg(validationErrorMsg, path, exc.msg)
|
|
exc.hint = hintMsg % path
|
|
if exc.warnInstalled or exc.warnAll:
|
|
display("Warning:", exc.msg, Warning, HighPriority)
|
|
# Don't show hints here because they are only useful for package
|
|
# owners.
|
|
else:
|
|
raise exc
|
|
except:
|
|
let tmplt = readErrorMsg & "\nMore info: $3"
|
|
let msg = createErrorMsg(tmplt, path, getCurrentException().msg)
|
|
var exc = newException(NimbleError, msg)
|
|
exc.hint = hintMsg % path
|
|
raise exc
|
|
|
|
pkg.isInstalled = true
|
|
pkg.isLinked =
|
|
cmpPaths(nimbleFile.splitFile().dir, path) != 0
|
|
result.add((pkg, meta))
|
|
|
|
proc isNimScript*(nf: string, options: Options): bool =
|
|
result = readPackageInfo(nf, options).isNimScript
|
|
|
|
proc toFullInfo*(pkg: PackageInfo, options: Options): PackageInfo =
|
|
if pkg.isMinimal:
|
|
result = getPkgInfoFromFile(pkg.mypath, options)
|
|
result.isInstalled = pkg.isInstalled
|
|
result.isLinked = pkg.isLinked
|
|
else:
|
|
return pkg
|
|
|
|
when isMainModule:
|
|
validatePackageName("foo_bar")
|
|
validatePackageName("f_oo_b_a_r")
|
|
try:
|
|
validatePackageName("foo__bar")
|
|
assert false
|
|
except NimbleError:
|
|
assert true
|
|
|
|
echo("Everything passed!")
|