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

SAT implementation #1200

Merged
merged 5 commits into from
Apr 6, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
run: nimble install -y
- name: Run nim c -r tester
run: |
nimble install
Araq marked this conversation as resolved.
Show resolved Hide resolved
cd tests
nim c -r tester
# there's no need to add nimblepkg unit tests --
Expand Down
2 changes: 1 addition & 1 deletion nimble.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ installExt = @["nim"]

# Dependencies

requires "nim >= 0.13.0"
requires "nim >= 0.13.0", "sat"
requires "checksums"

when defined(nimdistros):
Expand Down
112 changes: 53 additions & 59 deletions src/nimble.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ import std/options as std_opt

import strutils except toLower
from unicode import toLower

import sat/sat
ringabout marked this conversation as resolved.
Show resolved Hide resolved
import nimblepkg/packageinfotypes, nimblepkg/packageinfo, nimblepkg/version,
nimblepkg/tools, nimblepkg/download, nimblepkg/config, nimblepkg/common,
nimblepkg/tools, nimblepkg/download, nimblepkg/common,
nimblepkg/publish, nimblepkg/options, nimblepkg/packageparser,
nimblepkg/cli, nimblepkg/packageinstaller, nimblepkg/reversedeps,
nimblepkg/nimscriptexecutor, nimblepkg/init, nimblepkg/vcstools,
nimblepkg/checksums, nimblepkg/topologicalsort, nimblepkg/lockfile,
nimblepkg/nimscriptwrapper, nimblepkg/developfile, nimblepkg/paths,
nimblepkg/nimbledatafile, nimblepkg/packagemetadatafile,
nimblepkg/displaymessages, nimblepkg/sha1hashes, nimblepkg/syncfile,
nimblepkg/deps
nimblepkg/deps, nimblepkg/nimblesat

const
nimblePathsFileName* = "nimble.paths"
Expand All @@ -28,34 +28,6 @@ const
nimblePathsEnv = "__NIMBLE_PATHS"
separator = when defined(windows): ";" else: ":"

proc refresh(options: Options) =
## Downloads the package list from the specified URL.
##
## If the download is not successful, an exception is raised.
if options.offline:
raise nimbleError("Cannot refresh package list in offline mode.")

let parameter =
if options.action.typ == actionRefresh:
options.action.optionalURL
else:
""

if parameter.len > 0:
if parameter.isUrl:
let cmdLine = PackageList(name: "commandline", urls: @[parameter])
fetchList(cmdLine, options)
else:
if parameter notin options.config.packageLists:
let msg = "Package list with the specified name not found."
raise nimbleError(msg)

fetchList(options.config.packageLists[parameter], options)
else:
# Try each package list in config
for name, list in options.config.packageLists:
fetchList(list, options)

proc initPkgList(pkgInfo: PackageInfo, options: Options): seq[PackageInfo] =
let
installedPkgs = getInstalledPkgsMin(options.getPkgsDir(), options)
Expand All @@ -79,6 +51,50 @@ proc checkSatisfied(options: Options, dependencies: seq[PackageInfo]) =
[pkgInfo.basicInfo.name, $currentVer, $pkgsInPath[pkgInfo.basicInfo.name]])
pkgsInPath[pkgInfo.basicInfo.name] = currentVer

proc processFreeDependenciesSAT(rootPkgInfo: PackageInfo, pkgList: seq[PackageInfo], options: Options): HashSet[PackageInfo] =
result = solveLocalPackages(rootPkgInfo, pkgList)
if result.len > 0: return result

var reverseDependencies: seq[PackageBasicInfo] = @[]
var pkgsToInstall: seq[(string, Version)] = @[]
var output = ""
result = solvePackages(rootPkgInfo, pkgList, pkgsToInstall, options, output)
if pkgsToInstall.len > 0:
for pkg in pkgsToInstall:
let dep = (name: pkg[0], ver: pkg[1].toVersionRange)
let resolvedDep = dep.resolveAlias(options)
display("Installing", $resolvedDep, priority = HighPriority)
let toInstall = @[(resolvedDep.name, resolvedDep.ver)]
#TODO install here will download the package again. We could use the already downloaded package
#from the cache
let (packages, _) = install(toInstall, options,
doPrompt = false, first = false, fromLockFile = false, preferredPackages = @[])

for pkg in packages:
if result.contains pkg:
# If the result already contains the newly tried to install package
# we had to merge its special versions set into the set of the old
# one.
result[pkg].metaData.specialVersions.incl(
pkg.metaData.specialVersions)
else:
result.incl pkg

if not pkg.isLink:
reverseDependencies.add(pkg.basicInfo)
if result.len > 0:
# We add the reverse deps to the JSON file here because we don't want
# them added if the above errorenous condition occurs
# (unsatisfiable dependendencies).
# N.B. NimbleData is saved in installFromDir.
for i in reverseDependencies:
addRevDep(options.nimbleData, i, rootPkgInfo)
return result
else:
display("Error", output, Error, priority = HighPriority)
raise nimbleError("Unsatisfiable dependencies")


