/* From Frank Behner, John Allison 13th February 1999. From Christian Arnault Oct. 2000 */ #include #include #include #include "cmt_triggers.h" #include "cmt_std.h" #include "cmt_string.h" #include "cmt_system.h" #include "cmt_awk.h" #include "cmt_use.h" #include "cmt_symbol.h" #include "cmt_constituent.h" //-------------------------------------------------- class Libmap { public: typedef cmt_vector LibmapVector; static Libmap& find (const cmt_string& name, const cmt_string& package); static Libmap& add (const cmt_string& name, const cmt_string& package); static LibmapVector& libmaps (); static Libmap& find_with_trigger (const cmt_string& name); static Libmap& null (); public: Libmap (); void add_trigger (const cmt_string& trigger_name); void add_use (const cmt_string& use_name); int operator != (const Libmap& other) const; int operator == (const Libmap& other) const; void set_used (); cmt_string name; cmt_string package; CmtSystem::cmt_string_vector triggers; bool used; CmtSystem::cmt_string_vector uses; }; //-------------------------------------------------- //-------------------------------------------------- Libmap& Libmap::find (const cmt_string& name, const cmt_string& package) { LibmapVector& table = libmaps (); for (int i = 0; i < table.size (); i++) { Libmap& libmap = table[i]; #ifdef USE_PACKAGE_SCOPE if ((name == libmap.name) && (package == libmap.package)) return (libmap); #else if (name == libmap.name) return (libmap); #endif } return (null ()); } Libmap& Libmap::add (const cmt_string& name, const cmt_string& package) { { Libmap& libmap = find (name, package); if (libmap != null ()) return (libmap); } LibmapVector& table = libmaps (); Libmap& libmap = table.add (); libmap.name = name; libmap.package = package; return (libmap); } Libmap::LibmapVector& Libmap::libmaps () { static cmt_vector table; return (table); } Libmap::Libmap () : used (false) { } void Libmap::add_trigger (const cmt_string& trigger_name) { cmt_string& trigger = triggers.add (); trigger = trigger_name; } void Libmap::add_use (const cmt_string& use_name) { cmt_string& use = uses.add (); use = use_name; } Libmap& Libmap::find_with_trigger (const cmt_string& name) { LibmapVector& table = libmaps (); for (int i = 0; i < table.size (); i++) { Libmap& libmap = table[i]; for (int j = 0; j < libmap.triggers.size (); j++) { const cmt_string& trigger = libmap.triggers[j]; if (name == trigger) return (libmap); } } return (null ()); } Libmap& Libmap::null () { static Libmap null_libmap; return (null_libmap); } int Libmap::operator != (const Libmap& other) const { return (this != &other); } int Libmap::operator == (const Libmap& other) const { return (this == &other); } void Libmap::set_used () { if (used) return; used = true; cmt_string package_name; cmt_string use_name; for (int i = 0; i < uses.size (); i++) { const cmt_string& use = uses[i]; int pos = use.find ("::"); if (pos == cmt_string::npos) { package_name = ""; use_name = use; } else { use.substr (0, pos, package_name); use.substr (pos + 2, use_name); Libmap& libmap = find (use_name, package_name); if (libmap != null ()) { libmap.set_used (); } } } } //-------------------------------------------------- //-------------------------------------------------- // // This class analyzes a trigger description file (ie a file // named .triggers and located in the cmt directory of // a package). // // The file is expected to include lines of the form: // // _implied_libs= ... // _triggers= ... // // with having the form: // // :: // eg: Cm::Cm // // or more simply // // // // and being an include file path (in the same form as // the one used in the dependency files) // // eg: $(CMROOT)/src/Cm.h // $(AGDDROOT)/AGDD/AGDD.h // // // When reading one trigger description file, one Libmap object is // created, and its list of uses and triggers are filled in. // //-------------------------------------------------- class TriggerAnalyzer : public FAwk { public: TriggerAnalyzer (const cmt_string& package_name); void begin (); void filter (const cmt_string& line); void end (); private: cmt_string package; }; //-------------------------------------------------- //-------------------------------------------------- TriggerAnalyzer::TriggerAnalyzer (const cmt_string& package_name) : package (package_name) { } void TriggerAnalyzer::begin () { } void TriggerAnalyzer::filter (const cmt_string& line) { int pos = line.find ("="); if (pos == 0) { if (!Cmt::quiet) { cerr << "Syntax error in trigger file : empty name" << endl; } exit (0); } if (pos == cmt_string::npos) { if (!Cmt::quiet) { cerr << "Syntax error in trigger file : no = sign" << endl; } exit (0); } cmt_string name; cmt_string text; CmtSystem::cmt_string_vector words; line.substr (0, pos, name); line.substr (pos + 1, text); CmtSystem::split (text, " ", words); if (name.find ("_triggers") != cmt_string::npos) { name.replace ("_triggers", ""); Libmap& libmap = Libmap::add (name, package); for (int i = 0; i < words.size (); i++) { const cmt_string& w = words[i]; libmap.add_trigger (w); } } else if (name.find ("_implied_libraries") != cmt_string::npos) { name.replace ("_implied_libraries", ""); Libmap& libmap = Libmap::add (name, package); for (int i = 0; i < words.size (); i++) { const cmt_string& w = words[i]; libmap.add_use (w); } } else { if (!Cmt::quiet) { cerr << "Syntax error in trigger file : bad keyword (" << name << ")" << endl; } exit (0); } } void TriggerAnalyzer::end () { } //-------------------------------------------------- //-------------------------------------------------- // // This filter scans the CMT dependency file of a constituent. // // The file should contains a set of entries, each composed of one line: // // _dependencies= ... // // (Each such line actually defines one make macro used to trigger the // rebuild of the corresponding module) // // Dependencies may typically be: // o the module source itself // o a local dependency (ie. one which is preficed by one of the // include dirs) // o an external dependency (all other cases) // // Local dependencies will generate triggers for this constituent (filling // up the "triggers" unique-vector) // // External dependencies will trigger the use of one Libmap object (thus // filling up the "uses" unique-vector) // // At the end of the scan, the results are printed to standard output // with a format of a trigger description file. // // For libraries, only the first level list is output (ie. only Libmap // object directly triggered by dependencies of that constituent are // listed) // // For applications, indirect dependencies are considered and resolved // recursively. // //-------------------------------------------------- class DependencyAnalyzer : public FAwk { public: DependencyAnalyzer (const cmt_string& package_name, Constituent& constituent_ref); void begin (); void filter (const cmt_string& line); virtual void end (); protected: void add_trigger (const cmt_string& name); void add_use (Libmap& libmap); CmtSystem::cmt_string_vector include_dirs; cmt_vector uses; CmtSystem::cmt_string_vector triggers; Constituent& constituent; cmt_string package; cmt_string package_upper; }; class LibraryAnalyzer : public DependencyAnalyzer { public: LibraryAnalyzer (const cmt_string& package_name, Constituent& constituent_ref); void end (); }; class ApplicationAnalyzer : public DependencyAnalyzer { public: ApplicationAnalyzer (const cmt_string& package_name, Constituent& constituent_ref); void end (); }; //-------------------------------------------------- //-------------------------------------------------- DependencyAnalyzer::DependencyAnalyzer (const cmt_string& package_name, Constituent& constituent_ref) : package (package_name), constituent (constituent_ref) { cmt_string dirs; int pos; char c; package_upper = package; for (pos = 0; pos < package_upper.size (); pos++) { c = package_upper[pos]; package_upper[pos] = toupper (c); } CmtSystem::execute ("cmt show include_dirs", dirs); dirs.replace_all ("\n", ""); CmtSystem::split (dirs, " ", include_dirs); } void DependencyAnalyzer::begin () { } void DependencyAnalyzer::filter (const cmt_string& line) { /* Clip target out of dependency file... */ int pos = line.find ("="); if ((pos == 0) || (pos == cmt_string::npos)) { if (!Cmt::quiet) { cerr << " ERROR: Syntax in dependency file: " << line << endl; cerr << " Missing = or target name." << endl; } exit (1); } cmt_string module; line.substr (0, pos, module); module.trim (); module.replace ("_dependencies", ""); if (module == "cmt_path_make") return; int underscore = module.find_last_of ("_"); if (underscore != cmt_string::npos) { module[underscore] = '.'; } static cmt_string dependencies; line.substr (pos + 1, dependencies); if (dependencies == "") { cerr << " WARNING: It seems there is nothing after \'=\' " "in dependency file " << m_file_name << endl; return; } CmtSystem::cmt_string_vector deps; CmtSystem::split (dependencies, " ", deps); for (int i = 0; i < deps.size (); i++) { const cmt_string& dep = deps[i]; // // dep may either be: // o the module itself // o a file in one of include_dirs // o something else // if (dep.find (module) != cmt_string::npos) { // This is the module itself. } else { bool found = false; for (int j = 0; j < include_dirs.size (); j++) { const cmt_string& dir = include_dirs[j]; if (dep.find (dir) == 0) { // This is a local dependency. cmt_string name = dep; if (dir == "$(src)") { cmt_string new_dir; new_dir = "$("; new_dir += package_upper; new_dir += "ROOT)/src/"; name.replace (dir, new_dir); } if (CmtSystem::file_separator () == '\\') { name.replace_all (CmtSystem::file_separator (), "/"); } Libmap& libmap = Libmap::find_with_trigger (name); if (libmap != Libmap::null ()) { add_use (libmap); } else { add_trigger (name); } found = true; break; } } if (!found) { cmt_string name = dep; if (CmtSystem::file_separator () == '\\') { name.replace_all (CmtSystem::file_separator (), "/"); } // This is an external dependency. Libmap& libmap = Libmap::find_with_trigger (name); if (libmap != Libmap::null ()) { add_use (libmap); } } } } } void DependencyAnalyzer::end () { } void DependencyAnalyzer::add_trigger (const cmt_string& name) { for (int i = 0; i < triggers.size (); i++) { const cmt_string& trigger = triggers[i]; if (trigger == name) return; } cmt_string& new_trigger = triggers.add (); new_trigger = name; } void DependencyAnalyzer::add_use (Libmap& libmap) { for (int i = 0; i < uses.size (); i++) { const Libmap& ref = *(uses[i]); if (ref == libmap) return; } uses.push_back (&libmap); } LibraryAnalyzer::LibraryAnalyzer (const cmt_string& package_name, Constituent& constituent_ref) : DependencyAnalyzer (package_name, constituent_ref) { } void LibraryAnalyzer::end () { cmt_string macro_name; cmt_string output; int i; if (uses.size () > 0) { for (i = 0; i < uses.size (); i++) { Libmap& libmap = *(uses[i]); libmap.set_used (); } Libmap::LibmapVector& table = Libmap::libmaps (); macro_name = constituent.name; macro_name += "_implied_libraries"; output = "macro_prepend "; output += macro_name; output += " \""; for (i = 0; i < table.size (); i++) { Libmap& libmap = table[i]; if (libmap.used) { #ifdef USE_PACKAGE_SCOPE output += libmap.package; output += "::"; #endif output += libmap.name; output += " "; } } output += "\""; Cmt::parse_requirements_text (output, "", 0); Symbol* macro = Symbol::find (macro_name); output = macro_name; output += "="; output += macro->build_macro_value (); cout << output << endl; } if (triggers.size () > 0) { macro_name = constituent.name; macro_name += "_triggers"; output = "macro_prepend "; output += macro_name; output += " \""; for (i = 0; i < triggers.size (); i++) { const cmt_string& trigger = triggers[i]; output += trigger; output += " "; } output += "\""; Cmt::parse_requirements_text (output, "", 0); Symbol* macro = Symbol::find (macro_name); output = macro_name; output += "="; output += macro->build_macro_value (); cout << output << endl; } } ApplicationAnalyzer::ApplicationAnalyzer (const cmt_string& package_name, Constituent& constituent_ref) : DependencyAnalyzer (package_name, constituent_ref) { } void ApplicationAnalyzer::end () { cmt_string macro_name; cmt_string output; int i; if (uses.size () > 0) { for (i = 0; i < uses.size (); i++) { Libmap& libmap = *(uses[i]); libmap.set_used (); } Libmap::LibmapVector& table = Libmap::libmaps (); macro_name = constituent.name; macro_name += "linkopts"; output = "macro_prepend "; output += macro_name; output += " \""; for (i = 0; i < table.size (); i++) { Libmap& libmap = table[i]; if (libmap.used) { output += "$(implied_library_prefix)"; output += libmap.name; output += "$(implied_library_suffix) "; } } output += "\""; Cmt::parse_requirements_text (output, "", 0); Symbol* macro = Symbol::find (macro_name); output = macro_name; output += "="; output += macro->build_macro_value (); cout << output << endl; } } //-------------------------------------------------- //-------------------------------------------------- // // The UseAnalyzer is called first to reach all used packages. // For each package found, it retreives the *.triggers files // which contain the trigger descriptions for every constituent // of the package. // // For each trigger description file found, a TriggerAnalyzer is run // which in turn fills in the database of Libmap objects. // //-------------------------------------------------- class UseAnalyzer { public: void run (const cmt_string& constituent); void run (const cmt_string& location, const cmt_string& package, const cmt_string& filter_out = ""); }; //-------------------------------------------------- //-------------------------------------------------- void UseAnalyzer::run (const cmt_string& constituent) { Use* use = &(Use::current ()); run ("./", use->package, constituent); Use::UsePtrVector& uses = Use::uses (); for (int i = 0; i < uses.size (); i++) { use = uses[i]; cmt_string s; s = use->real_path; s += CmtSystem::file_separator (); s += use->package; s += CmtSystem::file_separator (); s += use->version; s += CmtSystem::file_separator (); if (use->style == mgr_style) s += "mgr"; else s += "cmt"; s += CmtSystem::file_separator (); run (s, use->package); } } void UseAnalyzer::run (const cmt_string& location, const cmt_string& package, const cmt_string& filter_out) { static cmt_regexp expression ("[.]triggers$"); TriggerAnalyzer analyzer (package); CmtSystem::cmt_string_vector files; CmtSystem::scan_dir (location, expression, files); cmt_string name; for (int i = 0; i < files.size (); i++) { const cmt_string& file = files[i]; if (filter_out != "") { CmtSystem::basename (file, ".triggers", name); if (name == filter_out) continue; } analyzer.run (file); } } //-------------------------------------------------- //-------------------------------------------------- void TriggerGenerator::run (const cmt_string& constituent_name) { Constituent* constituent = Constituent::find (constituent_name); Use* use = &(Use::current ()); cmt_string package = use->package; // UseAnalyzer use_analyzer (package); // use_analyzer.run ("cmt show uses -quiet"); UseAnalyzer use_analyzer; use_analyzer.run (constituent_name); cmt_string file_name; file_name = "./"; file_name += constituent_name; file_name += "_dependencies."; #ifdef WIN32 file_name += "nmake"; #else file_name += "make"; #endif DependencyAnalyzer* analyzer = 0; if (constituent->type == Library) { analyzer = new LibraryAnalyzer (package, *constituent); } else if (constituent->type == Application) { analyzer = new ApplicationAnalyzer (package, *constituent); } else { return; } if (analyzer->run (file_name) == Awk::failed) { cerr << " File " << file_name << " not found" << endl; } delete analyzer; } //--------------------------------------------------