2018-03-28 11:36:36 +02:00
#include "PresetUpdater.hpp"
2018-04-12 20:04:48 +02:00
#include <algorithm>
2018-03-28 11:36:36 +02:00
#include <thread>
2018-04-24 18:06:42 +02:00
#include <unordered_map>
2018-04-23 11:16:47 +02:00
#include <ostream>
2019-06-03 11:31:32 +02:00
#include <utility>
2018-04-13 15:08:58 +02:00
#include <stdexcept>
#include <boost/format.hpp>
2018-03-28 11:36:36 +02:00
#include <boost/algorithm/string.hpp>
2018-04-11 13:12:08 +02:00
#include <boost/filesystem.hpp>
2018-03-28 11:36:36 +02:00
#include <boost/filesystem/fstream.hpp>
2018-12-20 17:44:24 +01:00
#include <boost/lexical_cast.hpp>
2018-04-17 11:54:59 +02:00
#include <boost/log/trivial.hpp>
2018-03-28 11:36:36 +02:00
#include <wx/app.h>
2018-04-12 20:04:48 +02:00
#include <wx/msgdlg.h>
2018-03-28 11:36:36 +02:00
2018-04-17 16:59:53 +02:00
#include "libslic3r/libslic3r.h"
2020-04-01 09:48:56 +02:00
#include "libslic3r/format.hpp"
2018-03-28 11:36:36 +02:00
#include "libslic3r/Utils.hpp"
#include "slic3r/GUI/GUI.hpp"
2018-11-26 14:41:58 +01:00
#include "slic3r/GUI/I18N.hpp"
2018-03-28 11:36:36 +02:00
#include "slic3r/GUI/PresetBundle.hpp"
2018-04-24 18:06:42 +02:00
#include "slic3r/GUI/UpdateDialogs.hpp"
#include "slic3r/GUI/ConfigWizard.hpp"
2018-11-26 14:41:58 +01:00
#include "slic3r/GUI/GUI_App.hpp"
2020-05-26 17:42:57 +02:00
#include "slic3r/GUI/Plater.hpp"
2020-04-01 10:42:10 +02:00
#include "slic3r/GUI/format.hpp"
2018-03-28 11:36:36 +02:00
#include "slic3r/Utils/Http.hpp"
2018-04-12 20:04:48 +02:00
#include "slic3r/Config/Version.hpp"
#include "slic3r/Config/Snapshot.hpp"
2018-03-28 11:36:36 +02:00
namespace fs = boost :: filesystem ;
2018-04-12 20:04:48 +02:00
using Slic3r :: GUI :: Config :: Index ;
using Slic3r :: GUI :: Config :: Version ;
using Slic3r :: GUI :: Config :: Snapshot ;
using Slic3r :: GUI :: Config :: SnapshotDB ;
2018-03-28 11:36:36 +02:00
2019-08-11 18:57:33 +02:00
// FIXME: Incompat bundle resolution doesn't deal with inherited user presets
2018-03-28 11:36:36 +02:00
namespace Slic3r {
enum {
SLIC3R_VERSION_BODY_MAX = 256 ,
};
2018-04-13 15:08:58 +02:00
static const char * INDEX_FILENAME = "index.idx" ;
static const char * TMP_EXTENSION = ".download" ;
2018-04-12 20:04:48 +02:00
2019-06-03 11:31:32 +02:00
void copy_file_fix ( const fs :: path & source , const fs :: path & target )
{
static const auto perms = fs :: owner_read | fs :: owner_write | fs :: group_read | fs :: others_read ; // aka 644
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( debug ) << format ( "PresetUpdater: Copying %1% -> %2%" , source , target );
2019-06-03 11:31:32 +02:00
// Make sure the file has correct permission both before and after we copy over it
if ( fs :: exists ( target )) {
fs :: permissions ( target , perms );
}
fs :: copy_file ( source , target , fs :: copy_option :: overwrite_if_exists );
fs :: permissions ( target , perms );
}
2018-04-12 20:04:48 +02:00
struct Update
{
fs :: path source ;
fs :: path target ;
2019-06-03 11:31:32 +02:00
2018-04-12 20:04:48 +02:00
Version version ;
2019-05-13 18:38:48 +02:00
std :: string vendor ;
std :: string changelog_url ;
2018-04-12 20:04:48 +02:00
2020-02-04 15:24:35 +01:00
bool forced_update ;
2019-12-10 15:01:24 +01:00
Update () {}
2020-02-04 15:24:35 +01:00
Update ( fs :: path && source , fs :: path && target , const Version & version , std :: string vendor , std :: string changelog_url , bool forced = false )
2019-05-13 18:38:48 +02:00
: source ( std :: move ( source ))
, target ( std :: move ( target ))
, version ( version )
, vendor ( std :: move ( vendor ))
, changelog_url ( std :: move ( changelog_url ))
2020-02-04 15:24:35 +01:00
, forced_update ( forced )
2018-04-12 20:04:48 +02:00
{}
2019-06-03 11:31:32 +02:00
void install () const
{
copy_file_fix ( source , target );
}
friend std :: ostream & operator << ( std :: ostream & os , const Update & self )
{
2018-04-24 18:06:42 +02:00
os << "Update(" << self . source . string () << " -> " << self . target . string () << ')' ;
2018-04-23 11:16:47 +02:00
return os ;
}
2018-04-12 20:04:48 +02:00
};
2018-04-24 18:06:42 +02:00
struct Incompat
{
fs :: path bundle ;
Version version ;
2019-05-13 18:38:48 +02:00
std :: string vendor ;
2018-04-24 18:06:42 +02:00
2019-05-13 18:38:48 +02:00
Incompat ( fs :: path && bundle , const Version & version , std :: string vendor )
: bundle ( std :: move ( bundle ))
, version ( version )
, vendor ( std :: move ( vendor ))
2018-04-24 18:06:42 +02:00
{}
2019-08-05 17:28:02 +02:00
void remove () {
// Remove the bundle file
fs :: remove ( bundle );
// Look for an installed index and remove it too if any
const fs :: path installed_idx = bundle . replace_extension ( "idx" );
if ( fs :: exists ( installed_idx )) {
fs :: remove ( installed_idx );
}
}
2018-04-24 18:06:42 +02:00
friend std :: ostream & operator << ( std :: ostream & os , const Incompat & self ) {
os << "Incompat(" << self . bundle . string () << ')' ;
return os ;
}
};
struct Updates
{
std :: vector < Incompat > incompats ;
std :: vector < Update > updates ;
};
2018-04-12 20:04:48 +02:00
2019-02-14 15:28:18 +01:00
2019-02-14 16:51:41 +01:00
wxDEFINE_EVENT ( EVT_SLIC3R_VERSION_ONLINE , wxCommandEvent );
2018-04-12 20:04:48 +02:00
2018-03-28 11:36:36 +02:00
struct PresetUpdater :: priv
{
2018-04-13 15:08:58 +02:00
std :: vector < Index > index_db ;
bool enabled_version_check ;
bool enabled_config_update ;
std :: string version_check_url ;
2018-03-28 11:36:36 +02:00
fs :: path cache_path ;
2018-04-12 20:04:48 +02:00
fs :: path rsrc_path ;
fs :: path vendor_path ;
2018-03-28 11:36:36 +02:00
bool cancel ;
std :: thread thread ;
2018-09-21 01:33:41 +02:00
priv ();
2018-03-28 11:36:36 +02:00
2018-04-13 15:08:58 +02:00
void set_download_prefs ( AppConfig * app_config );
bool get_file ( const std :: string & url , const fs :: path & target_path ) const ;
void prune_tmps () const ;
void sync_version () const ;
2019-06-17 16:39:22 +02:00
void sync_config ( const VendorMap vendors );
2018-04-12 20:04:48 +02:00
void check_install_indices () const ;
2019-12-05 11:04:18 +01:00
Updates get_config_updates ( const Semver & old_slic3r_version ) const ;
2018-04-18 11:40:43 +02:00
void perform_updates ( Updates && updates , bool snapshot = true ) const ;
2018-03-28 11:36:36 +02:00
};
2019-02-14 15:28:18 +01:00
PresetUpdater :: priv :: priv ()
2019-08-09 17:01:37 +02:00
: cache_path ( fs :: path ( Slic3r :: data_dir ()) / "cache" )
2019-02-14 15:28:18 +01:00
, rsrc_path ( fs :: path ( resources_dir ()) / "profiles" )
, vendor_path ( fs :: path ( Slic3r :: data_dir ()) / "vendor" )
, cancel ( false )
2018-03-28 11:36:36 +02:00
{
2018-09-21 01:33:41 +02:00
set_download_prefs ( GUI :: wxGetApp (). app_config );
2019-12-05 11:04:18 +01:00
// Install indicies from resources. Only installs those that are either missing or older than in resources.
2018-04-13 15:08:58 +02:00
check_install_indices ();
2019-12-05 11:04:18 +01:00
// Load indices from the cache directory.
2018-12-20 17:44:24 +01:00
index_db = Index :: load_db ();
2018-04-13 15:08:58 +02:00
}
2018-03-28 11:36:36 +02:00
2018-04-23 11:16:47 +02:00
// Pull relevant preferences from AppConfig
2018-04-13 15:08:58 +02:00
void PresetUpdater :: priv :: set_download_prefs ( AppConfig * app_config )
{
enabled_version_check = app_config -> get ( "version_check" ) == "1" ;
2018-04-25 17:43:01 +02:00
version_check_url = app_config -> version_check_url ();
2018-05-15 12:14:26 +02:00
enabled_config_update = app_config -> get ( "preset_update" ) == "1" && ! app_config -> legacy_datadir ();
2018-04-13 15:08:58 +02:00
}
2018-04-23 11:16:47 +02:00
// Downloads a file (http get operation). Cancels if the Updater is being destroyed.
2018-04-13 15:08:58 +02:00
bool PresetUpdater :: priv :: get_file ( const std :: string & url , const fs :: path & target_path ) const
{
bool res = false ;
fs :: path tmp_path = target_path ;
2020-04-01 09:48:56 +02:00
tmp_path += format ( ".%1%%2%" , get_current_pid (), TMP_EXTENSION );
2018-04-20 11:05:00 +02:00
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Get: `%1%` \n\t -> `%2%` \n\t via tmp path `%3%`" ,
url ,
target_path . string (),
tmp_path . string ());
2018-04-13 15:08:58 +02:00
Http :: get ( url )
. on_progress ([ this ]( Http :: Progress , bool & cancel ) {
if ( cancel ) { cancel = true ; }
})
2018-04-23 11:16:47 +02:00
. on_error ([ & ]( std :: string body , std :: string error , unsigned http_status ) {
( void ) body ;
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( error ) << format ( "Error getting: `%1%`: HTTP %2%, %3%" ,
url ,
http_status ,
error );
2018-04-23 11:16:47 +02:00
})
2018-06-06 10:52:19 +02:00
. on_complete ([ & ]( std :: string body , unsigned /* http_status */ ) {
2018-04-13 15:08:58 +02:00
fs :: fstream file ( tmp_path , std :: ios :: out | std :: ios :: binary | std :: ios :: trunc );
file . write ( body . c_str (), body . size ());
2018-04-16 16:52:11 +02:00
file . close ();
2018-04-13 15:08:58 +02:00
fs :: rename ( tmp_path , target_path );
res = true ;
})
. perform_sync ();
return res ;
}
2018-04-23 11:16:47 +02:00
// Remove leftover paritally downloaded files, if any.
2018-04-13 15:08:58 +02:00
void PresetUpdater :: priv :: prune_tmps () const
{
2019-02-03 15:30:37 +01:00
for ( auto & dir_entry : boost :: filesystem :: directory_iterator ( cache_path ))
if ( is_plain_file ( dir_entry ) && dir_entry . path (). extension () == TMP_EXTENSION ) {
BOOST_LOG_TRIVIAL ( debug ) << "Cache prune: " << dir_entry . path (). string ();
fs :: remove ( dir_entry . path ());
2018-04-13 15:08:58 +02:00
}
}
2018-04-23 11:16:47 +02:00
// Get Slic3rPE version available online, save in AppConfig.
2018-04-13 15:08:58 +02:00
void PresetUpdater :: priv :: sync_version () const
{
if ( ! enabled_version_check ) { return ; }
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Downloading %1% online version from: `%2%`" , SLIC3R_APP_NAME , version_check_url );
2018-04-23 11:16:47 +02:00
2018-04-13 15:08:58 +02:00
Http :: get ( version_check_url )
2018-03-28 11:36:36 +02:00
. size_limit ( SLIC3R_VERSION_BODY_MAX )
. on_progress ([ this ]( Http :: Progress , bool & cancel ) {
cancel = this -> cancel ;
})
2018-04-23 11:16:47 +02:00
. on_error ([ & ]( std :: string body , std :: string error , unsigned http_status ) {
( void ) body ;
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( error ) << format ( "Error getting: `%1%`: HTTP %2%, %3%" ,
version_check_url ,
http_status ,
error );
2018-04-23 11:16:47 +02:00
})
2018-06-06 10:52:19 +02:00
. on_complete ([ & ]( std :: string body , unsigned /* http_status */ ) {
2018-03-28 11:36:36 +02:00
boost :: trim ( body );
2019-09-16 17:51:31 +02:00
const auto nl_pos = body . find_first_of ( " \n\r " );
if ( nl_pos != std :: string :: npos ) {
body . resize ( nl_pos );
}
if ( ! Semver :: parse ( body )) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( warning ) << format ( "Received invalid contents from `%1%`: Not a correct semver: `%2%`" , SLIC3R_APP_NAME , body );
2019-09-16 17:51:31 +02:00
return ;
}
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Got %1% online version: `%2%`. Sending to GUI thread..." , SLIC3R_APP_NAME , body );
2019-02-14 15:28:18 +01:00
2019-02-14 16:51:41 +01:00
wxCommandEvent * evt = new wxCommandEvent ( EVT_SLIC3R_VERSION_ONLINE );
evt -> SetString ( GUI :: from_u8 ( body ));
GUI :: wxGetApp (). QueueEvent ( evt );
2018-03-28 11:36:36 +02:00
})
. perform_sync ();
2018-04-13 15:08:58 +02:00
}
2018-03-28 11:36:36 +02:00
2018-04-23 11:16:47 +02:00
// Download vendor indices. Also download new bundles if an index indicates there's a new one available.
// Both are saved in cache.
2019-06-17 16:39:22 +02:00
void PresetUpdater :: priv :: sync_config ( const VendorMap vendors )
2018-04-13 15:08:58 +02:00
{
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Syncing configuration cache" ;
2018-04-13 15:08:58 +02:00
if ( ! enabled_config_update ) { return ; }
2018-03-28 11:36:36 +02:00
// Donwload vendor preset bundles
2019-12-05 11:04:18 +01:00
// Over all indices from the cache directory:
2018-12-20 17:44:24 +01:00
for ( auto & index : index_db ) {
2018-03-28 11:36:36 +02:00
if ( cancel ) { return ; }
2019-06-17 16:39:22 +02:00
const auto vendor_it = vendors . find ( index . vendor ());
2018-04-23 11:16:47 +02:00
if ( vendor_it == vendors . end ()) {
BOOST_LOG_TRIVIAL ( warning ) << "No such vendor: " << index . vendor ();
continue ;
}
2018-03-28 11:36:36 +02:00
2019-06-17 16:39:22 +02:00
const VendorProfile & vendor = vendor_it -> second ;
2018-04-23 11:16:47 +02:00
if ( vendor . config_update_url . empty ()) {
BOOST_LOG_TRIVIAL ( info ) << "Vendor has no config_update_url: " << vendor . name ;
continue ;
}
2018-04-13 15:08:58 +02:00
// Download a fresh index
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Downloading index for vendor: " << vendor . name ;
2018-04-13 15:08:58 +02:00
const auto idx_url = vendor . config_update_url + "/" + INDEX_FILENAME ;
2018-12-20 17:44:24 +01:00
const std :: string idx_path = ( cache_path / ( vendor . id + ".idx" )). string ();
const std :: string idx_path_temp = idx_path + "-update" ;
2020-02-04 15:24:35 +01:00
//check if idx_url is leading to our site
2020-02-04 16:10:39 +01:00
if ( ! boost :: starts_with ( idx_url , "http://files.prusa3d.com/wp-content/uploads/repository/" ))
2020-02-04 15:24:35 +01:00
{
2020-02-04 16:10:39 +01:00
BOOST_LOG_TRIVIAL ( warning ) << "unsafe url path for vendor \" " << vendor . name << " \" rejected: " << idx_url ;
2020-02-04 15:24:35 +01:00
continue ;
}
2018-12-20 17:44:24 +01:00
if ( ! get_file ( idx_url , idx_path_temp )) { continue ; }
2018-04-13 15:08:58 +02:00
if ( cancel ) { return ; }
// Load the fresh index up
2018-12-20 17:44:24 +01:00
{
Index new_index ;
try {
new_index . load ( idx_path_temp );
2018-12-22 10:02:42 +01:00
} catch ( const std :: exception & /* err */ ) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( error ) << format ( "Could not load downloaded index %1% for vendor %2%: invalid index?" , idx_path_temp , vendor . name );
2018-12-20 17:44:24 +01:00
continue ;
}
if ( new_index . version () < index . version ()) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( warning ) << format ( "The downloaded index %1% for vendor %2% is older than the active one. Ignoring the downloaded index." , idx_path_temp , vendor . name );
2018-12-20 17:44:24 +01:00
continue ;
}
Slic3r :: rename_file ( idx_path_temp , idx_path );
index = std :: move ( new_index );
if ( cancel )
return ;
}
2018-04-13 15:08:58 +02:00
// See if a there's a new version to download
2018-12-20 17:44:24 +01:00
const auto recommended_it = index . recommended ();
if ( recommended_it == index . end ()) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( error ) << format ( "No recommended version for vendor: %1%, invalid index?" , vendor . name );
2018-04-23 11:16:47 +02:00
continue ;
}
2019-02-14 15:28:18 +01:00
2018-04-13 15:08:58 +02:00
const auto recommended = recommended_it -> config_version ;
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( debug ) << format ( "Got index for vendor: %1%: current version: %2%, recommended version: %3%" ,
vendor . name ,
vendor . config_version . to_string (),
recommended . to_string ());
2018-04-13 15:08:58 +02:00
if ( vendor . config_version >= recommended ) { continue ; }
// Download a fresh bundle
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Downloading new bundle for vendor: " << vendor . name ;
2020-04-01 09:48:56 +02:00
const auto bundle_url = format ( "%1%/%2%.ini" , vendor . config_update_url , recommended . to_string ());
2018-04-13 15:08:58 +02:00
const auto bundle_path = cache_path / ( vendor . id + ".ini" );
if ( ! get_file ( bundle_url , bundle_path )) { continue ; }
if ( cancel ) { return ; }
2018-03-28 11:36:36 +02:00
}
}
2018-04-23 11:16:47 +02:00
// Install indicies from resources. Only installs those that are either missing or older than in resources.
2018-04-12 20:04:48 +02:00
void PresetUpdater :: priv :: check_install_indices () const
{
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Checking if indices need to be installed from resources..." ;
2019-02-03 15:30:37 +01:00
for ( auto & dir_entry : boost :: filesystem :: directory_iterator ( rsrc_path ))
if ( is_idx_file ( dir_entry )) {
const auto & path = dir_entry . path ();
2018-04-12 20:04:48 +02:00
const auto path_in_cache = cache_path / path . filename ();
if ( ! fs :: exists ( path_in_cache )) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Install index from resources: " << path . filename ();
2019-06-03 11:31:32 +02:00
copy_file_fix ( path , path_in_cache );
2018-04-17 10:28:32 +02:00
} else {
Index idx_rsrc , idx_cache ;
idx_rsrc . load ( path );
idx_cache . load ( path_in_cache );
if ( idx_cache . version () < idx_rsrc . version ()) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Update index from resources: " << path . filename ();
2019-06-03 11:31:32 +02:00
copy_file_fix ( path , path_in_cache );
2018-04-17 10:28:32 +02:00
}
2018-04-12 20:04:48 +02:00
}
}
}
2019-12-05 11:04:18 +01:00
// Generates a list of bundle updates that are to be performed.
// Version of slic3r that was running the last time and which was read out from PrusaSlicer.ini is provided
// as a parameter.
Updates PresetUpdater :: priv :: get_config_updates ( const Semver & old_slic3r_version ) const
2018-04-12 20:04:48 +02:00
{
Updates updates ;
2018-04-23 13:58:03 +02:00
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( info ) << "Checking for cached configuration updates..." ;
2018-04-12 20:04:48 +02:00
2019-12-05 11:04:18 +01:00
// Over all indices from the cache directory:
2018-04-12 20:04:48 +02:00
for ( const auto idx : index_db ) {
2018-04-24 18:06:42 +02:00
auto bundle_path = vendor_path / ( idx . vendor () + ".ini" );
2019-06-03 11:31:32 +02:00
auto bundle_path_idx = vendor_path / idx . path (). filename ();
2018-04-12 20:04:48 +02:00
if ( ! fs :: exists ( bundle_path )) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Confing bundle not installed for vendor %1%, skipping: " , idx . vendor ());
2018-04-12 20:04:48 +02:00
continue ;
}
2019-12-05 11:04:18 +01:00
// Perform a basic load and check the version of the installed preset bundle.
2019-05-13 18:38:48 +02:00
auto vp = VendorProfile :: from_ini ( bundle_path , false );
2018-04-12 20:04:48 +02:00
2018-07-21 09:32:45 +02:00
// Getting a recommended version from the latest index, wich may have been downloaded
// from the internet, or installed / updated from the installation resources.
2019-12-10 15:01:24 +01:00
auto recommended = idx . recommended ();
2018-04-12 20:04:48 +02:00
if ( recommended == idx . end ()) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( error ) << format ( "No recommended version for vendor: %1%, invalid index? Giving up." , idx . vendor ());
2019-06-03 11:31:32 +02:00
// XXX: what should be done here?
continue ;
2018-04-12 20:04:48 +02:00
}
2019-02-22 11:36:58 +01:00
const auto ver_current = idx . find ( vp . config_version );
const bool ver_current_found = ver_current != idx . end ();
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( debug ) << format ( "Vendor: %1%, version installed: %2%%3%, version cached: %4%" ,
vp . name ,
vp . config_version . to_string (),
( ver_current_found ? "" : " (not found in index!)" ),
recommended -> config_version . to_string ());
2018-04-23 11:16:47 +02:00
2019-06-28 11:35:54 +02:00
if ( ! ver_current_found ) {
2019-12-10 15:01:24 +01:00
// Any published config shall be always found in the latest config index.
2020-04-01 09:48:56 +02:00
auto message = format ( "Preset bundle `%1%` version not found in index: %2%" , idx . vendor (), vp . config_version . to_string ());
2019-06-28 11:35:54 +02:00
BOOST_LOG_TRIVIAL ( error ) << message ;
2020-02-21 13:38:06 +01:00
GUI :: show_error ( nullptr , message );
2019-06-28 11:35:54 +02:00
continue ;
}
2020-02-04 15:24:35 +01:00
bool current_not_supported = false ; //if slcr is incompatible but situation is not downgrade, we do forced updated and this bool is information to do it
if ( ver_current_found && ! ver_current -> is_current_slic3r_supported ()){
if ( ver_current -> is_current_slic3r_downgrade ()) {
// "Reconfigure" situation.
BOOST_LOG_TRIVIAL ( warning ) << "Current Slic3r incompatible with installed bundle: " << bundle_path . string ();
updates . incompats . emplace_back ( std :: move ( bundle_path ), * ver_current , vp . name );
continue ;
}
current_not_supported = true ;
2019-12-10 15:01:24 +01:00
}
2018-04-12 20:04:48 +02:00
2019-12-10 15:01:24 +01:00
if ( recommended -> config_version < vp . config_version ) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( warning ) << format ( "Recommended config version for the currently running PrusaSlicer is older than the currently installed config for vendor %1%. This should not happen." , idx . vendor ());
2019-12-10 15:01:24 +01:00
continue ;
}
if ( recommended -> config_version == vp . config_version ) {
// The recommended config bundle is already installed.
continue ;
}
// Config bundle update situation. The recommended config bundle version for this PrusaSlicer version from the index from the cache is newer
// than the version of the currently installed config bundle.
// The config index inside the cache directory (given by idx.path()) is one of the following:
// 1) The last config index downloaded by any previously running PrusaSlicer instance
// 2) The last config index installed by any previously running PrusaSlicer instance (older or newer) from its resources.
// 3) The last config index installed by the currently running PrusaSlicer instance from its resources.
// The config index is always the newest one (given by its newest config bundle referenced), and older config indices shall fully contain
// the content of the older config indices.
// Config bundle inside the cache directory.
fs :: path path_in_cache = cache_path / ( idx . vendor () + ".ini" );
// Config bundle inside the resources directory.
fs :: path path_in_rsrc = rsrc_path / ( idx . vendor () + ".ini" );
// Config index inside the resources directory.
fs :: path path_idx_in_rsrc = rsrc_path / ( idx . vendor () + ".idx" );
// Search for a valid config bundle in the cache directory.
bool found = false ;
Update new_update ;
fs :: path bundle_path_idx_to_install ;
if ( fs :: exists ( path_in_cache )) {
try {
VendorProfile new_vp = VendorProfile :: from_ini ( path_in_cache , false );
if ( new_vp . config_version == recommended -> config_version ) {
// The config bundle from the cache directory matches the recommended version of the index from the cache directory.
// This is the newest known recommended config. Use it.
2020-02-04 15:24:35 +01:00
new_update = Update ( std :: move ( path_in_cache ), std :: move ( bundle_path ), * recommended , vp . name , vp . changelog_url , current_not_supported );
2019-12-10 15:01:24 +01:00
// and install the config index from the cache into vendor's directory.
bundle_path_idx_to_install = idx . path ();
found = true ;
}
} catch ( const std :: exception & ex ) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Failed to load the config bundle `%1%`: %2%" , path_in_cache . string (), ex . what ());
2019-12-10 15:01:24 +01:00
}
}
// Keep the rsrc_idx outside of the next block, as we will reference the "recommended" version by an iterator.
Index rsrc_idx ;
if ( ! found && fs :: exists ( path_in_rsrc ) && fs :: exists ( path_idx_in_rsrc )) {
// Trying the config bundle from resources (from the installation).
// In that case, the recommended version number has to be compared against the recommended version reported by the config index from resources as well,
// as the config index in the cache directory may already be newer, recommending a newer config bundle than available in cache or resources.
VendorProfile rsrc_vp ;
try {
rsrc_vp = VendorProfile :: from_ini ( path_in_rsrc , false );
} catch ( const std :: exception & ex ) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Cannot load the config bundle at `%1%`: %2%" , path_in_rsrc . string (), ex . what ());
2019-12-10 15:01:24 +01:00
}
if ( rsrc_vp . valid ()) {
try {
rsrc_idx . load ( path_idx_in_rsrc );
} catch ( const std :: exception & ex ) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Cannot load the config index at `%1%`: %2%" , path_idx_in_rsrc . string (), ex . what ());
2019-12-10 15:01:24 +01:00
}
recommended = rsrc_idx . recommended ();
if ( recommended != rsrc_idx . end () && recommended -> config_version == rsrc_vp . config_version && recommended -> config_version > vp . config_version ) {
2020-02-04 15:24:35 +01:00
new_update = Update ( std :: move ( path_in_rsrc ), std :: move ( bundle_path ), * recommended , vp . name , vp . changelog_url , current_not_supported );
2019-12-10 15:01:24 +01:00
bundle_path_idx_to_install = path_idx_in_rsrc ;
found = true ;
} else {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( warning ) << format ( "The recommended config version for vendor `%1%` in resources does not match the recommended \n "
" config version for this version of PrusaSlicer. Corrupted installation?" , idx . vendor ());
2019-12-10 15:01:24 +01:00
}
}
}
if ( found ) {
2019-08-05 17:28:02 +02:00
// Load 'installed' idx, if any.
// 'Installed' indices are kept alongside the bundle in the `vendor` subdir
// for bookkeeping to remember a cancelled update and not offer it again.
if ( fs :: exists ( bundle_path_idx )) {
Index existing_idx ;
try {
existing_idx . load ( bundle_path_idx );
2019-12-05 11:04:18 +01:00
// Find a recommended config bundle version for the slic3r version last executed. This makes sure that a config bundle update will not be missed
// when upgrading an application. On the other side, the user will be bugged every time he will switch between slic3r versions.
2020-03-03 14:52:16 +01:00
/*const auto existing_recommended = existing_idx.recommended(old_slic3r_version);
if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) {
2019-08-05 17:28:02 +02:00
// The user has already seen (and presumably rejected) this update
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL(info) << format("Downloaded index for `%1%` is the same as installed one, not offering an update.",idx.vendor());
2019-08-05 17:28:02 +02:00
continue;
2020-02-04 15:24:35 +01:00
}*/
2019-08-05 17:28:02 +02:00
} catch ( const std :: exception & err ) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( error ) << format ( "Cannot load the installed index at `%1%`: %2%" , bundle_path_idx , err . what ());
2019-08-05 17:28:02 +02:00
}
}
2018-04-23 13:58:03 +02:00
// Check if the update is already present in a snapshot
2020-02-05 11:38:45 +01:00
if ( ! current_not_supported )
{
const auto recommended_snap = SnapshotDB :: singleton (). snapshot_with_vendor_preset ( vp . name , recommended -> config_version );
if ( recommended_snap != SnapshotDB :: singleton (). end ()) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Bundle update %1% %2% already found in snapshot %3%, skipping..." ,
vp . name ,
recommended -> config_version . to_string (),
recommended_snap -> id );
2020-02-05 11:38:45 +01:00
continue ;
}
2018-04-23 13:58:03 +02:00
}
2019-12-10 15:01:24 +01:00
updates . updates . emplace_back ( std :: move ( new_update ));
// 'Install' the index in the vendor directory. This is used to memoize
// offered updates and to not offer the same update again if it was cancelled by the user.
copy_file_fix ( bundle_path_idx_to_install , bundle_path_idx );
} else {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( warning ) << format ( "Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources" ,
idx . vendor (),
recommended -> config_version . to_string ());
2018-04-12 20:04:48 +02:00
}
}
return updates ;
}
2018-04-18 11:40:43 +02:00
void PresetUpdater :: priv :: perform_updates ( Updates && updates , bool snapshot ) const
2018-04-16 16:52:11 +02:00
{
2018-04-24 18:06:42 +02:00
if ( updates . incompats . size () > 0 ) {
if ( snapshot ) {
BOOST_LOG_TRIVIAL ( info ) << "Taking a snapshot..." ;
2018-09-21 01:33:41 +02:00
SnapshotDB :: singleton (). take_snapshot ( * GUI :: wxGetApp (). app_config , Snapshot :: SNAPSHOT_DOWNGRADE );
2018-04-24 18:06:42 +02:00
}
2020-02-04 15:24:35 +01:00
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Deleting %1% incompatible bundles" , updates . incompats . size ());
2018-04-24 18:06:42 +02:00
2019-08-05 17:28:02 +02:00
for ( auto & incompat : updates . incompats ) {
2018-04-24 18:06:42 +02:00
BOOST_LOG_TRIVIAL ( info ) << '\t' << incompat ;
2019-08-05 17:28:02 +02:00
incompat . remove ();
2018-04-24 18:06:42 +02:00
}
2020-02-04 15:24:35 +01:00
2019-08-05 17:28:02 +02:00
} else if ( updates . updates . size () > 0 ) {
2020-02-04 15:24:35 +01:00
2018-04-24 18:06:42 +02:00
if ( snapshot ) {
BOOST_LOG_TRIVIAL ( info ) << "Taking a snapshot..." ;
2018-09-21 01:33:41 +02:00
SnapshotDB :: singleton (). take_snapshot ( * GUI :: wxGetApp (). app_config , Snapshot :: SNAPSHOT_UPGRADE );
2018-04-24 18:06:42 +02:00
}
2018-04-16 16:52:11 +02:00
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Performing %1% updates" , updates . updates . size ());
2018-04-23 11:16:47 +02:00
2018-04-24 18:06:42 +02:00
for ( const auto & update : updates . updates ) {
BOOST_LOG_TRIVIAL ( info ) << '\t' << update ;
2018-04-16 16:52:11 +02:00
2019-06-03 11:31:32 +02:00
update . install ();
2018-04-16 16:52:11 +02:00
2018-04-24 18:06:42 +02:00
PresetBundle bundle ;
2019-06-03 11:31:32 +02:00
bundle . load_configbundle ( update . source . string (), PresetBundle :: LOAD_CFGBNDLE_SYSTEM );
2018-04-16 16:52:11 +02:00
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Deleting %1% conflicting presets" , bundle . prints . size () + bundle . filaments . size () + bundle . printers . size ());
2018-05-18 14:58:24 +02:00
2018-04-24 18:06:42 +02:00
auto preset_remover = []( const Preset & preset ) {
2018-05-18 14:58:24 +02:00
BOOST_LOG_TRIVIAL ( info ) << '\t' << preset . file ;
2018-04-24 18:06:42 +02:00
fs :: remove ( preset . file );
};
for ( const auto & preset : bundle . prints ) { preset_remover ( preset ); }
for ( const auto & preset : bundle . filaments ) { preset_remover ( preset ); }
for ( const auto & preset : bundle . printers ) { preset_remover ( preset ); }
2018-05-16 19:41:35 +02:00
// Also apply the `obsolete_presets` property, removing obsolete ini files
2018-05-18 14:58:24 +02:00
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Deleting %1% obsolete presets" ,
bundle . obsolete_presets . prints . size () + bundle . obsolete_presets . filaments . size () + bundle . obsolete_presets . printers . size ());
2018-05-18 14:58:24 +02:00
2018-05-16 19:41:35 +02:00
auto obsolete_remover = []( const char * subdir , const std :: string & preset ) {
auto path = fs :: path ( Slic3r :: data_dir ()) / subdir / preset ;
path += ".ini" ;
2018-05-18 14:58:24 +02:00
BOOST_LOG_TRIVIAL ( info ) << '\t' << path . string ();
2018-05-16 19:41:35 +02:00
fs :: remove ( path );
};
for ( const auto & name : bundle . obsolete_presets . prints ) { obsolete_remover ( "print" , name ); }
for ( const auto & name : bundle . obsolete_presets . filaments ) { obsolete_remover ( "filament" , name ); }
2018-11-19 11:10:22 +01:00
for ( const auto & name : bundle . obsolete_presets . sla_prints ) { obsolete_remover ( "sla_print" , name ); }
for ( const auto & name : bundle . obsolete_presets . sla_materials /*filaments*/ ) { obsolete_remover ( "sla_material" , name ); }
2018-05-16 19:41:35 +02:00
for ( const auto & name : bundle . obsolete_presets . printers ) { obsolete_remover ( "printer" , name ); }
2018-04-24 18:06:42 +02:00
}
2018-04-16 16:52:11 +02:00
}
}
2018-09-21 01:33:41 +02:00
PresetUpdater :: PresetUpdater () :
p ( new priv ())
2018-04-13 15:08:58 +02:00
{}
2018-03-28 11:36:36 +02:00
// Public
PresetUpdater ::~ PresetUpdater ()
{
if ( p && p -> thread . joinable ()) {
2018-04-23 11:16:47 +02:00
// This will stop transfers being done by the thread, if any.
// Cancelling takes some time, but should complete soon enough.
2018-03-28 11:36:36 +02:00
p -> cancel = true ;
p -> thread . join ();
}
}
2018-04-17 16:59:53 +02:00
void PresetUpdater :: sync ( PresetBundle * preset_bundle )
2018-03-28 11:36:36 +02:00
{
2018-09-21 01:33:41 +02:00
p -> set_download_prefs ( GUI :: wxGetApp (). app_config );
2018-04-13 15:08:58 +02:00
if ( ! p -> enabled_version_check && ! p -> enabled_config_update ) { return ; }
2018-03-28 11:36:36 +02:00
// Copy the whole vendors data for use in the background thread
// Unfortunatelly as of C++11, it needs to be copied again
// into the closure (but perhaps the compiler can elide this).
2019-06-17 16:39:22 +02:00
VendorMap vendors = preset_bundle -> vendors ;
2018-03-28 11:36:36 +02:00
p -> thread = std :: move ( std :: thread ([ this , vendors ]() {
2018-04-13 15:08:58 +02:00
this -> p -> prune_tmps ();
this -> p -> sync_version ();
this -> p -> sync_config ( std :: move ( vendors ));
2018-03-28 11:36:36 +02:00
}));
}
2018-04-17 16:59:53 +02:00
void PresetUpdater :: slic3r_update_notify ()
{
2018-04-23 11:16:47 +02:00
if ( ! p -> enabled_version_check ) { return ; }
2018-09-21 01:33:41 +02:00
auto * app_config = GUI :: wxGetApp (). app_config ;
2018-04-20 14:53:11 +02:00
const auto ver_online_str = app_config -> get ( "version_online" );
const auto ver_online = Semver :: parse ( ver_online_str );
const auto ver_online_seen = Semver :: parse ( app_config -> get ( "version_online_seen" ));
2018-04-17 16:59:53 +02:00
2018-04-20 14:53:11 +02:00
if ( ver_online ) {
// Only display the notification if the version available online is newer AND if we haven't seen it before
2019-08-09 17:01:37 +02:00
if ( * ver_online > Slic3r :: SEMVER && ( ! ver_online_seen || * ver_online_seen < * ver_online )) {
GUI :: MsgUpdateSlic3r notification ( Slic3r :: SEMVER , * ver_online );
2018-04-20 14:53:11 +02:00
notification . ShowModal ();
if ( notification . disable_version_check ()) {
app_config -> set ( "version_check" , "0" );
p -> enabled_version_check = false ;
}
2018-04-17 16:59:53 +02:00
}
2019-05-13 18:38:48 +02:00
2018-04-20 14:53:11 +02:00
app_config -> set ( "version_online_seen" , ver_online_str );
2018-04-17 16:59:53 +02:00
}
}
2019-12-05 11:04:18 +01:00
PresetUpdater :: UpdateResult PresetUpdater :: config_update ( const Semver & old_slic3r_version ) const
2018-04-11 13:12:08 +02:00
{
2019-05-17 17:35:15 +02:00
if ( ! p -> enabled_config_update ) { return R_NOOP ; }
2018-04-13 15:08:58 +02:00
2019-12-05 11:04:18 +01:00
auto updates = p -> get_config_updates ( old_slic3r_version );
2018-04-24 18:06:42 +02:00
if ( updates . incompats . size () > 0 ) {
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "%1% bundles incompatible. Asking for action..." , updates . incompats . size ());
2018-04-23 11:16:47 +02:00
2018-04-24 18:40:06 +02:00
std :: unordered_map < std :: string , wxString > incompats_map ;
2018-04-24 18:06:42 +02:00
for ( const auto & incompat : updates . incompats ) {
2018-07-23 12:34:07 +02:00
const auto min_slic3r = incompat . version . min_slic3r_version ;
const auto max_slic3r = incompat . version . max_slic3r_version ;
wxString restrictions ;
if ( min_slic3r != Semver :: zero () && max_slic3r != Semver :: inf ()) {
2020-04-01 09:48:56 +02:00
restrictions = GUI :: format_wxstr ( _L ( "requires min. %s and max. %s" ),
min_slic3r . to_string (),
max_slic3r . to_string ());
2018-07-23 12:34:07 +02:00
} else if ( min_slic3r != Semver :: zero ()) {
2020-04-01 09:48:56 +02:00
restrictions = GUI :: format_wxstr ( _L ( "requires min. %s" ), min_slic3r . to_string ());
2020-02-04 15:24:35 +01:00
BOOST_LOG_TRIVIAL ( debug ) << "Bundle is not downgrade, user will now have to do whole wizard. This should not happen." ;
2018-07-23 12:34:07 +02:00
} else {
2020-04-01 09:48:56 +02:00
restrictions = GUI :: format_wxstr ( _L ( "requires max. %s" ), max_slic3r . to_string ());
2018-07-23 12:34:07 +02:00
}
2019-05-13 18:38:48 +02:00
incompats_map . emplace ( std :: make_pair ( incompat . vendor , std :: move ( restrictions )));
2018-04-12 20:04:48 +02:00
}
2018-04-24 18:06:42 +02:00
GUI :: MsgDataIncompatible dlg ( std :: move ( incompats_map ));
const auto res = dlg . ShowModal ();
if ( res == wxID_REPLACE ) {
BOOST_LOG_TRIVIAL ( info ) << "User wants to re-configure..." ;
2019-08-11 18:57:33 +02:00
// This effectively removes the incompatible bundles:
// (snapshot is taken beforehand)
2018-04-24 18:06:42 +02:00
p -> perform_updates ( std :: move ( updates ));
2019-08-11 18:57:33 +02:00
2020-02-04 15:24:35 +01:00
if ( ! GUI :: wxGetApp (). run_wizard ( GUI :: ConfigWizard :: RR_DATA_INCOMPAT )) {
2019-05-17 17:35:15 +02:00
return R_INCOMPAT_EXIT ;
2018-04-24 18:06:42 +02:00
}
2019-08-11 18:57:33 +02:00
2019-05-17 17:35:15 +02:00
return R_INCOMPAT_CONFIGURED ;
2020-02-04 15:24:35 +01:00
}
else {
2018-04-24 18:06:42 +02:00
BOOST_LOG_TRIVIAL ( info ) << "User wants to exit Slic3r, bye..." ;
2019-05-17 17:35:15 +02:00
return R_INCOMPAT_EXIT ;
2018-04-24 18:06:42 +02:00
}
2020-02-04 15:24:35 +01:00
2019-05-17 17:35:15 +02:00
} else if ( updates . updates . size () > 0 ) {
2020-02-04 15:24:35 +01:00
bool incompatible_version = false ;
for ( const auto & update : updates . updates ) {
incompatible_version = ( update . forced_update ? true : incompatible_version );
//td::cout << update.forced_update << std::endl;
2020-04-01 09:48:56 +02:00
//BOOST_LOG_TRIVIAL(info) << format("Update requires higher version.");
2020-02-04 15:24:35 +01:00
}
//forced update
if ( incompatible_version )
{
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Update of %1% bundles available. At least one requires higher version of Slicer." , updates . updates . size ());
2020-02-04 15:24:35 +01:00
std :: vector < GUI :: MsgUpdateForced :: Update > updates_msg ;
for ( const auto & update : updates . updates ) {
std :: string changelog_url = update . version . config_version . prerelease () == nullptr ? update . changelog_url : std :: string ();
updates_msg . emplace_back ( update . vendor , update . version . config_version , update . version . comment , std :: move ( changelog_url ));
}
GUI :: MsgUpdateForced dlg ( updates_msg );
const auto res = dlg . ShowModal ();
if ( res == wxID_OK ) {
BOOST_LOG_TRIVIAL ( info ) << "User wants to update..." ;
p -> perform_updates ( std :: move ( updates ));
// Reload global configuration
auto * app_config = GUI :: wxGetApp (). app_config ;
GUI :: wxGetApp (). preset_bundle -> load_presets ( * app_config );
GUI :: wxGetApp (). load_current_presets ();
GUI :: wxGetApp (). plater () -> set_bed_shape ();
return R_UPDATE_INSTALLED ;
}
else {
BOOST_LOG_TRIVIAL ( info ) << "User wants to exit Slic3r, bye..." ;
return R_INCOMPAT_EXIT ;
}
}
// regular update
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Update of %1% bundles available. Asking for confirmation ..." , updates . updates . size ());
2018-04-24 18:06:42 +02:00
2019-05-13 18:38:48 +02:00
std :: vector < GUI :: MsgUpdateConfig :: Update > updates_msg ;
2018-04-24 18:06:42 +02:00
for ( const auto & update : updates . updates ) {
2019-05-13 18:38:48 +02:00
std :: string changelog_url = update . version . config_version . prerelease () == nullptr ? update . changelog_url : std :: string ();
updates_msg . emplace_back ( update . vendor , update . version . config_version , update . version . comment , std :: move ( changelog_url ));
2018-04-24 18:06:42 +02:00
}
2019-05-13 18:38:48 +02:00
GUI :: MsgUpdateConfig dlg ( updates_msg );
2018-04-24 18:06:42 +02:00
2018-04-12 20:04:48 +02:00
const auto res = dlg . ShowModal ();
2018-04-25 15:20:46 +02:00
if ( res == wxID_OK ) {
2018-04-23 11:16:47 +02:00
BOOST_LOG_TRIVIAL ( debug ) << "User agreed to perform the update" ;
2018-04-17 16:59:53 +02:00
p -> perform_updates ( std :: move ( updates ));
2018-06-06 10:52:19 +02:00
// Reload global configuration
2018-09-21 01:33:41 +02:00
auto * app_config = GUI :: wxGetApp (). app_config ;
2019-06-03 11:31:32 +02:00
GUI :: wxGetApp (). preset_bundle -> load_presets ( * app_config );
2018-10-01 15:09:31 +02:00
GUI :: wxGetApp (). load_current_presets ();
2019-05-17 17:35:15 +02:00
return R_UPDATE_INSTALLED ;
2018-04-23 11:16:47 +02:00
} else {
BOOST_LOG_TRIVIAL ( info ) << "User refused the update" ;
2019-05-17 17:35:15 +02:00
return R_UPDATE_REJECT ;
2018-04-11 13:12:08 +02:00
}
2018-04-23 11:16:47 +02:00
} else {
BOOST_LOG_TRIVIAL ( info ) << "No configuration updates available." ;
2018-04-11 13:12:08 +02:00
}
2018-04-24 18:06:42 +02:00
2019-05-17 17:35:15 +02:00
return R_NOOP ;
2018-04-11 13:12:08 +02:00
}
2018-04-24 18:06:42 +02:00
void PresetUpdater :: install_bundles_rsrc ( std :: vector < std :: string > bundles , bool snapshot ) const
2018-04-16 16:52:11 +02:00
{
Updates updates ;
2020-04-01 09:48:56 +02:00
BOOST_LOG_TRIVIAL ( info ) << format ( "Installing %1% bundles from resources ..." , bundles . size ());
2018-04-23 11:16:47 +02:00
2018-04-16 16:52:11 +02:00
for ( const auto & bundle : bundles ) {
2019-06-17 16:39:22 +02:00
auto path_in_rsrc = ( p -> rsrc_path / bundle ). replace_extension ( ".ini" );
auto path_in_vendors = ( p -> vendor_path / bundle ). replace_extension ( ".ini" );
2019-05-13 18:38:48 +02:00
updates . updates . emplace_back ( std :: move ( path_in_rsrc ), std :: move ( path_in_vendors ), Version (), "" , "" );
2018-04-16 16:52:11 +02:00
}
2018-04-18 11:40:43 +02:00
p -> perform_updates ( std :: move ( updates ), snapshot );
2018-04-16 16:52:11 +02:00
}
2018-03-28 11:36:36 +02:00
}