How to get current relative directory of your Makefile? - 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?

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

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

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

THIS_DIR := $(dir $(abspath $(firstword $(MAKEFILE_LIST))))

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.

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.

The simple, correct, modern way:
For GNU make >= 3.81, which was introduced in 2006
ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
MAKEFILE_LIST changes as include files come in and out of scope. The last item is the current file.
lastword plucks the last item (Makefile name, relative to pwd)
realpath is built-in to make, and resolves to a canonical path from filesystem root
dir trims off the filename, leaving just the directory.

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)

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/

Example for your reference, as below:
The folder structure might be as:
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.

use {} instead of ()
cur_dir=${shell pwd}
parent_dir=${shell dirname ${shell pwd}}}

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

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.

One line in the Makefile should be enough:
DIR := $(notdir $(CURDIR))

Related

How can I build two interactive web-sites from one GNU makefile and mostly the same source?

I'm using GNU Make to build a dynamic web site but I need to build two versions. As a net result currently I run my makefile using two command line incantations. This is inefficient and can result in errors (I don't always remember to run both versions or make a typing error and they fail) thus I want to make it one build.
The Command Line incantations are:
sudo make release build=test
sudo make release build=release
The two incantations activate ifeq blocks that set the path and modify some files.
Part of the much simplified (to help readability) top level makefile:
subs = today tomorrow
files = index.php foot.php
ifeq ($(build),test)
export path = /var/www/html/cgi-test
$(shell cp -f head-test.php head.php)
$(shell sed -i '/"DB"/c\ define("DB", "db_test");' subs.php)
else ifeq ($(build),release)
export path = /var/www/html/cgi-new
$(shell cp -f head-release.php head.php)
$(shell sed -i '/"DB"/c\ define("DB", "db_new");' subs.php)
endif
.PHONY: all go
all:
$(MAKE) go
#for ALL in $(subs);\
do $(MAKE) -C $$ALL all || exit $$?;\
done;
go:
cp $(files) $(path)/.
The sub-makefiles have a very similar structure but don't need the ifeq blocks because the files and paths have already been setup.
I think I can simply move the shell commands into the .PHONY rules but I can't do that with the exports because I get errors "export: : bad variable name".
I could do it with a batch file and call the makefile twice but that sidesteps the problem rather than cures it and I wish to learn from the process.
So can anybody show me the way to do this in a makefile?
Thanks to Tripleee here is the answer that finally worked back ported to match my starting post. The one major change is that I have gone back to 'all' as the rule I expect to start the build habits die hard! - Thanks
.PHONY: all both rel-test rel-release
cgi-test := cgi-test
db-test := db_test
cgi-release := cgi-new
db-release := db_new
subs = today tomorrow
files = index.php foot.php
all: both
both: rel-test rel-release
rel-test rel-release: rel-%:
cp -f head-$*.php head.php
sed -i '/"DB"/c\ define("DB", "$(db-$*)");' subs.php
$(MAKE) go path=/var/www/html/strutts/$(cgi-$*)
#for ALL in $(subs);\
do $(MAKE) build=$* path=/var/www/html/strutts/$(cgi-$*) -C $$ALL all || exit $$?;\
done;
Something like this?
.PHONY: both rel-test rel-release
both: rel-test rel-release
cgi-test := cgi-test
db-test := db_test
cgi-release := cgi-new
db-release := db_new
rel-%:
cp -f head-$*.php head.php
sed -i '/"DB"/c\ define("DB", "$(db-$*)")' subs.php
$(MAKE) release build=$* path=/var/www/html/$(cgi-$*)
The reason the export can't be moved into a recipe is that you are using the export command of make itself, not the shell's command with the same name.
You absolutely should not use sudo unless you specifically require the output files to be owned and only writable by root. Even then, running as much as possible as a regular user would be proper hygiene; maybe add a sudo command inside the Makefile to copy the files to their final location.

How to enter a parent directory in Makefile?

