Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/nimblepkg/declarativeparser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,10 @@ proc toRequiresInfo*(pkgInfo: PackageInfo, options: Options, nimBin: Option[stri
display("Warning", msg, Warning, HighPriority)
raise newNimbleError[BabelPackageError](msg)

# Skip re-parsing installed packages that have cached requires in metadata
if pkgInfo.source == psInstalled and pkgInfo.requires.len > 0 and pkgInfo.infoKind >= pikRequires:
return pkgInfo

let nimbleFileInfo = nimbleFileInfo.get(extractRequiresInfo(pkgInfo.myPath, options))
result.requires = getRequires(nimbleFileInfo, result.activeFeatures)
if pkgInfo.basicInfo.name.isNim:
Expand Down
20 changes: 20 additions & 0 deletions src/nimblepkg/install.nim
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,26 @@ proc installFromDirDownloadInfo(nimBin: Option[string], downloadDir: string, url
if pv.ver.kind == verSpecial:
pkgInfo.metadata.specialVersions.incl pv.ver.spe

# Cache parsed requires/features to avoid re-parsing installed packages at runtime
let nimbleFileInfo = extractRequiresInfo(pkgInfo.myPath, options)
proc serializeRequires(dep: PkgTuple): string =
## Serializes a PkgTuple to a string that parseRequires can parse.
## Matches toJsonHook behavior: no space for verSpecial, space for others.
case dep.ver.kind
of verAny: dep.name
of verSpecial: dep.name & $dep.ver
else: dep.name & " " & $dep.ver
pkgInfo.metaData.requires = pkgInfo.requires.map(serializeRequires)
for feature, reqs in pkgInfo.features:
pkgInfo.metaData.features[feature] = reqs.map(serializeRequires)
pkgInfo.metaData.srcDir = pkgInfo.srcDir
pkgInfo.metaData.paths = pkgInfo.paths
for hook in pkgInfo.preHooks:
pkgInfo.metaData.preHooks.add hook
for hook in pkgInfo.postHooks:
pkgInfo.metaData.postHooks.add hook
pkgInfo.metaData.nestedRequires = nimbleFileInfo.nestedRequires

saveMetaData(pkgInfo.metaData, pkgDestDir)

# Run after-install hook
Expand Down
21 changes: 20 additions & 1 deletion src/nimblepkg/packageinfo.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# Stdlib imports
import system except TResult
import hashes, strutils, os, sets, tables, times, httpclient, strformat
import hashes, strutils, os, sets, tables, times, httpclient, strformat, sequtils
from net import SslError

import zippy
Expand Down Expand Up @@ -304,6 +304,25 @@ proc getInstalledPackageMin*(options: Options, pkgDir, nimbleFilePath: string):
fillMetaData(result, pkgDir, true, options)
except MetaDataError:
discard

# Populate cached requires/features from metadata to avoid re-parsing
if result.metaData.requires.len > 0:
try:
result.requires = result.metaData.requires.map(parseRequires)
for feature, reqs in result.metaData.features:
result.features[feature] = reqs.map(parseRequires)
result.srcDir = result.metaData.srcDir
result.paths = result.metaData.paths
for hook in result.metaData.preHooks:
result.preHooks.incl hook
for hook in result.metaData.postHooks:
result.postHooks.incl hook
result.infoKind = if result.metaData.nestedRequires: pikFull else: pikRequires
except NimbleError:
# Corrupted or old-format cache data; ignore and let normal parsing handle it
result.requires = @[]
result.features = initTable[string, seq[PkgTuple]]()
result.infoKind = pikMinimal

proc getInstalledPkgsMin*(libsDir: string, options: Options): seq[PackageInfo] =
## Gets a list of installed packages. The resulting package info is
Expand Down
11 changes: 11 additions & 0 deletions src/nimblepkg/packageinfotypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ type
# Special versions are aliases with which a single package can be
# referred. For example a package can be versions `0.1.0`, `#head` and
# `#master` at the same time.
# Cached fields from declarative/VM parser to avoid re-parsing installed packages
requires*: seq[string]
# String-form requires as extracted by the declarative parser (or VM fallback)
features*: Table[string, seq[string]]
# String-form features as extracted by the declarative parser
srcDir*: string
paths*: seq[string]
preHooks*: seq[string]
postHooks*: seq[string]
nestedRequires*: bool
# True if the package has requires nested in control flow (needs VM parser)

PackageBasicInfo* = tuple
name: string
Expand Down
54 changes: 53 additions & 1 deletion src/nimblepkg/packagemetadatafile.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info.

import os, strformat, sets, sequtils
import os, strformat, sets, sequtils, tables
import common, version, packageinfotypes, cli, tools, sha1hashes, options
import compat/json

Expand Down Expand Up @@ -42,6 +42,58 @@ proc initFromJson(specialVersions: var HashSet[Version], jsonNode: JsonNode,
else:
assert false, "The `jsonNode` must be of kind JArray."

proc `%`(features: Table[string, seq[string]]): JsonNode =
result = newJObject()
for k, v in features:
result[k] = %v

proc initFromJson(features: var Table[string, seq[string]], jsonNode: JsonNode,
jsonPath: var string) =
features = initTable[string, seq[string]]()
if jsonNode.kind == JObject:
for k, v in jsonNode:
var seqVal: seq[string]
if v.kind == JArray:
for item in v:
if item.kind == JString:
seqVal.add item.str
features[k] = seqVal

proc initFromJson(dst: var PackageMetaData, jsonNode: JsonNode, jsonPath: var string) =
## Custom initFromJson that tolerates missing fields for backward compatibility.
if jsonNode.kind != JObject: return
for key, val in jsonNode:
case key
of "url": dst.url = val.getStr
of "downloadMethod":
if val.kind == JString:
case val.str
of "git": dst.downloadMethod = git
of "hg": dst.downloadMethod = hg
of "vcsRevision": initFromJson(dst.vcsRevision, val, jsonPath)
of "files":
if val.kind == JArray:
for item in val: dst.files.add item.getStr
of "binaries":
if val.kind == JArray:
for item in val: dst.binaries.add item.getStr
of "specialVersions": initFromJson(dst.specialVersions, val, jsonPath)
of "requires":
if val.kind == JArray:
for item in val: dst.requires.add item.getStr
of "features": initFromJson(dst.features, val, jsonPath)
of "srcDir": dst.srcDir = val.getStr
of "paths":
if val.kind == JArray:
for item in val: dst.paths.add item.getStr
of "preHooks":
if val.kind == JArray:
for item in val: dst.preHooks.add item.getStr
of "postHooks":
if val.kind == JArray:
for item in val: dst.postHooks.add item.getStr
of "nestedRequires": dst.nestedRequires = val.getBool

proc saveMetaData*(metaData: PackageMetaData, dirName: string,
changeRoots = true) =
## Saves some important data to file in the package installation directory.
Expand Down
Loading