Commit 04bd7217 authored by Kai Germaschewski's avatar Kai Germaschewski

kbuild: Make dependencies at compile time

Making dependencies once up front is not ideal. For one, you don't need
them initially, since when you don't have the .o file, you bet you have
to build it no matter what the dependencies say - dependencies are about
deciding when to *re*build.

There's more reasons, like:
o you don't even know which files you'll build, so you have to go over
  all files (even over drivers/{sbus,s390,...} on i386)
o generated files don't exist yet, so you cannot pick up dependencies
  on them
o even if dependencies are right initially, they change when you work on 
  your tree or patch it, and nobody will notice unless you run "make dep"
  explicitly again

Anyway, gcc knows hows to emit a correct dependency list, so we just use
that. Well, a little bit of hacking is necessary to remove the dependency
on autoconf.h and put in individual CONFIG_WHAT_EVER dependencies instead,
since otherwise changing one config option would cause everything to be
rebuilt.

I should add that I didn't come up with this all by myself, most work
is actually done in gcc and there were discussions about using -MD on
kbuild-devel way back, so I should mention Keith Owens and Michael
Elizabeth Chastain, and probably others that I forgot, so I apologize
just in case.
parent d02c2b1f
......@@ -300,39 +300,41 @@ include/linux/version.h: Makefile
# Helpers built in scripts/
# ---------------------------------------------------------------------------
scripts/mkdep scripts/split-include : scripts ;
scripts/fixdep scripts/split-include : scripts ;
.PHONY: scripts
scripts:
@$(MAKE) -C scripts
# Generate dependencies
# Generate module versions
# ---------------------------------------------------------------------------
# In the same pass, generate module versions, that's why it's
# all mixed up here.
# The targets are still named depend / dep for traditional
# reasons, but the only thing we do here is generating
# the module version checksums.
# FIXME: For now, we are also calling "archdep" from here,
# which should be replaced by a more sensible solution.
.PHONY: depend dep $(patsubst %,_sfdep_%,$(SUBDIRS))
depend dep: .hdepend
# .hdepend is missing prerequisites - in fact dependencies need
# to be redone basically each time anything changed - since
# that's too expensive, we traditionally rely on the user to
# run "make dep" manually whenever necessary. In this case,
# we make "FORCE" a prequisite, to force redoing the
# dependencies. Yeah, that's ugly, and it'll go away soon.
ifdef CONFIG_MODVERSIONS
# Before descending for the actual build, we need module
# versions done. - Still using the old, illogical name
# .hdepend
quiet_cmd_depend = Making dependencies (include)
cmd_depend = scripts/mkdep -- `find $(FINDHPATH) -name SCCS -prune -o -follow -name \*.h ! -name modversions.h -print` > $@
# .hdepend only indicates if we have generated module
# version checksums before now. For now, if they've
# been generated once, no rechecking will be done unless
# explicitly asked for using "make dep".
.hdepend: scripts/mkdep include/linux/version.h include/asm \
.hdepend: scripts/fixdep include/linux/version.h include/asm \
$(if $(filter dep depend,$(MAKECMDGOALS)),FORCE)
$(call cmd,cmd_depend)
touch $@
@$(MAKE) $(patsubst %,_sfdep_%,$(SUBDIRS))
ifdef CONFIG_MODVERSIONS
@$(MAKE) include/linux/modversions.h
endif
@$(MAKE) archdep
$(patsubst %,_sfdep_%,$(SUBDIRS)): FORCE
......@@ -358,6 +360,15 @@ include/linux/modversions.h: FORCE
mv -f $@.tmp $@; \
fi
else # !CONFIG_MODVERSIONS
.hdepend: include/linux/version.h include/asm \
$(if $(filter dep depend,$(MAKECMDGOALS)),FORCE)
touch $@
@$(MAKE) archdep
endif # CONFIG_MODVERSIONS
# ---------------------------------------------------------------------------
# Modules
......@@ -592,8 +603,8 @@ MRPROPER_FILES += \
scripts/lxdialog/*.o scripts/lxdialog/lxdialog \
.menuconfig.log \
include/asm \
.hdepend scripts/mkdep scripts/split-include scripts/docproc \
$(TOPDIR)/include/linux/modversions.h \
.hdepend scripts/split-include scripts/docproc \
scripts/fixdep $(TOPDIR)/include/linux/modversions.h \
tags TAGS kernel.spec \
# directories removed with 'make mrproper'
......@@ -607,7 +618,8 @@ include arch/$(ARCH)/Makefile
clean: archclean
@echo 'Cleaning up'
@find . \( -name \*.[oas] -o -name core -o -name .\*.cmd \) -type f -print \
@find . \( -name \*.[oas] -o -name core -o -name .\*.cmd -o \
-name .\*.tmp -o -name .\*.d \) -type f -print \
| grep -v lxdialog/ | xargs rm -f
@rm -f $(CLEAN_FILES)
@rm -rf $(CLEAN_DIRS)
......
......@@ -96,7 +96,7 @@ first_rule: vmlinux $(if $(BUILD_MODULES),$(obj-m))
# Compile C sources (.c)
# ---------------------------------------------------------------------------
# FIXME: if we don't know if built-in or modular, assume built-in.
# If we don't know if built-in or modular, assume built-in.
# Only happens in Makefiles which override the default first_rule:
modkern_cflags := $(CFLAGS_KERNEL)
......@@ -120,25 +120,25 @@ quiet_cmd_cc_s_c = CC $(RELDIR)/$@
cmd_cc_s_c = $(CC) $(c_flags) -S -o $@ $<
%.s: %.c FORCE
$(call if_changed,cmd_cc_s_c)
$(call cmd,cmd_cc_s_c)
quiet_cmd_cc_i_c = CPP $(RELDIR)/$@
cmd_cc_i_c = $(CPP) $(c_flags) -o $@ $<
%.i: %.c FORCE
$(call if_changed,cmd_cc_i_c)
$(call cmd,cmd_cc_i_c)
quiet_cmd_cc_o_c = CC $(RELDIR)/$@
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
cmd_cc_o_c = $(CC) -Wp,-MD,.$(subst /,_,$@).d $(c_flags) -c -o $@ $<
%.o: %.c FORCE
$(call if_changed,cmd_cc_o_c)
$(call if_changed_dep,cc_o_c)
quiet_cmd_cc_lst_c = Generating $(RELDIR)/$@
cmd_cc_lst_c = $(CC) $(c_flags) -g -c -o $*.o $< && $(TOPDIR)/scripts/makelst $*.o $(TOPDIR)/System.map $(OBJDUMP) > $@
%.lst: %.c FORCE
$(call if_changed,cmd_cc_lst_c)
$(call cmd,cmd_cc_lst_c)
# Compile assembler sources (.S)
# ---------------------------------------------------------------------------
......@@ -158,13 +158,13 @@ quiet_cmd_as_s_S = CPP $(RELDIR)/$@
cmd_as_s_S = $(CPP) $(a_flags) -o $@ $<
%.s: %.S FORCE
$(call if_changed,cmd_as_s_S)
$(call cmd,cmd_as_s_S)
quiet_cmd_as_o_S = AS $(RELDIR)/$@
cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $<
cmd_as_o_S = $(CC) -Wp,-MD,.$(subst /,_,$@).d $(a_flags) -c -o $@ $<
%.o: %.S FORCE
$(call if_changed,cmd_as_o_S)
$(call if_changed_dep,as_o_S)
# If a Makefile does define neither O_TARGET nor L_TARGET,
# use a standard O_TARGET named "built-in.o"
......@@ -226,14 +226,10 @@ $(multi-used-m) : %.o: $(multi-objs-m) FORCE
$(call if_changed,cmd_link_multi)
#
# This make dependencies quickly
# This makes module versions
#
quiet_cmd_fastdep = Making dependencies ($(RELDIR))
cmd_fastdep = $(TOPDIR)/scripts/mkdep $(CFLAGS) $(EXTRA_CFLAGS) -- $(wildcard *.[chS]) > .depend
fastdep: FORCE
$(call cmd,cmd_fastdep)
ifdef ALL_SUB_DIRS
@$(MAKE) $(patsubst %,_sfdep_%,$(ALL_SUB_DIRS)) _FASTDEP_ALL_SUB_DIRS="$(ALL_SUB_DIRS)"
endif
......@@ -287,7 +283,9 @@ modules_install: _modinst__ $(patsubst %,_modinst_%,$(MOD_SUB_DIRS))
# Add FORCE to the prequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------
.PHONY: FORCE
FORCE:
#
......@@ -301,7 +299,6 @@ script:
# Separate the object into "normal" objects and "exporting" objects
# Exporting objects are: all objects that define symbol tables
#
ifdef CONFIG_MODULES
ifdef CONFIG_MODVERSIONS
ifneq "$(strip $(export-objs))" ""
......@@ -323,16 +320,17 @@ endif
# We don't track dependencies for .ver files, so we FORCE to check
# them always (i.e. always at "make dep" time).
quiet_cmd_create_ver = Creating $@
cmd_create_ver = $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -E -D__GENKSYMS__ $< | \
$(GENKSYMS) $(genksyms_smp_prefix) -k $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) > $@.tmp
$(MODINCL)/$(MODPREFIX)%.ver: %.c FORCE
@echo $(cmd_create_ver)
@$(cmd_create_ver)
@$(call cmd,cmd_create_ver)
@if [ -r $@ ] && cmp -s $@ $@.tmp; then \
echo $@ is unchanged; rm -f $@.tmp; \
else \
echo mv $@.tmp $@; mv -f $@.tmp $@; \
mv -f $@.tmp $@; \
fi
# updates .ver files but not modversions.h
......@@ -340,30 +338,8 @@ fastdep: $(addprefix $(MODINCL)/$(MODPREFIX),$(export-objs:.o=.ver))
endif # export-objs
# make dep cannot correctly figure out the dependency on the generated
# modversions.h, so we list them here:
# o files which export symbols and are compiled into the kernel include
# it (to generate a correct symbol table)
# o all modules get compiled with -include modversions.h
$(filter $(export-objs),$(real-objs-y)): $(TOPDIR)/include/linux/modversions.h
$(real-objs-m): $(TOPDIR)/include/linux/modversions.h
endif # CONFIG_MODVERSIONS
endif # CONFIG_MODULES
#
# include dependency files if they exist
#
ifneq ($(wildcard .depend),)
include .depend
endif
ifneq ($(wildcard $(TOPDIR)/.hdepend),)
include $(TOPDIR)/.hdepend
endif
# ---------------------------------------------------------------------------
# Check if command line has changed
......@@ -395,7 +371,7 @@ endif
# which is saved in .<target>.o, to the current command line using
# the two filter-out commands)
# read all saved command lines
# read all saved command lines and dependencies
cmd_files := $(wildcard .*.cmd)
ifneq ($(cmd_files),)
......@@ -409,6 +385,20 @@ if_changed = $(if $(strip $? \
$(filter-out $(cmd_$(@F)),$($(1)))),\
@$(if $($(quiet)$(1)),echo ' $($(quiet)$(1))' &&) $($(1)) && echo 'cmd_$@ := $($(1))' > $(@D)/.$(@F).cmd)
# execute the command and also postprocess generated .d dependencies
# file
if_changed_dep = $(if $(strip $? \
$(filter-out $(cmd_$(1)),$(cmd_$@))\
$(filter-out $(cmd_$@),$(cmd_$(1)))),\
@set -e; \
$(if $($(quiet)cmd_$(1)),echo '$($(quiet)cmd_$(1))';) \
$(cmd_$(1)); \
$(TOPDIR)/scripts/fixdep $(subst /,_,$@) $(TOPDIR) '$(cmd_$(1))' > .$(subst /,_,$@).tmp; \
rm -f .$(subst /,_,$@).d; \
mv -f .$(subst /,_,$@).tmp .$(subst /,_,$@).cmd )
# If quiet is set, only print short version of command
cmd = @$(if $($(quiet)$(1)),echo ' $($(quiet)$(1))' &&) $($(1))
......@@ -7,9 +7,9 @@
# can't do it
CHMOD_FILES := docgen gen-all-syms kernel-doc mkcompile_h mkversion_h makelst
all: mkdep split-include $(CHMOD_FILES)
all: fixdep split-include $(CHMOD_FILES)
mkdep: mkdep.c
fixdep: fixdep.c
$(HOSTCC) $(HOSTCFLAGS) -o $@ $<
split-include: split-include.c
......
/*
* "Optimize" a list of dependencies as spit out by gcc -MD
* for the kernel build
* ===========================================================================
*
* Author Kai Germaschewski
* Copyright 2002 by Kai Germaschewski <kai.germaschewski@gmx.de>
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*
* Introduction:
*
* gcc produces a very nice and correct list of dependencies which
* tells make when to remake a file.
*
* To use this list as-is however has the drawback that virtually
* every file in the kernel includes <linux/config.h> which then again
* includes <linux/autoconf.h>
*
* If the user re-runs make *config, linux/autoconf.h will be
* regenerated. make notices that and will rebuild every file which
* includes autconf.h, i.e. basically all files. This is extremely
* annoying if the user just changed CONFIG_HIS_DRIVER from n to m.
*
* So we play the same trick that "mkdep" played before. We replace
* the dependency on linux/autoconf.h by a dependency on every config
* option which is mentioned in any of the listed prequisites.
*
* To be exact, split-include populates a tree in include/config/,
* e.g. include/config/his/driver.h, which contains the #define/#undef
* for the CONFIG_HIS_DRIVER option.
*
* So if the user changes his CONFIG_HIS_DRIVER option, only the objects
* which depend on "include/linux/config/his/driver.h" will be rebuilt,
* so most likely only his driver ;-)
*
* The idea above dates, by the way, back to Michael E Chastain, AFAIK.
*
* So to get dependencies right, there two issues:
* o if any of the files the compiler read changed, we need to rebuild
* o if the command line given to the compile the file changed, we
* better rebuild as well.
*
* The former is handled by using the -MD output, the later by saving
* the command line used to compile the old object and comparing it
* to the one we would now use.
*
* Again, also this idea is pretty old and has been discussed on
* kbuild-devel a long time ago. I don't have a sensibly working
* internet connection right now, so I rather don't mention names
* without double checking.
*
* This code here has been based partially based on mkdep.c, which
* says the following about its history:
*
* Copyright abandoned, Michael Chastain, <mailto:mec@shout.net>.
* This is a C version of syncdep.pl by Werner Almesberger.
*
*
* It is invoked as
*
* fixdep <target> <topdir> <cmdline>
*
* and will read the dependency file ".<target>.d".
*
* The transformed dependency snipped is written to stdout.
*
* It first generates a line
*
* cmd_<target> = <cmdline>
*
* and then basically copies the .<target>.d file to stdout, in the
* process filtering out the dependency on linux/autconf.h and adding
* dependencies on include/config/my/option.h for every
* CONFIG_MY_OPTION encountered in any of the prequisites.
*
* It will also filter out all the dependencies on *.ver. We need
* to make sure that the generated version checksum are globally up
* to date before even starting the recursive build, so it's too late
* at this point anyway.
*
* The algorithm to grep for "CONFIG_..." is bit unusual, but should
* be fast ;-) We don't even try to really parse the header files, but
* merely grep, i.e. if CONFIG_FOO is mentioned in a comment, it will
* be picked up as well. It's not a problem with respect to
* correctness, since that can only give too many dependencies, thus
* we cannot miss a rebuild. Since people tend to not mention totally
* unrelated CONFIG_ options all over the place, it's not an
* efficiency problem either.
*
* (Note: it'd be easy to port over the complete mkdep state machine,
* but I don't think the added complexity is worth it)
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
#include <netinet/in.h>
#define INT_CONF ntohl(0x434f4e46)
#define INT_ONFI ntohl(0x4f4e4649)
#define INT_NFIG ntohl(0x4e464947)
#define INT_FIG_ ntohl(0x4649475f)
char *topdir;
void usage(void)
{
fprintf(stderr, "Usage: fixdep <target> <topdir> <cmdline>\n");
exit(1);
}
void print_cmdline(char *target, char *cmdline)
{
char *s = strdup(target);
char *p = s;
if (!s) {
fprintf(stderr, "no mem!\n");
exit(2);
}
while ((p = strchr(p,'/')))
*p = '_';
printf("cmd_%s := %s\n\n", s, cmdline);
free(s);
}
char * str_config = NULL;
int size_config = 0;
int len_config = 0;
/*
* Grow the configuration string to a desired length.
* Usually the first growth is plenty.
*/
void grow_config(int len)
{
while (len_config + len > size_config) {
if (size_config == 0)
size_config = 2048;
str_config = realloc(str_config, size_config *= 2);
if (str_config == NULL)
{ perror("malloc"); exit(1); }
}
}
/*
* Lookup a value in the configuration string.
*/
int is_defined_config(const char * name, int len)
{
const char * pconfig;
const char * plast = str_config + len_config - len;
for ( pconfig = str_config + 1; pconfig < plast; pconfig++ ) {
if (pconfig[ -1] == '\n'
&& pconfig[len] == '\n'
&& !memcmp(pconfig, name, len))
return 1;
}
return 0;
}
/*
* Add a new value to the configuration string.
*/
void define_config(const char * name, int len)
{
grow_config(len + 1);
memcpy(str_config+len_config, name, len);
len_config += len;
str_config[len_config++] = '\n';
}
/*
* Clear the set of configuration strings.
*/
void clear_config(void)
{
len_config = 0;
define_config("", 0);
}
/*
* Record the use of a CONFIG_* word.
*/
void use_config(char *m, int slen)
{
char s[PATH_MAX];
char *p;
if (is_defined_config(m, slen))
return;
define_config(m, slen);
memcpy(s, m, slen); s[slen] = 0;
for (p = s; p < s + slen; p++) {
if (*p == '_')
*p = '/';
else
*p = tolower(*p);
}
printf(" $(wildcard %s/include/config/%s.h) \\\n", topdir, s);
}
void parse_config_file(char *map, size_t len)
{
int *end = (int *) (map + len);
// start at +1, so that p can never be < map
int *m = (int *) map + 1;
char *p, *q;
for (; m < end; m++) {
if (*m == INT_CONF) { p = (char *) m ; goto conf; }
if (*m == INT_ONFI) { p = (char *) m-1; goto conf; }
if (*m == INT_NFIG) { p = (char *) m-2; goto conf; }
if (*m == INT_FIG_) { p = (char *) m-3; goto conf; }
continue;
conf:
if (p > map + len - 7)
continue;
if (memcmp(p, "CONFIG_", 7))
continue;
for (q = p + 7; q < map + len; q++) {
if (!(isalnum(*q)))
goto found;
}
continue;
found:
use_config(p+7, q-p-7);
}
}
/* test is s ends in sub */
int strrcmp(char *s, char *sub)
{
int slen = strlen(s);
int sublen = strlen(sub);
if (sublen > slen)
return 1;
return memcmp(s + slen - sublen, sub, sublen);
}
void do_config_file(char *filename)
{
struct stat st;
int fd;
void *map;
fd = open(filename, O_RDONLY);
if (fd < 0) {
perror(filename);
exit(2);
}
fstat(fd, &st);
if (st.st_size == 0) {
close(fd);
return;
}
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if ((long) map == -1) {
perror("mmap");
close(fd);
return;
}
parse_config_file(map, st.st_size);
munmap(map, st.st_size);
}
void parse_dep_file(void *map, size_t len)
{
char *m = map;
char *end = map + len;
char *p;
char s[PATH_MAX];
p = strchr(m, ':');
if (!p) {
fprintf(stderr, "parse error at %d", __LINE__);
exit(1);
}
memcpy(s, m, p-m); s[p-m] = 0;
printf("%s: \\\n", s);
m = p+1;
clear_config();
while (m < end) {
while (*m == ' ' || *m == '\\' || *m == '\n')
m++;
p = strchr(m, ' ');
if (!p) {
p = end;
while (!isalpha(*p)) p--;
p++;
}
memcpy(s, m, p-m); s[p-m] = 0;
if (strrcmp(s, "include/linux/autoconf.h") &&
strrcmp(s, ".ver")) {
printf(" %s \\\n", s);
do_config_file(s);
}
m = p + 1;
}
printf("\n");
}
void print_deps(char *target)
{
char filename[PATH_MAX];
struct stat st;
int fd;
void *map;
sprintf(filename, ".%s.d", target);
fd = open(filename, O_RDONLY);
if (fd < 0) {
perror(filename);
exit(2);
}
fstat(fd, &st);
if (st.st_size == 0) {
fprintf(stderr,"%s is empty\n",filename);
close(fd);
return;
}
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if ((long) map == -1) {
perror("mmap");
close(fd);
return;
}
parse_dep_file(map, st.st_size);
munmap(map, st.st_size);
}
void traps(void)
{
char *test = "CONF";
if (*(int *)test != INT_CONF) {
fprintf(stderr, "sizeof(int) != 4 or wrong endianess? %#x\n",
*(int *)test);
exit(2);
}
}
int main(int argc, char *argv[])
{
char *target, *cmdline;
traps();
if (argc != 4)
usage();
target = argv[1];
topdir = argv[2];
cmdline = argv[3];
print_cmdline(target, cmdline);
print_deps(target);
return 0;
}
/*
* Originally by Linus Torvalds.
* Smart CONFIG_* processing by Werner Almesberger, Michael Chastain.
*
* Usage: mkdep cflags -- file ...
*
* Read source files and output makefile dependency lines for them.
* I make simple dependency lines for #include <*.h> and #include "*.h".
* I also find instances of CONFIG_FOO and generate dependencies
* like include/config/foo.h.
*
* 1 August 1999, Michael Elizabeth Chastain, <mec@shout.net>
* - Keith Owens reported a bug in smart config processing. There used
* to be an optimization for "#define CONFIG_FOO ... #ifdef CONFIG_FOO",
* so that the file would not depend on CONFIG_FOO because the file defines
* this symbol itself. But this optimization is bogus! Consider this code:
* "#if 0 \n #define CONFIG_FOO \n #endif ... #ifdef CONFIG_FOO". Here
* the definition is inactivated, but I still used it. It turns out this
* actually happens a few times in the kernel source. The simple way to
* fix this problem is to remove this particular optimization.
*
* 2.3.99-pre1, Andrew Morton <andrewm@uow.edu.au>
* - Changed so that 'filename.o' depends upon 'filename.[cS]'. This is so that
* missing source files are noticed, rather than silently ignored.
*
* 2.4.2-pre3, Keith Owens <kaos@ocs.com.au>
* - Accept cflags followed by '--' followed by filenames. mkdep extracts -I
* options from cflags and looks in the specified directories as well as the
* defaults. Only -I is supported, no attempt is made to handle -idirafter,
* -isystem, -I- etc.
*/
#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
char __depname[512] = "\n\t@touch ";
#define depname (__depname+9)
int hasdep;
struct path_struct {
int len;
char *buffer;
};
struct path_struct *path_array;
int paths;
/* Current input file */
static const char *g_filename;
/*
* This records all the configuration options seen.
* In perl this would be a hash, but here it's a long string
* of values separated by newlines. This is simple and
* extremely fast.
*/
char * str_config = NULL;
int size_config = 0;
int len_config = 0;
static void
do_depname(void)
{
if (!hasdep) {
hasdep = 1;
printf("%s:", depname);
if (g_filename)
printf(" %s", g_filename);
}
}
/*
* Grow the configuration string to a desired length.
* Usually the first growth is plenty.
*/
void grow_config(int len)
{
while (len_config + len > size_config) {
if (size_config == 0)
size_config = 2048;
str_config = realloc(str_config, size_config *= 2);
if (str_config == NULL)
{ perror("malloc config"); exit(1); }
}
}
/*
* Lookup a value in the configuration string.
*/
int is_defined_config(const char * name, int len)
{
const char * pconfig;
const char * plast = str_config + len_config - len;
for ( pconfig = str_config + 1; pconfig < plast; pconfig++ ) {
if (pconfig[ -1] == '\n'
&& pconfig[len] == '\n'
&& !memcmp(pconfig, name, len))
return 1;
}
return 0;
}
/*
* Add a new value to the configuration string.
*/
void define_config(const char * name, int len)
{
grow_config(len + 1);
memcpy(str_config+len_config, name, len);
len_config += len;
str_config[len_config++] = '\n';
}
/*
* Clear the set of configuration strings.
*/
void clear_config(void)
{
len_config = 0;
define_config("", 0);
}
/*
* This records all the precious .h filenames. No need for a hash,
* it's a long string of values enclosed in tab and newline.
*/
char * str_precious = NULL;
int size_precious = 0;
int len_precious = 0;
/*
* Grow the precious string to a desired length.
* Usually the first growth is plenty.
*/
void grow_precious(int len)
{
while (len_precious + len > size_precious) {
if (size_precious == 0)
size_precious = 2048;
str_precious = realloc(str_precious, size_precious *= 2);
if (str_precious == NULL)
{ perror("malloc"); exit(1); }
}
}
/*
* Add a new value to the precious string.
*/
void define_precious(const char * filename)
{
int len = strlen(filename);
grow_precious(len + 4);
*(str_precious+len_precious++) = '\t';
memcpy(str_precious+len_precious, filename, len);
len_precious += len;
memcpy(str_precious+len_precious, " \\\n", 3);
len_precious += 3;
}
/*
* Handle an #include line.
*/
void handle_include(int start, const char * name, int len)
{
struct path_struct *path;
int i;
if (len == 14 && !memcmp(name, "linux/config.h", len))
return;
if (len >= 7 && !memcmp(name, "config/", 7))
define_config(name+7, len-7-2);
for (i = start, path = path_array+start; i < paths; ++i, ++path) {
memcpy(path->buffer+path->len, name, len);
path->buffer[path->len+len] = '\0';
if (access(path->buffer, F_OK) == 0) {
do_depname();
printf(" \\\n %s", path->buffer);
return;
}
}
}
/*
* Add a path to the list of include paths.
*/
void add_path(const char * name)
{
struct path_struct *path;
char resolved_path[PATH_MAX+1];
const char *name2;
if (strcmp(name, ".")) {
name2 = realpath(name, resolved_path);
if (!name2) {
fprintf(stderr, "realpath(%s) failed, %m\n", name);
exit(1);
}
}
else {
name2 = "";
}
path_array = realloc(path_array, (++paths)*sizeof(*path_array));
if (!path_array) {
fprintf(stderr, "cannot expand path_arry\n");
exit(1);
}
path = path_array+paths-1;
path->len = strlen(name2);
path->buffer = malloc(path->len+1+256+1);
if (!path->buffer) {
fprintf(stderr, "cannot allocate path buffer\n");
exit(1);
}
strcpy(path->buffer, name2);
if (path->len && *(path->buffer+path->len-1) != '/') {
*(path->buffer+path->len) = '/';
*(path->buffer+(++(path->len))) = '\0';
}
}
/*
* Record the use of a CONFIG_* word.
*/
void use_config(const char * name, int len)
{
char *pc;
int i;
pc = path_array[paths-1].buffer + path_array[paths-1].len;
memcpy(pc, "config/", 7);
pc += 7;
for (i = 0; i < len; i++) {
int c = (unsigned char) name[i];
if (isupper(c)) c = tolower(c);
if (c == '_') c = '/';
pc[i] = c;
}
pc[len] = '\0';
if (is_defined_config(pc, len))
return;
define_config(pc, len);
do_depname();
printf(" \\\n $(wildcard %s.h)", path_array[paths-1].buffer);
}
/*
* Macros for stunningly fast map-based character access.
* __buf is a register which holds the current word of the input.
* Thus, there is one memory access per sizeof(unsigned long) characters.
*/
#if defined(__alpha__) || defined(__i386__) || defined(__ia64__) || defined(__x86_64__) || defined(__MIPSEL__) \
|| defined(__arm__)
#define LE_MACHINE
#endif
#ifdef LE_MACHINE
#define next_byte(x) (x >>= 8)
#define current ((unsigned char) __buf)
#else
#define next_byte(x) (x <<= 8)
#define current (__buf >> 8*(sizeof(unsigned long)-1))
#endif
#define GETNEXT { \
next_byte(__buf); \
if ((unsigned long) next % sizeof(unsigned long) == 0) { \
if (next >= end) \
break; \
__buf = * (unsigned long *) next; \
} \
next++; \
}
/*
* State machine macros.
*/
#define CASE(c,label) if (current == c) goto label
#define NOTCASE(c,label) if (current != c) goto label
/*
* Yet another state machine speedup.
*/
#define MAX2(a,b) ((a)>(b)?(a):(b))
#define MIN2(a,b) ((a)<(b)?(a):(b))
#define MAX5(a,b,c,d,e) (MAX2(a,MAX2(b,MAX2(c,MAX2(d,e)))))
#define MIN5(a,b,c,d,e) (MIN2(a,MIN2(b,MIN2(c,MIN2(d,e)))))
/*
* The state machine looks for (approximately) these Perl regular expressions:
*
* m|\/\*.*?\*\/|
* m|\/\/.*|
* m|'.*?'|
* m|".*?"|
* m|#\s*include\s*"(.*?)"|
* m|#\s*include\s*<(.*?>"|
* m|#\s*(?define|undef)\s*CONFIG_(\w*)|
* m|(?!\w)CONFIG_|
*
* About 98% of the CPU time is spent here, and most of that is in
* the 'start' paragraph. Because the current characters are
* in a register, the start loop usually eats 4 or 8 characters
* per memory read. The MAX5 and MIN5 tests dispose of most
* input characters with 1 or 2 comparisons.
*/
void state_machine(const char * map, const char * end)
{
const char * next = map;
const char * map_dot;
unsigned long __buf = 0;
for (;;) {
start:
GETNEXT
__start:
if (current > MAX5('/','\'','"','#','C')) goto start;
if (current < MIN5('/','\'','"','#','C')) goto start;
CASE('/', slash);
CASE('\'', squote);
CASE('"', dquote);
CASE('#', pound);
CASE('C', cee);
goto start;
/* // */
slash_slash:
GETNEXT
CASE('\n', start);
NOTCASE('\\', slash_slash);
GETNEXT
goto slash_slash;
/* / */
slash:
GETNEXT
CASE('/', slash_slash);
NOTCASE('*', __start);
slash_star_dot_star:
GETNEXT
__slash_star_dot_star:
NOTCASE('*', slash_star_dot_star);
GETNEXT
NOTCASE('/', __slash_star_dot_star);
goto start;
/* '.*?' */
squote:
GETNEXT
CASE('\'', start);
NOTCASE('\\', squote);
GETNEXT
goto squote;
/* ".*?" */
dquote:
GETNEXT
CASE('"', start);
NOTCASE('\\', dquote);
GETNEXT
goto dquote;
/* #\s* */
pound:
GETNEXT
CASE(' ', pound);
CASE('\t', pound);
CASE('i', pound_i);
CASE('d', pound_d);
CASE('u', pound_u);
goto __start;
/* #\s*i */
pound_i:
GETNEXT NOTCASE('n', __start);
GETNEXT NOTCASE('c', __start);
GETNEXT NOTCASE('l', __start);
GETNEXT NOTCASE('u', __start);
GETNEXT NOTCASE('d', __start);
GETNEXT NOTCASE('e', __start);
goto pound_include;
/* #\s*include\s* */
pound_include:
GETNEXT
CASE(' ', pound_include);
CASE('\t', pound_include);
map_dot = next;
CASE('"', pound_include_dquote);
CASE('<', pound_include_langle);
goto __start;
/* #\s*include\s*"(.*)" */
pound_include_dquote:
GETNEXT
CASE('\n', start);
NOTCASE('"', pound_include_dquote);
handle_include(0, map_dot, next - map_dot - 1);
goto start;
/* #\s*include\s*<(.*)> */
pound_include_langle:
GETNEXT
CASE('\n', start);
NOTCASE('>', pound_include_langle);
handle_include(1, map_dot, next - map_dot - 1);
goto start;
/* #\s*d */
pound_d:
GETNEXT NOTCASE('e', __start);
GETNEXT NOTCASE('f', __start);
GETNEXT NOTCASE('i', __start);
GETNEXT NOTCASE('n', __start);
GETNEXT NOTCASE('e', __start);
goto pound_define_undef;
/* #\s*u */
pound_u:
GETNEXT NOTCASE('n', __start);
GETNEXT NOTCASE('d', __start);
GETNEXT NOTCASE('e', __start);
GETNEXT NOTCASE('f', __start);
goto pound_define_undef;
/*
* #\s*(define|undef)\s*CONFIG_(\w*)
*
* this does not define the word, because it could be inside another
* conditional (#if 0). But I do parse the word so that this instance
* does not count as a use. -- mec
*/
pound_define_undef:
GETNEXT
CASE(' ', pound_define_undef);
CASE('\t', pound_define_undef);
NOTCASE('C', __start);
GETNEXT NOTCASE('O', __start);
GETNEXT NOTCASE('N', __start);
GETNEXT NOTCASE('F', __start);
GETNEXT NOTCASE('I', __start);
GETNEXT NOTCASE('G', __start);
GETNEXT NOTCASE('_', __start);
map_dot = next;
pound_define_undef_CONFIG_word:
GETNEXT
if (isalnum(current) || current == '_')
goto pound_define_undef_CONFIG_word;
goto __start;
/* \<CONFIG_(\w*) */
cee:
if (next >= map+2 && (isalnum(next[-2]) || next[-2] == '_'))
goto start;
GETNEXT NOTCASE('O', __start);
GETNEXT NOTCASE('N', __start);
GETNEXT NOTCASE('F', __start);
GETNEXT NOTCASE('I', __start);
GETNEXT NOTCASE('G', __start);
GETNEXT NOTCASE('_', __start);
map_dot = next;
cee_CONFIG_word:
GETNEXT
if (isalnum(current) || current == '_')
goto cee_CONFIG_word;
use_config(map_dot, next - map_dot - 1);
goto __start;
}
}
/*
* Generate dependencies for one file.
*/
void do_depend(const char * filename, const char * command)
{
int mapsize;
int pagesizem1 = getpagesize()-1;
int fd;
struct stat st;
char * map;
fd = open(filename, O_RDONLY);
if (fd < 0) {
perror(filename);
return;
}
fstat(fd, &st);
if (st.st_size == 0) {
fprintf(stderr,"%s is empty\n",filename);
close(fd);
return;
}
mapsize = st.st_size;
mapsize = (mapsize+pagesizem1) & ~pagesizem1;
map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
if ((long) map == -1) {
perror("mkdep: mmap");
close(fd);
return;
}
if ((unsigned long) map % sizeof(unsigned long) != 0)
{
fprintf(stderr, "do_depend: map not aligned\n");
exit(1);
}
hasdep = 0;
clear_config();
state_machine(map, map+st.st_size);
if (hasdep) {
puts(command);
if (*command)
define_precious(filename);
}
munmap(map, mapsize);
close(fd);
}
/*
* Generate dependencies for all files.
*/
int main(int argc, char **argv)
{
int len;
const char *hpath;
hpath = getenv("HPATH");
if (!hpath) {
fputs("mkdep: HPATH not set in environment. "
"Don't bypass the top level Makefile.\n", stderr);
return 1;
}
add_path("."); /* for #include "..." */
while (++argv, --argc > 0) {
if (strncmp(*argv, "-I", 2) == 0) {
if (*((*argv)+2)) {
add_path((*argv)+2);
}
else {
++argv;
--argc;
add_path(*argv);
}
}
else if (strcmp(*argv, "--") == 0) {
break;
}
}
add_path(hpath); /* must be last entry, for config files */
while (--argc > 0) {
const char * filename = *++argv;
const char * command = __depname;
g_filename = 0;
len = strlen(filename);
memcpy(depname, filename, len+1);
if (len > 2 && filename[len-2] == '.') {
if (filename[len-1] == 'c' || filename[len-1] == 'S') {
depname[len-1] = 'o';
g_filename = filename;
command = "";
}
}
do_depend(filename, command);
}
if (len_precious) {
*(str_precious+len_precious) = '\0';
printf(".PRECIOUS:%s\n", str_precious);
}
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment