10

I have a project with the following directory structure:

foo/
foo/Makefile
foo/bar/

My goal is to add a file foo/bar/Makefile, so that if I invoke make in foo/bar, the Makefile of the parent directory will be used. It's kind of an inverted recursive Makefile structure (I want every Makefile to invoke the parent directory until it reaches the project root).

Symlinking fails since invoking it from the wrong directory breaks all relative paths:

$ ln -s ../Makefile foo/bar/Makefile
$ make -C foo/bar
[error]

The only solution I have found is to write a Makefile which captures all of the targets that I am interested in and invokes the parent directory with that target:

$ cat foo/bar/Makefile
all clean:
    $(MAKE) -C .. $@

This is laborious and prone to break. Is there a more elegant solution?

Justification

The project is a large LaTeX document which contains multiple subdirectories for .tex files for Chapters, Figures, etc. I want to be able to invoke make from any one of these directories to build the document.

Chris Cummins
  • 935
  • 1
  • 8
  • 12
  • 1
    If you feel that your solution is "laborious and prone to break", why not have it `$(MAKE) -C /path/to/project/root $@`? That way you have *one* recursive call to Make rather than several, and one broken makefile doesn't break all those below it in the tree. – Beta Nov 28 '14 at 12:54
  • 1
    You don't need to manually indicate the targets you want you can use a [match-anything rule](http://www.gnu.org/software/make/manual/make.html#index-match_002danything-rule). [This article](http://make.mad-scientist.net/papers/multi-architecture-builds/) discusses doing that (among many other things). – Etan Reisner Nov 28 '14 at 13:15

4 Answers4

7

Thanks Etan Reisner for point out the match-anything rule. This Makefile will capture all targets and pass them up to the parent Makefile:

all:
    @make -C .. $@
%:
    @make -C .. $@

Note that it was necessary to provide a default all rule in addition to the match-anything rule, as otherwise make would complain if invoked with no arguments: make: *** No targets. Stop.

Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
Chris Cummins
  • 935
  • 1
  • 8
  • 12
5

Building on a previous answer by @chris-cummins, it would be preferable to use $(MAKE) instead of @make.

This would preserve, among other things, parallel builds (-j XX), otherwise you would leave a lot of performance on the table.

The final version would be (tested on GNU Make 4.2.1):

all:
    $(MAKE) -C .. $@
%:
    $(MAKE) -C .. $@

For an explanation, see this answer

muxator
  • 348
  • 3
  • 8
2

You could create a custom function to run make in the right folder:

make-foo() {
    make -C /absolute/path/to/foo "$@"
}
Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
l0b0
  • 55,365
  • 30
  • 138
  • 223
  • This works, thanks. I'm ideally after a make-only solution though, since I don't want to modify my shell environment just for a single project. This can also be achieved with an alias: `alias make-foo='make -C /abs/path/to/foo'` – Chris Cummins Nov 28 '14 at 12:49
1

You should not need multiple Makefiles and recursive call to Make here. Recursive Make is bad in itself and should not be used unless you understand exactly its limitations.

GNU Make functionality depends on its environment. Therefore, one should always (in a serious system, not a toy) control the environment and wrap Make in a wrapper shell script that sets up the environment then calls Make (once, from the top). Your desire for "make-only" is misguided.

In your case, your wrapper script, goes into a loop, where it looks for a file called Makefile and if not found, cd to parent directory and repeat. Once found, executes Make with the arguments given.

In my practice I also used a more elaborate version of the above, where the wrapper script studies the arguments given, and for those that are goals, prepends them with the path to the subdirectory that it started from. So that

subdir>make foobar

is equivalent to

>make subdir/foobar
Mark Galeck
  • 6,155
  • 1
  • 28
  • 55
  • 1
    Hi Mark, I appreciate your answer but respectfully disagree about recursive make. Recursive make is not bad in itself, and none of the usual criticisms about it apply in this simple case. All Makefiles apart from the top-level are simple wrappers, and will not inject anything into the environment. – Chris Cummins Dec 01 '14 at 00:30
  • @ChrisCummins no need to disagree, it sounds like, you do understand recursive Make limitations, then, like I said, it's OK to use it, if you prefer it to a wrapper script. – Mark Galeck Dec 01 '14 at 18:28