source: CMT/v1r20p20070720/source/cmt_deps_builder.cxx @ 662

Last change on this file since 662 was 424, checked in by rybkin, 17 years ago

See C.L. 332

  • Property svn:eol-style set to native
File size: 22.2 KB
Line 
1//-----------------------------------------------------------
2// Copyright Christian Arnault LAL-Orsay CNRS
3// arnault@lal.in2p3.fr
4// See the complete license in cmt_license.txt "http://www.cecill.info".
5//-----------------------------------------------------------
6
7#include "cmt_deps_builder.h"
8#include "cmt_system.h"
9#include "cmt_use.h"
10#include "cmt_include.h"
11#include "cmt_symbol.h"
12#include "cmt_log.h"
13
14//
15//  While parsing a C++ file, these are the possible usefull
16// states we can reach.
17//  Each state correspond to a specific state action function.
18//
19enum state_def
20  {
21    at_start,                                // beginning of the file
22    in_line,                                // along a line
23    in_string,                        // inside a quoted string
24    in_char,                                // inside a quoted char
25    in_comment,                        // inside a multi-line comment
26    in_string_comment,        // inside a quoted string in a comment
27    in_char_comment,                // inside a quoted char in a comment
28    in_line_comment                // inside a single-line comment
29  };
30
31//--------------------------------------------------
32static int build_deps (const cmt_string& name,
33                       const cmt_string& dir_name,
34                       int current_path_index,
35                       const CmtSystem::cmt_string_vector& include_paths,
36                       const CmtSystem::cmt_string_vector& substitutions,
37                       CmtSystem::cmt_string_vector& all_deps,
38                       CmtSystem::cmt_string_vector& deps);
39
40
41//--------------------------------------------------
42static void header_file_action (const char* header_file,
43                                const cmt_string& dir_name,
44                                int current_path_index,
45                                const CmtSystem::cmt_string_vector& include_paths,
46                                const CmtSystem::cmt_string_vector& substitutions,
47                                CmtSystem::cmt_string_vector& all_deps,
48                                CmtSystem::cmt_string_vector& deps)
49{
50  Log;
51
52  bool found = false;
53
54  for (int i = 0; i < all_deps.size (); i++)
55    {
56      const cmt_string& n = all_deps[i];
57
58      log << "CMT> check old header file name=" << n << " against " << header_file << log_endl;
59
60      if (n == header_file)
61        {
62          found = true;
63          break;
64        }
65    }
66 
67  if (!found)
68    {
69      log << "CMT> parsing new header file name=" << header_file << log_endl;
70
71      all_deps.push_back (header_file);
72     
73      int path_index = build_deps (header_file,
74                                   dir_name,
75                                   current_path_index,
76                                   include_paths,
77                                   substitutions,
78                                   all_deps,
79                                   deps);
80     
81      if (path_index >= 0)
82        {
83          cmt_string full_name;
84         
85          if (path_index == 1)
86            {
87              full_name = dir_name;
88              full_name += CmtSystem::file_separator ();
89             
90              if (current_path_index >= 2)
91                {
92                  full_name.replace (include_paths[current_path_index - 2],
93                                     substitutions[current_path_index - 2]);
94                }
95            }
96          else if (path_index > 1)
97            {
98              full_name  = substitutions[path_index - 2];
99              full_name += CmtSystem::file_separator ();
100            }
101         
102          full_name += header_file;
103         
104          deps.push_back (full_name);
105        }
106    }
107}
108
109
110//--------------------------------------------------
111static char* at_start_action (char* ptr,
112                              state_def& state,
113                              const cmt_string& dir_name,
114                              int current_path_index,
115                              const CmtSystem::cmt_string_vector& include_paths,
116                              const CmtSystem::cmt_string_vector& substitutions,
117                              CmtSystem::cmt_string_vector& all_deps,
118                              CmtSystem::cmt_string_vector& deps)
119{
120
121  char term = 0;
122  // To ignore leading spaces and tabs
123  while ( (*ptr == ' ') || (*ptr == '\t'))
124    {
125      ptr++;
126    }
127
128  if (*ptr == '#')
129    {
130      ptr++;
131
132      // skip spaces
133      while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
134
135      if (!strncmp (ptr, "include", 7))
136        {
137          // We have a #include statement
138
139          ptr += 7;
140
141          while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
142         
143          if (*ptr == '<')
144            {
145              term = '>';
146              ptr++;
147            }
148          else if (*ptr == '"')
149            {
150              term = '"';
151              ptr++;
152            }
153          else
154            {
155              // empty #include statement??
156              state = in_line;
157              ptr += strlen (ptr);
158              return (ptr);
159            }
160        }
161      else
162        {
163          // ignore other pre-processor statements
164
165          state = in_line;
166          ptr += strlen (ptr);
167          return (ptr);
168        }
169    }
170  else if (!strncmp (ptr, "      include", 13))
171    {
172      // fortran include statement
173
174      ptr += 13;
175
176      while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
177
178      if (*ptr == '\'')
179        {
180          term = '\'';
181          ptr++;
182        }
183      else
184        {
185          state = in_line;
186          return (ptr);
187        }
188    }
189  else if (!strncmp (ptr, "\tinclude", 8))
190    {
191      // fortran include statement
192
193      ptr += 8;
194
195      while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
196
197      if (*ptr == '\'')
198        {
199          term = '\'';
200          ptr++;
201        }
202      else
203        {
204          state = in_line;
205          return (ptr);
206        }
207    }
208  else
209    {
210      state = in_line;
211      return (ptr);
212    }
213
214  // At this point we do have to include a header file.
215
216  char* end;
217
218  end = strchr (ptr, term);
219  if (end != 0)
220    {
221      *end = 0;
222    }
223 
224  const char* header_file = ptr;
225 
226  header_file_action (header_file,
227                      dir_name,
228                      current_path_index,
229                      include_paths,
230                      substitutions,
231                      all_deps,
232                      deps);
233 
234  if (end != 0)
235    {
236      *end = term;
237    }
238
239  state = in_line;
240  ptr += strlen (ptr);
241 
242  return (ptr);
243}
244
245/**
246   Any line with no header inclusion
247   step through comments and strings
248*/
249static char* in_line_action (char* ptr, state_def& state)
250{
251  char* pattern = ptr + strlen (ptr);
252  int length = 0;
253
254  /*
255    search for the first occurrence of
256    {single-quote double-quote open-comment open-line-comment}
257
258    Must exclude escaped quotes \' and \"
259  */
260
261  char* pos = strchr (ptr, '"');
262  if ((pos != 0) && (pos < pattern) && (pos > ptr) && (*(pos-1) != '\\'))
263    {
264      state = in_string;
265      pattern = pos;
266      length = 1;
267    }
268
269  pos = strchr (ptr, '\'');
270  if ((pos != 0) && (pos < pattern) && (pos > ptr) && (*(pos-1) != '\\'))
271    {
272      state = in_char;
273      pattern = pos;
274      length = 1;
275    }
276
277  pos = strstr (ptr, "/*");   //*/
278  if ((pos != 0) && (pos < pattern))
279    {
280      state = in_comment;
281      pattern = pos;
282      length = 2;
283    }
284
285  pos = strstr (ptr, "//");
286  if ((pos != 0) && (pos < pattern))
287    {
288      state = in_line_comment;
289      pattern = pos;
290      length = 2;
291    }
292
293  ptr = pattern + length;
294
295  return (ptr);
296}
297
298//--------------------------------------------------
299static char* in_string_action (char* ptr, state_def& state)
300{
301  // we exclusively look for a double quote
302
303  char* pos = strchr (ptr, '"');
304  if (pos == 0)
305    {
306      // This string is not finished till the end of the line..
307      // we expect it to continue to the next line...
308      // thus we leave the state as it is
309      ptr += strlen (ptr);
310    }
311  else
312    {
313      if ((pos > ptr) && (*(pos - 1) == '\\'))
314        {
315          ptr = pos + 1;
316        }
317      else
318        {
319          ptr = pos + 1;
320          state = in_line;
321        }
322    }
323
324  return (ptr);
325}
326
327//--------------------------------------------------
328static char* in_char_action (char* ptr, state_def& state)
329{
330  // we exclusively look for a single quote
331
332  char* pos = strchr (ptr, '\'');
333  if (pos == 0)
334    {
335      // This string is not finished till the end of the line..
336      // we expect it continues to the nex line...
337      // thus we leave the state as it is
338      ptr += strlen (ptr);
339    }
340  else
341    {
342      if ((pos > ptr) && (*(pos - 1) == '\\'))
343        {
344          ptr = pos + 1;
345        }
346      else
347        {
348          ptr = pos + 1;
349          state = in_line;
350        }
351    }
352
353  return (ptr);
354}
355
356//--------------------------------------------------
357static char* in_comment_action (char* ptr, state_def& state)
358{
359  char* pattern = ptr + strlen (ptr);
360  int length    = 0;
361  char* pos;
362 
363
364  /*
365    Even if we are inside a comment, we must detect strings since comment markers may
366    be written inside them.
367 
368    pos = strchr (ptr, '"');
369 
370    if ((pos != 0) && (pos < pattern) && (pos > ptr) && (*(pos-1) != '\\'))
371    {
372    state = in_string_comment;
373    pattern = pos;
374    length = 1;
375    }
376
377    pos = strchr (ptr, '\'');
378    if ((pos != 0) && (pos < pattern) && (pos > ptr) && (*(pos-1) != '\\'))
379    {
380    state = in_char_comment;
381    pattern = pos;
382    length = 1;
383    }
384  */
385  pos = strstr (ptr, "*/");
386  if ((pos != 0) && (pos < pattern))
387    {
388      state   = in_line;
389      pattern = pos;
390      length  = 2;
391    }
392
393  ptr = pattern + length;
394
395  return (ptr);
396}
397
398//--------------------------------------------------
399static char* in_string_comment_action (char* ptr, state_def& state)
400{
401  char* pos = strchr (ptr, '"');
402  if (pos == 0)
403    {
404      // This string is not finished till the end of the line..
405      // we expect it continues to the nex line...
406      ptr += strlen (ptr);
407    }
408  else
409    {
410      if ((pos > ptr) && (*(pos - 1) == '\\'))
411        {
412          ptr = pos + 1;
413        }
414      else
415        {
416          ptr = pos + 1;
417          state = in_comment;
418        }
419    }
420
421  return (ptr);
422}
423
424//--------------------------------------------------
425static char* in_char_comment_action (char* ptr, state_def& state)
426{
427  char* pos = strchr (ptr, '\'');
428  if (pos == 0)
429    {
430      // This string is not finished till the end of the line..
431      // we expect it continues to the nex line...
432      ptr += strlen (ptr);
433    }
434  else
435    {
436      if ((pos > ptr) && (*(pos - 1) == '\\'))
437        {
438          ptr = pos + 1;
439        }
440      else
441        {
442          ptr = pos + 1;
443          state = in_comment;
444        }
445      pos--;
446    }
447
448  return (ptr);
449}
450
451//--------------------------------------------------
452static char* in_line_comment_action (char* ptr, state_def& state)
453{
454  char * pos = strchr (ptr, '\\'); 
455  /* Extend this part to deal with continuation character */ 
456  if ( (pos == NULL) || ( (ptr + strlen(ptr)-1)!=pos ))
457    {
458      state = in_line;
459    }
460 
461  ptr    += strlen (ptr);
462 
463  return (ptr);
464}
465
466//--------------------------------------------------
467static void build_deps_text (char* text,
468                             const cmt_string& dir_name,
469                             int current_path_index,
470                             const CmtSystem::cmt_string_vector& include_paths,
471                             const CmtSystem::cmt_string_vector& substitutions,
472                             CmtSystem::cmt_string_vector& all_deps,
473                             CmtSystem::cmt_string_vector& deps)
474{
475  Log;
476
477  int pos;
478  int max_pos;
479  int line_number = 1;
480
481  log << "CMT> build_deps_text dir_name="
482      << dir_name << log_endl;
483
484
485  // erase of continuation character
486  pos = 0;
487  max_pos = strlen (text);
488  char* current = text;
489  char* last = text + max_pos;
490  while (current < last)
491    {
492   
493      char* crnl = strstr (current, "\\\r\n");
494      char* nl   = strstr (current, "\\\n");
495     
496      if ( (crnl==0) && (nl ==0)) break;
497     
498      int length = 0;
499           
500      char * ptr = 0;
501      if (nl==0)  //crnl > 0
502        {
503          length = 3;
504          ptr    = crnl; 
505        }     
506      else if (crnl==0) //nl > 0
507        {
508          length = 2;                       
509          ptr    = nl; 
510        }     
511      else if (crnl < nl)
512        {
513          length = 3;
514          ptr    = crnl;                 
515        }
516      else // (crnl > nl)
517        {
518          length = 2;                       
519          ptr    = nl;         
520        }
521      strcpy (ptr, ptr+length);
522      current = ptr;
523      last -= length;
524    } 
525 
526  pos = 0;
527  max_pos = strlen (text);
528  current = text;
529  last = text + max_pos;
530
531  state_def state = at_start;
532 
533
534  while (current < last)
535    {
536             
537      char marker;
538      char* marker_pos = 0;
539
540      char* crnl = strstr (current, "\r\n");
541      char* nl = strchr (current, '\n');
542
543      char* first = nl;
544      int length = 1;
545
546      char* ptr = 0;
547
548      if (crnl != 0)
549        {
550          // cr+nl has been found
551
552          if (nl == 0)
553            {
554              // cr but no nl ??
555              first = crnl;
556              length = 2;
557            }
558          else
559            {
560              // both cr+nl and nl found
561              first = (nl < crnl) ? nl : crnl;
562              length = (nl < crnl) ? 1 : 2;
563            }
564        }
565      else
566        {
567          // no cr+nl but nl alone found
568          first = nl;
569          length = 1;
570        }
571
572      ptr = current;
573
574      if (first == 0)
575        {
576          // neither nl nor cr+nl found => this is the last line
577          marker_pos = 0;
578        }
579      else
580        {
581          marker_pos = first;
582          marker = *marker_pos;
583          *marker_pos = 0;
584        }
585
586      log << "CMT> build_deps_text2 line=[" 
587          << current << "]" << log_endl;
588     
589      while (strlen (ptr) > 0)
590        {
591          switch (state)
592            {
593            case at_start:
594              ptr = at_start_action (ptr,
595                                     state,
596                                     dir_name,
597                                     current_path_index,
598                                     include_paths,
599                                     substitutions,
600                                     all_deps,
601                                     deps);
602              break;
603            case in_line:
604              ptr = in_line_action (ptr, state);
605              break;
606            case in_string:
607              ptr = in_string_action (ptr, state);
608              break;
609            case in_char:
610              ptr = in_char_action (ptr, state);
611              break;
612            case in_comment:
613              ptr = in_comment_action (ptr, state);
614              break;
615            case in_string_comment:
616              ptr = in_string_comment_action (ptr, state);
617              break;
618            case in_char_comment:
619              ptr = in_char_comment_action (ptr, state);
620              break;
621            case in_line_comment:
622              ptr = in_line_comment_action (ptr, state);
623              break;
624            }
625        }
626
627      if (state == in_line) state = at_start;
628      line_number++;
629
630      if (marker_pos != 0)
631        {
632          *marker_pos = marker;
633          current = marker_pos + length;
634        }
635      else
636        {
637          break;
638        }
639    }
640           
641}
642
643//--------------------------------------------------
644static int build_deps (const cmt_string& name,
645                       const cmt_string& dir_name,
646                       int current_path_index,
647                       const CmtSystem::cmt_string_vector& include_paths,
648                       const CmtSystem::cmt_string_vector& substitutions,
649                       CmtSystem::cmt_string_vector& all_deps,
650                       CmtSystem::cmt_string_vector& deps)
651{
652  Log;
653
654  int result = -1;
655  cmt_string new_dir;
656
657  log << "CMT> build_deps name=" << name << " dir_name=" 
658      << dir_name << log_endl;
659
660  //
661  // Return 0 when the file is found in the current directory
662  //
663  if (CmtSystem::test_file (name))
664    {
665      cmt_string text;
666
667      text.read (name);
668
669      char* ptr = &text[0];
670      CmtSystem::dirname (name, new_dir);
671
672      build_deps_text (ptr, new_dir, current_path_index,
673                       include_paths, substitutions,
674                       all_deps, deps);
675      return (0);
676    }
677
678  cmt_string full_name;
679
680  full_name = dir_name;
681  full_name += CmtSystem::file_separator ();
682  full_name += name;
683
684  //
685  // Return 1 when the file is found in the directory of the
686  // upper level source file
687  //
688  if (CmtSystem::test_file (full_name))
689    {
690      cmt_string text;
691
692      text.read (full_name);
693
694      char* ptr = &text[0];
695      CmtSystem::dirname (full_name, new_dir);
696
697      build_deps_text (ptr, new_dir, current_path_index,
698                       include_paths, substitutions,
699                       all_deps, deps);
700      return (1);
701    }
702
703  int path_index = -1;
704
705  //
706  // Return [path_index + 2] when the include file is found at one of
707  // the include_paths
708  //
709  for (path_index = 0; path_index < include_paths.size (); path_index++)
710    {
711      full_name  = include_paths[path_index];
712      full_name += CmtSystem::file_separator ();
713      full_name += name;
714
715      log << "CMT> build_deps2 full_name=" << full_name << log_endl;
716
717      if (CmtSystem::test_file (full_name))
718        {
719          cmt_string text;
720
721          text.read (full_name);
722
723          char* ptr = &text[0];
724          CmtSystem::dirname (full_name, new_dir);
725
726          log << "CMT> build_deps3 new_dir=" << new_dir << log_endl;
727
728          build_deps_text (ptr,
729                           new_dir,
730                           path_index + 2,
731                           include_paths,
732                           substitutions,
733                           all_deps,
734                           deps);
735
736          return (path_index + 2);
737        }
738    }
739
740  log << "CMT> build_deps3" << log_endl;
741
742  return (-1);
743}
744
745//--------------------------------------------------------------------------
746void DepsBuilder::clear ()
747{
748  m_include_paths.clear ();
749  m_substitutions.clear ();
750}
751
752//--------------------------------------------------------------------------
753void DepsBuilder::add (const cmt_string& path, const cmt_string& substitution)
754{
755  if (path[path.size () - 1] == CmtSystem::file_separator ())
756    {
757      cmt_string p = path;
758      p.erase (path.size () - 1);
759      m_include_paths.push_back (p);
760    }
761  else
762    {
763      m_include_paths.push_back (path);
764    }
765
766  m_substitutions.push_back (substitution);
767}
768
769//--------------------------------------------------------------------------
770void DepsBuilder::add_includes (const Use& use)
771{
772  Log;
773
774  const Include::IncludeVector& includes = use.includes;
775  int include_number;
776
777  for (include_number = 0;
778       include_number < includes.size ();
779       include_number++)
780    {
781      const Include& include = includes[include_number];
782
783      cmt_string temp = include.name;
784      cmt_string pattern;
785      cmt_string name;
786      char end_pattern;
787
788      int start = 0;
789
790      for (;;)
791        {
792          int begin;
793
794          begin = temp.find (start, "${");
795          if (begin != cmt_string::npos)
796            {
797              end_pattern = '}';
798            }
799          else
800            {
801              begin = temp.find (start, "$(");
802              if (begin != cmt_string::npos)
803                {
804                  end_pattern = ')';
805                }
806              else
807                {
808                  break;
809                }
810            }
811
812          start = begin + 2;
813
814          int end;
815          end = temp.find (start, end_pattern);
816          if (end == cmt_string::npos) break;
817          if (end < begin) break;
818          start = end + 1;
819
820          temp.substr (begin, end - begin + 1, pattern);
821          temp.substr (begin + 2, end - begin - 2, name);
822
823          Symbol* macro = Symbol::find (name);
824          if (macro != 0)
825            {
826              cmt_string value = macro->resolve_macro_value ();
827              value += CmtSystem::file_separator ();
828              temp.replace_all (pattern, value);
829            }
830          else
831            {
832              cmt_string value = CmtSystem::getenv (name);
833              value += CmtSystem::file_separator ();
834              temp.replace_all (pattern, value);
835            }
836        }
837
838      log << "include = " << temp << log_endl;
839
840      add (temp, include.name);
841    }
842}
843
844//--------------------------------------------------------------------------
845CmtSystem::cmt_string_vector& DepsBuilder::run (const cmt_string& file_name)
846{
847  Log;
848
849  log << "Starting deps builder on " << file_name << log_endl;
850
851
852  m_deps.clear ();
853  m_all_deps.clear ();
854
855  cmt_string preprocessor;
856  Symbol* macro = Symbol::find ("preprocessor_command");
857  if (macro != 0)
858    {
859      preprocessor = macro->resolve_macro_value ();
860    }
861
862  if (preprocessor == "")
863    {
864      //
865      //   Since no preprocessor command is defined,
866      // we use the internal mechanism provided here.
867      //
868      cmt_string new_dir;
869
870      CmtSystem::dirname (file_name, new_dir);
871
872      build_deps (file_name,
873                  new_dir,
874                  0,
875                  m_include_paths,
876                  m_substitutions,
877                  m_all_deps,
878                  m_deps);
879    }
880  else
881    {
882      //
883      //  An external preprocessor command is defined. We expect it
884      // to follow a "standard" syntax for its output, ie:
885      //   o It starts with:
886      //       <module>.o: ...
887      //   o There may be many lines with trailing back-slashes
888      //   o All entries are space-separated
889      //   o One of the entries is the source file name itself
890      //
891      //  The preprocessor command expects the list of -I options
892      // (resolved from the "includes" macro) and the list of
893      // -D/-U options (resolved from the "*_pp_*flags" macros)
894      //
895
896      //
897      // Building the complete command (still the pp_*flags are
898      // missing)
899      //
900      preprocessor += " ";
901      macro = Symbol::find ("includes");
902      preprocessor += macro->resolve_macro_value ();
903      preprocessor += " ";
904      preprocessor += file_name;
905     
906      cmt_string output;
907     
908      CmtSystem::execute (preprocessor, output);
909
910      //
911      // Make the output as one single big line.
912      //
913
914      output.replace_all ("\n", " ");
915      output.replace_all ("\\ ", " ");
916     
917      CmtSystem::cmt_string_vector files;
918     
919      CmtSystem::split (output, " \t", files);
920
921      //
922      // Analyze each entry
923      //
924     
925      for (int i = 1; i < files.size (); i++)
926        {
927          const cmt_string& file = files[i];
928          if (file == file_name) continue;
929         
930          cmt_string dir;
931          cmt_string name;
932          cmt_string full_name;
933         
934          CmtSystem::dirname (file, dir);
935
936          //
937          // Only declared include_paths will be taken into account
938          // Others are considered as system include paths.
939          //
940         
941          for (int j = 0; j < m_include_paths.size (); j++)
942            {
943              const cmt_string& p = m_include_paths[j];
944              if (dir == p)
945                {
946                  CmtSystem::basename (file, name);
947                  full_name = m_substitutions[j];
948                  full_name += name;
949
950                  //
951                  // We add in the "m_deps" list the symbolic form
952                  // of the path rather that the expanded one.
953                  //
954                 
955                  m_deps.push_back (full_name);
956                 
957                  break;
958                }
959            }
960        }
961    }
962
963  return (m_deps);
964}
965
Note: See TracBrowser for help on using the repository browser.