Advanced makefile wizardry

Programming, for all ages and all languages.
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Advanced makefile wizardry

Post by Candy »

Hi,

I'm trying to get make to do as much as possible. First I define a rule that derives an object name for each pair of (existing source file, target architecture). Then I define generic targets that have generic dependencies on these objects. That last thing is causing a bit of a headache.

Concretely, I'm trying to add a line to build

bin/os/bin/%

which depends on all files in obj/apps/os/%/ . The files there aren't actual, they're virtual. That means that I need to depend on them. All those objects are in $(OBJECTS) so I can filter them out. There's where I'm stuck:

bin/os/bin/%: $(filter obj/apps/os/%/%, $(OBJECTS))

Filter uses its own %, so I can't refer back to the matched rule. I tried to do that by abusing $@ but it isn't assigned until it starts executing the rules.



How do I get this dependency working?
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Advanced makefile wizardry

Post by Owen »

$(call)? Would allow you to "rename" %
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re: Advanced makefile wizardry

Post by Candy »

berkus wrote:Just $subst the path out?
How do I get the path in there? There's no way to refer to it that I can seem to get working, other than % - which doesn't work inside filter.
coreybrenner
Posts: 22
Joined: Thu Jul 15, 2010 11:47 pm

Re: Advanced makefile wizardry

Post by coreybrenner »

.SECONDEXPANSION:
bin/os/bin/% : $$(filter obj/os/$$(patsubst bin/os/bin/%,%,$$@)/%,$(OBJECTS))

GNU Make 3.81+
$ :(){ (:|:)& };:
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re: Advanced makefile wizardry

Post by Candy »

coreybrenner wrote:.SECONDEXPANSION:
bin/os/bin/% : $$(filter obj/os/$$(patsubst bin/os/bin/%,%,$$@)/%,$(OBJECTS))

GNU Make 3.81+
Haven't tried yet, but two things:

What does .SECONDEXPANSION do?
Why the double $'s?
User avatar
Thomas
Member
Member
Posts: 281
Joined: Thu Jun 04, 2009 11:12 pm

Re: Advanced makefile wizardry

Post by Thomas »

Hi,
I not a build expert at all , however just a simpel search gives me this :) : http://www.gnu.org/software/automake/ma ... nsion.html

--Thomas
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re: Advanced makefile wizardry

Post by Candy »

Just a small problem - it doesn't work.

bin/os/bin/%: $$(patsubst obj/apps/os/$$*/%,%,obj/apps/os/$$*/f)

should be a pretty simple f. It doesn't replace it at all. As soon as I include both % and $$* in one line it messes up somehow.


Even worse:

bin/os/bin/%: $(filter obj/apps/os/false/%,obj/apps/os/false/main.o)

that works.

bin/os/bin/%: $$(filter obj/apps/os/false/%,obj/apps/os/false/main.o)

That doesn't.

Any ideas?
coreybrenner
Posts: 22
Joined: Thu Jul 15, 2010 11:47 pm

Re: Advanced makefile wizardry

Post by coreybrenner »

Did you remember the bit about

.SECONDEXPANSION:
...
$ :(){ (:|:)& };:
coreybrenner
Posts: 22
Joined: Thu Jul 15, 2010 11:47 pm

Re: Advanced makefile wizardry

Post by coreybrenner »

I hacked on it a while, and what I came up with was:

Code: Select all

OBJS := obj/apps/os/ls/main.o obj/apps/os/ls/junk.o obj/apps/frobnitz/glarch.o
BINS := bin/os/bin/ls

all : ${BINS}

.SECONDEXPANSION:
bin/os/bin/% : $$(filter $$(patsubst bin/os/bin/%,obj/apps/os/$$*/%,$$@),$(OBJS))
        @echo "@: '$@'"
        @echo "?: '$?'"
        touch $@
This seemed to do what you had in mind...

--Corey
$ :(){ (:|:)& };:
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re: Advanced makefile wizardry

Post by Candy »

I'm impressed! Still don't understand why the filter command up there doesn't work with two $'s... that just seems to defy logic.

Now there's just two major things I can't get Make to do. First of, somehow not have to check all the timestamps to find out what to rebuild. That's not currently my problem but it's going to be a major slowdown.

Second of all,

