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