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 write_state_file(int, Boolean exiting) 122 { 123 register FILE *fd; 124 int lock_err; 125 char buffer[MAXPATHLEN]; 126 char make_state_tempfile[MAXPATHLEN]; 127 jmp_buf long_jump; 128 register int attempts = 0; 129 Name_set::iterator np, e; 130 register Property lines; 131 register int m; 132 Dependency dependency; 133 register Boolean name_printed; 134 Boolean built_this_run = false; 135 char *target_name; 136 int line_length; 137 register Cmd_line cp; 138 139 140 if (!rewrite_statefile || 141 !command_changed || 142 !keep_state || 143 do_not_exec_rule || 144 (report_dependencies_level > 0)) { 145 return; 146 } 147 /* Lock the file for writing. */ 148 make_state_lockfile = getmem(strlen(make_state->string_mb) + strlen(NOCATGETS(".lock")) + 1); 149 (void) sprintf(make_state_lockfile, 150 NOCATGETS("%s.lock"), 151 make_state->string_mb); 152 if (lock_err = file_lock(make_state->string_mb, 153 make_state_lockfile, 154 (int *) &make_state_locked, 0)) { 155 retmem_mb(make_state_lockfile); 156 make_state_lockfile = NULL; 157 158 /* 159 * We need to make sure that we are not being 160 * called by the exit handler so we don't call 161 * it again. 162 */ 163 164 if (exiting) { 165 (void) sprintf(buffer, NOCATGETS("%s/.make.state.%d.XXXXXX"), tmpdir, getpid()); 166 report_pwd = true; 167 warning(catgets(catd, 1, 60, "Writing to %s"), buffer); 168 int fdes = mkstemp(buffer); 169 if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) { 170 fprintf(stderr, 171 catgets(catd, 1, 61, "Could not open statefile `%s': %s"), 172 buffer, 173 errmsg(errno)); 174 return; 175 } 176 } else { 177 report_pwd = true; 178 fatal(catgets(catd, 1, 62, "Can't lock .make.state")); 179 } 180 } 181 182 (void) sprintf(make_state_tempfile, 183 NOCATGETS("%s.tmp"), 184 make_state->string_mb); 185 /* Delete old temporary statefile (in case it exists) */ 186 (void) unlink(make_state_tempfile); 187 if ((fd = fopen(make_state_tempfile, "w")) == NULL) { 188 lock_err = errno; /* Save it! unlink() can change errno */ 189 (void) unlink(make_state_lockfile); 190 retmem_mb(make_state_lockfile); 191 make_state_lockfile = NULL; 192 make_state_locked = false; 193 fatal(catgets(catd, 1, 59, "Could not open temporary statefile `%s': %s"), 194 make_state_tempfile, 195 errmsg(lock_err)); 196 } 197 /* 198 * Set a trap for failed writes. If a write fails, the routine 199 * will try saving the .make.state file under another name in /tmp. 200 */ 201 if (setjmp(long_jump)) { 202 (void) fclose(fd); 203 if (attempts++ > 5) { 204 if ((make_state_lockfile != NULL) && 205 make_state_locked) { 206 (void) unlink(make_state_lockfile); 207 retmem_mb(make_state_lockfile); 208 make_state_lockfile = NULL; 209 make_state_locked = false; 210 } 211 fatal(catgets(catd, 1, 63, "Giving up on writing statefile")); 212 } 213 sleep(10); 214 (void) sprintf(buffer, NOCATGETS("%s/.make.state.%d.XXXXXX"), tmpdir, getpid()); 215 int fdes = mkstemp(buffer); 216 if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) { 217 fatal(catgets(catd, 1, 64, "Could not open statefile `%s': %s"), 218 buffer, 219 errmsg(errno)); 220 } 221 warning(catgets(catd, 1, 65, "Initial write of statefile failed. Trying again on %s"), 222 buffer); 223 } 224 225 /* Write the version stamp. */ 226 XFWRITE(make_version->string_mb, 227 strlen(make_version->string_mb), 228 fd); 229 XPUTC(colon_char, fd); 230 XPUTC(tab_char, fd); 231 XFWRITE(current_make_version->string_mb, 232 strlen(current_make_version->string_mb), 233 fd); 234 XPUTC(newline_char, fd); 235 236 /* 237 * Go through all the targets, dump their dependencies and 238 * command used. 239 */ 240 for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) { 241 /* 242 * If the target has no command used nor dependencies, 243 * we can go to the next one. 244 */ 245 if ((lines = get_prop(np->prop, line_prop)) == NULL) { 246 continue; 247 } 248 /* If this target is a special target, don't print. */ 249 if (np->special_reader != no_special) { 250 continue; 251 } 252 /* 253 * Find out if any of the targets dependencies should 254 * be written to .make.state. 255 */ 256 for (m = 0, dependency = lines->body.line.dependencies; 257 dependency != NULL; 258 dependency = dependency->next) { 259 if (m = !dependency->stale 260 && (dependency->name != force) 261 #ifndef PRINT_EXPLICIT_DEPEN 262 && dependency->automatic 263 #endif 264 ) { 265 break; 266 } 267 } 268 /* Only print if dependencies listed. */ 269 if (m || (lines->body.line.command_used != NULL)) { 270 name_printed = false; 271 /* 272 * If this target was built during this make run, 273 * we mark it. 274 */ 275 built_this_run = false; 276 if (np->has_built) { 277 built_this_run = true; 278 XFWRITE(built_last_make_run->string_mb, 279 strlen(built_last_make_run->string_mb), 280 fd); 281 XPUTC(colon_char, fd); 282 XPUTC(newline_char, fd); 283 } 284 /* If the target has dependencies, we dump them. */ 285 target_name = escape_target_name(np); 286 if (np->has_long_member_name) { 287 target_name = 288 get_prop(np->prop, long_member_name_prop) 289 ->body.long_member_name.member_name-> 290 string_mb; 291 } 292 if (m) { 293 XFPUTS(target_name, fd); 294 XPUTC(colon_char, fd); 295 XFPUTS("\t", fd); 296 name_printed = true; 297 line_length = 0; 298 for (dependency = 299 lines->body.line.dependencies; 300 dependency != NULL; 301 dependency = dependency->next) { 302 print_auto_depes(dependency, 303 fd, 304 built_this_run, 305 &line_length, 306 target_name, 307 long_jump); 308 } 309 XFPUTS("\n", fd); 310 } 311 /* If there is a command used, we dump it. */ 312 if (lines->body.line.command_used != NULL) { 313 /* 314 * Only write the target name if it 315 * wasn't done for the dependencies. 316 */ 317 if (!name_printed) { 318 XFPUTS(target_name, fd); 319 XPUTC(colon_char, fd); 320 XPUTC(newline_char, fd); 321 } 322 /* 323 * Write the command lines. 324 * Prefix each textual line with a tab. 325 */ 326 for (cp = lines->body.line.command_used; 327 cp != NULL; 328 cp = cp->next) { 329 char *csp; 330 int n; 331 332 XPUTC(tab_char, fd); 333 if (cp->command_line != NULL) { 334 for (csp = cp-> 335 command_line-> 336 string_mb, 337 n = strlen(cp-> 338 command_line-> 339 string_mb); 340 n > 0; 341 n--, csp++) { 342 XPUTC(*csp, fd); 343 if (*csp == 344 (int) newline_char) { 345 XPUTC(tab_char, 346 fd); 347 } 348 } 349 } 350 XPUTC(newline_char, fd); 351 } 352 } 353 (void)free(target_name); 354 } 355 } 356 if (fclose(fd) == EOF) { 357 longjmp(long_jump, LONGJUMP_VALUE); 358 } 359 if (attempts == 0) { 360 if (unlink(make_state->string_mb) != 0 && errno != ENOENT) { 361 lock_err = errno; /* Save it! unlink() can change errno */ 362 /* Delete temporary statefile */ 363 (void) unlink(make_state_tempfile); 364 (void) unlink(make_state_lockfile); 365 retmem_mb(make_state_lockfile); 366 make_state_lockfile = NULL; 367 make_state_locked = false; 368 fatal(catgets(catd, 1, 356, "Could not delete old statefile `%s': %s"), 369 make_state->string_mb, 370 errmsg(lock_err)); 371 } 372 if (rename(make_state_tempfile, make_state->string_mb) != 0) { 373 lock_err = errno; /* Save it! unlink() can change errno */ 374 /* Delete temporary statefile */ 375 (void) unlink(make_state_tempfile); 376 (void) unlink(make_state_lockfile); 377 retmem_mb(make_state_lockfile); 378 make_state_lockfile = NULL; 379 make_state_locked = false; 380 fatal(catgets(catd, 1, 357, "Could not rename `%s' to `%s': %s"), 381 make_state_tempfile, 382 make_state->string_mb, 383 errmsg(lock_err)); 384 } 385 } 386 if ((make_state_lockfile != NULL) && make_state_locked) { 387 (void) unlink(make_state_lockfile); 388 retmem_mb(make_state_lockfile); 389 make_state_lockfile = NULL; 390 make_state_locked = false; 391 } 392 } 393 394 /* 395 * print_auto_depes(dependency, fd, built_this_run, 396 * line_length, target_name, long_jump) 397 * 398 * Will print a dependency list for automatic entries. 399 * 400 * Parameters: 401 * dependency The dependency to print 402 * fd The file to print it to 403 * built_this_run If on we prefix each line with .BUILT_THIS... 404 * line_length Pointer to line length var that we update 405 * target_name We need this when we restart line 406 * long_jump setjmp/longjmp buffer used for IO error action 407 * 408 * Global variables used: 409 * built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written 410 * force The Name " FORCE", compared against 411 */ 412 static void 413 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) 414 { 415 if (!dependency->automatic || 416 dependency->stale || 417 (dependency->name == force)) { 418 return; 419 } 420 XFWRITE(dependency->name->string_mb, 421 strlen(dependency->name->string_mb), 422 fd); 423 /* 424 * Check if the dependency line is too long. 425 * If so, break it and start a new one. 426 */ 427 if ((*line_length += (int) strlen(dependency->name->string_mb) + 1) > 450) { 428 *line_length = 0; 429 XPUTC(newline_char, fd); 430 if (built_this_run) { 431 XFPUTS(built_last_make_run->string_mb, fd); 432 XPUTC(colon_char, fd); 433 XPUTC(newline_char, fd); 434 } 435 XFPUTS(target_name, fd); 436 XPUTC(colon_char, fd); 437 XPUTC(tab_char, fd); 438 } else { 439 XFPUTS(" ", fd); 440 } 441 return; 442 } 443 444