config_cache.cpp

Go to the documentation of this file.
00001 /* $Id: config_cache.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2008 - 2012 by Pauli Nieminen <paniemin@cc.hut.fi>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 #define GETTEXT_DOMAIN "wesnoth-lib"
00017 
00018 #include "config_cache.hpp"
00019 #include "filesystem.hpp"
00020 #include "foreach.hpp"
00021 #include "gettext.hpp"
00022 #include "game_config.hpp"
00023 #include "game_display.hpp"
00024 #include "log.hpp"
00025 #include "marked-up_text.hpp"
00026 #include "show_dialog.hpp"
00027 #include "sha1.hpp"
00028 #include "serialization/binary_or_text.hpp"
00029 #include "serialization/parser.hpp"
00030 #include "version.hpp"
00031 
00032 #include <boost/algorithm/string/replace.hpp>
00033 
00034 static lg::log_domain log_cache("cache");
00035 #define ERR_CACHE LOG_STREAM(err, log_cache)
00036 #define LOG_CACHE LOG_STREAM(info, log_cache)
00037 #define DBG_CACHE LOG_STREAM(debug, log_cache)
00038 
00039 namespace game_config {
00040 
00041     config_cache& config_cache::instance()
00042     {
00043         static config_cache cache;
00044         return cache;
00045     }
00046 
00047     config_cache::config_cache() :
00048         force_valid_cache_(false),
00049         use_cache_(true),
00050         fake_invalid_cache_(false),
00051         defines_map_()
00052     {
00053         // To set-up initial defines map correctly
00054         clear_defines();
00055     }
00056 
00057     struct output {
00058         void operator()(const preproc_map::value_type& def)
00059         {
00060             DBG_CACHE << "key: " << def.first << " " << def.second << "\n";
00061         }
00062     };
00063     const preproc_map& config_cache::get_preproc_map() const
00064     {
00065         return defines_map_;
00066     }
00067 
00068     void config_cache::clear_defines()
00069     {
00070         LOG_CACHE << "Clearing defines map!\n";
00071         defines_map_.clear();
00072         // set-up default defines map
00073 
00074 #ifdef LOW_MEM
00075         defines_map_["LOW_MEM"] = preproc_define();
00076 #endif
00077 
00078         if (game_config::small_gui)
00079             defines_map_["SMALL_GUI"] = preproc_define();
00080 
00081 #if defined(__APPLE__)
00082         defines_map_["APPLE"] = preproc_define();
00083 #endif
00084 
00085         defines_map_["WESNOTH_VERSION"] = preproc_define(game_config::wesnoth_version.str());
00086 
00087     }
00088 
00089     void config_cache::get_config(const std::string& path, config& cfg)
00090     {
00091         load_configs(path, cfg);
00092     }
00093 
00094     void config_cache::write_file(std::string path, const config& cfg)
00095     {
00096         scoped_ostream stream = ostream_file(path);
00097         const bool gzip = true;
00098         config_writer writer(*stream, gzip, game_config::cache_compression_level);
00099         writer.write(cfg);
00100     }
00101     void config_cache::write_file(std::string path, const preproc_map& defines_map)
00102     {
00103         if (defines_map.empty())
00104         {
00105             if (file_exists(path))
00106             {
00107                 delete_directory(path);
00108             }
00109             return;
00110         }
00111         scoped_ostream stream = ostream_file(path);
00112         const bool gzip = true;
00113         config_writer writer(*stream, gzip, game_config::cache_compression_level);
00114 
00115         // write all defines to stream
00116         foreach (const preproc_map::value_type &define, defines_map) {
00117             define.second.write(writer, define.first);
00118         }
00119     }
00120 
00121     void config_cache::read_file(const std::string& path, config& cfg)
00122     {
00123         scoped_istream stream = istream_file(path);
00124         read_gz(cfg, *stream);
00125     }
00126 
00127     preproc_map& config_cache::make_copy_map()
00128     {
00129         return config_cache_transaction::instance().get_active_map(defines_map_);
00130     }
00131 
00132 
00133     static bool compare_define(const preproc_map::value_type& a, const preproc_map::value_type& b)
00134     {
00135         if (a.first < b.first)
00136             return true;
00137         if (b.first < a.first)
00138             return false;
00139         if (a.second < b.second)
00140             return true;
00141         return false;
00142     }
00143 
00144     void config_cache::add_defines_map_diff(preproc_map& defines_map)
00145     {
00146         return config_cache_transaction::instance().add_defines_map_diff(defines_map);
00147     }
00148 
00149 
00150     void config_cache::read_configs(const std::string& path, config& cfg, preproc_map& defines_map)
00151     {
00152         //read the file and then write to the cache
00153         scoped_istream stream = preprocess_file(path, &defines_map);
00154         read(cfg, *stream);
00155     }
00156 
00157     void config_cache::read_cache(const std::string& path, config& cfg)
00158     {
00159         const std::string extension = ".gz";
00160         bool is_valid = true;
00161         std::stringstream defines_string;
00162         defines_string << path;
00163         for(preproc_map::const_iterator i = defines_map_.begin(); i != defines_map_.end(); ++i) {
00164             if(i->second.value != "" || i->second.arguments.empty() == false) {
00165                 // VERSION is defined non-empty by the engine,
00166                 // it should be safe to rely on caches containing it.
00167                 if(i->first != "WESNOTH_VERSION") {
00168                     is_valid = false;
00169                     ERR_CACHE << "Preprocessor define not valid\n";
00170                     break;
00171                 }
00172             }
00173 
00174             defines_string << " " << i->first;
00175         }
00176 
00177         // Do cache check only if  define map is valid and
00178         // caching is allowed
00179         if(is_valid) {
00180             const std::string& cache = get_cache_dir();
00181             if(cache != "") {
00182                 sha1_hash sha(defines_string.str()); // use a hash for a shorter display of the defines
00183                 const std::string fname = cache + "/cache-v" +
00184                     boost::algorithm::replace_all_copy(game_config::revision, ":", "_") +
00185                     "-" + sha.display();
00186                 const std::string fname_checksum = fname + ".checksum" + extension;
00187 
00188                 file_tree_checksum dir_checksum;
00189 
00190                 if(!force_valid_cache_ && !fake_invalid_cache_) {
00191                     try {
00192                         if(file_exists(fname_checksum)) {
00193                             DBG_CACHE << "Reading checksum: " << fname_checksum << "\n";
00194                             config checksum_cfg;
00195                             read_file(fname_checksum, checksum_cfg);
00196                             dir_checksum = file_tree_checksum(checksum_cfg);
00197                         }
00198                     } catch(config::error&) {
00199                         ERR_CACHE << "cache checksum is corrupt\n";
00200                     } catch(io_exception&) {
00201                         ERR_CACHE << "error reading cache checksum\n";
00202                     }
00203                 }
00204 
00205                 if(force_valid_cache_) {
00206                     LOG_CACHE << "skipping cache validation (forced)\n";
00207                 }
00208 
00209                 if(file_exists(fname + extension) && (force_valid_cache_ || (dir_checksum == data_tree_checksum()))) {
00210                     LOG_CACHE << "found valid cache at '" << fname << extension << "' with defines_map " << defines_string.str() << "\n";
00211                     log_scope("read cache");
00212                     try {
00213                         read_file(fname + extension,cfg);
00214                         const std::string define_file = fname + ".define" + extension;
00215                         if (file_exists(define_file))
00216                         {
00217                             config_cache_transaction::instance().add_define_file(define_file);
00218                         }
00219                         return;
00220                     } catch(config::error& e) {
00221                         ERR_CACHE << "cache " << fname << extension << " is corrupt. Loading from files: "<< e.message<<"\n";
00222                     } catch(io_exception&) {
00223                         ERR_CACHE << "error reading cache " << fname << extension << ". Loading from files\n";
00224                     }
00225                 }
00226 
00227                 LOG_CACHE << "no valid cache found. Writing cache to '" << fname << extension << " with defines_map "<< defines_string.str() << "'\n";
00228                 // Now we need queued defines so read them to memory
00229                 read_defines_queue();
00230 
00231                 preproc_map copy_map(make_copy_map());
00232 
00233                 read_configs(path, cfg, copy_map);
00234 
00235                 add_defines_map_diff(copy_map);
00236 
00237                 try {
00238                     write_file(fname + extension, cfg);
00239                     write_file(fname + ".define" + extension, copy_map);
00240                     config checksum_cfg;
00241                     data_tree_checksum().write(checksum_cfg);
00242                     write_file(fname_checksum, checksum_cfg);
00243                 } catch(io_exception&) {
00244                     ERR_CACHE << "could not write to cache '" << fname << "'\n";
00245                 }
00246                 return;
00247             }
00248         }
00249         LOG_CACHE << "Loading plain config instead of cache\n";
00250         preproc_map copy_map(make_copy_map());
00251         read_configs(path, cfg, copy_map);
00252         add_defines_map_diff(copy_map);
00253     }
00254 
00255 
00256     void config_cache::read_defines_file(const std::string& path)
00257     {
00258         config cfg;
00259         read_file(path, cfg);
00260 
00261         DBG_CACHE << "Reading cached defines from: " << path << "\n";
00262 
00263         // use static preproc_define::read_pair(config) to make a object
00264         // and pass that object config_cache_transaction::insert_to_active method
00265         foreach (const config::any_child &value, cfg.all_children_range()) {
00266             config_cache_transaction::instance().insert_to_active(
00267                 preproc_define::read_pair(value.cfg));
00268         }
00269     }
00270 
00271     void config_cache::read_defines_queue()
00272     {
00273         const config_cache_transaction::filenames& files = config_cache_transaction::instance().get_define_files();
00274         foreach (const std::string &path, files) {
00275             read_defines_file(path);
00276         }
00277     }
00278 
00279     void config_cache::load_configs(const std::string& path, config& cfg)
00280     {
00281         // Make sure that we have fake transaction if no real one is going on
00282         fake_transaction fake;
00283 
00284         if (use_cache_) {
00285             read_cache(path, cfg);
00286         } else {
00287             preproc_map copy_map(make_copy_map());
00288             read_configs(path, cfg, copy_map);
00289             add_defines_map_diff(copy_map);
00290         }
00291     }
00292 
00293     void config_cache::set_force_invalid_cache(bool force)
00294     {
00295         fake_invalid_cache_ = force;
00296     }
00297 
00298     void config_cache::set_use_cache(bool use)
00299     {
00300         use_cache_ = use;
00301     }
00302 
00303     void config_cache::set_force_valid_cache(bool force)
00304     {
00305         force_valid_cache_ = force;
00306     }
00307 
00308     void config_cache::recheck_filetree_checksum()
00309     {
00310         data_tree_checksum(true);
00311     }
00312 
00313     void config_cache::add_define(const std::string& define)
00314     {
00315         DBG_CACHE << "adding define: " << define << "\n";
00316         defines_map_[define] = preproc_define();
00317         if (config_cache_transaction::is_active())
00318         {
00319             // we have to add this to active map too
00320             config_cache_transaction::instance().get_active_map(defines_map_).insert(
00321                     std::make_pair(define, preproc_define()));
00322         }
00323 
00324     }
00325 
00326     void config_cache::remove_define(const std::string& define)
00327     {
00328         DBG_CACHE << "removing define: " << define << "\n";
00329         defines_map_.erase(define);
00330         if (config_cache_transaction::is_active())
00331         {
00332             // we have to remove this from active map too
00333             config_cache_transaction::instance().get_active_map(defines_map_).erase(define);
00334         }
00335     }
00336 
00337     config_cache_transaction::state config_cache_transaction::state_ = FREE;
00338     config_cache_transaction* config_cache_transaction::active_ = 0;
00339 
00340     config_cache_transaction::config_cache_transaction()
00341         : define_filenames_()
00342         , active_map_()
00343     {
00344         assert(state_ == FREE);
00345         state_ = NEW;
00346         active_ = this;
00347     }
00348 
00349     config_cache_transaction::~config_cache_transaction()
00350     {
00351         state_ = FREE;
00352         active_ = 0;
00353     }
00354 
00355     void config_cache_transaction::lock()
00356     {
00357         state_ = LOCKED;
00358     }
00359 
00360     const config_cache_transaction::filenames& config_cache_transaction::get_define_files() const
00361     {
00362         return define_filenames_;
00363     }
00364 
00365     void config_cache_transaction::add_define_file(const std::string& file)
00366     {
00367         define_filenames_.push_back(file);
00368     }
00369 
00370     preproc_map& config_cache_transaction::get_active_map(const preproc_map& defines_map)
00371     {
00372         if(active_map_.empty())
00373         {
00374             std::copy(defines_map.begin(),
00375                     defines_map.end(),
00376                     std::insert_iterator<preproc_map>(active_map_, active_map_.begin()));
00377             if ( get_state() == NEW)
00378                 state_ = ACTIVE;
00379          }
00380 
00381         return active_map_;
00382     }
00383 
00384     void config_cache_transaction::add_defines_map_diff(preproc_map& new_map)
00385     {
00386 
00387         if (get_state() == ACTIVE)
00388         {
00389             preproc_map temp;
00390             std::set_difference(new_map.begin(),
00391                     new_map.end(),
00392                     active_map_.begin(),
00393                     active_map_.end(),
00394                     std::insert_iterator<preproc_map>(temp,temp.begin()),
00395                     &compare_define);
00396 
00397             foreach (const preproc_map::value_type &def, temp) {
00398                 insert_to_active(def);
00399             }
00400 
00401             temp.swap(new_map);
00402         } else if (get_state() == LOCKED) {
00403             new_map.clear();
00404         }
00405     }
00406 
00407     void config_cache_transaction::insert_to_active(const preproc_map::value_type& def)
00408     {
00409         active_map_[def.first] = def.second;
00410     }
00411 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Thu May 24 2012 01:02:33 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs