Stamping is the act of embedding build metadata into the product that we ship to customers. It can help with issue diagnosis, analytics, and so on. Conveniently, Bazel offers us a first-class solution, and it is very easy to take advantage of it in the context of iOS apps.

Workspace status script

The first step in enabling stamping is to create a workspace status script. For example, we can create a script that emits the current Git commit hash:

#!/usr/bin/env bash

set -eu -o pipefail

echo "STABLE_GIT_COMMIT $(git rev-parse HEAD)"

Now we need to tell Bazel to execute the script:

--workspace_status_command=./tools/workspace_status.sh

Reading the value at build time

For iOS apps, or really any Apple platform app, it is usually best to embed this data in a plist file so we can read it at runtime. First, we use a genrule to read the workspace status data and materialize a plist file:

genrule(
    name = "commit_plist",
    outs = ["Commit.plist"],
    cmd = """
commit="$$(sed -n 's/^STABLE_GIT_COMMIT //p' bazel-out/stable-status.txt)"
plutil -convert xml1 -o "$@" - <<EOF
{
    "GIT_COMMIT": "$${commit}"
}
EOF
""",
    stamp = True,
)

From there, it is just a matter of adding this target to the infoplists attribute of any Apple platform application target, like ios_application. Because rules_apple performs plist merging, this value will end up in the final Info.plist file that we ship in the app bundle.

A word on the stamp attribute

Notice the stamp = True attribute on the genrule? That is what allows the genrule action to access bazel-out/stable-status.txt and bazel-out/volatile-status.txt.

Without it, the action should not rely on those files being present. In this example, the important part is stamping the genrule that materializes the plist.

This is separate from the stamp attribute you may see on Apple rules like ios_application, where stamping controls whether build information is encoded into the binary. For this plist-based approach, we do not need to rely on link stamping at the application target level.

Reading the value at runtime

Because the value ends up in Info.plist, we can read it at runtime through the Bundle / NSBundle API.

Conclusion

There you have it: an easy way to stamp iOS builds. I hope it helps you discover and fix bugs in production more easily.