xref: /illumos-gate/usr/src/cmd/make/bin/state.cc (revision ae389aa9)
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