The Homebrew build environment is designed with reproducible builds as a goal where possible. Some convenience tools are also available to formula authors to help achieve deterministic builds.
Some build tools embed or record the time at which the build occurs. This can cause build artifacts to differ between repeated builds of the same sources. To avoid this issue, the Homebrew build environment sets the SOURCE_DATE_EPOCH
environment variable to the modification time of the source code for tools that consume it.
In cases where a build time must be manually set, time
is a Ruby DateTime
object containing the same timestamp as the SOURCE_DATE_EPOCH
environment variable. Methods provided by Ruby on DateTime
objects can then be used to format this time into the desired format.
def install
system "make", "install",
"VERSION=#{version}",
"DATE=#{time.iso8601}",
"PREFIX=#{prefix}"
end
See the kustomize
formula for an example of using time.iso8601
or the git-town
formula for an example of using time.strftime
with custom format specifiers.
Some formulae may create gzip-compressed files during their build process (for example, compressing manpages or other data files). Build machines may provide different implementations of the gzip
utility, and by default gzip
will record the modification time of the file being compressed, which usually varies depending on the build time. Thus, relying on the build machine’s gzip
utility will usually result in non-reproducible outputs being part of the build.
To avoid this issue, Homebrew provides the Utils::Gzip.compress
helper function for situations where reproducible gzip
compression is needed. This function accepts one or more paths to be compressed and places the compressed files next to the original with a .gz
suffix, just like the gzip
utility does. It also returns an array of Pathname
objects which can be consumed by other methods.
def install
system "make", "install"
man1.install Utils::Gzip.compress("mycommand.1")
end
def install
system "make", "install"
(pkgshare/"data").install Utils::Gzip.compress(*Dir["#{buildpath}/path/to/some/folder/contents/*"])
end
See the par
formula for an example with a single file or the pari-elldata
formula for an example with multiple files.
Some formulae or build tools record paths specific to the build environment in configuration files or in binaries. When building redistributable bottles, Homebrew searches through the built files and replaces paths to common Homebrew locations, such as the Homebrew prefix and the Cellar, with a placeholder like @@HOMEBREW_PREFIX@@
or @@HOMEBREW_CELLAR@@
. When bottles are installed, Homebrew expands these placeholders to the respective paths on the end user’s machine.
This allows for some bottles to be used by users that may have Homebrew installed in a non-default prefix. It also results in bit-for-bit identical bottles between platforms where the location of Homebrew is the only difference.
This search and replace process occurs automatically and does not require any additional action from formula authors to use it.