<p>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:</p>

<p>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 <em>nvm_install_dir</em></p>

<p>Then we write a <code>_nvm_trampoline</code> script which use the name it is called as to trigger the right function, and create as many file aliases to <code>_nvm_trampoline</code>  as there are functions in the script.  Note the leading underscore, hinting at the “private” character of the script.</p>

<p>This script can go along the lines of</p>

<pre><code>#!/bin/sh
. "${NVM_DIR}/nvm.sh"

if [ "${0##*/}" = '_nvm_driver' ]; then
    : NOP
else
    eval "${0##*/}" "$@"
fi
</code></pre>

<p>Here we use <code>${0##*/}</code> to remove any path element from the name under which our script is called.  We install that script under <code>${nvm_install_dir}</code> and run once the following utility script:</p>

<pre><code>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_install
</code></pre>

<p>It is a good practice to pack the <em>nvm_install</em> 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 <em>nvm_install</em> than the body of the function, if we want to experiment with the script.)</p>

<p>Ater this, the directory pointed to by <em>nvm_install_dir</em> is populated with aliases to <code>_nvm_trampoline</code> that are executable and delegate their work to the corresponding function. We only need to add this directory to our <em>PATH</em> when running <em>make</em>.</p>

<p>A second approach would be to generate pseudo commands for each <em>nvm</em> functions, with the following script:</p>

<pre><code>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_generate
</code></pre>

<p>The output of this program consists of <em>make</em> variable assignments</p>

<pre><code>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
…
</code></pre>

<p>It can be saved to a file <code>Makefile.nvm</code> or <code>nvmtools.mk</code> that can be included our Makefiles. Calling the function <code>nvm_command_info</code> is done with</p>

<pre><code>${NVM_COMMAND_INFO} arg1 arg2 …
</code></pre>

<p><hr>
Using a customised bash profile is possible, using the assignment <code>SHELL=/bin/bash --rcfile PATH-TO-CUSTOM-PROFILE -i</code> but I would consider this esoteric enough to puzzle the maintenance programmer.</p>

CC BY-SA 3.0
2017-04-20T09:04:44.900Z
2017-04-20T09:04:44.900Z
Michaël Le Barbier
<p>One possible issue is that the NVM install script only writes the commands to set itself up to either <code>.bash_profile</code> OR <code>.bashrc</code>.  Depending on your systems configuration, it may only be setup for interactive shells, not ones spawned by other programs (<a href="https://apple.stackexchange.com/questions/51036/what-is-the-difference-between-bash-profile-and-bashrc">more info</a>).  Sourcing the nvm setup script can be a good option, as a previous comment noted.  If you have a number of commands to run for 1 make target, look into <a href="https://www.gnu.org/software/make/manual/html_node/One-Shell.html#One-Shell" rel="nofollow noreferrer">.ONESHELL</a>.  Sourcing <code>$NVM_DIR/nvm.sh</code> should take care of it.</p>

<p><a href="https://stackoverflow.com/a/13130922">Another option</a> that invokes bash directly with the interactive flag might work as well.</p>

CC BY-SA 3.0
2017-03-24T17:49:58.290Z
2017-04-18T15:55:40.083Z
2017-05-23T12:39:34.013Z
Matt
<p>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.</p>

<p>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 <code>export -f</code> for a zillion little NVM bash functions?</p>

