Makefile wiki

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
Civillian
Member
Member
Posts: 32
Joined: Tue Feb 21, 2012 3:26 pm

Makefile wiki

Post by Civillian »

Hello all.
I'm having trouble making use of the Makefile article (http://wiki.osdev.org/Makefile).
How do I include a call to NASM for my kernel's header/header.s file?
Where exactly is the linker called in the example, if it is called at all?

Also, if I have Minix-style library subdirectories with one file per function e.g. lib/string/strlen.c, do I need to add each subdirectory lib/string, /lib/stdio etc. to PROJDIRS?

Thank you.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: Makefile wiki

Post by Solar »

The Makefile tutorial is merely pointing out one way how the 'make' utility could be used. Starting from there, any additional information you might be looking for can be found in the 'make' documentation.

The skeleton of a more powerful Makefile can be found here, but I have not found the time to put that into appropriate prose. Since you are apparently new to the field of managing a source tree, I doubt it will be much help for you.

The first thing I'd do is to get rid of those "Minix-style library subdirectories"...
Every good solution is obvious once you've found it.
User avatar
Civillian
Member
Member
Posts: 32
Joined: Tue Feb 21, 2012 3:26 pm

Re: Makefile wiki

Post by Civillian »

Solar wrote:The Makefile tutorial is merely pointing out one way how the 'make' utility could be used. Starting from there, any additional information you might be looking for can be found in the 'make' documentation.
It's not used for compiling a kernel at all? Indeed, pdclib.a doesn't look like a kernel.
So the linker is called implicitly for the object files? That's a problem, because I need to feed it my linker.ld script.
As for the Assembly header.s, something like this should do it?

Code: Select all

%.o: %.s    # <--- ?
    @nasm ... # I don't have the file at hand but assume this line is OK
Solar wrote:The skeleton of a more powerful Makefile can be found here, but I have not found the time to put that into appropriate prose. Since you are apparently new to the field of managing a source tree, I doubt it will be much help for you.
True, I saw that page before posting this topic. It's too advanced for me. Thanks anyway.
Solar wrote:The first thing I'd do is to get rid of those "Minix-style library subdirectories"...
I'd like to know the reason why. Slower build? Because otherwise, I find the source very easy to browse.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: Makefile wiki

Post by Solar »

Civillian wrote:It's not used for compiling a kernel at all? Indeed, pdclib.a doesn't look like a kernel.
Makefile tutorial wrote:The 'make' manual will tell you about the hundreds of things you can do with a Makefile, but it doesn't give you an example for a good Makefile. The following examples are mostly shaped after the real-life PDCLib Makefile, and show some of the "tricks" used therein that may not be that obvious to the make beginner.

The Makefile creates only one project-wide linker library, but it should be easy to expand it for multiple binaries/libraries.
Civillian wrote:So the linker is called implicitly for the object files?
No, the linker is not called at all, because this example Makefile builds a linker archive.
Civillian wrote:That's a problem, because I need to feed it my linker.ld script.
Go for it:
Makefile tutorial wrote:The general syntax is:

Code: Select all

target: dependency
        command
This isn't about providing a ready-made solution. It's about giving people a starting point, an impression of what 'make' can do, and a couple of frequently used "tricks" (building file lists, phony targets, unit tests embedded in library implementation files, automatic C/C++ header dependency handling, conditional evaluation).

Makefiles are a tool and language of their own. There is no "one true Makefile" any more than the "one true C program".
Civillian wrote:
Solar wrote:The first thing I'd do is to get rid of those "Minix-style library subdirectories"...
I'd like to know the reason why. Slower build? Because otherwise, I find the source very easy to browse.
[/quote]

If every subdirectory of name "X" has only one file of name "X.c", what's the purpose of the subdirectory (other than making navigation, Makefile building etc. that one step more complex)?
Every good solution is obvious once you've found it.
User avatar
Civillian
Member
Member
Posts: 32
Joined: Tue Feb 21, 2012 3:26 pm

Re: Makefile wiki

Post by Civillian »

Solar wrote:This isn't about providing a ready-made solution. It's about giving people a starting point, an impression of what 'make' can do, and a couple of frequently used "tricks" (building file lists, phony targets, unit tests embedded in library implementation files, automatic C/C++ header dependency handling, conditional evaluation).
OK, although IMHO a better starting point would be a Makefile for a kernel, since that is what people need.
Solar wrote:If every subdirectory of name "X" has only one file of name "X.c", what's the purpose of the subdirectory (other than making navigation, Makefile building etc. that one step more complex)?
That would indeed be silly. So to clear that up, it's not like that.
It is: e.g. include/string.h
-> lib/string/strlen.c
-> lib/string/strcpy.c
-> lib/string/memcpy.c
and so on... with one function per file and one directory per header.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: Makefile wiki

