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 #ifdef SUNOS4_AND_AFTER 98 String_rec string; 99 #else 100 String string; 101 #endif 102 Name name; 103 Name target; 104 struct Line *line; 105 struct Recursive *r; 106 Property recurse; 107 wchar_t strbuf[STRING_BUFFER_LENGTH]; 108 wchar_t tmpbuf[STRING_BUFFER_LENGTH]; 109 110 #ifdef LTEST 111 printf("In nse_check_cd, nse = %d, nse_did_recursion = %d\n", nse, nse_did_recursion); 112 #endif 113 #ifdef SUNOS4_AND_AFTER 114 if (!nse_did_recursion || !nse) { 115 #else 116 if (is_false(nse_did_recursion) || is_false(flag.nse)) { 117 #endif 118 #ifdef LTEST 119 printf ("returning, nse = %d, nse_did_recursion = %d\n", nse, nse_did_recursion); 120 #endif 121 return; 122 } 123 line = &prop->body.line; 124 #ifdef LTEST 125 printf("string = %s\n", line->command_template->command_line->string_mb); 126 #endif 127 128 wscpy(tmpbuf, line->command_template->command_line->string); 129 our_template = tmpbuf; 130 cd = false; 131 while (nse_gettoken(&our_template, tok)) { 132 #ifdef LTEST 133 printf("in gettoken loop\n"); 134 #endif 135 #ifdef SUNOS4_AND_AFTER 136 if (IS_WEQUAL(tok, (wchar_t *) "cd")) { 137 #else 138 if (is_equal(tok, "cd")) { 139 #endif 140 cd = true; 141 } else if (cd && tok[0] == '$') { 142 nse_backquote_seen = NULL; 143 nse_shell_var_used = NULL; 144 nse_watch_vars = true; 145 #ifdef SUNOS4_AND_AFTER 146 INIT_STRING_FROM_STACK(string, strbuf); 147 name = GETNAME(tok, FIND_LENGTH); 148 #else 149 init_string_from_stack(string, strbuf); 150 name = getname(tok, FIND_LENGTH); 151 #endif 152 expand_value(name, &string, false); 153 nse_watch_vars = false; 154 155 #ifdef LTEST 156 printf("cd = %d, tok = $\n", cd); 157 #endif 158 /* 159 * Try to trim tok to just 160 * the variable. 161 */ 162 if (nse_shell_var_used != NULL) { 163 nse_warning(); 164 fprintf(stderr, "\tMake invoked recursively by cd'ing to a directory\n\tdefined by the shell environment variable %s\n\tCommand line: %s\n", 165 nse_shell_var_used->string_mb, 166 line->command_template->command_line->string_mb); 167 } 168 if (nse_backquote_seen != NULL) { 169 nse_warning(); 170 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", 171 nse_backquote_seen->string_mb, 172 line->command_template->command_line->string_mb); 173 } 174 cd = false; 175 } else if (cd && nse_backquotes(tok)) { 176 nse_warning(); 177 fprintf(stderr, "\tMake invoked recursively by cd'ing to a directory\n\tspecified by a command in backquotes\n\tCommand line: %s\n", 178 line->command_template->command_line->string_mb); 179 cd = false; 180 } else { 181 cd = false; 182 } 183 } 184 185 /* 186 * Now check for recursion to ".". 187 */ 188 if (primary_makefile != NULL) { 189 target = prop->body.line.target; 190 recurse = get_prop(target->prop, recursive_prop); 191 while (recurse != NULL) { 192 r = &recurse->body.recursive; 193 #ifdef SUNOS4_AND_AFTER 194 if (IS_WEQUAL(r->directory->string, (wchar_t *) ".") && 195 !IS_WEQUAL(r->makefiles->name->string, 196 primary_makefile->string)) { 197 #else 198 if (is_equal(r->directory->string, ".") && 199 !is_equal(r->makefiles->name->string, 200 primary_makefile->string)) { 201 #endif 202 nse_warning(); 203 fprintf(stderr, "\tRecursion to makefile `%s' in the same directory\n\tCommand line: %s\n", 204 r->makefiles->name->string_mb, 205 line->command_template->command_line->string_mb); 206 } 207 recurse = get_prop(recurse->next, recursive_prop); 208 } 209 } 210 } 211 212 /* 213 * Print an NSE make warning line. 214 * If the -P flag was given then consider this a fatal 215 * error, otherwise, just a warning. 216 */ 217 static void 218 nse_warning(void) 219 { 220 #ifdef SUNOS4_AND_AFTER 221 if (report_dependencies_level > 0) { 222 #else 223 if (is_true(flag.report_dependencies)) { 224 #endif 225 our_exit_status = 1; 226 } 227 if (primary_makefile != NULL) { 228 fprintf(stderr, "make: NSE warning from makefile %s/%s:\n", 229 get_current_path(), primary_makefile->string_mb); 230 } else { 231 fprintf(stderr, "make: NSE warning from directory %s:\n", 232 get_current_path()); 233 } 234 } 235 236 /* 237 * Get the next whitespace delimited token pointed to by *cp. 238 * Return it in tok. 239 */ 240 static Boolean 241 nse_gettoken(wchar_t **cp, wchar_t *tok) 242 { 243 wchar_t *to; 244 wchar_t *p; 245 246 p = *cp; 247 while (*p && iswspace(*p)) { 248 p++; 249 } 250 if (*p == '\0') { 251 return false; 252 } 253 to = tok; 254 while (*p && !iswspace(*p)) { 255 *to++ = *p++; 256 } 257 if (*p == '\0') { 258 return false; 259 } 260 *to = '\0'; 261 *cp = p; 262 return true; 263 } 264 265 /* 266 * Given a dependency and a target, see if the dependency 267 * is an SCCS file. Check for the last component of its name 268 * beginning with "s." and the component before that being "SCCS". 269 * The NSE does not consider a source file to be derived from 270 * an SCCS file. 271 */ 272 void 273 nse_check_sccs(wchar_t *targ, wchar_t *dep) 274 { 275 wchar_t *slash; 276 wchar_t *p; 277 278 #ifdef SUNOS4_AND_AFTER 279 if (!nse) { 280 #else 281 if (is_false(flag.nse)) { 282 #endif 283 return; 284 } 285 #ifdef SUNOS4_AND_AFTER 286 slash = wsrchr(dep, (int) slash_char); 287 #else 288 slash = rindex(dep, '/'); 289 #endif 290 if (slash == NULL) { 291 return; 292 } 293 if (slash[1] != 's' || slash[2] != '.') { 294 return; 295 } 296 297 /* 298 * Find the next to last filename component. 299 */ 300 for (p = slash - 1; p >= dep; p--) { 301 if (*p == '/') { 302 break; 303 } 304 } 305 p++; 306 #ifdef SUNOS4_AND_AFTER 307 MBSTOWCS(wcs_buffer, "SCCS/"); 308 if (IS_WEQUALN(p, wcs_buffer, wslen(wcs_buffer))) { 309 #else 310 if (is_equaln(p, "SCCS/", 5)) { 311 #endif 312 nse_warning(); 313 WCSTOMBS(mbs_buffer, targ); 314 WCSTOMBS(mbs_buffer2, dep); 315 fprintf(stderr, "\tFile `%s' depends upon SCCS file `%s'\n", 316 mbs_buffer, mbs_buffer2); 317 } 318 return; 319 } 320 321 /* 322 * Given a filename check to see if it has 2 backquotes in it. 323 * Complain about this because the shell expands the backquotes 324 * but make does not so the files always appear to be out of date. 325 */ 326 void 327 nse_check_file_backquotes(wchar_t *file) 328 { 329 #ifdef SUNOS4_AND_AFTER 330 if (!nse) { 331 #else 332 if (is_false(flag.nse)) { 333 #endif 334 return; 335 } 336 if (nse_backquotes(file)) { 337 nse_warning(); 338 WCSTOMBS(mbs_buffer, file); 339 fprintf(stderr, "\tFilename \"%s\" has backquotes in it\n", 340 mbs_buffer); 341 } 342 } 343 344 /* 345 * Return true if the string has two backquotes in it. 346 */ 347 Boolean 348 nse_backquotes(wchar_t *str) 349 { 350 wchar_t *bq; 351 352 #ifdef SUNOS4_AND_AFTER 353 bq = wschr(str, (int) backquote_char); 354 if (bq) { 355 bq = wschr(&bq[1], (int) backquote_char); 356 #else 357 bq = index(str, '`'); 358 if (bq) { 359 bq = index(&bq[1], '`'); 360 #endif 361 if (bq) { 362 return true; 363 } 364 } 365 return false; 366 } 367 368 /* 369 * A macro that was defined on the command-line was found to affect the 370 * set of dependencies. The NSE "target explode" will not know about 371 * this and will not get the same set of dependencies. 372 */ 373 void 374 nse_dep_cmdmacro(wchar_t *macro) 375 { 376 #ifdef SUNOS4_AND_AFTER 377 if (!nse) { 378 #else 379 if (is_false(flag.nse)) { 380 #endif 381 return; 382 } 383 nse_warning(); 384 WCSTOMBS(mbs_buffer, macro); 385 fprintf(stderr, "\tVariable `%s' is defined on the command-line and\n\taffects dependencies\n", 386 mbs_buffer); 387 } 388 389 /* 390 * A macro that was defined on the command-line was found to 391 * be part of the argument to a cd before a recursive make. 392 * This make cause the make to recurse to different places 393 * depending upon how it is invoked. 394 */ 395 void 396 nse_rule_cmdmacro(wchar_t *macro) 397 { 398 #ifdef SUNOS4_AND_AFTER 399 if (!nse) { 400 #else 401 if (is_false(flag.nse)) { 402 #endif 403 return; 404 } 405 nse_warning(); 406 WCSTOMBS(mbs_buffer, macro); 407 fprintf(stderr, "\tMake invoked recursively by cd'ing to a directory\n\tspecified by a variable (%s) defined on the command-line\n", 408 mbs_buffer); 409 } 410 411 /* 412 * A dependency has been found with a wildcard in it. 413 * This causes the NSE problems because the set of dependencies 414 * can change without changing the Makefile. 415 */ 416 void 417 nse_wildcard(wchar_t *targ, wchar_t *dep) 418 { 419 #ifdef SUNOS4_AND_AFTER 420 if (!nse) { 421 #else 422 if (is_false(flag.nse)) { 423 #endif 424 return; 425 } 426 nse_warning(); 427 WCSTOMBS(mbs_buffer, targ); 428 WCSTOMBS(mbs_buffer2, dep); 429 fprintf(stderr, "\tFile `%s' has a wildcard in dependency `%s'\n", 430 mbs_buffer, mbs_buffer2); 431 } 432 433 /* 434 * Read in the list of suffixes that are interpreted as source 435 * files. 436 */ 437 void 438 nse_init_source_suffixes(void) 439 { 440 FILE *fp; 441 wchar_t suffix[100]; 442 Nse_suffix sufx; 443 Nse_suffix *bpatch; 444 445 fp = fopen(TARG_SUFX, "r"); 446 if (fp == NULL) { 447 return; 448 } 449 bpatch = &sufx_hdr; 450 while (fscanf(fp, "%s %*s", suffix) == 1) { 451 #ifdef SUNOS4_AND_AFTER 452 sufx = ALLOC(Nse_suffix); 453 sufx->suffix = wscpy(ALLOC_WC(wslen(suffix) + 1), suffix); 454 #else 455 sufx = alloc(Nse_suffix); 456 sufx->suffix = strcpy(malloc(strlen(suffix) + 1), suffix); 457 #endif 458 sufx->next = NULL; 459 *bpatch = sufx; 460 bpatch = &sufx->next; 461 } 462 fclose(fp); 463 } 464 465 /* 466 * Check if a derived file (something with a dependency) appears 467 * to be a source file (by its suffix) but has no rule to build it. 468 * If so, complain. 469 * 470 * This generally arises from the old-style of make-depend that 471 * produces: 472 * foo.c: foo.h 473 */ 474 void 475 nse_check_derived_src(Name target, wchar_t *dep, Cmd_line command_template) 476 { 477 Nse_suffix sufx; 478 wchar_t *suffix; 479 wchar_t *depsufx; 480 481 #ifdef SUNOS4_AND_AFTER 482 if (!nse) { 483 #else 484 if (is_false(flag.nse)) { 485 #endif 486 return; 487 } 488 #ifdef SUNOS4_AND_AFTER 489 if (target->stat.is_derived_src) { 490 #else 491 if (is_true(target->stat.is_derived_src)) { 492 #endif 493 return; 494 } 495 if (command_template != NULL) { 496 return; 497 } 498 #ifdef SUNOS4_AND_AFTER 499 suffix = wsrchr(target->string, (int) period_char ); 500 #else 501 suffix = rindex(target->string, '.'); 502 #endif 503 if (suffix != NULL) { 504 for (sufx = sufx_hdr; sufx != NULL; sufx = sufx->next) { 505 #ifdef SUNOS4_AND_AFTER 506 if (IS_WEQUAL(sufx->suffix, suffix)) { 507 #else 508 if (is_equal(sufx->suffix, suffix)) { 509 #endif 510 nse_warning(); 511 WCSTOMBS(mbs_buffer, dep); 512 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", 513 target->string_mb, mbs_buffer); 514 break; 515 } 516 } 517 } 518 } 519 520 /* 521 * See if a target is a potential source file and has no 522 * dependencies and no rule but shows up on the right-hand 523 * side. This tends to occur from old "make depend" output. 524 */ 525 void 526 nse_check_no_deps_no_rule(Name target, Property line, Property command) 527 { 528 Nse_suffix sufx; 529 wchar_t *suffix; 530 531 #ifdef SUNOS4_AND_AFTER 532 if (!nse) { 533 #else 534 if (is_false(flag.nse)) { 535 #endif 536 return; 537 } 538 #ifdef SUNOS4_AND_AFTER 539 if (target->stat.is_derived_src) { 540 #else 541 if (is_true(target->stat.is_derived_src)) { 542 #endif 543 return; 544 } 545 if (line != NULL && line->body.line.dependencies != NULL) { 546 return; 547 } 548 #ifdef SUNOS4_AND_AFTER 549 if (command->body.line.sccs_command) { 550 #else 551 if (is_true(command->body.line.sccs_command)) { 552 #endif 553 return; 554 } 555 #ifdef SUNOS4_AND_AFTER 556 suffix = wsrchr(target->string, (int) period_char); 557 #else 558 suffix = rindex(target->string, '.'); 559 #endif 560 if (suffix != NULL) { 561 for (sufx = sufx_hdr; sufx != NULL; sufx = sufx->next) { 562 #ifdef SUNOS4_AND_AFTER 563 if (IS_WEQUAL(sufx->suffix, suffix)) { 564 #else 565 if (is_equal(sufx->suffix, suffix)) { 566 #endif 567 if (command->body.line.command_template == NULL) { 568 nse_warning(); 569 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", 570 target->string_mb); 571 } 572 } 573 } 574 } 575 } 576 577 /* 578 * Detected a situation where a recursive make derived a file 579 * without using a makefile. 580 */ 581 void 582 nse_no_makefile(Name target) 583 { 584 #ifdef SUNOS4_AND_AFTER 585 if (!nse) { 586 #else 587 if (is_false(flag.nse)) { 588 #endif 589 return; 590 } 591 nse_warning(); 592 fprintf(stderr, "Recursive make to derive %s did not use a makefile\n", 593 target->string_mb); 594 } 595 596 /* 597 * Return the NSE exit status. 598 * If the -P flag was given then a warning is considered fatal 599 */ 600 int 601 nse_exit_status(void) 602 { 603 return our_exit_status; 604 } 605 #endif