Getting make to create object files in a specific directory
Asked Answered
P

4

34
GNU Make 3.82
gcc 4.7.2
c89

I have the following make file:

INC_PATH=-I/home/dev_tools/apr/include/apr-1
LIB_PATH=-L/home/dev_tools/apr/lib
LIBS=-lapr-1 -laprutil-1
RUNTIME_PATH=-Wl,-rpath,/home/dev_tools/apr/lib
CC=gcc
CFLAGS=-Wall -Wextra -g -m32 -O2 -D_DEBUG -D_THREAD_SAFE -D_REENTRANT -D_LARGEFILE64_SOURCE $(INC_PATH)
SOURCES=$(wildcard src/*.c)
OBJECTS=$(patsubst %.c, %.o, $(SOURCES))

EXECUTABLE=bin/to

all:    build $(EXECUTABLE)

$(EXECUTABLE):  $(OBJECTS)
    $(CC) $(CFLAGS) -o $@ $(RUNTIME_PATH) $(OBJECTS) $(LIB_PATH) $(LIBS)

$(OBJECTS): $(SOURCES)
    $(CC) $(CFLAGS) -c $(SOURCES) $(LIB_PATH) $(LIBS)

build:
    @mkdir -p bin

clean:
    rm -rf $(EXECUTABLE) $(OBJECTS) bin
    find . -name "*~" -exec rm {} \;
    find . -name "*.o" -exec rm {} \;

My directory structure is like this project/src project/bin. My Makefile is in the project (root) folder, and all my *.h and *.c are in the src directory. Currently I have only one source file called timeout.c

I get this error:

gcc: error: src/timeout.o: No such file or directory

I have used this to get all the source files:

SOURCES=$(wildcard src/*.c)

And the object files:

OBJECTS=$(patsubst %.c, %.o, $(SOURCES))

However, the make seems to create the object file in the project root folder where the Makefile is. Should it not put it in the src directory?

Plausible answered 1/2, 2013 at 4:34 Comment(0)
E
30

You have two problems in this rule (well, three):

$(OBJECTS): $(SOURCES)
    $(CC) $(CFLAGS) -c $(SOURCES) $(LIB_PATH) $(LIBS)

You haven't noticed yet, but the rule makes each object dependent on all sources, and tries to build that way. Not a problem as long as you have only one source. Easy to fix with a static pattern rule and an automatic variable:

$(OBJECTS): src/%.o : src/%.c
    $(CC) $(CFLAGS) -c $< $(LIB_PATH) $(LIBS)

Also, the command ("$(CC)...") doesn't specify an output file name, so gcc will infer it from the source file name; if you give it src/timeout.c, it will produce timeout.o (in the working directory, project/). So you should specify the desired path to the output file. Easy to do with another automatic variable:

$(OBJECTS): src/%.o : src/%.c
    $(CC) $(CFLAGS) -c $< $(LIB_PATH) $(LIBS) -o $@
Exorcism answered 1/2, 2013 at 7:3 Comment(2)
What about for creating a shared library? I ran across a site claiming I need the -c switch for creating a shared library, but using this prevents me from using -o. And currently, make is creating the object files just in the root project dir. My folder structure is: src/lib/*, and I want obj files to be generated in obj/lib/* The gcc command is: g++ -std=c++11 -g -I./include -fPIC -Wall -c ./src/lib/Source1.cpp ./src/lib/Source2.cppSalonika
Ok, I got it. Was trying to compile multiple objects dependent on multiple sources. I replaced with your static pattern rule and automatic variables $< and $@ and it works like a charm. Also don't know why gcc complains and says you can't use -c with -o but you certainly can.Salonika
T
7

Use gcc's -o option to write the output file to a particular location. For instance, you could say:

$(CC) $(CFLAGS) -c $(SOURCES) $(LIB_PATH) $(LIBS) -o $(OBJECTS)

Unfortunately, there's a problem with this line: if there is more than one source file in $(SOURCES), it won't work, since $(OBJECTS) will also contain multiple file names, and the -o option only binds to the first argument.

A way to compile each file in a list of source code files is to use implicit rules. In gmake, you would write:

$(EXECUTABLE):  $(OBJECTS)
        $(CC) $(CFLAGS) -o $@ $(RUNTIME_PATH) $(OBJECTS) $(LIB_PATH) $(LIBS)

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

where $< is replaced with name of the input file and $@ is replaced with the name out the output file.

Thalamencephalon answered 1/2, 2013 at 5:54 Comment(5)
@Exorcism Sorry, what is the "This" you are referring to?Thalamencephalon
Your solution, the "proper way". Try it with more than one source file and you'll see what I mean.Exorcism
@Exorcism my approach works on the systems I've tested it on. If $(OBJECTS) contains multiple filename each of which can be driver from a .c file, this idiom will generate each .o in turn. I've used this all over the place, so I'd certainly be interested in which operating system and make you tried it on.Thalamencephalon
Did you check to see whether it handles dependencies correctly? Did you read my answer, the part about each object depending on all sources?Exorcism
I see your point; with OBJECTS depending on SOURCES, object files may be regenerated more often than necessary, but it still generates a correct solution, regardless of how many source files at there (assuming that all the sources are for a single executable).Thalamencephalon
K
2

I solved this request and here is my Makefile and directory tree.

PROJECT := main.exe

DIR_SRC += .
DIR_SRC += ./src

DIR_INC += -lpthread 
DIR_INC += -I./inc 
DIR_INC += $(addprefix -I, $(DIR_SRC))

SRC_C += $(wildcard $(addsuffix /*.c, $(DIR_SRC)))
#OBJ := $(filter %.o, $(SRC_C:.c=.o))
OBJ := $(patsubst %.c, %.o, $(SRC_C))
EXE := $(PROJECT)

CC_PREFIX := 
CC := $(CC_PREFIX)gcc
CFLAG = 
CLIB = -L .

.PHONY:all

all:$(OBJ) $(EXE)

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

$(EXE): $(OBJ)
    $(CC) $(CFLAG) $(CLIB) $(OBJ) -o $@ 

clean:
    rm -r $(EXE) $(OBJ) 

See my directory tree:

enter image description here

Kopeck answered 10/8, 2020 at 4:4 Comment(0)
C
1

Another approach without addsuffix which may helps.

CC = gcc
SRC = src/*.c

CFILES := $(wildcard $(SRC))
OFILES := $(patsubst %.c,%.o, $(CFILES))

.PHONY: run

run: $(OFILES)
    $(CC) $^ -o $@
Before compile After compile
enter image description here enter image description here
Colburn answered 5/6 at 0:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.