From e9db660d03bc58418b2601dd2f67d9256dfc49ce Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Sun, 15 Oct 2017 16:11:17 -0400 Subject: [PATCH 1/3] Allow 64-bit Windows builds of meteor-tool to download 32-bit builds. Once Windows developers update to a 64-bit version of Meteor 1.6, they may still wish to run apps using older versions of Meteor where only 32-bit builds of meteor-tool are available. This commit makes that possible. --- tools/cli/main.js | 74 ++++++++++++----------- tools/packaging/catalog/catalog-remote.js | 6 ++ tools/packaging/updater.js | 7 ++- tools/utils/archinfo.js | 19 ++++++ 4 files changed, 70 insertions(+), 36 deletions(-) diff --git a/tools/cli/main.js b/tools/cli/main.js index 6e22806ec5..5d66a7896b 100644 --- a/tools/cli/main.js +++ b/tools/cli/main.js @@ -425,44 +425,45 @@ var springboard = function (rel, options) { console.log("WILL SPRINGBOARD TO", rel.getToolsPackageAtVersion()); } - var archinfo = require('../utils/archinfo.js'); - var isopack = require('../isobuild/isopack.js'); + const archinfo = require('../utils/archinfo.js'); + const toolsPkg = rel.getToolsPackage(); + const toolsVersion = rel.getToolsVersion(); + const serverArchitectures = catalog.official.filterArchesWithBuilds( + toolsPkg, + toolsVersion, + archinfo.acceptableMeteorToolArches(), + ); - var toolsPkg = rel.getToolsPackage(); - var toolsVersion = rel.getToolsVersion(); - var packageMapModule = require('../packaging/package-map.js'); - var versionMap = {}; - versionMap[toolsPkg] = toolsVersion; - var packageMap = new packageMapModule.PackageMap(versionMap); + if (serverArchitectures.length === 0) { + var release = catalog.official.getDefaultReleaseVersion(); + var releaseName = release.track + "@" + release.version; - if (process.platform === "win32") { - // Make sure the tool we are trying to download has been built for Windows - var buildsForHostArch = catalog.official.getBuildsForArches( - rel.getToolsPackage(), rel.getToolsVersion(), [archinfo.host()]); + Console.error( + "This project uses " + rel.getDisplayName() + ", which isn't", + "available on this platform. To work with this app on all supported", + "platforms, use", Console.command("meteor update --release " + releaseName), + "to pin this app to the newest compatible release." + ); - if (! buildsForHostArch) { - var release = catalog.official.getDefaultReleaseVersion(); - var releaseName = release.track + "@" + release.version; - - Console.error( - "This project uses " + rel.getDisplayName() + ", which isn't", - "available on Windows. To work with this app on all supported", - "platforms, use", Console.command("meteor update --release " + releaseName), - "to pin this app to the newest Windows-compatible release."); - - process.exit(1); - } + process.exit(1); } + const packageMapModule = require('../packaging/package-map.js'); + const packageMap = new packageMapModule.PackageMap({ + [toolsPkg]: toolsVersion, + }); + // XXX split better Console.withProgressDisplayVisible(function () { - var messages = buildmessage.capture( - { title: "downloading the command-line tool" }, function () { - catalog.runAndRetryWithRefreshIfHelpful(function () { - tropohouse.default.downloadPackagesMissingFromMap(packageMap); + var messages = buildmessage.capture({ + title: "downloading the command-line tool" + }, function () { + catalog.runAndRetryWithRefreshIfHelpful(function () { + tropohouse.default.downloadPackagesMissingFromMap(packageMap, { + serverArchitectures, }); - } - ); + }); + }); if (messages.hasMessages()) { // We have failed to download the tool that we are supposed to springboard @@ -482,11 +483,16 @@ var springboard = function (rel, options) { } }); - var packagePath = tropohouse.default.packagePath(toolsPkg, toolsVersion); - var toolIsopack = new isopack.Isopack; + const isopack = require('../isobuild/isopack.js'); + const packagePath = tropohouse.default.packagePath(toolsPkg, toolsVersion); + const toolIsopack = new isopack.Isopack; toolIsopack.initFromPath(toolsPkg, packagePath); - var toolRecord = _.findWhere(toolIsopack.toolsOnDisk, - {arch: archinfo.host()}); + + let toolRecord = null; + serverArchitectures.some(arch => { + return toolRecord = _.findWhere(toolIsopack.toolsOnDisk, { arch }); + }); + if (!toolRecord) { throw Error("missing tool for " + archinfo.host() + " in " + toolsPkg + "@" + toolsVersion); diff --git a/tools/packaging/catalog/catalog-remote.js b/tools/packaging/catalog/catalog-remote.js index 2a818da632..f52dc85034 100644 --- a/tools/packaging/catalog/catalog-remote.js +++ b/tools/packaging/catalog/catalog-remote.js @@ -640,6 +640,12 @@ _.extend(RemoteCatalog.prototype, { return solution; // might be null! }, + filterArchesWithBuilds: function (name, version, arches) { + return arches.filter(arch => { + return !! this.getBuildsForArches(name, version, [arch]); + }); + }, + // Returns general (non-version-specific) information about a // release track, or null if there is no such release track. getReleaseTrack: function (name) { diff --git a/tools/packaging/updater.js b/tools/packaging/updater.js index 07bfaecb66..77de994e6a 100644 --- a/tools/packaging/updater.js +++ b/tools/packaging/updater.js @@ -201,8 +201,11 @@ export function updateMeteorToolSymlink(printErrors) { latestReleaseToolPackage, tropohouse.default.packagePath(latestReleaseToolPackage, latestReleaseToolVersion)); - var toolRecord = _.findWhere(toolIsopack.toolsOnDisk, - {arch: archinfo.host()}); + + var toolRecord = null; + archinfo.acceptableMeteorToolArches().some(arch => { + return toolRecord = _.findWhere(toolIsopack.toolsOnDisk, { arch }); + }); // XXX maybe we shouldn't throw from this background thing // counter: this is super weird and should never ever happen. diff --git a/tools/utils/archinfo.js b/tools/utils/archinfo.js index 81e3dc48a6..0eba6afde4 100644 --- a/tools/utils/archinfo.js +++ b/tools/utils/archinfo.js @@ -182,6 +182,25 @@ var host = function () { return _host; }; +// In order to springboard to earlier Meteor releases that did not have +// 64-bit Windows builds, Windows installations must be allowed to +// download 32-bit builds of meteor-tool. +exports.acceptableMeteorToolArches = function () { + if (os.platform() === "win32") { + switch (utils.architecture()) { + case "x86_32": + return ["os.windows.x86_32"]; + case "x86_64": + return [ + "os.windows.x86_64", + "os.windows.x86_32", + ]; + } + } + + return [host()]; +}; + // True if `host` (an architecture name such as 'os.linux.x86_64') can run // programs of architecture `program` (which might be something like 'os', // 'os.linux', or 'os.linux.x86_64'). From 5a9e837937319a9c4f49b6762408b7e3a307cda9 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Sun, 15 Oct 2017 19:23:52 -0400 Subject: [PATCH 2/3] Upgrade to 64-bit when possible on Windows. Since we can't change the springboarding logic of existing Meteor releases, this decision has to be made by the springboarded-to Meteor 1.6 release, and may result in a second springboarding. --- tools/cli/main.js | 40 ++++++++++++++++++++++++++++++++++------ tools/utils/archinfo.js | 8 ++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/tools/cli/main.js b/tools/cli/main.js index 5d66a7896b..f7379e6012 100644 --- a/tools/cli/main.js +++ b/tools/cli/main.js @@ -15,6 +15,7 @@ var projectContextModule = require('../project-context.js'); var catalog = require('../packaging/catalog/catalog.js'); var buildmessage = require('../utils/buildmessage.js'); var httpHelpers = require('../utils/http-helpers.js'); +const archinfo = require('../utils/archinfo.js'); var main = exports; @@ -425,7 +426,6 @@ var springboard = function (rel, options) { console.log("WILL SPRINGBOARD TO", rel.getToolsPackageAtVersion()); } - const archinfo = require('../utils/archinfo.js'); const toolsPkg = rel.getToolsPackage(); const toolsVersion = rel.getToolsVersion(); const serverArchitectures = catalog.official.filterArchesWithBuilds( @@ -497,7 +497,22 @@ var springboard = function (rel, options) { throw Error("missing tool for " + archinfo.host() + " in " + toolsPkg + "@" + toolsVersion); } - var executable = files.pathJoin(packagePath, toolRecord.path, 'meteor'); + + const newToolsDir = files.pathJoin(packagePath, toolRecord.path); + if (files.realpath(newToolsDir) === + files.realpath(files.getCurrentToolsDir())) { + if (options.mayReturn) { + // Return instead of springboarding (or throwing an exception), if + // we are allowed to keep using the current tools. + return; + } + + throw new Error( + "Cannot springboard to same tools directory: " + newToolsDir + ); + } + + const executable = files.pathJoin(newToolsDir, "meteor"); // Strip off the "node" and "meteor.js" from argv and replace it with the // appropriate tools's meteor shell script. @@ -1135,10 +1150,23 @@ Fiber(function () { // update, because the correct tools version will have been chosen // the first time around. It will also never happen if the current // release is a checkout, because that doesn't make any sense. - if (release.current && release.current.isProperRelease() && - release.current.getToolsPackageAtVersion() !== files.getToolsVersion()) { - springboard(release.current, { fromApp: releaseFromApp }); - // Does not return! + if (release.current && + release.current.isProperRelease()) { + if (files.getToolsVersion() !== + release.current.getToolsPackageAtVersion()) { + springboard(release.current, { + fromApp: releaseFromApp, + mayReturn: false, + }) + // Does not return! + } else if (archinfo.canSwitchTo64Bit()) { + springboard(release.current, { + fromApp: releaseFromApp, + // Switching to a 64-bit meteor-tool build may fail, in which case + // we should continue on as usual. + mayReturn: true, + }); + } } // Check for the '--help' option. diff --git a/tools/utils/archinfo.js b/tools/utils/archinfo.js index 0eba6afde4..732a59e1b1 100644 --- a/tools/utils/archinfo.js +++ b/tools/utils/archinfo.js @@ -201,6 +201,14 @@ exports.acceptableMeteorToolArches = function () { return [host()]; }; +// 64-bit Windows machines that have been using a 32-bit version of Meteor +// are eligible to switch to 64-bit beginning with Meteor 1.6, which is +// the first version of Meteor that contains this code. +exports.canSwitchTo64Bit = function () { + return utils.architecture() === "x86_64" && + host === "os.windows.x86_32"; +}; + // True if `host` (an architecture name such as 'os.linux.x86_64') can run // programs of architecture `program` (which might be something like 'os', // 'os.linux', or 'os.linux.x86_64'). From bbd3b9ffca0e68978e777f7d357354cdf6277562 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Sun, 15 Oct 2017 20:08:49 -0400 Subject: [PATCH 3/3] Tolerate springboarding to the same meteor-tool build directory. Though this seems wasteful, it can happen if a SpringboardToLatestRelease exception is thrown, and it definitely should spawn a new process rather than returning normally from the springboard function. --- tools/cli/main.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tools/cli/main.js b/tools/cli/main.js index f7379e6012..d2b3836368 100644 --- a/tools/cli/main.js +++ b/tools/cli/main.js @@ -502,14 +502,10 @@ var springboard = function (rel, options) { if (files.realpath(newToolsDir) === files.realpath(files.getCurrentToolsDir())) { if (options.mayReturn) { - // Return instead of springboarding (or throwing an exception), if - // we are allowed to keep using the current tools. + // Return instead of springboarding, if we are allowed to keep using + // the current tools without restarting the process. return; } - - throw new Error( - "Cannot springboard to same tools directory: " + newToolsDir - ); } const executable = files.pathJoin(newToolsDir, "meteor");