The Battle for Wesnoth  1.15.0-dev
build_info.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2015 - 2018 by Iris Morelle <shadowm2006@gmail.com>
3  Part of the Battle for Wesnoth Project https://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 "build_info.hpp"
18 
19 #include "desktop/version.hpp"
20 #include "game_config.hpp"
21 #include "filesystem.hpp"
22 #include "formatter.hpp"
23 #include "gettext.hpp"
25 #include "game_version.hpp"
26 
27 #include <algorithm>
28 
29 #include <SDL.h>
30 #include <SDL_image.h>
31 #include <SDL_mixer.h>
32 #include <SDL_ttf.h>
33 
34 #include <boost/version.hpp>
35 
36 #ifndef __APPLE__
37 #include <openssl/crypto.h>
38 #include <openssl/opensslv.h>
39 #endif
40 
41 #include <pango/pangocairo.h>
42 
43 #ifdef __APPLE__
44 // apple_notification.mm uses Foundation.h, which is an Objective-C header;
45 // but CoreFoundation.h is a C header which also defines these.
46 #include <CoreFoundation/CoreFoundation.h>
47 #endif
48 
49 namespace game_config
50 {
51 
52 namespace {
53 
54 struct version_table_manager
55 {
56  std::vector<std::string> compiled, linked, names;
57  std::vector<optional_feature> features;
58 
59  version_table_manager();
60 };
61 
62 const version_table_manager versions;
63 
64 #if 0
65 std::string format_version(unsigned a, unsigned b, unsigned c)
66 {
67  return formatter() << a << '.' << b << '.' << c;
68 }
69 #endif
70 
71 std::string format_version(const SDL_version& v)
72 {
73  return formatter() << static_cast<unsigned>(v.major) << '.'
74  << static_cast<unsigned>(v.minor) << '.'
75  << static_cast<unsigned>(v.patch);
76 }
77 
78 #ifndef __APPLE__
79 
80 std::string format_openssl_patch_level(uint8_t p)
81 {
82  return p <= 26
83  ? std::string(1, 'a' + static_cast<char>(p) - 1)
84  : "patch" + std::to_string(p);
85 }
86 
87 std::string format_openssl_version(long v)
88 {
89  int major, minor, fix, patch, status;
90  std::ostringstream fmt;
91 
92  //
93  // The people who maintain OpenSSL are not from this world. I suppose it's
94  // only fair that I'm the one who gets to try to make sense of their version
95  // encoding scheme. -- shadowm
96  //
97 
98  if(v < 0x0930L) {
99  // Pre-0.9.3 seems simpler times overall.
100  minor = v & 0x0F00L >> 8;
101  fix = v & 0x00F0L >> 4;
102  patch = v & 0x000FL;
103 
104  fmt << "0." << minor << '.' << fix;
105  if(patch) {
106  fmt << format_openssl_patch_level(patch);
107  }
108  } else {
109  //
110  // Note that they either assume the major version will never be greater than
111  // 9, they plan to use hexadecimal digits for versions 10.x.x through
112  // 15.x.x, or they expect long to be always > 32-bits by then. Who the hell
113  // knows, really.
114  //
115  major = (v & 0xF0000000L) >> 28;
116  minor = (v & 0x0FF00000L) >> 20;
117  fix = (v & 0x000FF000L) >> 12;
118  patch = (v & 0x00000FF0L) >> 4;
119  status = (v & 0x0000000FL);
120 
121  if(v < 0x00905100L) {
122  //
123  // From wiki.openssl.org (also mentioned in opensslv.h, in the most oblique
124  // fashion possible):
125  //
126  // "Versions between 0.9.3 and 0.9.5 had a version identifier with this interpretation:
127  // MMNNFFRBB major minor fix final beta/patch"
128  //
129  // Both the wiki and opensslv.h fail to accurately list actual version
130  // numbers that ended up used in the wild -- e.g. 0.9.3a is supposedly
131  // 0x0090301f when it really was 0x00903101.
132  //
133  const uint8_t is_final = (v & 0xF00L) >> 8;
134  status = is_final ? 0xF : 0;
135  patch = v & 0xFFL;
136  } else if(v < 0x00906000L) {
137  //
138  // Quoth opensslv.h:
139  //
140  // "For continuity reasons (because 0.9.5 is already out, and is coded
141  // 0x00905100), between 0.9.5 and 0.9.6 the coding of the patch level
142  // part is slightly different, by setting the highest bit. This means
143  // that 0.9.5a looks like this: 0x0090581f. At 0.9.6, we can start
144  // with 0x0090600S..."
145  //
146  patch ^= 1 << 7;
147  }
148 
149  fmt << major << '.' << minor << '.' << fix;
150 
151  if(patch) {
152  fmt << format_openssl_patch_level(patch);
153  }
154 
155  if(status == 0x0) {
156  fmt << "-dev";
157  } else if(status < 0xF) {
158  fmt << "-beta" << status;
159  }
160  }
161 
162  return fmt.str();
163 
164 }
165 
166 #endif
167 
168 version_table_manager::version_table_manager()
169  : compiled(LIB_COUNT, "")
170  , linked(LIB_COUNT, "")
171  , names(LIB_COUNT, "")
172  , features()
173 {
174  SDL_version sdl_version;
175 
176 
177  //
178  // SDL
179  //
180 
181  SDL_VERSION(&sdl_version);
182  compiled[LIB_SDL] = format_version(sdl_version);
183 
184  SDL_GetVersion(&sdl_version);
185  linked[LIB_SDL] = format_version(sdl_version);
186 
187  names[LIB_SDL] = "SDL";
188 
189  //
190  // SDL_image
191  //
192 
193  SDL_IMAGE_VERSION(&sdl_version);
194  compiled[LIB_SDL_IMAGE] = format_version(sdl_version);
195 
196  const SDL_version* sdl_rt_version = IMG_Linked_Version();
197  if(sdl_rt_version) {
198  linked[LIB_SDL_IMAGE] = format_version(*sdl_rt_version);
199  }
200 
201  names[LIB_SDL_IMAGE] = "SDL_image";
202 
203  //
204  // SDL_mixer
205  //
206 
207  SDL_MIXER_VERSION(&sdl_version);
208  compiled[LIB_SDL_MIXER] = format_version(sdl_version);
209 
210  sdl_rt_version = Mix_Linked_Version();
211  if(sdl_rt_version) {
212  linked[LIB_SDL_MIXER] = format_version(*sdl_rt_version);
213  }
214 
215  names[LIB_SDL_MIXER] = "SDL_mixer";
216 
217  //
218  // SDL_ttf
219  //
220 
221  SDL_TTF_VERSION(&sdl_version);
222  compiled[LIB_SDL_TTF] = format_version(sdl_version);
223 
224  sdl_rt_version = TTF_Linked_Version();
225  if(sdl_rt_version) {
226  linked[LIB_SDL_TTF] = format_version(*sdl_rt_version);
227  }
228 
229  names[LIB_SDL_TTF] = "SDL_ttf";
230 
231  //
232  // Boost
233  //
234 
235  compiled[LIB_BOOST] = BOOST_LIB_VERSION;
236  std::replace(compiled[LIB_BOOST].begin(), compiled[LIB_BOOST].end(), '_', '.');
237  names[LIB_BOOST] = "Boost";
238 
239  //
240  // OpenSSL/libcrypto
241  //
242 
243 #ifndef __APPLE__
244  compiled[LIB_CRYPTO] = format_openssl_version(OPENSSL_VERSION_NUMBER);
245  linked[LIB_CRYPTO] = format_openssl_version(SSLeay());
246  names[LIB_CRYPTO] = "OpenSSL/libcrypto";
247 #endif
248 
249  //
250  // Cairo
251  //
252 
253  compiled[LIB_CAIRO] = CAIRO_VERSION_STRING;
254  linked[LIB_CAIRO] = cairo_version_string();
255  names[LIB_CAIRO] = "Cairo";
256 
257  //
258  // Pango
259  //
260 
261  compiled[LIB_PANGO] = PANGO_VERSION_STRING;
262  linked[LIB_PANGO] = pango_version_string();
263  names[LIB_PANGO] = "Pango";
264 
265  //
266  // Features table.
267  //
268 
269  features.emplace_back(N_("feature^JPG screenshots"));
270 #ifdef SDL_IMAGE_VERSION_ATLEAST
271 #if SDL_IMAGE_VERSION_ATLEAST(2, 0, 2)
272  features.back().enabled = true;
273 #endif
274 #endif
275 
276  features.emplace_back(N_("feature^Lua console completion"));
277 #ifdef HAVE_HISTORY
278  features.back().enabled = true;
279 #endif
280 
281  features.emplace_back(N_("feature^Legacy bidirectional rendering"));
282 #ifdef HAVE_FRIBIDI
283  features.back().enabled = true;
284 #endif
285 
286 #ifdef _X11
287 
288  features.emplace_back(N_("feature^D-Bus notifications back end"));
289 #ifdef HAVE_LIBDBUS
290  features.back().enabled = true;
291 #endif
292 
293 #endif /* _X11 */
294 
295 #ifdef _WIN32
296  // Always compiled in.
297  features.emplace_back(N_("feature^Win32 notifications back end"));
298  features.back().enabled = true;
299 #endif
300 
301 #ifdef __APPLE__
302  // Always compiled in.
303  features.emplace_back(N_("feature^Cocoa notifications back end"));
304  features.back().enabled = true;
305 #endif /* __APPLE__ */
306 }
307 
308 const std::string empty_version = "";
309 
310 } // end anonymous namespace 1
311 
312 std::vector<optional_feature> optional_features_table()
313 {
314  std::vector<optional_feature> res = versions.features;
315 
316  for(std::size_t k = 0; k < res.size(); ++k) {
317  res[k].name = _(res[k].name.c_str());
318  }
319  return res;
320 }
321 
322 const std::string& library_build_version(LIBRARY_ID lib)
323 {
324  if(lib >= LIB_COUNT) {
325  return empty_version;
326  }
327 
328  return versions.compiled[lib];
329 }
330 
331 const std::string& library_runtime_version(LIBRARY_ID lib)
332 {
333  if(lib >= LIB_COUNT) {
334  return empty_version;
335  }
336 
337  return versions.linked[lib];
338 }
339 
340 const std::string& library_name(LIBRARY_ID lib)
341 {
342  if(lib >= LIB_COUNT) {
343  return empty_version;
344  }
345 
346  return versions.names[lib];
347 }
348 
349 namespace {
350 
351 bool strlen_comparator(const std::string& a, const std::string& b)
352 {
353  return a.length() < b.length();
354 }
355 
356 std::size_t max_strlen(const std::vector<std::string>& strs)
357 {
358  const std::vector<std::string>::const_iterator it =
359  std::max_element(strs.begin(), strs.end(), strlen_comparator);
360 
361  return it != strs.end() ? it->length() : 0;
362 }
363 
364 std::string report_heading(const std::string& heading_text)
365 {
366  return heading_text + '\n' + std::string(utf8::size(heading_text), '=') + '\n';
367 }
368 
369 } // end anonymous namespace 2
370 
372 {
373  std::ostringstream o;
374 
375  const std::size_t col2_start = max_strlen(versions.names) + 2;
376  const std::size_t col3_start = max_strlen(versions.compiled) + 1;
377 
378  for(unsigned n = 0; n < LIB_COUNT; ++n)
379  {
380  const std::string& name = versions.names[n];
381  const std::string& compiled = versions.compiled[n];
382  const std::string& linked = versions.linked[n];
383 
384  if(name.empty()) {
385  continue;
386  }
387 
388  o << name << ": ";
389 
390  const std::size_t pos2 = name.length() + 2;
391  if(pos2 < col2_start) {
392  o << std::string(col2_start - pos2, ' ');
393  }
394 
395  o << compiled;
396 
397  if(!linked.empty()) {
398  const std::size_t pos3 = compiled.length() + 1;
399  if(pos3 < col3_start) {
400  o << std::string(col3_start - pos3, ' ');
401  }
402  o << " (runtime " << linked << ")";
403  }
404 
405  o << '\n';
406  }
407 
408  return o.str();
409 }
410 
412 {
413  // Yes, it's for stdout/stderr but we still want the localized version so
414  // that the context prefixes are stripped.
415  const std::vector<optional_feature>& features = optional_features_table();
416 
417  std::size_t col2_start = 0;
418 
419  for(std::size_t k = 0; k < features.size(); ++k)
420  {
421  col2_start = std::max(col2_start, features[k].name.length() + 2);
422  }
423 
424  std::ostringstream o;
425 
426  for(std::size_t k = 0; k < features.size(); ++k)
427  {
428  const optional_feature& f = features[k];
429 
430  o << f.name << ": ";
431 
432  const std::size_t pos2 = f.name.length() + 2;
433  if(pos2 < col2_start) {
434  o << std::string(col2_start - pos2, ' ');
435  }
436 
437  o << (f.enabled ? "yes" : "no") << '\n';
438  }
439 
440  return o.str();
441 }
442 
443 std::string full_build_report()
444 {
445  std::ostringstream o;
446 
447  o << "The Battle for Wesnoth version " << game_config::revision << '\n'
448  << "Running on " << desktop::os_version() << '\n'
449  << '\n'
450  << report_heading("Game paths")
451  << '\n'
452  << "Data dir: " << filesystem::sanitize_path(game_config::path) << '\n'
453  << "User config dir: " << filesystem::sanitize_path(filesystem::get_user_config_dir()) << '\n'
454  << "User data dir: " << filesystem::sanitize_path(filesystem::get_user_data_dir()) << '\n'
455  << "Saves dir: " << filesystem::sanitize_path(filesystem::get_saves_dir()) << '\n'
456  << "Add-ons dir: " << filesystem::sanitize_path(filesystem::get_addons_dir()) << '\n'
457  << "Cache dir: " << filesystem::sanitize_path(filesystem::get_cache_dir()) << '\n'
458  << '\n'
459  << report_heading("Libraries")
460  << '\n'
462  << '\n'
463  << report_heading("Features")
464  << '\n'
466 
467  return o.str();
468 }
469 
470 } // end namespace game_config
std::string library_versions_report()
Produce a plain-text report of library versions suitable for stdout/stderr.
Definition: build_info.cpp:371
Interfaces for manipulating version numbers of engine, add-ons, etc.
std::string optional_features_report()
Produce a plain-text report of features suitable for stdout/stderr.
Definition: build_info.cpp:411
#define a
STL namespace.
const std::string & library_build_version(LIBRARY_ID lib)
Retrieve the build-time version number of the given library.
Definition: build_info.cpp:322
std::string get_saves_dir()
#define b
static const char * name(const std::vector< SDL_Joystick *> &joysticks, const std::size_t index)
Definition: joystick.cpp:48
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
const std::string & library_name(LIBRARY_ID lib)
Retrieve the user-visible name for the given library.
Definition: build_info.cpp:340
std::vector< optional_feature > optional_features_table()
Return a localized features table.
Definition: build_info.cpp:312
std::vector< optional_feature > features
Definition: build_info.cpp:57
std::ostringstream wrapper.
Definition: formatter.hpp:38
std::string get_user_data_dir()
Definition: filesystem.cpp:737
std::string path
Definition: game_config.cpp:39
std::string sanitize_path(const std::string &path)
Sanitizes a path to remove references to the user&#39;s name.
Platform identification and version information functions.
std::string get_cache_dir()
Definition: filesystem.cpp:742
std::vector< std::string > linked
Definition: build_info.cpp:56
const std::string revision
mock_party p
Game configuration data as global variables.
Definition: build_info.cpp:49
std::string os_version()
Returns a string with the running OS name and version information.
Definition: version.cpp:121
std::vector< std::string > names
Definition: build_info.cpp:56
const std::string & library_runtime_version(LIBRARY_ID lib)
Retrieve the runtime version number of the given library.
Definition: build_info.cpp:331
Declarations for File-IO.
#define N_(String)
Definition: gettext.hpp:97
std::string get_user_config_dir()
Definition: filesystem.cpp:708
#define f
std::string get_addons_dir()
mock_char c
static map_location::DIRECTION n
std::vector< std::string > compiled
Definition: build_info.cpp:56
std::string full_build_report()
Produce a bug report-style info dump.
Definition: build_info.cpp:443