Search code examples
shellmakefile

Makefiles: how to get a file name for a rule target from outside, in a portable way


I need to get some external data to form an output file name in a Makefile.

In GNU Make I can do something like this :

NAME=$(shell some_commands)
$(NAME).out: file.in
     compile "$<" -o "$@"

Some other rules have $(NAME).out as a prerequisite so I need $(NAME).out as a target. I can't use substitution+quotes here as this is not interpreted by a shell.

I see from the BSD Make man page that on Mac OS X I should be able to do something like this (I could not test it yet, though) :

NAME!=some_commands
$(NAME).out: file.in
     compile "$<" -o "$@"

Is there a hack to do this kind of things in a portable way?

I do not want to generate the Makefile and clutter the build process. I though of includes, but they also have a different syntax across Make versions.


Solution

  • I can propose several tricks. All them are based on the outsourcing the execution of some_commands to a rule body, and ensuring that their result will be utilized properly.

    1. That's my favourite. Include a makefile, which you create on your own, that contains the rule for $(NAME). At the beginning of processing it will be re-created and the proper target will appear. The thing is that the process of creation of this makefile is done within shell commands:

      include aux_makefile
      .PHONY aux_makefile
      aux_makefile:
          NAME=`some_commands` ; ( \
              echo $$NAME.out: file.in ; \
              echo "\t rm aux_makefile" ; \
              echo "\t compile ..."  ) >$@
      
    2. A variation of first one: make a wrapper makefile that first writes the auxilliary makefile and then recursively calls the original makefile, the auxilliary makefile being included into it. No special build target needed.

    3. Make a dummy target with a static name; it will represent $(NAME).out. I.e.:

      all: dummy    # Instead of all: $(NAME).out
      dummy: file.in
         NAME=`some_commands`; compile "$<" -o "$$NAME"
         touch $@