Ir al contenido
Molto

Dependencias

Versiones exactas, las dos tablas de dependencias, recipe.toml, Molto.lock y las cachés que hay detrás.

[deps]
sqlite = "3.53.4"                        # una coordenada de registry, en esa versión exacta
http   = { path = "modules/http" }       # un directorio en el que estás trabajando

[dev-deps]
tinytest = { path = "modules/tinytest" } # sólo se compila dentro de tests/
molto add sqlite                      # la última release, escrita en el manifiesto como exacta
molto add sqlite@3.53.4               # esa
molto add tinytest --dev --path ../tt # a [dev-deps]
molto remove sqlite

Cuatro reglas sostienen casi todo el comportamiento

Las versiones son exactas. ^, ~ y >= se rechazan, no se interpretan. Un rango dejaría entrar en tu build una release que nadie ha leído, sin diff.

Una versión por paquete, contando las dos tablas a la vez. Un binario de tests enlaza los objetos de src/ junto a los de los tests, y dos versiones ahí son símbolos duplicados.

Las dependencias de desarrollo no son transitivas, y sus directorios de include se ponen únicamente en la línea de comandos que compila tests/. Un archivo de src/ que incluya una de ellas falla al compilar, en el primer build. Esa es la garantía, no una convención documentada.

Ambas tablas fallan en cerrado. Una clave desconocida, dos orígenes, un rango o una clave especificada pero no implementada (recipe, artifact, optional, features) es un error que nombra la dependencia: justo lo contrario que el resto del manifiesto, que descarta lo que no conoce.

Declarar una

[deps]
yyjson = "1.2.32"                                              # atajo de { version = … }
sqlite = { git = "https://github.com/sqlite/sqlite", tag = "3.50.0" }
http   = { path = "modules/http" }
zlib   = { archive = "https://…", sha256 = "…" }
fast   = { version = "2.0.0", registry = "myorg" }

[registries]
myorg = "https://registry.example.com"

Claves de origen, hace falta exactamente una: version, git, path o archive. Modificadores: branch / tag / rev junto a git y como mucho uno de ellos, sha256 obligatorio con archive, y registry, que debe estar declarado en [registries].

Las opciones de molto add se llaman igual que las claves, así que lo que tecleas y lo que acaba en el manifiesto coinciden. add y remove editan líneas en vez de reescribir el archivo: los comentarios, la alineación y el orden de las claves sobreviven, volver a añadir un nombre con otra versión lo sustituye en su sitio, y cualquier edición que dejaría un manifiesto ilegible se rechaza antes de tocar el archivo.

recipe.toml

Una dependencia se describe a sí misma con un recipe en la raíz de su código. Una dependencia path o git sin él se rechaza con exactamente ese mensaje.

schema  = 1
form    = "source"
kind    = "package"
name    = "sqlite"
version = "3.53.4"
target  = "any"

[source]
archive = "https://sqlite.org/2025/sqlite-amalgamation-3530400.zip"
sha256  = "…"
compression_format = "zip"                     # zip | tar | tar.gz | tar.bz2 | tar.xz | tar.zst
strip_prefix = "sqlite-amalgamation-3530400"

[build]
system = "none"                                # make | cmake | autotools | meson | none

[artifacts]
type    = "source"                             # source | static | shared
sources = ["sqlite3.c"]
exclude = ["shell.c"]
include = ["."]
link    = ["m", "pthread"]
defines = ["SQLITE_THREADSAFE=1"]

Hoy sólo se puede consumir [artifacts] type = "source". Molto compila el código como unidades de traducción propias, fundiendo los includes, defines, flags y links del recipe en el build. static y shared se rechazan con un mensaje, y también cualquier [build] system que no sea none: nunca se ejecuta ningún sistema de build. Nada dentro de Molto invoca make, cmake ni meson.

Un recipe nunca lleva un script. Ni para descomprimir ni para construir. Por eso compression_format nombra un conjunto acotado y [build] nombra un sistema en lugar de una línea de comandos: un recipe capaz de ejecutar una cadena convertiría cada dependencia en una ejecución remota.

compression_format se declara, nunca se deduce: una URL puede acabar en .zip y servir un tarball. Ausente significa deducirlo de la extensión, para recipes escritos antes de que existiera la clave.

[about] es de donde sale la licencia de una dependencia, y molto metadata reporta exactamente lo que encuentre ahí. Un recipe que no declara ninguna produce un componente sin campo licenses: silencio en vez de una afirmación.

Resolución

  1. Lee [deps], [dev-deps] y Molto.lock si está.
  2. Para una coordenada, pregunta al registry una vez por nombre. Sin @<versión>, molto add elige la más nueva por precedencia semver, no por orden de cadena, y escribe ese número en el manifiesto.
  3. Descarga lo que no esté cacheado, recorre el [deps] de cada recipe y repite.
  4. Rechaza una resolución con la que el lock no esté de acuerdo; en caso contrario escribe Molto.lock.

Dos versiones de un mismo paquete es tu decisión, no la de Molto. No hay rango que ensanchar ni versión más alta que elegir, así que todo el trabajo del mensaje es decir quién pidió qué:

molto: sqlite is required at two versions
    3.50.3  ← your Project.toml
    3.53.4  ← required by http 1.2.0

Molto.lock

Se genera, se commitea, se ordena por nombre y con cada lista ordenada, de modo que su diff merezca leerse, que es justo el sentido de commitearlo.

version = 1
root = "app"

[[package]]
name = "a"
version = "1.0.0"
source = "registry+https://…"
checksum = "…"                # se omite en una dependencia path, nunca se escribe vacío
scopes = ["runtime", "dev"]
dependencies = ["b"]

Un lock ausente, ilegible, que no sea TOML válido o que venga de un formato más nuevo recibe el mismo trato: resolver otra vez. Un lock cuyas dependencias directas ya no coinciden con el manifiesto está caducado, y se vuelve a resolver en vez de parchearse.

Las cachés

Ruta Qué
~/.molto/cache/sources/<nombre>/<versión>/<target> Un árbol descargado, una vez por coordenada, sellado al terminar
~/.molto/cache/objects Objetos compilados de dependencias, indexados por el comando de compilación completo
~/.molto/credentials.toml El token del registry, modo 0600

$MOLTO_CACHE reubica las dos primeras. Un directorio sin su sello son los restos de una descarga interrumpida: se borra, no se lee.

La caché de objetos cubre sólo dependencias. Reutilizar un objeto exige saber que la fuente y cada cabecera que incluye son idénticas, y en una dependencia la coordenada responde por el árbol inmutable entero. Las fuentes propias de un proyecto son mutables y su grafo de cabeceras se desconoce hasta que el compilador ha corrido: ese es el problema de ccache, y Molto no intenta ser uno. Una dependencia path queda fuera por la misma razón: sus bytes son lo que haya en disco ahora mismo.

Descargar delega en curl, tar, unzip y git, con el mismo razonamiento que delegar en un compilador en vez de enlazar uno.

El registry

Las lecturas son públicas; sólo publicar necesita token. molto login cambia correo y contraseña por uno, o guarda con --token uno copiado de la página de cuenta. El token nunca aparece en argv: ps lo puede leer cualquiera, así que la cabecera va por un archivo de configuración de curl creado con permisos 0600 y borrado después.

molto publish envía primero el blob y al final la fila. --dry-run lee y calcula los hashes sin enviar nada, que es la única forma de revisar un recipe: una coordenada publicada es inmutable.