At first this seems pretty good. Now there is only one rule that can update the files, so there's no risk of duplicating work or corrupting outputs. In a from-scratch build, this construct will work fine. But it has some trouble in incremental builds. What happens if you delete just parser.h, but not parser.c? Since there are no commands specified for generating parser.h, make will not know how to produce it, and since parser.c is already up-to-date, it will not run the commands that would generate the files. Even if you explicitly ask make to build parser.h, by running "gmake parser.h", you're stuck:
gmake: Nothing to be done for `parser.h'.
If you only do from-scratch full builds, this solution may work for you, but if you do incrementals as well, use caution.
Another approach is to use a dummy target to do the work, and have the actual output files depend on the dummy:
all: parser.h parser.c
parser.h parser.c: generate_parser
@echo Generating parser.h and parser.c from parser.i
@touch parser.c parser.h
This looks promising: We have a single rule that generates both files, so it will work correctly in a parallel build. But this approach has one significant drawback: it breaks the relationship between the input file and the output files derived from it. We no longer have a depedency that says, "if parser.i is newer than parser.c or parser.h, rebuild those files." Instead, we have a dependency that says, "if parser.i is newer than 'generate_parser' (whatever that is), rebuild it". This makefile will rebuild parser.c and parser.h every time it is run, because make is comparing the times on parser.c and parser.h with generate_parser. Since generate_parser doesn't exist, make will run that rule. It doesn't matter if parser.i is older than parser.c and parser.i, because there is no direct relationship between those files in this makefile.
We can work around this by changing the generate_parser rule so that it also creates a file on disk named "generate_parser"; then on an incremental build, make will see that the file "generate_parser" is newer than parser.i and will not rebuild. But this is messy: we'll have an extra file hanging around that serves no purpose other than to work around a deficiency in the build tool, and we need to remember to manage that file along with the other outputs of the build. It should be deleted by "make clean", for example. And if somebody does something like "touch generate_output" in between builds, that make may not be able to correctly detect that parser.c and parser.h must be rebuilt. As with the previous solution, if you only do from-scratch full builds, this solution will work fine, but with incrementals you need to be careful.