c6e8380069
libcpp: * Makefile.am: Add makedepend. * Makefile.in, aclocal.m4: Regenerate. * charset.c: Insert a space to avoid a warning. * directives.c: Include mkdeps.h. (_cpp_handle_directive): Reenable macro expander if appropriate. (undefine_macros): Inline body of _cpp_free_definition for speed. Do not call undef callback or _cpp_warn_if_unused_macro. (cpp_get_deps): New interface. * files.c (search_cache): Add pfile argument. Check for file that would be found by "" or <> search here... (_cpp_find_file): ...not here. Correct recorded start_dir of files found by directory-of-current-file search that would be found by "" or <> search. * init.c (cpp_add_dependency_target): Delete. * internal.h (struct lexer_state): Add discarding_output flag. * lex.c (lex_identifier): Compute hash function while scanning. * macro.c (cpp_scan_nooutput): Disable macro expansion outside directives. * makedepend.c: New file. * mkdeps.c (struct deps): Add vpath vector. (apply_vpath, deps_add_vpath): New function. (deps_free): Free vpath vector. (deps_add_dep, deps_add_target): Use apply_vpath. * symtab.c (calc_hash): Use HT_HASHSTEP and HT_FINISH. (ht_lookup_with_hash): New function. * cpplib.h, mkdeps.h: Update prototypes. * symtab.h: Update prototypes. (HT_HASHSTEP, HT_FINISH): New macros. gcc: * Makefile.in (MKDEPS_H): New shorthand. (c-opts.o): Update dependencies. * c-opts.c: Include mkdeps.h. (handle_deferred_opts): Use cpp_get_deps and deps_add_target, not cpp_add_dependency_target. From-SVN: r82654
414 lines
9.5 KiB
C
414 lines
9.5 KiB
C
/* Dependency generator for Makefile fragments.
|
|
Copyright (C) 2000, 2001, 2003 Free Software Foundation, Inc.
|
|
Contributed by Zack Weinberg, Mar 2000
|
|
|
|
This program 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.
|
|
|
|
This program 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 this program; if not, write to the Free Software
|
|
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
In other words, you are welcome to use, share and improve this program.
|
|
You are forbidden to forbid anyone else to use, share and improve
|
|
what you give them. Help stamp out software-hoarding! */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "mkdeps.h"
|
|
|
|
/* Keep this structure local to this file, so clients don't find it
|
|
easy to start making assumptions. */
|
|
struct deps
|
|
{
|
|
const char **targetv;
|
|
unsigned int ntargets; /* number of slots actually occupied */
|
|
unsigned int targets_size; /* amt of allocated space - in words */
|
|
|
|
const char **depv;
|
|
unsigned int ndeps;
|
|
unsigned int deps_size;
|
|
|
|
const char **vpathv;
|
|
size_t *vpathlv;
|
|
unsigned int nvpaths;
|
|
unsigned int vpaths_size;
|
|
};
|
|
|
|
static const char *munge (const char *);
|
|
|
|
/* Given a filename, quote characters in that filename which are
|
|
significant to Make. Note that it's not possible to quote all such
|
|
characters - e.g. \n, %, *, ?, [, \ (in some contexts), and ~ are
|
|
not properly handled. It isn't possible to get this right in any
|
|
current version of Make. (??? Still true? Old comment referred to
|
|
3.76.1.) */
|
|
|
|
static const char *
|
|
munge (const char *filename)
|
|
{
|
|
int len;
|
|
const char *p, *q;
|
|
char *dst, *buffer;
|
|
|
|
for (p = filename, len = 0; *p; p++, len++)
|
|
{
|
|
switch (*p)
|
|
{
|
|
case ' ':
|
|
case '\t':
|
|
/* GNU make uses a weird quoting scheme for white space.
|
|
A space or tab preceded by 2N+1 backslashes represents
|
|
N backslashes followed by space; a space or tab
|
|
preceded by 2N backslashes represents N backslashes at
|
|
the end of a file name; and backslashes in other
|
|
contexts should not be doubled. */
|
|
for (q = p - 1; filename <= q && *q == '\\'; q--)
|
|
len++;
|
|
len++;
|
|
break;
|
|
|
|
case '$':
|
|
/* '$' is quoted by doubling it. */
|
|
len++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Now we know how big to make the buffer. */
|
|
buffer = xmalloc (len + 1);
|
|
|
|
for (p = filename, dst = buffer; *p; p++, dst++)
|
|
{
|
|
switch (*p)
|
|
{
|
|
case ' ':
|
|
case '\t':
|
|
for (q = p - 1; filename <= q && *q == '\\'; q--)
|
|
*dst++ = '\\';
|
|
*dst++ = '\\';
|
|
break;
|
|
|
|
case '$':
|
|
*dst++ = '$';
|
|
break;
|
|
|
|
default:
|
|
/* nothing */;
|
|
}
|
|
*dst = *p;
|
|
}
|
|
|
|
*dst = '\0';
|
|
return buffer;
|
|
}
|
|
|
|
/* If T begins with any of the partial pathnames listed in d->vpathv,
|
|
then advance T to point beyond that pathname. */
|
|
static const char *
|
|
apply_vpath (struct deps *d, const char *t)
|
|
{
|
|
if (d->vpathv)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < d->nvpaths; i++)
|
|
{
|
|
if (!strncmp (d->vpathv[i], t, d->vpathlv[i]))
|
|
{
|
|
const char *p = t + d->vpathlv[i];
|
|
if (!IS_DIR_SEPARATOR (*p))
|
|
goto not_this_one;
|
|
|
|
/* Do not simplify $(vpath)/../whatever. ??? Might not
|
|
be necessary. */
|
|
if (p[1] == '.' && p[2] == '.' && IS_DIR_SEPARATOR (p[3]))
|
|
goto not_this_one;
|
|
|
|
/* found a match */
|
|
t = t + d->vpathlv[i] + 1;
|
|
break;
|
|
}
|
|
not_this_one:;
|
|
}
|
|
}
|
|
|
|
/* Remove leading ./ in any case. */
|
|
while (t[0] == '.' && IS_DIR_SEPARATOR (t[1]))
|
|
t += 2;
|
|
|
|
return t;
|
|
}
|
|
|
|
/* Public routines. */
|
|
|
|
struct deps *
|
|
deps_init (void)
|
|
{
|
|
return xcalloc (sizeof (struct deps), 1);
|
|
}
|
|
|
|
void
|
|
deps_free (struct deps *d)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (d->targetv)
|
|
{
|
|
for (i = 0; i < d->ntargets; i++)
|
|
free ((void *) d->targetv[i]);
|
|
free (d->targetv);
|
|
}
|
|
|
|
if (d->depv)
|
|
{
|
|
for (i = 0; i < d->ndeps; i++)
|
|
free ((void *) d->depv[i]);
|
|
free (d->depv);
|
|
}
|
|
|
|
if (d->vpathv)
|
|
{
|
|
for (i = 0; i < d->nvpaths; i++)
|
|
free ((void *) d->vpathv[i]);
|
|
free (d->vpathv);
|
|
free (d->vpathlv);
|
|
}
|
|
|
|
free (d);
|
|
}
|
|
|
|
/* Adds a target T. We make a copy, so it need not be a permanent
|
|
string. QUOTE is true if the string should be quoted. */
|
|
void
|
|
deps_add_target (struct deps *d, const char *t, int quote)
|
|
{
|
|
if (d->ntargets == d->targets_size)
|
|
{
|
|
d->targets_size = d->targets_size * 2 + 4;
|
|
d->targetv = xrealloc (d->targetv,
|
|
d->targets_size * sizeof (const char *));
|
|
}
|
|
|
|
t = apply_vpath (d, t);
|
|
if (quote)
|
|
t = munge (t); /* Also makes permanent copy. */
|
|
else
|
|
t = xstrdup (t);
|
|
|
|
d->targetv[d->ntargets++] = t;
|
|
}
|
|
|
|
/* Sets the default target if none has been given already. An empty
|
|
string as the default target in interpreted as stdin. The string
|
|
is quoted for MAKE. */
|
|
void
|
|
deps_add_default_target (struct deps *d, const char *tgt)
|
|
{
|
|
/* Only if we have no targets. */
|
|
if (d->ntargets)
|
|
return;
|
|
|
|
if (tgt[0] == '\0')
|
|
deps_add_target (d, "-", 1);
|
|
else
|
|
{
|
|
#ifndef TARGET_OBJECT_SUFFIX
|
|
# define TARGET_OBJECT_SUFFIX ".o"
|
|
#endif
|
|
const char *start = lbasename (tgt);
|
|
char *o = alloca (strlen (start) + strlen (TARGET_OBJECT_SUFFIX) + 1);
|
|
char *suffix;
|
|
|
|
strcpy (o, start);
|
|
|
|
suffix = strrchr (o, '.');
|
|
if (!suffix)
|
|
suffix = o + strlen (o);
|
|
strcpy (suffix, TARGET_OBJECT_SUFFIX);
|
|
|
|
deps_add_target (d, o, 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
deps_add_dep (struct deps *d, const char *t)
|
|
{
|
|
t = munge (apply_vpath (d, t)); /* Also makes permanent copy. */
|
|
|
|
if (d->ndeps == d->deps_size)
|
|
{
|
|
d->deps_size = d->deps_size * 2 + 8;
|
|
d->depv = xrealloc (d->depv, d->deps_size * sizeof (const char *));
|
|
}
|
|
d->depv[d->ndeps++] = t;
|
|
}
|
|
|
|
void
|
|
deps_add_vpath (struct deps *d, const char *vpath)
|
|
{
|
|
const char *elem, *p;
|
|
char *copy;
|
|
size_t len;
|
|
|
|
for (elem = vpath; *elem; elem = p)
|
|
{
|
|
for (p = elem; *p && *p != ':'; p++);
|
|
len = p - elem;
|
|
copy = xmalloc (len + 1);
|
|
memcpy (copy, elem, len);
|
|
copy[len] = '\0';
|
|
if (*p == ':')
|
|
p++;
|
|
|
|
if (d->nvpaths == d->vpaths_size)
|
|
{
|
|
d->vpaths_size = d->vpaths_size * 2 + 8;
|
|
d->vpathv = xrealloc (d->vpathv,
|
|
d->vpaths_size * sizeof (const char *));
|
|
d->vpathlv = xrealloc (d->vpathlv, d->vpaths_size * sizeof (size_t));
|
|
}
|
|
d->vpathv[d->nvpaths] = copy;
|
|
d->vpathlv[d->nvpaths] = len;
|
|
d->nvpaths++;
|
|
}
|
|
}
|
|
|
|
void
|
|
deps_write (const struct deps *d, FILE *fp, unsigned int colmax)
|
|
{
|
|
unsigned int size, i, column;
|
|
|
|
column = 0;
|
|
if (colmax && colmax < 34)
|
|
colmax = 34;
|
|
|
|
for (i = 0; i < d->ntargets; i++)
|
|
{
|
|
size = strlen (d->targetv[i]);
|
|
column += size;
|
|
if (colmax && column > colmax)
|
|
{
|
|
fputs (" \\\n ", fp);
|
|
column = 1 + size;
|
|
}
|
|
if (i)
|
|
{
|
|
putc (' ', fp);
|
|
column++;
|
|
}
|
|
fputs (d->targetv[i], fp);
|
|
}
|
|
|
|
putc (':', fp);
|
|
putc (' ', fp);
|
|
column += 2;
|
|
|
|
for (i = 0; i < d->ndeps; i++)
|
|
{
|
|
size = strlen (d->depv[i]);
|
|
column += size;
|
|
if (colmax && column > colmax)
|
|
{
|
|
fputs (" \\\n ", fp);
|
|
column = 1 + size;
|
|
}
|
|
if (i)
|
|
{
|
|
putc (' ', fp);
|
|
column++;
|
|
}
|
|
fputs (d->depv[i], fp);
|
|
}
|
|
putc ('\n', fp);
|
|
}
|
|
|
|
void
|
|
deps_phony_targets (const struct deps *d, FILE *fp)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 1; i < d->ndeps; i++)
|
|
{
|
|
putc ('\n', fp);
|
|
fputs (d->depv[i], fp);
|
|
putc (':', fp);
|
|
putc ('\n', fp);
|
|
}
|
|
}
|
|
|
|
/* Write out a deps buffer to a file, in a form that can be read back
|
|
with deps_restore. Returns nonzero on error, in which case the
|
|
error number will be in errno. */
|
|
|
|
int
|
|
deps_save (struct deps *deps, FILE *f)
|
|
{
|
|
unsigned int i;
|
|
|
|
/* The cppreader structure contains makefile dependences. Write out this
|
|
structure. */
|
|
|
|
/* The number of dependences. */
|
|
if (fwrite (&deps->ndeps, sizeof (deps->ndeps), 1, f) != 1)
|
|
return -1;
|
|
/* The length of each dependence followed by the string. */
|
|
for (i = 0; i < deps->ndeps; i++)
|
|
{
|
|
size_t num_to_write = strlen (deps->depv[i]);
|
|
if (fwrite (&num_to_write, sizeof (size_t), 1, f) != 1)
|
|
return -1;
|
|
if (fwrite (deps->depv[i], num_to_write, 1, f) != 1)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Read back dependency information written with deps_save into
|
|
the deps buffer. The third argument may be NULL, in which case
|
|
the dependency information is just skipped, or it may be a filename,
|
|
in which case that filename is skipped. */
|
|
|
|
int
|
|
deps_restore (struct deps *deps, FILE *fd, const char *self)
|
|
{
|
|
unsigned int i, count;
|
|
size_t num_to_read;
|
|
size_t buf_size = 512;
|
|
char *buf = xmalloc (buf_size);
|
|
|
|
/* Number of dependences. */
|
|
if (fread (&count, 1, sizeof (count), fd) != sizeof (count))
|
|
return -1;
|
|
|
|
/* The length of each dependence string, followed by the string. */
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
/* Read in # bytes in string. */
|
|
if (fread (&num_to_read, 1, sizeof (size_t), fd) != sizeof (size_t))
|
|
return -1;
|
|
if (buf_size < num_to_read + 1)
|
|
{
|
|
buf_size = num_to_read + 1 + 127;
|
|
buf = xrealloc (buf, buf_size);
|
|
}
|
|
if (fread (buf, 1, num_to_read, fd) != num_to_read)
|
|
return -1;
|
|
buf[num_to_read] = '\0';
|
|
|
|
/* Generate makefile dependencies from .pch if -nopch-deps. */
|
|
if (self != NULL && strcmp (buf, self) != 0)
|
|
deps_add_dep (deps, buf);
|
|
}
|
|
|
|
free (buf);
|
|
return 0;
|
|
}
|