Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow locking nim version in nimble.lock #1017

Merged
merged 14 commits into from
Feb 13, 2023
Merged
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ src/nimblepkg/version
# Test procedure artifacts
*.nims
/buildTests
/tests/nimdep/nimble.lock
298 changes: 188 additions & 110 deletions src/nimble.nim

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/nimblepkg/developfile.nim
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ proc workingCopyNeeds*(dependencyPkg, dependentPkg: PackageInfo,
## if any.

let
lockFileVcsRev = dependentPkg.lockedDeps.getOrDefault(
lockFileVcsRev = dependentPkg.lockedDeps.getOrDefault("").getOrDefault(
dependencyPkg.basicInfo.name, notSetLockFileDep).vcsRevision
syncFile = getSyncFile(dependentPkg)
syncFileVcsRev = syncFile.getDepVcsRevision(dependencyPkg.basicInfo.name)
Expand Down
29 changes: 17 additions & 12 deletions src/nimblepkg/lockfile.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ type
lfjkVersion = "version"
lfjkPackages = "packages"
lfjkPkgVcsRevision = "vcsRevision"
lfjkTasks = "tasks"

const
lockFileVersion = 1
lockFileVersion = 2

proc initLockFileDep*: LockFileDep =
result = LockFileDep(
Expand All @@ -22,31 +23,35 @@ proc initLockFileDep*: LockFileDep =
const
notSetLockFileDep* = initLockFileDep()

proc writeLockFile*(fileName: string, packages: LockFileDeps,
topologicallySortedOrder: seq[string]) =
proc writeLockFile*(fileName: string, packages: AllLockFileDeps) =
## Saves lock file on the disk in topologically sorted order of the
## dependencies.

let packagesJsonNode = newJObject()
for packageName in topologicallySortedOrder:
packagesJsonNode.add packageName, %packages[packageName]

let mainJsonNode = %{
$lfjkVersion: %lockFileVersion,
$lfjkPackages: packagesJsonNode
}
$lfjkPackages: %packages[noTask]
}
# Store task graph seperate
mainJsonNode[$lfjkTasks] = newJObject()
for task, deps in packages:
if task != noTask:
mainJsonNode[$lfjkTasks][task] = %deps

var s = mainJsonNode.pretty
s.add '\n'
writeFile(fileName, s)

proc readLockFile*(filePath: string): LockFileDeps =
proc readLockFile*(filePath: string): AllLockFileDeps =
{.warning[UnsafeDefault]: off.}
{.warning[ProveInit]: off.}
result = parseFile(filePath)[$lfjkPackages].to(result.typeof)
let data = parseFile(filePath)
result[noTask] = data[$lfjkPackages].to(LockFileDeps)
if $lfjkTasks in data:
for task, deps in data[$lfjkTasks]:
result[task] = deps.to(LockFileDeps)
{.warning[ProveInit]: on.}
{.warning[UnsafeDefault]: on.}

proc getLockedDependencies*(lockFile: string): LockFileDeps =
proc getLockedDependencies*(lockFile: string): AllLockFileDeps =
if lockFile.fileExists:
result = lockFile.readLockFile
11 changes: 10 additions & 1 deletion src/nimblepkg/nimscriptapi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ var
project = ""
success = false
retVal = true
nimblePathsEnv = "__NIMBLE_PATHS"

proc requires*(deps: varargs[string]) =
## Call this to set the list of requirements of your Nimble
Expand Down Expand Up @@ -201,7 +202,7 @@ template task*(name: untyped; description: string; body: untyped): untyped =
proc `name Task`*() = body

nimbleTasks.add (astToStr(name), description)

if actionName.len == 0 or actionName == "help":
success = true
elif actionName == astToStr(name).normalize:
Expand Down Expand Up @@ -238,3 +239,11 @@ proc getPkgDir*(): string =
result = projectFile.rsplit(seps={'/', '\\', ':'}, maxsplit=1)[0]

proc thisDir*(): string = getPkgDir()

proc getPaths*(): seq[string] =
## Returns the paths to the dependencies
return getEnv(nimblePathsEnv).split("|")

proc getPathsClause*(): string =
## Returns the paths to the dependencies as consumed by the nim compiler.
return getPaths().mapIt("--path:" & it.quoteShell).join(" ")
23 changes: 22 additions & 1 deletion src/nimblepkg/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type
pkgInfoCache*: TableRef[string, PackageInfo]
showHelp*: bool
lockFileName*: string
useSystemNim*: bool
showVersion*: bool
offline*: bool
noColor*: bool
Expand All @@ -44,6 +45,7 @@ type
localdeps*: bool # True if project local deps mode
developLocaldeps*: bool # True if local deps + nimble develop pkg1 ...
disableSslCertCheck*: bool
disableLockFile*: bool
enableTarballs*: bool # Enable downloading of packages as tarballs from GitHub.
task*: string # Name of the task that is getting ran
package*: string
Expand Down Expand Up @@ -112,7 +114,7 @@ Usage: nimble [nimbleopts] COMMAND [cmdopts]
Commands:
install [pkgname, ...] Installs a list of packages.
[-d, --depsOnly] Only install dependencies. Leave out pkgname
to install deps for a local nimble package.
to install deps for a local nimble package.
[-p, --passNim] Forward specified flag to compiler.
[--noRebuild] Don't rebuild binaries if they're up-to-date.
develop [pkgname, ...] Clones a list of packages for development.
Expand Down Expand Up @@ -228,6 +230,9 @@ Nimble Options:
--noColor Don't colorise output.
--noSSLCheck Don't check SSL certificates.
--lock-file Override the lock file name.
--noLockFile Ignore the lock file if present.
--use-system-nim Use system nim and ignore nim from the lock
file if any

For more information read the Github readme:
https://github.com/nim-lang/nimble#readme
Expand Down Expand Up @@ -435,6 +440,20 @@ proc setNimBin*(options: var Options) =
raise nimbleError(
"Unable to find `nim` binary - add to $PATH or use `--nim`")

proc getNimbleFileDir*(pkgInfo: PackageInfo): string =
pkgInfo.myPath.splitFile.dir

proc getNimBin*(pkgInfo: PackageInfo, options: Options): string =
if pkgInfo.basicInfo.name == "nim":
let binaryPath = when defined(windows):
"bin\nim.exe"
else:
"bin/nim"
result = pkgInfo.getNimbleFileDir() / binaryPath
display("Info:", "compiling nim package using $1" % result, priority = HighPriority)
else:
result = options.nim

proc getNimBin*(options: Options): string =
return options.nim

Expand Down Expand Up @@ -515,9 +534,11 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
of "nim": result.nim = val
of "localdeps", "l": result.localdeps = true
of "nosslcheck": result.disableSslCertCheck = true
of "nolockfile": result.disableLockFile = true
of "tarballs", "t": result.enableTarballs = true
of "package", "p": result.package = val
of "lock-file": result.lockFileName = val
of "use-system-nim": result.useSystemNim = true
else: isGlobalFlag = false

var wasFlagHandled = true
Expand Down
37 changes: 31 additions & 6 deletions src/nimblepkg/packageinfo.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ proc isLoaded*(pkgInfo: PackageInfo): bool =
return pkgInfo.myPath.len > 0

proc assertIsLoaded*(pkgInfo: PackageInfo) =
assert pkgInfo.isLoaded, "The package info must be loaded."
assert pkgInfo.isLoaded, "The package info must be loaded. "

proc areLockedDepsLoaded*(pkgInfo: PackageInfo): bool =
pkgInfo.lockedDeps.len > 0
Expand All @@ -37,7 +37,8 @@ proc initPackageInfo*(options: Options, filePath: string): PackageInfo =
result.myPath = filePath
result.basicInfo.name = fileName
result.backend = "c"
result.lockedDeps = options.lockFile(fileDir).getLockedDependencies()
if not options.disableLockFile:
result.lockedDeps = options.lockFile(fileDir).getLockedDependencies()

proc toValidPackageName*(name: string): string =
for c in name:
Expand Down Expand Up @@ -200,8 +201,11 @@ proc readPackageList(name: string, options: Options, ignorePackageCache = false)
# going further.
gPackageJson[name] = newJArray()
return gPackageJson[name]
gPackageJson[name] = parseFile(options.getNimbleDir() / "packages_" &
name.toLowerAscii() & ".json")
let file = options.getNimbleDir() / "packages_" & name.toLowerAscii() & ".json"
if file.fileExists:
gPackageJson[name] = parseFile(file)
else:
gPackageJson[name] = newJArray()
return gPackageJson[name]

proc getPackage*(pkg: string, options: Options, resPkg: var Package, ignorePackageCache = false): bool
Expand Down Expand Up @@ -357,8 +361,6 @@ proc findAllPkgs*(pkglist: seq[PackageInfo], dep: PkgTuple): seq[PackageInfo] =
if withinRange(pkg, dep.ver):
result.add pkg

proc getNimbleFileDir*(pkgInfo: PackageInfo): string =
pkgInfo.myPath.splitFile.dir

proc getRealDir*(pkgInfo: PackageInfo): string =
## Returns the directory containing the package source files.
Expand Down Expand Up @@ -525,6 +527,26 @@ proc fullRequirements*(pkgInfo: PackageInfo): seq[PkgTuple] =
for requirements in pkgInfo.taskRequires.values:
result &= requirements

proc name*(pkgInfo: PackageInfo): string {.inline.} =
pkgInfo.basicInfo.name

iterator lockedDepsFor*(pkgInfo: PackageInfo, options: Options): (string, LockFileDep) =
for task, deps in pkgInfo.lockedDeps:
if task in ["", options.task]:
for name, dep in deps:
yield (name, dep)

proc hasLockedDeps*(pkgInfo: PackageInfo): bool =
## Returns true if pkgInfo has any locked deps (including any tasks)
# Check if any tasks have locked deps
for deps in pkgInfo.lockedDeps.values:
if deps.len > 0:
return true

proc hasPackage*(deps: AllLockFileDeps, pkgName: string): bool =
for deps in deps.values:
if pkgName in deps:
return true

proc `==`*(pkg1: PackageInfo, pkg2: PackageInfo): bool =
pkg1.myPath == pkg2.myPath
Expand All @@ -537,6 +559,9 @@ proc hash*(x: PackageInfo): Hash =
proc getNameAndVersion*(pkgInfo: PackageInfo): string =
&"{pkgInfo.basicInfo.name}@{pkgInfo.basicInfo.version}"

proc isNim*(name: string): bool =
result = name == "nim" or name == "nimrod" or name == "compiler"

when isMainModule:
import unittest

Expand Down
8 changes: 7 additions & 1 deletion src/nimblepkg/packageinfotypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ type

LockFileDeps* = OrderedTable[string, LockFileDep]

AllLockFileDeps* = Table[string, LockFileDeps]
## Base deps is stored with empty string key ""
## Other tasks have task name as key

PackageMetaData* = object
url*: string
downloadMethod*: DownloadMethod
Expand Down Expand Up @@ -62,7 +66,7 @@ type
backend*: string
foreignDeps*: seq[string]
basicInfo*: PackageBasicInfo
lockedDeps*: LockFileDeps
lockedDeps*: AllLockFileDeps
metaData*: PackageMetaData
isLink*: bool

Expand All @@ -81,3 +85,5 @@ type
alias*: string ## A name of another package, that this package aliases.

PackageDependenciesInfo* = tuple[deps: HashSet[PackageInfo], pkg: PackageInfo]

const noTask* = "" # Means that noTask is being ran. Use this as key for base dependencies
6 changes: 4 additions & 2 deletions src/nimblepkg/packageparser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ proc validatePackageInfo(pkgInfo: PackageInfo, options: Options) =
raise validationError("'" & pkgInfo.backend &
"' is an invalid backend.", false)

validatePackageStructure(pkginfo, options)
# nim is used for building the project, thus no need to validate its structure.
if not pkgInfo.basicInfo.name.isNim:
validatePackageStructure(pkginfo, options)

proc nimScriptHint*(pkgInfo: PackageInfo) =
if not pkgInfo.isNimScript:
Expand Down Expand Up @@ -323,7 +325,7 @@ proc inferInstallRules(pkgInfo: var PackageInfo, options: Options) =
# installed.)
let installInstructions =
pkgInfo.installDirs.len + pkgInfo.installExt.len + pkgInfo.installFiles.len
if installInstructions == 0 and pkgInfo.bin.len > 0:
if installInstructions == 0 and pkgInfo.bin.len > 0 and pkgInfo.basicInfo.name != "nim":
pkgInfo.skipExt.add("nim")

# When a package doesn't specify a `srcDir` it's fair to assume that
Expand Down
5 changes: 0 additions & 5 deletions src/nimblepkg/tools.nim
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,6 @@ proc tryDoCmdEx*(cmd: string): string {.discardable.} =
raise nimbleError(tryDoCmdExErrorMessage(cmd, output, exitCode))
return output

proc getNimBin*: string =
result = "nim"
if findExe("nim") != "": result = findExe("nim")
elif findExe("nimrod") != "": result = findExe("nimrod")

proc getNimrodVersion*(options: Options): Version =
let vOutput = doCmdEx(getNimBin(options).quoteShell & " -v").output
var matches: array[0..MaxSubpatterns, string]
Expand Down
2 changes: 1 addition & 1 deletion src/nimblepkg/topologicalsort.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ proc getDependencies(packages: seq[PackageInfo], package: PackageInfo,
## package. It is needed because some of the names of the packages in the
## `requires` clause of a package could be URLs.
for dep in package.requires:
if dep.name == "nim":
if dep.name.isNim:
continue
var depPkgInfo = initPackageInfo()
var found = findPkg(packages, dep, depPkgInfo)
Expand Down
15 changes: 15 additions & 0 deletions tests/nimdep/nimdep.nimble
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Package

version = "0.1.0"
author = "Ivan Yonchovski"
description = "A new awesome nimble package"
license = "MIT"
srcDir = "src"
bin = @["demo"]

# Dependencies

requires "nim"

task version, "Test nim version":
exec "nim --version"
Empty file added tests/nimdep/src/demo.nim
Empty file.
15 changes: 15 additions & 0 deletions tests/taskdeps/dependencies/dependencies.nimble
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Package

version = "0.1.0"
author = "John Doe"
description = "A new awesome nimble package"
license = "MIT"
srcDir = "."
bin = @["foo"]


# Dependencies

requires "nim >= 0.19.0"

taskRequires "test", "unittest2 == 0.0.4"
1 change: 1 addition & 0 deletions tests/taskdeps/main/foo.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import json_serialization
2 changes: 1 addition & 1 deletion tests/taskdeps/main/main.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
author = "John Doe"
description = "A new awesome nimble package"
license = "MIT"
srcDir = "."
srcDir = "src"
bin = @[]


Expand Down
Empty file.
17 changes: 17 additions & 0 deletions tests/tasks/getpaths/getpaths.nimble
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Package

version = "0.1.0"
author = "Ivan Yonchovski"
description = "A new awesome nimble package"
license = "MIT"
srcDir = "src"
bin = @["run"]


# Dependencies

requires "benchy", "unittest2"

task echoPaths, "":
echo getPaths()
echo getPathsClause()
3 changes: 2 additions & 1 deletion tests/testscommon.nim
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ proc uninstallDeps*() =
for line in output.splitLines:
let package = line.split(" ")[0]
if package != "":
verify execNimbleYes("uninstall", "-i", package)
discard execNimbleYes("uninstall", "-i", package)


template testRefresh*(body: untyped) =
# Backup current config
Expand Down
Loading