#!/bin/sh # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # # This file is part of dobuild. # Copyright (c) 2019 Contributors as noted in the AUTHORS file. # # SPDX-License-Identifier: MPL-2.0 set -e # Derived based on https://stackoverflow.com/questions/4219255/how-do-you-get-the-list-of-targets-in-a-makefile # # Note: This relies on -p printing the database nonetheless, which is the case as of GNU make 3.82. # Sadly, GNU make offers no direct option to just print the database. # # Invokes make in order to print the database derived from a makefile: # -p prints the database # -Rr suppresses inclusion of built-in rules and variables # -q only tests the up-to-date-status of a target (without remaking anything), but that by itself doesn't # prevent execution of recipe commands in all cases # "$@" list of separate commandline parameters (additional make parameters) # : is a deliberately invalid target that is meant to ensure that no commands are executed; # 2>&1 1>&3 # redirect stderr to stdout and stdout to fd 3 to allow filtering unwanted error message # grep -v '.*\s\+No rule to make target\s\+.*:.*' 1>&2 # suppresses the resulting error message - "make: *** No rule to make target ':'. Stop." and redirect # other output to stderr # 3>&1 redirect fd 3 back to stdout print_make_db() { { "$MAKE" -pRrq "$@" : 2>&1 1>&3 | grep -v '.*\s\+No rule to make target\s\+.*:.*' 1>&2; } 3>&1 } export LANG=C export LC_ALL=C MAKE="${MAKE:-make}" print_make_db "$@" \ | awk -v RS= -F: '/^# File/,/^# Finished Make database/ {if ($1 !~ "^[#.]") {print $1}}' \ | sort \ | grep -v -e '^[^[:alnum:]]' -e '[%]$' # Pipe-chain explanation: # awk -v RS= -F: '/^# File/,/^# Finished Make database/ {if ($1 !~ "^[#.]") {print $1}}' # v RS= this is an awk idiom that breaks the input into blocks of contiguous non-empty lines. # -F: input field speparator # /^# File/,/^# Finished Make database/ # matches the range of lines in the output that contains all targets (true as of GNU make 3.82) - by limiting # parsing to this range, there is no need to deal with false positives from other output sections. # if ($$1 !~ "^[#.]") # selectively ignores blocks: # # ... ignores non-targets, whose blocks start with # Not a target: # . ... ignores special targets # all other blocks should each start with a line containing only the name of an explicitly defined target # followed by : # sort # sorts the resulting list of targets, which is the best option, since not sorting doesn't produce a helpful # ordering in that the order in which the targets appear in the makefile is not preserved. # grep -v -e '^[^[:alnum:]]' -e '[%]$$' removes unwanted targets from the output: # -v revert machtes # -e '^[^[:alnum:]]' # ... hidden targets, which - by convention - are targets that start neither with a letter nor a digit. # -e '[%]$$' # ... targets ending with % (pattern targets)