From de6a037320949f7d21e345762f1d8b29fc148bda Mon Sep 17 00:00:00 2001 From: Slava Kim Date: Fri, 3 Oct 2014 16:13:12 -0700 Subject: [PATCH 1/2] Move HCP-cache clean-up to autoupdate code Clean up on every soft restart (every time the js app loads) not on every hard restart (every time the native shell loads). This fixes the problem described in issue #2727: HCP cache keeps accumulating in development mode leading to crashes. --- packages/autoupdate/autoupdate_cordova.js | 88 +++++++++++++++++++++-- tools/client/meteor_cordova_loader.js | 76 +++----------------- 2 files changed, 90 insertions(+), 74 deletions(-) diff --git a/packages/autoupdate/autoupdate_cordova.js b/packages/autoupdate/autoupdate_cordova.js index 2290c6d802..3cab024b6c 100644 --- a/packages/autoupdate/autoupdate_cordova.js +++ b/packages/autoupdate/autoupdate_cordova.js @@ -73,17 +73,21 @@ var restartServer = function (location) { }; var hasCalledReload = false; +var updating = false; +var localPathPrefix = null; + var onNewVersion = function () { var ft = new FileTransfer(); var urlPrefix = Meteor.absoluteUrl() + '__cordova'; - - var localPathPrefix = cordova.file.dataDirectory + 'meteor/'; HTTP.get(urlPrefix + '/manifest.json', function (err, res) { if (err || ! res.data) { log('Failed to download the manifest ' + (err && err.message) + ' ' + (res && res.content)); return; } + updating = true; + ensureLocalPathPrefix(); + var program = res.data; var manifest = _.clone(program.manifest); var version = program.version; @@ -113,8 +117,7 @@ var onNewVersion = function () { // success! downloaded all sources and saved the manifest // save the version string for atomicity - writeFile(localPathPrefix, 'version', version, - function (err) { + writeFile(localPathPrefix, 'version', version, function (err) { if (err) { log("Failed to write version: " + err); return; @@ -122,9 +125,7 @@ var onNewVersion = function () { // don't call reload twice! if (! hasCalledReload) { - // relative to 'bundle.app/www' - var location = - decodeURI(localPathPrefix + version).replace(/^file:\/\//, ''); + var location = uriToPath(localPathPrefix + version); restartServer(location); } }); @@ -214,5 +215,78 @@ Autoupdate._retrySubscription = function () { } }; +Meteor.startup(function () { + clearAutoupdateCache(autoupdateVersionCordova); +}); Meteor.startup(Autoupdate._retrySubscription); + +// A helper that removes old directories left from previous autoupdates +var clearAutoupdateCache = function (currentVersion) { + ensureLocalPathPrefix(); + // Try to clean up our cache directory, make sure to scan the directory + // *before* loading the actual app. This ordering will prevent race + // conditions when the app code tries to download a new version before + // the old-cache removal has scanned the cache folder. + listDirectory(localPathPrefix, {dirsOnly: true}, function (err, names) { + // Couldn't get the list of dirs or risking to get into a race with an + // on-going update to disk. + if (err || updating) { + return; + } + + _.each(names, function (name) { + // Skip the folder with the latest version + if (name === currentVersion) + return; + + // remove everything else, as we don't want to keep too much cache + // around on disk + removeDirectory(localPathPrefix + name + '/', function (err) { + if (err) { + log('Failed to remove an old cache folder ' + + name + ':' + err.message); + } else { + log('Successfully removed an old cache folder ' + name); + } + }); + }); + }); +}; + +// Cordova File plugin helpers +var listDirectory = function (url, options, cb) { + if (typeof options === 'function') + cb = options, options = {}; + + var fail = function (err) { cb(err); }; + window.resolveLocalFileSystemURL(url, function (entry) { + var reader = entry.createReader(); + reader.readEntries(function (entries) { + var names = []; + _.each(entries, function (entry) { + if (! options.dirsOnly || entry.isDirectory) + names.push(entry.name); + }); + cb(null, names); + }, fail); + }, fail); +}; + +var removeDirectory = function (url, cb) { + var fail = function (err) { + cb(err); + }; + window.resolveLocalFileSystemURL(url, function (entry) { + entry.removeRecursively(function () { cb(); }, fail); + }, fail); +}; + +var uriToPath = function (uri) { + return decodeURI(uri).replace(/^file:\/\//g, ''); +}; + +var ensureLocalPathPrefix = function () { + localPathPrefix = localPathPrefix || cordova.file.dataDirectory + 'meteor/'; +}; + diff --git a/tools/client/meteor_cordova_loader.js b/tools/client/meteor_cordova_loader.js index 346e68afbd..ad6a9c9e9c 100644 --- a/tools/client/meteor_cordova_loader.js +++ b/tools/client/meteor_cordova_loader.js @@ -11,6 +11,9 @@ var log = function (msg) { console.log(DEBUG_TAG + msg); }; + var uriToPath = function (uri) { + return decodeURI(uri).replace(/^file:\/\//g, ''); + }; var readFile = function (url, cb) { window.resolveLocalFileSystemURL(url, function (fileEntry) { var success = function (file) { @@ -32,21 +35,11 @@ }); }; - var each = function (array, f) { - for (var i = 0; i < array.length; i++) - f(array[i], i, array); - }; - - - var stripLeadingSlash = function (p) { - if (p.charAt(0) !== '/') - throw new Error("bad path: " + p); - return p.slice(1); - }; - var loadTries = 0; var loadFromLocation = function (location) { - var cordovaRoot = decodeURI(window.location.href).replace(/\/index.html$/, '/').replace(/^file:\/\/?/, ''); + var cordovaRoot = + uriToPath(window.location.href).replace(/\/index.html$/, '/'); + var httpd = cordova && cordova.plugins && cordova.plugins.CordovaUpdate; var retry = function () { @@ -85,41 +78,14 @@ log('No new versions saved to disk.'); } var location = cordova.file.applicationDirectory + 'www/application/'; - location = decodeURI(location).replace(/^file:\/\//g, ''); + location = uriToPath(location); loadFromLocation(location); }; - var listDirectory = function (url, options, cb) { - if (typeof options === 'function') - cb = options, options = {}; - - var fail = function (err) { cb(err); }; - window.resolveLocalFileSystemURL(url, function (entry) { - var reader = entry.createReader(); - reader.readEntries(function (entries) { - var names = []; - each(entries, function (entry) { - if (! options.dirsOnly || entry.isDirectory) - names.push(entry.name); - }); - cb(null, names); - }, fail); - }, fail); - }; - - var removeDirectory = function (url, cb) { - var fail = function (err) { - cb(err); - }; - window.resolveLocalFileSystemURL(url, function (entry) { - entry.removeRecursively(function () { cb(); }, fail); - }, fail); - }; - var loadVersion = function (version, localPathPrefix) { var versionPrefix = localPathPrefix + version + '/'; - var location = decodeURI(versionPrefix).replace(/^file:\/\//g, '') + var location = uriToPath(versionPrefix); loadFromLocation(location); }; @@ -131,31 +97,7 @@ return; } - // Try to clean up our cache directory, make sure to scan the directory - // *before* loading the actual app. This ordering will prevent race - // conditions when the app code tries to download a new version before - // the old-cache removal has scanned the cache folder. - listDirectory(localPathPrefix, {dirsOnly: true}, function (err, names) { - loadVersion(version, localPathPrefix); - - if (err) return; - each(names, function (name) { - // Skip the folder with the latest version - if (name === version) - return; - - // remove everything else, as we don't want to keep too much cache - // around on disk - removeDirectory(localPathPrefix + name + '/', function (err) { - if (err) { - log('Failed to remove an old cache folder ' - + name + ':' + err.message); - } else { - log('Successfully removed an old cache folder ' + name); - } - }); - }); - }); + loadVersion(version, localPathPrefix); }); }; From 3f2ee65a3b8473c16188862c68994b4821392487 Mon Sep 17 00:00:00 2001 From: Justin SB Date: Fri, 3 Oct 2014 17:09:00 -0700 Subject: [PATCH 2/2] Exit immediately from checkRequirements on failure We can't e.g. check if android targets are installed, if android isn't installed --- tools/commands-cordova.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/commands-cordova.js b/tools/commands-cordova.js index 584976c52f..6f6e1e46aa 100644 --- a/tools/commands-cordova.js +++ b/tools/commands-cordova.js @@ -1562,6 +1562,8 @@ _.extend(IOS.prototype, { okay = fix; } + if (!okay) return okay; + //Check if the full Xcode package is already installed: // // $ xcode-select -p @@ -1581,6 +1583,8 @@ _.extend(IOS.prototype, { } } + if (!okay) return okay; + _.each(['5.0', '5.0.1', '5.1', '6.0', '6.1'], function (version) { if (self.isSdkInstalled(version)) { log && Console.warn("An old version of the iPhone SDK is installed (" + version + "); you should"); @@ -1975,6 +1979,8 @@ _.extend(Android.prototype, { okay = fix; } + if (!okay) return okay; + // (hasAcceleration can also be undefined) var hasAcceleration = self.hasAcceleration(); if (hasAcceleration === false) { @@ -1986,6 +1992,8 @@ _.extend(Android.prototype, { log && Console.info(Console.success("HAXM is installed")); } + if (!okay) return okay; + if (self.hasAndroidBundle()) { log && Console.info(Console.success("Found Android bundle")); } else { @@ -1995,6 +2003,8 @@ _.extend(Android.prototype, { okay = fix; } + if (!okay) return okay; + if (self.hasTarget('19', 'default/x86')) { log && Console.info(Console.success("Found suitable Android API libraries")); } else { @@ -2004,6 +2014,8 @@ _.extend(Android.prototype, { okay = fix; } + if (!okay) return okay; + var avdName = self.getAvdName(); if (self.hasAvd(avdName)) { log && Console.info(Console.success("'" + avdName + "' android virtual device (AVD) found"));