summaryrefslogtreecommitdiff
path: root/scripts/mkdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/mkdep.c')
-rw-r--r--scripts/mkdep.c628
1 files changed, 628 insertions, 0 deletions
diff --git a/scripts/mkdep.c b/scripts/mkdep.c
new file mode 100644
index 0000000..c3e94bf
--- /dev/null
+++ b/scripts/mkdep.c
@@ -0,0 +1,628 @@
+/*
+ * 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, "include/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++) {
+ char c = 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("TOPDIR");
+ if (!hpath) {
+ fputs("mkdep: TOPDIR 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;
+}