310

I have a several Makefiles in app specific directories like this:

/project1/apps/app_typeA/Makefile
/project1/apps/app_typeB/Makefile
/project1/apps/app_typeC/Makefile

Each Makefile includes a .inc file in this path one level up:

/project1/apps/app_rules.inc

Inside app_rules.inc I'm setting the destination of where I want the binaries to be placed when built. I want all binaries to be in their respective app_type path:

/project1/bin/app_typeA/

I tried using $(CURDIR), like this:

OUTPUT_PATH = /project1/bin/$(CURDIR)

but instead I got the binaries buried in the entire path name like this: (notice the redundancy)

/project1/bin/projects/users/bob/project1/apps/app_typeA

What can I do to get the "current directory" of execution so that I can know just the app_typeX in order to put the binaries in their respective types folder?

kenorb
  • 155,785
  • 88
  • 678
  • 743
boltup_im_coding
  • 6,345
  • 6
  • 40
  • 52

14 Answers14

402

The shell function.

You can use shell function: current_dir = $(shell pwd). Or shell in combination with notdir, if you need not absolute path: current_dir = $(notdir $(shell pwd)).

Update.

Given solution only works when you are running make from the Makefile's current directory.
As @Flimm noted:

Note that this returns the current working directory, not the parent directory of the Makefile.
For example, if you run cd /; make -f /home/username/project/Makefile, the current_dir variable will be /, not /home/username/project/.

Code below will work for Makefiles invoked from any directory:

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
Bernardo Ramos
  • 4,048
  • 30
  • 28
