// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** MAME source code dependency generator ***************************************************************************/ #include #include #include #include #include #include "osdcore.h" #include "astring.h" #include "corefile.h" #include "tagmap.h" /*************************************************************************** CONSTANTS ***************************************************************************/ #define HASH_SIZE 193 /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ struct include_path { include_path * next; astring path; }; struct exclude_path { exclude_path * next; astring path; int pathlen; UINT8 recursive; }; struct list_entry { list_entry * next; astring name; }; struct file_entry; struct dependency { dependency * next; file_entry * file; }; struct file_entry { astring name; dependency * deplist; }; typedef tagmap_t dependency_map; /*************************************************************************** GLOBAL VARIABLES ***************************************************************************/ static include_path *incpaths; static exclude_path *excpaths; static tagmap_t file_map; /*************************************************************************** PROTOTYPES ***************************************************************************/ // core output functions static int recurse_dir(int srcrootlen, astring &srcdir); static file_entry &compute_dependencies(int srcrootlen, astring &srcfile); // path helpers static bool find_include_file(astring &srcincpath, int srcrootlen, const astring &srcfile, const astring &filename); /*************************************************************************** MAIN ***************************************************************************/ /*------------------------------------------------- main - main entry point -------------------------------------------------*/ void ATTR_NORETURN usage(const char *argv0) { fprintf(stderr, "Usage:\n%s [-Iincpath [-Iincpath [...]]]\n", argv0); exit(1); } int main(int argc, char *argv[]) { include_path **incpathhead = &incpaths; exclude_path **excpathhead = &excpaths; astring srcdir; int unadorned = 0; // loop over arguments for (int argnum = 1; argnum < argc; argnum++) { char *arg = argv[argnum]; // include path? if (arg[0] == '-' && arg[1] == 'I') { *incpathhead = new include_path; (*incpathhead)->next = NULL; (*incpathhead)->path.cpy(&arg[2]).replacechr('/', PATH_SEPARATOR[0]); incpathhead = &(*incpathhead)->next; } // exclude path? else if (arg[0] == '-' && arg[1] == 'X') { *excpathhead = new exclude_path; (*excpathhead)->next = NULL; (*excpathhead)->path.cpy(&arg[2]).replacechr(PATH_SEPARATOR[0], '/'); (*excpathhead)->recursive = ((*excpathhead)->path.replace((*excpathhead)->path.len() - 4, "/...", "") != 0); (*excpathhead)->pathlen = (*excpathhead)->path.len(); excpathhead = &(*excpathhead)->next; } else if (arg[0] == '-' && arg[1] == 'F') { argnum++; } else if (arg[0] == '-' && arg[1] == 'D') { // some pkgconfigs return defines (e.g. pkg-config QtGui --cflags) ==> ignore argnum++; } // ignore -include which is used by sdlmame to include sdlprefix.h before all other includes else if (strcmp(arg,"-include") == 0) { argnum++; } // other parameter else if (arg[0] != '-' && unadorned == 0) { srcdir.cpy(arg).replacechr('/', PATH_SEPARATOR[0]); unadorned++; } else usage(argv[0]); } // make sure we got 1 parameter if (srcdir.len() == 0) usage(argv[0]); // recurse over subdirectories return recurse_dir(srcdir.len(), srcdir); } /*************************************************************************** CORE OUTPUT FUNCTIONS ***************************************************************************/ static int compare_list_entries(const void *p1, const void *p2) { const list_entry *entry1 = *(const list_entry **)p1; const list_entry *entry2 = *(const list_entry **)p2; return entry1->name.cmp(entry2->name); } /*------------------------------------------------- recurse_dependencies - recurse through the dependencies found, adding the mto the tagmap unless we already exist in the map -------------------------------------------------*/ static void recurse_dependencies(file_entry &file, dependency_map &map) { // skip if we're in an exclude path int filelen = file.name.len(); for (exclude_path *exclude = excpaths; exclude != NULL; exclude = exclude->next) if (exclude->pathlen < filelen && strncmp(file.name, exclude->path, exclude->pathlen) == 0) if (exclude->recursive || file.name.chr(exclude->pathlen + 1, '/') == -1) return; // attempt to add; if we get an error, we're already present if (map.add(file.name, 0) != TMERR_NONE) return; // recurse the list from there for (dependency *dep = file.deplist; dep != NULL; dep = dep->next) recurse_dependencies(*dep->file, map); } /*------------------------------------------------- recurse_dir - recurse through a directory -------------------------------------------------*/ static int recurse_dir(int srcrootlen, astring &srcdir) { static const osd_dir_entry_type typelist[] = { ENTTYPE_DIR, ENTTYPE_FILE }; int result = 0; // iterate first over directories, then over files for (int entindex = 0; entindex < ARRAY_LENGTH(typelist) && result == 0; entindex++) { osd_dir_entry_type entry_type = typelist[entindex]; // open the directory and iterate through it osd_directory *dir = osd_opendir(srcdir); if (dir == NULL) { result = 1; goto error; } // build up the list of files const osd_directory_entry *entry; list_entry *list = NULL; int found = 0; while ((entry = osd_readdir(dir)) != NULL) if (entry->type == entry_type && entry->name[0] != '.') { list_entry *lentry = new list_entry; lentry->name.cpy(entry->name); lentry->next = list; list = lentry; found++; } // close the directory osd_closedir(dir); // skip if nothing found if (found == 0) continue; // allocate memory for sorting list_entry **listarray = new list_entry *[found]; found = 0; for (list_entry *curlist = list; curlist != NULL; curlist = curlist->next) listarray[found++] = curlist; // sort the list qsort(listarray, found, sizeof(listarray[0]), compare_list_entries); // rebuild the list list = NULL; while (--found >= 0) { listarray[found]->next = list; list = listarray[found]; } delete[] listarray; // iterate through each file for (list_entry *curlist = list; curlist != NULL && result == 0; curlist = curlist->next) { astring srcfile; // build the source filename srcfile.printf("%s%c%s", srcdir.cstr(), PATH_SEPARATOR[0], curlist->name.cstr()); // if we have a file, output it if (entry_type == ENTTYPE_FILE) { // make sure we care, first if (core_filename_ends_with(curlist->name, ".c")) { dependency_map depend_map; // find dependencies file_entry &file = compute_dependencies(srcrootlen, srcfile); recurse_dependencies(file, depend_map); // convert the target from source to object (makes assumptions about rules) astring target(file.name); target.replace(0, "src/", "$(OBJ)/"); target.replace(0, ".c", ".o"); printf("\n%s : \\\n", target.cstr()); // iterate over the hashed dependencies and output them as well for (dependency_map::entry_t *entry = depend_map.first(); entry != NULL; entry = depend_map.next(entry)) printf("\t%s \\\n", entry->tag().cstr()); } } // if we have a directory, recurse else result = recurse_dir(srcrootlen, srcfile); } // free all the allocated entries while (list != NULL) { list_entry *next = list->next; delete list; list = next; } } error: return result; } /*------------------------------------------------- output_file - output a file, converting to HTML -------------------------------------------------*/ static file_entry &compute_dependencies(int srcrootlen, astring &srcfile) { // see if we already have an entry astring normalfile(srcfile); normalfile.replacechr(PATH_SEPARATOR[0], '/'); file_entry *foundfile = file_map.find(normalfile); if (foundfile != NULL) return *foundfile; // create a new header entry file_entry &file = *new file_entry; file.deplist = NULL; file.name = normalfile; file_map.add(file.name, &file); // read the source file UINT32 filelength; char *filedata; if (core_fload(srcfile, (void **)&filedata, &filelength) != FILERR_NONE) { fprintf(stderr, "Unable to read file '%s'\n", srcfile.cstr()); return file; } // find the #include directives in this file for (int index = 0; index < filelength; index++) if (filedata[index] == '#' && strncmp(&filedata[index + 1], "include", 7) == 0) { // first make sure we're not commented or quoted bool just_continue = false; for (int scan = index; scan > 2 && filedata[scan] != 13 && filedata[scan] != 10; scan--) if ((filedata[scan] == '/' && filedata[scan - 1] == '/') || filedata[scan] == '"') { just_continue = true; break; } if (just_continue) continue; // scan forward to find the quotes or bracket index += 7; int scan; for (scan = index; scan < filelength && filedata[scan] != '<' && filedata[scan] != '"' && filedata[scan] != 13 && filedata[scan] != 10; scan++) ; // ignore if not found or if it's bracketed if (scan >= filelength || filedata[scan] != '"') continue; int start = ++scan; // find the closing quote while (scan < filelength && filedata[scan] != '"') scan++; if (scan >= filelength) continue; // find the include file astring filename(&filedata[start], scan - start); astring target; // create a new dependency if (find_include_file(target, srcrootlen, srcfile, filename)) { dependency *dep = new dependency; dep->next = file.deplist; file.deplist = dep; dep->file = &compute_dependencies(srcrootlen, target); } } osd_free(filedata); return file; } /*************************************************************************** HELPERS ***************************************************************************/ /*------------------------------------------------- find_include_file - find an include file -------------------------------------------------*/ static bool find_include_file(astring &srcincpath, int srcrootlen, const astring &srcfile, const astring &filename) { // iterate over include paths and find the file for (include_path *curpath = incpaths; curpath != NULL; curpath = curpath->next) { // a '.' include path is specially treated if (curpath->path == ".") srcincpath.cpysubstr(srcfile, 0, srcfile.rchr(0, PATH_SEPARATOR[0])); else srcincpath.cpy(curpath->path); // append the filename piecemeal to account for directories int lastsepindex = 0; int sepindex; while ((sepindex = filename.chr(lastsepindex, '/')) != -1) { astring pathpart(filename, lastsepindex, sepindex - lastsepindex); // handle .. by removing a chunk from the incpath if (pathpart == "..") { int sepindex_part = srcincpath.rchr(0, PATH_SEPARATOR[0]); if (sepindex_part != -1) srcincpath.substr(0, sepindex_part); } // otherwise, append a path separator and the pathpart else srcincpath.cat(PATH_SEPARATOR).cat(pathpart); // advance past the previous index lastsepindex = sepindex + 1; } // now append the filename srcincpath.cat(PATH_SEPARATOR).catsubstr(filename, lastsepindex, -1); // see if we can open it core_file *testfile; if (core_fopen(srcincpath, OPEN_FLAG_READ, &testfile) == FILERR_NONE) { // close the file core_fclose(testfile); return true; } } return false; }