Post by Solar »

Civillian wrote:OK, although IMHO a better starting point would be a Makefile for a kernel, since that is what people need.
There was no Makefile tutorial in the Wiki, and I opted to add one based on a Makefile I use frequently, so that any problems I solve in my Makefile go into the tutorial, too.

The differences between "makefile that creates a linker lib" and "makefile that creates a kernel binary" would be no more complex than the differences between "makefile that builds kernel X" and "makefile that builds kernel Y"; indeed, to turn the tutorial Makefile into one that generates a binary, all you need is to replace one rule. Instead of the pdclib.a rule, write:

Code: Select all

    kernel.bin: $(OBJFILES) linker.ld
            @ld -T linker.ld -o $@ $? $(OBJFILES)
Or somesuch.

Besides, there is a reason why the tutorial does not present a "complete" Makefile in one block, or why the GCC Cross-Compiler tutorial doesn't provide an automated script. This isn't about copy & paste, it's about understanding how it works, and adapting it to whatever needs you might have. The idea is not to do the work for you, but to provide the information that might be hard to come by otherwise. (Most Makefiles out in the wild suck because people did c&p some code they didn't understand, usually crappy code to begin with, and did some trial&error on it until it worked for them so-so. I wanted to provide a better starting point plus some guidance, not a ready-made solution.)
It is: e.g. include/string.h
-> lib/string/strlen.c
-> lib/string/strcpy.c
-> lib/string/memcpy.c
and so on... with one function per file and one directory per header.
In that case, it would be sufficient to set PROJDIRS like this:

Code: Select all

PROJDIRS := lib include
(The building of the file lists works recursively.)
Every good solution is obvious once you've found it.
User avatar
Civillian
Member
Member
Posts: 32
Joined: Tue Feb 21, 2012 3:26 pm

Re: Makefile wiki

Post by Civillian »

My current problem is that I can't get NASM to be used for the header/header.s file, which I stuck in SRCFILES alongside the C source files.
Also note that I had to remove the backslashes in -name's parameter at the find calls.

Code: Select all

KERNNAME := kernel.bin
PROJDIRS := header include lib
SRCFILES := $(shell find $(PROJDIRS) -type f -name "*.[cs]")
HDRFILES := $(shell find $(PROJDIRS) -type f -name "*.h")
OBJFILES := $(patsubst %.c,%.o,$(SRCFILES))
DEPFILES := $(patsubst %.c,%.d,$(SRCFILES))
WARNINGS := -Wall -Wextra -pedantic
CFLAGS := -std=c99 -Werror $(WARNINGS) -nostdlib -nostdinc -fno-builtin -fno-stack-protector -m32 -O2 -I ./include
ASFLAGS := -f elf
LDFLAGS := -T linker.ld -m elf_i386

.PHONY: build clean

build: $(KERNNAME)

$(KERNNAME): $(OBJFILES) linker.ld
	@ld $(LDFLAGS) -o $@ $? $(OBJFILES)

