51

I'm trying to consolidate some build information by using a common makefile. My problem is that I want to use that makefile from different subdirectory levels, which makes the working directory value (pwd) unpredictable. For example:

# Makefile.common
TOP := $(shell pwd)
COMPONENT_DIR := $(TOP)/component
COMPONENT_INC := $(COMPONENT_DIR)/include
COMPONENT_LIB := $(COMPONENT_DIR)/libcomponent.a

If I include Makefile.common from a subdirectory, like so, the $(TOP) directory is incorrect and everything else follows suit:

# other_component/Makefile
include ../Makefile.common
# $(COMPONENT_LIB) is incorrectly other_component/component

What's the best way to get Makefile.common to use its own directory path instead of the more fickle pwd?

cdleary
  • 69,512
  • 53
  • 163
  • 191

5 Answers5

77

You should be able to use the MAKEFILE_LIST variable, like this:

# This must be the first line in Makefile.common
TOP := $(dir $(firstword $(MAKEFILE_LIST)))

From the documentation:

As make reads various makefiles, including any obtained from the MAKEFILES variable, the command line, the default files, or from include directives, their names will be automatically appended to the MAKEFILE_LIST variable. They are added right before make begins to parse them. This means that if the first thing a makefile does is examine the last word in this variable, it will be the name of the current makefile. Once the current makefile has used include, however, the last word will be the just-included makefile.

Lesmana
  • 25,663
  • 9
  • 82
  • 87
JesperE
  • 63,317
  • 21
  • 138
  • 197
  • 1
    This worked wonderfully on my dev box, but I realized the target machine was running make 3.79, and the MAKEFILE_LIST feature was added in make 3.80: http://www.mail-archive.com/help-make@gnu.org/msg05902.html – cdleary Dec 12 '08 at 01:52
  • 1
    It seems MAKEFILE_LIST doesn't contain full paths, only relative paths. – kbyrd Feb 11 '10 at 14:31
  • 16
    Much after the fact, but if it helps anyone: Here's a snippet that'll give you an absolute path, but doesn't depend on realpath (based on this answer and [this blog post](http://blog.jgc.org/2007/01/what-makefile-am-i-in.html)): `TOP := $(dir $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))` – Xavier Holt Feb 14 '12 at 19:26
  • 6
    Xavier, you should post that as an answer, not a comment. After the fact is fine (and even encouraged), this is a living site. – Frank Szczerba Mar 15 '12 at 17:41
  • 6
    This is what has worked well for me to give absolute path: `TOP := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))`. `$(abspath ...)` was introduced in GNU make 3.81 (April 1, 2006) at the same time as `$(lastword ...)` according to my quick web search. Xavier Holt's solution is suitable for older versions of GNU make. – FooF Sep 24 '12 at 08:52
  • I'm confused. The answer uses `firstword`, but would that not give the original makefile? Shouldn't it use `lastword` to get the *current* (included) makefile as asked by the original poster? – Matthijs Kooijman Oct 01 '21 at 09:26
16

Try this:

ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))

Edit: Be sure to use := instead of = because the latter causes make to use late-binding and MAKEFILE_LIST may have changed due to later includes.

Stabledog
  • 3,110
  • 2
  • 32
  • 43
Bill
  • 161
  • 1
  • 2
  • I should note that this builds on JesperE's example form above. However, it provides an absolute path to build on top of. – Bill Mar 30 '10 at 19:15
  • +1 This solution is perfect and portable. I've successfully tested it on UNIXes and Windows/Cygwin. Note also the [VPATH](http://www.gnu.org/software/make/manual/make.html#General-Search) variable/directive. – Andreas Spindler Jul 31 '12 at 09:40
  • 3
    It looks to me like `realpath` is a builtin of `make` -- not a dependency on an installed binary ([docs](https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html#index-realpath)). – Brent Bradburn Nov 11 '15 at 04:14
  • 1
    [`realpath`](https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html) as used here is a gnu make builtin function. Also, instead of `shell dirname`, probably cleaner to use `dir` builtin. – akhan Oct 14 '19 at 18:14
  • 1
    Edited the answer to remove the confusion about `realpath`: this works, there's no need to be concerned about whether realpath is installed on the host system -- see also comments from @BrentBradburn and @akhan. `ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))` – Stabledog Aug 27 '22 at 09:47
2

write the common stuff in common.mk. Then put the common.mk in the default directories that Make looks for when it encounters an include statement. See the manual for common directories Make looks for.

You could also put the common.mk in custom directory, and then type make -I customdir.

Inside the Makefile in each subfolder, you do

include common.mk

That is all. No need to worry about path and moving things around.

Lesmana
  • 25,663
  • 9
  • 82
  • 87
Robert H
  • 1,603
  • 2
  • 17
  • 19
2

My solution:

cwd  :=  $(shell readlink -en $(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))) 

This also works for calls like make -f /opt/some/dir/Makefile whenn your in /opt/other/path/subdir.

MegaSoft
  • 21
  • 3
2

Have you tried doing:

# Makefile.common
TOP ?= $(shell pwd)
COMPONENT_DIR := $(TOP)/component
COMPONENT_INC := $(COMPONENT_DIR)/include
COMPONENT_LIB := $(COMPONENT_DIR)/libcomponent.a

# other_component/Makefile
TOP ?= ..
include ../Makefile.common

Using the ?= construct will keep TOP from being redefined if it is already set. You can set it to the appropriate value based on where you are in the tree when you invoke make. I confess it's been awhile since I've used GNU make so this may not work or may need some tweaks.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • Agreed, that would work. I'm hoping there's a way to do it that doesn't require preparation on the part of the makefile (other than inclusion), but that's certainly a good fallback. – cdleary Nov 27 '08 at 04:53