Hey guys, recently I’ve been running “candid-extractor target/wasm32-unknown-unknown/release/backend.wasm > ./backend/canisters/backend/backend.did” before deploying canister to generate .did files, but it’s a little troublesome.I’ve written a script to generate Candid files but my teammate think it would be better to integrate it into dfx.json. I’ve learned that dfx.json has a build parameter that automatically executes commands at compile time, but it doesn’t seem to work when I put this code in there.
Here are the codes below:
I have wrote this sh script and use it on all of my project.
filename : predeploy.sh
#!/usr/bin/env bash
set -eu
backend_dir="./backend"
target_dir="./target/wasm32-unknown-unknown/release"
yellow='\033[1;33m'
green='\033[0;32m'
red='\033[0;31m'
blue='\033[0;34m'
no_color='\033[0m'
# Check if a specific package name is provided
specified_package=${1:-}
# Check if the name is valid
if [ -n "$specified_package" ] && [ ! -d "$backend_dir/$specified_package" ]; then
printf "${red}No package named $specified_package found in $backend_dir. Skipping building $specified_package.${no_color}\n"
exit 0
fi
for app_root in "$backend_dir"/*; do
package=$(basename "$app_root")
# Skip packages that do not match the specified package (if one is specified)
if [ -n "$specified_package" ] && [ "$specified_package" != "$package" ]; then
continue
fi
did_file="$app_root/$package.did"
optimised_target_dir="./canisters/$package"
if [ ! -f "$app_root/Cargo.toml" ]; then
printf "${yellow}No Cargo.toml found in $app_root. Skipping $package.${no_color}\n"
continue
fi
printf "${green}Building $package in $app_root${no_color}\n"
cargo build --manifest-path="$app_root/Cargo.toml" \
--target wasm32-unknown-unknown \
--release \
--package "$package"
printf "Size of $package.wasm: $(ls -lh "$target_dir/$package.wasm" | awk '{print $5}')\n"
if command -v candid-extractor >/dev/null 2>&1; then
printf "${green}Generating Candid file for $package${no_color}\n"
candid-extractor "$target_dir/$package.wasm" > "$did_file"
printf "Size of $package.did: $(ls -lh "$did_file" | awk '{print $5}')\n"
else
printf "${yellow}candid-extractor not found. Skipping generating $package.did.${no_color}\n"
fi
# Check if ic-wasm is installed before attempting to shrink the wasm file
# you can install ic-wasm via `cargo install ic-wasm` for smaller wasm files
if command -v ic-wasm >/dev/null 2>&1; then
# create the optimised target dir
mkdir -p "$optimised_target_dir"
# copy the candid file to the optimised target dir
cp "$did_file" "$optimised_target_dir/$package.did"
# add candid file into wasm file as metadata
printf "${green}Adding Candid file into $package.wasm${no_color}\n"
ic-wasm "$target_dir/$package.wasm" -o "$optimised_target_dir/$package.wasm" metadata candid:service -f "$optimised_target_dir/$package.did" -v public
printf "Size of $package.wasm with Candid metadata: $(ls -lh "$optimised_target_dir/$package.wasm" | awk '{print $5}')\n"
# shrink wasm file
printf "${green}Shrinking $package.wasm${no_color}\n"
ic-wasm "$optimised_target_dir/$package.wasm" -o "$optimised_target_dir/$package.wasm" optimize O3
printf "Size of shrunk $package.wasm: $(ls -lh "$optimised_target_dir/$package.wasm" | awk '{print $5}')\n"
# Gunzip target
printf "${green}Gunzipping $package.wasm${no_color}\n"
gzip -c "$optimised_target_dir/$package.wasm" > "$optimised_target_dir/$package.wasm.gz"
printf "Size of Gunzipped $package.wasm.gz: $(ls -lh "$optimised_target_dir/$package.wasm.gz" | awk '{print $5}')\n"
else
printf "${yellow}ic-wasm not found. Skipping shrinking $package.${no_color}\n"
fi
# print the file directory for the package
printf "${blue}Files for $package are in $optimised_target_dir${no_color}\n"
dfx generate "$package"
done
Script Overview
The script, predeploy.sh, automates the build process for Rust-based canisters, including generating .did files and optimizing the WebAssembly (Wasm) output. Here’s a step-by-step explanation:
Setup and Variables:
The script sets up input (./backend) and output (./canisters) directories.
It checks if a specific package name is provided as an argument.
Package Validation:
If a package name is specified, the script verifies its existence in the backend directory. If not found, it skips the build for that package.
Build Loop:
The script iterates over all packages in the backend directory.
For each package, it checks for the presence of a Cargo.toml file to ensure it’s a valid Rust package.
Building the Package:
It builds the package using cargo build with the target set to wasm32-unknown-unknown and in release mode.
The script then prints the size of the generated Wasm file.
Generating Candid Files:
If candid-extractor is installed, the script generates a .did file from the Wasm file and prints its size.
If candid-extractor is not found, it skips this step.
Optimizing Wasm Files:
If ic-wasm is installed, the script performs several optimizations:
Adds Candid metadata to the Wasm file.
Shrinks the Wasm file for smaller size.
Compresses the Wasm file using gzip.
It prints the sizes of the optimized and compressed Wasm files.
Output Directory:
The script creates an optimized target directory for each package and copies the .did file there.
It prints the directory where the files are stored.
DFX Generate:
Finally, the script runs dfx generate for each package to generate TypeScript bindings.
Usage
You can use the script by running:
sh ./predeploy.sh <canister name>
dfx.json Configuration
Here’s how you can configure your dfx.json to use this script:
Wow~Thanks a lot,@b3hr4d, actually I 've written a script to generate Candid files, it is written by python language, but my teammate think that it would be better to integrate this function into dfx.json. Afterall, I’m really appreciate it that you give me help, deeply thank you!
Oh, @b3hr4d ,I just talked to my teammate and he thinks that your solution is a good option if we can’t integrate this into dfx.json, thanks again for your help!
Happy it helped! It’s actually integrated with dfx.json! If you put the script inside the dfx.json, it will build and use it directly from there, just like I mentioned.
This way, you can streamline your build process without needing to run the script manually. Thanks again for your kind words!