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