现在cpp_reader已经就绪,是时候读入源文件了。在下面,宏input_line访问全局变量input_location的line域。这个全局变量记录了当前文件名及当前处理行号。
c_common_post_option (continue)
1162 saved_lineno = input_line;
1163 input_line = 0;
1164
1165 /* If an error has occurred in cpplib, note it so we fail
1166 immediately. */
1167 errorcount += cpp_errors (parse_in);
1168
1169 *pfilename = this_input_filename
1170 = cpp_read_main_file (parse_in, in_fnames[0]);
1171 /* Don't do any compilation or preprocessing if there is no input file. */
1172 if (this_input_filename == NULL)
1173 {
1174 errorcount++;
1175 return false;
1176 }
1177
1178 if (flag_working_directory
1179 && flag_preprocess_only && ! flag_no_line_commands)
1180 pp_dir_change (parse_in, get_src_pwd ());
1181
1182 return flag_preprocess_only;
1183 }
注意到在上面in_fnames[0]持有命令行中第一个输入文件名(见handle_options的453行)。它是源文件之一。
456 const char *
457 cpp_read_main_file (cpp_reader *pfile, const char *fname) in cppinit.c
458 {
459 if (CPP_OPTION (pfile, deps.style) != DEPS_NONE)
460 {
461 if (!pfile->deps)
462 pfile->deps = deps_init ();
463
464 /* Set the default target (if there is none already). */
465 deps_add_default_target (pfile->deps, fname);
466 }
首先,如果应用了-M(M)或-M(M)D选项或环境变量DEPENDENCIES_OUTPUT,希望编译器为之生成适用于make的依赖规则,那么需要收集能用于makefile的目标。下面181行,如果目标是-,表示输出至stdout,而对于其他目标,则为文件名换上合适的后缀。
173 void
174 deps_add_default_target (struct deps *d, const char *tgt) in mkdeps.c
175 {
176 /* Only if we have no targets. */
177 if (d->ntargets)
178 return;
179
180 if (tgt[0] == '/0')
181 deps_add_target (d, "-", 1);
182 else
183 {
184 #ifndef TARGET_OBJECT_SUFFIX
185 # define TARGET_OBJECT_SUFFIX ".o"
186 #endif
187 const char *start = lbasename (tgt);
188 char *o = alloca (strlen (start) + strlen (TARGET_OBJECT_SUFFIX) + 1);
189 char *suffix;
190
191 strcpy (o, start);
192
193 suffix = strrchr (o, '.');
194 if (!suffix)
195 suffix = o + strlen (o);
196 strcpy (suffix, TARGET_OBJECT_SUFFIX);
197
198 deps_add_target (d, o, 1);
199 }
200 }
另2个会产生make依赖规则的选项是-MT及-MQ,其由handle_deferred_opts处理,其中所调用的cpp_add_dependency_target,及而后的deps_add_target,的参数quote为1如果是-MQ,否则为0。而在这里,quote一概为1。
152 void
153 deps_add_target (struct deps *d, const char *t, int quote) in mkdeps.c
154 {
155 if (d->ntargets == d->targets_size)
156 {
157 d->targets_size = d->targets_size * 2 + 4;
158 d->targetv = xrealloc (d->targetv,
159 d->targets_size * sizeof (const char *));
160 }
161
162 if (quote)
163 t = munge (t); /* Also makes permanent copy. */
164 else
165 t = xstrdup (t);
166
167 d->targetv[d->ntargets++] = t;
168 }
函数munge以GCC期望的形式 expected form. The major issue is replacing every single ‘/’ with ‘//’, and single ‘$’ with ‘$$’.
49 static const char *
50 munge (const char *filename) in mkdeps.c
51 {
52 int len;
53 const char *p, *q;
54 char *dst, *buffer;
55
56 for (p = filename, len = 0; *p; p++, len++)
57 {
58 switch (*p)
59 {
60 case ' ':
61 case '/t':
62 /* GNU make uses a weird quoting scheme for white space.
63 A space or tab preceded by 2N+1 backslashes represents
64 N backslashes followed by space; a space or tab
65 preceded by 2N backslashes represents N backslashes at
66 the end of a file name; and backslashes in other
67 contexts should not be doubled. */
68 for (q = p - 1; filename <= q && *q == '//'; q--)
69 len++;
70 len++;
71 break;
72
73 case '$':
74 /* '$' is quoted by doubling it. */
75 len++;
76 break;
77 }
78 }
79
80 /* Now we know how big to make the buffer. */
81 buffer = xmalloc (len + 1);
82
83 for (p = filename, dst = buffer; *p; p++, dst++)
84 {
85 switch (*p)
86 {
87 case ' ':
88 case '/t':
89 for (q = p - 1; filename <= q && *q == '//'; q--)
90 *dst++ = '//';
91 *dst++ = '//';
92 break;
93
94 case '$':
95 *dst++ = '$';
96 break;
97
98 default:
99 /* nothing */;
100 }
101 *dst = *p;
102 }
103
104 *dst = '/0';
105 return buffer;
106 }
千万注意,能在产生make依赖规则的选项中,除了-M(M)D外,其余均由GCC的shell默认加入-E选项。
cpp_read_main_file (continue)
468 pfile->main_file
469 = _cpp_find_file (pfile, fname, &pfile->no_search_path, false, 0);
470 if (_cpp_find_failed (pfile->main_file))
471 return NULL;
在cpp_reader结构中,具有3个查找路径,其中bracket_include用于尖括号包含的头文件(<>),quote_include用于引号包含的头文件(””),而no_search_path用于绝对路径。在完成查找路径设置部分,已经看到bracket_include及quote_include都已被设置并连接起来。现在我们不是处理头文件,因此在此使用no_search_path来作为文件查找路径(其为””)。
362 _cpp_file *
363 _cpp_find_file (cpp_reader *pfile, const char *fname, in cppfiles.c
364 cpp_dir *start_dir, bool fake, int angle_brackets)
365 {
366 struct file_hash_entry *entry, **hash_slot;
367 _cpp_file*file;
368 bool invalid_pch = false;
369
370 /* Ensure we get no confusion between cached files and directories. */
371 if (start_dir == NULL)
372 cpp_error (pfile, CPP_DL_ICE, "NULL directory in find_file");
373
374 hash_slot = (struct file_hash_entry **)
375 htab_find_slot_with_hash (pfile->file_hash, fname,
376 htab_hash_string (fname),
377 INSERT);
378
379 /* First check the cache before we resort to memory allocation. */
380 entry = search_cache (*hash_slot, start_dir);
381 if (entry)
382 return entry->u.file;
383
384 file = make_cpp_file (pfile, start_dir, fname);
所有已被找到的文件都由一个哈希表记录(cpp_reader的file_hash域)以使得而后对其访问能尽可能快。这个哈希表的元素具有如下file_hash_entry类型。
145 struct file_hash_entry in cppfiles.c
146 {
147 struct file_hash_entry *next;
148 cpp_dir *start_dir;
149 union
150 {
151 _cpp_file *file;
152 cpp_dir *dir;
153 } u;
154 };
打开的源文件,被保存在结构体_cpp_file中。
57 struct _cpp_file in cppfiles.c
58 {
59 /* Filename as given to #include or command line switch. */
60 const char *name;
61
62 /* The full path used to find the file. */
63 const char *path;
64
65 /* The full path of the pch file. */
66 const char *pchname;
67
68 /* The file's path with the basename stripped. NULL if it hasn't
69 been calculated yet. */
70 const char *dir_name;
71
72 /* Chain through all files. */
73 struct _cpp_file *next_file;
74
75 /* The contents of NAME after calling read_file(). */
76 const uchar *buffer;
77
78 /* The macro, if any, preventing re-inclusion. */
79 const cpp_hashnode *cmacro;
80
81 /* The directory in the search path where FILE was found. Used for
82 #include_next and determining whether a header is a system
83 header. */
84 cpp_dir *dir;
85
86 /* As filled in by stat(2) for the file. */
87 struct stat st;
88
89 /* File descriptor. Invalid if -1, otherwise open. */
90 int fd;
91
92 /* Zero if this file was successfully opened and stat()-ed,
93 otherwise errno obtained from failure. */
94 int err_no;
95
96 /* Number of times the file has been stacked for preprocessing. */
97 unsigned short stack_count;
98
99 /* If opened with #import or contains #pragma once. */
100 bool once_only;
101
102 /* If read() failed before. */
103 bool dont_read;
104
105 /* If this file is the main file. */
106 bool main_file;
107
108 /* If BUFFER above contains the true contents of the file. */
109 bool buffer_valid;
110
111 /* 0: file not known to be a PCH.
112 1: file is a PCH (on return from find_include_file).
113 2: file is not and never will be a valid precompiled header.
114 3: file is always a valid precompiled header. */
115 uchar pch;
116 };
这里如果start_dir为no_search_path(其内容为””),表明所缓存的文件是源文件。若是头文件,start_dir应为其所在目录。此处head是由在不同目录中的同名文件构成的链表,其已是所希求的文件名。
788 static struct file_hash_entry *
789 search_cache (struct file_hash_entry *head, const cpp_dir *start_dir) in cppfiles.c
790 {
791 while (head && head->start_dir != start_dir)
792 head = head->next;
793
794 return head;
795 }
如果文件未被哈希表所缓存,需要通过make_cpp_file为其创建一个_cpp_file对象,因为我们将要查找、打开及缓存这个文件。
798 static _cpp_file *
799 make_cpp_file (cpp_reader *pfile, cpp_dir *dir, const char *fname) in cppfiles.c
800 {
801 _cpp_file *file;
802
803 file = xcalloc (1, sizeof (_cpp_file));
804 file->main_file = !pfile->buffer;
805 file->fd = -1;
806 file->dir = dir;
807 file->name = xstrdup (fname);
808
809 return file;
810 }
完成这个_cpp_file对象的创建后,将检查在指定的路径下是否存在该文件。如果该文件不存在,则依次在相关的查找路径中查找,直至找到该文件或穷尽所有目录。注意,对于使用no_search_path作为查找目录的文件,一旦在指定的目录下找不到文件,即报错。而其他bracket_include及quote_include,则依据系统及命令行或环境变量的设置,将有有序的多个目录项。
_cpp_find_file (continue)
386 /* Try each path in the include chain. */
387 for (; !fake ;)
388 {
389 if (find_file_in_dir (pfile, file, &invalid_pch))
390 break;
391
392 file->dir = file->dir->next;
393 if (file->dir == NULL)
394 {
395 open_file_failed (pfile, file, angle_brackets);
396 if (invalid_pch)
397 {
398 cpp_error (pfile, CPP_DL_ERROR,
399 "one or more PCH files were found, but they were invalid");
400 if (!cpp_get_options (pfile)->warn_invalid_pch)
401 cpp_error (pfile, CPP_DL_ERROR,
402 "use -Winvalid-pch for more information");
403 }
404 break;
405 }
406
407 /* Only check the cache for the starting location (done above)
408 and the quote and bracket chain heads because there are no
409 other possible starting points for searches. */
410 if (file->dir != pfile->bracket_include
411 && file->dir != pfile->quote_include)
412 continue;
413
414 entry = search_cache (*hash_slot, file->dir);
415 if (entry)
416 break;
417 }
下面319行的remap为dos之类的只支持短文件名的系统所设置,它需要remap_filename进行文件名映射。否则在322行,append_file_to_dir通过嫁接文件名及目录路径构建出绝对路径。
314 static bool
315 find_file_in_dir (cpp_reader *pfile, _cpp_file *file, bool *invalid_pch) in cppfiles.c
316 {
317 char *path;
318
319 if (CPP_OPTION (pfile, remap) && (path = remap_filename (pfile, file)))
320 ;
321 else
322 path = append_file_to_dir (file->name, file->dir);
323
324 file->path = path;
325 if (pch_open_file (pfile, file, invalid_pch))
326 return true;
327
328 if (open_file (file))
329 return true;
330
331 if (file->err_no != ENOENT)
332 {
333 open_file_failed (pfile, file, 0);
334 return true;
335 }
336
337 free (path);
338 file->path = file->name;
339 return false;
340 }