diff options
Diffstat (limited to 'usr/src/make_src/Make/bin/make/common')
24 files changed, 23231 insertions, 0 deletions
diff --git a/usr/src/make_src/Make/bin/make/common/ar.cc b/usr/src/make_src/Make/bin/make/common/ar.cc new file mode 100644 index 0000000..dfd0362 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/ar.cc @@ -0,0 +1,908 @@ +/* + * 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. + */ +/* + * @(#)ar.cc 1.28 06/12/12 + */ + +#pragma ident "@(#)ar.cc 1.28 06/12/12" + +/* + * ar.c + * + * Deal with the lib.a(member.o) and lib.a((entry-point)) notations + * + * Look inside archives for notations a(b) and a((b)) + * a(b) is file member b in archive a + * a((b)) is entry point b in object archive a + * + * For 6.0, create a make which can understand all archive + * formats. This is kind of tricky, and <ar.h> isnt any help. + */ + +/* + * Included files + */ +#include <avo/avo_alloca.h> /* alloca() */ +#include <ar.h> +#include <errno.h> /* errno */ +#include <fcntl.h> /* open() */ +#include <mk/defs.h> +#include <mksh/misc.h> /* retmem_mb() */ + +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) +struct ranlib { + union { + off_t ran_strx; /* string table index of */ + char *ran_name; /* symbol defined by */ + } ran_un; + off_t ran_off; /* library member at this offset */ +}; +#else +#include <ranlib.h> +#endif + +#if defined(linux) +#include <ctype.h> /* isspace */ +#else +#include <unistd.h> /* close() */ +#endif + + +/* + * Defined macros + */ +#ifndef S5EMUL +#undef BITSPERBYTE +#define BITSPERBYTE 8 +#endif + +/* + * Defines for all the different archive formats. See next comment + * block for justification for not using <ar.h>s versions. + */ +#define AR_5_MAGIC "<ar>" /* 5.0 format magic string */ +#define AR_5_MAGIC_LENGTH 4 /* 5.0 format string length */ + +#define AR_PORT_MAGIC "!<arch>\n" /* Port. (6.0) magic string */ +#define AR_PORT_MAGIC_LENGTH 8 /* Port. (6.0) string length */ +#define AR_PORT_END_MAGIC "`\n" /* Port. (6.0) end of header */ +#define AR_PORT_WORD 4 /* Port. (6.0) 'word' length */ + +/* + * typedefs & structs + */ +/* + * These are the archive file headers for the formats. Note + * that it really doesnt matter if these structures are defined + * here. They are correct as of the respective archive format + * releases. If the archive format is changed, then since backwards + * compatability is the desired behavior, a new structure is added + * to the list. + */ +typedef struct { /* 5.0 ar header format: vax family; 3b family */ + char ar_magic[AR_5_MAGIC_LENGTH]; /* AR_5_MAGIC*/ + char ar_name[16]; /* Space terminated */ + char ar_date[AR_PORT_WORD]; /* sgetl() accessed */ + char ar_syms[AR_PORT_WORD]; /* sgetl() accessed */ +} Arh_5; + +typedef struct { /* 5.0 ar symbol format: vax family; 3b family */ + char sym_name[8]; /* Space terminated */ + char sym_ptr[AR_PORT_WORD]; /* sgetl() accessed */ +} Ars_5; + +typedef struct { /* 5.0 ar member format: vax family; 3b family */ + char arf_name[16]; /* Space terminated */ + char arf_date[AR_PORT_WORD]; /* sgetl() accessed */ + char arf_uid[AR_PORT_WORD]; /* sgetl() accessed */ + char arf_gid[AR_PORT_WORD]; /* sgetl() accessed */ + char arf_mode[AR_PORT_WORD]; /* sgetl() accessed */ + char arf_size[AR_PORT_WORD]; /* sgetl() accessed */ +} Arf_5; + +typedef struct { /* Portable (6.0) ar format: vax family; 3b family */ + char ar_name[16]; /* Space terminated */ + /* left-adjusted fields; decimal ascii; blank filled */ + char ar_date[12]; + char ar_uid[6]; + char ar_gid[6]; + char ar_mode[8]; /* octal ascii */ + char ar_size[10]; + /* special end-of-header string (AR_PORT_END_MAGIC) */ + char ar_fmag[2]; +} Ar_port; + +enum ar_type { + AR_5, + AR_PORT +}; + +typedef unsigned int ar_port_word; // must be 4-bytes long + +typedef struct { + FILE *fd; + /* to distiguish ar format */ + enum ar_type type; + /* where first ar member header is at */ + long first_ar_mem; + /* where the symbol lookup starts */ + long sym_begin; + /* the number of symbols available */ + long num_symbols; + /* length of symbol directory file */ + long sym_size; + Arh_5 arh_5; + Ars_5 ars_5; + Arf_5 arf_5; + Ar_port ar_port; +} Ar; + +/* + * Static variables + */ + +/* + * File table of contents + */ +extern timestruc_t& read_archive(register Name target); +static Boolean open_archive(char *filename, register Ar *arp); +static void close_archive(register Ar *arp); +static Boolean read_archive_dir(register Ar *arp, Name library, char **long_names_table); +static void translate_entry(register Ar *arp, Name target, register Property member, char **long_names_table); +static long sgetl(char *); + +/* + * read_archive(target) + * + * Read the contents of an ar file. + * + * Return value: + * The time the member was created + * + * Parameters: + * target The member to find time for + * + * Global variables used: + * empty_name The Name "" + */ + +int read_member_header (Ar_port *header, FILE *fd, char* filename); +int process_long_names_member (register Ar *arp, char **long_names_table, char *filename); + +timestruc_t& +read_archive(register Name target) +{ + register Property member; + wchar_t *slash; + String_rec true_member_name; + wchar_t buffer[STRING_BUFFER_LENGTH]; + register Name true_member = NULL; + Ar ar; + char *long_names_table = NULL; /* Table of long + member names */ + + member = get_prop(target->prop, member_prop); + /* + * Check if the member has directory component. + * If so, remove the dir and see if we know the date. + */ + if (member->body.member.member != NULL) { + Wstring member_string(member->body.member.member); + wchar_t * wcb = member_string.get_string(); + if((slash = (wchar_t *) wsrchr(wcb, (int) slash_char)) != NULL) { + INIT_STRING_FROM_STACK(true_member_name, buffer); + append_string(member->body.member.library->string_mb, + &true_member_name, + FIND_LENGTH); + append_char((int) parenleft_char, &true_member_name); + append_string(slash + 1, &true_member_name, FIND_LENGTH); + append_char((int) parenright_char, &true_member_name); + true_member = GETNAME(true_member_name.buffer.start, + FIND_LENGTH); + if (true_member->stat.time != file_no_time) { + target->stat.time = true_member->stat.time; + return target->stat.time; + } + } + } + if (open_archive(member->body.member.library->string_mb, &ar) == failed) { + if (errno == ENOENT) { + target->stat.stat_errno = ENOENT; + close_archive(&ar); + if (member->body.member.member == NULL) { + member->body.member.member = empty_name; + } + return target->stat.time = file_doesnt_exist; + } else { + fatal(catgets(catd, 1, 1, "Can't access archive `%s': %s"), + member->body.member.library->string_mb, + errmsg(errno)); + } + } + if (target->stat.time == file_no_time) { + if (read_archive_dir(&ar, member->body.member.library, + &long_names_table) + == failed){ + fatal(catgets(catd, 1, 2, "Can't access archive `%s': %s"), + member->body.member.library->string_mb, + errmsg(errno)); + } + } + if (member->body.member.entry != NULL) { + translate_entry(&ar, target, member,&long_names_table); + } + close_archive(&ar); + if (long_names_table) { + retmem_mb(long_names_table); + } + if (true_member != NULL) { + target->stat.time = true_member->stat.time; + } + if (target->stat.time == file_no_time) { + target->stat.time = file_doesnt_exist; + } + return target->stat.time; +} + +/* + * open_archive(filename, arp) + * + * Return value: + * Indicates if open failed or not + * + * Parameters: + * filename The name of the archive we need to read + * arp Pointer to ar file description block + * + * Global variables used: + */ +static Boolean +open_archive(char *filename, register Ar *arp) +{ + int fd; + char mag_5[AR_5_MAGIC_LENGTH]; + char mag_port[AR_PORT_MAGIC_LENGTH]; + char buffer[4]; + + arp->fd = NULL; + fd = open_vroot(filename, O_RDONLY, 0, NULL, VROOT_DEFAULT); + if ((fd < 0) || ((arp->fd = fdopen(fd, "r")) == NULL)) { + return failed; + } + (void) fcntl(fileno(arp->fd), F_SETFD, 1); + +#if !defined(SUN5_0) && !defined(linux) //XXX + /* Read enough of the archive to distinguish between the formats */ + if (fread(mag_5, AR_5_MAGIC_LENGTH, 1, arp->fd) != 1) { + return failed; + } + if (IS_EQUALN(mag_5, AR_5_MAGIC, AR_5_MAGIC_LENGTH)) { + arp->type = AR_5; + /* Must read in header to set necessary info */ + if (fseek(arp->fd, 0L, 0) != 0 || + fread((char *) &arp->arh_5, sizeof (Arh_5), 1, arp->fd) != + 1) { + return failed; + } + arp->sym_begin = ftell(arp->fd); + arp->num_symbols = sgetl(arp->arh_5.ar_syms); + arp->first_ar_mem = arp->sym_begin + + sizeof (Ars_5) * arp->num_symbols; + arp->sym_size = 0L; + return succeeded; + } + if (fseek(arp->fd, 0L, 0) != 0) { + return failed; + } +#endif + if (fread(mag_port, AR_PORT_MAGIC_LENGTH, 1, arp->fd) != 1) { + return failed; + } + if (IS_EQUALN(mag_port, AR_PORT_MAGIC, AR_PORT_MAGIC_LENGTH)) { + arp->type = AR_PORT; + /* + * Read in first member header to find out if there is + * a symbol definition table. + */ + + int ret = read_member_header(&arp->ar_port, arp->fd, filename); + if (ret == failed) { + return failed; + } else if(ret == -1) { + /* There is no member header - empty archive */ + arp->sym_size = arp->num_symbols = arp->sym_begin = 0L; + arp->first_ar_mem = ftell(arp->fd); + return succeeded; + } + /* + * The following values are the default if there is + * no symbol directory and long member names. + */ + arp->sym_size = arp->num_symbols = arp->sym_begin = 0L; + arp->first_ar_mem = ftell(arp->fd) - (long) sizeof (Ar_port); + + /* + * Do we have a symbol table? A symbol table is always + * the first member in an archive. In 4.1.x it has the + * name __.SYMDEF, in SVr4, it has the name "/ " + */ +/* +#ifdef SUN5_0 + MBSTOWCS(wcs_buffer, NOCATGETS("/ ")); + if (IS_WEQUALN(arp->ar_port.ar_name, wcs_buffer, 16)) { +#else + MBSTOWCS(wcs_buffer, NOCATGETS("__.SYMDEF ")); + if (IS_WEQUALN(arp->ar_port.ar_name, wcs_buffer, 16)) { +#endif + */ +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + if (IS_EQUALN(arp->ar_port.ar_name, + NOCATGETS("/ "), + 16)) { +#else + if (IS_EQUALN(arp->ar_port.ar_name, + NOCATGETS("__.SYMDEF "), + 16)) { +#endif + if (sscanf(arp->ar_port.ar_size, + "%ld", + &arp->sym_size) != 1) { + return failed; + } + arp->sym_size += (arp->sym_size & 1); /* round up */ + if (fread(buffer, sizeof buffer, 1, arp->fd) != 1) { + return failed; + } + arp->num_symbols = sgetl(buffer); + arp->sym_begin = ftell(arp->fd); + arp->first_ar_mem = arp->sym_begin + + arp->sym_size - sizeof buffer; + } + return succeeded; + } + fatal(catgets(catd, 1, 3, "`%s' is not an archive"), filename); + /* NOTREACHED */ + return failed; +} + + +/* + * close_archive(arp) + * + * Parameters: + * arp Pointer to ar file description block + * + * Global variables used: + */ +static void +close_archive(register Ar *arp) +{ + if (arp->fd != NULL) { + (void) fclose(arp->fd); + } +} + +/* + * read_archive_dir(arp, library, long_names_table) + * + * Reads the directory of an archive and enters all + * the members into the make symboltable in lib(member) format + * with their dates. + * + * Parameters: + * arp Pointer to ar file description block + * library Name of lib to enter members for. + * Used to form "lib(member)" string. + * long_names_table table that contains list of members + * with names > 15 characters long + * + * Global variables used: + */ +static Boolean +#if defined(SUN5_0) || defined(linux) //XXX +read_archive_dir(register Ar *arp, Name library, char **long_names_table) +#else +read_archive_dir(register Ar *arp, Name library, char **) +#endif +{ + wchar_t *name_string; + wchar_t *member_string; + register long len; + register wchar_t *p; + register char *q; + register Name name; + Property member; + long ptr; + long date; + +#if defined(SUN5_0) || defined(linux) //XXX + int offset; + + /* + * If any of the members has a name > 15 chars, + * it will be found here. + */ + if (process_long_names_member(arp, long_names_table, library->string_mb) == failed) { + return failed; + } +#endif + name_string = ALLOC_WC((int) (library->hash.length + + (int) ar_member_name_len * 2)); + (void) mbstowcs(name_string, library->string_mb, (int) library->hash.length); + member_string = name_string + library->hash.length; + *member_string++ = (int) parenleft_char; + + if (fseek(arp->fd, arp->first_ar_mem, 0) != 0) { + goto read_error; + } + /* Read the directory using the appropriate format */ + switch (arp->type) { + case AR_5: + for (;;) { + if (fread((char *) &arp->arf_5, sizeof arp->arf_5, 1, arp->fd) + != 1) { + if (feof(arp->fd)) { + return succeeded; + } + break; + } + len = sizeof arp->arf_5.arf_name; + for (p = member_string, q = arp->arf_5.arf_name; + (len > 0) && (*q != (int) nul_char) && !isspace(*q); + ) { + MBTOWC(p, q); + p++; + q++; + } + *p++ = (int) parenright_char; + *p = (int) nul_char; + name = GETNAME(name_string, FIND_LENGTH); + /* + * [tolik] Fix for dmake bug 1234018. + * If name->stat.time is already set, then it should not + * be changed. (D)make propogates time stamp for one + * member, and when it calls exists() for another member, + * the first one may be changed. + */ + if(name->stat.time == file_no_time) { + name->stat.time.tv_sec = sgetl(arp->arf_5.arf_date); + name->stat.time.tv_nsec = LONG_MAX; + } + name->is_member = library->is_member; + member = maybe_append_prop(name, member_prop); + member->body.member.library = library; + *--p = (int) nul_char; + if (member->body.member.member == NULL) { + member->body.member.member = + GETNAME(member_string, FIND_LENGTH); + } + ptr = sgetl(arp->arf_5.arf_size); + ptr += (ptr & 1); + if (fseek(arp->fd, ptr, 1) != 0) { + goto read_error; + } + } + break; + case AR_PORT: + for (;;) { + if ((fread((char *) &arp->ar_port, + sizeof arp->ar_port, + 1, + arp->fd) != 1) || + !IS_EQUALN(arp->ar_port.ar_fmag, + AR_PORT_END_MAGIC, + sizeof arp->ar_port.ar_fmag)) { + if (feof(arp->fd)) { + return succeeded; + } + fatal( + catgets(catd, 1, 28, "Read error in archive `%s': invalid archive file member header at 0x%x"), + library->string_mb, + ftell(arp->fd) + ); + } +#if defined(SUN5_0) || defined(linux) //XXX + /* If it's a long name, retrieve it from long name table */ + if (arp->ar_port.ar_name[0] == '/') { + /* + * "len" is used for hashing the string. + * We're using "ar_member_name_len" instead of + * the actual name length since it's the longest + * string the "ar" command can handle at this + * point. + */ + len = ar_member_name_len; + sscanf(arp->ar_port.ar_name + 1, + "%ld", + &offset); + q = *long_names_table + offset; + } else { + q = arp->ar_port.ar_name; + len = sizeof arp->ar_port.ar_name; + } +#else + q = arp->ar_port.ar_name; + len = sizeof arp->ar_port.ar_name; +#endif + + for (p = member_string; + (len > 0) && + (*q != (int) nul_char) && + !isspace(*q) && + (*q != (int) slash_char); + ) { + MBTOWC(p, q); + p++; + q++; + } + *p++ = (int) parenright_char; + *p = (int) nul_char; + name = GETNAME(name_string, FIND_LENGTH); + name->is_member = library->is_member; + member = maybe_append_prop(name, member_prop); + member->body.member.library = library; + *--p = (int) nul_char; + if (member->body.member.member == NULL) { + member->body.member.member = + GETNAME(member_string, FIND_LENGTH); + } + if (sscanf(arp->ar_port.ar_date, "%ld", &date) != 1) { + WCSTOMBS(mbs_buffer, name_string); + fatal(catgets(catd, 1, 4, "Bad date field for member `%s' in archive `%s'"), + mbs_buffer, + library->string_mb); + } + /* + * [tolik] Fix for dmake bug 1234018. + */ + if(name->stat.time == file_no_time) { + name->stat.time.tv_sec = date; + name->stat.time.tv_nsec = LONG_MAX; + } + if (sscanf(arp->ar_port.ar_size, "%ld", &ptr) != 1) { + WCSTOMBS(mbs_buffer, name_string); + fatal(catgets(catd, 1, 5, "Bad size field for member `%s' in archive `%s'"), + mbs_buffer, + library->string_mb); + } + ptr += (ptr & 1); + if (fseek(arp->fd, ptr, 1) != 0) { + goto read_error; + } + } + break; + } + + /* Only here if fread() [or IS_EQUALN()] failed and not at EOF */ +read_error: + fatal(catgets(catd, 1, 6, "Read error in archive `%s': %s"), + library->string_mb, + errmsg(errno)); + /* NOTREACHED */ +} + + +/* + * process_long_names_member(arp) + * + * If the archive contains members with names longer + * than 15 characters, then it has a special member + * with the name "// " that contains a table + * of null-terminated long names. This member + * is always the first member, after the symbol table + * if it exists. + * + * Parameters: + * arp Pointer to ar file description block + * + * Global variables used: + */ +int +process_long_names_member(register Ar *arp, char **long_names_table, char *filename) +{ + Ar_port *ar_member_header; + int table_size; + + if (fseek(arp->fd, arp->first_ar_mem, 0) != 0) { + return failed; + } + if ((ar_member_header = + (Ar_port *) alloca((int) sizeof(Ar_port))) == NULL){ + perror(catgets(catd, 1, 7, "memory allocation failure")); + return failed; + } + int ret = read_member_header(ar_member_header, arp->fd, filename); + if (ret == failed) { + return failed; + } else if(ret == -1) { + /* There is no member header - empty archive */ + return succeeded; + } + /* Do we have special member containing long names? */ + if (IS_EQUALN(ar_member_header->ar_name, + NOCATGETS("// "), + 16)){ + if (sscanf(ar_member_header->ar_size, + "%ld", + &table_size) != 1) { + return failed; + } + *long_names_table = (char *) malloc(table_size); + /* Read the list of long member names into the table */ + if (fread(*long_names_table, table_size, 1, arp->fd) != 1) { + return failed; + } + arp->first_ar_mem = ftell(arp->fd); + } + return succeeded; +} + +/* + * translate_entry(arp, target, member) + * + * Finds the member for one lib.a((entry)) + * + * Parameters: + * arp Pointer to ar file description block + * target Target to find member name for + * member Property to fill in with info + * + * Global variables used: + */ +static void +translate_entry(register Ar *arp, Name target, register Property member, char **long_names_table) +{ + register int len; + register int i; + wchar_t *member_string; + ar_port_word *offs; + int strtablen; + char *syms; /* string table */ + char *csym; /* string table */ + ar_port_word *offend; /* end of offsets table */ + int date; + register wchar_t *ap; + register char *hp; + int maxs; + int offset; + char buffer[4]; + + if (arp->sym_begin == 0L || arp->num_symbols == 0L) { + fatal(catgets(catd, 1, 8, "Cannot find symbol `%s' in archive `%s'"), + member->body.member.entry->string_mb, + member->body.member.library->string_mb); + } + + if (fseek(arp->fd, arp->sym_begin, 0) != 0) { + goto read_error; + } + member_string = ALLOC_WC((int) ((int) ar_member_name_len * 2)); + + switch (arp->type) { + case AR_5: + if ((len = member->body.member.entry->hash.length) > 8) { + len = 8; + } + for (i = 0; i < arp->num_symbols; i++) { + if (fread((char *) &arp->ars_5, + sizeof arp->ars_5, + 1, + arp->fd) != 1) { + goto read_error; + } + if (IS_EQUALN(arp->ars_5.sym_name, + member->body.member.entry->string_mb, + len)) { + if ((fseek(arp->fd, + sgetl(arp->ars_5.sym_ptr), + 0) != 0) || + (fread((char *) &arp->arf_5, + sizeof arp->arf_5, + 1, + arp->fd) != 1)) { + goto read_error; + } + MBSTOWCS(wcs_buffer, arp->arf_5.arf_name); + (void) wsncpy(member_string, + wcs_buffer, + wslen(wcs_buffer)); + member_string[sizeof(arp->arf_5.arf_name)] = + (int) nul_char; + member->body.member.member = + GETNAME(member_string, FIND_LENGTH); + target->stat.time.tv_sec = sgetl(arp->arf_5.arf_date); + target->stat.time.tv_nsec = LONG_MAX; + return; + } + } + break; + case AR_PORT: + offs = (ar_port_word *) alloca((int) (arp->num_symbols * AR_PORT_WORD)); + if (fread((char *) offs, + AR_PORT_WORD, + (int) arp->num_symbols, + arp->fd) != arp->num_symbols) { + goto read_error; + } + + for(i=0;i<arp->num_symbols;i++) { + *((int*)buffer)=offs[i]; + offs[i]=(ar_port_word)sgetl(buffer); + } + + strtablen=arp->sym_size-4-(int) (arp->num_symbols * AR_PORT_WORD); + syms = (char *) alloca(strtablen); + if (fread(syms, + sizeof (char), + strtablen, + arp->fd) != strtablen) { + goto read_error; + } + offend = &offs[arp->num_symbols]; + while (offs < offend) { + maxs = strlen(member->body.member.entry->string_mb); + if(strlen(syms) > maxs) + maxs = strlen(syms); + if (IS_EQUALN(syms, + member->body.member.entry->string_mb, + maxs)) { + if (fseek(arp->fd, + (long) *offs, + 0) != 0) { + goto read_error; + } + if ((fread((char *) &arp->ar_port, + sizeof arp->ar_port, + 1, + arp->fd) != 1) || + !IS_EQUALN(arp->ar_port.ar_fmag, + AR_PORT_END_MAGIC, + sizeof arp->ar_port.ar_fmag)) { + goto read_error; + } + if (sscanf(arp->ar_port.ar_date, + "%ld", + &date) != 1) { + fatal(catgets(catd, 1, 9, "Bad date field for member `%s' in archive `%s'"), + arp->ar_port.ar_name, + target->string_mb); + } +#if defined(SUN5_0) || defined(linux) //XXX + /* If it's a long name, retrieve it from long name table */ + if (arp->ar_port.ar_name[0] == '/') { + sscanf(arp->ar_port.ar_name + 1, + "%ld", + &offset); + len = ar_member_name_len; + hp = *long_names_table + offset; + } else { + len = sizeof arp->ar_port.ar_name; + hp = arp->ar_port.ar_name; + } +#else + hp = arp->ar_port.ar_name; +#endif + ap = member_string; + while (*hp && + (*hp != (int) slash_char) && + (ap < &member_string[len])) { + MBTOWC(ap, hp); + ap++; + hp++; + } + *ap = (int) nul_char; + member->body.member.member = + GETNAME(member_string, FIND_LENGTH); + target->stat.time.tv_sec = date; + target->stat.time.tv_nsec = LONG_MAX; + return; + } + offs++; + while(*syms!='\0') syms++; + syms++; + } + } + fatal(catgets(catd, 1, 10, "Cannot find symbol `%s' in archive `%s'"), + member->body.member.entry->string_mb, + member->body.member.library->string_mb); + /*NOTREACHED*/ + +read_error: + if (ferror(arp->fd)) { + fatal(catgets(catd, 1, 11, "Read error in archive `%s': %s"), + member->body.member.library->string_mb, + errmsg(errno)); + } else { + fatal(catgets(catd, 1, 12, "Read error in archive `%s': Premature EOF"), + member->body.member.library->string_mb); + } +} + +/* + * sgetl(buffer) + * + * The intent here is to provide a means to make the value of + * bytes in an io-buffer correspond to the value of a long + * in the memory while doing the io a long at a time. + * Files written and read in this way are machine-independent. + * + * Return value: + * Long int read from buffer + * Parameters: + * buffer buffer we need to read long int from + * + * Global variables used: + */ +static long +sgetl(register char *buffer) +{ + register long w = 0; + register int i = BITSPERBYTE * AR_PORT_WORD; + + while ((i -= BITSPERBYTE) >= 0) { + w |= (long) ((unsigned char) *buffer++) << i; + } + return w; +} + + +/* + * read_member_header(header, fd, filename) + * + * reads the member header for the 4.1.x and SVr4 archives. + * + * Return value: + * fails if read error or member + * header is not the right format + * Parameters: + * header There's one before each archive member + * fd file descriptor for the archive file. + * + * Global variables used: + */ +int +read_member_header(Ar_port *header, FILE *fd, char* filename) +{ + int num = fread((char *) header, sizeof (Ar_port), 1, fd); + if (num != 1 && feof(fd)) { + /* There is no member header - empty archive */ + return -1; + } + if ((num != 1) || + !IS_EQUALN( + AR_PORT_END_MAGIC, + header->ar_fmag, + sizeof (header->ar_fmag) + ) + ) { + fatal( + catgets(catd, 1, 28, "Read error in archive `%s': invalid archive file member header at 0x%x"), + filename, + ftell(fd) + ); + } + return succeeded; +} + diff --git a/usr/src/make_src/Make/bin/make/common/default.mk.file b/usr/src/make_src/Make/bin/make/common/default.mk.file new file mode 100644 index 0000000..6128d35 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/default.mk.file @@ -0,0 +1,231 @@ +# +# 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 1993 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# @(#)default.mk.file 1.4 06/12/12 +# + +SUFFIXES = .o .c .c~ .cc .cc~ .s .s~ .S .S~ .ln .f .f~ .F .F~ .l .l~ \ + .mod .mod~ .sym .def .def~ .p .p~ .r .r~ .y .y~ .h .h~ .sh .sh~ \ + .cps .cps~ +.SUFFIXES: $(SUFFIXES) + +# OUTPUT_OPTION should be defined to "-o $@" when +# the default rules are used for non-local files. +OUTPUT_OPTION= + +# C language section. +CC=cc +CFLAGS= +CPPFLAGS= +LINT=lint +LINTFLAGS= +COMPILE.c=$(CC) $(CFLAGS) $(CPPFLAGS) -target $(TARGET_ARCH:-%=%) -c +LINK.c=$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -target $(TARGET_ARCH:-%=%) +LINT.c=$(LINT) $(LINTFLAGS) $(CPPFLAGS) $(TARGET_ARCH) +.c: + $(LINK.c) -o $@ $< $(LDLIBS) +.c.ln: + $(LINT.c) $(OUTPUT_OPTION) -i $< +.c.o: + $(COMPILE.c) $(OUTPUT_OPTION) $< +.c.a: + $(COMPILE.c) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# C language section. yacc. +YACC=yacc +YFLAGS= +YACC.y=$(YACC) $(YFLAGS) +.y: + $(YACC.y) $< + $(LINK.c) -o $@ y.tab.c $(LDLIBS) + $(RM) y.tab.c +.y.c: + $(YACC.y) $< + mv y.tab.c $@ +.y.ln: + $(YACC.y) $< + $(LINT.c) -o $@ -i y.tab.c + $(RM) y.tab.c +.y.o: + $(YACC.y) $< + $(COMPILE.c) -o $@ y.tab.c + $(RM) y.tab.c + +# C language section. lex. +LEX=lex +LFLAGS= +LEX.l=$(LEX) $(LFLAGS) -t +.l: + $(RM) $*.c + $(LEX.l) $< > $*.c + $(LINK.c) -o $@ $*.c -ll $(LDLIBS) + $(RM) $*.c +.l.c : + $(RM) $@ + $(LEX.l) $< > $@ +.l.ln: + $(RM) $*.c + $(LEX.l) $< > $*.c + $(LINT.c) -o $@ -i $*.c + $(RM) $*.c +.l.o: + $(RM) $*.c + $(LEX.l) $< > $*.c + $(COMPILE.c) -o $@ $*.c + $(RM) $*.c + +# C++ language section. +CCC=CC +CCFLAGS= +COMPILE.cc=$(CCC) $(CCFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c +LINK.cc=$(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) +.cc: + $(LINK.cc) -o $@ $< $(LDLIBS) +.cc.o: + $(COMPILE.cc) $(OUTPUT_OPTION) $< +.cc.a: + $(COMPILE.cc) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# FORTRAN section. +FC=f77 +FFLAGS= +COMPILE.f=$(FC) $(FFLAGS) $(TARGET_ARCH) -c +LINK.f=$(FC) $(FFLAGS) $(LDFLAGS) $(TARGET_ARCH) +COMPILE.F=$(FC) $(FFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c +LINK.F=$(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) +.f: + $(LINK.f) -o $@ $< $(LDLIBS) +.f.o: + $(COMPILE.f) $(OUTPUT_OPTION) $< +.f.a: + $(COMPILE.f) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.F: + $(LINK.F) -o $@ $< $(LDLIBS) +.F.o: + $(COMPILE.F) $(OUTPUT_OPTION) $< +.F.a: + $(COMPILE.F) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# FORTRAN section. ratfor. +RFLAGS= +COMPILE.r=$(FC) $(FFLAGS) $(RFLAGS) $(TARGET_ARCH) -c +LINK.r=$(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) $(TARGET_ARCH) +.r: + $(LINK.r) -o $@ $< $(LDLIBS) +.r.o: + $(COMPILE.r) $(OUTPUT_OPTION) $< +.r.a: + $(COMPILE.r) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Modula-2 section. +M2C=m2c +M2FLAGS= +MODFLAGS= +DEFFLAGS= +COMPILE.def=$(M2C) $(M2FLAGS) $(DEFFLAGS) $(TARGET_ARCH) +COMPILE.mod=$(M2C) $(M2FLAGS) $(MODFLAGS) $(TARGET_ARCH) +.def.sym: + $(COMPILE.def) -o $@ $< +.mod: + $(COMPILE.mod) -o $@ -e $@ $< +.mod.o: + $(COMPILE.mod) -o $@ $< +.mod.a: + $(COMPILE.mod) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Pascal section. +PC=pc +PFLAGS= +COMPILE.p=$(PC) $(PFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c +LINK.p=$(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) +.p: + $(LINK.p) -o $@ $< $(LDLIBS) +.p.o: + $(COMPILE.p) $(OUTPUT_OPTION) $< +.p.a: + $(COMPILE.p) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Assembly section. +AS=as +ASFLAGS= +COMPILE.s=$(AS) $(ASFLAGS) $(TARGET_MACH) +COMPILE.S=$(CC) $(ASFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c +.s.o: + $(COMPILE.s) -o $@ $< +.s.a: + $(COMPILE.s) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.S.o: + $(COMPILE.S) -o $@ $< +.S.a: + $(COMPILE.S) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Shell section. +.sh: + $(RM) $@ + cat $< > $@ + chmod +x $@ + +# NeWS section +CPS=cps +CPSFLAGS= +.cps.h: + $(CPS) $(CPSFLAGS) $*.cps + +# Miscellaneous section. +LD=ld +LDFLAGS= +LDLIBS= +MAKE=make +RM=rm -f +AR=ar +ARFLAGS=rv +GET=/usr/sccs/get +GFLAGS= + +markfile.o: markfile + echo "static char _sccsid[] = \"`grep @'(#)' markfile`\";" > markfile.c + cc -c markfile.c + $(RM) markfile.c + +SCCSFLAGS= +SCCSGETFLAGS=-s +.SCCS_GET: + sccs $(SCCSFLAGS) get $(SCCSGETFLAGS) $@ -G$@ diff --git a/usr/src/make_src/Make/bin/make/common/depvar.cc b/usr/src/make_src/Make/bin/make/common/depvar.cc new file mode 100644 index 0000000..ee4be87 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/depvar.cc @@ -0,0 +1,149 @@ +/* + * 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 1995 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)depvar.cc 1.14 06/12/12 + */ + +#pragma ident "@(#)depvar.cc 1.14 06/12/12" + +/* + * Included files + */ +#include <mk/defs.h> +#include <mksh/misc.h> /* getmem() */ + +/* + * This file deals with "Dependency Variables". + * The "-V var" command line option is used to indicate + * that var is a dependency variable. Used in conjunction with + * the -P option the user is asking if the named variables affect + * the dependencies of the given target. + */ + +struct _Depvar { + Name name; /* Name of variable */ + struct _Depvar *next; /* Linked list */ + Boolean cmdline; /* Macro defined on the cmdline? */ +}; + +typedef struct _Depvar *Depvar; + +static Depvar depvar_list; +static Depvar *bpatch = &depvar_list; +static Boolean variant_deps; + +/* + * Add a name to the list. + */ + +void +depvar_add_to_list(Name name, Boolean cmdline) +{ + Depvar dv; + +#ifdef SUNOS4_AND_AFTER + dv = ALLOC(Depvar); +#else + dv = (Depvar) Malloc(sizeof(struct _Depvar)); +#endif + dv->name = name; + dv->next = NULL; + dv->cmdline = cmdline; + *bpatch = dv; + bpatch = &dv->next; +} + +/* + * The macro `name' has been used in either the left-hand or + * right-hand side of a dependency. See if it is in the + * list. Two things are looked for. Names given as args + * to the -V list are checked so as to set the same/differ + * output for the -P option. Names given as macro=value + * command-line args are checked and, if found, an NSE + * warning is produced. + */ +void +depvar_dep_macro_used(Name name) +{ + Depvar dv; + + for (dv = depvar_list; dv != NULL; dv = dv->next) { + if (name == dv->name) { +#ifdef NSE +#ifdef SUNOS4_AND_AFTER + if (dv->cmdline) { +#else + if (is_true(dv->cmdline)) { +#endif + nse_dep_cmdmacro(dv->name->string); + } +#endif + variant_deps = true; + break; + } + } +} + +#ifdef NSE +/* + * The macro `name' has been used in either the argument + * to a cd before a recursive make. See if it was + * defined on the command-line and, if so, complain. + */ +void +depvar_rule_macro_used(Name name) +{ + Depvar dv; + + for (dv = depvar_list; dv != NULL; dv = dv->next) { + if (name == dv->name) { +#ifdef SUNOS4_AND_AFTER + if (dv->cmdline) { +#else + if (is_true(dv->cmdline)) { +#endif + nse_rule_cmdmacro(dv->name->string); + } + break; + } + } +} +#endif + +/* + * Print the results. If any of the Dependency Variables + * affected the dependencies then the dependencies potentially + * differ because of these variables. + */ +void +depvar_print_results(void) +{ + if (variant_deps) { + printf(catgets(catd, 1, 234, "differ\n")); + } else { + printf(catgets(catd, 1, 235, "same\n")); + } +} + diff --git a/usr/src/make_src/Make/bin/make/common/dist.cc b/usr/src/make_src/Make/bin/make/common/dist.cc new file mode 100644 index 0000000..f825d43 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/dist.cc @@ -0,0 +1,581 @@ +/* + * 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. + */ +/* + * @(#)dist.cc 1.35 06/12/12 + */ + +#pragma ident "@(#)dist.cc 1.25 96/03/12" + +#ifdef DISTRIBUTED +/* + * dist.cc + * + * Deal with the distributed processing + */ + +#include <avo/err.h> +#include <avo/find_dir.h> +#include <avo/util.h> +#include <dm/Avo_AcknowledgeMsg.h> +#include <dm/Avo_DoJobMsg.h> +#include <dm/Avo_JobResultMsg.h> +#include <mk/defs.h> +#include <mksh/misc.h> /* getmem() */ +#include <rw/pstream.h> +#include <rw/queuecol.h> +#include <rw/xdrstrea.h> +#include <signal.h> +#ifdef linux +#include <sstream> +using namespace std; +#else +#include <strstream.h> +#endif +#include <sys/stat.h> /* stat() */ +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <errno.h> + +/* + * Defined macros + */ + +#define AVO_BLOCK_INTERUPTS sigfillset(&newset) ; \ + sigprocmask(SIG_SETMASK, &newset, &oldset) + +#define AVO_UNBLOCK_INTERUPTS \ + sigprocmask(SIG_SETMASK, &oldset, &newset) + + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +int dmake_ifd; +FILE* dmake_ifp; +XDR xdrs_in; + +int dmake_ofd; +FILE* dmake_ofp; +XDR xdrs_out; + +// These instances are required for the RWfactory. +static Avo_JobResultMsg dummyJobResultMsg; +static Avo_AcknowledgeMsg dummyAcknowledgeMsg; +static int firstAcknowledgeReceived = 0; + +int rxmPid = 0; + +/* + * File table of contents + */ +static void set_dmake_env_vars(void); + +/* + * void + * startup_rxm(void) + * + * When startup_rxm() is called, read_command_options() and + * read_files_and_state() have already been read, so DMake + * will now know what options to pass to rxm. + * + * rxm + * [ -k ] [ -n ] + * [ -c <dmake_rcfile> ] + * [ -g <dmake_group> ] + * [ -j <dmake_max_jobs> ] + * [ -m <dmake_mode> ] + * [ -o <dmake_odir> ] + * <read_fd> <write_fd> + * + * rxm will, among other things, read the rc file. + * + */ +void +startup_rxm(void) +{ + Name dmake_name; + Name dmake_value; + Avo_err *find_run_dir_err; + int pipe1[2], pipe2[2]; + Property prop; + char *run_dir; + char rxm_command[MAXPATHLEN]; + int rxm_debug = 0; + + int length; + char * env; + + firstAcknowledgeReceived = 0; + /* + * Create two pipes, one for dmake->rxm, and one for rxm->dmake. + * pipe1 is dmake->rxm, + * pipe2 is rxm->dmake. + */ + if ((pipe(pipe1) < 0) || (pipe(pipe2) < 0)) { + fatal(catgets(catd, 1, 245, "pipe() failed: %s"), errmsg(errno)); + } + + set_dmake_env_vars(); + + if ((rxmPid = fork()) < 0) { /* error */ + fatal(catgets(catd, 1, 246, "fork() failed: %s"), errmsg(errno)); + } else if (rxmPid > 0) { /* parent, dmake */ + dmake_ofd = pipe1[1]; // write side of pipe + if (!(dmake_ofp = fdopen(dmake_ofd, "a"))) { + fatal(catgets(catd, 1, 247, "fdopen() failed: %s"), errmsg(errno)); + } + xdrstdio_create(&xdrs_out, dmake_ofp, XDR_ENCODE); + + dmake_ifd = pipe2[0]; // read side of pipe + if (!(dmake_ifp = fdopen(dmake_ifd, "r"))) { + fatal(catgets(catd, 1, 248, "fdopen() failed: %s"), errmsg(errno)); + } + xdrstdio_create(&xdrs_in, dmake_ifp, XDR_DECODE); + + close(pipe1[0]); // read side + close(pipe2[1]); // write side + } else { /* child, rxm */ + close(pipe1[1]); // write side + close(pipe2[0]); // read side + + /* Find the run directory of dmake, for rxm. */ + find_run_dir_err = avo_find_run_dir(&run_dir); + if (find_run_dir_err) { + delete find_run_dir_err; + /* Use the path to find rxm. */ + (void) sprintf(rxm_command, NOCATGETS("rxm")); + } else { + /* Use the run dir of dmake for rxm. */ + (void) sprintf(rxm_command, NOCATGETS("%s/rxm"), run_dir); + } + + if (continue_after_error) { + (void) strcat(rxm_command, NOCATGETS(" -k")); + } + if (do_not_exec_rule) { + (void) strcat(rxm_command, NOCATGETS(" -n")); + } + if (rxm_debug) { + (void) strcat(rxm_command, NOCATGETS(" -S")); + } + if (send_mtool_msgs) { + (void) sprintf(&rxm_command[strlen(rxm_command)], + NOCATGETS(" -O %d"), + mtool_msgs_fd); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_RCFILE")); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + (void) sprintf(&rxm_command[strlen(rxm_command)], + NOCATGETS(" -c %s"), + dmake_value->string_mb); + } else { + length = 2 + strlen(NOCATGETS("DMAKE_RCFILE")); + env = getmem(length); + (void) sprintf(env, + "%s=", + NOCATGETS("DMAKE_RCFILE")); + (void) putenv(env); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_GROUP")); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + (void) sprintf(&rxm_command[strlen(rxm_command)], + NOCATGETS(" -g %s"), + dmake_value->string_mb); + } else { + length = 2 + strlen(NOCATGETS("DMAKE_GROUP")); + env = getmem(length); + (void) sprintf(env, + "%s=", + NOCATGETS("DMAKE_GROUP")); + (void) putenv(env); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MAX_JOBS")); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + (void) sprintf(&rxm_command[strlen(rxm_command)], + NOCATGETS(" -j %s"), + dmake_value->string_mb); + } else { + length = 2 + strlen(NOCATGETS("DMAKE_MAX_JOBS")); + env = getmem(length); + (void) sprintf(env, + "%s=", + NOCATGETS("DMAKE_MAX_JOBS")); + (void) putenv(env); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MODE")); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + (void) sprintf(&rxm_command[strlen(rxm_command)], + NOCATGETS(" -m %s"), + dmake_value->string_mb); + } else { + length = 2 + strlen(NOCATGETS("DMAKE_MODE")); + env = getmem(length); + (void) sprintf(env, + "%s=", + NOCATGETS("DMAKE_MODE")); + (void) putenv(env); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_ODIR")); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + (void) sprintf(&rxm_command[strlen(rxm_command)], + NOCATGETS(" -o %s"), + dmake_value->string_mb); + } else { + length = 2 + strlen(NOCATGETS("DMAKE_ODIR")); + env = getmem(length); + (void) sprintf(env, + "%s=", + NOCATGETS("DMAKE_ODIR")); + (void) putenv(env); + } + + (void) sprintf(&rxm_command[strlen(rxm_command)], + NOCATGETS(" %d %d"), + pipe1[0], pipe2[1]); +#ifdef linux + execl(NOCATGETS("/bin/sh"), +#else + execl(NOCATGETS("/usr/bin/sh"), +#endif + NOCATGETS("sh"), + NOCATGETS("-c"), + rxm_command, + (char *)NULL); + _exit(127); + } +} + +/* + * static void + * set_dmake_env_vars() + * + * Sets the DMAKE_* environment variables for rxm and rxs. + * DMAKE_PWD + * DMAKE_NPWD + * DMAKE_UMASK + * DMAKE_SHELL + */ +static void +set_dmake_env_vars() +{ + char *current_netpath; + char *current_path; + static char *env; + int length; + char netpath[MAXPATHLEN]; + mode_t um; + char um_buf[MAXPATHLEN]; + Name dmake_name; + Name dmake_value; + Property prop; + +#ifdef REDIRECT_ERR + /* Set __DMAKE_REDIRECT_STDERR */ + length = 2 + strlen(NOCATGETS("__DMAKE_REDIRECT_STDERR")) + 1; + env = getmem(length); + (void) sprintf(env, + "%s=%s", + NOCATGETS("__DMAKE_REDIRECT_STDERR"), + out_err_same ? NOCATGETS("0") : NOCATGETS("1")); + (void) putenv(env); +#endif + + /* Set DMAKE_PWD to the current working directory */ + current_path = get_current_path(); + length = 2 + strlen(NOCATGETS("DMAKE_PWD")) + strlen(current_path); + env = getmem(length); + (void) sprintf(env, + "%s=%s", + NOCATGETS("DMAKE_PWD"), + current_path); + (void) putenv(env); + + /* Set DMAKE_NPWD to machine:pathname */ + if (avo_path_to_netpath(current_path, netpath)) { + current_netpath = netpath; + } else { + current_netpath = current_path; + } + length = 2 + strlen(NOCATGETS("DMAKE_NPWD")) + strlen(current_netpath); + env = getmem(length); + (void) sprintf(env, + "%s=%s", + NOCATGETS("DMAKE_NPWD"), + current_netpath); + (void) putenv(env); + + /* Set DMAKE_UMASK to the value of umask */ + um = umask(0); + umask(um); + (void) sprintf(um_buf, NOCATGETS("%ul"), um); + length = 2 + strlen(NOCATGETS("DMAKE_UMASK")) + strlen(um_buf); + env = getmem(length); + (void) sprintf(env, + "%s=%s", + NOCATGETS("DMAKE_UMASK"), + um_buf); + (void) putenv(env); + + if (((prop = get_prop(shell_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + length = 2 + strlen(NOCATGETS("DMAKE_SHELL")) + + strlen(dmake_value->string_mb); + env = getmem(length); + (void) sprintf(env, + "%s=%s", + NOCATGETS("DMAKE_SHELL"), + dmake_value->string_mb); + (void) putenv(env); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_OUTPUT_MODE")); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + length = 2 + strlen(NOCATGETS("DMAKE_OUTPUT_MODE")) + + strlen(dmake_value->string_mb); + env = getmem(length); + (void) sprintf(env, + "%s=%s", + NOCATGETS("DMAKE_OUTPUT_MODE"), + dmake_value->string_mb); + (void) putenv(env); + } +} + +/* + * void + * distribute_rxm(Avo_DoJobMsg *dmake_job_msg) + * + * Write the DMake rule to be distributed down the pipe to rxm. + * + */ +void +distribute_rxm(Avo_DoJobMsg *dmake_job_msg) +{ + /* Add all dynamic env vars to the dmake_job_msg. */ + setvar_envvar(dmake_job_msg); + + /* + * Copying dosys()... + * Stat .make.state to see if we'll need to reread it later + */ + make_state->stat.time = file_no_time; + (void)exists(make_state); + make_state_before = make_state->stat.time; + + // Wait for the first Acknowledge message from the rxm process + // before sending the first message. + if (!firstAcknowledgeReceived) { + firstAcknowledgeReceived++; + Avo_AcknowledgeMsg *msg = getAcknowledgeMsg(); + if (msg) { + delete msg; + } + } + + RWCollectable *doJobMsg = (RWCollectable *)dmake_job_msg; + sigset_t newset; + sigset_t oldset; + + AVO_BLOCK_INTERUPTS; + int xdrResult = xdr(&xdrs_out, doJobMsg); + + if (xdrResult) { + fflush(dmake_ofp); + AVO_UNBLOCK_INTERUPTS; + } else { + AVO_UNBLOCK_INTERUPTS; + fatal(catgets(catd, 1, 249, "Couldn't send the job request to rxm")); + } + + delete dmake_job_msg; +} + +// Queue for JobResult messages. +static RWSlistCollectablesQueue jobResultQueue; + +// Queue for Acknowledge messages. +static RWSlistCollectablesQueue acknowledgeQueue; + +// Read a message from the rxm process, and put it into a queue, by +// message type. Return the message type. + +int +getRxmMessage(void) +{ + RWCollectable *msg = (RWCollectable *)0; + int msgType = 0; +// sigset_t newset; +// sigset_t oldset; + + // It seems unnecessarily to block interrupts here because + // any nonignored signal means exit for dmake in distributed mode. +// AVO_BLOCK_INTERUPTS; + int xdrResult = xdr(&xdrs_in, msg); +// AVO_UNBLOCK_INTERUPTS; + + if (xdrResult) { + switch(msg->isA()) { + case __AVO_ACKNOWLEDGEMSG: + acknowledgeQueue.append(msg); + msgType = __AVO_ACKNOWLEDGEMSG; + break; + case __AVO_JOBRESULTMSG: + jobResultQueue.append(msg); + msgType = __AVO_JOBRESULTMSG; + break; + default: + warning(catgets(catd, 1, 291, "Unknown message on rxm input fd")); + msgType = 0; + break; + } + } else { + if (errno == EINTR) { + fputs(NOCATGETS("dmake: Internal error: xdr() has been interrupted by a signal.\n"), stderr); + } + fatal(catgets(catd, 1, 250, "Couldn't receive message from rxm")); + } + + return msgType; +} + +// Get a JobResult message from it's queue, and +// if the queue is empty, call the getRxmMessage() function until +// a JobResult message shows. + +Avo_JobResultMsg * +getJobResultMsg(void) +{ + RWCollectable *msg = 0; + + if (!(msg = jobResultQueue.get())) { + while (getRxmMessage() != __AVO_JOBRESULTMSG); + msg = jobResultQueue.get(); + } + + return (Avo_JobResultMsg *)msg; +} + +// Get an Acknowledge message from it's queue, and +// if the queue is empty, call the getRxmMessage() function until +// a Acknowledge message shows. + +Avo_AcknowledgeMsg * +getAcknowledgeMsg(void) +{ + RWCollectable *msg = 0; + + if (!(msg = acknowledgeQueue.get())) { + while (getRxmMessage() != __AVO_ACKNOWLEDGEMSG); + msg = acknowledgeQueue.get(); + } + + return (Avo_AcknowledgeMsg *)msg; +} + + +/* + * Doname + * await_dist(Boolean waitflg) + * + * Waits for distributed children to exit and finishes their processing. + * Rxm will send a msg down the pipe when a child is done. + * + */ +Doname +await_dist(Boolean waitflg) +{ + Avo_JobResultMsg *dmake_result_msg; + int job_msg_id; + Doname result = build_ok; + int result_msg_cmd_status; + int result_msg_job_status; + Running rp; + + while (!(dmake_result_msg = getJobResultMsg())); + job_msg_id = dmake_result_msg->getId(); + result_msg_cmd_status = dmake_result_msg->getCmdStatus(); + result_msg_job_status = dmake_result_msg->getJobStatus(); + + if (waitflg) { + result = (result_msg_cmd_status == 0) ? build_ok : build_failed; + +#ifdef PRINT_EXIT_STATUS + if (result == build_ok) { + warning(NOCATGETS("I'm in await_dist(), waitflg is true, and result is build_ok.")); + } else { + warning(NOCATGETS("I'm in await_dist(), waitflg is true, and result is build_failed.")); + } +#endif + + } else { + for (rp = running_list; + (rp != NULL) && (job_msg_id != rp->job_msg_id); + rp = rp->next) { + } + if (rp == NULL) { + fatal(catgets(catd, 1, 251, "Internal error: returned child job_msg_id not in running_list")); + } else { + /* XXX - This may not be correct! */ + if (result_msg_job_status == RETURNED) { + rp->state = build_ok; + } else { + rp->state = (result_msg_cmd_status == 0) ? build_ok : build_failed; + } + result = rp->state; + +#ifdef PRINT_EXIT_STATUS + if (result == build_ok) { + warning(NOCATGETS("I'm in await_dist(), waitflg is false, and result is build_ok.")); + } else { + warning(NOCATGETS("I'm in await_dist(), waitflg is false, and result is build_failed.")); + } +#endif + + } + parallel_process_cnt--; + } + delete dmake_result_msg; + return result; +} + +#endif + + diff --git a/usr/src/make_src/Make/bin/make/common/dmake.cc b/usr/src/make_src/Make/bin/make/common/dmake.cc new file mode 100644 index 0000000..559a36d --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/dmake.cc @@ -0,0 +1,29 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)dmake.cc 1.2 06/12/12 + */ + +#pragma ident "@(#)dmake.cc 1.2 06/12/12" diff --git a/usr/src/make_src/Make/bin/make/common/doname.cc b/usr/src/make_src/Make/bin/make/common/doname.cc new file mode 100644 index 0000000..c06ba2f --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/doname.cc @@ -0,0 +1,3786 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)doname.cc 1.115 06/12/12 + */ + +#pragma ident "@(#)doname.cc 1.115 06/12/12" + +/* + * doname.c + * + * Figure out which targets are out of date and rebuild them + */ + +/* + * Included files + */ +#include <avo/avo_alloca.h> /* alloca() */ +#if defined(TEAMWARE_MAKE_CMN) +#include <avo/util.h> /* avo_get_user(), avo_hostname() */ +#endif + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ +# include <avo/strings.h> /* AVO_STRDUP() */ +# include <dm/Avo_MToolJobResultMsg.h> +# include <dm/Avo_MToolJobStartMsg.h> +# include <dm/Avo_MToolRsrcInfoMsg.h> +# include <dm/Avo_macro_defs.h> /* AVO_BLOCK_INTERUPTS & AVO_UNBLOCK_INTERUPTS */ +# include <dmthread/Avo_ServerState.h> +# include <rw/pstream.h> +# include <rw/xdrstrea.h> +#endif + +#include <fcntl.h> +#include <mk/defs.h> +#include <mksh/i18n.h> /* get_char_semantics_value() */ +#include <mksh/macro.h> /* getvar(), expand_value() */ +#include <mksh/misc.h> /* getmem() */ +#include <poll.h> + +#ifdef PARALLEL +# include <rx/api.h> +#endif + +#include <signal.h> + +#ifndef HP_UX +# include <stropts.h> +#endif + +#include <sys/errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> /* uname() */ +#include <sys/wait.h> +#include <unistd.h> /* close() */ + +/* + * Defined macros + */ +#ifndef PARALLEL +# define LOCALHOST "localhost" +#endif + +#define MAXRULES 100 + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ +#define SEND_MTOOL_MSG(cmds) \ + if (send_mtool_msgs) { \ + cmds \ + } +#else +#define SEND_MTOOL_MSG(cmds) +#endif + +// Sleep for .1 seconds between stat()'s +const int STAT_RETRY_SLEEP_TIME = 100000; + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static char hostName[MAXNAMELEN] = ""; +static char userName[MAXNAMELEN] = ""; + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ + static FILE *mtool_msgs_fp; + static XDR xdrs; + static int sent_rsrc_info_msg = 0; +#endif + +static int second_pass = 0; + +/* + * File table of contents + */ +extern Doname doname_check(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic); +extern Doname doname(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic); +static Boolean check_dependencies(Doname *result, Property line, Boolean do_get, Name target, Name true_target, Boolean doing_subtree, Chain *out_of_date_tail, Property old_locals, Boolean implicit, Property *command, Name less, Boolean rechecking_target, Boolean recheck_conditionals); +void dynamic_dependencies(Name target); +static Doname run_command(register Property line, Boolean print_machine); +extern Doname execute_serial(Property line); +extern Name vpath_translation(register Name cmd); +extern void check_state(Name temp_file_name); +static void read_dependency_file(register Name filename); +static void check_read_state_file(void); +static void do_assign(register Name line, register Name target); +static void build_command_strings(Name target, register Property line); +static Doname touch_command(register Property line, register Name target, Doname result); +extern void update_target(Property line, Doname result); +static Doname sccs_get(register Name target, register Property *command); +extern void read_directory_of_file(register Name file); +static void add_pattern_conditionals(register Name target); +extern void set_locals(register Name target, register Property old_locals); +extern void reset_locals(register Name target, register Property old_locals, register Property conditional, register int index); +extern Boolean check_auto_dependencies(Name target, int auto_count, Name *automatics); +static void delete_query_chain(Chain ch); + +// From read2.cc +extern Name normalize_name(register wchar_t *name_string, register int length); + + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ + static void append_job_result_msg(Avo_MToolJobResultMsg *job_result_msg); + static int pollResults(char *outFn, char *errFn, char *hostNm); + static void pollResultsAction(char *outFn, char *errFn); + static void rxmGetNextResultsBlock(int fd); + static int us_sleep(unsigned int nusecs); + extern "C" void Avo_PollResultsAction_Sigusr1Handler(int foo); +#endif + +/* + * DONE. + * + * doname_check(target, do_get, implicit, automatic) + * + * Will call doname() and then inspect the return value + * + * Return value: + * Indication if the build failed or not + * + * Parameters: + * target The target to build + * do_get Passed thru to doname() + * implicit Passed thru to doname() + * automatic Are we building a hidden dependency? + * + * Global variables used: + * build_failed_seen Set if -k is on and error occurs + * continue_after_error Indicates that -k is on + * report_dependencies No error msg if -P is on + */ +Doname +doname_check(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic) +{ + int first_time = 1; + (void) fflush(stdout); +try_again: + switch (doname(target, do_get, implicit, automatic)) { + case build_ok: + second_pass = 0; + return build_ok; + case build_running: + second_pass = 0; + return build_running; + case build_failed: + if (!continue_after_error) { + fatal(catgets(catd, 1, 13, "Target `%s' not remade because of errors"), + target->string_mb); + } + build_failed_seen = true; + second_pass = 0; + return build_failed; + case build_dont_know: + /* + * If we can't figure out how to build an automatic + * (hidden) dependency, we just ignore it. + * We later declare the target to be out of date just in + * case something changed. + * Also, don't complain if just reporting the dependencies + * and not building anything. + */ + if (automatic || (report_dependencies_level > 0)) { + second_pass = 0; + return build_dont_know; + } + if(first_time) { + first_time = 0; + second_pass = 1; + goto try_again; + } + second_pass = 0; + if (continue_after_error && !svr4) { + warning(catgets(catd, 1, 14, "Don't know how to make target `%s'"), + target->string_mb); + build_failed_seen = true; + return build_failed; + } + fatal(catgets(catd, 1, 15, "Don't know how to make target `%s'"), target->string_mb); + break; + } +#ifdef lint + return build_failed; +#endif +} + + +void +enter_explicit_rule_from_dynamic_rule(Name target, Name source) +{ + Property line, source_line; + Dependency dependency; + + source_line = get_prop(source->prop, line_prop); + line = maybe_append_prop(target, line_prop); + line->body.line.sccs_command = false; + line->body.line.target = target; + if (line->body.line.command_template == NULL) { + line->body.line.command_template = source_line->body.line.command_template; + for (dependency = source_line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + enter_dependency(line, dependency->name, false); + } + line->body.line.less = target; + } + line->body.line.percent = NULL; +} + + + +Name +find_dyntarget(Name target) +{ + Dyntarget p; + int i; + String_rec string; + wchar_t buffer[STRING_BUFFER_LENGTH]; + wchar_t *pp, * bufend; + wchar_t tbuffer[MAXPATHLEN]; + Wstring wcb(target); + + for (p = dyntarget_list; p != NULL; p = p->next) { + INIT_STRING_FROM_STACK(string, buffer); + expand_value(p->name, &string, false); + i = 0; + pp = string.buffer.start; + bufend = pp + STRING_BUFFER_LENGTH; + while((*pp != nul_char) && (pp < bufend)) { + if(iswspace(*pp)) { + tbuffer[i] = nul_char; + if(i > 0) { + if (wcb.equal(tbuffer)) { + enter_explicit_rule_from_dynamic_rule(target, p->name); + return(target); + } + } + pp++; + i = 0; + continue; + } + tbuffer[i] = *pp; + i++; + pp++; + if(*pp == nul_char) { + tbuffer[i] = nul_char; + if(i > 0) { + if (wcb.equal(tbuffer)) { + enter_explicit_rule_from_dynamic_rule(target, p->name); + return(target); + } + } + break; + } + } + } + return(NULL); +} + +/* + * DONE. + * + * doname(target, do_get, implicit) + * + * Chases all files the target depends on and builds any that + * are out of date. If the target is out of date it is then rebuilt. + * + * Return value: + * Indiates if build failed or nt + * + * Parameters: + * target Target to build + * do_get Run sccs get is nessecary + * implicit doname is trying to find an implicit rule + * + * Global variables used: + * assign_done True if command line assgnment has happened + * commands_done Preserved for the case that we need local value + * debug_level Should we trace make's actions? + * default_rule The rule for ".DEFAULT", used as last resort + * empty_name The Name "", used when looking for single sfx + * keep_state Indicates that .KEEP_STATE is on + * parallel True if building in parallel + * recursion_level Used for tracing + * report_dependencies make -P is on + */ +Doname +doname(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic) +{ + Doname result = build_dont_know; + Chain out_of_date_list = NULL; +#ifdef TEAMWARE_MAKE_CMN + Chain target_group; +#endif + Property old_locals = NULL; + register Property line; + Property command = NULL; + register Dependency dependency; + Name less = NULL; + Name true_target = target; + Name *automatics = NULL; + register int auto_count; + Boolean rechecking_target = false; + Boolean saved_commands_done; + Boolean restart = false; + Boolean save_parallel = parallel; +#ifdef NSE + Boolean save_readdep; +#endif + Boolean doing_subtree = false; + + Boolean recheck_conditionals = false; + + if (target->state == build_running) { + return build_running; + } + line = get_prop(target->prop, line_prop); +#ifdef TEAMWARE_MAKE_CMN + if (line != NULL) { + /* + * If this target is a member of target group and one of the + * other members of the group is running, mark this target + * as running. + */ + for (target_group = line->body.line.target_group; + target_group != NULL; + target_group = target_group->next) { + if (is_running(target_group->name)) { + target->state = build_running; + add_pending(target, + recursion_level, + do_get, + implicit, + false); + return build_running; + } + } + } +#ifdef NSE + nse_check_file_backquotes(target->string); +#endif +#endif + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + if (true_target->colon_splits > 0) { + /* Make sure we have a valid time for :: targets */ + Property time; + + time = get_prop(true_target->prop, time_prop); + if (time != NULL) { + true_target->stat.time = time->body.time.time; + } + } + } + (void) exists(true_target); + /* + * If the target has been processed, we don't need to do it again, + * unless it depends on conditional macros or a delayed assignment, + * or it has been done when KEEP_STATE is on. + */ + if (target->state == build_ok) { + if((!keep_state || (!target->depends_on_conditional && !assign_done))) { + return build_ok; + } else { + recheck_conditionals = true; + } + } + if (target->state == build_subtree) { + /* A dynamic macro subtree is being built */ + target->state = build_dont_know; + doing_subtree = true; + if (!target->checking_subtree) { + /* + * This target has been started before and therefore + * not all dependencies have to be built. + */ + restart = true; + } + } else if (target->state == build_pending) { + target->state = build_dont_know; + restart = true; +/* +#ifdef TEAMWARE_MAKE_CMN + } else if (parallel && + keep_state && + (target->conditional_cnt > 0)) { + if (!parallel_ok(target, false)) { + add_subtree(target, recursion_level, do_get, implicit); + target->state = build_running; + return build_running; + } +#endif + */ + } + /* + * If KEEP_STATE is on, we have to rebuild the target if the + * building of it caused new automatic dependencies to be reported. + * This is where we restart the build. + */ + if (line != NULL) { + line->body.line.percent = NULL; + } +recheck_target: + /* Init all local variables */ + result = build_dont_know; + out_of_date_list = NULL; + command = NULL; + less = NULL; + auto_count = 0; + if (!restart && line != NULL) { + /* + * If this target has never been built before, mark all + * of the dependencies as never built. + */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + dependency->built = false; + } + } + /* Save the set of automatic depes defined for this target */ + if (keep_state && + (line != NULL) && + (line->body.line.dependencies != NULL)) { + Name *p; + + /* + * First run thru the dependency list to see how many + * autos there are. + */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (dependency->automatic && !dependency->stale) { + auto_count++; + } + } + /* Create vector to hold the current autos */ + automatics = + (Name *) alloca((int) (auto_count * sizeof (Name))); + /* Copy them */ + for (p = automatics, dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (dependency->automatic && !dependency->stale) { + *p++ = dependency->name; + } + } + } + if (debug_level > 1) { + (void) printf(NOCATGETS("%*sdoname(%s)\n"), + recursion_level, + "", + target->string_mb); + } + recursion_level++; + /* Avoid infinite loops */ + if (target->state == build_in_progress) { + warning(catgets(catd, 1, 16, "Infinite loop: Target `%s' depends on itself"), + target->string_mb); + return build_ok; + } + target->state = build_in_progress; + + /* Activate conditional macros for the target */ + if (!target->added_pattern_conditionals) { + add_pattern_conditionals(target); + target->added_pattern_conditionals = true; + } + if (target->conditional_cnt > 0) { + old_locals = (Property) alloca(target->conditional_cnt * + sizeof (Property_rec)); + set_locals(target, old_locals); + } + +/* + * after making the call to dynamic_dependecies unconditional we can handle + * target names that are same as file name. In this case $$@ in the + * dependencies did not mean anything. WIth this change it expands it + * as expected. + */ + if (!target->has_depe_list_expanded) + { +#ifdef NSE + save_readdep = reading_dependencies; + reading_dependencies= true; +#endif + dynamic_dependencies(target); +#ifdef NSE + reading_dependencies= save_readdep; +#endif + } + +/* + * FIRST SECTION -- GO THROUGH DEPENDENCIES AND COLLECT EXPLICIT + * COMMANDS TO RUN + */ + if ((line = get_prop(target->prop, line_prop)) != NULL) { + if (check_dependencies(&result, + line, + do_get, + target, + true_target, + doing_subtree, + &out_of_date_list, + old_locals, + implicit, + &command, + less, + rechecking_target, + recheck_conditionals)) { + return build_running; + } + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = out_of_date_list; + } + +#ifdef PARALLEL + if (doing_subtree) { + parallel = false; + } +#endif + +/* + * If the target is a :: type, do not try to find the rule for the target, + * all actions will be taken by separate branches. + * Else, we try to find an implicit rule using various methods, + * we quit as soon as one is found. + * + * [tolik, 12 Sep 2002] Do not try to find implicit rule for the target + * being rechecked - the target is being rechecked means that it already + * has explicit dependencies derived from an implicit rule found + * in previous step. + */ + if (target->colon_splits == 0 && !rechecking_target) { + /* Look for percent matched rule */ + if ((result == build_dont_know) && + (command == NULL)) { + switch (find_percent_rule( + target, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; +#ifdef TEAMWARE_MAKE_CMN + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; +#endif + case build_ok: + result = build_ok; + break; + } + } + /* Look for double suffix rule */ + if (result == build_dont_know) { + Property member; + + if (target->is_member && + ((member = get_prop(target->prop, member_prop)) != + NULL)) { + switch (find_ar_suffix_rule(target, + member->body. + member.member, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; +#ifdef TEAMWARE_MAKE_CMN + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; +#endif + default: + /* ALWAYS bind $% for old style */ + /* ar rules */ + if (line == NULL) { + line = + maybe_append_prop(target, + line_prop); + } + line->body.line.percent = + member->body.member.member; + break; + } + } else { + switch (find_double_suffix_rule(target, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; +#ifdef TEAMWARE_MAKE_CMN + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target-> + prop, + conditional_prop), + 0); + } + return build_running; +#endif + } + } + } + /* Look for single suffix rule */ + +/* /tolik/ + * I commented !implicit to fix bug 1247448: Suffix Rules failed when combine with Pattern Matching Rules. + * This caused problem with SVR4 tilde rules (infinite recursion). So I made some changes in "implicit.cc" + */ +/* /tolik, 06.21.96/ + * Regression! See BugId 1255360 + * If more than one percent rules are defined for the same target then + * the behaviour of 'make' with my previous fix may be different from one + * of the 'old make'. + * The global variable second_pass (maybe it should be an argument to doname()) + * is intended to avoid this regression. It is set in doname_check(). + * First, 'make' will work as it worked before. Only when it is + * going to say "don't know how to make target" it sets second_pass to true and + * run 'doname' again but now trying to use Single Suffix Rules. + */ + if ((result == build_dont_know) && !automatic && (!implicit || second_pass) && + ((line == NULL) || + ((line->body.line.target != NULL) && + !line->body.line.target->has_regular_dependency))) { + switch (find_suffix_rule(target, + target, + empty_name, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; +#ifdef TEAMWARE_MAKE_CMN + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; +#endif + } + } + /* Try to sccs get */ + if ((command == NULL) && + (result == build_dont_know) && + do_get) { + result = sccs_get(target, &command); + } + + /* Use .DEFAULT rule if it is defined. */ + if ((command == NULL) && + (result == build_dont_know) && + (true_target->colons == no_colon) && + default_rule && + !implicit) { + /* Make sure we have a line prop */ + line = maybe_append_prop(target, line_prop); + command = line; + Boolean out_of_date; + if (true_target->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time, + line->body.line.dependency_time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time, + line->body.line.dependency_time); + } + if (build_unconditional || out_of_date) { + line->body.line.is_out_of_date = true; + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 17, "%*sBuilding %s using .DEFAULT because it is out of date\n"), + recursion_level, + "", + true_target->string_mb); + } + } + line->body.line.sccs_command = false; + line->body.line.command_template = default_rule; + line->body.line.target = true_target; + line->body.line.star = NULL; + line->body.line.less = true_target; + line->body.line.percent = NULL; + } + } + + /* We say "target up to date" if no cmd were executed for the target */ + if (!target->is_double_colon_parent) { + commands_done = false; + } + + silent = silent_all; + ignore_errors = ignore_errors_all; + if (posix) + { + if (!silent) + { + silent = (Boolean) target->silent_mode; + } + if (!ignore_errors) + { + ignore_errors = (Boolean) target->ignore_error_mode; + } + } + + int doname_dyntarget = 0; +r_command: + /* Run commands if any. */ + if ((command != NULL) && + (command->body.line.command_template != NULL)) { + if (result != build_failed) { + result = run_command(command, + (Boolean) ((parallel || save_parallel) && !silent)); +#ifdef NSE + nse_check_no_deps_no_rule(target, + get_prop(target->prop, line_prop), command); +#endif + } + switch (result) { +#ifdef TEAMWARE_MAKE_CMN + case build_running: + add_running(target, + true_target, + command, + --recursion_level, + auto_count, + automatics, + do_get, + implicit); + target->state = build_running; + if ((line = get_prop(target->prop, + line_prop)) != NULL) { + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + } + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; + case build_serial: + add_serial(target, + --recursion_level, + do_get, + implicit); + target->state = build_running; + line = get_prop(target->prop, line_prop); + if (line != NULL) { + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + } + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; +#endif + case build_ok: + /* If all went OK set a nice timestamp */ + if (true_target->stat.time == file_doesnt_exist) { + true_target->stat.time = file_max_time; + } + break; + } + } else { + /* + * If no command was found for the target, and it doesn't + * exist, and it is mentioned as a target in the makefile, + * we say it is extremely new and that it is OK. + */ + if (target->colons != no_colon) { + if (true_target->stat.time == file_doesnt_exist){ + true_target->stat.time = file_max_time; + } + result = build_ok; + } + /* + * Trying dynamic targets. + */ + if(!doname_dyntarget) { + doname_dyntarget = 1; + Name dtarg = find_dyntarget(target); + if(dtarg!=NULL) { + if (!target->has_depe_list_expanded) { + dynamic_dependencies(target); + } + if ((line = get_prop(target->prop, line_prop)) != NULL) { + if (check_dependencies(&result, + line, + do_get, + target, + true_target, + doing_subtree, + &out_of_date_list, + old_locals, + implicit, + &command, + less, + rechecking_target, + recheck_conditionals)) + { + return build_running; + } + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = out_of_date_list; + } + goto r_command; + } + } + /* + * If the file exists, it is OK that we couldnt figure + * out how to build it. + */ + (void) exists(target); + if ((target->stat.time != file_doesnt_exist) && + (result == build_dont_know)) { + result = build_ok; + } + } + + /* + * Some of the following is duplicated in the function finish_doname. + * If anything is changed here, check to see if it needs to be + * changed there. + */ + if ((line = get_prop(target->prop, line_prop)) != NULL) { + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + } + target->state = result; + parallel = save_parallel; + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, conditional_prop), + 0); + } + recursion_level--; + if (target->is_member) { + Property member; + + /* Propagate the timestamp from the member file to the member*/ + if ((target->stat.time != file_max_time) && + ((member = get_prop(target->prop, member_prop)) != NULL) && + (exists(member->body.member.member) > file_doesnt_exist)) { + target->stat.time = + member->body.member.member->stat.time; + } + } + /* + * Check if we found any new auto dependencies when we + * built the target. + */ + if ((result == build_ok) && check_auto_dependencies(target, + auto_count, + automatics)) { + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 18, "%*sTarget `%s' acquired new dependencies from build, rechecking all dependencies\n"), + recursion_level, + "", + true_target->string_mb); + } + rechecking_target = true; + saved_commands_done = commands_done; + goto recheck_target; + } + + if (rechecking_target && !commands_done) { + commands_done = saved_commands_done; + } + + return result; +} + +/* + * DONE. + * + * check_dependencies(result, line, do_get, + * target, true_target, doing_subtree, out_of_date_tail, + * old_locals, implicit, command, less, rechecking_target) + * + * Return value: + * True returned if some dependencies left running + * + * Parameters: + * result Pointer to cell we update if build failed + * line We get the dependencies from here + * do_get Allow use of sccs get in recursive doname() + * target The target to chase dependencies for + * true_target The real one for :: and lib(member) + * doing_subtree True if building a conditional macro subtree + * out_of_date_tail Used to set the $? list + * old_locals Used for resetting the local macros + * implicit Called when scanning for implicit rules? + * command Place to stuff command + * less Set to $< value + * + * Global variables used: + * command_changed Set if we suspect .make.state needs rewrite + * debug_level Should we trace actions? + * force The Name " FORCE", compared against + * recursion_level Used for tracing + * rewrite_statefile Set if .make.state needs rewriting + * wait_name The Name ".WAIT", compared against + */ +static Boolean +#ifdef TEAMWARE_MAKE_CMN +check_dependencies(Doname *result, Property line, Boolean do_get, Name target, Name true_target, Boolean doing_subtree, Chain *out_of_date_tail, Property old_locals, Boolean implicit, Property *command, Name less, Boolean rechecking_target, Boolean recheck_conditionals) +#else +check_dependencies(Doname *result, Property line, Boolean do_get, Name target, Name true_target, Boolean, Chain *out_of_date_tail, Property, Boolean, Property *command, Name less, Boolean rechecking_target, Boolean recheck_conditionals) +#endif +{ + Boolean dependencies_running; + register Dependency dependency; + Doname dep_result; + Boolean dependency_changed = false; + + line->body.line.dependency_time = file_doesnt_exist; + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + line->body.line.is_out_of_date = false; + dependencies_running = false; + /* + * Run thru all the dependencies and call doname() recursively + * on each of them. + */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + Boolean this_dependency_changed = false; + + if (!dependency->automatic && + (rechecking_target || target->rechecking_target)) { + /* + * We only bother with the autos when rechecking + */ + continue; + } + + if (dependency->name == wait_name) { + /* + * The special target .WAIT means finish all of + * the prior dependencies before continuing. + */ + if (dependencies_running) { + break; + } +#ifdef DISTRIBUTED + } else if ((!parallel_ok(dependency->name, false)) && + (dependencies_running)) { + /* + * If we can't execute the current dependency in + * parallel, hold off the dependency processing + * to preserve the order of the dependencies. + */ + break; +#endif + } else { + timestruc_t depe_time = file_doesnt_exist; + + + if (true_target->is_member) { + depe_time = exists(dependency->name); + } + if (dependency->built || + (dependency->name->state == build_failed)) { + dep_result = (Doname) dependency->name->state; + } else { +#ifdef NSE + nse_check_sccs(target->string, + dependency->name->string); + nse_check_derived_src(target, + dependency->name->string, + line->body.line.command_template); +#endif + dep_result = doname_check(dependency->name, + do_get, + false, + (Boolean) dependency->automatic); + } + if (true_target->is_member || dependency->name->is_member) { + /* should compare only secs, cause lib members does not have nsec time resolution */ + if (depe_time.tv_sec != dependency->name->stat.time.tv_sec) { + this_dependency_changed = + dependency_changed = + true; + } + } else { + if (depe_time != dependency->name->stat.time) { + this_dependency_changed = + dependency_changed = + true; + } + } + dependency->built = true; + switch (dep_result) { + case build_running: + dependencies_running = true; + continue; + case build_failed: + *result = build_failed; + break; + case build_dont_know: +/* + * If make can't figure out how to make a dependency, maybe the dependency + * is out of date. In this case, we just declare the target out of date + * and go on. If we really need the dependency, the make'ing of the target + * will fail. This will only happen for automatic (hidden) dependencies. + */ + if(!recheck_conditionals) { + line->body.line.is_out_of_date = true; + } + /* + * Make sure the dependency is not saved + * in the state file. + */ + dependency->stale = true; + rewrite_statefile = + command_changed = + true; + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 19, "Target %s rebuilt because dependency %s does not exist\n"), + true_target->string_mb, + dependency->name->string_mb); + } + break; + } + if (dependency->name->depends_on_conditional) { + target->depends_on_conditional = true; + } + if (dependency->name == force) { + target->stat.time = + dependency->name->stat.time; + } + /* + * Propagate new timestamp from "member" to + * "lib.a(member)". + */ + (void) exists(dependency->name); + + /* Collect the timestamp of the youngest dependency */ + line->body.line.dependency_time = + MAX(dependency->name->stat.time, + line->body.line.dependency_time); + + /* Correction: do not consider nanosecs for members */ + if(true_target->is_member || dependency->name->is_member) { + line->body.line.dependency_time.tv_nsec = 0; + } + + if (debug_level > 1) { + (void) printf(catgets(catd, 1, 20, "%*sDate(%s)=%s \n"), + recursion_level, + "", + dependency->name->string_mb, + time_to_string(dependency->name-> + stat.time)); + if (dependency->name->stat.time > line->body.line.dependency_time) { + (void) printf(catgets(catd, 1, 21, "%*sDate-dependencies(%s) set to %s\n"), + recursion_level, + "", + true_target->string_mb, + time_to_string(line->body.line. + dependency_time)); + } + } + + /* Build the $? list */ + if (true_target->is_member) { + if (this_dependency_changed == true) { + true_target->stat.time = dependency->name->stat.time; + true_target->stat.time.tv_sec--; + } else { + /* Dina: + * The next statement is commented + * out as a fix for bug #1051032. + * if dependency hasn't changed + * then there's no need to invalidate + * true_target. This statemnt causes + * make to take much longer to process + * an already-built archive. Soren + * said it was a quick fix for some + * problem he doesn't remember. + true_target->stat.time = file_no_time; + */ + (void) exists(true_target); + } + } else { + (void) exists(true_target); + } + Boolean out_of_date; + if (true_target->is_member || dependency->name->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time, + dependency->name->stat.time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time, + dependency->name->stat.time); + } + if ((build_unconditional || out_of_date) && + (dependency->name != force) && + (dependency->stale == false)) { + *out_of_date_tail = ALLOC(Chain); + if (dependency->name->is_member && + (get_prop(dependency->name->prop, + member_prop) != NULL)) { + (*out_of_date_tail)->name = + get_prop(dependency->name->prop, + member_prop)-> + body.member.member; + } else { + (*out_of_date_tail)->name = + dependency->name; + } + (*out_of_date_tail)->next = NULL; + out_of_date_tail = &(*out_of_date_tail)->next; + if (debug_level > 0) { + if (dependency->name->stat.time == file_max_time) { + (void) printf(catgets(catd, 1, 22, "%*sBuilding %s because %s does not exist\n"), + recursion_level, + "", + true_target->string_mb, + dependency->name->string_mb); + } else { + (void) printf(catgets(catd, 1, 23, "%*sBuilding %s because it is out of date relative to %s\n"), + recursion_level, + "", + true_target->string_mb, + dependency->name->string_mb); + } + } + } + if (dependency->name == force) { + force->stat.time = + file_max_time; + force->state = build_dont_know; + } + } + } +#ifdef TEAMWARE_MAKE_CMN + if (dependencies_running) { + if (doing_subtree) { + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return true; + } else { + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return true; + } + } +#endif + /* + * Collect the timestamp of the youngest double colon target + * dependency. + */ + if (target->is_double_colon_parent) { + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + Property tmp_line; + + if ((tmp_line = get_prop(dependency->name->prop, line_prop)) != NULL) { + if(tmp_line->body.line.dependency_time != file_max_time) { + target->stat.time = + MAX(tmp_line->body.line.dependency_time, + target->stat.time); + } + } + } + } + if ((true_target->is_member) && (dependency_changed == true)) { + true_target->stat.time = file_no_time; + } + /* + * After scanning all the dependencies, we check the rule + * if we found one. + */ + if (line->body.line.command_template != NULL) { + if (line->body.line.command_template_redefined) { + warning(catgets(catd, 1, 24, "Too many rules defined for target %s"), + target->string_mb); + } + *command = line; + /* Check if the target is out of date */ + Boolean out_of_date; + if (true_target->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time, + line->body.line.dependency_time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time, + line->body.line.dependency_time); + } + if (build_unconditional || out_of_date){ + if(!recheck_conditionals) { + line->body.line.is_out_of_date = true; + } + } + line->body.line.sccs_command = false; + line->body.line.target = true_target; + if(gnu_style) { + + // set $< for explicit rule + if(line->body.line.dependencies != NULL) { + less = line->body.line.dependencies->name; + } + + // set $* for explicit rule + Name target_body; + Name tt = true_target; + Property member; + register wchar_t *target_end; + register Dependency suffix; + register int suffix_length; + Wstring targ_string; + Wstring suf_string; + + if (true_target->is_member && + ((member = get_prop(target->prop, member_prop)) != + NULL)) { + tt = member->body.member.member; + } + targ_string.init(tt); + target_end = targ_string.get_string() + tt->hash.length; + for (suffix = suffixes; suffix != NULL; suffix = suffix->next) { + suffix_length = suffix->name->hash.length; + suf_string.init(suffix->name); + if (tt->hash.length < suffix_length) { + continue; + } else if (!IS_WEQUALN(suf_string.get_string(), + (target_end - suffix_length), + suffix_length)) { + continue; + } + target_body = GETNAME( + targ_string.get_string(), + (int)(tt->hash.length - suffix_length) + ); + line->body.line.star = target_body; + } + + // set result = build_ok so that implicit rules are not used. + if(*result == build_dont_know) { + *result = build_ok; + } + } + if (less != NULL) { + line->body.line.less = less; + } + } + + return false; +} + +/* + * dynamic_dependencies(target) + * + * Checks if any dependency contains a macro ref + * If so, it replaces the dependency with the expanded version. + * Here, "$@" gets translated to target->string. That is + * the current name on the left of the colon in the + * makefile. Thus, + * xyz: s.$@.c + * translates into + * xyz: s.xyz.c + * + * Also, "$(@F)" translates to the same thing without a preceeding + * directory path (if one exists). + * Note, to enter "$@" on a dependency line in a makefile + * "$$@" must be typed. This is because make expands + * macros in dependency lists upon reading them. + * dynamic_dependencies() also expands file wildcards. + * If there are any Shell meta characters in the name, + * search the directory, and replace the dependency + * with the set of files the pattern matches + * + * Parameters: + * target Target to sanitize dependencies for + * + * Global variables used: + * c_at The Name "@", used to set macro value + * debug_level Should we trace actions? + * dot The Name ".", used to read directory + * recursion_level Used for tracing + */ +void +dynamic_dependencies(Name target) +{ + wchar_t pattern[MAXPATHLEN]; + register wchar_t *p; + Property line; + register Dependency dependency; + register Dependency *remove; + String_rec string; + wchar_t buffer[MAXPATHLEN]; + register Boolean set_at = false; + register wchar_t *start; + Dependency new_depe; + register Boolean reuse_cell; + Dependency first_member; + Name directory; + Name lib; + Name member; + Property prop; + Name true_target = target; + wchar_t *library; + + if ((line = get_prop(target->prop, line_prop)) == NULL) { + return; + } + /* If the target is constructed from a "::" target we consider that */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + /* Scan all dependencies and process the ones that contain "$" chars */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (!dependency->name->dollar) { + continue; + } + target->has_depe_list_expanded = true; + + /* The make macro $@ is bound to the target name once per */ + /* invocation of dynamic_dependencies() */ + if (!set_at) { + (void) SETVAR(c_at, true_target, false); + set_at = true; + } + /* Expand this dependency string */ + INIT_STRING_FROM_STACK(string, buffer); + expand_value(dependency->name, &string, false); + /* Scan the expanded string. It could contain whitespace */ + /* which mean it expands to several dependencies */ + start = string.buffer.start; + while (iswspace(*start)) { + start++; + } + /* Remove the cell (later) if the macro was empty */ + if (start[0] == (int) nul_char) { + dependency->name = NULL; + } + +/* azv 10/26/95 to fix bug BID_1170218 */ + if ((start[0] == (int) period_char) && + (start[1] == (int) slash_char)) { + start += 2; + } +/* azv */ + + first_member = NULL; + /* We use the original dependency cell for the first */ + /* dependency from the expansion */ + reuse_cell = true; + /* We also have to deal with dependencies that expand to */ + /* lib.a(members) notation */ + for (p = start; *p != (int) nul_char; p++) { + if ((*p == (int) parenleft_char)) { + lib = GETNAME(start, p - start); + lib->is_member = true; + first_member = dependency; + start = p + 1; + while (iswspace(*start)) { + start++; + } + break; + } + } + do { + /* First skip whitespace */ + for (p = start; *p != (int) nul_char; p++) { + if ((*p == (int) nul_char) || + iswspace(*p) || + (*p == (int) parenright_char)) { + break; + } + } + /* Enter dependency from expansion */ + if (p != start) { + /* Create new dependency cell if */ + /* this is not the first dependency */ + /* picked from the expansion */ + if (!reuse_cell) { + new_depe = ALLOC(Dependency); + new_depe->next = dependency->next; + new_depe->automatic = false; + new_depe->stale = false; + new_depe->built = false; + dependency->next = new_depe; + dependency = new_depe; + } + reuse_cell = false; + /* Internalize the dependency name */ + // tolik. Fix for bug 4110429: inconsistent expansion for macros that + // include "//" and "/./" + //dependency->name = GETNAME(start, p - start); + dependency->name = normalize_name(start, p - start); + if ((debug_level > 0) && + (first_member == NULL)) { + (void) printf(catgets(catd, 1, 25, "%*sDynamic dependency `%s' for target `%s'\n"), + recursion_level, + "", + dependency->name->string_mb, + true_target->string_mb); + } + for (start = p; iswspace(*start); start++); + p = start; + } + } while ((*p != (int) nul_char) && + (*p != (int) parenright_char)); + /* If the expansion was of lib.a(members) format we now */ + /* enter the proper member cells */ + if (first_member != NULL) { + /* Scan the new dependencies and transform them from */ + /* "foo" to "lib.a(foo)" */ + for (; 1; first_member = first_member->next) { + /* Build "lib.a(foo)" name */ + INIT_STRING_FROM_STACK(string, buffer); + APPEND_NAME(lib, + &string, + (int) lib->hash.length); + append_char((int) parenleft_char, &string); + APPEND_NAME(first_member->name, + &string, + FIND_LENGTH); + append_char((int) parenright_char, &string); + member = first_member->name; + /* Replace "foo" with "lib.a(foo)" */ + first_member->name = + GETNAME(string.buffer.start, FIND_LENGTH); + if (string.free_after_use) { + retmem(string.buffer.start); + } + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 26, "%*sDynamic dependency `%s' for target `%s'\n"), + recursion_level, + "", + first_member->name-> + string_mb, + true_target->string_mb); + } + first_member->name->is_member = lib->is_member; + /* Add member property to member */ + prop = maybe_append_prop(first_member->name, + member_prop); + prop->body.member.library = lib; + prop->body.member.entry = NULL; + prop->body.member.member = member; + if (first_member == dependency) { + break; + } + } + } + } + Wstring wcb; + /* Then scan all the dependencies again. This time we want to expand */ + /* shell file wildcards */ + for (remove = &line->body.line.dependencies, dependency = *remove; + dependency != NULL; + dependency = *remove) { + if (dependency->name == NULL) { + dependency = *remove = (*remove)->next; + continue; + } + /* If dependency name string contains shell wildcards */ + /* replace the name with the expansion */ + if (dependency->name->wildcard) { +#ifdef NSE + nse_wildcard(target->string, dependency->name->string); +#endif + wcb.init(dependency->name); + if ((start = (wchar_t *) wschr(wcb.get_string(), + (int) parenleft_char)) != NULL) { + /* lib(*) type pattern */ + library = buffer; + (void) wsncpy(buffer, + wcb.get_string(), + start - wcb.get_string()); + buffer[start-wcb.get_string()] = + (int) nul_char; + (void) wsncpy(pattern, + start + 1, +(int) (dependency->name->hash.length-(start-wcb.get_string())-2)); + pattern[dependency->name->hash.length - + (start-wcb.get_string()) - 2] = + (int) nul_char; + } else { + library = NULL; + (void) wsncpy(pattern, + wcb.get_string(), + (int) dependency->name->hash.length); + pattern[dependency->name->hash.length] = + (int) nul_char; + } + start = (wchar_t *) wsrchr(pattern, (int) slash_char); + if (start == NULL) { + directory = dot; + p = pattern; + } else { + directory = GETNAME(pattern, start-pattern); + p = start+1; + } + /* The expansion is handled by the read_dir() routine*/ + if (read_dir(directory, p, line, library)) { + *remove = (*remove)->next; + } else { + remove = &dependency->next; + } + } else { + remove = &dependency->next; + } + } + + /* Then unbind $@ */ + (void) SETVAR(c_at, (Name) NULL, false); +} + +/* + * DONE. + * + * run_command(line) + * + * Takes one Cmd_line and runs the commands from it. + * + * Return value: + * Indicates if the command failed or not + * + * Parameters: + * line The command line to run + * + * Global variables used: + * commands_done Set if we do run command + * current_line Set to the line we run a command from + * current_target Set to the target we run a command for + * file_number Used to form temp file name + * keep_state Indicates that .KEEP_STATE is on + * make_state The Name ".make.state", used to check timestamp + * parallel True if currently building in parallel + * parallel_process_cnt Count of parallel processes running + * quest Indicates that make -q is on + * rewrite_statefile Set if we do run a command + * sunpro_dependencies The Name "SUNPRO_DEPENDENCIES", set value + * temp_file_directory Used to form temp fie name + * temp_file_name Set to the name of the temp file + * touch Indicates that make -t is on + */ +static Doname +run_command(register Property line, Boolean) +{ + register Doname result = build_ok; + register Boolean remember_only = false; + register Name target = line->body.line.target; + wchar_t *string; + char tmp_file_path[MAXPATHLEN]; + + if (!line->body.line.is_out_of_date && target->rechecking_target) { + target->rechecking_target = false; + return build_ok; + } + + /* + * Build the command if we know the target is out of date, + * or if we want to check cmd consistency. + */ + if (line->body.line.is_out_of_date || keep_state) { + /* Hack for handling conditional macros in DMake. */ + if (!line->body.line.dont_rebuild_command_used) { + build_command_strings(target, line); + } + } + /* Never mind */ + if (!line->body.line.is_out_of_date) { + return build_ok; + } + /* If quest, then exit(1) because the target is out of date */ + if (quest) { + if (posix) { +#ifdef TEAMWARE_MAKE_CMN + result = execute_parallel(line, true); +#else + result = execute_serial(line); +#endif + } +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + exit_status = 1; +#endif + exit(1); + } + /* We actually had to do something this time */ + rewrite_statefile = commands_done = true; + /* + * If this is an sccs command, we have to do some extra checking + * and possibly complain. If the file can't be gotten because it's + * checked out, we complain and behave as if the command was + * executed eventhough we ignored the command. + */ + if (!touch && + line->body.line.sccs_command && + (target->stat.time != file_doesnt_exist) && + ((target->stat.mode & 0222) != 0)) { + fatal(catgets(catd, 1, 27, "%s is writable so it cannot be sccs gotten"), + target->string_mb); + target->has_complained = remember_only = true; + } + /* + * If KEEP_STATE is on, we make sure we have the timestamp for + * .make.state. If .make.state changes during the command run, + * we reread .make.state after the command. We also setup the + * environment variable that asks utilities to report dependencies. + */ + if (!touch && + keep_state && + !remember_only) { + (void) exists(make_state); + if((strlen(temp_file_directory) == 1) && + (temp_file_directory[0] == '/')) { + tmp_file_path[0] = '\0'; + } else { + strcpy(tmp_file_path, temp_file_directory); + } + sprintf(mbs_buffer, + NOCATGETS("%s/.make.dependency.%08x.%d.%d"), + tmp_file_path, + hostid, + getpid(), + file_number++); + MBSTOWCS(wcs_buffer, mbs_buffer); + Boolean fnd; + temp_file_name = getname_fn(wcs_buffer, FIND_LENGTH, false, &fnd); + temp_file_name->stat.is_file = true; + int len = 2*MAXPATHLEN + strlen(target->string_mb) + 2; + wchar_t *to = string = ALLOC_WC(len); + for (wchar_t *from = wcs_buffer; *from != (int) nul_char; ) { + if (*from == (int) space_char) { + *to++ = (int) backslash_char; + } + *to++ = *from++; + } + *to++ = (int) space_char; + MBSTOWCS(to, target->string_mb); + Name sprodep_name = getname_fn(string, FIND_LENGTH, false, &fnd); + (void) SETVAR(sunpro_dependencies, + sprodep_name, + false); + retmem(string); + } else { + temp_file_name = NULL; + } + + /* + * In case we are interrupted, we need to know what was going on. + */ + current_target = target; + /* + * We also need to be able to save an empty command instead of the + * interrupted one in .make.state. + */ + current_line = line; + if (remember_only) { + /* Empty block!!! */ + } else if (touch) { + result = touch_command(line, target, result); + if (posix) { +#ifdef TEAMWARE_MAKE_CMN + result = execute_parallel(line, true); +#else + result = execute_serial(line); +#endif + } + } else { + /* + * If this is not a touch run, we need to execute the + * proper command(s) for the target. + */ +#ifdef TEAMWARE_MAKE_CMN + if (parallel) { + if (!parallel_ok(target, true)) { + /* + * We are building in parallel, but + * this target must be built in serial. + */ + /* + * If nothing else is building, + * do this one, else wait. + */ + if (parallel_process_cnt == 0) { +#ifdef TEAMWARE_MAKE_CMN + result = execute_parallel(line, true, target->localhost); +#else + result = execute_serial(line); +#endif + } else { + current_target = NULL; + current_line = NULL; +/* + line->body.line.command_used = NULL; + */ + line->body.line.dont_rebuild_command_used = true; + return build_serial; + } + } else { + result = execute_parallel(line, false); + switch (result) { + case build_running: + return build_running; + case build_serial: + if (parallel_process_cnt == 0) { +#ifdef TEAMWARE_MAKE_CMN + result = execute_parallel(line, true, target->localhost); +#else + result = execute_serial(line); +#endif + } else { + current_target = NULL; + current_line = NULL; + target->parallel = false; + line->body.line.command_used = + NULL; + return build_serial; + } + } + } + } else { +#endif +#ifdef TEAMWARE_MAKE_CMN + result = execute_parallel(line, true, target->localhost); +#else + result = execute_serial(line); +#endif +#ifdef TEAMWARE_MAKE_CMN + } +#endif + } + temp_file_name = NULL; + if (report_dependencies_level == 0){ + update_target(line, result); + } + current_target = NULL; + current_line = NULL; + return result; +} + +/* + * execute_serial(line) + * + * Runs thru the command line for the target and + * executes the rules one by one. + * + * Return value: + * The result of the command build + * + * Parameters: + * line The command to execute + * + * Static variables used: + * + * Global variables used: + * continue_after_error -k flag + * do_not_exec_rule -n flag + * report_dependencies -P flag + * silent Don't echo commands before executing + * temp_file_name Temp file for auto dependencies + * vpath_defined If true, translate path for command + */ +Doname +execute_serial(Property line) +{ + int child_pid = 0; +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ + Avo_MToolJobResultMsg *job_result_msg; + RWCollectable *xdr_msg; +#endif + Boolean printed_serial; + Doname result = build_ok; + Cmd_line rule, cmd_tail, command = NULL; + char mbstring[MAXPATHLEN]; + int filed; + Name target = line->body.line.target; + + SEND_MTOOL_MSG( + if (!sent_rsrc_info_msg) { + if (userName[0] == '\0') { + avo_get_user(userName, NULL); + } + if (hostName[0] == '\0') { + strcpy(hostName, avo_hostname()); + } + send_rsrc_info_msg(1, hostName, userName); + sent_rsrc_info_msg = 1; + } + send_job_start_msg(line); + job_result_msg = new Avo_MToolJobResultMsg(); + ); + + target->has_recursive_dependency = false; + // We have to create a copy of the rules chain for processing because + // the original one can be destroyed during .make.state file rereading. + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (command == NULL) { + command = cmd_tail = ALLOC(Cmd_line); + } else { + cmd_tail->next = ALLOC(Cmd_line); + cmd_tail = cmd_tail->next; + } + *cmd_tail = *rule; + } + if (command) { + cmd_tail->next = NULL; + } + for (rule = command; rule != NULL; rule = rule->next) { + if (posix && (touch || quest) && !rule->always_exec) { + continue; + } + if (vpath_defined) { + rule->command_line = + vpath_translation(rule->command_line); + } + /* Echo command line, maybe. */ + if ((rule->command_line->hash.length > 0) && + !silent && + (!rule->silent || do_not_exec_rule) && + (report_dependencies_level == 0)) { + (void) printf("%s\n", rule->command_line->string_mb); + SEND_MTOOL_MSG( + job_result_msg->appendOutput(AVO_STRDUP(rule->command_line->string_mb)); + ); + } + if (rule->command_line->hash.length > 0) { + SEND_MTOOL_MSG( + (void) sprintf(mbstring, + NOCATGETS("%s/make.stdout.%d.%d.XXXXXX"), + tmpdir, + getpid(), + file_number++); + + int tmp_fd = mkstemp(mbstring); + if(tmp_fd) { + (void) close(tmp_fd); + } + + stdout_file = strdup(mbstring); + stderr_file = NULL; + child_pid = pollResults(stdout_file, + (char *)NULL, + (char *)NULL); + ); + /* Do assignment if command line prefixed with "=" */ + if (rule->assign) { + result = build_ok; + do_assign(rule->command_line, target); + } else if (report_dependencies_level == 0) { + /* Execute command line. */ +#ifdef DISTRIBUTED + setvar_envvar((Avo_DoJobMsg *)NULL); +#else + setvar_envvar(); +#endif + result = dosys(rule->command_line, + (Boolean) rule->ignore_error, + (Boolean) rule->make_refd, + /* ds 98.04.23 bug #4085164. make should always show error messages */ + false, + /* BOOLEAN(rule->silent && + rule->ignore_error), */ + (Boolean) rule->always_exec, + target, + send_mtool_msgs); +#ifdef NSE + nse_did_recursion= false; +#endif + check_state(temp_file_name); +#ifdef NSE + nse_check_cd(line); +#endif + } + SEND_MTOOL_MSG( + append_job_result_msg(job_result_msg); + if (child_pid > 0) { + kill(child_pid, SIGUSR1); + while (!((waitpid(child_pid, 0, 0) == -1) + && (errno == ECHILD))); + } + child_pid = 0; + (void) unlink(stdout_file); + retmem_mb(stdout_file); + stdout_file = NULL; + ); + } else { + result = build_ok; + } + if (result == build_failed) { + if (silent || rule->silent) { + (void) printf(catgets(catd, 1, 242, "The following command caused the error:\n%s\n"), + rule->command_line->string_mb); + SEND_MTOOL_MSG( + job_result_msg->appendOutput(AVO_STRDUP(catgets(catd, 1, 243, "The following command caused the error:"))); + job_result_msg->appendOutput(AVO_STRDUP(rule->command_line->string_mb)); + ); + } + if (!rule->ignore_error && !ignore_errors) { + if (!continue_after_error) { + SEND_MTOOL_MSG( + job_result_msg->setResult(job_msg_id, (result == build_ok) ? 0 : 1, DONE); + xdr_msg = (RWCollectable*) + job_result_msg; + xdr(&xdrs, xdr_msg); + (void) fflush(mtool_msgs_fp); + delete job_result_msg; + ); + fatal(catgets(catd, 1, 244, "Command failed for target `%s'"), + target->string_mb); + } + /* + * Make sure a failing command is not + * saved in .make.state. + */ + line->body.line.command_used = NULL; + break; + } else { + result = build_ok; + } + } + } + for (rule = command; rule != NULL; rule = cmd_tail) { + cmd_tail = rule->next; + free(rule); + } + command = NULL; + SEND_MTOOL_MSG( + job_result_msg->setResult(job_msg_id, (result == build_ok) ? 0 : 1, DONE); + xdr_msg = (RWCollectable*) job_result_msg; + xdr(&xdrs, xdr_msg); + (void) fflush(mtool_msgs_fp); + + delete job_result_msg; + ); + if (temp_file_name != NULL) { + free_name(temp_file_name); + } + temp_file_name = NULL; + + Property spro = get_prop(sunpro_dependencies->prop, macro_prop); + if(spro != NULL) { + Name val = spro->body.macro.value; + if(val != NULL) { + free_name(val); + spro->body.macro.value = NULL; + } + } + spro = get_prop(sunpro_dependencies->prop, env_mem_prop); + if(spro) { + char *val = spro->body.env_mem.value; + if(val != NULL) { + /* + * Do not return memory allocated for SUNPRO_DEPENDENCIES + * It will be returned in setvar_daemon() in macro.cc + */ + // retmem_mb(val); + spro->body.env_mem.value = NULL; + } + } + + return result; +} + + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ + +/* + * Create and send an Avo_MToolRsrcInfoMsg. + */ +void +send_rsrc_info_msg(int max_jobs, char *hostname, char *username) +{ + static int first = 1; + Avo_MToolRsrcInfoMsg *msg; + RWSlistCollectables server_list; + Avo_ServerState *server_state; + RWCollectable *xdr_msg; + + if (!first) { + return; + } + first = 0; + + create_xdrs_ptr(); + + server_state = new Avo_ServerState(max_jobs, hostname, username); + server_list.append(server_state); + msg = new Avo_MToolRsrcInfoMsg(&server_list); + + xdr_msg = (RWCollectable *)msg; + xdr(get_xdrs_ptr(), xdr_msg); + (void) fflush(get_mtool_msgs_fp()); + + delete server_state; + delete msg; +} + +/* + * Create and send an Avo_MToolJobStartMsg. + */ +void +send_job_start_msg(Property line) +{ + int cmd_options = 0; + Avo_MToolJobStartMsg *msg; + Cmd_line rule; + Name target = line->body.line.target; + RWCollectable *xdr_msg; + + if (userName[0] == '\0') { + avo_get_user(userName, NULL); + } + if (hostName[0] == '\0') { + strcpy(hostName, avo_hostname()); + } + + msg = new Avo_MToolJobStartMsg(); + msg->setJobId(++job_msg_id); + msg->setTarget(AVO_STRDUP(target->string_mb)); + msg->setHost(AVO_STRDUP(hostName)); + msg->setUser(AVO_STRDUP(userName)); + + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (posix && (touch || quest) && !rule->always_exec) { + continue; + } + if (vpath_defined) { + rule->command_line = + vpath_translation(rule->command_line); + } + cmd_options = 0; + if (rule->ignore_error || ignore_errors) { + cmd_options |= ignore_mask; + } + if (rule->silent || silent) { + cmd_options |= silent_mask; + } + if (rule->command_line->meta) { + cmd_options |= meta_mask; + } + if (!touch && (rule->command_line->hash.length > 0)) { + msg->appendCmd(new Avo_DmakeCommand(rule->command_line->string_mb, cmd_options)); + } + } + + xdr_msg = (RWCollectable*) msg; + xdr(&xdrs, xdr_msg); + (void) fflush(mtool_msgs_fp); + +/* tolik, 08/39/2002. + I commented out this code because it causes using unallocated memory. + delete msg; +*/ +} + +/* + * Append the stdout/err to Avo_MToolJobResultMsg. + */ +static void +append_job_result_msg(Avo_MToolJobResultMsg *job_result_msg) +{ + FILE *fp; + char line[MAXPATHLEN]; + char stdout_file2[MAXPATHLEN]; + + if (stdout_file != NULL) { + fp = fopen(stdout_file, "r"); + if (fp == NULL) { + /* Hmmm... what should we do here? */ + warning(catgets(catd, 1, 326, "fopen() of stdout_file failed. Output may be lost")); + return; + } + while (fgets(line, MAXPATHLEN, fp) != NULL) { + if (line[strlen(line) - 1] == '\n') { + line[strlen(line) - 1] = '\0'; + } + job_result_msg->appendOutput(AVO_STRDUP(line)); + } + (void) fclose(fp); + us_sleep(STAT_RETRY_SLEEP_TIME); + } else { + /* Hmmm... stdout_file shouldn't be NULL */ + warning(catgets(catd, 1, 327, "Internal stdout_file variable shouldn't be NULL. Output may be lost")); + } +} +#endif /* TEAMWARE_MAKE_CMN */ + +/* + * vpath_translation(cmd) + * + * Translates one command line by + * checking each word. If the word has an alias it is translated. + * + * Return value: + * The translated command + * + * Parameters: + * cmd Command to translate + * + * Global variables used: + */ +Name +vpath_translation(register Name cmd) +{ + wchar_t buffer[STRING_BUFFER_LENGTH]; + String_rec new_cmd; + wchar_t *p; + wchar_t *start; + + if (!vpath_defined || (cmd == NULL) || (cmd->hash.length == 0)) { + return cmd; + } + INIT_STRING_FROM_STACK(new_cmd, buffer); + + Wstring wcb(cmd); + p = wcb.get_string(); + + while (*p != (int) nul_char) { + while (iswspace(*p) && (*p != (int) nul_char)) { + append_char(*p++, &new_cmd); + } + start = p; + while (!iswspace(*p) && (*p != (int) nul_char)) { + p++; + } + cmd = GETNAME(start, p - start); + if (cmd->has_vpath_alias_prop) { + cmd = get_prop(cmd->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + APPEND_NAME(cmd, + &new_cmd, + (int) cmd->hash.length); + } else { + append_string(start, &new_cmd, p - start); + } + } + cmd = GETNAME(new_cmd.buffer.start, FIND_LENGTH); + if (new_cmd.free_after_use) { + retmem(new_cmd.buffer.start); + } + return cmd; +} + +/* + * check_state(temp_file_name) + * + * Reads and checks the state changed by the previously executed command. + * + * Parameters: + * temp_file_name The auto dependency temp file + * + * Global variables used: + */ +void +check_state(Name temp_file_name) +{ + if (!keep_state) { + return; + } + + /* + * Then read the temp file that now might + * contain dependency reports from utilities + */ + read_dependency_file(temp_file_name); + + /* + * And reread .make.state if it + * changed (the command ran recursive makes) + */ + check_read_state_file(); + if (temp_file_name != NULL) { + (void) unlink(temp_file_name->string_mb); + } +} + +/* + * read_dependency_file(filename) + * + * Read the temp file used for reporting dependencies to make + * + * Parameters: + * filename The name of the file with the state info + * + * Global variables used: + * makefile_type The type of makefile being read + * read_trace_level Debug flag + * temp_file_number The always increasing number for unique files + * trace_reader Debug flag + */ +static void +read_dependency_file(register Name filename) +{ + register Makefile_type save_makefile_type; + + if (filename == NULL) { + return; + } + filename->stat.time = file_no_time; + if (exists(filename) > file_doesnt_exist) { + save_makefile_type = makefile_type; + makefile_type = reading_cpp_file; + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(filename, + false, + false, + false, + false, + false, + false); + trace_reader = false; + makefile_type = save_makefile_type; + } +} + +/* + * check_read_state_file() + * + * Check if .make.state has changed + * If it has we reread it + * + * Parameters: + * + * Global variables used: + * make_state Make state file name + * makefile_type Type of makefile being read + * read_trace_level Debug flag + * trace_reader Debug flag + */ +static void +check_read_state_file(void) +{ + timestruc_t previous = make_state->stat.time; + register Makefile_type save_makefile_type; + register Property makefile; + + make_state->stat.time = file_no_time; + if ((exists(make_state) == file_doesnt_exist) || + (make_state->stat.time == previous)) { + return; + } + save_makefile_type = makefile_type; + makefile_type = rereading_statefile; + /* Make sure we clear the old cached contents of .make.state */ + makefile = maybe_append_prop(make_state, makefile_prop); + if (makefile->body.makefile.contents != NULL) { + retmem(makefile->body.makefile.contents); + makefile->body.makefile.contents = NULL; + } + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + makefile_type = save_makefile_type; +} + +/* + * do_assign(line, target) + * + * Handles runtime assignments for command lines prefixed with "=". + * + * Parameters: + * line The command that contains an assignment + * target The Name of the target, used for error reports + * + * Global variables used: + * assign_done Set to indicate doname needs to reprocess + */ +static void +do_assign(register Name line, register Name target) +{ + Wstring wcb(line); + register wchar_t *string = wcb.get_string(); + register wchar_t *equal; + register Name name; + register Boolean append = false; + + /* + * If any runtime assignments are done, doname() must reprocess all + * targets in the future since the macro values used to build the + * command lines for the targets might have changed. + */ + assign_done = true; + /* Skip white space. */ + while (iswspace(*string)) { + string++; + } + equal = string; + /* Find "+=" or "=". */ + while (!iswspace(*equal) && + (*equal != (int) plus_char) && + (*equal != (int) equal_char)) { + equal++; + } + /* Internalize macro name. */ + name = GETNAME(string, equal - string); + /* Skip over "+=" "=". */ + while (!((*equal == (int) nul_char) || + (*equal == (int) equal_char) || + (*equal == (int) plus_char))) { + equal++; + } + switch (*equal) { + case nul_char: + fatal(catgets(catd, 1, 31, "= expected in rule `%s' for target `%s'"), + line->string_mb, + target->string_mb); + case plus_char: + append = true; + equal++; + break; + } + equal++; + /* Skip over whitespace in front of value. */ + while (iswspace(*equal)) { + equal++; + } + /* Enter new macro value. */ + enter_equal(name, + GETNAME(equal, wcb.get_string() + line->hash.length - equal), + append); +} + +/* + * build_command_strings(target, line) + * + * Builds the command string to used when + * building a target. If the string is different from the previous one + * is_out_of_date is set. + * + * Parameters: + * target Target to build commands for + * line Where to stuff result + * + * Global variables used: + * c_at The Name "@", used to set macro value + * command_changed Set if command is different from old + * debug_level Should we trace activities? + * do_not_exec_rule Always echo when running -n + * empty_name The Name "", used for empty rule + * funny Semantics of characters + * ignore_errors Used to init field for line + * is_conditional Set to false befor evaling macro, checked + * after expanding macros + * keep_state Indicates that .KEEP_STATE is on + * make_word_mentioned Set by macro eval, inits field for cmd + * query The Name "?", used to set macro value + * query_mentioned Set by macro eval, inits field for cmd + * recursion_level Used for tracing + * silent Used to init field for line + */ +static void +build_command_strings(Name target, register Property line) +{ + String_rec command_line; + register Cmd_line command_template = line->body.line.command_template; + register Cmd_line *insert = &line->body.line.command_used; + register Cmd_line used = *insert; + wchar_t buffer[STRING_BUFFER_LENGTH]; + wchar_t *start; + Name new_command_line; + register Boolean new_command_longer = false; + register Boolean ignore_all_command_dependency = true; + Property member; + static Name less_name; + static Name percent_name; + static Name star; + Name tmp_name; + + if (less_name == NULL) { + MBSTOWCS(wcs_buffer, "<"); + less_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "%"); + percent_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "*"); + star = GETNAME(wcs_buffer, FIND_LENGTH); + } + + /* We have to check if a target depends on conditional macros */ + /* Targets that do must be reprocessed by doname() each time around */ + /* since the macro values used when building the target might have */ + /* changed */ + conditional_macro_used = false; + /* If we are building a lib.a(member) target $@ should be bound */ + /* to lib.a */ + if (target->is_member && + ((member = get_prop(target->prop, member_prop)) != NULL)) { + target = member->body.member.library; + } + /* If we are building a "::" help target $@ should be bound to */ + /* the real target name */ + /* A lib.a(member) target is never :: */ + if (target->has_target_prop) { + target = get_prop(target->prop, target_prop)-> + body.target.target; + } + /* Bind the magic macros that make supplies */ + tmp_name = target; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(c_at, tmp_name, false); + + tmp_name = line->body.line.star; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(star, tmp_name, false); + + tmp_name = line->body.line.less; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(less_name, tmp_name, false); + + tmp_name = line->body.line.percent; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(percent_name, tmp_name, false); + + /* $? is seldom used and it is expensive to build */ + /* so we store the list form and build the string on demand */ + Chain query_list = NULL; + Chain *query_list_tail = &query_list; + + for (Chain ch = line->body.line.query; ch != NULL; ch = ch->next) { + *query_list_tail = ALLOC(Chain); + (*query_list_tail)->name = ch->name; + if ((*query_list_tail)->name->has_vpath_alias_prop) { + (*query_list_tail)->name = + get_prop((*query_list_tail)->name->prop, + vpath_alias_prop)->body.vpath_alias.alias; + } + (*query_list_tail)->next = NULL; + query_list_tail = &(*query_list_tail)->next; + } + (void) setvar_daemon(query, + (Name) query_list, + false, + chain_daemon, + false, + debug_level); + + /* build $^ */ + Chain hat_list = NULL; + Chain *hat_list_tail = &hat_list; + + for (Dependency dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + /* skip automatic dependencies */ + if (!dependency->automatic) { + if ((dependency->name != force) && + (dependency->stale == false)) { + *hat_list_tail = ALLOC(Chain); + + if (dependency->name->is_member && + (get_prop(dependency->name->prop, member_prop) != NULL)) { + (*hat_list_tail)->name = + get_prop(dependency->name->prop, + member_prop)->body.member.member; + } else { + (*hat_list_tail)->name = dependency->name; + } + + if((*hat_list_tail)->name != NULL) { + if ((*hat_list_tail)->name->has_vpath_alias_prop) { + (*hat_list_tail)->name = + get_prop((*hat_list_tail)->name->prop, + vpath_alias_prop)->body.vpath_alias.alias; + } + } + + (*hat_list_tail)->next = NULL; + hat_list_tail = &(*hat_list_tail)->next; + } + } + } + (void) setvar_daemon(hat, + (Name) hat_list, + false, + chain_daemon, + false, + debug_level); + +/* We have two command sequences we need to handle */ +/* The old one that we probably read from .make.state */ +/* and the new one we are building that will replace the old one */ +/* Even when KEEP_STATE is not on we build a new command sequence and store */ +/* it in the line prop. This command sequence is then executed by */ +/* run_command(). If KEEP_STATE is on it is also later written to */ +/* .make.state. The routine replaces the old command line by line with the */ +/* new one trying to reuse Cmd_lines */ + + /* If there is no old command_used we have to start creating */ + /* Cmd_lines to keep the new cmd in */ + if (used == NULL) { + new_command_longer = true; + *insert = used = ALLOC(Cmd_line); + used->next = NULL; + used->command_line = NULL; + insert = &used->next; + } + /* Run thru the template for the new command and build the expanded */ + /* new command lines */ + for (; + command_template != NULL; + command_template = command_template->next, insert = &used->next, used = *insert) { + /* If there is no old command_used Cmd_line we need to */ + /* create one and say that cmd consistency failed */ + if (used == NULL) { + new_command_longer = true; + *insert = used = ALLOC(Cmd_line); + used->next = NULL; + used->command_line = empty_name; + } + /* Prepare the Cmd_line for the processing */ + /* The command line prefixes "@-=?" are stripped and that */ + /* information is saved in the Cmd_line */ + used->assign = false; + used->ignore_error = ignore_errors; + used->silent = silent; + used->always_exec = false; + /* Expand the macros in the command line */ + INIT_STRING_FROM_STACK(command_line, buffer); + make_word_mentioned = + query_mentioned = + false; + expand_value(command_template->command_line, &command_line, true); + /* If the macro $(MAKE) is mentioned in the command */ + /* "make -n" runs actually execute the command */ + used->make_refd = make_word_mentioned; + used->ignore_command_dependency = query_mentioned; + /* Strip the prefixes */ + start = command_line.buffer.start; + for (; + iswspace(*start) || + (get_char_semantics_value(*start) & (int) command_prefix_sem); + start++) { + switch (*start) { + case question_char: + used->ignore_command_dependency = true; + break; + case exclam_char: + used->ignore_command_dependency = false; + break; + case equal_char: + used->assign = true; + break; + case hyphen_char: + used->ignore_error = true; + break; + case at_char: + if (!do_not_exec_rule) { + used->silent = true; + } + break; + case plus_char: + if(posix) { + used->always_exec = true; + } + break; + } + } + /* If all command lines of the template are prefixed with "?"*/ + /* the VIRTUAL_ROOT is not used for cmd consistency checks */ + if (!used->ignore_command_dependency) { + ignore_all_command_dependency = false; + } + /* Internalize the expanded and stripped command line */ + new_command_line = GETNAME(start, FIND_LENGTH); + if ((used->command_line == NULL) && + (line->body.line.sccs_command)) { + used->command_line = new_command_line; + new_command_longer = false; + } + /* Compare it with the old one for command consistency */ + if (used->command_line != new_command_line) { + Name vpath_translated = vpath_translation(new_command_line); + if (keep_state && + !used->ignore_command_dependency && (vpath_translated != used->command_line)) { + if (debug_level > 0) { + if (used->command_line != NULL + && *used->command_line->string_mb != + '\0') { + (void) printf(catgets(catd, 1, 32, "%*sBuilding %s because new command \n\t%s\n%*sdifferent from old\n\t%s\n"), + recursion_level, + "", + target->string_mb, + vpath_translated->string_mb, + recursion_level, + "", + used-> + command_line-> + string_mb); + } else { + (void) printf(catgets(catd, 1, 33, "%*sBuilding %s because new command \n\t%s\n%*sdifferent from empty old command\n"), + recursion_level, + "", + target->string_mb, + vpath_translated->string_mb, + recursion_level, + ""); + } + } + command_changed = true; + line->body.line.is_out_of_date = true; + } + used->command_line = new_command_line; + } + if (command_line.free_after_use) { + retmem(command_line.buffer.start); + } + } + /* Check if the old command is longer than the new for */ + /* command consistency */ + if (used != NULL) { + *insert = NULL; + if (keep_state && + !ignore_all_command_dependency) { + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 34, "%*sBuilding %s because new command shorter than old\n"), + recursion_level, + "", + target->string_mb); + } + command_changed = true; + line->body.line.is_out_of_date = true; + } + } + /* Check if the new command is longer than the old command for */ + /* command consistency */ + if (new_command_longer && + !ignore_all_command_dependency && + keep_state) { + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 35, "%*sBuilding %s because new command longer than old\n"), + recursion_level, + "", + target->string_mb); + } + command_changed = true; + line->body.line.is_out_of_date = true; + } + /* Unbind the magic macros */ + (void) SETVAR(c_at, (Name) NULL, false); + (void) SETVAR(star, (Name) NULL, false); + (void) SETVAR(less_name, (Name) NULL, false); + (void) SETVAR(percent_name, (Name) NULL, false); + (void) SETVAR(query, (Name) NULL, false); + if (query_list != NULL) { + delete_query_chain(query_list); + } + (void) SETVAR(hat, (Name) NULL, false); + if (hat_list != NULL) { + delete_query_chain(hat_list); + } + + if (conditional_macro_used) { + target->conditional_macro_list = cond_macro_list; + cond_macro_list = NULL; + target->depends_on_conditional = true; + } +} + +/* + * touch_command(line, target, result) + * + * If this is an "make -t" run we do this. + * We touch all targets in the target group ("foo + fie:") if any. + * + * Return value: + * Indicates if the command failed or not + * + * Parameters: + * line The command line to update + * target The target we are touching + * result Initial value for the result we return + * + * Global variables used: + * do_not_exec_rule Indicates that -n is on + * silent Do not echo commands + */ +static Doname +touch_command(register Property line, register Name target, Doname result) +{ + Name name; + register Chain target_group; + String_rec touch_string; + wchar_t buffer[MAXPATHLEN]; + Name touch_cmd; + Cmd_line rule; + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ + Avo_MToolJobResultMsg *job_result_msg; + RWCollectable *xdr_msg; + int child_pid = 0; + wchar_t string[MAXPATHLEN]; + char mbstring[MAXPATHLEN]; + int filed; +#endif + + SEND_MTOOL_MSG( + if (!sent_rsrc_info_msg) { + if (userName[0] == '\0') { + avo_get_user(userName, NULL); + } + if (hostName[0] == '\0') { + strcpy(hostName, avo_hostname()); + } + send_rsrc_info_msg(1, hostName, userName); + sent_rsrc_info_msg = 1; + } + send_job_start_msg(line); + job_result_msg = new Avo_MToolJobResultMsg(); + ); + for (name = target, target_group = NULL; name != NULL;) { + if (!name->is_member) { + /* + * Build a touch command that can be passed + * to dosys(). If KEEP_STATE is on, "make -t" + * will save the proper command, not the + * "touch" in .make.state. + */ + INIT_STRING_FROM_STACK(touch_string, buffer); + MBSTOWCS(wcs_buffer, NOCATGETS("touch ")); + append_string(wcs_buffer, &touch_string, FIND_LENGTH); + touch_cmd = name; + if (name->has_vpath_alias_prop) { + touch_cmd = get_prop(name->prop, + vpath_alias_prop)-> + body.vpath_alias.alias; + } + APPEND_NAME(touch_cmd, + &touch_string, + FIND_LENGTH); + touch_cmd = GETNAME(touch_string.buffer.start, + FIND_LENGTH); + if (touch_string.free_after_use) { + retmem(touch_string.buffer.start); + } + if (!silent || + do_not_exec_rule && + (target_group == NULL)) { + (void) printf("%s\n", touch_cmd->string_mb); + SEND_MTOOL_MSG( + job_result_msg->appendOutput(AVO_STRDUP(touch_cmd->string_mb)); + ); + } + /* Run the touch command, or simulate it */ + if (!do_not_exec_rule) { + + SEND_MTOOL_MSG( + (void) sprintf(mbstring, + NOCATGETS("%s/make.stdout.%d.%d.XXXXXX"), + tmpdir, + getpid(), + file_number++); + + int tmp_fd = mkstemp(mbstring); + if(tmp_fd) { + (void) close(tmp_fd); + } + + stdout_file = strdup(mbstring); + stderr_file = NULL; + child_pid = pollResults(stdout_file, + (char *)NULL, + (char *)NULL); + ); + + result = dosys(touch_cmd, + false, + false, + false, + false, + name, + send_mtool_msgs); + + SEND_MTOOL_MSG( + append_job_result_msg(job_result_msg); + if (child_pid > 0) { + kill(child_pid, SIGUSR1); + while (!((waitpid(child_pid, 0, 0) == -1) + && (errno == ECHILD))); + } + child_pid = 0; + (void) unlink(stdout_file); + retmem_mb(stdout_file); + stdout_file = NULL; + ); + + } else { + result = build_ok; + } + } else { + result = build_ok; + } + if (target_group == NULL) { + target_group = line->body.line.target_group; + } else { + target_group = target_group->next; + } + if (target_group != NULL) { + name = target_group->name; + } else { + name = NULL; + } + } + SEND_MTOOL_MSG( + job_result_msg->setResult(job_msg_id, (result == build_ok) ? 0 : 1, DONE); + xdr_msg = (RWCollectable*) job_result_msg; + xdr(&xdrs, xdr_msg); + (void) fflush(mtool_msgs_fp); + delete job_result_msg; + ); + return result; +} + +/* + * update_target(line, result) + * + * updates the status of a target after executing its commands. + * + * Parameters: + * line The command line block to update + * result Indicates that build is OK so can update + * + * Global variables used: + * do_not_exec_rule Indicates that -n is on + * touch Fake the new timestamp if we are just touching + */ +void +update_target(Property line, Doname result) +{ + Name target; + Chain target_group; + Property line2; + timestruc_t old_stat_time; + Property member; + + /* + * [tolik] Additional fix for bug 1063790. It was fixed + * for serial make long ago, but DMake dumps core when + * target is a symlink and sccs file is newer then target. + * In this case, finish_children() calls update_target() + * with line==NULL. + */ + if(line == NULL) { + /* XXX. Should we do anything here? */ + return; + } + + target = line->body.line.target; + + if ((result == build_ok) && (line->body.line.command_used != NULL)) { + if (do_not_exec_rule || + touch || + (target->is_member && + (line->body.line.command_template != NULL) && + (line->body.line.command_template->command_line->string_mb[0] == 0) && + (line->body.line.command_template->next == NULL))) { + /* If we are simulating execution we need to fake a */ + /* new timestamp for the target we didnt build */ + target->stat.time = file_max_time; + } else { + /* + * If we really built the target we read the new + * timestamp. + * Fix for bug #1110906: if .c file is newer than + * the corresponding .o file which is in an archive + * file, make will compile the .c file but it won't + * update the object in the .a file. + */ + old_stat_time = target->stat.time; + target->stat.time = file_no_time; + (void) exists(target); + if ((target->is_member) && + (target->stat.time == old_stat_time)) { + member = get_prop(target->prop, member_prop); + if (member != NULL) { + target->stat.time = member->body.member.library->stat.time; + target->stat.time.tv_sec++; + } + } + } + /* If the target is part of a group we need to propagate the */ + /* result of the run to all members */ + for (target_group = line->body.line.target_group; + target_group != NULL; + target_group = target_group->next) { + target_group->name->stat.time = target->stat.time; + line2 = maybe_append_prop(target_group->name, + line_prop); + line2->body.line.command_used = + line->body.line.command_used; + line2->body.line.target = target_group->name; + } + } + target->has_built = true; +} + +/* + * sccs_get(target, command) + * + * Figures out if it possible to sccs get a file + * and builds the command to do it if it is. + * + * Return value: + * Indicates if sccs get failed or not + * + * Parameters: + * target Target to get + * command Where to deposit command to use + * + * Global variables used: + * debug_level Should we trace activities? + * recursion_level Used for tracing + * sccs_get_rule The rule to used for sccs getting + */ +static Doname +sccs_get(register Name target, register Property *command) +{ + register int result; + char link[MAXPATHLEN]; + String_rec string; + wchar_t name[MAXPATHLEN]; + register wchar_t *p; + timestruc_t sccs_time; + register Property line; + int sym_link_depth = 0; + + /* For sccs, we need to chase symlinks. */ + while (target->stat.is_sym_link) { + if (sym_link_depth++ > 90) { + fatal(catgets(catd, 1, 95, "Can't read symbolic link `%s': Number of symbolic links encountered during path name traversal exceeds 90."), + target->string_mb); + } + /* Read the value of the link. */ + result = readlink_vroot(target->string_mb, + link, + sizeof(link), + NULL, + VROOT_DEFAULT); + if (result == -1) { + fatal(catgets(catd, 1, 36, "Can't read symbolic link `%s': %s"), + target->string_mb, errmsg(errno)); + } + link[result] = 0; + /* Use the value to build the proper filename. */ + INIT_STRING_FROM_STACK(string, name); + + Wstring wcb(target); + if ((link[0] != slash_char) && + ((p = (wchar_t *) wsrchr(wcb.get_string(), slash_char)) != NULL)) { + append_string(wcb.get_string(), &string, p - wcb.get_string() + 1); + } + append_string(link, &string, result); + /* Replace the old name with the translated name. */ + target = normalize_name(string.buffer.start, string.text.p - string.buffer.start); + (void) exists(target); + if (string.free_after_use) { + retmem(string.buffer.start); + } + } + + /* + * read_dir() also reads the ?/SCCS dir and saves information + * about which files have SCSC/s. files. + */ + if (target->stat.has_sccs == DONT_KNOW_SCCS) { + read_directory_of_file(target); + } + switch (target->stat.has_sccs) { + case DONT_KNOW_SCCS: + /* We dont know by now there is no SCCS/s.* */ + target->stat.has_sccs = NO_SCCS; + case NO_SCCS: + /* + * If there is no SCCS/s.* but the plain file exists, + * we say things are OK. + */ + if (target->stat.time > file_doesnt_exist) { + return build_ok; + } + /* If we cant find the plain file, we give up. */ + return build_dont_know; + case HAS_SCCS: + /* + * Pay dirt. We now need to figure out if the plain file + * is out of date relative to the SCCS/s.* file. + */ + sccs_time = exists(get_prop(target->prop, + sccs_prop)->body.sccs.file); + break; + } + + if ((!target->has_complained && + (sccs_time != file_doesnt_exist) && + (sccs_get_rule != NULL))) { + /* only checking */ + if (command == NULL) { + return build_ok; + } + /* + * We provide a command line for the target. The line is a + * "sccs get" command from default.mk. + */ + line = maybe_append_prop(target, line_prop); + *command = line; + if (sccs_time > target->stat.time) { + /* + * And only if the plain file is out of date do we + * request execution of the command. + */ + line->body.line.is_out_of_date = true; + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 37, "%*sSccs getting %s because s. file is younger than source file\n"), + recursion_level, + "", + target->string_mb); + } + } + line->body.line.sccs_command = true; + line->body.line.command_template = sccs_get_rule; + if(!svr4 && (!allrules_read || posix)) { + if((target->prop) && + (target->prop->body.sccs.file) && + (target->prop->body.sccs.file->string_mb)) { + if((strlen(target->prop->body.sccs.file->string_mb) == + strlen(target->string_mb) + 2) && + (target->prop->body.sccs.file->string_mb[0] == 's') && + (target->prop->body.sccs.file->string_mb[1] == '.')) { + + line->body.line.command_template = get_posix_rule; + } + } + } + line->body.line.target = target; + /* + * Also make sure the rule is build with $* and $< + * bound properly. + */ + line->body.line.star = NULL; + line->body.line.less = NULL; + line->body.line.percent = NULL; + return build_ok; + } + return build_dont_know; +} + +/* + * read_directory_of_file(file) + * + * Reads the directory the specified file lives in. + * + * Parameters: + * file The file we need to read dir for + * + * Global variables used: + * dot The Name ".", used as the default dir + */ +void +read_directory_of_file(register Name file) +{ + + Wstring file_string(file); + wchar_t * wcb = file_string.get_string(); + wchar_t usr_include_buf[MAXPATHLEN]; + wchar_t usr_include_sys_buf[MAXPATHLEN]; + + register Name directory = dot; + register wchar_t *p = (wchar_t *) wsrchr(wcb, + (int) slash_char); + register int length = p - wcb; + static Name usr_include; + static Name usr_include_sys; + + if (usr_include == NULL) { + MBSTOWCS(usr_include_buf, NOCATGETS("/usr/include")); + usr_include = GETNAME(usr_include_buf, FIND_LENGTH); + MBSTOWCS(usr_include_sys_buf, NOCATGETS("/usr/include/sys")); + usr_include_sys = GETNAME(usr_include_sys_buf, FIND_LENGTH); + } + + /* + * If the filename contains a "/" we have to extract the path + * Else the path defaults to ".". + */ + if (p != NULL) { + /* + * Check some popular directories first to possibly + * save time. Compare string length first to gain speed. + */ + if ((usr_include->hash.length == length) && + IS_WEQUALN(usr_include_buf, + wcb, + length)) { + directory = usr_include; + } else if ((usr_include_sys->hash.length == length) && + IS_WEQUALN(usr_include_sys_buf, + wcb, + length)) { + directory = usr_include_sys; + } else { + directory = GETNAME(wcb, length); + } + } + (void) read_dir(directory, + (wchar_t *) NULL, + (Property) NULL, + (wchar_t *) NULL); +} + +/* + * add_pattern_conditionals(target) + * + * Scan the list of conditionals defined for pattern targets and add any + * that match this target to its list of conditionals. + * + * Parameters: + * target The target we should add conditionals for + * + * Global variables used: + * conditionals The list of pattern conditionals + */ +static void +add_pattern_conditionals(register Name target) +{ + register Property conditional; + Property new_prop; + Property *previous; + Name_rec dummy; + wchar_t *pattern; + wchar_t *percent; + int length; + + Wstring wcb(target); + Wstring wcb1; + + for (conditional = get_prop(conditionals->prop, conditional_prop); + conditional != NULL; + conditional = get_prop(conditional->next, conditional_prop)) { + wcb1.init(conditional->body.conditional.target); + pattern = wcb1.get_string(); + if (pattern[1] != 0) { + percent = (wchar_t *) wschr(pattern, (int) percent_char); + if (!wcb.equaln(pattern, percent-pattern) || + !IS_WEQUAL(wcb.get_string(wcb.length()-wslen(percent+1)), percent+1)) { + continue; + } + } + for (previous = &target->prop; + *previous != NULL; + previous = &(*previous)->next) { + if (((*previous)->type == conditional_prop) && + ((*previous)->body.conditional.sequence > + conditional->body.conditional.sequence)) { + break; + } + } + if (*previous == NULL) { + new_prop = append_prop(target, conditional_prop); + } else { + dummy.prop = NULL; + new_prop = append_prop(&dummy, conditional_prop); + new_prop->next = *previous; + *previous = new_prop; + } + target->conditional_cnt++; + new_prop->body.conditional = conditional->body.conditional; + } +} + +/* + * set_locals(target, old_locals) + * + * Sets any conditional macros for the target. + * Each target carries a possibly empty set of conditional properties. + * + * Parameters: + * target The target to set conditional macros for + * old_locals Space to store old values in + * + * Global variables used: + * debug_level Should we trace activity? + * is_conditional We need to preserve this value + * recursion_level Used for tracing + */ +void +set_locals(register Name target, register Property old_locals) +{ + register Property conditional; + register int i; + register Boolean saved_conditional_macro_used; + Chain cond_name; + Chain cond_chain; + +#ifdef DISTRIBUTED + if (target->dont_activate_cond_values) { + return; + } +#endif + + saved_conditional_macro_used = conditional_macro_used; + + /* Scan the list of conditional properties and apply each one */ + for (conditional = get_prop(target->prop, conditional_prop), i = 0; + conditional != NULL; + conditional = get_prop(conditional->next, conditional_prop), + i++) { + /* Save the old value */ + old_locals[i].body.macro = + maybe_append_prop(conditional->body.conditional.name, + macro_prop)->body.macro; + if (debug_level > 1) { + (void) printf(catgets(catd, 1, 38, "%*sActivating conditional value: "), + recursion_level, + ""); + } + /* Set the conditional value. Macros are expanded when the */ + /* macro is refd as usual */ + if ((conditional->body.conditional.name != virtual_root) || + (conditional->body.conditional.value != virtual_root)) { + (void) SETVAR(conditional->body.conditional.name, + conditional->body.conditional.value, + (Boolean) conditional->body.conditional.append); + } + cond_name = ALLOC(Chain); + cond_name->name = conditional->body.conditional.name; + } + /* Put this target on the front of the chain of conditional targets */ + cond_chain = ALLOC(Chain); + cond_chain->name = target; + cond_chain->next = conditional_targets; + conditional_targets = cond_chain; + conditional_macro_used = saved_conditional_macro_used; +} + +/* + * reset_locals(target, old_locals, conditional, index) + * + * Removes any conditional macros for the target. + * + * Parameters: + * target The target we are retoring values for + * old_locals The values to restore + * conditional The first conditional block for the target + * index into the old_locals vector + * Global variables used: + * debug_level Should we trace activities? + * recursion_level Used for tracing + */ +void +reset_locals(register Name target, register Property old_locals, register Property conditional, register int index) +{ + register Property this_conditional; + Chain cond_chain; + +#ifdef DISTRIBUTED + if (target->dont_activate_cond_values) { + return; + } +#endif + + /* Scan the list of conditional properties and restore the old value */ + /* to each one Reverse the order relative to when we assigned macros */ + this_conditional = get_prop(conditional->next, conditional_prop); + if (this_conditional != NULL) { + reset_locals(target, old_locals, this_conditional, index+1); + } else { + /* Remove conditional target from chain */ + if (conditional_targets == NULL || + conditional_targets->name != target) { + warning(catgets(catd, 1, 39, "Internal error: reset target not at head of condtional_targets chain")); + } else { + cond_chain = conditional_targets->next; + retmem_mb((caddr_t) conditional_targets); + conditional_targets = cond_chain; + } + } + get_prop(conditional->body.conditional.name->prop, + macro_prop)->body.macro = old_locals[index].body.macro; + if (conditional->body.conditional.name == virtual_root) { + (void) SETVAR(virtual_root, getvar(virtual_root), false); + } + if (debug_level > 1) { + if (old_locals[index].body.macro.value != NULL) { + (void) printf(catgets(catd, 1, 40, "%*sdeactivating conditional value: %s= %s\n"), + recursion_level, + "", + conditional->body.conditional.name-> + string_mb, + old_locals[index].body.macro.value-> + string_mb); + } else { + (void) printf(catgets(catd, 1, 41, "%*sdeactivating conditional value: %s =\n"), + recursion_level, + "", + conditional->body.conditional.name-> + string_mb); + } + } +} + +/* + * check_auto_dependencies(target, auto_count, automatics) + * + * Returns true if the target now has a dependency + * it didn't previously have (saved on automatics). + * + * Return value: + * true if new dependency found + * + * Parameters: + * target Target we check + * auto_count Number of old automatic vars + * automatics Saved old automatics + * + * Global variables used: + * keep_state Indicates that .KEEP_STATE is on + */ +Boolean +check_auto_dependencies(Name target, int auto_count, Name *automatics) +{ + Name *p; + int n; + Property line; + Dependency dependency; + + if (keep_state) { + if ((line = get_prop(target->prop, line_prop)) == NULL) { + return false; + } + /* Go thru new list of automatic depes */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + /* And make sure that each one existed before we */ + /* built the target */ + if (dependency->automatic && !dependency->stale) { + for (n = auto_count, p = automatics; + n > 0; + n--) { + if (*p++ == dependency->name) { + /* If we can find it on the */ + /* saved list of autos we */ + /* are OK */ + goto not_new; + } + } + /* But if we scan over the old list */ + /* of auto. without finding it it is */ + /* new and we must check it */ + return true; + } + not_new:; + } + return false; + } else { + return false; + } +} + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ +void +create_xdrs_ptr(void) +{ + static int xdrs_init = 0; + + if (!xdrs_init) { + xdrs_init = 1; + mtool_msgs_fp = fdopen(mtool_msgs_fd, "a"); + xdrstdio_create(&xdrs, + mtool_msgs_fp, + XDR_ENCODE); + } +} + +XDR * +get_xdrs_ptr(void) +{ + return &xdrs; +} + +FILE * +get_mtool_msgs_fp(void) +{ + return mtool_msgs_fp; +} + +int +get_job_msg_id(void) +{ + return job_msg_id; +} + +// Continuously poll and show the results of remotely executing a job, +// i.e., output the stdout and stderr files. + +static int +pollResults(char *outFn, char *errFn, char *hostNm) +{ + int child; + + child = fork(); + switch (child) { + case -1: + break; + case 0: + enable_interrupt((void (*) (int))SIG_DFL); +#ifdef linux + (void) signal(SIGUSR1, Avo_PollResultsAction_Sigusr1Handler); +#else + (void) sigset(SIGUSR1, Avo_PollResultsAction_Sigusr1Handler); +#endif + pollResultsAction(outFn, errFn); + + exit(0); + break; + default: + break; + } + return child; +} + +// This is the PollResultsAction SIGUSR1 handler. + +static bool_t pollResultsActionTimeToFinish = FALSE; + +extern "C" void +Avo_PollResultsAction_Sigusr1Handler(int foo) +{ + pollResultsActionTimeToFinish = TRUE; +} + +static void +pollResultsAction(char *outFn, char *errFn) +{ + int fd; + time_t file_time = 0; + long file_time_nsec = 0; + struct stat statbuf; + int stat_rc; + + // Keep stat'ing until file exists. + while (((stat_rc = stat(outFn, &statbuf)) != 0) && + (errno == ENOENT) && + !pollResultsActionTimeToFinish) { + us_sleep(STAT_RETRY_SLEEP_TIME); + } + // The previous stat() could be failed due to EINTR + // So one more try is needed + if (stat_rc != 0 && stat(outFn, &statbuf) != 0) { + // stat() failed + warning(NOCATGETS("Internal error: stat(\"%s\", ...) failed: %s\n"), + outFn, strerror(errno)); + exit(1); + } + + if ((fd = open(outFn, O_RDONLY)) < 0 + && (errno != EINTR || (fd = open(outFn, O_RDONLY)) < 0)) { + // open() failed + warning(NOCATGETS("Internal error: open(\"%s\", O_RDONLY) failed: %s\n"), + outFn, strerror(errno)); + exit(1); + } + + while (!pollResultsActionTimeToFinish && stat(outFn, &statbuf) == 0) { +#ifdef linux + if ((statbuf.st_mtime > file_time) + ) { + file_time = statbuf.st_mtime; + rxmGetNextResultsBlock(fd); + } +#else + if ((statbuf.st_mtim.tv_sec > file_time) || + ((statbuf.st_mtim.tv_sec == file_time) && + (statbuf.st_mtim.tv_nsec > file_time_nsec)) + ) { + file_time = statbuf.st_mtim.tv_sec; + file_time_nsec = statbuf.st_mtim.tv_nsec; + rxmGetNextResultsBlock(fd); + } +#endif + us_sleep(STAT_RETRY_SLEEP_TIME); + } + // Check for the rest of output + rxmGetNextResultsBlock(fd); + + (void) close(fd); +} + +static void +rxmGetNextResultsBlock(int fd) +{ + size_t to_read = 8 * 1024; + ssize_t bytes_read; + ssize_t bytes_written; + char results_buf[8 * 1024]; + sigset_t newset; + sigset_t oldset; + + // Read some more from the output/results file. + // Hopefully the kernel managed to prefetch the stuff. + bytes_read = read(fd, results_buf, to_read); + while (bytes_read > 0) { + AVO_BLOCK_INTERUPTS; + bytes_written = write(1, results_buf, bytes_read); + AVO_UNBLOCK_INTERUPTS; + if (bytes_written != bytes_read) { + // write() failed + warning(NOCATGETS("Internal error: write(1, ...) failed: %s\n"), + strerror(errno)); + exit(1); + } + bytes_read = read(fd, results_buf, to_read); + } +} + +// Generic, interruptable microsecond resolution sleep member function. + +static int +us_sleep(unsigned int nusecs) +{ + struct pollfd dummy; + int timeout; + + if ((timeout = nusecs/1000) <= 0) { + timeout = 1; + } + return poll(&dummy, 0, timeout); +} +#endif /* TEAMWARE_MAKE_CMN */ + +// Recursively delete each of the Chain struct on the chain. + +static void +delete_query_chain(Chain ch) +{ + if (ch == NULL) { + return; + } else { + delete_query_chain(ch->next); + retmem_mb((char *) ch); + } +} + +Doname +target_can_be_built(register Name target) { + Doname result = build_dont_know; + Name true_target = target; + Property line; + + if (target == wait_name) { + return(build_ok); + } + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + + (void) exists(true_target); + + if (true_target->state == build_running) { + return(build_running); + } + if (true_target->stat.time != file_doesnt_exist) { + result = build_ok; + } + + /* get line property for the target */ + line = get_prop(true_target->prop, line_prop); + + /* first check for explicit rule */ + if (line != NULL && line->body.line.command_template != NULL) { + result = build_ok; + } + /* try to find pattern rule */ + if (result == build_dont_know) { + result = find_percent_rule(target, NULL, false); + } + + /* try to find double suffix rule */ + if (result == build_dont_know) { + if (target->is_member) { + Property member = get_prop(target->prop, member_prop); + if (member != NULL && member->body.member.member != NULL) { + result = find_ar_suffix_rule(target, member->body.member.member, NULL, false); + } else { + result = find_double_suffix_rule(target, NULL, false); + } + } else { + result = find_double_suffix_rule(target, NULL, false); + } + } + + /* try to find suffix rule */ + if ((result == build_dont_know) && second_pass) { + result = find_suffix_rule(target, target, empty_name, NULL, false); + } + + /* check for sccs */ + if (result == build_dont_know) { + result = sccs_get(target, NULL); + } + + /* try to find dyn target */ + if (result == build_dont_know) { + Name dtarg = find_dyntarget(target); + if (dtarg != NULL) { + result = target_can_be_built(dtarg); + } + } + + /* check whether target was mentioned in makefile */ + if (result == build_dont_know) { + if (target->colons != no_colon) { + result = build_ok; + } + } + + /* result */ + return result; +} diff --git a/usr/src/make_src/Make/bin/make/common/dosys.cc b/usr/src/make_src/Make/bin/make/common/dosys.cc new file mode 100644 index 0000000..de9b043 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/dosys.cc @@ -0,0 +1,174 @@ +/* + * 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. + */ +/* + * @(#)dosys.cc 1.45 06/12/12 + */ + +#pragma ident "@(#)dosys.cc 1.45 06/12/12" + +/* + * dosys.cc + * + * Execute one commandline + */ + +/* + * Included files + */ +#include <fcntl.h> /* open() */ +#include <mk/defs.h> +#include <mksh/dosys.h> /* doshell(), doexec() */ +#include <mksh/misc.h> /* getmem() */ +#include <sys/stat.h> /* open() */ +#include <unistd.h> /* getpid() */ + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static int filter_file; +static char *filter_file_name; + +/* + * File table of contents + */ +static void redirect_stderr(void); + +/* + * dosys(command, ignore_error, call_make, silent_error, target) + * + * Check if command string contains meta chars and dispatch to + * the proper routine for executing one command line. + * + * Return value: + * Indicates if the command execution failed + * + * Parameters: + * command The command to run + * ignore_error Should make abort when an error is seen? + * call_make Did command reference $(MAKE) ? + * silent_error Should error messages be suppressed for pmake? + * target Target we are building + * + * Global variables used: + * do_not_exec_rule Is -n on? + * working_on_targets We started processing real targets + */ +Doname +dosys(register Name command, register Boolean ignore_error, register Boolean call_make, Boolean silent_error, Boolean always_exec, Name target, Boolean redirect_out_err) +{ + timestruc_t before; + register int length = command->hash.length; + Wstring wcb(command); + register wchar_t *p = wcb.get_string(); + register wchar_t *q; + Doname result; + + /* Strip spaces from head of command string */ + while (iswspace(*p)) { + p++, length--; + } + if (*p == (int) nul_char) { + return build_failed; + } + /* If we are faking it we just return */ + if (do_not_exec_rule && + working_on_targets && + !call_make && + !always_exec) { + return build_ok; + } + /* no_action_was_taken is used to print special message */ + no_action_was_taken = false; + + /* Copy string to make it OK to write it. */ + q = ALLOC_WC(length + 1); + (void) wscpy(q, p); + /* Write the state file iff this command uses make. */ + if (call_make && command_changed) { + write_state_file(0, false); + } + make_state->stat.time = file_no_time; + (void)exists(make_state); + before = make_state->stat.time; + /* + * Run command directly if it contains no shell meta chars, + * else run it using the shell. + */ + if (await(ignore_error, + silent_error, + target, + wcb.get_string(), + command->meta ? + doshell(q, ignore_error, redirect_out_err, + stdout_file, stderr_file, 0) : + doexec(q, ignore_error, redirect_out_err, + stdout_file, stderr_file, + vroot_path, 0), + send_mtool_msgs, +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ + get_xdrs_ptr(), + get_job_msg_id() +#else + NULL, + -1 +#endif + )) { + result = build_ok; + } else { + result = build_failed; + } + retmem(q); + + if ((report_dependencies_level == 0) && + call_make) { + make_state->stat.time = file_no_time; + (void)exists(make_state); + if (before == make_state->stat.time) { + return result; + } + makefile_type = reading_statefile; + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + } + return result; +} diff --git a/usr/src/make_src/Make/bin/make/common/files.cc b/usr/src/make_src/Make/bin/make/common/files.cc new file mode 100644 index 0000000..6ca6919 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/files.cc @@ -0,0 +1,733 @@ +/* + * 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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)files.cc 1.37 06/12/12 + */ + +#pragma ident "@(#)files.cc 1.37 06/12/12" + +/* + * files.c + * + * Various file related routines: + * Figure out if file exists + * Wildcard resolution for directory reader + * Directory reader + */ + + +/* + * Included files + */ +#if defined(SUN5_0) || defined(HP_UX) +#include <dirent.h> /* opendir() */ +#else +#include <sys/dir.h> /* opendir() */ +#endif +#include <errno.h> /* errno */ +#include <mk/defs.h> +#include <mksh/macro.h> /* getvar() */ +#include <mksh/misc.h> /* get_prop(), append_prop() */ +#include <sys/stat.h> /* lstat() */ + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +extern timestruc_t& exists(register Name target); +extern void set_target_stat(register Name target, struct stat buf); +static timestruc_t& vpath_exists(register Name target); +static Name enter_file_name(wchar_t *name_string, wchar_t *library); +static Boolean star_match(register char *string, register char *pattern); +static Boolean amatch(register wchar_t *string, register wchar_t *pattern); + +/* + * exists(target) + * + * Figure out the timestamp for one target. + * + * Return value: + * The time the target was created + * + * Parameters: + * target The target to check + * + * Global variables used: + * debug_level Should we trace the stat call? + * recursion_level Used for tracing + * vpath_defined Was the variable VPATH defined in environment? + */ +timestruc_t& +exists(register Name target) +{ + struct stat buf; + register int result; + + /* We cache stat information. */ + if (target->stat.time != file_no_time) { + return target->stat.time; + } + + /* + * If the target is a member, we have to extract the time + * from the archive. + */ + if (target->is_member && + (get_prop(target->prop, member_prop) != NULL)) { + return read_archive(target); + } + + if (debug_level > 1) { + (void) printf(NOCATGETS("%*sstat(%s)\n"), + recursion_level, + "", + target->string_mb); + } + + result = lstat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT); + if ((result != -1) && ((buf.st_mode & S_IFMT) == S_IFLNK)) { + /* + * If the file is a symbolic link, we remember that + * and then we get the status for the refd file. + */ + target->stat.is_sym_link = true; + result = stat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT); + } else { + target->stat.is_sym_link = false; + } + + if (result < 0) { + target->stat.time = file_doesnt_exist; + target->stat.stat_errno = errno; + if ((errno == ENOENT) && + vpath_defined && +/* azv, fixing bug 1262942, VPATH works with a leaf name + * but not a directory name. + */ + (target->string_mb[0] != (int) slash_char) ) { +/* BID_1214655 */ +/* azv */ + vpath_exists(target); + // return vpath_exists(target); + } + } else { + /* Save all the information we need about the file */ + target->stat.stat_errno = 0; + target->stat.is_file = true; + target->stat.mode = buf.st_mode & 0777; + target->stat.size = buf.st_size; + target->stat.is_dir = + BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR); + if (target->stat.is_dir) { + target->stat.time = file_is_dir; + } else { + /* target->stat.time = buf.st_mtime; */ +/* BID_1129806 */ +/* vis@nbsp.nsk.su */ +#if defined(linux) + timestruc_t ttime = { buf.st_mtime, 0 }; + target->stat.time = MAX(ttime, file_min_time); +#else + target->stat.time = MAX(buf.st_mtim, file_min_time); +#endif + } + } + if ((target->colon_splits > 0) && + (get_prop(target->prop, time_prop) == NULL)) { + append_prop(target, time_prop)->body.time.time = + target->stat.time; + } + return target->stat.time; +} + +/* + * set_target_stat( target, buf) + * + * Called by exists() to set some stat fields in the Name structure + * to those read by the stat_vroot() call (from disk). + * + * Parameters: + * target The target whose stat field is set + * buf stat values (on disk) of the file + * represented by target. + */ +void +set_target_stat(register Name target, struct stat buf) +{ + target->stat.stat_errno = 0; + target->stat.is_file = true; + target->stat.mode = buf.st_mode & 0777; + target->stat.size = buf.st_size; + target->stat.is_dir = + BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR); + if (target->stat.is_dir) { + target->stat.time = file_is_dir; + } else { + /* target->stat.time = buf.st_mtime; */ +/* BID_1129806 */ +/* vis@nbsp.nsk.su */ +#if defined(linux) + timestruc_t ttime = { buf.st_mtime, 0 }; + target->stat.time = ttime; +#else + target->stat.time = MAX(buf.st_mtim, file_min_time); +#endif + } +} + + +/* + * vpath_exists(target) + * + * Called if exists() discovers that there is a VPATH defined. + * This function stats the VPATH translation of the target. + * + * Return value: + * The time the target was created + * + * Parameters: + * target The target to check + * + * Global variables used: + * vpath_name The Name "VPATH", used to get macro value + */ +static timestruc_t& +vpath_exists(register Name target) +{ + wchar_t *vpath; + wchar_t file_name[MAXPATHLEN]; + wchar_t *name_p; + Name alias; + + /* + * To avoid recursive search through VPATH when exists(alias) is called + */ + vpath_defined = false; + + Wstring wcb(getvar(vpath_name)); + Wstring wcb1(target); + + vpath = wcb.get_string(); + + while (*vpath != (int) nul_char) { + name_p = file_name; + while ((*vpath != (int) colon_char) && + (*vpath != (int) nul_char)) { + *name_p++ = *vpath++; + } + *name_p++ = (int) slash_char; + (void) wscpy(name_p, wcb1.get_string()); + alias = GETNAME(file_name, FIND_LENGTH); + if (exists(alias) != file_doesnt_exist) { + target->stat.is_file = true; + target->stat.mode = alias->stat.mode; + target->stat.size = alias->stat.size; + target->stat.is_dir = alias->stat.is_dir; + target->stat.time = alias->stat.time; + maybe_append_prop(target, vpath_alias_prop)-> + body.vpath_alias.alias = alias; + target->has_vpath_alias_prop = true; + vpath_defined = true; + return alias->stat.time; + } + while ((*vpath != (int) nul_char) && + ((*vpath == (int) colon_char) || iswspace(*vpath))) { + vpath++; + } + } + /* + * Restore vpath_defined + */ + vpath_defined = true; + return target->stat.time; +} + +/* + * read_dir(dir, pattern, line, library) + * + * Used to enter the contents of directories into makes namespace. + * Presence of a file is important when scanning for implicit rules. + * read_dir() is also used to expand wildcards in dependency lists. + * + * Return value: + * Non-0 if we found files to match the pattern + * + * Parameters: + * dir Path to the directory to read + * pattern Pattern for that files should match or NULL + * line When we scan using a pattern we enter files + * we find as dependencies for this line + * library If we scan for "lib.a(<wildcard-member>)" + * + * Global variables used: + * debug_level Should we trace the dir reading? + * dot The Name ".", compared against + * sccs_dir_path The path to the SCCS dir (from PROJECTDIR) + * vpath_defined Was the variable VPATH defined in environment? + * vpath_name The Name "VPATH", use to get macro value + */ +int +read_dir(Name dir, wchar_t *pattern, Property line, wchar_t *library) +{ + wchar_t file_name[MAXPATHLEN]; + wchar_t *file_name_p = file_name; + Name file; + wchar_t plain_file_name[MAXPATHLEN]; + wchar_t *plain_file_name_p; + Name plain_file; + wchar_t tmp_wcs_buffer[MAXPATHLEN]; + DIR *dir_fd; + int m_local_dependency=0; +#if defined(SUN5_0) || defined(HP_UX) +#define d_fileno d_ino + register struct dirent *dp; +#else + register struct direct *dp; +#endif + wchar_t *vpath = NULL; + wchar_t *p; + int result = 0; + + if(dir->hash.length >= MAXPATHLEN) { + return 0; + } + + Wstring wcb(dir); + Wstring vps; + + /* A directory is only read once unless we need to expand wildcards. */ + if (pattern == NULL) { + if (dir->has_read_dir) { + return 0; + } + dir->has_read_dir = true; + } + /* Check if VPATH is active and setup list if it is. */ + if (vpath_defined && (dir == dot)) { + vps.init(getvar(vpath_name)); + vpath = vps.get_string(); + } + + /* + * Prepare the string where we build the full name of the + * files in the directory. + */ + if ((dir->hash.length > 1) || (wcb.get_string()[0] != (int) period_char)) { + (void) wscpy(file_name, wcb.get_string()); + MBSTOWCS(wcs_buffer, "/"); + (void) wscat(file_name, wcs_buffer); + file_name_p = file_name + wslen(file_name); + } + + /* Open the directory. */ +vpath_loop: + dir_fd = opendir(dir->string_mb); + if (dir_fd == NULL) { + return 0; + } + + /* Read all the directory entries. */ + while ((dp = readdir(dir_fd)) != NULL) { + /* We ignore "." and ".." */ + if ((dp->d_fileno == 0) || + ((dp->d_name[0] == (int) period_char) && + ((dp->d_name[1] == 0) || + ((dp->d_name[1] == (int) period_char) && + (dp->d_name[2] == 0))))) { + continue; + } + /* + * Build the full name of the file using whatever + * path supplied to the function. + */ + MBSTOWCS(tmp_wcs_buffer, dp->d_name); + (void) wscpy(file_name_p, tmp_wcs_buffer); + file = enter_file_name(file_name, library); + if ((pattern != NULL) && amatch(tmp_wcs_buffer, pattern)) { + /* + * If we are expanding a wildcard pattern, we + * enter the file as a dependency for the target. + */ + if (debug_level > 0){ + WCSTOMBS(mbs_buffer, pattern); + (void) printf(catgets(catd, 1, 231, "'%s: %s' due to %s expansion\n"), + line->body.line.target->string_mb, + file->string_mb, + mbs_buffer); + } + enter_dependency(line, file, false); + result++; + } else { + /* + * If the file has an SCCS/s. file, + * we will detect that later on. + */ + file->stat.has_sccs = NO_SCCS; + /* + * If this is an s. file, we also enter it as if it + * existed in the plain directory. + */ + if ((dp->d_name[0] == 's') && + (dp->d_name[1] == (int) period_char)) { + + MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); + plain_file_name_p = plain_file_name; + (void) wscpy(plain_file_name_p, tmp_wcs_buffer); + plain_file = GETNAME(plain_file_name, FIND_LENGTH); + plain_file->stat.is_file = true; + plain_file->stat.has_sccs = HAS_SCCS; + /* + * Enter the s. file as a dependency for the + * plain file. + */ + maybe_append_prop(plain_file, sccs_prop)-> + body.sccs.file = file; + MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); + if ((pattern != NULL) && + amatch(tmp_wcs_buffer, pattern)) { + if (debug_level > 0) { + WCSTOMBS(mbs_buffer, pattern); + (void) printf(catgets(catd, 1, 232, "'%s: %s' due to %s expansion\n"), + line->body.line.target-> + string_mb, + plain_file->string_mb, + mbs_buffer); + } + enter_dependency(line, plain_file, false); + result++; + } + } + } + } + (void) closedir(dir_fd); + if ((vpath != NULL) && (*vpath != (int) nul_char)) { + while ((*vpath != (int) nul_char) && + (iswspace(*vpath) || (*vpath == (int) colon_char))) { + vpath++; + } + p = vpath; + while ((*vpath != (int) colon_char) && + (*vpath != (int) nul_char)) { + vpath++; + } + if (vpath > p) { + dir = GETNAME(p, vpath - p); + goto vpath_loop; + } + } +/* + * look into SCCS directory only if it's not svr4. For svr4 dont do that. + */ + +/* + * Now read the SCCS directory. + * Files in the SCSC directory are considered to be part of the set of + * files in the plain directory. They are also entered in their own right. + * Prepare the string where we build the true name of the SCCS files. + */ + (void) wsncpy(plain_file_name, + file_name, + file_name_p - file_name); + plain_file_name[file_name_p - file_name] = 0; + plain_file_name_p = plain_file_name + wslen(plain_file_name); + + if(!svr4) { + + if (sccs_dir_path != NULL) { + wchar_t tmp_wchar; + wchar_t path[MAXPATHLEN]; + char mb_path[MAXPATHLEN]; + + if (file_name_p - file_name > 0) { + tmp_wchar = *file_name_p; + *file_name_p = 0; + WCSTOMBS(mbs_buffer, file_name); + (void) sprintf(mb_path, NOCATGETS("%s/%s/SCCS"), + sccs_dir_path, + mbs_buffer); + *file_name_p = tmp_wchar; + } else { + (void) sprintf(mb_path, NOCATGETS("%s/SCCS"), sccs_dir_path); + } + MBSTOWCS(path, mb_path); + (void) wscpy(file_name, path); + } else { + MBSTOWCS(wcs_buffer, NOCATGETS("SCCS")); + (void) wscpy(file_name_p, wcs_buffer); + } + } else { + MBSTOWCS(wcs_buffer, NOCATGETS(".")); + (void) wscpy(file_name_p, wcs_buffer); + } + /* Internalize the constructed SCCS dir name. */ + (void) exists(dir = GETNAME(file_name, FIND_LENGTH)); + /* Just give up if the directory file doesnt exist. */ + if (!dir->stat.is_file) { + return result; + } + /* Open the directory. */ + dir_fd = opendir(dir->string_mb); + if (dir_fd == NULL) { + return result; + } + MBSTOWCS(wcs_buffer, "/"); + (void) wscat(file_name, wcs_buffer); + file_name_p = file_name + wslen(file_name); + + while ((dp = readdir(dir_fd)) != NULL) { + if ((dp->d_fileno == 0) || + ((dp->d_name[0] == (int) period_char) && + ((dp->d_name[1] == 0) || + ((dp->d_name[1] == (int) period_char) && + (dp->d_name[2] == 0))))) { + continue; + } + /* Construct and internalize the true name of the SCCS file. */ + MBSTOWCS(wcs_buffer, dp->d_name); + (void) wscpy(file_name_p, wcs_buffer); + file = GETNAME(file_name, FIND_LENGTH); + file->stat.is_file = true; + file->stat.has_sccs = NO_SCCS; + /* + * If this is an s. file, we also enter it as if it + * existed in the plain directory. + */ + if ((dp->d_name[0] == 's') && + (dp->d_name[1] == (int) period_char)) { + + MBSTOWCS(wcs_buffer, dp->d_name + 2); + (void) wscpy(plain_file_name_p, wcs_buffer); + plain_file = GETNAME(plain_file_name, FIND_LENGTH); + plain_file->stat.is_file = true; + plain_file->stat.has_sccs = HAS_SCCS; + /* if sccs dependency is already set,skip */ + if(plain_file->prop) { + Property sprop = get_prop(plain_file->prop,sccs_prop); + if(sprop != NULL) { + if (sprop->body.sccs.file) { + goto try_pattern; + } + } + } + + /* + * Enter the s. file as a dependency for the + * plain file. + */ + maybe_append_prop(plain_file, sccs_prop)-> + body.sccs.file = file; +try_pattern: + MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); + if ((pattern != NULL) && + amatch(tmp_wcs_buffer, pattern)) { + if (debug_level > 0) { + WCSTOMBS(mbs_buffer, pattern); + (void) printf(catgets(catd, 1, 233, "'%s: %s' due to %s expansion\n"), + line->body.line.target-> + string_mb, + plain_file->string_mb, + mbs_buffer); + } + enter_dependency(line, plain_file, false); + result++; + } + } + } + (void) closedir(dir_fd); + + return result; +} + +/* + * enter_file_name(name_string, library) + * + * Helper function for read_dir(). + * + * Return value: + * The Name that was entered + * + * Parameters: + * name_string Name of the file we want to enter + * library The library it is a member of, if any + * + * Global variables used: + */ +static Name +enter_file_name(wchar_t *name_string, wchar_t *library) +{ + wchar_t buffer[STRING_BUFFER_LENGTH]; + String_rec lib_name; + Name name; + Property prop; + + if (library == NULL) { + name = GETNAME(name_string, FIND_LENGTH); + name->stat.is_file = true; + return name; + } + + INIT_STRING_FROM_STACK(lib_name, buffer); + append_string(library, &lib_name, FIND_LENGTH); + append_char((int) parenleft_char, &lib_name); + append_string(name_string, &lib_name, FIND_LENGTH); + append_char((int) parenright_char, &lib_name); + + name = GETNAME(lib_name.buffer.start, FIND_LENGTH); + name->stat.is_file = true; + name->is_member = true; + prop = maybe_append_prop(name, member_prop); + prop->body.member.library = GETNAME(library, FIND_LENGTH); + prop->body.member.library->stat.is_file = true; + prop->body.member.entry = NULL; + prop->body.member.member = GETNAME(name_string, FIND_LENGTH); + prop->body.member.member->stat.is_file = true; + return name; +} + +/* + * star_match(string, pattern) + * + * This is a regular shell type wildcard pattern matcher + * It is used when xpanding wildcards in dependency lists + * + * Return value: + * Indication if the string matched the pattern + * + * Parameters: + * string String to match + * pattern Pattern to match it against + * + * Global variables used: + */ +static Boolean +star_match(register wchar_t *string, register wchar_t *pattern) +{ + register int pattern_ch; + + switch (*pattern) { + case 0: + return succeeded; + case bracketleft_char: + case question_char: + case asterisk_char: + while (*string) { + if (amatch(string++, pattern)) { + return succeeded; + } + } + break; + default: + pattern_ch = (int) *pattern++; + while (*string) { + if ((*string++ == pattern_ch) && + amatch(string, pattern)) { + return succeeded; + } + } + break; + } + return failed; +} + +/* + * amatch(string, pattern) + * + * Helper function for shell pattern matching + * + * Return value: + * Indication if the string matched the pattern + * + * Parameters: + * string String to match + * pattern Pattern to match it against + * + * Global variables used: + */ +static Boolean +amatch(register wchar_t *string, register wchar_t *pattern) +{ + register long lower_bound; + register long string_ch; + register long pattern_ch; + register int k; + +top: + for (; 1; pattern++, string++) { + lower_bound = 017777777777; + string_ch = *string; + switch (pattern_ch = *pattern) { + case bracketleft_char: + k = 0; + while ((pattern_ch = *++pattern) != 0) { + switch (pattern_ch) { + case bracketright_char: + if (!k) { + return failed; + } + string++; + pattern++; + goto top; + case hyphen_char: + k |= (lower_bound <= string_ch) && + (string_ch <= + (pattern_ch = pattern[1])); + default: + if (string_ch == + (lower_bound = pattern_ch)) { + k++; + } + } + } + return failed; + case asterisk_char: + return star_match(string, ++pattern); + case 0: + return BOOLEAN(!string_ch); + case question_char: + if (string_ch == 0) { + return failed; + } + break; + default: + if (pattern_ch != string_ch) { + return failed; + } + break; + } + } + /* NOTREACHED */ +} + diff --git a/usr/src/make_src/Make/bin/make/common/globals.cc b/usr/src/make_src/Make/bin/make/common/globals.cc new file mode 100644 index 0000000..903a109 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/globals.cc @@ -0,0 +1,217 @@ +/* + * 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. + */ +/* + * @(#)globals.cc 1.42 06/12/12 + */ + +#pragma ident "@(#)globals.cc 1.42 06/12/12" + +/* + * globals.cc + * + * This declares all global variables + */ + +/* + * Included files + */ +#include <nl_types.h> +#include <mk/defs.h> +#include <sys/stat.h> + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Global variables used by make only + */ + FILE *dependency_report_file; + +/* + * Global variables used by make + */ + Boolean allrules_read=false; + Name posix_name; + Name svr4_name; + Boolean sdot_target; /* used to identify s.m(/M)akefile */ + Boolean all_parallel; /* TEAMWARE_MAKE_CMN */ + Boolean assign_done; + int foo; + Boolean build_failed_seen; +#ifdef DISTRIBUTED + Boolean building_serial; +#endif + Name built_last_make_run; + Name c_at; +#ifdef DISTRIBUTED + Boolean called_make = false; +#endif + Boolean cleanup; + Boolean close_report; + Boolean command_changed; + Boolean commands_done; + Chain conditional_targets; + Name conditionals; + Boolean continue_after_error; /* `-k' */ + Property current_line; + Name current_make_version; + Name current_target; + short debug_level; + Cmd_line default_rule; + Name default_rule_name; + Name default_target_to_build; + Name dmake_group; + Name dmake_max_jobs; + Name dmake_mode; + DMake_mode dmake_mode_type; + Name dmake_output_mode; + DMake_output_mode output_mode = txt1_mode; + Name dmake_odir; + Name dmake_rcfile; + Name done; + Name dot; + Name dot_keep_state; + Name dot_keep_state_file; + Name empty_name; +#if defined(HP_UX) || defined(linux) + int exit_status; +#endif + Boolean fatal_in_progress; + int file_number; +#if 0 + Boolean filter_stderr; /* `-X' */ +#endif + Name force; + Name ignore_name; + Boolean ignore_errors; /* `-i' */ + Boolean ignore_errors_all; /* `-i' */ + Name init; + int job_msg_id; + Boolean keep_state; + Name make_state; +#ifdef TEAMWARE_MAKE_CMN + timestruc_t make_state_before; +#endif + Dependency makefiles_used; + Name makeflags; +// Boolean make_state_locked; // Moved to lib/mksh + Name make_version; + char mbs_buffer2[(MAXPATHLEN * MB_LEN_MAX)]; + char *mbs_ptr; + char *mbs_ptr2; + int mtool_msgs_fd; + Boolean depinfo_already_read = false; +#ifdef NSE + Name derived_src; + Boolean nse; /* NSE on */ + Name nse_backquote_seen; + char nse_depinfo_lockfile[MAXPATHLEN]; + Boolean nse_depinfo_locked; + Boolean nse_did_recursion; + Name nse_shell_var_used; + Boolean nse_watch_vars = false; + wchar_t current_makefile[MAXPATHLEN]; +#endif + Boolean no_action_was_taken = true; /* true if we've not ** + ** run any command */ + + Boolean no_parallel = false; /* TEAMWARE_MAKE_CMN */ +#ifdef SGE_SUPPORT + Boolean grid = false; /* TEAMWARE_MAKE_CMN */ +#endif + Name no_parallel_name; + Name not_auto; + Boolean only_parallel; /* TEAMWARE_MAKE_CMN */ + Boolean parallel; /* TEAMWARE_MAKE_CMN */ + Name parallel_name; + Name localhost_name; + int parallel_process_cnt; + Percent percent_list; + Dyntarget dyntarget_list; + Name plus; + Name pmake_machinesfile; + Name precious; + Name primary_makefile; + Boolean quest; /* `-q' */ + short read_trace_level; + Boolean reading_dependencies = false; + Name recursive_name; + int recursion_level; + short report_dependencies_level = 0; /* -P */ + Boolean report_pwd; + Boolean rewrite_statefile; + Running running_list; + char *sccs_dir_path; + Name sccs_get_name; + Name sccs_get_posix_name; + Cmd_line sccs_get_rule; + Cmd_line sccs_get_org_rule; + Cmd_line sccs_get_posix_rule; + Name get_name; + Cmd_line get_rule; + Name get_posix_name; + Cmd_line get_posix_rule; + Boolean send_mtool_msgs; /* `-K' */ + Boolean all_precious; + Boolean silent_all; /* `-s' */ + Boolean report_cwd; /* `-w' */ + Boolean silent; /* `-s' */ + Name silent_name; + char *stderr_file = NULL; + char *stdout_file = NULL; +#ifdef SGE_SUPPORT + char script_file[MAXPATHLEN] = ""; +#endif + Boolean stdout_stderr_same; + Dependency suffixes; + Name suffixes_name; + Name sunpro_dependencies; + Boolean target_variants; + char *tmpdir = NOCATGETS("/tmp"); + char *temp_file_directory = NOCATGETS("."); + Name temp_file_name; + short temp_file_number; + time_t timing_start; + wchar_t *top_level_target; + Boolean touch; /* `-t' */ + Boolean trace_reader; /* `-D' */ + Boolean build_unconditional; /* `-u' */ + pathpt vroot_path = VROOT_DEFAULT; + Name wait_name; + wchar_t wcs_buffer2[MAXPATHLEN]; + wchar_t *wcs_ptr; + wchar_t *wcs_ptr2; + nl_catd catd; + long int hostid; + +/* + * File table of contents + */ + diff --git a/usr/src/make_src/Make/bin/make/common/implicit.cc b/usr/src/make_src/Make/bin/make/common/implicit.cc new file mode 100644 index 0000000..a9b2f1d --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/implicit.cc @@ -0,0 +1,1479 @@ +/* + * 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. + */ +/* + * @(#)implicit.cc 1.64 06/12/12 + */ + +#pragma ident "@(#)implicit.cc 1.64 06/12/12" + +/* + * implicit.c + * + * Handle suffix and percent rules + */ + +/* + * Included files + */ +#include <mk/defs.h> +#include <mksh/macro.h> /* expand_value() */ +#include <mksh/misc.h> /* retmem() */ + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static wchar_t WIDE_NULL[1] = {(wchar_t) nul_char}; + +/* + * File table of contents + */ +extern Doname find_suffix_rule(Name target, Name target_body, Name target_suffix, Property *command, Boolean rechecking); +extern Doname find_ar_suffix_rule(register Name target, Name true_target, Property *command, Boolean rechecking); +extern Doname find_double_suffix_rule(register Name target, Property *command, Boolean rechecking); +extern void build_suffix_list(register Name target_suffix); +extern Doname find_percent_rule(register Name target, Property *command, Boolean rechecking); +static void create_target_group_and_dependencies_list(Name target, Percent pat_rule, String percent); +static Boolean match_found_with_pattern(Name target, Percent pat_rule, String percent, wchar_t *percent_buf); +static void construct_string_from_pattern(Percent pat_rule, String percent, String result); +static Boolean dependency_exists(Name target, Property line); +extern Property maybe_append_prop(Name, Property_id); +extern void add_target_to_chain(Name target, Chain * query); + +/* + * find_suffix_rule(target, target_body, target_suffix, command, rechecking) + * + * Does the lookup for single and double suffix rules. + * It calls build_suffix_list() to build the list of possible suffixes + * for the given target. + * It then scans the list to find the first possible source file that + * exists. This is done by concatenating the body of the target name + * (target name less target suffix) and the source suffix and checking + * if the resulting file exists. + * + * Return value: + * Indicates if search failed or not + * + * Parameters: + * target The target we need a rule for + * target_body The target name without the suffix + * target_suffix The suffix of the target + * command Pointer to slot to deposit cmd in if found + * rechecking true if we are rechecking target which depends + * on conditional macro and keep_state is set + * + * Global variables used: + * debug_level Indicates how much tracing to do + * recursion_level Used for tracing + */ + +extern int printf (const char *, ...); + +static Boolean actual_doname = false; + +/* /tolik/ + * fix bug 1247448: Suffix Rules failed when combine with Pattern Matching Rules. + * When make attemps to apply % rule it didn't look for a single suffix rule because + * if "doname" is called from "find_percent_rule" argument "implicit" is set to true + * and find_suffix_rule was not called. I've commented the checking of "implicit" + * in "doname" and make got infinite recursion for SVR4 tilde rules. + * Usage of "we_are_in_tilde" is intended to avoid this recursion. + */ + +static Boolean we_are_in_tilde = false; + +Doname +find_suffix_rule(Name target, Name target_body, Name target_suffix, Property *command, Boolean rechecking) +{ + static wchar_t static_string_buf_3M [ 3 * MAXPATHLEN ]; + Name true_target = target; + wchar_t *sourcename = (wchar_t*)static_string_buf_3M; + register wchar_t *put_suffix; + register Property source_suffix; + register Name source; + Doname result; + register Property line; + extern Boolean tilde_rule; + Boolean name_found = true; + Boolean posix_tilde_attempt = true; + int src_len = MAXPATHLEN + strlen(target_body->string_mb); + + /* + * To avoid infinite recursion + */ + if(we_are_in_tilde) { + we_are_in_tilde = false; + return(build_dont_know); + } + + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + if (debug_level > 1) { + (void) printf(NOCATGETS("%*sfind_suffix_rule(%s,%s,%s)\n"), + recursion_level, + "", + true_target->string_mb, + target_body->string_mb, + target_suffix->string_mb); + } + if (command != NULL) { + if ((true_target->suffix_scan_done == true) && (*command == NULL)) { + return build_ok; + } + } + true_target->suffix_scan_done = true; + /* + * Enter all names from the directory where the target lives as + * files that makes sense. + * This will make finding the synthesized source possible. + */ + read_directory_of_file(target_body); + /* Cache the suffixes for this target suffix if not done. */ + if (!target_suffix->has_read_suffixes) { + build_suffix_list(target_suffix); + } + /* Preload the sourcename vector with the head of the target name. */ + if (src_len >= sizeof(static_string_buf_3M)) { + sourcename = ALLOC_WC(src_len); + } + (void) mbstowcs(sourcename, + target_body->string_mb, + (int) target_body->hash.length); + put_suffix = sourcename + target_body->hash.length; + /* Scan the suffix list for the target if one exists. */ + if (target_suffix->has_suffixes) { +posix_attempts: + for (source_suffix = get_prop(target_suffix->prop, + suffix_prop); + source_suffix != NULL; + source_suffix = get_prop(source_suffix->next, + suffix_prop)) { + /* Build the synthesized source name. */ + (void) mbstowcs(put_suffix, + source_suffix->body. + suffix.suffix->string_mb, + (int) source_suffix->body. + suffix.suffix->hash.length); + put_suffix[source_suffix->body. + suffix.suffix->hash.length] = + (int) nul_char; + if (debug_level > 1) { + WCSTOMBS(mbs_buffer, sourcename); + (void) printf(catgets(catd, 1, 218, "%*sTrying %s\n"), + recursion_level, + "", + mbs_buffer); + } + source = getname_fn(sourcename, FIND_LENGTH, false, &name_found); + /* + * If the source file is not registered as + * a file, this source suffix did not match. + */ + if(vpath_defined && !posix && !svr4) { + (void) exists(source); + } + if (!source->stat.is_file) { + if(!(posix|svr4)) + { + if(!name_found) { + free_name(source); + } + continue; + } + + /* following code will ensure that the corresponding + ** tilde rules are executed when corresponding s. file + ** exists in the current directory. Though the current + ** target ends with a ~ character, there wont be any + ** any file in the current directory with that suffix + ** as it's fictitious. Even if it exists, it'll + ** execute all the rules for the ~ target. + */ + + if(source->string_mb[source->hash.length - 1] == '~' && + ( svr4 || posix_tilde_attempt ) ) + { + char *p, *np; + char *tmpbuf; + + tmpbuf = getmem(source->hash.length + 8); + /* + 8 to add "s." or "SCCS/s." */ + memset(tmpbuf,0,source->hash.length + 8); + source->string_mb[source->hash.length - 1] = '\0'; + if(p = (char *) memchr((char *)source->string_mb,'/',source->hash.length)) + { + while(1) { + if(np = (char *) memchr((char *)p+1,'/',source->hash.length - (p - source->string_mb))) { + p = np; + } else {break;} + } + /* copy everything including '/' */ + strncpy(tmpbuf, source->string_mb, p - source->string_mb + 1); + strcat(tmpbuf, NOCATGETS("s.")); + strcat(tmpbuf, p+1); + retmem((wchar_t *) source->string_mb); + source->string_mb = tmpbuf; + + } else { + strcpy(tmpbuf, NOCATGETS("s.")); + strcat(tmpbuf, source->string_mb); + retmem((wchar_t *) source->string_mb); + source->string_mb = tmpbuf; + + } + source->hash.length = strlen(source->string_mb); + if(exists(source) == file_doesnt_exist) + continue; + tilde_rule = true; + we_are_in_tilde = true; + } else { + if(!name_found) { + free_name(source); + } + continue; + } + } else { + if(posix && posix_tilde_attempt) { + if(exists(source) == file_doesnt_exist) { + if(!name_found) { + free_name(source); + } + continue; + } + } + } + + if (command != NULL) { + if(!name_found) { + store_name(source); + } + /* + * The source file is a file. + * Make sure it is up to date. + */ + if (dependency_exists(source, + get_prop(target->prop, + line_prop))) { + result = (Doname) source->state; + } else { +#ifdef NSE + nse_check_derived_src(target, source->string, + source_suffix->body.suffix.command_template); +#endif +#if 0 /* with_squiggle sends false, which is buggy. : djay */ + result = doname(source, + (Boolean) source_suffix->body. + suffix.suffix->with_squiggle, + true); +#else + result = doname(source, + true, + true); +#endif + } + } else { + result = target_can_be_built(source); + + if (result == build_ok) { + return result; + } else { + if(!name_found) { + free_name(source); + } + continue; + } + } + + switch (result) { + case build_dont_know: + /* + * If we still can't build the source, + * this rule is not a match, + * try the next one. + */ + if (source->stat.time == file_doesnt_exist) { + if(!name_found) { + free_name(source); + } + continue; + } + case build_running: + if(!name_found) { + store_name(source); + } + true_target->suffix_scan_done = false; + line = maybe_append_prop(target, line_prop); + enter_dependency(line, source, false); + line->body.line.target = true_target; + return build_running; + case build_ok: + if(!name_found) { + store_name(source); + } + break; + case build_failed: + if(!name_found) { + store_name(source); + } + if (sourcename != static_string_buf_3M) { + retmem(sourcename); + } + return build_failed; + } + + if (debug_level > 1) { + WCSTOMBS(mbs_buffer, sourcename); + (void) printf(catgets(catd, 1, 219, "%*sFound %s\n"), + recursion_level, + "", + mbs_buffer); + } + + if (source->depends_on_conditional) { + target->depends_on_conditional = true; + } +/* + * Since it is possible that the same target is built several times during + * the make run, we have to patch the target with all information we found + * here. Thus, the target will have an explicit rule the next time around. + */ + line = maybe_append_prop(target, line_prop); + if (*command == NULL) { + *command = line; + } + if ((source->stat.time > (*command)->body.line.dependency_time) && + (debug_level > 1)) { + (void) printf(catgets(catd, 1, 220, "%*sDate(%s)=%s Date-dependencies(%s)=%s\n"), + recursion_level, + "", + source->string_mb, + time_to_string(source-> + stat.time), + true_target->string_mb, + time_to_string((*command)-> + body.line. + dependency_time)); + } + /* + * Determine if this new dependency made the + * target out of date. + */ + (*command)->body.line.dependency_time = + MAX((*command)->body.line.dependency_time, + source->stat.time); + Boolean out_of_date; + if (target->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(target->stat.time, + (*command)->body.line.dependency_time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(target->stat.time, + (*command)->body.line.dependency_time); + } + if (build_unconditional || out_of_date) { + if(!rechecking) { + line->body.line.is_out_of_date = true; + } + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 221, "%*sBuilding %s using suffix rule for %s%s because it is out of date relative to %s\n"), + recursion_level, + "", + true_target->string_mb, + source_suffix->body.suffix.suffix->string_mb, + target_suffix->string_mb, + source->string_mb); + } + } + /* + * Add the implicit rule as the target's explicit + * rule if none actually given, and register + * dependency. + * The time checking above really should be + * conditional on actual use of implicit rule + * as well. + */ + line->body.line.sccs_command = false; + if (line->body.line.command_template == NULL) { + line->body.line.command_template = + source_suffix->body.suffix.command_template; + } + enter_dependency(line, source, false); + line->body.line.target = true_target; + /* + * Also make sure the rule is built with + * $* and $< bound properly. + */ + line->body.line.star = target_body; + if(svr4|posix) { + char * p; + char tstr[256]; + extern Boolean dollarless_flag; + extern Name dollarless_value; + + if(tilde_rule) { + MBSTOWCS(wcs_buffer, NOCATGETS(source->string_mb)); + dollarless_value = GETNAME(wcs_buffer,FIND_LENGTH); + } + else { + dollarless_flag = false; + } + } + line->body.line.less = source; + line->body.line.percent = NULL; + add_target_to_chain(source, &(line->body.line.query)); + if (sourcename != static_string_buf_3M) { + retmem(sourcename); + } + return build_ok; + } + if(posix && posix_tilde_attempt) { + posix_tilde_attempt = false; + goto posix_attempts; + } + if ((command != NULL) && + ((*command) != NULL) && + ((*command)->body.line.star == NULL)) { + (*command)->body.line.star = target_body; + } + } + if (sourcename != static_string_buf_3M) { + retmem(sourcename); + } + /* Return here in case no rule matched the target */ + return build_dont_know; +} + +/* + * find_ar_suffix_rule(target, true_target, command, rechecking) + * + * Scans the .SUFFIXES list and tries + * to find a suffix on it that matches the tail of the target member name. + * If it finds a matching suffix it calls find_suffix_rule() to find + * a rule for the target using the suffix ".a". + * + * Return value: + * Indicates if search failed or not + * + * Parameters: + * target The target we need a rule for + * true_target The proper name + * command Pointer to slot where we stuff cmd, if found + * rechecking true if we are rechecking target which depends + * on conditional macro and keep_state is set + * + * Global variables used: + * debug_level Indicates how much tracing to do + * dot_a The Name ".a", compared against + * recursion_level Used for tracing + * suffixes List of suffixes used for scan (from .SUFFIXES) + */ +Doname +find_ar_suffix_rule(register Name target, Name true_target, Property *command, Boolean rechecking) +{ + wchar_t *target_end; + register Dependency suffix; + register int suffix_length; + Property line; + Name body; + static Name dot_a; + + Wstring targ_string(true_target); + Wstring suf_string; + + if (dot_a == NULL) { + MBSTOWCS(wcs_buffer, NOCATGETS(".a")); + dot_a = GETNAME(wcs_buffer, FIND_LENGTH); + } + target_end = targ_string.get_string() + true_target->hash.length; + + /* + * We compare the tail of the target name with the suffixes + * from .SUFFIXES. + */ + if (debug_level > 1) { + (void) printf(NOCATGETS("%*sfind_ar_suffix_rule(%s)\n"), + recursion_level, + "", + true_target->string_mb); + } + /* + * Scan the .SUFFIXES list to see if the target matches any of + * those suffixes. + */ + for (suffix = suffixes; suffix != NULL; suffix = suffix->next) { + /* Compare one suffix. */ + suffix_length = suffix->name->hash.length; + suf_string.init(suffix->name); + if (!IS_WEQUALN(suf_string.get_string(), + target_end - suffix_length, + suffix_length)) { + goto not_this_one; + } + /* + * The target tail matched a suffix from the .SUFFIXES list. + * Now check for a rule to match. + */ + target->suffix_scan_done = false; + body = GETNAME(targ_string.get_string(), + (int)(true_target->hash.length - + suffix_length)); + we_are_in_tilde = false; + switch (find_suffix_rule(target, + body, + dot_a, + command, + rechecking)) { + case build_ok: + line = get_prop(target->prop, line_prop); + line->body.line.star = body; + return build_ok; + case build_running: + return build_running; + } + /* + * If no rule was found, we try the next suffix to see + * if it matches the target tail, and so on. + * Go here if the suffix did not match the target tail. + */ + not_this_one:; + } + return build_dont_know; +} + +/* + * find_double_suffix_rule(target, command, rechecking) + * + * Scans the .SUFFIXES list and tries + * to find a suffix on it that matches the tail of the target name. + * If it finds a matching suffix it calls find_suffix_rule() to find + * a rule for the target. + * + * Return value: + * Indicates if scan failed or not + * + * Parameters: + * target Target we need a rule for + * command Pointer to slot where we stuff cmd, if found + * rechecking true if we are rechecking target which depends + * on conditional macro and keep_state is set + * + * Global variables used: + * debug_level Indicates how much tracing to do + * recursion_level Used for tracing + * suffixes List of suffixes used for scan (from .SUFFIXES) + */ +Doname +find_double_suffix_rule(register Name target, Property *command, Boolean rechecking) +{ + Name true_target = target; + Name target_body; + register wchar_t *target_end; + register Dependency suffix; + register int suffix_length; + Boolean scanned_once = false; + Boolean name_found = true; + + Wstring targ_string; + Wstring suf_string; + + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + targ_string.init(true_target); + + /* + * We compare the tail of the target name with the + * suffixes from .SUFFIXES. + */ + target_end = targ_string.get_string() + true_target->hash.length; + if (debug_level > 1) { + (void) printf(NOCATGETS("%*sfind_double_suffix_rule(%s)\n"), + recursion_level, + "", + true_target->string_mb); + } + /* + * Scan the .SUFFIXES list to see if the target matches + * any of those suffixes. + */ + for (suffix = suffixes; suffix != NULL; suffix = suffix->next) { + target->suffix_scan_done = false; + true_target->suffix_scan_done = false; + /* Compare one suffix. */ + suffix_length = suffix->name->hash.length; + suf_string.init(suffix->name); + /* Check the lengths, or else RTC will report rua. */ + if (true_target->hash.length < suffix_length) { + goto not_this_one; + } else if (!IS_WEQUALN(suf_string.get_string(), + (target_end - suffix_length), + suffix_length)) { + goto not_this_one; + } + /* + * The target tail matched a suffix from the .SUFFIXES list. + * Now check for a rule to match. + */ + we_are_in_tilde = false; + target_body = GETNAME( + targ_string.get_string(), + (int)(true_target->hash.length - suffix_length) + ); + switch (find_suffix_rule(target, + target_body, + suffix->name, + command, + rechecking)) { + case build_ok: + return build_ok; + case build_running: + return build_running; + } + if (true_target->suffix_scan_done == true) { + scanned_once = true; + } + /* + * If no rule was found, we try the next suffix to see + * if it matches the target tail. And so on. + * Go here if the suffix did not match the target tail. + */ + not_this_one:; + } + if (scanned_once) + true_target->suffix_scan_done = true; + return build_dont_know; +} + +/* + * build_suffix_list(target_suffix) + * + * Scans the .SUFFIXES list and figures out + * which suffixes this target can be derived from. + * The target itself is not know here, we just know the suffix of the + * target. For each suffix on the list the target can be derived iff + * a rule exists for the name "<suffix-on-list><target-suffix>". + * A list of all possible building suffixes is built, with the rule for + * each, and tacked to the target suffix nameblock. + * + * Parameters: + * target_suffix The suffix we build a match list for + * + * Global variables used: + * debug_level Indicates how much tracing to do + * recursion_level Used for tracing + * suffixes List of suffixes used for scan (from .SUFFIXES) + * working_on_targets Indicates that this is a real target + */ +void +build_suffix_list(register Name target_suffix) +{ + register Dependency source_suffix; + wchar_t rule_name[MAXPATHLEN]; + register Property line; + register Property suffix; + Name rule; + + /* If this is before default.mk has been read we just return to try */ + /* again later */ + if ((suffixes == NULL) || !working_on_targets) { + return; + } + if (debug_level > 1) { + (void) printf(NOCATGETS("%*sbuild_suffix_list(%s) "), + recursion_level, + "", + target_suffix->string_mb); + } + /* Mark the target suffix saying we cashed its list */ + target_suffix->has_read_suffixes = true; + /* Scan the .SUFFIXES list */ + for (source_suffix = suffixes; + source_suffix != NULL; + source_suffix = source_suffix->next) { + /* + * Build the name "<suffix-on-list><target-suffix>". + * (a popular one would be ".c.o"). + */ + (void) mbstowcs(rule_name, + source_suffix->name->string_mb, + (int) source_suffix->name->hash.length); + (void) mbstowcs(rule_name + source_suffix->name->hash.length, + target_suffix->string_mb, + (int) target_suffix->hash.length); + /* + * Check if that name has a rule. If not, it cannot match + * any implicit rule scan and is ignored. + * The GETNAME() call only checks for presence, it will not + * enter the name if it is not defined. + */ + if (((rule = getname_fn(rule_name, + (int) (source_suffix->name-> + hash.length + + target_suffix->hash.length), + true)) != NULL) && + ((line = get_prop(rule->prop, line_prop)) != NULL)) { + if (debug_level > 1) { + (void) printf("%s ", rule->string_mb); + } + /* + * This makes it possible to quickly determine if + * it will pay to look for a suffix property. + */ + target_suffix->has_suffixes = true; + /* + * Add the suffix property to the target suffix + * and save the rule with it. + * All information the implicit rule scanner need + * is saved in the suffix property. + */ + suffix = append_prop(target_suffix, suffix_prop); + suffix->body.suffix.suffix = source_suffix->name; + suffix->body.suffix.command_template = + line->body.line.command_template; + } + } + if (debug_level > 1) { + (void) printf("\n"); + } +} + +/* + * find_percent_rule(target, command, rechecking) + * + * Tries to find a rule from the list of wildcard matched rules. + * It scans the list attempting to match the target. + * For each target match it checks if the corresponding source exists. + * If it does the match is returned. + * The percent_list is built at makefile read time. + * Each percent rule get one entry on the list. + * + * Return value: + * Indicates if the scan failed or not + * + * Parameters: + * target The target we need a rule for + * command Pointer to slot where we stuff cmd, if found + * rechecking true if we are rechecking target which depends + * on conditional macro and keep_state is set + * + * Global variables used: + * debug_level Indicates how much tracing to do + * percent_list List of all percent rules + * recursion_level Used for tracing + * empty_name + */ +Doname +find_percent_rule(register Name target, Property *command, Boolean rechecking) +{ + register Percent pat_rule, pat_depe; + register Name depe_to_check; + register Dependency depe; + register Property line; + String_rec string; + wchar_t string_buf[STRING_BUFFER_LENGTH]; + String_rec percent; + wchar_t percent_buf[STRING_BUFFER_LENGTH]; + Name true_target = target; + Name less; + Boolean nonpattern_less; + Boolean dep_name_found = false; + Doname result = build_dont_know; + Percent rule_candidate = NULL; + Boolean rule_maybe_ok; + Boolean is_pattern; + + /* If the target is constructed for a "::" target we consider that */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + if (target->has_long_member_name) { + true_target = get_prop(target->prop, + long_member_name_prop)->body.long_member_name.member_name; + } + if (debug_level > 1) { + (void) printf(catgets(catd, 1, 222, "%*sLooking for %% rule for %s\n"), + recursion_level, + "", + true_target->string_mb); + } + for (pat_rule = percent_list; + pat_rule != NULL; + pat_rule = pat_rule->next) { + /* Avoid infinite recursion when expanding patterns */ + if (pat_rule->being_expanded == true) { + continue; + } + + /* Mark this pat_rule as "maybe ok". If no % rule is found + make will use this rule. The following algorithm is used: + 1) make scans all pattern rules in order to find the rule + where ALL dependencies, including nonpattern ones, exist or + can be built (GNU behaviour). If such rule is found make + will apply it. + 2) During this check make also remembers the first pattern rule + where all PATTERN dependencies can be build (no matter what + happens with nonpattern dependencies). + 3) If no rule satisfying 1) is found, make will apply the rule + remembered in 2) if there is one. + */ + rule_maybe_ok = true; + + /* used to track first percent dependency */ + less = NULL; + nonpattern_less = true; + + /* check whether pattern matches. + if it matches, percent string will contain matched percent part of pattern */ + if (!match_found_with_pattern(true_target, pat_rule, &percent, percent_buf)) { + continue; + } + if (pat_rule->dependencies != NULL) { + for (pat_depe = pat_rule->dependencies; + pat_depe != NULL; + pat_depe = pat_depe->next) { + /* checking result for dependency */ + result = build_dont_know; + + dep_name_found = true; + if (pat_depe->name->percent) { + is_pattern = true; + /* build dependency name */ + INIT_STRING_FROM_STACK(string, string_buf); + construct_string_from_pattern(pat_depe, &percent, &string); + depe_to_check = getname_fn(string.buffer.start, + FIND_LENGTH, + false, + &dep_name_found + ); + + if ((less == NULL) || nonpattern_less) { + less = depe_to_check; + nonpattern_less = false; + } + } else { + /* nonpattern dependency */ + is_pattern = false; + depe_to_check = pat_depe->name; + if(depe_to_check->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(depe_to_check, &string, false); + depe_to_check = getname_fn(string.buffer.start, + FIND_LENGTH, + false, + &dep_name_found + ); + } + if (less == NULL) { + less = depe_to_check; + } + } + + if (depe_to_check == empty_name) { + result = build_ok; + } else { + if (debug_level > 1) { + (void) printf(catgets(catd, 1, 223, "%*sTrying %s\n"), + recursion_level, + "", + depe_to_check->string_mb); + } + + pat_rule->being_expanded = true; + + /* suppress message output */ + int save_debug_level = debug_level; + debug_level = 0; + + /* check whether dependency can be built */ + if (dependency_exists(depe_to_check, + get_prop(target->prop, + line_prop))) + { + result = (Doname) depe_to_check->state; + } else { + if(actual_doname) { + result = doname(depe_to_check, true, true); + } else { + result = target_can_be_built(depe_to_check); + } + if(!dep_name_found) { + if(result != build_ok && result != build_running) { + free_name(depe_to_check); + } else { + store_name(depe_to_check); + } + } + } + if(result != build_ok && is_pattern) { + rule_maybe_ok = false; + } + + /* restore debug_level */ + debug_level = save_debug_level; + } + + if (pat_depe->name->percent) { + if (string.free_after_use) { + retmem(string.buffer.start); + } + } + /* make can't figure out how to make this dependency */ + if (result != build_ok && result != build_running) { + pat_rule->being_expanded = false; + break; + } + } + } else { + result = build_ok; + } + + /* this pattern rule is the needed one since all dependencies could be built */ + if (result == build_ok || result == build_running) { + break; + } + + /* Make does not know how to build some of dependencies from this rule. + But if all "pattern" dependencies can be built, we remember this rule + as a candidate for the case if no other pattern rule found. + */ + if(rule_maybe_ok && rule_candidate == NULL) { + rule_candidate = pat_rule; + } + } + + /* if no pattern matching rule was found, use the remembered candidate + or return build_dont_know if there is no candidate. + */ + if (result != build_ok && result != build_running) { + if(rule_candidate) { + pat_rule = rule_candidate; + } else { + return build_dont_know; + } + } + + /* if we are performing only check whether dependency could be built with existing rules, + return success */ + if (command == NULL) { + if(pat_rule != NULL) { + pat_rule->being_expanded = false; + } + return result; + } + + if (debug_level > 1) { + (void) printf(catgets(catd, 1, 224, "%*sMatched %s:"), + recursion_level, + "", + target->string_mb); + + for (pat_depe = pat_rule->dependencies; + pat_depe != NULL; + pat_depe = pat_depe->next) { + if (pat_depe->name->percent) { + INIT_STRING_FROM_STACK(string, string_buf); + construct_string_from_pattern(pat_depe, &percent, &string); + depe_to_check = GETNAME(string.buffer.start, FIND_LENGTH); + } else { + depe_to_check = pat_depe->name; + if(depe_to_check->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(depe_to_check, &string, false); + depe_to_check = GETNAME(string.buffer.start, FIND_LENGTH); + } + } + + if (depe_to_check != empty_name) { + (void) printf(" %s", depe_to_check->string_mb); + } + } + + (void) printf(catgets(catd, 1, 225, " from: %s:"), + pat_rule->name->string_mb); + + for (pat_depe = pat_rule->dependencies; + pat_depe != NULL; + pat_depe = pat_depe->next) { + (void) printf(" %s", pat_depe->name->string_mb); + } + + (void) printf("\n"); + } + + if (true_target->colons == no_colon) { + true_target->colons = one_colon; + } + + /* create deppendency list and target group from matched pattern rule */ + create_target_group_and_dependencies_list(target, pat_rule, &percent); + + /* save command */ + line = get_prop(target->prop, line_prop); + *command = line; + + /* free query chain if one exist */ + while(line->body.line.query != NULL) { + Chain to_free = line->body.line.query; + line->body.line.query = line->body.line.query->next; + retmem_mb((char *) to_free); + } + + if (line->body.line.dependencies != NULL) { + /* build all collected dependencies */ + for (depe = line->body.line.dependencies; + depe != NULL; + depe = depe->next) { + actual_doname = true; + result = doname_check(depe->name, true, true, depe->automatic); + + actual_doname = false; + if (result == build_failed) { + pat_rule->being_expanded = false; + return build_failed; + } + if (result == build_running) { + pat_rule->being_expanded = false; + return build_running; + } + + if ((depe->name->stat.time > line->body.line.dependency_time) && + (debug_level > 1)) { + (void) printf(catgets(catd, 1, 226, "%*sDate(%s)=%s Date-dependencies(%s)=%s\n"), + recursion_level, + "", + depe->name->string_mb, + time_to_string(depe->name->stat.time), + true_target->string_mb, + time_to_string(line->body.line.dependency_time)); + } + + line->body.line.dependency_time = + MAX(line->body.line.dependency_time, depe->name->stat.time); + + /* determine whether this dependency made target out of date */ + Boolean out_of_date; + if (target->is_member || depe->name->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(target->stat.time, depe->name->stat.time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(target->stat.time, depe->name->stat.time); + } + if (build_unconditional || out_of_date) { + if(!rechecking) { + line->body.line.is_out_of_date = true; + } + add_target_to_chain(depe->name, &(line->body.line.query)); + + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 227, "%*sBuilding %s using pattern rule %s:"), + recursion_level, + "", + true_target->string_mb, + pat_rule->name->string_mb); + + for (pat_depe = pat_rule->dependencies; + pat_depe != NULL; + pat_depe = pat_depe->next) { + (void) printf(" %s", pat_depe->name->string_mb); + } + + (void) printf(catgets(catd, 1, 228, " because it is out of date relative to %s\n"), + depe->name->string_mb); + } + } + } + } else { + if ((true_target->stat.time <= file_doesnt_exist) || + (true_target->stat.time < line->body.line.dependency_time)) { + if(!rechecking) { + line->body.line.is_out_of_date = true; + } + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 229, "%*sBuilding %s using pattern rule %s: "), + recursion_level, + "", + true_target->string_mb, + pat_rule->name->string_mb, + (target->stat.time > file_doesnt_exist) ? + catgets(catd, 1, 230, "because it is out of date") : + catgets(catd, 1, 236, "because it does not exist")); + } + } + } + + /* enter explicit rule from percent rule */ + Name lmn_target = true_target; + if (true_target->has_long_member_name) { + lmn_target = get_prop(true_target->prop, long_member_name_prop)->body.long_member_name.member_name; + } + line->body.line.sccs_command = false; + line->body.line.target = true_target; + line->body.line.command_template = pat_rule->command_template; + line->body.line.star = GETNAME(percent.buffer.start, FIND_LENGTH); + line->body.line.less = less; + + if (lmn_target->parenleft) { + Wstring lmn_string(lmn_target); + + wchar_t *left = (wchar_t *) wschr(lmn_string.get_string(), (int) parenleft_char); + wchar_t *right = (wchar_t *) wschr(lmn_string.get_string(), (int) parenright_char); + + if ((left == NULL) || (right == NULL)) { + line->body.line.percent = NULL; + } else { + line->body.line.percent = GETNAME(left + 1, right - left - 1); + } + } else { + line->body.line.percent = NULL; + } + pat_rule->being_expanded = false; + +#ifdef TEAMWARE_MAKE_CMN + /* + * This #ifdef fixes a dmake bug, but introduces bugid 1136156. + */ + return result; +#else + return build_ok; +#endif +} + +/* + * match_found_with_pattern + * ( target, pat_rule, percent, percent_buf) + * + * matches "target->string" with a % pattern. + * If pattern contains a MACRO definition, it's expanded first. + * + * Return value: + * true if a match was found + * + * Parameters: + * target The target we're trying to match + * pattern + * percent record that contains "percent_buf" below + * percent_buf This is where the patched % part of pattern is stored + * + */ + +static Boolean +match_found_with_pattern(Name target, Percent pat_rule, String percent, wchar_t *percent_buf) { + String_rec string; + wchar_t string_buf[STRING_BUFFER_LENGTH]; + + /* construct prefix string and check whether prefix matches */ + Name prefix = pat_rule->patterns[0]; + int prefix_length; + + Wstring targ_string(target); + Wstring pref_string(prefix); + Wstring suf_string; + + if (prefix->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(prefix, &string, false); + prefix_length = string.text.p - string.buffer.start; + if ((string.buffer.start[0] == (int) period_char) && + (string.buffer.start[1] == (int) slash_char)) { + string.buffer.start += 2; + prefix_length -= 2; + } + if (!targ_string.equaln(string.buffer.start, prefix_length)) { + return false; + } + } else { + prefix_length = prefix->hash.length; + if (!targ_string.equaln(&pref_string, prefix_length)) { + return false; + } + } + + /* do the same with pattern suffix */ + Name suffix = pat_rule->patterns[pat_rule->patterns_total - 1]; + suf_string.init(suffix); + + int suffix_length; + if (suffix->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(suffix, &string, false); + suffix_length = string.text.p - string.buffer.start; + if(suffix_length > target->hash.length) { + return false; + } + if (!targ_string.equal(string.buffer.start, target->hash.length - suffix_length)) { + return false; + } + } else { + suffix_length = (int) suffix->hash.length; + if(suffix_length > target->hash.length) { + return false; + } + if (!targ_string.equal(&suf_string, target->hash.length - suffix_length)) { + return false; + } + } + + Boolean match_found = false; + int percent_length = target->hash.length - prefix_length - suffix_length; + + while (!match_found && (percent_length >= 0)) { + /* init result string */ + INIT_STRING_FROM_STACK(string, string_buf); + + /* init percent string */ + percent->buffer.start = percent_buf; + percent->text.p = percent_buf; + percent->text.end = NULL; + percent->free_after_use = false; + percent->buffer.end = percent_buf + STRING_BUFFER_LENGTH; + + /* construct percent and result strings */ + targ_string.append_to_str(percent, prefix_length, percent_length); + construct_string_from_pattern(pat_rule, percent, &string); + + /* check for match */ + if (targ_string.equal(string.buffer.start, 0)) { + match_found = true; + } else { + percent_length--; + } + } + + /* result */ + return match_found; +} + + +/* + * create_target_group_and_dependencies_list + * (target, pat_rule, percent) + * + * constructs dependency list and a target group from pattern. + * + * If we have the lines + * %/%.a + %/%.b + C%/CC%.c: yyy %.d bb%/BB%.e + * commands + * + * and we have matched the pattern xx/xx.a with %/%.a, then we + * construct a target group that looks like this: + * xx/xx.a + xx/xx.b + Cxx/CCxx.c: dependencies + * + * and construct dependency list that looks like this: + * yyy xx.d bbxx/BBxx.e + already existed dependencies + * + * Return value: + * none + * + * Parameters: + * target The target we are building, in the previous + * example, this is xx/xx.a + * pat_rule the % pattern that matched "target", here %/%.a + * percent string containing matched % part. In the example=xx. + * + * Global variables used: + * empty_name + */ + +static void +create_target_group_and_dependencies_list(Name target, Percent pat_rule, String percent) { + String_rec string; + wchar_t string_buf[STRING_BUFFER_LENGTH]; + Percent pat_depe; + Name depe; + Property line = maybe_append_prop(target, line_prop); + Chain new_target_group = NULL; + Chain *new_target_group_tail = &new_target_group; + Chain group_member; + + /* create and append dependencies from rule */ + for (pat_depe = pat_rule->dependencies; pat_depe != NULL; pat_depe = pat_depe->next) { + if (pat_depe->name->percent) { + INIT_STRING_FROM_STACK(string, string_buf); + construct_string_from_pattern(pat_depe, percent, &string); + depe = GETNAME(string.buffer.start, FIND_LENGTH); + if (depe != empty_name) { + enter_dependency(line, depe, false); + } + } else { + depe = pat_depe->name; + if(depe->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(depe, &string, false); + depe = GETNAME(string.buffer.start, FIND_LENGTH); + } + enter_dependency(line, depe, false); + } + } + + /* if matched pattern is a group member, create new target group */ + for (group_member = pat_rule->target_group; group_member != NULL; group_member = group_member->next) { + Name new_target = group_member->name; + if (group_member->name->percent) { + INIT_STRING_FROM_STACK(string, string_buf); + construct_string_from_pattern(group_member->percent_member, percent, &string); + new_target = GETNAME(string.buffer.start, FIND_LENGTH); + if (new_target == empty_name) { + continue; + } + } + + /* check for duplicates */ + Chain tgm; + for (tgm = new_target_group; tgm != NULL; tgm = tgm->next) { + if (new_target == tgm->name) { + break; + } + } + if (tgm != NULL) { + continue; + } + + /* insert it into the targets list */ + (*new_target_group_tail) = ALLOC(Chain); + (*new_target_group_tail)->name = new_target; + (*new_target_group_tail)->next = NULL; + new_target_group_tail = &(*new_target_group_tail)->next; + } + + /* now we gathered all dependencies and created target group */ + line->body.line.target_group = new_target_group; + + /* update properties for group members */ + for (group_member = new_target_group; group_member != NULL; group_member = group_member->next) { + if (group_member->name != target) { + group_member->name->prop = target->prop; + group_member->name->conditional_cnt = target->conditional_cnt; + } + } +} + +/* + * construct_string_from_pattern + * (pat_rule, percent, result) + * + * after pattern matched a target this routine is called to construct targets and dependencies + * strings from this matched pattern rule and a string (percent) with substitutes % sign in pattern. + * + * Return value: + * none + * + * Parameters: + * pat_rule matched pattern rule + * percent string containing matched % sign part. + * result holds the result of string construction. + * + */ +static void +construct_string_from_pattern(Percent pat_rule, String percent, String result) { + for (int i = 0; i < pat_rule->patterns_total; i++) { + if (pat_rule->patterns[i]->dollar) { + expand_value(pat_rule->patterns[i], + result, + false); + + } else { + append_string(pat_rule->patterns[i]->string_mb, + result, + pat_rule->patterns[i]->hash.length); + } + + if (i < pat_rule->patterns_total - 1) { + append_string(percent->buffer.start, + result, + percent->text.p - percent->buffer.start); + } + } + + if ((result->buffer.start[0] == (int) period_char) && + (result->buffer.start[1] == (int) slash_char)) { + result->buffer.start += 2; + } +} + +/* + * dependency_exists(target, line) + * + * Returns true if the target exists in the + * dependency list of the line. + * + * Return value: + * True if target is on dependency list + * + * Parameters: + * target Target we scan for + * line We get the dependency list from here + * + * Global variables used: + */ +static Boolean +dependency_exists(Name target, Property line) +{ + Dependency dp; + + if (line == NULL) { + return false; + } + for (dp = line->body.line.dependencies; dp != NULL; dp = dp->next) { + if (dp->name == target) { + return true; + } + } + return false; +} + +void +add_target_to_chain(Name target, Chain * query) +{ + if (target->is_member && (get_prop(target->prop, member_prop) != NULL)) { + target = get_prop(target->prop, member_prop)->body.member.member; + } + Chain *query_tail; + for (query_tail = query; *query_tail != NULL; query_tail = &(*query_tail)->next) { + if ((*query_tail)->name == target) { + return; + } + } + *query_tail = ALLOC(Chain); + (*query_tail)->name = target; + (*query_tail)->next = NULL; +} + diff --git a/usr/src/make_src/Make/bin/make/common/macro.cc b/usr/src/make_src/Make/bin/make/common/macro.cc new file mode 100644 index 0000000..0f5bf82 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/macro.cc @@ -0,0 +1,214 @@ +/* + * 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. + */ +/* + * @(#)macro.cc 1.28 06/12/12 + */ + +#pragma ident "@(#)macro.cc 1.28 06/12/12" + +/* + * macro.cc + * + * Handle expansion of make macros + */ + +/* + * Included files + */ +#ifdef DISTRIBUTED +#include <avo/strings.h> /* AVO_STRDUP() */ +#include <dm/Avo_DoJobMsg.h> +#endif +#include <mk/defs.h> +#include <mksh/macro.h> /* getvar(), expand_value() */ +#include <mksh/misc.h> /* getmem() */ + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ + +void +setvar_append(register Name name, register Name value) +{ + register Property macro_apx = get_prop(name->prop, macro_append_prop); + register Property macro = get_prop(name->prop, macro_prop); + int length; + String_rec destination; + wchar_t buffer[STRING_BUFFER_LENGTH]; + register Chain chain; + Name val = NULL; + + if(macro_apx == NULL) { + macro_apx = append_prop(name, macro_append_prop); + if(macro != NULL) { + macro_apx->body.macro_appendix.value = macro->body.macro.value; + } + } + + val = macro_apx->body.macro_appendix.value_to_append; + + INIT_STRING_FROM_STACK(destination, buffer); + buffer[0] = 0; + if (val != NULL) { + APPEND_NAME(val, + &destination, + (int) val->hash.length); + if (value != NULL) { + MBTOWC(wcs_buffer, " "); + append_char(wcs_buffer[0], &destination); + } + } + if (value != NULL) { + APPEND_NAME(value, + &destination, + (int) value->hash.length); + } + value = GETNAME(destination.buffer.start, FIND_LENGTH); + if (destination.free_after_use) { + retmem(destination.buffer.start); + } + macro_apx->body.macro_appendix.value_to_append = value; + + SETVAR(name, empty_name, true); +} + +/* + * setvar_envvar() + * + * This function scans the list of environment variables that have + * dynamic values and sets them. + * + * Parameters: + * + * Global variables used: + * envvar A list of environment vars with $ in value + */ +void +#ifdef DISTRIBUTED +setvar_envvar(Avo_DoJobMsg *dmake_job_msg) +#else +setvar_envvar(void) +#endif +{ + wchar_t buffer[STRING_BUFFER_LENGTH]; + int length; +#ifdef DISTRIBUTED + Property macro; +#endif + register char *mbs, *tmp_mbs_buffer = NULL; + register char *env, *tmp_mbs_buffer2 = NULL; + Envvar p; + String_rec value; + + for (p = envvar; p != NULL; p = p->next) { + if (p->already_put +#ifdef DISTRIBUTED + && !dmake_job_msg +#endif + ) { + continue; + } + INIT_STRING_FROM_STACK(value, buffer); + expand_value(p->value, &value, false); + if ((length = wslen(value.buffer.start)) >= MAXPATHLEN) { + mbs = tmp_mbs_buffer = getmem((length + 1) * MB_LEN_MAX); + (void) wcstombs(mbs, + value.buffer.start, + (length + 1) * MB_LEN_MAX); + } else { + mbs = mbs_buffer; + WCSTOMBS(mbs, value.buffer.start); + } + length = 2 + strlen(p->name->string_mb) + strlen(mbs); + if (!p->already_put || length > (MAXPATHLEN * MB_LEN_MAX)) { + env = tmp_mbs_buffer2 = getmem(length); + } else { + env = mbs_buffer2; + } + (void) sprintf(env, + "%s=%s", + p->name->string_mb, + mbs); + if (!p->already_put) { + (void) putenv(env); + p->already_put = true; + if (p->env_string) { + retmem_mb(p->env_string); + } + p->env_string = env; + tmp_mbs_buffer2 = NULL; // We should not return this memory now + } +#ifdef DISTRIBUTED + if (dmake_job_msg) { + dmake_job_msg->appendVar(env); + } +#endif + if (tmp_mbs_buffer2) { + retmem_mb(tmp_mbs_buffer2); + tmp_mbs_buffer2 = NULL; + } + if (tmp_mbs_buffer) { + retmem_mb(tmp_mbs_buffer); + tmp_mbs_buffer = NULL; + } + } +#ifdef DISTRIBUTED + /* Append SUNPRO_DEPENDENCIES to the dmake_job_msg. */ + if (keep_state && dmake_job_msg) { + macro = get_prop(sunpro_dependencies->prop, macro_prop); + length = 2 + + strlen(sunpro_dependencies->string_mb) + + strlen(macro->body.macro.value->string_mb); + if (length > (MAXPATHLEN * MB_LEN_MAX)) { + env = tmp_mbs_buffer2 = getmem(length); + } else { + env = mbs_buffer2; + } + (void) sprintf(env, + "%s=%s", + sunpro_dependencies->string_mb, + macro->body.macro.value->string_mb); + dmake_job_msg->appendVar(env); + if (tmp_mbs_buffer2) { + retmem_mb(tmp_mbs_buffer2); + tmp_mbs_buffer2 = NULL; + } + } +#endif +} + + diff --git a/usr/src/make_src/Make/bin/make/common/main.cc b/usr/src/make_src/Make/bin/make/common/main.cc new file mode 100644 index 0000000..e594d59 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/main.cc @@ -0,0 +1,3787 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)main.cc 1.158 06/12/12 + */ + +#pragma ident "@(#)main.cc 1.158 06/12/12" + +/* + * main.cc + * + * make program main routine plus some helper routines + */ + +/* + * Included files + */ +#if defined(TEAMWARE_MAKE_CMN) +# include <avo/intl.h> +# include <avo/libcli.h> /* libcli_init() */ +# include <avo/cli_license.h> /* avo_cli_get_license() */ +# include <avo/find_dir.h> /* avo_find_run_dir() */ +# include <avo/version_string.h> +# include <avo/util.h> /* avo_init() */ +#ifdef USE_DMS_CCR +# include <avo/usage_tracking.h> +#else +# include <avo/cleanup.h> +#endif +#endif + +#if defined(TEAMWARE_MAKE_CMN) +/* This is for dmake only (not for Solaris make). + * Include code to check updates (dmake patches) + */ +#ifdef _CHECK_UPDATE_H +#include <libAU.h> +#endif +#endif + +#include <bsd/bsd.h> /* bsd_signal() */ + +#ifdef DISTRIBUTED +# include <dm/Avo_AcknowledgeMsg.h> +# include <rw/xdrstrea.h> +# include <dmrc/dmrc.h> /* dmakerc file processing */ +#endif + +#include <locale.h> /* setlocale() */ +#include <mk/copyright.h> +#include <mk/defs.h> +#include <mksdmsi18n/mksdmsi18n.h> /* libmksdmsi18n_init() */ +#include <mksh/macro.h> /* getvar() */ +#include <mksh/misc.h> /* getmem(), setup_char_semantics() */ + +#if defined(TEAMWARE_MAKE_CMN) +#ifdef USE_DMS_CCR +# include <pubdmsi18n/pubdmsi18n.h> /* libpubdmsi18n_init() */ +#endif +#endif + +#include <pwd.h> /* getpwnam() */ +#include <setjmp.h> +#include <signal.h> +#include <stdlib.h> +#include <sys/errno.h> /* ENOENT */ +#include <sys/stat.h> /* fstat() */ +#include <fcntl.h> /* open() */ + +#ifdef SUN5_0 +# include <sys/systeminfo.h> /* sysinfo() */ +#endif + +#include <sys/types.h> /* stat() */ +#include <sys/wait.h> /* wait() */ +#include <unistd.h> /* execv(), unlink(), access() */ +#include <vroot/report.h> /* report_dependency(), get_report_file() */ + +// From read2.cc +extern Name normalize_name(register wchar_t *name_string, register int length); + +// From parallel.cc +#if defined(TEAMWARE_MAKE_CMN) +#define MAXJOBS_ADJUST_RFE4694000 + +#ifdef MAXJOBS_ADJUST_RFE4694000 +extern void job_adjust_fini(); +#endif /* MAXJOBS_ADJUST_RFE4694000 */ +#endif /* TEAMWARE_MAKE_CMN */ + +#if defined(linux) +#include <ctype.h> +#endif + +/* + * Defined macros + */ +#define LD_SUPPORT_ENV_VAR NOCATGETS("SGS_SUPPORT") +#define LD_SUPPORT_MAKE_LIB NOCATGETS("libmakestate.so.1") + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static char *argv_zero_string; +static Boolean build_failed_ever_seen; +static Boolean continue_after_error_ever_seen; /* `-k' */ +static Boolean dmake_group_specified; /* `-g' */ +static Boolean dmake_max_jobs_specified; /* `-j' */ +static Boolean dmake_mode_specified; /* `-m' */ +static Boolean dmake_add_mode_specified; /* `-x' */ +static Boolean dmake_output_mode_specified; /* `-x DMAKE_OUTPUT_MODE=' */ +static Boolean dmake_compat_mode_specified; /* `-x SUN_MAKE_COMPAT_MODE=' */ +static Boolean dmake_odir_specified; /* `-o' */ +static Boolean dmake_rcfile_specified; /* `-c' */ +static Boolean env_wins; /* `-e' */ +static Boolean ignore_default_mk; /* `-r' */ +static Boolean list_all_targets; /* `-T' */ +static int mf_argc; +static char **mf_argv; +static Dependency_rec not_auto_depen_struct; +static Dependency not_auto_depen = ¬_auto_depen_struct; +static Boolean pmake_cap_r_specified; /* `-R' */ +static Boolean pmake_machinesfile_specified; /* `-M' */ +static Boolean stop_after_error_ever_seen; /* `-S' */ +static Boolean trace_status; /* `-p' */ + +#ifdef DMAKE_STATISTICS +static Boolean getname_stat = false; +#endif + +#if defined(TEAMWARE_MAKE_CMN) + static time_t start_time; + static int g_argc; + static char **g_argv; +#ifdef USE_DMS_CCR + static Avo_usage_tracking *usageTracking = NULL; +#else + static Avo_cleanup *cleanup = NULL; +#endif +#endif + +/* + * File table of contents + */ +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + extern "C" void cleanup_after_exit(void); +#else + extern void cleanup_after_exit(int, ...); +#endif + +#ifdef TEAMWARE_MAKE_CMN +extern "C" { + extern void dmake_exit_callback(void); + extern void dmake_message_callback(char *); +} +#endif + +extern Name normalize_name(register wchar_t *name_string, register int length); + +extern int main(int, char * []); + +static void append_makeflags_string(Name, String); +static void doalarm(int); +static void enter_argv_values(int , char **, ASCII_Dyn_Array *); +static void make_targets(int, char **, Boolean); +static int parse_command_option(char); +static void read_command_options(int, char **); +static void read_environment(Boolean); +static void read_files_and_state(int, char **); +static Boolean read_makefile(Name, Boolean, Boolean, Boolean); +static void report_recursion(Name); +static void set_sgs_support(void); +static void setup_for_projectdir(void); +static void setup_makeflags_argv(void); +static void report_dir_enter_leave(Boolean entering); + +extern void expand_value(Name, register String , Boolean); + +#ifdef DISTRIBUTED + extern int dmake_ofd; + extern FILE* dmake_ofp; + extern int rxmPid; + extern XDR xdrs_out; +#endif +#ifdef TEAMWARE_MAKE_CMN + extern char verstring[]; +#endif + +jmp_buf jmpbuffer; +#if !defined(linux) +nl_catd catd; +#endif + +/* + * main(argc, argv) + * + * Parameters: + * argc You know what this is + * argv You know what this is + * + * Static variables used: + * list_all_targets make -T seen + * trace_status make -p seen + * + * Global variables used: + * debug_level Should we trace make actions? + * keep_state Set if .KEEP_STATE seen + * makeflags The Name "MAKEFLAGS", used to get macro + * remote_command_name Name of remote invocation cmd ("on") + * running_list List of parallel running processes + * stdout_stderr_same true if stdout and stderr are the same + * auto_dependencies The Name "SUNPRO_DEPENDENCIES" + * temp_file_directory Set to the dir where we create tmp file + * trace_reader Set to reflect tracing status + * working_on_targets Set when building user targets + */ +int +main(int argc, char *argv[]) +{ + /* + * cp is a -> to the value of the MAKEFLAGS env var, + * which has to be regular chars. + */ + register char *cp; + char make_state_dir[MAXPATHLEN]; + Boolean parallel_flag = false; + char *prognameptr; + char *slash_ptr; + mode_t um; + int i; +#ifdef TEAMWARE_MAKE_CMN + struct itimerval value; + char def_dmakerc_path[MAXPATHLEN]; + Name dmake_name, dmake_name2; + Name dmake_value, dmake_value2; + Property prop, prop2; + struct stat statbuf; + int statval; +#endif + +#ifndef PARALLEL + struct stat out_stat, err_stat; +#endif + hostid = gethostid(); +#ifdef TEAMWARE_MAKE_CMN + avo_get_user(NULL, NULL); // Initialize user name +#endif + bsd_signals(); + + (void) setlocale(LC_ALL, ""); + +#if defined (HP_UX) || defined(linux) + /* HP-UX users typically will not have NLSPATH set, and this binary + * requires that it be set. On HP-UX 9.0x, /usr/lib/nls/%L/%N.cat is + * the path to set it to. + */ + + if (getenv(NOCATGETS("NLSPATH")) == NULL) { + putenv(NOCATGETS("NLSPATH=/usr/lib/nls/%L/%N.cat")); + } +#endif + +#ifdef DMAKE_STATISTICS + if (getenv(NOCATGETS("DMAKE_STATISTICS"))) { + getname_stat = true; + } +#endif + + + /* + * avo_init() sets the umask to 0. Save it here and restore + * it after the avo_init() call. + */ +#if defined(TEAMWARE_MAKE_CMN) || defined(MAKETOOL) + um = umask(0); + avo_init(argv[0]); + umask(um); + +#ifdef USE_DMS_CCR + usageTracking = new Avo_usage_tracking(NOCATGETS("dmake"), argc, argv); +#else + cleanup = new Avo_cleanup(NOCATGETS("dmake"), argc, argv); +#endif +#endif + +#if defined(TEAMWARE_MAKE_CMN) + catd = catopen(AVO_DOMAIN_DMAKE, NL_CAT_LOCALE); + libcli_init(); + +#ifdef _CHECK_UPDATE_H + /* This is for dmake only (not for Solaris make). + * Check (in background) if there is an update (dmake patch) + * and inform user + */ + { + Avo_err *err; + char *dir; + err = avo_find_run_dir(&dir); + if (AVO_OK == err) { + AU_check_update_service(NOCATGETS("Dmake"), dir); + } + } +#endif /* _CHECK_UPDATE_H */ +#endif + +// ---> fprintf(stderr, catgets(catd, 15, 666, "--- SUN make ---\n")); + + +#if defined(TEAMWARE_MAKE_CMN) || defined(MAKETOOL) +/* + * I put libmksdmsi18n_init() under #ifdef because it requires avo_i18n_init() + * from avo_util library. + */ + libmksdmsi18n_init(); +#ifdef USE_DMS_CCR + libpubdmsi18n_init(); +#endif +#endif + + +#ifndef TEAMWARE_MAKE_CMN + textdomain(NOCATGETS("SUNW_SPRO_MAKE")); +#endif /* TEAMWARE_MAKE_CMN */ + +#ifdef TEAMWARE_MAKE_CMN + g_argc = argc; + g_argv = (char **) malloc((g_argc + 1) * sizeof(char *)); + for (i = 0; i < argc; i++) { + g_argv[i] = argv[i]; + } + g_argv[i] = NULL; +#endif /* TEAMWARE_MAKE_CMN */ + + /* + * Set argv_zero_string to some form of argv[0] for + * recursive MAKE builds. + */ + + if (*argv[0] == (int) slash_char) { + /* argv[0] starts with a slash */ + argv_zero_string = strdup(argv[0]); + } else if (strchr(argv[0], (int) slash_char) == NULL) { + /* argv[0] contains no slashes */ + argv_zero_string = strdup(argv[0]); + } else { + /* + * argv[0] contains at least one slash, + * but doesn't start with a slash + */ + char *tmp_current_path; + char *tmp_string; + + tmp_current_path = get_current_path(); + tmp_string = getmem(strlen(tmp_current_path) + 1 + + strlen(argv[0]) + 1); + (void) sprintf(tmp_string, + "%s/%s", + tmp_current_path, + argv[0]); + argv_zero_string = strdup(tmp_string); + retmem_mb(tmp_string); + } + + /* + * The following flags are reset if we don't have the + * (.nse_depinfo or .make.state) files locked and only set + * AFTER the file has been locked. This ensures that if the user + * interrupts the program while file_lock() is waiting to lock + * the file, the interrupt handler doesn't remove a lock + * that doesn't belong to us. + */ + make_state_lockfile = NULL; + make_state_locked = false; + +#ifdef NSE + nse_depinfo_lockfile[0] = '\0'; + nse_depinfo_locked = false; +#endif + + /* + * look for last slash char in the path to look at the binary + * name. This is to resolve the hard link and invoke make + * in svr4 mode. + */ + + /* Sun OS make standart */ + svr4 = false; + posix = false; + if(!strcmp(argv_zero_string, NOCATGETS("/usr/xpg4/bin/make"))) { + svr4 = false; + posix = true; + } else { + prognameptr = strrchr(argv[0], '/'); + if(prognameptr) { + prognameptr++; + } else { + prognameptr = argv[0]; + } + if(!strcmp(prognameptr, NOCATGETS("svr4.make"))) { + svr4 = true; + posix = false; + } + } +#if !defined(HP_UX) && !defined(linux) + if (getenv(USE_SVR4_MAKE) || getenv(NOCATGETS("USE_SVID"))){ + svr4 = true; + posix = false; + } +#endif + + /* + * Find the dmake_compat_mode: posix, sun, svr4, or gnu_style, . + */ + char * dmake_compat_mode_var = getenv(NOCATGETS("SUN_MAKE_COMPAT_MODE")); + if (dmake_compat_mode_var != NULL) { + if (0 == strcasecmp(dmake_compat_mode_var, NOCATGETS("GNU"))) { + gnu_style = true; + } + //svr4 = false; + //posix = false; + } + + /* + * Temporary directory set up. + */ + char * tmpdir_var = getenv(NOCATGETS("TMPDIR")); + if (tmpdir_var != NULL && *tmpdir_var == '/' && strlen(tmpdir_var) < MAXPATHLEN) { + strcpy(mbs_buffer, tmpdir_var); + for (tmpdir_var = mbs_buffer+strlen(mbs_buffer); + *(--tmpdir_var) == '/' && tmpdir_var > mbs_buffer; + *tmpdir_var = '\0'); + if (strlen(mbs_buffer) + 32 < MAXPATHLEN) { /* 32 = strlen("/dmake.stdout.%d.%d.XXXXXX") */ + sprintf(mbs_buffer2, NOCATGETS("%s/dmake.tst.%d.XXXXXX"), + mbs_buffer, getpid()); + int fd = mkstemp(mbs_buffer2); + if (fd >= 0) { + close(fd); + unlink(mbs_buffer2); + tmpdir = strdup(mbs_buffer); + } + } + } + +#ifndef PARALLEL + /* find out if stdout and stderr point to the same place */ + if (fstat(1, &out_stat) < 0) { + fatal(catgets(catd, 1, 165, "fstat of standard out failed: %s"), errmsg(errno)); + } + if (fstat(2, &err_stat) < 0) { + fatal(catgets(catd, 1, 166, "fstat of standard error failed: %s"), errmsg(errno)); + } + if ((out_stat.st_dev == err_stat.st_dev) && + (out_stat.st_ino == err_stat.st_ino)) { + stdout_stderr_same = true; + } else { + stdout_stderr_same = false; + } +#else + stdout_stderr_same = false; +#endif + /* Make the vroot package scan the path using shell semantics */ + set_path_style(0); + + setup_char_semantics(); + + setup_for_projectdir(); + + /* + * If running with .KEEP_STATE, curdir will be set with + * the connected directory. + */ +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + (void) atexit(cleanup_after_exit); +#else + (void) on_exit(cleanup_after_exit, (char *) NULL); +#endif + + load_cached_names(); + +/* + * Set command line flags + */ + setup_makeflags_argv(); + read_command_options(mf_argc, mf_argv); + read_command_options(argc, argv); + if (debug_level > 0) { + cp = getenv(makeflags->string_mb); + (void) printf(catgets(catd, 1, 167, "MAKEFLAGS value: %s\n"), cp == NULL ? "" : cp); + } + + setup_interrupt(handle_interrupt); + + read_files_and_state(argc, argv); + +#ifdef TEAMWARE_MAKE_CMN + /* + * Find the dmake_output_mode: TXT1, TXT2 or HTML1. + */ + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_OUTPUT_MODE")); + dmake_name2 = GETNAME(wcs_buffer, FIND_LENGTH); + prop2 = get_prop(dmake_name2->prop, macro_prop); + if (prop2 == NULL) { + /* DMAKE_OUTPUT_MODE not defined, default to TXT1 mode */ + output_mode = txt1_mode; + } else { + dmake_value2 = prop2->body.macro.value; + if ((dmake_value2 == NULL) || + (IS_EQUAL(dmake_value2->string_mb, NOCATGETS("TXT1")))) { + output_mode = txt1_mode; + } else if (IS_EQUAL(dmake_value2->string_mb, NOCATGETS("TXT2"))) { + output_mode = txt2_mode; + } else if (IS_EQUAL(dmake_value2->string_mb, NOCATGETS("HTML1"))) { + output_mode = html1_mode; + } else { + warning(catgets(catd, 1, 352, "Unsupported value `%s' for DMAKE_OUTPUT_MODE after -x flag (ignored)"), + dmake_value2->string_mb); + } + } + /* + * Find the dmake_mode: distributed, parallel, or serial. + */ + if ((!pmake_cap_r_specified) && + (!pmake_machinesfile_specified)) { + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MODE")); + dmake_name2 = GETNAME(wcs_buffer, FIND_LENGTH); + prop2 = get_prop(dmake_name2->prop, macro_prop); + if (prop2 == NULL) { + /* DMAKE_MODE not defined, default to distributed mode */ + dmake_mode_type = distributed_mode; + no_parallel = false; + } else { + dmake_value2 = prop2->body.macro.value; + if ((dmake_value2 == NULL) || + (IS_EQUAL(dmake_value2->string_mb, NOCATGETS("distributed")))) { + dmake_mode_type = distributed_mode; + no_parallel = false; + } else if (IS_EQUAL(dmake_value2->string_mb, NOCATGETS("parallel"))) { + dmake_mode_type = parallel_mode; + no_parallel = false; +#ifdef SGE_SUPPORT + grid = false; + } else if (IS_EQUAL(dmake_value2->string_mb, NOCATGETS("grid"))) { + dmake_mode_type = parallel_mode; + no_parallel = false; + grid = true; +#endif + } else if (IS_EQUAL(dmake_value2->string_mb, NOCATGETS("serial"))) { + dmake_mode_type = serial_mode; + no_parallel = true; + } else { + fatal(catgets(catd, 1, 307, "Unknown dmake mode argument `%s' after -m flag"), dmake_value2->string_mb); + } + } + + if ((!list_all_targets) && + (report_dependencies_level == 0)) { + /* + * Check to see if either DMAKE_RCFILE or DMAKE_MODE is defined. + * They could be defined in the env, in the makefile, or on the + * command line. + * If neither is defined, and $(HOME)/.dmakerc does not exists, + * then print a message, and default to parallel mode. + */ +#ifdef DISTRIBUTED + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_RCFILE")); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MODE")); + dmake_name2 = GETNAME(wcs_buffer, FIND_LENGTH); + if ((((prop = get_prop(dmake_name->prop, macro_prop)) == NULL) || + ((dmake_value = prop->body.macro.value) == NULL)) && + (((prop2 = get_prop(dmake_name2->prop, macro_prop)) == NULL) || + ((dmake_value2 = prop2->body.macro.value) == NULL))) { + Boolean empty_dmakerc = true; + char *homedir = getenv(NOCATGETS("HOME")); + if ((homedir != NULL) && (strlen(homedir) < (sizeof(def_dmakerc_path) - 16))) { + sprintf(def_dmakerc_path, NOCATGETS("%s/.dmakerc"), homedir); + if ((((statval = stat(def_dmakerc_path, &statbuf)) != 0) && (errno == ENOENT)) || + ((statval == 0) && (statbuf.st_size == 0))) { + } else { + Avo_dmakerc *rcfile = new Avo_dmakerc(); + Avo_err *err = rcfile->read(def_dmakerc_path, NULL, TRUE); + if (err) { + fatal(err->str); + } + empty_dmakerc = rcfile->was_empty(); + delete rcfile; + } + } + if (empty_dmakerc) { + if (getenv(NOCATGETS("DMAKE_DEF_PRINTED")) == NULL) { + putenv(NOCATGETS("DMAKE_DEF_PRINTED=TRUE")); + (void) fprintf(stdout, catgets(catd, 1, 302, "dmake: defaulting to parallel mode.\n")); + (void) fprintf(stdout, catgets(catd, 1, 303, "See the man page dmake(1) for more information on setting up the .dmakerc file.\n")); + } + dmake_mode_type = parallel_mode; + no_parallel = false; + } + } +#else + if(dmake_mode_type == distributed_mode) { + (void) fprintf(stdout, NOCATGETS("dmake: Distributed mode not implemented.\n")); + (void) fprintf(stdout, NOCATGETS(" Defaulting to parallel mode.\n")); + dmake_mode_type = parallel_mode; + no_parallel = false; + } +#endif /* DISTRIBUTED */ + } + } +#endif + +#ifdef TEAMWARE_MAKE_CMN + parallel_flag = true; + /* XXX - This is a major hack for DMake/Licensing. */ + if (getenv(NOCATGETS("DMAKE_CHILD")) == NULL) { + if (!avo_cli_search_license(argv[0], dmake_exit_callback, TRUE, dmake_message_callback)) { + /* + * If the user can not get a TeamWare license, + * default to serial mode. + */ + dmake_mode_type = serial_mode; + no_parallel = true; + } else { + putenv(NOCATGETS("DMAKE_CHILD=TRUE")); + } + start_time = time(NULL); + /* + * XXX - Hack to disable SIGALRM's from licensing library's + * setitimer(). + */ + value.it_interval.tv_sec = 0; + value.it_interval.tv_usec = 0; + value.it_value.tv_sec = 0; + value.it_value.tv_usec = 0; + (void) setitimer(ITIMER_REAL, &value, NULL); + } + +// +// If dmake is running with -t option, set dmake_mode_type to serial. +// This is done because doname() calls touch_command() that runs serially. +// If we do not do that, maketool will have problems. +// + if(touch) { + dmake_mode_type = serial_mode; + no_parallel = true; + } +#else + parallel_flag = false; +#endif + +#if defined (TEAMWARE_MAKE_CMN) && defined(REDIRECT_ERR) + /* + * Check whether stdout and stderr are physically same. + * This is in order to decide whether we need to redirect + * stderr separately from stdout. + * This check is performed only if __DMAKE_SEPARATE_STDERR + * is not set. This variable may be used in order to preserve + * the 'old' behaviour. + */ + out_err_same = true; + char * dmake_sep_var = getenv(NOCATGETS("__DMAKE_SEPARATE_STDERR")); + if (dmake_sep_var == NULL || (0 != strcasecmp(dmake_sep_var, NOCATGETS("NO")))) { + struct stat stdout_stat; + struct stat stderr_stat; + if( (fstat(1, &stdout_stat) == 0) + && (fstat(2, &stderr_stat) == 0) ) + { + if( (stdout_stat.st_dev != stderr_stat.st_dev) + || (stdout_stat.st_ino != stderr_stat.st_ino) ) + { + out_err_same = false; + } + } + } +#endif + +#ifdef DISTRIBUTED + /* + * At this point, DMake should startup an rxm with any and all + * DMake command line options. Rxm will, among other things, + * read the rc file. + */ + if ((!list_all_targets) && + (report_dependencies_level == 0) && + (dmake_mode_type == distributed_mode)) { + startup_rxm(); + } +#endif + +/* + * Enable interrupt handler for alarms + */ + (void) bsd_signal(SIGALRM, (SIG_PF)doalarm); + +/* + * Check if make should report + */ + if (getenv(sunpro_dependencies->string_mb) != NULL) { + FILE *report_file; + + report_dependency(""); + report_file = get_report_file(); + if ((report_file != NULL) && (report_file != (FILE*)-1)) { + (void) fprintf(report_file, "\n"); + } + } + +/* + * Make sure SUNPRO_DEPENDENCIES is exported (or not) properly + * and NSE_DEP. + */ + if (keep_state) { + maybe_append_prop(sunpro_dependencies, macro_prop)-> + body.macro.exported = true; +#ifdef NSE + (void) setenv(NOCATGETS("NSE_DEP"), get_current_path()); +#endif + } else { + maybe_append_prop(sunpro_dependencies, macro_prop)-> + body.macro.exported = false; + } + + working_on_targets = true; + if (trace_status) { + dump_make_state(); +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + fclose(stdout); + fclose(stderr); + exit_status = 0; +#endif + exit(0); + } + if (list_all_targets) { + dump_target_list(); +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + fclose(stdout); + fclose(stderr); + exit_status = 0; +#endif + exit(0); + } + trace_reader = false; + + /* + * Set temp_file_directory to the directory the .make.state + * file is written to. + */ + if ((slash_ptr = strrchr(make_state->string_mb, (int) slash_char)) == NULL) { + temp_file_directory = strdup(get_current_path()); + } else { + *slash_ptr = (int) nul_char; + (void) strcpy(make_state_dir, make_state->string_mb); + *slash_ptr = (int) slash_char; + /* when there is only one slash and it's the first + ** character, make_state_dir should point to '/'. + */ + if(make_state_dir[0] == '\0') { + make_state_dir[0] = '/'; + make_state_dir[1] = '\0'; + } + if (make_state_dir[0] == (int) slash_char) { + temp_file_directory = strdup(make_state_dir); + } else { + char tmp_current_path2[MAXPATHLEN]; + + (void) sprintf(tmp_current_path2, + "%s/%s", + get_current_path(), + make_state_dir); + temp_file_directory = strdup(tmp_current_path2); + } + } + +#ifdef DISTRIBUTED + building_serial = false; +#endif + + report_dir_enter_leave(true); + + make_targets(argc, argv, parallel_flag); + + report_dir_enter_leave(false); + +#ifdef NSE + exit(nse_exit_status()); +#else + if (build_failed_ever_seen) { +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + if (posix) { + exit_status = 1; + } +#endif + exit(1); + } +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + exit_status = 0; +#endif + exit(0); +#endif + /* NOTREACHED */ +} + +/* + * cleanup_after_exit() + * + * Called from exit(), performs cleanup actions. + * + * Parameters: + * status The argument exit() was called with + * arg Address of an argument vector to + * cleanup_after_exit() + * + * Global variables used: + * command_changed Set if we think .make.state should be rewritten + * current_line Is set we set commands_changed + * do_not_exec_rule + * True if -n flag on + * done The Name ".DONE", rule we run + * keep_state Set if .KEEP_STATE seen + * parallel True if building in parallel + * quest If -q is on we do not run .DONE + * report_dependencies + * True if -P flag on + * running_list List of parallel running processes + * temp_file_name The temp file is removed, if any + * catd the message catalog file + * usage_tracking Should have been constructed in main() + * should destroyed just before exiting + */ +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) +extern "C" void +cleanup_after_exit(void) +#else +void cleanup_after_exit(int status, ...) +#endif +{ + Running rp; +#ifdef NSE + char push_cmd[NSE_TFS_PUSH_LEN + 3 + + (MAXPATHLEN * MB_LEN_MAX) + 12]; + char *active; +#endif + +extern long getname_bytes_count; +extern long getname_names_count; +extern long getname_struct_count; +extern long freename_bytes_count; +extern long freename_names_count; +extern long freename_struct_count; +extern long other_alloc; + +extern long env_alloc_num; +extern long env_alloc_bytes; + + +#ifdef DMAKE_STATISTICS +if(getname_stat) { + printf(NOCATGETS(">>> Getname statistics:\n")); + printf(NOCATGETS(" Allocated:\n")); + printf(NOCATGETS(" Names: %ld\n"), getname_names_count); + printf(NOCATGETS(" Strings: %ld Kb (%ld bytes)\n"), getname_bytes_count/1000, getname_bytes_count); + printf(NOCATGETS(" Structs: %ld Kb (%ld bytes)\n"), getname_struct_count/1000, getname_struct_count); + printf(NOCATGETS(" Total bytes: %ld Kb (%ld bytes)\n"), getname_struct_count/1000 + getname_bytes_count/1000, getname_struct_count + getname_bytes_count); + + printf(NOCATGETS("\n Unallocated: %ld\n"), freename_names_count); + printf(NOCATGETS(" Names: %ld\n"), freename_names_count); + printf(NOCATGETS(" Strings: %ld Kb (%ld bytes)\n"), freename_bytes_count/1000, freename_bytes_count); + printf(NOCATGETS(" Structs: %ld Kb (%ld bytes)\n"), freename_struct_count/1000, freename_struct_count); + printf(NOCATGETS(" Total bytes: %ld Kb (%ld bytes)\n"), freename_struct_count/1000 + freename_bytes_count/1000, freename_struct_count + freename_bytes_count); + + printf(NOCATGETS("\n Total used: %ld Kb (%ld bytes)\n"), (getname_struct_count/1000 + getname_bytes_count/1000) - (freename_struct_count/1000 + freename_bytes_count/1000), (getname_struct_count + getname_bytes_count) - (freename_struct_count + freename_bytes_count)); + + printf(NOCATGETS("\n>>> Other:\n")); + printf( + NOCATGETS(" Env (%ld): %ld Kb (%ld bytes)\n"), + env_alloc_num, + env_alloc_bytes/1000, + env_alloc_bytes + ); + +} +#endif + +/* +#ifdef DISTRIBUTED + if (get_parent() == TRUE) { +#endif + */ + + parallel = false; +#ifdef SUN5_0 + /* If we used the SVR4_MAKE, don't build .DONE or .FAILED */ + if (!getenv(USE_SVR4_MAKE)){ +#endif + /* Build the target .DONE or .FAILED if we caught an error */ + if (!quest && !list_all_targets) { + Name failed_name; + + MBSTOWCS(wcs_buffer, NOCATGETS(".FAILED")); + failed_name = GETNAME(wcs_buffer, FIND_LENGTH); +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + if ((exit_status != 0) && (failed_name->prop != NULL)) { +#else + if ((status != 0) && (failed_name->prop != NULL)) { +#endif +#ifdef TEAMWARE_MAKE_CMN + /* + * [tolik] switch DMake to serial mode + */ + dmake_mode_type = serial_mode; + no_parallel = true; +#endif + (void) doname(failed_name, false, true); + } else { + if (!trace_status) { +#ifdef TEAMWARE_MAKE_CMN + /* + * Switch DMake to serial mode + */ + dmake_mode_type = serial_mode; + no_parallel = true; +#endif + (void) doname(done, false, true); + } + } + } +#ifdef SUN5_0 + } +#endif + /* + * Remove the temp file utilities report dependencies thru if it + * is still around + */ + if (temp_file_name != NULL) { + (void) unlink(temp_file_name->string_mb); + } + /* + * Do not save the current command in .make.state if make + * was interrupted. + */ + if (current_line != NULL) { + command_changed = true; + current_line->body.line.command_used = NULL; + } + /* + * For each parallel build process running, remove the temp files + * and zap the command line so it won't be put in .make.state + */ + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->temp_file != NULL) { + (void) unlink(rp->temp_file->string_mb); + } + if (rp->stdout_file != NULL) { + (void) unlink(rp->stdout_file); + retmem_mb(rp->stdout_file); + rp->stdout_file = NULL; + } + if (rp->stderr_file != NULL) { + (void) unlink(rp->stderr_file); + retmem_mb(rp->stderr_file); + rp->stderr_file = NULL; + } + command_changed = true; +/* + line = get_prop(rp->target->prop, line_prop); + if (line != NULL) { + line->body.line.command_used = NULL; + } + */ + } + /* Remove the statefile lock file if the file has been locked */ + if ((make_state_lockfile != NULL) && (make_state_locked)) { + (void) unlink(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } + /* Write .make.state */ + write_state_file(1, (Boolean) 1); + +#ifdef TEAMWARE_MAKE_CMN + // Deleting the usage tracking object sends the usage mail +#ifdef USE_DMS_CCR + //usageTracking->setExitStatus(exit_status, NULL); + //delete usageTracking; +#else + cleanup->set_exit_status(exit_status); + delete cleanup; +#endif +#endif + +#ifdef NSE + /* If running inside an activated environment, push the */ + /* .nse_depinfo file (if written) */ + active = getenv(NSE_VARIANT_ENV); + if (keep_state && + (active != NULL) && + !IS_EQUAL(active, NSE_RT_SOURCE_NAME) && + !do_not_exec_rule && + (report_dependencies_level == 0)) { + (void) sprintf(push_cmd, + "%s %s/%s", + NSE_TFS_PUSH, + get_current_path(), + NSE_DEPINFO); + (void) system(push_cmd); + } +#endif + +/* +#ifdef DISTRIBUTED + } +#endif + */ + +#if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000) + job_adjust_fini(); +#endif + +#ifdef TEAMWARE_MAKE_CMN + catclose(catd); +#endif +#ifdef DISTRIBUTED + if (rxmPid > 0) { + // Tell rxm to exit by sending it an Avo_AcknowledgeMsg + Avo_AcknowledgeMsg acknowledgeMsg; + RWCollectable *msg = (RWCollectable *)&acknowledgeMsg; + + int xdrResult = xdr(&xdrs_out, msg); + + if (xdrResult) { + fflush(dmake_ofp); + } else { +/* + fatal(catgets(catd, 1, 266, "couldn't tell rxm to exit")); + */ + kill(rxmPid, SIGTERM); + } + + waitpid(rxmPid, NULL, 0); + rxmPid = 0; + } +#endif +} + +/* + * handle_interrupt() + * + * This is where C-C traps are caught. + * + * Parameters: + * + * Global variables used (except DMake 1.0): + * current_target Sometimes the current target is removed + * do_not_exec_rule But not if -n is on + * quest or -q + * running_list List of parallel running processes + * touch Current target is not removed if -t on + */ +void +handle_interrupt(int) +{ + Property member; + Running rp; + + (void) fflush(stdout); +#ifdef DISTRIBUTED + if (rxmPid > 0) { + // Tell rxm to exit by sending it an Avo_AcknowledgeMsg + Avo_AcknowledgeMsg acknowledgeMsg; + RWCollectable *msg = (RWCollectable *)&acknowledgeMsg; + + int xdrResult = xdr(&xdrs_out, msg); + + if (xdrResult) { + fflush(dmake_ofp); + } else { + kill(rxmPid, SIGTERM); + rxmPid = 0; + } + } +#endif + if (childPid > 0) { + kill(childPid, SIGTERM); + childPid = -1; + } + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->state != build_running) { + continue; + } + if (rp->pid > 0) { + kill(rp->pid, SIGTERM); + rp->pid = -1; + } + } + if (getpid() == getpgrp()) { + bsd_signal(SIGTERM, SIG_IGN); + kill (-getpid(), SIGTERM); + } +#ifdef TEAMWARE_MAKE_CMN + /* Clean up all parallel/distributed children already finished */ + finish_children(false); +#endif + + /* Make sure the processes running under us terminate first */ + +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + while (wait((int *) NULL) != -1); +#else + while (wait((union wait*) NULL) != -1); +#endif + /* Delete the current targets unless they are precious */ + if ((current_target != NULL) && + current_target->is_member && + ((member = get_prop(current_target->prop, member_prop)) != NULL)) { + current_target = member->body.member.library; + } + if (!do_not_exec_rule && + !touch && + !quest && + (current_target != NULL) && + !(current_target->stat.is_precious || all_precious)) { + +/* BID_1030811 */ +/* azv 16 Oct 95 */ + current_target->stat.time = file_no_time; + + if (exists(current_target) != file_doesnt_exist) { + (void) fprintf(stderr, + "\n*** %s ", + current_target->string_mb); + if (current_target->stat.is_dir) { + (void) fprintf(stderr, + catgets(catd, 1, 168, "not removed.\n"), + current_target->string_mb); + } else if (unlink(current_target->string_mb) == 0) { + (void) fprintf(stderr, + catgets(catd, 1, 169, "removed.\n"), + current_target->string_mb); + } else { + (void) fprintf(stderr, + catgets(catd, 1, 170, "could not be removed: %s.\n"), + current_target->string_mb, + errmsg(errno)); + } + } + } + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->state != build_running) { + continue; + } + if (rp->target->is_member && + ((member = get_prop(rp->target->prop, member_prop)) != + NULL)) { + rp->target = member->body.member.library; + } + if (!do_not_exec_rule && + !touch && + !quest && + !(rp->target->stat.is_precious || all_precious)) { + + rp->target->stat.time = file_no_time; + if (exists(rp->target) != file_doesnt_exist) { + (void) fprintf(stderr, + "\n*** %s ", + rp->target->string_mb); + if (rp->target->stat.is_dir) { + (void) fprintf(stderr, + catgets(catd, 1, 171, "not removed.\n"), + rp->target->string_mb); + } else if (unlink(rp->target->string_mb) == 0) { + (void) fprintf(stderr, + catgets(catd, 1, 172, "removed.\n"), + rp->target->string_mb); + } else { + (void) fprintf(stderr, + catgets(catd, 1, 173, "could not be removed: %s.\n"), + rp->target->string_mb, + errmsg(errno)); + } + } + } + } + +#ifdef SGE_SUPPORT + /* Remove SGE script file */ + if (grid) { + unlink(script_file); + } +#endif + + /* Have we locked .make.state or .nse_depinfo? */ + if ((make_state_lockfile != NULL) && (make_state_locked)) { + unlink(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } +#ifdef NSE + if ((nse_depinfo_lockfile[0] != '\0') && (nse_depinfo_locked)) { + unlink(nse_depinfo_lockfile); + nse_depinfo_lockfile[0] = '\0'; + nse_depinfo_locked = false; + } +#endif + /* + * Re-read .make.state file (it might be changed by recursive make) + */ + check_state(NULL); + + report_dir_enter_leave(false); + +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + exit_status = 2; +#endif + exit(2); +} + +/* + * doalarm(sig, ...) + * + * Handle the alarm interrupt but do nothing. Side effect is to + * cause return from wait3. + * + * Parameters: + * sig + * + * Global variables used: + */ +/*ARGSUSED*/ +static void +doalarm(int) +{ + return; +} + + +/* + * read_command_options(argc, argv) + * + * Scan the cmd line options and process the ones that start with "-" + * + * Return value: + * -M argument, if any + * + * Parameters: + * argc You know what this is + * argv You know what this is + * + * Global variables used: + */ +static void +read_command_options(register int argc, register char **argv) +{ + register int ch; + int current_optind = 1; + int last_optind_with_double_hyphen = 0; + int last_optind; + int last_current_optind; + register int i; + register int j; + register int k; + register int makefile_next = 0; /* + * flag to note options: + * -c, f, g, j, m, o + */ + const char *tptr; + const char *CMD_OPTS; + + extern char *optarg; + extern int optind, opterr, optopt; + +#define SUNPRO_CMD_OPTS "-~Bbc:Ddef:g:ij:K:kM:m:NnO:o:PpqRrSsTtuVvwx:" + +#ifdef TEAMWARE_MAKE_CMN +# define SVR4_CMD_OPTS "-c:ef:g:ij:km:nO:o:pqrsTtVv" +#else +# define SVR4_CMD_OPTS "-ef:iknpqrstV" +#endif + + /* + * Added V in SVR4_CMD_OPTS also, which is going to be a hidden + * option, just to make sure that the getopt doesn't fail when some + * users leave their USE_SVR4_MAKE set and try to use the makefiles + * that are designed to issue commands like $(MAKE) -V. Anyway it + * sets the same flag but ensures that getopt doesn't fail. + */ + + opterr = 0; + optind = 1; + while (1) { + last_optind=optind; /* Save optind and current_optind values */ + last_current_optind=current_optind; /* in case we have to repeat this round. */ + if (svr4) { + CMD_OPTS=SVR4_CMD_OPTS; + ch = getopt(argc, argv, SVR4_CMD_OPTS); + } else { + CMD_OPTS=SUNPRO_CMD_OPTS; + ch = getopt(argc, argv, SUNPRO_CMD_OPTS); + } + if (ch == EOF) { + if(optind < argc) { + /* + * Fixing bug 4102537: + * Strange behaviour of command make using -- option. + * Not all argv have been processed + * Skip non-flag argv and continue processing. + */ + optind++; + current_optind++; + continue; + } else { + break; + } + + } + if (ch == '?') { + if (optopt == '-') { + /* Bug 5060758: getopt() changed behavior (s10_60), + * and now we have to deal with cases when options + * with double hyphen appear here, from -$(MAKEFLAGS) + */ + i = current_optind; + if (argv[i][0] == '-') { + if (argv[i][1] == '-') { + if (argv[i][2] != '\0') { + /* Check if this option is allowed */ + tptr = strchr(CMD_OPTS, argv[i][2]); + if (tptr) { + if (last_optind_with_double_hyphen != current_optind) { + /* This is first time we are trying to fix "--" + * problem with this option. If we come here second + * time, we will go to fatal error. + */ + last_optind_with_double_hyphen = current_optind; + + /* Eliminate first hyphen character */ + for (j=0; argv[i][j] != '\0'; j++) { + argv[i][j] = argv[i][j+1]; + } + + /* Repeat the processing of this argument */ + optind=last_optind; + current_optind=last_current_optind; + continue; + } + } + } + } + } + } + } + + if (ch == '?') { + if (svr4) { +#ifdef TEAMWARE_MAKE_CMN + fprintf(stderr, + catgets(catd, 1, 267, "Usage : dmake [ -f makefile ][ -c dmake_rcfile ][ -g dmake_group ]\n")); + fprintf(stderr, + catgets(catd, 1, 268, " [ -j dmake_max_jobs ][ -m dmake_mode ][ -o dmake_odir ]...\n")); + fprintf(stderr, + catgets(catd, 1, 269, " [ -e ][ -i ][ -k ][ -n ][ -p ][ -q ][ -r ][ -s ][ -t ][ -v ]\n")); +#else + fprintf(stderr, + catgets(catd, 1, 270, "Usage : make [ -f makefile ]... [ -e ][ -i ][ -k ][ -n ][ -p ][ -q ][ -r ]\n")); + fprintf(stderr, + catgets(catd, 1, 271, " [ -s ][ -t ]\n")); +#endif + tptr = strchr(SVR4_CMD_OPTS, optopt); + } else { +#ifdef TEAMWARE_MAKE_CMN + fprintf(stderr, + catgets(catd, 1, 272, "Usage : dmake [ -f makefile ][ -c dmake_rcfile ][ -g dmake_group ]\n")); + fprintf(stderr, + catgets(catd, 1, 273, " [ -j dmake_max_jobs ][ -K statefile ][ -m dmake_mode ][ -x MODE_NAME=VALUE ][ -o dmake_odir ]...\n")); + fprintf(stderr, + catgets(catd, 1, 274, " [ -d ][ -dd ][ -D ][ -DD ][ -e ][ -i ][ -k ][ -n ][ -p ][ -P ][ -u ][ -w ]\n")); + fprintf(stderr, + catgets(catd, 1, 275, " [ -q ][ -r ][ -s ][ -S ][ -t ][ -v ][ -V ][ target... ][ macro=value... ][ \"macro +=value\"... ]\n")); +#else + fprintf(stderr, + catgets(catd, 1, 276, "Usage : make [ -f makefile ][ -K statefile ]... [ -d ][ -dd ][ -D ][ -DD ]\n")); + fprintf(stderr, + catgets(catd, 1, 277, " [ -e ][ -i ][ -k ][ -n ][ -p ][ -P ][ -q ][ -r ][ -s ][ -S ][ -t ]\n")); + fprintf(stderr, + catgets(catd, 1, 278, " [ -u ][ -w ][ -V ][ target... ][ macro=value... ][ \"macro +=value\"... ]\n")); +#endif + tptr = strchr(SUNPRO_CMD_OPTS, optopt); + } + if (!tptr) { + fatal(catgets(catd, 1, 279, "Unknown option `-%c'"), optopt); + } else { + fatal(catgets(catd, 1, 280, "Missing argument after `-%c'"), optopt); + } + } + +#if defined(linux) + if (ch == 1) { + if(optind < argc) { + //optind++; + //current_optind++; + makefile_next = 0; + current_optind = optind; + continue; + } else { + break; + } + } +#endif + + + makefile_next |= parse_command_option(ch); + /* + * If we're done processing all of the options of + * ONE argument string... + */ + if (current_optind < optind) { + i = current_optind; + k = 0; + /* If there's an argument for an option... */ + if ((optind - current_optind) > 1) { + k = i + 1; + } + switch (makefile_next) { + case 0: + argv[i] = NULL; + /* This shouldn't happen */ + if (k) { + argv[k] = NULL; + } + break; + case 1: /* -f seen */ + argv[i] = NOCATGETS("-f"); + break; + case 2: /* -c seen */ + argv[i] = NOCATGETS("-c"); +#ifndef TEAMWARE_MAKE_CMN + warning(catgets(catd, 1, 281, "Ignoring DistributedMake -c option")); +#endif + break; + case 4: /* -g seen */ + argv[i] = NOCATGETS("-g"); +#ifndef TEAMWARE_MAKE_CMN + warning(catgets(catd, 1, 282, "Ignoring DistributedMake -g option")); +#endif + break; + case 8: /* -j seen */ + argv[i] = NOCATGETS("-j"); +#ifndef TEAMWARE_MAKE_CMN + warning(catgets(catd, 1, 283, "Ignoring DistributedMake -j option")); +#endif + break; + case 16: /* -M seen */ + argv[i] = NOCATGETS("-M"); +#ifndef TEAMWARE_MAKE_CMN + warning(catgets(catd, 1, 284, "Ignoring ParallelMake -M option")); +#endif + break; + case 32: /* -m seen */ + argv[i] = NOCATGETS("-m"); +#ifndef TEAMWARE_MAKE_CMN + warning(catgets(catd, 1, 285, "Ignoring DistributedMake -m option")); +#endif + break; +#ifndef PARALLEL + case 128: /* -O seen */ + argv[i] = NOCATGETS("-O"); + break; +#endif + case 256: /* -K seen */ + argv[i] = NOCATGETS("-K"); + break; + case 512: /* -o seen */ + argv[i] = NOCATGETS("-o"); +#ifndef TEAMWARE_MAKE_CMN + warning(catgets(catd, 1, 311, "Ignoring DistributedMake -o option")); +#endif + break; + case 1024: /* -x seen */ + argv[i] = NOCATGETS("-x"); +#ifndef TEAMWARE_MAKE_CMN + warning(catgets(catd, 1, 353, "Ignoring DistributedMake -x option")); +#endif + break; + default: /* > 1 of -c, f, g, j, K, M, m, O, o, x seen */ + fatal(catgets(catd, 1, 286, "Illegal command line. More than one option requiring\nan argument given in the same argument group")); + } + + makefile_next = 0; + current_optind = optind; + } + } +} + +static void +quote_str(char *str, char *qstr) +{ + char *to; + char *from; + + to = qstr; + for (from = str; *from; from++) { + switch (*from) { + case ';': /* End of command */ + case '(': /* Start group */ + case ')': /* End group */ + case '{': /* Start group */ + case '}': /* End group */ + case '[': /* Reg expr - any of a set of chars */ + case ']': /* End of set of chars */ + case '|': /* Pipe or logical-or */ + case '^': /* Old-fashioned pipe */ + case '&': /* Background or logical-and */ + case '<': /* Redirect stdin */ + case '>': /* Redirect stdout */ + case '*': /* Reg expr - any sequence of chars */ + case '?': /* Reg expr - any single char */ + case '$': /* Variable substitution */ + case '\'': /* Singe quote - turn off all magic */ + case '"': /* Double quote - span whitespace */ + case '`': /* Backquote - run a command */ + case '#': /* Comment */ + case ' ': /* Space (for MACRO=value1 value2 */ + case '\\': /* Escape char - turn off magic of next char */ + *to++ = '\\'; + break; + + default: + break; + } + *to++ = *from; + } + *to = '\0'; +} + +static void +unquote_str(char *str, char *qstr) +{ + char *to; + char *from; + + to = qstr; + for (from = str; *from; from++) { + if (*from == '\\') { + from++; + } + *to++ = *from; + } + *to = '\0'; +} + +/* + * Convert the MAKEFLAGS string value into a vector of char *, similar + * to argv. + */ +static void +setup_makeflags_argv() +{ + char *cp; + char *cp1; + char *cp2; + char *cp3; + char *cp_orig; + Boolean add_hyphen; + int i; + char tmp_char; + + mf_argc = 1; + cp = getenv(makeflags->string_mb); + cp_orig = cp; + + if (cp) { + /* + * If new MAKEFLAGS format, no need to add hyphen. + * If old MAKEFLAGS format, add hyphen before flags. + */ + + if ((strchr(cp, (int) hyphen_char) != NULL) || + (strchr(cp, (int) equal_char) != NULL)) { + + /* New MAKEFLAGS format */ + + add_hyphen = false; +#ifdef ADDFIX5060758 + /* Check if MAKEFLAGS value begins with multiple + * hyphen characters, and remove all duplicates. + * Usually it happens when the next command is + * used: $(MAKE) -$(MAKEFLAGS) + * This is a workaround for BugID 5060758. + */ + while (*cp) { + if (*cp != (int) hyphen_char) { + break; + } + cp++; + if (*cp == (int) hyphen_char) { + /* There are two hyphens. Skip one */ + cp_orig = cp; + cp++; + } + if (!(*cp)) { + /* There are hyphens only. Skip all */ + cp_orig = cp; + break; + } + } +#endif + } else { + + /* Old MAKEFLAGS format */ + + add_hyphen = true; + } + } + + /* Find the number of arguments in MAKEFLAGS */ + while (cp && *cp) { + /* Skip white spaces */ + while (cp && *cp && isspace(*cp)) { + cp++; + } + if (cp && *cp) { + /* Increment arg count */ + mf_argc++; + /* Go to next white space */ + while (cp && *cp && !isspace(*cp)) { + if(*cp == (int) backslash_char) { + cp++; + } + cp++; + } + } + } + /* Allocate memory for the new MAKEFLAGS argv */ + mf_argv = (char **) malloc((mf_argc + 1) * sizeof(char *)); + mf_argv[0] = NOCATGETS("MAKEFLAGS"); + /* + * Convert the MAKEFLAGS string value into a vector of char *, + * similar to argv. + */ + cp = cp_orig; + for (i = 1; i < mf_argc; i++) { + /* Skip white spaces */ + while (cp && *cp && isspace(*cp)) { + cp++; + } + if (cp && *cp) { + cp_orig = cp; + /* Go to next white space */ + while (cp && *cp && !isspace(*cp)) { + if(*cp == (int) backslash_char) { + cp++; + } + cp++; + } + tmp_char = *cp; + *cp = (int) nul_char; + if (add_hyphen) { + mf_argv[i] = getmem(2 + strlen(cp_orig)); + mf_argv[i][0] = '\0'; + (void) strcat(mf_argv[i], "-"); + // (void) strcat(mf_argv[i], cp_orig); + unquote_str(cp_orig, mf_argv[i]+1); + } else { + mf_argv[i] = getmem(2 + strlen(cp_orig)); + //mf_argv[i] = strdup(cp_orig); + unquote_str(cp_orig, mf_argv[i]); + } + *cp = tmp_char; + } + } + mf_argv[i] = NULL; +} + +/* + * parse_command_option(ch) + * + * Parse make command line options. + * + * Return value: + * Indicates if any -f -c or -M were seen + * + * Parameters: + * ch The character to parse + * + * Static variables used: + * dmake_group_specified Set for make -g + * dmake_max_jobs_specified Set for make -j + * dmake_mode_specified Set for make -m + * dmake_add_mode_specified Set for make -x + * dmake_compat_mode_specified Set for make -x SUN_MAKE_COMPAT_MODE= + * dmake_output_mode_specified Set for make -x DMAKE_OUTPUT_MODE= + * dmake_odir_specified Set for make -o + * dmake_rcfile_specified Set for make -c + * env_wins Set for make -e + * ignore_default_mk Set for make -r + * trace_status Set for make -p + * + * Global variables used: + * .make.state path & name set for make -K + * continue_after_error Set for make -k + * debug_level Set for make -d + * do_not_exec_rule Set for make -n + * filter_stderr Set for make -X + * ignore_errors_all Set for make -i + * no_parallel Set for make -R + * quest Set for make -q + * read_trace_level Set for make -D + * report_dependencies Set for make -P + * send_mtool_msgs Set for make -K + * silent_all Set for make -s + * touch Set for make -t + */ +static int +parse_command_option(register char ch) +{ + static int invert_next = 0; + int invert_this = invert_next; + + invert_next = 0; + switch (ch) { + case '-': /* Ignore "--" */ + return 0; + case '~': /* Invert next option */ + invert_next = 1; + return 0; + case 'B': /* Obsolete */ + return 0; + case 'b': /* Obsolete */ + return 0; + case 'c': /* Read alternative dmakerc file */ + if (invert_this) { + dmake_rcfile_specified = false; + } else { + dmake_rcfile_specified = true; + } + return 2; + case 'D': /* Show lines read */ + if (invert_this) { + read_trace_level--; + } else { + read_trace_level++; + } + return 0; + case 'd': /* Debug flag */ + if (invert_this) { + debug_level--; + } else { +#if defined( HP_UX) || defined(linux) + if (debug_level < 2) /* Fixes a bug on HP-UX */ +#endif + debug_level++; + } + return 0; +#ifdef NSE + case 'E': + if (invert_this) { + nse = false; + } else { + nse = true; + } + nse_init_source_suffixes(); + return 0; +#endif + case 'e': /* Environment override flag */ + if (invert_this) { + env_wins = false; + } else { + env_wins = true; + } + return 0; + case 'f': /* Read alternative makefile(s) */ + return 1; + case 'g': /* Use alternative DMake group */ + if (invert_this) { + dmake_group_specified = false; + } else { + dmake_group_specified = true; + } + return 4; + case 'i': /* Ignore errors */ + if (invert_this) { + ignore_errors_all = false; + } else { + ignore_errors_all = true; + } + return 0; + case 'j': /* Use alternative DMake max jobs */ + if (invert_this) { + dmake_max_jobs_specified = false; + } else { + dmake_max_jobs_specified = true; + } + return 8; + case 'K': /* Read alternative .make.state */ + return 256; + case 'k': /* Keep making even after errors */ + if (invert_this) { + continue_after_error = false; + } else { + continue_after_error = true; + continue_after_error_ever_seen = true; + } + return 0; + case 'M': /* Read alternative make.machines file */ + if (invert_this) { + pmake_machinesfile_specified = false; + } else { + pmake_machinesfile_specified = true; + dmake_mode_type = parallel_mode; + no_parallel = false; + } + return 16; + case 'm': /* Use alternative DMake build mode */ + if (invert_this) { + dmake_mode_specified = false; + } else { + dmake_mode_specified = true; + } + return 32; + case 'x': /* Use alternative DMake mode */ + if (invert_this) { + dmake_add_mode_specified = false; + } else { + dmake_add_mode_specified = true; + } + return 1024; + case 'N': /* Reverse -n */ + if (invert_this) { + do_not_exec_rule = true; + } else { + do_not_exec_rule = false; + } + return 0; + case 'n': /* Print, not exec commands */ + if (invert_this) { + do_not_exec_rule = false; + } else { + do_not_exec_rule = true; + } + return 0; +#ifndef PARALLEL + case 'O': /* Send job start & result msgs */ + if (invert_this) { + send_mtool_msgs = false; + } else { +#ifdef DISTRIBUTED + send_mtool_msgs = true; +#endif + } + return 128; +#endif + case 'o': /* Use alternative dmake output dir */ + if (invert_this) { + dmake_odir_specified = false; + } else { + dmake_odir_specified = true; + } + return 512; + case 'P': /* Print for selected targets */ + if (invert_this) { + report_dependencies_level--; + } else { + report_dependencies_level++; + } + return 0; + case 'p': /* Print description */ + if (invert_this) { + trace_status = false; + do_not_exec_rule = false; + } else { + trace_status = true; + do_not_exec_rule = true; + } + return 0; + case 'q': /* Question flag */ + if (invert_this) { + quest = false; + } else { + quest = true; + } + return 0; + case 'R': /* Don't run in parallel */ +#ifdef TEAMWARE_MAKE_CMN + if (invert_this) { + pmake_cap_r_specified = false; + no_parallel = false; + } else { + pmake_cap_r_specified = true; + dmake_mode_type = serial_mode; + no_parallel = true; + } +#else + warning(catgets(catd, 1, 182, "Ignoring ParallelMake -R option")); +#endif + return 0; + case 'r': /* Turn off internal rules */ + if (invert_this) { + ignore_default_mk = false; + } else { + ignore_default_mk = true; + } + return 0; + case 'S': /* Reverse -k */ + if (invert_this) { + continue_after_error = true; + } else { + continue_after_error = false; + stop_after_error_ever_seen = true; + } + return 0; + case 's': /* Silent flag */ + if (invert_this) { + silent_all = false; + } else { + silent_all = true; + } + return 0; + case 'T': /* Print target list */ + if (invert_this) { + list_all_targets = false; + do_not_exec_rule = false; + } else { + list_all_targets = true; + do_not_exec_rule = true; + } + return 0; + case 't': /* Touch flag */ + if (invert_this) { + touch = false; + } else { + touch = true; + } + return 0; + case 'u': /* Unconditional flag */ + if (invert_this) { + build_unconditional = false; + } else { + build_unconditional = true; + } + return 0; + case 'V': /* SVR4 mode */ + svr4 = true; + return 0; + case 'v': /* Version flag */ + if (invert_this) { + } else { +#ifdef TEAMWARE_MAKE_CMN + fprintf(stdout, NOCATGETS("dmake: %s\n"), verstring); +#ifdef SUN5_0 + exit_status = 0; +#endif + exit(0); +#else + warning(catgets(catd, 1, 324, "Ignoring DistributedMake -v option")); +#endif + } + return 0; + case 'w': /* Unconditional flag */ + if (invert_this) { + report_cwd = false; + } else { + report_cwd = true; + } + return 0; +#if 0 + case 'X': /* Filter stdout */ + if (invert_this) { + filter_stderr = false; + } else { + filter_stderr = true; + } + return 0; +#endif + default: + break; + } + return 0; +} + +/* + * setup_for_projectdir() + * + * Read the PROJECTDIR variable, if defined, and set the sccs path + * + * Parameters: + * + * Global variables used: + * sccs_dir_path Set to point to SCCS dir to use + */ +static void +setup_for_projectdir(void) +{ +static char path[MAXPATHLEN]; +char cwdpath[MAXPATHLEN]; +uid_t uid; +int done=0; + + /* Check if we should use PROJECTDIR when reading the SCCS dir. */ + sccs_dir_path = getenv(NOCATGETS("PROJECTDIR")); + if ((sccs_dir_path != NULL) && + (sccs_dir_path[0] != (int) slash_char)) { + struct passwd *pwent; + + { + uid = getuid(); + pwent = getpwuid(uid); + if (pwent == NULL) { + fatal(catgets(catd, 1, 188, "Bogus USERID ")); + } + if ((pwent = getpwnam(sccs_dir_path)) == NULL) { + /*empty block : it'll go & check cwd */ + } + else { + (void) sprintf(path, NOCATGETS("%s/src"), pwent->pw_dir); + if (access(path, F_OK) == 0) { + sccs_dir_path = path; + done = 1; + } else { + (void) sprintf(path, NOCATGETS("%s/source"), pwent->pw_dir); + if (access(path, F_OK) == 0) { + sccs_dir_path = path; + done = 1; + } + } + } + if (!done) { + if (getcwd(cwdpath, MAXPATHLEN - 1 )) { + + (void) sprintf(path, NOCATGETS("%s/%s"), cwdpath,sccs_dir_path); + if (access(path, F_OK) == 0) { + sccs_dir_path = path; + done = 1; + } else { + fatal(catgets(catd, 1, 189, "Bogus PROJECTDIR '%s'"), sccs_dir_path); + } + } + } + } + } +} + +/* + * set_sgs_support() + * + * Add the libmakestate.so.1 lib to the env var SGS_SUPPORT + * if it's not already in there. + * The SGS_SUPPORT env var and libmakestate.so.1 is used by + * the linker ld to report .make.state info back to make. + */ +static void +set_sgs_support() +{ + int len; + char *newpath; + char *oldpath; + static char *prev_path; + + oldpath = getenv(LD_SUPPORT_ENV_VAR); + if (oldpath == NULL) { + len = strlen(LD_SUPPORT_ENV_VAR) + 1 + + strlen(LD_SUPPORT_MAKE_LIB) + 1; + newpath = (char *) malloc(len); + sprintf(newpath, "%s=", LD_SUPPORT_ENV_VAR); + } else { + len = strlen(LD_SUPPORT_ENV_VAR) + 1 + strlen(oldpath) + 1 + + strlen(LD_SUPPORT_MAKE_LIB) + 1; + newpath = (char *) malloc(len); + sprintf(newpath, "%s=%s", LD_SUPPORT_ENV_VAR, oldpath); + } + +#if defined(TEAMWARE_MAKE_CMN) + + /* function maybe_append_str_to_env_var() is defined in avo_util library + * Serial make should not use this library !!! + */ + maybe_append_str_to_env_var(newpath, LD_SUPPORT_MAKE_LIB); +#else + if (oldpath == NULL) { + sprintf(newpath, "%s%s", newpath, LD_SUPPORT_MAKE_LIB); + } else { + sprintf(newpath, "%s:%s", newpath, LD_SUPPORT_MAKE_LIB); + } +#endif + putenv(newpath); + if (prev_path) { + free(prev_path); + } + prev_path = newpath; +} + +/* + * read_files_and_state(argc, argv) + * + * Read the makefiles we care about and the environment + * Also read the = style command line options + * + * Parameters: + * argc You know what this is + * argv You know what this is + * + * Static variables used: + * env_wins make -e, determines if env vars are RO + * ignore_default_mk make -r, determines if make.rules is read + * not_auto_depen dwight + * + * Global variables used: + * default_target_to_build Set to first proper target from file + * do_not_exec_rule Set to false when makfile is made + * dot The Name ".", used to read current dir + * empty_name The Name "", use as macro value + * keep_state Set if KEEP_STATE is in environment + * make_state The Name ".make.state", used to read file + * makefile_type Set to type of file being read + * makeflags The Name "MAKEFLAGS", used to set macro value + * not_auto dwight + * nse Set if NSE_ENV is in the environment + * read_trace_level Checked to se if the reader should trace + * report_dependencies If -P is on we do not read .make.state + * trace_reader Set if reader should trace + * virtual_root The Name "VIRTUAL_ROOT", used to check value + */ +static void +read_files_and_state(int argc, char **argv) +{ + wchar_t buffer[1000]; + wchar_t buffer_posix[1000]; + register char ch; + register char *cp; + Property def_make_macro = NULL; + Name def_make_name; + Name default_makefile; + String_rec dest; + wchar_t destbuffer[STRING_BUFFER_LENGTH]; + register int i; + register int j; + Name keep_state_name; + int length; + Name Makefile; + register Property macro; + struct stat make_state_stat; + Name makefile_name; + register int makefile_next = 0; + register Boolean makefile_read = false; + String_rec makeflags_string; + String_rec makeflags_string_posix; + String_rec * makeflags_string_current; + Name makeflags_value_saved; + register Name name; + Name new_make_value; + Boolean save_do_not_exec_rule; + Name sdotMakefile; + Name sdotmakefile_name; + static wchar_t state_file_str; + static char state_file_str_mb[MAXPATHLEN]; + static struct _Name state_filename; + Boolean temp; + char tmp_char; + wchar_t *tmp_wcs_buffer; + register Name value; + ASCII_Dyn_Array makeflags_and_macro; + Boolean is_xpg4; + +/* + * Remember current mode. It may be changed after reading makefile + * and we will have to correct MAKEFLAGS variable. + */ + is_xpg4 = posix; + + MBSTOWCS(wcs_buffer, NOCATGETS("KEEP_STATE")); + keep_state_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("Makefile")); + Makefile = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("makefile")); + makefile_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("s.makefile")); + sdotmakefile_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("s.Makefile")); + sdotMakefile = GETNAME(wcs_buffer, FIND_LENGTH); + +/* + * Set flag if NSE is active + */ +#ifdef NSE + if (getenv(NOCATGETS("NSE_ENV")) != NULL) { + nse = true; + } +#endif + +/* + * initialize global dependency entry for .NOT_AUTO + */ + not_auto_depen->next = NULL; + not_auto_depen->name = not_auto; + not_auto_depen->automatic = not_auto_depen->stale = false; + +/* + * Read internal definitions and rules. + */ + if (read_trace_level > 1) { + trace_reader = true; + } + if (!ignore_default_mk) { +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + if (svr4) { + MBSTOWCS(wcs_buffer, NOCATGETS("svr4.make.rules")); + default_makefile = GETNAME(wcs_buffer, FIND_LENGTH); + } else { + MBSTOWCS(wcs_buffer, NOCATGETS("make.rules")); + default_makefile = GETNAME(wcs_buffer, FIND_LENGTH); + } +#else + MBSTOWCS(wcs_buffer, NOCATGETS("default.mk")); + default_makefile = GETNAME(wcs_buffer, FIND_LENGTH); +#endif + default_makefile->stat.is_file = true; + + (void) read_makefile(default_makefile, + true, + false, + true); + } + + /* + * If the user did not redefine the MAKE macro in the + * default makefile (make.rules), then we'd like to + * change the macro value of MAKE to be some form + * of argv[0] for recursive MAKE builds. + */ + MBSTOWCS(wcs_buffer, NOCATGETS("MAKE")); + def_make_name = GETNAME(wcs_buffer, wslen(wcs_buffer)); + def_make_macro = get_prop(def_make_name->prop, macro_prop); + if ((def_make_macro != NULL) && + (IS_EQUAL(def_make_macro->body.macro.value->string_mb, + NOCATGETS("make")))) { + MBSTOWCS(wcs_buffer, argv_zero_string); + new_make_value = GETNAME(wcs_buffer, wslen(wcs_buffer)); + (void) SETVAR(def_make_name, + new_make_value, + false); + } + + default_target_to_build = NULL; + trace_reader = false; + +/* + * Read environment args. Let file args which follow override unless + * -e option seen. If -e option is not mentioned. + */ + read_environment(env_wins); + if (getvar(virtual_root)->hash.length == 0) { + maybe_append_prop(virtual_root, macro_prop) + ->body.macro.exported = true; + MBSTOWCS(wcs_buffer, "/"); + (void) SETVAR(virtual_root, + GETNAME(wcs_buffer, FIND_LENGTH), + false); + } + +/* + * We now scan mf_argv and argv to see if we need to set + * any of the DMake-added options/variables in MAKEFLAGS. + */ + + makeflags_and_macro.start = 0; + makeflags_and_macro.size = 0; + enter_argv_values(mf_argc, mf_argv, &makeflags_and_macro); + enter_argv_values(argc, argv, &makeflags_and_macro); + +/* + * Set MFLAGS and MAKEFLAGS + * + * Before reading makefile we do not know exactly which mode + * (posix or not) is used. So prepare two MAKEFLAGS strings + * for both posix and solaris modes because they are different. + */ + INIT_STRING_FROM_STACK(makeflags_string, buffer); + INIT_STRING_FROM_STACK(makeflags_string_posix, buffer_posix); + append_char((int) hyphen_char, &makeflags_string); + append_char((int) hyphen_char, &makeflags_string_posix); + + switch (read_trace_level) { + case 2: + append_char('D', &makeflags_string); + append_char('D', &makeflags_string_posix); + case 1: + append_char('D', &makeflags_string); + append_char('D', &makeflags_string_posix); + } + switch (debug_level) { + case 2: + append_char('d', &makeflags_string); + append_char('d', &makeflags_string_posix); + case 1: + append_char('d', &makeflags_string); + append_char('d', &makeflags_string_posix); + } +#ifdef NSE + if (nse) { + append_char('E', &makeflags_string); + } +#endif + if (env_wins) { + append_char('e', &makeflags_string); + append_char('e', &makeflags_string_posix); + } + if (ignore_errors_all) { + append_char('i', &makeflags_string); + append_char('i', &makeflags_string_posix); + } + if (continue_after_error) { + if (stop_after_error_ever_seen) { + append_char('S', &makeflags_string_posix); + append_char((int) space_char, &makeflags_string_posix); + append_char((int) hyphen_char, &makeflags_string_posix); + } + append_char('k', &makeflags_string); + append_char('k', &makeflags_string_posix); + } else { + if (stop_after_error_ever_seen + && continue_after_error_ever_seen) { + append_char('k', &makeflags_string_posix); + append_char((int) space_char, &makeflags_string_posix); + append_char((int) hyphen_char, &makeflags_string_posix); + append_char('S', &makeflags_string_posix); + } + } + if (do_not_exec_rule) { + append_char('n', &makeflags_string); + append_char('n', &makeflags_string_posix); + } + switch (report_dependencies_level) { + case 4: + append_char('P', &makeflags_string); + append_char('P', &makeflags_string_posix); + case 3: + append_char('P', &makeflags_string); + append_char('P', &makeflags_string_posix); + case 2: + append_char('P', &makeflags_string); + append_char('P', &makeflags_string_posix); + case 1: + append_char('P', &makeflags_string); + append_char('P', &makeflags_string_posix); + } + if (trace_status) { + append_char('p', &makeflags_string); + append_char('p', &makeflags_string_posix); + } + if (quest) { + append_char('q', &makeflags_string); + append_char('q', &makeflags_string_posix); + } + if (silent_all) { + append_char('s', &makeflags_string); + append_char('s', &makeflags_string_posix); + } + if (touch) { + append_char('t', &makeflags_string); + append_char('t', &makeflags_string_posix); + } + if (build_unconditional) { + append_char('u', &makeflags_string); + append_char('u', &makeflags_string_posix); + } + if (report_cwd) { + append_char('w', &makeflags_string); + append_char('w', &makeflags_string_posix); + } +#ifndef PARALLEL + /* -c dmake_rcfile */ + if (dmake_rcfile_specified) { + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_RCFILE")); + dmake_rcfile = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_rcfile, &makeflags_string); + append_makeflags_string(dmake_rcfile, &makeflags_string_posix); + } + /* -g dmake_group */ + if (dmake_group_specified) { + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_GROUP")); + dmake_group = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_group, &makeflags_string); + append_makeflags_string(dmake_group, &makeflags_string_posix); + } + /* -j dmake_max_jobs */ + if (dmake_max_jobs_specified) { + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MAX_JOBS")); + dmake_max_jobs = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_max_jobs, &makeflags_string); + append_makeflags_string(dmake_max_jobs, &makeflags_string_posix); + } + /* -m dmake_mode */ + if (dmake_mode_specified) { + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MODE")); + dmake_mode = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_mode, &makeflags_string); + append_makeflags_string(dmake_mode, &makeflags_string_posix); + } + /* -x dmake_compat_mode */ +// if (dmake_compat_mode_specified) { +// MBSTOWCS(wcs_buffer, NOCATGETS("SUN_MAKE_COMPAT_MODE")); +// dmake_compat_mode = GETNAME(wcs_buffer, FIND_LENGTH); +// append_makeflags_string(dmake_compat_mode, &makeflags_string); +// append_makeflags_string(dmake_compat_mode, &makeflags_string_posix); +// } + /* -x dmake_output_mode */ + if (dmake_output_mode_specified) { + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_OUTPUT_MODE")); + dmake_output_mode = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_output_mode, &makeflags_string); + append_makeflags_string(dmake_output_mode, &makeflags_string_posix); + } + /* -o dmake_odir */ + if (dmake_odir_specified) { + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_ODIR")); + dmake_odir = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_odir, &makeflags_string); + append_makeflags_string(dmake_odir, &makeflags_string_posix); + } + /* -M pmake_machinesfile */ + if (pmake_machinesfile_specified) { + MBSTOWCS(wcs_buffer, NOCATGETS("PMAKE_MACHINESFILE")); + pmake_machinesfile = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(pmake_machinesfile, &makeflags_string); + append_makeflags_string(pmake_machinesfile, &makeflags_string_posix); + } + /* -R */ + if (pmake_cap_r_specified) { + append_char((int) space_char, &makeflags_string); + append_char((int) hyphen_char, &makeflags_string); + append_char('R', &makeflags_string); + append_char((int) space_char, &makeflags_string_posix); + append_char((int) hyphen_char, &makeflags_string_posix); + append_char('R', &makeflags_string_posix); + } +#endif + +/* + * Make sure MAKEFLAGS is exported + */ + maybe_append_prop(makeflags, macro_prop)-> + body.macro.exported = true; + + if (makeflags_string.buffer.start[1] != (int) nul_char) { + if (makeflags_string.buffer.start[1] != (int) space_char) { + MBSTOWCS(wcs_buffer, NOCATGETS("MFLAGS")); + (void) SETVAR(GETNAME(wcs_buffer, FIND_LENGTH), + GETNAME(makeflags_string.buffer.start, + FIND_LENGTH), + false); + } else { + MBSTOWCS(wcs_buffer, NOCATGETS("MFLAGS")); + (void) SETVAR(GETNAME(wcs_buffer, FIND_LENGTH), + GETNAME(makeflags_string.buffer.start + 2, + FIND_LENGTH), + false); + } + } + +/* + * Add command line macro to POSIX makeflags_string + */ + if (makeflags_and_macro.start) { + tmp_char = (char) space_char; + cp = makeflags_and_macro.start; + do { + append_char(tmp_char, &makeflags_string_posix); + } while ( tmp_char = *cp++ ); + retmem_mb(makeflags_and_macro.start); + } + +/* + * Now set the value of MAKEFLAGS macro in accordance + * with current mode. + */ + macro = maybe_append_prop(makeflags, macro_prop); + temp = (Boolean) macro->body.macro.read_only; + macro->body.macro.read_only = false; + if(posix || gnu_style) { + makeflags_string_current = &makeflags_string_posix; + } else { + makeflags_string_current = &makeflags_string; + } + if (makeflags_string_current->buffer.start[1] == (int) nul_char) { + makeflags_value_saved = + GETNAME( makeflags_string_current->buffer.start + 1 + , FIND_LENGTH + ); + } else { + if (makeflags_string_current->buffer.start[1] != (int) space_char) { + makeflags_value_saved = + GETNAME( makeflags_string_current->buffer.start + , FIND_LENGTH + ); + } else { + makeflags_value_saved = + GETNAME( makeflags_string_current->buffer.start + 2 + , FIND_LENGTH + ); + } + } + (void) SETVAR( makeflags + , makeflags_value_saved + , false + ); + macro->body.macro.read_only = temp; + +/* + * Read command line "-f" arguments and ignore -c, g, j, K, M, m, O and o args. + */ + save_do_not_exec_rule = do_not_exec_rule; + do_not_exec_rule = false; + if (read_trace_level > 0) { + trace_reader = true; + } + + for (i = 1; i < argc; i++) { + if (argv[i] && + (argv[i][0] == (int) hyphen_char) && + (argv[i][1] == 'f') && + (argv[i][2] == (int) nul_char)) { + argv[i] = NULL; /* Remove -f */ + if (i >= argc - 1) { + fatal(catgets(catd, 1, 190, "No filename argument after -f flag")); + } + MBSTOWCS(wcs_buffer, argv[++i]); + primary_makefile = GETNAME(wcs_buffer, FIND_LENGTH); + (void) read_makefile(primary_makefile, true, true, true); + argv[i] = NULL; /* Remove filename */ + makefile_read = true; + } else if (argv[i] && + (argv[i][0] == (int) hyphen_char) && + (argv[i][1] == 'c' || + argv[i][1] == 'g' || + argv[i][1] == 'j' || + argv[i][1] == 'K' || + argv[i][1] == 'M' || + argv[i][1] == 'm' || + argv[i][1] == 'O' || + argv[i][1] == 'o') && + (argv[i][2] == (int) nul_char)) { + argv[i] = NULL; + argv[++i] = NULL; + } + } + +/* + * If no command line "-f" args then look for "makefile", and then for + * "Makefile" if "makefile" isn't found. + */ + if (!makefile_read) { + (void) read_dir(dot, + (wchar_t *) NULL, + (Property) NULL, + (wchar_t *) NULL); + if (!posix) { + if (makefile_name->stat.is_file) { + if (Makefile->stat.is_file) { + warning(catgets(catd, 1, 310, "Both `makefile' and `Makefile' exist")); + } + primary_makefile = makefile_name; + makefile_read = read_makefile(makefile_name, + false, + false, + true); + } + if (!makefile_read && + Makefile->stat.is_file) { + primary_makefile = Makefile; + makefile_read = read_makefile(Makefile, + false, + false, + true); + } + } else { + + enum sccs_stat save_m_has_sccs = NO_SCCS; + enum sccs_stat save_M_has_sccs = NO_SCCS; + + if (makefile_name->stat.is_file) { + if (Makefile->stat.is_file) { + warning(catgets(catd, 1, 191, "Both `makefile' and `Makefile' exist")); + } + } + if (makefile_name->stat.is_file) { + if (makefile_name->stat.has_sccs == NO_SCCS) { + primary_makefile = makefile_name; + makefile_read = read_makefile(makefile_name, + false, + false, + true); + } else { + save_m_has_sccs = makefile_name->stat.has_sccs; + makefile_name->stat.has_sccs = NO_SCCS; + primary_makefile = makefile_name; + makefile_read = read_makefile(makefile_name, + false, + false, + true); + } + } + if (!makefile_read && + Makefile->stat.is_file) { + if (Makefile->stat.has_sccs == NO_SCCS) { + primary_makefile = Makefile; + makefile_read = read_makefile(Makefile, + false, + false, + true); + } else { + save_M_has_sccs = Makefile->stat.has_sccs; + Makefile->stat.has_sccs = NO_SCCS; + primary_makefile = Makefile; + makefile_read = read_makefile(Makefile, + false, + false, + true); + } + } + if (!makefile_read && + makefile_name->stat.is_file) { + makefile_name->stat.has_sccs = save_m_has_sccs; + primary_makefile = makefile_name; + makefile_read = read_makefile(makefile_name, + false, + false, + true); + } + if (!makefile_read && + Makefile->stat.is_file) { + Makefile->stat.has_sccs = save_M_has_sccs; + primary_makefile = Makefile; + makefile_read = read_makefile(Makefile, + false, + false, + true); + } + } + } + do_not_exec_rule = save_do_not_exec_rule; + allrules_read = makefile_read; + trace_reader = false; + +/* + * Now get current value of MAKEFLAGS and compare it with + * the saved value we set before reading makefile. + * If they are different then MAKEFLAGS is subsequently set by + * makefile, just leave it there. Otherwise, if make mode + * is changed by using .POSIX target in makefile we need + * to correct MAKEFLAGS value. + */ + Name mf_val = getvar(makeflags); + if( (posix != is_xpg4) + && (!strcmp(mf_val->string_mb, makeflags_value_saved->string_mb))) + { + if (makeflags_string_posix.buffer.start[1] == (int) nul_char) { + (void) SETVAR(makeflags, + GETNAME(makeflags_string_posix.buffer.start + 1, + FIND_LENGTH), + false); + } else { + if (makeflags_string_posix.buffer.start[1] != (int) space_char) { + (void) SETVAR(makeflags, + GETNAME(makeflags_string_posix.buffer.start, + FIND_LENGTH), + false); + } else { + (void) SETVAR(makeflags, + GETNAME(makeflags_string_posix.buffer.start + 2, + FIND_LENGTH), + false); + } + } + } + + if (makeflags_string.free_after_use) { + retmem(makeflags_string.buffer.start); + } + if (makeflags_string_posix.free_after_use) { + retmem(makeflags_string_posix.buffer.start); + } + makeflags_string.buffer.start = NULL; + makeflags_string_posix.buffer.start = NULL; + + if (posix) { + /* + * If the user did not redefine the ARFLAGS macro in the + * default makefile (make.rules), then we'd like to + * change the macro value of ARFLAGS to be in accordance + * with "POSIX" requirements. + */ + MBSTOWCS(wcs_buffer, NOCATGETS("ARFLAGS")); + name = GETNAME(wcs_buffer, wslen(wcs_buffer)); + macro = get_prop(name->prop, macro_prop); + if ((macro != NULL) && /* Maybe (macro == NULL) || ? */ + (IS_EQUAL(macro->body.macro.value->string_mb, + NOCATGETS("rv")))) { + MBSTOWCS(wcs_buffer, NOCATGETS("-rv")); + value = GETNAME(wcs_buffer, wslen(wcs_buffer)); + (void) SETVAR(name, + value, + false); + } + } + + if (!posix && !svr4) { + set_sgs_support(); + } + + +/* + * Make sure KEEP_STATE is in the environment if KEEP_STATE is on. + */ + macro = get_prop(keep_state_name->prop, macro_prop); + if ((macro != NULL) && + macro->body.macro.exported) { + keep_state = true; + } + if (keep_state) { + if (macro == NULL) { + macro = maybe_append_prop(keep_state_name, + macro_prop); + } + macro->body.macro.exported = true; + (void) SETVAR(keep_state_name, + empty_name, + false); + + /* + * Read state file + */ + + /* Before we read state, let's make sure we have + ** right state file. + */ + /* just in case macro references are used in make_state file + ** name, we better expand them at this stage using expand_value. + */ + INIT_STRING_FROM_STACK(dest, destbuffer); + expand_value(make_state, &dest, false); + + make_state = GETNAME(dest.buffer.start, FIND_LENGTH); + + if(!stat(make_state->string_mb, &make_state_stat)) { + if(!(make_state_stat.st_mode & S_IFREG) ) { + /* copy the make_state structure to the other + ** and then let make_state point to the new + ** one. + */ + memcpy(&state_filename, make_state,sizeof(state_filename)); + state_filename.string_mb = state_file_str_mb; + /* Just a kludge to avoid two slashes back to back */ + if((make_state->hash.length == 1)&& + (make_state->string_mb[0] == '/')) { + make_state->hash.length = 0; + make_state->string_mb[0] = '\0'; + } + sprintf(state_file_str_mb,NOCATGETS("%s%s"), + make_state->string_mb,NOCATGETS("/.make.state")); + make_state = &state_filename; + /* adjust the length to reflect the appended string */ + make_state->hash.length += 12; + } + } else { /* the file doesn't exist or no permission */ + char tmp_path[MAXPATHLEN]; + char *slashp; + + if (slashp = strrchr(make_state->string_mb, '/')) { + strncpy(tmp_path, make_state->string_mb, + (slashp - make_state->string_mb)); + tmp_path[slashp - make_state->string_mb]=0; + if(strlen(tmp_path)) { + if(stat(tmp_path, &make_state_stat)) { + warning(catgets(catd, 1, 192, "directory %s for .KEEP_STATE_FILE does not exist"),tmp_path); + } + if (access(tmp_path, F_OK) != 0) { + warning(catgets(catd, 1, 193, "can't access dir %s"),tmp_path); + } + } + } + } + if (report_dependencies_level != 1) { + Makefile_type makefile_type_temp = makefile_type; + makefile_type = reading_statefile; + if (read_trace_level > 1) { + trace_reader = true; + } + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + makefile_type = makefile_type_temp; + } + } +} + +/* + * Scan the argv for options and "=" type args and make them readonly. + */ +static void +enter_argv_values(int argc, char *argv[], ASCII_Dyn_Array *makeflags_and_macro) +{ + register char *cp; + register int i; + int length; + register Name name; + int opt_separator = argc; + char tmp_char; + wchar_t *tmp_wcs_buffer; + register Name value; + Boolean append = false; + Property macro; + struct stat statbuf; + + + /* Read argv options and "=" type args and make them readonly. */ + makefile_type = reading_nothing; + for (i = 1; i < argc; ++i) { + append = false; + if (argv[i] == NULL) { + continue; + } else if (((argv[i][0] == '-') && (argv[i][1] == '-')) || + ((argv[i][0] == (int) ' ') && + (argv[i][1] == (int) '-') && + (argv[i][2] == (int) ' ') && + (argv[i][3] == (int) '-'))) { + argv[i] = NULL; + opt_separator = i; + continue; + } else if ((i < opt_separator) && (argv[i][0] == (int) hyphen_char)) { + switch (parse_command_option(argv[i][1])) { + case 1: /* -f seen */ + ++i; + continue; + case 2: /* -c seen */ + if (argv[i+1] == NULL) { + fatal(catgets(catd, 1, 194, "No dmake rcfile argument after -c flag")); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_RCFILE")); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 4: /* -g seen */ + if (argv[i+1] == NULL) { + fatal(catgets(catd, 1, 195, "No dmake group argument after -g flag")); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_GROUP")); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 8: /* -j seen */ + if (argv[i+1] == NULL) { + fatal(catgets(catd, 1, 196, "No dmake max jobs argument after -j flag")); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MAX_JOBS")); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 16: /* -M seen */ + if (argv[i+1] == NULL) { + fatal(catgets(catd, 1, 323, "No pmake machinesfile argument after -M flag")); + } + MBSTOWCS(wcs_buffer, NOCATGETS("PMAKE_MACHINESFILE")); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 32: /* -m seen */ + if (argv[i+1] == NULL) { + fatal(catgets(catd, 1, 197, "No dmake mode argument after -m flag")); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MODE")); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 128: /* -O seen */ + if (argv[i+1] == NULL) { + fatal(catgets(catd, 1, 287, "No file descriptor argument after -O flag")); + } + mtool_msgs_fd = atoi(argv[i+1]); + /* find out if mtool_msgs_fd is a valid file descriptor */ + if (fstat(mtool_msgs_fd, &statbuf) < 0) { + fatal(catgets(catd, 1, 355, "Invalid file descriptor %d after -O flag"), mtool_msgs_fd); + } + argv[i] = NULL; + argv[i+1] = NULL; + continue; + case 256: /* -K seen */ + if (argv[i+1] == NULL) { + fatal(catgets(catd, 1, 288, "No makestate filename argument after -K flag")); + } + MBSTOWCS(wcs_buffer, argv[i+1]); + make_state = GETNAME(wcs_buffer, FIND_LENGTH); + keep_state = true; + argv[i] = NULL; + argv[i+1] = NULL; + continue; + case 512: /* -o seen */ + if (argv[i+1] == NULL) { + fatal(catgets(catd, 1, 312, "No dmake output dir argument after -o flag")); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_ODIR")); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 1024: /* -x seen */ + if (argv[i+1] == NULL) { + fatal(catgets(catd, 1, 351, "No argument after -x flag")); + } + length = strlen( NOCATGETS("SUN_MAKE_COMPAT_MODE=")); + if (strncmp(argv[i+1], NOCATGETS("SUN_MAKE_COMPAT_MODE="), length) == 0) { + argv[i+1] = &argv[i+1][length]; + MBSTOWCS(wcs_buffer, NOCATGETS("SUN_MAKE_COMPAT_MODE")); + name = GETNAME(wcs_buffer, FIND_LENGTH); + dmake_compat_mode_specified = dmake_add_mode_specified; + break; + } + length = strlen( NOCATGETS("DMAKE_OUTPUT_MODE=")); + if (strncmp(argv[i+1], NOCATGETS("DMAKE_OUTPUT_MODE="), length) == 0) { + argv[i+1] = &argv[i+1][length]; + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_OUTPUT_MODE")); + name = GETNAME(wcs_buffer, FIND_LENGTH); + dmake_output_mode_specified = dmake_add_mode_specified; + } else { + warning(catgets(catd, 1, 354, "Unknown argument `%s' after -x flag (ignored)"), + argv[i+1]); + argv[i] = argv[i + 1] = NULL; + continue; + } + break; + default: /* Shouldn't reach here */ + argv[i] = NULL; + continue; + } + argv[i] = NULL; + if (i == (argc - 1)) { + break; + } + if ((length = strlen(argv[i+1])) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + (void) mbstowcs(tmp_wcs_buffer, argv[i+1], length + 1); + value = GETNAME(tmp_wcs_buffer, FIND_LENGTH); + retmem(tmp_wcs_buffer); + } else { + MBSTOWCS(wcs_buffer, argv[i+1]); + value = GETNAME(wcs_buffer, FIND_LENGTH); + } + argv[i+1] = NULL; + } else if ((cp = strchr(argv[i], (int) equal_char)) != NULL) { +/* + * Combine all macro in dynamic array + */ + if(*(cp-1) == (int) plus_char) + { + if(isspace(*(cp-2))) { + append = true; + cp--; + } + } + if(!append) + append_or_replace_macro_in_dyn_array(makeflags_and_macro, argv[i]); + + while (isspace(*(cp-1))) { + cp--; + } + tmp_char = *cp; + *cp = (int) nul_char; + MBSTOWCS(wcs_buffer, argv[i]); + *cp = tmp_char; + name = GETNAME(wcs_buffer, wslen(wcs_buffer)); + while (*cp != (int) equal_char) { + cp++; + } + cp++; + while (isspace(*cp) && (*cp != (int) nul_char)) { + cp++; + } + if ((length = strlen(cp)) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + (void) mbstowcs(tmp_wcs_buffer, cp, length + 1); + value = GETNAME(tmp_wcs_buffer, FIND_LENGTH); + retmem(tmp_wcs_buffer); + } else { + MBSTOWCS(wcs_buffer, cp); + value = GETNAME(wcs_buffer, FIND_LENGTH); + } + argv[i] = NULL; + } else { + /* Illegal MAKEFLAGS argument */ + continue; + } + if(append) { + setvar_append(name, value); + append = false; + } else { + macro = maybe_append_prop(name, macro_prop); + macro->body.macro.exported = true; + SETVAR(name, value, false)->body.macro.read_only = true; + } + } +} + +/* + * Append the DMake option and value to the MAKEFLAGS string. + */ +static void +append_makeflags_string(Name name, register String makeflags_string) +{ + char *option; + + if (strcmp(name->string_mb, NOCATGETS("DMAKE_GROUP")) == 0) { + option = NOCATGETS(" -g "); + } else if (strcmp(name->string_mb, NOCATGETS("DMAKE_MAX_JOBS")) == 0) { + option = NOCATGETS(" -j "); + } else if (strcmp(name->string_mb, NOCATGETS("DMAKE_MODE")) == 0) { + option = NOCATGETS(" -m "); + } else if (strcmp(name->string_mb, NOCATGETS("DMAKE_ODIR")) == 0) { + option = NOCATGETS(" -o "); + } else if (strcmp(name->string_mb, NOCATGETS("DMAKE_RCFILE")) == 0) { + option = NOCATGETS(" -c "); + } else if (strcmp(name->string_mb, NOCATGETS("PMAKE_MACHINESFILE")) == 0) { + option = NOCATGETS(" -M "); + } else if (strcmp(name->string_mb, NOCATGETS("DMAKE_OUTPUT_MODE")) == 0) { + option = NOCATGETS(" -x DMAKE_OUTPUT_MODE="); + } else if (strcmp(name->string_mb, NOCATGETS("SUN_MAKE_COMPAT_MODE")) == 0) { + option = NOCATGETS(" -x SUN_MAKE_COMPAT_MODE="); + } else { + fatal(catgets(catd, 1, 289, "Internal error: name not recognized in append_makeflags_string()")); + } + Property prop = maybe_append_prop(name, macro_prop); + if( prop == 0 || prop->body.macro.value == 0 || + prop->body.macro.value->string_mb == 0 ) { + return; + } + char mbs_value[MAXPATHLEN + 100]; + strcpy(mbs_value, option); + strcat(mbs_value, prop->body.macro.value->string_mb); + MBSTOWCS(wcs_buffer, mbs_value); + append_string(wcs_buffer, makeflags_string, FIND_LENGTH); +} + +/* + * read_environment(read_only) + * + * This routine reads the process environment when make starts and enters + * it as make macros. The environment variable SHELL is ignored. + * + * Parameters: + * read_only Should we make env vars read only? + * + * Global variables used: + * report_pwd Set if this make was started by other make + */ +static void +read_environment(Boolean read_only) +{ + register char **environment; + int length; + wchar_t *tmp_wcs_buffer; + Boolean alloced_tmp_wcs_buffer = false; + register wchar_t *name; + register wchar_t *value; + register Name macro; + Property val; + Boolean read_only_saved; + + reading_environment = true; + environment = environ; + for (; *environment; environment++) { + read_only_saved = read_only; + if ((length = strlen(*environment)) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + alloced_tmp_wcs_buffer = true; + (void) mbstowcs(tmp_wcs_buffer, *environment, length + 1); + name = tmp_wcs_buffer; + } else { + MBSTOWCS(wcs_buffer, *environment); + name = wcs_buffer; + } + value = (wchar_t *) wschr(name, (int) equal_char); + + /* + * Looks like there's a bug in the system, but sometimes + * you can get blank lines in *environment. + */ + if (!value) { + continue; + } + MBSTOWCS(wcs_buffer2, NOCATGETS("SHELL=")); + if (IS_WEQUALN(name, wcs_buffer2, wslen(wcs_buffer2))) { + continue; + } + MBSTOWCS(wcs_buffer2, NOCATGETS("MAKEFLAGS=")); + if (IS_WEQUALN(name, wcs_buffer2, wslen(wcs_buffer2))) { + report_pwd = true; + /* + * In POSIX mode we do not want MAKEFLAGS to be readonly. + * If the MAKEFLAGS macro is subsequently set by the makefile, + * it replaces the MAKEFLAGS variable currently found in the + * environment. + * See Assertion 50 in section 6.2.5.3 of standard P1003.3.2/D8. + */ + if(posix) { + read_only_saved = false; + } + } + + /* + * We ignore SUNPRO_DEPENDENCIES and NSE_DEP. Those + * environment variables are set by make and read by + * cpp which then writes info to .make.dependency.xxx and + * .nse_depinfo. When make is invoked by another make + * (recursive make), we don't want to read this because + * then the child make will end up writing to the parent + * directory's .make.state and .nse_depinfo and clobbering + * them. + */ + MBSTOWCS(wcs_buffer2, NOCATGETS("SUNPRO_DEPENDENCIES")); + if (IS_WEQUALN(name, wcs_buffer2, wslen(wcs_buffer2))) { + continue; + } +#ifdef NSE + MBSTOWCS(wcs_buffer2, NOCATGETS("NSE_DEP")); + if (IS_WEQUALN(name, wcs_buffer2, wslen(wcs_buffer2))) { + continue; + } +#endif + + macro = GETNAME(name, value - name); + maybe_append_prop(macro, macro_prop)->body.macro.exported = + true; + if ((value == NULL) || ((value + 1)[0] == (int) nul_char)) { + val = setvar_daemon(macro, + (Name) NULL, + false, no_daemon, false, debug_level); + } else { + val = setvar_daemon(macro, + GETNAME(value + 1, FIND_LENGTH), + false, no_daemon, false, debug_level); + } +#ifdef NSE + /* + * Must be after the call to setvar() as it sets + * imported to false. + */ + maybe_append_prop(macro, macro_prop)->body.macro.imported = true; +#endif + val->body.macro.read_only = read_only_saved; + if (alloced_tmp_wcs_buffer) { + retmem(tmp_wcs_buffer); + alloced_tmp_wcs_buffer = false; + } + } + reading_environment = false; +} + +/* + * read_makefile(makefile, complain, must_exist, report_file) + * + * Read one makefile and check the result + * + * Return value: + * false is the read failed + * + * Parameters: + * makefile The file to read + * complain Passed thru to read_simple_file() + * must_exist Passed thru to read_simple_file() + * report_file Passed thru to read_simple_file() + * + * Global variables used: + * makefile_type Set to indicate we are reading main file + * recursion_level Initialized + */ +static Boolean +read_makefile(register Name makefile, Boolean complain, Boolean must_exist, Boolean report_file) +{ + Boolean b; + + makefile_type = reading_makefile; + recursion_level = 0; +#ifdef NSE + wscpy(current_makefile, makefile->string); +#endif + reading_dependencies = true; + b = read_simple_file(makefile, true, true, complain, + must_exist, report_file, false); + reading_dependencies = false; + return b; +} + +/* + * make_targets(argc, argv, parallel_flag) + * + * Call doname on the specified targets + * + * Parameters: + * argc You know what this is + * argv You know what this is + * parallel_flag True if building in parallel + * + * Global variables used: + * build_failed_seen Used to generated message after failed -k + * commands_done Used to generate message "Up to date" + * default_target_to_build First proper target in makefile + * init The Name ".INIT", use to run command + * parallel Global parallel building flag + * quest make -q, suppresses messages + * recursion_level Initialized, used for tracing + * report_dependencies make -P, regroves whole process + */ +static void +make_targets(int argc, char **argv, Boolean parallel_flag) +{ + int i; + char *cp; + Doname result; + register Boolean target_to_make_found = false; + + (void) doname(init, true, true); + recursion_level = 1; + parallel = parallel_flag; +/* + * make remaining args + */ +#ifdef TEAMWARE_MAKE_CMN +/* + if ((report_dependencies_level == 0) && parallel) { + */ + if (parallel) { + /* + * If building targets in parallel, start all of the + * remaining args to build in parallel. + */ + for (i = 1; i < argc; i++) { + if ((cp = argv[i]) != NULL) { + commands_done = false; + if ((cp[0] == (int) period_char) && + (cp[1] == (int) slash_char)) { + cp += 2; + } + if((cp[0] == (int) ' ') && + (cp[1] == (int) '-') && + (cp[2] == (int) ' ') && + (cp[3] == (int) '-')) { + argv[i] = NULL; + continue; + } + MBSTOWCS(wcs_buffer, cp); + //default_target_to_build = GETNAME(wcs_buffer, + // FIND_LENGTH); + default_target_to_build = normalize_name(wcs_buffer, + wslen(wcs_buffer)); + if (default_target_to_build == wait_name) { + if (parallel_process_cnt > 0) { + finish_running(); + } + continue; + } + top_level_target = get_wstring(default_target_to_build->string_mb); + /* + * If we can't execute the current target in + * parallel, hold off the target processing + * to preserve the order of the targets as they appeared + * in command line. + */ + if (!parallel_ok(default_target_to_build, false) + && parallel_process_cnt > 0) { + finish_running(); + } + result = doname_check(default_target_to_build, + true, + false, + false); + gather_recursive_deps(); + if (/* !commands_done && */ + (result == build_ok) && + !quest && + (report_dependencies_level == 0) /* && + (exists(default_target_to_build) > file_doesnt_exist) */) { + if (posix) { + if (!commands_done) { + (void) printf(catgets(catd, 1, 293, "`%s' is updated.\n"), + default_target_to_build->string_mb); + } else { + if (no_action_was_taken) { + (void) printf(catgets(catd, 1, 294, "`%s': no action was taken.\n"), + default_target_to_build->string_mb); + } + } + } else { + default_target_to_build->stat.time = file_no_time; + if (!commands_done && + (exists(default_target_to_build) > file_doesnt_exist)) { + (void) printf(catgets(catd, 1, 295, "`%s' is up to date.\n"), + default_target_to_build->string_mb); + } + } + } + } + } + /* Now wait for all of the targets to finish running */ + finish_running(); + // setjmp(jmpbuffer); + + } +#endif + for (i = 1; i < argc; i++) { + if ((cp = argv[i]) != NULL) { + target_to_make_found = true; + if ((cp[0] == (int) period_char) && + (cp[1] == (int) slash_char)) { + cp += 2; + } + if((cp[0] == (int) ' ') && + (cp[1] == (int) '-') && + (cp[2] == (int) ' ') && + (cp[3] == (int) '-')) { + argv[i] = NULL; + continue; + } + MBSTOWCS(wcs_buffer, cp); + default_target_to_build = normalize_name(wcs_buffer, wslen(wcs_buffer)); + top_level_target = get_wstring(default_target_to_build->string_mb); + report_recursion(default_target_to_build); + commands_done = false; + if (parallel) { + result = (Doname) default_target_to_build->state; + } else { + result = doname_check(default_target_to_build, + true, + false, + false); + } + gather_recursive_deps(); + if (build_failed_seen) { + build_failed_ever_seen = true; + warning(catgets(catd, 1, 200, "Target `%s' not remade because of errors"), + default_target_to_build->string_mb); + } + build_failed_seen = false; + if (report_dependencies_level > 0) { + print_dependencies(default_target_to_build, + get_prop(default_target_to_build->prop, + line_prop)); + } + default_target_to_build->stat.time = + file_no_time; + if (default_target_to_build->colon_splits > 0) { + default_target_to_build->state = + build_dont_know; + } + if (!parallel && + /* !commands_done && */ + (result == build_ok) && + !quest && + (report_dependencies_level == 0) /* && + (exists(default_target_to_build) > file_doesnt_exist) */) { + if (posix) { + if (!commands_done) { + (void) printf(catgets(catd, 1, 296, "`%s' is updated.\n"), + default_target_to_build->string_mb); + } else { + if (no_action_was_taken) { + (void) printf(catgets(catd, 1, 297, "`%s': no action was taken.\n"), + default_target_to_build->string_mb); + } + } + } else { + if (!commands_done && + (exists(default_target_to_build) > file_doesnt_exist)) { + (void) printf(catgets(catd, 1, 298, "`%s' is up to date.\n"), + default_target_to_build->string_mb); + } + } + } + } + } + +/* + * If no file arguments have been encountered, + * make the first name encountered that doesnt start with a dot + */ + if (!target_to_make_found) { + if (default_target_to_build == NULL) { + fatal(catgets(catd, 1, 202, "No arguments to build")); + } + commands_done = false; + top_level_target = get_wstring(default_target_to_build->string_mb); + report_recursion(default_target_to_build); + + + if (getenv(NOCATGETS("SPRO_EXPAND_ERRORS"))){ + (void) printf(NOCATGETS("::(%s)\n"), + default_target_to_build->string_mb); + } + + +#ifdef TEAMWARE_MAKE_CMN + result = doname_parallel(default_target_to_build, true, false); +#else + result = doname_check(default_target_to_build, true, + false, false); +#endif + gather_recursive_deps(); + if (build_failed_seen) { + build_failed_ever_seen = true; + warning(catgets(catd, 1, 203, "Target `%s' not remade because of errors"), + default_target_to_build->string_mb); + } + build_failed_seen = false; + if (report_dependencies_level > 0) { + print_dependencies(default_target_to_build, + get_prop(default_target_to_build-> + prop, + line_prop)); + } + default_target_to_build->stat.time = file_no_time; + if (default_target_to_build->colon_splits > 0) { + default_target_to_build->state = build_dont_know; + } + if (/* !commands_done && */ + (result == build_ok) && + !quest && + (report_dependencies_level == 0) /* && + (exists(default_target_to_build) > file_doesnt_exist) */) { + if (posix) { + if (!commands_done) { + (void) printf(catgets(catd, 1, 299, "`%s' is updated.\n"), + default_target_to_build->string_mb); + } else { + if (no_action_was_taken) { + (void) printf(catgets(catd, 1, 300, "`%s': no action was taken.\n"), + default_target_to_build->string_mb); + } + } + } else { + if (!commands_done && + (exists(default_target_to_build) > file_doesnt_exist)) { + (void) printf(catgets(catd, 1, 301, "`%s' is up to date.\n"), + default_target_to_build->string_mb); + } + } + } + } +} + +/* + * report_recursion(target) + * + * If this is a recursive make and the parent make has KEEP_STATE on + * this routine reports the dependency to the parent make + * + * Parameters: + * target Target to report + * + * Global variables used: + * makefiles_used List of makefiles read + * recursive_name The Name ".RECURSIVE", printed + * report_dependency dwight + */ +static void +report_recursion(register Name target) +{ + register FILE *report_file = get_report_file(); + + if ((report_file == NULL) || (report_file == (FILE*)-1)) { + return; + } + if (primary_makefile == NULL) { + /* + * This can happen when there is no makefile and + * only implicit rules are being used. + */ +#ifdef NSE + nse_no_makefile(target); +#endif + return; + } + (void) fprintf(report_file, + "%s: %s ", + get_target_being_reported_for(), + recursive_name->string_mb); + report_dependency(get_current_path()); + report_dependency(target->string_mb); + report_dependency(primary_makefile->string_mb); + (void) fprintf(report_file, "\n"); +} + +/* Next function "append_or_replace_macro_in_dyn_array" must be in "misc.cc". */ +/* NIKMOL */ +extern void +append_or_replace_macro_in_dyn_array(ASCII_Dyn_Array *Ar, char *macro) +{ + register char *cp0; /* work pointer in macro */ + register char *cp1; /* work pointer in array */ + register char *cp2; /* work pointer in array */ + register char *cp3; /* work pointer in array */ + register char *name; /* macro name */ + register char *value; /* macro value */ + register int len_array; + register int len_macro; + + char * esc_value = NULL; + int esc_len; + + if (!(len_macro = strlen(macro))) return; + name = macro; + while (isspace(*(name))) { + name++; + } + if (!(value = strchr(name, (int) equal_char))) { + /* no '=' in macro */ + goto ERROR_MACRO; + } + cp0 = value; + value++; + while (isspace(*(value))) { + value++; + } + while (isspace(*(cp0-1))) { + cp0--; + } + if (cp0 <= name) goto ERROR_MACRO; /* no name */ + if (!(Ar->size)) goto ALLOC_ARRAY; + cp1 = Ar->start; + +LOOK_FOR_NAME: + if (!(cp1 = strchr(cp1, name[0]))) goto APPEND_MACRO; + if (!(cp2 = strchr(cp1, (int) equal_char))) goto APPEND_MACRO; + if (strncmp(cp1, name, (size_t)(cp0-name))) { + /* another name */ + cp1++; + goto LOOK_FOR_NAME; + } + if (cp1 != Ar->start) { + if (!isspace(*(cp1-1))) { + /* another name */ + cp1++; + goto LOOK_FOR_NAME; + } + } + for (cp3 = cp1 + (cp0-name); cp3 < cp2; cp3++) { + if (isspace(*cp3)) continue; + /* else: another name */ + cp1++; + goto LOOK_FOR_NAME; + } + /* Look for the next macro name in array */ + cp3 = cp2+1; + if (*cp3 != (int) doublequote_char) { + /* internal error */ + goto ERROR_MACRO; + } + if (!(cp3 = strchr(cp3+1, (int) doublequote_char))) { + /* internal error */ + goto ERROR_MACRO; + } + cp3++; + while (isspace(*cp3)) { + cp3++; + } + + cp2 = cp1; /* remove old macro */ + if ((*cp3) && (cp3 < Ar->start + Ar->size)) { + for (; cp3 < Ar->start + Ar->size; cp3++) { + *cp2++ = *cp3; + } + } + for (; cp2 < Ar->start + Ar->size; cp2++) { + *cp2 = 0; + } + if (*cp1) { + /* check next name */ + goto LOOK_FOR_NAME; + } + goto APPEND_MACRO; + +ALLOC_ARRAY: + if (Ar->size) { + cp1 = Ar->start; + } else { + cp1 = 0; + } + Ar->size += 128; + Ar->start = getmem(Ar->size); + for (len_array=0; len_array < Ar->size; len_array++) { + Ar->start[len_array] = 0; + } + if (cp1) { + strcpy(Ar->start, cp1); + retmem((wchar_t *) cp1); + } + +APPEND_MACRO: + len_array = strlen(Ar->start); + esc_value = (char*)malloc(strlen(value)*2 + 1); + quote_str(value, esc_value); + esc_len = strlen(esc_value) - strlen(value); + if (len_array + len_macro + esc_len + 5 >= Ar->size) goto ALLOC_ARRAY; + strcat(Ar->start, " "); + strncat(Ar->start, name, cp0-name); + strcat(Ar->start, "="); + strncat(Ar->start, esc_value, strlen(esc_value)); + free(esc_value); + return; +ERROR_MACRO: + /* Macro without '=' or with invalid left/right part */ + return; +} + +#ifdef TEAMWARE_MAKE_CMN +/* + * This function, if registered w/ avo_cli_get_license(), will be called + * if the application is about to exit because: + * 1) there has been certain unrecoverable error(s) that cause the + * application to exit immediately. + * 2) the user has lost a license while the application is running. + */ +extern "C" void +dmake_exit_callback(void) +{ + fatal(catgets(catd, 1, 306, "can not get a license, exiting...")); + exit(1); +} + +/* + * This function, if registered w/ avo_cli_get_license(), will be called + * if the application can not get a license. + */ +extern "C" void +dmake_message_callback(char *err_msg) +{ + static Boolean first = true; + + if (!first) { + return; + } + first = false; + if ((!list_all_targets) && + (report_dependencies_level == 0) && + (dmake_mode_type != serial_mode)) { + warning(catgets(catd, 1, 313, "can not get a TeamWare license, defaulting to serial mode...")); + } +} +#endif + +#ifdef DISTRIBUTED +/* + * Returns whether -c is set or not. + */ +Boolean +get_dmake_rcfile_specified(void) +{ + return(dmake_rcfile_specified); +} + +/* + * Returns whether -g is set or not. + */ +Boolean +get_dmake_group_specified(void) +{ + return(dmake_group_specified); +} + +/* + * Returns whether -j is set or not. + */ +Boolean +get_dmake_max_jobs_specified(void) +{ + return(dmake_max_jobs_specified); +} + +/* + * Returns whether -m is set or not. + */ +Boolean +get_dmake_mode_specified(void) +{ + return(dmake_mode_specified); +} + +/* + * Returns whether -o is set or not. + */ +Boolean +get_dmake_odir_specified(void) +{ + return(dmake_odir_specified); +} + +#endif + +static void +report_dir_enter_leave(Boolean entering) +{ + char rcwd[MAXPATHLEN]; +static char * mlev = NULL; + char * make_level_str = NULL; + int make_level_val = 0; + + make_level_str = getenv(NOCATGETS("MAKELEVEL")); + if(make_level_str) { + make_level_val = atoi(make_level_str); + } + if(mlev == NULL) { + mlev = (char*) malloc(MAXPATHLEN); + } + if(entering) { + sprintf(mlev, NOCATGETS("MAKELEVEL=%d"), make_level_val + 1); + } else { + make_level_val--; + sprintf(mlev, NOCATGETS("MAKELEVEL=%d"), make_level_val); + } + putenv(mlev); + + if(report_cwd) { + if(make_level_val <= 0) { + if(entering) { +#ifdef TEAMWARE_MAKE_CMN + sprintf( rcwd + , catgets(catd, 1, 329, "dmake: Entering directory `%s'\n") + , get_current_path()); +#else + sprintf( rcwd + , catgets(catd, 1, 330, "make: Entering directory `%s'\n") + , get_current_path()); +#endif + } else { +#ifdef TEAMWARE_MAKE_CMN + sprintf( rcwd + , catgets(catd, 1, 331, "dmake: Leaving directory `%s'\n") + , get_current_path()); +#else + sprintf( rcwd + , catgets(catd, 1, 332, "make: Leaving directory `%s'\n") + , get_current_path()); +#endif + } + } else { + if(entering) { +#ifdef TEAMWARE_MAKE_CMN + sprintf( rcwd + , catgets(catd, 1, 333, "dmake[%d]: Entering directory `%s'\n") + , make_level_val, get_current_path()); +#else + sprintf( rcwd + , catgets(catd, 1, 334, "make[%d]: Entering directory `%s'\n") + , make_level_val, get_current_path()); +#endif + } else { +#ifdef TEAMWARE_MAKE_CMN + sprintf( rcwd + , catgets(catd, 1, 335, "dmake[%d]: Leaving directory `%s'\n") + , make_level_val, get_current_path()); +#else + sprintf( rcwd + , catgets(catd, 1, 336, "make[%d]: Leaving directory `%s'\n") + , make_level_val, get_current_path()); +#endif + } + } + printf(NOCATGETS("%s"), rcwd); + } +} diff --git a/usr/src/make_src/Make/bin/make/common/make.cc b/usr/src/make_src/Make/bin/make/common/make.cc new file mode 100644 index 0000000..8096f2a --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/make.cc @@ -0,0 +1,29 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)make.cc 1.2 06/12/12 + */ + +#pragma ident "@(#)make.cc 1.2 06/12/12" diff --git a/usr/src/make_src/Make/bin/make/common/make.rules.file b/usr/src/make_src/Make/bin/make/common/make.rules.file new file mode 100644 index 0000000..3b4b7bf --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/make.rules.file @@ -0,0 +1,500 @@ +# +# 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 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# @(#)make.rules.file 1.22 06/12/12 +# + +SUFFIXES = .o .c .c~ .cc .cc~ .y .y~ .l .l~ .s .s~ .sh .sh~ .S .S~ .i .ln \ + .h .h~ .f .f~ .for .for~ .F .F~ .f90 .f90~ .ftn .ftn~ .mod .mod~ \ + .sym .def .def~ .p .p~ .r .r~ .cps .cps~ .C .C~ .Y .Y~ .L .L~ \ + .java .java~ .class + +.SUFFIXES: $(SUFFIXES) + +# OUTPUT_OPTION should be defined to "-o $@" when +# the default rules are used for non-local files. +OUTPUT_OPTION= + +# C language section. +CC=cc +CFLAGS= +CPPFLAGS= +LINT=lint +LINTFLAGS= +COMPILE.c=$(CC) $(CFLAGS) $(CPPFLAGS) -c +LINK.c=$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) +LINT.c=$(LINT) $(LINTFLAGS) $(CPPFLAGS) +.c: + $(LINK.c) -o $@ $< $(LDLIBS) +.c~: + $(GET) $(GFLAGS) -p $< > $*.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $*.c +.c.o: + $(COMPILE.c) $(OUTPUT_OPTION) $< +.c~.o: + $(GET) $(GFLAGS) -p $< > $*.c + $(CC) $(CFLAGS) -c $*.c +.c.i: + $(CC) $(CFLAGS) $(CPPFLAGS) -P $< +.c~.i: + $(GET) $(GFLAGS) -p $< > $*.c + $(CC) $(CFLAGS) $(CPPFLAGS) -P $*.c +.c.ln: + $(LINT.c) $(OUTPUT_OPTION) -c $< +.c~.ln: + $(GET) $(GFLAGS) -p $< > $*.c + $(LINT.c) $(OUTPUT_OPTION) -c $*.c +.c.a: + $(COMPILE.c) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.c~.a: + $(GET) $(GFLAGS) -p $< > $*.c + $(COMPILE.c) -o $% $*.c + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# C language section. yacc. +YACC=yacc +YFLAGS= +YACC.y=$(YACC) $(YFLAGS) +.y: + $(YACC.y) $< + $(LINK.c) -o $@ y.tab.c $(LDLIBS) + $(RM) y.tab.c +.y~: + $(GET) $(GFLAGS) -p $< > $*.y + $(YACC) $(YFLAGS) $*.y + $(COMPILE.c) -o $@ y.tab.c + $(RM) y.tab.c + +.y.c: + $(YACC.y) $< + mv y.tab.c $@ +.y~.c: + $(GET) $(GFLAGS) -p $< > $*.y + $(YACC) $(YFLAGS) $*.y + mv y.tab.c $@ +.y.ln: + $(YACC.y) $< + $(LINT.c) -o $@ -i y.tab.c + $(RM) y.tab.c +.y~.ln: + $(GET) $(GFLAGS) -p $< > $*.y + $(YACC.y) $*.y + $(LINT.c) -o $@ -i y.tab.c + $(RM) y.tab.c +.y.o: + $(YACC.y) $< + $(COMPILE.c) -o $@ y.tab.c + $(RM) y.tab.c +.y~.o: + $(GET) $(GFLAGS) -p $< > $*.y + $(YACC) $(YFLAGS) $*.y + $(CC) $(CFLAGS) -c y.tab.c + rm -f y.tab.c + mv y.tab.o $@ + +# C language section. lex. +LEX=lex +LFLAGS= +LEX.l=$(LEX) $(LFLAGS) -t +.l: + $(RM) $*.c + $(LEX.l) $< > $*.c + $(LINK.c) -o $@ $*.c -ll $(LDLIBS) + $(RM) $*.c +.l~: + $(GET) $(GFLAGS) -p $< > $*.l + $(LEX) $(LFLAGS) $*.l + $(CC) $(CFLAGS) -c lex.yy.c + rm -f lex.yy.c + mv lex.yy.c $@ + +.l.c : + $(RM) $@ + $(LEX.l) $< > $@ +.l~.c: + $(GET) $(GFLAGS) -p $< > $*.l + $(LEX) $(LFLAGS) $*.l + mv lex.yy.c $@ +.l.ln: + $(RM) $*.c + $(LEX.l) $< > $*.c + $(LINT.c) -o $@ -i $*.c + $(RM) $*.c +.l~.ln: + $(GET) $(GFLAGS) -p $< > $*.l + $(RM) $*.c + $(LEX.l) $*.l > $*.c + $(LINT.c) -o $@ -i $*.c + $(RM) $*.c +.l.o: + $(RM) $*.c + $(LEX.l) $< > $*.c + $(COMPILE.c) -o $@ $*.c + $(RM) $*.c +.l~.o: + $(GET) $(GFLAGS) -p $< > $*.l + $(LEX) $(LFLAGS) $*.l + $(CC) $(CFLAGS) -c lex.yy.c + rm -f lex.yy.c + mv lex.yy.c $@ + +# C++ language section. +CCC=CC +CCFLAGS= +COMPILE.cc=$(CCC) $(CCFLAGS) $(CPPFLAGS) -c +LINK.cc=$(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS) +COMPILE.C=$(CCC) $(CCFLAGS) $(CPPFLAGS) -c +LINK.C=$(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS) +.cc: + $(LINK.cc) -o $@ $< $(LDLIBS) +.cc~: + $(GET) $(GFLAGS) -p $< > $*.cc + $(LINK.cc) -o $@ $*.cc $(LDLIBS) +.cc.o: + $(COMPILE.cc) $(OUTPUT_OPTION) $< +.cc~.o: + $(GET) $(GFLAGS) -p $< > $*.cc + $(COMPILE.cc) $(OUTPUT_OPTION) $*.cc +.cc.i: + $(CCC) $(CCFLAGS) $(CPPFLAGS) -P $< +.cc~.i: + $(GET) $(GFLAGS) -p $< > $*.cc + $(CCC) $(CCFLAGS) $(CPPFLAGS) -P $*.cc +.cc.a: + $(COMPILE.cc) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.cc~.a: + $(GET) $(GFLAGS) -p $< > $*.cc + $(COMPILE.cc) -o $% $*.cc + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +.C: + $(LINK.C) -o $@ $< $(LDLIBS) +.C~: + $(GET) $(GFLAGS) -p $< > $*.C + $(LINK.C) -o $@ $*.C $(LDLIBS) +.C.o: + $(COMPILE.C) $(OUTPUT_OPTION) $< +.C~.o: + $(GET) $(GFLAGS) -p $< > $*.C + $(COMPILE.C) $(OUTPUT_OPTION) $*.C +.C.i: + $(CCC) $(CCFLAGS) $(CPPFLAGS) -P $< +.C~.i: + $(GET) $(GFLAGS) -p $< > $*.C + $(CCC) $(CCFLAGS) $(CPPFLAGS) -P $*.C +.C.a: + $(COMPILE.C) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.C~.a: + $(GET) $(GFLAGS) -p $< > $*.C + $(COMPILE.C) -o $% $*.C + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# FORTRAN section. +FC=f77 +FFLAGS= +COMPILE.f=$(FC) $(FFLAGS) -c +LINK.f=$(FC) $(FFLAGS) $(LDFLAGS) +COMPILE.F=$(FC) $(FFLAGS) $(CPPFLAGS) -c +LINK.F=$(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS) +.f: + $(LINK.f) -o $@ $< $(LDLIBS) +.f~: + $(GET) $(GFLAGS) -p $< > $*.f + $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $*.f +.f.o: + $(COMPILE.f) $(OUTPUT_OPTION) $< +.f~.o: + $(GET) $(GFLAGS) -p $< > $*.f + $(FC) $(FFLAGS) -c $*.f +.f.a: + $(COMPILE.f) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.f~.a: + $(GET) $(GFLAGS) -p $< > $*.f + $(COMPILE.f) -o $% $*.f + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.for: + $(LINK.f) -o $@ $< $(LDLIBS) +.for~: + $(GET) $(GFLAGS) -p $< > $*.for + $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $*.for +.for.o: + $(COMPILE.f) $(OUTPUT_OPTION) $< +.for~.o: + $(GET) $(GFLAGS) -p $< > $*.for + $(FC) $(FFLAGS) -c $*.for +.for.a: + $(COMPILE.f) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.for~.a: + $(GET) $(GFLAGS) -p $< > $*.for + $(COMPILE.f) -o $% $*.for + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.F: + $(LINK.F) -o $@ $< $(LDLIBS) +.F~: + $(GET) $(GFLAGS) -p $< > $*.F + $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $*.F +.F.o: + $(COMPILE.F) $(OUTPUT_OPTION) $< +.F~.o: + $(GET) $(GFLAGS) -p $< > $*.F + $(FC) $(FFLAGS) -c $*.F +.F.a: + $(COMPILE.F) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.F~.a: + $(GET) $(GFLAGS) -p $< > $*.F + $(COMPILE.F) -o $% $*.F + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# FORTRAN section. ratfor. +RFLAGS= +COMPILE.r=$(FC) $(FFLAGS) $(RFLAGS) -c +LINK.r=$(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) +.r: + $(LINK.r) -o $@ $< $(LDLIBS) +.r~: + $(GET) $(GFLAGS) -p $< > $*.r + $(LINK.r) -o $@ $*.r $(LDLIBS) +.r.o: + $(COMPILE.r) $(OUTPUT_OPTION) $< +.r~.o: + $(GET) $(GFLAGS) -p $< > $*.r + $(COMPILE.r) $(OUTPUT_OPTION) $*.r +.r.a: + $(COMPILE.r) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.r~.a: + $(GET) $(GFLAGS) -p $< > $*.r + $(COMPILE.r) -o $% $*.r + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# FORTRAN 90 section. +F90C=f90 +F90FLAGS= +COMPILE.f90=$(F90C) $(F90FLAGS) -c +LINK.f90=$(F90C) $(F90FLAGS) $(LDFLAGS) +COMPILE.ftn=$(F90C) $(F90FLAGS) -c +LINK.ftn=$(F90C) $(F90FLAGS) $(LDFLAGS) +.f90: + $(LINK.f90) -o $@ $< $(LDLIBS) +.f90~: + $(GET) $(GFLAGS) -p $< > $*.f90 + $(LINK.f90) -o $@ $*.f90 $(LDLIBS) +.f90.o: + $(COMPILE.f90) $(OUTPUT_OPTION) $< +.f90~.o: + $(GET) $(GFLAGS) -p $< > $*.f90 + $(COMPILE.f90) $(OUTPUT_OPTION) $*.f90 +.f90.a: + $(COMPILE.f90) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.f90~.a: + $(GET) $(GFLAGS) -p $< > $*.f90 + $(COMPILE.f90) -o $% $*.f90 + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.ftn: + $(LINK.ftn) -o $@ $< $(LDLIBS) +.ftn~: + $(GET) $(GFLAGS) -p $< > $*.ftn + $(LINK.ftn) -o $@ $*.ftn $(LDLIBS) +.ftn.o: + $(COMPILE.ftn) $(OUTPUT_OPTION) $< +.ftn~.o: + $(GET) $(GFLAGS) -p $< > $*.ftn + $(COMPILE.ftn) $(OUTPUT_OPTION) $*.ftn +.ftn.a: + $(COMPILE.ftn) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.ftn~.a: + $(GET) $(GFLAGS) -p $< > $*.ftn + $(COMPILE.ftn) -o $% $*.ftn + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Modula-2 section. +M2C=m2c +M2FLAGS= +MODFLAGS= +DEFFLAGS= +COMPILE.def=$(M2C) $(M2FLAGS) $(DEFFLAGS) +COMPILE.mod=$(M2C) $(M2FLAGS) $(MODFLAGS) +.def.sym: + $(COMPILE.def) -o $@ $< +.def~.sym: + $(GET) $(GFLAGS) -p $< > $*.def + $(COMPILE.def) -o $@ $*.def +.mod: + $(COMPILE.mod) -o $@ -e $@ $< +.mod~: + $(GET) $(GFLAGS) -p $< > $*.mod + $(COMPILE.mod) -o $@ -e $@ $*.mod +.mod.o: + $(COMPILE.mod) -o $@ $< +.mod~.o: + $(GET) $(GFLAGS) -p $< > $*.mod + $(COMPILE.mod) -o $@ $*.mod +.mod.a: + $(COMPILE.mod) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.mod~.a: + $(GET) $(GFLAGS) -p $< > $*.mod + $(COMPILE.mod) -o $% $*.mod + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Pascal section. +PC=pc +PFLAGS= +COMPILE.p=$(PC) $(PFLAGS) $(CPPFLAGS) -c +LINK.p=$(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS) +.p: + $(LINK.p) -o $@ $< $(LDLIBS) +.p~: + $(GET) $(GFLAGS) -p $< > $*.p + $(LINK.p) -o $@ $*.p $(LDLIBS) +.p.o: + $(COMPILE.p) $(OUTPUT_OPTION) $< +.p~.o: + $(GET) $(GFLAGS) -p $< > $*.p + $(COMPILE.p) $(OUTPUT_OPTION) $*.p +.p.a: + $(COMPILE.p) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.p~.a: + $(GET) $(GFLAGS) -p $< > $*.p + $(COMPILE.p) -o $% $*.p + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Assembly section. +AS=as +ASFLAGS= +COMPILE.s=$(AS) $(ASFLAGS) +COMPILE.S=$(CC) $(ASFLAGS) $(CPPFLAGS) -c +.s.o: + $(COMPILE.s) -o $@ $< +.s~.o: + $(GET) $(GFLAGS) -p $< > $*.s + $(COMPILE.s) -o $@ $*.s +.s.a: + $(COMPILE.s) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.s~.a: + $(GET) $(GFLAGS) -p $< > $*.s + $(COMPILE.s) -o $% $*.s + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.S.o: + $(COMPILE.S) -o $@ $< +.S~.o: + $(GET) $(GFLAGS) -p $< > $*.S + $(COMPILE.S) -o $@ $*.S +.S.a: + $(COMPILE.S) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.S~.a: + $(GET) $(GFLAGS) -p $< > $*.S + $(COMPILE.S) -o $% $*.S + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Shell section. +.sh: + $(RM) $@ + cat $< > $@ + chmod +x $@ +.sh~: + $(GET) $(GFLAGS) -p $< > $*.sh + cp $*.sh $@ + chmod a+x $@ + +# NeWS section +CPS=cps +CPSFLAGS= +.cps.h: + $(CPS) $(CPSFLAGS) $*.cps +.cps~.h: + $(GET) $(GFLAGS) -p $< > $*.cps + $(CPS) $(CPSFLAGS) $*.cps + +# JAVA section +JAVAC=javac +JAVACFLAGS= +.java.class: + $(JAVAC) $(JAVACFLAGS) $< +.java~.class: + $(GET) $(GFLAGS) -p $< > $*.java + $(JAVAC) $(JAVACFLAGS) $< + +# Miscellaneous section. +LD=ld +LDFLAGS= +LDLIBS= +MAKE=make +RM=rm -f +AR=ar +ARFLAGS=rv +GET=get +GFLAGS= + +markfile.o: markfile + echo "static char _sccsid[] = \"`grep @'(#)' markfile`\";" > markfile.c + cc -c markfile.c + $(RM) markfile.c + +SCCSFLAGS= +SCCSGETFLAGS=-s +.SCCS_GET: + sccs $(SCCSFLAGS) get $(SCCSGETFLAGS) $@ -G$@ + +.SCCS_GET_POSIX: + sccs $(SCCSFLAGS) get $(SCCSGETFLAGS) $@ + +.GET_POSIX: + $(GET) $(GFLAGS) s.$@ diff --git a/usr/src/make_src/Make/bin/make/common/misc.cc b/usr/src/make_src/Make/bin/make/common/misc.cc new file mode 100644 index 0000000..d718de0 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/misc.cc @@ -0,0 +1,1007 @@ +/* + * 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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)misc.cc 1.50 06/12/12 + */ + +#pragma ident "@(#)misc.cc 1.34 95/10/04" + +/* + * misc.cc + * + * This file contains various unclassified routines. Some main groups: + * getname + * Memory allocation + * String handling + * Property handling + * Error message handling + * Make internal state dumping + * main routine support + */ + +/* + * Included files + */ +#include <errno.h> +#include <mk/defs.h> +#include <mksh/macro.h> /* SETVAR() */ +#include <mksh/misc.h> /* enable_interrupt() */ +#include <stdarg.h> /* va_list, va_start(), va_end() */ +#include <vroot/report.h> /* SUNPRO_DEPENDENCIES */ + +#if defined(HP_UX) || defined(linux) +#include <unistd.h> +#endif + +#ifdef TEAMWARE_MAKE_CMN +#define MAXJOBS_ADJUST_RFE4694000 + +#ifdef MAXJOBS_ADJUST_RFE4694000 +extern void job_adjust_fini(); +#endif /* MAXJOBS_ADJUST_RFE4694000 */ +#endif /* TEAMWARE_MAKE_CMN */ + +#if defined(linux) +#include <time.h> /* localtime() */ +#endif + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +static void print_rule(register Name target); +static void print_target_n_deps(register Name target); + +/***************************************** + * + * getname + */ + +/***************************************** + * + * Memory allocation + */ + +/* + * free_chain() + * + * frees a chain of Name_vector's + * + * Parameters: + * ptr Pointer to the first element in the chain + * to be freed. + * + * Global variables used: + */ +void +free_chain(Name_vector ptr) +{ + if (ptr != NULL) { + if (ptr->next != NULL) { + free_chain(ptr->next); + } + free((char *) ptr); + } +} + +/***************************************** + * + * String manipulation + */ + +/***************************************** + * + * Nameblock property handling + */ + +/***************************************** + * + * Error message handling + */ + +/* + * fatal(format, args...) + * + * Print a message and die + * + * Parameters: + * format printf type format string + * args Arguments to match the format + * + * Global variables used: + * fatal_in_progress Indicates if this is a recursive call + * parallel_process_cnt Do we need to wait for anything? + * report_pwd Should we report the current path? + */ +/*VARARGS*/ +void +fatal(char * message, ...) +{ + va_list args; + + va_start(args, message); + (void) fflush(stdout); +#ifdef DISTRIBUTED + (void) fprintf(stderr, catgets(catd, 1, 262, "dmake: Fatal error: ")); +#else + (void) fprintf(stderr, catgets(catd, 1, 263, "make: Fatal error: ")); +#endif + (void) vfprintf(stderr, message, args); + (void) fprintf(stderr, "\n"); + va_end(args); + if (report_pwd) { + (void) fprintf(stderr, + catgets(catd, 1, 156, "Current working directory %s\n"), + get_current_path()); + } + (void) fflush(stderr); + if (fatal_in_progress) { +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + exit_status = 1; +#endif + exit(1); + } + fatal_in_progress = true; +#ifdef TEAMWARE_MAKE_CMN + /* Let all parallel children finish */ + if ((dmake_mode_type == parallel_mode) && + (parallel_process_cnt > 0)) { + (void) fprintf(stderr, + catgets(catd, 1, 157, "Waiting for %d %s to finish\n"), + parallel_process_cnt, + parallel_process_cnt == 1 ? + catgets(catd, 1, 158, "job") : catgets(catd, 1, 159, "jobs")); + (void) fflush(stderr); + } + + while (parallel_process_cnt > 0) { +#ifdef DISTRIBUTED + if (dmake_mode_type == distributed_mode) { + (void) await_dist(false); + } else { + await_parallel(true); + } +#else + await_parallel(true); +#endif + finish_children(false); + } +#endif + +#if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000) + job_adjust_fini(); +#endif + +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + exit_status = 1; +#endif + exit(1); +} + +/* + * warning(format, args...) + * + * Print a message and continue. + * + * Parameters: + * format printf type format string + * args Arguments to match the format + * + * Global variables used: + * report_pwd Should we report the current path? + */ +/*VARARGS*/ +void +warning(char * message, ...) +{ + va_list args; + + va_start(args, message); + (void) fflush(stdout); +#ifdef DISTRIBUTED + (void) fprintf(stderr, catgets(catd, 1, 264, "dmake: Warning: ")); +#else + (void) fprintf(stderr, catgets(catd, 1, 265, "make: Warning: ")); +#endif + (void) vfprintf(stderr, message, args); + (void) fprintf(stderr, "\n"); + va_end(args); + if (report_pwd) { + (void) fprintf(stderr, + catgets(catd, 1, 161, "Current working directory %s\n"), + get_current_path()); + } + (void) fflush(stderr); +} + +/* + * time_to_string(time) + * + * Take a numeric time value and produce + * a proper string representation. + * + * Return value: + * The string representation of the time + * + * Parameters: + * time The time we need to translate + * + * Global variables used: + */ +char * +time_to_string(const timestruc_t &time) +{ + struct tm *tm; + char buf[128]; + + if (time == file_doesnt_exist) { + return catgets(catd, 1, 163, "File does not exist"); + } + if (time == file_max_time) { + return catgets(catd, 1, 164, "Younger than any file"); + } + tm = localtime(&time.tv_sec); + strftime(buf, sizeof (buf), NOCATGETS("%c %Z"), tm); + buf[127] = (int) nul_char; + return strdup(buf); +} + +/* + * get_current_path() + * + * Stuff current_path with the current path if it isnt there already. + * + * Parameters: + * + * Global variables used: + */ +char * +get_current_path(void) +{ + char pwd[(MAXPATHLEN * MB_LEN_MAX)]; + static char *current_path; + + if (current_path == NULL) { +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + getcwd(pwd, sizeof(pwd)); +#else + (void) getwd(pwd); +#endif + if (pwd[0] == (int) nul_char) { + pwd[0] = (int) slash_char; + pwd[1] = (int) nul_char; +#ifdef DISTRIBUTED + current_path = strdup(pwd); + } else if (IS_EQUALN(pwd, NOCATGETS("/tmp_mnt"), 8)) { + current_path = strdup(pwd + 8); + } else { + current_path = strdup(pwd); + } +#else + } + current_path = strdup(pwd); +#endif + } + return current_path; +} + +/***************************************** + * + * Make internal state dumping + * + * This is a set of routines for dumping the internal make state + * Used for the -p option + */ + +/* + * dump_make_state() + * + * Dump make's internal state to stdout + * + * Parameters: + * + * Global variables used: + * svr4 Was ".SVR4" seen in makefile? + * svr4_name The Name ".SVR4", printed + * posix Was ".POSIX" seen in makefile? + * posix_name The Name ".POSIX", printed + * default_rule Points to the .DEFAULT rule + * default_rule_name The Name ".DEFAULT", printed + * default_target_to_build The first target to print + * dot_keep_state The Name ".KEEP_STATE", printed + * dot_keep_state_file The Name ".KEEP_STATE_FILE", printed + * hashtab The make hash table for Name blocks + * ignore_errors Was ".IGNORE" seen in makefile? + * ignore_name The Name ".IGNORE", printed + * keep_state Was ".KEEP_STATE" seen in makefile? + * percent_list The list of % rules + * precious The Name ".PRECIOUS", printed + * sccs_get_name The Name ".SCCS_GET", printed + * sccs_get_posix_name The Name ".SCCS_GET_POSIX", printed + * get_name The Name ".GET", printed + * get_posix_name The Name ".GET_POSIX", printed + * sccs_get_rule Points to the ".SCCS_GET" rule + * silent Was ".SILENT" seen in makefile? + * silent_name The Name ".SILENT", printed + * suffixes The suffix list from ".SUFFIXES" + * suffixes_name The Name ".SUFFIX", printed + */ +void +dump_make_state(void) +{ + Name_set::iterator p, e; + register Property prop; + register Dependency dep; + register Cmd_line rule; + Percent percent, percent_depe; + + /* Default target */ + if (default_target_to_build != NULL) { + print_rule(default_target_to_build); + } + (void) printf("\n"); + + /* .POSIX */ + if (posix) { + (void) printf("%s:\n", posix_name->string_mb); + } + + /* .DEFAULT */ + if (default_rule != NULL) { + (void) printf("%s:\n", default_rule_name->string_mb); + for (rule = default_rule; rule != NULL; rule = rule->next) { + (void) printf("\t%s\n", rule->command_line->string_mb); + } + } + + /* .IGNORE */ + if (ignore_errors) { + (void) printf("%s:\n", ignore_name->string_mb); + } + + /* .KEEP_STATE: */ + if (keep_state) { + (void) printf("%s:\n\n", dot_keep_state->string_mb); + } + + /* .PRECIOUS */ + (void) printf("%s:", precious->string_mb); + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + if ((p->stat.is_precious) || (all_precious)) { + (void) printf(" %s", p->string_mb); + } + } + (void) printf("\n"); + + /* .SCCS_GET */ + if (sccs_get_rule != NULL) { + (void) printf("%s:\n", sccs_get_name->string_mb); + for (rule = sccs_get_rule; rule != NULL; rule = rule->next) { + (void) printf("\t%s\n", rule->command_line->string_mb); + } + } + + /* .SILENT */ + if (silent) { + (void) printf("%s:\n", silent_name->string_mb); + } + + /* .SUFFIXES: */ + (void) printf("%s:", suffixes_name->string_mb); + for (dep = suffixes; dep != NULL; dep = dep->next) { + (void) printf(" %s", dep->name->string_mb); + build_suffix_list(dep->name); + } + (void) printf("\n\n"); + + /* % rules */ + for (percent = percent_list; + percent != NULL; + percent = percent->next) { + (void) printf("%s:", + percent->name->string_mb); + + for (percent_depe = percent->dependencies; + percent_depe != NULL; + percent_depe = percent_depe->next) { + (void) printf(" %s", percent_depe->name->string_mb); + } + + (void) printf("\n"); + + for (rule = percent->command_template; + rule != NULL; + rule = rule->next) { + (void) printf("\t%s\n", rule->command_line->string_mb); + } + } + + /* Suffix rules */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + Wstring wcb(p); + if (wcb.get_string()[0] == (int) period_char) { + print_rule(p); + } + } + + /* Macro assignments */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + if (((prop = get_prop(p->prop, macro_prop)) != NULL) && + (prop->body.macro.value != NULL)) { + (void) printf("%s", p->string_mb); + print_value(prop->body.macro.value, + (Daemon) prop->body.macro.daemon); + } + } + (void) printf("\n"); + + /* Conditional macro assignments */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + for (prop = get_prop(p->prop, conditional_prop); + prop != NULL; + prop = get_prop(prop->next, conditional_prop)) { + (void) printf("%s := %s", + p->string_mb, + prop->body.conditional.name-> + string_mb); + if (prop->body.conditional.append) { + printf(" +"); + } + else { + printf(" "); + } + print_value(prop->body.conditional.value, + no_daemon); + } + } + (void) printf("\n"); + + /* All other dependencies */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + if (p->colons != no_colon) { + print_rule(p); + } + } + (void) printf("\n"); +} + +/* + * print_rule(target) + * + * Print the rule for one target + * + * Parameters: + * target Target we print rule for + * + * Global variables used: + */ +static void +print_rule(register Name target) +{ + register Cmd_line rule; + register Property line; + register Dependency dependency; + + if (target->dependency_printed || + ((line = get_prop(target->prop, line_prop)) == NULL) || + ((line->body.line.command_template == NULL) && + (line->body.line.dependencies == NULL))) { + return; + } + target->dependency_printed = true; + + (void) printf("%s:", target->string_mb); + + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + (void) printf(" %s", dependency->name->string_mb); + } + + (void) printf("\n"); + + for (rule = line->body.line.command_template; + rule != NULL; + rule = rule->next) { + (void) printf("\t%s\n", rule->command_line->string_mb); + } +} + +void +dump_target_list(void) +{ + Name_set::iterator p, e; + Wstring str; + + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + str.init(p); + wchar_t * wcb = str.get_string(); + if ((p->colons != no_colon) && + ((wcb[0] != (int) period_char) || + ((wcb[0] == (int) period_char) && + (wschr(wcb, (int) slash_char))))) { + print_target_n_deps(p); + } + } +} + +static void +print_target_n_deps(register Name target) +{ + register Cmd_line rule; + register Property line; + register Dependency dependency; + + if (target->dependency_printed) { + return; + } + target->dependency_printed = true; + + (void) printf("%s\n", target->string_mb); + + if ((line = get_prop(target->prop, line_prop)) == NULL) { + return; + } + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (!dependency->automatic) { + print_target_n_deps(dependency->name); + } + } +} + +/***************************************** + * + * main() support + */ + +/* + * load_cached_names() + * + * Load the vector of cached names + * + * Parameters: + * + * Global variables used: + * Many many pointers to Name blocks. + */ +void +load_cached_names(void) +{ + char *cp; + Name dollar; + + /* Load the cached_names struct */ + MBSTOWCS(wcs_buffer, NOCATGETS(".BUILT_LAST_MAKE_RUN")); + built_last_make_run = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("@")); + c_at = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(" *conditionals* ")); + conditionals = GETNAME(wcs_buffer, FIND_LENGTH); + /* + * A version of make was released with NSE 1.0 that used + * VERSION-1.1 but this version is identical to VERSION-1.0. + * The version mismatch code makes a special case for this + * situation. If the version number is changed from 1.0 + * it should go to 1.2. + */ + MBSTOWCS(wcs_buffer, NOCATGETS("VERSION-1.0")); + current_make_version = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".SVR4")); + svr4_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".POSIX")); + posix_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".DEFAULT")); + default_rule_name = GETNAME(wcs_buffer, FIND_LENGTH); +#ifdef NSE + MBSTOWCS(wcs_buffer, NOCATGETS(".DERIVED_SRC")); + derived_src= GETNAME(wcs_buffer, FIND_LENGTH); +#endif + MBSTOWCS(wcs_buffer, NOCATGETS("$")); + dollar = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".DONE")); + done = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".")); + dot = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".KEEP_STATE")); + dot_keep_state = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".KEEP_STATE_FILE")); + dot_keep_state_file = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("")); + empty_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(" FORCE")); + force = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("HOST_ARCH")); + host_arch = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("HOST_MACH")); + host_mach = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".IGNORE")); + ignore_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".INIT")); + init = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".LOCAL")); + localhost_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".make.state")); + make_state = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("MAKEFLAGS")); + makeflags = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".MAKE_VERSION")); + make_version = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".NO_PARALLEL")); + no_parallel_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".NOT_AUTO")); + not_auto = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".PARALLEL")); + parallel_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("PATH")); + path_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("+")); + plus = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".PRECIOUS")); + precious = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("?")); + query = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("^")); + hat = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".RECURSIVE")); + recursive_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".SCCS_GET")); + sccs_get_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".SCCS_GET_POSIX")); + sccs_get_posix_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".GET")); + get_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".GET_POSIX")); + get_posix_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("SHELL")); + shell_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".SILENT")); + silent_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".SUFFIXES")); + suffixes_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, SUNPRO_DEPENDENCIES); + sunpro_dependencies = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("TARGET_ARCH")); + target_arch = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("TARGET_MACH")); + target_mach = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("VIRTUAL_ROOT")); + virtual_root = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("VPATH")); + vpath_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS(".WAIT")); + wait_name = GETNAME(wcs_buffer, FIND_LENGTH); + + wait_name->state = build_ok; + + /* Mark special targets so that the reader treats them properly */ + svr4_name->special_reader = svr4_special; + posix_name->special_reader = posix_special; + built_last_make_run->special_reader = built_last_make_run_special; + default_rule_name->special_reader = default_special; +#ifdef NSE + derived_src->special_reader= derived_src_special; +#endif + dot_keep_state->special_reader = keep_state_special; + dot_keep_state_file->special_reader = keep_state_file_special; + ignore_name->special_reader = ignore_special; + make_version->special_reader = make_version_special; + no_parallel_name->special_reader = no_parallel_special; + parallel_name->special_reader = parallel_special; + localhost_name->special_reader = localhost_special; + precious->special_reader = precious_special; + sccs_get_name->special_reader = sccs_get_special; + sccs_get_posix_name->special_reader = sccs_get_posix_special; + get_name->special_reader = get_special; + get_posix_name->special_reader = get_posix_special; + silent_name->special_reader = silent_special; + suffixes_name->special_reader = suffixes_special; + + /* The value of $$ is $ */ + (void) SETVAR(dollar, dollar, false); + dollar->dollar = false; + + /* Set the value of $(SHELL) */ + #ifdef HP_UX + MBSTOWCS(wcs_buffer, NOCATGETS("/bin/posix/sh")); + #else + #if defined(SUN5_0) + if (posix) { + MBSTOWCS(wcs_buffer, NOCATGETS("/usr/xpg4/bin/sh")); + } else { + MBSTOWCS(wcs_buffer, NOCATGETS("/bin/sh")); + } + #else /* ^SUN5_0 */ + MBSTOWCS(wcs_buffer, NOCATGETS("/bin/sh")); + #endif /* ^SUN5_0 */ + #endif + (void) SETVAR(shell_name, GETNAME(wcs_buffer, FIND_LENGTH), false); + + /* + * Use " FORCE" to simulate a FRC dependency for :: type + * targets with no dependencies. + */ + (void) append_prop(force, line_prop); + force->stat.time = file_max_time; + + /* Make sure VPATH is defined before current dir is read */ + if ((cp = getenv(vpath_name->string_mb)) != NULL) { + MBSTOWCS(wcs_buffer, cp); + (void) SETVAR(vpath_name, + GETNAME(wcs_buffer, FIND_LENGTH), + false); + } + + /* Check if there is NO PATH variable. If not we construct one. */ + if (getenv(path_name->string_mb) == NULL) { + vroot_path = NULL; + add_dir_to_path(NOCATGETS("."), &vroot_path, -1); + add_dir_to_path(NOCATGETS("/bin"), &vroot_path, -1); + add_dir_to_path(NOCATGETS("/usr/bin"), &vroot_path, -1); + } +} + +/* + * iterate on list of conditional macros in np, and place them in + * a String_rec starting with, and separated by the '$' character. + */ +void +cond_macros_into_string(Name np, String_rec *buffer) +{ + Macro_list macro_list; + + /* + * Put the version number at the start of the string + */ + MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION); + append_string(wcs_buffer, buffer, FIND_LENGTH); + /* + * Add the rest of the conditional macros to the buffer + */ + if (np->depends_on_conditional){ + for (macro_list = np->conditional_macro_list; + macro_list != NULL; macro_list = macro_list->next){ + append_string(macro_list->macro_name, buffer, + FIND_LENGTH); + append_char((int) equal_char, buffer); + append_string(macro_list->value, buffer, FIND_LENGTH); + append_char((int) dollar_char, buffer); + } + } +} +/* + * Copyright (c) 1987-1992 Sun Microsystems, Inc. All Rights Reserved. + * Sun considers its source code as an unpublished, proprietary + * trade secret, and it is available only under strict license + * provisions. This copyright notice is placed here only to protect + * Sun in the event the source is deemed a published work. Dissassembly, + * decompilation, or other means of reducing the object code to human + * readable form is prohibited by the license agreement under which + * this code is provided to the user or company in possession of this + * copy. + * RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the + * Government is subject to restrictions as set forth in subparagraph + * (c)(1)(ii) of the Rights in Technical Data and Computer Software + * clause at DFARS 52.227-7013 and in similar clauses in the FAR and + * NASA FAR Supplement. + * + * 1.3 91/09/30 + */ + + +/* Some includes are commented because of the includes at the beginning */ +/* #include <signal.h> */ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +/* #include <string.h> */ +#include <unistd.h> +#include <stdlib.h> +/* #include <stdio.h> */ +/* #include <avo/find_dir.h> */ +/* #ifndef TEAMWARE_MAKE_CMN +#include <avo/find_dir.h> +#endif */ + +/* Routines to find the base directory name from which the various components + * -executables, *crt* libraries etc will be accessed + */ + +/* This routine checks to see if a given filename is an executable or not. + Logically similar to the csh statement : if ( -x $i && ! -d $i ) + */ + +static int +check_if_exec(char *file) +{ + struct stat stb; + if (stat(file, &stb) < 0) { + return ( -1); + } + if (S_ISDIR(stb.st_mode)) { + return (-1); + } + if (!(stb.st_mode & S_IEXEC)) { + return ( -1); + } + return (0); +} + +/* resolve - check for specified file in specified directory + * sets up dir, following symlinks. + * returns zero for success, or + * -1 for error (with errno set properly) + */ +static int +resolve (char *indir, /* search directory */ + char *cmd, /* search for name */ + char *dir, /* directory buffer */ + char **run) /* resultion name ptr ptr */ +{ + char *p; + int rv = -1; + int sll; + char symlink[MAXPATHLEN + 1]; + + do { + errno = ENAMETOOLONG; + if ((strlen (indir) + strlen (cmd) + 2) > (size_t) MAXPATHLEN) + break; + + sprintf(dir, "%s/%s", indir, cmd); + if (check_if_exec(dir) != 0) /* check if dir is an executable */ + { + break; /* Not an executable program */ + } + + /* follow symbolic links */ + while ((sll = readlink (dir, symlink, MAXPATHLEN)) >= 0) { + symlink[sll] = 0; + if (*symlink == '/') + strcpy (dir, symlink); + else + sprintf (strrchr (dir, '/'), "/%s", symlink); + } + if (errno != EINVAL) + break; + + p = strrchr (dir, '/'); + *p++ = 0; + if (run) /* user wants resolution name */ + *run = p; + rv = 0; /* complete, with success! */ + + } while (0); + + return rv; +} + +/* + *find_run_directory - find executable file in PATH + * + * PARAMETERS: + * cmd filename as typed by user (argv[0]) + * cwd buffer from which is read the working directory + * if first character is '/' or into which is + * copied working directory name otherwise + * dir buffer into which is copied program's directory + * pgm where to return pointer to tail of cmd (may be NULL + * if not wanted) + * run where to return pointer to tail of final resolved + * name ( dir/run is the program) (may be NULL + * if not wanted) + * path user's path from environment + * + * Note: run and pgm will agree except when symbolic links have + * renamed files + * + * RETURNS: + * returns zero for success, + * -1 for error (with errno set properly). + * + * EXAMPLE: + * find_run_directory (argv[0], ".", &charray1, (char **) 0, (char **) 0, + * getenv(NOGETTEXT("PATH"))); + */ +extern int +find_run_directory (char *cmd, + char *cwd, + char *dir, + char **pgm, + char **run, + char *path) +{ + int rv = 0; + char *f, *s; + int i; + char tmp_path[MAXPATHLEN]; + + if (!cmd || !*cmd || !cwd || !dir) { + errno = EINVAL; /* stupid arguments! */ + return -1; + } + + if (*cwd != '/') + if (!(getcwd (cwd, MAXPATHLEN))) + return -1; /* can not get working directory */ + + f = strrchr (cmd, '/'); + if (pgm) /* user wants program name */ + *pgm = f ? f + 1 : cmd; + + /* get program directory */ + rv = -1; + if (*cmd == '/') /* absname given */ + rv = resolve ("", cmd + 1, dir, run); + else if (f) /* relname given */ + rv = resolve (cwd, cmd, dir, run); + else { /* from searchpath */ + if (!path || !*path) { /* if missing or null path */ + tmp_path[0] = '.'; /* assume sanity */ + tmp_path[1] = '\0'; + } else { + strcpy(tmp_path, path); + } + f = tmp_path; + rv = -1; + errno = ENOENT; /* errno gets this if path empty */ + while (*f && (rv < 0)) { + s = f; + while (*f && (*f != ':')) + ++f; + if (*f) + *f++ = 0; + if (*s == '/') + rv = resolve (s, cmd, dir, run); + else { + char abuf[MAXPATHLEN]; + + sprintf (abuf, "%s/%s", cwd, s); + rv = resolve (abuf, cmd, dir, run); + } + } + } + + /* Remove any trailing /. */ + i = strlen(dir); + if ( dir[i-2] == '/' && dir[i-1] == '.') { + dir[i-2] = '\0'; + } + + return rv; +} + diff --git a/usr/src/make_src/Make/bin/make/common/nse.cc b/usr/src/make_src/Make/bin/make/common/nse.cc new file mode 100644 index 0000000..4cc75e4 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/nse.cc @@ -0,0 +1,610 @@ +/* + * 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 1994 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)nse.cc 1.15 06/12/12 + */ + +#pragma ident "@(#)nse.cc 1.15 06/12/12" + +#ifdef NSE + +/* + * Included files + */ +#include <mk/defs.h> +#include <mksh/macro.h> /* expand_value() */ +#include <mksh/misc.h> /* get_prop() */ + +/* + * This file does some extra checking on behalf of the NSE. + * It does some stuff that is analogous to lint in that it + * looks for things which may be legal but that give the NSE + * trouble. Currently it looks for: + * 1) recursion by cd'ing to a directory specified by a + * make variable that is defined from the shell environment. + * 2) recursion by cd'ing to a directory specified by a + * make variable that has backquotes in it. + * 3) recursion to another makefile in the same directory. + * 4) a dependency upon an SCCS file (SCCS/s.*) + * 5) backquotes in a file name + * 6) a make variable being defined on the command-line that + * ends up affecting dependencies + * 7) wildcards (*[) in dependencies + * 8) recursion to the same directory + * 9) potential source files on the left-hand-side so + * that they appear as derived files + * + * Things it should look for: + * 1) makefiles that are symlinks (why are these a problem?) + */ + +#define TARG_SUFX "/usr/nse/lib/nse_targ.sufx" + +typedef struct _Nse_suffix *Nse_suffix, Nse_suffix_rec; +struct _Nse_suffix { + wchar_t *suffix; /* The suffix */ + struct _Nse_suffix *next; /* Linked list */ +}; +static Nse_suffix sufx_hdr; +static int our_exit_status; + +static void nse_warning(void); +static Boolean nse_gettoken(wchar_t **, wchar_t *); + +/* + * Given a command that has just recursed to a sub make + * try to determine if it cd'ed to a directory that was + * defined by a make variable imported from the shell + * environment or a variable with backquotes in it. + * This routine will find something like: + * cd $(DIR); $(MAKE) + * where DIR is imported from the shell environment. + * However it well not find: + * CD = cd + * $(CD) $(DIR); $(MAKE) + * or + * CD = cd $(DIR) + * $(CD); $(MAKE) + * + * This routine also checks for recursion to the same + * directory. + */ +void +nse_check_cd(Property prop) +{ + wchar_t tok[512]; + wchar_t *p; + wchar_t *our_template; + int len; + Boolean cd; +#ifdef SUNOS4_AND_AFTER + String_rec string; +#else + String string; +#endif + Name name; + Name target; + struct Line *line; + struct Recursive *r; + Property recurse; + wchar_t strbuf[STRING_BUFFER_LENGTH]; + wchar_t tmpbuf[STRING_BUFFER_LENGTH]; + +#ifdef LTEST + printf("In nse_check_cd, nse = %d, nse_did_recursion = %d\n", nse, nse_did_recursion); +#endif +#ifdef SUNOS4_AND_AFTER + if (!nse_did_recursion || !nse) { +#else + if (is_false(nse_did_recursion) || is_false(flag.nse)) { +#endif +#ifdef LTEST + printf ("returning, nse = %d, nse_did_recursion = %d\n", nse, nse_did_recursion); +#endif + return; + } + line = &prop->body.line; +#ifdef LTEST + printf("string = %s\n", line->command_template->command_line->string_mb); +#endif + + wscpy(tmpbuf, line->command_template->command_line->string); + our_template = tmpbuf; + cd = false; + while (nse_gettoken(&our_template, tok)) { +#ifdef LTEST + printf("in gettoken loop\n"); +#endif +#ifdef SUNOS4_AND_AFTER + if (IS_WEQUAL(tok, (wchar_t *) "cd")) { +#else + if (is_equal(tok, "cd")) { +#endif + cd = true; + } else if (cd && tok[0] == '$') { + nse_backquote_seen = NULL; + nse_shell_var_used = NULL; + nse_watch_vars = true; +#ifdef SUNOS4_AND_AFTER + INIT_STRING_FROM_STACK(string, strbuf); + name = GETNAME(tok, FIND_LENGTH); +#else + init_string_from_stack(string, strbuf); + name = getname(tok, FIND_LENGTH); +#endif + expand_value(name, &string, false); + nse_watch_vars = false; + +#ifdef LTEST + printf("cd = %d, tok = $\n", cd); +#endif + /* + * Try to trim tok to just + * the variable. + */ + if (nse_shell_var_used != NULL) { + nse_warning(); + fprintf(stderr, "\tMake invoked recursively by cd'ing to a directory\n\tdefined by the shell environment variable %s\n\tCommand line: %s\n", + nse_shell_var_used->string_mb, + line->command_template->command_line->string_mb); + } + if (nse_backquote_seen != NULL) { + nse_warning(); + fprintf(stderr, "\tMake invoked recursively by cd'ing to a directory\n\tdefined by a variable (%s) with backquotes in it\n\tCommand line: %s\n", + nse_backquote_seen->string_mb, + line->command_template->command_line->string_mb); + } + cd = false; + } else if (cd && nse_backquotes(tok)) { + nse_warning(); + fprintf(stderr, "\tMake invoked recursively by cd'ing to a directory\n\tspecified by a command in backquotes\n\tCommand line: %s\n", + line->command_template->command_line->string_mb); + cd = false; + } else { + cd = false; + } + } + + /* + * Now check for recursion to ".". + */ + if (primary_makefile != NULL) { + target = prop->body.line.target; + recurse = get_prop(target->prop, recursive_prop); + while (recurse != NULL) { + r = &recurse->body.recursive; +#ifdef SUNOS4_AND_AFTER + if (IS_WEQUAL(r->directory->string, (wchar_t *) ".") && + !IS_WEQUAL(r->makefiles->name->string, + primary_makefile->string)) { +#else + if (is_equal(r->directory->string, ".") && + !is_equal(r->makefiles->name->string, + primary_makefile->string)) { +#endif + nse_warning(); + fprintf(stderr, "\tRecursion to makefile `%s' in the same directory\n\tCommand line: %s\n", + r->makefiles->name->string_mb, + line->command_template->command_line->string_mb); + } + recurse = get_prop(recurse->next, recursive_prop); + } + } +} + +/* + * Print an NSE make warning line. + * If the -P flag was given then consider this a fatal + * error, otherwise, just a warning. + */ +static void +nse_warning(void) +{ +#ifdef SUNOS4_AND_AFTER + if (report_dependencies_level > 0) { +#else + if (is_true(flag.report_dependencies)) { +#endif + our_exit_status = 1; + } + if (primary_makefile != NULL) { + fprintf(stderr, "make: NSE warning from makefile %s/%s:\n", + get_current_path(), primary_makefile->string_mb); + } else { + fprintf(stderr, "make: NSE warning from directory %s:\n", + get_current_path()); + } +} + +/* + * Get the next whitespace delimited token pointed to by *cp. + * Return it in tok. + */ +static Boolean +nse_gettoken(wchar_t **cp, wchar_t *tok) +{ + wchar_t *to; + wchar_t *p; + + p = *cp; + while (*p && iswspace(*p)) { + p++; + } + if (*p == '\0') { + return false; + } + to = tok; + while (*p && !iswspace(*p)) { + *to++ = *p++; + } + if (*p == '\0') { + return false; + } + *to = '\0'; + *cp = p; + return true; +} + +/* + * Given a dependency and a target, see if the dependency + * is an SCCS file. Check for the last component of its name + * beginning with "s." and the component before that being "SCCS". + * The NSE does not consider a source file to be derived from + * an SCCS file. + */ +void +nse_check_sccs(wchar_t *targ, wchar_t *dep) +{ + wchar_t *slash; + wchar_t *p; + +#ifdef SUNOS4_AND_AFTER + if (!nse) { +#else + if (is_false(flag.nse)) { +#endif + return; + } +#ifdef SUNOS4_AND_AFTER + slash = wsrchr(dep, (int) slash_char); +#else + slash = rindex(dep, '/'); +#endif + if (slash == NULL) { + return; + } + if (slash[1] != 's' || slash[2] != '.') { + return; + } + + /* + * Find the next to last filename component. + */ + for (p = slash - 1; p >= dep; p--) { + if (*p == '/') { + break; + } + } + p++; +#ifdef SUNOS4_AND_AFTER + MBSTOWCS(wcs_buffer, "SCCS/"); + if (IS_WEQUALN(p, wcs_buffer, wslen(wcs_buffer))) { +#else + if (is_equaln(p, "SCCS/", 5)) { +#endif + nse_warning(); + WCSTOMBS(mbs_buffer, targ); + WCSTOMBS(mbs_buffer2, dep); + fprintf(stderr, "\tFile `%s' depends upon SCCS file `%s'\n", + mbs_buffer, mbs_buffer2); + } + return; +} + +/* + * Given a filename check to see if it has 2 backquotes in it. + * Complain about this because the shell expands the backquotes + * but make does not so the files always appear to be out of date. + */ +void +nse_check_file_backquotes(wchar_t *file) +{ +#ifdef SUNOS4_AND_AFTER + if (!nse) { +#else + if (is_false(flag.nse)) { +#endif + return; + } + if (nse_backquotes(file)) { + nse_warning(); + WCSTOMBS(mbs_buffer, file); + fprintf(stderr, "\tFilename \"%s\" has backquotes in it\n", + mbs_buffer); + } +} + +/* + * Return true if the string has two backquotes in it. + */ +Boolean +nse_backquotes(wchar_t *str) +{ + wchar_t *bq; + +#ifdef SUNOS4_AND_AFTER + bq = wschr(str, (int) backquote_char); + if (bq) { + bq = wschr(&bq[1], (int) backquote_char); +#else + bq = index(str, '`'); + if (bq) { + bq = index(&bq[1], '`'); +#endif + if (bq) { + return true; + } + } + return false; +} + +/* + * A macro that was defined on the command-line was found to affect the + * set of dependencies. The NSE "target explode" will not know about + * this and will not get the same set of dependencies. + */ +void +nse_dep_cmdmacro(wchar_t *macro) +{ +#ifdef SUNOS4_AND_AFTER + if (!nse) { +#else + if (is_false(flag.nse)) { +#endif + return; + } + nse_warning(); + WCSTOMBS(mbs_buffer, macro); + fprintf(stderr, "\tVariable `%s' is defined on the command-line and\n\taffects dependencies\n", + mbs_buffer); +} + +/* + * A macro that was defined on the command-line was found to + * be part of the argument to a cd before a recursive make. + * This make cause the make to recurse to different places + * depending upon how it is invoked. + */ +void +nse_rule_cmdmacro(wchar_t *macro) +{ +#ifdef SUNOS4_AND_AFTER + if (!nse) { +#else + if (is_false(flag.nse)) { +#endif + return; + } + nse_warning(); + WCSTOMBS(mbs_buffer, macro); + fprintf(stderr, "\tMake invoked recursively by cd'ing to a directory\n\tspecified by a variable (%s) defined on the command-line\n", + mbs_buffer); +} + +/* + * A dependency has been found with a wildcard in it. + * This causes the NSE problems because the set of dependencies + * can change without changing the Makefile. + */ +void +nse_wildcard(wchar_t *targ, wchar_t *dep) +{ +#ifdef SUNOS4_AND_AFTER + if (!nse) { +#else + if (is_false(flag.nse)) { +#endif + return; + } + nse_warning(); + WCSTOMBS(mbs_buffer, targ); + WCSTOMBS(mbs_buffer2, dep); + fprintf(stderr, "\tFile `%s' has a wildcard in dependency `%s'\n", + mbs_buffer, mbs_buffer2); +} + +/* + * Read in the list of suffixes that are interpreted as source + * files. + */ +void +nse_init_source_suffixes(void) +{ + FILE *fp; + wchar_t suffix[100]; + Nse_suffix sufx; + Nse_suffix *bpatch; + + fp = fopen(TARG_SUFX, "r"); + if (fp == NULL) { + return; + } + bpatch = &sufx_hdr; + while (fscanf(fp, "%s %*s", suffix) == 1) { +#ifdef SUNOS4_AND_AFTER + sufx = ALLOC(Nse_suffix); + sufx->suffix = wscpy(ALLOC_WC(wslen(suffix) + 1), suffix); +#else + sufx = alloc(Nse_suffix); + sufx->suffix = strcpy(malloc(strlen(suffix) + 1), suffix); +#endif + sufx->next = NULL; + *bpatch = sufx; + bpatch = &sufx->next; + } + fclose(fp); +} + +/* + * Check if a derived file (something with a dependency) appears + * to be a source file (by its suffix) but has no rule to build it. + * If so, complain. + * + * This generally arises from the old-style of make-depend that + * produces: + * foo.c: foo.h + */ +void +nse_check_derived_src(Name target, wchar_t *dep, Cmd_line command_template) +{ + Nse_suffix sufx; + wchar_t *suffix; + wchar_t *depsufx; + +#ifdef SUNOS4_AND_AFTER + if (!nse) { +#else + if (is_false(flag.nse)) { +#endif + return; + } +#ifdef SUNOS4_AND_AFTER + if (target->stat.is_derived_src) { +#else + if (is_true(target->stat.is_derived_src)) { +#endif + return; + } + if (command_template != NULL) { + return; + } +#ifdef SUNOS4_AND_AFTER + suffix = wsrchr(target->string, (int) period_char ); +#else + suffix = rindex(target->string, '.'); +#endif + if (suffix != NULL) { + for (sufx = sufx_hdr; sufx != NULL; sufx = sufx->next) { +#ifdef SUNOS4_AND_AFTER + if (IS_WEQUAL(sufx->suffix, suffix)) { +#else + if (is_equal(sufx->suffix, suffix)) { +#endif + nse_warning(); + WCSTOMBS(mbs_buffer, dep); + fprintf(stderr, "\tProbable source file `%s' appears as a derived file\n\tas it depends upon file `%s', but there is\n\tno rule to build it\n", + target->string_mb, mbs_buffer); + break; + } + } + } +} + +/* + * See if a target is a potential source file and has no + * dependencies and no rule but shows up on the right-hand + * side. This tends to occur from old "make depend" output. + */ +void +nse_check_no_deps_no_rule(Name target, Property line, Property command) +{ + Nse_suffix sufx; + wchar_t *suffix; + +#ifdef SUNOS4_AND_AFTER + if (!nse) { +#else + if (is_false(flag.nse)) { +#endif + return; + } +#ifdef SUNOS4_AND_AFTER + if (target->stat.is_derived_src) { +#else + if (is_true(target->stat.is_derived_src)) { +#endif + return; + } + if (line != NULL && line->body.line.dependencies != NULL) { + return; + } +#ifdef SUNOS4_AND_AFTER + if (command->body.line.sccs_command) { +#else + if (is_true(command->body.line.sccs_command)) { +#endif + return; + } +#ifdef SUNOS4_AND_AFTER + suffix = wsrchr(target->string, (int) period_char); +#else + suffix = rindex(target->string, '.'); +#endif + if (suffix != NULL) { + for (sufx = sufx_hdr; sufx != NULL; sufx = sufx->next) { +#ifdef SUNOS4_AND_AFTER + if (IS_WEQUAL(sufx->suffix, suffix)) { +#else + if (is_equal(sufx->suffix, suffix)) { +#endif + if (command->body.line.command_template == NULL) { + nse_warning(); + fprintf(stderr, "\tProbable source file `%s' appears as a derived file because\n\tit is on the left-hand side, but it has no dependencies and\n\tno rule to build it\n", + target->string_mb); + } + } + } + } +} + +/* + * Detected a situation where a recursive make derived a file + * without using a makefile. + */ +void +nse_no_makefile(Name target) +{ +#ifdef SUNOS4_AND_AFTER + if (!nse) { +#else + if (is_false(flag.nse)) { +#endif + return; + } + nse_warning(); + fprintf(stderr, "Recursive make to derive %s did not use a makefile\n", + target->string_mb); +} + +/* + * Return the NSE exit status. + * If the -P flag was given then a warning is considered fatal + */ +int +nse_exit_status(void) +{ + return our_exit_status; +} +#endif diff --git a/usr/src/make_src/Make/bin/make/common/nse_printdep.cc b/usr/src/make_src/Make/bin/make/common/nse_printdep.cc new file mode 100644 index 0000000..cda02f8 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/nse_printdep.cc @@ -0,0 +1,517 @@ +/* + * 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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)nse_printdep.cc 1.18 06/12/12 + */ + +#pragma ident "@(#)nse_printdep.cc 1.18 06/12/12" + +/* + * Included files + */ +#include <mk/defs.h> +#include <mksh/misc.h> /* get_prop() */ + +/* + * File table of contents + */ +void print_dependencies(register Name target, register Property line); +static void print_deps(register Name target, register Property line); +static void print_more_deps(Name target, Name name); +static void print_filename(Name name); +static Boolean should_print_dep(Property line); +static void print_forest(Name target); +static void print_deplist(Dependency head); +void print_value(register Name value, Daemon daemon); +static void print_rule(register Name target); +static void print_rec_info(Name target); +static Boolean is_out_of_date(Property line); +extern void depvar_print_results (void); +extern int printf (const char *, ...); +extern int _flsbuf (unsigned int, FILE *); + +/* + * print_dependencies(target, line) + * + * Print all the dependencies of a target. First print all the Makefiles. + * Then print all the dependencies. Finally, print all the .INIT + * dependencies. + * + * Parameters: + * target The target we print dependencies for + * line We get the dependency list from here + * + * Global variables used: + * done The Name ".DONE" + * init The Name ".INIT" + * makefiles_used List of all makefiles read + */ +void +print_dependencies(register Name target, register Property line) +{ + Dependency dp; + static Boolean makefiles_printed = false; + +#ifdef SUNOS4_AND_AFTER + if (target_variants) { +#else + if (is_true(flag.target_variants)) { +#endif + depvar_print_results(); + } + + if (!makefiles_printed) { + /* + * Search the makefile list for the primary makefile, + * then print it and its inclusions. After that go back + * and print the default.mk file and its inclusions. + */ + for (dp = makefiles_used; dp != NULL; dp = dp->next) { + if (dp->name == primary_makefile) { + break; + } + } + if (dp) { + print_deplist(dp); + for (dp = makefiles_used; dp != NULL; dp = dp->next) { + if (dp->name == primary_makefile) { + break; + } + (void)printf(" %s", dp->name->string_mb); + } + } + (void) printf("\n"); + makefiles_printed = true; + } + print_deps(target, line); +#ifdef SUNOS4_AND_AFTER +/* + print_more_deps(target, init); + print_more_deps(target, done); + */ + if (target_variants) { +#else + print_more_deps(target, cached_names.init); + print_more_deps(target, cached_names.done); + if (is_true(flag.target_variants)) { +#endif + print_forest(target); + } +} + +/* + * print_more_deps(target, name) + * + * Print some special dependencies. + * These are the dependencies for the .INIT and .DONE targets. + * + * Parameters: + * target Target built during make run + * name Special target to print dependencies for + * + * Global variables used: + */ +static void +print_more_deps(Name target, Name name) +{ + Property line; + register Dependency dependencies; + + line = get_prop(name->prop, line_prop); + if (line != NULL && line->body.line.dependencies != NULL) { + (void) printf("%s:\t", target->string_mb); + print_deplist(line->body.line.dependencies); + (void) printf("\n"); + for (dependencies= line->body.line.dependencies; + dependencies != NULL; + dependencies= dependencies->next) { + print_deps(dependencies->name, + get_prop(dependencies->name->prop, line_prop)); + } + } +} + +/* + * print_deps(target, line, go_recursive) + * + * Print a regular dependency list. Append to this information which + * indicates whether or not the target is recursive. + * + * Parameters: + * target target to print dependencies for + * line We get the dependency list from here + * go_recursive Should we show all dependencies recursively? + * + * Global variables used: + * recursive_name The Name ".RECURSIVE", printed + */ +static void +print_deps(register Name target, register Property line) +{ + register Dependency dep; + +#ifdef SUNOS4_AND_AFTER + if ((target->dependency_printed) || + (target == force)) { +#else + if (is_true(target->dependency_printed)) { +#endif + return; + } + target->dependency_printed = true; + + /* only print entries that are actually derived and are not leaf + * files and are not the result of sccs get. + */ + if (should_print_dep(line)) { +#ifdef NSE + nse_check_no_deps_no_rule(target, line, line); +#endif + if ((report_dependencies_level == 2) || + (report_dependencies_level == 4)) { + if (is_out_of_date(line)) { + (void) printf("1 "); + } else { + (void) printf("0 "); + } + } + print_filename(target); + (void) printf(":\t"); + print_deplist(line->body.line.dependencies); + print_rec_info(target); + (void) printf("\n"); + for (dep = line->body.line.dependencies; + dep != NULL; + dep = dep->next) { + print_deps(dep->name, + get_prop(dep->name->prop, line_prop)); + } + } +} + +static Boolean +is_out_of_date(Property line) +{ + Dependency dep; + Property line2; + + if (line == NULL) { + return false; + } + if (line->body.line.is_out_of_date) { + return true; + } + for (dep = line->body.line.dependencies; + dep != NULL; + dep = dep->next) { + line2 = get_prop(dep->name->prop, line_prop); + if (is_out_of_date(line2)) { + line->body.line.is_out_of_date = true; + return true; + } + } + return false; +} + +/* + * Given a dependency print it and all its siblings. + */ +static void +print_deplist(Dependency head) +{ + Dependency dp; + + for (dp = head; dp != NULL; dp = dp->next) { + if ((report_dependencies_level != 2) || + ((!dp->automatic) || + (dp->name->is_double_colon))) { + if (dp->name != force) { + putwchar(' '); + print_filename(dp->name); + } + } + } +} + +/* + * Print the name of a file for the -P option. + * If the file is a directory put on a trailing slash. + */ +static void +print_filename(Name name) +{ + (void) printf("%s", name->string_mb); +/* + if (name->stat.is_dir) { + putwchar('/'); + } + */ +} + +/* + * should_print_dep(line) + * + * Test if we should print the dependencies of this target. + * The line must exist and either have children dependencies + * or have a command that is not an SCCS command. + * + * Return value: + * true if the dependencies should be printed + * + * Parameters: + * line We get the dependency list from here + * + * Global variables used: + */ +static Boolean +should_print_dep(Property line) +{ + if (line == NULL) { + return false; + } + if (line->body.line.dependencies != NULL) { + return true; + } +#ifdef SUNOS4_AND_AFTER + if (line->body.line.sccs_command) { +#else + if (is_true(line->body.line.sccs_command)) { +#endif + return false; + } + return true; +} + +/* + * Print out the root nodes of all the dependency trees + * in this makefile. + */ +static void +print_forest(Name target) +{ + Name_set::iterator np, e; + Property line; + + for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) { +#ifdef SUNOS4_AND_AFTER + if (np->is_target && !np->has_parent && np != target) { +#else + if (is_true(np->is_target) && + is_false(np->has_parent) && + np != target) { +#endif + (void) doname_check(np, true, false, false); + line = get_prop(np->prop, line_prop); + printf("-\n"); + print_deps(np, line); + } + } +} + +#ifndef SUNOS4_AND_AFTER +printdesc() +{ + Name_set::iterator p, e; + register Property prop; + register Dependency dep; + register Cmd_line rule; + Percent percent, percent_depe; + + /* Default target */ + if (default_target_to_build != NULL) { + print_rule(default_target_to_build); + default_target_to_build->dependency_printed= true; + }; + (void)printf("\n"); + + /* .AR_REPLACE */ + if (ar_replace_rule != NULL) { + (void)printf("%s:\n", cached_names.ar_replace->string_mb); + for (rule= ar_replace_rule; rule != NULL; rule= rule->next) + (void)printf("\t%s\n", rule->command_line->string_mb); + }; + + /* .DEFAULT */ + if (default_rule != NULL) { + (void)printf("%s:\n", cached_names.default_rule->string_mb); + for (rule= default_rule; rule != NULL; rule= rule->next) + (void)printf("\t%s\n", rule->command_line->string_mb); + }; + + /* .IGNORE */ + if (is_true(flag.ignore_errors)) + (void)printf("%s:\n", cached_names.ignore->string_mb); + + /* .KEEP_STATE: */ + if (is_true(flag.keep_state)) + (void)printf("%s:\n\n", cached_names.dot_keep_state->string_mb); + + /* .PRECIOUS */ + (void)printf("%s: ", cached_names.precious->string_mb); + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) + if (is_true(p->stat.is_precious | all_precious)) + (void)printf("%s ", p->string_mb); + (void)printf("\n"); + + /* .SCCS_GET */ + if (sccs_get_rule != NULL) { + (void)printf("%s:\n", cached_names.sccs_get->string_mb); + for (rule= sccs_get_rule; rule != NULL; rule= rule->next) + (void)printf("\t%s\n", rule->command_line->string_mb); + }; + + /* .SILENT */ + if (is_true(flag.silent)) + (void)printf("%s:\n", cached_names.silent->string_mb); + + /* .SUFFIXES: */ + (void)printf("%s: ", cached_names.suffixes->string_mb); + for (dep= suffixes; dep != NULL; dep= dep->next) { + (void)printf("%s ", dep->name->string_mb); + build_suffix_list(dep->name); + }; + (void)printf("\n\n"); + + /* % rules */ + for (percent= percent_list; percent != NULL; percent= percent->next) { + (void) printf("%s:", percent->name->string_mb); + + for (percent_depe= percent->dependencies; percent_depe != NULL; percent_depe = percent_depe->next) + (void) printf(" %s", percent_depe->name->string_mb); + + (void) printf("\n"); + + for (rule= percent->command_template; rule != NULL; rule= rule->next) + (void)printf("\t%s\n", rule->command_line->string_mb); + }; + + /* Suffix rules */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) + if (is_false(p->dependency_printed) && (p->string[0] == PERIOD)) { + print_rule(p); + p->dependency_printed= true; + }; + + /* Macro assignments */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) + if (((prop= get_prop(p->prop, macro_prop)) != NULL) && + (prop->body.macro.value != NULL)) { + (void)printf("%s", p->string_mb); + print_value(prop->body.macro.value, + prop->body.macro.daemon); + }; + (void)printf("\n"); + + /* Delays */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) + for (prop= get_prop(p->prop, conditional_prop); + prop != NULL; + prop= get_prop(prop->next, conditional_prop)) { + (void)printf("%s := %s", + p->string_mb, + prop->body.conditional.name->string_mb); + print_value(prop->body.conditional.value, no_daemon); + }; + (void)printf("\n"); + + /* All other dependencies */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) + if (is_false(p->dependency_printed) && (p->colons != no_colon)) + print_rule(p); + (void)printf("\n"); + exit(0); +} +#endif + +/* + * This is a set of routines for dumping the internal make state + * Used for the -p option + */ +void +print_value(register Name value, Daemon daemon) + +#ifdef SUNOS4_AND_AFTER + +#else + +#endif +{ + Chain cp; + + if (value == NULL) + (void)printf("=\n"); + else + switch (daemon) { + case no_daemon: + (void)printf("= %s\n", value->string_mb); + break; + case chain_daemon: + for (cp= (Chain) value; cp != NULL; cp= cp->next) + (void)printf(cp->next == NULL ? "%s" : "%s ", + cp->name->string_mb); + (void)printf("\n"); + break; + }; +} + +static void +print_rule(register Name target) +{ + register Cmd_line rule; + register Property line; + + if (((line= get_prop(target->prop, line_prop)) == NULL) || + ((line->body.line.command_template == NULL) && + (line->body.line.dependencies == NULL))) + return; + print_dependencies(target, line); + for (rule= line->body.line.command_template; rule != NULL; rule= rule->next) + (void)printf("\t%s\n", rule->command_line->string_mb); +} + + +/* + * If target is recursive, print the following to standard out: + * .RECURSIVE subdir targ Makefile + */ +static void +print_rec_info(Name target) +{ + Recursive_make rp; + wchar_t *colon; + + report_recursive_init(); + + rp = find_recursive_target(target); + + if (rp) { + /* + * if found, print starting with the space after the ':' + */ + colon = (wchar_t *) wschr(rp->oldline, (int) colon_char); + (void) printf("%s", colon + 1); + } +} + diff --git a/usr/src/make_src/Make/bin/make/common/parallel.cc b/usr/src/make_src/Make/bin/make/common/parallel.cc new file mode 100644 index 0000000..1831415 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/parallel.cc @@ -0,0 +1,2477 @@ +/* + * 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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)parallel.cc 1.75 06/12/12 + */ + +#pragma ident "@(#)parallel.cc 1.75 06/12/12" + +#ifdef TEAMWARE_MAKE_CMN + +/* + * parallel.cc + * + * Deal with the parallel processing + */ + +/* + * Included files + */ +#ifdef DISTRIBUTED +#include <avo/strings.h> /* AVO_STRDUP() */ +#include <dm/Avo_DoJobMsg.h> +#include <dm/Avo_MToolJobResultMsg.h> +#endif +#include <errno.h> /* errno */ +#include <fcntl.h> +#include <avo/util.h> /* avo_get_user(), avo_hostname() */ +#include <mk/defs.h> +#include <mksh/dosys.h> /* redirect_io() */ +#include <mksh/macro.h> /* expand_value() */ +#include <mksh/misc.h> /* getmem() */ +#include <sys/signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/wait.h> +#include <unistd.h> + +#ifdef SGE_SUPPORT +#include <dmthread/Avo_PathNames.h> +#endif + + +/* + * Defined macros + */ +#define MAXRULES 100 + +/* + * This const should be in avo_dms/include/AvoDmakeCommand.h + */ +const int local_host_mask = 0x20; + + +/* + * typedefs & structs + */ + + +/* + * Static variables + */ +#ifdef TEAMWARE_MAKE_CMN +static Boolean just_did_subtree = false; +static char local_host[MAXNAMELEN] = ""; +static char user_name[MAXNAMELEN] = ""; +#endif +static int pmake_max_jobs = 0; +static pid_t process_running = -1; +static Running *running_tail = &running_list; +static Name subtree_conflict; +static Name subtree_conflict2; + + +/* + * File table of contents + */ +#ifdef DISTRIBUTED +static void append_dmake_cmd(Avo_DoJobMsg *dmake_job_msg, char *orig_cmd_line, int cmd_options); +static void append_job_result_msg(Avo_MToolJobResultMsg *msg, char *outFn, char *errFn); +static void send_job_result_msg(Running rp); +#endif +static void delete_running_struct(Running rp); +static Boolean dependency_conflict(Name target); +static Doname distribute_process(char **commands, Property line); +static void doname_subtree(Name target, Boolean do_get, Boolean implicit); +static void dump_out_file(char *filename, Boolean err); +static void finish_doname(Running rp); +static void maybe_reread_make_state(void); +static void process_next(void); +static void reset_conditionals(int cnt, Name *targets, Property *locals); +static pid_t run_rule_commands(char *host, char **commands); +static Property *set_conditionals(int cnt, Name *targets); +static void store_conditionals(Running rp); + + +/* + * execute_parallel(line, waitflg) + * + * DMake 2.x: + * parallel mode: spawns a parallel process to execute the command group. + * distributed mode: sends the command group down the pipe to rxm. + * + * Return value: + * The result of the execution + * + * Parameters: + * line The command group to execute + */ +Doname +execute_parallel(Property line, Boolean waitflg, Boolean local) +{ + int argcnt; + int cmd_options = 0; + char *commands[MAXRULES + 5]; + char *cp; +#ifdef DISTRIBUTED + Avo_DoJobMsg *dmake_job_msg = NULL; +#endif + Name dmake_name; + Name dmake_value; + int ignore; + Name make_machines_name; + char **p; + Property prop; + Doname result = build_ok; + Cmd_line rule; + Boolean silent_flag; + Name target = line->body.line.target; + Boolean wrote_state_file = false; + + if ((pmake_max_jobs == 0) && + (dmake_mode_type == parallel_mode)) { + if (user_name[0] == '\0') { + avo_get_user(user_name, NULL); + } + if (local_host[0] == '\0') { + strcpy(local_host, avo_hostname()); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MAX_JOBS")); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + pmake_max_jobs = atoi(dmake_value->string_mb); + if (pmake_max_jobs <= 0) { + warning(catgets(catd, 1, 308, "DMAKE_MAX_JOBS cannot be less than or equal to zero.")); + warning(catgets(catd, 1, 309, "setting DMAKE_MAX_JOBS to %d."), PMAKE_DEF_MAX_JOBS); + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + } + } else { + /* + * For backwards compatibility w/ PMake 1.x, when + * DMake 2.x is being run in parallel mode, DMake + * should parse the PMake startup file + * $(HOME)/.make.machines to get the pmake_max_jobs. + */ + MBSTOWCS(wcs_buffer, NOCATGETS("PMAKE_MACHINESFILE")); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + make_machines_name = dmake_value; + } else { + make_machines_name = NULL; + } + if ((pmake_max_jobs = read_make_machines(make_machines_name)) <= 0) { + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + } + } +#ifdef DISTRIBUTED + if (send_mtool_msgs) { + send_rsrc_info_msg(pmake_max_jobs, local_host, user_name); + } +#endif + } + + if ((dmake_mode_type == serial_mode) || + ((dmake_mode_type == parallel_mode) && (waitflg))) { + return (execute_serial(line)); + } + +#ifdef DISTRIBUTED + if (dmake_mode_type == distributed_mode) { + if(local) { +// return (execute_serial(line)); + waitflg = true; + } + dmake_job_msg = new Avo_DoJobMsg(); + dmake_job_msg->setJobId(++job_msg_id); + dmake_job_msg->setTarget(target->string_mb); + dmake_job_msg->setImmediateOutput(0); + called_make = false; + } else +#endif + { + p = commands; + } + + argcnt = 0; + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (posix && (touch || quest) && !rule->always_exec) { + continue; + } + if (vpath_defined) { + rule->command_line = + vpath_translation(rule->command_line); + } + if (dmake_mode_type == distributed_mode) { + cmd_options = 0; + if(local) { + cmd_options |= local_host_mask; + } + } else { + silent_flag = false; + ignore = 0; + } + if (rule->command_line->hash.length > 0) { + if (++argcnt == MAXRULES) { + if (dmake_mode_type == distributed_mode) { + /* XXX - tell rxm to execute on local host. */ + /* I WAS HERE!!! */ + } else { + /* Too many rules, run serially instead. */ + return build_serial; + } + } +#ifdef DISTRIBUTED + if (dmake_mode_type == distributed_mode) { + /* + * XXX - set assign_mask to tell rxm + * to do the following. + */ +/* From execute_serial(): + if (rule->assign) { + result = build_ok; + do_assign(rule->command_line, target); + */ + if (0) { + } else if (report_dependencies_level == 0) { + if (rule->ignore_error) { + cmd_options |= ignore_mask; + } + if (rule->silent) { + cmd_options |= silent_mask; + } + if (rule->command_line->meta) { + cmd_options |= meta_mask; + } + if (rule->make_refd) { + cmd_options |= make_refd_mask; + } + if (do_not_exec_rule) { + cmd_options |= do_not_exec_mask; + } + append_dmake_cmd(dmake_job_msg, + rule->command_line->string_mb, + cmd_options); + /* Copying dosys()... */ + if (rule->make_refd) { + if (waitflg) { + dmake_job_msg->setImmediateOutput(1); + } + called_make = true; + if (command_changed && + !wrote_state_file) { + write_state_file(0, false); + wrote_state_file = true; + } + } + } + } else +#endif + { + if (rule->silent && !silent) { + silent_flag = true; + } + if (rule->ignore_error) { + ignore++; + } + /* XXX - need to add support for + prefix */ + if (silent_flag || ignore) { + *p = getmem((silent_flag ? 1 : 0) + + ignore + + (strlen(rule-> + command_line-> + string_mb)) + + 1); + cp = *p++; + if (silent_flag) { + *cp++ = (int) at_char; + } + if (ignore) { + *cp++ = (int) hyphen_char; + } + (void) strcpy(cp, rule->command_line->string_mb); + } else { + *p++ = rule->command_line->string_mb; + } + } + } + } + if ((argcnt == 0) || + (report_dependencies_level > 0)) { +#ifdef DISTRIBUTED + if (dmake_job_msg) { + delete dmake_job_msg; + } +#endif + return build_ok; + } +#ifdef DISTRIBUTED + if (dmake_mode_type == distributed_mode) { + // Send a DoJob message to the rxm process. + distribute_rxm(dmake_job_msg); + + // Wait for an acknowledgement. + Avo_AcknowledgeMsg *ackMsg = getAcknowledgeMsg(); + if (ackMsg) { + delete ackMsg; + } + + if (waitflg) { + // Wait for, and process a job result. + result = await_dist(waitflg); + if (called_make) { + maybe_reread_make_state(); + } + check_state(temp_file_name); + if (result == build_failed) { + if (!continue_after_error) { + +#ifdef PRINT_EXIT_STATUS + warning(NOCATGETS("I'm in execute_parallel. await_dist() returned build_failed")); +#endif + + fatal(catgets(catd, 1, 252, "Command failed for target `%s'"), + target->string_mb); + } + /* + * Make sure a failing command is not + * saved in .make.state. + */ + line->body.line.command_used = NULL; + } + if (temp_file_name != NULL) { + free_name(temp_file_name); + } + temp_file_name = NULL; + Property spro = get_prop(sunpro_dependencies->prop, macro_prop); + if(spro != NULL) { + Name val = spro->body.macro.value; + if(val != NULL) { + free_name(val); + spro->body.macro.value = NULL; + } + } + spro = get_prop(sunpro_dependencies->prop, env_mem_prop); + if(spro) { + char *val = spro->body.env_mem.value; + if(val != NULL) { + retmem_mb(val); + spro->body.env_mem.value = NULL; + } + } + return result; + } else { + parallel_process_cnt++; + return build_running; + } + } else +#endif + { + *p = NULL; + + Doname res = distribute_process(commands, line); + if (res == build_running) { + parallel_process_cnt++; + } + + /* + * Return only those memory that were specially allocated + * for part of commands. + */ + for (int i = 0; commands[i] != NULL; i++) { + if ((commands[i][0] == (int) at_char) || + (commands[i][0] == (int) hyphen_char)) { + retmem_mb(commands[i]); + } + } + return res; + } +} + +#ifdef DISTRIBUTED +/* + * append_dmake_cmd() + * + * Replaces all escaped newline's (\<cr>) + * in the original command line with space's, + * then append the new command line to the DoJobMsg object. + */ +static void +append_dmake_cmd(Avo_DoJobMsg *dmake_job_msg, + char *orig_cmd_line, + int cmd_options) +{ +/* + Avo_DmakeCommand *tmp_dmake_command; + + tmp_dmake_command = new Avo_DmakeCommand(orig_cmd_line, cmd_options); + dmake_job_msg->appendCmd(tmp_dmake_command); + delete tmp_dmake_command; + */ + dmake_job_msg->appendCmd(new Avo_DmakeCommand(orig_cmd_line, cmd_options)); +} +#endif + +#ifdef TEAMWARE_MAKE_CMN +#define MAXJOBS_ADJUST_RFE4694000 + +#ifdef MAXJOBS_ADJUST_RFE4694000 + +#include <unistd.h> /* sysconf(_SC_NPROCESSORS_ONLN) */ +#include <sys/ipc.h> /* ftok() */ +#include <sys/shm.h> /* shmget(), shmat(), shmdt(), shmctl() */ +#include <semaphore.h> /* sem_init(), sem_trywait(), sem_post(), sem_destroy() */ +#if defined(linux) +#define LOADAVG_1MIN 0 +#else +#include <sys/loadavg.h> /* getloadavg() */ +#endif /* linux */ + +/* + * adjust_pmake_max_jobs (int pmake_max_jobs) + * + * Parameters: + * pmake_max_jobs - max jobs limit set by user + * + * External functions used: + * sysconf() + * getloadavg() + */ +static int +adjust_pmake_max_jobs (int pmake_max_jobs) +{ + static int ncpu = 0; + double loadavg[3]; + int adjustment; + int adjusted_max_jobs; + + if (ncpu <= 0) { + if ((ncpu = sysconf(_SC_NPROCESSORS_ONLN)) <= 0) { + ncpu = 1; + } + } + if (getloadavg(loadavg, 3) != 3) return(pmake_max_jobs); + adjustment = ((int)loadavg[LOADAVG_1MIN]); + if (adjustment < 2) return(pmake_max_jobs); + if (ncpu > 1) { + adjustment = adjustment / ncpu; + } + adjusted_max_jobs = pmake_max_jobs - adjustment; + if (adjusted_max_jobs < 1) adjusted_max_jobs = 1; + return(adjusted_max_jobs); +} + +/* + * M2 adjust mode data and functions + * + * m2_init() - initializes M2 shared semaphore + * m2_acquire_job() - decrements M2 semaphore counter + * m2_release_job() - increments M2 semaphore counter + * m2_fini() - destroys M2 semaphore and shared memory* + * + * Environment variables: + * __DMAKE_M2_FILE__ + * + * External functions: + * ftok(), shmget(), shmat(), shmdt(), shmctl() + * sem_init(), sem_trywait(), sem_post(), sem_destroy() + * creat(), close(), unlink() + * getenv(), putenv() + * + * Static variables: + * m2_file - tmp file name to create ipc key for shared memory + * m2_shm_id - shared memory id + * m2_shm_sem - shared memory semaphore + */ + +static char m2_file[MAXPATHLEN]; +static int m2_shm_id = -1; +static sem_t* m2_shm_sem = 0; + +static int +m2_init() { + char *var; + key_t key; + + if ((var = getenv(NOCATGETS("__DMAKE_M2_FILE__"))) == 0) { + /* compose tmp file name */ + sprintf(m2_file, NOCATGETS("%s/dmake.m2.%d.XXXXXX"), tmpdir, getpid()); + + /* create tmp file */ + int fd = mkstemp(m2_file); + if (fd < 0) { + return -1; + } else { + close(fd); + } + } else { + /* using existing semaphore */ + strcpy(m2_file, var); + } + + /* combine IPC key */ + if ((key = ftok(m2_file, 38)) == (key_t) -1) { + return -1; + } + + /* create shared memory */ + if ((m2_shm_id = shmget(key, sizeof(*m2_shm_sem), 0666 | (var ? 0 : IPC_CREAT|IPC_EXCL))) == -1) { + return -1; + } + + /* attach shared memory */ + if ((m2_shm_sem = (sem_t*) shmat(m2_shm_id, 0, 0666)) == (sem_t*)-1) { + return -1; + } + + /* root process */ + if (var == 0) { + /* initialize semaphore */ + if (sem_init(m2_shm_sem, 1, pmake_max_jobs)) { + return -1; + } + + /* alloc memory for env variable */ + if ((var = (char*) malloc(MAXPATHLEN)) == 0) { + return -1; + } + + /* put key to env */ + sprintf(var, NOCATGETS("__DMAKE_M2_FILE__=%s"), m2_file); + if (putenv(var)) { + return -1; + } + } + return 0; +} + +static void +m2_fini() { + if (m2_shm_id >= 0) { + struct shmid_ds stat; + + /* determine the number of attached processes */ + if (shmctl(m2_shm_id, IPC_STAT, &stat) == 0) { + if (stat.shm_nattch <= 1) { + /* destroy semaphore */ + if (m2_shm_sem != 0) { + (void) sem_destroy(m2_shm_sem); + } + + /* destroy shared memory */ + (void) shmctl(m2_shm_id, IPC_RMID, &stat); + + /* remove tmp file created for the key */ + (void) unlink(m2_file); + } else { + /* detach shared memory */ + if (m2_shm_sem != 0) { + (void) shmdt((char*) m2_shm_sem); + } + } + } + + m2_shm_id = -1; + m2_shm_sem = 0; + } +} + +static int +m2_acquire_job() { + if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) { + if (sem_trywait(m2_shm_sem) == 0) { + return 1; + } + if (errno == EAGAIN) { + return 0; + } + } + return -1; +} + +static int +m2_release_job() { + if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) { + if (sem_post(m2_shm_sem) == 0) { + return 0; + } + } + return -1; +} + +/* + * job adjust mode + * + * Possible values: + * ADJUST_M1 - adjustment by system load (default) + * ADJUST_M2 - fixed limit of jobs for the group of nested dmakes + * ADJUST_NONE - no adjustment - fixed limit of jobs for the current dmake + */ +static enum { + ADJUST_UNKNOWN, + ADJUST_M1, + ADJUST_M2, + ADJUST_NONE +} job_adjust_mode = ADJUST_UNKNOWN; + +/* + * void job_adjust_fini() + * + * Description: + * Cleans up job adjust data. + * + * Static variables: + * job_adjust_mode Current job adjust mode + */ +void +job_adjust_fini() { + if (job_adjust_mode == ADJUST_M2) { + m2_fini(); + } +} + +/* + * void job_adjust_error() + * + * Description: + * Prints warning message, cleans up job adjust data, and disables job adjustment + * + * Environment: + * DMAKE_ADJUST_MAX_JOBS + * + * External functions: + * putenv() + * + * Static variables: + * job_adjust_mode Current job adjust mode + */ +static void +job_adjust_error() { + if (job_adjust_mode != ADJUST_NONE) { + /* cleanup internals */ + job_adjust_fini(); + + /* warning message for the user */ + warning(catgets(catd, 1, 339, "Encountered max jobs auto adjustment error - disabling auto adjustment.")); + + /* switch off job adjustment for the children */ + putenv(NOCATGETS("DMAKE_ADJUST_MAX_JOBS=NO")); + + /* and for this dmake */ + job_adjust_mode = ADJUST_NONE; + } +} + +/* + * void job_adjust_init() + * + * Description: + * Parses DMAKE_ADJUST_MAX_JOBS env variable + * and performs appropriate initializations. + * + * Environment: + * DMAKE_ADJUST_MAX_JOBS + * DMAKE_ADJUST_MAX_JOBS == "NO" - no adjustment + * DMAKE_ADJUST_MAX_JOBS == "M2" - M2 adjust mode + * other - M1 adjust mode + * + * External functions: + * getenv() + * + * Static variables: + * job_adjust_mode Current job adjust mode + */ +static void +job_adjust_init() { + if (job_adjust_mode == ADJUST_UNKNOWN) { + /* default mode */ + job_adjust_mode = ADJUST_M1; + + /* determine adjust mode */ + if (char *var = getenv(NOCATGETS("DMAKE_ADJUST_MAX_JOBS"))) { + if (strcasecmp(var, NOCATGETS("NO")) == 0) { + job_adjust_mode = ADJUST_NONE; + } else if (strcasecmp(var, NOCATGETS("M2")) == 0) { + job_adjust_mode = ADJUST_M2; + } + } + + /* M2 specific initialization */ + if (job_adjust_mode == ADJUST_M2) { + if (m2_init()) { + job_adjust_error(); + } + } + } +} + +#endif /* MAXJOBS_ADJUST_RFE4694000 */ +#endif /* TEAMWARE_MAKE_CMN */ + +/* + * distribute_process(char **commands, Property line) + * + * Parameters: + * commands argv vector of commands to execute + * + * Return value: + * The result of the execution + * + * Static variables used: + * process_running Set to the pid of the process set running + * #if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000) + * job_adjust_mode Current job adjust mode + * #endif + */ +static Doname +distribute_process(char **commands, Property line) +{ + static unsigned file_number = 0; + wchar_t string[MAXPATHLEN]; + char mbstring[MAXPATHLEN]; + int filed; + int res; + int tmp_index; + char *tmp_index_str_ptr; + +#if !defined (TEAMWARE_MAKE_CMN) || !defined (MAXJOBS_ADJUST_RFE4694000) + while (parallel_process_cnt >= pmake_max_jobs) { + await_parallel(false); + finish_children(true); + } +#else /* TEAMWARE_MAKE_CMN && MAXJOBS_ADJUST_RFE4694000 */ + /* initialize adjust mode, if not initialized */ + if (job_adjust_mode == ADJUST_UNKNOWN) { + job_adjust_init(); + } + + /* actions depend on adjust mode */ + switch (job_adjust_mode) { + case ADJUST_M1: + while (parallel_process_cnt >= adjust_pmake_max_jobs (pmake_max_jobs)) { + await_parallel(false); + finish_children(true); + } + break; + case ADJUST_M2: + if ((res = m2_acquire_job()) == 0) { + if (parallel_process_cnt > 0) { + await_parallel(false); + finish_children(true); + + if ((res = m2_acquire_job()) == 0) { + return build_serial; + } + } else { + return build_serial; + } + } + if (res < 0) { + /* job adjustment error */ + job_adjust_error(); + + /* no adjustment */ + while (parallel_process_cnt >= pmake_max_jobs) { + await_parallel(false); + finish_children(true); + } + } + break; + default: + while (parallel_process_cnt >= pmake_max_jobs) { + await_parallel(false); + finish_children(true); + } + } +#endif /* TEAMWARE_MAKE_CMN && MAXJOBS_ADJUST_RFE4694000 */ +#ifdef DISTRIBUTED + if (send_mtool_msgs) { + send_job_start_msg(line); + } +#endif +#ifdef DISTRIBUTED + setvar_envvar((Avo_DoJobMsg *)NULL); +#else + setvar_envvar(); +#endif + /* + * Tell the user what DMake is doing. + */ + if (!silent && output_mode != txt2_mode) { + /* + * Print local_host --> x job(s). + */ + (void) fprintf(stdout, + catgets(catd, 1, 325, "%s --> %d %s\n"), + local_host, + parallel_process_cnt + 1, + (parallel_process_cnt == 0) ? catgets(catd, 1, 124, "job") : catgets(catd, 1, 125, "jobs")); + + /* Print command line(s). */ + tmp_index = 0; + while (commands[tmp_index] != NULL) { + /* No @ char. */ + /* XXX - need to add [2] when + prefix is added */ + if ((commands[tmp_index][0] != (int) at_char) && + (commands[tmp_index][1] != (int) at_char)) { + tmp_index_str_ptr = commands[tmp_index]; + if (*tmp_index_str_ptr == (int) hyphen_char) { + tmp_index_str_ptr++; + } + (void) fprintf(stdout, "%s\n", tmp_index_str_ptr); + } + tmp_index++; + } + (void) fflush(stdout); + } + + (void) sprintf(mbstring, + NOCATGETS("%s/dmake.stdout.%d.%d.XXXXXX"), + tmpdir, + getpid(), + file_number++); + + mktemp(mbstring); + + stdout_file = strdup(mbstring); + stderr_file = NULL; +#if defined (TEAMWARE_MAKE_CMN) && defined(REDIRECT_ERR) + if (!out_err_same) { + (void) sprintf(mbstring, + NOCATGETS("%s/dmake.stderr.%d.%d.XXXXXX"), + tmpdir, + getpid(), + file_number++); + + mktemp(mbstring); + + stderr_file = strdup(mbstring); + } +#endif + +#ifdef SGE_SUPPORT + if (grid) { + static char *dir4gridscripts = NULL; + static char *hostName = NULL; + if (dir4gridscripts == NULL) { + Name dmakeOdir_name, dmakeOdir_value; + Property prop; + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_ODIR")); + dmakeOdir_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmakeOdir_name->prop, macro_prop)) != NULL) && + ((dmakeOdir_value = prop->body.macro.value) != NULL)) { + dir4gridscripts = dmakeOdir_value->string_mb; + } + dir4gridscripts = Avo_PathNames::pathname_output_directory(dir4gridscripts); + hostName = Avo_PathNames::pathname_local_host(); + } + (void) sprintf(script_file, + NOCATGETS("%s/dmake.script.%s.%d.%d.XXXXXX"), + dir4gridscripts, + hostName, + getpid(), + file_number++); + } +#endif /* SGE_SUPPORT */ + process_running = run_rule_commands(local_host, commands); + + return build_running; +} + +/* + * doname_parallel(target, do_get, implicit) + * + * Processes the given target and finishes up any parallel + * processes left running. + * + * Return value: + * Result of target build + * + * Parameters: + * target Target to build + * do_get True if sccs get to be done + * implicit True if this is an implicit target + */ +Doname +doname_parallel(Name target, Boolean do_get, Boolean implicit) +{ + Doname result; + + result = doname_check(target, do_get, implicit, false); + if (result == build_ok || result == build_failed) { + return result; + } + finish_running(); + return (Doname) target->state; +} + +/* + * doname_subtree(target, do_get, implicit) + * + * Completely computes an object and its dependents for a + * serial subtree build. + * + * Parameters: + * target Target to build + * do_get True if sccs get to be done + * implicit True if this is an implicit target + * + * Static variables used: + * running_tail Tail of the list of running processes + * + * Global variables used: + * running_list The list of running processes + */ +static void +doname_subtree(Name target, Boolean do_get, Boolean implicit) +{ + Running save_running_list; + Running *save_running_tail; + + save_running_list = running_list; + save_running_tail = running_tail; + running_list = NULL; + running_tail = &running_list; + target->state = build_subtree; + target->checking_subtree = true; + while(doname_check(target, do_get, implicit, false) == build_running) { + target->checking_subtree = false; + finish_running(); + target->state = build_subtree; + } + target->checking_subtree = false; + running_list = save_running_list; + running_tail = save_running_tail; +} + +/* + * finish_running() + * + * Keeps processing until the running_list is emptied out. + * + * Parameters: + * + * Global variables used: + * running_list The list of running processes + */ +void +finish_running(void) +{ + while (running_list != NULL) { +#ifdef DISTRIBUTED + if (dmake_mode_type == distributed_mode) { + if ((just_did_subtree) || + (parallel_process_cnt == 0)) { + just_did_subtree = false; + } else { + (void) await_dist(false); + finish_children(true); + } + } else +#endif + { + await_parallel(false); + finish_children(true); + } + if (running_list != NULL) { + process_next(); + } + } +} + +/* + * process_next() + * + * Searches the running list for any targets which can start processing. + * This can be a pending target, a serial target, or a subtree target. + * + * Parameters: + * + * Static variables used: + * running_tail The end of the list of running procs + * subtree_conflict A target which conflicts with a subtree + * subtree_conflict2 The other target which conflicts + * + * Global variables used: + * commands_done True if commands executed + * debug_level Controls debug output + * parallel_process_cnt Number of parallel process running + * recursion_level Indentation for debug output + * running_list List of running processes + */ +static void +process_next(void) +{ + Running rp; + Running *rp_prev; + Property line; + Chain target_group; + Dependency dep; + Boolean quiescent = true; + Running *subtree_target; + Boolean saved_commands_done; + Property *conditionals; + + subtree_target = NULL; + subtree_conflict = NULL; + subtree_conflict2 = NULL; + /* + * If nothing currently running, build a serial target, if any. + */ +start_loop_1: + for (rp_prev = &running_list, rp = running_list; + rp != NULL && parallel_process_cnt == 0; + rp = rp->next) { + if (rp->state == build_serial) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + recursion_level = rp->recursion_level; + rp->target->state = build_pending; + (void) doname_check(rp->target, + rp->do_get, + rp->implicit, + false); + quiescent = false; + delete_running_struct(rp); + goto start_loop_1; + } else { + rp_prev = &rp->next; + } + } + /* + * Find a target to build. The target must be pending, have all + * its dependencies built, and not be in a target group with a target + * currently building. + */ +start_loop_2: + for (rp_prev = &running_list, rp = running_list; + rp != NULL; + rp = rp->next) { + if (!(rp->state == build_pending || + rp->state == build_subtree)) { + quiescent = false; + rp_prev = &rp->next; + } else if (rp->state == build_pending) { + line = get_prop(rp->target->prop, line_prop); + for (dep = line->body.line.dependencies; + dep != NULL; + dep = dep->next) { + if (dep->name->state == build_running || + dep->name->state == build_pending || + dep->name->state == build_serial) { + break; + } + } + if (dep == NULL) { + for (target_group = line->body.line.target_group; + target_group != NULL; + target_group = target_group->next) { + if (is_running(target_group->name)) { + break; + } + } + if (target_group == NULL) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + recursion_level = rp->recursion_level; + rp->target->state = rp->redo ? + build_dont_know : build_pending; + saved_commands_done = commands_done; + conditionals = + set_conditionals + (rp->conditional_cnt, + rp->conditional_targets); + rp->target->dont_activate_cond_values = true; + if ((doname_check(rp->target, + rp->do_get, + rp->implicit, + rp->target->has_target_prop ? true : false) != + build_running) && + !commands_done) { + commands_done = + saved_commands_done; + } + rp->target->dont_activate_cond_values = false; + reset_conditionals + (rp->conditional_cnt, + rp->conditional_targets, + conditionals); + quiescent = false; + delete_running_struct(rp); + goto start_loop_2; + } else { + rp_prev = &rp->next; + } + } else { + rp_prev = &rp->next; + } + } else { + rp_prev = &rp->next; + } + } + /* + * If nothing has been found to build and there exists a subtree + * target with no dependency conflicts, build it. + */ + if (quiescent) { +start_loop_3: + for (rp_prev = &running_list, rp = running_list; + rp != NULL; + rp = rp->next) { + if (rp->state == build_subtree) { + if (!dependency_conflict(rp->target)) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + recursion_level = rp->recursion_level; + doname_subtree(rp->target, + rp->do_get, + rp->implicit); +#ifdef DISTRIBUTED + just_did_subtree = true; +#endif + quiescent = false; + delete_running_struct(rp); + goto start_loop_3; + } else { + subtree_target = rp_prev; + rp_prev = &rp->next; + } + } else { + rp_prev = &rp->next; + } + } + } + /* + * If still nothing found to build, we either have a deadlock + * or a subtree with a dependency conflict with something waiting + * to build. + */ + if (quiescent) { + if (subtree_target == NULL) { + fatal(catgets(catd, 1, 126, "Internal error: deadlock detected in process_next")); + } else { + rp = *subtree_target; + if (debug_level > 0) { + warning(catgets(catd, 1, 127, "Conditional macro conflict encountered for %s between %s and %s"), + subtree_conflict2->string_mb, + rp->target->string_mb, + subtree_conflict->string_mb); + } + *subtree_target = (*subtree_target)->next; + if (rp->next == NULL) { + running_tail = subtree_target; + } + recursion_level = rp->recursion_level; + doname_subtree(rp->target, rp->do_get, rp->implicit); +#ifdef DISTRIBUTED + just_did_subtree = true; +#endif + delete_running_struct(rp); + } + } +} + +/* + * set_conditionals(cnt, targets) + * + * Sets the conditional macros for the targets given in the array of + * targets. The old macro values are returned in an array of + * Properties for later resetting. + * + * Return value: + * Array of conditional macro settings + * + * Parameters: + * cnt Number of targets + * targets Array of targets + */ +static Property * +set_conditionals(int cnt, Name *targets) +{ + Property *locals, *lp; + Name *tp; + + locals = (Property *) getmem(cnt * sizeof(Property)); + for (lp = locals, tp = targets; + cnt > 0; + cnt--, lp++, tp++) { + *lp = (Property) getmem((*tp)->conditional_cnt * + sizeof(struct _Property)); + set_locals(*tp, *lp); + } + return locals; +} + +/* + * reset_conditionals(cnt, targets, locals) + * + * Resets the conditional macros as saved in the given array of + * Properties. The resets are done in reverse order. Afterwards the + * data structures are freed. + * + * Parameters: + * cnt Number of targets + * targets Array of targets + * locals Array of dependency macro settings + */ +static void +reset_conditionals(int cnt, Name *targets, Property *locals) +{ + Name *tp; + Property *lp; + + for (tp = targets + (cnt - 1), lp = locals + (cnt - 1); + cnt > 0; + cnt--, tp--, lp--) { + reset_locals(*tp, + *lp, + get_prop((*tp)->prop, conditional_prop), + 0); + retmem_mb((caddr_t) *lp); + } + retmem_mb((caddr_t) locals); +} + +/* + * dependency_conflict(target) + * + * Returns true if there is an intersection between + * the subtree of the target and any dependents of the pending targets. + * + * Return value: + * True if conflict found + * + * Parameters: + * target Subtree target to check + * + * Static variables used: + * subtree_conflict Target conflict found + * subtree_conflict2 Second conflict found + * + * Global variables used: + * running_list List of running processes + * wait_name .WAIT, not a real dependency + */ +static Boolean +dependency_conflict(Name target) +{ + Property line; + Property pending_line; + Dependency dp; + Dependency pending_dp; + Running rp; + + /* Return if we are already checking this target */ + if (target->checking_subtree) { + return false; + } + target->checking_subtree = true; + line = get_prop(target->prop, line_prop); + if (line == NULL) { + target->checking_subtree = false; + return false; + } + /* Check each dependency of the target for conflicts */ + for (dp = line->body.line.dependencies; dp != NULL; dp = dp->next) { + /* Ignore .WAIT dependency */ + if (dp->name == wait_name) { + continue; + } + /* + * For each pending target, look for a dependency which + * is the same as a dependency of the subtree target. Since + * we can't build the subtree until all pending targets have + * finished which depend on the same dependency, this is + * a conflict. + */ + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->state == build_pending) { + pending_line = get_prop(rp->target->prop, + line_prop); + if (pending_line == NULL) { + continue; + } + for(pending_dp = pending_line-> + body.line.dependencies; + pending_dp != NULL; + pending_dp = pending_dp->next) { + if (dp->name == pending_dp->name) { + target->checking_subtree + = false; + subtree_conflict = rp->target; + subtree_conflict2 = dp->name; + return true; + } + } + } + } + if (dependency_conflict(dp->name)) { + target->checking_subtree = false; + return true; + } + } + target->checking_subtree = false; + return false; +} + +/* + * await_parallel(waitflg) + * + * Waits for parallel children to exit and finishes their processing. + * If waitflg is false, the function returns after update_delay. + * + * Parameters: + * waitflg dwight + */ +void +await_parallel(Boolean waitflg) +{ +#ifdef _CHECK_UPDATE_H + static int number_of_unknown_children = 0; +#endif /* _CHECK_UPDATE_H */ + Boolean nohang; + pid_t pid; + int status; + Running rp; + int waiterr; + + nohang = false; + for ( ; ; ) { + if (!nohang) { + (void) alarm((int) update_delay); + } + pid = waitpid((pid_t)-1, + &status, + nohang ? WNOHANG : 0); + waiterr = errno; + if (!nohang) { + (void) alarm(0); + } + if (pid <= 0) { + if (waiterr == EINTR) { + if (waitflg) { + continue; + } else { + return; + } + } else { + return; + } + } + for (rp = running_list; + (rp != NULL) && (rp->pid != pid); + rp = rp->next) { + ; + } + if (rp == NULL) { +#ifdef _CHECK_UPDATE_H + /* Ignore first child - it is check_update */ + if (number_of_unknown_children <= 0) { + number_of_unknown_children = 1; + return; + } +#endif /* _CHECK_UPDATE_H */ + if (send_mtool_msgs) { + continue; + } else { + fatal(catgets(catd, 1, 128, "Internal error: returned child pid not in running_list")); + } + } else { + rp->state = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? build_ok : build_failed; + } + nohang = true; + parallel_process_cnt--; + +#if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000) + if (job_adjust_mode == ADJUST_M2) { + if (m2_release_job()) { + job_adjust_error(); + } + } +#endif + } +} + +/* + * finish_children(docheck) + * + * Finishes the processing for all targets which were running + * and have now completed. + * + * Parameters: + * docheck Completely check the finished target + * + * Static variables used: + * running_tail The tail of the running list + * + * Global variables used: + * continue_after_error -k flag + * fatal_in_progress True if we are finishing up after fatal err + * running_list List of running processes + */ +void +finish_children(Boolean docheck) +{ + int cmds_length; + Property line; + Property line2; + struct stat out_buf; + Running rp; + Running *rp_prev; + Cmd_line rule; + Boolean silent_flag; + + for (rp_prev = &running_list, rp = running_list; + rp != NULL; + rp = rp->next) { +bypass_for_loop_inc_4: + /* + * If the state is ok or failed, then this target has + * finished building. + * In parallel_mode, output the accumulated stdout/stderr. + * Read the auto dependency stuff, handle a failed build, + * update the target, then finish the doname process for + * that target. + */ + if (rp->state == build_ok || rp->state == build_failed) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + if ((line2 = rp->command) == NULL) { + line2 = get_prop(rp->target->prop, line_prop); + } + if (dmake_mode_type == distributed_mode) { + if (rp->make_refd) { + maybe_reread_make_state(); + } + } else { + /* + * Send an Avo_MToolJobResultMsg to maketool. + */ +#ifdef DISTRIBUTED + if (send_mtool_msgs) { + send_job_result_msg(rp); + } +#endif + /* + * Check if there were any job output + * from the parallel build. + */ + if (rp->stdout_file != NULL) { + if (stat(rp->stdout_file, &out_buf) < 0) { + fatal(catgets(catd, 1, 130, "stat of %s failed: %s"), + rp->stdout_file, + errmsg(errno)); + } + if ((line2 != NULL) && + (out_buf.st_size > 0)) { + cmds_length = 0; + for (rule = line2->body.line.command_used, + silent_flag = silent; + rule != NULL; + rule = rule->next) { + cmds_length += rule->command_line->hash.length + 1; + silent_flag = BOOLEAN(silent_flag || rule->silent); + } + if (out_buf.st_size != cmds_length || silent_flag || + output_mode == txt2_mode) { + dump_out_file(rp->stdout_file, false); + } + } + (void) unlink(rp->stdout_file); + retmem_mb(rp->stdout_file); + rp->stdout_file = NULL; + } +#if defined(REDIRECT_ERR) + if (!out_err_same && (rp->stderr_file != NULL)) { + if (stat(rp->stderr_file, &out_buf) < 0) { + fatal(catgets(catd, 1, 130, "stat of %s failed: %s"), + rp->stderr_file, + errmsg(errno)); + } + if ((line2 != NULL) && + (out_buf.st_size > 0)) { + dump_out_file(rp->stderr_file, true); + } + (void) unlink(rp->stderr_file); + retmem_mb(rp->stderr_file); + rp->stderr_file = NULL; + } +#endif + } + check_state(rp->temp_file); + if (rp->temp_file != NULL) { + free_name(rp->temp_file); + } + rp->temp_file = NULL; + if (rp->state == build_failed) { + line = get_prop(rp->target->prop, line_prop); + if (line != NULL) { + line->body.line.command_used = NULL; + } + if (continue_after_error || + fatal_in_progress || + !docheck) { + warning(catgets(catd, 1, 256, "Command failed for target `%s'"), + rp->command ? line2->body.line.target->string_mb : rp->target->string_mb); + build_failed_seen = true; + } else { + /* + * XXX??? - DMake needs to exit(), + * but shouldn't call fatal(). + */ +#ifdef PRINT_EXIT_STATUS + warning(NOCATGETS("I'm in finish_children. rp->state == build_failed.")); +#endif + + fatal(catgets(catd, 1, 258, "Command failed for target `%s'"), + rp->command ? line2->body.line.target->string_mb : rp->target->string_mb); + } + } + if (!docheck) { + delete_running_struct(rp); + rp = *rp_prev; + if (rp == NULL) { + break; + } else { + goto bypass_for_loop_inc_4; + } + } + update_target(get_prop(rp->target->prop, line_prop), + rp->state); + finish_doname(rp); + delete_running_struct(rp); + rp = *rp_prev; + if (rp == NULL) { + break; + } else { + goto bypass_for_loop_inc_4; + } + } else { + rp_prev = &rp->next; + } + } +} + +/* + * dump_out_file(filename, err) + * + * Write the contents of the file to stdout, then unlink the file. + * + * Parameters: + * filename Name of temp file containing output + * + * Global variables used: + */ +static void +dump_out_file(char *filename, Boolean err) +{ + int chars_read; + char copybuf[BUFSIZ]; + int fd; + int out_fd = (err ? 2 : 1); + + if ((fd = open(filename, O_RDONLY)) < 0) { + fatal(catgets(catd, 1, 141, "open failed for output file %s: %s"), + filename, + errmsg(errno)); + } + if (!silent && output_mode != txt2_mode) { + (void) fprintf(err ? stderr : stdout, + err ? + catgets(catd, 1, 338, "%s --> Job errors\n") : + catgets(catd, 1, 259, "%s --> Job output\n"), + local_host); + (void) fflush(err ? stderr : stdout); + } + for (chars_read = read(fd, copybuf, BUFSIZ); + chars_read > 0; + chars_read = read(fd, copybuf, BUFSIZ)) { + /* + * Read buffers from the source file until end or error. + */ + if (write(out_fd, copybuf, chars_read) < 0) { + fatal(catgets(catd, 1, 260, "write failed for output file %s: %s"), + filename, + errmsg(errno)); + } + } + (void) close(fd); + (void) unlink(filename); +} + +/* + * finish_doname(rp) + * + * Completes the processing for a target which was left running. + * + * Parameters: + * rp Running list entry for target + * + * Global variables used: + * debug_level Debug flag + * recursion_level Indentation for debug output + */ +static void +finish_doname(Running rp) +{ + int auto_count = rp->auto_count; + Name *automatics = rp->automatics; + Doname result = rp->state; + Name target = rp->target; + Name true_target = rp->true_target; + Property *conditionals; + + recursion_level = rp->recursion_level; + if (result == build_ok) { + if (true_target == NULL) { + (void) printf(NOCATGETS("Target = %s\n"), target->string_mb); + (void) printf(NOCATGETS(" State = %d\n"), result); + fatal(NOCATGETS("Internal error: NULL true_target in finish_doname")); + } + /* If all went OK, set a nice timestamp */ + if (true_target->stat.time == file_doesnt_exist) { + true_target->stat.time = file_max_time; + } + } + target->state = result; + if (target->is_member) { + Property member; + + /* Propagate the timestamp from the member file to the member */ + if ((target->stat.time != file_max_time) && + ((member = get_prop(target->prop, member_prop)) != NULL) && + (exists(member->body.member.member) > file_doesnt_exist)) { + target->stat.time = +/* + exists(member->body.member.member); + */ + member->body.member.member->stat.time; + } + } + /* + * Check if we found any new auto dependencies when we + * built the target. + */ + if ((result == build_ok) && check_auto_dependencies(target, + auto_count, + automatics)) { + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 261, "%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"), + recursion_level, + "", + true_target->string_mb); + } + target->rechecking_target = true; + target->state = build_running; + + /* [tolik, Tue Mar 25 1997] + * Fix for bug 4038824: + * command line options set by conditional macros get dropped + * rp->conditional_cnt and rp->conditional_targets must be copied + * to new 'rp' during add_pending(). Set_conditionals() stores + * rp->conditional_targets to the global variable 'conditional_targets' + * Add_pending() will use this variable to set up 'rp'. + */ + conditionals = set_conditionals(rp->conditional_cnt, rp->conditional_targets); + add_pending(target, + recursion_level, + rp->do_get, + rp->implicit, + true); + reset_conditionals(rp->conditional_cnt, rp->conditional_targets, conditionals); + } +} + +/* + * new_running_struct() + * + * Constructor for Running struct. Creates a structure and initializes + * its fields. + * + */ +static Running new_running_struct() +{ + Running rp; + + rp = ALLOC(Running); + rp->target = NULL; + rp->true_target = NULL; + rp->command = NULL; + rp->sprodep_value = NULL; + rp->sprodep_env = NULL; + rp->auto_count = 0; + rp->automatics = NULL; + rp->pid = -1; + rp->job_msg_id = -1; + rp->stdout_file = NULL; + rp->stderr_file = NULL; + rp->temp_file = NULL; + rp->next = NULL; + return rp; +} + +/* + * add_running(target, true_target, command, recursion_level, auto_count, + * automatics, do_get, implicit) + * + * Adds a record on the running list for this target, which + * was just spawned and is running. + * + * Parameters: + * target Target being built + * true_target True target for target + * command Running command. + * recursion_level Debug indentation level + * auto_count Count of automatic dependencies + * automatics List of automatic dependencies + * do_get Sccs get flag + * implicit Implicit flag + * + * Static variables used: + * running_tail Tail of running list + * process_running PID of process + * + * Global variables used: + * current_line Current line for target + * current_target Current target being built + * stderr_file Temporary file for stdout + * stdout_file Temporary file for stdout + * temp_file_name Temporary file for auto dependencies + */ +void +add_running(Name target, Name true_target, Property command, int recursion_level, int auto_count, Name *automatics, Boolean do_get, Boolean implicit) +{ + Running rp; + Name *p; + + rp = new_running_struct(); + rp->state = build_running; + rp->target = target; + rp->true_target = true_target; + rp->command = command; + Property spro_val = get_prop(sunpro_dependencies->prop, macro_prop); + if(spro_val) { + rp->sprodep_value = spro_val->body.macro.value; + spro_val->body.macro.value = NULL; + spro_val = get_prop(sunpro_dependencies->prop, env_mem_prop); + if(spro_val) { + rp->sprodep_env = spro_val->body.env_mem.value; + spro_val->body.env_mem.value = NULL; + } + } + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->auto_count = auto_count; + if (auto_count > 0) { + rp->automatics = (Name *) getmem(auto_count * sizeof (Name)); + for (p = rp->automatics; auto_count > 0; auto_count--) { + *p++ = *automatics++; + } + } else { + rp->automatics = NULL; + } +#ifdef DISTRIBUTED + if (dmake_mode_type == distributed_mode) { + rp->make_refd = called_make; + called_make = false; + } else +#endif + { + rp->pid = process_running; + process_running = -1; + childPid = -1; + } + rp->job_msg_id = job_msg_id; + rp->stdout_file = stdout_file; + rp->stderr_file = stderr_file; + rp->temp_file = temp_file_name; + rp->redo = false; + rp->next = NULL; + store_conditionals(rp); + stdout_file = NULL; + stderr_file = NULL; + temp_file_name = NULL; + current_target = NULL; + current_line = NULL; + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * add_pending(target, recursion_level, do_get, implicit, redo) + * + * Adds a record on the running list for a pending target + * (waiting for its dependents to finish running). + * + * Parameters: + * target Target being built + * recursion_level Debug indentation level + * do_get Sccs get flag + * implicit Implicit flag + * redo True if this target is being redone + * + * Static variables used: + * running_tail Tail of running list + */ +void +add_pending(Name target, int recursion_level, Boolean do_get, Boolean implicit, Boolean redo) +{ + Running rp; + rp = new_running_struct(); + rp->state = build_pending; + rp->target = target; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->redo = redo; + store_conditionals(rp); + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * add_serial(target, recursion_level, do_get, implicit) + * + * Adds a record on the running list for a target which must be + * executed in serial after others have finished. + * + * Parameters: + * target Target being built + * recursion_level Debug indentation level + * do_get Sccs get flag + * implicit Implicit flag + * + * Static variables used: + * running_tail Tail of running list + */ +void +add_serial(Name target, int recursion_level, Boolean do_get, Boolean implicit) +{ + Running rp; + + rp = new_running_struct(); + rp->target = target; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->state = build_serial; + rp->redo = false; + store_conditionals(rp); + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * add_subtree(target, recursion_level, do_get, implicit) + * + * Adds a record on the running list for a target which must be + * executed in isolation after others have finished. + * + * Parameters: + * target Target being built + * recursion_level Debug indentation level + * do_get Sccs get flag + * implicit Implicit flag + * + * Static variables used: + * running_tail Tail of running list + */ +void +add_subtree(Name target, int recursion_level, Boolean do_get, Boolean implicit) +{ + Running rp; + + rp = new_running_struct(); + rp->target = target; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->state = build_subtree; + rp->redo = false; + store_conditionals(rp); + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * store_conditionals(rp) + * + * Creates an array of the currently active targets with conditional + * macros (found in the chain conditional_targets) and puts that + * array in the Running struct. + * + * Parameters: + * rp Running struct for storing chain + * + * Global variables used: + * conditional_targets Chain of current dynamic conditionals + */ +static void +store_conditionals(Running rp) +{ + int cnt; + Chain cond_name; + + if (conditional_targets == NULL) { + rp->conditional_cnt = 0; + rp->conditional_targets = NULL; + return; + } + cnt = 0; + for (cond_name = conditional_targets; + cond_name != NULL; + cond_name = cond_name->next) { + cnt++; + } + rp->conditional_cnt = cnt; + rp->conditional_targets = (Name *) getmem(cnt * sizeof(Name)); + for (cond_name = conditional_targets; + cond_name != NULL; + cond_name = cond_name->next) { + rp->conditional_targets[--cnt] = cond_name->name; + } +} + +/* + * parallel_ok(target, line_prop_must_exists) + * + * Returns true if the target can be run in parallel + * + * Return value: + * True if can run in parallel + * + * Parameters: + * target Target being tested + * + * Global variables used: + * all_parallel True if all targets default to parallel + * only_parallel True if no targets default to parallel + */ +Boolean +parallel_ok(Name target, Boolean line_prop_must_exists) +{ + Boolean assign; + Boolean make_refd; + Property line; + Cmd_line rule; + + assign = make_refd = false; + if (((line = get_prop(target->prop, line_prop)) == NULL) && + line_prop_must_exists) { + return false; + } + if (line != NULL) { + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (rule->assign) { + assign = true; + } else if (rule->make_refd) { + make_refd = true; + } + } + } + if (assign) { + return false; + } else if (target->parallel) { + return true; + } else if (target->no_parallel) { + return false; + } else if (all_parallel) { + return true; + } else if (only_parallel) { + return false; + } else if (make_refd) { + return false; + } else { + return true; + } +} + +/* + * is_running(target) + * + * Returns true if the target is running. + * + * Return value: + * True if target is running + * + * Parameters: + * target Target to check + * + * Global variables used: + * running_list List of running processes + */ +Boolean +is_running(Name target) +{ + Running rp; + + if (target->state != build_running) { + return false; + } + for (rp = running_list; + rp != NULL && target != rp->target; + rp = rp->next); + if (rp == NULL) { + return false; + } else { + return (rp->state == build_running) ? true : false; + } +} + +/* + * This function replaces the makesh binary. + */ + +#ifdef SGE_SUPPORT +#define DO_CHECK(f) if (f <= 0) { \ + fprintf(stderr, \ + catgets(catd, 1, 347, "Could not write to file: %s: %s\n"), \ + script_file, errmsg(errno)); \ + _exit(1); \ + } +#endif /* SGE_SUPPORT */ + +static pid_t +run_rule_commands(char *host, char **commands) +{ + Boolean always_exec; + Name command; + Boolean ignore; + int length; + Doname result; + Boolean silent_flag; +#ifdef SGE_SUPPORT + wchar_t *wcmd, *tmp_wcs_buffer = NULL; + char *cmd, *tmp_mbs_buffer = NULL; + FILE *scrfp; + Name shell = getvar(shell_name); +#else + wchar_t *tmp_wcs_buffer; +#endif /* SGE_SUPPORT */ + + childPid = fork(); + switch (childPid) { + case -1: /* Error */ + fatal(catgets(catd, 1, 337, "Could not fork child process for dmake job: %s"), + errmsg(errno)); + break; + case 0: /* Child */ + /* To control the processed targets list is not the child's business */ + running_list = NULL; +#if defined(REDIRECT_ERR) + if(out_err_same) { + redirect_io(stdout_file, (char*)NULL); + } else { + redirect_io(stdout_file, stderr_file); + } +#else + redirect_io(stdout_file, (char*)NULL); +#endif +#ifdef SGE_SUPPORT + if (grid) { + int fdes = mkstemp(script_file); + if ((fdes < 0) || (scrfp = fdopen(fdes, "w")) == NULL) { + fprintf(stderr, + catgets(catd, 1, 341, "Could not create file: %s: %s\n"), + script_file, errmsg(errno)); + _exit(1); + } + if (IS_EQUAL(shell->string_mb, "")) { + shell = shell_name; + } + } +#endif /* SGE_SUPPORT */ + for (commands = commands; + (*commands != (char *)NULL); + commands++) { + silent_flag = silent; + ignore = false; + always_exec = false; + while ((**commands == (int) at_char) || + (**commands == (int) hyphen_char) || + (**commands == (int) plus_char)) { + if (**commands == (int) at_char) { + silent_flag = true; + } + if (**commands == (int) hyphen_char) { + ignore = true; + } + if (**commands == (int) plus_char) { + always_exec = true; + } + (*commands)++; + } +#ifdef SGE_SUPPORT + if (grid) { + if ((length = strlen(*commands)) >= MAXPATHLEN / 2) { + wcmd = tmp_wcs_buffer = ALLOC_WC(length * 2 + 1); + (void) mbstowcs(tmp_wcs_buffer, *commands, length * 2 + 1); + } else { + MBSTOWCS(wcs_buffer, *commands); + wcmd = wcs_buffer; + cmd = mbs_buffer; + } + wchar_t *from = wcmd + wslen(wcmd); + wchar_t *to = from + (from - wcmd); + *to = (int) nul_char; + while (from > wcmd) { + *--to = *--from; + if (*from == (int) newline_char) { // newline symbols are already quoted + *--to = *--from; + } else if (wschr(char_semantics_char, *from)) { + *--to = (int) backslash_char; + } + } + if (length >= MAXPATHLEN*MB_LEN_MAX/2) { // sizeof(mbs_buffer) / 2 + cmd = tmp_mbs_buffer = getmem((length * MB_LEN_MAX * 2) + 1); + (void) wcstombs(tmp_mbs_buffer, to, (length * MB_LEN_MAX * 2) + 1); + } else { + WCSTOMBS(mbs_buffer, to); + cmd = mbs_buffer; + } + char *mbst, *mbend; + if ((length > 0) && + !silent_flag) { + for (mbst = cmd; (mbend = strstr(mbst, "\\\n")) != NULL; mbst = mbend + 2) { + *mbend = '\0'; + DO_CHECK(fprintf(scrfp, NOCATGETS("/usr/bin/printf '%%s\\n' %s\\\\\n"), mbst)); + *mbend = '\\'; + } + DO_CHECK(fprintf(scrfp, NOCATGETS("/usr/bin/printf '%%s\\n' %s\n"), mbst)); + } + if (!do_not_exec_rule || + !working_on_targets || + always_exec) { +#if defined(linux) + if (0 != strcmp(shell->string_mb, (char*)NOCATGETS("/bin/sh"))) { + DO_CHECK(fprintf(scrfp, NOCATGETS("%s -c %s\n"), shell->string_mb, cmd)); + } else +#endif + DO_CHECK(fprintf(scrfp, NOCATGETS("%s -ce %s\n"), shell->string_mb, cmd)); + DO_CHECK(fputs(NOCATGETS("__DMAKECMDEXITSTAT=$?\nif [ ${__DMAKECMDEXITSTAT} -ne 0 ]; then\n"), scrfp)); + if (ignore) { + DO_CHECK(fprintf(scrfp, NOCATGETS("\techo %s ${__DMAKECMDEXITSTAT} %s\n"), + catgets(catd, 1, 343, "\"*** Error code"), + catgets(catd, 1, 344, "(ignored)\""))); + } else { + DO_CHECK(fprintf(scrfp, NOCATGETS("\techo %s ${__DMAKECMDEXITSTAT}\n"), + catgets(catd, 1, 342, "\"*** Error code\""))); + } + if (silent_flag) { + DO_CHECK(fprintf(scrfp, NOCATGETS("\techo %s\n"), + catgets(catd, 1, 345, "The following command caused the error:"))); + for (mbst = cmd; (mbend = strstr(mbst, "\\\n")) != NULL; mbst = mbend + 2) { + *mbend = '\0'; + DO_CHECK(fprintf(scrfp, NOCATGETS("\t/usr/bin/printf '%%s\\n' %s\\\\\n"), mbst)); + *mbend = '\\'; + } + DO_CHECK(fprintf(scrfp, NOCATGETS("\t/usr/bin/printf '%%s\\n' %s\n"), mbst)); + } + if (!ignore) { + DO_CHECK(fputs(NOCATGETS("\texit ${__DMAKECMDEXITSTAT}\n"), scrfp)); + } + DO_CHECK(fputs(NOCATGETS("fi\n"), scrfp)); + } + if (tmp_wcs_buffer) { + retmem_mb(tmp_mbs_buffer); + tmp_mbs_buffer = NULL; + } + if (tmp_wcs_buffer) { + retmem(tmp_wcs_buffer); + tmp_wcs_buffer = NULL; + } + continue; + } +#endif /* SGE_SUPPORT */ + if ((length = strlen(*commands)) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + (void) mbstowcs(tmp_wcs_buffer, *commands, length + 1); + command = GETNAME(tmp_wcs_buffer, FIND_LENGTH); + retmem(tmp_wcs_buffer); + } else { + MBSTOWCS(wcs_buffer, *commands); + command = GETNAME(wcs_buffer, FIND_LENGTH); + } + if ((command->hash.length > 0) && + !silent_flag) { + (void) printf("%s\n", command->string_mb); + } + result = dosys(command, + ignore, + false, + false, /* bugs #4085164 & #4990057 */ + /* BOOLEAN(silent_flag && ignore), */ + always_exec, + (Name) NULL, + false); + if (result == build_failed) { + if (silent_flag) { + (void) printf(catgets(catd, 1, 152, "The following command caused the error:\n%s\n"), command->string_mb); + } + if (!ignore) { + _exit(1); + } + } + } +#ifndef SGE_SUPPORT + _exit(0); +#else + if (!grid) { + _exit(0); + } + DO_CHECK(fputs(NOCATGETS("exit 0\n"), scrfp)); + if (fclose(scrfp) != 0) { + fprintf(stderr, + catgets(catd, 1, 346, "Could not close file: %s: %s\n"), + script_file, errmsg(errno)); + _exit(1); + } + { + +#define DEFAULT_QRSH_TRIES_NUMBER 1 +#define DEFAULT_QRSH_TIMEOUT 0 + + static char *sge_env_var = NULL; + static int qrsh_tries_number = DEFAULT_QRSH_TRIES_NUMBER; + static int qrsh_timeout = DEFAULT_QRSH_TIMEOUT; +#define SGE_DEBUG +#ifdef SGE_DEBUG + static Boolean do_not_remove = false; +#endif /* SGE_DEBUG */ + if (sge_env_var == NULL) { + sge_env_var = getenv(NOCATGETS("__SPRO_DMAKE_SGE_TRIES")); + if (sge_env_var != NULL) { + qrsh_tries_number = atoi(sge_env_var); + if (qrsh_tries_number < 1 || qrsh_tries_number > 9) { + qrsh_tries_number = DEFAULT_QRSH_TRIES_NUMBER; + } + } + sge_env_var = getenv(NOCATGETS("__SPRO_DMAKE_SGE_TIMEOUT")); + if (sge_env_var != NULL) { + qrsh_timeout = atoi(sge_env_var); + if (qrsh_timeout <= 0) { + qrsh_timeout = DEFAULT_QRSH_TIMEOUT; + } + } else { + sge_env_var = ""; + } +#ifdef SGE_DEBUG + sge_env_var = getenv(NOCATGETS("__SPRO_DMAKE_SGE_DEBUG")); + if (sge_env_var == NULL) { + sge_env_var = ""; + } + if (strstr(sge_env_var, NOCATGETS("noqrsh")) != NULL) + qrsh_tries_number = 0; + if (strstr(sge_env_var, NOCATGETS("donotremove")) != NULL) + do_not_remove = true; +#endif /* SGE_DEBUG */ + } + for (int i = qrsh_tries_number; ; i--) + if ((childPid = fork()) < 0) { + fatal(catgets(catd, 1, 348, "Could not fork child process for qrsh job: %s"), + errmsg(errno)); + _exit(1); + } else if (childPid == 0) { + enable_interrupt((void (*) (int))SIG_DFL); + if (i > 0) { + static char qrsh_cmd[50+MAXPATHLEN] = NOCATGETS("qrsh -cwd -V -noshell -nostdin /bin/sh "); + static char *fname_ptr = NULL; + static char *argv[] = { NOCATGETS("sh"), + NOCATGETS("-fce"), + qrsh_cmd, + NULL}; + if (fname_ptr == NULL) { + fname_ptr = qrsh_cmd + strlen(qrsh_cmd); + } + strcpy(fname_ptr, script_file); + (void) execve(NOCATGETS("/bin/sh"), argv, environ); + } else { + static char *argv[] = { NOCATGETS("sh"), + script_file, + NULL}; + (void) execve(NOCATGETS("/bin/sh"), argv, environ); + } + fprintf(stderr, + catgets(catd, 1, 349, "Could not load `qrsh': %s\n"), + errmsg(errno)); + _exit(1); + } else { +#if defined (HP_UX) || defined (linux) || defined (SUN5_0) + int status; +#else + union wait status; +#endif + pid_t pid; + while ((pid = wait(&status)) != childPid) { + if (pid == -1) { + fprintf(stderr, + catgets(catd, 1, 350, "wait() failed: %s\n"), + errmsg(errno)); + _exit(1); + } + } + if (status != 0 && i > 0) { + if (i > 1) { + sleep(qrsh_timeout); + } + continue; + } +#ifdef SGE_DEBUG + if (do_not_remove) { + if (status) { + fprintf(stderr, + NOCATGETS("SGE script failed: %s\n"), + script_file); + } + _exit(status ? 1 : 0); + } +#endif /* SGE_DEBUG */ + (void) unlink(script_file); + _exit(status ? 1 : 0); + } + } +#endif /* SGE_SUPPORT */ + break; + default: + break; + } + return childPid; +} + +static void +maybe_reread_make_state(void) +{ + /* Copying dosys()... */ + if (report_dependencies_level == 0) { + make_state->stat.time = file_no_time; + (void) exists(make_state); + if (make_state_before == make_state->stat.time) { + return; + } + makefile_type = reading_statefile; + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + } +} + +#ifdef DISTRIBUTED +/* + * Create and send an Avo_MToolJobResultMsg. + */ +static void +send_job_result_msg(Running rp) +{ + Avo_MToolJobResultMsg *msg; + RWCollectable *xdr_msg; + + msg = new Avo_MToolJobResultMsg(); + msg->setResult(rp->job_msg_id, + (rp->state == build_ok) ? 0 : 1, + DONE); + append_job_result_msg(msg, + rp->stdout_file, + rp->stderr_file); + + xdr_msg = (RWCollectable *)msg; + xdr(get_xdrs_ptr(), xdr_msg); + (void) fflush(get_mtool_msgs_fp()); + + delete msg; +} + +/* + * Append the stdout/err to Avo_MToolJobResultMsg. + */ +static void +append_job_result_msg(Avo_MToolJobResultMsg *msg, char *outFn, char *errFn) +{ + FILE *fp; + char line[MAXPATHLEN]; + + fp = fopen(outFn, "r"); + if (fp == NULL) { + /* Hmmm... what should we do here? */ + return; + } + while (fgets(line, MAXPATHLEN, fp) != NULL) { + if (line[strlen(line) - 1] == '\n') { + line[strlen(line) - 1] = '\0'; + } + msg->appendOutput(AVO_STRDUP(line)); + } + (void) fclose(fp); +} +#endif + +static void +delete_running_struct(Running rp) +{ + if ((rp->conditional_cnt > 0) && + (rp->conditional_targets != NULL)) { + retmem_mb((char *) rp->conditional_targets); + } +/**/ + if ((rp->auto_count > 0) && + (rp->automatics != NULL)) { + retmem_mb((char *) rp->automatics); + } +/**/ + if(rp->sprodep_value) { + free_name(rp->sprodep_value); + } + if(rp->sprodep_env) { + retmem_mb(rp->sprodep_env); + } + retmem_mb((char *) rp); + +} + +#endif + diff --git a/usr/src/make_src/Make/bin/make/common/pmake.cc b/usr/src/make_src/Make/bin/make/common/pmake.cc new file mode 100644 index 0000000..3544077 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/pmake.cc @@ -0,0 +1,434 @@ +/* + * 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. + */ +/* + * @(#)pmake.cc 1.9 06/12/12 + */ + +#pragma ident "@(#)pmake.cc 1.9 06/12/12" + +#ifdef TEAMWARE_MAKE_CMN + +/* + * Included files + */ +#include <arpa/inet.h> +#include <mk/defs.h> +#include <mksh/misc.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <rpc/rpc.h> /* host2netname(), netname2host() */ +#ifdef linux +# include <unistd.h> /* getdomainname() */ +#endif + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +static int get_max(wchar_t **ms_address, wchar_t *hostname); +static Boolean pskip_comment(wchar_t **cp_address); +static void pskip_till_next_word(wchar_t **cp); +static Boolean pskip_white_space(wchar_t **cp_address); + + +/* + * read_make_machines(Name make_machines_name) + * + * For backwards compatibility w/ PMake 1.x, when DMake 2.x is + * being run in parallel mode, DMake should parse the PMake startup + * file $(HOME)/.make.machines to get the PMake max jobs. + * + * Return value: + * int of PMake max jobs + * + * Parameters: + * make_machines_name Name of .make.machines file + * + */ +int +read_make_machines(Name make_machines_name) +{ + wchar_t c; + Boolean default_make_machines; + struct hostent *hp; + wchar_t local_host[MAX_HOSTNAMELEN + 1]; + char local_host_mb[MAX_HOSTNAMELEN + 1] = ""; + int local_host_wslen; + wchar_t full_host[MAXNETNAMELEN + 1]; + int full_host_wslen = 0; + char *homedir; + Name MAKE_MACHINES; + struct stat make_machines_buf; + FILE *make_machines_file; + wchar_t *make_machines_list = NULL; + char *make_machines_list_mb = NULL; + wchar_t make_machines_path[MAXPATHLEN]; + char mb_make_machines_path[MAXPATHLEN]; + wchar_t *mp; + wchar_t *ms; + int pmake_max_jobs = 0; + struct utsname uts_info; + + + MBSTOWCS(wcs_buffer, NOCATGETS("MAKE_MACHINES")); + MAKE_MACHINES = GETNAME(wcs_buffer, FIND_LENGTH); + /* Did the user specify a .make.machines file on the command line? */ + default_make_machines = false; + if (make_machines_name == NULL) { + /* Try reading the default .make.machines file, in $(HOME). */ + homedir = getenv(NOCATGETS("HOME")); + if ((homedir != NULL) && (strlen(homedir) < (sizeof(mb_make_machines_path) - 16))) { + sprintf(mb_make_machines_path, + NOCATGETS("%s/.make.machines"), homedir); + MBSTOWCS(make_machines_path, mb_make_machines_path); + make_machines_name = GETNAME(make_machines_path, FIND_LENGTH); + default_make_machines = true; + } + if (make_machines_name == NULL) { + /* + * No $(HOME)/.make.machines file. + * Return 0 for PMake max jobs. + */ + return(0); + } + } +/* + make_machines_list_mb = getenv(MAKE_MACHINES->string_mb); + */ + /* Open the .make.machines file. */ + if ((make_machines_file = fopen(make_machines_name->string_mb, "r")) == NULL) { + if (!default_make_machines) { + /* Error opening .make.machines file. */ + fatal(catgets(catd, 1, 314, "Open of %s failed: %s"), + make_machines_name->string_mb, + errmsg(errno)); + } else { + /* + * No $(HOME)/.make.machines file. + * Return 0 for PMake max jobs. + */ + return(0); + } + /* Stat the .make.machines file to get the size of the file. */ + } else if (fstat(fileno(make_machines_file), &make_machines_buf) < 0) { + /* Error stat'ing .make.machines file. */ + fatal(catgets(catd, 1, 315, "Stat of %s failed: %s"), + make_machines_name->string_mb, + errmsg(errno)); + } else { + /* Allocate memory for "MAKE_MACHINES=<contents of .m.m>" */ + make_machines_list_mb = + (char *) getmem((int) (strlen(MAKE_MACHINES->string_mb) + + 2 + + make_machines_buf.st_size)); + sprintf(make_machines_list_mb, + "%s=", + MAKE_MACHINES->string_mb); + /* Read in the .make.machines file. */ + if (fread(make_machines_list_mb + strlen(MAKE_MACHINES->string_mb) + 1, + sizeof(char), + (int) make_machines_buf.st_size, + make_machines_file) != make_machines_buf.st_size) { + /* + * Error reading .make.machines file. + * Return 0 for PMake max jobs. + */ + warning(catgets(catd, 1, 316, "Unable to read %s"), + make_machines_name->string_mb); + (void) fclose(make_machines_file); + retmem_mb((caddr_t) make_machines_list_mb); + return(0); + } else { + (void) fclose(make_machines_file); + /* putenv "MAKE_MACHINES=<contents of .m.m>" */ + *(make_machines_list_mb + + strlen(MAKE_MACHINES->string_mb) + + 1 + + make_machines_buf.st_size) = (int) nul_char; + if (putenv(make_machines_list_mb) != 0) { + warning(catgets(catd, 1, 317, "Couldn't put contents of %s in environment"), + make_machines_name->string_mb); + } else { + make_machines_list_mb += strlen(MAKE_MACHINES->string_mb) + 1; + make_machines_list = ALLOC_WC(strlen(make_machines_list_mb) + 1); + (void) mbstowcs(make_machines_list, + make_machines_list_mb, + (strlen(make_machines_list_mb) + 1)); + } + } + } + + uname(&uts_info); + strcpy(local_host_mb, &uts_info.nodename[0]); + MBSTOWCS(local_host, local_host_mb); + local_host_wslen = wslen(local_host); + + // There is no getdomainname() function on Solaris. + // And netname2host() function does not work on Linux. + // So we have to use different APIs. +#ifdef linux + if (getdomainname(mbs_buffer, MAXNETNAMELEN+1) == 0) { + sprintf(mbs_buffer2, "%s.%s", local_host_mb, mbs_buffer); +#else + if (host2netname(mbs_buffer, NULL, NULL) && + netname2host(mbs_buffer, mbs_buffer2, MAXNETNAMELEN+1)) { +#endif + MBSTOWCS(full_host, mbs_buffer2); + full_host_wslen = wslen(full_host); + } + + for (ms = make_machines_list; + (ms) && (*ms ); + ) { + /* + * Skip white space and comments till you reach + * a machine name. + */ + pskip_till_next_word(&ms); + + /* + * If we haven't reached the end of file, process the + * machine name. + */ + if (*ms) { + /* + * If invalid machine name decrement counter + * and skip line. + */ + mp = ms; + SKIPWORD(ms); + c = *ms; + *ms++ = '\0'; /* Append null to machine name. */ + /* + * If this was the beginning of a comment + * (we overwrote a # sign) and it's not + * end of line yet, shift the # sign. + */ + if ((c == '#') && (*ms != '\n') && (*ms)) { + *ms = '#'; + } + WCSTOMBS(mbs_buffer, mp); + /* + * Print "Ignoring unknown host" if: + * 1) hostname is longer than MAX_HOSTNAMELEN, or + * 2) hostname is unknown + */ + if ((wslen(mp) > MAX_HOSTNAMELEN) || + ((hp = gethostbyname(mbs_buffer)) == NULL)) { + warning(catgets(catd, 1, 318, "Ignoring unknown host %s"), + mbs_buffer); + SKIPTOEND(ms); + /* Increment ptr if not end of file. */ + if (*ms) { + ms++; + } + } else { + /* Compare current hostname with local_host. */ + if (wslen(mp) == local_host_wslen && + IS_WEQUALN(mp, local_host, local_host_wslen)) { + /* + * Bingo, local_host is in .make.machines. + * Continue reading. + */ + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + /* Compare current hostname with full_host. */ + } else if (wslen(mp) == full_host_wslen && + IS_WEQUALN(mp, full_host, full_host_wslen)) { + /* + * Bingo, full_host is in .make.machines. + * Continue reading. + */ + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + } else { + if (c != '\n') { + SKIPTOEND(ms); + if (*ms) { + ms++; + } + } + continue; + } + /* If we get here, local_host is in .make.machines. */ + if (c != '\n') { + /* Now look for keyword 'max'. */ + MBSTOWCS(wcs_buffer, NOCATGETS("max")); + SKIPSPACE(ms); + while ((*ms != '\n') && (*ms)) { + if (*ms == '#') { + pskip_comment(&ms); + } else if (IS_WEQUALN(ms, wcs_buffer, 3)) { + /* Skip "max". */ + ms += 3; + pmake_max_jobs = get_max(&ms, mp); + SKIPSPACE(ms); + } else { + warning(catgets(catd, 1, 322, "unknown option for host %s"), mbs_buffer); + SKIPTOEND(ms); + break; + } + } + } + break; /* out of outermost for() loop. */ + } + } + } + retmem(make_machines_list); + return(pmake_max_jobs); +} + +/* + * pskip_till_next_word(cp) + * + * Parameters: + * cp the address of the string pointer. + * + * On return: + * cp points to beginning of machine name. + * + */ +static void +pskip_till_next_word(wchar_t **cp) +{ + /* + * Keep recursing until all combinations of white spaces + * and comments have been skipped. + */ + if (pskip_white_space(cp) || pskip_comment(cp)) { + pskip_till_next_word(cp); + } +} + +/* + * pskip_white_space(cp_address) + * + * Advances the string pointer so that it points to the first + * non white character (space/tab/linefeed). + * + * Parameters: + * cp_address the address of the string pointer. + * + * Return Value: + * True if the pointer was changed. + * + */ +static Boolean +pskip_white_space(wchar_t **cp_address) +{ + wchar_t *cp = *cp_address; + + while (*cp && iswspace(*cp)) { + cp++; + } + /* Have we skipped any characters? */ + if (cp != *cp_address) { + *cp_address = cp; + return(true); + } else { + return(false); + } +} + +/* + * pskip_comment(cp_address) + * + * If cp_address is pointing to '#' (the beginning of a comment), + * increment the pointer till you reach end of line. + * + * Parameters: + * cp_address the address of the string pointer. + * + * Return Value: + * True if the pointer was changed. + * + */ +static Boolean +pskip_comment(wchar_t **cp_address) +{ + wchar_t *cp = *cp_address; + + /* Is this the beginning of a comment? Skip till end of line. */ + if (*cp == '#') { + SKIPTOEND(cp); + } + /* Have we skipped a comment line? */ + if (cp != *cp_address) { + *cp_address = cp; + return(true); + } else { + return(false); + } +} + +static int +get_max(wchar_t **ms_address, wchar_t *hostname) +{ + wchar_t *ms = *ms_address; + int limit = PMAKE_DEF_MAX_JOBS; /* Default setting. */ + + WCSTOMBS(mbs_buffer, hostname); + /* Look for `='. */ + SKIPSPACE(ms); + if ((!*ms) || (*ms == '\n') || (*ms != '=')) { + SKIPTOEND(ms); + warning(catgets(catd, 1, 319, "expected `=' after max, ignoring rest of line for host %s"), + mbs_buffer); + *ms_address = ms; + return((int) limit); + } else { + ms++; + SKIPSPACE(ms); + if ((*ms != '\n') && (*ms != '\0')) { + /* We've found, hopefully, a valid "max" value. */ + limit = (int) wcstol(ms, &ms, 10); + if (limit < 1) { + limit = PMAKE_DEF_MAX_JOBS; + warning(catgets(catd, 1, 320, "max value cannot be less than or equal to zero for host %s"), mbs_buffer); + } + } else { + /* No "max" value after "max=". */ + warning(catgets(catd, 1, 321, "no max value specified for host %s"), mbs_buffer); + } + *ms_address = ms; + return(limit); + } +} + +#endif + diff --git a/usr/src/make_src/Make/bin/make/common/read.cc b/usr/src/make_src/Make/bin/make/common/read.cc new file mode 100644 index 0000000..586d609 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/read.cc @@ -0,0 +1,2197 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)read.cc 1.64 06/12/12 + */ + +#pragma ident "@(#)read.cc 1.64 06/12/12" + +/* + * read.c + * + * This file contains the makefile reader. + */ + +/* + * Included files + */ +#include <avo/avo_alloca.h> /* alloca() */ +#include <errno.h> /* errno */ +#include <fcntl.h> /* fcntl() */ +#include <mk/defs.h> +#include <mksh/macro.h> /* expand_value(), expand_macro() */ +#include <mksh/misc.h> /* getmem() */ +#include <mksh/read.h> /* get_next_block_fn() */ +#include <sys/uio.h> /* read() */ +#include <unistd.h> /* read(), unlink() */ + +#if defined(HP_UX) || defined(linux) +#include <avo/types.h> +extern "C" Avo_err *avo_find_run_dir(char **dirp); +#endif + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +static int line_started_with_space=0; // Used to diagnose spaces instead of tabs + +/* + * File table of contents + */ +static void parse_makefile(register Name true_makefile_name, register Source source); +static Source push_macro_value(register Source bp, register wchar_t *buffer, int size, register Source source); +extern void enter_target_groups_and_dependencies(Name_vector target, Name_vector depes, Cmd_line command, Separator separator, Boolean target_group_seen); +extern Name normalize_name(register wchar_t *name_string, register int length); + +/* + * read_simple_file(makefile_name, chase_path, doname_it, + * complain, must_exist, report_file, lock_makefile) + * + * Make the makefile and setup to read it. Actually read it if it is stdio + * + * Return value: + * false if the read failed + * + * Parameters: + * makefile_name Name of the file to read + * chase_path Use the makefile path when opening file + * doname_it Call doname() to build the file first + * complain Print message if doname/open fails + * must_exist Generate fatal if file is missing + * report_file Report file when running -P + * lock_makefile Lock the makefile when reading + * + * Static variables used: + * + * Global variables used: + * do_not_exec_rule Is -n on? + * file_being_read Set to the name of the new file + * line_number The number of the current makefile line + * makefiles_used A list of all makefiles used, appended to + */ + + +Boolean +read_simple_file(register Name makefile_name, register Boolean chase_path, register Boolean doname_it, Boolean complain, Boolean must_exist, Boolean report_file, Boolean lock_makefile) +{ + static short max_include_depth; + register Property makefile = maybe_append_prop(makefile_name, + makefile_prop); + Boolean forget_after_parse = false; + static pathpt makefile_path; + register int n; + char *path; + register Source source = ALLOC(Source); + Property orig_makefile = makefile; + Dependency *dpp; + Dependency dp; + register int length; + wchar_t *previous_file_being_read = file_being_read; + int previous_line_number = line_number; + wchar_t previous_current_makefile[MAXPATHLEN]; + Makefile_type save_makefile_type; + Name normalized_makefile_name; + register wchar_t *string_start; + register wchar_t *string_end; + + +#if defined(HP_UX) || defined(linux) + Avo_err *findrundir_err; + char *run_dir, makerules_dir[BUFSIZ]; +#endif + + wchar_t * wcb = get_wstring(makefile_name->string_mb); + +#ifdef NSE + if (report_file){ + wscpy(previous_current_makefile, current_makefile); + wscpy(current_makefile, wcb); + } +#endif + if (max_include_depth++ >= 40) { + fatal(catgets(catd, 1, 66, "Too many nested include statements")); + } + if (makefile->body.makefile.contents != NULL) { + retmem(makefile->body.makefile.contents); + } + source->inp_buf = + source->inp_buf_ptr = + source->inp_buf_end = NULL; + source->error_converting = false; + makefile->body.makefile.contents = NULL; + makefile->body.makefile.size = 0; + if ((makefile_name->hash.length != 1) || + (wcb[0] != (int) hyphen_char)) { + if ((makefile->body.makefile.contents == NULL) && + (doname_it)) { + if (makefile_path == NULL) { + add_dir_to_path(".", + &makefile_path, + -1); +#ifdef SUN5_0 + add_dir_to_path(NOCATGETS("/usr/share/lib/make"), + &makefile_path, + -1); + add_dir_to_path(NOCATGETS("/etc/default"), + &makefile_path, + -1); +#elif defined(HP_UX) + findrundir_err = avo_find_run_dir(&run_dir); + if (! findrundir_err) { + (void) sprintf(makerules_dir, NOCATGETS("%s/../share/lib/make"), run_dir); + add_dir_to_path(makerules_dir, + &makefile_path, + -1); + } + + add_dir_to_path(NOCATGETS("/opt/SUNWspro/share/lib/make"), + &makefile_path, + -1); + add_dir_to_path(NOCATGETS("/usr/share/lib/make"), + &makefile_path, + -1); +#elif defined(linux) + findrundir_err = avo_find_run_dir(&run_dir); + if (! findrundir_err) { + (void) sprintf(makerules_dir, NOCATGETS("%s/../lib"), run_dir); + add_dir_to_path(makerules_dir, + &makefile_path, + -1); + } + + add_dir_to_path(NOCATGETS("/usr/SUNWspro/lib"), + &makefile_path, + -1); + add_dir_to_path(NOCATGETS("/opt/SUNWspro/share/lib/make"), + &makefile_path, + -1); + add_dir_to_path(NOCATGETS("/usr/share/lib/make"), + &makefile_path, + -1); +#else + add_dir_to_path(NOCATGETS("/usr/include/make"), + &makefile_path, + -1); +#endif + } + save_makefile_type = makefile_type; + makefile_type = reading_nothing; + if (doname(makefile_name, true, false) == build_dont_know) { + /* Try normalized filename */ + string_start=get_wstring(makefile_name->string_mb); + for (string_end=string_start+1; *string_end != NULL; string_end++); + normalized_makefile_name=normalize_name(string_start, string_end - string_start); + if ((strcmp(makefile_name->string_mb, normalized_makefile_name->string_mb) == 0) || + (doname(normalized_makefile_name, true, false) == build_dont_know)) { + n = access_vroot(makefile_name->string_mb, + 4, + chase_path ? + makefile_path : NULL, + VROOT_DEFAULT); + if (n == 0) { + get_vroot_path((char **) NULL, + &path, + (char **) NULL); + if ((path[0] == (int) period_char) && + (path[1] == (int) slash_char)) { + path += 2; + } + MBSTOWCS(wcs_buffer, path); + makefile_name = GETNAME(wcs_buffer, + FIND_LENGTH); + } + } + retmem(string_start); + /* + * Commented out: retmem_mb(normalized_makefile_name->string_mb); + * We have to return this memory, but it seems to trigger a bug + * in dmake or in Sun C++ 5.7 compiler (it works ok if this code + * is compiled using Sun C++ 5.6). + */ + // retmem_mb(normalized_makefile_name->string_mb); + } + makefile_type = save_makefile_type; + } + source->string.free_after_use = false; + source->previous = NULL; + source->already_expanded = false; + /* Lock the file for read, but not when -n. */ + if (lock_makefile && + !do_not_exec_rule) { + + make_state_lockfile = getmem(strlen(make_state->string_mb) + strlen(NOCATGETS(".lock")) + 1); + (void) sprintf(make_state_lockfile, + NOCATGETS("%s.lock"), + make_state->string_mb); + (void) file_lock(make_state->string_mb, + make_state_lockfile, + (int *) &make_state_locked, + 0); + if(!make_state_locked) { + printf(NOCATGETS("-- NO LOCKING for read\n")); + retmem_mb(make_state_lockfile); + make_state_lockfile = 0; + return failed; + } + } + if (makefile->body.makefile.contents == NULL) { + save_makefile_type = makefile_type; + makefile_type = reading_nothing; + if ((doname_it) && + (doname(makefile_name, true, false) == build_failed)) { + if (complain) { + (void) fprintf(stderr, +#ifdef DISTRIBUTED + catgets(catd, 1, 67, "dmake: Couldn't dmake `%s'\n"), +#else + catgets(catd, 1, 237, "make: Couldn't make `%s'\n"), +#endif + makefile_name->string_mb); + } + max_include_depth--; + makefile_type = save_makefile_type; + return failed; + } + makefile_type = save_makefile_type; + // + // Before calling exists() make sure that we have the right timestamp + // + makefile_name->stat.time = file_no_time; + + if (exists(makefile_name) == file_doesnt_exist) { + if (complain || + (makefile_name->stat.stat_errno != ENOENT)) { + if (must_exist) { + fatal(catgets(catd, 1, 68, "Can't find `%s': %s"), + makefile_name->string_mb, + errmsg(makefile_name-> + stat.stat_errno)); + } else { + warning(catgets(catd, 1, 69, "Can't find `%s': %s"), + makefile_name->string_mb, + errmsg(makefile_name-> + stat.stat_errno)); + } + } + max_include_depth--; + if(make_state_locked && (make_state_lockfile != NULL)) { + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } + retmem(wcb); + retmem_mb((char *)source); + return failed; + } + /* + * These values are the size and bytes of + * the MULTI-BYTE makefile. + */ + orig_makefile->body.makefile.size = + makefile->body.makefile.size = + source->bytes_left_in_file = + makefile_name->stat.size; + if (report_file) { + for (dpp = &makefiles_used; + *dpp != NULL; + dpp = &(*dpp)->next); + dp = ALLOC(Dependency); + dp->next = NULL; + dp->name = makefile_name; + dp->automatic = false; + dp->stale = false; + dp->built = false; + *dpp = dp; + } + source->fd = open_vroot(makefile_name->string_mb, + O_RDONLY, + 0, + NULL, + VROOT_DEFAULT); + if (source->fd < 0) { + if (complain || (errno != ENOENT)) { + if (must_exist) { + fatal(catgets(catd, 1, 70, "Can't open `%s': %s"), + makefile_name->string_mb, + errmsg(errno)); + } else { + warning(catgets(catd, 1, 71, "Can't open `%s': %s"), + makefile_name->string_mb, + errmsg(errno)); + } + } + max_include_depth--; + return failed; + } + (void) fcntl(source->fd, F_SETFD, 1); + orig_makefile->body.makefile.contents = + makefile->body.makefile.contents = + source->string.text.p = + source->string.buffer.start = + ALLOC_WC((int) (makefile_name->stat.size + 2)); + if (makefile_type == reading_cpp_file) { + forget_after_parse = true; + } + source->string.text.end = source->string.text.p; + source->string.buffer.end = + source->string.text.p + makefile_name->stat.size; + } else { + /* Do we ever reach here? */ + source->fd = -1; + source->string.text.p = + source->string.buffer.start = + makefile->body.makefile.contents; + source->string.text.end = + source->string.buffer.end = + source->string.text.p + makefile->body.makefile.size; + source->bytes_left_in_file = + makefile->body.makefile.size; + } + file_being_read = wcb; + } else { + char *stdin_text_p; + char *stdin_text_end; + char *stdin_buffer_start; + char *stdin_buffer_end; + char *p_mb; + int num_mb_chars; + size_t num_wc_chars; + + MBSTOWCS(wcs_buffer, NOCATGETS("Standard in")); + makefile_name = GETNAME(wcs_buffer, FIND_LENGTH); + /* + * Memory to read standard in, then convert it + * to wide char strings. + */ + stdin_buffer_start = + stdin_text_p = getmem(length = 1024); + stdin_buffer_end = stdin_text_p + length; + MBSTOWCS(wcs_buffer, NOCATGETS("standard input")); + file_being_read = (wchar_t *) wsdup(wcs_buffer); + line_number = 0; + while ((n = read(fileno(stdin), + stdin_text_p, + length)) > 0) { + length -= n; + stdin_text_p += n; + if (length == 0) { + p_mb = getmem(length = 1024 + + (stdin_buffer_end - + stdin_buffer_start)); + (void) strncpy(p_mb, + stdin_buffer_start, + (stdin_buffer_end - + stdin_buffer_start)); + retmem_mb(stdin_buffer_start); + stdin_text_p = p_mb + + (stdin_buffer_end - stdin_buffer_start); + stdin_buffer_start = p_mb; + stdin_buffer_end = + stdin_buffer_start + length; + length = 1024; + } + } + if (n < 0) { + fatal(catgets(catd, 1, 72, "Error reading standard input: %s"), + errmsg(errno)); + } + stdin_text_p = stdin_buffer_start; + stdin_text_end = stdin_buffer_end - length; + num_mb_chars = stdin_text_end - stdin_text_p; + + /* + * Now, convert the sequence of multibyte chars into + * a sequence of corresponding wide character codes. + */ + source->string.free_after_use = false; + source->previous = NULL; + source->bytes_left_in_file = 0; + source->fd = -1; + source->already_expanded = false; + source->string.buffer.start = + source->string.text.p = ALLOC_WC(num_mb_chars + 1); + source->string.buffer.end = + source->string.text.p + num_mb_chars; + num_wc_chars = mbstowcs(source->string.text.p, + stdin_text_p, + num_mb_chars); + if ((int) num_wc_chars >= 0) { + source->string.text.end = + source->string.text.p + num_wc_chars; + } + (void) retmem_mb(stdin_text_p); + } + line_number = 1; + if (trace_reader) { + (void) printf(catgets(catd, 1, 73, ">>>>>>>>>>>>>>>> Reading makefile %s\n"), + makefile_name->string_mb); + } + parse_makefile(makefile_name, source); + if (trace_reader) { + (void) printf(catgets(catd, 1, 74, ">>>>>>>>>>>>>>>> End of makefile %s\n"), + makefile_name->string_mb); + } +#ifdef NSE + if (report_file && (previous_current_makefile[0] != NULL)) { + wscpy(current_makefile, previous_current_makefile); + } +#endif + if(file_being_read) { + retmem(file_being_read); + } + file_being_read = previous_file_being_read; + line_number = previous_line_number; + makefile_type = reading_nothing; + max_include_depth--; + if (make_state_locked) { + /* Unlock .make.state. */ + unlink(make_state_lockfile); + make_state_locked = false; + retmem_mb(make_state_lockfile); + } + if (forget_after_parse) { + retmem(makefile->body.makefile.contents); + makefile->body.makefile.contents = NULL; + } + retmem_mb((char *)source); + return succeeded; +} + +/* + * parse_makefile(true_makefile_name, source) + * + * Strings are read from Sources. + * When macros are found, their values are represented by a + * Source that is pushed on a stack. At end of string + * (that is returned from GET_CHAR() as 0), the block is popped. + * + * Parameters: + * true_makefile_name The name of makefile we are parsing + * source The source block to read from + * + * Global variables used: + * do_not_exec_rule Is -n on? + * line_number The number of the current makefile line + * makefile_type What kind of makefile are we reading? + * empty_name The Name "" + */ +static void +parse_makefile(register Name true_makefile_name, register Source source) +{ +/* + char mb_buffer[MB_LEN_MAX]; + */ + register wchar_t *source_p; + register wchar_t *source_end; + register wchar_t *string_start; + wchar_t *string_end; + register Boolean macro_seen_in_string; + Boolean append; + String_rec name_string; + wchar_t name_buffer[STRING_BUFFER_LENGTH]; + register int distance; + register int paren_count; + int brace_count; + int char_number; + Cmd_line command; + Cmd_line command_tail; + Name macro_value; + + Name_vector_rec target; + Name_vector_rec depes; + Name_vector_rec extra_name_vector; + Name_vector current_names; + Name_vector extra_names = &extra_name_vector; + Name_vector nvp; + Boolean target_group_seen; + + register Reader_state state; + register Reader_state on_eoln_state; + register Separator separator; + + wchar_t buffer[4 * STRING_BUFFER_LENGTH]; + Source extrap; + + Boolean save_do_not_exec_rule = do_not_exec_rule; + Name makefile_name; + + static Name sh_name; + static Name shell_name; + int i; + + static wchar_t include_space[10]; + static wchar_t include_tab[10]; + int tmp_bytes_left_in_string; + Boolean tmp_maybe_include = false; + int emptycount = 0; + Boolean first_target; + + String_rec include_name; + wchar_t include_buffer[STRING_BUFFER_LENGTH]; + + target.next = depes.next = NULL; + /* Move some values from their struct to register declared locals */ + CACHE_SOURCE(0); + + start_new_line: + /* + * Read whitespace on old line. Leave pointer on first char on + * next line. + */ + first_target = true; + on_eoln_state = exit_state; +/* + for (WCTOMB(mb_buffer, GET_CHAR()); + 1; + source_p++, WCTOMB(mb_buffer, GET_CHAR())) + switch (mb_buffer[0]) { + */ + for (char_number=0; 1; source_p++,char_number++) switch (GET_CHAR()) { + case nul_char: + /* End of this string. Pop it and return to the previous one */ + GET_NEXT_BLOCK(source); + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case newline_char: + end_of_line: + source_p++; + if (source->fd >= 0) { + line_number++; + } + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + /* Go back to the top of this loop */ + goto start_new_line; + case newline_char: + case numbersign_char: + case dollar_char: + case space_char: + case tab_char: + /* + * Go back to the top of this loop since the + * new line does not start with a regular char. + */ + goto start_new_line; + default: + /* We found the first proper char on the new line */ + goto start_new_line_no_skip; + } + case space_char: + if (char_number == 0) + line_started_with_space=line_number; + case tab_char: + /* Whitespace. Just keep going in this loop */ + break; + case numbersign_char: + /* Comment. Skip over it */ + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + } + source_p--; + break; + case backslash_char: + /* Comments can be continued */ + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + source_p--; + break; + } + } + if(*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + } + break; + case newline_char: + /* + * After we skip the comment we go to + * the end of line handler since end of + * line terminates comments. + */ + goto end_of_line; + } + } + case dollar_char: + /* Macro reference */ + if (source->already_expanded) { + /* + * If we are reading from the expansion of a + * macro we already expanded everything enough. + */ + goto start_new_line_no_skip; + } + /* + * Expand the value and push the Source on the stack of + * things being read. + */ + source_p++; + UNCACHE_SOURCE(); + { + Source t = (Source) alloca((int) sizeof (Source_rec)); + source = push_macro_value(t, + buffer, + sizeof buffer, + source); + } + CACHE_SOURCE(1); + break; + default: + /* We found the first proper char on the new line */ + goto start_new_line_no_skip; + } + + /* + * We found the first normal char (one that starts an identifier) + * on the newline. + */ +start_new_line_no_skip: + /* Inspect that first char to see if it maybe is special anyway */ + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + goto start_new_line_no_skip; + case newline_char: + /* Just in case */ + goto start_new_line; + case exclam_char: + /* Evaluate the line before it is read */ + string_start = source_p + 1; + macro_seen_in_string = false; + /* Stuff the line in a string so we can eval it. */ + for (; 1; source_p++) { + switch (GET_CHAR()) { + case newline_char: + goto eoln_1; + case nul_char: + if (source->fd > 0) { + if (!macro_seen_in_string) { + macro_seen_in_string = true; + INIT_STRING_FROM_STACK( + name_string, name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + break; + } + eoln_1: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + extrap = (Source) + alloca((int) sizeof (Source_rec)); + extrap->string.buffer.start = NULL; + extrap->inp_buf = + extrap->inp_buf_ptr = + extrap->inp_buf_end = NULL; + extrap->error_converting = false; + if (*source_p == (int) nul_char) { + source_p++; + } + /* Eval the macro */ + expand_value(GETNAME(name_string.buffer.start, + FIND_LENGTH), + &extrap->string, + false); + if (name_string.free_after_use) { + retmem(name_string.buffer.start); + } + UNCACHE_SOURCE(); + extrap->string.text.p = + extrap->string.buffer.start; + extrap->fd = -1; + /* And push the value */ + extrap->previous = source; + source = extrap; + CACHE_SOURCE(0); + goto line_evald; + } + } + default: + goto line_evald; + } + + /* We now have a line we can start reading */ + line_evald: + if (source == NULL) { + GOTO_STATE(exit_state); + } + /* Check if this is an include command */ + if ((makefile_type == reading_makefile) && + !source->already_expanded) { + if (include_space[0] == (int) nul_char) { + MBSTOWCS(include_space, NOCATGETS("include ")); + MBSTOWCS(include_tab, NOCATGETS("include\t")); + } + if ((IS_WEQUALN(source_p, include_space, 8)) || + (IS_WEQUALN(source_p, include_tab, 8))) { + source_p += 7; + if (iswspace(*source_p)) { + Makefile_type save_makefile_type; + wchar_t *name_start; + int name_length; + + /* + * Yes, this is an include. + * Skip spaces to get to the filename. + */ + while (iswspace(*source_p) || + (*source_p == (int) nul_char)) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + + default: + source_p++; + break; + } + } + + string_start = source_p; + /* Find the end of the filename */ + macro_seen_in_string = false; + while (!iswspace(*source_p) || + (*source_p == (int) nul_char)) { + switch (GET_CHAR()) { + case nul_char: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + GET_NEXT_BLOCK(source); + string_start = source_p; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + + default: + source_p++; + break; + } + } + + source->string.text.p = source_p; + if (macro_seen_in_string) { + append_string(string_start, + &name_string, + source_p - string_start); + name_start = name_string.buffer.start; + name_length = name_string.text.p - name_start; + } else { + name_start = string_start; + name_length = source_p - string_start; + } + + /* Strip "./" from the head of the name */ + if ((name_start[0] == (int) period_char) && + (name_start[1] == (int) slash_char)) { + name_start += 2; + name_length -= 2; + } + /* if include file name is surrounded by double quotes */ + if ((name_start[0] == (int) doublequote_char) && + (name_start[name_length - 1] == (int) doublequote_char)) { + name_start += 1; + name_length -= 2; + + /* if name does not begin with a slash char */ + if (name_start[0] != (int) slash_char) { + if ((name_start[0] == (int) period_char) && + (name_start[1] == (int) slash_char)) { + name_start += 2; + name_length -= 2; + } + + INIT_STRING_FROM_STACK(include_name, include_buffer); + APPEND_NAME(true_makefile_name, + &include_name, + true_makefile_name->hash.length); + + wchar_t *slash = wsrchr(include_name.buffer.start, (int) slash_char); + if (slash != NULL) { + include_name.text.p = slash + 1; + append_string(name_start, + &include_name, + name_length); + + name_start = include_name.buffer.start; + name_length = include_name.text.p - name_start; + } + } + } + + /* Even when we run -n we want to create makefiles */ + do_not_exec_rule = false; + makefile_name = GETNAME(name_start, name_length); + if (makefile_name->dollar) { + String_rec destination; + wchar_t buffer[STRING_BUFFER_LENGTH]; + wchar_t *p; + wchar_t *q; + + INIT_STRING_FROM_STACK(destination, buffer); + expand_value(makefile_name, + &destination, + false); + for (p = destination.buffer.start; + (*p != (int) nul_char) && iswspace(*p); + p++); + for (q = p; + (*q != (int) nul_char) && !iswspace(*q); + q++); + makefile_name = GETNAME(p, q-p); + if (destination.free_after_use) { + retmem(destination.buffer.start); + } + } + source_p++; + UNCACHE_SOURCE(); + /* Read the file */ + save_makefile_type = makefile_type; + if (read_simple_file(makefile_name, + true, + true, + true, + false, + true, + false) == failed) { + fatal_reader(catgets(catd, 1, 75, "Read of include file `%s' failed"), + makefile_name->string_mb); + } + makefile_type = save_makefile_type; + do_not_exec_rule = save_do_not_exec_rule; + CACHE_SOURCE(0); + goto start_new_line; + } else { + source_p -= 7; + } + } else { + /* Check if the word include was split across 8K boundary. */ + + tmp_bytes_left_in_string = source->string.text.end - source_p; + if (tmp_bytes_left_in_string < 8) { + tmp_maybe_include = false; + if (IS_WEQUALN(source_p, + include_space, + tmp_bytes_left_in_string)) { + tmp_maybe_include = true; + } + if (tmp_maybe_include) { + GET_NEXT_BLOCK(source); + tmp_maybe_include = false; + goto line_evald; + } + } + } + } + + /* Reset the status in preparation for the new line */ + for (nvp = ⌖ nvp != NULL; nvp = nvp->next) { + nvp->used = 0; + } + for (nvp = &depes; nvp != NULL; nvp = nvp->next) { + nvp->used = 0; + } + target_group_seen = false; + command = command_tail = NULL; + macro_value = NULL; + append = false; + current_names = ⌖ + SET_STATE(scan_name_state); + on_eoln_state = illegal_eoln_state; + separator = none_seen; + + /* The state machine starts here */ + enter_state: + while (1) switch (state) { + +/**************************************************************** + * Scan name state + */ +case scan_name_state: + /* Scan an identifier. We skip over chars until we find a break char */ + /* First skip white space. */ + for (; 1; source_p++) switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case newline_char: + /* We found the end of the line. */ + /* Do postprocessing or return error */ + source_p++; + if (source->fd >= 0) { + line_number++; + } + GOTO_STATE(on_eoln_state); + case backslash_char: + /* Continuation */ + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + if (*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + } else { + source_p--; + } + break; + case tab_char: + case space_char: + /* Whitespace is skipped */ + break; + case numbersign_char: + /* Comment. Skip over it */ + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + } + source_p--; + break; + case backslash_char: + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + source_p--; + break; + } + } + if(*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + } + break; + case newline_char: + source_p++; + if (source->fd >= 0) { + line_number++; + } + GOTO_STATE(on_eoln_state); + } + } + case dollar_char: + /* Macro reference. Expand and push value */ + if (source->already_expanded) { + goto scan_name; + } + source_p++; + UNCACHE_SOURCE(); + { + Source t = (Source) alloca((int) sizeof (Source_rec)); + source = push_macro_value(t, + buffer, + sizeof buffer, + source); + } + CACHE_SOURCE(1); + break; + default: + /* End of white space */ + goto scan_name; + } + + /* First proper identifier character */ + scan_name: + + string_start = source_p; + paren_count = brace_count = 0; + macro_seen_in_string = false; + resume_name_scan: + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + /* Save what we have seen so far of the identifier */ + if (source_p != string_start) { + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + } + /* Get more text to read */ + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case newline_char: + if (paren_count > 0) { + fatal_reader(catgets(catd, 1, 76, "Unmatched `(' on line")); + } + if (brace_count > 0) { + fatal_reader(catgets(catd, 1, 77, "Unmatched `{' on line")); + } + source_p++; + /* Enter name */ + current_names = enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p - 1, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) sizeof (Name_vector_rec)); + } + /* Do postprocessing or return error */ + if (source->fd >= 0) { + line_number++; + } + GOTO_STATE(on_eoln_state); + case backslash_char: + /* Check if this is a quoting backslash */ + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + macro_seen_in_string = true; + } + append_string(string_start, + &name_string, + source_p - string_start); + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + if (*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + *source_p = (int) space_char; + string_start = source_p; + goto resume_name_scan; + } else { + string_start = source_p; + break; + } + break; + case numbersign_char: + if (paren_count + brace_count > 0) { + break; + } + fatal_reader(catgets(catd, 1, 78, "Unexpected comment seen")); + case dollar_char: + if (source->already_expanded) { + break; + } + /* Save the identifier so far */ + if (source_p != string_start) { + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + } + /* Eval and push the macro */ + source_p++; + UNCACHE_SOURCE(); + { + Source t = + (Source) alloca((int) sizeof (Source_rec)); + source = push_macro_value(t, + buffer, + sizeof buffer, + source); + } + CACHE_SOURCE(1); + string_start = source_p + 1; + break; + case parenleft_char: + paren_count++; + break; + case parenright_char: + if (--paren_count < 0) { + fatal_reader(catgets(catd, 1, 79, "Unmatched `)' on line")); + } + break; + case braceleft_char: + brace_count++; + break; + case braceright_char: + if (--brace_count < 0) { + fatal_reader(catgets(catd, 1, 80, "Unmatched `}' on line")); + } + break; + case ampersand_char: + case greater_char: + case bar_char: + if (paren_count + brace_count == 0) { + source_p++; + } + /* Fall into */ + case tab_char: + case space_char: + if (paren_count + brace_count > 0) { + break; + } + current_names = enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) sizeof (Name_vector_rec)); + } + goto enter_state; + case colon_char: + if (paren_count + brace_count > 0) { + break; + } + if (separator == conditional_seen) { + break; + } +/** POSIX **/ +#if 0 + if(posix) { + emptycount = 0; + } +#endif +/** END POSIX **/ + /* End of the target list. We now start reading */ + /* dependencies or a conditional assignment */ + if (separator != none_seen) { + fatal_reader(catgets(catd, 1, 81, "Extra `:', `::', or `:=' on dependency line")); + } + /* Enter the last target */ + if ((string_start != source_p) || + macro_seen_in_string) { + current_names = + enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) + sizeof (Name_vector_rec)); + } + } + /* Check if it is ":" "::" or ":=" */ + scan_colon_label: + switch (*++source_p) { + case nul_char: + GET_NEXT_BLOCK(source); + source_p--; + if (source == NULL) { + GOTO_STATE(enter_dependencies_state); + } + goto scan_colon_label; + case equal_char: + if(svr4) { + fatal_reader(catgets(catd, 1, 82, "syntax error")); + } + separator = conditional_seen; + source_p++; + current_names = &depes; + GOTO_STATE(scan_name_state); + case colon_char: + separator = two_colon; + source_p++; + break; + default: + separator = one_colon; + } + current_names = &depes; + on_eoln_state = enter_dependencies_state; + GOTO_STATE(scan_name_state); + case semicolon_char: + if (paren_count + brace_count > 0) { + break; + } + /* End of reading names. Start reading the rule */ + if ((separator != one_colon) && + (separator != two_colon)) { + fatal_reader(catgets(catd, 1, 83, "Unexpected command seen")); + } + /* Enter the last dependency */ + if ((string_start != source_p) || + macro_seen_in_string) { + current_names = + enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) + sizeof (Name_vector_rec)); + } + } + source_p++; + /* Make sure to enter a rule even if the is */ + /* no text here */ + command = command_tail = ALLOC(Cmd_line); + command->next = NULL; + command->command_line = empty_name; + command->make_refd = false; + command->ignore_command_dependency = false; + command->assign = false; + command->ignore_error = false; + command->silent = false; + + GOTO_STATE(scan_command_state); + case plus_char: + /* + ** following code drops the target separator plus char if it starts + ** a line. + */ + if(first_target && !macro_seen_in_string && + source_p == string_start) { + for (; 1; source_p++) + switch (GET_CHAR()) { + case nul_char: + if (source_p != string_start) { + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + } + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case plus_char: + source_p++; + while (*source_p == (int) nul_char) { + if (source_p != string_start) { + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + } + GET_NEXT_BLOCK(source); + string_start = source_p; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + if (*source_p == (int) tab_char || + *source_p == (int) space_char) { + macro_seen_in_string = false; + string_start = source_p + 1; + } else { + goto resume_name_scan; + } + break; + case tab_char: + case space_char: + string_start = source_p + 1; + break; + default: + goto resume_name_scan; + } + } + if (paren_count + brace_count > 0) { + break; + } + /* We found "+=" construct */ + if (source_p != string_start) { + /* "+" is not a break char. */ + /* Ignore it if it is part of an identifier */ + source_p++; + goto resume_name_scan; + } + /* Make sure the "+" is followed by a "=" */ + scan_append: + switch (*++source_p) { + case nul_char: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + GET_NEXT_BLOCK(source); + source_p--; + string_start = source_p; + if (source == NULL) { + GOTO_STATE(illegal_eoln_state); + } + goto scan_append; + case equal_char: + if(!svr4) { + append = true; + } else { + fatal_reader(catgets(catd, 1, 84, "Must be a separator on rules")); + } + break; + default: + /* The "+" just starts a regular name. */ + /* Start reading that name */ + goto resume_name_scan; + } + /* Fall into */ + case equal_char: + if (paren_count + brace_count > 0) { + break; + } + /* We found macro assignment. */ + /* Check if it is legal and if it is appending */ + switch (separator) { + case none_seen: + separator = equal_seen; + on_eoln_state = enter_equal_state; + break; + case conditional_seen: + on_eoln_state = enter_conditional_state; + break; + default: + /* Reader must special check for "MACRO:sh=" */ + /* notation */ + if (sh_name == NULL) { + MBSTOWCS(wcs_buffer, NOCATGETS("sh")); + sh_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, NOCATGETS("shell")); + shell_name = GETNAME(wcs_buffer, FIND_LENGTH); + } + + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start + ); + + if ( (((target.used == 1) && + (depes.used == 1) && + (depes.names[0] == sh_name)) || + ((target.used == 1) && + (depes.used == 0) && + (separator == one_colon) && + (GETNAME(name_string.buffer.start,FIND_LENGTH) == sh_name))) && + (!svr4)) { + String_rec macro_name; + wchar_t buffer[100]; + + INIT_STRING_FROM_STACK(macro_name, + buffer); + APPEND_NAME(target.names[0], + ¯o_name, + FIND_LENGTH); + append_char((int) colon_char, + ¯o_name); + APPEND_NAME(sh_name, + ¯o_name, + FIND_LENGTH); + target.names[0] = + GETNAME(macro_name.buffer.start, + FIND_LENGTH); + separator = equal_seen; + on_eoln_state = enter_equal_state; + break; + } else if ( (((target.used == 1) && + (depes.used == 1) && + (depes.names[0] == shell_name)) || + ((target.used == 1) && + (depes.used == 0) && + (separator == one_colon) && + (GETNAME(name_string.buffer.start,FIND_LENGTH) == shell_name))) && + (!svr4)) { + String_rec macro_name; + wchar_t buffer[100]; + + INIT_STRING_FROM_STACK(macro_name, + buffer); + APPEND_NAME(target.names[0], + ¯o_name, + FIND_LENGTH); + append_char((int) colon_char, + ¯o_name); + APPEND_NAME(shell_name, + ¯o_name, + FIND_LENGTH); + target.names[0] = + GETNAME(macro_name.buffer.start, + FIND_LENGTH); + separator = equal_seen; + on_eoln_state = enter_equal_state; + break; + } + if(svr4) { + fatal_reader(catgets(catd, 1, 85, "syntax error")); + } + else { + fatal_reader(catgets(catd, 1, 86, "Macro assignment on dependency line")); + } + } + if (append) { + source_p--; + } + /* Enter the macro name */ + if ((string_start != source_p) || + macro_seen_in_string) { + current_names = + enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) + sizeof (Name_vector_rec)); + } + } + if (append) { + source_p++; + } + macro_value = NULL; + source_p++; + distance = 0; + /* Skip whitespace to the start of the value */ + macro_seen_in_string = false; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case backslash_char: + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + if (*source_p != (int) newline_char) { + if (!macro_seen_in_string) { + macro_seen_in_string = + true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_char((int) + backslash_char, + &name_string); + append_char(*source_p, + &name_string); + string_start = source_p+1; + goto macro_value_start; + } else { + if (source->fd >= 0) { + line_number++; + } + } + break; + case newline_char: + case numbersign_char: + string_start = source_p; + goto macro_value_end; + case tab_char: + case space_char: + break; + default: + string_start = source_p; + goto macro_value_start; + } + } + macro_value_start: + /* Find the end of the value */ + for (; 1; source_p++) { + if (distance != 0) { + *source_p = *(source_p + distance); + } + switch (GET_CHAR()) { + case nul_char: + if (!macro_seen_in_string) { + macro_seen_in_string = true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case backslash_char: + source_p++; + if (distance != 0) { + *source_p = + *(source_p + distance); + } + if (*source_p == (int) nul_char) { + if (!macro_seen_in_string) { + macro_seen_in_string = + true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + +/* BID_1225561 */ + *(source_p - 1) = (int) space_char; + append_string(string_start, + &name_string, + source_p - + string_start - 1); + GET_NEXT_BLOCK(source); + string_start = source_p; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (distance != 0) { + *source_p = + *(source_p + + distance); + } + if (*source_p == (int) newline_char) { + append_char((int) space_char, &name_string); + } else { + append_char((int) backslash_char, &name_string); + } +/****************/ + } + if (*source_p == (int) newline_char) { + source_p--; + line_number++; + distance++; + *source_p = (int) space_char; + while ((*(source_p + + distance + 1) == + (int) tab_char) || + (*(source_p + + distance + 1) == + (int) space_char)) { + distance++; + } + } + break; + case newline_char: + case numbersign_char: + goto macro_value_end; + } + } + macro_value_end: + /* Complete the value in the string */ + if (!macro_seen_in_string) { + macro_seen_in_string = true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + if (name_string.buffer.start != name_string.text.p) { + macro_value = + GETNAME(name_string.buffer.start, + FIND_LENGTH); + } + if (name_string.free_after_use) { + retmem(name_string.buffer.start); + } + for (; distance > 0; distance--) { + *source_p++ = (int) space_char; + } + GOTO_STATE(on_eoln_state); + } + } + +/**************************************************************** + * enter dependencies state + */ + case enter_dependencies_state: + enter_dependencies_label: +/* Expects pointer on first non whitespace char after last dependency. (On */ +/* next line.) We end up here after having read a "targets : dependencies" */ +/* line. The state checks if there is a rule to read and if so dispatches */ +/* to scan_command_state scan_command_state reads one rule line and the */ +/* returns here */ + + /* First check if the first char on the next line is special */ + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + if (source == NULL) { + break; + } + goto enter_dependencies_label; + case exclam_char: + /* The line should be evaluate before it is read */ + macro_seen_in_string = false; + string_start = source_p + 1; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case newline_char: + goto eoln_2; + case nul_char: + if (source->fd > 0) { + if (!macro_seen_in_string) { + macro_seen_in_string = true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + break; + } + eoln_2: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + extrap = (Source) + alloca((int) sizeof (Source_rec)); + extrap->string.buffer.start = NULL; + extrap->inp_buf = + extrap->inp_buf_ptr = + extrap->inp_buf_end = NULL; + extrap->error_converting = false; + expand_value(GETNAME(name_string.buffer.start, + FIND_LENGTH), + &extrap->string, + false); + if (name_string.free_after_use) { + retmem(name_string.buffer.start); + } + UNCACHE_SOURCE(); + extrap->string.text.p = + extrap->string.buffer.start; + extrap->fd = -1; + extrap->previous = source; + source = extrap; + CACHE_SOURCE(0); + goto enter_dependencies_label; + } + } + case dollar_char: + if (source->already_expanded) { + break; + } + source_p++; + UNCACHE_SOURCE(); + { + Source t = (Source) alloca((int) sizeof (Source_rec)); + source = push_macro_value(t, + buffer, + sizeof buffer, + source); + } + CACHE_SOURCE(0); + goto enter_dependencies_label; + case numbersign_char: + if (makefile_type != reading_makefile) { + source_p++; + GOTO_STATE(scan_command_state); + } + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + } + source_p--; + break; + case backslash_char: + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + source_p--; + break; + } + } + if(*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + } + break; + case newline_char: + source_p++; + if (source->fd >= 0) { + line_number++; + } + goto enter_dependencies_label; + } + } + + case tab_char: + GOTO_STATE(scan_command_state); + } + + /* We read all the command lines for the target/dependency line. */ + /* Enter the stuff */ + enter_target_groups_and_dependencies( &target, &depes, command, + separator, target_group_seen); + + goto start_new_line; + +/**************************************************************** + * scan command state + */ +case scan_command_state: + /* We need to read one rule line. Do that and return to */ + /* the enter dependencies state */ + string_start = source_p; + macro_seen_in_string = false; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case backslash_char: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + string_start = source_p; + goto command_newline; + } + } + append_char((int) backslash_char, &name_string); + append_char(*source_p, &name_string); + if (*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + string_start = source_p; + goto command_newline; + } + } + if (*source_p == (int) tab_char) { + source_p++; + } + } else { + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + string_start = source_p; + goto command_newline; + } + } + } + string_start = source_p; + if ((*source_p == (int) newline_char) || + (*source_p == (int) backslash_char) || + (*source_p == (int) nul_char)) { + source_p--; + } + break; + case newline_char: + command_newline: + if ((string_start != source_p) || + macro_seen_in_string) { + if (macro_seen_in_string) { + append_string(string_start, + &name_string, + source_p - string_start); + string_start = + name_string.buffer.start; + string_end = name_string.text.p; + } else { + string_end = source_p; + } + while ((*string_start != (int) newline_char) && + iswspace(*string_start)){ + string_start++; + } + if ((string_end > string_start) || + (makefile_type == reading_statefile)) { + if (command_tail == NULL) { + command = + command_tail = + ALLOC(Cmd_line); + } else { + command_tail->next = + ALLOC(Cmd_line); + command_tail = + command_tail->next; + } + command_tail->next = NULL; + command_tail->make_refd = false; + command_tail->ignore_command_dependency = false; + command_tail->assign = false; + command_tail->ignore_error = false; + command_tail->silent = false; + command_tail->command_line = + GETNAME(string_start, + string_end - string_start); + if (macro_seen_in_string && + name_string.free_after_use) { + retmem(name_string. + buffer.start); + } + } + } + do { + if ((source != NULL) && (source->fd >= 0)) { + line_number++; + } + if ((source != NULL) && + (*++source_p == (int) nul_char)) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + } while (*source_p == (int) newline_char); + + GOTO_STATE(enter_dependencies_state); + case nul_char: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + if (source == NULL) { + GOTO_STATE(enter_dependencies_state); + } + break; + } + } + +/**************************************************************** + * enter equal state + */ +case enter_equal_state: + if (target.used != 1) { + GOTO_STATE(poorly_formed_macro_state); + } + enter_equal(target.names[0], macro_value, append); + goto start_new_line; + +/**************************************************************** + * enter conditional state + */ +case enter_conditional_state: + if (depes.used != 1) { + GOTO_STATE(poorly_formed_macro_state); + } + for (nvp = ⌖ nvp != NULL; nvp = nvp->next) { + for (i = 0; i < nvp->used; i++) { + enter_conditional(nvp->names[i], + depes.names[0], + macro_value, + append); + } + } + goto start_new_line; + +/**************************************************************** + * Error states + */ +case illegal_bytes_state: + fatal_reader(catgets(catd, 1, 340, "Invalid byte sequence")); +case illegal_eoln_state: + if (line_number > 1) { + if (line_started_with_space == (line_number - 1)) { + line_number--; + fatal_reader(catgets(catd, 1, 90, "Unexpected end of line seen\n\t*** missing separator (did you mean TAB instead of 8 spaces?)")); + } + } + fatal_reader(catgets(catd, 1, 87, "Unexpected end of line seen")); +case poorly_formed_macro_state: + fatal_reader(catgets(catd, 1, 88, "Badly formed macro assignment")); +case exit_state: + return; +default: + fatal_reader(catgets(catd, 1, 89, "Internal error. Unknown reader state")); +} +} + +/* + * push_macro_value(bp, buffer, size, source) + * + * Macro and function that evaluates one macro + * and makes the reader read from the value of it + * + * Return value: + * The source block to read the macro from + * + * Parameters: + * bp The new source block to fill in + * buffer Buffer to read from + * size size of the buffer + * source The old source block + * + * Global variables used: + */ +static Source +push_macro_value(register Source bp, register wchar_t *buffer, int size, register Source source) +{ + bp->string.buffer.start = bp->string.text.p = buffer; + bp->string.text.end = NULL; + bp->string.buffer.end = buffer + (size/SIZEOFWCHAR_T); + bp->string.free_after_use = false; + bp->inp_buf = + bp->inp_buf_ptr = + bp->inp_buf_end = NULL; + bp->error_converting = false; + expand_macro(source, &bp->string, (wchar_t *) NULL, false); + bp->string.text.p = bp->string.buffer.start; + + /* 4209588: 'make' doesn't understand a macro with whitespaces in the head as target. + * strip whitespace from the begining of the macro value + */ + while (iswspace(*bp->string.text.p)) { + bp->string.text.p++; + } + + bp->fd = -1; + bp->already_expanded = true; + bp->previous = source; + return bp; +} + +/* + * enter_target_groups_and_dependencies(target, depes, command, separator, + * target_group_seen) + * + * Parameters: + * target Structure that shows the target(s) on the line + * we are currently parsing. This can looks like + * target1 .. targetN : dependencies + * commands + * or + * target1 + .. + targetN : dependencies + * commands + * depes Dependencies + * command Points to the command(s) to be executed for + * this target. + * separator : or :: or := + * target_group_seen Set if we have target1 + .. + targetN + * + * + * After reading the command lines for a target, this routine + * is called to setup the dependencies and the commands for it. + * If the target is a % pattern or part of a target group, then + * the appropriate routines are called. + */ + +void +enter_target_groups_and_dependencies(Name_vector target, Name_vector depes, Cmd_line command, Separator separator, Boolean target_group_seen) +{ + int i; + Boolean reset= true; + Chain target_group_member; + Percent percent_ptr; + + for (; target != NULL; target = target->next) { + for (i = 0; i < target->used; i++) { + if (target->names[i] != NULL) { + if (target_group_seen) { + target_group_member = + find_target_groups(target, i, reset); + if(target_group_member == NULL) { + fatal_reader(catgets(catd, 1, 328, "Unexpected '+' on dependency line")); + } + } + reset = false; + + /* If we saw it in the makefile it must be + * a file */ + target->names[i]->stat.is_file = true; + /* Make sure that we use dependencies + * entered for makefiles */ + target->names[i]->state = build_dont_know; + + /* If the target is special we delegate + * the processing */ + if (target->names[i]->special_reader + != no_special) { + special_reader(target->names[i], + depes, + command); + } + /* Check if this is a "a%b : x%y" type rule */ + else if (target->names[i]->percent) { + percent_ptr = + enter_percent(target->names[i], + target->target_group[i], + depes, command); + if (target_group_seen) { + target_group_member->percent_member = + percent_ptr; + } + } else if (target->names[i]->dollar) { + enter_dyntarget(target->names[i]); + enter_dependencies + (target->names[i], + target->target_group[i], + depes, + command, + separator); + } else { + if (target_group_seen) { + target_group_member->percent_member = + NULL; + } + + enter_dependencies + (target->names[i], + target->target_group[i], + depes, + command, + separator); + } + } + } + } +} + + diff --git a/usr/src/make_src/Make/bin/make/common/read2.cc b/usr/src/make_src/Make/bin/make/common/read2.cc new file mode 100644 index 0000000..e1a9e1f --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/read2.cc @@ -0,0 +1,1938 @@ +/* + * 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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)read2.cc 1.53 06/12/12 + */ + +#pragma ident "@(#)read2.cc 1.53 06/12/12" + +/* + * read.c + * + * This file contains the makefile reader. + */ + +/* + * Included files + */ +#include <mk/defs.h> +#include <mksh/dosys.h> /* sh_command2string() */ +#include <mksh/macro.h> /* expand_value() */ +#include <mksh/misc.h> /* retmem() */ +#include <stdarg.h> /* va_list, va_start(), va_end() */ + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static Boolean built_last_make_run_seen; + +/* + * File table of contents + */ +static Name_vector enter_member_name(register wchar_t *lib_start, register wchar_t *member_start, register wchar_t *string_end, Name_vector current_names, Name_vector *extra_names); +extern Name normalize_name(register wchar_t *name_string, register int length); +static void read_suffixes_list(register Name_vector depes); +static void make_relative(wchar_t *to, wchar_t *result); +static void print_rule(register Cmd_line command); +static void sh_transform(Name *name, Name *value); + + +/* + * enter_name(string, tail_present, string_start, string_end, + * current_names, extra_names, target_group_seen) + * + * Take one string and enter it as a name. The string is passed in + * two parts. A make string and possibly a C string to append to it. + * The result is stuffed in the vector current_names. + * extra_names points to a vector that is used if current_names overflows. + * This is allocad in the calling routine. + * Here we handle the "lib.a[members]" notation. + * + * Return value: + * The name vector that was used + * + * Parameters: + * tail_present Indicates if both C and make string was passed + * string_start C string + * string_end Pointer to char after last in C string + * string make style string with head of name + * current_names Vector to deposit the name in + * extra_names Where to get next name vector if we run out + * target_group_seen Pointer to boolean that is set if "+" is seen + * + * Global variables used: + * makefile_type When we read a report file we normalize paths + * plus Points to the Name "+" + */ + +Name_vector +enter_name(String string, Boolean tail_present, register wchar_t *string_start, register wchar_t *string_end, Name_vector current_names, Name_vector *extra_names, Boolean *target_group_seen) +{ + Name name; + register wchar_t *cp; + wchar_t ch; + + /* If we were passed a separate tail of the name we append it to the */ + /* make string with the rest of it */ + if (tail_present) { + append_string(string_start, string, string_end - string_start); + string_start = string->buffer.start; + string_end = string->text.p; + } + ch = *string_end; + *string_end = (int) nul_char; + /* + * Check if there are any ( or [ that are not prefixed with $. + * If there are, we have to deal with the lib.a(members) format. + */ + for (cp = (wchar_t *) wschr(string_start, (int) parenleft_char); + cp != NULL; + cp = (wchar_t *) wschr(cp + 1, (int) parenleft_char)) { + if (*(cp - 1) != (int) dollar_char) { + *string_end = ch; + return enter_member_name(string_start, + cp, + string_end, + current_names, + extra_names); + } + } + *string_end = ch; + + if (makefile_type == reading_cpp_file) { + /* Remove extra ../ constructs if we are reading from a report file */ + name = normalize_name(string_start, string_end - string_start); + } else { + /* + * /tolik, fix bug 1197477/ + * Normalize every target name before entering. + * ..//obj/a.o and ../obj//a.o are not two different targets. + * There is only one target ../obj/a.o + */ + /*name = GETNAME(string_start, string_end - string_start);*/ + name = normalize_name(string_start, string_end - string_start); + } + + /* Internalize the name. Detect the name "+" (target group here) */ +if(current_names->used != 0 && current_names->names[current_names->used-1] == plus) { + if(name == plus) { + return current_names; + } +} + /* If the current_names vector is full we patch in the one from */ + /* extra_names */ + if (current_names->used == VSIZEOF(current_names->names)) { + if (current_names->next != NULL) { + current_names = current_names->next; + } else { + current_names->next = *extra_names; + *extra_names = NULL; + current_names = current_names->next; + current_names->used = 0; + current_names->next = NULL; + } + } + current_names->target_group[current_names->used] = NULL; + current_names->names[current_names->used++] = name; + if (name == plus) { + *target_group_seen = true; + } + if (tail_present && string->free_after_use) { + retmem(string->buffer.start); + } + return current_names; +} + +/* + * enter_member_name(lib_start, member_start, string_end, + * current_names, extra_names) + * + * A string has been found to contain member names. + * (The "lib.a[members]" and "lib.a(members)" notation) + * Handle it pretty much as enter_name() does for simple names. + * + * Return value: + * The name vector that was used + * + * Parameters: + * lib_start Points to the of start of "lib.a(member.o)" + * member_start Points to "member.o" from above string. + * string_end Points to char after last of above string. + * current_names Vector to deposit the name in + * extra_names Where to get next name vector if we run out + * + * Global variables used: + */ +static Name_vector +enter_member_name(register wchar_t *lib_start, register wchar_t *member_start, register wchar_t *string_end, Name_vector current_names, Name_vector *extra_names) +{ + register Boolean entry = false; + wchar_t buffer[STRING_BUFFER_LENGTH]; + Name lib; + Name member; + Name name; + Property prop; + wchar_t *memberp; + wchar_t *q; + register int paren_count; + register Boolean has_dollar; + register wchar_t *cq; + Name long_member_name = NULL; + + /* Internalize the name of the library */ + lib = GETNAME(lib_start, member_start - lib_start); + lib->is_member = true; + member_start++; + if (*member_start == (int) parenleft_char) { + /* This is really the "lib.a((entries))" format */ + entry = true; + member_start++; + } + /* Move the library name to the buffer where we intend to build the */ + /* "lib.a(member)" for each member */ + (void) wsncpy(buffer, lib_start, member_start - lib_start); + memberp = buffer + (member_start-lib_start); + while (1) { + long_member_name = NULL; + /* Skip leading spaces */ + for (; + (member_start < string_end) && iswspace(*member_start); + member_start++); + /* Find the end of the member name. Allow nested (). Detect $*/ + for (cq = memberp, has_dollar = false, paren_count = 0; + (member_start < string_end) && + ((*member_start != (int) parenright_char) || + (paren_count > 0)) && + !iswspace(*member_start); + *cq++ = *member_start++) { + switch (*member_start) { + case parenleft_char: + paren_count++; + break; + case parenright_char: + paren_count--; + break; + case dollar_char: + has_dollar = true; + } + } + /* Internalize the member name */ + member = GETNAME(memberp, cq - memberp); + *cq = 0; + if ((q = (wchar_t *) wsrchr(memberp, (int) slash_char)) == NULL) { + q = memberp; + } + if ((cq - q > (int) ar_member_name_len) && + !has_dollar) { + *cq++ = (int) parenright_char; + if (entry) { + *cq++ = (int) parenright_char; + } + long_member_name = GETNAME(buffer, cq - buffer); + cq = q + (int) ar_member_name_len; + } + *cq++ = (int) parenright_char; + if (entry) { + *cq++ = (int) parenright_char; + } + /* Internalize the "lib.a(member)" notation for this member */ + name = GETNAME(buffer, cq - buffer); + name->is_member = lib->is_member; + if (long_member_name != NULL) { + prop = append_prop(name, long_member_name_prop); + name->has_long_member_name = true; + prop->body.long_member_name.member_name = + long_member_name; + } + /* And add the member prop */ + prop = append_prop(name, member_prop); + prop->body.member.library = lib; + if (entry) { + /* "lib.a((entry))" notation */ + prop->body.member.entry = member; + prop->body.member.member = NULL; + } else { + /* "lib.a(member)" Notation */ + prop->body.member.entry = NULL; + prop->body.member.member = member; + } + /* Handle overflow of current_names */ + if (current_names->used == VSIZEOF(current_names->names)) { + if (current_names->next != NULL) { + current_names = current_names->next; + } else { + if (*extra_names == NULL) { + current_names = + current_names->next = + ALLOC(Name_vector); + } else { + current_names = + current_names->next = + *extra_names; + *extra_names = NULL; + } + current_names->used = 0; + current_names->next = NULL; + } + } + current_names->target_group[current_names->used] = NULL; + current_names->names[current_names->used++] = name; + while (iswspace(*member_start)) { + member_start++; + } + /* Check if there are more members */ + if ((*member_start == (int) parenright_char) || + (member_start >= string_end)) { + return current_names; + } + } + /* NOTREACHED */ +} + +/* + * normalize_name(name_string, length) + * + * Take a namestring and remove redundant ../, // and ./ constructs + * + * Return value: + * The normalized name + * + * Parameters: + * name_string Path string to normalize + * length Length of that string + * + * Global variables used: + * dot The Name ".", compared against + * dotdot The Name "..", compared against + */ +Name +normalize_name(register wchar_t *name_string, register int length) +{ + static Name dotdot; + register wchar_t *string = ALLOC_WC(length + 1); + register wchar_t *string2; + register wchar_t *cdp; + wchar_t *current_component; + Name name; + register int count; + + if (dotdot == NULL) { + MBSTOWCS(wcs_buffer, ".."); + dotdot = GETNAME(wcs_buffer, FIND_LENGTH); + } + + /* + * Copy string removing ./ and //. + * First strip leading ./ + */ + while ((length > 1) && + (name_string[0] == (int) period_char) && + (name_string[1] == (int) slash_char)) { + name_string += 2; + length -= 2; + while ((length > 0) && (name_string[0] == (int) slash_char)) { + name_string++; + length--; + } + } + /* Then copy the rest of the string removing /./ & // */ + cdp = string; + while (length > 0) { + if (((length > 2) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) period_char) && + (name_string[2] == (int) slash_char)) || + ((length == 2) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) period_char))) { + name_string += 2; + length -= 2; + continue; + } + if ((length > 1) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) slash_char)) { + name_string++; + length--; + continue; + } + *cdp++ = *name_string++; + length--; + } + *cdp = (int) nul_char; + /* + * Now scan for <name>/../ and remove such combinations iff <name> + * is not another .. + * Each time something is removed, the whole process is restarted. + */ +removed_one: + name_string = string; + string2 = name_string; /*save for free*/ + current_component = + cdp = + string = + ALLOC_WC((length = wslen(name_string)) + 1); + while (length > 0) { + if (((length > 3) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) period_char) && + (name_string[2] == (int) period_char) && + (name_string[3] == (int) slash_char)) || + ((length == 3) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) period_char) && + (name_string[2] == (int) period_char))) { + /* Positioned on the / that starts a /.. sequence */ + if (((count = cdp - current_component) != 0) && + (exists(name = GETNAME(string, cdp - string)) > file_doesnt_exist) && + (!name->stat.is_sym_link)) { + name = GETNAME(current_component, count); + if(name != dotdot) { + cdp = current_component; + name_string += 3; + length -= 3; + if (length > 0) { + name_string++; /* skip slash */ + length--; + while (length > 0) { + *cdp++ = *name_string++; + length--; + } + } + *cdp = (int) nul_char; + retmem(string2); + goto removed_one; + } + } + } + if ((*cdp++ = *name_string++) == (int) slash_char) { + current_component = cdp; + } + length--; + } + *cdp = (int) nul_char; + if (string[0] == (int) nul_char) { + name = dot; + } else { + name = GETNAME(string, FIND_LENGTH); + } + retmem(string); + retmem(string2); + return name; +} + +/* + * find_target_groups(target_list) + * + * If a "+" was seen when the target list was scanned we need to extract + * the groups. Each target in the name vector that is a member of a + * group gets a pointer to a chain of all the members stuffed in its + * target_group vector slot + * + * Parameters: + * target_list The list of targets that contains "+" + * + * Global variables used: + * plus The Name "+", compared against + */ +Chain +find_target_groups(register Name_vector target_list, register int i, Boolean reset) +{ + static Chain target_group = NULL; + static Chain tail_target_group = NULL; + static Name *next; + static Boolean clear_target_group = false; + + if (reset) { + target_group = NULL; + tail_target_group = NULL; + clear_target_group = false; + } + + /* Scan the list of targets */ + /* If the previous target terminated a group */ + /* we flush the pointer to that member chain */ + if (clear_target_group) { + clear_target_group = false; + target_group = NULL; + } + /* Pick up a pointer to the cell with */ + /* the next target */ + if (i + 1 != target_list->used) { + next = &target_list->names[i + 1]; + } else { + next = (target_list->next != NULL) ? + &target_list->next->names[0] : NULL; + } + /* We have four states here : + * 0: No target group started and next element is not "+" + * This is not interesting. + * 1: A target group is being built and the next element + * is not "+". This terminates the group. + * 2: No target group started and the next member is "+" + * This is the first target in a group. + * 3: A target group started and the next member is a "+" + * The group continues. + */ + switch ((target_group ? 1 : 0) + + (next && (*next == plus) ? + 2 : 0)) { + case 0: /* Not target_group */ + break; + case 1: /* Last group member */ + /* We need to keep this pointer so */ + /* we can stuff it for last member */ + clear_target_group = true; + /* fall into */ + case 3: /* Middle group member */ + /* Add this target to the */ + /* current chain */ + tail_target_group->next = ALLOC(Chain); + tail_target_group = tail_target_group->next; + tail_target_group->next = NULL; + tail_target_group->name = target_list->names[i]; + break; + case 2: /* First group member */ + /* Start a new chain */ + target_group = tail_target_group = ALLOC(Chain); + target_group->next = NULL; + target_group->name = target_list->names[i]; + break; + } + /* Stuff the current chain, if any, in the */ + /* targets group slot */ + target_list->target_group[i] = target_group; + if ((next != NULL) && + (*next == plus)) { + *next = NULL; + } + return (tail_target_group); +} + +/* + * enter_dependencies(target, target_group, depes, command, separator) + * + * Take one target and a list of dependencies and process the whole thing. + * The target might be special in some sense in which case that is handled + * + * Parameters: + * target The target we want to enter + * target_group Non-NULL if target is part of a group this time + * depes A list of dependencies for the target + * command The command the target should be entered with + * separator Indicates if this is a ":" or a "::" rule + * + * Static variables used: + * built_last_make_run_seen If the previous target was + * .BUILT_LAST_MAKE_RUN we say to rewrite + * the state file later on + * + * Global variables used: + * command_changed Set to indicate if .make.state needs rewriting + * default_target_to_build Set to the target if reading makefile + * and this is the first regular target + * force The Name " FORCE", used with "::" targets + * makefile_type We do different things for makefile vs. report + * not_auto The Name ".NOT_AUTO", compared against + * recursive_name The Name ".RECURSIVE", compared against + * temp_file_number Used to figure out when to clear stale + * automatic dependencies + * trace_reader Indicates that we should echo stuff we read + */ +void +enter_dependencies(register Name target, Chain target_group, register Name_vector depes, register Cmd_line command, register Separator separator) +{ + register int i; + register Property line; + Name name; + Name directory; + wchar_t *namep; + char *mb_namep; + Dependency dp; + Dependency *dpp; + Property line2; + wchar_t relative[MAXPATHLEN]; + register int recursive_state; + Boolean register_as_auto; + Boolean not_auto_found; + char *slash; + Wstring depstr; + + /* Check if this is a .RECURSIVE line */ + if ((depes->used >= 3) && + (depes->names[0] == recursive_name)) { +#ifdef NSE + nse_did_recursion= true; +#endif + target->has_recursive_dependency = true; + depes->names[0] = NULL; + recursive_state = 0; + dp = NULL; + dpp = &dp; + /* Read the dependencies. They are "<directory> <target-made>*/ + /* <makefile>*" */ + for (; depes != NULL; depes = depes->next) { + for (i = 0; i < depes->used; i++) { + if (depes->names[i] != NULL) { + switch (recursive_state++) { + case 0: /* Directory */ + { + depstr.init(depes->names[i]); + make_relative(depstr.get_string(), + relative); + directory = + GETNAME(relative, + FIND_LENGTH); + } + break; + case 1: /* Target */ + name = depes->names[i]; + break; + default: /* Makefiles */ + *dpp = ALLOC(Dependency); + (*dpp)->next = NULL; + (*dpp)->name = depes->names[i]; + (*dpp)->automatic = false; + (*dpp)->stale = false; + (*dpp)->built = false; + dpp = &((*dpp)->next); + break; + } + } + } + } + /* Check if this recursion already has been reported else */ + /* enter the recursive prop for the target */ + /* The has_built flag is used to tell if this .RECURSIVE */ + /* was discovered from this run (read from a tmp file) */ + /* or was from discovered from the original .make.state */ + /* file */ + for (line = get_prop(target->prop, recursive_prop); + line != NULL; + line = get_prop(line->next, recursive_prop)) { + if ((line->body.recursive.directory == directory) && + (line->body.recursive.target == name)) { + line->body.recursive.makefiles = dp; + line->body.recursive.has_built = + (Boolean) + (makefile_type == reading_cpp_file); + return; + } + } + line2 = append_prop(target, recursive_prop); + line2->body.recursive.directory = directory; + line2->body.recursive.target = name; + line2->body.recursive.makefiles = dp; + line2->body.recursive.has_built = + (Boolean) (makefile_type == reading_cpp_file); + line2->body.recursive.in_depinfo = false; + return; + } + /* If this is the first target that doesnt start with a "." in the */ + /* makefile we remember that */ + Wstring tstr(target); + wchar_t * wcb = tstr.get_string(); + if ((makefile_type == reading_makefile) && + (default_target_to_build == NULL) && + ((wcb[0] != (int) period_char) || + wschr(wcb, (int) slash_char))) { + +/* BID 1181577: $(EMPTY_MACRO) + $(EMPTY_MACRO): +** The target with empty name cannot be default_target_to_build +*/ + if (target->hash.length != 0) + default_target_to_build = target; + } + /* Check if the line is ":" or "::" */ + if (makefile_type == reading_makefile) { + if (target->colons == no_colon) { + target->colons = separator; + } else { + if (target->colons != separator) { + fatal_reader(catgets(catd, 1, 92, ":/:: conflict for target `%s'"), + target->string_mb); + } + } + if (target->colons == two_colon) { + if (depes->used == 0) { + /* If this is a "::" type line with no */ + /* dependencies we add one "FRC" type */ + /* dependency for free */ + depes->used = 1; /* Force :: targets with no + * depes to always run */ + depes->names[0] = force; + } + /* Do not delete "::" type targets when interrupted */ + target->stat.is_precious = true; + /* + * Build a synthetic target "<number>%target" + * for "target". + */ + mb_namep = getmem((int) (strlen(target->string_mb) + 10)); + namep = ALLOC_WC((int) (target->hash.length + 10)); + slash = strrchr(target->string_mb, (int) slash_char); + if (slash == NULL) { + (void) sprintf(mb_namep, + "%d@%s", + target->colon_splits++, + target->string_mb); + } else { + *slash = 0; + (void) sprintf(mb_namep, + "%s/%d@%s", + target->string_mb, + target->colon_splits++, + slash + 1); + *slash = (int) slash_char; + } + MBSTOWCS(namep, mb_namep); + retmem_mb(mb_namep); + name = GETNAME(namep, FIND_LENGTH); + retmem(namep); + if (trace_reader) { + (void) printf("%s:\t", target->string_mb); + } + /* Make "target" depend on "<number>%target */ + line2 = maybe_append_prop(target, line_prop); + enter_dependency(line2, name, true); + line2->body.line.target = target; + /* Put a prop on "<number>%target that makes */ + /* appear as "target" */ + /* when it is processed */ + maybe_append_prop(name, target_prop)-> + body.target.target = target; + target->is_double_colon_parent = true; + name->is_double_colon = true; + name->has_target_prop = true; + if (trace_reader) { + (void) printf("\n"); + } + (target = name)->stat.is_file = true; + } + } + /* This really is a regular dependency line. Just enter it */ + line = maybe_append_prop(target, line_prop); + line->body.line.target = target; + /* Depending on what kind of makefile we are reading we have to */ + /* treat things differently */ + switch (makefile_type) { + case reading_makefile: + /* Reading regular makefile. Just notice whether this */ + /* redefines the rule for the target */ + if (command != NULL) { + if (line->body.line.command_template != NULL) { + line->body.line.command_template_redefined = + true; + if ((wcb[0] == (int) period_char) && + !wschr(wcb, (int) slash_char)) { + line->body.line.command_template = + command; + } + } else { + line->body.line.command_template = command; + } + } else { + if ((wcb[0] == (int) period_char) && + !wschr(wcb, (int) slash_char)) { + line->body.line.command_template = command; + } + } + break; + case rereading_statefile: + /* Rereading the statefile. We only enter thing that changed */ + /* since the previous time we read it */ + if (!built_last_make_run_seen) { + for (Cmd_line next, cmd = command; cmd != NULL; cmd = next) { + next = cmd->next; + free(cmd); + } + return; + } + built_last_make_run_seen = false; + command_changed = true; + target->ran_command = true; + case reading_statefile: + /* Reading the statefile for the first time. Enter the rules */ + /* as "Commands used" not "templates to use" */ + if (command != NULL) { + for (Cmd_line next, cmd = line->body.line.command_used; + cmd != NULL; cmd = next) { + next = cmd->next; + free(cmd); + } + line->body.line.command_used = command; + } + case reading_cpp_file: + /* Reading report file from programs that reports */ + /* dependencies. If this is the first time the target is */ + /* read from this reportfile we clear all old */ + /* automatic depes */ + if (target->temp_file_number == temp_file_number) { + break; + } + target->temp_file_number = temp_file_number; + command_changed = true; + if (line != NULL) { + for (dp = line->body.line.dependencies; + dp != NULL; + dp = dp->next) { + if (dp->automatic) { + dp->stale = true; + } + } + } + break; + default: + fatal_reader(catgets(catd, 1, 93, "Internal error. Unknown makefile type %d"), + makefile_type); + } + /* A target may only be involved in one target group */ + if (line->body.line.target_group != NULL) { + if (target_group != NULL) { + fatal_reader(catgets(catd, 1, 94, "Too many target groups for target `%s'"), + target->string_mb); + } + } else { + line->body.line.target_group = target_group; + } + + if (trace_reader) { + (void) printf("%s:\t", target->string_mb); + } + /* Enter the dependencies */ + register_as_auto = BOOLEAN(makefile_type != reading_makefile); + not_auto_found = false; + for (; + (depes != NULL) && !not_auto_found; + depes = depes->next) { + for (i = 0; i < depes->used; i++) { + /* the dependency .NOT_AUTO signals beginning of + * explicit dependancies which were put at end of + * list in .make.state file - we stop entering + * dependencies at this point + */ + if (depes->names[i] == not_auto) { + not_auto_found = true; + break; + } + enter_dependency(line, + depes->names[i], + register_as_auto); + } + } + if (trace_reader) { + (void) printf("\n"); + print_rule(command); + } +} + +/* + * enter_dependency(line, depe, automatic) + * + * Enter one dependency. Do not enter duplicates. + * + * Parameters: + * line The line block that the dependeny is + * entered for + * depe The dependency to enter + * automatic Used to set the field "automatic" + * + * Global variables used: + * makefile_type We do different things for makefile vs. report + * trace_reader Indicates that we should echo stuff we read + * wait_name The Name ".WAIT", compared against + */ +void +enter_dependency(Property line, register Name depe, Boolean automatic) +{ + register Dependency dp; + register Dependency *insert; + + if (trace_reader) { + (void) printf("%s ", depe->string_mb); + } + /* Find the end of the list and check for duplicates */ + for (insert = &line->body.line.dependencies, dp = *insert; + dp != NULL; + insert = &dp->next, dp = *insert) { + if ((dp->name == depe) && (depe != wait_name)) { + if (dp->automatic) { + dp->automatic = automatic; + if (automatic) { + dp->built = false; + depe->stat.is_file = true; +#ifdef NSE + depe->has_parent= true; + depe->is_target= true; +#endif + } + } + dp->stale = false; + return; + } + } + /* Insert the new dependency since we couldnt find it */ + dp = *insert = ALLOC(Dependency); + dp->name = depe; + dp->next = NULL; + dp->automatic = automatic; + dp->stale = false; + dp->built = false; + depe->stat.is_file = true; +#ifdef NSE + depe->has_parent= true; + depe->is_target= true; +#endif + + if ((makefile_type == reading_makefile) && + (line != NULL) && + (line->body.line.target != NULL)) { + line->body.line.target->has_regular_dependency = true; +#ifdef NSE + line->body.line.target->is_target= true; +#endif + } +} + +/* + * enter_percent(target, depes, command) + * + * Enter "x%y : a%b" type lines + * % patterns are stored in four parts head and tail for target and source + * + * Parameters: + * target Left hand side of pattern + * depes The dependency list with the rh pattern + * command The command for the pattern + * + * Global variables used: + * empty_name The Name "", compared against + * percent_list The list of all percent rules, added to + * trace_reader Indicates that we should echo stuff we read + */ +Percent +enter_percent(register Name target, Chain target_group, register Name_vector depes, Cmd_line command) +{ + register Percent result = ALLOC(Percent); + register Percent depe; + register Percent *depe_tail = &result->dependencies; + register Percent *insert; + register wchar_t *cp, *cp1; + Name_vector nvp; + int i; + int pattern; + + result->next = NULL; + result->patterns = NULL; + result->patterns_total = 0; + result->command_template = command; + result->being_expanded = false; + result->name = target; + result->dependencies = NULL; + result->target_group = target_group; + + /* get patterns count */ + Wstring wcb(target); + cp = wcb.get_string(); + while (true) { + cp = (wchar_t *) wschr(cp, (int) percent_char); + if (cp != NULL) { + result->patterns_total++; + cp++; + } else { + break; + } + } + result->patterns_total++; + + /* allocate storage for patterns */ + result->patterns = (Name *) getmem(sizeof(Name) * result->patterns_total); + + /* then create patterns */ + cp = wcb.get_string(); + pattern = 0; + while (true) { + cp1 = (wchar_t *) wschr(cp, (int) percent_char); + if (cp1 != NULL) { + result->patterns[pattern] = GETNAME(cp, cp1 - cp); + cp = cp1 + 1; + pattern++; + } else { + result->patterns[pattern] = GETNAME(cp, (int) target->hash.length - (cp - wcb.get_string())); + break; + } + } + + Wstring wcb1; + + /* build dependencies list */ + for (nvp = depes; nvp != NULL; nvp = nvp->next) { + for (i = 0; i < nvp->used; i++) { + depe = ALLOC(Percent); + depe->next = NULL; + depe->patterns = NULL; + depe->patterns_total = 0; + depe->name = nvp->names[i]; + depe->dependencies = NULL; + depe->command_template = NULL; + depe->being_expanded = false; + depe->target_group = NULL; + + *depe_tail = depe; + depe_tail = &depe->next; + + if (depe->name->percent) { + /* get patterns count */ + wcb1.init(depe->name); + cp = wcb1.get_string(); + while (true) { + cp = (wchar_t *) wschr(cp, (int) percent_char); + if (cp != NULL) { + depe->patterns_total++; + cp++; + } else { + break; + } + } + depe->patterns_total++; + + /* allocate storage for patterns */ + depe->patterns = (Name *) getmem(sizeof(Name) * depe->patterns_total); + + /* then create patterns */ + cp = wcb1.get_string(); + pattern = 0; + while (true) { + cp1 = (wchar_t *) wschr(cp, (int) percent_char); + if (cp1 != NULL) { + depe->patterns[pattern] = GETNAME(cp, cp1 - cp); + cp = cp1 + 1; + pattern++; + } else { + depe->patterns[pattern] = GETNAME(cp, (int) depe->name->hash.length - (cp - wcb1.get_string())); + break; + } + } + } + } + } + + /* Find the end of the percent list and append the new pattern */ + for (insert = &percent_list; (*insert) != NULL; insert = &(*insert)->next); + *insert = result; + + if (trace_reader) { + (void) printf("%s:", result->name->string_mb); + + for (depe = result->dependencies; depe != NULL; depe = depe->next) { + (void) printf(" %s", depe->name->string_mb); + } + + (void) printf("\n"); + + print_rule(command); + } + + return result; +} + +/* + * enter_dyntarget(target) + * + * Enter "$$(MACRO) : b" type lines + * + * Parameters: + * target Left hand side of pattern + * + * Global variables used: + * dyntarget_list The list of all percent rules, added to + * trace_reader Indicates that we should echo stuff we read + */ +Dyntarget +enter_dyntarget(register Name target) +{ + register Dyntarget result = ALLOC(Dyntarget); + Dyntarget p; + Dyntarget *insert; + int i; + + result->next = NULL; + result->name = target; + + + /* Find the end of the dyntarget list and append the new pattern */ + for (insert = &dyntarget_list, p = *insert; + p != NULL; + insert = &p->next, p = *insert); + *insert = result; + + if (trace_reader) { + (void) printf(NOCATGETS("Dynamic target %s:\n"), result->name->string_mb); + } + return( result); +} + + +/* + * special_reader(target, depes, command) + * + * Read the pseudo targets make knows about + * This handles the special targets that should not be entered as regular + * target/dependency sets. + * + * Parameters: + * target The special target + * depes The list of dependencies it was entered with + * command The command it was entered with + * + * Static variables used: + * built_last_make_run_seen Set to indicate .BUILT_LAST... seen + * + * Global variables used: + * all_parallel Set to indicate that everything runs parallel + * svr4 Set when ".SVR4" target is read + * svr4_name The Name ".SVR4" + * posix Set when ".POSIX" target is read + * posix_name The Name ".POSIX" + * current_make_version The Name "<current version number>" + * default_rule Set when ".DEFAULT" target is read + * default_rule_name The Name ".DEFAULT", used for tracing + * dot_keep_state The Name ".KEEP_STATE", used for tracing + * ignore_errors Set if ".IGNORE" target is read + * ignore_name The Name ".IGNORE", used for tracing + * keep_state Set if ".KEEP_STATE" target is read + * no_parallel_name The Name ".NO_PARALLEL", used for tracing + * only_parallel Set to indicate only some targets runs parallel + * parallel_name The Name ".PARALLEL", used for tracing + * precious The Name ".PRECIOUS", used for tracing + * sccs_get_name The Name ".SCCS_GET", used for tracing + * sccs_get_posix_name The Name ".SCCS_GET_POSIX", used for tracing + * get_name The Name ".GET", used for tracing + * sccs_get_rule Set when ".SCCS_GET" target is read + * silent Set when ".SILENT" target is read + * silent_name The Name ".SILENT", used for tracing + * trace_reader Indicates that we should echo stuff we read + */ +void +special_reader(Name target, register Name_vector depes, Cmd_line command) +{ + register int n; + + switch (target->special_reader) { + + case svr4_special: + if (depes->used != 0) { + fatal_reader(catgets(catd, 1, 98, "Illegal dependencies for target `%s'"), + target->string_mb); + } + svr4 = true; + posix = false; + keep_state = false; + all_parallel = false; + only_parallel = false; + if (trace_reader) { + (void) printf("%s:\n", svr4_name->string_mb); + } + break; + + case posix_special: + if(svr4) + break; + if (depes->used != 0) { + fatal_reader(catgets(catd, 1, 99, "Illegal dependencies for target `%s'"), + target->string_mb); + } + posix = true; + /* with posix on, use the posix get rule */ + sccs_get_rule = sccs_get_posix_rule; + /* turn keep state off being SunPro make specific */ + keep_state = false; + #if defined(SUN5_0) + /* Use /usr/xpg4/bin/sh on Solaris */ + MBSTOWCS(wcs_buffer, NOCATGETS("/usr/xpg4/bin/sh")); + (void) SETVAR(shell_name, GETNAME(wcs_buffer, FIND_LENGTH), false); + #endif + if (trace_reader) { + (void) printf("%s:\n", posix_name->string_mb); + } + break; + + case built_last_make_run_special: + built_last_make_run_seen = true; + break; + + case default_special: + if (depes->used != 0) { + warning(catgets(catd, 1, 100, "Illegal dependency list for target `%s'"), + target->string_mb); + } + default_rule = command; + if (trace_reader) { + (void) printf("%s:\n", + default_rule_name->string_mb); + print_rule(command); + } + break; + +#ifdef NSE + case derived_src_special: + for (; depes != NULL; depes= depes->next) + for (n= 0; n < depes->used; n++) { + if (trace_reader) + (void)printf("%s:\t%s\n", + precious->string_mb, + depes->names[n]->string_mb); + depes->names[n]->stat.is_derived_src= true; + }; + break; +#endif + + case ignore_special: + if ((depes->used != 0) &&(!posix)){ + fatal_reader(catgets(catd, 1, 101, "Illegal dependencies for target `%s'"), + target->string_mb); + } + if (depes->used == 0) + { + ignore_errors_all = true; + } + if(svr4) { + ignore_errors_all = true; + break; + } + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + depes->names[n]->ignore_error_mode = true; + } + } + if (trace_reader) { + (void) printf("%s:\n", ignore_name->string_mb); + } + break; + + case keep_state_special: + if(svr4) + break; + /* ignore keep state, being SunPro make specific */ + if(posix) + break; + if (depes->used != 0) { + fatal_reader(catgets(catd, 1, 102, "Illegal dependencies for target `%s'"), + target->string_mb); + } + keep_state = true; + if (trace_reader) { + (void) printf("%s:\n", + dot_keep_state->string_mb); + } + break; + + case keep_state_file_special: + if(svr4) + break; + if(posix) + break; + /* it's not necessary to specify KEEP_STATE, if this + ** is given, so set the keep_state. + */ + keep_state = true; + if (depes->used != 0) { + if((!make_state) ||(!strcmp(make_state->string_mb,NOCATGETS(".make.state")))) { + make_state = depes->names[0]; + } + } + break; + case make_version_special: + if(svr4) + break; + if (depes->used != 1) { + fatal_reader(catgets(catd, 1, 103, "Illegal dependency list for target `%s'"), + target->string_mb); + } + if (depes->names[0] != current_make_version) { + /* + * Special case the fact that version 1.0 and 1.1 + * are identical. + */ + if (!IS_EQUAL(depes->names[0]->string_mb, + NOCATGETS("VERSION-1.1")) || + !IS_EQUAL(current_make_version->string_mb, + NOCATGETS("VERSION-1.0"))) { + /* + * Version mismatches should cause the + * .make.state file to be skipped. + * This is currently not true - it is read + * anyway. + */ + warning(catgets(catd, 1, 104, "Version mismatch between current version `%s' and `%s'"), + current_make_version->string_mb, + depes->names[0]->string_mb); + } + } + break; + + case no_parallel_special: + if(svr4) + break; + /* Set the no_parallel bit for all the targets on */ + /* the dependency list */ + if (depes->used == 0) { + /* only those explicitly made parallel */ + only_parallel = true; + all_parallel = false; + } + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + if (trace_reader) { + (void) printf("%s:\t%s\n", + no_parallel_name->string_mb, + depes->names[n]->string_mb); + } + depes->names[n]->no_parallel = true; + depes->names[n]->parallel = false; + } + } + break; + + case parallel_special: + if(svr4) + break; + if (depes->used == 0) { + /* everything runs in parallel */ + all_parallel = true; + only_parallel = false; + } + /* Set the parallel bit for all the targets on */ + /* the dependency list */ + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + if (trace_reader) { + (void) printf("%s:\t%s\n", + parallel_name->string_mb, + depes->names[n]->string_mb); + } + depes->names[n]->parallel = true; + depes->names[n]->no_parallel = false; + } + } + break; + + case localhost_special: + if(svr4) + break; + /* Set the no_parallel bit for all the targets on */ + /* the dependency list */ + if (depes->used == 0) { + /* only those explicitly made parallel */ + only_parallel = true; + all_parallel = false; + } + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + if (trace_reader) { + (void) printf("%s:\t%s\n", + localhost_name->string_mb, + depes->names[n]->string_mb); + } + depes->names[n]->no_parallel = true; + depes->names[n]->parallel = false; + depes->names[n]->localhost = true; + } + } + break; + + case precious_special: + if (depes->used == 0) { + /* everything is precious */ + all_precious = true; + } else { + all_precious = false; + } + if(svr4) { + all_precious = true; + break; + } + /* Set the precious bit for all the targets on */ + /* the dependency list */ + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + if (trace_reader) { + (void) printf("%s:\t%s\n", + precious->string_mb, + depes->names[n]->string_mb); + } + depes->names[n]->stat.is_precious = true; + } + } + break; + + case sccs_get_special: + if (depes->used != 0) { + fatal_reader(catgets(catd, 1, 105, "Illegal dependencies for target `%s'"), + target->string_mb); + } + sccs_get_rule = command; + sccs_get_org_rule = command; + if (trace_reader) { + (void) printf("%s:\n", sccs_get_name->string_mb); + print_rule(command); + } + break; + + case sccs_get_posix_special: + if (depes->used != 0) { + fatal_reader(catgets(catd, 1, 106, "Illegal dependencies for target `%s'"), + target->string_mb); + } + sccs_get_posix_rule = command; + if (trace_reader) { + (void) printf("%s:\n", sccs_get_posix_name->string_mb); + print_rule(command); + } + break; + + case get_posix_special: + if (depes->used != 0) { + fatal_reader(catgets(catd, 1, 107, "Illegal dependencies for target `%s'"), + target->string_mb); + } + get_posix_rule = command; + if (trace_reader) { + (void) printf("%s:\n", get_posix_name->string_mb); + print_rule(command); + } + break; + + case get_special: + if(!svr4) { + break; + } + if (depes->used != 0) { + fatal_reader(catgets(catd, 1, 108, "Illegal dependencies for target `%s'"), + target->string_mb); + } + get_rule = command; + sccs_get_rule = command; + if (trace_reader) { + (void) printf("%s:\n", get_name->string_mb); + print_rule(command); + } + break; + + case silent_special: + if ((depes->used != 0) && (!posix)){ + fatal_reader(catgets(catd, 1, 109, "Illegal dependencies for target `%s'"), + target->string_mb); + } + if (depes->used == 0) + { + silent_all = true; + } + if(svr4) { + silent_all = true; + break; + } + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + depes->names[n]->silent_mode = true; + } + } + if (trace_reader) { + (void) printf("%s:\n", silent_name->string_mb); + } + break; + + case suffixes_special: + read_suffixes_list(depes); + break; + + default: + + fatal_reader(catgets(catd, 1, 110, "Internal error: Unknown special reader")); + } +} + +/* + * read_suffixes_list(depes) + * + * Read the special list .SUFFIXES. If it is empty the old list is + * cleared. Else the new one is appended. Suffixes with ~ are extracted + * and marked. + * + * Parameters: + * depes The list of suffixes + * + * Global variables used: + * hashtab The central hashtable for Names. + * suffixes The list of suffixes, set or appended to + * suffixes_name The Name ".SUFFIXES", used for tracing + * trace_reader Indicates that we should echo stuff we read + */ +static void +read_suffixes_list(register Name_vector depes) +{ + register int n; + register Dependency dp; + register Dependency *insert_dep; + register Name np; + Name np2; + register Boolean first = true; + + if (depes->used == 0) { + /* .SUFFIXES with no dependency list clears the */ + /* suffixes list */ + for (Name_set::iterator np = hashtab.begin(), e = hashtab.end(); np != e; np++) { + np->with_squiggle = + np->without_squiggle = + false; + } + suffixes = NULL; + if (trace_reader) { + (void) printf("%s:\n", suffixes_name->string_mb); + } + return; + } + Wstring str; + /* Otherwise we append to the list */ + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + np = depes->names[n]; + /* Find the end of the list and check if the */ + /* suffix already has been entered */ + for (insert_dep = &suffixes, dp = *insert_dep; + dp != NULL; + insert_dep = &dp->next, dp = *insert_dep) { + if (dp->name == np) { + goto duplicate_suffix; + } + } + if (trace_reader) { + if (first) { + (void) printf("%s:\t", + suffixes_name->string_mb); + first = false; + } + (void) printf("%s ", depes->names[n]->string_mb); + } + if(!(posix|svr4)) { + /* If the suffix is suffixed with "~" we */ + /* strip that and mark the suffix nameblock */ + str.init(np); + wchar_t * wcb = str.get_string(); + if (wcb[np->hash.length - 1] == + (int) tilde_char) { + np2 = GETNAME(wcb, + (int)(np->hash.length - 1)); + np2->with_squiggle = true; + if (np2->without_squiggle) { + continue; + } + np = np2; + } + } + np->without_squiggle = true; + /* Add the suffix to the list */ + dp = *insert_dep = ALLOC(Dependency); + insert_dep = &dp->next; + dp->next = NULL; + dp->name = np; + dp->built = false; + duplicate_suffix:; + } + } + if (trace_reader) { + (void) printf("\n"); + } +} + +/* + * make_relative(to, result) + * + * Given a file name compose a relative path name from it to the + * current directory. + * + * Parameters: + * to The path we want to make relative + * result Where to put the resulting relative path + * + * Global variables used: + */ +static void +make_relative(wchar_t *to, wchar_t *result) +{ + wchar_t *from; + wchar_t *allocated; + wchar_t *cp; + wchar_t *tocomp; + int ncomps; + int i; + int len; + + /* Check if the path is already relative. */ + if (to[0] != (int) slash_char) { + (void) wscpy(result, to); + return; + } + + MBSTOWCS(wcs_buffer, get_current_path()); + from = allocated = (wchar_t *) wsdup(wcs_buffer); + + /* + * Find the number of components in the from name. + * ncomp = number of slashes + 1. + */ + ncomps = 1; + for (cp = from; *cp != (int) nul_char; cp++) { + if (*cp == (int) slash_char) { + ncomps++; + } + } + + /* + * See how many components match to determine how many "..", + * if any, will be needed. + */ + result[0] = (int) nul_char; + tocomp = to; + while ((*from != (int) nul_char) && (*from == *to)) { + if (*from == (int) slash_char) { + ncomps--; + tocomp = &to[1]; + } + from++; + to++; + } + + /* + * Now for some special cases. Check for exact matches and + * for either name terminating exactly. + */ + if (*from == (int) nul_char) { + if (*to == (int) nul_char) { + MBSTOWCS(wcs_buffer, "."); + (void) wscpy(result, wcs_buffer); + retmem(allocated); + return; + } + if (*to == (int) slash_char) { + ncomps--; + tocomp = &to[1]; + } + } else if ((*from == (int) slash_char) && (*to == (int) nul_char)) { + ncomps--; + tocomp = to; + } + /* Add on the ".."s. */ + for (i = 0; i < ncomps; i++) { + MBSTOWCS(wcs_buffer, "../"); + (void) wscat(result, wcs_buffer); + } + + /* Add on the remainder of the to name, if any. */ + if (*tocomp == (int) nul_char) { + len = wslen(result); + result[len - 1] = (int) nul_char; + } else { + (void) wscat(result, tocomp); + } + retmem(allocated); + return; +} + +/* + * print_rule(command) + * + * Used when tracing the reading of rules + * + * Parameters: + * command Command to print + * + * Global variables used: + */ +static void +print_rule(register Cmd_line command) +{ + for (; command != NULL; command = command->next) { + (void) printf("\t%s\n", command->command_line->string_mb); + } +} + +/* + * enter_conditional(target, name, value, append) + * + * Enter "target := MACRO= value" constructs + * + * Parameters: + * target The target the macro is for + * name The name of the macro + * value The value for the macro + * append Indicates if the assignment is appending or not + * + * Global variables used: + * conditionals A special Name that stores all conditionals + * where the target is a % pattern + * trace_reader Indicates that we should echo stuff we read + */ +void +enter_conditional(register Name target, Name name, Name value, register Boolean append) +{ + register Property conditional; + static int sequence; + Name orig_target = target; + + if (name == target_arch) { + enter_conditional(target, virtual_root, virtual_root, false); + } + + if (target->percent) { + target = conditionals; + } + + if (name->colon) { + sh_transform(&name, &value); + } + + /* Count how many conditionals we must activate before building the */ + /* target */ + if (target->percent) { + target = conditionals; + } + + target->conditional_cnt++; + maybe_append_prop(name, macro_prop)->body.macro.is_conditional = true; + /* Add the property for the target */ + conditional = append_prop(target, conditional_prop); + conditional->body.conditional.target = orig_target; + conditional->body.conditional.name = name; + conditional->body.conditional.value = value; + conditional->body.conditional.sequence = sequence++; + conditional->body.conditional.append = append; + if (trace_reader) { + if (value == NULL) { + (void) printf("%s := %s %c=\n", + target->string_mb, + name->string_mb, + append ? + (int) plus_char : (int) space_char); + } else { + (void) printf("%s := %s %c= %s\n", + target->string_mb, + name->string_mb, + append ? + (int) plus_char : (int) space_char, + value->string_mb); + } + } +} + +/* + * enter_equal(name, value, append) + * + * Enter "MACRO= value" constructs + * + * Parameters: + * name The name of the macro + * value The value for the macro + * append Indicates if the assignment is appending or not + * + * Global variables used: + * trace_reader Indicates that we should echo stuff we read + */ +void +enter_equal(Name name, Name value, register Boolean append) +{ + wchar_t *string; + Name temp; + + if (name->colon) { + sh_transform(&name, &value); + } + (void) SETVAR(name, value, append); + + /* if we're setting FC, we want to set F77 to the same value. */ + Wstring nms(name); + wchar_t * wcb = nms.get_string(); + string = wcb; + if (string[0]=='F' && + string[1]=='C' && + string[2]=='\0') { + MBSTOWCS(wcs_buffer, NOCATGETS("F77")); + temp = GETNAME(wcs_buffer, FIND_LENGTH); + (void) SETVAR(temp, value, append); +/* + fprintf(stderr, catgets(catd, 1, 111, "warning: FC is obsolete, use F77 instead\n")); + */ + } + + if (trace_reader) { + if (value == NULL) { + (void) printf("%s %c=\n", + name->string_mb, + append ? + (int) plus_char : (int) space_char); + } else { + (void) printf("%s %c= %s\n", + name->string_mb, + append ? + (int) plus_char : (int) space_char, + value->string_mb); + } + } +} + +/* + * sh_transform(name, value) + * + * Parameters: + * name The name of the macro we might transform + * value The value to transform + * + */ +static void +sh_transform(Name *name, Name *value) +{ + /* Check if we need :sh transform */ + wchar_t *colon; + String_rec command; + String_rec destination; + wchar_t buffer[1000]; + wchar_t buffer1[1000]; + + static wchar_t colon_sh[4]; + static wchar_t colon_shell[7]; + + if (colon_sh[0] == (int) nul_char) { + MBSTOWCS(colon_sh, NOCATGETS(":sh")); + MBSTOWCS(colon_shell, NOCATGETS(":shell")); + } + Wstring nms((*name)); + wchar_t * wcb = nms.get_string(); + + colon = (wchar_t *) wsrchr(wcb, (int) colon_char); + if ((colon != NULL) && (IS_WEQUAL(colon, colon_sh) || IS_WEQUAL(colon, colon_shell))) { + INIT_STRING_FROM_STACK(destination, buffer); + + if(*value == NULL) { + buffer[0] = 0; + } else { + Wstring wcb1((*value)); + if (IS_WEQUAL(colon, colon_shell)) { + INIT_STRING_FROM_STACK(command, buffer1); + expand_value(*value, &command, false); + } else { + command.text.p = wcb1.get_string() + (*value)->hash.length; + command.text.end = command.text.p; + command.buffer.start = wcb1.get_string(); + command.buffer.end = command.text.p; + } + sh_command2string(&command, &destination); + } + + (*value) = GETNAME(destination.buffer.start, FIND_LENGTH); + *colon = (int) nul_char; + (*name) = GETNAME(wcb, FIND_LENGTH); + *colon = (int) colon_char; + } +} + +/* + * fatal_reader(format, args...) + * + * Parameters: + * format printf style format string + * args arguments to match the format + * + * Global variables used: + * file_being_read Name of the makefile being read + * line_number Line that is being read + * report_pwd Indicates whether current path should be shown + * temp_file_name When reading tempfile we report that name + */ +/*VARARGS*/ +void +fatal_reader(char * pattern, ...) +{ + va_list args; + char message[1000]; + + va_start(args, pattern); + if (file_being_read != NULL) { + WCSTOMBS(mbs_buffer, file_being_read); + if (line_number != 0) { + (void) sprintf(message, + catgets(catd, 1, 112, "%s, line %d: %s"), + mbs_buffer, + line_number, + pattern); + } else { + (void) sprintf(message, + "%s: %s", + mbs_buffer, + pattern); + } + pattern = message; + } + + (void) fflush(stdout); +#ifdef DISTRIBUTED + (void) fprintf(stderr, catgets(catd, 1, 113, "dmake: Fatal error in reader: ")); +#else + (void) fprintf(stderr, catgets(catd, 1, 238, "make: Fatal error in reader: ")); +#endif + (void) vfprintf(stderr, pattern, args); + (void) fprintf(stderr, "\n"); + va_end(args); + + if (temp_file_name != NULL) { + (void) fprintf(stderr, +#ifdef DISTRIBUTED + catgets(catd, 1, 114, "dmake: Temp-file %s not removed\n"), +#else + catgets(catd, 1, 239, "make: Temp-file %s not removed\n"), +#endif + temp_file_name->string_mb); + temp_file_name = NULL; + } + + if (report_pwd) { + (void) fprintf(stderr, + catgets(catd, 1, 115, "Current working directory %s\n"), + get_current_path()); + } + (void) fflush(stderr); +#if defined(SUN5_0) || defined(HP_UX) + exit_status = 1; +#endif + exit(1); +} + diff --git a/usr/src/make_src/Make/bin/make/common/rep.cc b/usr/src/make_src/Make/bin/make/common/rep.cc new file mode 100644 index 0000000..6ad4007 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/rep.cc @@ -0,0 +1,529 @@ +/* + * 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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)rep.cc 1.25 06/12/12 + */ + +#pragma ident "@(#)rep.cc 1.25 06/12/12" + +/* + * rep.c + * + * This file handles the .nse_depinfo file + */ + +/* + * Included files + */ +#include <mk/defs.h> +#include <mksh/misc.h> /* retmem() */ +#include <vroot/report.h> /* NSE_DEPINFO */ + +/* + * Static variables + */ +static Recursive_make recursive_list; +static Recursive_make *bpatch = &recursive_list; +static Boolean changed; + +/* + * File table of contents + */ + + +/* + * report_recursive_init() + * + * Read the .nse_depinfo file and make a list of all the + * .RECURSIVE entries. + * + * Parameters: + * + * Static variables used: + * bpatch Points to slot where next cell should be added + * + * Global variables used: + * recursive_name The Name ".RECURSIVE", compared against + */ + +void +report_recursive_init(void) +{ + char *search_dir; + char nse_depinfo[MAXPATHLEN]; + FILE *fp; + int line_size, line_index; + wchar_t *line; + wchar_t *bigger_line; + wchar_t *colon; + wchar_t *dollar; + Recursive_make rp; + + /* + * This routine can be called more than once, don't do + * anything after the first time. + */ + if (depinfo_already_read) { + return; + } else { + depinfo_already_read = true; + } + + search_dir = getenv(NOCATGETS("NSE_DEP")); + if (search_dir == NULL) { + return; + } + (void) sprintf(nse_depinfo, "%s/%s", search_dir, NSE_DEPINFO); + fp = fopen(nse_depinfo, "r"); + if (fp == NULL) { + return; + } + line_size = MAXPATHLEN; + line_index = line_size - 1; + line = ALLOC_WC(line_size); + Wstring rns(recursive_name); + wchar_t * wcb = rns.get_string(); + while (fgetws(line, line_size, fp) != NULL) { + while (wslen(line) == line_index) { + if (line[wslen(line) - 1] == '\n') { + continue; + } + bigger_line = ALLOC_WC(2 * line_size); + wscpy(bigger_line, line); + retmem(line); + line = bigger_line; + if (fgetws(&line[line_index], line_size, fp) == NULL) + continue; + line_index = 2 * line_index; + line_size = 2 * line_size; + } + + colon = (wchar_t *) wschr(line, (int) colon_char); + if (colon == NULL) { + continue; + } + dollar = (wchar_t *) wschr(line, (int) dollar_char); + line[wslen(line) - 1] = (int) nul_char; + if (IS_WEQUALN(&colon[2], wcb, + (int) recursive_name->hash.length)) { + /* + * If this entry is an old entry, ignore it + */ + MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION); + if (dollar == NULL || + !IS_WEQUALN(wcs_buffer, (dollar+1) - VER_LEN, VER_LEN)){ + continue; + } + rp = ALLOC(Recursive_make); + (void) memset((char *) rp, 0, sizeof (Recursive_make_rec)); + /* + * set conditional_macro_string if string is present + */ + rp->oldline = (wchar_t *) wsdup(line); + if ( dollar != NULL ){ + rp->cond_macrostring = + (wchar_t *) wsdup(dollar - VER_LEN + 1); + } + /* + * get target name into recursive struct + */ + *colon = (int) nul_char; + rp->target = (wchar_t *) wsdup(line); + *bpatch = rp; + bpatch = &rp->next; + } + } + (void) fclose(fp); +} + +/* + * report_recursive_dep(target, line) + * + * Report a target as recursive. + * + * Parameters: + * line Dependency line reported + * + * Static variables used: + * bpatch Points to slot where next cell should be added + * changed Written if report set changed + */ +void +report_recursive_dep(Name target, wchar_t *line) +{ + Recursive_make rp; + wchar_t rec_buf[STRING_BUFFER_LENGTH]; + String_rec string; + + INIT_STRING_FROM_STACK(string, rec_buf); + cond_macros_into_string(target, &string); + /* + * find an applicable recursive entry, if there isn't one, create it + */ + rp = find_recursive_target(target); + if (rp == NULL) { + rp = ALLOC(Recursive_make); + (void) memset((char *) rp, 0, sizeof (Recursive_make_rec)); + wchar_t * wcb = get_wstring(target->string_mb); // XXX Tolik: needs retmem + rp->target = wcb; + rp->newline = (wchar_t *) wsdup(line); + rp->cond_macrostring = (wchar_t *) wsdup(rec_buf); + *bpatch = rp; + bpatch = &rp->next; + changed = true; + } else { + if ((rp->oldline != NULL) && !IS_WEQUAL(rp->oldline, line)) { + rp->newline = (wchar_t *) wsdup(line); + changed = true; + } + rp->removed = false; + } +} + +/* + * find_recursive_target(target) + * + * Search the list for a given target. + * + * Return value: + * The target cell + * + * Parameters: + * target The target we need + * top_level_target more info used to determinde the + * target we need + * + * Static variables used: + * recursive_list The list of targets + */ +Recursive_make +find_recursive_target(Name target) +{ + Recursive_make rp; + String_rec string; + wchar_t rec_buf[STRING_BUFFER_LENGTH]; + + INIT_STRING_FROM_STACK(string, rec_buf); + cond_macros_into_string(target, &string); + + Wstring tstr(target); + wchar_t * wcb = tstr.get_string(); + for (rp = recursive_list; rp != NULL; rp = rp->next) { + /* + * If this entry has already been removed, ignore it. + */ + if (rp->removed) + continue; + /* + * If this target, and the target on the list are the same + * and if one of them contains conditional macro info, while + * the other doesn't, remove this entry from the list of + * recursive entries. This can only happen if the Makefile + * has changed to no longer contain conditional macros. + */ + if (IS_WEQUAL(rp->target, wcb)) { + if (rp->cond_macrostring[VER_LEN] == '\0' && + string.buffer.start[VER_LEN] != '\0'){ + rp->removed = true; + continue; + } else if (rp->cond_macrostring[VER_LEN] != '\0' && + string.buffer.start[VER_LEN] == '\0'){ + rp->removed = true; + continue; + } + } + /* + * If this is not a VERS2 entry, only need to match + * the target name. toptarg information from VERS1 entries + * are ignored. + */ + MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION); + if (IS_WEQUALN(wcs_buffer, string.buffer.start, VER_LEN)) { + if (IS_WEQUAL(rp->cond_macrostring, + string.buffer.start) && + IS_WEQUAL(rp->target, wcb)) { + return rp; + } + } else { + if (IS_WEQUAL(rp->target, wcb)) { + return rp; + } + } + } + return NULL; +} + +/* + * remove_recursive_dep(target, top_level_target) + * + * Mark a target as no longer recursive. + * + * Parameters: + * target The target we want to remove + * top_level_target target we want to remove must be built from + * the same top level target + * + * Static variables used: + * changed Written if report set changed + */ +void +remove_recursive_dep(Name target) +{ + Recursive_make rp; + + rp = find_recursive_target(target); + + if ( rp != NULL ) { + rp->removed = true; + changed = true; + if(rp->target) { + retmem(rp->target); + rp->target = NULL; + } + if(rp->newline) { + retmem(rp->newline); + rp->newline = NULL; + } + if(rp->oldline) { + retmem(rp->oldline); + rp->oldline = NULL; + } + if(rp->cond_macrostring) { + retmem(rp->cond_macrostring); + rp->cond_macrostring = NULL; + } + } +} + +#ifdef NSE +/* + * report_recursive_done() + * + * Write the .nse_depinfo file. + * + * Parameters: + * + * Static variables used: + * recursive_list The list of targets + * changed Written if report set changed + * + * Global variables used: + * recursive_name The Name ".RECURSIVE", compared against + */ +void +report_recursive_done(void) +{ + char *search_dir; + char nse_depinfo[MAXPATHLEN]; + char tmpfile[MAXPATHLEN]; + FILE *ofp; + FILE *ifp; + wchar_t *space; + wchar_t *data; + wchar_t *line; + wchar_t *bigger_line; + int line_size, line_index; + int lock_err; + Recursive_make rp; + + if (changed == false) { + return; + } + + search_dir = getenv(NOCATGETS("NSE_DEP")); + if (search_dir == NULL) { + return; + } + (void) sprintf(nse_depinfo, "%s/%s", search_dir, NSE_DEPINFO); + (void) sprintf(tmpfile, "%s.%d", nse_depinfo, getpid()); + ofp = fopen(tmpfile, "w"); + if (ofp == NULL) { + (void) fprintf(stderr, + catgets(catd, 1, 116, "Cannot open `%s' for writing\n"), + tmpfile); + return; + } + (void) sprintf(nse_depinfo_lockfile, + "%s/%s", search_dir, NSE_DEPINFO_LOCK); + if (lock_err = file_lock(nse_depinfo, + nse_depinfo_lockfile, + (int *) &nse_depinfo_locked, 0)) { + (void) fprintf(stderr, + catgets(catd, 1, 117, "writing .RECURSIVE lines to %s\n"), + tmpfile); + (void) fprintf(stderr, + catgets(catd, 1, 118, "To recover, merge .nse_depinfo.%d with .nse_depinfo\n"), + getpid(), + catgets(catd, 1, 119, "with .nse_depinfo")); + } + + if (nse_depinfo_locked) { + ifp = fopen(nse_depinfo, "r"); + if (ifp != NULL) { + /* + * Copy all the non-.RECURSIVE lines from + * the old file to the new one. + */ + line_size = MAXPATHLEN; + line_index = line_size - 1; + line = ALLOC_WC(line_size); + while (fgetws(line, line_size, ifp) != NULL) { + while (wslen(line) == line_index) { + if (line[wslen(line) - 1] == '\n') { + continue; + } + bigger_line = ALLOC_WC(2 * line_size); + wscpy(bigger_line, line); + retmem(line); + line = bigger_line; + if (fgetws(&line[line_index], + line_size, ifp) == NULL) + continue; + line_index = 2 * line_index; + line_size = 2 * line_size; + } + + space = wschr(line, (int) space_char); + if (space != NULL && + IS_WEQUALN(&space[1], + recursive_name->string, + (int) recursive_name->hash.length)) { + continue; + } + WCSTOMBS(mbs_buffer, line); + (void) fprintf(ofp, "%s", mbs_buffer); + } + (void) fclose(ifp); + } + } + + /* + * Write out the .RECURSIVE lines. + */ + for (rp = recursive_list; rp != NULL; rp = rp->next) { + if (rp->removed) { + continue; + } + if (rp->newline != NULL) { + data = rp->newline; + } else { + data = rp->oldline; + } + if (data != NULL) { + WCSTOMBS(mbs_buffer, data); + (void) fprintf(ofp, "%s\n", mbs_buffer); + } + } + (void) fclose(ofp); + + if (nse_depinfo_locked) { + (void) rename(tmpfile, nse_depinfo); + (void) unlink(nse_depinfo_lockfile); + nse_depinfo_locked = false; + nse_depinfo_lockfile[0] = '\0'; + (void) chmod(nse_depinfo, 0666); + } +} +#endif // NSE + +/* gather_recursive_deps() + * + * Create or update list of recursive targets. + */ +void +gather_recursive_deps(void) +{ + Name_set::iterator np, e; + String_rec rec; + wchar_t rec_buf[STRING_BUFFER_LENGTH]; + register Property lines; + Boolean has_recursive; + Dependency dp; + + report_recursive_init(); + + /* Go thru all targets and dump recursive dependencies */ + for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) { + if (np->has_recursive_dependency){ + has_recursive = false; + /* + * start .RECURSIVE line with target: + */ + INIT_STRING_FROM_STACK(rec, rec_buf); + APPEND_NAME(np, &rec, FIND_LENGTH); + append_char((int) colon_char, &rec); + append_char((int) space_char, &rec); + + for (lines = get_prop(np->prop,recursive_prop); + lines != NULL; + lines = get_prop(lines->next, recursive_prop)) { + /* + * if entry is already in depinfo + * file or entry was not built, ignore it + */ + if (lines->body.recursive.in_depinfo) + continue; + if (!lines->body.recursive.has_built) + continue; + has_recursive = true; + lines->body.recursive.in_depinfo=true; + + /* + * Write the remainder of the + * .RECURSIVE line + */ + APPEND_NAME(recursive_name, &rec, + FIND_LENGTH); + append_char((int) space_char, &rec); + APPEND_NAME(lines->body.recursive.directory, + &rec, FIND_LENGTH); + append_char((int) space_char, &rec); + APPEND_NAME(lines->body.recursive.target, + &rec, FIND_LENGTH); + append_char((int) space_char, &rec); + + /* Complete list of makefiles used */ + for (dp = lines->body.recursive.makefiles; + dp != NULL; + dp = dp->next) { + APPEND_NAME(dp->name, &rec, FIND_LENGTH); + append_char((int) space_char, &rec); + } + } + /* + * dump list of conditional targets, + * and report recursive entry, if needed + */ + cond_macros_into_string(np, &rec); + if (has_recursive){ + report_recursive_dep(np, rec.buffer.start); + } + + } else if ( np->has_built ) { + remove_recursive_dep(np); + } + } +} + diff --git a/usr/src/make_src/Make/bin/make/common/state.cc b/usr/src/make_src/Make/bin/make/common/state.cc new file mode 100644 index 0000000..eff066a --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/state.cc @@ -0,0 +1,463 @@ +/* + * 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.cc 1.27 06/12/12 + */ + +#pragma ident "@(#)state.cc 1.27 06/12/12" + +/* + * 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 */ + +/* + * 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 +#ifdef NSE +write_state_file(int report_recursive, Boolean exiting) +#else +write_state_file(int, Boolean exiting) +#endif +{ + 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(NOCATGETS(".lock")) + 1); + (void) sprintf(make_state_lockfile, + NOCATGETS("%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, NOCATGETS("%s/.make.state.%d.XXXXXX"), tmpdir, getpid()); + report_pwd = true; + warning(catgets(catd, 1, 60, "Writing to %s"), buffer); + int fdes = mkstemp(buffer); + if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) { + fprintf(stderr, + catgets(catd, 1, 61, "Could not open statefile `%s': %s"), + buffer, + errmsg(errno)); + return; + } + } else { + report_pwd = true; + fatal(catgets(catd, 1, 62, "Can't lock .make.state")); + } + } + + (void) sprintf(make_state_tempfile, + NOCATGETS("%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(catgets(catd, 1, 59, "Could not open temporary statefile `%s': %s"), + make_state_tempfile, + errmsg(lock_err)); + } +#ifdef NSE + if (nse) { + (void) fchmod(fileno(fd), 0666); + } +#endif + /* + * 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(catgets(catd, 1, 63, "Giving up on writing statefile")); + } + sleep(10); + (void) sprintf(buffer, NOCATGETS("%s/.make.state.%d.XXXXXX"), tmpdir, getpid()); + int fdes = mkstemp(buffer); + if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) { + fatal(catgets(catd, 1, 64, "Could not open statefile `%s': %s"), + buffer, + errmsg(errno)); + } + warning(catgets(catd, 1, 65, "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(catgets(catd, 1, 356, "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(catgets(catd, 1, 357, "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; + } +#ifdef NSE + if (report_recursive) { + report_recursive_done(); + } +#endif +} + +/* + * 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; +} + + diff --git a/usr/src/make_src/Make/bin/make/common/svr4.make.rules.file b/usr/src/make_src/Make/bin/make/common/svr4.make.rules.file new file mode 100644 index 0000000..55c9a1d --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/svr4.make.rules.file @@ -0,0 +1,242 @@ +# +# 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 1994 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# @(#)svr4.make.rules.file 1.4 06/12/12 +# +.SUFFIXES: .o .c .c~ .y .y~ .l .l~ .s .s~ .sh .sh~ .h .h~ .f .f~ \ +.C .C~ .Y .Y~ .L .L~ + +MAKE=make +BUILD=build +AR=ar +ARFLAGS=rv +AS=as +ASFLAGS= +CC=cc +CFLAGS=-O +F77=f77 +FFLAGS=-O +GET=get +GFLAGS= +LD=ld +LDFLAGS= +LEX=lex +LFLAGS= +YACC=yacc +YFLAGS= +C++C=CC +C++FLAGS=-O + + +.c: + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) +.c~: + $(GET) $(GFLAGS) $< + $(CC) $(CFLAGS) $*.c -o $@ $(LDFLAGS) + -rm -f $*.c +.f: + $(F77) $(FFLAGS) $< -o $@ $(LDFLAGS) +.f~: + $(GET) $(GFLAGS) $< + $(F77) $(FFLAGS) $*.f -o $@ $(LDFLAGS) + -rm -f $*.f +.s: + $(AS) $(ASFLAGS) $< -o $@ $(LDFLAGS) +.s~: + $(GET) $(GFLAGS) $< + $(AS) $(ASFLAGS) $*.s -o $* $(LDFLAGS) + -rm -f $*.s +.sh: + cp $< $@; chmod 0777 $@ +.sh~: + $(GET) $(GFLAGS) $< + cp $*.sh $*; chmod 0777 $@ + -rm -f $*.sh +.C: + $(C++C) $(C++FLAGS) $< -o $@ $(LDFLAGS) +.C~: + $(GET) $(GFLAGS) $< + $(C++C) $(C++FLAGS) $*.C -o $@ $(LDFLAGS) + -rm -f $*.C + +.c.a: + $(CC) $(CFLAGS) -c $< + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.o +.c.o: + $(CC) $(CFLAGS) -c $< +.c~.a: + $(GET) $(GFLAGS) $< + $(CC) $(CFLAGS) -c $*.c + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.[co] +.c~.c: + $(GET) $(GFLAGS) $< +.c~.o: + $(GET) $(GFLAGS) $< + $(CC) $(CFLAGS) -c $*.c + -rm -f $*.c +.f.a: + $(F77) $(FFLAGS) -c $*.f + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.o +.f.o: + $(F77) $(FFLAGS) -c $*.f +.f~.a: + $(GET) $(GFLAGS) $< + $(F77) $(FFLAGS) -c $*.f + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.[fo] +.f~.f: + $(GET) $(GFLAGS) $< +.f~.o: + $(GET) $(GFLAGS) $< + $(F77) $(FFLAGS) -c $*.f + -rm -f $*.f +.h~.h: + $(GET) $(GFLAGS) $< +.l.c: + $(LEX) $(LFLAGS) $< + mv lex.yy.c $@ +.l.o: + $(LEX) $(LFLAGS) $< + $(CC) $(CFLAGS) -c lex.yy.c + -rm lex.yy.c; mv lex.yy.o $@ +.l~.c: + $(GET) $(GFLAGS) $< + $(LEX) $(LFLAGS) $*.l + mv lex.yy.c $@ + -rm -f $*.l +.l~.l: + $(GET) $(GFLAGS) $< +.l~.o: + $(GET) $(GFLAGS) $< + $(LEX) $(LFLAGS) $*.l + $(CC) $(CFLAGS) -c lex.yy.c + -rm -f lex.yy.c $*.l + mv lex.yy.o $@ +.s.a: + $(AS) $(ASFLAGS) -o $*.o $*.s + $(AR) $(ARFLAGS) $@ $*.o +.s.o: + $(AS) $(ASFLAGS) -o $@ $< +.s~.a: + $(GET) $(GFLAGS) $< + $(AS) $(ASFLAGS) -o $*.o $*.s + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.[so] +.s~.o: + $(GET) $(GFLAGS) $< + $(AS) $(ASFLAGS) -o $*.o $*.s + -rm -f $*.s +.s~.s: + $(GET) $(GFLAGS) $< +.sh~.sh: + $(GET) $(GFLAGS) $< +.y.c: + $(YACC) $(YFLAGS) $< + mv y.tab.c $@ +.y.o: + $(YACC) $(YFLAGS) $< + $(CC) $(CFLAGS) -c y.tab.c + -rm y.tab.c + mv y.tab.o $@ +.y~.c: + $(GET) $(GFLAGS) $< + $(YACC) $(YFLAGS) $*.y + mv y.tab.c $*.c + -rm -f $*.y +.y~.o: + $(GET) $(GFLAGS) $< + $(YACC) $(YFLAGS) $*.y + $(CC) $(CFLAGS) -c y.tab.c + -rm -f y.tab.c $*.y + mv y.tab.o $*.o +.y~.y : + $(GET) $(GFLAGS) $< +.C.a: + $(C++C) $(C++FLAGS) -c $< + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.o +.C.o: + $(C++C) $(C++FLAGS) -c $< +.C~.a: + $(GET) $(GFLAGS) $< + $(C++C) $(C++FLAGS) -c $*.C + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.[Co] +.C~.C: + $(GET) $(GFLAGS) $< +.C~.o: + $(GET) $(GFLAGS) $< + $(C++C) $(C++FLAGS) -c $*.C + -rm -f $*.C +.L.C: + $(LEX) $(LFLAGS) $< + mv lex.yy.c $@ +.L.o: + $(LEX) $(LFLAGS) $< + $(C++C) $(C++FLAGS) -c lex.yy.c + -rm lex.yy.c; mv lex.yy.o $@ +.L~.C: + $(GET) $(GFLAGS) $< + $(LEX) $(LFLAGS) $*.L + mv lex.yy.c $@ + -rm -f $*.L +.L~.L: + $(GET) $(GFLAGS) $< +.L~.o: + $(GET) $(GFLAGS) $< + $(LEX) $(LFLAGS) $*.L + $(C++C) $(C++FLAGS) -c lex.yy.c + -rm -f lex.yy.c $*.L + mv lex.yy.c $@ +.Y.C: + $(YACC) $(YFLAGS) $< + mv y.tab.c $@ +.Y.o: + $(YACC) $(YFLAGS) $< + $(C++C) $(C++FLAGS) -c y.tab.c + -rm y.tab.c + mv y.tab.o $@ +.Y~.C: + $(GET) $(GFLAGS) $< + $(YACC) $(YFLAGS) $*.Y + mv y.tab.c $*.C + -rm -f $*.Y +.Y~.o: + $(GET) $(GFLAGS) $< + $(YACC) $(YFLAGS) $*.Y + $(C++C) $(C++FLAGS) -c y.tab.c + -rm -f y.tab.c $*.Y + mv y.tab.o $*.o +.Y~.Y : + $(GET) $(GFLAGS) $< + +markfile.o: markfile + echo "static char _sccsid[] = \"`grep @'(#)' markfile`\";" > markfile.c + $(CC) -c markfile.c + -rm -f markfile.c + +.SCCS_GET: + $(GET) $(GFLAGS) s.$@ |