From 0bd8fc78672dd160bd8227079b2eafa6ac2646fb Mon Sep 17 00:00:00 2001 From: Ivan Yonchovski Date: Mon, 25 Jul 2022 12:07:30 +0300 Subject: [PATCH 1/2] Implement 'nimble depsTree' I am open on suggestion for better/different representation of the tree. Once we agree on the format, I will update the documentation and add tests --- src/nimble.nim | 72 +++++++++++++++++++++++++++++++++++++++ src/nimblepkg/options.nim | 15 +++++++- tests/deps/deps.nimble | 6 ++++ tests/deps/src/def.nim | 1 + tests/tdeps.nim | 43 +++++++++++++++++++++++ 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 tests/deps/deps.nimble create mode 100644 tests/deps/src/def.nim create mode 100644 tests/tdeps.nim diff --git a/src/nimble.nim b/src/nimble.nim index deb2e0e4..dca833ec 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -1630,6 +1630,76 @@ proc lock(options: Options) = updateSyncFile(pkgInfo, options) displayLockOperationFinish(doesLockFileExist) +type + DependencyNode = ref object of RootObj + name*: string + version*: string + resolvedTo*: string + error*: string + dependencies*: seq[DependencyNode] + +proc depsRecursive(pkgInfo: PackageInfo, + dependencies: seq[PackageInfo], + errors: ValidationErrors): seq[DependencyNode] = + result = @[] + for (name, ver) in pkgInfo.requires: + var depPkgInfo = initPackageInfo() + let + found = dependencies.findPkg((name, ver), depPkgInfo) + packageName = if found: depPkgInfo.basicInfo.name else: name + + let node = DependencyNode(name: packageName) + + result.add node + node.version = if ver.kind == verAny: "@any" else: $ver + node.resolvedTo = if found: $depPkgInfo.basicInfo.version else: "" + node.error = if errors.contains(packageName): + getValidationErrorMessage(packageName, errors.getOrDefault packageName) + else: "" + + if found: + node.dependencies = depsRecursive(depPkgInfo, dependencies, errors) + +proc printDepsHumanReadable(pkgInfo: PackageInfo, dependencies: seq[PackageInfo], level: int, errors: ValidationErrors) = + for (name, ver) in pkgInfo.requires: + var depPkgInfo = initPackageInfo() + let + found = dependencies.findPkg((name, ver), depPkgInfo) + packageName = if found: depPkgInfo.basicInfo.name else: name + + echo " ".repeat(level * 2), + packageName, + if ver.kind == verAny: "@any" else: " " & $ver, + if found: fmt "(resolved {depPkgInfo.basicInfo.version})" else: "", + if errors.contains(packageName): + " - error: " & getValidationErrorMessage(packageName, errors.getOrDefault packageName) + else: + "" + if found: printDepsHumanReadable(depPkgInfo, dependencies, level + 1, errors) + +proc depsTree(options: Options) = + ## Prints the dependency tree + + let pkgInfo = getPkgInfo(getCurrentDir(), options) + + var errors = validateDevModeDepsWorkingCopiesBeforeLock(pkgInfo, options) + + let dependencies = pkgInfo.processFreeDependencies(options).map( + pkg => pkg.toFullInfo(options)).toSeq + pkgInfo.validateDevelopDependenciesVersionRanges(dependencies, options) + var dependencyGraph = buildDependencyGraph(dependencies, options) + + # delete errors for dependencies that aren't part of the graph + for name, error in common.dup errors: + if not dependencyGraph.contains name: + errors.del name + + if options.action.format == "json": + echo (%depsRecursive(pkgInfo, dependencies, errors)).pretty + else: + echo pkgInfo.basicInfo.name + printDepsHumanReadable(pkgInfo, dependencies, 1, errors) + proc syncWorkingCopy(name: string, path: Path, dependentPkg: PackageInfo, options: Options) = ## Syncs a working copy of a develop mode dependency of package `dependentPkg` @@ -1969,6 +2039,8 @@ proc doAction(options: var Options) = check(options) of actionLock: lock(options) + of actionDeps: + depsTree(options) of actionSync: sync(options) of actionSetup: diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim index cf31eeb2..449a8fc4 100644 --- a/src/nimblepkg/options.nim +++ b/src/nimblepkg/options.nim @@ -55,7 +55,7 @@ type actionInstall, actionSearch, actionList, actionBuild, actionPath, actionUninstall, actionCompile, actionDoc, actionCustom, actionTasks, actionDevelop, actionCheck, actionLock, actionRun, actionSync, actionSetup, - actionClean + actionClean, actionDeps DevelopActionType* = enum datAdd, datRemoveByPath, datRemoveByName, datInclude, datExclude @@ -101,6 +101,8 @@ type arguments*: seq[string] custCompileFlags*: seq[string] custRunFlags*: seq[string] + of actionDeps: + format*: string const help* = """ @@ -189,6 +191,9 @@ Commands: the name of an installed package. [--ini, --json] Selects the output format (the default is --ini). lock Generates or updates a package lock file. + deps Outputs dependency tree + [--format type] Specify the output format. Json is the only supported + format sync Synchronizes develop mode dependencies with the content of the lock file. [-l, --list-only] Only lists the packages which are not synced @@ -283,6 +288,8 @@ proc parseActionType*(action: string): ActionType = result = actionCheck of "lock": result = actionLock + of "deps": + result = actionDeps of "sync": result = actionSync of "setup": @@ -603,6 +610,12 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) = result.action.listOnly = true else: wasFlagHandled = false + of actionDeps: + case f + of "format": + result.action.format = val + else: + wasFlagHandled = false else: wasFlagHandled = false diff --git a/tests/deps/deps.nimble b/tests/deps/deps.nimble new file mode 100644 index 00000000..13da44a9 --- /dev/null +++ b/tests/deps/deps.nimble @@ -0,0 +1,6 @@ +version = "0.1.0" +author = "Ivan Yonchovski" +description = "Nim package manager." +license = "BSD" + +requires "timezones == 0.5.4" diff --git a/tests/deps/src/def.nim b/tests/deps/src/def.nim new file mode 100644 index 00000000..36270b21 --- /dev/null +++ b/tests/deps/src/def.nim @@ -0,0 +1 @@ +echo "def727" \ No newline at end of file diff --git a/tests/tdeps.nim b/tests/tdeps.nim new file mode 100644 index 00000000..f5cfdccc --- /dev/null +++ b/tests/tdeps.nim @@ -0,0 +1,43 @@ +# Copyright (C) Dominik Picheta. All rights reserved. +# BSD License. Look at license.txt for more info. + +{.used.} + +import unittest, os, osproc, strutils, strformat +import testscommon +from nimblepkg/common import cd + +suite "nimble deps": + test "nimble deps": + cd "deps": + let (output, exitCode) = execCmdEx(nimblePath & " --silent deps -y") + check exitCode == QuitSuccess + check output.contains(""" +deps + timezones 0.5.4(resolved 0.5.4) + nim >= 0.19.9 +""") + + test "nimble deps(json)": + cd "issue727": + let (output, exitCode) = execCmdEx(nimblePath & " --format:json deps -y") + check exitCode == QuitSuccess + check output.contains(""" +[ + { + "name": "timezones", + "version": "@any", + "resolvedTo": "0.5.4", + "error": "", + "dependencies": [ + { + "name": "nim", + "version": ">= 0.19.9", + "resolvedTo": "", + "error": "", + "dependencies": [] + } + ] + } +] +""") From adbf0770e8ea9ef1c8e3650c15ddba91a30e13cf Mon Sep 17 00:00:00 2001 From: Ivan Yonchovski Date: Thu, 1 Sep 2022 13:42:01 +0300 Subject: [PATCH 2/2] Move deps related stuff into separate package --- src/nimble.nim | 49 ++-------------------------------------- src/nimblepkg/deps.nim | 51 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 47 deletions(-) create mode 100644 src/nimblepkg/deps.nim diff --git a/src/nimble.nim b/src/nimble.nim index dca833ec..2d66714a 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -19,7 +19,8 @@ import nimblepkg/packageinfotypes, nimblepkg/packageinfo, nimblepkg/version, nimblepkg/checksums, nimblepkg/topologicalsort, nimblepkg/lockfile, nimblepkg/nimscriptwrapper, nimblepkg/developfile, nimblepkg/paths, nimblepkg/nimbledatafile, nimblepkg/packagemetadatafile, - nimblepkg/displaymessages, nimblepkg/sha1hashes, nimblepkg/syncfile + nimblepkg/displaymessages, nimblepkg/sha1hashes, nimblepkg/syncfile, + nimblepkg/deps const nimblePathsFileName* = "nimble.paths" @@ -1630,52 +1631,6 @@ proc lock(options: Options) = updateSyncFile(pkgInfo, options) displayLockOperationFinish(doesLockFileExist) -type - DependencyNode = ref object of RootObj - name*: string - version*: string - resolvedTo*: string - error*: string - dependencies*: seq[DependencyNode] - -proc depsRecursive(pkgInfo: PackageInfo, - dependencies: seq[PackageInfo], - errors: ValidationErrors): seq[DependencyNode] = - result = @[] - for (name, ver) in pkgInfo.requires: - var depPkgInfo = initPackageInfo() - let - found = dependencies.findPkg((name, ver), depPkgInfo) - packageName = if found: depPkgInfo.basicInfo.name else: name - - let node = DependencyNode(name: packageName) - - result.add node - node.version = if ver.kind == verAny: "@any" else: $ver - node.resolvedTo = if found: $depPkgInfo.basicInfo.version else: "" - node.error = if errors.contains(packageName): - getValidationErrorMessage(packageName, errors.getOrDefault packageName) - else: "" - - if found: - node.dependencies = depsRecursive(depPkgInfo, dependencies, errors) - -proc printDepsHumanReadable(pkgInfo: PackageInfo, dependencies: seq[PackageInfo], level: int, errors: ValidationErrors) = - for (name, ver) in pkgInfo.requires: - var depPkgInfo = initPackageInfo() - let - found = dependencies.findPkg((name, ver), depPkgInfo) - packageName = if found: depPkgInfo.basicInfo.name else: name - - echo " ".repeat(level * 2), - packageName, - if ver.kind == verAny: "@any" else: " " & $ver, - if found: fmt "(resolved {depPkgInfo.basicInfo.version})" else: "", - if errors.contains(packageName): - " - error: " & getValidationErrorMessage(packageName, errors.getOrDefault packageName) - else: - "" - if found: printDepsHumanReadable(depPkgInfo, dependencies, level + 1, errors) proc depsTree(options: Options) = ## Prints the dependency tree diff --git a/src/nimblepkg/deps.nim b/src/nimblepkg/deps.nim new file mode 100644 index 00000000..1caee424 --- /dev/null +++ b/src/nimblepkg/deps.nim @@ -0,0 +1,51 @@ +import packageinfotypes, developfile, packageinfo, version, tables, strformat, strutils + +type + DependencyNode = ref object of RootObj + name*: string + version*: string + resolvedTo*: string + error*: string + dependencies*: seq[DependencyNode] + +proc depsRecursive*(pkgInfo: PackageInfo, + dependencies: seq[PackageInfo], + errors: ValidationErrors): seq[DependencyNode] = + result = @[] + for (name, ver) in pkgInfo.requires: + var depPkgInfo = initPackageInfo() + let + found = dependencies.findPkg((name, ver), depPkgInfo) + packageName = if found: depPkgInfo.basicInfo.name else: name + + let node = DependencyNode(name: packageName) + + result.add node + node.version = if ver.kind == verAny: "@any" else: $ver + node.resolvedTo = if found: $depPkgInfo.basicInfo.version else: "" + node.error = if errors.contains(packageName): + getValidationErrorMessage(packageName, errors.getOrDefault packageName) + else: "" + + if found: + node.dependencies = depsRecursive(depPkgInfo, dependencies, errors) + +proc printDepsHumanReadable*(pkgInfo: PackageInfo, + dependencies: seq[PackageInfo], + level: int, + errors: ValidationErrors) = + for (name, ver) in pkgInfo.requires: + var depPkgInfo = initPackageInfo() + let + found = dependencies.findPkg((name, ver), depPkgInfo) + packageName = if found: depPkgInfo.basicInfo.name else: name + + echo " ".repeat(level * 2), + packageName, + if ver.kind == verAny: "@any" else: " " & $ver, + if found: fmt "(resolved {depPkgInfo.basicInfo.version})" else: "", + if errors.contains(packageName): + " - error: " & getValidationErrorMessage(packageName, errors.getOrDefault packageName) + else: + "" + if found: printDepsHumanReadable(depPkgInfo, dependencies, level + 1, errors)