根据【6】,由选项–include包含的头文件应该在源文件中所包含的头文件之前被引入,但要在被–macro指定的文件处理完成后,才能被处理。如果给出了多个-include选项,那么文件要按在命令行出现的顺序被引入。
finish_options (continue)
1466 include_cursor = 0;
1467 push_command_line_include ();
1468 }
首先在上面的1466行,include_cursor被置为0,表示还没有处理延迟处理选项中的-include选项。注意在下面的函数里,如果我们成功地加入了一个-include选项指定头文件,我们将在1480行返回,留下剩余未处理的-include选项在deferred_opts里。
1471 static void
1472 push_command_line_include (void) in c-opts.c
1473 {
1474 while (include_cursor < deferred_count)
1475 {
1476 struct deferred_opt *opt = &deferred_opts[include_cursor++];
1477
1478 if (! cpp_opts->preprocessed && opt->code == OPT_include
1479 && cpp_push_include (parse_in, opt->arg))
1480 return;
1481 }
1482
1483 if (include_cursor == deferred_count)
1484 {
1485 include_cursor++;
1486 /* -Wunused-macros should only warn about macros defined hereafter. */
1487 cpp_opts->warn_unused_macros = warn_unused_macros;
1488 /* Restore the line map from <command line>. */
1489 if (! cpp_opts->preprocessed)
1490 cpp_change_file (parse_in, LC_RENAME, main_input_filename);
1491
1492 /* Set this here so the client can change the option if it wishes,
1493 and after stacking the main file so we don't trace the main file. */
1494 cpp_get_line_maps (parse_in)->trace_includes
1495 = cpp_opts->print_include_names;
1496 }
1497 }
那么如何实现依次加入多个-include指定头文件呢?答案从cb_file_change开始。在cb_file_change中,有如下2行:
1509 if (new_map == 0 || (new_map->reason == LC_LEAVE && MAIN_FILE_P (new_map)))
1510 push_command_line_include ();
而cb_file_change在c_common_post_options的1158行,被赋予了cpp_reader的cb域中的file_change钩子,这个钩子在_cpp_do_file_change里,得到调用。而且在这之前,_cpp_do_file_change先调用了linemap_add来往cpp_reader的map链中将插入一个记录文件变化的line_map。
如果是完成了文件的读入,例如,这里对-include包含头文件,那么_cpp_pop_buffer会被_cpp_get_fresh_line所调用。_cpp_pop_buffer的定义如下:
1949 void
1950 _cpp_pop_buffer (cpp_reader *pfile) in cpplib.c
1951 {
1952 cpp_buffer *buffer = pfile->buffer;
1953 struct _cpp_file *inc = buffer->file;
1954 struct if_stack *ifs;
1955
1956 /* Walk back up the conditional stack till we reach its level at
1957 entry to this file, issuing error messages. */
1958 for (ifs = buffer->if_stack; ifs; ifs = ifs->next)
1959 cpp_error_with_line (pfile, CPP_DL_ERROR, ifs->line, 0,
1960 "unterminated #%s", dtable[ifs->type].name);
1961
1962 /* In case of a missing #endif. */
1963 pfile->state.skipping = 0;
1964
1965 /* _cpp_do_file_change expects pfile->buffer to be the new one. */
1966 pfile->buffer = buffer->prev;
1967
1968 free (buffer->notes);
1969
1970 /* Free the buffer object now; we may want to push a new buffer
1971 in _cpp_push_next_include_file. */
1972 obstack_free (&pfile->buffer_ob, buffer);
1973
1974 if (inc)
1975 {
1976 _cpp_pop_file_buffer (pfile, inc);
1977
1978 _cpp_do_file_change (pfile, LC_LEAVE, 0, 0, 0);
1979 }
1980 }
上面1953行的buffer->file只有在_cpp_stack_file里才被设置,代表文件的主读入缓存,而该文件在处理过程中临时创建的读入缓存(如do_pragma),都是在obstack上创建,即不设置file域,也不需要手动释放。
对于我们的-include包含的头文件,这个line_map的构造将符合1509行条件,将通过push_command_line_include尝试读入下一-include头文件。注意其1479行的cpp_push_include里调用了_cpp_stack_include(参考堆叠文件(PCH文件除外)一节)。如此循环往复,直到所有-include头文件处理完成。
如果在命令行上给出了选项–output-pch,头文件除了被编译外,还会产生PCH文件(预编译头文件)。在这个选项中,给出了PCH文件名,并且保存在变量pch_file里。在这里pch_init为将要产生的PCH文件准备文件头。
108 void
109 pch_init (void) in c-pch.c
110 {
111 FILE *f;
112 struct c_pch_validity v;
113 void *target_validity;
114 static const char partial_pch[IDENT_LENGTH] = "gpcWrite";
115 unsigned int current_flags_info = 0;
116
117 if (! pch_file)
118 return;
119
120 if (flag_unit_at_a_time)
121 current_flags_info |= FLAG_UNIT_AT_A_TIME_SET;
122
123 f = fopen (pch_file, "w+b");
124 if (f == NULL)
125 fatal_error ("can't create precompiled header %s: %m", pch_file);
126 pch_outfile = f;
127
128 if (strlen (host_machine) > 255 || strlen (target_machine) > 255
129 || strlen (version_string) > 255)
130 abort ();
131
132 v.host_machine_length = strlen (host_machine);
133 v.target_machine_length = strlen (target_machine);
134 v.version_length = strlen (version_string);
135 v.debug_info_type = write_symbols;
136 v.flags_info = current_flags_info;
137 v.pch_init = &pch_init;
138 target_validity = targetm.get_pch_validity (&v.target_data_length);
139
140 if (fwrite (partial_pch, IDENT_LENGTH, 1, f) != 1
141 || fwrite (&v, sizeof (v), 1, f) != 1
142 || fwrite (host_machine, v.host_machine_length, 1, f) != 1
143 || fwrite (target_machine, v.target_machine_length, 1, f) != 1
144 || fwrite (version_string, v.version_length, 1, f) != 1
145 || fwrite (target_validity, v.target_data_length, 1, f) != 1)
146 fatal_error ("can't write to %s: %m", pch_file);
147
148 /* We need to be able to re-read the output. */
149 /* The driver always provides a valid -o option. */
150 if (asm_file_name == NULL
151 || strcmp (asm_file_name, "-") == 0)
152 fatal_error ("`%s' is not a valid output file", asm_file_name);
153
154 asm_file_startpos = ftell (asm_out_file);
155
156 /* Let the debugging format deal with the PCHness. */
157 (*debug_hooks->handle_pch) (0);
158
159 cpp_save_state (parse_in, f);
160 }
我们已经看过,c_pch_validity描述了PCH文件的结构,参考图形pch文件布局。在138行,钩子get_pch_validity将为PCH文件产生目标数据。在这里,这个钩子把调用转给default_get_pch_validity。
4069 void *
4070 default_get_pch_validity (size_t *len) in toplev.c
4071 {
4072 #ifdef TARGET_OPTIONS
4073 size_t i;
4074 #endif
4075 char *result, *r;
4076
4077 *len = sizeof (target_flags) + 2;
4078 #ifdef TARGET_OPTIONS
4079 for (i = 0; i < ARRAY_SIZE (target_options); i++)
4080 {
4081 *len += 1;
4082 if (*target_options[i].variable)
4083 *len += strlen (*target_options[i].variable);
4084 }
4085 #endif
4086
4087 result = r = xmalloc (*len);
4088 r[0] = flag_pic;
4089 r[1] = flag_pie;
4090 r += 2;
4091 memcpy (r, &target_flags, sizeof (target_flags));
4092 r += sizeof (target_flags);
4093
4094 #ifdef TARGET_OPTIONS
4095 for (i = 0; i < ARRAY_SIZE (target_options); i++)
4096 {
4097 const char *str = *target_options[i].variable;
4098 size_t l;
4099 if (! str)
4100 str = "";
4101 l = strlen (str) + 1;
4102 memcpy (r, str, l);
4103 r += l;
4104 }
4105 #endif
4106
4107 return result;
4108 }
target_options的内容参考初始化与目标平台相关选项一节,而PCH文件目标数据段的布局参考图PCH文件中target data的布局。
下面的函数出于依赖性检测的目的,保存parse_in中当前所有的定义。它在处理第一个源文件前执行。在那个时候,parse_in包含了由finish_options加入的定义(包括内建定义,由-D或-U选项引入的定义)。这里参数f是由–output-pch 指定,保存在pch_file中的文件的打开句柄。
192 int
193 cpp_save_state (cpp_reader *r, FILE *f) in cpppch.c
194 {
195 /* Save the list of non-void identifiers for the dependency checking. */
196 r->savedstate = xmalloc (sizeof (struct cpp_savedstate));
197 r->savedstate->definedhash = htab_create (100, cpp_string_hash,
198 cpp_string_eq, NULL);
199 cpp_forall_identifiers (r, save_idents, r->savedstate);
200
201 /* Write out the list of defined identifiers. */
202 cpp_forall_identifiers (r, write_macdef, f);
203
204 return 0;
205 }
结构体cpp_savestate的定义如下。域definedhash是保存定义名字的哈希表。
103 struct cpp_savedstate in cpppch.c
104 {
105 /* A hash table of the defined identifiers. */
106 htab_t definedhash;
107 /* The size of the definitions of those identifiers (the size of
108 'definedstrs'). */
109 size_t hashsize;
110 /* Number of definitions */
111 size_t n_defs;
112 /* Array of definitions. In cpp_write_pch_deps it is used for sorting. */
113 cpp_hashnode **defs;
114 /* Space for the next definition. Definitions are null-terminated
115 strings. */
116 unsigned char *definedstrs;
117 };
函数cpp_forall_identifiers遍历parse_in的hash_table—这个标识符所用的哈希表。我们在前面的章节已经看过,该哈希表中的节点可以有2种方式解读,一种是树节点,另一种是cpp_hashnode,即在下面传给参数hn。注意到在134行,htab_find_slot的最后的参数是INSERT,它将自动插入缺少的标识符。
122 static int
123 save_idents (cpp_reader *pfile ATTRIBUTE_UNUSED, cpp_hashnode *hn, void *ss_p)
124 {
125 struct cpp_savedstate *const ss = (struct cpp_savedstate *)ss_p;
126
127 if (hn->type != NT_VOID)
128 {
129 struct cpp_string news;
130 void **slot;
131
132 news.len = NODE_LEN (hn);
133 news.text= NODE_NAME (hn);
134 slot = htab_find_slot (ss->definedhash, &news, INSERT);
135 if (*slot == NULL)
136 {
137 struct cpp_string *sp;
138 unsigned char *text;
139
140 sp = xmalloc (sizeof (struct cpp_string));
141 *slot = sp;
142
143 sp->len = NODE_LEN (hn);
144 sp->text = text = xmalloc (NODE_LEN (hn));
145 memcpy (text, NODE_NAME (hn), NODE_LEN (hn));
146 }
147 }
148
149 return 1;
150 }
在第一次遍历中,我们得到所有标识符的名字。而在第二次遍历中,我们则记录所有的宏定义。注意,因为我们尚在预处理中,在预处理器看来,节点只有3种类型:NT_VOID,NT_MACRO和NT_ASSERTION。
48 static int
49 write_macdef (cpp_reader *pfile, cpp_hashnode *hn, void *file_p) in cpppch.c
50 {
51 FILE *f = (FILE *) file_p;
52 switch (hn->type)
53 {
54 case NT_VOID:
55 if (! (hn->flags & NODE_POISONED))
56 return 1;
57
58 case NT_MACRO:
59 if ((hn->flags & NODE_BUILTIN))
60 return 1;
61
62 {
63 struct macrodef_struct s;
64 const unsigned char *defn;
65
66 s.name_length = NODE_LEN (hn);
67 s.flags = hn->flags & NODE_POISONED;
68
69 if (hn->type == NT_MACRO)
70 {
71 defn = cpp_macro_definition (pfile, hn);
72 s.definition_length = ustrlen (defn);
73 }
74 else
75 {
76 defn = NODE_NAME (hn);
77 s.definition_length = s.name_length;
78 }
79
80 if (fwrite (&s, sizeof (s), 1, f) != 1
81 || fwrite (defn, 1, s.definition_length, f) != s.definition_length)
82 {
83 cpp_errno (pfile, CPP_DL_ERROR,
84 "while writing precompiled header");
85 return 0;
86 }
87 }
88 return 1;
89
90 case NT_ASSERTION:
91 /* Not currently implemented. */
92 return 1;
93
94 default:
95 abort ();
96 }
97 }
宏的定义被hashnode的cpp_macro域,及cpp_reader的macro_buffer域所记录。其写入PCH文件的内容就如同读入宏定义一节所显示的那样。
1605 const unsigned char *
1606 cpp_macro_definition (cpp_reader *pfile, const cpp_hashnode *node) in cppmacro.c
1607 {
1608 unsigned int i, len;
1609 const cpp_macro *macro = node->value.macro;
1610 unsigned char *buffer;
1611
1612 if (node->type != NT_MACRO || (node->flags & NODE_BUILTIN))
1613 {
1614 cpp_error (pfile, CPP_DL_ICE,
1615 "invalid hash type %d in cpp_macro_definition", node->type);
1616 return 0;
1617 }
1618
1619 /* Calculate length. */
1620 len = NODE_LEN (node) + 2; /* ' ' and NUL. */
1621 if (macro->fun_like)
1622 {
1623 len += 4; /* "()" plus possible final ".." of named
1624 varargs (we have + 1 below). */
1625 for (i = 0; i < macro->paramc; i++)
1626 len += NODE_LEN (macro->params[i]) + 1; /* "," */
1627 }
1628
1629 /* This should match below where we fill in the buffer. */
1630 if (CPP_OPTION (pfile, traditional))
1631 len += _cpp_replacement_text_len (macro);
1632 else
1633 {
1634 for (i = 0; i < macro->count; i++)
1635 {
1636 cpp_token *token = ¯o->exp.tokens[i];
1637
1638 if (token->type == CPP_MACRO_ARG)
1639 len += NODE_LEN (macro->params[token->val.arg_no - 1]);
1640 else
1641 len += cpp_token_len (token);
1642
1643 if (token->flags & STRINGIFY_ARG)
1644 len++; /* "#" */
1645 if (token->flags & PASTE_LEFT)
1646 len += 3; /* " ##" */
1647 if (token->flags & PREV_WHITE)
1648 len++; /* " " */
1649 }
1650 }
1651
1652 if (len > pfile->macro_buffer_len)
1653 {
1654 pfile->macro_buffer = xrealloc (pfile->macro_buffer, len);
1655 pfile->macro_buffer_len = len;
1656 }
1657
1658 /* Fill in the buffer. Start with the macro name. */
1659 buffer = pfile->macro_buffer;
1660 memcpy (buffer, NODE_NAME (node), NODE_LEN (node));
1661 buffer += NODE_LEN (node);
1662
1663 /* Parameter names. */
1664 if (macro->fun_like)
1665 {
1666 *buffer++ = '(';
1667 for (i = 0; i < macro->paramc; i++)
1668 {
1669 cpp_hashnode *param = macro->params[i];
1670
1671 if (param != pfile->spec_nodes.n__VA_ARGS__)
1672 {
1673 memcpy (buffer, NODE_NAME (param), NODE_LEN (param));
1674 buffer += NODE_LEN (param);
1675 }
1676
1677 if (i + 1 < macro->paramc)
1678 /* Don't emit a space after the comma here; we're trying
1679 to emit a Dwarf-friendly definition, and the Dwarf spec
1680 forbids spaces in the argument list. */
1681 *buffer++ = ',';
1682 else if (macro->variadic)
1683 *buffer++ = '.', *buffer++ = '.', *buffer++ = '.';
1684 }
1685 *buffer++ = ')';
1686 }
1687
1688 /* The Dwarf spec requires a space after the macro name, even if the
1689 definition is the empty string. */
1690 *buffer++ = ' ';
1691
1692 if (CPP_OPTION (pfile, traditional))
1693 buffer = _cpp_copy_replacement_text (macro, buffer);
1694 else if (macro->count)
1695 /* Expansion tokens. */
1696 {
1697 for (i = 0; i < macro->count; i++)
1698 {
1699 cpp_token *token = ¯o->exp.tokens[i];
1700
1701 if (token->flags & PREV_WHITE)
1702 *buffer++ = ' ';
1703 if (token->flags & STRINGIFY_ARG)
1704 *buffer++ = '#';
1705
1706 if (token->type == CPP_MACRO_ARG)
1707 {
1708 memcpy (buffer,
1709 NODE_NAME (macro->params[token->val.arg_no - 1]),
1710 NODE_LEN (macro->params[token->val.arg_no - 1]));
1711 buffer += NODE_LEN (macro->params[token->val.arg_no - 1]);
1712 }
1713 else
1714 buffer = cpp_spell_token (pfile, token, buffer);
1715
1716 if (token->flags & PASTE_LEFT)
1717 {
1718 *buffer++ = ' ';
1719 *buffer++ = '#';
1720 *buffer++ = '#';
1721 /* Next has PREV_WHITE; see _cpp_create_definition. */
1722 }
1723 }
1724 }
1725
1726 *buffer = '/0';
1727 return pfile->macro_buffer;
1728 }
这个函数走入宏的定义,首先计算出所需要的缓存,然后拷贝在macro_buffer中的整个定义,接着返回write_macrodef 将其写入PCH文件。