CC BY-SA 3.0
2017-03-23T12:42:16.943Z
Probably [related](http://unix.stackexchange.com/questions/184508/nvm-command-not-available-in-bash-script).
Tensibai
CC BY-SA 3.0
2017-03-23T12:43:41.933Z
indeed. but does not offer a good solution. I can make it work with prepending `source ...` to every line of my makefile, for example, but clearly that's sub-optimal.
Assaf Lavie
CC BY-SA 3.0
2017-03-23T12:45:30.353Z
Maybe this may worth a word in your question on what you already tried/found and why it doesn't solve your problem, an example Makefile exhibiting the problem could help aslo I think.
Tensibai
CC BY-SA 3.0
2017-03-23T12:46:44.247Z
Btw another [source](https://blog.jayway.com/2013/05/19/auto-install-node-js-and-npm-from-makefile/), I'm not working with node.js so that's just in case it may be of help to solve the problem
Tensibai
CC BY-SA 3.0
2017-03-23T12:47:51.313Z
not so much, actually, but I appreciate the help.
Assaf Lavie
CC BY-SA 3.0
2017-03-23T13:04:16.387Z
Let me see if I get right: you're problem is to avoid writing `export -f <funcname>` for each nvm function of your parent shell. (if this is the case I assume `declare -F | sed 's/declare/export/' > func.sh` and then a `source funcs.sh` within the makefile should do.)
Tensibai
CC BY-SA 3.0
2017-03-23T17:11:59.790Z
This probably belongs on StackOverflow as it is not specifically related to DevOps in any way
coderanger
CC BY-SA 3.0
2017-03-23T18:55:20.487Z
@coderanger I've the feeling SO would redirect to server fault which would redirect to SO. I agree that's 100% programming but I feel it would be badly received on SO or SF/SU
Tensibai
CC BY-SA 3.0
2017-04-17T00:44:52.837Z
I'd say this on-topic at SF, SO, or here.  It'd be nice if the OP clarified which functions they want from NVM.
chicks
<make>
How can I make use of bash functions in a makefile?
Assaf Lavie
# How can I make use of bash functions in a makefile?

(Asked by: username_0 on 2017-03-23)

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 `export -f` for a zillion little NVM bash functions?

**Tags:** make

> **Comments:**
> **username_1**: Probably [related](http://unix.stackexchange.com/questions/184508/nvm-command-not-available-in-bash-script).  
> **username_0**: indeed. but does not offer a good solution. I can make it work with prepending `source ...` to every line of my makefile, for example, but clearly that's sub-optimal.  
> **username_1**: Maybe this may worth a word in your question on what you already tried/found and why it doesn't solve your problem, an example Makefile exhibiting the problem could help aslo I think.  
> **username_1**: Btw another [source](https://blog.jayway.com/2013/05/19/auto-install-node-js-and-npm-from-makefile/), I'm not working with node.js so that's just in case it may be of help to solve the problem  
> **username_0**: not so much, actually, but I appreciate the help.  
> **username_1**: Let me see if I get right: you're problem is to avoid writing `export -f <funcname>` for each nvm function of your parent shell. (if this is the case I assume `declare -F | sed 's/declare/export/' > func.sh` and then a `source funcs.sh` within the makefile should do.)  
> **username_2**: This probably belongs on StackOverflow as it is not specifically related to DevOps in any way  
> **username_1**: @username_2 I've the feeling SO would redirect to server fault which would redirect to SO. I agree that's 100% programming but I feel it would be badly received on SO or SF/SU  
> **username_3**: I'd say this on-topic at SF, SO, or here.  It'd be nice if the OP clarified which functions they want from NVM.  

---

## 2 answers

### Answer by username_4 on 2017-04-20. Score: 6

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 _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##*/}" "$@"
fi

Here 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_install

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 _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_generate

The 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.


---

### Answer by username_5 on 2017-03-24. Score: 3

One possible issue is that the NVM install script only writes the commands to set itself up to either `.bash_profile` OR `.bashrc`. Depending on your systems configuration, it may only be setup for interactive shells, not ones spawned by other programs ([more info](https://apple.stackexchange.com/questions/51036/what-is-the-difference-between-bash-profile-and-bashrc)). Sourcing the nvm setup script can be a good option, as a previous comment noted. If you have a number of commands to run for 1 make target, look into [.ONESHELL](https://www.gnu.org/software/make/manual/html_node/One-Shell.html#One-Shell). Sourcing `$NVM_DIR/nvm.sh` should take care of it.

[Another option](https://stackoverflow.com/a/13130922) that invokes bash directly with the interactive flag might work as well.


---

