450 lines
12 KiB
C++
450 lines
12 KiB
C++
/*
|
|
* CDDL HEADER START
|
|
*
|
|
* The contents of this file are subject to the terms of the
|
|
* Common Development and Distribution License (the "License").
|
|
* You may not use this file except in compliance with the License.
|
|
*
|
|
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
* or http://www.opensolaris.org/os/licensing.
|
|
* See the License for the specific language governing permissions
|
|
* and limitations under the License.
|
|
*
|
|
* When distributing Covered Code, include this CDDL HEADER in each
|
|
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
* If applicable, add the following below this CDDL HEADER, with the
|
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
|
* information: Portions Copyright [yyyy] [name of copyright owner]
|
|
*
|
|
* CDDL HEADER END
|
|
*/
|
|
/*
|
|
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
|
* Use is subject to license terms.
|
|
*/
|
|
|
|
/*
|
|
* state.c
|
|
*
|
|
* This file contains the routines that write the .make.state file
|
|
*/
|
|
|
|
/*
|
|
* Included files
|
|
*/
|
|
#include <mk/defs.h>
|
|
#include <mksh/misc.h> /* errmsg() */
|
|
#include <setjmp.h> /* setjmp() */
|
|
#include <unistd.h> /* getpid() */
|
|
#include <errno.h> /* errno */
|
|
#include <locale.h> /* MB_CUR_MAX */
|
|
|
|
#ifdef __sun
|
|
#else
|
|
#include <libintl.h> // gettext()
|
|
#endif
|
|
|
|
/*
|
|
* Defined macros
|
|
*/
|
|
#define LONGJUMP_VALUE 17
|
|
#define XFWRITE(string, length, fd) {if (fwrite(string, 1, length, fd) == 0) \
|
|
longjmp(long_jump, LONGJUMP_VALUE);}
|
|
#define XPUTC(ch, fd) { \
|
|
if (putc((int) ch, fd) == EOF) \
|
|
longjmp(long_jump, LONGJUMP_VALUE); \
|
|
}
|
|
#define XFPUTS(string, fd) fputs(string, fd)
|
|
|
|
/*
|
|
* typedefs & structs
|
|
*/
|
|
|
|
/*
|
|
* Static variables
|
|
*/
|
|
|
|
/*
|
|
* File table of contents
|
|
*/
|
|
static char * escape_target_name(Name np)
|
|
{
|
|
if(np->dollar) {
|
|
int len = strlen(np->string_mb);
|
|
char * buff = (char*)malloc(2 * len);
|
|
int pos = 0;
|
|
wchar_t wc;
|
|
int pp = 0;
|
|
while(pos < len) {
|
|
int n = mbtowc(&wc, np->string_mb + pos, MB_CUR_MAX);
|
|
if(n < 0) { // error - this shouldn't happen
|
|
(void)free(buff);
|
|
return strdup(np->string_mb);
|
|
}
|
|
if(wc == dollar_char) {
|
|
buff[pp] = '\\'; pp++;
|
|
buff[pp] = '$'; pp++;
|
|
} else {
|
|
for(int j=0;j<n;j++) {
|
|
buff[pp] = np->string_mb[pos+j]; pp++;
|
|
}
|
|
}
|
|
pos += n;
|
|
}
|
|
buff[pp] = '\0';
|
|
return buff;
|
|
} else {
|
|
return strdup(np->string_mb);
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
/*
|
|
* write_state_file(report_recursive, exiting)
|
|
*
|
|
* Write a new version of .make.state
|
|
*
|
|
* Parameters:
|
|
* report_recursive Should only be done at end of run
|
|
* exiting true if called from the exit handler
|
|
*
|
|
* Global variables used:
|
|
* built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written
|
|
* command_changed If no command changed we do not need to write
|
|
* current_make_version The Name "<current version>", written
|
|
* do_not_exec_rule If -n is on we do not write statefile
|
|
* hashtab The hashtable that contains all names
|
|
* keep_state If .KEEP_STATE is no on we do not write file
|
|
* make_state The Name ".make.state", used for opening file
|
|
* make_version The Name ".MAKE_VERSION", written
|
|
* recursive_name The Name ".RECURSIVE", written
|
|
* rewrite_statefile Indicates that something changed
|
|
*/
|
|
|
|
void
|
|
write_state_file(int, Boolean exiting)
|
|
{
|
|
register FILE *fd;
|
|
int lock_err;
|
|
char buffer[MAXPATHLEN];
|
|
char make_state_tempfile[MAXPATHLEN];
|
|
jmp_buf long_jump;
|
|
register int attempts = 0;
|
|
Name_set::iterator np, e;
|
|
register Property lines;
|
|
register int m;
|
|
Dependency dependency;
|
|
register Boolean name_printed;
|
|
Boolean built_this_run = false;
|
|
char *target_name;
|
|
int line_length;
|
|
register Cmd_line cp;
|
|
|
|
|
|
if (!rewrite_statefile ||
|
|
!command_changed ||
|
|
!keep_state ||
|
|
do_not_exec_rule ||
|
|
(report_dependencies_level > 0)) {
|
|
return;
|
|
}
|
|
/* Lock the file for writing. */
|
|
make_state_lockfile = getmem(strlen(make_state->string_mb) + strlen(".lock") + 1);
|
|
(void) sprintf(make_state_lockfile,
|
|
"%s.lock",
|
|
make_state->string_mb);
|
|
if (lock_err = file_lock(make_state->string_mb,
|
|
make_state_lockfile,
|
|
(int *) &make_state_locked, 0)) {
|
|
retmem_mb(make_state_lockfile);
|
|
make_state_lockfile = NULL;
|
|
|
|
/*
|
|
* We need to make sure that we are not being
|
|
* called by the exit handler so we don't call
|
|
* it again.
|
|
*/
|
|
|
|
if (exiting) {
|
|
(void) sprintf(buffer, "%s/.make.state.%d.XXXXXX", tmpdir, getpid());
|
|
report_pwd = true;
|
|
warning(gettext("Writing to %s"), buffer);
|
|
int fdes = mkstemp(buffer);
|
|
if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) {
|
|
fprintf(stderr,
|
|
gettext("Could not open statefile `%s': %s"),
|
|
buffer,
|
|
errmsg(errno));
|
|
return;
|
|
}
|
|
} else {
|
|
report_pwd = true;
|
|
fatal(gettext("Can't lock .make.state"));
|
|
}
|
|
}
|
|
|
|
(void) sprintf(make_state_tempfile,
|
|
"%s.tmp",
|
|
make_state->string_mb);
|
|
/* Delete old temporary statefile (in case it exists) */
|
|
(void) unlink(make_state_tempfile);
|
|
if ((fd = fopen(make_state_tempfile, "w")) == NULL) {
|
|
lock_err = errno; /* Save it! unlink() can change errno */
|
|
(void) unlink(make_state_lockfile);
|
|
retmem_mb(make_state_lockfile);
|
|
make_state_lockfile = NULL;
|
|
make_state_locked = false;
|
|
fatal(gettext("Could not open temporary statefile `%s': %s"),
|
|
make_state_tempfile,
|
|
errmsg(lock_err));
|
|
}
|
|
/*
|
|
* Set a trap for failed writes. If a write fails, the routine
|
|
* will try saving the .make.state file under another name in /tmp.
|
|
*/
|
|
if (setjmp(long_jump)) {
|
|
(void) fclose(fd);
|
|
if (attempts++ > 5) {
|
|
if ((make_state_lockfile != NULL) &&
|
|
make_state_locked) {
|
|
(void) unlink(make_state_lockfile);
|
|
retmem_mb(make_state_lockfile);
|
|
make_state_lockfile = NULL;
|
|
make_state_locked = false;
|
|
}
|
|
fatal(gettext("Giving up on writing statefile"));
|
|
}
|
|
sleep(10);
|
|
(void) sprintf(buffer, "%s/.make.state.%d.XXXXXX", tmpdir, getpid());
|
|
int fdes = mkstemp(buffer);
|
|
if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) {
|
|
fatal(gettext("Could not open statefile `%s': %s"),
|
|
buffer,
|
|
errmsg(errno));
|
|
}
|
|
warning(gettext("Initial write of statefile failed. Trying again on %s"),
|
|
buffer);
|
|
}
|
|
|
|
/* Write the version stamp. */
|
|
XFWRITE(make_version->string_mb,
|
|
strlen(make_version->string_mb),
|
|
fd);
|
|
XPUTC(colon_char, fd);
|
|
XPUTC(tab_char, fd);
|
|
XFWRITE(current_make_version->string_mb,
|
|
strlen(current_make_version->string_mb),
|
|
fd);
|
|
XPUTC(newline_char, fd);
|
|
|
|
/*
|
|
* Go through all the targets, dump their dependencies and
|
|
* command used.
|
|
*/
|
|
for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) {
|
|
/*
|
|
* If the target has no command used nor dependencies,
|
|
* we can go to the next one.
|
|
*/
|
|
if ((lines = get_prop(np->prop, line_prop)) == NULL) {
|
|
continue;
|
|
}
|
|
/* If this target is a special target, don't print. */
|
|
if (np->special_reader != no_special) {
|
|
continue;
|
|
}
|
|
/*
|
|
* Find out if any of the targets dependencies should
|
|
* be written to .make.state.
|
|
*/
|
|
for (m = 0, dependency = lines->body.line.dependencies;
|
|
dependency != NULL;
|
|
dependency = dependency->next) {
|
|
if (m = !dependency->stale
|
|
&& (dependency->name != force)
|
|
#ifndef PRINT_EXPLICIT_DEPEN
|
|
&& dependency->automatic
|
|
#endif
|
|
) {
|
|
break;
|
|
}
|
|
}
|
|
/* Only print if dependencies listed. */
|
|
if (m || (lines->body.line.command_used != NULL)) {
|
|
name_printed = false;
|
|
/*
|
|
* If this target was built during this make run,
|
|
* we mark it.
|
|
*/
|
|
built_this_run = false;
|
|
if (np->has_built) {
|
|
built_this_run = true;
|
|
XFWRITE(built_last_make_run->string_mb,
|
|
strlen(built_last_make_run->string_mb),
|
|
fd);
|
|
XPUTC(colon_char, fd);
|
|
XPUTC(newline_char, fd);
|
|
}
|
|
/* If the target has dependencies, we dump them. */
|
|
target_name = escape_target_name(np);
|
|
if (np->has_long_member_name) {
|
|
target_name =
|
|
get_prop(np->prop, long_member_name_prop)
|
|
->body.long_member_name.member_name->
|
|
string_mb;
|
|
}
|
|
if (m) {
|
|
XFPUTS(target_name, fd);
|
|
XPUTC(colon_char, fd);
|
|
XFPUTS("\t", fd);
|
|
name_printed = true;
|
|
line_length = 0;
|
|
for (dependency =
|
|
lines->body.line.dependencies;
|
|
dependency != NULL;
|
|
dependency = dependency->next) {
|
|
print_auto_depes(dependency,
|
|
fd,
|
|
built_this_run,
|
|
&line_length,
|
|
target_name,
|
|
long_jump);
|
|
}
|
|
XFPUTS("\n", fd);
|
|
}
|
|
/* If there is a command used, we dump it. */
|
|
if (lines->body.line.command_used != NULL) {
|
|
/*
|
|
* Only write the target name if it
|
|
* wasn't done for the dependencies.
|
|
*/
|
|
if (!name_printed) {
|
|
XFPUTS(target_name, fd);
|
|
XPUTC(colon_char, fd);
|
|
XPUTC(newline_char, fd);
|
|
}
|
|
/*
|
|
* Write the command lines.
|
|
* Prefix each textual line with a tab.
|
|
*/
|
|
for (cp = lines->body.line.command_used;
|
|
cp != NULL;
|
|
cp = cp->next) {
|
|
char *csp;
|
|
int n;
|
|
|
|
XPUTC(tab_char, fd);
|
|
if (cp->command_line != NULL) {
|
|
for (csp = cp->
|
|
command_line->
|
|
string_mb,
|
|
n = strlen(cp->
|
|
command_line->
|
|
string_mb);
|
|
n > 0;
|
|
n--, csp++) {
|
|
XPUTC(*csp, fd);
|
|
if (*csp ==
|
|
(int) newline_char) {
|
|
XPUTC(tab_char,
|
|
fd);
|
|
}
|
|
}
|
|
}
|
|
XPUTC(newline_char, fd);
|
|
}
|
|
}
|
|
(void)free(target_name);
|
|
}
|
|
}
|
|
if (fclose(fd) == EOF) {
|
|
longjmp(long_jump, LONGJUMP_VALUE);
|
|
}
|
|
if (attempts == 0) {
|
|
if (unlink(make_state->string_mb) != 0 && errno != ENOENT) {
|
|
lock_err = errno; /* Save it! unlink() can change errno */
|
|
/* Delete temporary statefile */
|
|
(void) unlink(make_state_tempfile);
|
|
(void) unlink(make_state_lockfile);
|
|
retmem_mb(make_state_lockfile);
|
|
make_state_lockfile = NULL;
|
|
make_state_locked = false;
|
|
fatal(gettext("Could not delete old statefile `%s': %s"),
|
|
make_state->string_mb,
|
|
errmsg(lock_err));
|
|
}
|
|
if (rename(make_state_tempfile, make_state->string_mb) != 0) {
|
|
lock_err = errno; /* Save it! unlink() can change errno */
|
|
/* Delete temporary statefile */
|
|
(void) unlink(make_state_tempfile);
|
|
(void) unlink(make_state_lockfile);
|
|
retmem_mb(make_state_lockfile);
|
|
make_state_lockfile = NULL;
|
|
make_state_locked = false;
|
|
fatal(gettext("Could not rename `%s' to `%s': %s"),
|
|
make_state_tempfile,
|
|
make_state->string_mb,
|
|
errmsg(lock_err));
|
|
}
|
|
}
|
|
if ((make_state_lockfile != NULL) && make_state_locked) {
|
|
(void) unlink(make_state_lockfile);
|
|
retmem_mb(make_state_lockfile);
|
|
make_state_lockfile = NULL;
|
|
make_state_locked = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* print_auto_depes(dependency, fd, built_this_run,
|
|
* line_length, target_name, long_jump)
|
|
*
|
|
* Will print a dependency list for automatic entries.
|
|
*
|
|
* Parameters:
|
|
* dependency The dependency to print
|
|
* fd The file to print it to
|
|
* built_this_run If on we prefix each line with .BUILT_THIS...
|
|
* line_length Pointer to line length var that we update
|
|
* target_name We need this when we restart line
|
|
* long_jump setjmp/longjmp buffer used for IO error action
|
|
*
|
|
* Global variables used:
|
|
* built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written
|
|
* force The Name " FORCE", compared against
|
|
*/
|
|
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)
|
|
{
|
|
if (!dependency->automatic ||
|
|
dependency->stale ||
|
|
(dependency->name == force)) {
|
|
return;
|
|
}
|
|
XFWRITE(dependency->name->string_mb,
|
|
strlen(dependency->name->string_mb),
|
|
fd);
|
|
/*
|
|
* Check if the dependency line is too long.
|
|
* If so, break it and start a new one.
|
|
*/
|
|
if ((*line_length += (int) strlen(dependency->name->string_mb) + 1) > 450) {
|
|
*line_length = 0;
|
|
XPUTC(newline_char, fd);
|
|
if (built_this_run) {
|
|
XFPUTS(built_last_make_run->string_mb, fd);
|
|
XPUTC(colon_char, fd);
|
|
XPUTC(newline_char, fd);
|
|
}
|
|
XFPUTS(target_name, fd);
|
|
XPUTC(colon_char, fd);
|
|
XPUTC(tab_char, fd);
|
|
} else {
|
|
XFPUTS(" ", fd);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|