clean:
	-@$(RM) -v $(wildcard $(OBJFILES) $(DEPFILES) $(KERNNAME) *~ */*~ */*/*~)

-include $(DEPFILES)

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

%.o: %.s
	nasm $(ASFLAGS) $<
Cognition
Member
Member
Posts: 191
Joined: Tue Apr 15, 2008 6:37 pm
Location: Gotham, Batmanistan

Re: Makefile wiki

Post by Cognition »

You have to set make to handle specific suffixes.

Code: Select all

CC = gcc
CFLAGS = 
ASM = nasm
ASM_FLAGS = -f elf

.SUFFIXES: .c .s .o

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

.s.o: $^
       $(ASM) $(ASM_FLAGS) -o $@ $^
Note: Verify this works on some junk files first, if you mess up the $^ and $@ tokens you can actually end up overwriting your source files
Reserved for OEM use.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: Makefile wiki

Post by Solar »

@ Cognition:

You got it wrong. It's either:

Code: Select all

% GNU style
%.o: %.s
or:

Code: Select all

% POSIX style
.s.o:
I prefer GNU style, as it is more consistent with "normal" rules. In any case, no "$^" in either the rule target or the prerequisite; those variables are for the commands only.
Cognition wrote:Verify this works on some junk files first, if you mess up the $^ and $@ tokens you can actually end up overwriting your source files.
Sound advice, but checking the manual, section "Automatic Variables", would be even better. There is a lot more that 'make' can do that is not covered in the tutorial, which is only an introduction to a basic use case. That's why it says "tutorial"...

$@: The target of the rule.

$^: All prerequisites of a rule.

(You should rather use $<, which is the first prerequisite for a rule; not that it makes much of a difference when you only have one prerequisite, but you might want to add the Makefile itself as prerequisite so that changes in e.g. the compiler flags trigger a recompile.)
Every good solution is obvious once you've found it.
Cognition
Member
Member
Posts: 191
Joined: Tue Apr 15, 2008 6:37 pm
Location: Gotham, Batmanistan

Re: Makefile wiki

Post by Cognition »

@Solar: You're right, it's been a long time since I hacked my Makefile together and tbh it's a bit of mess. Thanks for the advice.
Reserved for OEM use.
User avatar
Civillian
Member
Member
Posts: 32
Joined: Tue Feb 21, 2012 3:26 pm

Re: Makefile wiki

Post by Civillian »

Thanks for the replies.
Because I kept getting the below error, I changed the makefile.
header/header.s:1: *** missing rule before commands. Stop.
Let me know if you spot anything wrong, so far it seems to work.

Code: Select all

KERNNAME := kernel.bin
PROJDIRS := header include lib
SRCFILES := $(shell find $(PROJDIRS) -type f -name "*.c")
HDRFILES := $(shell find $(PROJDIRS) -type f -name "*.h")
ASMFILES := $(shell find $(PROJDIRS) -type f -name "*.s")
OBJFILES := $(patsubst %.c,%.o,$(SRCFILES))
ASOFILES := $(patsubst %.s,%.o,$(ASMFILES))
DEPFILES := $(patsubst %.c,%.d,$(SRCFILES))
WARNINGS := -Wall -Wextra -pedantic
CFLAGS := -std=c99 -Werror $(WARNINGS) -nostdlib -nostdinc -fno-builtin -fno-stack-protector -m32 -O2 -I ./include
ASFLAGS := -f elf
LDFLAGS := -T linker.ld -m elf_i386

.PHONY: build clean

build: $(KERNNAME)

$(KERNNAME): $(ASOFILES) $(OBJFILES) linker.ld
	@ld $(LDFLAGS) -o $@ $? $(ASOFILES) $(OBJFILES)

clean:
	-@$(RM) -v $(wildcard $(OBJFILES) $(ASOFILES) $(DEPFILES) $(KERNNAME) *~ */*~ */*/*~)

-include $(DEPFILES)

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

%.o: %.s
	@nasm $(ASFLAGS) $< -o $@
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: Makefile wiki

Post by Solar »

Nothing wrong as far as I could see while skimming over it.

But two things could be simpler:

Code: Select all

HDRFILES := $(shell find $(PROJDIRS) -type f -name "*.h")
You don't need this anywhere.

Code: Select all

OBJFILES := $(patsubst %.c,%.o,$(SRCFILES))
ASOFILES := $(patsubst %.s,%.o,$(ASMFILES))
You don't need to keep a separate ASOFILES. Once it's a *.o file, it doesn't matter what it was originally. Replace with:

Code: Select all

OBJFILES := $(patsubst %.c,%.o,$(SRCFILES)) $(patsubst %.s,%.o,$(ASMFILES))
...and delete all other occurences of ASOFILES (in kernel and clean rule).
Every good solution is obvious once you've found it.
User avatar
Civillian
Member
Member
Posts: 32
Joined: Tue Feb 21, 2012 3:26 pm

Re: Makefile wiki

Post by Civillian »

Good suggestions, thank you.

As a final improvement, I wanted to add Automated testing, but I'm given this error:
make: *** No rule to make target `lib/string/strlen_test', needed by `tests'. Stop.
I deviated only slightly from the Wiki to increase readability:
used TESTFILES, TESTDEPFILES and the _test suffix instead of the original ones.

Edit: extra question, what does the $? do, in the linker call?

Code: Select all

KERNNAME := kernel.bin
PROJDIRS := header include lib
SRCFILES := $(shell find $(PROJDIRS) -type f -name "*.c")
ASMFILES := $(shell find $(PROJDIRS) -type f -name "*.s")
OBJFILES := $(patsubst %.c,%.o,$(SRCFILES)) $(patsubst %.s,%.o,$(ASMFILES))
TESTFILES := $(patsubst %.c,%_test,$(SRCFILES))
DEPFILES := $(patsubst %.c,%.d,$(SRCFILES))
TESTDEPFILES := $(patsubst %,%.d,$(TESTFILES))
WARNINGS := -Wall -Wextra -pedantic
CFLAGS := -std=c99 -Werror $(WARNINGS) -nostdlib -nostdinc -fno-builtin -fno-stack-protector -m32 -O2 -I ./include
ASFLAGS := -f elf -Ox
LDFLAGS := -T linker.ld -m elf_i386

.PHONY: build clean check tests

build: $(KERNNAME)

$(KERNNAME): $(OBJFILES) linker.ld
	@ld $(LDFLAGS) -o $@ $? $(OBJFILES)

clean:
	-@$(RM) -v $(wildcard $(OBJFILES) $(DEPFILES) $(TESTFILES) $(KERNNAME) $(shell find -type f -name "*~"))

check: tests
	-@rc=0; count=0; \
	for file in $(TESTFILES); do \
		echo "Test: $$file"; ./$$file; \
		rc=`expr $$rc + $$?`; count=`expr $$count + 1`; \
		done; \
	echo; \
	echo "Tests failed: $$rc out of $$count."

tests: $(TESTFILES)

-include $(DEPFILES) $(TESTDEPFILES)

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

%.o: %.s
	nasm $(ASFLAGS) $< -o $@

%_test: %.c Makefile
	@$(CC) $(CFLAGS) -MMD -MP -D TESTING $< -o $@
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: Makefile wiki

Post by Solar »

Civillian wrote:Edit: extra question, what does the $? do, in the linker call?
There is nothing I could say, other than RTfineM...
Every good solution is obvious once you've found it.
User avatar
Civillian
Member
Member
Posts: 32
Joined: Tue Feb 21, 2012 3:26 pm

Re: Makefile wiki

Post by Civillian »

Judging by what I read here: http://www.gnu.org/software/make/manual ... -Variables
$? isn't needed there in the makefile.

I also found that in order to compile the test drivers, the Makefile dependency needed to be removed and the compiler flags rewritten.

Code: Select all

KERNNAME := kernel.bin
PROJDIRS := header include lib
SRCFILES := $(shell find $(PROJDIRS) -type f -name "*.c")
ASMFILES := $(shell find $(PROJDIRS) -type f -name "*.s")
OBJFILES := $(patsubst %.c,%.o,$(SRCFILES)) $(patsubst %.s,%.o,$(ASMFILES))
TESTFILES := $(patsubst %.c,%_test,$(SRCFILES))
DEPFILES := $(patsubst %.c,%.d,$(SRCFILES))
TESTDEPFILES := $(patsubst %,%.d,$(TESTFILES))
WARNINGS := -Wall -Wextra -pedantic
CFLAGS := -std=c99 -Werror $(WARNINGS) -nostdlib -nostdinc -fno-builtin -fno-stack-protector -m32 -O2 -I ./include
TESTCFLAGS := -std=c99 $(WARNINGS) -nostdinc -m32 -O2 -I ./include
ASFLAGS := -f elf -Ox
LDFLAGS := -T linker.ld -m elf_i386

.PHONY: build clean check tests

build: $(KERNNAME)

$(KERNNAME): $(OBJFILES) linker.ld
	ld $(LDFLAGS) -o $@ $(OBJFILES)

clean:
	-@$(RM) -v $(wildcard $(OBJFILES) $(DEPFILES) $(TESTFILES) $(TESTDEPFILES) $(KERNNAME) $(shell find -type f -name "*~"))

check: tests
	-@failed=0; file_count=0; \
	for file in $(TESTFILES); do \
		echo "Test: $$file"; \
		./$$file; \
		failed=`expr $$failed + $$?`; \
		file_count=`expr $$file_count + 1`; \
		done; \
	echo "Tests failed: $$failed in $$file_count test files."

tests: $(TESTFILES)

-include $(DEPFILES) $(TESTDEPFILES)

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

%.o: %.s
	nasm $(ASFLAGS) $< -o $@

%_test: %.c
	@$(CC) $(TESTCFLAGS) -MMD -MP -D TESTING $< -o $@
Post Reply