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