Skip to content

Commit

Permalink
Allow locking nim version in nimble.lock
Browse files Browse the repository at this point in the history
- Fixes nim-lang#953

Allow having nim as locked dependency.

- I will add unit tests once we agree on the approach and once nimble related
changes in nim are merged (I will link the PR in comment). Ditto for the
documentation. In order that change to work we have to add nim package in nimble
packages repo and also add alias compiler -> nim to avoid breaking backward
compatibility.

Here it is the flow:

``` bash
nimble develop nim
nimble lock
```

After that `nimble install` and `nimble build` commands will use the locked
`nim` version
  • Loading branch information
yyoncho committed Aug 10, 2022
1 parent aef6f4f commit 3f8d087
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 21 deletions.
53 changes: 42 additions & 11 deletions src/nimble.nim
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ proc initPkgList(pkgInfo: PackageInfo, options: Options): seq[PackageInfo] =
proc install(packages: seq[PkgTuple], options: Options,
doPrompt, first, fromLockFile: bool): PackageDependenciesInfo

proc processFreeDependencies(pkgInfo: PackageInfo, options: Options):
proc processFreeDependencies(pkgInfo: PackageInfo, options: Options, nimAsDependency = false):
HashSet[PackageInfo] =
## Verifies and installs dependencies.
##
Expand All @@ -85,7 +85,7 @@ proc processFreeDependencies(pkgInfo: PackageInfo, options: Options):

var reverseDependencies: seq[PackageBasicInfo] = @[]
for dep in pkgInfo.requires:
if dep.name == "nimrod" or dep.name == "nim":
if not nimAsDependency and dep.name.isNim:
let nimVer = getNimrodVersion(options)
if not withinRange(nimVer, dep.ver):
let msg = "Unsatisfied dependency: " & dep.name & " (" & $dep.ver & ")"
Expand Down Expand Up @@ -213,7 +213,7 @@ proc buildFromDir(pkgInfo: PackageInfo, paths: HashSet[string],
# `quoteShell` would be more robust than `\"` (and avoid quoting when
# un-necessary) but would require changing `extractBin`
let cmd = "$# $# --colors:on --noNimblePath $# $# $#" % [
getNimBin(options).quoteShell, pkgInfo.backend, join(args, " "),
pkgInfo.getNimBin(options).quoteShell, pkgInfo.backend, join(args, " "),
outputOpt, input.quoteShell]
try:
doCmd(cmd)
Expand Down Expand Up @@ -319,7 +319,7 @@ proc packageExists(pkgInfo: PackageInfo, options: Options):
fillMetaData(oldPkgInfo, pkgDestDir, true)
return some(oldPkgInfo)

proc processLockedDependencies(pkgInfo: PackageInfo, options: Options):
proc processLockedDependencies(pkgInfo: PackageInfo, options: Options, onlyNim = false):
HashSet[PackageInfo]

proc processAllDependencies(pkgInfo: PackageInfo, options: Options):
Expand All @@ -329,6 +329,22 @@ proc processAllDependencies(pkgInfo: PackageInfo, options: Options):
else:
pkgInfo.processFreeDependencies(options)

proc useLockedNimIfNeeded(pkgInfo: PackageInfo, options: var Options) =
if pkgInfo.lockedDeps.len > 0:
var deps = pkgInfo.processLockedDependencies(options, true)
if deps.len != 0:
const binaryName = when defined(windows): "nim.exe" else: "nim"
let
nimDep = deps.pop
nim = nimDep.getRealDir() / "bin" / binaryName

if not fileExists(nim):
raise nimbleError("Trying to use nim from $1 " % nimDep.getRealDir(),
"If you are using develop mode nim make sure to compile it.")

options.nim = nim
display("Info:", "using $1 for compilation" % options.nim, priority = HighPriority)

proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
url: string, first: bool, fromLockFile: bool,
vcsRevision = notSetSha1Hash):
Expand Down Expand Up @@ -400,10 +416,14 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
result.pkg = oldPkg
return

# nim is intended only for local project local usage, so avoid installing it
# in .nimble/bin
let isNimPackage = pkgInfo.basicInfo.name.isNim

