/* This program accepts a list of C or C++ source files for a project and outputs a dependency list suitable for a Makefile. It recursively parses the include files to insure that all dependencies are noted. Relative and absolute pathnames for both source and include files are correctly handled. Only include lines that start with `#include "' are looked at. Commented-out include lines are not ignored. Only dependency lists for the object files corresponding to the source files are generated. It is the responsibility of the programmer to supply the dependency lists for the desired executables and to define $(CC) and $(CFLAGS). If a file called `.makeheader' exists in the project directory, the contents of this file will be prepended to the output of this program. Thus, if the user places user-defined dependencies in this file, the command deps *.c > Makefile will correctly create a complete Makefile for a C project. Written by Keith Pomakis on October 10, 1994. Public Domain. */ #include #include #include #include #include "../KPbasic.h" #include "../KPList.h" #include "../KPString.h" #include "../KPSet.h" // Explicit instantiations of required templates. This is necessary for // g++ 2.6.x in conjunction with the "-fno-implicit-templates" option, but // may not be necessary in the future. template class KPSet; template class KPList; template class KPComparableList; template class KPSortableList; template class KPReadOnlyIterator; template class KPIterator; KPString progname; const char *const headerfile = ".makeheader"; /****************************************************************************/ static inline KPString base(const KPString &string) { return string.tokens('/').tail(); } static inline KPString path(const KPString &string) { return string.substr(0, string.length() - base(string).length()); } static inline bool is_relative(const KPString &string) { return string.is_empty() || string[0] != '/'; } static inline KPString root(const KPString &string) { return string.tokens('.').head(); } static void process_header(); static void process_source(const KPString &sourcename); static bool process_file(const KPString &filename, KPSet &includes, KPString pathname); /****************************************************************************/ int main(int argc, char *argv[]) { progname = base(argv[0]); if (argc < 2) { cerr << "Usage: " << progname << " -help\n" << " " << progname << " ...\n"; exit(EXIT_FAILURE); } if (strncmp(argv[1], "-h", 2) == 0) { cout << "This program accepts a list of C or C++ source files for a project\n" "and outputs a dependency list suitable for a Makefile. It\n" "recursively parses the include files to insure that all\n" "dependencies are noted. Relative and absolute pathnames for both\n" "source and include files are correctly handled. Only include lines\n" "that start with `#include \"' are looked at. Commented-out\n" "include lines are not ignored.\n\n" "Only dependency lists for the object files corresponding to the\n" "source files are generated. It is the responsibility of the\n" "programmer to supply the dependency lists for the desired\n" "executables and to define $(CC) and $(CFLAGS). If a file called\n" "`.makeheader' exists in the project directory, the contents of this\n" "file will be prepended to the output of this program. Thus, if the\n" "user places user-defined dependencies in this file, the command\n\n" " " << progname << " *.c > Makefile\n\n" "will correctly create a complete Makefile for a C project.\n\n" "Written by Keith Pomakis on October 10, 1994. Public Domain.\n"; exit(EXIT_SUCCESS); } process_header(); time_t current_time = time(NULL); cout << "\n###########################################################\n"; cout << "#\n"; cout << "# File dependencies generated by \"" << progname << "\".\n"; cout << "# " << ctime(¤t_time); cout << "#\n"; cout << "###########################################################\n\n"; for (int i=1; i includes = sourcename; cout << root(base(sourcename)) << ".o: \\\n"; if (process_file(sourcename, includes, path(sourcename))) { KPReadOnlyIterator iter(includes.list()); FOREACH(iter) { cout << " " << *iter; if (!iter.at_end()) cout << " \\"; cout << '\n'; } cout << "\t$(CC) $(CFLAGS) -c " << sourcename << "\n\n"; } } /****************************************************************************/ static bool process_file(const KPString &filename, KPSet &includes, KPString pathname) { ifstream stream(filename, ios::in); if (!stream) { cerr << progname << ": error opening file \"" << filename << "\"\n"; return false; } KPString line; while (!stream.eof()) { line.read_line(stream); if (line.length() > 11 && memcmp(line, "#include \"", 10) == 0) { const int second_quote = line.index_of('\"', 10); if (second_quote < 0 || second_quote == 10) { cerr << progname << ": error in file \"" << filename << "\" - bad include line:\n" << line << '\n'; return false; } KPString new_include = line.substr(10, second_quote-10); if (is_relative(new_include)) new_include = pathname + new_include; if (!includes.contains(new_include)) { includes += new_include; if (!process_file(new_include, includes, path(new_include))) return false; } } } return true; } /****************************************************************************/