ChatGPT解决这个技术问题 Extra ChatGPT

How to get current relative directory of your Makefile?

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?


B
Bernardo Ramos

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))))

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.
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
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
That current_dir doesn't work for me. $(PWD) does and it's much simpler.
"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.
L
Lesmana

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

Hope this helps


IMHO it would better to use internal make-function dir instead of shell dirname though it gets pathname with trailing / character.
because of reduction of dependancies on external tools. Ie what if there will alias of dirname command at some system?..
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)))))
To reference the current makefile, the use of $MAKEFILE_LIST must precede any include operations (docs).
Should use quotes -- at least around the argument to dirname -- in case there are spaces in the path.
A
Alastair Irvine

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


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.
Downvoted because the answer is not technically correct, as noted by Simon Peverett in the previous comment.
@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.
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) <subdir>.
This should be the correct answer. No need to reinvent the wheel. Make does it for you ootb.
L
Lesmana
THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))

... unless you have spaces in any paths.
... unless you have included some other makefile in previous line
@robal Yep. That is what I just encountered. This works great if you only have two Makefiles (the main one plus an included one).
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.
B
Bruno Bronosky

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.


J
Jesse Chisholm

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.


I, for one, have compiled gnu-make-3.82 on aix (5-6-7) without problems.
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. :(
k
kenorb

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)

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)
First one is a repeated error and doesn't work for out-of-tree-builds.
p
parasrish

Example for your reference, as below:

The folder structure might be as:

https://i.stack.imgur.com/1vdmj.png

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.


This gets the current working directory, not the makefile's home directory.
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.
@Jesse Chisholm please revisit. I have explained with usage.
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.
S
Soma Bini

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

Welcome to Stack Overflow. Can you add more detail to your answer? The question states that $(CURDIR) was already tried unsuccessfully.
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. ;)
This is flat out wrong and a redundant wrong answer
P
Peter Frost

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/

z
zhukunqian

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.


"It can show correct if executed if other shell and other directory" doesn't make sense in English. Can you try explaining again?
sorry for my poor english
Thanks for the edit, I see you meant IN now. Can you explain what this does before I try it? I'm not sure how to use it. Like what do you mean by "other shell"
This answer has so many wrong things it can't possibly be fixed. I suggest a simple removal.
T
Thach Van

One line in the Makefile should be enough:

DIR := $(notdir $(CURDIR))


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now