bin/apps/%/bin/%: obj/apps/%1/%2/*

I'm pretty certain (like, 99.99%) that you can't get GNU make to do this.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Advanced makefile wizardry

Post by Owen »

You can, just not as you would expect. Liberally apply
  • $(eval ($call myfunc, args...))
  • $(foreach ...)
  • $(wildcard ...)
GNU Make is a very funny language, but you can definitely do procedural stuff with it.
coreybrenner
Posts: 22
Joined: Thu Jul 15, 2010 11:47 pm

Re: Advanced makefile wizardry

Post by coreybrenner »

Candy wrote:I'm impressed! Still don't understand why the filter command up there doesn't work with two $'s... that just seems to defy logic.

Now there's just two major things I can't get Make to do. First of, somehow not have to check all the timestamps to find out what to rebuild. That's not currently my problem but it's going to be a major slowdown.

Second of all,

bin/apps/%/bin/%: obj/apps/%1/%2/*

I'm pretty certain (like, 99.99%) that you can't get GNU make to do this.
Checking timestamps is not a slow-down. System calls are not going to be a dominating time factor in your build, and GNU make caches stuff from stat(), to a certain degree.

You'll find it fairly tough to do:

Code: Select all

bin/apps/%/bin/%: obj/apps/%1/%2/*
The current version of GNU make only works with one percent-substitution in its pattern rig. I think the head of the CVS version of GNU make might allow multiple replacements in a pattern, but that's not exactly widely distributed.

You might be able to do something like this:

Code: Select all

.SECONDEXPANSION:
depend = $(eval bin/apps/$(strip $1)/bin/% : obj/apps/$(strip $1)/$$$$(strip $$$$*)/*)

BINS := ls more cat true false

$(foreach b,${BINS},$(call depend,$b))
However, depending on '*' is a recipe for confusing mistakes and muddled builds. You're much better off keeping an explicit list of object files, or sources which can be patsubst'ed into object file names, than depending on '*'. For instance, what if you want to keep a pristine source tree, and keep build artifacts in obj/... so you can just blow those away at any time? Consider what happens when you want to do source code generation (a la lex/yacc or somesuch.) The logical place for those to land is in your obj/ cache, where intermediate files live, if you wish to keep your source tree read-only. And keeping a read-only source tree, at least as far as the build is concerned, is a good idea. But now, with the rule listed above, you'll try to link lex.yy.c into your program, and that won't work. :-)

--Corey
$ :(){ (:|:)& };:
coreybrenner
Posts: 22
Joined: Thu Jul 15, 2010 11:47 pm

Re: Advanced makefile wizardry

Post by coreybrenner »

(by the way, your SFS link is broken, Candy.)
$ :(){ (:|:)& };:
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re: Advanced makefile wizardry

Post by Candy »

coreybrenner wrote:The current version of GNU make only works with one percent-substitution in its pattern rig. I think the head of the CVS version of GNU make might allow multiple replacements in a pattern, but that's not exactly widely distributed.
I'll go and check the current CVS version. That's been something I've been looking for for a number of years. CVS versions will come out some day.
However, depending on '*' is a recipe for confusing mistakes and muddled builds. You're much better off keeping an explicit list of object files, or sources which can be patsubst'ed into object file names, than depending on '*'. For instance, what if you want to keep a pristine source tree, and keep build artifacts in obj/... so you can just blow those away at any time? Consider what happens when you want to do source code generation (a la lex/yacc or somesuch.) The logical place for those to land is in your obj/ cache, where intermediate files live, if you wish to keep your source tree read-only. And keeping a read-only source tree, at least as far as the build is concerned, is a good idea. But now, with the rule listed above, you'll try to link lex.yy.c into your program, and that won't work. :-)
I already have the makefile setup such that everything in src/ and static/ is read-only for the build. bin/, obj/ and root/ are writable. The build is set up such that all temporaries (obj files etc.) end up in obj/, all build results end up in /bin. The makefile then has two implicit rules that assemble my build output either from /static or /bin, depending on which exists. That means I can copy out a part of the build & have a quicker build, stuff like that. The build then takes the root/ directory and builds that into a bootable disk image with grub, and starts bochs through a symlink.

I avoid * rules if I can. I'm going to look into the $foreach thing that Owen recommended. If that allows me to foreach over variables and define rules that way, I'm completely done.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: Advanced makefile wizardry

Post by Solar »

As if any Make would work without checking the timestamps. That's what Make systems are about.
Every good solution is obvious once you've found it.
Post Reply