如何在makefile中获得目标列表?

如何在makefile中获得目标列表?,makefile,gnu-make,targets,Makefile,Gnu Make,Targets,我曾经使用过rake(一个Ruby make程序),它有一个选项可以获取所有可用目标的列表,例如 > rake --tasks rake db:charset # retrieve the charset for your data... rake db:collation # retrieve the collation for your da... rake db:create # Creates the databases defined in y...

我曾经使用过rake(一个Ruby make程序),它有一个选项可以获取所有可用目标的列表,例如

> rake --tasks
rake db:charset      # retrieve the charset for your data...
rake db:collation    # retrieve the collation for your da...
rake db:create       # Creates the databases defined in y...
rake db:drop         # Drops the database for your curren...
...
但在GNU make中似乎没有这样做的选项

很明显,从2007年开始,代码就差不多在那里了

无论如何,我做了一点小技巧,从makefile中提取目标,您可以将其包含在makefile中

list:
    @grep '^[^#[:space:]].*:' Makefile
它将为您提供一个已定义目标的列表。这只是一个开始——例如,它不会过滤掉依赖项

> make list
list:
copy:
run:
plot:
turnin:

我结合了这两个答案:和 并进行了一些转义,以便可以从makefile内部使用

.PHONY: no_targets__ list
no_targets__:
list:
    sh -c "$(MAKE) -p no_targets__ | awk -F':' '/^[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {split(\$$1,A,/ /);for(i in A)print A[i]}' | grep -v '__\$$' | sort"

在Bash下(至少),这可以通过制表符完成自动完成:


make
spacetab

如果安装了
make
的bash completion,则完成脚本将定义一个函数
\u make\u target\u extract\u script
。此函数用于创建一个
sed
脚本,该脚本可用于以列表形式获取目标

像这样使用它:

# Make sure bash completion is enabled
source /etc/bash_completion 

# List targets from Makefile
sed -nrf <(_make_target_extract_script --) Makefile
.PHONY: target1 # Target 1 help text
target1: deps
    [... target 1 build commands]

.PHONY: target2 # Target 2 help text
target2:
    [... target 2 build commands]

...                                                                                                         

.PHONY: help # Generate list of targets with descriptions                                                                
help:                                                                                                                    
    @grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1 \2/' | expand -t20
#确保已启用bash完成
来源/etc/bash_完成
#列出Makefile中的目标

sed-nrf这是对以下方面进行改进的尝试:

  • 使用一个更健壮的命令来提取目标名称,这有望防止任何误报(同时也消除了不必要的
    sh-c
  • 不总是以当前目录中的makefile为目标;使用
    -f
  • 排除隐藏目标-按照惯例,这些目标的名称既不以字母开头,也不以数字开头
  • 用一个假目标凑合
  • 在命令前面加上
    @
    ,以防止在执行前回显该命令

奇怪的是,GNU
make
没有只列出makefile中定义的目标名称的特性。当
-p
选项生成包含所有目标的输出时,它将这些目标隐藏在许多其他信息中,并执行默认目标(可以使用
-f/dev/null
抑制)

将以下规则放置在GNU
make
的makefile中,以实现名为
list
的目标,该目标仅按字母顺序列出所有目标名称,即:调用为
makelist

.PHONY: list
list:
    @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$'
重要信息:粘贴时,确保最后一行正好缩进1个实际制表符。(空格不起作用)

请注意,对生成的目标列表进行排序是最好的选择,因为不进行排序不会产生有用的排序,因为目标在makefile中的显示顺序不会被保留。
此外,包含多个目标的规则的子目标总是单独输出,因此,由于排序,通常不会出现在彼此旁边;e、 例如,如果存在其他目标,则以
a z:
开头的规则将不会在输出中相邻列出目标
a
z

规则解释

.PHONY: list
list:
    @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$'
  • .
    PHONY:list

    • 声明目标列表为虚假目标,即未引用文件的目标,因此应无条件调用其配方
  • $(MAKE)-pRrq-f$(lastword$(MAKEFILE_LIST)):2>/dev/null

    • 再次调用
      make
      ,以打印和解析从makefile派生的数据库:
      • -p
        打印数据库
      • -Rr
        禁止包含内置规则和变量
      • -q
        仅测试目标的最新状态(无需重新生成任何内容),但这本身并不妨碍在所有情况下执行配方命令;因此:
      • -f$(lastword$(MAKEFILE_LIST))
        确保以原始调用中相同的MAKEFILE为目标,而不管它是以
        -f…
        隐式还是显式为目标 警告如果makefile包含
        include
        指令,则此命令将中断;要解决此问题,请在任何
        包含
        指令之前定义变量
        此文件:=$(lastword$(MAKEFILE\u LIST))
        ,并改用
        -f$(此文件)
      • 是一个故意无效的目标,用于确保不执行任何命令<代码>2>/dev/null
抑制生成的错误消息。注意:这依赖于
-p
打印数据库,GNU make 3.82就是这种情况。遗憾的是,gnumake没有提供直接选项来打印数据库,也没有执行默认(或给定)任务;如果不需要针对特定的Makefile,可以使用
make-p-f/dev/null
,如
man
页面中所建议的那样
  • -vrs=

    • 这是一个awk习惯用法,它将输入分成连续的非空行块
  • /^文件/,/^成品制作数据库/

    • 匹配包含所有目标的输出中的行范围(自GNU make 3.82起为true)-通过将解析限制在此范围内,无需处理来自其他输出部分的误报
  • if($$1!~“^[#.]”)

    • 选择性地忽略块:
      • #
        。。。忽略非目标,其块以
        开头#不是目标:
      • 。。。忽视特殊目标
    • 所有其他块都应以一行开头,该行仅包含明确定义的目标的名称,后跟
  • egrep-v-e'^[^[:alnum:]'-e'^$@$$'
    从输出中删除不需要的目标:

    • “^[^[:alnum:][]”
      。。。排除隐藏目标,根据惯例-
      list:
          cat Makefile | grep "^[A-z]" | awk '{print $$1}' | sed "s/://g" 
      
      $ make help
      The following are some of the valid targets for this Makefile:
      ... all (the default if no target is provided)
      ... clean
      ... depend
      ... install
      etc
      
      .PHONY: target1 # Target 1 help text
      target1: deps
          [... target 1 build commands]
      
      .PHONY: target2 # Target 2 help text
      target2:
          [... target 2 build commands]
      
      ...                                                                                                         
      
      .PHONY: help # Generate list of targets with descriptions                                                                
      help:                                                                                                                    
          @grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1 \2/' | expand -t20
      
      $ make help
      target1             Target 1 help text
      target2             Target 2 help text
      
      ...
      help                Generate list of targets with descriptions
      
      cat Makefile | egrep "^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$"
      
      make -p 2&>/dev/null | grep -A 100000 "# Files" | grep -v "^$" | grep -v "^\(\s\|#\|\.\)" | grep -v "Makefile:" | cut -d ":" -f 1
      
      .PHONY: list ls
      ls list :
          @# search all include files for targets.
          @# ... excluding special targets, and output dynamic rule definitions unresolved.
          @for inc in $(MAKEFILE_LIST); do \
          echo ' =' $$inc '= '; \
          grep -Eo '^[^\.#[:blank:]]+.*:.*' $$inc | grep -v ':=' | \
          cut -f 1 | sort | sed 's/.*/  &/' | sed -n 's/:.*$$//p' | \
          tr $$ \\\ | tr $(open_paren) % | tr $(close_paren) % \
      ; done
      
      # to get around escaping limitations:
      open_paren := \(
      close_paren := \)
      
       = Makefile = 
        includes
        ls list
       = util/kiss/snapshots.mk = 
        rotate-db-snapshots
        rotate-file-snapshots
        snap-db
        snap-files
        snapshot
       = util/kiss/main.mk = 
        dirs
        install
         %MK_DIR_PREFIX%env-config.php
         %MK_DIR_PREFIX%../srdb
      
      .PHONY: all
      all: build
              : "same as 'make build'"
      
      .PHONY: build
      build:
              @echo "Build the project"
      
      .PHONY: clean
      clean:
              @echo "Clean the project"
      
      .PHONY: help
      help:
              @echo -n "Common make targets"
              @echo ":"
              @cat Makefile | sed -n '/^\.PHONY: / h; /\(^\t@*echo\|^\t:\)/ {H; x; /PHONY/ s/.PHONY: \(.*\)\n.*"\(.*\)"/    make \1\t\2/p; d; x}'| sort -k2,2 |expand -t 20
      
      $ make help
      Common make targets:
          make all        same as 'make build'
          make build      Build the project
          make clean      Clean the project
          make help       Common make targets
      
      cat Makefile | awk '!/SHELL/ && /^[A-z]/ {print $1}' | awk '{print substr($0, 1, length($0)-1)}'
      
      install_targets = install-xxx1 install-xxx2 ... etc
      
      make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'
      
      make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}' | sort -u
      
      help:
          @grep -B1 -E "^[a-zA-Z0-9_-]+\:([^\=]|$$)" Makefile \
           | grep -v -- -- \
           | sed 'N;s/\n/###/' \
           | sed -n 's/^#: \(.*\)###\(.*\):.*/\2###\1/p' \
           | column -t  -s '###'
      
      
      #: Starts the container stack
      up: a b
        command
      
      #: Pulls in new container images
      pull: c d 
          another command
      
      make-target-not-shown:
      
      # this does not count as a description, so leaving
      # your implementation comments alone, e.g TODOs
      also-not-shown:
      
      > make help
      up          Starts the container stack
      pull        Pulls in new container images
      
      grep : Makefile | awk -F: '/^[^.]/ {print $1;}'
      
      help:
        make -qpRr $(lastword $(MAKEFILE_LIST)) | egrep -v '(^(\.|:|#|\s|$)|=)' | cut -d: -f1
      
      RANDOM_VARIABLE := this will be expanded in help text
      
      .PHONY: target1 # Target 1 help with $(RANDOM_VARIABLE)
      target1: deps
          [... target 1 build commands]
      
      # TARGETDOC: $(BUILDDIR)/real-file.txt # real-file.txt help text
      $(BUILDDIR)/real-file.txt:
          [... $(BUILDDIR)/real-file.txt build commands]
      
      .PHONY: help # Generate list of targets with descriptions
      help:
          @# find all help in targets and .PHONY and evaluate the embedded variables
          $(eval doc_expanded := $(shell grep -E -h '^(.PHONY:|# TARGETDOC:) .* #' $(MAKEFILE_LIST) | sed -E -n 's/(\.PHONY|# TARGETDOC): (.*) # (.*)/\2  \3\\n/'p | expand -t40))
          @echo
          @echo ' TARGET   HELP' | expand -t40
          @echo ' ------   ----' | expand -t40
          @echo -e ' $(doc_expanded)'
      
      # vim:ft=make
      # Makefile
      
      .DEFAULT_GOAL := help
      .PHONY: test help
      
      help:  ## these help instructions
          @sed -rn 's/^([a-zA-Z_-]+):.*?## (.*)$$/"\1" "\2"/p' < $(MAKEFILE_LIST) | xargs printf "make %-20s# %s\n"
      
      lint: ## style, bug and quality checker
          pylint src test
      
      private: # for internal usage only
          @true
      
      test: private ## run pytest with coverage
          pytest --cov test
      
      
      
      $ make
      make help                # these help instructions
      make lint                # style, bug and quality checker
      make test                # run pytest with coverage
      
      APPS?=app1 app2
      
      bin: $(APPS:%=%.bin)
          @# Help: A composite target that relies only on other targets
      
      $(APPS:%=%.bin): %.bin:
          echo "build binary"
          @# Help: A target with variable name, value = $*
      
      test:
          echo $(MAKEFLAGS)
          echo "starting test"
          @# Help: A normal target without variables
      
      # A target without any help description
      clean:
          echo $(MAKEFLAGS)
          echo "Cleaning..."
      
      MAKEOVERRIDES =
      help:
          @printf "%-20s %s\n" "Target" "Description"
          @printf "%-20s %s\n" "------" "-----------"
          @make -pqR : 2>/dev/null \
              | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' \
              | sort \
              | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' \
              | xargs -I _ sh -c 'printf "%-20s " _; make _ -nB | (grep -i "^# Help:" || echo "") | tail -1 | sed "s/^# Help: //g"'
      
      $ make help
      Target               Description
      ------               -----------
      app1.bin             A target with variable name, value = app1
      app2.bin             A target with variable name, value = app2
      bin                  A composite target that relies only on other targets
      clean
      test                 A normal target without variables
      
      help:
          @echo "\nTARGETS:\n"
          @make -qpRr | egrep -e '^[a-z].*:$$' | sed -e 's~:~~g' | sort
          @echo ""