raco mod:   Per-Project Dependencies
1 Commands
2 The Project
3 Choosing Versions
3.1 Git Sources
3.2 Archives
3.3 Directory Sources
4 Isolation and the Cache
5 Limitations
9.3

raco mod: Per-Project Dependencies🔗ℹ

dannypsnl

raco mod gives each project its own dependencies, resolved with Go’s Minimal Version Selection (MVS) and kept out of the global installation. "info.rkt" is still the manifest format, therefore, the same package still installs with plain raco pkg.

  raco mod get megaparsack-lib@latest

    1 Commands

    2 The Project

    3 Choosing Versions

      3.1 Git Sources

      3.2 Archives

      3.3 Directory Sources

    4 Isolation and the Cache

    5 Limitations

1 Commands🔗ℹ

Command

  

Effect

sync

  

resolve "info.rkt" deps with MVS, write "info.sum", and link the build list into ".rkt/"

get pkg[@X.Y|@latest|@none] ...

  

set #:version in the deps of "info.rkt" (or remove the dependency), re-pin, then sync

list

  

print the build list

graph

  

print the requirement graph

why pkg

  

show the requirement chain that selects pkg’s version

env

  

print this project’s environment variables (fish or sh)

exec cmd args ...

  

sync, then run cmd in the project’s environment

clean [-n]

  

delete cached packages, downloads, mirrors, and compiled code that no project uses (-n: only list them)

get rewrites only the deps form of "info.rkt"; comments and every other definition stay as they were. When another dependency forces a higher version than the one asked for, get says so and shows the chain from why.

2 The Project🔗ℹ

A project is an ordinary raco pkg package directory: the nearest directory with "info.sum", else the nearest with "info.rkt". Its own collection is linked following raco pkg’s rules (a collection string, 'multi, or the directory name), so the project’s modules can require each other inside exec.

Concept

  

Comes from

requirements

  

deps, plus build-deps for the project itself

versions

  

the version field of each package’s "info.rkt"

local overrides

  

(define mod-replace '(("pkg" "../path"))) in the project’s "info.rkt"; the package takes the version its directory declares and need not be in any catalog

standard library

  

packages in installation scope: not selected by MVS, but their #:version floors are checked

"info.sum" is generated: one pkg version id time h1:hash line per package, where the id is a commit, sha1:archive-hash, or dir:content-hash, and the time is when that content was made. It makes builds reproducible and verifies content. Commit it.

get edits "info.rkt" and "info.sum" together: if anything fails, both are left as they were.

3 Choosing Versions🔗ℹ

MVS selects, for every package, the maximum of the minimum versions required anywhere in the graph. What a "minimum" is matters in an ecosystem where most packages write "tzinfo" rather than ("tzinfo" #:version "0.6"):

  • A dependency’s minimum is its #:version, raised to the version that was current when the depending content was made (its commit time). An author who wrote an unversioned dependency built against what was current then; the oldest version is almost never what they meant.

  • The project’s own unversioned dependencies keep the version "info.sum" pinned, or else take the latest known one; sync warns about them, and raco mod get pkg@latest records one.

  • The time of each package’s content is pinned in "info.sum" with the content itself, so the same "info.rkt" and "info.sum" select the same build list on every machine, even after upstream moves on.

get pkg@latest re-pins: if the content behind a version moved on upstream (the newest commit of the current version, a republished archive, a changed directory), get switches to it, while sync keeps what "info.sum" pinned.

Catalog entries are read as raco pkg reads them: an entry in versions for the running Racket version wins over the default source. Source types are inferred exactly like raco pkg does (package-source->name+type), so a non-GitHub URL not ending in ".git" is a remote directory, not a git repository.

3.1 Git Sources🔗ℹ

  • Versions are the history of version in "info.rkt" on the default branch; no tags are needed.

  • Version V is the last commit while the version is still V. A superseded version is therefore fixed forever; the current one is pinned by "info.sum".

3.2 Archives🔗ℹ

".zip", ".tgz", and ".plt" archives, such as the Rhombus packages:

  • The version is the version in the archive’s "info.rkt"; the id is the archive’s SHA1. An unchanged catalog checksum means no download, and a download that does not match the catalog’s checksum is refused, as raco pkg does.

  • An archive URL only serves its current content, so older versions survive only in the local cache. If one is needed, missing from the cache, and gone upstream, raco mod says so and suggests get pkg@latest.

  • Entries that would escape the destination ("..", absolute paths) are refused. ".plt" archives are only unpacked; their config and setup unit are never evaluated.

  • Built packages ship machine-independent ".zo" files; the first raco make turns them into native code, the same step raco setup performs.

3.3 Directory Sources🔗ℹ

  • Remote directories follow raco pkg: fetch "MANIFEST", then every file it lists.

  • Local directories are copied; a local directory that is a git repository is used as one.

  • The id is a content hash. Like archives, only the current content is served.

4 Isolation and the Cache🔗ℹ

~/.cache/raco-mod/

  pkg/<pkg>@<ver>+<id>/   extracted packages, read-only, shared by all projects

  zo/<key>/               compiled code, shared by all projects

  vcs/ dl/ index/ source/ git mirrors, downloads, version indexes, catalog entries

  projects.rktd           projects that use the cache, for `clean`

<project>/.rkt/           (gitignored)

  addon/<version>/links.rktd

  compiled/

exec, and the variables printed by env, set:

  • PLTADDONDIR to "<project>/.rkt/addon", so the build list is the user-scope links file and globally installed user-scope packages are invisible.

  • PLTCOMPILEDROOTS to "<project>/.rkt/compiled:", so nothing is written into the shared cache and the installation’s own ".zo" files are reused. The empty trailing element is the symbol 'same; a literal same would be a relative directory named "same".

Compiled code is shared: "<project>/.rkt/compiled/<package path>" is a symbolic link to "zo/<key>", where the key covers the package’s content, the keys of all its dependencies, and the Racket installation. Projects that select the same versions compile each package once (Rhombus: 29 seconds in the first project, 1.3 seconds in the second). The compilation manager still validates every ".zo" by SHA1, so sharing cannot make a build wrong. Not shared: mod-replaced packages, anything depending on them, and dependency cycles.

raco mod clean removes what no registered project uses. Every sync registers its project in "projects.rktd"; clean keeps the "zo/" directories those projects link to and the packages their "info.sum" names, together with those packages’ archives and git mirrors, deletes the rest, and forgets projects that no longer exist. Removed archive and directory versions also leave their version index, so they are never selected again; removed git versions stay, since their mirror can be cloned again.

clean never runs on its own. An archive’s older versions exist only in this cache, so deleting one cannot be undone; use raco mod clean -n to see what would go first.

5 Limitations🔗ℹ

  • Version granularity is whatever authors declare: a package that never bumps version (i.e. always 0.0) has a single version.

  • One version per package in a build, matching Racket’s collection model.

  • No Go-style downgrade: asking for a version below another dependency’s floor selects the higher version and explains why, without downgrading others.

  • clean only knows projects that ran sync on this machine: a checkout that has not synced yet re-fetches what it needs, and an archive version that is gone upstream can no longer be fetched.