How to define global shell functions in a Makefile?

前端 未结 7 980
醉酒成梦
醉酒成梦 2021-02-12 12:18

I want to define a shell function

#!/bin/sh

test ()
{
  do_some_complicated_tests $1 $2;
  if something; then
    build_thisway $1 $2;
  else
    build_otherway         


        
相关标签:
7条回答
  • 2021-02-12 12:49

    I don't think this qualifies as "elegant", but it seems to do what you want:

    ##
    ## --- Start of ugly hack
    ##
    
    THIS_FILE := $(lastword $(MAKEFILE_LIST))
    
    define shell-functions
    : BEGIN
      # Shell syntax here
      f()
      {
        echo "Here you define your shell function. This is f(): $@"
      }
    
      g()
      {
        echo "Another shell function. This is g(): $@"
      }
    : END
    endef
    
    # Generate the file with function declarations for the shell
    $(shell sed -n '/^: BEGIN/,/^: END/p' $(THIS_FILE) > .functions.sh)
    
    # The -i is necessary to use --init-file
    SHELL := /bin/bash --init-file .functions.sh -i
    
    ##
    ## -- End of ugly hack
    ##
    
    all:
        @f 1 2 3 4
        @g a b c d
    

    Running this produces:

    $ make -f hack.mk
    Here you define your shell function. This if f(): 1 2 3 4
    Another shell function. This is g(): a b c d
    

    Running it with -n produces:

    $ make -f hack.mk -n
    f 1 2 3 4
    g a b c d
    

    This relies on the fact that macros defined between define and endef are not interpreted by make at all until they are actually used, so you can use shell syntax directly and you don't need to end each line with a backslash. Of course, it will bomb if you call the shell-functions macro.

    0 讨论(0)
  • 2021-02-12 12:50

    This solution does not rely on an external temporary file and does not force you to tinker with the SHELL variable.

    TESTTOOL=sh -c '\
      do_some_complicated_tests $$1 $$2; \
      if something; then
        build_thisway $$1 $$2;
      else
        build_otherway $$1 $$2;
      fi' TESTTOOL
    
    ifneq (,$(findstring n,$(MAKEFLAGS)))
    TESTTOOL=: TESTTOOL
    endif
    
    foo: bar
        ${TESTTOOL} foo baz
    

    The ifneq…endif block checks for the -n flag on the command line and sets the expansion of TESTTOOL to : TESTTOOL which is easy to read and safe to execute.

    The best solution could be to turn the shell function into an actual program if this is an option for you.

    0 讨论(0)
  • 2021-02-12 12:56

    Something tells me you'd be better off filtering the output of make -n, but what you ask is possible:

    define test
      @echo do some tests with $(1) and $(2); \
      SOMETHING=$(1)_3 ; \
      if [ $$SOMETHING == foo_3 ]; then \
        echo build this way $(1) $(2); \
      else \
        echo build another way $(1) $(2) ; \
      fi
    endef
    
    someTarget:
        $(call test,foo,bar)
    
    someOtherTarget:
        $(call test,baz,quartz)
    
    0 讨论(0)
  • 2021-02-12 12:59

    Since the question is tagged gnu-make, you might be happy with Beta's solution, but I think the best solution is to 1) rename the function anything other than test (avoid names like ls and cd as well); for the purpose of discussion let's assume you've renamed it foo, 2) simply write a script named foo and invoke it from the Makefile. If you really want to define a shell function in the Makefile, just define it for each rule:

    F = foo() { \
      do_some_complicated_tests $$1 $$2; \
      if something; then \
        build_thisway $$1 $$2; \
      else \
        build_otherway $$1 $$2; \
      fi \
    }
    
    all: bar
      @$(F); foo baz bar
    

    Not that you must have line continuations on each line of the definition of foo, and the $ are all escaped so that they are passed to the shell.

    0 讨论(0)
  • 2021-02-12 13:01

    Why not using .ONESHELL in Makefile and define a Makefile function like bash function:

    jeromesun@km:~/workshop/hello.test$ tree
    .
    ├── Makefile
    └── mkfiles
        └── func_test.mk
    
    1 directory, 2 files
    jeromesun@km:~/workshop/hello.test$ cat Makefile 
    .ONESHELL:
    
    -include mkfiles/*.mk
    
    test:
            @$(call func_test, one, two)
    jeromesun@km:~/workshop/hello.test$ cat mkfiles/func_test.mk 
    .ONESHELL:
    
    define func_test
        echo "func_test parameters: 0:$0, 1:$1, 2:$2"
        if [ ! -d abc ]; then
            mkdir abc
            echo "abc not found"
        else
           echo "abc found"
        fi
    endef
    jeromesun@km:~/workshop/hello.test$ 
    

    Result:

    jeromesun@km:~/workshop/hello.test$ make test 
    func_test parameters: 0:func_test, 1: one, 2: two
    abc not found
    jeromesun@km:~/workshop/hello.test$ make test 
    func_test parameters: 0:func_test, 1: one, 2: two
    abc found
    
    0 讨论(0)
  • 2021-02-12 13:09

    This is really ghetto, but whatever. I use zsh, but I'm sure there are bash and sh equivalents.

    Makefile:

    export ZDOTDIR := ./
    
    SHELL := /usr/bin/env zsh
    
    default:
        f
    

    .zshenv, same directory as Makefile:

    f () { echo 'CHECK OUT THIS AWESOME FUNCTION!' }
    

    The ZDOTDIR variable makes zsh look in the current directory for dotfiles. Then you just stick what you want in .zshenv.

    $ make
    f
    CHECK OUT THIS AWESOME FUNCTION!
    
    0 讨论(0)
提交回复
热议问题