Skip to content

Installing with dependencies

The procedure a client follows to write a program and everything it needs, in the right order, without walking the graph.

This is what Swoop does, and what any client writing to a calculator should do.

  1. Ask what has to come first

    curl -s https://roost.ceagle.cc/api/v1/programs/snake/requires

    The answer is already flattened and already ordered: deepest dependency first, the program itself not included.

    Do not walk the graph yourself. It is flattened server-side precisely so that a client on a calculator does not have to.

  2. Skip what is already there

    Every entry carries a checksum. Anything on the calculator at the same checksum is already correct, whatever its version string says.

    for each requirement:
        if calculator has requirement.var_name
           and checksum(calculator copy) == requirement.checksum:
            skip
  3. Write each requirement, in order

    Fetch raw_url, then write it using the metadata from the same entry:

    FieldUse
    var_namethe name to create on the calculator
    var_typeprogram or appvar
    sizehow many bytes to expect
    archivedwhether to move it to archive memory
    checksumwhat to verify after writing
  4. Write the program last

    Same procedure, using the program's own record.

The response#

{
  "slug": "snake",
  "programs": [
    {
      "slug": "usb-driver",
      "title": "USB Driver",
      "version": "2.1.0",
      "checksum": "a3f1…",
      "size": 8412,
      "size_text": "8.2 KB",
      "var_name": "USBDRVCE",
      "var_type": "appvar",
      "archived": true,
      "language": "lib",
      "raw_url": "/d/usb-driver/raw"
    }
  ]
}

The same array rides along on the single-program endpoint as requires, so a client that is already fetching the program page does not need a second request.

Why the order matters#

LibLoad resolves a program's imports when the program runs, against whatever appvars are present. A program written before its library will fail to run rather than fail to install, which is a worse failure because it happens later and away from the thing that caused it.

Writing deepest-first means the calculator is never in a state where a program is present and its library is not.

Free space#

The list gives you every size before you write anything, so a client can add them up and refuse early rather than failing halfway through.

needed = sum(requirement.size for requirement in skipped_none) + program.size

When a dependency has gone#

Archiving a project suspends its program, and a suspended program is filtered out of the dependency graph rather than returned with a flag.

So a program whose library was archived comes back with a shorter requires list than its dependencies array. A careful client can compare the two and warn:

declared  = program.dependencies      # what the manifest said
resolved  = program.requires          # what Roost will serve
missing   = declared - {r.slug for r in resolved}

Limits#

direct dependenciesat most 8 per program
cyclesrefused at build time

So the flattened list always terminates.

published firstenforced at build time

A dependency that does not exist fails the build that names it, rather than producing a broken install later.

Declaring dependencies

The publishing side of this.

Read on
CEagle documentation