Skip to content

Building and Packaging

A Daml application ships as one or more packages. Each package is built into a DAR file, uploaded to a participant node, and identified on the ledger by its package ID. DamlForge compiles .vy files into the Daml source of a package and uses DPM to build the DAR, so everything DPM does still applies.

This page uses the project from the previous four modules: a main package with the .vy contracts and a test package with Daml Script tests.

Command What it does
forge dar <project> -o <file.dar> Compiles every .vy file in the project, then runs dpm build
forge daml <file-or-dir> -o <dir> Compiles .vy files to .daml without building a DAR
forge stubs <file.dar> -o <dir> Writes .vy declarations for the templates and types in a DAR
forge json <file.vy> Prints the parsed .vy file as JSON
dpm build / dpm build --all Builds one package, or every package in multi-package.yaml
dpm test Runs the Daml Script tests in the current package
dpm damlc inspect-dar <file.dar> Lists the files in a DAR

forge daml writes a JSON source map next to each .daml file. Directory input is intentionally non-recursive; forge dar recursively compiles every .vy below the project’s configured source directory.

  1. Reads daml.yaml. The project needs name, version, source, and a non-empty dependencies list. Every data-dependencies entry must point to an existing file.
  2. Compiles every .vy file under source, including subdirectories, and writes a .daml file next to each one. A file’s path gives its module name: daml/Finance/Coin.vy becomes module Finance.Coin.
  3. Runs dpm build and writes the DAR. Without -o, the DAR goes to <project>/<name>-<version>.dar. DPM’s own default, .daml/dist/<name>-<version>.dar, is where other packages in a multi-package project look for it, so the examples pass that path to -o.
  4. Maps damlc errors back to .vy lines. When damlc reports an error in a generated file, DamlForge adds the .vy file and line it came from.

The generated .daml files are ordinary Daml, so you can read them to see exactly what was compiled.

main/daml.yaml for the project used in these modules:

sdk-version: 3.5.2
name: guide-main
source: daml
version: 0.0.1
dependencies:
- daml-prim
- daml-stdlib
build-options:
- --target=2.3
- -Wno-upgrade-interfaces

These settings change what DamlForge generates or accepts:

Setting Effect
sdk-version 3.5 enables contract keys and Serializable derivation; 3.4 doesn’t support keys
--target=2.3 Required for contract keys (DF1234)
--explicit-serializable=yes With SDK 3.5, data types derive Serializable
-Wno-upgrade-interfaces Lets interfaces and their implementations share a package

forge daml uses the nearest daml.yaml in the input’s directory or its parents. Without one, it applies no SDK checks.

dpm new creates two packages and a multi-package.yaml that lists them:

packages:
- ./main
- ./test

The test package depends on the DAR of the main package:

sdk-version: 3.5.2
name: guide-test
source: daml
version: 0.0.1
dependencies:
- daml-prim
- daml-stdlib
- daml-script
data-dependencies:
- ../main/.daml/dist/guide-main-0.0.1.dar
build-options:
- --target=2.3

A package that depends on a DAR built for LF 2.3 must target LF 2.3 or later too, or damlc reports dependencies have incompatible LF versions.

Build the .vy package first, then the rest:

Terminal window
forge dar main -o main/.daml/dist/guide-main-0.0.1.dar
dpm build --all
cd test && dpm test

dpm build --all reuses the generated .daml files in main, so it only needs forge dar to have run after the last .vy change.

  • dependencies is for the Daml libraries that come with the SDK: daml-prim, daml-stdlib, and daml-script.
  • data-dependencies is for DARs: your own packages and third-party ones. Daml reads only the compiled Daml-LF from them, so they can come from a ledger or an older SDK.

Keep daml-script out of the packages you deploy. It’s for tests, and it isn’t compatible across SDK versions.

A .vy file can import from three places:

  • Other .vy files in the same package, by module name: from Coin import Coin.
  • The Daml standard library, through stubs that ship with DamlForge: from DA.List import sort. The compiler adds the matching Daml import.
  • Splice packages, through bundled stubs: from Splice.Amulet import Amulet. The project must also list the matching Splice DAR in data-dependencies, so damlc can find the real definitions.

A .vy package can’t import templates from a DAR you built yourself. Stubs generated into the source directory would be compiled into the package as new modules instead of referring to the DAR. Keep .vy modules that use each other in one package, and consume its DAR from Daml packages, such as the test package, with data-dependencies.

A DAR is a ZIP archive:

Terminal window
dpm damlc inspect-dar main/.daml/dist/guide-main-0.0.1.dar

It contains:

  • a .dalf file for the package and for each dependency, holding the compiled Daml-LF that participants run;
  • the .daml source of each module, as generated by DamlForge (the .vy files aren’t included);
  • .hi and .hie interface files, used by dependencies and IDEs;
  • a META-INF/MANIFEST.MF that names the main package.

--json prints the same information as JSON, including main_package_id. The ledger identifies templates by package ID, module, and name, never by package name or version. Any change to a package changes its ID and the IDs of the packages that depend on it.

forge stubs turns a DAR back into .vy declarations, with choice bodies left as pass. Use it to read the API of a package you depend on:

Terminal window
forge stubs main/.daml/dist/guide-main-0.0.1.dar -o stubs --module Coin

It needs the stubgen extra (pip install 'damlforge[stubgen]'), and reads DARs for LF 2.1 to 2.3.

It accepts either a DAR file or a Daml project directory (the latter is built with DPM first):

Terminal window
forge stubs path/to/package.dar -o generated/
forge stubs path/to/daml_project -o generated/ --dpm /path/to/dpm
forge stubs path/to/package.dar --module Some.Module --module Other.Module

The --module option may be repeated and selects modules from the main package or its dependencies. Unsupported declarations are skipped and reported by the command.

  • Separate tests from contracts. Tests depend on daml-script, which shouldn’t be uploaded to a production ledger. That’s why dpm new creates a separate test package.
  • Separate public APIs from implementations. Interfaces that other teams build on should change rarely, so they belong in their own package, as in the Token Standard. With DamlForge, an interface and the templates that implement it must currently share a .vy package (see above), so this split is only possible when the implementations are written in Daml.
  • Separate by business domain. Stakeholders can review and upload only the packages for the workflows they take part in.

Variants goes deeper into sum types: payload styles, recursive and generic variants, and how to test them. Compiler Errors is the reference for every DF error code, and the examples directory has larger projects, including Splice token standard contracts.