RepoParser.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include <iostream>
00013 #include "zypp/base/Easy.h"
00014 #include "zypp/base/Logger.h"
00015 #include "zypp/base/LogTools.h"
00016 #include "zypp/base/Iterator.h"
00017 #include "zypp/base/String.h"
00018 #include "zypp/base/Gettext.h"
00019 
00020 #include "zypp/parser/susetags/FileReaderBaseImpl.h"
00021 #include "zypp/parser/susetags/RepoParser.h"
00022 #include "zypp/parser/susetags/ContentFileReader.h"
00023 #include "zypp/parser/susetags/PackagesFileReader.h"
00024 #include "zypp/parser/susetags/PackagesLangFileReader.h"
00025 #include "zypp/parser/susetags/PackagesDuFileReader.h"
00026 #include "zypp/parser/susetags/PatternFileReader.h"
00027 #include "zypp/parser/susetags/RepoIndex.h"
00028 #include "zypp/parser/ParseException.h"
00029 
00030 #include "zypp/ZConfig.h"
00031 
00032 using std::endl;
00033 #undef  ZYPP_BASE_LOGGER_LOGGROUP
00034 #define ZYPP_BASE_LOGGER_LOGGROUP "parser::susetags"
00035 
00037 namespace zypp
00038 { 
00039 
00040   namespace parser
00041   { 
00042 
00043     namespace susetags
00044     { 
00045 
00047       //
00048       //        CLASS NAME : RepoParser::Impl
00049       //
00053       class RepoParser::Impl
00054       {
00055         public:
00056           Impl( const data::RecordId & repositoryId_r,
00057                 data::ResolvableDataConsumer & consumer_r,
00058                 const ProgressData::ReceiverFnc & fnc_r )
00059           : _repositoryId( repositoryId_r )
00060           , _consumer( consumer_r )
00061           {
00062             _ticks.sendTo( fnc_r );
00063           }
00064 
00066           void parse( const Pathname & reporoot_r );
00067 
00070 
00071           void consumeIndex( const RepoIndex_Ptr & data_r )
00072           {
00073             //SEC << "[Index]" << data_r << endl;
00074             _repoIndex = data_r;
00075           }
00076 
00078           void consumeProd( const data::Product_Ptr & data_r )
00079           {
00080             MIL << "[Product] " << data_r << endl;
00081             ++_stats.prod;
00082             _prodData = data_r;
00083             _defaultVendor = data_r->vendor;
00084             _consumer.consumeProduct( _repositoryId, data_r );
00085           }
00086 
00088           void consumePkg( const data::Package_Ptr & data_r )
00089           {
00090             fixVendor( data_r );
00091             fixLocationPath( data_r );
00092             resolveSharedDataTag( data_r );
00093 
00094             ++_stats.pack;
00095             data::RecordId newid = _consumer.consumePackage( _repositoryId, data_r );
00096 
00097             // remember for later reference
00098             idMapAdd( makeSharedIdent( ResTraits<Package>::kind,
00099                                        data_r->name,
00100                                        data_r->edition,
00101                                        data_r->arch ),
00102                       newid );
00103           }
00104 
00106           void consumeSrcPkg( const data::SrcPackage_Ptr & data_r )
00107           {
00108             fixVendor( data_r );
00109             fixLocationPath( data_r );
00110             resolveSharedDataTag( data_r );
00111 
00112             ++_stats.srcp;
00113             data::RecordId newid = _consumer.consumeSourcePackage( _repositoryId, data_r );
00114 
00115             // remember for later reference
00116             idMapAdd( makeSharedIdent( ResTraits<SrcPackage>::kind,
00117                                        data_r->name,
00118                                        data_r->edition,
00119                                        data_r->arch ),
00120                       newid );
00121           }
00122 
00124           void consumePkgLang( const data::Package_Ptr & data_r )
00125           {
00126             data::RecordId id = idMapGet( makeSharedIdent( ResTraits<Package>::kind,
00127                                                            data_r->name,
00128                                                            data_r->edition,
00129                                                            data_r->arch ) );
00130             if ( id != data::noRecordId )
00131             {
00132               _consumer.updatePackageLang( id, data_r );
00133             }
00134           }
00135 
00137           void consumeSrcPkgLang( const data::SrcPackage_Ptr & data_r )
00138           {
00139             data::RecordId id = idMapGet( makeSharedIdent( ResTraits<SrcPackage>::kind,
00140                                                            data_r->name,
00141                                                            data_r->edition,
00142                                                            data_r->arch ) );
00143             if ( id != data::noRecordId )
00144             {
00145               _consumer.updatePackageLang( id, data_r );
00146             }
00147           }
00148 
00150           void consumePkgDu( const data::Package_Ptr & data_r )
00151           {
00152             data::RecordId id = idMapGet( makeSharedIdent( ResTraits<Package>::kind,
00153                                                            data_r->name,
00154                                                            data_r->edition,
00155                                                            data_r->arch ) );
00156             if ( id != data::noRecordId )
00157             {
00158               _consumer.consumeDiskUsage( id, data_r->diskusage );
00159             }
00160           }
00161 
00163           void consumeSrcPkgDu( const data::SrcPackage_Ptr & data_r )
00164           {
00165             data::RecordId id = idMapGet( makeSharedIdent( ResTraits<SrcPackage>::kind,
00166                                                            data_r->name,
00167                                                            data_r->edition,
00168                                                            data_r->arch ) );
00169             if ( id != data::noRecordId )
00170             {
00171               _consumer.consumeDiskUsage( id, data_r->diskusage );
00172             }
00173           }
00174 
00176           void consumePat( const data::Pattern_Ptr & data_r )
00177           {
00178             //SEC << "[Pattern]" << data_r << endl;
00179             fixVendor( data_r );
00180             ++_stats.patt;
00181             _consumer.consumePattern( _repositoryId, data_r );
00182           }
00184 
00185         public:
00187           void fixVendor( const data::ResObject_Ptr & data_r )
00188           {
00189             if ( data_r->vendor.empty() && ! _defaultVendor.empty() )
00190             {
00191               data_r->vendor = _defaultVendor;
00192             }
00193           }
00194 
00196           void fixLocationPath( const data::Packagebase_Ptr & data_r )
00197           {
00198             Pathname tofix( data_r->repositoryLocation.filename() );
00199             data_r->repositoryLocation.changeFilename( _datadir / tofix );
00200           }
00201 
00203           void resolveSharedDataTag( const data::Packagebase_Ptr & data_r )
00204           {
00205             if ( ! data_r->sharedDataTag.empty() )
00206             {
00207               data_r->shareDataWith = idMapGet( data_r->sharedDataTag );
00208             }
00209           }
00210 
00211         public:
00215           Pathname assertMandatoryFile( const Pathname & file_r, bool lookgz = true ) const
00216           {
00217             if ( lookgz )
00218             {
00219               PathInfo gzinputfile( _reporoot / (file_r.extend(".gz")) );
00220               if ( !gzinputfile.isFile() )
00221               {
00222                 WAR << _reporoot << ": Skip gz required file (will look for non-gz): " <<  file_r.extend(".gz").asString() << endl;
00223               }
00224               else
00225               {
00226                 return gzinputfile.path();
00227               }
00228             }
00229             PathInfo inputfile( _reporoot / file_r );
00230             if ( ! inputfile.isFile() )
00231             {
00232               ZYPP_THROW( ParseException( _reporoot.asString() + ": " + _("Required file is missing: ") + file_r.asString() ) );
00233             }
00234             return inputfile.path();
00235           }
00236 
00240           Pathname getOptionalFile( const Pathname & file_r, bool lookgz = true  ) const
00241           {
00242             if ( lookgz )
00243             {
00244               PathInfo gzinputfile( _reporoot / (file_r.extend(".gz")) );
00245               if ( !gzinputfile.isFile() )
00246               {
00247                 //WAR << _reporoot << ": Skip optional file: " <<  file_r.extend(".gz").asString() << endl;
00248               }
00249               else
00250               {
00251                 return gzinputfile.path();
00252               }
00253             }
00254             PathInfo inputfile( _reporoot / file_r );
00255             if ( ! inputfile.isFile() )
00256             {
00257               //WAR << _reporoot << ": Skip optional file: " <<  file_r.asString() << endl;
00258               return Pathname();
00259             }
00260             return inputfile.path();
00261           }
00262 
00263           bool isPatternFile( const std::string & name_r ) const
00264           {
00265             return(
00266                 (name_r.size() > 4 && name_r.substr( name_r.size() - 4 ) == ".pat" )
00267              || (name_r.size() > 7 && name_r.substr( name_r.size() - 7 ) == ".pat.gz" )
00268             );
00269           }
00270 
00272           bool haveLocale( const Locale & locale_r ) const
00273           {
00274             std::string searchFor( "packages." + locale_r.code() );
00275             for ( RepoIndex::FileChecksumMap::const_iterator it = _repoIndex->metaFileChecksums.begin();
00276                   it != _repoIndex->metaFileChecksums.end(); ++it )
00277             {
00278               if ( it->first == searchFor )
00279               {
00280                 // got it
00281                 PathInfo inputfile( _reporoot / _descrdir / searchFor );
00282                 if ( ! inputfile.isFile() )
00283                 {
00284                   WAR << "Known and desired file is not on disk: " << inputfile << endl;
00285                 }
00286                 else
00287                   return true; // got it
00288               }
00289 
00290               if ( it->first == (searchFor+".gz") )
00291               {
00292                 // got it
00293                 PathInfo inputfile( _reporoot / _descrdir / (searchFor+".gz") );
00294                 if ( ! inputfile.isFile() )
00295                 {
00296                   WAR << "Known and desired file is not on disk: " << inputfile << endl;
00297                 }
00298                 else
00299                   return true; // got it
00300               }
00301             }
00302             return false; // not found
00303           }
00304 
00308           void parseLocaleIf( const Locale & locale_r )
00309           {
00310             Locale toParse( locale_r );
00311             bool alreadyParsed = false;
00312 
00313             while ( toParse != Locale::noCode )
00314             {
00315               alreadyParsed = ( _parsedLocales.find( toParse ) != _parsedLocales.end() );
00316               if ( alreadyParsed || haveLocale( toParse ) )
00317                 break; // no return because we want to log in case of a fallback
00318               toParse = toParse.fallback();
00319             }
00320 
00321             if ( toParse != locale_r )
00322             {
00323               WAR << "Using fallback locale " << toParse << " for " << locale_r << endl;
00324             }
00325 
00326             if ( alreadyParsed )
00327             {
00328               return; // now return...
00329             }
00330             // ...or parse
00331             _parsedLocales.insert( toParse ); // don't try again.
00332 
00333             Pathname inputfile( getOptionalFile( _descrdir / ("packages." + toParse.code()) ) );
00334             if ( ! inputfile.empty() )
00335             {
00336               PackagesLangFileReader reader;
00337               reader.setLocale( toParse );
00338               reader.setPkgConsumer( bind( &Impl::consumePkgLang, this, _1 ) );
00339               reader.setSrcPkgConsumer( bind( &Impl::consumeSrcPkgLang, this, _1 ) );
00340               CombinedProgressData progress( _ticks, PathInfo(inputfile).size()  );
00341               reader.parse( inputfile, progress );
00342             }
00343 
00344             if ( ! _ticks.incr( PathInfo(inputfile).size() ) )
00345               ZYPP_THROW( AbortRequestException() );
00346           }
00347 
00348         private:
00349           void idMapAdd( const std::string & key_r, data::RecordId value_r )
00350           {
00351             if ( _idMap[key_r] != data::noRecordId )
00352             {
00353               WAR << "Multiple record ids for " << key_r
00354                   << " (first " << _idMap[key_r] << ", now " << value_r << ")" << endl;
00355             }
00356             _idMap[key_r] = value_r;
00357           }
00358 
00359           data::RecordId idMapGet( const std::string & key_r )
00360           {
00361             data::RecordId ret = _idMap[key_r];
00362             if ( ret == data::noRecordId )
00363             {
00364               //WAR << "No record id for " << key_r << endl;
00365             }
00366             return ret;
00367           }
00368 
00369         private:
00370           data::RecordId                 _repositoryId;
00371           data::ResolvableDataConsumer & _consumer;
00372           ProgressData                   _ticks;
00373 
00374         private: // these (and _ticks) are actually scoped per parse() run.
00375           RepoIndex_Ptr     _repoIndex;
00376           data::Product_Ptr _prodData;
00377           std::string       _defaultVendor;
00378           Pathname          _reporoot; // full path
00379           Pathname          _descrdir; // path below reporoot
00380           Pathname          _datadir;  // path below reporoot
00381 
00383           std::set<Locale>  _parsedLocales;
00384 
00386           std::map<std::string,data::RecordId> _idMap;
00387 
00388           struct Stats {
00389             DefaultIntegral<unsigned,0> prod;
00390             DefaultIntegral<unsigned,0> patt;
00391             DefaultIntegral<unsigned,0> pack;
00392             DefaultIntegral<unsigned,0> srcp;
00393           };
00394           Stats _stats;
00395       };
00397 
00399       //
00400       //        METHOD NAME : RepoParser::Impl::parse
00401       //        METHOD TYPE : void
00402       //
00403       void RepoParser::Impl::parse( const Pathname & reporoot_r )
00404       {
00405         _prodData = 0;
00406         _repoIndex = 0;
00407         _defaultVendor.clear();
00408         _reporoot = reporoot_r;
00409         _descrdir = _datadir = Pathname();
00410         _parsedLocales.clear();
00411 
00412         // Content file first to get the repoindex
00413         {
00414           Pathname inputfile( assertMandatoryFile( "content" ) );
00415           ContentFileReader content;
00416           content.setProductConsumer( bind( &Impl::consumeProd, this, _1 ) );
00417           content.setRepoIndexConsumer( bind( &Impl::consumeIndex, this, _1 ) );
00418           content.parse( inputfile );
00419         }
00420         if ( ! _repoIndex )
00421         {
00422           ZYPP_THROW( ParseException( reporoot_r.asString() + ": " + "No repository index in content file." ) );
00423         }
00424         DBG << _repoIndex << endl;
00425 
00426         // Prepare parsing
00427         _descrdir = _repoIndex->descrdir; // path below reporoot
00428         _datadir  = _repoIndex->datadir;  // path below reporoot
00429 
00430         _ticks.name( "Parsing susetags repo at " + reporoot_r.asString() );
00431 
00432         // calculate progress range based on file sizes to parse
00433         int jobssize = 0;
00434         for ( RepoIndex::FileChecksumMap::const_iterator it = _repoIndex->metaFileChecksums.begin();
00435               it != _repoIndex->metaFileChecksums.end(); ++it )
00436         {
00437           // here the paths already contain the gz extension
00438           jobssize += PathInfo(getOptionalFile(_descrdir / it->first, false)).size();
00439 
00440         }
00441         MIL << "Total job size: " << jobssize << endl;
00442 
00443         _ticks.range(jobssize);
00444 
00445         if ( ! _ticks.toMin() )
00446           ZYPP_THROW( AbortRequestException() );
00447 
00448         // Start with packages
00449         {
00450           Pathname inputfile( assertMandatoryFile( _descrdir / "packages" ) );
00451           PackagesFileReader reader;
00452           reader.setPkgConsumer( bind( &Impl::consumePkg, this, _1 ) );
00453           reader.setSrcPkgConsumer( bind( &Impl::consumeSrcPkg, this, _1 ) );
00454 
00455           CombinedProgressData progress( _ticks, PathInfo(inputfile).size() );
00456           reader.parse( inputfile, progress );
00457         }
00458 
00459         // Now process packages.lang. Always parse 'en'.
00460         // At least packages.en is mandatory, because the file might
00461         // contain license texts.
00462         assertMandatoryFile( _descrdir / "packages.en" );
00463         parseLocaleIf( Locale("en") );
00464         // For each wanted locale at least
00465         // some fallback, if locale is not present.
00466         parseLocaleIf( ZConfig::instance().textLocale() );
00467 
00468         // Now process packages.DU.
00469         //if ( 0 ) // remove the if to enable, but leave the {} around.
00470         {
00471           Pathname inputfile( getOptionalFile( _descrdir / "packages.DU" ) );
00472           if ( ! inputfile.empty() )
00473           {
00474             PackagesDuFileReader reader;
00475             reader.setPkgConsumer( bind( &Impl::consumePkgDu, this, _1 ) );
00476             reader.setSrcPkgConsumer( bind( &Impl::consumeSrcPkgDu, this, _1 ) );
00477 
00478             CombinedProgressData progress( _ticks, PathInfo(inputfile).size() );
00479             reader.parse( inputfile, progress );
00480           }
00481         }
00482 
00483         // Now process the rest of RepoIndex
00484         for ( RepoIndex::FileChecksumMap::const_iterator it = _repoIndex->metaFileChecksums.begin();
00485               it != _repoIndex->metaFileChecksums.end(); ++it )
00486         {
00487           if ( isPatternFile( it->first ) )
00488           {
00489             // *** see also zypp/repo/susetags/Downloader.cc ***
00490 
00491             // omit unwanted patterns, see https://bugzilla.novell.com/show_bug.cgi?id=298716
00492             // expect "<name>.<arch>.pat[.gz]", <name> might contain additional dots
00493             // split at dots, take .pat or .pat.gz into account
00494 
00495             // vector of splitted pattern filename elements
00496             std::vector<std::string> patparts;
00497 
00498             // this is the offset from the last element of patparts
00499             unsigned archoff = 2;
00500 
00501             unsigned count = str::split( it->first, std::back_inserter( patparts ), "." );
00502 
00503             if ( patparts[count-1] == "gz" )
00504               archoff++;
00505 
00506             if ( count > archoff )
00507             {
00508               try                               // might by an invalid architecture
00509               {
00510                 Arch patarch( patparts[count-archoff] );
00511                 if ( !patarch.compatibleWith( ZConfig::instance().systemArchitecture() ) )
00512                 {
00513                   // discard, if not compatible
00514                   MIL << "Discarding pattern " << it->first << endl;
00515                   continue;
00516                 }
00517               }
00518               catch ( const Exception & excpt )
00519               {
00520                 WAR << "Pattern file name does not contain recognizable architecture: " << it->first << endl;
00521                 // keep .pat file if it doesn't contain an recognizable arch
00522               }
00523             }
00524 
00525             Pathname inputfile( getOptionalFile( _descrdir / it->first, false /*filename already contains .gz */ ) );
00526             if ( ! inputfile.empty() )
00527             {
00528               PatternFileReader reader;
00529               reader.setConsumer( bind( &Impl::consumePat, this, _1 ) );
00530               CombinedProgressData progress( _ticks, PathInfo(inputfile).size()  );
00531               reader.parse( inputfile, progress );
00532             }
00533           }
00534         }
00535 
00536         // Done
00537         if ( ! _ticks.toMax() )
00538           ZYPP_THROW( AbortRequestException() );
00539 
00540         MIL << "DONE " << reporoot_r << "("
00541             << _stats.prod << " products, "
00542             << _stats.patt << " patterns, "
00543             << _stats.pack << " packages, "
00544             << _stats.srcp << " srcpackages)" << endl;
00545       }
00547       //
00548       //        CLASS NAME : RepoParser
00549       //
00551 
00553       //
00554       //        METHOD NAME : RepoParser::RepoParser
00555       //        METHOD TYPE : Ctor
00556       //
00557       RepoParser::RepoParser( const data::RecordId & repositoryId_r,
00558                               data::ResolvableDataConsumer & consumer_r,
00559                               const ProgressData::ReceiverFnc & fnc_r )
00560       : _pimpl( new Impl( repositoryId_r, consumer_r, fnc_r ) )
00561       {}
00562 
00564       //
00565       //        METHOD NAME : RepoParser::~RepoParser
00566       //        METHOD TYPE : Dtor
00567       //
00568       RepoParser::~RepoParser()
00569       {}
00570 
00572       //
00573       //        METHOD NAME : RepoParser::parse
00574       //        METHOD TYPE : void
00575       //
00576       void RepoParser::parse( const Pathname & reporoot_r )
00577       {
00578         _pimpl->parse( reporoot_r );
00579       }
00580 
00582     } // namespace susetags
00585   } // namespace parser
00588 } // namespace zypp

Generated on Tue Sep 25 19:23:02 2007 for libzypp by  doxygen 1.5.3