envsetup.sh -- a very useful automation technique
Cliff Brake January 10, 2025 #automation #shellDuring a Android development stint, I learned about the
envsetup.sh
concept. This is a script that contains numerous functions get populated in a
shell envrionment when the script is sourced. There two ways to source the file
-- you can type:
source envsetup.sh
Or use a '.' for a shortcut for source and type:
. envsetup.sh
Most of my projects and Git repos now contain a envsetup.sh
file (examples:
Simple IoT,
Yoe,
TMPDIR Podcast).
Typically all of the functions in the file have a common prefix. This gives them
a namespace and allows you to see all the functions quickly in your terminal by
typing <prefix><tab><tab>
.
Why shell functions instead of something like Make, Earthly, Nix, Task, Mage,
Pants, Bazel, Python ...? Shell script is about the simplest abstraction on top
of typing commands. Thus, if mostly what you need to do is execute a sequence of
terminal commands, then shell functions are very efficient at doing this. A
shell function is often the best documentation for how to do something -- it is
almost like a powerful checklist. There are no dependencies -- every Linux and
MacOS system comes with a shell built-in. envsetup.sh
functions can often be
used directly in CI/CD and other build systems (like Yocto/Bitbake tasks).
There are some common objections ...
Shell is not cross-platform to Windows. That is unfortunate, but I rarely use Windows, and most developers I interact with use either Linux or MacOS systems. However, this may change with Zephyr as MCU developers traditionally have used Windows.
Why not use something more powerful like Make? With modern programming languages and build systems like Go, Rust, and Yocto, the is no need for yet another build system. The complex issues like dependencies are already handled. Thus, all you need to do is remember how to kick it off.
Why not use Python -- everyone uses Python for everything -- it is the most popular programming language ever! Python is great if you need to do something more complex, but it is a more complex programming abstraction. And mostly what you need to do is record a sequence of commands, any abstraction costs you more than benefits.
Why not a shell script that you execute instead of sourcing a script that
populates functions? I'm not sure -- the sourcing just seems more ergonomic to
me and simpler. If you need to run a script to execute a command, then you need
to process command line arguments, etc. You are now programming, and programming
is what we are trying to avoid in envsetup.sh.
Also, the functions are not as
discoverable as when they are populated in your environment.
Shell scripting is not without its tradeoffs -- when you do need to program something in shell, it can be very painful and errors can be subtle and hard to catch. However, AI tools are very effective at writing shell script, so this can be a big help. Linters and formatters are also very good at catching problems early and making your scripts more readable (more on this later).
In summary, think of your envsetup.sh
file primarily as documentation. Create
one in every project and instead of documentation how to do something, create a
shell function. Your future self will thank you.