snippetMinor
How can I make use of bash functions in a makefile?
Viewed 0 times
canmakefilemakehowfunctionsusebash
Problem
If I need to use something like NVM in my makefile, I run into a problem, because NVM exports some bash functions that are not available in Make, even if my shell is defined as bash in the makefile.
How do I cause make to "inherit" all the parent shell's functions? Is there a way to make NVM accessible to a makefile, without writing
How do I cause make to "inherit" all the parent shell's functions? Is there a way to make NVM accessible to a makefile, without writing
export -f for a zillion little NVM bash functions?Solution
There are several options that can be used here. Maybe one of the easiest is to install each function as a real script in some directory that is added to the path. Here is how to do this:
First, we choose a path name where to store all these functions, it can be a a directory in our project, where other utility scripts used in our Makefile live. We call it nvm_install_dir
Then we write a
This script can go along the lines of
Here we use
It is a good practice to pack the nvm_install procedure in a real function instead of just inlining its body in the script, as it gives better testing options. (It is easier to comment out the call to nvm_install than the body of the function, if we want to experiment with the script.)
Ater this, the directory pointed to by nvm_install_dir is populated with aliases to
A second approach would be to generate pseudo commands for each nvm functions, with the following script:
The output of this program consists of make variable assignments
It can be saved to a file
Using a customised bash profile is possible, using the assignment
First, we choose a path name where to store all these functions, it can be a a directory in our project, where other utility scripts used in our Makefile live. We call it nvm_install_dir
Then we write a
_nvm_trampoline script which use the name it is called as to trigger the right function, and create as many file aliases to _nvm_trampoline as there are functions in the script. Note the leading underscore, hinting at the “private” character of the script.This script can go along the lines of
#!/bin/sh
. "${NVM_DIR}/nvm.sh"
if [ "${0##*/}" = '_nvm_driver' ]; then
: NOP
else
eval "${0##*/}" "$@"
fiHere we use
${0##*/} to remove any path element from the name under which our script is called. We install that script under ${nvm_install_dir} and run once the following utility script:nvm_install_dir='SET-TO-ACTUAL-PARAMETER-VALUE!'
nvm_file="${NVM_DIR}/nvm.sh"
nvm_trampoline='_nvm_trampoline'
nvm_list_functions()
{
awk -F'[(][)]' '$1 ~ /^nvm_[^ ]*$/{print($1)}' "${nvm_file}"
}
nvm_install()
{
nvm_list_functions | {
while read nvm_function; do
ln\
"${nvm_install_dir}/${nvm_driver}"\
"${nvm_install_dir}/${nvm_function}"
chmod 755 "${nvm_install_dir}/${nvm_function}"
done
}
}
nvm_installIt is a good practice to pack the nvm_install procedure in a real function instead of just inlining its body in the script, as it gives better testing options. (It is easier to comment out the call to nvm_install than the body of the function, if we want to experiment with the script.)
Ater this, the directory pointed to by nvm_install_dir is populated with aliases to
_nvm_trampoline that are executable and delegate their work to the corresponding function. We only need to add this directory to our PATH when running make.A second approach would be to generate pseudo commands for each nvm functions, with the following script:
nvm_file="${NVM_DIR}/nvm.sh"
nvm_list_functions()
{
awk -F'[(][)]' '$1 ~ /^nvm_[^ ]*$/{print($1)}' "${nvm_file}"
}
nvm_generate()
{
nvm_list_functions | {
while read nvm_function; do
nvm_FUNCTION=$(printf '%s' "${nvm_function}" | tr '[a-z]' '[A-Z]')
printf '%s=sh -c \047source "${NVM_DIR}/nvm.sh"; %s "$@"\047 %s\n'\
"${nvm_FUNCTION}"\
"${nvm_function}"\
"${nvm_function}"
done
}
}
nvm_generateThe output of this program consists of make variable assignments
NVM_ECHO=sh -c 'source "${NVM_DIR}/nvm.sh"; nvm_echo "$@"' nvm_echo
NVM_CD=sh -c 'source "${NVM_DIR}/nvm.sh"; nvm_cd "$@"' nvm_cd
…
NVM_COMMAND_INFO=sh -c 'source "${NVM_DIR}/nvm.sh"; nvm_command_info "$@"' nvm_command_info
…It can be saved to a file
Makefile.nvm or nvmtools.mk that can be included our Makefiles. Calling the function nvm_command_info is done with${NVM_COMMAND_INFO} arg1 arg2 …Using a customised bash profile is possible, using the assignment
SHELL=/bin/bash --rcfile PATH-TO-CUSTOM-PROFILE -i but I would consider this esoteric enough to puzzle the maintenance programmer.Code Snippets
#!/bin/sh
. "${NVM_DIR}/nvm.sh"
if [ "${0##*/}" = '_nvm_driver' ]; then
: NOP
else
eval "${0##*/}" "$@"
finvm_install_dir='SET-TO-ACTUAL-PARAMETER-VALUE!'
nvm_file="${NVM_DIR}/nvm.sh"
nvm_trampoline='_nvm_trampoline'
nvm_list_functions()
{
awk -F'[(][)]' '$1 ~ /^nvm_[^ ]*$/{print($1)}' "${nvm_file}"
}
nvm_install()
{
nvm_list_functions | {
while read nvm_function; do
ln\
"${nvm_install_dir}/${nvm_driver}"\
"${nvm_install_dir}/${nvm_function}"
chmod 755 "${nvm_install_dir}/${nvm_function}"
done
}
}
nvm_installnvm_file="${NVM_DIR}/nvm.sh"
nvm_list_functions()
{
awk -F'[(][)]' '$1 ~ /^nvm_[^ ]*$/{print($1)}' "${nvm_file}"
}
nvm_generate()
{
nvm_list_functions | {
while read nvm_function; do
nvm_FUNCTION=$(printf '%s' "${nvm_function}" | tr '[a-z]' '[A-Z]')
printf '%s=sh -c \047source "${NVM_DIR}/nvm.sh"; %s "$$@"\047 %s\n'\
"${nvm_FUNCTION}"\
"${nvm_function}"\
"${nvm_function}"
done
}
}
nvm_generateNVM_ECHO=sh -c 'source "${NVM_DIR}/nvm.sh"; nvm_echo "$$@"' nvm_echo
NVM_CD=sh -c 'source "${NVM_DIR}/nvm.sh"; nvm_cd "$$@"' nvm_cd
…
NVM_COMMAND_INFO=sh -c 'source "${NVM_DIR}/nvm.sh"; nvm_command_info "$$@"' nvm_command_info
…${NVM_COMMAND_INFO} arg1 arg2 …Context
StackExchange DevOps Q#629, answer score: 6
Revisions (0)
No revisions yet.