4

Suppose I have a directory structure like:

./Header
./Srcs
./makefile

The contents of the folder ./Header/ are two header files:

header1.h

#ifndef HEADER1_H
#define HEADER1_H
#include <stdio.h>

void func1();

#endif

header2.h

#ifndef HEADER2_H
#define HEADER2_H
#include <stdio.h>

void func2();

#endif

In ./Srcs/ I have the following srcs:

src1.c

#include <header1.h>

void func1() {
   printf("func1()\n");
}

src2.c

#include <header2.h>

void func2() {
   printf("func2()\n");
}

main.c

#include <header1.h>
#include <header2.h>

int main(int argc, char** argv) {
   func1();
   func2();
   return 0;
}

Finally the makefile look like this:

CC=gcc
INCLUDE=-I./Header/
SRC_DIR=./Srcs/
SRC_LIST=$(addprefix $(SRC_DIR), main.c src1.c src2.c)
OBJ_LIST=$(addsuffix .o, $(basename $(SRC_LIST)))
OUTPUT=test

%.o : %.c
    $(CC) -c $< -o $@ $(INCLUDE)

all : $(OBJ_LIST)
    $(CC) -o $(OUTPUT) $(OBJ_LIST)

clean : $(OUTPUT) $(OBJ_LIST)
    rm -r $(OUTPUT) $(OBJ_LIST)

Executing the makefile I get:

gcc -c Srcs/main.c -o Srcs/main.o -I./Header/
gcc -c Srcs/src1.c -o Srcs/src1.o -I./Header/
gcc -c Srcs/src2.c -o Srcs/src2.o -I./Header/
gcc -o test ./Srcs/main.o ./Srcs/src1.o ./Srcs/src2.o

Specifically, because of the pattern rule I've used, all the objects file are generated in the folder ./Srcs/, what I would like to achieve is to put all the object files into the directory where the final output will be, in my specific example the final output will be in the same directory where the makefile is.

How can I write my makefile in order to achieve such goal?

LPs
  • 16,045
  • 8
  • 30
  • 61
user8469759
  • 2,522
  • 6
  • 26
  • 50
  • http://stackoverflow.com/questions/14639794/getting-make-to-create-object-files-in-a-specific-directory – Jite Aug 25 '16 at 13:18

2 Answers2

0

If you want to put your build outputs in the same strcture than your sources, but in the build directory :

#PROJDIR is the folder where your Makefile is
PROJDIR := $(realpath $(CURDIR)/..)

#SOURCEDIR is the root folder where your sources sub-folder ares
SOURCEDIR := $(PROJDIR)/Sources

#BUILDDIR is the root folder where your objects will be
BUILDDIR := $(PROJDIR)/Build

#DIRS are the sub-folder, e.g. Sources/Folder1 and Build/Folder1
DIRS = Folder0 Folder1 Folder2

#Here we create a list of source and builder folders with full path
SOURCEDIRS = $(foreach dir, $(DIRS), $(addprefix $(SOURCEDIR)/, $(dir)))
TARGETDIRS = $(foreach dir, $(DIRS), $(addprefix $(BUILDDIR)/, $(dir)))

#We allow GNU make to search for the sources in all the sources folders
VPATH = $(SOURCEDIRS)

#We define a function to generate a Build/Folder1/%.o: %.c rule for each sub-folder
define generateRules
$(1)/%.o: %.c
    @echo Building $$@
    $(CC) -c  -o $$@ $$< 
endef

#Finally we generate the rule for each build folder
$(foreach targetdir, $(TARGETDIRS), $(eval $(call generateRules, $(targetdir))))

Or if you want to put all the build items in the build directory :

#PROJDIR is the folder where your Makefile is
PROJDIR := $(realpath $(CURDIR)/..)

#SOURCEDIR is the root folder where your sources sub-folder ares
SOURCEDIR := $(PROJDIR)/Sources

#BUILDDIR is the root folder where your objects will be
BUILDDIR := $(PROJDIR)/Build

#DIRS are the sub-folder, e.g. Sources/Folder1 and Build/Folder1
DIRS = Folder0 Folder1 Folder2

#Here we create a list of source with full path
SOURCEDIRS = $(foreach dir, $(DIRS), $(addprefix $(SOURCEDIR)/, $(dir)))

#We allow GNU make to search for the sources in all the sources folders
VPATH = $(SOURCEDIRS)

$(BUILDDIR)/%.o: %.c
    $(CC) -o $@ $<
Tim
  • 1,853
  • 2
  • 24
  • 36
0

what I would like to achieve is to put all the object files into the directory where the final output will be, in my specific example the final output will be in the same directory where the makefile is.

This is a bit trickier if you restrict yourself to standard make, but you're already committed to GNU make, and you're already using the appropriate feature: pattern rules. The core of what you want would be something like this:

SRC_DIR = ./Srcs
OUTPUT_DIR = .
OBJ_LIST = $(addprefix $(OUTPUT_DIR)/, main.o src1.o src2.o)
OUTPUT = $(OUTPUT_DIR)/test

all : $(OUTPUT)

$(OUTPUT) : $(OBJ_LIST)
    $(CC) -o $@ $(OBJ_LIST)

# Here's the key:
$(OUTPUT_DIR)/%.o : $(SRC_DIR)/%.c
    $(CC) -c $< -o $@ $(INCLUDE)

clean :
    rm -rf $(OUTPUT) $(OBJ_LIST)

.PHONY: all clean

Additional notes:

  • Unless you specify otherwise, the first target in the file is the default target. This ordinarily should be the one that builds the whole project, conventionally named all.

  • Each target you want to be able to build should be named as a target. The all target ordinarily specifies all desired specific targets as dependencies; it should not build them directly.

  • The clean target (by whatever name) should not depend on targets that build files you want to clean! If it does, then you cannot clean without first building.

  • In GNU make, at least, it is advisable to flag artificial targets that do not actually build the file that they name as PHONY, as demonstrated above.

  • GNU make's standard recipe for building .o from .c is a bit more flexible than yours:

    $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
  • GNU make's standard recipe for linking .o into a program is also a bit more flexible than yours:
    $(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
  • If you want specifically and only to put built files into the top source directory, then you can simplify the things considerably by getting rid of all the $(OUTPUT_DIR) stuff.
John Bollinger
  • 160,171
  • 8
  • 81
  • 157