Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Makefile with source get error `No such file or directory`

Tags:

I have a really simple Makefile that's doing source to set ENV variables. But it doesn't work if I call it from Makefile

I get this error

make dev
source ./bin/authenticate.sh
make: source: No such file or directory
make: *** [dev] Error 1

The script exists.

If I run this in command-line it works.

source ./bin/authenticate.sh
Works!

This is my Makefile

test:
    pytest -s

dev:
    source ./bin/authenticate.sh

I'm using OSX. I'm not sure if this would make a difference.

like image 360
toy Avatar asked May 18 '17 15:05

toy


People also ask

Where is the makefile located?

The makefile is a text file that contains the recipe for building your program. It usually resides in the same directory as the sources, and it is usually called Makefile .

What type of file is a makefile?

Script written as a Makefile, a developer file type that is used for compiling and linking programs from source code files; stores instructions using the GNU make standard. NOTE: Makefiles more commonly are created with the filename Makefile, which does not have a file extension.

What is shell in makefile?

$(shell) is a special function in gmake that runs an external command and captures the output for use in the makefile. For example, you could get the current working directory like this: CWD=$(shell pwd) all: @echo This makefile lives in $(CWD).


2 Answers

tldr; lose the source, use the dot (.)

Longer explanation follows...

It doesn't work because make is looking for a source program in the $PATH. This fails because source is a (non-POSIX) shell builtin, not an executable program on any conventional UNIX-like system.

I was able to replicate failure shown in the question; the following (much abridged) output was generated on Ubuntu 16.04 with GNU Make v4.1:

$ strace -f -s65536 -- make dev 2>&1 | grep 'authenticate'
read(3, "test:\n\tpytest -s\n\ndev:\n\tsource ./bin/authenticate.sh\n", 4096) = 53
write(1, "source ./bin/authenticate.sh\n", 29source ./bin/authenticate.sh
[pid 32100] execve("/usr/local/sbin/source", ["source", "./bin/authenticate.sh"], [/* 82 vars */]) = -1 ENOENT (No such file or directory)
[pid 32100] execve("/usr/local/bin/source", ["source", "./bin/authenticate.sh"], [/* 82 vars */]) = -1 ENOENT (No such file or directory)
[pid 32100] execve("/usr/sbin/source", ["source", "./bin/authenticate.sh"], [/* 82 vars */]) = -1 ENOENT (No such file or directory)
[pid 32100] execve("/usr/bin/source", ["source", "./bin/authenticate.sh"], [/* 82 vars */]) = -1 ENOENT (No such file or directory)
[pid 32100] execve("/sbin/source", ["source", "./bin/authenticate.sh"], [/* 82 vars */]) = -1 ENOENT (No such file or directory)
[pid 32100] execve("/bin/source", ["source", "./bin/authenticate.sh"], [/* 82 vars */]) = -1 ENOENT (No such file or directory)

You can see that make made six failed attempts to find the source program; i.e., one for each component of my $PATH.

If source is changed to ., make changes its strategy; instead of trying to find and execute a program, it just passes the rule body to the system shell:

$ strace -f -s65536 -- make dev 2>&1 | grep 'authenticate'
read(3, "test:\n\tpytest -s\n\ndev:\n\t. ./bin/authenticate.sh\n", 4096) = 48
write(1, ". ./bin/authenticate.sh\n", 24. ./bin/authenticate.sh
[pid 32122] execve("/bin/sh", ["/bin/sh", "-c", ". ./bin/authenticate.sh"], [/* 82 vars */]) = 0
[pid 32122] open("./bin/authenticate.sh", O_RDONLY) = 3

The kind of shell used by make determines which shell builtins get expanded within each Makefile rule. You can tell make to use a shell of your choosing like so:

$ cat Makefile
SHELL = /bin/bash
test:
        pytest -s

dev:
        source ./bin/authenticate.sh

Since bash defines the builtin source as a synonym for ., the rule using source now succeeds:

$ strace -f -s65536 -- make dev 2>&1 | grep 'authenticate'
read(3, "SHELL = /bin/bash\ntest:\n\tpytest -s\n\ndev:\n\tsource ./bin/authenticate.sh\n", 4096) = 71
write(1, "source ./bin/authenticate.sh\n", 29source ./bin/authenticate.sh
[pid 32573] execve("/bin/bash", ["/bin/bash", "-c", "source ./bin/authenticate.sh"], [/* 82 vars */]) = 0
[pid 32573] open("./bin/authenticate.sh", O_RDONLY) = 3

References:

  • https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html#Bash-Builtins
  • http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html
  • http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01
  • https://www.gnu.org/software/make/manual/make.html#Choosing-the-Shell
like image 77
rubicks Avatar answered Sep 18 '22 12:09

rubicks


do cd bin && source authenticate.sh it works

like image 44
Nabaz Maaruf Avatar answered Sep 19 '22 12:09

Nabaz Maaruf