diff --git a/src/nimble.nim b/src/nimble.nim index 5027b6e..57c8736 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -13,7 +13,7 @@ from sequtils import toSeq import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools, nimblepkg/download, nimblepkg/config, nimblepkg/common, nimblepkg/publish, nimblepkg/options, nimblepkg/packageparser, - nimblepkg/cli + nimblepkg/cli, nimblepkg/packageinstaller import nimblepkg/nimscriptsupport @@ -364,10 +364,21 @@ proc removePkgDir(dir: string, options: Options) = display("Warning:", ("Cannot completely remove $1. Files not installed " & "by nimble are present.") % dir, Warning, HighPriority) - # Remove binaries. if nimblemeta.hasKey("binaries"): + # Remove binaries. for binary in nimblemeta["binaries"]: removeFile(options.getBinDir() / binary.str) + + # Search for an older version of the package we are removing. + let (pkgName, _) = getNameVersion(dir) + let pkgList = getInstalledPkgsMin(options.getPkgsDir(), options) + var pkgInfo: PackageInfo + if pkgList.findPkg((pkgName, newVRAny()), pkgInfo): + pkgInfo = pkgInfo.toFullInfo(options) + for bin in pkgInfo.bin: + let symlinkDest = pkgInfo.getRealDir() / bin + let symlinkFilename = options.getBinDir() / bin.extractFilename + discard setupBinSymlink(symlinkDest, symlinkFilename) else: display("Warning:", ("Cannot completely remove $1. Binary symlinks may " & "have been left over in $2.") % @@ -477,49 +488,11 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options, filesInstalled.incl copyFileD(pkgInfo.getOutputDir(bin), pkgDestDir / bin) - let currentPerms = getFilePermissions(pkgDestDir / bin) - setFilePermissions(pkgDestDir / bin, currentPerms + {fpUserExec}) - let cleanBin = bin.extractFilename - when defined(unix): - display("Creating", "symlink: $1 -> $2" % - [pkgDestDir / bin, binDir / cleanBin], priority = MediumPriority) - if existsFile(binDir / cleanBin): - display("Warning:", "Symlink already exists in $1. Replacing." % binDir, - Warning, HighPriority) - removeFile(binDir / cleanBin) - createSymlink(pkgDestDir / bin, binDir / cleanBin) - binariesInstalled.incl(cleanBin) - elif defined(windows): - # There is a bug on XP, described here: - # http://stackoverflow.com/questions/2182568/batch-script-is-not-executed-if-chcp-was-called - # But this workaround brakes code page on newer systems, so we need to detect OS version - var osver = OSVERSIONINFO() - osver.dwOSVersionInfoSize = cast[DWORD](sizeof(OSVERSIONINFO)) - if GetVersionExA(osver) == WINBOOL(0): - raise newException(NimbleError, - "Can't detect OS version: GetVersionExA call failed") - let fixChcp = osver.dwMajorVersion <= 5 - - # Create cmd.exe/powershell stub. - let dest = binDir / cleanBin.changeFileExt("cmd") - display("Creating", "stub: $1 -> $2" % [pkgDestDir / bin, dest], - priority = MediumPriority) - var contents = "@" - if options.config.chcp: - if fixChcp: - contents.add "chcp 65001 > nul && " - else: contents.add "chcp 65001 > nul\n@" - contents.add "\"" & pkgDestDir / bin & "\" %*\n" - writeFile(dest, contents) - binariesInstalled.incl(dest.extractFilename) - # For bash on Windows (Cygwin/Git bash). - let bashDest = dest.changeFileExt("") - display("Creating", "Cygwin stub: $1 -> $2" % - [pkgDestDir / bin, bashDest], priority = MediumPriority) - writeFile(bashDest, "\"" & pkgDestDir / bin & "\" \"$@\"\n") - binariesInstalled.incl(bashDest.extractFilename) - else: - {.error: "Sorry, your platform is not supported.".} + # Set up a symlink. + let symlinkDest = pkgDestDir / bin + let symlinkFilename = binDir / bin.extractFilename + for filename in setupBinSymlink(symlinkDest, symlinkFilename): + binariesInstalled.incl(filename) let vcsRevision = vcsRevisionInDir(realDir) diff --git a/src/nimblepkg/packageinfo.nim b/src/nimblepkg/packageinfo.nim index 4c47197..389342d 100644 --- a/src/nimblepkg/packageinfo.nim +++ b/src/nimblepkg/packageinfo.nim @@ -300,7 +300,7 @@ proc getInstalledPkgsMin*(libsDir: string, options: Options): ## minimal. This has the advantage that it does not depend on the ## ``packageparser`` module, and so can be used by ``nimscriptsupport``. ## - ## ``libsDir`` is in most cases: ~/.nimble/pkgs/ + ## ``libsDir`` is in most cases: ~/.nimble/pkgs/ (options.getPkgsDir) result = @[] for kind, path in walkDir(libsDir): if kind == pcDir: diff --git a/src/nimblepkg/packageinstaller.nim b/src/nimblepkg/packageinstaller.nim new file mode 100644 index 0000000..3b9c330 --- /dev/null +++ b/src/nimblepkg/packageinstaller.nim @@ -0,0 +1,52 @@ +# Copyright (C) Dominik Picheta. All rights reserved. +# BSD License. Look at license.txt for more info. +import os, strutils + +# Local imports +import cli + +proc setupBinSymlink*(symlinkDest, symlinkFilename: string): seq[string] = + result = @[] + let currentPerms = getFilePermissions(symlinkDest) + setFilePermissions(symlinkDest, currentPerms + {fpUserExec}) + when defined(unix): + display("Creating", "symlink: $1 -> $2" % + [symlinkDest, symlinkFilename], priority = MediumPriority) + if existsFile(symlinkFilename): + let msg = "Symlink already exists in $1. Replacing." % symlinkFilename + display("Warning:", msg, Warning, HighPriority) + removeFile(symlinkFilename) + + createSymlink(symlinkDest, symlinkFilename) + result.add symlinkFilename.extractFilename + elif defined(windows): + # There is a bug on XP, described here: + # http://stackoverflow.com/questions/2182568/batch-script-is-not-executed-if-chcp-was-called + # But this workaround brakes code page on newer systems, so we need to detect OS version + var osver = OSVERSIONINFO() + osver.dwOSVersionInfoSize = cast[DWORD](sizeof(OSVERSIONINFO)) + if GetVersionExA(osver) == WINBOOL(0): + raise newException(NimbleError, + "Can't detect OS version: GetVersionExA call failed") + let fixChcp = osver.dwMajorVersion <= 5 + + # Create cmd.exe/powershell stub. + let dest = symlinkFilename.changeFileExt("cmd") + display("Creating", "stub: $1 -> $2" % [symlinkDest, dest], + priority = MediumPriority) + var contents = "@" + if options.config.chcp: + if fixChcp: + contents.add "chcp 65001 > nul && " + else: contents.add "chcp 65001 > nul\n@" + contents.add "\"" & symlinkDest & "\" %*\n" + writeFile(dest, contents) + result.add dest.extractFilename + # For bash on Windows (Cygwin/Git bash). + let bashDest = dest.changeFileExt("") + display("Creating", "Cygwin stub: $1 -> $2" % + [symlinkDest, bashDest], priority = MediumPriority) + writeFile(bashDest, "\"" & symlinkDest & "\" \"$@\"\n") + result.add bashDest.extractFilename + else: + {.error: "Sorry, your platform is not supported.".} \ No newline at end of file diff --git a/tests/tester.nim b/tests/tester.nim index 5f5fb8a..bb06d8d 100644 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -43,7 +43,9 @@ proc inLines(lines: seq[string], line: string): bool = test "picks #head when looking for packages": cd "versionClashes" / "aporiaScenario": - check execNimble("install", "-y", "--verbose").exitCode == QuitSuccess + let (output, exitCode) = execNimble("install", "-y", "--verbose") + checkpoint output + check exitCode == QuitSuccess check execNimble("remove", "aporiascenario", "-y").exitCode == QuitSuccess check execNimble("remove", "packagea", "-y").exitCode == QuitSuccess @@ -419,4 +421,35 @@ test "can install diamond deps (#184)": # reproduce #184. let (output, exitCode) = execNimble("install", "-y") checkpoint(output) - check exitCode == 0 \ No newline at end of file + check exitCode == 0 + +suite "can handle two binary versions": + setup: + cd "binaryPackage/v1": + check execNimble("install", "-y").exitCode == QuitSuccess + + cd "binaryPackage/v2": + check execNimble("install", "-y").exitCode == QuitSuccess + + test "can execute v2": + let (output, exitCode) = + execCmdEx(installDir / "bin" / "binaryPackage".addFileExt(ExeExt)) + check exitCode == QuitSuccess + check output.strip() == "v2" + + test "can update symlink to earlier version after removal": + check execNimble("remove", "binaryPackage@2.0", "-y").exitCode==QuitSuccess + + let (output, exitCode) = + execCmdEx(installDir / "bin" / "binaryPackage".addFileExt(ExeExt)) + check exitCode == QuitSuccess + check output.strip() == "v1" + + test "can keep symlink version after earlier version removal": + check execNimble("remove", "binaryPackage@1.0", "-y").exitCode==QuitSuccess + + let (output, exitCode) = + execCmdEx(installDir / "bin" / "binaryPackage".addFileExt(ExeExt)) + check exitCode == QuitSuccess + check output.strip() == "v2" +