# Build before removing an existing package (if one exists). This way
# if the build fails then the old package will still be installed.

if pkgInfo.bin.len > 0:
if pkgInfo.bin.len > 0 and not isNimPackage:
let paths = result.deps.map(dep => dep.getRealDir())
let flags = if options.action.typ in {actionInstall, actionPath, actionUninstall, actionDevelop}:
options.action.passNimFlags
Expand Down Expand Up @@ -601,7 +621,7 @@ proc installDependency(pkgInfo: PackageInfo, downloadInfo: DownloadInfo,

return newlyInstalledPkgInfo

proc processLockedDependencies(pkgInfo: PackageInfo, options: Options):
proc processLockedDependencies(pkgInfo: PackageInfo, options: Options, onlyNim = false):
HashSet[PackageInfo] =
# Returns a hash set with `PackageInfo` of all packages from the lock file of
# the package `pkgInfo` by getting the info for develop mode dependencies from
Expand All @@ -612,6 +632,8 @@ proc processLockedDependencies(pkgInfo: PackageInfo, options: Options):
let developModeDeps = getDevelopDependencies(pkgInfo, options)

for name, dep in pkgInfo.lockedDeps:
if onlyNim and not name.isNim:
continue
if developModeDeps.hasKey(name):
result.incl developModeDeps[name][]
elif isInstalled(name, dep, options):
Expand Down Expand Up @@ -710,9 +732,10 @@ proc build(pkgInfo: PackageInfo, options: Options) =
var args = options.getCompilationFlags()
buildFromDir(pkgInfo, paths, args, options)

proc build(options: Options) =
proc build(options: var Options) =
let dir = getCurrentDir()
let pkgInfo = getPkgInfo(dir, options)
useLockedNimIfNeeded(pkgInfo, options)
pkgInfo.build(options)

proc clean(options: Options) =
Expand Down Expand Up @@ -767,7 +790,7 @@ proc execBackend(pkgInfo: PackageInfo, options: Options) =
"backend") % [bin, pkgInfo.basicInfo.name, backend], priority = HighPriority)

doCmd("$# $# --noNimblePath $# $# $#" %
[getNimBin(options).quoteShell,
[pkgInfo.getNimBin(options).quoteShell,
backend,
join(args, " "),
bin.quoteShell,
Expand Down Expand Up @@ -1295,7 +1318,7 @@ proc developFreeDependencies(pkgInfo: PackageInfo,
"developFreeDependencies needs pkgInfo.requires"

for dep in pkgInfo.requires:
if dep.name == "nimrod" or dep.name == "nim":
if dep.name.isNim:
continue

let resolvedDep = dep.resolveAlias(options)
Expand Down Expand Up @@ -1595,8 +1618,12 @@ proc lock(options: Options) =
let doesLockFileExist = displayLockOperationStart(currentDir)
var errors = validateDevModeDepsWorkingCopiesBeforeLock(pkgInfo, options)

let dependencies = pkgInfo.processFreeDependencies(options).map(
pkg => pkg.toFullInfo(options)).toSeq
let
includeNim = pkgInfo.getDevelopDependencies(options).contains("nim")
dependencies = pkgInfo.processFreeDependencies(options, includeNim)
.map(pkg => pkg.toFullInfo(options))
.toSeq

pkgInfo.validateDevelopDependenciesVersionRanges(dependencies, options)
var dependencyGraph = buildDependencyGraph(dependencies, options)

Expand Down Expand Up @@ -1918,6 +1945,10 @@ proc doAction(options: var Options) =
of actionRefresh:
refresh(options)
of actionInstall:
if options.action.packages.len != 0:
let pkgInfo = getPkgInfo(getCurrentDir(), options)
useLockedNimIfNeeded(pkgInfo, options)

let (_, pkgInfo) = install(options.action.packages, options,
doPrompt = true,
first = true,
Expand Down
14 changes: 14 additions & 0 deletions src/nimblepkg/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,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
5 changes: 3 additions & 2 deletions src/nimblepkg/packageinfo.nim
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,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 @@ -530,6 +528,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"

when isMainModule:
import unittest

Expand Down
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 @@ -308,7 +310,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

0 comments on commit 3f8d087

Please sign in to comment.