implemented 'atlas install' command (#20530)

This commit is contained in:
Andreas Rumpf 2022-10-10 11:05:44 +02:00 • committed by GitHub
commit 0f2775aacf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -13,7 +13,7 @@ import std/[parseopt, strutils, os, osproc, unicode, tables, sets, json, jsonuti
import parse_requires, osutils, packagesjson import parse_requires, osutils, packagesjson
const const
Version = "0.1" Version = "0.2"
Usage = "atlas - Nim Package Cloner Version " & Version & """ Usage = "atlas - Nim Package Cloner Version " & Version & """
(c) 2021 Andreas Rumpf (c) 2021 Andreas Rumpf
@ -21,6 +21,7 @@ Usage:
atlas [options] [command] [arguments] atlas [options] [command] [arguments]
Command: Command:
clone url|pkgname clone a package and all of its dependencies clone url|pkgname clone a package and all of its dependencies
install proj.nimble use the .nimble file to setup the project's dependencies
search keyw keywB... search for package that contains the given keywords search keyw keywB... search for package that contains the given keywords
extract file.nimble extract the requirements and custom commands from extract file.nimble extract the requirements and custom commands from
the given Nimble file the given Nimble file
@ -87,7 +88,7 @@ include testdata
proc exec(c: var AtlasContext; cmd: Command; args: openArray[string]): (string, int) = proc exec(c: var AtlasContext; cmd: Command; args: openArray[string]): (string, int) =
when MockupRun: when MockupRun:
assert TestLog[c.step].cmd == cmd assert TestLog[c.step].cmd == cmd, $(TestLog[c.step].cmd, cmd)
case cmd case cmd
of GitDiff, GitTags, GitRevParse, GitPull, GitCurrentCommit: of GitDiff, GitTags, GitRevParse, GitPull, GitCurrentCommit:
result = (TestLog[c.step].output, TestLog[c.step].exitCode) result = (TestLog[c.step].output, TestLog[c.step].exitCode)
@ -318,53 +319,55 @@ proc addUniqueDep(c: var AtlasContext; work: var seq[Dependency];
work.add Dependency(name: toName(tokens[0]), url: url, commit: tokens[2], work.add Dependency(name: toName(tokens[0]), url: url, commit: tokens[2],
rel: toDepRelation(tokens[1])) rel: toDepRelation(tokens[1]))
template toDestDir(p: PackageName): string = p.string
proc collectDeps(c: var AtlasContext; work: var seq[Dependency];
dep: Dependency; nimbleFile: string): string =
# If there is a .nimble file, return the dependency path & srcDir
# else return "".
assert nimbleFile != ""
let nimbleInfo = extractRequiresInfo(c, nimbleFile)
for r in nimbleInfo.requires:
var tokens: seq[string] = @[]
for token in tokenizeRequires(r):
tokens.add token
if tokens.len == 1:
# nimx uses dependencies like 'requires "sdl2"'.
# Via this hack we map them to the first tagged release.
# (See the `isStrictlySmallerThan` logic.)
tokens.add "<"
tokens.add InvalidCommit
elif tokens.len == 2 and tokens[1].startsWith("#"):
# Dependencies can also look like 'requires "sdl2#head"
var commit = tokens[1][1 .. ^1]
tokens[1] = "=="
tokens.add commit
if tokens.len >= 3 and cmpIgnoreCase(tokens[0], "nim") != 0:
c.addUniqueDep work, tokens
result = toDestDir(dep.name) / nimbleInfo.srcDir
proc collectNewDeps(c: var AtlasContext; work: var seq[Dependency]; proc collectNewDeps(c: var AtlasContext; work: var seq[Dependency];
dep: Dependency; result: var seq[string]; dep: Dependency; result: var seq[string];
isMainProject: bool) = isMainProject: bool) =
let nimbleFile = findNimbleFile(c, dep) let nimbleFile = findNimbleFile(c, dep)
if nimbleFile != "": if nimbleFile != "":
let nimbleInfo = extractRequiresInfo(c, nimbleFile) let x = collectDeps(c, work, dep, nimbleFile)
for r in nimbleInfo.requires: result.add x
var tokens: seq[string] = @[]
for token in tokenizeRequires(r):
tokens.add token
if tokens.len == 1:
# nimx uses dependencies like 'requires "sdl2"'.
# Via this hack we map them to the first tagged release.
# (See the `isStrictlySmallerThan` logic.)
tokens.add "<"
tokens.add InvalidCommit
elif tokens.len == 2 and tokens[1].startsWith("#"):
# Dependencies can also look like 'requires "sdl2#head"
var commit = tokens[1][1 .. ^1]
tokens[1] = "=="
tokens.add commit
if tokens.len >= 3 and cmpIgnoreCase(tokens[0], "nim") != 0:
c.addUniqueDep work, tokens
result.add dep.name.string / nimbleInfo.srcDir
else: else:
result.add dep.name.string result.add toDestDir(dep.name)
proc clone(c: var AtlasContext; start: string): seq[string] = proc cloneLoop(c: var AtlasContext; work: var seq[Dependency]): seq[string] =
# non-recursive clone.
let oldErrors = c.errors
var work = @[Dependency(name: toName(start), url: toUrl(c, start), commit: "")]
if oldErrors != c.errors:
error c, toName(start), "cannot resolve package name"
return
c.projectDir = c.workspace / work[0].name.string
result = @[] result = @[]
var i = 0 var i = 0
while i < work.len: while i < work.len:
let w = work[i] let w = work[i]
let destDir = toDestDir(w.name)
let oldErrors = c.errors let oldErrors = c.errors
if not dirExists(c.workspace / w.name.string):
if not dirExists(c.workspace / destDir):
withDir c, c.workspace: withDir c, c.workspace:
let err = cloneUrl(c, w.url, w.name.string, false) let err = cloneUrl(c, w.url, destDir, false)
if err != "": if err != "":
error c, w.name, err error c, w.name, err
if oldErrors == c.errors: if oldErrors == c.errors:
@ -375,30 +378,38 @@ proc clone(c: var AtlasContext; start: string): seq[string] =
collectNewDeps(c, work, w, result, i == 0) collectNewDeps(c, work, w, result, i == 0)
inc i inc i
proc clone(c: var AtlasContext; start: string): seq[string] =
# non-recursive clone.
let url = toUrl(c, start)
var work = @[Dependency(name: toName(start), url: url, commit: "")]
if url == "":
error c, toName(start), "cannot resolve package name"
return
c.projectDir = c.workspace / toDestDir(work[0].name)
result = cloneLoop(c, work)
const const
configPatternBegin = "############# begin Atlas config section ##########\n" configPatternBegin = "############# begin Atlas config section ##########\n"
configPatternEnd = "############# end Atlas config section ##########\n" configPatternEnd = "############# end Atlas config section ##########\n"
proc patchNimCfg(c: var AtlasContext; deps: seq[string]; cfgHere: bool) = proc patchNimCfg(c: var AtlasContext; deps: seq[string]; cfgPath: string) =
var paths = "--noNimblePath\n" var paths = "--noNimblePath\n"
if cfgHere: for d in deps:
let cwd = getCurrentDir() let pkgname = toDestDir d.PackageName
for d in deps: let x = relativePath(c.workspace / pkgname, cfgPath, '/')
let x = relativePath(c.workspace / d, cwd, '/') paths.add "--path:\"" & x & "\"\n"
paths.add "--path:\"" & x & "\"\n"
else:
for d in deps:
paths.add "--path:\"../" & d.replace("\\", "/") & "\"\n"
var cfgContent = configPatternBegin & paths & configPatternEnd var cfgContent = configPatternBegin & paths & configPatternEnd
when MockupRun: when MockupRun:
assert readFile(TestsDir / "nim.cfg") == cfgContent assert readFile(TestsDir / "nim.cfg") == cfgContent
c.mockupSuccess = true c.mockupSuccess = true
else: else:
let cfg = if cfgHere: getCurrentDir() / "nim.cfg" let cfg = cfgPath / "nim.cfg"
else: c.projectDir / "nim.cfg" if cfgPath.len > 0 and not dirExists(cfgPath):
if not fileExists(cfg): error(c, c.projectDir.PackageName, "could not write the nim.cfg")
elif not fileExists(cfg):
writeFile(cfg, cfgContent) writeFile(cfg, cfgContent)
else: else:
let content = readFile(cfg) let content = readFile(cfg)
@ -420,6 +431,22 @@ proc error*(msg: string) =
writeStackTrace() writeStackTrace()
quit "[Error] " & msg quit "[Error] " & msg
proc findSrcDir(c: var AtlasContext): string =
for nimbleFile in walkPattern("*.nimble"):
let nimbleInfo = extractRequiresInfo(c, nimbleFile)
return nimbleInfo.srcDir
return ""
proc installDependencies(c: var AtlasContext; nimbleFile: string) =
# 1. find .nimble file in CWD
# 2. install deps from .nimble
var work: seq[Dependency] = @[]
let (path, pkgname, _) = splitFile(nimbleFile)
let dep = Dependency(name: toName(pkgname), url: "", commit: "")
discard collectDeps(c, work, dep, nimbleFile)
let paths = cloneLoop(c, work)
patchNimCfg(c, paths, if c.cfgHere: getCurrentDir() else: findSrcDir(c))
proc main = proc main =
var action = "" var action = ""
var args: seq[string] = @[] var args: seq[string] = @[]
@ -460,15 +487,26 @@ proc main =
of "clone": of "clone":
singleArg() singleArg()
let deps = clone(c, args[0]) let deps = clone(c, args[0])
patchNimCfg c, deps, false patchNimCfg c, deps, if c.cfgHere: getCurrentDir() else: findSrcDir(c)
if c.cfgHere:
patchNimCfg c, deps, true
when MockupRun: when MockupRun:
if not c.mockupSuccess: if not c.mockupSuccess:
error "There were problems." error "There were problems."
else: else:
if c.errors > 0: if c.errors > 0:
error "There were problems." error "There were problems."
of "install":
if args.len > 1:
error "install command takes a single argument"
var nimbleFile = ""
if args.len == 1:
nimbleFile = args[0]
else:
for x in walkPattern("*.nimble"):
nimbleFile = x
break
if nimbleFile.len == 0:
error "could not find a .nimble file"
installDependencies(c, nimbleFile)
of "refresh": of "refresh":
noArgs() noArgs()
updatePackages(c) updatePackages(c)