Sega Master System / Mark III / Game Gear|
SG-1000 / SC-3000 / SF-7000 / OMV
Home - Forums - Games - Scans - Maps - Cheats - Credits
Music - Videos - Development - Hacks - Translations - Homebrew
a better makefile for your devkitSMS projectsPosted: Fri May 14, 2021 3:30 pm
Last edited by sverx on Wed May 19, 2021 11:13 am; edited 2 times in total
I admit I never really used makefiles for compiling code that uses devkitSMS, but when a project grows over a certain size it starts to be annoyingly long to recompile everything over and over again just because of a small change somewhere which really doesn't require to recompile *everything*.
So I looked into make and makefiles.
Unfortunately, the serious problem with devkitSMS and makefiles is that the assets2banks tool creates the assets2banks.h file, which is a file you'll easily have to #include in many of your sources.
So I wrote an example makefile that has one additional advantage apart from all the basic stuff (will compile only the sources that are indeed modified).
With this, IF you do any changes to the assets that DON'T change the CONTENTS of assets2banks.h, your C sources referencing that file won't be uselessly compiled (this saves a lot of time!) but you'll still get the changed assets linked and the ROM updated (found the right suggestion for this trick here - basically we check the assets2banks.h MD5 hash instead of its timestamp to see if a recompilation of program source files is needed).
to-md5 = $(patsubst %,%.md5,$1)
$(MAKESMS) output.ihx output.sms
output.ihx: main.rel banked_code1.rel banked_code2.rel banked_code3.rel bank4.rel bank5.rel bank6.rel bank7.rel
$(SDCC) -o output.ihx -mz80 --no-std-crt0 --data-loc 0xC000 \
-Wl-b_BANK1=0x14000 -Wl-b_BANK2=0x24000 -Wl-b_BANK3=0x34000 \
-Wl-b_BANK4=0x48000 -Wl-b_BANK5=0x58000 -Wl-b_BANK6=0x68000 -Wl-b_BANK7=0x78000 \
$(CRT0) main.rel $(SMSLIB) \
banked_code1.rel banked_code2.rel banked_code3.rel \
bank4.rel bank5.rel bank6.rel bank7.rel
main.rel: common.h main.h banked_code1.h banked_code2.h banked_code3.h
main.rel: $(call to-md5,assets2banks.h)
$(SDCC) -c -mz80 --peep-file $(PEEPFILE) main.c
banked_code1.rel: common.h main.h banked_code1.h banked_code2.h banked_code3.h
banked_code1.rel: $(call to-md5,assets2banks.h)
$(SDCC) -c -mz80 --peep-file $(PEEPFILE) --codeseg BANK1 banked_code1.c
banked_code2.rel: common.h main.h banked_code1.h banked_code2.h banked_code3.h
banked_code2.rel: $(call to-md5,assets2banks.h)
$(SDCC) -c -mz80 --peep-file $(PEEPFILE) --codeseg BANK2 banked_code2.c
banked_code3.rel: common.h main.h banked_code1.h banked_code2.h banked_code3.h
banked_code3.rel: $(call to-md5,assets2banks.h)
$(SDCC) -c -mz80 --peep-file $(PEEPFILE) --codeseg BANK3 banked_code3.c
@$(if $(filter-out $(shell cat [email protected] 2>/dev/null),$(shell md5sum $<)),md5sum $< > [email protected])
assets2banks.h bank4.rel bank5.rel bank6.rel bank7.rel: ./assets/*
$(ASSETS2BANKS) assets --firstbank=4 --compile --singleheader
Feedback/suggestions welcome! (also, I'm a makefile beginner so if this file can be improved, I'm all ears! :D )
||Posted: Fri May 14, 2021 7:24 pm|
In my opinion, the thing that makes Makefiles really nice is the ability to write generic rules using "automatic variables". For a simple SMS project, I wrote this:
$(IHX2SMS) $< [email protected]
%.asm %.sym %.lst %.rel: %.c
sdcc -c -mz80 --std-sdcc99 $<
sdasz80 -g -o $<
# plus a bunch of other specific rules that I've not included here
This allows me to say "How can you create an .sms file? If you have an .ihx file ("%.ihx"), run this program on it"
||Posted: Sat May 15, 2021 7:02 am|
|You could make assets2banks implement the check itself - if the generated .h is identical to the existing file, do not write it. In C it’s easy to pretend you’re super concerned about resources so you generate straight into the file pointer, but you can actually do it all in memory easily enough, the file is small, making it easier to compare.|
||Posted: Sat May 15, 2021 11:43 am|
That's true, and surely my example makefile could use a bit of that too, but the point was mainly to avoid recompilation of what hasn't changed (and, in this regard, generic rules won't disturb at all!) *and* to avoid recompilation when assets2banks.h gets rewritten with same contents (you won't be able to handle that without some specific workaround).
I thought about this too, and I choose to go that different route for many reasons:
- assets2banks it's written in Python and I don't really know Python very well
- I would prefer a simpler approach instead of generating the output and compare it to an existing file
- if I could workaround the problem with no changes to assets2banks code I would do that
- I'm lazy ;)
||Posted: Mon May 17, 2021 1:35 pm|
question about this line:
how would it understand if a .rel needs to be recompiled on a .h change?
||Posted: Mon May 17, 2021 6:22 pm|
You're right, it doesn't.
You can separate the "dependencies" rules from the "how to build" rules, if that helps:
specific.rel: specific.c shared.h
Or if you know that all of the c files in any given directory do require a specific header file, you can also
%.rel: %.c shared.h
||Posted: Tue May 18, 2021 2:48 pm|
|It makes sense, but at this point I wonder how you tell that some rel files are to be built by compiling C sources and other are to be built by assets2banks utility?|
||Posted: Tue May 18, 2021 8:54 pm|
You can have multiple rules specifying how to make a output, such as:
%.rel: assets2banks.cfg %.bin
python assets2banks.py blah
Make will only use the ones whose prerequisites exist (or it knows how to make)
If you have a more complicated set of rules, such as
||Posted: Wed May 19, 2021 11:23 am|
oh, this is interesting, though a bit confusing at first.
Does this mean that make actually doesn't know which recipe is the correct one to create, say, bank4.rel or banked_code2.rel but as long as there are source files or assets files that are newer than any .rel file it will trigger either one or the other recipe?
||Posted: Wed May 19, 2021 7:59 pm|
Make determines which recipe is the correct one based on which recipes can be satisfied. "%" doesn't mean *, but instead means "the exact same text on both sides of the :"
so if you wanted to make bank4.rel, and bank4.bin exists, it could use assets2banks; but if you also had bank4.c it'd pick one of the two.
For example, this makefile:
cp $< [email protected]
cp $< [email protected]
touch [email protected]
touch [email protected]
invoked with "make a.1", will generate a.2, generate a.1, and then delete a.2. If only one of a.2 or a.3 already exist, it'll use that one in preference (and then not delete it).
If a.2 exists and is older than a.1, even if a.3 is newer than a.1, make will think it's done.
||Posted: Fri May 21, 2021 10:48 am|
mmm, that's not how assets2banks works, output file names don't come from input file names
but I think if there are two recipes for %.rel it will anyway execute those with some newer file in the dependencies, right?
I mean as in
assets2banks assets blah blah
sdcc -c blah blah
if some file in the assets folder has changed, make will launch assets2banks program and if some c sources changed it will launch SDCC... and at the end hopefully all the needed rel files will be created/updated, or am I assuming wrong?
||Posted: Fri May 21, 2021 7:40 pm|
I don't usually use actual wildcards, but I think that's correct.
||Posted: Sun May 23, 2021 1:03 pm|
You can have SDCC generate the prerequisites automatically with the -MMD preprocessor option (it's based on GCC so many of the same flags should work). It will generate a .d file for every source file it compiles which can then be included in the makefile.
This is an example from my current makefile
# program executable file name.
PROG = worms
CC = sdcc
LD = sdcc
# delete command
RM = del /f
# Compiler flags go here.
CFLAGS = -MMD -c -mz80 --peep-file peep-rules.txt
# Linker flags go here.
LDFLAGS = -mz80 --no-std-crt0 --data-loc 0xC000
CRT0 = crt0_sms.rel
SMSLIB = SMSlib.lib
OBJS = $(PROG).rel bank2.rel
DEPS = $(OBJS:.rel=.d)
ihx2sms $< [email protected]
-$(LD) -o [email protected] $(LDFLAGS) $(CRT0) $^ $(SMSLIB)
$(CC) $(CFLAGS) $<
-$(RM) *.sms *.ihx $(OBJS) $(DEPS)
This way any changes to the headers will trigger a rebuild.