1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 1994 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #ifdef NSE
27
28 /*
29 * Included files
30 */
31 #include <mk/defs.h>
32 #include <mksh/macro.h> /* expand_value() */
33 #include <mksh/misc.h> /* get_prop() */
34
35 /*
36 * This file does some extra checking on behalf of the NSE.
37 * It does some stuff that is analogous to lint in that it
38 * looks for things which may be legal but that give the NSE
39 * trouble. Currently it looks for:
40 * 1) recursion by cd'ing to a directory specified by a
41 * make variable that is defined from the shell environment.
42 * 2) recursion by cd'ing to a directory specified by a
43 * make variable that has backquotes in it.
44 * 3) recursion to another makefile in the same directory.
45 * 4) a dependency upon an SCCS file (SCCS/s.*)
46 * 5) backquotes in a file name
47 * 6) a make variable being defined on the command-line that
48 * ends up affecting dependencies
49 * 7) wildcards (*[) in dependencies
50 * 8) recursion to the same directory
51 * 9) potential source files on the left-hand-side so
52 * that they appear as derived files
53 *
54 * Things it should look for:
55 * 1) makefiles that are symlinks (why are these a problem?)
56 */
57
58 #define TARG_SUFX "/usr/nse/lib/nse_targ.sufx"
59
60 typedef struct _Nse_suffix *Nse_suffix, Nse_suffix_rec;
61 struct _Nse_suffix {
62 wchar_t *suffix; /* The suffix */
63 struct _Nse_suffix *next; /* Linked list */
64 };
65 static Nse_suffix sufx_hdr;
66 static int our_exit_status;
67
68 static void nse_warning(void);
69 static Boolean nse_gettoken(wchar_t **, wchar_t *);
70
71 /*
72 * Given a command that has just recursed to a sub make
73 * try to determine if it cd'ed to a directory that was
74 * defined by a make variable imported from the shell
75 * environment or a variable with backquotes in it.
76 * This routine will find something like:
77 * cd $(DIR); $(MAKE)
78 * where DIR is imported from the shell environment.
79 * However it well not find:
80 * CD = cd
81 * $(CD) $(DIR); $(MAKE)
82 * or
83 * CD = cd $(DIR)
84 * $(CD); $(MAKE)
85 *
86 * This routine also checks for recursion to the same
87 * directory.
88 */
89 void
90 nse_check_cd(Property prop)
91 {
92 wchar_t tok[512];
93 wchar_t *p;
94 wchar_t *our_template;
95 int len;
96 Boolean cd;
97 String_rec string;
98 Name name;
99 Name target;
100 struct Line *line;
101 struct Recursive *r;
102 Property recurse;
103 wchar_t strbuf[STRING_BUFFER_LENGTH];
104 wchar_t tmpbuf[STRING_BUFFER_LENGTH];
105
106 #ifdef LTEST
107 printf("In nse_check_cd, nse = %d, nse_did_recursion = %d\n", nse, nse_did_recursion);
108 #endif
109 if (!nse_did_recursion || !nse) {
110 #ifdef LTEST
111 printf ("returning, nse = %d, nse_did_recursion = %d\n", nse, nse_did_recursion);
112 #endif
113 return;
114 }
115 line = &prop->body.line;
116 #ifdef LTEST
117 printf("string = %s\n", line->command_template->command_line->string_mb);
118 #endif
119
120 wscpy(tmpbuf, line->command_template->command_line->string);
121 our_template = tmpbuf;
122 cd = false;
123 while (nse_gettoken(&our_template, tok)) {
124 #ifdef LTEST
125 printf("in gettoken loop\n");
126 #endif
127 if (IS_WEQUAL(tok, (wchar_t *) "cd")) {
128 cd = true;
129 } else if (cd && tok[0] == '$') {
130 nse_backquote_seen = NULL;
131 nse_shell_var_used = NULL;
132 nse_watch_vars = true;
133 INIT_STRING_FROM_STACK(string, strbuf);
134 name = GETNAME(tok, FIND_LENGTH);
135 expand_value(name, &string, false);
136 nse_watch_vars = false;
137
138 #ifdef LTEST
139 printf("cd = %d, tok = $\n", cd);
140 #endif
141 /*
142 * Try to trim tok to just
143 * the variable.
144 */
145 if (nse_shell_var_used != NULL) {
146 nse_warning();
147 fprintf(stderr, "\tMake invoked recursively by cd'ing to a directory\n\tdefined by the shell environment variable %s\n\tCommand line: %s\n",
148 nse_shell_var_used->string_mb,
149 line->command_template->command_line->string_mb);
150 }
151 if (nse_backquote_seen != NULL) {
152 nse_warning();
153 fprintf(stderr, "\tMake invoked recursively by cd'ing to a directory\n\tdefined by a variable (%s) with backquotes in it\n\tCommand line: %s\n",
154 nse_backquote_seen->string_mb,
155 line->command_template->command_line->string_mb);
156 }
157 cd = false;
158 } else if (cd && nse_backquotes(tok)) {
159 nse_warning();
160 fprintf(stderr, "\tMake invoked recursively by cd'ing to a directory\n\tspecified by a command in backquotes\n\tCommand line: %s\n",
161 line->command_template->command_line->string_mb);
162 cd = false;
163 } else {
164 cd = false;
165 }
166 }
167
168 /*
169 * Now check for recursion to ".".
170 */
171 if (primary_makefile != NULL) {
172 target = prop->body.line.target;
173 recurse = get_prop(target->prop, recursive_prop);
174 while (recurse != NULL) {
175 r = &recurse->body.recursive;
176 if (IS_WEQUAL(r->directory->string, (wchar_t *) ".") &&
177 !IS_WEQUAL(r->makefiles->name->string,
178 primary_makefile->string)) {
179 nse_warning();
180 fprintf(stderr, "\tRecursion to makefile `%s' in the same directory\n\tCommand line: %s\n",
181 r->makefiles->name->string_mb,
182 line->command_template->command_line->string_mb);
183 }
184 recurse = get_prop(recurse->next, recursive_prop);
185 }
186 }
187 }
188
189 /*
190 * Print an NSE make warning line.
191 * If the -P flag was given then consider this a fatal
192 * error, otherwise, just a warning.
193 */
194 static void
195 nse_warning(void)
196 {
197 if (report_dependencies_level > 0) {
198 our_exit_status = 1;
199 }
200 if (primary_makefile != NULL) {
201 fprintf(stderr, "make: NSE warning from makefile %s/%s:\n",
202 get_current_path(), primary_makefile->string_mb);
203 } else {
204 fprintf(stderr, "make: NSE warning from directory %s:\n",
205 get_current_path());
206 }
207 }
208
209 /*
210 * Get the next whitespace delimited token pointed to by *cp.
211 * Return it in tok.
212 */
213 static Boolean
214 nse_gettoken(wchar_t **cp, wchar_t *tok)
215 {
216 wchar_t *to;
217 wchar_t *p;
218
219 p = *cp;
220 while (*p && iswspace(*p)) {
221 p++;
222 }
223 if (*p == '\0') {
224 return false;
225 }
226 to = tok;
227 while (*p && !iswspace(*p)) {
228 *to++ = *p++;
229 }
230 if (*p == '\0') {
231 return false;
232 }
233 *to = '\0';
234 *cp = p;
235 return true;
236 }
237
238 /*
239 * Given a dependency and a target, see if the dependency
240 * is an SCCS file. Check for the last component of its name
241 * beginning with "s." and the component before that being "SCCS".
242 * The NSE does not consider a source file to be derived from
243 * an SCCS file.
244 */
245 void
246 nse_check_sccs(wchar_t *targ, wchar_t *dep)
247 {
248 wchar_t *slash;
249 wchar_t *p;
250
251 if (!nse) {
252 return;
253 }
254 slash = wsrchr(dep, (int) slash_char);
255 if (slash == NULL) {
256 return;
257 }
258 if (slash[1] != 's' || slash[2] != '.') {
259 return;
260 }
261
262 /*
263 * Find the next to last filename component.
264 */
265 for (p = slash - 1; p >= dep; p--) {
266 if (*p == '/') {
267 break;
268 }
269 }
270 p++;
271 MBSTOWCS(wcs_buffer, "SCCS/");
272 if (IS_WEQUALN(p, wcs_buffer, wslen(wcs_buffer))) {
273 nse_warning();
274 WCSTOMBS(mbs_buffer, targ);
275 WCSTOMBS(mbs_buffer2, dep);
276 fprintf(stderr, "\tFile `%s' depends upon SCCS file `%s'\n",
277 mbs_buffer, mbs_buffer2);
278 }
279 return;
280 }
281
282 /*
283 * Given a filename check to see if it has 2 backquotes in it.
284 * Complain about this because the shell expands the backquotes
285 * but make does not so the files always appear to be out of date.
286 */
287 void
288 nse_check_file_backquotes(wchar_t *file)
289 {
290 if (!nse) {
291 return;
292 }
293 if (nse_backquotes(file)) {
294 nse_warning();
295 WCSTOMBS(mbs_buffer, file);
296 fprintf(stderr, "\tFilename \"%s\" has backquotes in it\n",
297 mbs_buffer);
298 }
299 }
300
301 /*
302 * Return true if the string has two backquotes in it.
303 */
304 Boolean
305 nse_backquotes(wchar_t *str)
306 {
307 wchar_t *bq;
308
309 bq = wschr(str, (int) backquote_char);
310 if (bq) {
311 bq = wschr(&bq[1], (int) backquote_char);
312 if (bq) {
313 return true;
314 }
315 }
316 return false;
317 }
318
319 /*
320 * A macro that was defined on the command-line was found to affect the
321 * set of dependencies. The NSE "target explode" will not know about
322 * this and will not get the same set of dependencies.
323 */
324 void
325 nse_dep_cmdmacro(wchar_t *macro)
326 {
327 if (!nse) {
328 return;
329 }
330 nse_warning();
331 WCSTOMBS(mbs_buffer, macro);
332 fprintf(stderr, "\tVariable `%s' is defined on the command-line and\n\taffects dependencies\n",
333 mbs_buffer);
334 }
335
336 /*
337 * A macro that was defined on the command-line was found to
338 * be part of the argument to a cd before a recursive make.
339 * This make cause the make to recurse to different places
340 * depending upon how it is invoked.
341 */
342 void
343 nse_rule_cmdmacro(wchar_t *macro)
344 {
345 if (!nse) {
346 return;
347 }
348 nse_warning();
349 WCSTOMBS(mbs_buffer, macro);
350 fprintf(stderr, "\tMake invoked recursively by cd'ing to a directory\n\tspecified by a variable (%s) defined on the command-line\n",
351 mbs_buffer);
352 }
353
354 /*
355 * A dependency has been found with a wildcard in it.
356 * This causes the NSE problems because the set of dependencies
357 * can change without changing the Makefile.
358 */
359 void
360 nse_wildcard(wchar_t *targ, wchar_t *dep)
361 {
362 if (!nse) {
363 return;
364 }
365 nse_warning();
366 WCSTOMBS(mbs_buffer, targ);
367 WCSTOMBS(mbs_buffer2, dep);
368 fprintf(stderr, "\tFile `%s' has a wildcard in dependency `%s'\n",
369 mbs_buffer, mbs_buffer2);
370 }
371
372 /*
373 * Read in the list of suffixes that are interpreted as source
374 * files.
375 */
376 void
377 nse_init_source_suffixes(void)
378 {
379 FILE *fp;
380 wchar_t suffix[100];
381 Nse_suffix sufx;
382 Nse_suffix *bpatch;
383
384 fp = fopen(TARG_SUFX, "r");
385 if (fp == NULL) {
386 return;
387 }
388 bpatch = &sufx_hdr;
389 while (fscanf(fp, "%s %*s", suffix) == 1) {
390 sufx = ALLOC(Nse_suffix);
391 sufx->suffix = wscpy(ALLOC_WC(wslen(suffix) + 1), suffix);
392 sufx->next = NULL;
393 *bpatch = sufx;
394 bpatch = &sufx->next;
395 }
396 fclose(fp);
397 }
398
399 /*
400 * Check if a derived file (something with a dependency) appears
401 * to be a source file (by its suffix) but has no rule to build it.
402 * If so, complain.
403 *
404 * This generally arises from the old-style of make-depend that
405 * produces:
406 * foo.c: foo.h
407 */
408 void
409 nse_check_derived_src(Name target, wchar_t *dep, Cmd_line command_template)
410 {
411 Nse_suffix sufx;
412 wchar_t *suffix;
413 wchar_t *depsufx;
414
415 if (!nse) {
416 return;
417 }
418 if (target->stat.is_derived_src) {
419 return;
420 }
421 if (command_template != NULL) {
422 return;
423 }
424 suffix = wsrchr(target->string, (int) period_char );
425 if (suffix != NULL) {
426 for (sufx = sufx_hdr; sufx != NULL; sufx = sufx->next) {
427 if (IS_WEQUAL(sufx->suffix, suffix)) {
428 nse_warning();
429 WCSTOMBS(mbs_buffer, dep);
430 fprintf(stderr, "\tProbable source file `%s' appears as a derived file\n\tas it depends upon file `%s', but there is\n\tno rule to build it\n",
431 target->string_mb, mbs_buffer);
432 break;
433 }
434 }
435 }
436 }
437
438 /*
439 * See if a target is a potential source file and has no
440 * dependencies and no rule but shows up on the right-hand
441 * side. This tends to occur from old "make depend" output.
442 */
443 void
444 nse_check_no_deps_no_rule(Name target, Property line, Property command)
445 {
446 Nse_suffix sufx;
447 wchar_t *suffix;
448
449 if (!nse) {
450 return;
451 }
452 if (target->stat.is_derived_src) {
453 return;
454 }
455 if (line != NULL && line->body.line.dependencies != NULL) {
456 return;
457 }
458 if (command->body.line.sccs_command) {
459 return;
460 }
461 suffix = wsrchr(target->string, (int) period_char);
462 if (suffix != NULL) {
463 for (sufx = sufx_hdr; sufx != NULL; sufx = sufx->next) {
464 if (IS_WEQUAL(sufx->suffix, suffix)) {
465 if (command->body.line.command_template == NULL) {
466 nse_warning();
467 fprintf(stderr, "\tProbable source file `%s' appears as a derived file because\n\tit is on the left-hand side, but it has no dependencies and\n\tno rule to build it\n",
468 target->string_mb);
469 }
470 }
471 }
472 }
473 }
474
475 /*
476 * Detected a situation where a recursive make derived a file
477 * without using a makefile.
478 */
479 void
480 nse_no_makefile(Name target)
481 {
482 if (!nse) {
483 return;
484 }
485 nse_warning();
486 fprintf(stderr, "Recursive make to derive %s did not use a makefile\n",
487 target->string_mb);
488 }
489
490 /*
491 * Return the NSE exit status.
492 * If the -P flag was given then a warning is considered fatal
493 */
494 int
495 nse_exit_status(void)
496 {
497 return our_exit_status;
498 }
499 #endif