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