Skip to content

Commit d533b93

Browse files
authored
Merge pull request #23 from CosmWasm/workspace-optimizer
Add workspace optimizer
2 parents 6b99f94 + 006514d commit d533b93

File tree

7 files changed

+163
-19
lines changed

7 files changed

+163
-19
lines changed

.editorconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ insert_final_newline = true
1010
[Makefile]
1111
indent_style = tab
1212
indent_size = 4
13+
14+
[*.py]
15+
indent_style = space
16+
indent_size = 4

Makefile

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
1-
.PHONY: build publish run debug
1+
.PHONY: build-rust-optimizer build-workspace-optimizer build publish-rust-optimizer publish-workspace-optimizer publish
22

3-
DOCKER_NAME := "cosmwasm/rust-optimizer"
4-
DOCKER_TAG := 0.10.1
5-
CODE ?= "/path/to/contract"
6-
USER_ID := $(shell id -u)
7-
USER_GROUP = $(shell id -g)
3+
DOCKER_NAME_RUST_OPTIMIZER := "cosmwasm/rust-optimizer"
4+
DOCKER_NAME_WORKSPACE_OPTIMIZER := "cosmwasm/workspace-optimizer"
5+
DOCKER_TAG := 0.10.2
86

9-
build:
10-
docker build . -t $(DOCKER_NAME):$(DOCKER_TAG)
7+
build-rust-optimizer:
8+
docker build -t $(DOCKER_NAME_RUST_OPTIMIZER):$(DOCKER_TAG) --file rust-optimizer.Dockerfile .
119

12-
publish: build
13-
docker push $(DOCKER_NAME):$(DOCKER_TAG)
10+
build-workspace-optimizer:
11+
docker build -t $(DOCKER_NAME_WORKSPACE_OPTIMIZER):$(DOCKER_TAG) --file workspace-optimizer.Dockerfile .
1412

15-
# Usage: make run CODE=/path/to/contract
16-
run:
17-
docker run --rm -u $(USER_ID):$(USER_GROUP) -v "$(CODE)":/code $(DOCKER_NAME):$(DOCKER_TAG)
13+
publish-rust-optimizer: build-rust-optimizer
14+
docker push $(DOCKER_NAME_RUST_OPTIMIZER):$(DOCKER_TAG)
1815

19-
debug:
20-
docker run --rm -it -u $(USER_ID):$(USER_GROUP) -v "$(CODE)":/code $(DOCKER_NAME):$(DOCKER_TAG) /bin/bash
16+
publish-workspace-optimizer: build-workspace-optimizer
17+
docker push $(DOCKER_NAME_WORKSPACE_OPTIMIZER):$(DOCKER_TAG)
18+
19+
build: build-rust-optimizer build-workspace-optimizer
20+
21+
publish: publish-rust-optimizer publish-workspace-optimizer

README.md

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ optimization on the build size, using binary stripping and `wasm-opt`.
88

99
## Usage
1010

11-
*This works for most cases, for cosmwasm builds see advanced*
11+
*This works for most cases, for monorepo builds see advanced*
1212

