Major rework of the directory structure and the entire build system.
-Erik
This commit is contained in:
628
scripts/mkdep.c
Normal file
628
scripts/mkdep.c
Normal file
@ -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;
|
||||
}
|
Reference in New Issue
Block a user