Make uses same source file for different object fi

2020-05-04 05:51发布

Make chooses the same source file for different object files. Both are a list of files, only with different filenames. Make switches between the object files but not the source files.

I've already tried some of the answers on StackOverflow with related problems, though those solutions either seem too complicated for what's needed, some don't work and others need the files to be in one directory.

I've also tried compiling the files together in one go (with gcc), but this gives some problems with the linking of the rest of the file.

$(OBJFILES): $(SRCFILES)
    $(CC) $(CCFLAGS) -c $< -o $@

$(OBJFILES) contains the following files: src/kernel.o src/screen/screen_basic.o

And $(SRCFILES) contains these files: src/kernel.c src/screen/screen_basic.c

Basically, src/kernel.c gets compiled to both src/kernel.o and src/screen/screen_basic.o, while src/screen/screen_basic.c never gets compiled.

What's run by make (I replaced the options for gcc with the variable names to keep it short):

i686-elf-gcc $(CFLAGS) $(WARNINGS) -c src/kernel.c -o src/kernel.o

i686-elf-gcc $(CFLAGS) $(WARNINGS) -c src/kernel.c -o src/screen/screen_basic.o

I don't really know what you need to see what's going wrong. So, the source files (all of them) are at https://github.com/m44rtn/vireo-kernel. It may be nice to know that this is a rewrite of the project. In the previous 'version' I manually added the file names to the makefile, which worked perfectly, but isn't nice when you have to add new files or when you're moving them around. That makefile is on the master branch (which ISN'T the default branch anymore).

The make version is the newest:

GNU Make 4.2.1
Built for x86_64-pc-linux-gnu

So, I expected this to work absolutely great. I thought it would just compile all files in the list. Unfortunately, it didn't. I don't really know what's going wrong here.

It compiles kernel.c to both kernel.o and screen_basic.o. I, of course, had hoped it would compile kernel.c to kernel.o and screen_basic.c to screen_basic.o.

Later on, the two files get linked. However, because they are the same, the linker throws errors because everything is defined twice, which isn't ideal.

I've tried to solve it by compiling every C file in one go, but this gave some issues with linking the Assembly files with the C files (sometimes making it non GRUB multibootable, which is necessarry to have, in my case).

I do not know what's wrong with the makefile for it to behave like this.

All the solutions from stack overflow I've tried:

Some solutions involve throwing all files in the root directory and just using:

%.o: %.c
       (..)

However, this project will have a lot of files. This makes having everything in the same directory very annoying, very fast. I think this didn't work as well, but I don't know if that's true or just my brain lying to me. Sorry.

I've heard something about 'static rules':

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

This didn't work, however I may have used it wrong. I don't know.

I like the makefile to stay the same as much as possible, because it's a very convenient one (it just detects all files automatically).

I really hope I've provided enough information, and that this question wasn't already asked. If it has been, I'm sorry in advance.

If you need more information, please ask! :)

--EDIT-- I'm quite new to make in this way, although I've used it for five years. I've always used it wrong. It is possible that my makefile is very ugly or bad. And I did use an example to write the makefile.

2条回答
家丑人穷心不美
2楼-- · 2020-05-04 06:20

Consider the rule...

$(OBJFILES): $(SRCFILES)
        $(CC) $(CCFLAGS) -c $< -o $@

Let's say, for the sake of argument, that we have...

SRCFILES := a.c b.c
OBJFILES := a.o b.o

If you expand the rule manually it becomes...

a.o b.o: a.c b.c
        $(CC) $(CCFLAGS) -c $< -o $@

I think (correct me if I'm wrong) you are under the impression that this will be interpreted by make as a.o depends on a.c and, separately, b.o depends on b.c. That's not the case. What it actually states is that both of a.o and b.o depend on both of a.c and b.c.

So, when make tries to update the target a.o it sees the full prerequisite list of a.c and b.c and assigns the first of these, a.c, to the builtin variable $<. That's why you always see the first source file in $(SRCFILES) being compiled.

The best way to solve this probably depends on how you intend to structure your source file hierarchy and object files but you might want to take a look at using one or more vpath directives.

查看更多
放荡不羁爱自由
3楼-- · 2020-05-04 06:35

The pattern rule does not put all objects in root directory, consider

CFILES := path/to/a.c b.c
OBJFILES := $(foreach f,$(CFILES),$(f:%.c=%.o))

all: $(OBJFILES)

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

Here is what you get:

cc -c path/to/a.c -o path/to/a.o
cc -c b.c -o b.o

The following is not a recommendation, but kind of an exercise in makefile programming.

If you have $(SRCFILES) and want to compile them one at a time, you can use:

define compile
$1.o: $1.c
    $(CC) $(CCFLAGS) -c $$< -o $$@
endef

$(foreach src,$(SRCFILES),$(eval $(call compile, $(src:%.c=%))))

If the correspondence of lists of sources and objects is not by name, but only by placement in list, you can destroy the CFILES list

define compile
src := $(CFILES)
CFILES := $(wordlist 2, $(words $(CFILES)), $(CFILES))
$1: $(src)
    $(CC) $(CCFLAGS) -c $$< -o $$@
endef

$(foreach obj,$(OBJFILES),$(eval $(call compile, $(obj))))

Or you may use a helper list, to keep CFILES unchanged:

helperlist := $(CFILES)

define compile
src := $(firstword $(helperlist))
helperlist := $(wordlist 2, $(words $(helperlist)), $(helperlist))
$1: $(src)
    $(CC) $(CCFLAGS) -c $$< -o $$@
endef

$(foreach obj,$(OBJFILES),$(eval $(call compile, $(obj))))
查看更多
登录 后发表回答