From a5b57bb8bdb8074cadc97eb1b5b35ae19bad5700 Mon Sep 17 00:00:00 2001 From: ishmal Date: Mon, 19 Jun 2006 18:24:50 +0000 Subject: [PATCH] new file --- src/deptool.cpp | 1256 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1256 insertions(+) create mode 100644 src/deptool.cpp diff --git a/src/deptool.cpp b/src/deptool.cpp new file mode 100644 index 000000000..c35a63e1f --- /dev/null +++ b/src/deptool.cpp @@ -0,0 +1,1256 @@ +/** + * DepTool dependency tool + * + * This is a simple dependency generator coded in C++ + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2006 Bob Jamison + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + INSTRUCTIONS + + 1. First you optionally create a file named 'make.exclude.' This + lists the file names relative to the current directory. Lines starting + with a '#' are ignored. Here is an example: + +========= SNIP ======== +###################################################################### +# File: make.exclude +# +# This is a list of files to exclude from building, using DepTool +# +###################################################################### + + +ast +bonobo + +dialogs/filedialog-win32.cpp +display/testnr.cpp +display/bezier-utils-test.cpp +dom/work +#next line is ignored +#dom/odf/SvgOdg.cpp +extension/api.cpp +========= SNIP ======== + +2. Run deptool. This will take a few seconds to scan all of your files +and make its dependency lists. Three files are created: +make.files: lists all of the files that were considered +make.dep: contains the output INCLUDE and OBJECT declarations + for you to include and use in your makefile. It also contains + the dependency listings. +make.ref: contains a reverse-dependency listing. This takes each file + and recursively determines which other files include it, and how + far the nesting is. + +3. Include deptool.mk in your makefile. + +*/ + + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + +//# This will allow us to redefine the string in the future +typedef std::string DTString; + +/** + * Class which holds information for each file. + */ +class FileRec +{ +public: + + typedef enum + { + UNKNOWN, + CFILE, + HFILE, + OFILE + } FileType; + + /** + * Constructor + */ + FileRec() + {type = UNKNOWN;} + + /** + * Copy constructor + */ + FileRec(const FileRec &other) + {assign(other);} + /** + * Constructor + */ + FileRec(int typeVal) + {init(); type = typeVal;} + /** + * Assignment operator + */ + FileRec &operator=(const FileRec &other) + {assign(other); return *this;} + + + /** + * Destructor + */ + ~FileRec() + {} + + /** + * Directory part of the file name + */ + DTString path; + + /** + * Base name, sans directory and suffix + */ + DTString baseName; + + /** + * File extension, such as cpp or h + */ + DTString suffix; + + /** + * Type of file: CFILE, HFILE, OFILE + */ + int type; + + /** + * 'Distance' of inclusion from a file that depends on this one. + */ + int distance; + + /** + * Used to list files ref'd by this one, in the case of allFiles, + * or other files which reference this one, such as refFiles; + */ + std::map files; + + +private: + + void init() + { + } + + void assign(const FileRec &other) + { + type = other.type; + distance = other.distance; + baseName = other.baseName; + suffix = other.suffix; + files = other.files; + } + +}; + + + +/** + * Main class which does the work. + */ +class DepTool +{ +public: + + /** + * Constructor + */ + DepTool(); + + /** + * Destructor + */ + virtual ~DepTool(); + + /** + * Creates the list of all file names which will be + * candidates for further processing. Reads make.exclude + * to see which files for directories to leave out. + */ + virtual bool createFileList(); + + + /** + * Generates the forward and reverse dependency lists + */ + virtual bool generateDependencies(); + + /** + * Calls the other two methods, then generates the files. + */ + virtual bool run(); + + +private: + + /** + * + */ + void reset(); + + /** + * + */ + void error(char *fmt, ...); + + /** + * + */ + void trace(char *fmt, ...); + + /** + * Removes whitespace from beginning and end of a string + */ + DTString trim(const DTString &val); + + /** + * + */ + DTString getSuffix(const DTString &fname); + + /** + * + */ + void parseName(const DTString &fullname, + DTString &path, + DTString &basename, + DTString &suffix); + + /** + * + */ + int get(int pos); + + /** + * + */ + int skipwhite(int pos); + + /** + * + */ + int getword(int pos, DTString &ret); + + /** + * + */ + bool sequ(int pos, char *key); + + /** + * + */ + bool listFilesInDirectory(const DTString &dirname, int depth); + + /** + * + */ + bool saveFileList(); + + /** + * + */ + bool saveDepFile(); + + /** + * + */ + bool saveRefFile(); + + /** + * + */ + bool addIncludeFile(FileRec *frec, const DTString &fname); + + /** + * + */ + bool scanFile(const DTString &fname, FileRec *frec); + + /** + * + */ + bool processDependency(FileRec *ofile, + FileRec *include, + int depth); + + /** + * + */ + bool processReference(FileRec *ofile, + DTString &fname, + int depth); + + /** + * + */ + std::set excludes; + + /** + * + */ + std::set excludesUsed; + + /** + * + */ + std::set excludesUnused; + + /** + * + */ + std::vector directories; + + /** + * A list of all files which will be processed for + * dependencies. + */ + std::map allFiles; + + /** + * A list of .h files, with a list for each one + * of which other files include them. + */ + std::map refFiles; + + /** + * The list of .o files, and the + * dependencies upon them. + */ + std::map depFiles; + + char *fileBuf; + int fileSize; + + +}; + + +//######################################################################## +//# M A I N +//######################################################################## + +/** + * Constructor + */ +DepTool::DepTool() +{ +} + + +/** + * Destructor + */ +DepTool::~DepTool() +{ + reset(); +} + +/** + * Clean up after processing. Called by the destructor, but should + * also be called before the object is reused. + */ +void DepTool::reset() +{ + std::map::iterator iter; + for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++) + { + FileRec *frec = iter->second; + delete frec; + } + allFiles.clear(); + for (iter=refFiles.begin() ; iter!=refFiles.end() ; iter++) + { + FileRec *frec = iter->second; + delete frec; + } + refFiles.clear(); + for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++) + { + FileRec *frec = iter->second; + delete frec; + } + depFiles.clear(); + + excludes.clear(); + excludesUsed.clear(); + excludesUnused.clear(); +} + + +/** + * Format an error message in printf() style + */ +void DepTool::error(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "DepTool error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + + +/** + * Format an trace message in printf() style + */ +void DepTool::trace(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stdout, "DepTool: "); + vfprintf(stdout, fmt, ap); + fprintf(stdout, "\n"); + va_end(ap); +} + + + +//######################################################################## +//# U T I L I T Y +//######################################################################## + +/** + * Removes whitespace from beginning and end of a string + */ +DTString DepTool::trim(const DTString &s) +{ + if (s.size() < 1) + return s; + + //Find first non-ws char + unsigned int begin = 0; + for ( ; begin < s.size() ; begin++) + { + if (!isspace(s[begin])) + break; + } + + //Find first non-ws char, going in reverse + unsigned int end = s.size() - 1; + for ( ; end > begin ; end--) + { + if (!isspace(s[end])) + break; + } + //trace("begin:%d end:%d", begin, end); + + DTString res = s.substr(begin, end-begin+1); + return res; +} + + +/** + * Parse a full path name into path, base name, and suffix + */ +void DepTool::parseName(const DTString &fullname, + DTString &path, + DTString &basename, + DTString &suffix) +{ + if (fullname.size() < 2) + return; + + unsigned int pos = fullname.find_last_of('/'); + if (pos != fullname.npos && pos subdirs; + DIR *dir = opendir(dirname.c_str()); + while (true) + { + struct dirent *de = readdir(dir); + if (!de) + break; + DTString s = de->d_name; + struct stat finfo; + DTString fname; + if (s.size() == 0 || s[0] == '.') + continue; + + if (dirname.size()>0 && dirname != ".") + { + fname.append(dirname); + fname.append("/"); + } + fname.append(s); + if (stat(fname.c_str(), &finfo)<0) + { + error("cannot stat file:%s", s.c_str()); + } + else if (excludes.find(fname) != excludes.end()) + { + //trace("excluded file/dir: %s", fname.c_str()); + excludesUsed.insert(fname); + } + else if (S_ISDIR(finfo.st_mode)) + { + //trace("directory: %s", fname.c_str()); + subdirs.push_back(fname); + } + else if (!S_ISREG(finfo.st_mode)) + { + trace("not regular: %s", fname.c_str()); + } + else + { + DTString path; + DTString basename; + DTString sfx; + parseName(fname, path, basename, sfx); + if (sfx == "cpp" || sfx == "c" || sfx == "cxx" || sfx == "cc") + { + cFiles++; + FileRec *fe = new FileRec(FileRec::CFILE); + fe->path = path; + fe->baseName = basename; + fe->suffix = sfx; + allFiles[fname] = fe; + } + else if (sfx == "h" || sfx == "hh" || + sfx == "hpp" || sfx == "hxx") + { + hFiles++; + FileRec *fe = new FileRec(FileRec::HFILE); + fe->path = path; + fe->baseName = basename; + fe->suffix = sfx; + allFiles[fname] = fe; + } + } + } + closedir(dir); + + if (hFiles > 0) + directories.push_back(dirname); + + for (unsigned int i=0 ; i 0 && s[0]!='#') + excludes.insert(s); + } + fclose(f); + } + + listFilesInDirectory(".", 0); + + // Note which files in the exclude list were not used. + std::set::iterator iter; + for (iter=excludes.begin() ; iter!=excludes.end() ; iter++) + { + DTString fname = *iter; + std::set::iterator citer = excludesUsed.find(fname); + if (citer == excludesUsed.end()) + excludesUnused.insert(fname); + } + + saveFileList(); + + return true; +} + + +/** + * Get a character from the buffer at pos. If out of range, + * return -1 for safety + */ +int DepTool::get(int pos) +{ + if (pos>fileSize) + return -1; + return fileBuf[pos]; +} + + + +/** + * Skip over all whitespace characters beginning at pos. Return + * the position of the first non-whitespace character. + */ +int DepTool::skipwhite(int pos) +{ + while (pos < fileSize) + { + int ch = get(pos); + if (ch < 0) + break; + if (!isspace(ch)) + break; + pos++; + } + return pos; +} + + +/** + * Parse the buffer beginning at pos, for a word. Fill + * 'ret' with the result. Return the position after the + * word. + */ +int DepTool::getword(int pos, DTString &ret) +{ + while (pos < fileSize) + { + int ch = get(pos); + if (ch < 0) + break; + if (isspace(ch)) + break; + ret.push_back((char)ch); + pos++; + } + return pos; +} + +/** + * Return whether the sequence of characters in the buffer + * beginning at pos match the key, for the length of the key + */ +bool DepTool::sequ(int pos, char *key) +{ + while (*key) + { + if (*key != get(pos)) + return false; + key++; pos++; + } + return true; +} + + + +/** + * Add an include file name to a file record. If the name + * is not found in allFiles explicitly, try prepending include + * directory names to it and try again. + */ +bool DepTool::addIncludeFile(FileRec *frec, const DTString &iname) +{ + + std::map::iterator iter = + allFiles.find(iname); + if (iter != allFiles.end()) + { + //h file in same dir + FileRec *other = iter->second; + //trace("local: '%s'", iname.c_str()); + frec->files[iname] = other; + return true; + } + else + { + //look in other dirs + std::vector::iterator diter; + for (diter=directories.begin() ; + diter!=directories.end() ; diter++) + { + DTString dfname = *diter; + dfname.append("/"); + dfname.append(iname); + iter = allFiles.find(dfname); + if (iter != allFiles.end()) + { + FileRec *other = iter->second; + //trace("other: '%s'", iname.c_str()); + frec->files[dfname] = other; + return true; + } + } + } + return true; +} + + + +/** + * Lightly parse a file to find the #include directives. Do + * a bit of state machine stuff to make sure that the directive + * is valid. (Like not in a comment). + */ +bool DepTool::scanFile(const DTString &fname, FileRec *frec) +{ + FILE *f = fopen(fname.c_str(), "r"); + if (!f) + { + error("Could not open '%s' for reading", fname.c_str()); + return false; + } + DTString buf; + while (true) + { + int ch = fgetc(f); + if (ch < 0) + break; + buf.push_back((char)ch); + } + fclose(f); + + fileSize = buf.size(); + fileBuf = (char *)buf.c_str(); + int pos = 0; + + + while (pos < fileSize) + { + //trace("p:%c", get(pos)); + + //# Block comment + if (get(pos) == '/' && get(pos+1) == '*') + { + pos += 2; + while (pos < fileSize) + { + if (get(pos) == '*' && get(pos+1) == '/') + { + pos += 2; + break; + } + else + pos++; + } + } + //# Line comment + else if (get(pos) == '/' && get(pos+1) == '/') + { + pos += 2; + while (pos < fileSize) + { + if (get(pos) == '\n') + { + pos++; + break; + } + else + pos++; + } + } + //# #include! yaay + else if (sequ(pos, "#include")) + { + pos += 8; + pos = skipwhite(pos); + DTString iname; + pos = getword(pos, iname); + if (iname.size()>2) + { + iname = iname.substr(1, iname.size()-2); + addIncludeFile(frec, iname); + } + } + else + { + pos++; + } + } + + return true; +} + + + +/** + * Recursively check include lists to find all files in allFiles to which + * a given file is dependent. + */ +bool DepTool::processDependency(FileRec *ofile, + FileRec *include, + int depth) +{ + std::map::iterator iter; + for (iter=include->files.begin() ; iter!=include->files.end() ; iter++) + { + DTString fname = iter->first; + if (ofile->files.find(fname) != ofile->files.end()) + { + //trace("file '%s' already seen", fname.c_str()); + continue; + } + FileRec *child = iter->second; + ofile->files[fname] = child; + + processDependency(ofile, child, depth+1); + } + + + return true; +} + + + +/** + * Recursively check include lists to find all files in allFiles which + * will eventually have a dependency on this file. This is basically + * the inverse of processDependency(). + */ +bool DepTool::processReference(FileRec *hfile, + DTString &fname, + int depth) +{ + std::map::iterator iter; + for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++) + { + FileRec *frec = iter->second; + std::map::iterator fiter = + frec->files.find(fname); + if (fiter != frec->files.end()) + { + DTString cfname = iter->first; + if (hfile->files.find(cfname) != hfile->files.end()) + { + //trace("%d reffile '%s' already seen", depth, cfname.c_str()); + continue; + } + FileRec *child = iter->second; + child->distance = depth; + hfile->files[cfname] = child; + processReference(hfile, cfname, depth+1); + } + } + + return true; +} + + + +/** + * Generate the file dependency and reference lists. + */ +bool DepTool::generateDependencies() +{ + std::map::iterator iter; + //# First pass. Scan for all includes + for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++) + { + FileRec *frec = iter->second; + if (!scanFile(iter->first, frec)) + { + //quit? + } + } + + //# Second pass. Scan for all includes + for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++) + { + FileRec *include = iter->second; + if (include->type == FileRec::CFILE) + { + DTString cFileName = iter->first; + FileRec *ofile = new FileRec(FileRec::OFILE); + ofile->path = include->path; + ofile->baseName = include->baseName; + ofile->suffix = include->suffix; + DTString fname = include->path; + if (fname.size()>0) + fname.append("/"); + fname.append(include->baseName); + fname.append(".o"); + depFiles[fname] = ofile; + //add the .c file first, of course + ofile->files[cFileName] = include; + //trace("ofile:%s", fname.c_str()); + + processDependency(ofile, include, 0); + } + else if (include->type == FileRec::HFILE) + { + DTString fname = iter->first; + FileRec *hfile = new FileRec(FileRec::HFILE); + hfile->path = include->path; + hfile->baseName = include->baseName; + hfile->suffix = include->suffix; + refFiles[fname] = hfile; + //trace("hfile:%s", fname.c_str()); + + processReference(hfile, fname, 0); + } + } + + + return true; +} + + +/** + * High-level call to do what DepTool does. + */ +bool DepTool::run() +{ + reset(); + if (!createFileList()) + return false; + if (!generateDependencies()) + return false; + saveDepFile(); + saveRefFile(); + return true; +} + + +//######################################################################## +//# O U T P U T S +//######################################################################## + + + +/** + * Save the allFiles list. This is basically all files in a directory + * except those denied in the exclude list. + */ +bool DepTool::saveFileList() +{ + time_t tim; + time(&tim); + + FILE *f = fopen("make.files", "w"); + if (!f) + { + trace("cannot open 'make.files' for writing"); + } + fprintf(f, "########################################################\n"); + fprintf(f, "## File: make.files\n"); + fprintf(f, "## Generated by DepTool at :%s", ctime(&tim)); + fprintf(f, "########################################################\n"); + + fprintf(f, "\n\n\n"); + fprintf(f, "########################################################\n"); + fprintf(f, "## F I L E S\n"); + fprintf(f, "########################################################\n"); + + std::map::iterator iter; + for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++) + { + fprintf(f, "%s\n", iter->first.c_str()); + } + + fprintf(f, "\n\n\n"); + fprintf(f, "########################################################\n"); + fprintf(f, "## E X C L U D E D\n"); + fprintf(f, "########################################################\n"); + + std::set::iterator uiter; + for (uiter=excludesUsed.begin() ; uiter!=excludesUsed.end() ; uiter++) + { + DTString fname = *uiter; + fprintf(f, "%s\n", fname.c_str()); + } + + fprintf(f, "\n\n\n"); + fprintf(f, "########################################################\n"); + fprintf(f, "## E X C L U D E entries unused\n"); + fprintf(f, "########################################################\n"); + + for (uiter=excludesUnused.begin() ; uiter!=excludesUnused.end() ; uiter++) + { + DTString fname = *uiter; + fprintf(f, "%s\n", fname.c_str()); + } + + fprintf(f, "\n\n\n"); + fprintf(f, "########################################################\n"); + fprintf(f, "## E N D\n"); + fprintf(f, "########################################################\n"); + + fclose(f); + + return true; +} + + +/** + * This is the main product. This file lists the Include directives, + * the Object list, and the dependency list. + */ +bool DepTool::saveDepFile() +{ + time_t tim; + time(&tim); + + FILE *f = fopen("make.dep", "w"); + if (!f) + { + trace("cannot open 'make.dep' for writing"); + } + fprintf(f, "########################################################\n"); + fprintf(f, "## File: make.dep\n"); + fprintf(f, "## Generated by DepTool at :%s", ctime(&tim)); + fprintf(f, "########################################################\n"); + + fprintf(f, "\n\n"); + + fprintf(f, "########################################################\n"); + fprintf(f, "## I N C L U D E\n"); + fprintf(f, "########################################################\n"); + fprintf(f, "DEPTOOL_INCLUDE ="); + + std::vector::iterator inciter; + for (inciter=directories.begin() ; inciter!=directories.end() ; inciter++) + { + fprintf(f, " \\\n"); + DTString dirname = *inciter; + fprintf(f, "-I%s", dirname.c_str()); + } + + fprintf(f, "\n\n\n"); + fprintf(f, "########################################################\n"); + fprintf(f, "## O B J E C T S\n"); + fprintf(f, "########################################################\n"); + fprintf(f, "DEPTOOL_OBJECTS ="); + + std::map::iterator oiter; + for (oiter=allFiles.begin() ; oiter!=allFiles.end() ; oiter++) + { + FileRec *frec = oiter->second; + if (frec->type == FileRec::CFILE) + { + fprintf(f, " \\\n"); + DTString fname = frec->path; + if (fname.size()>0) + fname.append("/"); + fname.append(frec->baseName); + fname.append(".o"); + fprintf(f, "%s", fname.c_str()); + } + } + + + fprintf(f, "\n\n\n"); + fprintf(f, "########################################################\n"); + fprintf(f, "## D E P E N D E N C I E S\n"); + fprintf(f, "########################################################\n"); + std::map::iterator iter; + for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++) + { + FileRec *frec = iter->second; + if (frec->type == FileRec::OFILE) + { + DTString fname = iter->first; + fprintf(f, "%s:", fname.c_str()); + std::map::iterator citer; + for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++) + { + DTString cfname = citer->first; + fprintf(f, " \\\n"); + fprintf(f, "\t%s", cfname.c_str()); + } + fprintf(f, "\n\n\n"); + } + } + + fprintf(f, "\n\n\n"); + fprintf(f, "########################################################\n"); + fprintf(f, "## E N D\n"); + fprintf(f, "########################################################\n"); + + fclose(f); + + return true; +} + + +/** + * Save the "reference" file, which lists each include file, and any files + * that are judged to be dependent upon it. + */ +bool DepTool::saveRefFile() +{ + time_t tim; + time(&tim); + + FILE *f = fopen("make.ref", "w"); + if (!f) + { + trace("cannot open 'make.ref' for writing"); + } + fprintf(f, "########################################################\n"); + fprintf(f, "## File: make.ref\n"); + fprintf(f, "## Generated by DepTool at :%s", ctime(&tim)); + fprintf(f, "## Note: The metric is the 'distance' of inclusion from\n"); + fprintf(f, "## the given file.\n"); + fprintf(f, "########################################################\n"); + + fprintf(f, "\n\n"); + + std::map::iterator iter; + for (iter=refFiles.begin() ; iter!=refFiles.end() ; iter++) + { + FileRec *frec = iter->second; + if (frec->type == FileRec::HFILE) + { + DTString fname = iter->first; + fprintf(f, "### %s\n", fname.c_str()); + std::map::iterator citer; + for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++) + { + DTString cfname = citer->first; + fprintf(f, "%3d %s\n", citer->second->distance, + cfname.c_str()); + } + fprintf(f, "\n"); + } + } + + fprintf(f, "\n\n\n"); + fprintf(f, "########################################################\n"); + fprintf(f, "## E N D\n"); + fprintf(f, "########################################################\n"); + + fclose(f); + + return true; +} + + + + + + +//######################################################################## +//# M A I N +//######################################################################## + + +/** + * Run the DepTool's main functions + */ +static bool runTool() +{ + DepTool depTool; + + if (!depTool.run()) + return false; + + return true; +} + + +/** + * Console main() + */ +int main(int argc, char **argv) +{ + if (!runTool()) + return 1; + + return 0; +} + + +//######################################################################## +//# E N D O F F I L E +//######################################################################## -- 2.30.2