From 9aea1e29d2b146e2a6fde61ae26d7928e427ed0f Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 22 May 2026 12:37:28 +0200 Subject: [PATCH 1/5] fix: pass mops moc-args to moc in Motoko recipe Compiler flags defined in the [moc] section of mops.toml were silently ignored because the recipe never called `mops moc-args`. Adding it to the compile command ensures those flags are respected alongside the recipe-level `args` parameter. --- recipes/motoko/README.md | 2 +- recipes/motoko/recipe.hbs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/recipes/motoko/README.md b/recipes/motoko/README.md index 0e883f1..a058165 100644 --- a/recipes/motoko/README.md +++ b/recipes/motoko/README.md @@ -77,7 +77,7 @@ canisters: When this recipe is executed: 1. Checks if `mops` is installed (used for Motoko toolchain management) -2. Compiles the specified Motoko entry file using `moc` via `mops toolchain` +2. Compiles the specified Motoko entry file using `moc` via `mops toolchain`, including any global compiler flags defined in the `[moc]` section of `mops.toml` (via `mops moc-args`) 3. Handles Candid interface: auto-generates from Motoko source, or uses the provided `candid` file 4. Injects compiler version metadata ("moc:version") 5. Injects template type metadata ("template:type" = "motoko") diff --git a/recipes/motoko/recipe.hbs b/recipes/motoko/recipe.hbs index 9508fac..6088862 100644 --- a/recipes/motoko/recipe.hbs +++ b/recipes/motoko/recipe.hbs @@ -19,7 +19,7 @@ build: - type: script commands: - - sh -c '$(mops toolchain bin moc) "{{ main }}" {{#if args}}{{ args }} {{/if}}{{#if candid}}--omit-metadata{{else}}--public-metadata{{/if}} candid:service $(mops sources) -o "$ICP_WASM_OUTPUT_PATH"' + - sh -c '$(mops toolchain bin moc) "{{ main }}" {{#if args}}{{ args }} {{/if}}$(mops moc-args) {{#if candid}}--omit-metadata{{else}}--public-metadata{{/if}} candid:service $(mops sources) -o "$ICP_WASM_OUTPUT_PATH"' - type: script commands: From d9705fa00ef1f82adf2181a0a923003e9f28d893 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 26 May 2026 12:48:10 +0200 Subject: [PATCH 2/5] feat!: rewrite Motoko recipe to delegate compilation to mops build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace direct moc invocation with `mops build`, aligning the Motoko recipe with how the Rust recipe delegates to `cargo build`. Toolchain, dependencies, compiler flags, candid, and per-canister args all move to mops.toml — the recipe retains only icp-specific concerns (output path, moc:version / template:type metadata, shrink, compress). BREAKING CHANGE: `main`, `candid`, and `args` params removed. Add a `name` param matching the [canisters] key in mops.toml. Co-Authored-By: Claude Sonnet 4.6 --- recipes/motoko/README.md | 107 +++++++++++++++++++++++++------------- recipes/motoko/recipe.hbs | 11 ++-- 2 files changed, 75 insertions(+), 43 deletions(-) diff --git a/recipes/motoko/README.md b/recipes/motoko/README.md index a058165..9c5a5d4 100644 --- a/recipes/motoko/README.md +++ b/recipes/motoko/README.md @@ -1,6 +1,6 @@ # Motoko Recipe -Compile Motoko source code using the moc compiler to create IC canisters. +Compile Motoko source code using `mops build` to create IC canisters. ## Usage @@ -12,26 +12,36 @@ canisters: recipe: type: "@dfinity/motoko@" configuration: - main: src/main.mo + name: backend shrink: true ``` -> Replace `` with a release version (e.g. `v4.0.0`). See [available versions](https://github.com/dfinity/icp-cli-recipes/releases?q=motoko&expanded=true). +> Replace `` with a release version (e.g. `v5.0.0`). See [available versions](https://github.com/dfinity/icp-cli-recipes/releases?q=motoko&expanded=true). + +The canister must also be defined in `mops.toml`. The `name` in the recipe configuration must match the key in the `[canisters]` section: + +```toml +[toolchain] +moc = "1.8.2" + +[canisters] +backend = "src/main.mo" +``` + +Compiler flags, per-canister args, and the Candid file are all configured in `mops.toml`. See the [mops documentation](https://mops.one/docs) for the full `[canisters]` schema. ## Configuration Parameters | Parameter | Type | Required | Description | Default | |-----------|---------|----------|----------------------------------------------|---------| -| main | string | Yes | Path to the main Motoko source file | | -| args | string | No | Additional arguments to pass to moc compiler (e.g., `--incremental-gc`) | | -| candid | string | No | Path to a custom Candid interface file. If not provided, the interface is auto-generated by the Motoko compiler | (auto-generated) | +| name | string | Yes | Canister name — must match the key in `[canisters]` in `mops.toml` | | | metadata | array | No | Array of key-value pairs for custom metadata | [] | | shrink | boolean | No | Remove unused functions and debug info to reduce file size | false | | compress | boolean | No | Gzip compress the WASM file | false | ## Prerequisites -- `mops` (Motoko package manager) - this recipe uses `mops toolchain` to manage the Motoko compiler +- `mops` (Motoko package manager) — manages the toolchain, dependencies, and canister build - `ic-wasm` (included with icp-cli installation) > **Note:** If you followed the [icp-cli installation guide](https://github.com/dfinity/icp-cli#installation), both `mops` and `ic-wasm` are already installed. @@ -45,42 +55,67 @@ If mops is not installed, see: ### Basic Example ```yaml +# icp.yaml canisters: - name: hello-world recipe: type: "@dfinity/motoko@" configuration: - main: src/main.mo + name: hello-world +``` + +```toml +# mops.toml +[toolchain] +moc = "1.8.2" + +[canisters] +hello-world = "src/main.mo" ``` ### Advanced Example ```yaml +# icp.yaml canisters: - - name: complex-backend + - name: backend recipe: type: "@dfinity/motoko@" configuration: - main: src/backend.mo - args: --incremental-gc + name: backend shrink: true compress: true metadata: - name: "canister:type" value: "backend" - - name: "language:version" - value: "0.10.0" +``` + +```toml +# mops.toml +[toolchain] +moc = "1.8.2" + +[dependencies] +core = "2.5.0" + +[moc] +args = ["--default-persistent-actors"] + +[canisters.backend] +main = "src/main.mo" +candid = "backend.did" +args = ["--incremental-gc"] ``` ## Build Process When this recipe is executed: -1. Checks if `mops` is installed (used for Motoko toolchain management) -2. Compiles the specified Motoko entry file using `moc` via `mops toolchain`, including any global compiler flags defined in the `[moc]` section of `mops.toml` (via `mops moc-args`) -3. Handles Candid interface: auto-generates from Motoko source, or uses the provided `candid` file -4. Injects compiler version metadata ("moc:version") -5. Injects template type metadata ("template:type" = "motoko") +1. Checks if `mops` and `ic-wasm` are installed +2. Runs `mops build ` which compiles the canister using the toolchain, dependencies, and compiler flags defined in `mops.toml` +3. Copies the built WASM to the icp-cli output path +4. Injects compiler version metadata (`moc:version`) +5. Injects template type metadata (`template:type` = `motoko`) 6. Injects any custom metadata specified in the configuration 7. Optionally removes unused functions if `shrink` is enabled 8. Optionally gzip compresses the WASM file if `compress` is enabled @@ -95,31 +130,35 @@ my-project/ │ ├── main.mo # Entry point │ ├── types.mo # Type definitions │ └── utils.mo # Utility functions -├── .vessel/ # Package manager dependencies -└── icp.yaml # Build configuration +├── mops.toml # Toolchain, dependencies, canister config +└── icp.yaml # Build configuration ``` -## Common Issues +## Migrating from v4 -### Issue 1 +The `main`, `candid`, and `args` recipe parameters have been removed. Move them to `mops.toml`: -**Problem**: `moc not found` error -**Solution**: Install the Motoko compiler following the instructions at +| Before (`icp.yaml`) | After (`mops.toml`) | +|-----------------------------|--------------------------------------------| +| `main: src/main.mo` | `[canisters.backend] main = "src/main.mo"` | +| `candid: backend.did` | `[canisters.backend] candid = "backend.did"` | +| `args: --incremental-gc` | `[canisters.backend] args = ["--incremental-gc"]` | -### Issue 2 +Add a `name` parameter to the recipe configuration matching your canister's key in `[canisters]`. -**Problem**: Compilation errors in Motoko code -**Solution**: Check your Motoko syntax and ensure all imports are correctly resolved +## Common Issues + +### "No Motoko canisters found in mops.toml configuration" -### Issue 3 +The `[canisters]` section is missing from `mops.toml`, or the canister name does not match the `name` parameter in `icp.yaml`. Ensure the key in `[canisters]` exactly matches the `name` value in the recipe configuration. -**Problem**: Entry file not found -**Solution**: Verify the entry path is correct relative to your project root and the file exists +### "moc not found" error -### Issue 4 +Install the Motoko compiler via mops: run `mops install` in your project directory. The toolchain version is set in `mops.toml` under `[toolchain]`. -**Problem**: Missing dependencies -**Solution**: Ensure all imported modules are available or install them using vessel package manager +### Compilation errors + +Check your Motoko syntax and ensure all imports resolve. Run `mops check` for detailed diagnostics. ## Related Recipes @@ -127,8 +166,6 @@ my-project/ - [Pre-built Recipe](../prebuilt/README.md) - For using pre-compiled WASM files - [Asset Canister Recipe](../asset-canister/README.md) - For frontend assets canister -Use this recipe when developing IC canisters in Motoko, the native language for the Internet Computer. - ## Release History See the [release history](https://github.com/dfinity/icp-cli-recipes/releases?q=motoko&expanded=true) for changelogs, version updates, and breaking changes. diff --git a/recipes/motoko/recipe.hbs b/recipes/motoko/recipe.hbs index 6088862..dcb1f6e 100644 --- a/recipes/motoko/recipe.hbs +++ b/recipes/motoko/recipe.hbs @@ -1,7 +1,5 @@ -{{! A recipe for building a Motoko canister }} -{{! `main: string` Motoko file which contains the main actor }} -{{! `args: string` Additional arguments to pass to the moc compiler }} -{{! `candid: string` Optional, the path to the Candid interface file }} +{{! A recipe for building a Motoko canister using `mops build` }} +{{! `name: string` The canister name, must match the key in [canisters] in mops.toml }} {{! `shrink: boolean` Optimizes the wasm with ic-wasm }} {{! `compress: boolean` determines whether the wasm should be gzipped }} {{! `metadata: [name: string, value: string]`: An array of name/value pairs that get injected into the wasm metadata section }} @@ -19,7 +17,7 @@ build: - type: script commands: - - sh -c '$(mops toolchain bin moc) "{{ main }}" {{#if args}}{{ args }} {{/if}}$(mops moc-args) {{#if candid}}--omit-metadata{{else}}--public-metadata{{/if}} candid:service $(mops sources) -o "$ICP_WASM_OUTPUT_PATH"' + - sh -c 'BUILD_OUT=$(mktemp -d) && mops build "{{ name }}" --output "$BUILD_OUT" && cp "$BUILD_OUT/{{ name }}.wasm" "$ICP_WASM_OUTPUT_PATH"' - type: script commands: @@ -28,9 +26,6 @@ build: - type: script commands: - ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "${ICP_WASM_OUTPUT_PATH}" metadata "template:type" -d "motoko" --keep-name-section - {{#if candid}} - - ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "${ICP_WASM_OUTPUT_PATH}" metadata "candid:service" -f '{{ candid }}' -v public --keep-name-section - {{/if}} {{#if metadata}} {{#each metadata}} - ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "${ICP_WASM_OUTPUT_PATH}" metadata "{{ name }}" -d "{{ value }}" --keep-name-section From bc9581d9258d2d7feb15ab4ae8d04baf923b985f Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 26 May 2026 12:59:44 +0200 Subject: [PATCH 3/5] fix: use mops default output dir instead of mktemp Follow the same pattern as the Rust recipe: let the build tool write to its natural output directory and cp from there. Always pass --output .mops/.build so the path is stable regardless of any [build].outputDir set in mops.toml. Co-Authored-By: Claude Sonnet 4.6 --- recipes/motoko/recipe.hbs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/recipes/motoko/recipe.hbs b/recipes/motoko/recipe.hbs index dcb1f6e..9c34495 100644 --- a/recipes/motoko/recipe.hbs +++ b/recipes/motoko/recipe.hbs @@ -17,7 +17,8 @@ build: - type: script commands: - - sh -c 'BUILD_OUT=$(mktemp -d) && mops build "{{ name }}" --output "$BUILD_OUT" && cp "$BUILD_OUT/{{ name }}.wasm" "$ICP_WASM_OUTPUT_PATH"' + - mops build "{{ name }}" --output .mops/.build + - cp ".mops/.build/{{ name }}.wasm" "$ICP_WASM_OUTPUT_PATH" - type: script commands: From 09ae8084e7e8db087628c34b8388ed2f26a03094 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 26 May 2026 18:50:51 +0200 Subject: [PATCH 4/5] feat: use _.canister.name built-in instead of explicit name param MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the `name` configuration parameter — the canister name is now injected automatically via {{_.canister.name}} (requires icp-cli#567). The [canisters] key in mops.toml must still match the canister name in icp.yaml, but no extra recipe configuration is needed. Co-Authored-By: Claude Sonnet 4.6 --- recipes/motoko/README.md | 9 ++------- recipes/motoko/recipe.hbs | 5 ++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/recipes/motoko/README.md b/recipes/motoko/README.md index 9c5a5d4..429349e 100644 --- a/recipes/motoko/README.md +++ b/recipes/motoko/README.md @@ -12,13 +12,12 @@ canisters: recipe: type: "@dfinity/motoko@" configuration: - name: backend shrink: true ``` > Replace `` with a release version (e.g. `v5.0.0`). See [available versions](https://github.com/dfinity/icp-cli-recipes/releases?q=motoko&expanded=true). -The canister must also be defined in `mops.toml`. The `name` in the recipe configuration must match the key in the `[canisters]` section: +The canister must also be defined in `mops.toml`. The key in the `[canisters]` section must match the canister name in `icp.yaml`: ```toml [toolchain] @@ -34,7 +33,6 @@ Compiler flags, per-canister args, and the Candid file are all configured in `mo | Parameter | Type | Required | Description | Default | |-----------|---------|----------|----------------------------------------------|---------| -| name | string | Yes | Canister name — must match the key in `[canisters]` in `mops.toml` | | | metadata | array | No | Array of key-value pairs for custom metadata | [] | | shrink | boolean | No | Remove unused functions and debug info to reduce file size | false | | compress | boolean | No | Gzip compress the WASM file | false | @@ -60,8 +58,6 @@ canisters: - name: hello-world recipe: type: "@dfinity/motoko@" - configuration: - name: hello-world ``` ```toml @@ -82,7 +78,6 @@ canisters: recipe: type: "@dfinity/motoko@" configuration: - name: backend shrink: true compress: true metadata: @@ -144,7 +139,7 @@ The `main`, `candid`, and `args` recipe parameters have been removed. Move them | `candid: backend.did` | `[canisters.backend] candid = "backend.did"` | | `args: --incremental-gc` | `[canisters.backend] args = ["--incremental-gc"]` | -Add a `name` parameter to the recipe configuration matching your canister's key in `[canisters]`. +The canister name in `icp.yaml` is used automatically — no additional recipe configuration is needed. ## Common Issues diff --git a/recipes/motoko/recipe.hbs b/recipes/motoko/recipe.hbs index 9c34495..77ef5ed 100644 --- a/recipes/motoko/recipe.hbs +++ b/recipes/motoko/recipe.hbs @@ -1,5 +1,4 @@ {{! A recipe for building a Motoko canister using `mops build` }} -{{! `name: string` The canister name, must match the key in [canisters] in mops.toml }} {{! `shrink: boolean` Optimizes the wasm with ic-wasm }} {{! `compress: boolean` determines whether the wasm should be gzipped }} {{! `metadata: [name: string, value: string]`: An array of name/value pairs that get injected into the wasm metadata section }} @@ -17,8 +16,8 @@ build: - type: script commands: - - mops build "{{ name }}" --output .mops/.build - - cp ".mops/.build/{{ name }}.wasm" "$ICP_WASM_OUTPUT_PATH" + - mops build "{{ _.canister.name }}" --output .mops/.build + - cp ".mops/.build/{{ _.canister.name }}.wasm" "$ICP_WASM_OUTPUT_PATH" - type: script commands: From 4f4f0a3af5a409a9b0cef6be3b936bfa74260d72 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 1 Jun 2026 15:57:03 +0200 Subject: [PATCH 5/5] docs: remove redundant --incremental-gc from example --incremental-gc is the default GC in recent moc versions, so showing it in the example implies it does something meaningful when it doesn't. Co-Authored-By: Claude Sonnet 4.6 --- recipes/motoko/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/recipes/motoko/README.md b/recipes/motoko/README.md index 429349e..195369f 100644 --- a/recipes/motoko/README.md +++ b/recipes/motoko/README.md @@ -99,7 +99,6 @@ args = ["--default-persistent-actors"] [canisters.backend] main = "src/main.mo" candid = "backend.did" -args = ["--incremental-gc"] ``` ## Build Process