Answers: <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>
  
Answers: CC BY-SA 3.0
Answers: 2017-04-20T09:04:44.900Z
Answers: 2017-04-20T09:04:44.900Z
Answers: Michaël Le Barbier
Answers: <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>
  
Answers: CC BY-SA 3.0
Answers: 2017-03-24T17:49:58.290Z
Answers: 2017-04-18T15:55:40.083Z
Answers: 2017-05-23T12:39:34.013Z
Answers: Matt
Body: <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>
  
Comments: CC BY-SA 3.0
Comments: 2017-03-23T12:42:16.943Z
Comments: Probably [related](http://unix.stackexchange.com/questions/184508/nvm-command-not-available-in-bash-script).
Comments: Tensibai
Comments: CC BY-SA 3.0
Comments: 2017-03-23T12:43:41.933Z
Comments: 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.
Comments: Assaf Lavie
Comments: CC BY-SA 3.0
Comments: 2017-03-23T12:45:30.353Z
Comments: 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.
Comments: Tensibai
Comments: CC BY-SA 3.0
Comments: 2017-03-23T12:46:44.247Z
Comments: 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
Comments: Tensibai
Comments: CC BY-SA 3.0
Comments: 2017-03-23T12:47:51.313Z
Comments: not so much, actually, but I appreciate the help.
Comments: Assaf Lavie
Comments: CC BY-SA 3.0
Comments: 2017-03-23T13:04:16.387Z
Comments: 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.)
Comments: Tensibai
Comments: CC BY-SA 3.0
Comments: 2017-03-23T17:11:59.790Z
Comments: This probably belongs on StackOverflow as it is not specifically related to DevOps in any way
Comments: coderanger
Comments: CC BY-SA 3.0
Comments: 2017-03-23T18:55:20.487Z
Comments: @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
Comments: Tensibai
Comments: CC BY-SA 3.0
Comments: 2017-04-17T00:44:52.837Z
Comments: 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.
Comments: chicks
Tags: <make>
Title: How can I make use of bash functions in a makefile?
UserDisplayName: Assaf Lavie
ThreadText: # 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.
  
  
  ---
  
  