From b8593206069f19229dfb8f41233ea61d891ad6e4 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Sat, 11 Jan 1992 11:37:36 +0000 Subject: Initial revision --- default.c | 327 +++++++++++++++++ file.c | 469 ++++++++++++++++++++++++ function.c | 1193 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ rule.c | 524 ++++++++++++++++++++++++++ 4 files changed, 2513 insertions(+) create mode 100644 default.c create mode 100644 file.c create mode 100644 function.c create mode 100644 rule.c diff --git a/default.c b/default.c new file mode 100644 index 0000000..495603e --- /dev/null +++ b/default.c @@ -0,0 +1,327 @@ +/* Data base of default implicit rules for GNU Make. +Copyright (C) 1988, 1989, 1990, 1991, 1992 Free Software Foundation, Inc. +This file is part of GNU Make. + +GNU Make is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Make is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Make; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "make.h" +#include "rule.h" +#include "dep.h" +#include "file.h" +#include "commands.h" +#include "variable.h" + + +/* This is the default list of suffixes for suffix rules. + `.s' must come last, so that a `.o' file will be made from + a `.c' or `.p' or ... file rather than from a .s file. */ + +static char default_suffixes[] + = ".out .a .ln .o .c .cc .C .p .f .F .r .y .l .s .S \ +.mod .sym .def .h .info .dvi .tex .texinfo .texi .cweb .web .sh .elc .el"; + +static struct pspec default_pattern_rules[] = + { + "(%)", "%", + "$(AR) $(ARFLAGS) $@ $<", + + /* The X.out rules are only in BSD's default set because + BSD Make has no null-suffix rules, so `foo.out' and + `foo' are the same thing. */ + "%.out", "%", + "@rm -f $@ \n cp $< $@", + + 0, 0, 0 + }; + +static struct pspec default_terminal_rules[] = + { + /* RCS. */ + "%", "%,v", + "test -f $@ || $(CO) $(COFLAGS) $< $@", + "%", "RCS/%,v", + "test -f $@ || $(CO) $(COFLAGS) $< $@", + + /* SCCS. */ + "%", "s.%", + "$(GET) $(GFLAGS) $<", + "%", "SCCS/s.%", + "$(GET) $(GFLAGS) $<", + + 0, 0, 0 + }; + +static char *default_suffix_rules[] = + { + ".o", + "$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@", + ".s", + "$(LINK.s) $^ $(LOADLIBES) $(LDLIBS) -o $@", + ".S", + "$(LINK.S) $^ $(LOADLIBES) $(LDLIBS) -o $@", + ".c", + "$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@", + ".cc", + "$(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@", + ".C", + "$(LINK.C) $^ $(LOADLIBES) $(LDLIBS) -o $@", + ".f", + "$(LINK.f) $^ $(LOADLIBES) $(LDLIBS) -o $@", + ".p", + "$(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@", + ".F", + "$(LINK.F) $^ $(LOADLIBES) $(LDLIBS) -o $@", + ".r", + "$(LINK.r) $^ $(LOADLIBES) $(LDLIBS) -o $@", + ".mod", + "$(COMPILE.mod) -o $@ -e $@ $^", + + ".def.sym", + "$(COMPILE.def) -o $@ $<", + + ".sh", + "cat $< >$@ \n chmod a+x $@", + + ".s.o", +#if !defined(M_XENIX) || defined(__GNUC__) + "$(COMPILE.s) -o $@ $<", +#else /* Xenix. */ + "$(COMPILE.s) -o$@ $<", +#endif /* Not Xenix. */ + ".S.o", +#if !defined(M_XENIX) || defined(__GNUC__) + "$(COMPILE.S) -o $@ $<", +#else /* Xenix. */ + "$(COMPILE.S) -o$@ $<", +#endif /* Not Xenix. */ + ".c.o", + "$(COMPILE.c) $< $(OUTPUT_OPTION)", + ".cc.o", + "$(COMPILE.cc) $< $(OUTPUT_OPTION)", + ".C.o", + "$(COMPILE.C) $< $(OUTPUT_OPTION)", + ".f.o", + "$(COMPILE.f) $< $(OUTPUT_OPTION)", + ".p.o", + "$(COMPILE.p) $< $(OUTPUT_OPTION)", + ".F.o", + "$(COMPILE.F) $< $(OUTPUT_OPTION)", + ".r.o", + "$(COMPILE.r) $< $(OUTPUT_OPTION)", + ".mod.o", + "$(COMPILE.mod) -o $@ $<", + + ".c.ln", + "$(LINT.c) -C$* $<", + ".y.ln", + "$(YACC.y) $< \n $(LINT.c) -C$* y.tab.c \n $(RM) y.tab.c", + ".l.ln", + "@$(RM) $*.c \n $(LEX.l) $< > $*.c \n\ +$(LINT.c) -i $*.c -o $@ \n $(RM) $*.c", + + ".y.c", + "$(YACC.y) $< \n mv -f y.tab.c $@", + ".l.c", + "@$(RM) $@ \n $(LEX.l) $< > $@", + + ".F.f", + "$(PREPROCESS.F) $< $(OUTPUT_OPTION)", + ".r.f", + "$(PREPROCESS.r) $< $(OUTPUT_OPTION)", + + /* This might actually make lex.yy.c if there's no %R% + directive in $*.l, but in that case why were you + trying to make $*.r anyway? */ + ".l.r", + "$(LEX.l) $< > $@ \n mv -f lex.yy.r $@", + + ".S.s", + "$(PREPROCESS.S) $< > $@", + + ".texinfo.info", + "$(MAKEINFO) $<", + + ".texi.info", + "$(MAKEINFO) $<", + + ".tex.dvi", + "$(TEX) $<", + + ".texinfo.dvi", + "$(TEXI2DVI) $<", + + ".texi.dvi", + "$(TEXI2DVI) $<", + + ".cweb.c", + "$(CTANGLE) $<", + + ".web.p", + "$(TANGLE) $<", + + ".cweb.tex", + "$(CWEAVE) $<", + + ".web.tex", + "$(WEAVE) $<", + + 0} +; + +static char *default_variables[] = + { + "AR", "ar", + "ARFLAGS", "rv", + "AS", "as", + "CC", "cc", + "C++", "g++", + "CO", "co", + "CPP", "$(CC) -E", +#ifdef _IBMR2 + "FC", "xlf", +#else + "FC", "f77", +#endif + /* System V uses these, so explicit rules using them should work. + However, there is no way to make implicit rules use them and FC. */ + "F77", "$(FC)", + "F77FLAGS", "$(FFLAGS)", +#ifdef USG + "GET", "get", +#else + "GET", "/usr/sccs/get", +#endif + "LD", "ld", + "LEX", "lex", + "LINT", "lint", + "M2C", "m2c", +#ifdef pyr + "PC", "pascal", +#else + "PC", "pc", +#endif + "YACC", "yacc", /* Or "bison -y" */ + "MAKEINFO", "makeinfo", + "TEX", "tex", + "TEXI2DVI", "texi2dvi", + "WEAVE", "weave", + "CWEAVE", "cweave", + "TANGLE", "tangle", + "CTANGLE", "ctangle", + + "RM", "rm -f", + + "LINK.o", "$(CC) $(LDFLAGS) $(TARGET_ARCH)", + "COMPILE.c", "$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c", + "LINK.c", "$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)", + "COMPILE.cc", "$(C++) $(C++FLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c", + "COMPILE.C", "$(COMPILE.cc)", + "LINK.cc", "$(C++) $(C++FLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)", + "LINK.C", "$(LINK.cc)", + "YACC.y", "$(YACC) $(YFLAGS)", + "LEX.l", "$(LEX) $(LFLAGS) -t", + "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)", + "COMPILE.r", "$(FC) $(FFLAGS) $(RFLAGS) $(TARGET_ARCH) -c", + "LINK.r", "$(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) $(TARGET_ARCH)", + "COMPILE.def", "$(M2C) $(M2FLAGS) $(DEFFLAGS) $(TARGET_ARCH)", + "COMPILE.mod", "$(M2C) $(M2FLAGS) $(MODFLAGS) $(TARGET_ARCH)", + "COMPILE.p", "$(PC) $(PFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c", + "LINK.p", "$(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)", + "LINK.s", "$(CC) $(ASFLAGS) $(LDFLAGS) $(TARGET_MACH)", + "COMPILE.s", "$(AS) $(ASFLAGS) $(TARGET_MACH)", + "LINK.S", "$(CC) $(ASFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_MACH)", + "COMPILE.S", "$(CC) $(ASFLAGS) $(CPPFLAGS) $(TARGET_MACH) -c", +#if !defined(M_XENIX) || defined(__GNUC__) + "PREPROCESS.S", "$(CC) -E $(CPPFLAGS)", +#else /* Xenix. */ + "PREPROCESS.S", "$(CC) -EP $(CPPFLAGS)", +#endif /* Not Xenix. */ + "PREPROCESS.F", "$(FC) $(FFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -F", + "PREPROCESS.r", "$(FC) $(FFLAGS) $(RFLAGS) $(TARGET_ARCH) -F", + "LINT.c", "$(LINT) $(LINTFLAGS) $(CPPFLAGS) $(TARGET_ARCH)", + +#ifndef NO_MINUS_C_MINUS_O +#if !defined(M_XENIX) || defined(__GNUC__) + "OUTPUT_OPTION", "-o $@", +#else /* Xenix. */ + "OUTPUT_OPTION", "-Fo$@", +#endif /* Not Xenix. */ +#endif + + 0, 0 + }; + +/* Set up the default .SUFFIXES list. */ + +void +set_default_suffixes () +{ + suffix_file = enter_file (".SUFFIXES"); + + if (no_builtin_rules_flag) + (void) define_variable ("SUFFIXES", 8, "", o_default, 0); + else + { + char *p = default_suffixes; + suffix_file->deps = (struct dep *) + multi_glob (parse_file_seq (&p, '\0', sizeof (struct dep), 1), + sizeof (struct dep)); + (void) define_variable ("SUFFIXES", 8, default_suffixes, o_default, 0); + } +} + +/* Install the default pattern rules and enter + the default suffix rules as file rules. */ + +void +install_default_implicit_rules () +{ + register struct pspec *p; + register char **s; + + if (no_builtin_rules_flag) + return; + + for (p = default_pattern_rules; p->target != 0; ++p) + install_pattern_rule (p, 0); + + for (p = default_terminal_rules; p->target != 0; ++p) + install_pattern_rule (p, 1); + + for (s = default_suffix_rules; *s != 0; s += 2) + { + register struct file *f = enter_file (s[0]); + /* Don't clobber cmds given in a makefile if there were any. */ + if (f->cmds == 0) + { + f->cmds = (struct commands *) xmalloc (sizeof (struct commands)); + f->cmds->filename = 0; + f->cmds->commands = s[1]; + f->cmds->command_lines = 0; + } + } +} + +void +define_default_variables () +{ + register char **s; + + for (s = default_variables; *s != 0; s += 2) + (void) define_variable (s[0], strlen (s[0]), s[1], o_default, 1); +} diff --git a/file.c b/file.c new file mode 100644 index 0000000..95c836e --- /dev/null +++ b/file.c @@ -0,0 +1,469 @@ +/* Copyright (C) 1988, 1989, 1990, 1991 Free Software Foundation, Inc. +This file is part of GNU Make. + +GNU Make is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Make is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Make; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "make.h" +#include "commands.h" +#include "dep.h" +#include "file.h" +#include "variable.h" +#include + + +extern int errno; + + +/* Hash table of files the makefile knows how to make. */ + +#ifndef FILE_BUCKETS +#define FILE_BUCKETS 1007 +#endif +static struct file *files[FILE_BUCKETS]; + +/* Number of files with the `intermediate' flag set. */ + +unsigned int num_intermediates = 0; + + +/* Access the hash table of all file records. + lookup_file given a name, return the struct file * for that name, + or nil if there is none. + enter_file similar, but create one if there is none. */ + +struct file * +lookup_file (name) + char *name; +{ + register struct file *f; + register char *n; + register unsigned int hashval; + + if (*name == '\0') + abort (); + + while (name[0] == '.' && name[1] == '/' && name[2] != '\0') + name += 2; + + hashval = 0; + for (n = name; *n != '\0'; ++n) + HASH (hashval, *n); + hashval %= FILE_BUCKETS; + + for (f = files[hashval]; f != 0; f = f->next) + if (streq (f->name, name)) + return f; + return 0; +} + +struct file * +enter_file (name) + char *name; +{ + register struct file *f, *new; + register char *n; + register unsigned int hashval; + + if (*name == '\0') + abort (); + + hashval = 0; + for (n = name; *n != '\0'; ++n) + HASH (hashval, *n); + hashval %= FILE_BUCKETS; + + for (f = files[hashval]; f != 0; f = f->next) + if (streq (f->name, name)) + break; + + if (f != 0 && !f->double_colon) + return f; + + new = (struct file *) xmalloc (sizeof (struct file)); + bzero ((char *) new, sizeof (struct file)); + new->name = name; + new->update_status = -1; + + if (f == 0) + { + /* This is a completely new file. */ + new->next = files[hashval]; + files[hashval] = new; + } + else + { + /* There is already a double-colon entry for this file. */ + while (f->prev != 0) + f = f->prev; + f->prev = new; + } + + return new; +} + +/* Rename FILE to NAME. This is not as simple as resetting + the `name' member, since it must be put in a new hash bucket, + and possibly merged with an existing file called NAME. */ + +void +rename_file (file, name) + register struct file *file; + char *name; +{ + char *oldname = file->name; + register unsigned int oldhash, newhash; + register char *n; + register struct file *f; + struct file *oldfile; + + /* Find the hash values of the old and new names. */ + + oldhash = 0; + for (n = oldname; *n != '\0'; ++n) + HASH (oldhash, *n); + oldhash %= FILE_BUCKETS; + + newhash = 0; + for (n = name; *n != '\0'; ++n) + HASH (newhash, *n); + newhash %= FILE_BUCKETS; + + /* Look for an existing file under the new name. */ + + for (oldfile = files[newhash]; oldfile != 0; oldfile = oldfile->next) + if (streq (oldfile->name, name)) + break; + + if (newhash != oldhash || oldfile != 0) + { + /* Remove FILE from its hash bucket. */ + + struct file *lastf = 0; + + for (f = files[oldhash]; f != file; f = f->next) + lastf = f; + + if (lastf == 0) + files[oldhash] = f->next; + else + lastf->next = f->next; + } + + /* Give FILE its new name. */ + + for (f = file; f != 0; f = f->prev) + f->name = name; + + if (oldfile == 0) + { + /* There is no existing file with the new name. */ + + if (newhash != oldhash) + { + /* Put FILE in its new hash bucket. */ + file->next = files[newhash]; + files[newhash] = file; + } + } + else + { + /* There is an existing file with the new name. + We must merge FILE into the existing file. */ + + register struct dep *d; + + if (file->cmds != 0) + { + if (oldfile->cmds == 0) + oldfile->cmds = file->cmds; + else if (file->cmds != oldfile->cmds) + { + /* We have two sets of commands. We will go with the + one given in the rule explicitly mentioning this name, + but give a message to let the user know what's going on. */ + makefile_error (file->cmds->filename, file->cmds->lineno, + "Commands were specified for file `%s' at %s:%u,", + oldname, oldfile->cmds->filename, oldfile->cmds->lineno); + makefile_error (file->cmds->filename, file->cmds->lineno, + "but `%s' is now considered the same file \ +as `%s'.", + oldname, name); + makefile_error (file->cmds->filename, file->cmds->lineno, + "Commands for `%s' will be ignored \ +in favor of those for `%s'.", + name, oldname); + } + } + + /* Merge the dependencies of the two files. */ + + d = oldfile->deps; + if (d == 0) + oldfile->deps = file->deps; + else + { + while (d->next != 0) + d = d->next; + d->next = file->deps; + uniquize_deps (oldfile->deps); + } + + merge_variable_set_lists (&oldfile->variables, file->variables); + + if (oldfile->double_colon && !file->double_colon) + fatal ("can't rename single-colon `%s' to double-colon `%s'", + oldname, name); + if (!oldfile->double_colon && file->double_colon) + fatal ("can't rename double-colon `%s' to single-colon `%s'", + oldname, name); + + if (file->last_mtime > oldfile->last_mtime) + /* %%% Kludge so -W wins on a file that gets vpathized. */ + oldfile->last_mtime = file->last_mtime; + +#define MERGE(field) oldfile->field |= file->field + MERGE (precious); + MERGE (tried_implicit); + MERGE (updating); + MERGE (updated); + MERGE (is_target); + MERGE (cmd_target); + MERGE (phony); +#undef MERGE + + file->renamed = oldfile; + } +} + +/* Remove all nonprecious intermediate files. + If SIG is nonzero, this was caused by a fatal signal, + meaning that a different message will be printed, and + the message will go to stderr rather than stdout. */ + +void +remove_intermediates (sig) + int sig; +{ + register int i; + register struct file *f; + char doneany; + + if (!sig && just_print_flag) + return; + + doneany = 0; + for (i = 0; i < FILE_BUCKETS; ++i) + for (f = files[i]; f != 0; f = f->next) + if (f->intermediate && (f->dontcare || !f->precious)) + { + int status; + if (just_print_flag) + status = 0; + else + { + status = unlink (f->name); + if (status < 0 && errno == ENOENT) + continue; + } + if (!f->dontcare) + { + if (sig) + error ("*** Deleting file `%s'", f->name); + else if (!silent_flag && !just_print_flag) + { + if (!doneany) + { + fputs ("rm ", stdout); + doneany = 1; + } + putchar (' '); + fputs (f->name, stdout); + fflush (stdout); + } + if (status < 0) + perror_with_name ("unlink: ", f->name); + } + } + + if (doneany && !sig) + { + putchar ('\n'); + fflush (stdout); + } +} + +/* For each dependency of each file, make the `struct dep' point + at the appropriate `struct file' (which may have to be created). + + Also mark the files depended on by .PRECIOUS and .PHONY. */ + +void +snap_deps () +{ + register struct file *f, *f2; + register struct dep *d; + register int i; + + /* Enter each dependency name as a file. */ + for (i = 0; i < FILE_BUCKETS; ++i) + for (f = files[i]; f != 0; f = f->next) + for (f2 = f; f2 != 0; f2 = f2->prev) + for (d = f2->deps; d != 0; d = d->next) + if (d->name != 0) + { + d->file = lookup_file (d->name); + if (d->file == 0) + d->file = enter_file (d->name); + else + free (d->name); + d->name = 0; + } + + for (f = lookup_file (".PRECIOUS"); f != 0; f = f->prev) + for (d = f->deps; d != 0; d = d->next) + for (f2 = d->file; f2 != 0; f2 = f2->prev) + f2->precious = 1; + + for (f = lookup_file (".PHONY"); f != 0; f = f->prev) + for (d = f->deps; d != 0; d = d->next) + for (f2 = d->file; f2 != 0; f2 = f2->prev) + { + /* Mark this file as phony and nonexistent. */ + f2->phony = 1; + f2->last_mtime = (time_t) -1; + } +} + +/* Print the data base of files. */ + +void +print_file_data_base () +{ + register unsigned int i, nfiles, per_bucket; + register struct file *file; + register struct dep *d; + + puts ("\n# Files"); + + per_bucket = nfiles = 0; + for (i = 0; i < FILE_BUCKETS; ++i) + { + register unsigned int this_bucket = 0; + + for (file = files[i]; file != 0; file = file->next) + { + register struct file *f; + + ++this_bucket; + + for (f = file; f != 0; f = f->prev) + { + putchar ('\n'); + if (!f->is_target) + puts ("# Not a target:"); + printf ("%s:%s", f->name, f->double_colon ? ":" : ""); + + for (d = f->deps; d != 0; d = d->next) + printf (" %s", dep_name (d)); + putchar ('\n'); + + if (f->precious) + puts ("# Precious file (dependency of .PRECIOUS)."); + if (f->phony) + puts ("# Phony target (dependency of .PHONY)."); + if (f->cmd_target) + puts ("# Command-line target."); + if (f->dontcare) + puts ("# A default or MAKEFILES makefile."); + printf ("# Implicit rule search has%s been done.\n", + f->tried_implicit ? "" : " not"); + if (f->stem != 0) + printf ("# Implicit/static pattern stem: `%s'\n", f->stem); + if (f->intermediate) + puts ("# File is an intermediate dependency."); + if (f->also_make != 0) + { + fputs ("# Also makes:", stdout); + for (d = f->also_make; d != 0; d = d->next) + printf (" %s", dep_name (d)); + putchar ('\n'); + } + if (f->last_mtime == (time_t) 0) + puts ("# Modification time never checked."); + else if (f->last_mtime == (time_t) -1) + puts ("# File does not exist."); + else + printf ("# Last modified %.24s (%ld)\n", + ctime (&f->last_mtime), (long int) f->last_mtime); + printf ("# File has%s been updated.\n", + f->updated ? "" : " not"); + switch (f->command_state) + { + case cs_running: + puts ("# Commands currently running (?!)."); + break; + case cs_deps_running: + puts ("# Dependencies currently being made (?!)."); + break; + case cs_not_started: + case cs_finished: + switch (f->update_status) + { + case -1: + break; + case 0: + puts ("# Successfully updated."); + break; + case 1: + puts ("# Failed to be updated."); + break; + default: + puts ("# Invalid value in `update_status' member!"); + fflush (stdout); + fflush (stderr); + abort (); + } + break; + default: + puts ("# Invalid value in `command_state' member!"); + fflush (stdout); + fflush (stderr); + abort (); + } + + if (f->variables != 0) + print_file_variables (file); + + if (f->cmds != 0) + print_commands (f->cmds); + } + } + + nfiles += this_bucket; + if (this_bucket > per_bucket) + per_bucket = this_bucket; + } + + if (nfiles == 0) + puts ("\n# No files."); + else + { + printf ("\n# %u files in %u hash buckets.\n", nfiles, FILE_BUCKETS); +#ifndef NO_FLOAT + printf ("# average %.1f files per bucket, max %u files in one bucket.\n", + ((double) FILE_BUCKETS) / ((double) nfiles) * 100.0, per_bucket); +#endif + } +} diff --git a/function.c b/function.c new file mode 100644 index 0000000..1bc66e6 --- /dev/null +++ b/function.c @@ -0,0 +1,1193 @@ +/* Variable function expansion for GNU Make. +Copyright (C) 1988, 1989, 1991 Free Software Foundation, Inc. +This file is part of GNU Make. + +GNU Make is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Make is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Make; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "make.h" +#include "variable.h" +#include "dep.h" +#include "commands.h" +#include "job.h" +#include + +extern int errno; + +static char *string_glob (); + +/* Store into VARIABLE_BUFFER at O the result of scanning TEXT and replacing + each occurrence of SUBST with REPLACE. TEXT is null-terminated. SLEN is + the length of SUBST and RLEN is the length of REPLACE. If BY_WORD is + nonzero, substitutions are done only on matches which are complete + whitespace-delimited words. If SUFFIX_ONLY is nonzero, substitutions are + done only at the ends of whitespace-delimited words. */ + +char * +subst_expand (o, text, subst, replace, slen, rlen, by_word, suffix_only) + char *o; + char *text; + char *subst, *replace; + unsigned int slen, rlen; + int by_word, suffix_only; +{ + register char *t = text; + register char *p; + + if (slen == 0 && !by_word && !suffix_only) + { + /* The first occurrence of "" in any string is its end. */ + o = variable_buffer_output (o, t, strlen (t)); + if (rlen > 0) + o = variable_buffer_output (o, replace, rlen); + return o; + } + + while ((p = sindex (t, 0, subst, slen)) != 0) + { + /* Output everything before this occurrence of the string to replace. */ + if (p > t) + o = variable_buffer_output (o, t, p - t); + + /* If we're substituting only by fully matched words, + or only at the ends of words, check that this case qualifies. */ + if ((by_word + && ((p > t && !isblank (p[-1])) + || (p[slen] != '\0' && !isblank (p[slen])))) + || (suffix_only + && (p[slen] != '\0' && !isblank (p[slen])))) + /* Struck out. Output the rest of the string that is + no longer to be replaced. */ + o = variable_buffer_output (o, subst, slen); + else if (rlen > 0) + /* Output the replacement string. */ + o = variable_buffer_output (o, replace, rlen); + + /* Advance T past the string to be replaced. */ + t = p + slen; + } + + /* Output everything left on the end. */ + if (*t != '\0') + o = variable_buffer_output (o, t, strlen (t)); + + return o; +} + + +/* Store into VARIABLE_BUFFER at O the result of scanning TEXT + and replacing strings matching PATTERN with REPLACE. + If PATTERN_PERCENT is not nil, PATTERN has already been + run through find_percent, and PATTERN_PERCENT is the result. + If REPLACE_PERCENT is not nil, REPLACE has already been + run through find_percent, and REPLACE_PERCENT is the result. */ + +char * +patsubst_expand (o, text, pattern, replace, pattern_percent, replace_percent) + char *o; + char *text; + register char *pattern, *replace; + register char *pattern_percent, *replace_percent; +{ + register int pattern_prepercent_len, pattern_postpercent_len; + register int replace_prepercent_len, replace_postpercent_len; + register char *t; + unsigned int len; + int doneany = 0; + + /* We call find_percent on REPLACE before checking PATTERN so that REPLACE + will be collapsed before we call subst_expand if PATTERN has no %. */ + if (replace_percent == 0) + replace_percent = find_percent (replace); + if (replace_percent != 0) + { + /* Record the length of REPLACE before and after the % so + we don't have to compute these lengths more than once. */ + replace_prepercent_len = replace_percent - replace; + replace_postpercent_len = strlen (replace_percent + 1); + } + else + /* We store the length of the replacement + so we only need to compute it once. */ + replace_prepercent_len = strlen (replace); + + if (pattern_percent == 0) + pattern_percent = find_percent (pattern); + if (pattern_percent == 0) + /* With no % in the pattern, this is just a simple substitution. */ + return subst_expand (o, text, pattern, replace, + strlen (pattern), strlen (replace), 1, 0); + + /* Record the length of PATTERN before and after the % + so we don't have to compute it more than once. */ + pattern_prepercent_len = pattern_percent - pattern; + pattern_postpercent_len = strlen (pattern_percent + 1); + + while (t = find_next_token (&text, &len)) + { + int fail = 0; + + /* Is it big enough to match? */ + if (len < pattern_prepercent_len + pattern_postpercent_len) + fail = 1; + + /* Does the prefix match? */ + if (!fail && pattern_prepercent_len > 0 + && (*t != *pattern + || t[pattern_prepercent_len - 1] != pattern_percent[-1] + || strncmp (t + 1, pattern + 1, pattern_prepercent_len - 1))) + fail = 1; + + /* Does the suffix match? */ + if (!fail && pattern_postpercent_len > 0 + && (t[len - 1] != pattern_percent[pattern_postpercent_len] + || t[len - pattern_postpercent_len] != pattern_percent[1] + || strncmp (&t[len - pattern_postpercent_len], + &pattern_percent[1], pattern_postpercent_len - 1))) + fail = 1; + + if (fail) + /* It didn't match. Output the string. */ + o = variable_buffer_output (o, t, len); + else + { + /* It matched. Output the replacement. */ + + /* Output the part of the replacement before the %. */ + o = variable_buffer_output (o, replace, replace_prepercent_len); + + if (replace_percent != 0) + { + /* Output the part of the matched string that + matched the % in the pattern. */ + o = variable_buffer_output (o, t + pattern_prepercent_len, + len - (pattern_prepercent_len + + pattern_postpercent_len)); + /* Output the part of the replacement after the %. */ + o = variable_buffer_output (o, replace_percent + 1, + replace_postpercent_len); + } + } + + /* Output a space, but not if the replacement is "". */ + if (fail || replace_prepercent_len > 0 + || (replace_percent != 0 && len + replace_postpercent_len > 0)) + { + o = variable_buffer_output (o, " ", 1); + doneany = 1; + } + } + if (doneany) + /* Kill the last space. */ + --o; + + return o; +} + +/* Handle variable-expansion-time functions such as $(dir foo/bar) ==> foo/ */ + +/* These enumeration constants distinguish the + various expansion-time built-in functions. */ + +enum function + { + function_subst, + function_addsuffix, + function_addprefix, + function_dir, + function_notdir, + function_suffix, + function_basename, + function_wildcard, + function_firstword, + function_word, + function_words, + function_findstring, + function_strip, + function_join, + function_patsubst, + function_filter, + function_filter_out, + function_foreach, + function_sort, + function_origin, + function_shell, + function_invalid + }; + +/* Greater than the length of any function name. */ +#define MAXFUNCTIONLEN 11 + +/* The function names and lengths of names, for looking them up. */ + +static struct + { + char *name; + unsigned int len; + enum function function; + } function_table[] = + { + { "subst", 5, function_subst }, + { "addsuffix", 9, function_addsuffix }, + { "addprefix", 9, function_addprefix }, + { "dir", 3, function_dir }, + { "notdir", 6, function_notdir }, + { "suffix", 6, function_suffix }, + { "basename", 8, function_basename }, + { "wildcard", 8, function_wildcard }, + { "firstword", 9, function_firstword }, + { "word", 4, function_word }, + { "words", 5, function_words }, + { "findstring", 10, function_findstring }, + { "strip", 5, function_strip }, + { "join", 4, function_join }, + { "patsubst", 8, function_patsubst }, + { "filter", 6, function_filter }, + { "filter-out", 10, function_filter_out }, + { "foreach", 7, function_foreach }, + { "sort", 4, function_sort }, + { "origin", 6, function_origin }, + { "shell", 5, function_shell }, + { 0, 0, function_invalid } + }; + +/* Return 1 if PATTERN matches WORD, 0 if not. */ + +int +pattern_matches (pattern, percent, word) + register char *pattern, *percent, *word; +{ + unsigned int len; + + if (percent == 0) + { + unsigned int len = strlen (pattern) + 1; + char *new = (char *) alloca (len); + bcopy (pattern, new, len); + pattern = new; + percent = find_percent (pattern); + if (percent == 0) + return streq (pattern, word); + } + + len = strlen (percent + 1); + + if (strlen (word) < (percent - pattern) + len + || strncmp (pattern, word, percent - pattern)) + return 0; + + return !strcmp (percent + 1, word + (strlen (word) - len)); +} + +int shell_function_pid = 0, shell_function_completed; + +/* Perform the function specified by FUNCTION on the text at TEXT. + END is points to the end of the argument text (exclusive). + The output is written into VARIABLE_BUFFER starting at O. */ + +/* Note this absorbs a semicolon and is safe to use in conditionals. */ +#define BADARGS(func) \ + if (reading_filename != 0) \ + makefile_fatal (reading_filename, *reading_lineno_ptr, \ + "insufficient arguments to function `%s'", \ + func); \ + else \ + fatal ("insufficient arguments to function `%s'", func) + +static char * +expand_function (o, function, text, end) + char *o; + enum function function; + char *text; + char *end; +{ + char *p, *p2, *p3; + unsigned int i, len; + int doneany = 0; + int count; + char endparen = *end, startparen = *end == ')' ? '(' : '{'; + + switch (function) + { + default: + abort (); + break; + + case function_shell: + { + extern int fork (); + extern int pipe (); + char **argv; + char *error_prefix; + int pipedes[2]; + int pid; + + /* Expand the command line. */ + text = expand_argument (text, end); + + /* Construct the argument list. */ + argv = construct_command_argv (text, (char *) NULL, (struct file *) 0); + if (argv == 0) + break; + + /* For error messages. */ + if (reading_filename != 0) + { + error_prefix = (char *) alloca (strlen (reading_filename) + 100); + sprintf (error_prefix, + "%s:%u: ", reading_filename, *reading_lineno_ptr); + } + else + error_prefix = ""; + + if (pipe (pipedes) < 0) + { + perror_with_name (error_prefix, "pipe"); + break; + } + + push_signals_blocked_p (1); + + pid = fork (); + if (pid < 0) + perror_with_name (error_prefix, "fork"); + else if (pid == 0) + child_execute_job (0, pipedes[1], argv, environ); + else + { + /* We are the parent. Set up and read from the pipe. */ + char *buffer = (char *) xmalloc (201); + unsigned int maxlen = 200; + int cc; + + /* Record the PID for child_handler. */ + shell_function_pid = pid; + shell_function_completed = 0; + + /* Close the write side of the pipe. */ + (void) close (pipedes[1]); + + /* Read from the pipe until it gets EOF. */ + i = 0; + do + { + if (i == maxlen) + { + maxlen += 512; + buffer = (char *) xrealloc (buffer, maxlen + 1); + } + + errno = 0; + cc = read (pipedes[0], &buffer[i], maxlen - i); + if (cc > 0) + i += cc; + } +#ifdef EINTR + while (cc > 0 || errno == EINTR); +#else + while (cc > 0); +#endif + + /* Close the read side of the pipe. */ + (void) close (pipedes[0]); + + /* Loop until child_handler sets shell_function_completed + to the status of our child shell. */ + while (shell_function_completed == 0) + wait_for_children (1, 0); + + shell_function_pid = 0; + + /* The child_handler function will set shell_function_completed + to 1 when the child dies normally, or to -1 if it + dies with status 127, which is most likely an exec fail. */ + + if (shell_function_completed == -1) + { + /* This most likely means that the execvp failed, + so we should just write out the error message + that came in over the pipe from the child. */ + fputs (buffer, stderr); + fflush (stderr); + } + else + { + /* The child finished normally. Replace all + newlines in its output with spaces, and put + that in the variable output buffer. */ + if (i > 0) + { + if (buffer[i - 1] == '\n') + buffer[--i] = '\0'; + p = buffer; + while ((p = index (p, '\n')) != 0) + *p++ = ' '; + o = variable_buffer_output (o, buffer, i); + } + } + + free (argv[0]); + free ((char *) argv); + free (buffer); + } + + pop_signals_blocked_p (); + + free (text); + break; + } + + case function_origin: + /* Expand the argument. */ + text = expand_argument (text, end); + + { + register struct variable *v = lookup_variable (text, strlen (text)); + if (v == 0) + o = variable_buffer_output (o, "undefined", 9); + else + switch (v->origin) + { + default: + case o_invalid: + abort (); + break; + case o_default: + o = variable_buffer_output (o, "default", 7); + break; + case o_env: + o = variable_buffer_output (o, "environment", 11); + break; + case o_file: + o = variable_buffer_output (o, "file", 4); + break; + case o_env_override: + o = variable_buffer_output (o, "environment override", 20); + break; + case o_command: + o = variable_buffer_output (o, "command line", 12); + break; + case o_override: + o = variable_buffer_output (o, "override", 8); + break; + case o_automatic: + o = variable_buffer_output (o, "automatic", 9); + break; + } + } + + free (text); + break; + + case function_sort: + /* Expand the argument. */ + text = expand_argument (text, end); + + { + char **words = (char **) xmalloc (10 * sizeof (char *)); + unsigned int nwords = 10; + register unsigned int wordi = 0; + char *t; + + /* Chop TEXT into words and put them in WORDS. */ + t = text; + while (p = find_next_token (&t, &len)) + { + if (wordi >= nwords - 1) + { + nwords += 5; + words = (char **) xrealloc ((char *) words, + nwords * sizeof (char *)); + } + words[wordi++] = savestring (p, len); + } + + if (wordi > 0) + { + /* Now sort the list of words. */ + qsort ((char *) words, wordi, sizeof (char *), alpha_compare); + + /* Now write the sorted list. */ + for (i = 0; i < wordi; ++i) + { + len = strlen (words[i]); + if (i == wordi - 1 || strlen (words[i + 1]) != len + || strcmp (words[i], words[i + 1])) + { + o = variable_buffer_output (o, words[i], len); + o = variable_buffer_output (o, " ", 1); + } + free (words[i]); + } + /* Kill the last space. */ + --o; + } + + free ((char *) words); + } + + free (text); + break; + + case function_foreach: + { + /* Get three comma-separated arguments but + expand only the first two. */ + char *var, *list; + register struct variable *v; + + count = 0; + for (p = text; p < end; ++p) + { + if (*p == startparen) + ++count; + else if (*p == endparen) + --count; + else if (*p == ',' && count <= 0) + break; + } + if (p == end) + BADARGS ("foreach"); + var = expand_argument (text, p); + + p2 = p + 1; + count = 0; + for (p = p2; p < end; ++p) + { + if (*p == startparen) + ++count; + else if (*p == endparen) + --count; + else if (*p == ',' && count <= 0) + break; + } + if (p == end) + BADARGS ("foreach"); + list = expand_argument (p2, p); + + ++p; + text = savestring (p, end - p); + + push_new_variable_scope (); + v = define_variable (var, strlen (var), "", o_automatic, 0); + p3 = list; + while ((p = find_next_token (&p3, &len)) != 0) + { + char *result; + char save = p[len]; + p[len] = '\0'; + v->value = p; + result = allocated_variable_expand (text); + p[len] = save; + + o = variable_buffer_output (o, result, strlen (result)); + o = variable_buffer_output (o, " ", 1); + doneany = 1; + free (result); + } + if (doneany) + /* Kill the last space. */ + --o; + + pop_variable_scope (); + + free (var); + free (list); + free (text); + } + break; + + case function_filter: + case function_filter_out: + { + struct word + { + struct word *next; + char *word; + int matched; + } *words, *wordtail, *wp; + + /* Get two comma-separated arguments and expand each one. */ + count = 0; + for (p = text; p < end; ++p) + { + if (*p == startparen) + ++count; + else if (*p == endparen) + --count; + else if (*p == ',' && count <= 0) + break; + } + if (p == end) + BADARGS (function == function_filter ? "filter" : "filter-out"); + p2 = expand_argument (text, p); + + text = expand_argument (p + 1, end); + + /* Chop TEXT up into words and then run each pattern through. */ + words = wordtail = 0; + p3 = text; + while ((p = find_next_token (&p3, &len)) != 0) + { + struct word *w = (struct word *) alloca (sizeof (struct word)); + if (words == 0) + words = w; + else + wordtail->next = w; + wordtail = w; + + if (*p3 != '\0') + ++p3; + p[len] = '\0'; + w->word = p; + w->matched = 0; + } + + if (words != 0) + { + wordtail->next = 0; + + /* Run each pattern through the words, killing words. */ + p3 = p2; + while ((p = find_next_token (&p3, &len)) != 0) + { + char *percent; + char save = p[len]; + p[len] = '\0'; + + percent = find_percent (p); + for (wp = words; wp != 0; wp = wp->next) + wp->matched |= (percent == 0 ? streq (p, wp->word) + : pattern_matches (p, percent, wp->word)); + + p[len] = save; + } + + /* Output the words that matched (or didn't, for filter-out). */ + for (wp = words; wp != 0; wp = wp->next) + if (function == function_filter ? wp->matched : !wp->matched) + { + o = variable_buffer_output (o, wp->word, strlen (wp->word)); + o = variable_buffer_output (o, " ", 1); + doneany = 1; + } + if (doneany) + /* Kill the last space. */ + --o; + } + + free (p2); + free (text); + } + break; + + case function_patsubst: + /* Get three comma-separated arguments and expand each one. */ + count = 0; + for (p = text; p < end; ++p) + { + if (*p == startparen) + ++count; + else if (*p == endparen) + --count; + else if (*p == ',' && count <= 0) + break; + } + if (p == end) + BADARGS ("patsubst"); + + p2 = p; + count = 0; + for (++p; p < end; ++p) + { + if (*p == startparen) + ++count; + else if (*p == endparen) + --count; + else if (*p == ',' && count <= 0) + break; + } + if (p == end) + BADARGS ("patsubst"); + + text = expand_argument (text, p2); + p3 = expand_argument (p2 + 1, p); + p2 = expand_argument (p + 1, end); + + o = patsubst_expand (o, p2, text, p3, (char *) 0, (char *) 0); + + free (text); + free (p3); + free (p2); + break; + + case function_join: + /* Get two comma-separated arguments and expand each one. */ + count = 0; + for (p = text; p < end; ++p) + { + if (*p == startparen) + ++count; + else if (*p == endparen) + --count; + else if (*p == ',' && count <= 0) + break; + } + if (p == end) + BADARGS ("join"); + text = expand_argument (text, p); + + p = expand_argument (p + 1, end); + + { + /* Write each word of the first argument directly followed + by the corresponding word of the second argument. + If the two arguments have a different number of words, + the excess words are just output separated by blanks. */ + register char *tp, *pp; + p2 = text; + p3 = p; + do + { + unsigned int tlen, plen; + + tp = find_next_token (&p2, &tlen); + if (tp != 0) + o = variable_buffer_output (o, tp, tlen); + + pp = find_next_token (&p3, &plen); + if (pp != 0) + o = variable_buffer_output (o, pp, plen); + + if (tp != 0 || pp != 0) + { + o = variable_buffer_output (o, " ", 1); + doneany = 1; + } + } + while (tp != 0 || pp != 0); + if (doneany) + /* Kill the last blank. */ + --o; + } + + free (text); + free (p); + break; + + case function_strip: + /* Expand the argument. */ + text = expand_argument (text, end); + + p2 = text; + while ((p = find_next_token (&p2, &i)) != 0) + { + o = variable_buffer_output (o, p, i); + o = variable_buffer_output (o, " ", 1); + doneany = 1; + } + if (doneany) + /* Kill the last space. */ + --o; + + free (text); + break; + + case function_wildcard: + text = expand_argument (text, end); + + p = string_glob (text); + o = variable_buffer_output (o, p, strlen (p)); + + free (text); + break; + + case function_subst: + /* Get three comma-separated arguments and expand each one. */ + count = 0; + for (p = text; p < end; ++p) + { + if (*p == startparen) + ++count; + else if (*p == endparen) + --count; + else if (*p == ',' && count <= 0) + break; + } + if (p == end) + BADARGS ("subst"); + + p2 = p; + count = 0; + for (++p; p < end; ++p) + { + if (*p == startparen) + ++count; + else if (*p == endparen) + --count; + else if (*p == ',' && count <= 0) + break; + } + if (p == end) + BADARGS ("subst"); + + text = expand_argument (text, p2); + p3 = expand_argument (p2 + 1, p); + p2 = expand_argument (p + 1, end); + + o = subst_expand (o, p2, text, p3, strlen (text), strlen (p3), 0, 0); + + free (text); + free (p3); + free (p2); + break; + + case function_firstword: + /* Expand the argument. */ + text = expand_argument (text, end); + + /* Find the first word in TEXT. */ + p2 = text; + p = find_next_token (&p2, &i); + if (p != 0) + o = variable_buffer_output (o, p, i); + + free (text); + break; + + case function_word: + /* Get two comma-separated arguments and expand each one. */ + count = 0; + for (p = text; p < end; ++p) + { + if (*p == startparen) + ++count; + else if (*p == endparen) + --count; + else if (*p == ',' && count <= 0) + break; + } + if (p == end) + BADARGS ("word"); + text = expand_argument (text, p); + + p3 = expand_argument (p + 1, end); + + /* Check the first argument. */ + for (p2 = text; *p2 != '\0'; ++p2) + if (*p2 < '0' || *p2 > '9') + { + if (reading_filename != 0) + makefile_fatal (reading_filename, *reading_lineno_ptr, + "non-numeric first argument to `word' function"); + else + fatal ("non-numeric first argument to `word' function"); + } + + i = (unsigned int) atoi (text); + if (i == 0) + { + if (reading_filename != 0) + makefile_fatal (reading_filename, *reading_lineno_ptr, + "the `word' function takes a one-origin \ +index argument"); + else + fatal ("the `word' function takes a one-origin index argument"); + } + + p2 = p3; + while ((p = find_next_token (&p2, &len)) != 0) + if (--i == 0) + break; + if (i == 0) + o = variable_buffer_output (o, p, len); + + free (text); + free (p3); + break; + + case function_words: + /* Expand the argument. */ + text = expand_argument (text, end); + + i = 0; + p2 = text; + while (find_next_token (&p2, (unsigned int *) 0) != 0) + ++i; + + { + char buf[20]; + sprintf (buf, "%d", i); + o = variable_buffer_output (o, buf, strlen (buf)); + } + + free (text); + break; + + case function_findstring: + /* Get two comma-separated arguments and expand each one. */ + count = 0; + for (p = text; p < end; ++p) + { + if (*p == startparen) + ++count; + else if (*p == endparen) + --count; + else if (*p == ',' && count <= 0) + break; + } + if (p == end) + BADARGS ("findstring"); + text = expand_argument (text, p); + + p = expand_argument (p + 1, end); + + /* Find the first occurrence of the first string in the second. */ + i = strlen (text); + if (sindex (p, 0, text, i) != 0) + o = variable_buffer_output (o, text, i); + + free (p); + free (text); + break; + + case function_addsuffix: + case function_addprefix: + /* Get two comma-separated arguments and expand each one. */ + count = 0; + for (p = text; p < end; ++p) + { + if (*p == startparen) + ++count; + else if (*p == endparen) + --count; + else if (*p == ',' && count <= 0) + break; + } + if (p == end) + BADARGS (function == function_addsuffix ? "addsuffix" : "addprefix"); + text = expand_argument (text, p); + i = strlen (text); + + p2 = expand_argument (p + 1, end); + + p3 = p2; + while ((p = find_next_token (&p3, &len)) != 0) + { + if (function == function_addprefix) + o = variable_buffer_output (o, text, i); + o = variable_buffer_output (o, p, len); + if (function == function_addsuffix) + o = variable_buffer_output (o, text, i); + o = variable_buffer_output (o, " ", 1); + doneany = 1; + } + if (doneany) + /* Kill last space. */ + --o; + + free (p2); + free (text); + break; + + case function_dir: + case function_basename: + /* Expand the argument. */ + text = expand_argument (text, end); + + p3 = text; + while ((p2 = find_next_token (&p3, &len)) != 0) + { + p = p2 + len; + while (p >= p2 && *p != (function == function_dir ? '/' : '.')) + --p; + if (p >= p2) + { + if (function == function_dir) + ++p; + o = variable_buffer_output (o, p2, p - p2); + } + else if (function == function_dir) + o = variable_buffer_output (o, "./", 2); + else + /* The entire name is the basename. */ + o = variable_buffer_output (o, p2, len); + + o = variable_buffer_output (o, " ", 1); + doneany = 1; + } + if (doneany) + /* Kill last space. */ + --o; + + free (text); + break; + + case function_notdir: + case function_suffix: + /* Expand the argument. */ + text = expand_argument (text, end); + + p3 = text; + while ((p2 = find_next_token (&p3, &len)) != 0) + { + p = p2 + len; + while (p >= p2 && *p != (function == function_notdir ? '/' : '.')) + --p; + if (p >= p2) + { + if (function == function_notdir) + ++p; + o = variable_buffer_output (o, p, len - (p - p2)); + } + else if (function == function_notdir) + o = variable_buffer_output (o, p2, len); + + if (function == function_notdir || p >= p2) + { + o = variable_buffer_output (o, " ", 1); + doneany = 1; + } + } + if (doneany) + /* Kill last space. */ + --o; + + free (text); + break; + } + + return o; +} + +/* Check for a function invocation in *STRINGP. *STRINGP points at the + opening ( or { and is not null-terminated. If a function invocation + is found, expand it into the buffer at *OP, updating *OP, incrementing + *STRINGP past the reference and returning nonzero. If not, return zero. */ + +int +handle_function (op, stringp) + char **op; + char **stringp; + +{ + register unsigned int code; + unsigned int maxlen; + char *beg = *stringp + 1; + char *endref; + + endref = lindex (beg, beg + MAXFUNCTIONLEN, '\0'); + maxlen = endref != 0 ? endref - beg : MAXFUNCTIONLEN; + + for (code = 0; function_table[code].name != 0; ++code) + { + if (maxlen < function_table[code].len) + continue; + endref = beg + function_table[code].len; + if (isblank (*endref) + && !strncmp (function_table[code].name, beg, + function_table[code].len)) + break; + } + if (function_table[code].name != 0) + { + /* We have found a call to an expansion-time function. + Find the end of the arguments, and do the function. */ + + char openparen = beg[-1], closeparen = openparen == '(' ? ')' : '}'; + int count = 0; + char *argbeg; + register char *p; + + /* Space after function name isn't part of the args. */ + p = next_token (endref); + argbeg = p; + + /* Count nested use of whichever kind of parens we use, + so that nested calls and variable refs work. */ + + for (; *p != '\0'; ++p) + { + if (*p == openparen) + ++count; + else if (*p == closeparen && --count < 0) + break; + } + + /* We found the end; expand the function call. */ + + *op = expand_function (*op, function_table[code].function, argbeg, p); + *stringp = p; + return 1; + } + + return 0; +} + +/* Glob-expand LINE. The returned pointer is + only good until the next call to string_glob. */ + +static char * +string_glob (line) + char *line; +{ + static char *result = 0; + static unsigned int length; + register struct nameseq *chain; + register unsigned int idx; + + chain = multi_glob (parse_file_seq (&line, '\0', sizeof (struct nameseq), 0), + sizeof (struct nameseq)); + + if (result == 0) + { + length = 100; + result = (char *) xmalloc (100); + } + + idx = 0; + while (chain != 0) + { + register char *name = chain->name; + unsigned int len = strlen (name); + + struct nameseq *next = chain->next; + free ((char *) chain); + chain = next; + + /* multi_glob will pass names without globbing metacharacters + through as is, but we want only files that actually exist. */ + if (file_exists_p (name)) + { + if (idx + len + 1 > length) + { + length += (len + 1) * 2; + result = (char *) xrealloc (result, length); + } + bcopy (name, &result[idx], len); + idx += len; + result[idx++] = ' '; + } + + free (name); + } + + /* Kill the last space and terminate the string. */ + if (idx == 0) + result[0] = '\0'; + else + result[idx - 1] = '\0'; + + return result; +} diff --git a/rule.c b/rule.c new file mode 100644 index 0000000..02302b0 --- /dev/null +++ b/rule.c @@ -0,0 +1,524 @@ +/* Pattern and suffix rule internals for GNU Make. +Copyright (C) 1988, 1989, 1990, 1991 Free Software Foundation, Inc. +This file is part of GNU Make. + +GNU Make is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Make is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Make; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "make.h" +#include "commands.h" +#include "dep.h" +#include "file.h" +#include "variable.h" +#include "rule.h" + +static void freerule (); +static int new_pattern_rule (); + +/* Chain of all pattern rules. */ + +struct rule *pattern_rules; + +/* Pointer to last rule in the chain, so we can add onto the end. */ + +struct rule *last_pattern_rule; + +/* Number of rules in the chain. */ + +unsigned int num_pattern_rules; + +/* Maximum number of dependencies of any pattern rule. */ + +unsigned int max_pattern_deps; + +/* Maximum length of the name of a dependencies of any pattern rule. */ + +unsigned int max_pattern_dep_length; + +/* Pointer to structure for the file .SUFFIXES + whose dependencies are the suffixes to be searched. */ + +struct file *suffix_file; + +/* Maximum length of a suffix. */ + +unsigned int maxsuffix; + +/* Compute the maximum dependency length and maximum number of + dependencies of all implicit rules. Also sets the subdir + flag for a rule when appropriate, possibly removing the rule + completely when appropriate. */ + +void +count_implicit_rule_limits () +{ + char *name; + unsigned int namelen; + register struct rule *rule, *lastrule; + + num_pattern_rules = 0; + + name = 0; + namelen = 0; + rule = lastrule = pattern_rules; + while (rule != 0) + { + unsigned int ndeps = 0; + register struct dep *dep; + + ++num_pattern_rules; + + for (dep = rule->deps; dep != 0; dep = dep->next) + { + unsigned int len = strlen (dep->name); + char *p = rindex (dep->name, '/'); + char *p2 = p != 0 ? index (dep->name, '%') : 0; + + ndeps++; + + if (len > max_pattern_dep_length) + max_pattern_dep_length = len; + + if (p != 0 && p2 > p) + { + if (p == dep->name) + ++p; + if (p - dep->name > namelen) + { + if (name != 0) + free (name); + namelen = p - dep->name; + name = (char *) xmalloc (namelen + 1); + } + bcopy (dep->name, name, p - dep->name); + name[p - dep->name] = '\0'; + + if (!dir_file_exists_p (name, ".")) + { + if (*name == '/') + { + freerule (rule, lastrule); + rule = lastrule; + goto end_main_loop; + } + else + rule->subdir = 1; + } + } + } + + if (ndeps > max_pattern_deps) + max_pattern_deps = ndeps; + + end_main_loop:; + lastrule = rule; + rule = rule->next; + } + + if (name != 0) + free (name); +} + +/* Convert old-style suffix rules to pattern rules. + All rules for the suffixes on the .SUFFIXES list + are converted and added to the chain of pattern rules. */ + +void +convert_to_pattern () +{ + register struct dep *d, *d2, *newd; + register struct file *f; + register char *rulename; + register unsigned int slen, s2len; + register char *name, **names; + + /* Compute maximum length of all the suffixes. */ + + maxsuffix = 0; + for (d = suffix_file->deps; d != 0; d = d->next) + { + register unsigned int namelen = strlen (dep_name (d)); + if (namelen > maxsuffix) + maxsuffix = namelen; + } + + rulename = (char *) alloca ((maxsuffix * 2) + 1); + + for (d = suffix_file->deps; d != 0; d = d->next) + { + /* Make a rule that is just the suffix, with no deps or commands. + This rule exists solely to disqualify match-anything rules. */ + slen = strlen (dep_name (d)); + name = (char *) xmalloc (1 + slen + 1); + name[0] = '%'; + bcopy (dep_name (d), name + 1, slen + 1); + names = (char **) xmalloc (2 * sizeof (char *)); + names[0] = name; + names[1] = 0; + create_pattern_rule (names, (char **) 0, 0, (struct dep *) 0, + (struct commands *) 0, 0); + + f = d->file; + if (f->cmds != 0) + { + /* Record a pattern for this suffix's null-suffix rule. */ + newd = (struct dep *) xmalloc (sizeof (struct dep)); + /* Construct this again rather than using the contents + of NAME (above), since that may have been freed by + create_pattern_rule. */ + newd->name = (char *) xmalloc (1 + slen + 1); + newd->name[0] = '%'; + bcopy (dep_name (d), newd->name + 1, slen + 1); + newd->next = 0; + names = (char **) xmalloc (2 * sizeof (char *)); + names[0] = savestring ("%", 1); + names[1] = 0; + create_pattern_rule (names, (char **) 0, 0, newd, f->cmds, 0); + } + + /* Record a pattern for each of this suffix's two-suffix rules. */ + bcopy (dep_name (d), rulename, slen); + for (d2 = suffix_file->deps; d2 != 0; d2 = d2->next) + { + s2len = strlen (dep_name (d2)); + + if (slen == s2len && streq (dep_name (d), dep_name (d2))) + continue; + + bcopy (dep_name (d2), rulename + slen, s2len + 1); + f = lookup_file (rulename); + if (f == 0 || f->cmds == 0) + continue; + + if (s2len == 2 && rulename[slen] == '.' && rulename[slen + 1] == 'a') + /* The suffix rule `.X.a:' is converted + to the pattern rule `(%.o): %.X'. */ + name = savestring ("(%.o)", 5); + else + { + /* The suffix rule `.X.Y:' is converted + to the pattern rule `%.Y: %.X'. */ + name = (char *) xmalloc (1 + s2len + 1); + name[0] = '%'; + bcopy (dep_name (d2), name + 1, s2len + 1); + } + names = (char **) xmalloc (2 * sizeof (char *)); + names[0] = name; + names[1] = 0; + newd = (struct dep *) xmalloc (sizeof (struct dep)); + newd->next = 0; + /* Construct this again (see comment above). */ + newd->name = (char *) xmalloc (1 + slen + 1); + newd->name[0] = '%'; + bcopy (dep_name (d), newd->name + 1, slen + 1); + create_pattern_rule (names, (char **) 0, 0, newd, f->cmds, 0); + } + } +} + + +/* Install the pattern rule RULE (whose fields have been filled in) + at the end of the list (so that any rules previously defined + will take precedence). If this rule duplicates a previous one + (identical target and dependents), the old one is replaced + if OVERRIDE is nonzero, otherwise this new one is thrown out. + When an old rule is replaced, the new one is put at the end of the + list. Return nonzero if RULE is used; zero if not. */ + +static int +new_pattern_rule (rule, override) + register struct rule *rule; + int override; +{ + register struct rule *r, *lastrule; + register unsigned int i, j; + + rule->subdir = 0; + rule->in_use = 0; + rule->terminal = 0; + + rule->next = 0; + + /* Search for an identical rule. */ + lastrule = pattern_rules; + for (r = pattern_rules; r != 0; lastrule = r, r = r->next) + for (i = 0; rule->targets[i] != 0; ++i) + for (j = 0; r->targets[j] != 0; ++j) + if (streq (rule->targets[i], r->targets[j])) + { + register struct dep *d, *d2; + for (d = rule->deps, d2 = r->deps; + d != 0 && d2 != 0; d = d->next, d2 = d2->next) + if (!streq (dep_name (d), dep_name (d2))) + break; + if (d == 0 && d2 == 0) + /* All the dependencies matched. */ + if (override) + { + /* Remove the old rule. */ + freerule (r, lastrule); + /* Install the new one. */ + if (pattern_rules == 0) + pattern_rules = rule; + else + last_pattern_rule->next = rule; + last_pattern_rule = rule; + + /* We got one. Stop looking. */ + goto matched; + } + else + { + /* The old rule stays intact. Destroy the new one. */ + freerule (rule, (struct rule *) 0); + return 0; + } + } + + matched:; + + if (r == 0) + { + /* There was no rule to replace. */ + if (pattern_rules == 0) + pattern_rules = rule; + else + last_pattern_rule->next = rule; + last_pattern_rule = rule; + } + + return 1; +} + + +/* Install an implicit pattern rule based on the three text strings + in the structure P points to. These strings come from one of + the arrays of default implicit pattern rules. + TERMINAL specifies what the `terminal' field of the rule should be. */ + +void +install_pattern_rule (p, terminal) + struct pspec *p; + int terminal; +{ + register struct rule *r; + char *ptr; + + r = (struct rule *) xmalloc (sizeof (struct rule)); + + r->targets = (char **) xmalloc (2 * sizeof (char *)); + r->suffixes = (char **) xmalloc (2 * sizeof (char *)); + r->lens = (unsigned int *) xmalloc (2 * sizeof (unsigned int)); + + r->targets[1] = 0; + r->suffixes[1] = 0; + r->lens[1] = 0; + + r->lens[0] = strlen (p->target); + /* These will all be string literals, but we malloc space for + them anyway because somebody might want to free them later on. */ + r->targets[0] = savestring (p->target, r->lens[0]); + r->suffixes[0] = find_percent (r->targets[0]); + if (r->suffixes[0] == 0) + /* Programmer-out-to-lunch error. */ + abort (); + else + ++r->suffixes[0]; + + ptr = p->dep; + r->deps = (struct dep *) multi_glob (parse_file_seq (&ptr, '\0', + sizeof (struct dep), 1), + sizeof (struct dep)); + + if (new_pattern_rule (r, 0)) + { + r->terminal = terminal; + r->cmds = (struct commands *) xmalloc (sizeof (struct commands)); + r->cmds->filename = 0; + r->cmds->lineno = 0; + /* These will all be string literals, but we malloc space for them + anyway because somebody might want to free them later. */ + r->cmds->commands = savestring (p->commands, strlen (p->commands)); + r->cmds->command_lines = 0; + } +} + + +/* Free all the storage used in RULE and take it out of the + pattern_rules chain. LASTRULE is the rule whose next pointer + points to RULE. */ + +static void +freerule (rule, lastrule) + register struct rule *rule, *lastrule; +{ + struct rule *next = rule->next; + register unsigned int i; + + for (i = 0; rule->targets[i] != 0; ++i) + free (rule->targets[i]); + + free ((char *) rule->targets); + free ((char *) rule->suffixes); + free ((char *) rule->lens); + + /* We can't free the storage for the commands because there + are ways that they could be in more than one place: + * If the commands came from a suffix rule, they could also be in + the `struct file's for other suffix rules or plain targets given + on the same makefile line. + * If two suffixes that together make a two-suffix rule were each + given twice in the .SUFFIXES list, and in the proper order, two + identical pattern rules would be created and the second one would + be discarded here, but both would contain the same `struct commands' + pointer from the `struct file' for the suffix rule. */ + + free ((char *) rule); + + if (lastrule == 0) + return; + + if (pattern_rules == rule) + if (lastrule != pattern_rules) + abort (); + else + pattern_rules = next; + else + lastrule->next = next; + if (last_pattern_rule == rule) + last_pattern_rule = lastrule; +} + +/* Create a new pattern rule with the targets in the nil-terminated + array TARGETS. If TARGET_PERCENTS is not nil, it is an array of + pointers into the elements of TARGETS, where the `%'s are. + The new rule has dependencies DEPS and commands from COMMANDS. + It is a terminal rule if TERMINAL is nonzero. This rule overrides + identical rules with different commands if OVERRIDE is nonzero. + + The storage for TARGETS and its elements is used and must not be freed + until the rule is destroyed. The storage for TARGET_PERCENTS is not used; + it may be freed. */ + +void +create_pattern_rule (targets, target_percents, + terminal, deps, commands, override) + char **targets, **target_percents; + int terminal; + struct dep *deps; + struct commands *commands; + int override; +{ + register struct rule *r = (struct rule *) xmalloc (sizeof (struct rule)); + register unsigned int max_targets, i; + + r->cmds = commands; + r->deps = deps; + r->targets = targets; + + max_targets = 2; + r->lens = (unsigned int *) xmalloc (2 * sizeof (unsigned int)); + r->suffixes = (char **) xmalloc (2 * sizeof (char *)); + for (i = 0; targets[i] != 0; ++i) + { + if (i == max_targets - 1) + { + max_targets += 5; + r->lens = (unsigned int *) + xrealloc ((char *) r->lens, max_targets * sizeof (unsigned int)); + r->suffixes = (char **) + xrealloc ((char *) r->suffixes, max_targets * sizeof (char *)); + } + r->lens[i] = strlen (targets[i]); + r->suffixes[i] = (target_percents == 0 ? find_percent (targets[i]) + : target_percents[i]) + 1; + if (r->suffixes[i] == 0) + abort (); + } + + if (i < max_targets - 1) + { + r->lens = (unsigned int *) xrealloc ((char *) r->lens, + (i + 1) * sizeof (unsigned int)); + r->suffixes = (char **) xrealloc ((char *) r->suffixes, + (i + 1) * sizeof (char *)); + } + + if (new_pattern_rule (r, override)) + r->terminal = terminal; +} + +/* Print the data base of rules. */ + +void +print_rule_data_base () +{ + register unsigned int rules, terminal, subdir; + register struct rule *r; + register struct dep *d; + register unsigned int i; + + puts ("\n# Implicit Rules"); + + rules = terminal = subdir = 0; + for (r = pattern_rules; r != 0; r = r->next) + { + ++rules; + + putchar ('\n'); + for (i = 0; r->targets[i] != 0; ++i) + { + fputs (r->targets[i], stdout); + if (r->targets[i + 1] != 0) + putchar (' '); + else + putchar (':'); + } + if (r->terminal) + { + ++terminal; + putchar (':'); + } + + for (d = r->deps; d != 0; d = d->next) + printf (" %s", dep_name (d)); + putchar ('\n'); + + if (r->subdir) + { + ++subdir; + puts ("# references nonexistent subdirectory."); + } + + if (r->cmds != 0) + print_commands (r->cmds); + } + + if (rules == 0) + puts ("\n# No implicit rules."); + else + { + printf ("\n# %u implicit rules, %u", rules, terminal); +#ifndef NO_FLOAT + printf (" (%.1f%%)", (double) terminal / (double) rules * 100.0); +#endif + puts (" terminal."); + + printf ("# %u", subdir); +#ifndef NO_FLOAT + printf (" (%.1f%%)", (double) subdir / (double) rules * 100.0); +#endif + puts (" reference nonexistent subdirectories."); + } +} -- cgit v1.2.3