00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
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
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
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
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
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
00166
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
00178
00179 if(is_valid) {
00180 const std::string& cache = get_cache_dir();
00181 if(cache != "") {
00182 sha1_hash sha(defines_string.str());
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
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
00264
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
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
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
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 }