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 2004 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * @(#)state.cc 1.27 06/12/12 27 */ 28 29 #pragma ident "@(#)state.cc 1.27 06/12/12" 30 31 /* 32 * state.c 33 * 34 * This file contains the routines that write the .make.state file 35 */ 36 37 /* 38 * Included files 39 */ 40 #include <mk/defs.h> 41 #include <mksh/misc.h> /* errmsg() */ 42 #include <setjmp.h> /* setjmp() */ 43 #include <unistd.h> /* getpid() */ 44 #include <errno.h> /* errno */ 45 #include <locale.h> /* MB_CUR_MAX */ 46 47 /* 48 * Defined macros 49 */ 50 #define LONGJUMP_VALUE 17 51 #define XFWRITE(string, length, fd) {if (fwrite(string, 1, length, fd) == 0) \ 52 longjmp(long_jump, LONGJUMP_VALUE);} 53 #define XPUTC(ch, fd) { \ 54 if (putc((int) ch, fd) == EOF) \ 55 longjmp(long_jump, LONGJUMP_VALUE); \ 56 } 57 #define XFPUTS(string, fd) fputs(string, fd) 58 59 /* 60 * typedefs & structs 61 */ 62 63 /* 64 * Static variables 65 */ 66 67 /* 68 * File table of contents 69 */ 70 static char * escape_target_name(Name np) 71 { 72 if(np->dollar) { 73 int len = strlen(np->string_mb); 74 char * buff = (char*)malloc(2 * len); 75 int pos = 0; 76 wchar_t wc; 77 int pp = 0; 78 while(pos < len) { 79 int n = mbtowc(&wc, np->string_mb + pos, MB_CUR_MAX); 80 if(n < 0) { // error - this shouldn't happen 81 (void)free(buff); 82 return strdup(np->string_mb); 83 } 84 if(wc == dollar_char) { 85 buff[pp] = '\\'; pp++; 86 buff[pp] = '$'; pp++; 87 } else { 88 for(int j=0;j<n;j++) { 89 buff[pp] = np->string_mb[pos+j]; pp++; 90 } 91 } 92 pos += n; 93 } 94 buff[pp] = '\0'; 95 return buff; 96 } else { 97 return strdup(np->string_mb); 98 } 99 } 100 101 static void print_auto_depes(register Dependency dependency, register FILE *fd, register Boolean built_this_run, register int *line_length, register char *target_name, jmp_buf long_jump); 102 103 /* 104 * write_state_file(report_recursive, exiting) 105 * 106 * Write a new version of .make.state 107 * 108 * Parameters: 109 * report_recursive Should only be done at end of run 110 * exiting true if called from the exit handler 111 * 112 * Global variables used: 113 * built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written 114 * command_changed If no command changed we do not need to write 115 * current_make_version The Name "<current version>", written 116 * do_not_exec_rule If -n is on we do not write statefile 117 * hashtab The hashtable that contains all names 118 * keep_state If .KEEP_STATE is no on we do not write file 119 * make_state The Name ".make.state", used for opening file 120 * make_version The Name ".MAKE_VERSION", written 121 * recursive_name The Name ".RECURSIVE", written 122 * rewrite_statefile Indicates that something changed 123 */ 124 125 void 126 #ifdef NSE 127 write_state_file(int report_recursive, Boolean exiting) 128 #else 129 write_state_file(int, Boolean exiting) 130 #endif 131 { 132 register FILE *fd; 133 int lock_err; 134 char buffer[MAXPATHLEN]; 135 char make_state_tempfile[MAXPATHLEN]; 136 jmp_buf long_jump; 137 register int attempts = 0; 138 Name_set::iterator np, e; 139 register Property lines; 140 register int m; 141 Dependency dependency; 142 register Boolean name_printed; 143 Boolean built_this_run = false; 144 char *target_name; 145 int line_length; 146 register Cmd_line cp; 147 148 149 if (!rewrite_statefile || 150 !command_changed || 151 !keep_state || 152 do_not_exec_rule || 153 (report_dependencies_level > 0)) { 154 return; 155 } 156 /* Lock the file for writing. */ 157 make_state_lockfile = getmem(strlen(make_state->string_mb) + strlen(NOCATGETS(".lock")) + 1); 158 (void) sprintf(make_state_lockfile, 159 NOCATGETS("%s.lock"), 160 make_state->string_mb); 161 if (lock_err = file_lock(make_state->string_mb, 162 make_state_lockfile, 163 (int *) &make_state_locked, 0)) { 164 retmem_mb(make_state_lockfile); 165 make_state_lockfile = NULL; 166 167 /* 168 * We need to make sure that we are not being 169 * called by the exit handler so we don't call 170 * it again. 171 */ 172 173 if (exiting) { 174 (void) sprintf(buffer, NOCATGETS("%s/.make.state.%d.XXXXXX"), tmpdir, getpid()); 175 report_pwd = true; 176 warning(catgets(catd, 1, 60, "Writing to %s"), buffer); 177 int fdes = mkstemp(buffer); 178 if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) { 179 fprintf(stderr, 180 catgets(catd, 1, 61, "Could not open statefile `%s': %s"), 181 buffer, 182 errmsg(errno)); 183 return; 184 } 185 } else { 186 report_pwd = true; 187 fatal(catgets(catd, 1, 62, "Can't lock .make.state")); 188 } 189 } 190 191 (void) sprintf(make_state_tempfile, 192 NOCATGETS("%s.tmp"), 193 make_state->string_mb); 194 /* Delete old temporary statefile (in case it exists) */ 195 (void) unlink(make_state_tempfile); 196 if ((fd = fopen(make_state_tempfile, "w")) == NULL) { 197 lock_err = errno; /* Save it! unlink() can change errno */ 198 (void) unlink(make_state_lockfile); 199 retmem_mb(make_state_lockfile); 200 make_state_lockfile = NULL; 201 make_state_locked = false; 202 fatal(catgets(catd, 1, 59, "Could not open temporary statefile `%s': %s"), 203 make_state_tempfile, 204 errmsg(lock_err)); 205 } 206 #ifdef NSE 207 if (nse) { 208 (void) fchmod(fileno(fd), 0666); 209 } 210 #endif 211 /* 212 * Set a trap for failed writes. If a write fails, the routine 213 * will try saving the .make.state file under another name in /tmp. 214 */ 215 if (setjmp(long_jump)) { 216 (void) fclose(fd); 217 if (attempts++ > 5) { 218 if ((make_state_lockfile != NULL) && 219 make_state_locked) { 220 (void) unlink(make_state_lockfile); 221 retmem_mb(make_state_lockfile); 222 make_state_lockfile = NULL; 223 make_state_locked = false; 224 } 225 fatal(catgets(catd, 1, 63, "Giving up on writing statefile")); 226 } 227 sleep(10); 228 (void) sprintf(buffer, NOCATGETS("%s/.make.state.%d.XXXXXX"), tmpdir, getpid()); 229 int fdes = mkstemp(buffer); 230 if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) { 231 fatal(catgets(catd, 1, 64, "Could not open statefile `%s': %s"), 232 buffer, 233 errmsg(errno)); 234 } 235 warning(catgets(catd, 1, 65, "Initial write of statefile failed. Trying again on %s"), 236 buffer); 237 } 238 239 /* Write the version stamp. */ 240 XFWRITE(make_version->string_mb, 241 strlen(make_version->string_mb), 242 fd); 243 XPUTC(colon_char, fd); 244 XPUTC(tab_char, fd); 245 XFWRITE(current_make_version->string_mb, 246 strlen(current_make_version->string_mb), 247 fd); 248 XPUTC(newline_char, fd); 249 250 /* 251 * Go through all the targets, dump their dependencies and 252 * command used. 253 */ 254 for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) { 255 /* 256 * If the target has no command used nor dependencies, 257 * we can go to the next one. 258 */ 259 if ((lines = get_prop(np->prop, line_prop)) == NULL) { 260 continue; 261 } 262 /* If this target is a special target, don't print. */ 263 if (np->special_reader != no_special) { 264 continue; 265 } 266 /* 267 * Find out if any of the targets dependencies should 268 * be written to .make.state. 269 */ 270 for (m = 0, dependency = lines->body.line.dependencies; 271 dependency != NULL; 272 dependency = dependency->next) { 273 if (m = !dependency->stale 274 && (dependency->name != force) 275 #ifndef PRINT_EXPLICIT_DEPEN 276 && dependency->automatic 277 #endif 278 ) { 279 break; 280 } 281 } 282 /* Only print if dependencies listed. */ 283 if (m || (lines->body.line.command_used != NULL)) { 284 name_printed = false; 285 /* 286 * If this target was built during this make run, 287 * we mark it. 288 */ 289 built_this_run = false; 290 if (np->has_built) { 291 built_this_run = true; 292 XFWRITE(built_last_make_run->string_mb, 293 strlen(built_last_make_run->string_mb), 294 fd); 295 XPUTC(colon_char, fd); 296 XPUTC(newline_char, fd); 297 } 298 /* If the target has dependencies, we dump them. */ 299 target_name = escape_target_name(np); 300 if (np->has_long_member_name) { 301 target_name = 302 get_prop(np->prop, long_member_name_prop) 303 ->body.long_member_name.member_name-> 304 string_mb; 305 } 306 if (m) { 307 XFPUTS(target_name, fd); 308 XPUTC(colon_char, fd); 309 XFPUTS("\t", fd); 310 name_printed = true; 311 line_length = 0; 312 for (dependency = 313 lines->body.line.dependencies; 314 dependency != NULL; 315 dependency = dependency->next) { 316 print_auto_depes(dependency, 317 fd, 318 built_this_run, 319 &line_length, 320 target_name, 321 long_jump); 322 } 323 XFPUTS("\n", fd); 324 } 325 /* If there is a command used, we dump it. */ 326 if (lines->body.line.command_used != NULL) { 327 /* 328 * Only write the target name if it 329 * wasn't done for the dependencies. 330 */ 331 if (!name_printed) { 332 XFPUTS(target_name, fd); 333 XPUTC(colon_char, fd); 334 XPUTC(newline_char, fd); 335 } 336 /* 337 * Write the command lines. 338 * Prefix each textual line with a tab. 339 */ 340 for (cp = lines->body.line.command_used; 341 cp != NULL; 342 cp = cp->next) { 343 char *csp; 344 int n; 345 346 XPUTC(tab_char, fd); 347 if (cp->command_line != NULL) { 348 for (csp = cp-> 349 command_line-> 350 string_mb, 351 n = strlen(cp-> 352 command_line-> 353 string_mb); 354 n > 0; 355 n--, csp++) { 356 XPUTC(*csp, fd); 357 if (*csp == 358 (int) newline_char) { 359 XPUTC(tab_char, 360 fd); 361 } 362 } 363 } 364 XPUTC(newline_char, fd); 365 } 366 } 367 (void)free(target_name); 368 } 369 } 370 if (fclose(fd) == EOF) { 371 longjmp(long_jump, LONGJUMP_VALUE); 372 } 373 if (attempts == 0) { 374 if (unlink(make_state->string_mb) != 0 && errno != ENOENT) { 375 lock_err = errno; /* Save it! unlink() can change errno */ 376 /* Delete temporary statefile */ 377 (void) unlink(make_state_tempfile); 378 (void) unlink(make_state_lockfile); 379 retmem_mb(make_state_lockfile); 380 make_state_lockfile = NULL; 381 make_state_locked = false; 382 fatal(catgets(catd, 1, 356, "Could not delete old statefile `%s': %s"), 383 make_state->string_mb, 384 errmsg(lock_err)); 385 } 386 if (rename(make_state_tempfile, make_state->string_mb) != 0) { 387 lock_err = errno; /* Save it! unlink() can change errno */ 388 /* Delete temporary statefile */ 389 (void) unlink(make_state_tempfile); 390 (void) unlink(make_state_lockfile); 391 retmem_mb(make_state_lockfile); 392 make_state_lockfile = NULL; 393 make_state_locked = false; 394 fatal(catgets(catd, 1, 357, "Could not rename `%s' to `%s': %s"), 395 make_state_tempfile, 396 make_state->string_mb, 397 errmsg(lock_err)); 398 } 399 } 400 if ((make_state_lockfile != NULL) && make_state_locked) { 401 (void) unlink(make_state_lockfile); 402 retmem_mb(make_state_lockfile); 403 make_state_lockfile = NULL; 404 make_state_locked = false; 405 } 406 #ifdef NSE 407 if (report_recursive) { 408 report_recursive_done(); 409 } 410 #endif 411 } 412 413 /* 414 * print_auto_depes(dependency, fd, built_this_run, 415 * line_length, target_name, long_jump) 416 * 417 * Will print a dependency list for automatic entries. 418 * 419 * Parameters: 420 * dependency The dependency to print 421 * fd The file to print it to 422 * built_this_run If on we prefix each line with .BUILT_THIS... 423 * line_length Pointer to line length var that we update 424 * target_name We need this when we restart line 425 * long_jump setjmp/longjmp buffer used for IO error action 426 * 427 * Global variables used: 428 * built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written 429 * force The Name " FORCE", compared against 430 */ 431 static void 432 print_auto_depes(register Dependency dependency, register FILE *fd, register Boolean built_this_run, register int *line_length, register char *target_name, jmp_buf long_jump) 433 { 434 if (!dependency->automatic || 435 dependency->stale || 436 (dependency->name == force)) { 437 return; 438 } 439 XFWRITE(dependency->name->string_mb, 440 strlen(dependency->name->string_mb), 441 fd); 442 /* 443 * Check if the dependency line is too long. 444 * If so, break it and start a new one. 445 */ 446 if ((*line_length += (int) strlen(dependency->name->string_mb) + 1) > 450) { 447 *line_length = 0; 448 XPUTC(newline_char, fd); 449 if (built_this_run) { 450 XFPUTS(built_last_make_run->string_mb, fd); 451 XPUTC(colon_char, fd); 452 XPUTC(newline_char, fd); 453 } 454 XFPUTS(target_name, fd); 455 XPUTC(colon_char, fd); 456 XPUTC(tab_char, fd); 457 } else { 458 XFPUTS(" ", fd); 459 } 460 return; 461 } 462 463