1010#include < boost/uuid/uuid.hpp>
1111#include < boost/uuid/uuid_io.hpp>
1212#include < rime/common.h>
13+ #include < rime/resource.h>
1314#include < rime/schema.h>
15+ #include < rime/service.h>
1416#include < rime/setup.h>
1517#include < rime/ticket.h>
1618#include < rime/algo/utilities.h>
1719#include < rime/dict/dictionary.h>
1820#include < rime/dict/dict_compiler.h>
19- #include < rime/lever/customizer.h>
2021#include < rime/lever/deployment_tasks.h>
2122#include < rime/lever/user_dict_manager.h>
2223#ifdef _WIN32
@@ -115,22 +116,18 @@ bool WorkspaceUpdate::Run(Deployer* deployer) {
115116 the<DeploymentTask> t;
116117 t.reset (new ConfigFileUpdate (" default.yaml" , " config_version" ));
117118 t->Run (deployer);
118- // since brise 0.18
119119 t.reset (new ConfigFileUpdate (" symbols.yaml" , " config_version" ));
120120 t->Run (deployer);
121121 t.reset (new SymlinkingPrebuiltDictionaries);
122122 t->Run (deployer);
123123 }
124124
125- fs::path user_data_path (deployer->user_data_dir );
126- fs::path default_config_path (user_data_path / " default.yaml" );
127- Config config;
128- if (!config.LoadFromFile (default_config_path.string ())) {
129- LOG (ERROR) << " Error loading default config from '"
130- << default_config_path.string () << " '." ;
125+ the<Config> config (Config::Require (" config" )->Create (" default" ));
126+ if (!config) {
127+ LOG (ERROR) << " Error loading default config." ;
131128 return false ;
132129 }
133- auto schema_list = config. GetList (" schema_list" );
130+ auto schema_list = config-> GetList (" schema_list" );
134131 if (!schema_list) {
135132 LOG (WARNING) << " schema list not defined." ;
136133 return false ;
@@ -140,90 +137,60 @@ bool WorkspaceUpdate::Run(Deployer* deployer) {
140137 int success = 0 ;
141138 int failure = 0 ;
142139 map<string, string> schemas;
143- for (auto it = schema_list->begin (); it != schema_list->end (); ++it) {
144- auto item = As<ConfigMap>(*it);
145- if (!item)
146- continue ;
147- auto schema_property = item->GetValue (" schema" );
148- if (!schema_property)
149- continue ;
150- const string& schema_id (schema_property->str ());
140+ the<ResourceResolver> resolver (
141+ Service::instance ().CreateResourceResolver ({
142+ " schema" , " " , " .schema.yaml"
143+ }));
144+ auto build_schema = [&](const string& schema_id) {
145+ if (schemas.find (schema_id) != schemas.end ()) // already built
146+ return ;
151147 LOG (INFO) << " schema: " << schema_id;
152148 string schema_path;
153149 if (schemas.find (schema_id) == schemas.end ()) {
154- schema_path = GetSchemaPath (deployer, schema_id, true );
150+ schema_path = resolver-> ResolvePath ( schema_id). string ( );
155151 schemas[schema_id] = schema_path;
156152 }
157153 else {
158154 schema_path = schemas[schema_id];
159155 }
160156 if (schema_path.empty ()) {
161157 LOG (WARNING) << " missing schema file for '" << schema_id << " '." ;
162- continue ;
158+ return ;
163159 }
164- // build schema
165160 the<DeploymentTask> t (new SchemaUpdate (schema_path));
166161 if (t->Run (deployer))
167162 ++success;
168163 else
169164 ++failure;
170- }
171- // find dependencies
172- for (auto s = schemas.cbegin (); s != schemas.cend (); ++s) {
173- Config schema_config;
174- // user could have customized dependencies in the resulting schema
175- string user_schema_path = GetSchemaPath (deployer, s->first , false );
176- if (!schema_config.LoadFromFile (user_schema_path))
165+ };
166+ auto schema_component = Config::Require (" schema" );
167+ for (auto it = schema_list->begin (); it != schema_list->end (); ++it) {
168+ auto item = As<ConfigMap>(*it);
169+ if (!item)
177170 continue ;
178- auto dependencies = schema_config. GetList (" schema/dependencies " );
179- if (!dependencies )
171+ auto schema_property = item-> GetValue (" schema" );
172+ if (!schema_property )
180173 continue ;
181- for (auto d = dependencies->begin (); d != dependencies->end (); ++d) {
182- auto dependency = As<ConfigValue>(*d);
183- if (!dependency)
184- continue ;
185- string dependency_id (dependency->str ());
186- if (schemas.find (dependency_id) != schemas.end ()) // already built
187- continue ;
188- LOG (INFO) << " new dependency: " << dependency_id;
189- string dependency_path = GetSchemaPath (deployer, dependency_id, true );
190- schemas[dependency_id] = dependency_path;
191- if (dependency_path.empty ()) {
192- LOG (WARNING) << " missing schema file for dependency '" << dependency_id << " '." ;
193- continue ;
174+ const string& schema_id = schema_property->str ();
175+ build_schema (schema_id);
176+ the<Config> schema_config (schema_component->Create (schema_id));
177+ if (!schema_config)
178+ continue ;
179+ if (auto dependencies = schema_config->GetList (" schema/dependencies" )) {
180+ for (auto d = dependencies->begin (); d != dependencies->end (); ++d) {
181+ auto dependency = As<ConfigValue>(*d);
182+ if (!dependency)
183+ continue ;
184+ const string& dependency_id = dependency->str ();
185+ build_schema (dependency_id);
194186 }
195- // build dependency
196- the<DeploymentTask> t (new SchemaUpdate (dependency_path));
197- if (t->Run (deployer))
198- ++success;
199- else
200- ++failure;
201187 }
202188 }
203189 LOG (INFO) << " finished updating schemas: "
204190 << success << " success, " << failure << " failure." ;
205191 return failure == 0 ;
206192}
207193
208- string WorkspaceUpdate::GetSchemaPath (Deployer* deployer,
209- const string& schema_id,
210- bool prefer_shared_copy) {
211- fs::path schema_path;
212- if (prefer_shared_copy) {
213- fs::path shared_data_path (deployer->shared_data_dir );
214- schema_path = shared_data_path / (schema_id + " .schema.yaml" );
215- if (!fs::exists (schema_path))
216- schema_path.clear ();
217- }
218- if (schema_path.empty ()) {
219- fs::path user_data_path (deployer->user_data_dir );
220- schema_path = user_data_path / (schema_id + " .schema.yaml" );
221- if (!fs::exists (schema_path))
222- schema_path.clear ();
223- }
224- return schema_path.string ();
225- }
226-
227194SchemaUpdate::SchemaUpdate (TaskInitializer arg) : verbose_(false ) {
228195 try {
229196 schema_file_ = boost::any_cast<string>(arg);
@@ -233,6 +200,43 @@ SchemaUpdate::SchemaUpdate(TaskInitializer arg) : verbose_(false) {
233200 }
234201}
235202
203+ static bool IsCustomizedCopy (const string& file_name);
204+
205+ static bool TrashCustomizedCopy (const fs::path& shared_copy,
206+ const fs::path& user_copy,
207+ const string& version_key,
208+ const fs::path& trash) {
209+ if (fs::equivalent (shared_copy, user_copy))
210+ return false ;
211+ if (IsCustomizedCopy (user_copy.string ())) {
212+ string shared_copy_version;
213+ string user_copy_version;
214+ Config shared_config;
215+ if (shared_config.LoadFromFile (shared_copy.string ())) {
216+ shared_config.GetString (version_key, &shared_copy_version);
217+ }
218+ Config user_config;
219+ if (user_config.LoadFromFile (user_copy.string ()) &&
220+ user_config.GetString (version_key, &user_copy_version)) {
221+ size_t custom_version_suffix = user_copy_version.find (" .custom." );
222+ if (custom_version_suffix != string::npos) {
223+ user_copy_version.erase (custom_version_suffix);
224+ }
225+ }
226+ if (CompareVersionString (shared_copy_version, user_copy_version) >= 0 ) {
227+ fs::path backup = trash / user_copy.filename ();
228+ boost::system::error_code ec;
229+ fs::rename (user_copy, backup, ec);
230+ if (ec) {
231+ LOG (ERROR) << " error trashing file " << user_copy.string ();
232+ return false ;
233+ }
234+ return true ;
235+ }
236+ }
237+ return false ;
238+ }
239+
236240bool SchemaUpdate::Run (Deployer* deployer) {
237241 fs::path source_path (schema_file_);
238242 if (!fs::exists (source_path)) {
@@ -241,37 +245,33 @@ bool SchemaUpdate::Run(Deployer* deployer) {
241245 return false ;
242246 }
243247 string schema_id;
244- {
245- Config source;
246- if (!source.LoadFromFile (schema_file_) ||
247- !source.GetString (" schema/schema_id" , &schema_id) ||
248- schema_id.empty ()) {
249- LOG (ERROR) << " invalid schema definition in '" << schema_file_ << " '." ;
250- return false ;
251- }
248+ the<Config> config (new Config);
249+ if (!config->LoadFromFile (schema_file_) ||
250+ !config->GetString (" schema/schema_id" , &schema_id) ||
251+ schema_id.empty ()) {
252+ LOG (ERROR) << " invalid schema definition in '" << schema_file_ << " '." ;
253+ return false ;
252254 }
253255 fs::path shared_data_path (deployer->shared_data_dir );
254256 fs::path user_data_path (deployer->user_data_dir );
255257 fs::path destination_path (user_data_path / (schema_id + " .schema.yaml" ));
256- Customizer customizer (source_path, destination_path, " schema/version" );
257- if (customizer.TrashCustomizedCopy ()) {
258- LOG (INFO) << " patched copy of schema '" << schema_id << " ' is moved to trash" ;
258+ fs::path trash = user_data_path / " trash" ;
259+ if (TrashCustomizedCopy (source_path,
260+ destination_path,
261+ " schema/version" ,
262+ trash)) {
263+ LOG (INFO) << " patched copy of schema '" << schema_id
264+ << " ' is moved to trash" ;
259265 }
260266
261- Schema schema (schema_id, new Config);
262- Config* config = schema.config ();
263- if (!config || !config->LoadFromFile (destination_path.string ())) {
264- LOG (ERROR) << " Error loading schema file '"
265- << destination_path.string () << " '." ;
266- return false ;
267- }
268267 string dict_name;
269268 if (!config->GetString (" translator/dictionary" , &dict_name)) {
270269 // not requiring a dictionary
271270 return true ;
272271 }
273- DictionaryComponent component;
274- the<Dictionary> dict (component.Create ({&schema, " translator" }));
272+ Schema schema (schema_id, config.release ());
273+ the<Dictionary> dict (
274+ Dictionary::Require (" dictionary" )->Create ({&schema, " translator" }));
275275 if (!dict) {
276276 LOG (ERROR) << " Error creating dictionary '" << dict_name << " '." ;
277277 return false ;
@@ -281,7 +281,7 @@ bool SchemaUpdate::Run(Deployer* deployer) {
281281 if (verbose_) {
282282 dict_compiler.set_options (DictCompiler::kRebuild | DictCompiler::kDump );
283283 }
284- if (!dict_compiler.Compile (destination_path. string () )) {
284+ if (!dict_compiler.Compile (schema_file_ )) {
285285 LOG (ERROR) << " dictionary '" << dict_name << " ' failed to compile." ;
286286 return false ;
287287 }
@@ -305,13 +305,18 @@ bool ConfigFileUpdate::Run(Deployer* deployer) {
305305 fs::path user_data_path (deployer->user_data_dir );
306306 fs::path source_config_path (shared_data_path / file_name_);
307307 fs::path dest_config_path (user_data_path / file_name_);
308+ fs::path trash = user_data_path / " trash" ;
308309 if (!fs::exists (source_config_path)) {
309310 LOG (WARNING) << " '" << file_name_
310311 << " ' is missing from shared data directory." ;
311- source_config_path = dest_config_path;
312+ return false ;
313+ }
314+ if (TrashCustomizedCopy (source_config_path,
315+ dest_config_path,
316+ version_key_,
317+ trash)) {
318+ LOG (INFO) << " patched copy of '" << file_name_ << " ' is moved to trash." ;
312319 }
313- Customizer customizer (source_config_path, dest_config_path, version_key_);
314- customizer.TrashCustomizedCopy ();
315320 return true ;
316321}
317322
@@ -341,15 +346,16 @@ bool SymlinkingPrebuiltDictionaries::Run(Deployer* deployer) {
341346 fs::equivalent (shared_data_path, user_data_path))
342347 return false ;
343348 bool success = false ;
344- // test existing link
349+ // remove symlinks to shared data files created by previous version
345350 for (fs::directory_iterator test (user_data_path), end;
346351 test != end; ++test) {
347352 fs::path entry (test->path ());
348- if (fs::is_symlink (entry) && entry. extension (). string () == " .bin " ) {
353+ if (fs::is_symlink (entry)) {
349354 try {
350- if (!fs::exists (entry)) {
351- LOG (INFO) << " removing dangling symlink: "
352- << entry.filename ().string ();
355+ auto target_path = fs::canonical (entry);
356+ if (target_path.has_parent_path () &&
357+ fs::equivalent (shared_data_path, target_path.parent_path ())) {
358+ LOG (INFO) << " removing symlink: " << entry.filename ().string ();
353359 fs::remove (entry);
354360 }
355361 }
@@ -359,24 +365,6 @@ bool SymlinkingPrebuiltDictionaries::Run(Deployer* deployer) {
359365 }
360366 }
361367 }
362- // create link
363- for (fs::directory_iterator iter (shared_data_path), end;
364- iter != end; ++iter) {
365- fs::path entry (iter->path ());
366- fs::path link (user_data_path / entry.filename ());
367- try {
368- if (fs::is_regular_file (entry) &&
369- entry.extension ().string () == " .bin" &&
370- !fs::exists (link)) {
371- LOG (INFO) << " symlinking '" << entry.filename ().string () << " '." ;
372- fs::create_symlink (entry, link);
373- }
374- }
375- catch (const fs::filesystem_error& ex) {
376- LOG (ERROR) << ex.what ();
377- success = false ;
378- }
379- }
380368 return success;
381369}
382370
@@ -402,6 +390,19 @@ bool UserDictSync::Run(Deployer* deployer) {
402390 return mgr.SynchronizeAll ();
403391}
404392
393+ static bool IsCustomizedCopy (const string& file_name) {
394+ if (boost::ends_with (file_name, " .yaml" ) &&
395+ !boost::ends_with (file_name, " .custom.yaml" )) {
396+ Config config;
397+ string checksum;
398+ if (config.LoadFromFile (file_name) &&
399+ config.GetString (" customization" , &checksum)) {
400+ return true ;
401+ }
402+ }
403+ return false ;
404+ }
405+
405406bool BackupConfigFiles::Run (Deployer* deployer) {
406407 LOG (INFO) << " backing up config files." ;
407408 fs::path user_data_path (deployer->user_data_dir );
@@ -433,14 +434,9 @@ bool BackupConfigFiles::Run(Deployer* deployer) {
433434 ++latest; // already up-to-date
434435 continue ;
435436 }
436- if (is_yaml_file && !boost::ends_with (entry.string (), " .custom.yaml" )) {
437- Config config;
438- string checksum;
439- if (config.LoadFromFile (entry.string ()) &&
440- config.GetString (" customization" , &checksum)) {
441- ++skipped; // customized copy
442- continue ;
443- }
437+ if (is_yaml_file && IsCustomizedCopy (entry.string ())) {
438+ ++skipped; // customized copy
439+ continue ;
444440 }
445441 boost::system::error_code ec;
446442 fs::copy_file (entry, backup, fs::copy_option::overwrite_if_exists, ec);
0 commit comments