Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Build a package with dependencies

A package is a directory with a plenty.toml manifest and sources under src/. plenty new hello creates one; plenty new shapes --lib creates a library. Here is a library package in shapes/:

manifest-version = 1

[package]
name = "shapes"
version = "0.1.0"

src/lib.plenty is the library: the module that other packages import.

pub class Square:
    pub side: i64

    pub def area(self) -> Result[i64, OverflowError[i64]]:
        Ok(self.side * self.side)

An application beside it declares the library as a dependency. The key under [dependencies] is the name that application code imports, and path is relative to this manifest:

manifest-version = 1

[package]
name = "app"
version = "0.1.0"

[dependencies]
geometry = { path = "../shapes" }

src/main.plenty is the executable, named after the package:

import geometry
from report import show

def main() -> Result[(), AllocError | IoError | OverflowError[i64]]:
    square = geometry.Square(4)
    show(square.area()?)

Other files under src/ are the package’s own modules, imported as before:

pub def show(area: i64) -> Result[(), IoError]:
    print("area:")?
    print(area)?
    Ok(())

Run plenty run anywhere inside app/:

plenty run
area:
16

plenty run compiles into target/optimized/app and runs it. Progress messages go to standard error; program arguments follow --, as in plenty run -- a b. Builds optimize by default. For shorter edit-run cycles in large projects, use plenty run --fast-compile or plenty build --fast-compile; these write to target/fast-compile/ and keep the same overflow, bounds and ownership checks. Use the default profile when measuring runtime performance. A later build with no changed source reports Fresh and reuses the executable.

The first build also writes plenty.lock, which records the resolved packages. Commit it with the manifest. --locked makes a build fail instead of changing it.

A dependency can also come from a Git repository, at a tag or a commit. The official time-zone package is one:

[dependencies]
tz = { git = "https://github.com/plentylang/tz.git", tag = "v0.3.0", version = "0.3.0" }

The first build fetches the repository into .pkg/ and records the exact commit in plenty.lock. Later builds use that commit even if the tag moves upstream, until plenty update resolves it again. The optional version checks that the dependency’s own version is compatible. plenty add writes these entries and updates the lockfile:

plenty add geometry --path ../shapes
plenty add tz --git https://github.com/plentylang/tz.git --tag v0.3.0

Several related packages can share one lockfile and target/ as a workspace.

A dependency’s name belongs only to the package that declares it. app cannot import what shapes depends on without declaring it too, and a local module named geometry would be an error. Two packages may each define Square; those are distinct types.

The dependency’s name also reaches its other modules. If shapes had src/circles.plenty, import geometry.circles would bind it beside import geometry, unless the library itself declares something named circles; modules and imports gives the rule.

Programs in tests/ are integration tests. Each one sees the package’s library under the package name, and passes when main returns exit status zero:

from shapes import Square

def main() -> Result[i32, OverflowError[i64]]:
    if Square(3).area()? == 9:
        Ok(0i32)
    else:
        Ok(1i32)
plenty test
running 1 test
test area ... ok

test result: ok. 1 passed; 0 failed

A later lesson shows test functions and std.test. plenty check type-checks every target without linking. It reaches only the files that targets import, so it warns about any other .plenty file under src/ and ends with a summary such as Checked app v0.1.0: 1 executable; 2 of 2 files in src/. Keep test fixtures and other files that are not package source outside src/. plenty clean removes target/. Single files still run with plenty FILE; the compiler is also available alone as plentyc.