您的位置:首页 > 编程语言

GCC-3.4.6源代码学习笔记(28)

2010-04-15 09:41 441 查看

4.1.1. 完成查找路径设置

GCC在多个不同的地方查找头文件。在一个普通的Unix系统上,如果不另外指示,由`#include <FILE>'请求的文件将在以下目录中查找:
/usr/local/include
LIBDIR/gcc/TARGET/VERSION/include
/usr/TARGET/include
/usr/include
对于C++程序,它还首先会在/usr/include/g++-v3中查找。在上面,TARGET是GCC为之编译代码的系统的规范名(canonical name);通常但不总是运行编译器的系统的规范名。VERSION则是GCC的版本。
通过-IDIR命令行选项可以向此列表添加查找目录DIR。所有为-I所指定的目录,以从左到右的次序,在默认目录前,被查找。唯一的例外是当dir已由默认查找所覆盖。在这种情况下,该选项被忽略且系统目录的查找次序不变。
在引号包含查找链及尖括号包含查找链合并为最终的查找链之前,重复的目录被从中移出。然而,一个目录可能在最终查找链中出现2次,如果它在引号包含查找链及尖括号包含查找链中均被指定。
当编译操作系统内核或其它不使用标准C库的程序或C标准库本身时,可以通过-nostdinc选项来阻止GCC在任一默认目录中查找。当-nonstdinc起用时,选项-I不会像上面所说的那样被忽略。
GCC首先在包含当前文件的目录中查找由#include "FILE"所请求的头文件,然后在那些它查找尖括号引用的头文件的目录中查找。例如,如果/usr/include/sys/stat.h中包含#include "types.h",GCC首先在/usr/include/sys中查找types.h,然后才在它的通常查找路径中查找。
#line不会改变GCC中包含当前文件的目录。
可以在-I选项列表中任意处插入-I-。这将有2个作用。首先,在-I-之前出现的目录仅用于查找由引号引用之头文件。在-I-之后的目录则用于查找所有头文件。其次,包含当前文件的目录不用于查找,除非它正好由-I选项指定。
-I. -I-不同于不使用-I选项,它不能导致<>包含具有,””包含在没有特别选项时,同样的行为。-I.查找编译器的当前工作目录。它可能是包含当前文件的目录,也可能不是。
如果需要在名为“-”的目录中查找头文件,应写作-I./-。
在前面的章节中,我们已经看过了-I选项的处理。现在编译器应该完成查找路径的设置了。这里,参数sysroot是全局变量sysroot,由-isysroot选项设置,表示系统头文件的查找根目录。而参数iprefix是全局变量iprefix,由-iprefix选项设置,表示查找目录的前缀。

335 void
336 register_include_chains (cpp_reader *pfile, const char *sysroot, in c-incpath.c
337 const char *iprefix, int stdinc, int cxx_stdinc,
338 int verbose)
339 {
340 static const char *const lang_env_vars[] =
341 { "C_INCLUDE_PATH", "CPLUS_INCLUDE_PATH",
342 "OBJC_INCLUDE_PATH", "OBJCPLUS_INCLUDE_PATH" };
343 cpp_options *cpp_opts = cpp_get_options (pfile);
344 size_t idx = (cpp_opts->objc ? 2: 0);
345
346 if (cpp_opts->cplusplus)
347 idx++;
348 else
349 cxx_stdinc = false;
350
351 /* CPATH and language-dependent environment variables may add to the
352 include chain. */
353 add_env_var_paths ("CPATH", BRACKET);
354 add_env_var_paths (lang_env_vars[idx], SYSTEM);
355
356 /* Finally chain on the standard directories. */
357 if (stdinc)
358 add_standard_paths (sysroot, iprefix, cxx_stdinc);
359
360 merge_include_chains (pfile, verbose);
361
362 cpp_set_include_chains (pfile, heads[QUOTE], heads[BRACKET],
363 quote_ignores_source_dir);
364 }

查找目录亦可通过环境变量来添加,这些环境变量包括CPATH,C_INCLUDE_PATH,CPLUS_INCLUDE_PATH及OBJC_INCLUDE_PATH。
每个环境变量的值是由特殊字符分割的目录列表,与PATH非常相似,在其中查找头文件。这个特殊字符,PATH_SEPARATOR,是依赖于目标平台并且在编译GCC的时候确定的。对于Microsoft 基于Windows的平台,它是分号,而对于几乎所有其他目标平台是冒号。
CPATH指定了一组用于查找的目录,如同-I所指定那样,但它们排在命令行中-I选项给出的路径后。这个环境变量不管被预处理的是何种语言,都得到使用。
剩下的环境变量只应用于预处理特定的语言。每个指定了一组用于查找的目录,如同-isystem所指定那样,但它们排在命令行中-isystem选项给出的路径后。
在所有这些环境变量中,一个空的元素指示编译器在当前工作目录查找。空元素可以出现在路径的开头或结尾。例如,如果CPATH的值是::/special/include,这与“-I. -I/special/include”效果相同。
这些路径由add_env_var_paths执行添加。

90 static void
91 add_env_var_paths (const char *env_var, int chain) in c-incpath.c
92 {
93 char *p, *q, *path;
94
95 GET_ENVIRONMENT (q, env_var);
96
97 if (!q)
98 return;
99
100 for (p = q; *q; p = q + 1)
101 {
102 q = p;
103 while (*q != 0 && *q != PATH_SEPARATOR)
104 q++;
105
106 if (p == q)
107 path = xstrdup (".");
108 else
109 {
110 path = xmalloc (q - p + 1);
111 memcpy (path, p, q - p);
112 path[q - p] = '/0';
113 }
114
115 add_path (path, chain, chain == SYSTEM);
116 }
117 }

注意到在register_include_chains357行的stdinc来自全局变量std_inc,其内容来自选项–nonstdinc(默认值为1),而cxx_stdinc来自std_cxx_inc,其内容由–nonstdinc++设定(默认值为1)。因此,如果在标准系统目录下查找头文件是被允许的,通过add_standard_paths把标准包含链加入heads链。

120 static void
121 add_standard_paths (const char *sysroot, const char *iprefix, int cxx_stdinc) in c-incpath.c
122 {
123 const struct default_include *p;
124 size_t len;
125
126 if (iprefix && (len = cpp_GCC_INCLUDE_DIR_len) != 0)
127 {
128 /* Look for directories that start with the standard prefix.
129 "Translate" them, ie. replace /usr/local/lib/gcc... with
130 IPREFIX and search them first. */
131 for (p = cpp_include_defaults; p->fname; p++)
132 {
133 if (!p->cplusplus || cxx_stdinc)
134 {
135 /* Should we be translating sysrooted dirs too? Assume
136 that iprefix and sysroot are mutually exclusive, for
137 now. */
138 if (sysroot && p->add_sysroot)
139 continue;
140 if (!strncmp (p->fname, cpp_GCC_INCLUDE_DIR, len))
141 {
142 char *str = concat (iprefix, p->fname + len, NULL);
143 add_path (str, SYSTEM, p->cxx_aware);
144 }
145 }
146 }
147 }
148
149 for (p = cpp_include_defaults; p->fname; p++)
150 {
151 if (!p->cplusplus || cxx_stdinc)
152 {
153 char *str;
154
155 /* Should this directory start with the sysroot? */
156 if (sysroot && p->add_sysroot)
157 str = concat (sysroot, p->fname, NULL);
158 else
159 str = update_path (p->fname, p->component);
160
161 add_path (str, SYSTEM, p->cxx_aware);
162 }
163 }
164 }

结构体default_include持有查找包含文件的默认目录列表的信息。它可以被不同的-I和-ixxx选项改写。所有这些目录都被处理为系统包含目录(在某些情况下它们不受pedantic警告的影响)。

35 struct default_include in cppdefault.h
36 {
37 const char *const fname; /* The name of the directory. */
38 const char *const component; /* The component containing the directory
39 (see update_path in prefix.c) */
40 const char cplusplus; /* Only look here if we're compiling C++. */
41 const char cxx_aware; /* Includes in this directory don't need to
42 be wrapped in extern "C" when compiling
43 C++. */
44 const char add_sysroot; /* FNAME should be prefixed by
45 cpp_SYSROOT. */
46 };

在Linux平台上,cpp_GCC_INCLUDE_DIR是空的,因此cpp_GCC_INCLUDE_DIR_len是0。对于149行的cpp_include_defaults,在我的机器上(Linux),它包含以下内容。这些路径都被认为是SYSTEM。

44 const struct default_include cpp_include_defaults[] in cppdefault.c
48 = {
66 { "/localdisk/data/gcc346/include", 0, 0, 1, 0 },
86 { "/include", 0, 0, 0, 1 },
88 { 0, 0, 0, 0, 0 }
89 };

在加入了所有的头文件查找路径后,需要把这四个包含链以quote,bracket,system,after的次序合并一起,期间去除重复的目录。这由merge_include_chains完成。

247 static void
248 merge_include_chains (cpp_reader *pfile, int verbose) in c-incpath.c
249 {
250 /* Join the SYSTEM and AFTER chains. Remove duplicates in the
251 resulting SYSTEM chain. */
252 if (heads[SYSTEM])
253 tails[SYSTEM]->next = heads[AFTER];
254 else
255 heads[SYSTEM] = heads[AFTER];
256 heads[SYSTEM] = remove_duplicates (pfile, heads[SYSTEM], 0, 0, verbose);
257
258 /* Remove duplicates from BRACKET that are in itself or SYSTEM, and
259 join it to SYSTEM. */
260 heads[BRACKET] = remove_duplicates (pfile, heads[BRACKET], heads[SYSTEM],
261 heads[SYSTEM], verbose);
262
263 /* Remove duplicates from QUOTE that are in itself or SYSTEM, and
264 join it to BRACKET. */
265 heads[QUOTE] = remove_duplicates (pfile, heads[QUOTE], heads[SYSTEM],
266 heads[BRACKET], verbose);
267
268 /* If verbose, print the list of dirs to search. */
269 if (verbose)
270 {
271 struct cpp_dir *p;
272
273 fprintf (stderr, _("#include /".../" search starts here:/n"));
274 for (p = heads[QUOTE];; p = p->next)
275 {
276 if (p == heads[BRACKET])
277 fprintf (stderr, _("#include <...> search starts here:/n"));
278 if (!p)
279 break;
280 fprintf (stderr, " %s/n", p->name);
281 }
282 fprintf (stderr, _("End of search list./n"));
283 }
284 }

remove_duplicates中,对于链head中每个重复路径,保留第一个出现的。而在system链中亦出现的路径,则将其从head链移除。把移除重复路径(注意只是移除与system重复的部分)后所得的链中最后路径节点中的next指针设置为指向join,除非它也出现在join中。在这种情况下它则被从head链中移除(现次结尾的节点为结尾节点,重复上步骤,直至发现join所无之节点,从而将head和join相接)。由此可见,在head和join链中,实可出现重复路径,除非join就是system。

172 static struct cpp_dir *
173 remove_duplicates (cpp_reader *pfile, struct cpp_dir *head, in c-incpath.c
174 struct cpp_dir *system, struct cpp_dir *join,
175 int verbose)
176 {
177 struct cpp_dir **pcur, *tmp, *cur;
178 struct stat st;
179
180 for (pcur = &head; *pcur; )
181 {
182 int reason = REASON_QUIET;
183
184 cur = *pcur;
185
186 if (stat (cur->name, &st))
187 {
188 /* Dirs that don't exist are silently ignored, unless verbose. */
189 if (errno != ENOENT)
190 cpp_errno (pfile, CPP_DL_ERROR, cur->name);
191 else
192 reason = REASON_NOENT;
193 }
194 else if (!S_ISDIR (st.st_mode))
195 cpp_error_with_line (pfile, CPP_DL_ERROR, 0, 0,
196 "%s: not a directory", cur->name);
197 else
198 {
199 INO_T_COPY (cur->ino, st.st_ino);
200 cur->dev = st.st_dev;
201
202 /* Remove this one if it is in the system chain. */
203 reason = REASON_DUP_SYS;
204 for (tmp = system; tmp; tmp = tmp->next)
205 if (INO_T_EQ (tmp->ino, cur->ino) && tmp->dev == cur->dev)
206 break;
207
208 if (!tmp)
209 {
210 /* Duplicate of something earlier in the same chain? */
211 reason = REASON_DUP;
212 for (tmp = head; tmp != cur; tmp = tmp->next)
213 if (INO_T_EQ (cur->ino, tmp->ino) && cur->dev == tmp->dev)
214 break;
215
216 if (tmp == cur
217 /* Last in the chain and duplicate of JOIN? */
218 && !(cur->next == NULL && join
219 && INO_T_EQ (cur->ino, join->ino)
220 && cur->dev == join->dev))
221 {
222 /* Unique, so keep this directory. */
223 pcur = &cur->next;
224 continue;
225 }
226 }
227 }
228
229 /* Remove this entry from the chain. */
230 *pcur = cur->next;
231 free_path (cur, verbose ? reason: REASON_QUIET);
232 }
233
234 *pcur = join;
235 return head;
236 }

register_include_chains中,执行完merge_include_chains后,现在头文件的查找次序被设置为引号(quote),尖括号(bracket),系统(system),而后后续(after)。注意到下面参数quote引用括号引用头文件的包含路径,bracket引用尖括号引用头文件的包含路径。据此cpp_set_include_chains设置parse_in——解析器对象。

1083 void
1084 cpp_set_include_chains (cpp_reader *pfile, cpp_dir *quote, in cppfiles.c
1085 cpp_dir *bracket, int quote_ignores_source_dir)
1086 {
1087 pfile->quote_include = quote;
1088 pfile->bracket_include = quote;
1089 pfile->quote_ignores_source_dir = quote_ignores_source_dir;
1090
1091 for (; quote; quote = quote->next)
1092 {
1093 quote->name_map = NULL;
1094 quote->len = strlen (quote->name);
1095 if (quote == bracket)
1096 pfile->bracket_include = bracket;
1097 }
1098 }

注意,只有使用了-I-选项,才会出现非空的quote(参见split_quote_chain,及-I-的解释)。否则解析器对象中的quote_include和bracket_include是相同的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: