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 */
escape_target_name(Name np)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
write_state_file(int,Boolean exiting)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(".lock") + 1);
149 (void) sprintf(make_state_lockfile,
150 "%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, "%s/.make.state.%d.XXXXXX", tmpdir, getpid());
166 report_pwd = true;
167 warning(gettext("Writing to %s"), buffer);
168 int fdes = mkstemp(buffer);
169 if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) {
170 fprintf(stderr,
171 gettext("Could not open statefile `%s': %s"),
172 buffer,
173 errmsg(errno));
174 return;
175 }
176 } else {
177 report_pwd = true;
178 fatal(gettext("Can't lock .make.state"));
179 }
180 }
181
182 (void) sprintf(make_state_tempfile,
183 "%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(gettext("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(gettext("Giving up on writing statefile"));
212 }
213 sleep(10);
214 (void) sprintf(buffer, "%s/.make.state.%d.XXXXXX", tmpdir, getpid());
215 int fdes = mkstemp(buffer);
216 if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) {
217 fatal(gettext("Could not open statefile `%s': %s"),
218 buffer,
219 errmsg(errno));
220 }
221 warning(gettext("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(gettext("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(gettext("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
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)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
445