summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2023-05-24 16:39:58 +0200
committerGitHub <noreply@github.com>2023-05-24 16:39:58 +0200
commitb63b5c930e143139197dbacd24bad7d263bbd2e3 (patch)
tree5a1c5c1765933da666b17d3eaab62f121f7c2413
parent266cc69f1973773228534f074febd153c472b949 (diff)
downloadNim-b63b5c930e143139197dbacd24bad7d263bbd2e3.tar.gz
Atlas: added 'use' command (#21902)
* Atlas: added 'use' command

* typo
-rw-r--r--tools/atlas/atlas.md21
-rw-r--r--tools/atlas/atlas.nim90
2 files changed, 104 insertions, 7 deletions
diff --git a/tools/atlas/atlas.md b/tools/atlas/atlas.md
index 3e9bc32e5..40cf6411e 100644
--- a/tools/atlas/atlas.md
+++ b/tools/atlas/atlas.md
@@ -60,6 +60,27 @@ patch the `nim.cfg` then.
 Atlas supports the following commands:
 
 
+## Use <url> / <package name>
+
+Clone the package behind `url` or `package name` and its dependencies into
+the `_deps` directory and make it available for your current project which
+should be in the current working directory. Atlas will create or patch
+the files `$project.nimble` and `nim.cfg` for you so that you can simply
+import the required modules.
+
+For example:
+
+```
+  mkdir newproject
+  cd newproject
+  git init
+  atlas use lexim
+  # add `import lexim` to your example.nim file
+  nim c example.nim
+
+```
+
+
 ### Clone/Update <url>
 
 Clones a URL and all of its dependencies (recursively) into the workspace.
diff --git a/tools/atlas/atlas.nim b/tools/atlas/atlas.nim
index 0358e107c..ddfbbd086 100644
--- a/tools/atlas/atlas.nim
+++ b/tools/atlas/atlas.nim
@@ -23,6 +23,8 @@ const
 Usage:
   atlas [options] [command] [arguments]
 Command:
+  use url|pkgname       clone a package and all of its dependencies and make
+                        it importable for the current project
   clone url|pkgname     clone a package and all of its dependencies
   update url|pkgname    update a package and all of its dependencies
   install proj.nimble   use the .nimble file to setup the project's dependencies
@@ -193,13 +195,14 @@ proc message(c: var AtlasContext; category: string; p: PackageName; args: vararg
     msg.add ' '
     msg.add a
   stdout.writeLine msg
-  inc c.errors
 
 proc warn(c: var AtlasContext; p: PackageName; args: varargs[string]) =
   message(c, "[Warning] ", p, args)
+  inc c.errors
 
 proc error(c: var AtlasContext; p: PackageName; args: varargs[string]) =
   message(c, "[Error] ", p, args)
+  inc c.errors
 
 proc sameVersionAs(tag, ver: string): bool =
   const VersionChars = {'0'..'9', '.'}
@@ -424,7 +427,7 @@ proc collectNewDeps(c: var AtlasContext; work: var seq[Dependency];
   else:
     result.add toDestDir(dep.name)
 
-proc cloneLoop(c: var AtlasContext; work: var seq[Dependency]): seq[string] =
+proc cloneLoop(c: var AtlasContext; work: var seq[Dependency]; startIsDep: bool): seq[string] =
   result = @[]
   var i = 0
   while i < work.len:
@@ -433,7 +436,7 @@ proc cloneLoop(c: var AtlasContext; work: var seq[Dependency]): seq[string] =
     let oldErrors = c.errors
 
     if not dirExists(c.workspace / destDir) and not dirExists(c.depsDir / destDir):
-      withDir c, (if i == 0: c.workspace else: c.depsDir):
+      withDir c, (if i != 0 or startIsDep: c.depsDir else: c.workspace):
         let err = cloneUrl(c, w.url, destDir, false)
         if err != "":
           error c, w.name, err
@@ -453,7 +456,7 @@ proc readLockFile(c: var AtlasContext) =
   for d in items(data):
     c.lockFileToUse[d.dir] = d
 
-proc clone(c: var AtlasContext; start: string): seq[string] =
+proc clone(c: var AtlasContext; start: string; startIsDep: bool): seq[string] =
   # non-recursive clone.
   let url = toUrl(c, start)
   var work = @[Dependency(name: toName(start), url: url, commit: "")]
@@ -465,7 +468,7 @@ proc clone(c: var AtlasContext; start: string): seq[string] =
   c.projectDir = c.workspace / toDestDir(work[0].name)
   if c.lockOption == useLock:
     readLockFile c
-  result = cloneLoop(c, work)
+  result = cloneLoop(c, work, startIsDep)
   if c.lockOption == genLock:
     writeFile c.projectDir / LockFileName, toJson(c.lockFileToWrite).pretty
 
@@ -525,7 +528,7 @@ proc installDependencies(c: var AtlasContext; nimbleFile: string) =
   let (_, pkgname, _) = splitFile(nimbleFile)
   let dep = Dependency(name: toName(pkgname), url: "", commit: "")
   discard collectDeps(c, work, dep, nimbleFile)
-  let paths = cloneLoop(c, work)
+  let paths = cloneLoop(c, work, startIsDep = true)
   patchNimCfg(c, paths, if c.cfgHere: getCurrentDir() else: findSrcDir(c))
 
 proc updateWorkspace(c: var AtlasContext; dir, filter: string) =
@@ -549,6 +552,71 @@ proc updateWorkspace(c: var AtlasContext; dir, filter: string) =
             else:
               error c, pkg, "could not fetch current branch name"
 
+proc addUnique[T](s: var seq[T]; elem: sink T) =
+  if not s.contains(elem): s.add elem
+
+proc addDepFromNimble(c: var AtlasContext; deps: var seq[string]; project: PackageName; dep: string) =
+  var depDir = c.workspace / dep
+  if not dirExists(depDir):
+    depDir = c.depsDir /  dep
+  if dirExists(depDir):
+    withDir c, depDir:
+      let src = findSrcDir(c)
+      if src.len != 0:
+        deps.addUnique dep / src
+      else:
+        deps.addUnique dep
+  else:
+    warn c, project, "cannot find: " & depDir
+
+proc patchNimbleFile(c: var AtlasContext; dep: string; deps: var seq[string]) =
+  let thisProject = getCurrentDir().splitPath.tail
+  let oldErrors = c.errors
+  let url = toUrl(c, dep)
+  if oldErrors != c.errors:
+    warn c, toName(dep), "cannot resolve package name"
+  else:
+    var nimbleFile = ""
+    for x in walkFiles("*.nimble"):
+      if nimbleFile.len == 0:
+        nimbleFile = x
+      else:
+        # ambiguous .nimble file
+        warn c, toName(dep), "cannot determine `.nimble` file; there are multiple to choose from"
+        return
+    # see if we have this requirement already listed. If so, do nothing:
+    var found = false
+    if nimbleFile.len > 0:
+      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 > 0:
+          let oldErrors = c.errors
+          let urlB = toUrl(c, tokens[0])
+          if oldErrors != c.errors:
+            warn c, toName(tokens[0]), "cannot resolve package name; found in: " & nimbleFile
+          if url == urlB:
+            found = true
+
+          if cmpIgnoreCase(tokens[0], "nim") != 0:
+            c.addDepFromNimble deps, toName(thisProject), tokens[0]
+
+    if not found:
+      let line = "requires \"$1@#head\"\n" % dep.escape("", "")
+      if nimbleFile.len > 0:
+        let oldContent = readFile(nimbleFile)
+        writeFile nimbleFile, oldContent & "\n" & line
+        message(c, "[Info] ", toName(thisProject), "updated: " & nimbleFile)
+      else:
+        let outfile = thisProject & ".nimble"
+        writeFile outfile, line
+        message(c, "[Info] ", toName(thisProject), "created: " & outfile)
+      c.addDepFromNimble deps, toName(thisProject), dep
+    else:
+      message(c, "[Info] ", toName(thisProject), "up to date: " & nimbleFile)
+
 proc main =
   var action = ""
   var args: seq[string] = @[]
@@ -627,7 +695,7 @@ proc main =
     error "No action."
   of "clone", "update":
     singleArg()
-    let deps = clone(c, args[0])
+    let deps = clone(c, args[0], startIsDep = false)
     patchNimCfg c, deps, if c.cfgHere: getCurrentDir() else: findSrcDir(c)
     when MockupRun:
       if not c.mockupSuccess:
@@ -635,6 +703,14 @@ proc main =
     else:
       if c.errors > 0:
         error "There were problems."
+  of "use":
+    singleArg()
+    discard clone(c, args[0], startIsDep = true)
+    var deps: seq[string] = @[]
+    patchNimbleFile(c, args[0], deps)
+    patchNimCfg c, deps, getCurrentDir()
+    if c.errors > 0:
+      error "There were problems."
   of "install":
     if args.len > 1:
       error "install command takes a single argument"