Nikolai Popov
  • 5,397
  • 1
  • 17
  • 18
  • 6
    That does not work. It is the same problem. pwd prints the full path name, I just want the name of the actual current folder I'm in. – boltup_im_coding Aug 08 '13 at 22:13
  • @unexpected62 What about `notdir`? – Nikolai Popov Aug 08 '13 at 22:18
  • Thanks. I am a make noob. So what's going on here exactly? notdir is removing everything _through_ the last slash in the output of pwd? I saw https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html – boltup_im_coding Aug 08 '13 at 23:05
  • Yep, `notdir` does just that. – Nikolai Popov Aug 08 '13 at 23:22
  • 8
    It needs to have the value expanded immediately so the := operator should be used. As in mkfile_path := and current_dir = (otherwise the right side values could be evaluated later when the MAKEFILE_LIST has changed after other Makefiles were included – Tom Gruner Mar 22 '14 at 04:55
  • Note that a similar issue occurs is you use -C to specify the directory in which to make. The $PWD will be the directory you ran gmake from. – simpleuser Mar 31 '14 at 22:32
  • sad thing is that this doesn't work for AutoMake (Makefile.am files) :( – knocte May 18 '14 at 12:30
  • 17
    I know this is an old question but I just ran into and thought this could be useful. This also will not work when there is a space in the relative path of the makefile. Specifically, `$(lastword "$(MAKEFILE_LIST)")` of a makefile at path `"Sub Directory/Makefile"` will give you `"Directory/Makefile"`. I dont think there is any way around this since `$(MAKEFILE_LIST)` with two makefiles gives you a single string `"Makefile One Makefile Two`, which cannot handle spaces – mrosales Jan 24 '15 at 17:05
  • @mrosales I am having this same problem. You could get it from shell with `${PWD##*/} `, but `$(shell ${PWD##*/} )` complains, that `*** unterminated call to function `shell': missing `)'.` – fbence Nov 10 '15 at 11:15
  • See my variant answer for gnu make version 3.80, which doesn't have lastword, realpath or abspath. – Jesse Chisholm Jan 06 '16 at 23:03
  • these don't seem to work if there is a space in the path – fbence Dec 04 '17 at 12:11
  • I have created an answer that demonstrates this. https://stackoverflow.com/a/49740268/117471 – Bruno Bronosky Apr 09 '18 at 19:29
  • 1
    The updated methods doesn't work if there are spaces in the path to the Makefile. It's the `dir` that fails if there are spaces. That should be mentioned in the answer along with any possible workarounds. – CMCDragonkai Jun 25 '18 at 02:45
  • 7
    That `current_dir` doesn't work for me. `$(PWD)` does and it's much simpler. – CaTx Sep 19 '18 at 06:05
  • @mrosales @fbence GNU make, in general, doesn't really handle paths that have spaces in them. Something as simple as `SRCS` variable with all your source file names in it no longer works if a single file has a space in its name. – villapx Oct 31 '18 at 13:40
  • `current_dir` is not what I expected. `mkfile_dir := $(dir $(mkfile_path))` is what I expected: full path to the makefile directory. – Stéphane Gourichon Aug 30 '19 at 14:27
  • 11
    `"solution only works when you are running make from the Makefile's current directory"` is a mild way to say "not really working". I would put the latest piece at the top of the answer. – Victor Sergienko Jan 20 '20 at 23:47
  • @CMCDragonkai except that answer is abysmally wrong... any answer of PWD is wrong. And if you are going to be that incorrect use the built in CURDIR instead. – UpAndAdam Feb 25 '20 at 20:34
  • does not work - if you're including other files from `Makefile` - this will return the path of the last included file! – carbolymer Mar 21 '20 at 13:40
  • 1
    @carbolmyer: I use included makefiles extensively. I *think* that by using $(firstword...) instead of $(lastword...), this can be made to work, assuming the makefile you want is the first one you read. But I won't claim that this works in every situation. In particular, it would not work to find the directory for an intermediate 'include'd makefile. So the statement in my case is `current_dir=$(dir $(abspath $(firstword $(MAKEFILE_LIST))))` It is a little surprising that make doesn't have a built-in variable for the full path of the current makefile. – Mike Maxwell Jan 04 '21 at 18:10
  • 1
    Changing `mkfile_path` to `mkfile_path := $(lastword $(abspath $(MAKEFILE_LIST)))` works for me when the directory path contains spaces – devatherock Jan 30 '21 at 20:44
193

As taken from here;

ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))

Shows up as;

$ cd /home/user/

$ make -f test/Makefile 
/home/user/test

$ cd test; make Makefile 
/home/user/test
starball
  • 20,030
  • 7
  • 43
  • 238
SleepyCal
  • 5,739
  • 5
  • 33
  • 47
  • 5
    IMHO it would better to use internal make-function `dir` instead of `shell dirname` though it gets pathname with trailing `/` character. – Serge Roussak Jan 23 '15 at 15:13
  • 1
    @SergeRoussak Could you perhaps write a seperate answer explaining why? I don't know enough about make to give an informed opinion. – SleepyCal Jan 23 '15 at 16:11
  • 4
    because of reduction of dependancies on external tools. Ie what if there will alias of dirname command at some system?.. – Serge Roussak Jan 23 '15 at 20:27
  • 8
    This almost worked for me, but DIR had a leading whitespace, so a minor change made work perfectly: `DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))` – Thorsten Lorenz May 02 '15 at 21:28
  • Thanks @ThorstenLorenz, I've edited the answer to reflect this – SleepyCal May 04 '15 at 13:28
  • 13
    To reference the *current* makefile, the use of `$MAKEFILE_LIST` must precede any `include` operations ([docs](https://www.gnu.org/software/make/manual/html_node/Special-Variables.html#index-makefiles_002c-and-MAKEFILE_005fLIST-variable)). – Brent Bradburn Nov 11 '15 at 04:11
  • 4
    Should use quotes -- at least around the argument to `dirname` -- in case there are spaces in the path. – Brent Bradburn Nov 11 '15 at 04:21
  • The `:=` (vs plain `=`) is important if you have any `include` statements, since otherwise the included makefiles will come after the current makefile. – ntc2 Sep 14 '16 at 01:23
  • @SergeRoussak: `dirname` (and its cousin `basename`) are POSIX. I agree, "what if your system does not have `dirname`?" is a valid question, but so is "what if your system does not have `make`?" :-) Also, AFAIK, `$(realpath )` is a relatively recent, and GNU-make specific. So here is *more* dependency on tools for ya. But your `$(dir )` suggestion is a good one! – kkm inactive - support strike Apr 15 '19 at 05:19
  • Note that you need to enforce immediate variable expansion using the `:=` operator. Otherwise, you get funky behavior. – Richard Kiefer Nov 05 '19 at 12:09
  • 1
    This answer is susceptible to path splitting. If the path of the Makefile has a space in it then the ROOT_DIR will not contain the correct path. To resolve this the output of realpath must be surrounded by double quotes such as follows: `ROOT_DIR:=$(shell dirname "$(realpath $(firstword $(MAKEFILE_LIST)))")` – user7343148 Sep 14 '22 at 07:32
  • @user7343148 Feel free to submit an amendment, thanks – SleepyCal Oct 10 '22 at 18:24
74

If you are using GNU make, $(CURDIR) is actually a built-in variable. It is the location where the Makefile resides the current working directory, which is probably where the Makefile is, but not always.

OUTPUT_PATH = /project1/bin/$(notdir $(CURDIR))

See Appendix A Quick Reference in http://www.gnu.org/software/make/manual/make.html

Alastair Irvine
  • 1,166
  • 12
  • 16
Tzunghsing David Wong
  • 1,271
  • 14
  • 10
  • 24
    From the GNU Make manual (page 51): " when GNU make starts (after it has processed any -C options) it sets the variable CURDIR to the pathname of the current working directory." Not the location of where the Makefile is located - although, they might be the same. – Simon Peverett Apr 23 '14 at 07:31
  • 7
    Downvoted because the answer is not technically correct, as noted by Simon Peverett in the previous comment. – Subfuzion Dec 31 '14 at 02:20
  • 6
    @SimonPeverett: The expression in brackets means the the "current working directory AFTER -C opts applied". Namely $(CURDIR) gives the exactly the same results for "make -C xxx" and "cd xxx; make". Also it removes the trailing `/`. I tried tied with `gmake` v3.81. But You are right if `make` is called as `make -f xxx/Makefile`. – TrueY Apr 01 '15 at 09:41
  • 3
    `CURDIR` works for me where `PWD` did not. I did `mkdir -p a/s/d` then in each folder had a makefile which printed `PWD` and `CURDIR` before `+$(MAKE) `. – Mark K Cowan Mar 25 '17 at 22:46
  • This should be the correct answer. No need to reinvent the wheel. Make does it for you ootb. – user503582 Oct 06 '21 at 17:30
35
THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))
Lesmana
  • 25,663
  • 9
  • 82
  • 87
momo
  • 375
  • 3
  • 2
  • 17
    ... unless you have spaces in any paths. – Craig Ringer Mar 16 '15 at 11:56
  • 13
    ... unless you have included some other makefile in previous line – robal Oct 09 '15 at 16:56
  • 1
    @robal Yep. That is what I just encountered. This works _great_ if you only have two Makefiles (the main one plus an included one). – sherrellbc Apr 20 '18 at 21:38
  • This works well for me (haven't tested spaces in dirs, but I don't expect to encounter this). The main issue I had with this code is that `$(dir)` retains the last path separator. Adding `$(realpath)` before it solves that problem. – Guss Jul 08 '20 at 19:07
  • To make more details to what @robal wrote, you should use `lastword` and not `firstword`, while for simple Makefile this "array" is of size one so choosing the first element is equivalent to choosing the last element, but in order to have the path of the directory where this line is written you would like to pick the last name from that space delimited list. – Yogev Neumann Jul 02 '23 at 16:00
17

The simple, correct, modern way:

For GNU make >= 3.81, which was introduced in 2006

ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
  1. MAKEFILE_LIST changes as include files come in and out of scope. The last item is the current file.

  2. lastword plucks the last item (Makefile name, relative to pwd)

  3. realpath is built-in to make, and resolves to a canonical path from filesystem root

  4. dir trims off the filename, leaving just the directory.

Stabledog
  • 3,110
  • 2
  • 32
  • 43
  • 2
    This is the best solution I found so far. This code should be placed in the very beginning of each Makefile if you use `include` and need relative path in each of them. Yes, it does not work with spaces but none of answers using `MAKEFILE_LIST` will not work with spaces, just because `make` uses space as delimiter in `MAKEFILE_LIST`, and as a result you never know whether this space is delimiter or part of file path or file name. That makes me sad( – kdmitry Nov 11 '22 at 17:39
11

I like the chosen answer, but I think it would be more helpful to actually show it working than explain it.

/tmp/makefile_path_test.sh

#!/bin/bash -eu

# Create a testing dir
temp_dir=/tmp/makefile_path_test
proj_dir=$temp_dir/dir1/dir2/dir3
mkdir -p $proj_dir

# Create the Makefile in $proj_dir
# (Because of this, $proj_dir is what $(path) should evaluate to.)
cat > $proj_dir/Makefile <<'EOF'
path := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
cwd  := $(shell pwd)

all:
    @echo "MAKEFILE_LIST: $(MAKEFILE_LIST)"
    @echo "         path: $(path)"
    @echo "          cwd: $(cwd)"
    @echo ""
EOF

# See/debug each command
set -x

# Test using the Makefile in the current directory
cd $proj_dir
make

# Test passing a Makefile
cd $temp_dir
make -f $proj_dir/Makefile

# Cleanup
rm -rf $temp_dir

Output:

+ cd /tmp/makefile_path_test/dir1/dir2/dir3
+ make
MAKEFILE_LIST:  Makefile
         path: /private/tmp/makefile_path_test/dir1/dir2/dir3
          cwd: /tmp/makefile_path_test/dir1/dir2/dir3

+ cd /tmp/makefile_path_test
+ make -f /tmp/makefile_path_test/dir1/dir2/dir3/Makefile
MAKEFILE_LIST:  /tmp/makefile_path_test/dir1/dir2/dir3/Makefile
         path: /tmp/makefile_path_test/dir1/dir2/dir3
          cwd: /tmp/makefile_path_test

+ rm -rf /tmp/makefile_path_test

NOTE: The function $(patsubst %/,%,[path/goes/here/]) is used to strip the trailing slash.

Bruno Bronosky
  • 66,273
  • 12
  • 162
  • 149
8

I tried many of these answers, but on my AIX system with gnu make 3.80 I needed to do some things old school.

Turns out that lastword, abspath and realpath were not added until 3.81. :(

mkfile_path := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
mkfile_dir:=$(shell cd $(shell dirname $(mkfile_path)); pwd)
current_dir:=$(notdir $(mkfile_dir))

As others have said, not the most elegant as it invokes a shell twice, and it still has the spaces issues.

But as I don't have any spaces in my paths, it works for me regardless of how I started make:

  • make -f ../wherever/makefile
  • make -C ../wherever
  • make -C ~/wherever
  • cd ../wherever; make

All give me wherever for current_dir and the absolute path to wherever for mkfile_dir.

Jesse Chisholm
  • 3,857
  • 1
  • 35
  • 29
  • 1
    I, for one, have compiled gnu-make-3.82 on aix (5-6-7) without problems. – Zsigmond Lőrinczy Jan 07 '16 at 06:08
  • 1
    Yes, by 3.81 the needed functions for the earlier solutions were implemented. My answer is for older systems with 3.80 or earlier. Sadly, at work I am not allowed to add or upgrade tools to the AIX system. :( – Jesse Chisholm Jan 08 '16 at 13:01
2

Here is one-liner to get absolute path to your Makefile file using shell syntax:

SHELL := /bin/bash
CWD := $(shell cd -P -- '$(shell dirname -- "$0")' && pwd -P)

And here is version without shell based on @0xff answer:

CWD := $(abspath $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))))

Test it by printing it, like:

cwd:
        @echo $(CWD)
kenorb
  • 155,785
  • 88
  • 678
  • 743
  • 1
    Critical Importance! The second example above needs the := assignment operator or else some very weird and angry things can happen with complex makefiles (trust me) – EMon Mar 20 '19 at 02:49
  • 2
    First one is a repeated error and doesn't work for out-of-tree-builds. – Johan Boulé Apr 07 '19 at 02:26
  • Unfortunately, `CWD := $(shell cd -P -- '$(shell dirname -- "$0")' && pwd -P)` always returns the directory from which you executed `make` and it may differ from the directory of Makefile. Such approach would work for shell scripting but not in `make`. – kdmitry Nov 11 '22 at 17:30
1

As far as I'm aware this is the only answer here that works correctly with spaces:

space:= 
space+=

CURRENT_PATH := $(subst $(lastword $(notdir $(MAKEFILE_LIST))),,$(subst $(space),\$(space),$(shell realpath '$(strip $(MAKEFILE_LIST))')))

It essentially works by escaping space characters by substituting ' ' for '\ ' which allows Make to parse it correctly, and then it removes the filename of the makefile in MAKEFILE_LIST by doing another substitution so you're left with the directory that makefile is in. Not exactly the most compact thing in the world but it does work.

You'll end up with something like this where all the spaces are escaped:

$(info CURRENT_PATH = $(CURRENT_PATH))

CURRENT_PATH = /mnt/c/Users/foobar/gDrive/P\ roje\ cts/we\ b/sitecompiler/
Peter Frost
  • 351
  • 3
  • 6
1

use {} instead of ()

cur_dir=${shell pwd}
parent_dir=${shell dirname ${shell pwd}}}
Jackiexiao
  • 832
  • 9
  • 15
0

Example for your reference, as below:

The folder structure might be as:

enter image description here

Where there are two Makefiles, each as below;

sample/Makefile
test/Makefile

Now, let us see the content of the Makefiles.

sample/Makefile

export ROOT_DIR=${PWD}

all:
    echo ${ROOT_DIR}
    $(MAKE) -C test

test/Makefile

all:
    echo ${ROOT_DIR}
    echo "make test ends here !"

Now, execute the sample/Makefile, as;

cd sample
make

OUTPUT:

echo /home/symphony/sample
/home/symphony/sample
make -C test
make[1]: Entering directory `/home/symphony/sample/test'
echo /home/symphony/sample
/home/symphony/sample
echo "make test ends here !"
make test ends here !
make[1]: Leaving directory `/home/symphony/sample/test'

Explanation, would be that the parent/home directory can be stored in the environment-flag, and can be exported, so that it can be used in all the sub-directory makefiles.

parasrish
  • 3,864
  • 26
  • 32
  • 2
    This gets the current working directory, not the makefile's home directory. – Jesse Chisholm Jan 08 '16 at 13:01
  • These are the lines in the Makefile. Now, as the Makefile command executes, this will get the path at which the Makefile is. I understand "makefile home directory" is refering to the same, i.e. the directory-path in which Makefile is executing. – parasrish Jan 08 '16 at 18:34
  • @Jesse Chisholm please revisit. I have explained with usage. – parasrish Jan 18 '16 at 14:54
  • A repeated error: it doesn't work for out-of-tree-builds. Also, your gnome terminal provides a simple way to copy text: just select it. The rumor has Imgur is brankrupt. – Johan Boulé Apr 07 '19 at 02:34
-1

Solution found here : https://sourceforge.net/p/ipt-netflow/bugs-requests-patches/53/

The solution is : $(CURDIR)

You can use it like that :

CUR_DIR = $(CURDIR)

## Start :
start:
    cd $(CUR_DIR)/path_to_folder
Soma Bini
  • 103
  • 1
  • 3
  • 3
    Welcome to Stack Overflow. Can you add more detail to your answer? The question states that `$(CURDIR)` was already tried unsuccessfully. – Sean Jul 18 '19 at 15:28
  • 12
    WARNING, quick-googlers: This is *not* the directory, where the makefile is, but the current *working* directory (where `make` was launched in). It is, indeed, what OP really wanted, but the question title is bogus, so the question itself is inconsistent. So, this is a correct answer to an incorrect question. ;) – Sz. Aug 21 '19 at 18:50
  • 1
    This is flat out wrong and a redundant wrong answer – UpAndAdam Feb 25 '20 at 20:29
-2

update 2018/03/05 finnaly I use this:


shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

It can be executed correct in relative path or absolute path. Executed correct invoked by crontab. Executed correct in other shell.

show example, a.sh print self path.

[root@izbp1a7wyzv7b5hitowq2yz /]# more /root/test/a.sh
shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

echo $shellPath
[root@izbp1a7wyzv7b5hitowq2yz /]# more /root/b.sh
shellPath=`echo $PWD/``echo ${0%/*}`

# process absolute path
shellPath1=`echo $PWD/`
shellPath2=`echo ${0%/*}`
if [ ${shellPath2:0:1} == '/' ] ; then
    shellPath=${shellPath2}
fi

$shellPath/test/a.sh
[root@izbp1a7wyzv7b5hitowq2yz /]# ~/b.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz /]# /root/b.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz /]# cd ~
[root@izbp1a7wyzv7b5hitowq2yz ~]# ./b.sh
/root/./test
[root@izbp1a7wyzv7b5hitowq2yz ~]# test/a.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz ~]# cd test
[root@izbp1a7wyzv7b5hitowq2yz test]# ./a.sh
/root/test/.
[root@izbp1a7wyzv7b5hitowq2yz test]# cd /
[root@izbp1a7wyzv7b5hitowq2yz /]# /root/test/a.sh
/root/test
[root@izbp1a7wyzv7b5hitowq2yz /]# 

old: I use this:

MAKEFILE_PATH := $(PWD)/$({0%/*})

It can show correct if executed in other shell and other directory.

zhukunqian
  • 103
  • 4
-3

One line in the Makefile should be enough:

DIR := $(notdir $(CURDIR))

Thach Van
  • 1,381
  • 16
  • 19