1

I am writing up a Makefile and I would like to separate the build and source directories. My initial thought was just “Oh, I can write my rules prepending the source path to the filename and be done with it.”

The problem is that this solution would be super repetitive and doesn't look very nice. What would be the best approach for writing myself a Makefile that stores targets in a directory distinct from the directory holding my sources.

Michaël Le Barbier
  • 6,103
  • 5
  • 28
  • 57
csteifel
  • 2,854
  • 6
  • 35
  • 61
  • Use variables and `vpath`. Does it work yet? Can you post a representative example? We can show you how to tidy up a makefile, but it's harder if we have to guess at what you're trying to do. – Beta Aug 08 '13 at 02:15
  • you could use foreach to append the directory path to respective collection of file names .... Its stupid , but is simple and works just fine – Nitin4873 Aug 08 '13 at 08:14
  • @Beta see thats the thing is I haven't written my makefile yet because I would like to start off properly. My directory structure would ideally be `./src/` `./build/` and `./tests/` – csteifel Aug 10 '13 at 19:42
  • Where do you want to run Make? I presume you want to put sources and headers in `./src/`, and build objects and executables in `./build/`. What about `./tests/`? – Beta Aug 10 '13 at 22:22
  • I don't particularly care where I call make. I am writing a library so `build` would be the actual library and `tests` would be like unit tests and what not for the library. I really just want to follow typical convention – csteifel Aug 11 '13 at 03:30

1 Answers1

1

The program has a very good support for storing the built targets in a directory distinct form the one holding the sources. A Makefile using this functionality might be a bit harder to write, so I will review the most important techniques in my answer.

Choosing a directory to store built targets

The built-in variable .OBJDIR contains the name of a directory where targets are built. It is determined like this (excerpt from the man page):

 .OBJDIR         A path to the directory where the targets are built.  Its
                 value is determined by trying to chdir(2) to the follow‐
                 ing directories in order and using the first match:

                 1.   ${MAKEOBJDIRPREFIX}${.CURDIR}

                      (Only if ‘MAKEOBJDIRPREFIX’ is set in the environ‐
                      ment or on the command line.)

                 2.   ${MAKEOBJDIR}

                 …

                 6.   ${.CURDIR}

The program make will chdir(2)to .OBJDIR and set PWD to that directory before executing any targets. In the absence of any adequate preparation .OBJDIR is .CURDIR, therefore we need to create the desired directory — or tree structure ­— before running make to build our targets.

Assume, you choose to rely on the first mechanism to set .OBJDIR, you can then organise your build system so that, before running make to build your targets,

  • The environment variable MAKEOBJDIRPREFIX is set to suitable value.
  • A special script (it might be a make target) prepares the necessary directory or directories under ${MAKEOBJDIRPREFIX}.

Makefiles arrangements

We consider a simple Makefile and describe arrangements necessary for the previous setup to work.

program         : a.o b.o c.o
        cc a.o b.o c.o -o program

a.o b.o c.o     : defs.h
a.o             : a.c
       cc -c a.c

b.o             : b.c
       cc -c b.c

c.o             : c.c
       cc -c c.c

manual.html: manual.css
manual.html: a.c b.c c.c defs.h
       manualtool -c manual.css -o manual.html a.c b.c c.c defs.h

Before building a.o the program make will chdir(2) to ${.OBJDIR} and run the compiler cc from that directory. Since the source a.c is located in another directory, the compiler cc will not find it and the compilation will fail. This issue is solved by using the local variables defined by make, the previous Makefile therefore translates to:

program         : a.o b.o c.o
        cc ${.ALLSRC} -o program

a.o b.o c.o     : defs.h
a.o             : a.c
       cc -c ${.ALLSRC:M*.c}

b.o             : b.c
       cc -c ${.ALLSRC:M*.c}

c.o             : c.c
       cc -c ${.ALLSRC:M*.c}

manual.html: manual.css
manual.html: a.c b.c c.c defs.h
       manualtool -c ${.ALLSRC:M*.css} -o manual.html ${.ALLSRC:N*.css}

Note how globbing patterns are used in the last recipe to appropriately select pieces of the sources at the various locations of the command line.

Examples

The build system for — and possibly and — use this functionality to be able to build simultaneously the operating system for various architectures. (It also make cleaning easier!) You could study these Makefiles to gain a better understanding of the techniques involved.

My portable macros bsdowl for also support this functionality of bsdmake, you could look at the file bps.objdir.mk and the examples.

Michaël Le Barbier
  • 6,103
  • 5
  • 28
  • 57