source: CMT/v1r18p20051108/source/cmt_deps_builder.cxx

Last change on this file was 79, checked in by arnault, 19 years ago

Fixing session - see CL#275

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