source: CMT/v1r25p20140131/source/cmt_triggers.cxx

Last change on this file was 664, checked in by rybkin, 10 years ago

merge -r 646:663 HEAD

  • Property svn:eol-style set to native
File size: 19.0 KB
Line 
1//-----------------------------------------------------------
2// Copyright Christian Arnault LAL-Orsay CNRS
3// arnault@lal.in2p3.fr
4// Modified by Grigory Rybkin
5// See the complete license in cmt_license.txt "http://www.cecill.info".
6//-----------------------------------------------------------
7
8/*
9
10From Frank Behner, John Allison 13th February 1999.
11From Christian Arnault               Oct.     2000
12
13*/
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <ctype.h>
18
19#include "cmt_triggers.h"
20#include "cmt_std.h"
21#include "cmt_string.h"
22#include "cmt_system.h"
23#include "cmt_awk.h"
24#include "cmt_use.h"
25#include "cmt_symbol.h"
26#include "cmt_constituent.h"
27#include "cmt_syntax.h"
28#include "cmt_log.h"
29
30//--------------------------------------------------
31class Libmap
32{
33public:
34  typedef cmt_vector<Libmap> LibmapVector;
35
36  static Libmap& find (const cmt_string& name, const cmt_string& package);
37  static Libmap& add (const cmt_string& name, const cmt_string& package);
38  static LibmapVector& libmaps ();
39  static Libmap& find_with_trigger (const cmt_string& name);
40  static Libmap& null ();
41
42public:
43  Libmap ();
44
45  void add_trigger (const cmt_string& trigger_name);
46  void add_use (const cmt_string& use_name);
47  int operator != (const Libmap& other) const;
48  int operator == (const Libmap& other) const;
49  void set_used ();
50
51  cmt_string name;
52  cmt_string package;
53  CmtSystem::cmt_string_vector triggers;
54  bool used;
55  CmtSystem::cmt_string_vector uses;
56};
57//--------------------------------------------------
58
59//--------------------------------------------------
60Libmap& Libmap::find (const cmt_string& name, const cmt_string& package)
61{
62  LibmapVector& table = libmaps ();
63
64  for (int i = 0; i < table.size (); i++)
65    {
66      Libmap& libmap = table[i];
67
68#ifdef USE_PACKAGE_SCOPE
69      if ((name == libmap.name) &&
70          (package == libmap.package)) return (libmap);
71#else
72      if (name == libmap.name) return (libmap);
73#endif
74    }
75
76  return (null ());
77}
78
79Libmap& Libmap::add (const cmt_string& name, const cmt_string& package)
80{
81  {
82    Libmap& libmap = find (name, package);
83
84    if (libmap != null ()) return (libmap);
85  }
86
87  LibmapVector& table = libmaps ();
88
89  Libmap& libmap = table.add ();
90
91  libmap.name = name;
92  libmap.package = package;
93
94  return (libmap);
95}
96
97Libmap::LibmapVector& Libmap::libmaps ()
98{
99  static cmt_vector<Libmap> table;
100
101  return (table);
102}
103
104Libmap::Libmap () : used (false)
105{
106}
107
108void Libmap::add_trigger (const cmt_string& trigger_name)
109{
110  cmt_string& trigger = triggers.add ();
111
112  trigger = trigger_name;
113}
114
115void Libmap::add_use (const cmt_string& use_name)
116{
117  cmt_string& use = uses.add ();
118
119  use = use_name;
120}
121
122Libmap& Libmap::find_with_trigger (const cmt_string& name)
123{
124  LibmapVector& table = libmaps ();
125
126  for (int i = 0; i < table.size (); i++)
127    {
128      Libmap& libmap = table[i];
129
130      for (int j = 0; j < libmap.triggers.size (); j++)
131        {
132          const cmt_string& trigger = libmap.triggers[j];
133
134          if (name == trigger) return (libmap);
135        }
136    }
137
138  return (null ());
139}
140
141Libmap& Libmap::null ()
142{
143  static Libmap null_libmap;
144
145  return (null_libmap);
146}
147
148int Libmap::operator != (const Libmap& other) const
149{
150  return (this != &other);
151}
152
153int Libmap::operator == (const Libmap& other) const
154{
155  return (this == &other);
156}
157
158void Libmap::set_used ()
159{
160  if (used) return;
161
162  used = true;
163
164  cmt_string package_name;
165  cmt_string use_name;
166
167  for (int i = 0; i < uses.size (); i++)
168    {
169      const cmt_string& use = uses[i];
170
171      int pos = use.find ("::");
172
173      if (pos == cmt_string::npos)
174        {
175          package_name = "";
176          use_name = use;
177        }
178      else
179        {
180          use.substr (0, pos, package_name);
181          use.substr (pos + 2, use_name);
182
183          Libmap& libmap = find (use_name, package_name);
184          if (libmap != null ())
185            {
186              libmap.set_used ();
187            }
188        }
189    }
190}
191//--------------------------------------------------
192
193//--------------------------------------------------
194//
195//  This class analyzes a trigger description file (ie a file
196// named <constituent>.triggers and located in the cmt directory of
197// a package).
198//
199//  The file is expected to include lines of the form:
200//
201//    <constituent>_implied_libs=<lib> <lib> ...
202//    <constituent>_triggers=<trigger> <trigger> ...
203//
204//  with <lib> having the form:
205//
206//    <package>::<constituent>
207//     eg: Cm::Cm
208//
209//    or more simply
210//
211//    <constituent>
212//
213//  and <trigger> being an include file path (in the same form as
214//  the one used in the dependency files)
215//
216//     eg: $(CMROOT)/src/Cm.h
217//         $(AGDDROOT)/AGDD/AGDD.h
218//
219//
220//    When reading one trigger description file, one Libmap object is
221// created, and its list of uses and triggers are filled in.
222//
223//--------------------------------------------------
224class TriggerAnalyzer : public FAwk
225{
226public:
227  TriggerAnalyzer (const cmt_string& package_name);
228  void begin ();
229  void filter (const cmt_string& line);
230  void end ();
231
232private:
233  cmt_string package;
234};
235//--------------------------------------------------
236
237//--------------------------------------------------
238TriggerAnalyzer::TriggerAnalyzer (const cmt_string& package_name) :
239  package (package_name)
240{
241}
242
243void TriggerAnalyzer::begin ()
244{
245}
246
247void TriggerAnalyzer::filter (const cmt_string& line)
248{
249  int pos = line.find ("=");
250  if (pos == 0)
251    {
252      CmtMessage::error ("Syntax in trigger file : empty name");
253      /*
254      if (!Cmt::get_quiet ())
255        {
256          cerr << "Syntax error in trigger file : empty name" << endl;
257        }
258      */
259      exit (0);
260    }
261  if (pos == cmt_string::npos)
262    {
263      CmtMessage::error ("Syntax in trigger file : no = sign");
264      /*
265      if (!Cmt::get_quiet ())
266        {
267          cerr << "Syntax error in trigger file : no = sign" << endl;
268        }
269      */
270      exit (0);
271    }
272
273  cmt_string name;
274  cmt_string text;
275  CmtSystem::cmt_string_vector words;
276
277  line.substr (0, pos, name);
278  line.substr (pos + 1, text);
279  CmtSystem::split (text, " ", words);
280
281  if (name.find ("_triggers") != cmt_string::npos)
282    {
283      name.replace ("_triggers", "");
284
285      Libmap& libmap = Libmap::add (name, package);
286
287      for (int i = 0; i < words.size (); i++)
288        {
289          const cmt_string& w = words[i];
290          libmap.add_trigger (w);
291        }
292    }
293  else if (name.find ("_implied_libraries") != cmt_string::npos)
294    {
295      name.replace ("_implied_libraries", "");
296
297      Libmap& libmap = Libmap::add (name, package);
298
299      for (int i = 0; i < words.size (); i++)
300        {
301          const cmt_string& w = words[i];
302          libmap.add_use (w);
303        }
304    }
305  else
306    {
307      CmtMessage::error ("Syntax in trigger file : bad keyword (" + name + ")");
308      /*
309      if (!Cmt::get_quiet ())
310        {
311          cerr << "Syntax error in trigger file : bad keyword (" <<
312            name << ")" << endl;
313        }
314      */
315      exit (0);
316    }
317}
318
319void TriggerAnalyzer::end ()
320{
321}
322//--------------------------------------------------
323
324//--------------------------------------------------
325//
326//  This filter scans the CMT dependency file of a constituent.
327//
328//  The file should contains a set of entries, each composed of one line:
329//
330//      <module-name>_dependencies=<dependency> <dependency> ...
331//
332//  (Each such line actually defines one make macro used to trigger the
333//   rebuild of the corresponding module)
334//
335//  Dependencies may typically be:
336//     o the module source itself
337//     o a local dependency (ie. one which is preficed by one of the
338//              include dirs)
339//     o an external dependency (all other cases)
340//
341//  Local dependencies will generate triggers for this constituent (filling
342//  up the "triggers" unique-vector)
343//
344//  External dependencies will trigger the use of one Libmap object (thus
345//  filling up the "uses" unique-vector)
346//
347//  At the end of the scan, the results are printed to standard output
348//  with a format of a trigger description file.
349//
350//    For libraries, only the first level list is output (ie. only Libmap
351//  object directly triggered by dependencies of that constituent are
352//  listed)
353//
354//    For applications, indirect dependencies are considered and resolved
355//  recursively.
356//
357//--------------------------------------------------
358class DependencyAnalyzer : public FAwk
359{
360public:
361  DependencyAnalyzer (const cmt_string& package_name, 
362                      Constituent& constituent_ref);
363  void begin ();
364  void filter (const cmt_string& line);
365  virtual void end ();
366
367protected:
368
369  void add_trigger (const cmt_string& name);
370  void add_use (Libmap& libmap);
371
372  CmtSystem::cmt_string_vector include_dirs;
373  cmt_vector<Libmap*> uses;
374  CmtSystem::cmt_string_vector triggers;
375  Constituent& constituent;
376  cmt_string package;
377  cmt_string package_upper;
378};
379
380class LibraryAnalyzer : public DependencyAnalyzer
381{
382public:
383  LibraryAnalyzer (const cmt_string& package_name, 
384                   Constituent& constituent_ref);
385  void end ();
386};
387
388class ApplicationAnalyzer : public DependencyAnalyzer
389{
390public:
391  ApplicationAnalyzer (const cmt_string& package_name, 
392                       Constituent& constituent_ref);
393  void end ();
394};
395//--------------------------------------------------
396
397//--------------------------------------------------
398DependencyAnalyzer::DependencyAnalyzer (const cmt_string& package_name, 
399                                        Constituent& constituent_ref) :
400  package (package_name),
401  constituent (constituent_ref)
402{
403  cmt_string dirs;
404
405  int pos;
406  char c;
407
408  package_upper = package;
409
410  for (pos = 0; pos < package_upper.size (); pos++)
411    {
412      c = package_upper[pos];
413      package_upper[pos] = toupper (c);
414    }
415
416  CmtSystem::execute ("cmt show include_dirs", dirs);
417  dirs.replace_all ("\n", "");
418  CmtSystem::split (dirs, " ", include_dirs);
419}
420
421void DependencyAnalyzer::begin ()
422{
423}
424
425void DependencyAnalyzer::filter (const cmt_string& line)
426{
427  /* Clip target out of dependency file... */
428  int pos = line.find ("=");
429  if ((pos == 0) || (pos == cmt_string::npos))
430    {
431      CmtMessage::error ("Syntax in dependency file: " + line
432                         + ": Missing = or target name.");
433      /*
434      if (!Cmt::get_quiet ())
435        {
436          cerr << "  ERROR: Syntax in dependency file: " << line << endl;
437          cerr << "  Missing = or target name." << endl;
438        }
439      */
440      exit (1);
441    }
442
443  cmt_string module;
444
445  line.substr (0, pos, module);
446  module.trim ();
447  module.replace ("_dependencies", "");
448
449  if (module == "cmt_path_make") return;
450
451  int underscore = module.find_last_of ("_");
452
453  if (underscore != cmt_string::npos)
454    {
455      module[underscore] = '.';
456    }
457
458  static cmt_string dependencies;
459
460  line.substr (pos + 1, dependencies);
461
462  if (dependencies == "") 
463    {
464      CmtMessage::warning ("It seems there is nothing after \'=\' "
465                           "in dependency file " + m_file_name);
466      //      cerr << "#CMT> Warning: It seems there is nothing after \'=\' "
467      //        "in dependency file " << m_file_name << endl;
468      return;
469    }
470
471  CmtSystem::cmt_string_vector deps;
472
473  CmtSystem::split (dependencies, " ", deps);
474
475  for (int i = 0; i < deps.size (); i++)
476    {
477      const cmt_string& dep = deps[i];
478
479      //
480      // dep may either be:
481      //  o the module itself
482      //  o a file in one of include_dirs
483      //  o something else
484      //
485
486      if (dep.find (module) != cmt_string::npos)
487        {
488          // This is the module itself.
489        }
490      else
491        {
492          bool found = false;
493
494          for (int j = 0; j < include_dirs.size (); j++)
495            {
496              const cmt_string& dir = include_dirs[j];
497
498              if (dep.find (dir) == 0)
499                {
500                  // This is a local dependency.
501
502                  cmt_string name = dep;
503
504                  if (dir == "$(src)")
505                    {
506                      cmt_string new_dir;
507
508                      new_dir = "$(";
509                      new_dir += package_upper;
510                      new_dir += "ROOT)/src/";
511
512                      name.replace (dir, new_dir);
513                    }
514
515                  if (CmtSystem::file_separator () == '\\')
516                    {
517                      name.replace_all (CmtSystem::file_separator (), "/");
518                    }
519
520                  Libmap& libmap = Libmap::find_with_trigger (name);
521
522                  if (libmap != Libmap::null ())
523                    {
524                      add_use (libmap);
525                    }
526                  else
527                    {
528                      add_trigger (name);
529                    }
530
531                  found = true;
532                  break;
533                }
534            }
535
536          if (!found)
537            {
538              cmt_string name = dep;
539
540              if (CmtSystem::file_separator () == '\\')
541                {
542                  name.replace_all (CmtSystem::file_separator (), "/");
543                }
544
545              // This is an external dependency.
546
547              Libmap& libmap = Libmap::find_with_trigger (name);
548
549              if (libmap != Libmap::null ())
550                {
551                  add_use (libmap);
552                }
553            }
554        }
555    }
556}
557
558void DependencyAnalyzer::end ()
559{
560}
561
562void DependencyAnalyzer::add_trigger (const cmt_string& name)
563{
564  for (int i = 0; i < triggers.size (); i++)
565    {
566      const cmt_string& trigger = triggers[i];
567
568      if (trigger == name) return;
569    }
570
571  cmt_string& new_trigger = triggers.add ();
572
573  new_trigger = name;
574}
575
576void DependencyAnalyzer::add_use (Libmap& libmap)
577{
578  for (int i = 0; i < uses.size (); i++)
579    {
580      const Libmap& ref = *(uses[i]);
581
582      if (ref == libmap) return;
583    }
584
585  uses.push_back (&libmap);
586}
587
588LibraryAnalyzer::LibraryAnalyzer (const cmt_string& package_name, 
589                                  Constituent& constituent_ref) :
590  DependencyAnalyzer (package_name, constituent_ref)
591{
592}
593
594void LibraryAnalyzer::end ()
595{
596  cmt_string macro_name;
597  cmt_string output;
598
599  int i;
600
601  if (uses.size () > 0)
602    {
603      for (i = 0; i < uses.size (); i++)
604        {
605          Libmap& libmap = *(uses[i]);
606
607          libmap.set_used ();
608        }
609
610      Libmap::LibmapVector& table = Libmap::libmaps ();
611
612      macro_name = constituent.name;
613      macro_name += "_implied_libraries";
614
615      output  = "macro_prepend ";
616      output += macro_name;
617      output += " \"";
618      for (i = 0; i < table.size (); i++)
619        {
620          Libmap& libmap = table[i];
621         
622          if (libmap.used)
623            {
624#ifdef USE_PACKAGE_SCOPE
625              output += libmap.package;
626              output += "::";
627#endif
628              output += libmap.name;
629              output += " ";
630            }
631        }
632      output += "\"";
633
634      SyntaxParser::parse_requirements_text (output, "", 0);
635
636      Symbol* macro = Symbol::find (macro_name);
637      output = macro_name;
638      output += "=";
639      output += macro->build_macro_value ();
640
641      cout << output << endl;
642    }
643
644  if (triggers.size () > 0)
645    {
646      macro_name = constituent.name;
647      macro_name += "_triggers";
648
649      output  = "macro_prepend ";
650      output += macro_name;
651      output += " \"";
652      for (i = 0; i < triggers.size (); i++)
653        {
654          const cmt_string& trigger = triggers[i];
655         
656          output += trigger;
657          output += " ";
658        }
659      output += "\"";
660
661      SyntaxParser::parse_requirements_text (output, "", 0);
662
663      Symbol* macro = Symbol::find (macro_name);
664      output = macro_name;
665      output += "=";
666      output += macro->build_macro_value ();
667
668      cout << output << endl;
669    }
670}
671
672ApplicationAnalyzer::ApplicationAnalyzer (const cmt_string& package_name, 
673                                          Constituent& constituent_ref) :
674  DependencyAnalyzer (package_name, constituent_ref)
675{
676}
677
678void ApplicationAnalyzer::end ()
679{
680  cmt_string macro_name;
681  cmt_string output;
682
683  int i;
684
685  if (uses.size () > 0)
686    {
687      for (i = 0; i < uses.size (); i++)
688        {
689          Libmap& libmap = *(uses[i]);
690
691          libmap.set_used ();
692        }
693
694      Libmap::LibmapVector& table = Libmap::libmaps ();
695
696      macro_name = constituent.name;
697      macro_name += "linkopts";
698
699      output  = "macro_prepend ";
700      output += macro_name;
701      output += " \"";
702      for (i = 0; i < table.size (); i++)
703        {
704          Libmap& libmap = table[i];
705         
706          if (libmap.used)
707            {
708              output += "$(implied_library_prefix)";
709              output += libmap.name;
710              output += "$(implied_library_suffix) ";
711            }
712        }
713      output += "\"";
714
715      SyntaxParser::parse_requirements_text (output, "", 0);
716
717      Symbol* macro = Symbol::find (macro_name);
718      output = macro_name;
719      output += "=";
720      output += macro->build_macro_value ();
721
722      cout << output << endl;
723    }
724}
725//--------------------------------------------------
726
727//--------------------------------------------------
728//
729//  The UseAnalyzer is called first to reach all used packages.
730// For each package found, it retreives the *.triggers files
731// which contain the trigger descriptions for every constituent
732// of the package.
733//
734//  For each trigger description file found, a TriggerAnalyzer is run
735// which in turn fills in the database of Libmap objects.
736//
737//--------------------------------------------------
738class UseAnalyzer
739{
740public:
741  void run (const cmt_string& constituent);
742  void run (const cmt_string& location, 
743            const cmt_string& package,
744            const cmt_string& filter_out = "");
745};
746//--------------------------------------------------
747
748//--------------------------------------------------
749void UseAnalyzer::run (const cmt_string& constituent)
750{
751  Use* use = &(Use::current ());
752
753  run ("./", use->get_package_name (), constituent);
754
755  Use::UsePtrVector& uses = Use::get_ordered_uses ();
756  for (int i = 0; i < uses.size (); i++)
757    {
758      use = uses[i];
759
760      cmt_string s;
761                                 
762      s = use->real_path;
763      s += CmtSystem::file_separator ();
764      s += use->get_package_name ();
765      s += CmtSystem::file_separator ();
766      s += use->version;
767      s += CmtSystem::file_separator ();
768                                 
769      if (use->style == mgr_style) s += "mgr";
770      else s += "cmt";
771
772      s += CmtSystem::file_separator ();
773
774      run (s, use->get_package_name ());
775    }
776}
777
778void UseAnalyzer::run (const cmt_string& location, 
779                       const cmt_string& package,
780                       const cmt_string& filter_out)
781{
782  static cmt_regexp expression ("[.]triggers$");
783
784  TriggerAnalyzer analyzer (package);
785
786  CmtSystem::cmt_string_vector files;
787
788  CmtSystem::scan_dir (location, expression, files);
789
790  cmt_string name;
791
792  for (int i = 0; i < files.size (); i++)
793    {
794      const cmt_string& file = files[i];
795
796      if (filter_out != "")
797        {
798          CmtSystem::basename (file, ".triggers", name);
799          if (name == filter_out) continue;
800        }
801
802      analyzer.run (file);
803    }
804}
805//--------------------------------------------------
806
807
808//--------------------------------------------------
809void TriggerGenerator::run (const cmt_string& constituent_name)
810{
811  Constituent* constituent = Constituent::find (constituent_name);
812
813  Use* use = &(Use::current ());
814  cmt_string package = use->get_package_name ();
815
816  // UseAnalyzer use_analyzer (package);
817  // use_analyzer.run ("cmt show uses -quiet");
818
819  UseAnalyzer use_analyzer;
820  use_analyzer.run (constituent_name);
821
822  cmt_string file_name;
823
824  file_name = "./";
825  file_name += constituent_name;
826  file_name += "_dependencies.";
827#ifdef WIN32
828  file_name += "nmake";
829#else
830  file_name += "make";
831#endif
832
833  DependencyAnalyzer* analyzer = 0;
834
835  if (constituent->type == Library)
836    {
837      analyzer = new LibraryAnalyzer (package, *constituent);
838    }
839  else if (constituent->type == Application)
840    {
841      analyzer = new ApplicationAnalyzer (package, *constituent);
842    }
843  else
844    {
845      return;
846    }
847
848  if (analyzer->run (file_name) == Awk::failed)
849    {
850      CmtMessage::warning (" File " + file_name + " not found");
851      //      cerr << "  File " << file_name << " not found" << endl;
852    }
853
854  delete analyzer;
855}
856//--------------------------------------------------
Note: See TracBrowser for help on using the repository browser.