15

I'm new to make and Makefiles, but I'm trying to create one for my next project and I'm running into PATH issues. I keep getting the error: "No such file or directory"

I've created a simple target called test that runs all my tests using mocha.

Mocha is installed as a local node module, so its executable can be found at ./node_modules/.bin/mocha. I'm altering my PATH as described in this make tutorial so I can refer to it as mocha instead of typing the full path, but something doesn't seem to be working.

Here's what I have so far:

export PATH := node_modules/.bin:$(PATH) test: which mocha mocha .PHONY: test 

When I run make test I get the following output:

which mocha node_modules/.bin/mocha mocha make: mocha: No such file or directory make: *** [test] Error 1 

As you can see from the output, which mocha is correctly printing the path to the mocha executable, but when I simply run mocha, it can't find it.

What am I doing wrong? Is there bigger picture about variable scope or persistence in Makefiles that I'm missing?

P.S. If it's important, I'm using a Mac and the version of make that comes with the XCode developer tools. This is what I get when I run make -v

GNU Make 3.81 Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program built for i386-apple-darwin11.3.0 
2
  • 1
    This is weird.. What happens if you remove the line export PATH := node_modules/.bin:$(PATH) and run make like this : $ PATH=node_modules/.bin:$PATH make test Is it working ? Commented Feb 11, 2014 at 19:29
  • Yep, works just fine if I do that. Commented Feb 11, 2014 at 19:51

5 Answers 5

13

I can't reproduce your results (using GNU make 3.81 on a GNU/Linux system). It appears that there may be some difference in either the way the Apple system works, or that Apple has made some kind of patch to the GNU make version they ship that is causing this problem.

GNU make has two ways of running recipes: the normal way, where it invokes a shell and passes the recipe to the shell to be run, and the "fast path", where, if make sees that the command is "simple enough" (that is if it contains no shell special characters), it will chop up the command into words and directly execute the command without invoking the shell. The latter is much faster, but since it's being invoked directly it inherits the PATH setting from GNU make itself not from the shell.

It appears that for some reason the version of GNU make shipped by Apple is not working properly in that it's not setting the environment correctly for commands which are run directly by GNU make, via the "fast path".

I have a very vague recollection of something like this being discussed on the GNU make mailing lists, but after spending some time searching I wasn't able to come up with anything.

You can "fix" this problem by forcing your command to use the slow path, by introducing some shell special characters (globbing, ;, pipes, etc.). Or you can use a fully-qualified path to the program.

Or, you can go get the source code for GNU make and build it yourself; if this difference is a result of a "fix" by Apple that should make it work better. Or install GNU make from homebrew or ports, which will also get you a newer version with more features, I expect.

Sign up to request clarification or add additional context in comments.

8 Comments

Yes, it does work if I use the full path, which is the weird part. And yes it does work if I put that code into a file and run it. Perhaps it's a mac thing if you can get it working on Linux.
I find this extremely odd. If you have the inclination you might try installing straight GNU make, either by building it yourself (it's easy) or maybe through MacPorts or Brew. I'm wondering if Apple might have "helpfully" patched their version of GNU make in some way that is breaking things.
Another thing to check is change your invocation of mocha to look like this: : ; mocha. Doing this will circumvent GNU make's fast-path processing and force it to invoke a shell. If this works then I'm even more suspicious that something is bizarre with the Apple version of GNU make.
It's not a single command. It's a simple command. For efficiency GNU make has a "fast path" where it examines the command to be invoked and if there are no "special characters" in it (this is defined as characters that need the shell to handle them) then it invokes the command directly rather than starting a shell. When you use ./test/* you're using a special character (*, for globbing) that requires the shell, just like when I used ;. Somehow Apple's version of GNU make is not setting the local PATH environment variable, it's only setting PATH in subprocesses.
It would be great if this answer were easier to find. I struggled for a while to figure out why the exported PATH didn't apply to some recipes while it did to others. It is trivial to reproduce this on OSX with GNU Make 3.81. Adding a ; to commands is a quick workaround.
|
13

In OSX you also need to also set SHELL:

PATH := node_modules/.bin:$(PATH) SHELL := /bin/bash 

Comments

3

This should work:

PATH := $(PATH):$(PWD)/node_modules/.bin SHELL := env PATH=$(PATH) /bin/bash 

1 Comment

It would almost work if it weren't for the double quotes around the value for PATH; quotes have no special meaning in Makefile assignments, so they are simply prepended and appended to the PATH, effectively masking the first and last values (unless there is a directory called node_modules/.bin"
3

I'm using GNU make version 4.1 on Ubuntu 16.04.4

I had a similar issue and none of the suggested solutions worked for me.

Settings to PATH do not seem to be in effect when later variable settings take place (at least in this version of make).

export PATH := /usr/local/bin:/bin:/usr/bin # Path setting is not in effect here, 'which' returns an empty string # even when 'vw' is installed in /usr/local/bin/vw VW = $(which vw) 

The solution that worked for me was to inject the earlier PATH setting explicitly into the particular sub-shell used to assign a later make variable, like this:

VW = $(shell env PATH=$(PATH) which vw) 

The following Makefile example demonstrates the case that works vs the one that doesn't:

SHELL := /bin/bash export PATH := /usr/local/bin:/bin:/usr/bin # Expecting 'vw' to be found, due to PATH setting above # ('vw' is in /usr/local/bin/vw) VW_NOT_OK = $(which vw) VW_OK = $(shell env PATH=$(PATH) which vw) all: # -- Doesn't work: @echo "VW_NOT_OK='$(VW_NOT_OK)'" # -- Works: @echo "VW_OK='$(VW_OK)'" 

Reproducing the problem and solution:

$ make --version| head -1 GNU Make 4.1 $ make # -- Doesn't work: VW_NOT_OK='' # -- Works: VW_OK='/usr/local/bin/vw' 

4 Comments

Your problem is different, because you're using the shell function, not a recipe as in the original question. The handling of the environment for the shell function is not the same as for recipes.
@MadScientist the reason I was forced to use $(shell ...) was that nothing else I tried worked. Setting PATH globally seems to affect Makefile commands/rules but it doesn't seem be in effect in later variable settings: VARNAME = ... where the setting depends on $(PATH) in order to work (like in my call to which). Note that I also wrote "I had a similar issue" not an identical one.
"Variable name settings" don't use PATH for anything, so how does the setting of PATH matter? Certainly $(which foo) is the empty string: which is not a valid make function so you're just expanding a variable named, literally, which foo, that has no value and so expands to the empty string. The ONLY way to run a shell command from a makefile, other than in a recipe, is by using the $(shell ...) function. $(which foo) expands the variable named which foo; $(shell which foo) runs the shell command which foo and expands to its output.
Thanks for the explanation. The reason is clear now!
0

I am afraid that you can't do that Try with "local" variables instead

NODE_MODULES := node_modules/.bin test: @ $(NODE_MODULES)/mocha 

3 Comments

Well, according to the article I read, you can do that. And since which mocha is working, it appears that it did work for that line. Also, if I print out the PATH variable, it does include the node_modules bit, though perhaps that's only scoped to the make sub-process, I don't know.
The linked SO question is not relevant to this one. It's true that a sub-process cannot modify its parent's environment but that's not what's happening here. That question is asking how to have a makefile modify its caller's environment. This question is entirely different: it wants to modify its children's environment, which is perfectly reasonable and supported.
I see and I am ready to remove it if you think that it could be misleading for future readers

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.