1 /* -*- c-file-style: "java"; indent-tabs-mode: nil -*-
2  *
3  * distcc -- A simple distributed compiler system
4  *
5  * Copyright (C) 2002, 2003, 2004 by Martin Pool
6  * Copyright 2007 Google Inc.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
21  * USA.
22  */
23 
24 
25 #include <config.h>
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 
33 #include "distcc.h"
34 #include "trace.h"
35 #include "util.h"
36 #include "exitcode.h"
37 
38 
39 
40 /**
41  * @file
42  *
43  * Everything we know about C filenames.
44  *
45  * We need to have some heuristics about input and output filenames to
46  * understand command lines, because that's what cc does.
47  *
48  * @note As of 0.10, .s and .S files are never distributed, because
49  * they might contain '.include' pseudo-operations, which are resolved
50  * by the assembler.
51  */
52 
53 
54 
55 /**
56  * Return a pointer to the extension, including the dot, or NULL.
57  **/
dcc_find_extension(char * sfile)58 char * dcc_find_extension(char *sfile)
59 {
60     char *dot;
61 
62     dot = strrchr(sfile, '.');
63     if (dot == NULL || dot[1] == '\0') {
64         /* make sure there's space for one more character after the
65          * dot */
66         return NULL;
67     }
68     return dot;
69 }
70 
71 /**
72  * Return a pointer to the extension, including the dot, or NULL.
73  * Same as dcc_find_extension(), but the argument and return
74  * value are both pointers to const.
75  **/
dcc_find_extension_const(const char * sfile)76 const char * dcc_find_extension_const(const char *sfile) {
77 #if 0
78   return dcc_find_extension((char *) sfile);
79 #else
80   /* The following intermediate variable works around a bug in gcc 4.2.3 where
81    * for the code above gcc spuriously reports "warning: passing argument 1
82    * of 'dcc_find_extension' discards qualifiers from pointer target type",
83    * despite the explicit cast. */
84   char *sfile_nonconst = (char *)sfile;
85   return dcc_find_extension(sfile_nonconst);
86 #endif
87 }
88 
89 
90 /**
91  * Return a pointer to the basename of the file (everything after the
92  * last slash.)  If there is no slash, return the whole filename,
93  * which is presumably in the current directory.
94  **/
dcc_find_basename(const char * sfile)95 const char * dcc_find_basename(const char *sfile)
96 {
97     char *slash;
98 
99     if (!sfile)
100         return sfile;
101 
102     slash = strrchr(sfile, '/');
103 
104     if (slash == NULL || slash[1] == '\0')
105         return sfile;
106 
107     return slash+1;
108 }
109 
110 /** Truncate the filename to its dirname (everything before the last slash).
111  *  If the filename ends with a slash, just lop off the last slash.
112  *  Note: this is destructive.
113  */
dcc_truncate_to_dirname(char * file)114 void dcc_truncate_to_dirname(char *file)
115 {
116     char *slash = 0;
117 
118     slash = strrchr(file, '/');
119 
120     if (slash == NULL) {
121       file[0] = '\0';
122     } else {
123         *slash = '\0';
124     }
125 }
126 
127 
dcc_set_file_extension(const char * sfile,const char * new_ext,char ** ofile)128 static int dcc_set_file_extension(const char *sfile,
129                                   const char *new_ext,
130                                   char **ofile)
131 {
132     char *dot, *o;
133 
134     o = strdup(sfile);
135     if (!o) {
136         rs_log_error("strdup failed (out of memory?)");
137         return EXIT_DISTCC_FAILED;
138     }
139     dot = dcc_find_extension(o);
140     if (!dot) {
141         rs_log_error("couldn't find extension in \"%s\"", o);
142         return EXIT_DISTCC_FAILED;
143     }
144     if (strlen(dot) < strlen(new_ext)) {
145         rs_log_error("not enough space for new extension");
146         return EXIT_DISTCC_FAILED;
147     }
148     strcpy(dot, new_ext);
149     *ofile = o;
150 
151     return 0;
152 }
153 
154 
155 /*
156  * Apple extensions:
157  * file.mm, file.M
158  * Objective-C++ source code which must be preprocessed. (APPLE ONLY)
159  *
160  * file.mii Objective-C++ source code which should not be
161  * preprocessed. (APPLE ONLY)
162  *
163  * http://developer.apple.com/techpubs/macosx/DeveloperTools/gcc3/gcc/Overall-Options.html
164  */
165 
166 
167 
168 /**
169  * If you preprocessed a file with extension @p e, what would you get?
170  *
171  * @param e original extension (e.g. ".c")
172  *
173  * @returns preprocessed extension, (e.g. ".i"), or NULL if
174  * unrecognized.
175  **/
dcc_preproc_exten(const char * e)176 const char * dcc_preproc_exten(const char *e)
177 {
178     if (e[0] != '.')
179         return NULL;
180     e++;
181     if (!strcmp(e, "i") || !strcmp(e, "c")) {
182         return ".i";
183     } else if (!strcmp(e, "c") || !strcmp(e, "cc")
184                || !strcmp(e, "cpp") || !strcmp(e, "cxx")
185                || !strcmp(e, "cp") || !strcmp(e, "c++")
186                || !strcmp(e, "C") || !strcmp(e, "ii")) {
187         return ".ii";
188     } else if(!strcmp(e,"mi") || !strcmp(e, "m")) {
189         return ".mi";
190     } else if(!strcmp(e,"mii") || !strcmp(e,"mm")
191                 || !strcmp(e,"M")) {
192         return ".mii";
193     } else if (!strcasecmp(e, "s")) {
194         return ".s";
195     } else {
196         return NULL;
197     }
198 }
199 
200 
201 /**
202  * Does the extension of this file indicate that it is already
203  * preprocessed?
204  **/
dcc_is_preprocessed(const char * sfile)205 int dcc_is_preprocessed(const char *sfile)
206 {
207     const char *dot, *ext;
208     dot = dcc_find_extension_const(sfile);
209     if (!dot)
210         return 0;
211     ext = dot+1;
212 
213     switch (ext[0]) {
214 #ifdef ENABLE_REMOTE_ASSEMBLE
215     case 's':
216         /* .S needs to be run through cpp; .s does not */
217         return !strcmp(ext, "s");
218 #endif
219     case 'i':
220         return !strcmp(ext, "i")
221             || !strcmp(ext, "ii");
222     case 'm':
223         return !strcmp(ext, "mi")
224             || !strcmp(ext, "mii");
225     default:
226         return 0;
227     }
228 }
229 
230 
231 /**
232  * Work out whether @p sfile is source based on extension
233  **/
dcc_is_source(const char * sfile)234 int dcc_is_source(const char *sfile)
235 {
236     const char *dot, *ext;
237     dot = dcc_find_extension_const(sfile);
238     if (!dot)
239         return 0;
240     ext = dot+1;
241 
242     /* you could expand this out further into a RE-like set of case
243      * statements, but i'm not sure it's that important. */
244 
245     switch (ext[0]) {
246     case 'i':
247         return !strcmp(ext, "i")
248             || !strcmp(ext, "ii");
249     case 'c':
250         return !strcmp(ext, "c")
251             || !strcmp(ext, "cc")
252             || !strcmp(ext, "cpp")
253             || !strcmp(ext, "cxx")
254             || !strcmp(ext, "cp")
255             || !strcmp(ext, "c++");
256     case 'C':
257         return !strcmp(ext, "C");
258     case 'm':
259         return !strcmp(ext,"m")
260             || !strcmp(ext,"mm")
261             || !strcmp(ext,"mi")
262             || !strcmp(ext,"mii");
263     case 'M':
264         return !strcmp(ext, "M");
265 #ifdef ENABLE_REMOTE_ASSEMBLE
266     case 's':
267         return !strcmp(ext, "s");
268     case 'S':
269         return !strcmp(ext, "S");
270 #endif
271     default:
272         return 0;
273     }
274 }
275 
276 
277 
278 /**
279  * Decide whether @p filename is an object file, based on its
280  * extension.
281  **/
dcc_is_object(const char * filename)282 int dcc_is_object(const char *filename)
283 {
284     const char *dot;
285     dot = dcc_find_extension_const(filename);
286     if (!dot)
287         return 0;
288 
289     return !strcmp(dot, ".o");
290 }
291 
292 
293 /* Some files should always be built locally... */
294 int
dcc_source_needs_local(const char * filename)295 dcc_source_needs_local(const char *filename)
296 {
297     const char *p;
298 
299     p = dcc_find_basename(filename);
300 
301     if (str_startswith("conftest.", p) || str_startswith("tmp.conftest.", p)) {
302         rs_trace("autoconf tests are run locally: %s", filename);
303         return EXIT_DISTCC_FAILED;
304     }
305 
306     return 0;
307 }
308 
309 
310 
311 /**
312  * Work out the default object file name the compiler would use if -o
313  * was not specified.  We don't need to worry about "a.out" because
314  * we've already determined that -c or -S was specified.
315  *
316  * However, the compiler does put the output file in the current
317  * directory even if the source file is elsewhere, so we need to strip
318  * off all leading directories.
319  *
320  * @param sfile Source filename.  Assumed to match one of the
321  * recognized patterns, otherwise bad things might happen.
322  **/
dcc_output_from_source(const char * sfile,const char * out_extn,char ** ofile)323 int dcc_output_from_source(const char *sfile,
324                            const char *out_extn,
325                            char **ofile)
326 {
327     char *slash;
328 
329     if ((slash = strrchr(sfile, '/')))
330         sfile = slash+1;
331     if (strlen(sfile) < 3) {
332         rs_log_error("source file %s is bogus", sfile);
333         return EXIT_DISTCC_FAILED;
334     }
335 
336     return dcc_set_file_extension(sfile, out_extn, ofile);
337 }
338