In our previous blog post “Go Ahead ‘make’ My
Day” we
presented the scriptlet, an advanced make technique for spicing up
your Makefile recipes. In this follow-up, we’ll deconstruct the
scriptlet and detail the ingredients that make up the secret sauce.
Makefile scriptlets are an advanced technique that uses
GNU make’s powerful functions to safely embed a multi-line script
(Perl, in our example) into a single, clean shell command. It turns a
complex block of logic into an easily executable template.
#-*- mode: makefile; -*-
DARKPAN_TEMPLATE="https://cpan.openbedrock.net/orepan2/authors/D/DU/DUMMY/%s-%s.tar.gz"
define create_requires =
# scriptlet to create cpanfile from an list of required Perl modules
# skip comments
my $DARKPAN_TEMPLATE=$ENV{DARKPAN_TEMPLATE};
while (s/^#[^\n]+\n//g){};
# skip blank lines
while (s/\n\n/\n/) {};
for (split/\n/) {
my ($mod, $v) = split /\s+/;
next if !$mod;
my $dist = $mod;
$dist =~s/::/\-/g;
my $url = sprintf $DARKPAN_TEMPLATE, $dist, $v;
print <<"EOF";
requires \"$mod\", \"$v\",
url => \"$url\";
EOF
}
endef
export s_create_requires = $(value create_requires)
cpanfile.darkpan: requires.darkpan
DARKPAN_TEMPLATE=$(DARKPAN_TEMPLATE); \
DARKPAN_TEMPLATE=$$DARKPAN_TEMPLATE perl -0ne "$$s_create_requires" $< > $@ || rm $@
define / endef)This section creates the multi-line variable that holds your entire Perl program.
define create_requires =
# Perl code here...
endef
define ... endef: This is GNU Make’s mechanism for defining
a recursively expanded variable that spans multiple lines. The
content is not processed by the shell yet; it’s simply stored by
make.while loop and if statements)
directly inside a Makefile.my $ENV{...})This is a critical step for making your script template portable and configurable.
my $DARKPAN_TEMPLATE=$ENV{DARKPAN_TEMPLATE};
make.$ENV{DARKPAN_TEMPLATE}. This makes the script agnostic to its
calling environment, delegating the data management back to the
Makefile.export and $(value))This is the “magic” that turns the multi-line Make variable into a single, clean shell command.
export s_create_requires = $(value create_requires)
$(value create_requires): This is a specific Make function
that performs a direct, single-pass expansion of the
variable’s raw content. Crucially, it converts the entire
multi-line block into a single-line string suitable for export,
preserving special characters and line breaks that the shell will
execute.export s_create_requires = ...: This exports the multi-line
Perl script content as an environment variable
(s_create_requires) that will be accessible to any shell process
running in the recipe’s environment.$$ and perl -0ne)The final recipe executes the entire, complex process as a single, atomic operation, which is the goal of robust Makefiles.
cpanfile.darkpan: requires.darkpan
DARKPAN_TEMPLATE=$(DARKPAN_TEMPLATE); \
DARKPAN_TEMPLATE=$$DARKPAN_TEMPLATE perl -0ne "$$s_create_requires" $< > $@ || rm $@
DARKPAN_TEMPLATE=$(DARKPAN_TEMPLATE): This creates the local
shell variable.DARKPAN_TEMPLATE=$$DARKPAN_TEMPLATE perl...: This is the
clean execution. The first DARKPAN_TEMPLATE= passes the newly
created shell variable’s value as an environment variable to
the perl process. The $$ ensures the shell variable is
properly expanded before the Perl interpreter runs it.perl -0ne "...": Runs the Perl script:
* `-n` and `-e` (Execute script on input)
* `-0`: Tells Perl to read the input as one single block
(slurping the file), which is necessary for your multi-line
regex and `split/\n/` logic.
|| rm $@: This is the final mark of quality. It makes the
entire command transactional—if the Perl script fails, the
half-written target file ($@) is deleted, forcing make to try
again later.Mastering build automation using make will transform you from
being an average DevOps engineer into a rockstar. GNU make is a Swiss
Army knife with more tools than you might think! The knives are sharp
and the tools are highly targeted to handle all the real-world issues
build automation has encountered over the decades. Learning to use
make effectively will put you head and shoulders above the herd (see
what I did there? 😉).
The scriptlet technique creates a powerful, universal pattern for clean, atomic builds:
define/export technique works perfectly with python -c.Learn more about GNU
make
and move your Makefiles from simple shell commands to precision
instruments of automation.
Thanks for reading.