Search code examples
makefilegnu

How to stop GNU make from interfering with filenames that contain dollar-signs?


So I am compiling java code, which often creates .class files where there is a dollar-sign in the filename. GNU make seems to hate this and keeps interfering with the filenames.

As a simple (contrived) example, suppose I want to create a GNU Makefile task that will do a hexdump on all the .class files in the MyFolder directory (and it's subfolders).

Now suppose MyFolder has the following files:

MyFolder/makefile
MyFolder/sample.java
MyFolder/subdir/abc$1.class
MyFolder/subdir/test.java
MyFolder/xyz.class

The goal is to write a recipe that runs hexdump, passing it all the .class files in MyFolder (and its subfolders). So for the above list of files the final command that GNU make should execute, would be:

hexdump subdir/abc$1.class xyz.class

This should be so easy as falling off a log, but GNU make seems to have gone to extreme pains to make this as difficult as possible. The naive attempt with the following Makefile does not work:

default:
    hexdump $(shell find -name '*.class')

If you run this Makefile, you get the following error:

hexdump ./subdir/abc$1.class ./xyz.class
hexdump: ./subdir/abc.class: No such file or directory
make: *** [makefile:3: default] Error 1

Notice how in the first line of the above output, the command is correct (the filenames contain the dollar-sign character). But then the actual command that get's executed, the "$1" has been evaluated away.

In fact, even the following Makefile fails, when properly escaping the dollar-sign with a double-dollar-sign. Of course I want to write a recipe that doesn't hard-code the filenames, but the files should be found with the $(shell find ...) command

default:
    hexdump subdir/abc$$1.class xyz.class

Even the above Makefile fails with the same error as the first Makefile.

How can I stop GNU make from interfering with my filenames that contain dollar-signs? The java compiler creates these things all the time, so surely someone must have run into this before?


Solution

  • Make will not interpret the $(shell …) output again when used this way in a recipe. The problem is that the shell interprets $1 and expands it to the first (non-existing) parameter, which is the empty string.

    How to fix this really depends on what you are doing when you encounter this problem. GNU make does not offer an arbitrary shell quoting function, and would have problems with file names which contain spaces anyway. You could perhaps try this to avoid parameter substitution for Java class files by surrounding the file names with ':

    hexdump $(patsubst %,'%',$(shell find -name '*.class'))
    

    Another approach would be to create the file list within the shell, using something like:

    find -name -print0 | xargs -0 hexdump
    

    This completely avoids interpretation of the strings by the shell. Even with

    hexdump $$(find -name '*.class')
    

    the shell would not interpret the $ signs in the file names because command substitution output is only subject to word splitting and pathname expansion (globbing), but not parameter substitution.