为什么GNU会定义隐式模式和隐式后缀规则?

时间:2013-05-30 17:57:13

标签: makefile gnu-make

查看Catalogue of Implicit Rules几个陈述让我想知道:

  

默认后缀列表为:.out,.a,.ln,.o,.c,.cc,.C,.cpp,.p,.f,.F,.m,.r,.y ,.l,.ym,.lm,.s,.S,。mod,.sym,.def,.h,。info,.dvi,.tex,.texinfo,.texi,.txinfo,.w ,. ch .web,.sh,.elc,.el。   下面描述的所有隐含规则,其先决条件都有这些后缀之一,实际上是后缀规则。

嗯,不是吗?

$ make -p -f/dev/null | grep '%.o: %\.c$' -A2
%.o: %.c
#  recipe to execute (built-in):
    $(COMPILE.c) $(OUTPUT_OPTION) $<

后缀.c在列表中,但是它被定义为模式规则?

好的,它还定义了一个后缀规则:

make -p -f/dev/null | grep '^.c.o:' -A5
.c.o:
#  Implicit rule search has not been done.
#  Modification time never checked.
#  File has not been updated.
#  recipe to execute (built-in):
    $(COMPILE.c) $(OUTPUT_OPTION) $<

但为什么?

后缀规则在模式规则之后定义。那么模式规则应该优先,对吧?

但是,既然行动是平等的,那又有什么意义呢?

2 个答案:

答案 0 :(得分:4)

简答:

应用列出的模式规则。后缀规则仅列出但未使用。

答案很长:

使用make -p -f /dev/null转储Make的内部数据库时,它会打印预定义的规则和变量。输出结构分为不同的部分。例如,我的Make(v3.81)返回以下内容:

# GNU Make 3.81
# Copyright (C) 2006  Free Software Foundation, Inc.
# This is free software; see the source for copying conditions.
# There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.

# This program built for i686-pc-linux-gnu make: *** No targets.  Stop.

# Make data base, printed on Fri May 31 12:48:50 2013

# Variables 
...
# Directories 
...
# Implicit Rules 
...
# Pattern-specific variable values 
...
# Files 
...
# VPATH Search Paths
....

Managing Projects with GNU Make and the GNU Make manual解释了以下不同部分:

  

“变量”部分列出了每个变量以及一个描述性变量   评论。但是,未列出自动变量。

     

“目录”部分对于制作开发人员比制作人员更有用   用户。它列出了Make检查的目录,包括SCCS   和RCS子目录可能存在,但通常不存在。对于每一个   目录,Make显示实现细节,例如设备   数字,索引节点和文件模式匹配的统计信息。

     

隐含规则部分如下。这包含所有内置和   make的数据库中用户定义的模式规则。

     

下一节对目录中定义的特定于模式的变量进行编目   makefile。回想一下特定于模式的变量是可变的   定义的范围恰好是其执行时间   相关模式规则。

     

“文件”部分将列出并列出所有显式和后缀规则   与特定文件有关。 [此上下文中的文件表示目标]

     

最后一节标有VPATH。它包含搜索路径和列表   VPATH的值和所有vpath模式。

因此,应用的规则列在“隐式规则”部分中。后缀规则列为“文件”部分的一部分。

答案很长:

每个人都知道源是唯一真正的文档;)因此,对于GNU精神,我冒险进入Make的源代码,试图找出它的作用。 免责声明:我只花了大约两个小时 - 足够长的时间来全面了解Make的架构。

