1. 11
    1. 8

      This is almost adorably naive in its implementation. I guess it’s fine for simple Makefiles, but is not of much use for complex ones, where targets are computed, generated, or included.

      Also, having to add markers manually is a hopeless endeavor.

      1. 2

        I’ve done basically what the author suggests many times and pretty much by definition I don’t need it for anything but the “main” targets that a user is expected to type. So while I totally agree with you that it won’t work in some cases, I suspect that those cases are less common than you seem to imply.

        1. 1

          Then you have been fortunate to work on projects where the Make definitions probably haven’t gotten too messy or rely on something like GNU autotools.

          Except for small, personal projects, I have not ever worked on something where this approach to Makefile documentation was viable. Documenting the “main” targets was always done as part of the README or something similar so that you would know about targets that were conditionally included or generated.

      2. 2

        I’ve done something very similar, but instead of reading all the Makefiles when running the help target, it generates an included file that is updated as a dependency of the Makefiles. (And the delimiter I used was #: rather than ##) You are right that it requires adding the markers manually, but the intent is to document the “main” target commands rather than every possible target.

        help: #: Prints this help
        .SILENT: help.Makefile
        help.Makefile: $(MAKEFILE_LIST)
        	printf '# This file was generated by make\n' help >"$@"
        	printf '.PHONY: %s\n' help >>"$@"
        	printf '%s:\n' help >>"$@"
        	grep -E '^.+: *#: *' $(MAKEFILE_LIST) \
        	| cut -d: -f2,4 \
        	| sort \
        	| awk 'BEGIN {FS = ":"};{printf "\t$$(info %-18s %s)\n",$$1,$$2}' \
        	>>"$@"
        
        -include help.Makefile
        
      3. 1

        generate from make -pn?

    2. 2

      Use remake instead of make. It is completely compatible (remake is a layer over gnu make), and much more powerful. Using remake, it is just

      remake --tasks
      
    3. 2

      Very nice solution. I also find it useful to have a default help task, but I’ve been manually building them. This seems easier to manage. I did tweak one small thing. In the sed, I inserted a pipe into \1|\3 and then told column to -s '|'. I like having more than one word in my task descriptions. I could use a comma, but I use those in descriptions too. Pipe seemed easy, but whatever character you don’t use often would be fine.

    4. 2

      I would rather build on top of existing bash-completion scripts. Try:

      type _make_target_extract_script
      type _make
      

      depending on your distribution, you might have these function. If they are good enough for you, use them. If not and you can write something better, it is worth to contribute and improve the completion scripts/functions.

      Yes, sometimes you can realize that existing solution is total mess or too complex for what it does, and you would rather write it from scratch. But usually it is better to at least look how others do it.

    5. 1

      Here’s how I do it, taken from the bash_completion script for make:

      __load_completion make # to load _make_target_extract_script
      make -npq __BASH_MAKE_COMPLETION__=1 .DEFAULT 2>/dev/null | sed -nf <( _make_target_extract_script --)