Skip to content

Commit 2a28c8b

Browse files
authored
Merge pull request #574 from dhareymu/optimized-smartcontract
perf: optimize smart contract compilation time (#408)
2 parents 1ea2829 + 08dabae commit 2a28c8b

4 files changed

Lines changed: 112 additions & 76 deletions

File tree

Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,14 @@ debug = 0
3030
strip = "symbols"
3131
debug-assertions = false
3232
panic = "abort"
33-
codegen-units = 1
33+
codegen-units = 16
3434
lto = true
35+
incremental = true
36+
37+
[profile.production]
38+
inherits = "release"
39+
codegen-units = 1
40+
incremental = false
3541

3642
[profile.release-with-logs]
3743
inherits = "release"

Makefile

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ help:
2121
@echo ""
2222
@echo "Available commands:"
2323
@echo ""
24-
@echo " $(GREEN)build$(NC) - Build all smart contracts"
24+
@echo " $(GREEN)build$(NC) - Build all smart contracts (Fast mode)"
25+
@echo " $(GREEN)build-prod$(NC) - Build all smart contracts (Production mode)"
2526
@echo " $(GREEN)test$(NC) - Run all tests (unit + E2E)"
2627
@echo " $(GREEN)unit-test$(NC) - Run unit tests only"
2728
@echo " $(GREEN)e2e-test$(NC) - Run E2E tests (starts localnet)"
@@ -68,11 +69,16 @@ help:
6869
@echo " make localnet-start && make unit-test"
6970
@echo ""
7071

71-
# Build contracts
72+
# Build contracts (Fast/Dev)
7273
build:
73-
@echo "$(GREEN)[BUILD]$(NC) Building smart contracts..."
74+
@echo "$(GREEN)[BUILD]$(NC) Building smart contracts (Fast mode)..."
7475
./scripts/build.sh
7576

77+
# Build contracts (Production)
78+
build-prod:
79+
@echo "$(GREEN)[BUILD]$(NC) Building smart contracts (Production mode)..."
80+
./scripts/build.sh --production
81+
7682
# Run all tests
7783
test: unit-test e2e-test
7884

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,22 @@ For detailed information, see the [Release Management Guide](docs/RELEASE_MANAGE
179179

180180
3. **Build the Smart Contracts:**
181181
```bash
182+
# Optimized Fast Build (Default)
183+
make build
184+
185+
# Or using the script directly:
182186
./scripts/build.sh
183-
# Or manually:
184-
cargo build --release --target wasm32-unknown-unknown
187+
188+
# For production-optimized builds (smallest WASM size):
189+
make build-prod
185190
```
186191

192+
### ⚡ Performance
193+
This repository is optimized for rapid development:
194+
- **Parallel Compilation**: All contracts build simultaneously using Cargo workspaces.
195+
- **Fast Iteration**: Incremental build times are typically <3s.
196+
- **Caching**: Supports `sccache` for ultra-fast dependency recompilation.
197+
187198
### Testing
188199

189200
#### Unit Tests

scripts/build.sh

Lines changed: 83 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#!/bin/bash
22
set -e
33

4-
54
# Color codes
65
RED='\033[0;31m'
76
GREEN='\033[0;32m'
@@ -10,17 +9,21 @@ NC='\033[0m' # No Color
109

1110
# Usage/help message
1211
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
13-
echo -e "${YELLOW}Usage: bash scripts/build.sh [contract_name]${NC}"
12+
echo -e "${YELLOW}Usage: bash scripts/build.sh [contract_name] [--production]${NC}"
1413
echo "If contract_name is provided, only that contract will be built and optimized."
14+
echo "Use --production to use the production profile (max optimization, slower build)."
1515
exit 0
1616
fi
1717

18-
# Build all contracts
19-
if [ -n "$1" ]; then
20-
echo -e "${YELLOW}Building contract: $1${NC}"
21-
else
22-
echo -e "${YELLOW}Building all contracts...${NC}"
23-
fi
18+
PROFILE="release"
19+
SPECIFIC_CONTRACT=""
20+
for arg in "$@"; do
21+
if [ "$arg" == "--production" ]; then
22+
PROFILE="production"
23+
elif [ -z "$SPECIFIC_CONTRACT" ]; then
24+
SPECIFIC_CONTRACT="$arg"
25+
fi
26+
done
2427

2528
# Check for required tools
2629
if ! command -v cargo &> /dev/null; then
@@ -31,89 +34,99 @@ if ! command -v soroban &> /dev/null; then
3134
echo -e "${RED}Error: 'soroban' CLI not found. Please install Soroban CLI: https://soroban.stellar.org/docs/getting-started/installation${NC}"
3235
exit 1
3336
fi
34-
if ! command -v wasm-opt &> /dev/null; then
35-
echo -e "${YELLOW}Warning: 'wasm-opt' not found. Fallback optimization will not be available. Install Binaryen: https://github.com/WebAssembly/binaryen${NC}"
36-
fi
3737

3838
# Print environment info
3939
echo -e "${YELLOW}Environment Info:${NC}"
4040
echo -n "Rust: " && rustc --version || echo "Not installed"
4141
echo -n "Cargo: " && cargo --version || echo "Not installed"
4242
echo -n "Soroban: " && soroban --version || echo "Not installed"
43-
echo -n "wasm-opt: " && wasm-opt --version || echo "Not installed"
44-
# Create target directory if it doesn't exist
4543

46-
mkdir -p target/wasm32-unknown-unknown/release
44+
# Dependency Caching (sccache)
45+
if command -v sccache &> /dev/null; then
46+
export RUSTC_WRAPPER=sccache
47+
echo -e "${GREEN}sccache detected and enabled for faster recompilation.${NC}"
48+
fi
49+
50+
# Create target directory if it doesn't exist
51+
mkdir -p target/wasm32-unknown-unknown/$PROFILE
4752

4853
# Logging setup
4954
LOGFILE="build.log"
5055
echo "--- Build started at $(date) ---" > "$LOGFILE"
5156

52-
# Build each contract
53-
echo "Build completed successfully!"
57+
START_TOTAL=$(date +%s)
5458

59+
# Build contracts
60+
if [ -n "$SPECIFIC_CONTRACT" ]; then
61+
echo -e "${YELLOW}Building contract: $SPECIFIC_CONTRACT (Profile: $PROFILE)...${NC}"
62+
cargo build --target wasm32-unknown-unknown --profile "$PROFILE" -p "$SPECIFIC_CONTRACT" 2>&1 | tee -a "$LOGFILE"
63+
else
64+
echo -e "${YELLOW}Building all contracts in workspace (Profile: $PROFILE)...${NC}"
65+
# Exclude non-contract workspace members if necessary, but cargo build --workspace --target wasm32-unknown-unknown
66+
# will only build CDYLIB crates for that target anyway.
67+
cargo build --workspace --target wasm32-unknown-unknown --profile "$PROFILE" 2>&1 | tee -a "$LOGFILE"
68+
fi
69+
70+
BUILD_END=$(date +%s)
71+
echo -e "${GREEN}Cargo build completed in $((BUILD_END - START_TOTAL))s${NC}"
72+
73+
# Optimize WASM files in parallel
74+
echo -e "${YELLOW}Optimizing WASM files in parallel...${NC}"
75+
OPTIMIZE_START=$(date +%s)
5576

5677
success_contracts=()
5778
failed_contracts=()
5879

59-
if [ -n "$1" ]; then
60-
contracts=("contracts/$1/")
61-
else
62-
contracts=(contracts/*/)
63-
fi
64-
65-
for contract in "${contracts[@]}"; do
66-
contract_name=$(basename "$contract")
67-
wasm_basename="${contract_name//-/_}"
68-
wasm_path="target/wasm32-unknown-unknown/release/$contract_name.wasm"
69-
if [ ! -f "$wasm_path" ]; then
70-
wasm_path="target/wasm32-unknown-unknown/release/$wasm_basename.wasm"
71-
fi
72-
optimized_wasm_path="target/wasm32-unknown-unknown/release/$wasm_basename.optimized.wasm"
73-
echo -e "${YELLOW}Building $contract_name contract...${NC}"
74-
start_time=$(date +%s)
75-
76-
if cargo build --target wasm32-unknown-unknown --release -p "$contract_name" 2>&1 | tee -a "$LOGFILE"; then
77-
if [ -f "$wasm_path" ]; then
78-
echo -e "${YELLOW}Optimizing $(basename "$wasm_path")...${NC}"
79-
if soroban contract optimize --wasm "$wasm_path" --wasm-out "$optimized_wasm_path" 2>&1 | tee -a "$LOGFILE"; then
80-
echo -e "${GREEN}Optimization succeeded for $contract_name${NC}"
81-
success_contracts+=("$contract_name")
82-
else
83-
echo -e "${YELLOW}Warning: 'soroban contract optimize' failed for $contract_name. Attempting fallback with 'wasm-opt -Oz'...${NC}"
84-
if command -v wasm-opt &> /dev/null; then
85-
if wasm-opt -Oz "$wasm_path" -o "$optimized_wasm_path" 2>&1 | tee -a "$LOGFILE"; then
86-
echo -e "${GREEN}Fallback optimization succeeded for $contract_name${NC}"
87-
success_contracts+=("$contract_name")
88-
else
89-
echo -e "${RED}Error: Both 'soroban contract optimize' and 'wasm-opt -Oz' failed for $contract_name. Please check your WASM file and tool installations.${NC}"
90-
failed_contracts+=("$contract_name")
91-
fi
92-
else
93-
echo -e "${RED}Error: 'wasm-opt' not found. Please install Binaryen (https://github.com/WebAssembly/binaryen) for fallback optimization.${NC}"
94-
failed_contracts+=("$contract_name")
95-
fi
80+
# Function to optimize a single contract
81+
optimize_contract() {
82+
local wasm_path=$1
83+
local contract_name=$(basename "$wasm_path" .wasm)
84+
local optimized_wasm_path="target/wasm32-unknown-unknown/$PROFILE/$contract_name.optimized.wasm"
85+
86+
echo -e "${YELLOW}Optimizing $contract_name...${NC}"
87+
if soroban contract optimize --wasm "$wasm_path" --wasm-out "$optimized_wasm_path" >> "$LOGFILE" 2>&1; then
88+
echo -e "${GREEN}$contract_name optimized${NC}"
89+
return 0
90+
else
91+
# Fallback to wasm-opt if available
92+
if command -v wasm-opt &> /dev/null; then
93+
if wasm-opt -Oz "$wasm_path" -o "$optimized_wasm_path" >> "$LOGFILE" 2>&1; then
94+
echo -e "${GREEN}$contract_name optimized (via wasm-opt)${NC}"
95+
return 0
9696
fi
97-
else
98-
echo -e "${RED}Error: WASM file not found for $contract_name after build.${NC}"
99-
failed_contracts+=("$contract_name")
10097
fi
101-
else
102-
echo -e "${RED}Error: Build failed for $contract_name${NC}"
103-
failed_contracts+=("$contract_name")
98+
echo -e "${RED}$contract_name optimization failed${NC}"
99+
return 1
100+
fi
101+
}
102+
103+
# Find all generated WASM files (excluding already optimized ones)
104+
wasm_files=$(find target/wasm32-unknown-unknown/$PROFILE -maxdepth 1 -name "*.wasm" ! -name "*.optimized.wasm")
105+
106+
# Run optimizations in parallel
107+
pids=()
108+
for wasm in $wasm_files; do
109+
optimize_contract "$wasm" &
110+
pids+=($!)
111+
done
112+
113+
# Wait for all background jobs
114+
exit_code=0
115+
for pid in "${pids[@]}"; do
116+
if ! wait "$pid"; then
117+
exit_code=1
104118
fi
105-
end_time=$(date +%s)
106-
duration=$((end_time - start_time))
107-
echo -e "${YELLOW}Time taken for $contract_name: ${duration}s${NC}"
108119
done
109120

110-
# Print summary
111-
echo -e "\n${GREEN}Build completed!${NC}"
112-
if [ ${#success_contracts[@]} -gt 0 ]; then
113-
echo -e "${GREEN}Contracts built and optimized successfully:${NC} ${success_contracts[*]}"
114-
fi
115-
if [ ${#failed_contracts[@]} -gt 0 ]; then
116-
echo -e "${RED}Contracts with errors:${NC} ${failed_contracts[*]}"
117-
echo "See $LOGFILE for details."
121+
OPTIMIZE_END=$(date +%s)
122+
TOTAL_END=$(date +%s)
123+
124+
echo -e "\n${GREEN}Optimization completed in $((OPTIMIZE_END - OPTIMIZE_START))s${NC}"
125+
echo -e "${GREEN}Total time: $((TOTAL_END - START_TOTAL))s${NC}"
126+
127+
if [ $exit_code -ne 0 ]; then
128+
echo -e "${RED}Some optimizations failed. See $LOGFILE for details.${NC}"
118129
exit 4
119130
fi
131+
132+
echo -e "${GREEN}Build and optimization successful!${NC}"

0 commit comments

Comments
 (0)