The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
config_cache.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2016 by Pauli Nieminen <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
17 #include "config_cache.hpp"
18 #include "filesystem.hpp"
19 #include "gettext.hpp"
20 #include "game_config.hpp"
21 #include "log.hpp"
22 #include "marked-up_text.hpp"
23 #include "utils/sha1.hpp"
25 #include "serialization/parser.hpp"
27 #include "version.hpp"
28 
29 #include <boost/algorithm/string/replace.hpp>
30 #include <boost/iostreams/filter/gzip.hpp>
31 
32 static lg::log_domain log_cache("cache");
33 #define ERR_CACHE LOG_STREAM(err, log_cache)
34 #define LOG_CACHE LOG_STREAM(info, log_cache)
35 #define DBG_CACHE LOG_STREAM(debug, log_cache)
36 
37 namespace game_config
38 {
39 
41 {
42  static config_cache cache;
43  return cache;
44 }
45 
47  : force_valid_cache_(false)
48  , use_cache_(true)
49  , fake_invalid_cache_(false)
50  , defines_map_()
51  , cache_file_prefix_()
52 {
54  = "cache-v" +
55  boost::algorithm::replace_all_copy(game_config::revision,
56  ":", "_") +
57  "-";
58 
59  // To set-up initial defines map correctly
60  clear_defines();
61 }
62 
64 {
65  return defines_map_;
66 }
67 
69 {
70  LOG_CACHE << "Clearing defines map!" << std::endl;
71 
72  defines_map_.clear();
73 
74  //
75  // Set-up default defines map.
76  //
77 
78 #ifdef LOW_MEM
79  defines_map_["LOW_MEM"] = preproc_define();
80 #endif
81 
82 #ifdef __APPLE__
83  defines_map_["APPLE"] = preproc_define();
84 #endif
85 
86  defines_map_["WESNOTH_VERSION"] = preproc_define(game_config::wesnoth_version.str());
87 }
88 
90 {
91  load_configs(path, cfg);
92 }
93 
95 {
98  writer.write(cfg);
99 }
100 
102 {
103  if(defines.empty()) {
104  if(filesystem::file_exists(path)) {
106  }
107  return;
108  }
109 
112 
113  // Write all defines to stream.
114  for(const preproc_map::value_type& define : defines) {
115  define.second.write(writer, define.first);
116  }
117 }
118 
120 {
122  read_gz(cfg, *stream);
123 }
124 
126 {
128 }
129 
131 {
133 }
134 
136 {
137  //read the file and then write to the cache
138  filesystem::scoped_istream stream = preprocess_file(path, &defines_map);
139  read(cfg, *stream);
140 }
141 
143 {
144  static const std::string extension = ".gz";
145 
146  std::stringstream defines_string;
147  defines_string << path;
148 
149  bool is_valid = true;
150 
151  for(const preproc_map::value_type& d : defines_map_) {
152  //
153  // Only WESNOTH_VERSION is allowed to be non-empty.
154  //
155  if((!d.second.value.empty() || !d.second.arguments.empty()) &&
156  d.first != "WESNOTH_VERSION")
157  {
158  is_valid = false;
159  ERR_CACHE << "Invalid preprocessor define: " << d.first << '\n';
160  break;
161  }
162 
163  defines_string << " " << d.first;
164  }
165 
166  // Do cache check only if define map is valid and
167  // caching is allowed.
168  const std::string& cache_path = filesystem::get_cache_dir();
169 
170  if(is_valid && !cache_path.empty()) {
171  // Use a hash for a shorter display of the defines.
172  const std::string fname = cache_path + "/" +
174  sha1_hash(defines_string.str()).display();
175  const std::string fname_checksum = fname + ".checksum" + extension;
176 
177  filesystem::file_tree_checksum dir_checksum;
178 
180  try {
181  if(filesystem::file_exists(fname_checksum)) {
182  config checksum_cfg;
183 
184  DBG_CACHE << "Reading checksum: " << fname_checksum << "\n";
185  read_file(fname_checksum, checksum_cfg);
186 
187  dir_checksum = filesystem::file_tree_checksum(checksum_cfg);
188  }
189  } catch(config::error&) {
190  ERR_CACHE << "cache checksum is corrupt" << std::endl;
191  } catch(filesystem::io_exception&) {
192  ERR_CACHE << "error reading cache checksum" << std::endl;
193  }
194  }
195 
196  if(force_valid_cache_) {
197  LOG_CACHE << "skipping cache validation (forced)\n";
198  }
199 
200  if(filesystem::file_exists(fname + extension) && (force_valid_cache_ || (dir_checksum == filesystem::data_tree_checksum()))) {
201  LOG_CACHE << "found valid cache at '" << fname << extension << "' with defines_map " << defines_string.str() << "\n";
202  log_scope("read cache");
203 
204  try {
205  read_file(fname + extension,cfg);
206  const std::string define_file = fname + ".define" + extension;
207 
208  if(filesystem::file_exists(define_file)) {
210  }
211 
212  return;
213  } catch(config::error& e) {
214  ERR_CACHE << "cache " << fname << extension << " is corrupt. Loading from files: "<< e.message << std::endl;
215  } catch(filesystem::io_exception&) {
216  ERR_CACHE << "error reading cache " << fname << extension << ". Loading from files" << std::endl;
217  } catch (boost::iostreams::gzip_error& e) {
218  //read_file -> ... -> read_gz can throw this exception.
219  ERR_CACHE << "cache " << fname << extension << " is corrupt. Error code: " << e.error() << std::endl;
220  }
221  }
222 
223  LOG_CACHE << "no valid cache found. Writing cache to '" << fname << extension << " with defines_map "<< defines_string.str() << "'\n";
224 
225  // Now we need queued defines so read them to memory
227 
228  preproc_map copy_map(make_copy_map());
229 
230  read_configs(path, cfg, copy_map);
231  add_defines_map_diff(copy_map);
232 
233  try {
234  write_file(fname + extension, cfg);
235  write_file(fname + ".define" + extension, copy_map);
236 
237  config checksum_cfg;
238 
239  filesystem::data_tree_checksum().write(checksum_cfg);
240  write_file(fname_checksum, checksum_cfg);
241  } catch(filesystem::io_exception&) {
242  ERR_CACHE << "could not write to cache '" << fname << "'" << std::endl;
243  }
244 
245  return;
246  }
247 
248  LOG_CACHE << "Loading plain config instead of cache\n";
249 
250  preproc_map copy_map(make_copy_map());
251  read_configs(path, cfg, copy_map);
252  add_defines_map_diff(copy_map);
253 }
254 
256 {
257  config cfg;
258  read_file(path, cfg);
259 
260  DBG_CACHE << "Reading cached defines from: " << path << "\n";
261 
262  // use static preproc_define::read_pair(config) to make a object
263  // and pass that object config_cache_transaction::insert_to_active method
264  for(const config::any_child &value : cfg.all_children_range()) {
267  }
268 }
269 
271 {
272  const std::vector<std::string>& files = config_cache_transaction::instance().get_define_files();
273 
274  for(const std::string &path : files) {
276  }
277 }
278 
280 {
281  // Make sure that we have fake transaction if no real one is going on
282  fake_transaction fake;
283 
284  if (use_cache_) {
285  read_cache(path, cfg);
286  } else {
287  preproc_map copy_map(make_copy_map());
288  read_configs(path, cfg, copy_map);
289  add_defines_map_diff(copy_map);
290  }
291 }
292 
294 {
295  fake_invalid_cache_ = force;
296 }
297 
299 {
300  use_cache_ = use;
301 }
302 
304 {
305  force_valid_cache_ = force;
306 }
307 
309 {
311 }
312 
314 {
315  DBG_CACHE << "adding define: " << define << "\n";
316  defines_map_[define] = preproc_define();
317 
319  // we have to add this to active map too
321  std::make_pair(define, preproc_define()));
322  }
323 
324 }
325 
327 {
328  DBG_CACHE << "removing define: " << define << "\n";
329  defines_map_.erase(define);
330 
332  // we have to remove this from active map too
334  }
335 }
336 
338 {
339  std::vector<std::string> files, dirs;
341 
342  LOG_CACHE << "clean_cache(): " << files.size() << " files, "
343  << dirs.size() << " dirs to check\n";
344 
345  const std::string& exclude_current = cache_file_prefix_ + "*";
346 
347  bool status = true;
348 
349  status &= delete_cache_files(files, exclude_current);
350  status &= delete_cache_files(dirs, exclude_current);
351 
352  LOG_CACHE << "clean_cache(): done\n";
353 
354  return status;
355 }
356 
358 {
359  std::vector<std::string> files, dirs;
361 
362  LOG_CACHE << "purge_cache(): deleting " << files.size() << " files, "
363  << dirs.size() << " dirs\n";
364 
365  bool status = true;
366 
367  status &= delete_cache_files(files);
368  status &= delete_cache_files(dirs);
369 
370  LOG_CACHE << "purge_cache(): done\n";
371  return status;
372 }
373 
374 bool config_cache::delete_cache_files(const std::vector<std::string>& paths,
375  const std::string& exclude_pattern)
376 {
377  const bool delete_everything = exclude_pattern.empty();
378  bool status = true;
379 
380  for(const std::string& path : paths)
381  {
382  if(!delete_everything) {
384 
385  if(utils::wildcard_string_match(fn, exclude_pattern)) {
386  LOG_CACHE << "delete_cache_files(): skipping " << path
387  << " excluded by '" << exclude_pattern << "'\n";
388  continue;
389  }
390  }
391 
392  LOG_CACHE << "delete_cache_files(): deleting " << path << '\n';
393  if(!filesystem::delete_directory(path)) {
394  ERR_CACHE << "delete_cache_files(): could not delete "
395  << path << '\n';
396  status = false;
397  }
398  }
399 
400  return status;
401 }
402 
405 
407  : define_filenames_()
408  , active_map_()
409 {
410  assert(state_ == FREE);
411  state_ = NEW;
412  active_ = this;
413 }
414 
416 {
417  state_ = FREE;
418  active_ = 0;
419 }
420 
422 {
423  state_ = LOCKED;
424 }
425 
426 const std::vector<std::string>& config_cache_transaction::get_define_files() const
427 {
428  return define_filenames_;
429 }
430 
432 {
433  define_filenames_.push_back(file);
434 }
435 
437 {
438  if(active_map_.empty()) {
439  active_map_.insert(defines_map.begin(), defines_map.end());
440  if(get_state() == NEW) {
441  state_ = ACTIVE;
442  }
443  }
444 
445  return active_map_;
446 }
447 
448 namespace
449 {
450 
451 bool compare_define(const preproc_map::value_type& a, const preproc_map::value_type& b)
452 {
453  if(a.first < b.first) {
454  return true;
455  }
456 
457  if(b.first < a.first) {
458  return false;
459  }
460 
461  if(a.second < b.second) {
462  return true;
463  }
464 
465  return false;
466 }
467 
468 } // end anonymous namespace
469 
471 {
472  if(get_state() == ACTIVE) {
473  preproc_map temp;
474  std::set_difference(new_map.begin(),
475  new_map.end(),
476  active_map_.begin(),
477  active_map_.end(),
478  std::insert_iterator<preproc_map>(temp,temp.begin()),
479  &compare_define);
480 
481  for(const preproc_map::value_type &def : temp) {
482  insert_to_active(def);
483  }
484 
485  temp.swap(new_map);
486  } else if (get_state() == LOCKED) {
487  new_map.clear();
488  }
489 }
490 
491 void config_cache_transaction::insert_to_active(const preproc_map::value_type& def)
492 {
493  active_map_[def.first] = def.second;
494 }
495 
496 }
static config_cache & instance()
Get reference to the singleton object.
void write(const config &cfg)
void write_file(std::string file, const config &cfg)
void read_configs(const std::string &path, config &cfg, preproc_map &defines)
void read_file(const std::string &file, config &cfg)
void lock()
Lock the transaction so no more macros are added.
bool wildcard_string_match(const std::string &str, const std::string &match)
Match using '*' as any number of characters (including none), and '?' as any one character.
static config_cache_transaction * active_
void remove_define(const std::string &define)
Remove a entry to preproc defines map.
Holds a fake cache transaction if no real one is used.
#define d
std::vector< std::string > define_filenames_
static config_cache_transaction & instance()
const std::vector< std::string > & get_define_files() const
std::map< std::string, preproc_define > preproc_map
void read_gz(config &cfg, std::istream &file, abstract_validator *validator)
might throw a std::ios_base::failure especially a gzip_error
Definition: parser.cpp:454
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
GLuint GLuint stream
Definition: glew.h:5239
GLsizei const char ** path
Definition: glew.h:4654
bool delete_cache_files(const std::vector< std::string > &paths, const std::string &exclude_pattern="")
Class for writing a config out to a file in pieces.
bool clean_cache()
Deletes stale cache files not in use by the game.
void read_defines_file(const std::string &path)
GLsizei const GLfloat * value
Definition: glew.h:1817
all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:1127
std::string base_name(const std::string &file)
Returns the base filename of a file, with directory name stripped.
std::istream * istream_file(const std::string &fname, bool treat_failure_as_error=true)
const preproc_map & get_preproc_map() const
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
void get_config(const std::string &path, config &cfg)
Gets a config object from given path.
std::ostream * ostream_file(std::string const &fname, bool create_directory=true)
void add_define_file(const std::string &file)
void set_force_valid_cache(bool force)
Enable/disable cache validation.
#define LOG_CACHE
std::string path
static lg::log_domain log_cache("cache")
void set_use_cache(bool use)
Enable/disable caching.
void load_configs(const std::string &path, config &cfg)
std::string get_cache_dir()
void clear_defines()
Clear stored defines map to default values.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs=nullptr, file_name_option mode=FILE_NAME_ONLY, file_filter_option filter=NO_FILTER, file_reorder_option reorder=DONT_REORDER, file_tree_checksum *checksum=nullptr)
Populates 'files' with all the files and 'dirs' with all the directories in dir.
const std::string revision
Definition: game_config.cpp:57
void add_defines_map_diff(preproc_map &)
Game configuration data as global variables.
Definition: build_info.cpp:38
#define DBG_CACHE
An exception object used when an IO error occurs.
Definition: filesystem.hpp:40
static tcache cache
Definition: minimap.cpp:139
preproc_map & get_active_map(const preproc_map &defines_map)
#define log_scope(description)
Definition: log.hpp:185
void write(config &cfg) const
void read_cache(const std::string &path, config &cfg)
Declarations for File-IO.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:400
static int writer(lua_State *L, const void *b, size_t size, void *B)
Definition: lstrlib.cpp:166
const version_info wesnoth_version(VERSION)
bool delete_directory(const std::string &dirname, const bool keep_pbl=false)
const file_tree_checksum & data_tree_checksum(bool reset=false)
Get the time at which the data/ tree was last modified at.
Used to share macros between cache objects You have to create transaction object to load all macros t...
#define ERR_CACHE
std::istream * preprocess_file(std::string const &fname, preproc_map *defines)
void add_defines_map_diff(preproc_map &defines_map)
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:29
#define e
void insert_to_active(const preproc_map::value_type &def)
Used to let std::for_each insert new defines to active_map map to active.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
static preproc_map::value_type read_pair(const config &)
preproc_map & make_copy_map()
Interfaces for manipulating version numbers of engine, add-ons, etc.
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
void add_define(const std::string &define)
Add a entry to preproc defines map.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
void recheck_filetree_checksum()
Force cache checksum validation.
bool purge_cache()
Deletes all cache files.
Singleton class to manage game config file caching.
int cache_compression_level
Definition: game_config.cpp:66