Hacker News new | ask | show | jobs
by apples2apples 4437 days ago
Sure, consider a compiler that produces an (foo.o) object file and an annotation (foo.a). Now if a target requires both foo.o and foo.a you have to create two targets on them (even though its really one command).

You can do implicit rules which requires a very verbose makefile, which is what automake and other make generation tools do. God help you figure out what went wrong.

If you make people go to a directory approach you've now imposed a new structure on their code. One reason for the multitude of packages is each one matches their target community better.

4 comments

Huh? Doesn't this work:

    all: copied o a
    source:
    	echo "message" > source
    foo.o foo.a: source
    	(echo -n ".o: "; cat source) > foo.o
    	(echo -n ".a: "; cat source) > foo.a
    o: foo.o
    	cat foo.o > o
    a: foo.a
    	cat foo.a > a
    copied: foo.o foo.a
    	cat foo.o foo.a > copied
The third rule simulates a compiler producing two outputs. Now if foo.o changes, both "copied" and "o" will be updated, and if foo.a changes, both "copied" and "a" will be updated. (And if either foo.o or foo.a are deleted, the compiler will be rerun, as will everything depending on foo.a or foo.o.)

This is gnu make 4.0.

I don't understand.

If both the .o and the .a are created from another file, wouldn't it be safe to just rely on either one of them? (Obviously, you will need to be consistent in choice.)

That is, if every time a .o is created, so is the .a, then where is the difficulty? Just rely on one (the .o). I could conceive of a scenario where the .a updated but the .o didn't, but I don't know of any tools that really work that way right now. I thought the norm was to at least touch all output files.

Further, if that is happening, seems you are safest having two rules, anyway.

Say you have a long build process and do a quick semi-clean by hand to speed up the next buld (not the best idea, but not inconceivable), deleting the .a files, but fogetting to delete the matching .o files. Then, your next build will produce some novel (to you) error messages that may take long to clean up. Worse, the command building on the .o and the .a might just say "OK, I'm given a .o without a .a; fine, then I'll do a slightly different thing"

Also, having two rules means duplicating a command:

    foo: foo.a
        baz $(BAZ_OPTIONS) foo

    foo: foo.o
        baz $(BAZ_OPTIONS) foo
That's bad from a maintenance perspective.
Invoking the command twice can also screw up things if you run parallel build, which you should always do! Not only to speed things up it's also a good way to verify that your make file actually is correct. If your make file doesn't work in parallel build it is broken, in the same way as C code that breaks at -O2 and above due to reliance on undefined behavior.

The solution to the multiple target problem is using the built in magic .INTERMEDIATE rule which isn't entirely obvious how it works.

Ok, that makes sense. I'm tempted to rattle the knee jerk, "don't go deleting random crap," but I realize that is a hollow response.

I'm curious how .INTERMEDIATE helps in this case. I did find this link[1], which was a rather fun read down how one might go about solving this, along with all of the inherent problems.

[1] http://www.gnu.org/software/automake/manual/html_node/Multip...

I was able to do that with GNU Make. Granted, the syntax is a bit ... odd, but it was doable.

    ASM = A09/a09

    %.o %.l : %.a
            $(ASM) -o $(*F).o -l $(*F).l $(*F).a

    clean:
            /bin/rm -rf *.o *.l

    foo : disasm.l
    bar : disasm.o
    baz : disasm.l disasm.o
The target baz has both a .l and a .o, both of which are produced in one command. The line that begins with "%.o" starts an implicit rule, which loosely states, in English: "to produce a .o file, or a .l file, run the following ...". $(*F) is a GNUism that maps to the filename of the source (directory part, if any, is stripped). This works. I tested all three targets (foo, bar, baz) with a "make clean" between each one.

(and for the really curious, a09 is a 6809 assembler; disasm.a is a 6809 diassembler, written in 6809; binary is a 2K relocatable library)

Or if you don't like taeric's suggestion you can just touch a .ao file after the line that creates the .a and .o files and have your further rule(s) depend on that .ao file. Have .ao depend on your source. If you still want to be able to type stuff like 'make foo.a' instead of 'make foo.ao' and have it work, then you can make a rule where .a depends on .ao and all the rule does is touch the .a file. Create the same rule for the .o too.