Main?Page | Class?Hierarchy | Class?List | File?List | Class?Members | File?Members

cmt_deps_builder.cxx

Go to the documentation of this file.
00001 //-----------------------------------------------------------
00002 // Copyright Christian Arnault LAL-Orsay CNRS
00003 // 
00004 // See the complete license in cmt_license.txt "http://www.cecill.info". 
00005 //-----------------------------------------------------------
00006 
00007 #include "cmt_deps_builder.h"
00008 #include "cmt_system.h"
00009 #include "cmt_use.h"
00010 #include "cmt_include.h"
00011 #include "cmt_symbol.h"
00012 #include "cmt_log.h"
00013 
00014 //
00015 //  While parsing a C++ file, these are the possible usefull
00016 // states we can reach.
00017 //  Each state correspond to a specific state action function.
00018 //
00019 enum state_def
00020 {
00021   at_start,                                // beginning of the file
00022   in_line,                                // along a line
00023   in_string,                        // inside a quoted string
00024   in_char,                                // inside a quoted char
00025   in_comment,                        // inside a multi-line comment
00026   in_string_comment,        // inside a quoted string in a comment
00027   in_char_comment,                // inside a quoted char in a comment
00028   in_line_comment                // inside a single-line comment
00029 };
00030 
00031 //--------------------------------------------------
00032 static int build_deps (const cmt_string& name,
00033                        const cmt_string& dir_name,
00034                        int current_path_index,
00035                        const CmtSystem::cmt_string_vector& include_paths,
00036                        const CmtSystem::cmt_string_vector& substitutions,
00037                        CmtSystem::cmt_string_vector& all_deps,
00038                        CmtSystem::cmt_string_vector& deps);
00039 
00040 
00041 //--------------------------------------------------
00042 static void header_file_action (const char* header_file,
00043                                 const cmt_string& dir_name,
00044                                 int current_path_index,
00045                                 const CmtSystem::cmt_string_vector& include_paths,
00046                                 const CmtSystem::cmt_string_vector& substitutions,
00047                                 CmtSystem::cmt_string_vector& all_deps,
00048                                 CmtSystem::cmt_string_vector& deps)
00049 {
00050   bool found = false;
00051 
00052   for (int i = 0; i < all_deps.size (); i++)
00053     {
00054       if (all_deps[i] == header_file)
00055         {
00056           found = true;
00057           break;
00058         }
00059     }
00060   
00061   if (!found)
00062     {
00063       all_deps.push_back (header_file);
00064       
00065       int path_index = build_deps (header_file,
00066                                    dir_name,
00067                                    current_path_index,
00068                                    include_paths,
00069                                    substitutions,
00070                                    all_deps,
00071                                    deps);
00072       
00073       if (path_index >= 0)
00074         {
00075           cmt_string full_name;
00076           
00077           if (path_index == 1)
00078             {
00079               full_name = dir_name;
00080               full_name += CmtSystem::file_separator ();
00081               
00082               if (current_path_index >= 2)
00083                 {
00084                   full_name.replace (include_paths[current_path_index - 2],
00085                                      substitutions[current_path_index - 2]);
00086                 }
00087             }
00088           else if (path_index > 1)
00089             {
00090               full_name  = substitutions[path_index - 2];
00091               full_name += CmtSystem::file_separator ();
00092             }
00093           
00094           full_name += header_file;
00095           
00096           deps.push_back (full_name);
00097         }
00098     }
00099 }
00100 
00101 
00102 //--------------------------------------------------
00103 static char* at_start_action (char* ptr,
00104                               state_def& state,
00105                               const cmt_string& dir_name,
00106                               int current_path_index,
00107                               const CmtSystem::cmt_string_vector& include_paths,
00108                               const CmtSystem::cmt_string_vector& substitutions,
00109                               CmtSystem::cmt_string_vector& all_deps,
00110                               CmtSystem::cmt_string_vector& deps)
00111 {
00112   char term = 0;
00113 
00114   if (*ptr == '#')
00115     {
00116       ptr++;
00117       while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
00118       if (!strncmp (ptr, "include", 7))
00119         {
00120           ptr += 7;
00121 
00122           while (*ptr == ' ') ptr++;
00123           if (*ptr == '<')
00124             {
00125               term = '>';
00126               ptr++;
00127             }
00128           else if (*ptr == '"')
00129             {
00130               term = '"';
00131               ptr++;
00132             }
00133           else
00134             {
00135               state = in_line;
00136               ptr += strlen (ptr);
00137               return (ptr);
00138             }
00139         }
00140       else
00141         {
00142           state = in_line;
00143           ptr += strlen (ptr);
00144           return (ptr);
00145         }
00146     }
00147   else if (!strncmp (ptr, "      include", 13))
00148     {
00149       ptr += 13;
00150 
00151       while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
00152       if (*ptr == '\'')
00153         {
00154           term = '\'';
00155           ptr++;
00156         }
00157       else
00158         {
00159           state = in_line;
00160           return (ptr);
00161         }
00162     }
00163   else if (!strncmp (ptr, "\tinclude", 8))
00164     {
00165       ptr += 8;
00166 
00167       while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
00168       if (*ptr == '\'')
00169         {
00170           term = '\'';
00171           ptr++;
00172         }
00173       else
00174         {
00175           state = in_line;
00176           return (ptr);
00177         }
00178     }
00179   else
00180     {
00181       state = in_line;
00182       return (ptr);
00183     }
00184 
00185   char* end;
00186 
00187   end = strchr (ptr, term);
00188   if (end != 0)
00189     {
00190       *end = 0;
00191     }
00192   
00193   const char* header_file = ptr;
00194   
00195   header_file_action (header_file,
00196                       dir_name,
00197                       current_path_index,
00198                       include_paths,
00199                       substitutions,
00200                       all_deps,
00201                       deps);
00202   
00203   if (end != 0)
00204     {
00205       *end = term;
00206     }
00207 
00208   state = in_line;
00209   ptr += strlen (ptr);
00210   
00211   return (ptr);
00212 }
00213 
00214 //--------------------------------------------------
00215 static char* in_line_action (char* ptr, state_def& state)
00216 {
00217   char* pattern = &ptr[strlen (ptr)];
00218 
00219   char* pos = strchr (ptr, '"');
00220   if (pos != 0)
00221     {
00222       if (pos < pattern)
00223         {
00224           state = in_string;
00225           pattern = pos;
00226         }
00227     }
00228 
00229   pos = strchr (ptr, '\'');
00230   if (pos != 0)
00231     {
00232       if (pos < pattern)
00233         {
00234           state = in_char;
00235           pattern = pos;
00236         }
00237     }
00238 
00239   pos = strstr (ptr, "/*");   //*/
00240   if (pos != 0)
00241     {
00242       if (pos < pattern)
00243         {
00244           state = in_comment;
00245           pattern = pos + 1;
00246         }
00247     }
00248 
00249   pos = strstr (ptr, "//");
00250   if (pos != 0)
00251     {
00252       if (pos < pattern)
00253         {
00254           state = in_line_comment;
00255           pattern = pos + 1;
00256         }
00257     }
00258 
00259   if (state != in_line)
00260     {
00261       ptr = pattern + 1;
00262     }
00263   else
00264     {
00265       ptr += strlen (ptr);
00266     }
00267 
00268   return (ptr);
00269 }
00270 
00271 //--------------------------------------------------
00272 static char* in_string_action (char* ptr, state_def& state)
00273 {
00274   char* pos = strchr (ptr, '"');
00275   if (pos == 0)
00276     {
00277         // This string is not finished till the end of the line..
00278         // we expect it continues to the nex line...
00279       ptr += strlen (ptr);
00280     }
00281   else
00282     {
00283       pos--;
00284       if (*pos == '\\')
00285         {
00286           ptr = pos + 2;
00287         }
00288       else
00289         {
00290           ptr = pos + 2;
00291           state = in_line;
00292         }
00293     }
00294 
00295   return (ptr);
00296 }
00297 
00298 //--------------------------------------------------
00299 static char* in_char_action (char* ptr, state_def& state)
00300 {
00301   char* pos = strchr (ptr, '\'');
00302   if (pos == 0)
00303     {
00304         // This string is not finished till the end of the line..
00305         // we expect it continues to the nex line...
00306       ptr += strlen (ptr);
00307     }
00308   else
00309     {
00310       pos--;
00311       if (*pos == '\\')
00312         {
00313           ptr = pos + 2;
00314         }
00315       else
00316         {
00317           ptr = pos + 2;
00318           state = in_line;
00319         }
00320     }
00321 
00322   return (ptr);
00323 }
00324 
00325 //--------------------------------------------------
00326 static char* in_comment_action (char* ptr, state_def& state)
00327 {
00328   char* pattern = &ptr[strlen (ptr)];
00329   char* pos = strchr (ptr, '"');
00330   if (pos != 0)
00331     {
00332       if (pos < pattern)
00333         {
00334           state = in_string_comment;
00335           pattern = pos;
00336         }
00337     }
00338   pos = strchr (ptr, '\'');
00339   if (pos != 0)
00340     {
00341       if (pos < pattern)
00342         {
00343           state = in_char_comment;
00344           pattern = pos;
00345         }
00346     }
00347   pos = strstr (ptr, "*/");
00348   if (pos != 0)
00349     {
00350       if (pos < pattern)
00351         {
00352           state = in_line;
00353           pattern = pos + 1;
00354         }
00355     }
00356 
00357   if (state == in_comment)
00358     {
00359       ptr += strlen (ptr);
00360     }
00361   else
00362     {
00363       ptr = pattern + 1;
00364     }
00365 
00366   return (ptr);
00367 }
00368 
00369 //--------------------------------------------------
00370 static char* in_string_comment_action (char* ptr, state_def& state)
00371 {
00372   char* pos = strchr (ptr, '"');
00373   if (pos == 0)
00374     {
00375         // This string is not finished till the end of the line..
00376         // we expect it continues to the nex line...
00377       ptr += strlen (ptr);
00378     }
00379   else
00380     {
00381       pos--;
00382       if (*pos == '\\')
00383         {
00384           ptr = pos + 2;
00385         }
00386       else
00387         {
00388           ptr = pos + 2;
00389           state = in_comment;
00390         }
00391     }
00392 
00393   return (ptr);
00394 }
00395 
00396 //--------------------------------------------------
00397 static char* in_char_comment_action (char* ptr, state_def& state)
00398 {
00399   char* pos = strchr (ptr, '\'');
00400   if (pos == 0)
00401     {
00402         // This string is not finished till the end of the line..
00403         // we expect it continues to the nex line...
00404       ptr += strlen (ptr);
00405     }
00406   else
00407     {
00408       pos--;
00409       if (*pos == '\\')
00410         {
00411           ptr = pos + 2;
00412         }
00413       else
00414         {
00415           ptr = pos + 2;
00416           state = in_comment;
00417         }
00418     }
00419 
00420   return (ptr);
00421 }
00422 
00423 //--------------------------------------------------
00424 static char* in_line_comment_action (char* ptr, state_def& state)
00425 {
00426   ptr += strlen (ptr);
00427 
00428   return (ptr);
00429 }
00430 
00431 //--------------------------------------------------
00432 static void build_deps_stream (istream& input,
00433                                const cmt_string& dir_name,
00434                                int current_path_index,
00435                                const CmtSystem::cmt_string_vector& include_paths,
00436                                const CmtSystem::cmt_string_vector& substitutions,
00437                                CmtSystem::cmt_string_vector& all_deps,
00438                                CmtSystem::cmt_string_vector& deps)
00439 {
00440   Log;
00441 
00442   if (input)
00443     {
00444       log << "CMT> build_deps_stream dir_name="
00445           << dir_name << log_endl;
00446 
00447       while (!input.eof ())
00448         {
00449           char line[16384];
00450 
00451           input.getline (line, sizeof (line));
00452           char* ptr = &line[0];
00453           state_def state = at_start;
00454 
00455           log << "CMT> build_deps_stream2 line=[" 
00456               << line << "]" << log_endl;
00457 
00458           while (strlen (ptr) > 0)
00459             {
00460               switch (state)
00461                 {
00462                   case at_start:
00463                     ptr = at_start_action (ptr,
00464                                            state,
00465                                            dir_name,
00466                                            current_path_index,
00467                                            include_paths,
00468                                            substitutions,
00469                                            all_deps,
00470                                            deps);
00471                     break;
00472                   case in_line:
00473                     ptr = in_line_action (ptr, state);
00474                     break;
00475                   case in_string:
00476                     ptr = in_string_action (ptr, state);
00477                     break;
00478                   case in_char:
00479                     ptr = in_char_action (ptr, state);
00480                     break;
00481                   case in_comment:
00482                     ptr = in_comment_action (ptr, state);
00483                     break;
00484                   case in_string_comment:
00485                     ptr = in_string_comment_action (ptr, state);
00486                     break;
00487                   case in_char_comment:
00488                     ptr = in_char_comment_action (ptr, state);
00489                     break;
00490                   case in_line_comment:
00491                     ptr = in_line_action (ptr, state);
00492                     break;
00493                 }
00494             }
00495         }
00496     }
00497 }
00498 
00499 //--------------------------------------------------
00500 static int build_deps (const cmt_string& name,
00501                        const cmt_string& dir_name,
00502                        int current_path_index,
00503                        const CmtSystem::cmt_string_vector& include_paths,
00504                        const CmtSystem::cmt_string_vector& substitutions,
00505                        CmtSystem::cmt_string_vector& all_deps,
00506                        CmtSystem::cmt_string_vector& deps)
00507 {
00508   Log;
00509 
00510   int result = -1;
00511   cmt_string new_dir;
00512 
00513   log << "CMT> build_deps name=" << name << " dir_name=" 
00514       << dir_name << log_endl;
00515 
00516     //
00517     // Return 0 when the file is found in the current directory
00518     //
00519   if (CmtSystem::test_file (name))
00520     {
00521       ifstream input (name.c_str ());
00522       if (input)
00523         {
00524           CmtSystem::dirname (name, new_dir);
00525           build_deps_stream (input, new_dir, current_path_index,
00526                              include_paths, substitutions,
00527                              all_deps, deps);
00528           return (0);
00529         }
00530     }
00531 
00532   cmt_string full_name;
00533 
00534   full_name = dir_name;
00535   full_name += CmtSystem::file_separator ();
00536   full_name += name;
00537 
00538     //
00539     // Return 1 when the file is found in the directory of the
00540     // upper level source file
00541     //
00542   if (CmtSystem::test_file (full_name))
00543     {
00544       ifstream input (full_name.c_str ());
00545       if (input)
00546         {
00547           CmtSystem::dirname (full_name, new_dir);
00548           build_deps_stream (input, new_dir, current_path_index,
00549                              include_paths, substitutions,
00550                              all_deps, deps);
00551           return (1);
00552         }
00553     }
00554 
00555   int path_index = -1;
00556 
00557     //
00558     // Return [path_index + 2] when the include file is found at one of
00559     // the include_paths
00560     //
00561   for (path_index = 0; path_index < include_paths.size (); path_index++)
00562     {
00563       full_name  = include_paths[path_index];
00564       full_name += CmtSystem::file_separator ();
00565       full_name += name;
00566 
00567       log << "CMT> build_deps2 full_name=" << full_name << log_endl;
00568 
00569       if (CmtSystem::test_file (full_name))
00570         {
00571           ifstream in (full_name.c_str ());
00572           if (in)
00573             {
00574               CmtSystem::dirname (full_name, new_dir);
00575 
00576               log << "CMT> build_deps3 new_dir=" << new_dir << log_endl;
00577 
00578               build_deps_stream (in,
00579                                  new_dir,
00580                                  path_index + 2,
00581                                  include_paths,
00582                                  substitutions,
00583                                  all_deps,
00584                                  deps);
00585 
00586               return (path_index + 2);
00587             }
00588         }
00589     }
00590 
00591   log << "CMT> build_deps3" << log_endl;
00592 
00593   return (-1);
00594 }
00595 
00596 //--------------------------------------------------------------------------
00597 void DepsBuilder::clear ()
00598 {
00599   m_include_paths.clear ();
00600   m_substitutions.clear ();
00601 }
00602 
00603 //--------------------------------------------------------------------------
00604 void DepsBuilder::add (const cmt_string& path, const cmt_string& substitution)
00605 {
00606   if (path[path.size () - 1] == CmtSystem::file_separator ())
00607     {
00608       cmt_string p = path;
00609       p.erase (path.size () - 1);
00610       m_include_paths.push_back (p);
00611     }
00612   else
00613     {
00614       m_include_paths.push_back (path);
00615     }
00616 
00617   m_substitutions.push_back (substitution);
00618 }
00619 
00620 //--------------------------------------------------------------------------
00621 void DepsBuilder::add_includes (const Use& use)
00622 {
00623   Log;
00624 
00625   const Include::IncludeVector& includes = use.includes;
00626   int include_number;
00627 
00628   for (include_number = 0;
00629        include_number < includes.size ();
00630        include_number++)
00631     {
00632       const Include& include = includes[include_number];
00633 
00634       cmt_string temp = include.name;
00635       cmt_string pattern;
00636       cmt_string name;
00637       char end_pattern;
00638 
00639       int start = 0;
00640 
00641       for (;;)
00642         {
00643           int begin;
00644 
00645           begin = temp.find (start, "${");
00646           if (begin != cmt_string::npos)
00647             {
00648               end_pattern = '}';
00649             }
00650           else
00651             {
00652               begin = temp.find (start, "$(");
00653               if (begin != cmt_string::npos)
00654                 {
00655                   end_pattern = ')';
00656                 }
00657               else
00658                 {
00659                   break;
00660                 }
00661             }
00662 
00663           start = begin + 2;
00664 
00665           int end;
00666           end = temp.find (start, end_pattern);
00667           if (end == cmt_string::npos) break;
00668           if (end < begin) break;
00669           start = end + 1;
00670 
00671           temp.substr (begin, end - begin + 1, pattern);
00672           temp.substr (begin + 2, end - begin - 2, name);
00673 
00674           Symbol* macro = Symbol::find (name);
00675           if (macro != 0)
00676             {
00677               cmt_string value = macro->resolve_macro_value ();
00678               value += CmtSystem::file_separator ();
00679               temp.replace_all (pattern, value);
00680             }
00681           else
00682             {
00683               cmt_string value = CmtSystem::getenv (name);
00684               value += CmtSystem::file_separator ();
00685               temp.replace_all (pattern, value);
00686             }
00687         }
00688 
00689       log << "include = " << temp << log_endl;
00690 
00691       add (temp, include.name);
00692     }
00693 }
00694 
00695 //--------------------------------------------------------------------------
00696 CmtSystem::cmt_string_vector& DepsBuilder::run (const cmt_string& file_name)
00697 {
00698   m_deps.clear ();
00699   m_all_deps.clear ();
00700 
00701   cmt_string preprocessor;
00702   Symbol* macro = Symbol::find ("preprocessor_command");
00703   if (macro != 0)
00704     {
00705       preprocessor = macro->resolve_macro_value ();
00706     }
00707 
00708   if (preprocessor == "")
00709     {
00710         //
00711         //   Since no preprocessor command is defined,
00712         // we use the internal mechanism provided here.
00713         //
00714       cmt_string new_dir;
00715 
00716       CmtSystem::dirname (file_name, new_dir);
00717 
00718       build_deps (file_name,
00719                   new_dir,
00720                   0,
00721                   m_include_paths,
00722                   m_substitutions,
00723                   m_all_deps,
00724                   m_deps);
00725     }
00726   else
00727     {
00728         //
00729         //  An external preprocessor command is defined. We expect it
00730         // to follow a "standard" syntax for its output, ie:
00731         //   o It starts with:
00732         //       .o: ...
00733         //   o There may be many lines with trailing back-slashes
00734         //   o All entries are space-separated
00735         //   o One of the entries is the source file name itself
00736         //
00737         //  The preprocessor command expects the list of -I options
00738         // (resolved from the "includes" macro) and the list of 
00739         // -D/-U options (resolved from the "*_pp_*flags" macros)
00740         //
00741 
00742         //
00743         // Building the complete command (still the pp_*flags are
00744         // missing)
00745         //
00746       preprocessor += " ";
00747       macro = Symbol::find ("includes");
00748       preprocessor += macro->resolve_macro_value ();
00749       preprocessor += " ";
00750       preprocessor += file_name;
00751       
00752       cmt_string output;
00753       
00754       CmtSystem::execute (preprocessor, output);
00755 
00756         //
00757         // Make the output as one single big line.
00758         //
00759 
00760       output.replace_all ("\n", " ");
00761       output.replace_all ("\\ ", " ");
00762       
00763       CmtSystem::cmt_string_vector files;
00764       
00765       CmtSystem::split (output, " \t", files);
00766 
00767         //
00768         // Analyze each entry
00769         //
00770       
00771       for (int i = 1; i < files.size (); i++)
00772         {
00773           const cmt_string& file = files[i];
00774           if (file == file_name) continue;
00775           
00776           cmt_string dir;
00777           cmt_string name;
00778           cmt_string full_name;
00779           
00780           CmtSystem::dirname (file, dir);
00781 
00782             //
00783             // Only declared include_paths will be taken into account
00784             // Others are considered as system include paths.
00785             //
00786           
00787           for (int j = 0; j < m_include_paths.size (); j++)
00788             {
00789               const cmt_string& p = m_include_paths[j];
00790               if (dir == p)
00791                 {
00792                   CmtSystem::basename (file, name);
00793                   full_name = m_substitutions[j];
00794                   full_name += name;
00795 
00796                     //
00797                     // We add in the "m_deps" list the symbolic form
00798                     // of the path rather that the expanded one.
00799                     //
00800                   
00801                   m_deps.push_back (full_name);
00802                   
00803                   break;
00804                 }
00805             }
00806         }
00807     }
00808 
00809   return (m_deps);
00810 }
00811 

Generated on Mon May 2 10:25:04 2005 for CMT by 1.3.5