初始化阶段: main.c:第1600行(我只删除了几个处理不同平台的 #ifdefs ):

/* Define the initial list of suffixes for old-style rules.  */
set_default_suffixes ();  

/* Define the file rules for the built-in suffix rules.  These will later
 be converted into pattern rules.  We used to do this in
 install_default_implicit_rules, but since that happens after reading
 makefiles, it results in the built-in pattern rules taking precedence
 over makefile-specified suffix rules, which is wrong.  */
install_default_suffix_rules ();

/* Define some internal and special variables.  */
define_automatic_variables ();

/* Set up the MAKEFLAGS and MFLAGS variables
 so makefiles can look at them.  */
define_makeflags (0, 0);

/* Define the default variables.  */
define_default_variables ();

default_file = enter_file (strcache_add (".DEFAULT"));

default_goal_var = define_variable_cname (".DEFAULT_GOAL", "", o_file, 0);

// I removed the block that evalutes user input entered 
// through the `--eval` switch for brevity
[...]

/* Read all the makefiles.  */
read_makefiles = read_all_makefiles (makefiles == 0 ? 0 : makefiles->list);

/* Set up MAKEFLAGS and MFLAGS again, so they will be right.  */
define_makeflags (1, 0);

/* Make each `struct dep' point at the `struct file' for the file
 depended on.  Also do magic for special targets.  */
snap_deps ();

/* Convert old-style suffix rules to pattern rules.  It is important to
 do this before installing the built-in pattern rules below, so that
 makefile-specified suffix rules take precedence over built-in pattern
 rules.  */
convert_to_pattern ();

/* Install the default implicit pattern rules.
 This used to be done before reading the makefiles.
 But in that case, built-in pattern rules were in the chain
 before user-defined ones, so they matched first.  */
install_default_implicit_rules ();

/* Compute implicit rule limits.  */
count_implicit_rule_limits ();

/* Construct the listings of directories in VPATH lists.  */
build_vpath_lists ();
  1. 函数set_default_suffixes()仅定义.SUFFIXES变量,仅此而已。
  2. 接下来,通过install_default_suffix_rules()函数定义默认后缀规则。它将内置后缀规则添加为目标。
  3. 我要跳过接下来的两个函数,跳转到define_default_variables(),这也是非常自我解释的。它定义了ARCH,AR,CC,CXX等。基本上Make的默认环境现在已经设置好了。
  4. 之后定义.DEFAULT和.DEFAULT_GOAL目标。它们分别记录在Special-TargetsSpecial-Variables中。
  5. 然后通过read_all_makefiles()评估Makefile。 Make的评估可能很棘手。但是,基本思路很简单:在文件中循环。策略是累积FILENAMES中的目标名称,DEPS中的依赖项和COMMANDS中的命令。这些用于在遇到下一个规则(或eof)的开始时定义规则。在我们的示例中,可以忽略此步骤,因为我们使用-f /dev/null调用了Make。
  6. 然后snap_deps()将目标与其依赖关联起来。它们存储为dep结构的链接列表(在dep.h中定义)。
  7. 现在是有趣的部分。 Convert_to_pattern()遍历.SUFFIXES列表中定义的后缀,将等效模式规则转换为追加它们到退出模式规则链。这正是您引用的部分中记录的内容。由于步骤5中已包含所有用户定义的规则,因此它们优先于已转换的内置后缀规则。
  8. 构建阶段:

    稍后,当Make尝试构建目标时,它仅搜索规则数据库。规则搜索在implicit.c。

    中实现

    为什么规则在打印数据库输出中显示为双倍?

    最后,我查看了--print-database开关背后的逻辑。

    main.c:第3078行:

    /* Print a bunch of information about this and that.  */
    
    static void
    print_data_base ()
    {
      time_t when;
    
      when = time ((time_t *) 0);
      printf (_("\n# Make data base, printed on %s"), ctime (&when));
    
      print_variable_data_base ();
      print_dir_data_base ();
      print_rule_data_base ();
      print_file_data_base ();
      print_vpath_data_base ();
      strcache_print_stats ("#");
    
      when = time ((time_t *) 0);
      printf (_("\n# Finished Make data base on %s\n"), ctime (&when));
    }
    

    print_rule_data_base()函数非常简单地打印所有“活动”规则。所有现有的后缀规则以前也已转换为模式规则。

    print_file_data_base()函数列出了所有目标。后缀规则仍在那里。实际上,似乎没有从数据库中删除目标的功能。但是,根据我收集的内容,后缀 - 目标在其他方面未被使用。

答案 1 :(得分:2)

答案非常简短:后缀规则的定义是因为POSIX make requires them to be built-in。请参阅名为“默认规则”的部分。模式规则是(非POSIX)GNU值添加。