I've got the following Makefile (GNU Make 3.81):
CWD:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
all:
pwd
cd "$(CWD)/.."
pwd
cd ".."
pwd
Here is the output:
$ make
pwd
/Users/kenorb/temp/foo
cd "/Users/kenorb/temp/foo/.."
pwd
/Users/kenorb/temp/foo
cd ".."
pwd
/Users/kenorb/temp/foo
It seems that cd'ing to parent directory via .. doesn't take any effect.
How do I change the current working directory to a parent directory relatively to Makefile file?
This issue has to do with the fact that each line of a rule's recipe is executed in a dedicated shell instance (i.e.: a new process). So, running cd in one line, won't have any effect for a command in another line, because these commands are executed by different processes.
Changing the working directory for a recipe
You can either use .ONESHELL (GNU Make 3.82 or later) to run all the recipe's lines in a single and the same shell instance:
CWD:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
.ONESHELL:
all:
pwd
cd "$(CWD)/.."
pwd
cd ".."
pwd
or just keep all the commands in a single line:
CWD:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
all:
pwd; cd "$(CWD)/.."; pwd; cd ".."; pwd
The change of the working directory takes effect only inside that recipe.
Note that changing the current working directory inside the makefile for the shell that invoked make is not possible, since that's a process' property, and the running make instance that will execute cd is another (sub)process. Therefore, it will only change the working directory of that subprocess.
That is the same reason you cannot change the working directory of a shell by executing a script without sourcing it: because the script runs in a new shell instance (i.e.: a subprocess).

Makefile error cd can't cd

