Search code examples
functionmakefilemultilineintrospection

Printing variables including functions from Makefile and/or variable introspection


If you iterate over .VARIABLES and print each, any true variable can be printed correctly with the following rule:

print_variables: $(foreach V,$(.VARIABLES),print-$(V)) .phony_explicit
print-%: .phony_explicit; @echo "$* = \"$($*)\""
.PHONY: .phony_explicit ...

A 0- or 1-line function will still work, but any more will result in Syntax error: Unterminated quote string. Just one multiline function will break the entire print_variables rule. As a workaround, I have added ;\ to each line in my function definitions, but that won't fix existing multiline functions (either via includes from this makefile or via other makefiles including this one.) What can I do? Is there a container of just function variables, or a way to test if a variable is a function definition?


Solution

  • A simple minimal example would be easier to understand; this has nothing to do with .VARIABLES, pattern rules, etc. (and I'm not sure what the point of the .phony_explicit prereq is..)

    define F
    foo
    bar
    endef
    
    print: ; echo "F = $(F)"
    

    will show the problem:

    echo "F = foo
    /bin/sh: 1: Syntax error: Unterminated quoted string
    

    This is because when make sees a variable that contains newlines in a recipe, it assumes that the newlines mean you want the lines of the variable to become lines in the recipe.

    First in general you should use single-quotes around strings you send to the shell, unless you need the shell to expand them; it won't help in this situation but in general it's much safer.

    There's no way to undo that, really. You have a number of options.

    The first is to not use echo but instead use the make function info:

    print-F: ; $(info F = "$(F)")
    

    yields:

    F = "foo
    bar"
    

    Another option is to use subst to replace the newlines with some other value. The new value cannot itself contain explicit newlines, but you can ask the shell to print a newline for you:

    # Create a variable containing a single newline
    # Note this must contain TWO newlines!
    define NL
    
    
    endef
    
    print-F: printf 'F = "$(subst %,%%,$(subst $(NL),\n,$(F))"\n'
    

    Yields:

    printf 'F = "foo\nbar"\n'
    F = "foo
    bar"
    

    One final option is to convert your makefile to use the .ONESHELL feature, but I assume that's a step too far just to get this debugging output available :).