proc processFreeDependencies(pkgInfo: PackageInfo,
requirements: seq[PkgTuple],
options: Options,
Expand All @@ -92,7 +108,12 @@ proc processFreeDependencies(pkgInfo: PackageInfo,
"processFreeDependencies needs pkgInfo.requires"

var pkgList {.global.}: seq[PackageInfo]
once: pkgList = initPkgList(pkgInfo, options)

once:
pkgList = initPkgList(pkgInfo, options)
if options.useSatSolver:
return processFreeDependenciesSAT(pkgInfo, pkgList, options)

display("Verifying", "dependencies for $1@$2" %
[pkgInfo.basicInfo.name, $pkgInfo.basicInfo.version],
priority = HighPriority)
Expand Down Expand Up @@ -686,33 +707,6 @@ proc processLockedDependencies(pkgInfo: PackageInfo, options: Options):

return res.toHashSet

proc getDownloadInfo*(pv: PkgTuple, options: Options,
doPrompt: bool, ignorePackageCache = false): (DownloadMethod, string,
Table[string, string]) =
if pv.name.isURL:
let (url, metadata) = getUrlData(pv.name)
return (checkUrlType(url), url, metadata)
else:
var pkg = initPackage()
if getPackage(pv.name, options, pkg, ignorePackageCache):
let (url, metadata) = getUrlData(pkg.url)
return (pkg.downloadMethod, url, metadata)
else:
# If package is not found give the user a chance to refresh
# package.json
if doPrompt and not options.offline and
options.prompt(pv.name & " not found in any local packages.json, " &
"check internet for updated packages?"):
refresh(options)

# Once we've refreshed, try again, but don't prompt if not found
# (as we've already refreshed and a failure means it really
# isn't there)
# Also ignore the package cache so the old info isn't used
return getDownloadInfo(pv, options, false, true)
else:
raise nimbleError(pkgNotFoundMsg(pv))

proc compileNim(realDir: string) =
let command = when defined(windows): "build_all.bat" else: "./build_all.sh"
cd realDir:
Expand Down
68 changes: 65 additions & 3 deletions src/nimblepkg/download.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import parseutils, os, osproc, strutils, tables, pegs, uri, strformat,
from algorithm import SortOrder, sorted

import packageinfotypes, packageparser, version, tools, common, options, cli,
sha1hashes, vcstools
sha1hashes, vcstools, displaymessages, packageinfo, config

type
DownloadPkgResult* = tuple
Expand Down Expand Up @@ -461,13 +461,20 @@ proc downloadPkg*(url: string, verRange: VersionRange,

if options.offline:
raise nimbleError("Cannot download in offline mode.")

let downloadDir =
if downloadPath == "":
(getNimbleTempDir() / getDownloadDirName(url, verRange, vcsRevision))
let dir = if options.pkgCachePath != "":
options.pkgCachePath
else:
getNimbleTempDir()
(dir / getDownloadDirName(url, verRange, vcsRevision))
else:
downloadPath

if options.pkgCachePath != "" and dirExists(downloadDir):
#TODO test integrity of the package
return (dir: downloadDir, version: newVersion getSimpleString(verRange), vcsRevision: notSetSha1Hash)

createDir(downloadDir)
var modUrl =
if url.startsWith("git://") and options.config.cloneUsingHttps:
Expand Down Expand Up @@ -544,6 +551,61 @@ proc getDevelopDownloadDir*(url, subdir: string, options: Options): string =
else:
getCurrentDir() / options.action.path / downloadDirName

proc refresh*(options: Options) =
## Downloads the package list from the specified URL.
##
## If the download is not successful, an exception is raised.
if options.offline:
raise nimbleError("Cannot refresh package list in offline mode.")

let parameter =
if options.action.typ == actionRefresh:
options.action.optionalURL
else:
""

if parameter.len > 0:
if parameter.isUrl:
let cmdLine = PackageList(name: "commandline", urls: @[parameter])
fetchList(cmdLine, options)
else:
if parameter notin options.config.packageLists:
let msg = "Package list with the specified name not found."
raise nimbleError(msg)

fetchList(options.config.packageLists[parameter], options)
else:
# Try each package list in config
for name, list in options.config.packageLists:
fetchList(list, options)

proc getDownloadInfo*(pv: PkgTuple, options: Options,
doPrompt: bool, ignorePackageCache = false): (DownloadMethod, string,
Table[string, string]) =
if pv.name.isURL:
let (url, metadata) = getUrlData(pv.name)
return (checkUrlType(url), url, metadata)
else:
var pkg = initPackage()
if getPackage(pv.name, options, pkg, ignorePackageCache):
let (url, metadata) = getUrlData(pkg.url)
return (pkg.downloadMethod, url, metadata)
else:
# If package is not found give the user a chance to refresh
# package.json
if doPrompt and not options.offline and
options.prompt(pv.name & " not found in any local packages.json, " &
"check internet for updated packages?"):
refresh(options)

# Once we've refreshed, try again, but don't prompt if not found
# (as we've already refreshed and a failure means it really
# isn't there)
# Also ignore the package cache so the old info isn't used
return getDownloadInfo(pv, options, false, true)
else:
raise nimbleError(pkgNotFoundMsg(pv))

when isMainModule:
import unittest

Expand Down
Loading