I'm new in makefiles and I'm trying to change directories inside makefile, my problem is that I'm not able to change those directories and I'm receiving this message : /bin/sh:1: cd can't cd to /home/.......
my code is :
# !/bin/sh
CHECK_LIB = true`
DIR= /home/me/dir/
bootstrap:
ifeq($(CHECK_LIB), true)
cd $(DIR); ./bootstarp -c ;
endif
also I have tried $(shell cd $(DIR) ; ls )
without any luck.
Has some a good opinion how should I do/try ? os: ubuntu 12.04
Tks!
First of all you don't need /bin/sh on top, since Makefile is not a shell script, then, rules are written prepended by TAB. However, conditionals are not part of rules, so, they don't need to be prepended with TAB. So try this:
CHECK_LIB = true
DIR = /home/me/dir/
bootstrap:
ifeq($(CHECK_LIB), true)
<TAB>cd $(DIR) && ./bootstarp -c
endif
You can also use make shell command but not in rule:
ifeq($(CHECK_LIB), true)
BOOTSTRAP_OUT := $(shell cd $(DIR) && ./bootstarp -c)
endif
but I guess it's not what you want since it'll be executed before any rule and not considering any dependency. And this way you can grab the list in of files/folders in your makefile:
FILELIST := $(shell cd $(DIR) && ls)
$(warning FILELIST is $(FILELIST))

Directory wildcard in Makefile pattern rule

I'm trying to create a Makefile that will compile terminfo files residing in a directory via tic. tic also copies the termcap files it creates automatically to a system- or user-specific destination folder. For a regular user if the terminfo file is e.g. screen-256color-bce-s.terminfo, it will be compiled and copied to ~/.terminfo/s/screen-256color-bce-s. So it will look something like this:
terminfo/screen-256color-bce-s.terminfo => /home/user/.terminfo/s/screen-256color-bce-s
terminfo/screen-256color-s.terminfo => /home/user/.terminfo/s/screen-256color-s
If I put something like this into my Makefile:
TISRC = $(wildcard terminfo/*.terminfo)
TIDST = $(foreach x, $(TISRC), $(HOME)/.terminfo/$(shell basename $x|cut -c 1)/$(shell basename $x .terminfo))
$(HOME)/.terminfo/s/%: terminfo/%.terminfo
#echo "$< => $#"
#tic $<
install: $(TIDST)
it works. However, I'd like to make it general, and use a wildcard in the target, i.e.:
$(HOME)/.terminfo/**/%: terminfo/%.terminfo
#echo "$< => $#"
#tic $<
to be able to add terminfo files to my local repository. The above, however, does not work. How can I specify a wildcard directory in a pattern rule?
You can do that with GNU Make Secondary Expansion feature:
all : ${HOME}/.terminfo/x/a
all : ${HOME}/.terminfo/y/b
.SECONDEXPANSION:
${HOME}/.terminfo/%: terminfo/$$(notdir $$*).terminfo
#echo "$< ---> $#"
Output:
[~/tmp] $ make
terminfo/a.terminfo ---> /home/max/.terminfo/x/a
terminfo/b.terminfo ---> /home/max/.terminfo/y/b
As a side note, make provides some path manipulation functions, so that you don't really need to invoke the shell for that.
I don't think you can use wildcards the way you're trying to, but if you don't mind using eval trickery, you can get the effect you're shooting for without having to spell out all the directory paths explicitly:
TISRC = $(wildcard terminfo/*.terminfo)
BASENAMES = $(notdir $(basename ${TISRC}))
MKDST = ${HOME}/.terminfo/$(shell echo $1 | cut -c 1)/$1
TIDST := $(foreach s,${BASENAMES},$(call MKDST,$s))
DIRLTRS = $(notdir $(patsubst %/,%,$(sort $(dir ${TIDST}))))
install: ${TIDST}
# $1 - Directory Name
# $2 - File name
define T
${HOME}/.terminfo/$1/$2 : terminfo/$2.terminfo
#echo "$$< => $$#"
tic $$<
endef
# This is the tricky part: use template T to make the rules you need.
$(foreach d,${DIRLTRS},$(foreach f,${BASENAMES},$(eval $(call T,$d,$f))))

Define make variable at rule execution time

In my GNUmakefile, I would like to have a rule that uses a temporary directory. For example:
out.tar: TMP := $(shell mktemp -d)
echo hi $(TMP)/hi.txt
tar -C $(TMP) cf $# .
rm -rf $(TMP)
As written, the above rule creates the temporary directory at the time that the rule is parsed. This means that, even I don't make out.tar all the time, many temporary directories get created. I would like to avoid my /tmp being littered with unused temporary directories.
Is there a way to cause the variable to only be defined when the rule is fired, as opposed to whenever it is defined?
My main thought is to dump the mktemp and tar into a shell script but that seems somewhat unsightly.
In your example, the TMP variable is set (and the temporary directory created) whenever the rules for out.tar are evaluated. In order to create the directory only when out.tar is actually fired, you need to move the directory creation down into the steps:
out.tar :
$(eval TMP := $(shell mktemp -d))
#echo hi $(TMP)/hi.txt
tar -C $(TMP) cf $# .
rm -rf $(TMP)
The eval function evaluates a string as if it had been typed into the makefile manually. In this case, it sets the TMP variable to the result of the shell function call.
edit (in response to comments):
To create a unique variable, you could do the following:
out.tar :
$(eval $#_TMP := $(shell mktemp -d))
#echo hi $($#_TMP)/hi.txt
tar -C $($#_TMP) cf $# .
rm -rf $($#_TMP)
This would prepend the name of the target (out.tar, in this case) to the variable, producing a variable with the name out.tar_TMP. Hopefully, that is enough to prevent conflicts.
A relatively easy way of doing this is to write the entire sequence as a shell script.
out.tar:
set -e ;\
TMP=$$(mktemp -d) ;\
echo hi $$TMP/hi.txt ;\
tar -C $$TMP cf $# . ;\
rm -rf $$TMP ;\
I have consolidated some related tips here: Multi-line bash commands in makefile
Another possibility is to use separate lines to set up Make variables when a rule fires.
For example, here is a makefile with two rules. If a rule fires, it creates a temp dir and sets TMP to the temp dir name.
PHONY = ruleA ruleB display
all: ruleA
ruleA: TMP = $(shell mktemp -d testruleA_XXXX)
ruleA: display
ruleB: TMP = $(shell mktemp -d testruleB_XXXX)
ruleB: display
display:
echo ${TMP}
Running the code produces the expected result:
$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile testruleB_Y4Ow
I dislike "Don't" answers, but... don't.
make's variables are global and are supposed to be evaluated during makefile's "parsing" stage, not during execution stage.
In this case, as long as the variable local to a single target, follow #nobar's answer and make it a shell variable.
Target-specific variables, too, are considered harmful by other make implementations: kati, Mozilla pymake. Because of them, a target can be built differently depending on if it's built standalone, or as a dependency of a parent target with a target-specific variable. And you won't know which way it was, because you don't know what is already built.

Resources