简体   繁体   中英

How I strip path of source file while writing a compilation rule in makefile?

In short: I want to compile sources from different directories, and put object files into current directory.

For example, I have files:

test.c
../../lib1/boot.c
../../lib2/startup.c
../common/utils.c

(also few files .s (assembly) and .cpp, but I hope this is not important).

All of their object-files I want to be in the current directory:

test.o
boot.o
startup.o
utils.o

And I can't figure out how to write such rule in my makefile.

For example,

%o.: %.c

does not work now because make can't find a rule to build boot.o from ../../lib1/boot.c , it can only find rule to build ../../lib1/boot.o from ../../lib1/boot.c .

I tried to use this:

%o.: %.c
 (my compilation line, for example "gcc -c $^ -o $@")
%o.: ../../lib1/%.c
 (my compilation line)
%o.: ../../lib2/%.c
 (my compilation line)
%o.: ../common/%.c
 (my compilation line)

and it works. But obviously this is not generic enough, and in addition, some user came to me today and said that his application has also some ../../some_other_lib/common_things.c , hence my makefile failed. I looked through our project, and found many such cases with a lot of different directories involved. With my approach, I'll have to write a separate rule for each such directory, with identical compilation line. This does not seem good to me.

So my question is: how to make some generic compilation rule that puts (and checks) object files in current directory, while operating with sources in different directories?

Thank you.

The directories can be extracted from the CSRC variable with $(dir ...) and this list can then be used in the vpath directive.

vpath %.c $(sort $(dir $(CSRC)))
vpath %.s $(sort $(dir $(SSRC)))
vpath %.cpp $(sort $(dir $(CPPSRC)))

(I've thrown in the sort function to remove duplicates, but that's not absolutely necessary.)

Now the rules can be kept simple and make will search the source files in the list of directories.

$(COBJ) := $(notdir $(CSRC))
$(SOBJ) := $(notdir $(SSRC))
$(CPPOBJ) := $(notdir $(CPPSRC))

.PHONY: all
all: $(EXECUTABLE)

$(EXECUTABLE): $(COBJ) $(SOBJ) $(CPPOBJ)
    ....

$(COBJ): %.o: %.c
    ...

$(SOBJ): %.o: %.s
    ...

$(CPPOBJ): %.o: %.cpp
    ...

Try to use makefile function notdir as this:

%.o:    %.c
    gcc -c $< -o $(notdir $@)

$@ must be equal to the full path ex: ../../lib2/startup.o ad notdir will trunk it to: startup.o .

With this rule you will be able to compile all your source in the current directory.

Actually, your example is like that:

.
└── common
    ├── lib1
    │   └── boot.c
    ├── lib2
    │   └── startup.c
    ├── test
    │   ├── Makefile
    │   └── test.c
    └── utils.c

I think i will be better like that:

.
├── common
│   ├── lib1
│   │   ├── Makefile
│   │   ├── obj
│   │   └── src
│   │       └── boot.c
│   ├── lib2
│   │   ├── Makefile
│   │   ├── obj
│   │   └── src
│   │       └── startup.c
│   ├── Makefile
│   ├── obj
│   ├── src
│   │   └── utils.c
│   └── test
│       ├── Makefile
│       ├── obj
│       └── src
│           └── test.c
└── Makefile

For that you need all your Makefiles to call the subdirs Makefiles. and the src / obj dirs is a separation between your source and objects.

SRC := utils.c

OBJ := $(SRC:%.c=%.o)

NAME := project

SRC_D := src
OBJ_D := obj

SUBDIRS := lib1/ \
           lib2/ \
           test/

all: $(NAME) $(SUBDIRS)
    @for dir in $(SUBDIRS); \
    do \
    $(MAKE) -C $$dir; \
    done

$(NAME):    $(OBJ:%.o=$(OBJ_D)/%.o)

$(OBJ_D)/%.o :  $(SRC_D)/%.c
    gcc -c $< -o $@

OK, took me some time, but finally I found the solution (using some threads on this site by the way):

# Defining compilation rules in a way that object files will be produced in current directory, and not in the directory of source files:
all: <List of my targets>

define my_c_rule
$(subst .c,.o,$(notdir $(1))): $(1)
    $(CC) $(CFLAGS) $(CDEFINES) $$^ -o $$@
endef
$(foreach f, $(CSRC), $(eval $(call my_c_rule, $(f))))

$(CSRC) contains list of source files with their paths.

Just need to take into account that if earlier I had something like this:

.c.o:
    $(CC) $(CFLAGS) $(CDEFINES) $^ -o $@

all: <List of my targets>

...now I have to put all sentence above the rules which I described in my_c_rule procedure. If I don't do this, make stops after compiling first source file. This is because old "wildcard" rules like .co or %.o: %.c do not replace all as a default target (even being written earlier), but non-wildcard rules like boot.o: ../../lib1/boot.c (result of the above macros) do replace the default target in case they are written earlier.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM