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