1313
The easiest way is to simply use the [published docker image](https://hub.docker.com/r/cosmwasm/rust-optimizer).
1414
You must set the local path to the smart contract you wish to compile and
@@ -24,7 +24,7 @@ you to produce a smaller build that works with the cosmwasm integration tests
2424
docker run --rm -v "$(pwd)":/code \
2525
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
2626
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
27-
cosmwasm/rust-optimizer:0.10.1
27+
cosmwasm/rust-optimizer:0.10.2
2828
```
2929

3030
Demo this with `cosmwasm-examples` (going into eg. `erc20` subdir before running),
@@ -34,7 +34,38 @@ Note that we use one registry cache (to avoid excessive downloads), but the targ
3434
contract that we compile. This means no interference between contracts, but very fast recompile times when making
3535
minor adjustments to a contract you had previously created an optimized build for.
3636

37-
## Advanced usage
37+
## Mono Repos
38+
39+
### Contracts as Workspace Members
40+
41+
*This is designed for cosmwasm-plus samples. We use a separate docker image*
42+
43+
Sometime you want many contracts to be related and import common functionality. This is
44+
exactly the case of [`cosmwasm-plus`](https://github.com/CosmWasm/cosmwasm-plus).
45+
In such a case, we can often not just compile from root, as the compile order is
46+
not deterministic and there are feature flags shared among the repos.
47+
This has lead to [issues in the past](https://github.com/CosmWasm/rust-optimizer/issues/21).
48+
49+
For this use-case we made a second docker image, which will compile all the
50+
`contracts/*` folders inside the workspace and do so one-by-one in alphabetical order.
51+
It will then add all the generated wasm files to an `artifacts` directory with a checksum,
52+
just like the basic docker image (same output format).
53+
54+
To compile all contracts in the workspace deterministically, you can run:
55+
56+
```shell
57+
docker run --rm -v "$(pwd)":/code \
58+
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
59+
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
60+
cosmwasm/workspace-optimizer:0.10.2
61+
```
62+
63+
The downside is that to verify one contract in the workspace, you need to compile them
64+
all, but the majority of the build time is in dependencies, which are shared and cached
65+
between the various contracts and thus the time is sub-linear with respect to number
66+
of contracts.
67+
68+
### Contracts excluded from Workspace
3869

3970
*This is designed for cosmwasm samples. You cannot provide automatic verification for these*
4071

@@ -52,7 +83,7 @@ case, we can use the optimize.sh command:
5283
docker run --rm -v "$(pwd)":/code \
5384
--mount type=volume,source="devcontract_cache_burner",target=/code/contracts/burner/target \
5485
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
55-
cosmwasm/rust-optimizer:0.10.1 ./contracts/burner
86+
cosmwasm/rust-optimizer:0.10.2 ./contracts/burner
5687
```
5788

5889
## Development

optimize_workspace.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/usr/bin/env python3
2+
3+
# Build script for cargo workspaces
4+
5+
CARGO_PATH="cargo"
6+
PACKAGE_PREFIX="contracts/"
7+
8+
import glob
9+
import os
10+
import shutil
11+
import stat
12+
import subprocess
13+
import toml
14+
15+
def log(*args):
16+
print(*args, flush=True)
17+
18+
with open("Cargo.toml") as file:
19+
document = toml.load(file)
20+
members = document['workspace']['members']
21+
22+
log("Found workspace member entries:", members)
23+
24+
all_packages = []
25+
for member in members:
26+
all_packages.extend(glob.glob(member))
27+
all_packages.sort()
28+
log("Package directories:", all_packages)
29+
30+
contract_packages = [p for p in all_packages if p.startswith(PACKAGE_PREFIX)]
31+
log("Contracts to be built:", contract_packages)
32+
33+
artifacts_dir = os.path.realpath("artifacts")
34+
os.makedirs(artifacts_dir, exist_ok=True)
35+
36+
for contract in contract_packages:
37+
log("Building {} ...".format(contract))
38+
# make a tmp dir for the output (*.wasm and other) to not touch the host filesystem
39+
tmp_dir = "/tmp/" + contract
40+
os.makedirs(tmp_dir, exist_ok=True)
41+
42+
# Rust nightly and unstable-options is needed to use --out-dir
43+
cmd = [CARGO_PATH, "-Z=unstable-options", "build", "--release", "--target=wasm32-unknown-unknown", "--locked", "--out-dir={}".format(tmp_dir)]
44+
os.environ["RUSTFLAGS"] = "-C link-arg=-s"
45+
subprocess.check_call(cmd, cwd=contract)
46+
47+
for build_result in glob.glob("{}/*.wasm".format(tmp_dir)):
48+
log("Optimizing built {} ...".format(build_result))
49+
name = os.path.basename(build_result)
50+
cmd = ["wasm-opt", "-Os", "-o", "artifacts/{}".format(name), build_result]
51+
subprocess.check_call(cmd)
52+

optimize_workspace.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/bash
2+
set -o errexit -o nounset -o pipefail
3+
command -v shellcheck > /dev/null && shellcheck "$0"
4+
5+
export PATH="$PATH:/root/.cargo/bin"
6+
7+
rustup toolchain list
8+
cargo --version
9+
10+
optimize_workspace.py
11+
12+
# Postprocess artifacts
13+
(
14+
cd artifacts
15+
chmod -x ./*.wasm
16+
sha256sum -- *.wasm > checksums.txt
17+
)
18+
19+
echo "done"
File renamed without changes.

workspace-optimizer.Dockerfile

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# See https://github.com/emscripten-core/emscripten/pull/9119/files for fastcomp vs. upstream docs
2+
# Using the traditional fastcomp for now.
3+
FROM trzeci/emscripten:1.39.8-fastcomp
4+
5+
# This version of Rust will not be used for compilation but just serves as a stable base image to get debian+rustup.
6+
# See Rust nightly config below.
7+
FROM rust:1.45.2
8+
RUN rustup toolchain remove 1.45.2
9+
10+
RUN apt update
11+
RUN apt install python3 python3-toml -y
12+
13+
RUN python3 --version
14+
15+
# Install Rust nightly
16+
# Choose version from: https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu.html
17+
RUN rustup toolchain install nightly-2020-08-20 --allow-downgrade --profile minimal --target wasm32-unknown-unknown
18+
RUN rustup default nightly-2020-08-20
19+
RUN rustup toolchain list
20+
# Check cargo version
21+
RUN cargo --version
22+
23+
# copy wasm-opt into our path
24+
COPY --from=0 /emsdk_portable/binaryen/bin/wasm-opt /usr/local/bin
25+
26+
# Assume we mount the source code in /code
27+
WORKDIR /code
28+
29+
# Add our scripts as entry point
30+
ADD optimize_workspace.py /usr/local/bin/
31+
ADD optimize_workspace.sh /usr/local/bin/
32+
RUN chmod +x /usr/local/bin/optimize_workspace.py
33+
RUN chmod +x /usr/local/bin/optimize_workspace.sh
34+
35+
ENTRYPOINT ["optimize_workspace.sh"]
36+
# Default argument when none is provided
37+
CMD ["."]

0 commit comments

Comments
 (0)