Advertisement
Guest User

Untitled

a guest
Dec 5th, 2019
115
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 12.38 KB | None | 0 0
  1. /*
  2.  * usage: ./dependencyDiscoverer [-Idir] ... file.c|file.l|file.y ...
  3.  *
  4.  * processes the c/yacc/lex source file arguments, outputting the dependencies
  5.  * between the corresponding .o file, the .c source file, and any included
  6.  * .h files
  7.  *
  8.  * each .h file is also processed to yield a dependency between it and any
  9.  * included .h files
  10.  *
  11.  * these dependencies are written to standard output in a form compatible with
  12.  * make; for example, assume that foo.c includes inc1.h, and inc1.h includes
  13.  * inc2.h and inc3.h; this results in
  14.  *
  15.  *                  foo.o: foo.c inc1.h inc2.h inc3.h
  16.  *
  17.  * note that system includes (i.e. those in angle brackets) are NOT processed
  18.  *
  19.  * dependencyDiscoverer uses the CPATH environment variable, which can contain a
  20.  * set of directories separated by ':' to find included files
  21.  * if any additional directories are specified in the command line,
  22.  * these are prepended to those in CPATH, left to right
  23.  *
  24.  * for example, if CPATH is "/home/user/include:/usr/local/group/include",
  25.  * and if "-Ifoo/bar/include" is specified on the command line, then when
  26.  * processing
  27.  *           #include "x.h"
  28.  * x.h will be located by searching for the following files in this order
  29.  *
  30.  *      ./x.h
  31.  *      foo/bar/include/x.h
  32.  *      /home/user/include/x.h
  33.  *      /usr/local/group/include/x.h
  34.  */
  35.  
  36. /*
  37.  * general design of main()
  38.  * ========================
  39.  * There are three globally accessible variables:
  40.  * - dirs: a vector storing the directories to search for headers
  41.  * - theTable: a hash table mapping file names to a list of dependent file names
  42.  * - workQ: a list of file names that have to be processed
  43.  *
  44.  * 1. look up CPATH in environment
  45.  * 2. assemble dirs vector from ".", any -Idir flags, and fields in CPATH
  46.  *    (if it is defined)
  47.  * 3. for each file argument (after -Idir flags)
  48.  *    a. insert mapping from file.o to file.ext (where ext is c, y, or l) into
  49.  *       table
  50.  *    b. insert mapping from file.ext to empty list into table
  51.  *    c. append file.ext on workQ
  52.  * 4. for each file on the workQ
  53.  *    a. lookup list of dependencies
  54.  *    b. invoke process(name, list_of_dependencies)
  55.  * 5. for each file argument (after -Idir flags)
  56.  *    a. create a hash table in which to track file names already printed
  57.  *    b. create a linked list to track dependencies yet to print
  58.  *    c. print "foo.o:", insert "foo.o" into hash table
  59.  *       and append "foo.o" to linked list
  60.  *    d. invoke printDependencies()
  61.  *
  62.  * general design for process()
  63.  * ============================
  64.  *
  65.  * 1. open the file
  66.  * 2. for each line of the file
  67.  *    a. skip leading whitespace
  68.  *    b. if match "#include"
  69.  *       i. skip leading whitespace
  70.  *       ii. if next character is '"'
  71.  *           * collect remaining characters of file name (up to '"')
  72.  *           * append file name to dependency list for this open file
  73.  *           * if file name not already in the master Table
  74.  *             - insert mapping from file name to empty list in master table
  75.  *             - append file name to workQ
  76.  * 3. close file
  77.  *
  78.  * general design for printDependencies()
  79.  * ======================================
  80.  *
  81.  * 1. while there is still a file in the toProcess linked list
  82.  * 2. fetch next file from toProcess
  83.  * 3. lookup up the file in the master table, yielding the linked list of dependencies
  84.  * 4. iterate over dependenceies
  85.  *    a. if the filename is already in the printed hash table, continue
  86.  *    b. print the filename
  87.  *    c. insert into printed
  88.  *    d. append to toProcess
  89.  *
  90.  * Additional helper functions
  91.  * ===========================
  92.  *
  93.  * dirName() - appends trailing '/' if needed
  94.  * parseFile() - breaks up filename into root and extension
  95.  * openFile()  - attempts to open a filename using the search path defined by the dirs vector.
  96.  */
  97.  
  98. #include <ctype.h>
  99. #include <stdio.h>
  100. #include <stdlib.h>
  101. #include <string.h>
  102.  
  103. #include <string>
  104. #include <vector>
  105. #include <unordered_map>
  106. #include <unordered_set>
  107. #include <list>
  108. #include <mutex>
  109. #include <optional>
  110. #include <condition_variable>
  111. #include <thread>
  112. #include <experimental/optional>
  113.  
  114. std::vector<std::string> dirs;
  115. hashTable theTable;
  116. work_q workQ;
  117.  
  118.  
  119. struct hashTable{
  120. private:
  121.     std::unordered_map<std::string,std::list<std::string>> map;
  122.     std::mutex mutex;
  123.     std::condition_variable ready;
  124. public:
  125.     void insert(std::string key,std::list<std::string> value) {
  126.         {
  127.             std::unique_lock <std::mutex> lock(mutex);
  128.             map.insert({key, {value}});
  129.         }
  130.         ready.notify_one();
  131.     }
  132.  
  133.     std::list<std::string> *at(std::string key){
  134.         {
  135.             std::unique_lock<std::mutex> lock(mutex);
  136.             return(&map[key]);
  137.         }
  138.     }
  139.     bool check(std::string key)
  140.     {
  141.         {
  142.             std::unique_lock<std::mutex> lock(mutex);
  143.             if(map.find(key) != map.end()){
  144.                 return (true);
  145.             }
  146.             return (false);
  147.         }
  148.     }
  149. };
  150.  
  151. struct work_q {
  152. private:
  153.     std::list<std::string> q;
  154.     std::mutex m;
  155.     std::condition_variable ready;
  156.     bool done = false;
  157. public:
  158.   std::optional<std::string> pop(){
  159.     std::unique_lock<std::mutex> lock(m);
  160.     ready.wait(lock, [this]{ return (!q.empty()) || done; });
  161.     if (q.empty()) return {};
  162.     auto f = q.front();
  163.     q.pop_front();
  164.     return f;
  165. }
  166. void push(std::string f) {
  167.     {
  168.         std::unique_lock<std::mutex> lock(m);
  169.         workQ.push(f);
  170.     }
  171.     ready.notify_one();
  172. }
  173. void setDone() {
  174.     {
  175.         std::unique_lock<std::mutex> lock(m);
  176.         done = true;
  177.     }
  178.     ready.notify_all();
  179. }
  180.  
  181. int getSize()
  182. {
  183.   std::unique_lock<std::mutex> lock(m);
  184.   int size = q.size();
  185.   return(size);
  186. }
  187. };
  188.  
  189.  
  190.  
  191. struct task_system {
  192. private:
  193.     int count;
  194.     std::vector<std::thread> threads;
  195.     void run(int i) {
  196.         while (true) {
  197.             auto optional_f = theTable.pop();
  198.             if (!optional_f.has_value()) return;
  199.             auto filename = optional_f.value();
  200.             process(filename.c_str(), theTable.look(optional_f));
  201.         }
  202.     }
  203. public:
  204.     task_system(int c){
  205.         count = c;
  206.         //printf("Start task system with %d threads\n", count);
  207.         for (auto n = 0; n != count; n++) {
  208.             threads.emplace_back([this, n](){ run(n); }); }
  209.     }
  210.     ~task_system() {
  211.         workQ.setDone();
  212.         for (auto n = 0; n != count; n++) { threads[n].join(); }
  213.     }
  214.     void async(std::string f) {
  215.         workQ.push(f);
  216.     }
  217. };
  218.  
  219.  
  220.  
  221. std::string dirName(const char * c_str) {
  222.   std::string s = c_str; // s takes ownership of the string content by allocating memory for it
  223.   if (s.back() != '/') { s += '/'; }
  224.   return s;
  225. }
  226.  
  227. std::pair<std::string, std::string> parseFile(const char* c_file) {
  228.   std::string file = c_file;
  229.   std::string::size_type pos = file.rfind('.');
  230.   if (pos == std::string::npos) {
  231.     return {file, ""};
  232.   } else {
  233.     return {file.substr(0, pos), file.substr(pos + 1)};
  234.   }
  235. }
  236.  
  237. // open file using the directory search path constructed in main()
  238. static FILE *openFile(const char *file) {
  239.   FILE *fd;
  240.   for (unsigned int i = 0; i < dirs.size(); i++) {
  241.     std::string path = dirs[i] + file;
  242.     fd = fopen(path.c_str(), "r");
  243.     if (fd != NULL)
  244.       return fd; // return the first file that successfully opens
  245.   }
  246.   return NULL;
  247. }
  248.  
  249. // process file, looking for #include "foo.h" lines
  250. static void process(const char *file, std::list<std::string> *ll) {
  251.   char buf[4096], name[4096];
  252.   // 1. open the file
  253.   FILE *fd = openFile(file);
  254.   if (fd == NULL) {
  255.     fprintf(stderr, "Error opening %s\n", file);
  256.     exit(-1);
  257.   }
  258.   while (fgets(buf, sizeof(buf), fd) != NULL) {
  259.     char *p = buf;
  260.     // 2a. skip leading whitespace
  261.     while (isspace((int)*p)) { p++; }
  262.     // 2b. if match #include
  263.     if (strncmp(p, "#include", 8) != 0) { continue; }
  264.     p += 8; // point to first character past #include
  265.     // 2bi. skip leading whitespace
  266.     while (isspace((int)*p)) { p++; }
  267.     if (*p != '"') { continue; }
  268.     // 2bii. next character is a "
  269.     p++; // skip "
  270.     // 2bii. collect remaining characters of file name
  271.     char *q = name;
  272.     while (*p != '\0') {
  273.       if (*p == '"') { break; }
  274.       *q++ = *p++;
  275.     }
  276.     *q = '\0';
  277.     // 2bii. append file name to dependency list
  278.     ll->push_back( {name} );
  279.     // 2bii. if file name not already in table ...
  280.     if (theTable.check(name) != theTable.end()) { continue; }
  281.     // ... insert mapping from file name to empty list in table ...
  282.     workQ.insert( { name, {} } );
  283.     // ... append file name to workQ
  284.     workQ.push( name );
  285.   }
  286.   // 3. close file
  287.   fclose(fd);
  288. }
  289.  
  290. // iteratively print dependencies
  291. static void printDependencies(std::unordered_set<std::string> *printed,
  292.                               std::list<std::string> *toProcess,
  293.                               FILE *fd) {
  294.   if (!printed || !toProcess || !fd) return;
  295.  
  296.   // 1. while there is still a file in the toProcess list
  297.   while ( toProcess->size() > 0 ) {
  298.     // 2. fetch next file to process
  299.     std::string name = toProcess->front();
  300.     toProcess->pop_front();
  301.     // 3. lookup file in the table, yielding list of dependencies
  302.     std::list<std::string> *ll = &theTable[name];
  303.     // 4. iterate over dependencies
  304.     for (auto iter = ll->begin(); iter != ll->end(); iter++) {
  305.       // 4a. if filename is already in the printed table, continue
  306.       if (printed->find(*iter) != printed->end()) { continue; }
  307.       // 4b. print filename
  308.       fprintf(fd, " %s", iter->c_str());
  309.       // 4c. insert into printed
  310.       printed->insert( *iter );
  311.       // 4d. append to toProcess
  312.       toProcess->push_back( *iter );
  313.     }
  314.   }
  315. }
  316.  
  317. int main(int argc, char *argv[]) {
  318.     // 1. look up CPATH in environment
  319.     char *cpath = getenv("CPATH");
  320.     int count =2;
  321.     // determine the number of -Idir arguments
  322.     int i;
  323.     for (i = 1; i < argc; i++) {
  324.         if (strncmp(argv[i], "-I", 2) != 0)
  325.             break;
  326.     }
  327.     int start = i;
  328.  
  329.     // 2. start assembling dirs vector
  330.     dirs.push_back(dirName("./")); // always search current directory first
  331.     for (i = 1; i < start; i++) {
  332.         dirs.push_back(dirName(argv[i] + 2 /* skip -I */));
  333.     }
  334.     if (cpath != NULL) {
  335.         std::string str(cpath);
  336.         std::string::size_type last = 0;
  337.         std::string::size_type next = 0;
  338.         while ((next = str.find(":", last)) != std::string::npos) {
  339.             dirs.push_back(str.substr(last, next - last));
  340.             last = next + 1;
  341.         }
  342.         dirs.push_back(str.substr(last));
  343.     }
  344.     // 2. finished assembling dirs vector
  345.     task_system tasks(count);
  346.     // 3. for each file argument ...
  347.     for (i = start; i < argc; i++) {
  348.         std::pair <std::string, std::string> pair = parseFile(argv[i]);
  349.         if (pair.second != "c" && pair.second != "y" && pair.second != "l") {
  350.             fprintf(stderr, "Illegal extension: %s - must be .c, .y or .l\n",
  351.                     pair.second.c_str());
  352.             return -1;
  353.         }
  354.  
  355.         std::string obj = pair.first + ".o";
  356.  
  357.         // 3a. insert mapping from file.o to file.ext
  358.         theTable.insert({obj, {argv[i]}});
  359.  
  360.         // 3b. insert mapping from file.ext to empty list
  361.         theTable.insert({argv[i], {}});
  362.  
  363.         // 3c. append file.ext on workQ
  364.         workQ.push(argv[i]);
  365.     }
  366.  
  367.  
  368.   // 4. for each file on the workQ
  369.   while ( workQ.size() > 0 ) {
  370.     std::string filename = workQ.front();
  371.     workQ.pop();
  372.  
  373.     if (theTable.find(filename) == theTable.end()) {
  374.       fprintf(stderr, "Mismatch between table and workQ\n");
  375.       return -1;
  376.     }
  377.  
  378.     // 4a&b. lookup dependencies and invoke 'process'
  379.     process(filename.c_str(), &theTable[filename]);
  380.   }
  381.  
  382.   // 5. for each file argument
  383.   for (i = start; i < argc; i++) {
  384.     // 5a. create hash table in which to track file names already printed
  385.     std::unordered_set<std::string> printed;
  386.     // 5b. create list to track dependencies yet to print
  387.     std::list<std::string> toProcess;
  388.  
  389.     std::pair<std::string, std::string> pair = parseFile(argv[i]);
  390.  
  391.     std::string obj = pair.first + ".o";
  392.     // 5c. print "foo.o:" ...
  393.     printf("%s:", obj.c_str());
  394.     // 5c. ... insert "foo.o" into hash table and append to list
  395.     printed.insert( obj );
  396.     toProcess.push_back( obj );
  397.     // 5d. invoke
  398.     printDependencies(&printed, &toProcess, stdout);
  399.  
  400.     printf("\n");
  401.   }
  402.  
  403.   return 0;
  404. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement