diff --git a/packages/constraint-solver/constraint-solver-tests.js b/packages/constraint-solver/constraint-solver-tests.js index 68c7554164..4284e30e8e 100644 --- a/packages/constraint-solver/constraint-solver-tests.js +++ b/packages/constraint-solver/constraint-solver-tests.js @@ -17,7 +17,7 @@ var insertVersion = function (name, version, ecv, deps) { references: [ { arch: "os", targetSlice: "main", weak: false, implied: false, unordered: false }, - { arch: "browser", targetSlice: "main", weak: false, + { arch: "web", targetSlice: "main", weak: false, implied: false, unordered: false }] }; }); @@ -25,7 +25,7 @@ var insertVersion = function (name, version, ecv, deps) { earliestCompatibleVersion: ecv, dependencies: constructedDeps }); Builds.insert({ packageName: name, version: version, - buildArchitectures: "browser+os" }); + buildArchitectures: "web+os" }); }; insertVersion("sparky-forms", "1.1.2", "1.0.0", {"forms": "=1.0.1", "sparkle": "=2.1.1"}); insertVersion("sparky-forms", "1.0.0", "1.0.0", {"awesome-dropdown": "=1.4.0"}); @@ -505,7 +505,7 @@ function getCatalogStub (gems) { packageVersion.dependencies[name] = { constraint: convertConstraints(constraints)[0], // XXX pick first one only references: [{ - "arch": "browser" + "arch": "web" }, { "arch": "os" }] }; diff --git a/packages/constraint-solver/constraint-solver.js b/packages/constraint-solver/constraint-solver.js index d769b3030f..49ff0aa0e7 100644 --- a/packages/constraint-solver/constraint-solver.js +++ b/packages/constraint-solver/constraint-solver.js @@ -1,5 +1,12 @@ var semver = Npm.require('semver'); +// Copied from archinfo.matches() in tools/ +var archMatches = function (arch, baseArch) { + return arch.substr(0, baseArch.length) === baseArch && + (arch.length === baseArch.length || + arch.substr(baseArch.length, 1) === "."); +}; + ConstraintSolver = {}; // catalog is a catalog.Catalog object. We have to pass this in because @@ -60,9 +67,10 @@ ConstraintSolver.PackagesResolver.prototype._loadPackageInfo = function ( var unibuilds = {}; // XXX in theory there might be different archs but in practice they are - // always "os" and "browser". Fix this once we actually have different - // archs used. - _.each(["os", "browser"], function (arch) { + // always "os", "web.browser" and "web.cordova". Fix this once we + // actually have different archs used. + var allArchs = ["os", "web.browser", "web.cordova"]; + _.each(allArchs, function (arch) { var unitName = packageName + "#" + arch; unibuilds[unitName] = new ConstraintSolver.UnitVersion( unitName, version, versionDef.earliestCompatibleVersion); @@ -73,24 +81,28 @@ ConstraintSolver.PackagesResolver.prototype._loadPackageInfo = function ( self._ensurePackageInfoLoaded(depName); _.each(dep.references, function (ref) { - var unitName = packageName + "#" + ref.arch; - var unitVersion = unibuilds[unitName]; + _.each(allArchs, function (arch) { + if (archMatches(arch, ref.arch)) { + var unitName = packageName + "#" + arch; + var unitVersion = unibuilds[unitName]; - if (! unitVersion) - throw new Error("A non-standard arch " + ref.arch + " for package " + packageName); + if (! unitVersion) + throw new Error("A non-standard arch " + arch + " for package " + packageName); - var targetUnitName = depName + "#" + ref.arch; + var targetUnitName = depName + "#" + arch; - // Add the dependency if needed - if (! ref.weak) - unitVersion.addDependency(targetUnitName); + // Add the dependency if needed + if (! ref.weak) + unitVersion.addDependency(targetUnitName); - // Add a constraint if such exists - if (dep.constraint && dep.constraint !== "none") { - var constraint = - self.resolver.getConstraint(targetUnitName, dep.constraint); - unitVersion.addConstraint(constraint); - } + // Add a constraint if such exists + if (dep.constraint && dep.constraint !== "none") { + var constraint = + self.resolver.getConstraint(targetUnitName, dep.constraint); + unitVersion.addConstraint(constraint); + } + } + }); }); }); @@ -168,10 +180,11 @@ ConstraintSolver.PackagesResolver.prototype.resolve = function ( } // split every package name to one or more archs belonging to that package - // (["foobar"] => ["foobar#os", "foobar#browser"]) - // XXX for now just put #os and #browser + // (["foobar"] => ["foobar#os", "foobar#web.browser", ...]) + // XXX for now just hardcode in all of the known architectures options.upgrade = _.filter(_.flatten(_.map(options.upgrade, function (packageName) { - return [packageName + "#os", packageName + "#browser"]; + return [packageName + "#os", packageName + "#web.browser", + packageName + "#web.cordova"]; })), _.identity); var dc = self._splitDepsToConstraints(dependencies, constraints); @@ -202,7 +215,7 @@ ConstraintSolver.PackagesResolver.prototype.resolve = function ( var resultChoices = {}; _.each(res, function (uv) { // Since we don't yet define the interface for a an app to depend only on - // certain unibuilds of the packages (like only browser unibuilds) and we know + // certain unibuilds of the packages (like only web unibuilds) and we know // that each unibuild weakly depends on other sibling unibuilds of the same // version, we can safely output the whole package for each unibuild in the // result. @@ -245,7 +258,7 @@ ConstraintSolver.PackagesResolver.prototype.propagateExactDeps = }; // takes dependencies and constraints and rewrites the names from "foo" to -// "foo#os" and "foo#browser" +// "foo#os" and "foo#web.browser" and "foo#web.cordova" // XXX right now creates a dependency for every unibuild it can find ConstraintSolver.PackagesResolver.prototype._splitDepsToConstraints = function (inputDeps, inputConstraints) { @@ -280,8 +293,9 @@ ConstraintSolver.PackagesResolver.prototype._unibuildsForPackage = var self = this; var unibuildPrefix = packageName + "#"; var unibuilds = []; - // XXX hardcode os and browser - _.each(["os", "browser"], function (arch) { + // XXX hardcode all common architectures assuming that every package has the + // same set of architectures. + _.each(["os", "web.browser", "web.cordova"], function (arch) { if (self.resolver.unitsVersions[unibuildPrefix + arch]) unibuilds.push(unibuildPrefix + arch); }); diff --git a/packages/less/plugin/compile-less.js b/packages/less/plugin/compile-less.js index 7eccb5e42f..fbbf1e4eea 100644 --- a/packages/less/plugin/compile-less.js +++ b/packages/less/plugin/compile-less.js @@ -5,7 +5,7 @@ var Future = Npm.require('fibers/future'); Plugin.registerSourceHandler("less", function (compileStep) { // XXX annoying that this is replicated in .css, .less, and .styl - if (! compileStep.archMatches('browser')) { + if (! compileStep.archMatches('web')) { // XXX in the future, might be better to emit some kind of a // warning if a stylesheet is included on the server, rather than // silently ignoring it. but that would mean you can't stick .css diff --git a/packages/meteor/plugin/basic-file-types.js b/packages/meteor/plugin/basic-file-types.js index 3c87328198..5cadb96ab3 100644 --- a/packages/meteor/plugin/basic-file-types.js +++ b/packages/meteor/plugin/basic-file-types.js @@ -4,7 +4,7 @@ Plugin.registerSourceHandler("css", function (compileStep) { // XXX annoying that this is replicated in .css, .less, and .styl - if (! compileStep.archMatches('browser')) { + if (! compileStep.archMatches('web')) { // XXX in the future, might be better to emit some kind of a // warning if a stylesheet is included on the server, rather than // silently ignoring it. but that would mean you can't stick .css diff --git a/packages/star-translate/translator.js b/packages/star-translate/translator.js index bff404d034..a0ea3d71f2 100644 --- a/packages/star-translate/translator.js +++ b/packages/star-translate/translator.js @@ -39,8 +39,8 @@ StarTranslator._translate = function (bundlePath) { "builtBy": "Star translator", "programs": [ { - "name": "client", - "arch": "browser", + "name": "web.browser", + "arch": "web.browser", "path": "client.json" }, { @@ -99,7 +99,7 @@ StarTranslator._writeClientProg = function (bundlePath, clientProgPath) { "app.json"), 'utf8')); var clientManifest = { - "format": "browser-program-pre1", + "format": "web-program-pre1", "manifest": origClientManifest.manifest, // XXX Haven't updated this for the app.html -> head/body change, but // surely we don't need to because code in pre-star apps doesn't diff --git a/packages/stylus/plugin/compile-stylus.js b/packages/stylus/plugin/compile-stylus.js index 7e3c7372e5..556db16b64 100644 --- a/packages/stylus/plugin/compile-stylus.js +++ b/packages/stylus/plugin/compile-stylus.js @@ -6,7 +6,7 @@ var Future = Npm.require('fibers/future'); Plugin.registerSourceHandler("styl", function (compileStep) { // XXX annoying that this is replicated in .css, .less, and .styl - if (! compileStep.archMatches('browser')) { + if (! compileStep.archMatches('web')) { // XXX in the future, might be better to emit some kind of a // warning if a stylesheet is included on the server, rather than // silently ignoring it. but that would mean you can't stick .css diff --git a/packages/templating/plugin/compile-templates.js b/packages/templating/plugin/compile-templates.js index b364737c2e..d91ad23360 100644 --- a/packages/templating/plugin/compile-templates.js +++ b/packages/templating/plugin/compile-templates.js @@ -2,8 +2,7 @@ var path = Npm.require('path'); var doHTMLScanning = function (compileStep, htmlScanner) { - // XXX use archinfo rather than rolling our own - if (! compileStep.arch.match(/^browser(\.|$)/)) + if (! compileStep.archMatches("web")) // XXX might be nice to throw an error here, but then we'd have to // make it so that packages.js ignores html files that appear in // the server directories in an app tree.. or, it might be nice to diff --git a/packages/webapp/webapp_server.js b/packages/webapp/webapp_server.js index af460e1ffe..bbfd8118db 100644 --- a/packages/webapp/webapp_server.js +++ b/packages/webapp/webapp_server.js @@ -417,7 +417,7 @@ var runWebAppServer = function () { __meteor_bootstrap__.configJson.client); clientDir = path.dirname(clientJsonPath); clientJson = JSON.parse(readUtf8FileSync(clientJsonPath)); - if (clientJson.format !== "browser-program-pre1") + if (clientJson.format !== "web-program-pre1") throw new Error("Unsupported format for client assets: " + JSON.stringify(clientJson.format)); diff --git a/tools/bundler.js b/tools/bundler.js index 0a561ee442..e2cf9545a5 100644 --- a/tools/bundler.js +++ b/tools/bundler.js @@ -51,19 +51,19 @@ // really the build tool can lay out the star however it wants. // // -// == Format of a program when arch is "browser.*" == +// == Format of a program when arch is "web.*" == // // Standard: // // /program.json // -// - format: "browser-program-pre1" for this version +// - format: "web-program-pre1" for this version // // - manifest: array of resources to serve with HTTP, each an object: // - path: path of file relative to program.json // - where: "client" // - type: "js", "css", or "asset" -// - cacheable: is it safe to ask the browser to cache this file (boolean) +// - cacheable: is it safe to ask the client to cache this file (boolean) // - url: relative url to download the resource, includes cache busting // parameter when used // - size: size of file in bytes @@ -387,7 +387,7 @@ var Target = function (options) { // PackageLoader to use for resolving package dependenices. self.packageLoader = options.packageLoader; - // Something like "browser.w3c" or "os" or "os.osx.x86_64" + // Something like "web.browser" or "os" or "os.osx.x86_64" self.arch = options.arch; // All of the Unibuilds that are to go into this target, in the order @@ -411,7 +411,7 @@ var Target = function (options) { self.nodeModulesDirectories = {}; // Static assets to include in the bundle. List of File. - // For browser targets, these are served over HTTP. + // For client targets, these are served over HTTP. self.asset = []; }; @@ -579,7 +579,7 @@ _.extend(Target.prototype, { _emitResources: function () { var self = this; - var isBrowser = archinfo.matches(self.arch, "browser"); + var isWeb = archinfo.matches(self.arch, "web"); var isOs = archinfo.matches(self.arch, "os"); // Copy their resources into the bundle in order @@ -607,7 +607,7 @@ _.extend(Target.prototype, { : stripLeadingSlash(resource.servePath); f.setTargetPathFromRelPath(relPath); - if (isBrowser) + if (isWeb) f.setUrlFromRelPath(resource.servePath); else { unibuildAssets[resource.path] = resource.data; @@ -622,7 +622,7 @@ _.extend(Target.prototype, { return; // already handled if (_.contains(["js", "css"], resource.type)) { - if (resource.type === "css" && ! isBrowser) + if (resource.type === "css" && ! isWeb) // XXX might be nice to throw an error here, but then we'd // have to make it so that package.js ignores css files // that appear in the server directories in an app tree @@ -636,7 +636,7 @@ _.extend(Target.prototype, { var relPath = stripLeadingSlash(resource.servePath); f.setTargetPathFromRelPath(relPath); - if (isBrowser) { + if (isWeb) { f.setUrlFromRelPath(resource.servePath); } @@ -673,8 +673,8 @@ _.extend(Target.prototype, { } if (_.contains(["head", "body"], resource.type)) { - if (! isBrowser) - throw new Error("HTML segments can only go to the browser"); + if (! isWeb) + throw new Error("HTML segments can only go to the client"); self[resource.type].push(resource.data); return; } @@ -760,8 +760,8 @@ var ClientTarget = function (options) { self.head = []; self.body = []; - if (! archinfo.matches(self.arch, "browser")) - throw new Error("ClientTarget targeting something that isn't a browser?"); + if (! archinfo.matches(self.arch, "web")) + throw new Error("ClientTarget targeting something that isn't a client?"); }; util.inherits(ClientTarget, Target); @@ -953,7 +953,7 @@ _.extend(ClientTarget.prototype, { // Control file builder.writeJson('program.json', { - format: "browser-program-pre1", + format: "web-program-pre1", manifest: manifest }); return "program.json"; @@ -1296,7 +1296,7 @@ var JsImageTarget = function (options) { Target.apply(this, arguments); if (! archinfo.matches(self.arch, "os")) - // Conceivably we could support targeting the browser as long as + // Conceivably we could support targeting the client as long as // no native node modules were used. No use case for that though. throw new Error("JsImageTarget targeting something unusual?"); }; @@ -1615,7 +1615,9 @@ var writeSiteArchive = function (targets, outputPath, options) { * * - buildOptions: may include * - minify: minify the CSS and JS assets (boolean, default false) - * - arch: the server architecture to target (defaults to archinfo.host()) + * - serverArch: the server architecture to target + * (defaults to archinfo.host()) + * - webArchs: an array of web archs to target * * - hasCachedBundle: true if we already have a cached bundle stored in * /build. When true, we only build the new client targets in the bundle. @@ -1648,7 +1650,8 @@ exports.bundle = function (options) { if (! release.usingRightReleaseForApp(appDir)) throw new Error("running wrong release for app?"); - var arch = buildOptions.arch || archinfo.host(); + var serverArch = buildOptions.serverArch || archinfo.host(); + var webArchs = buildOptions.webArchs || [ "web.browser" ]; var releaseName = release.current.isCheckout() ? "none" : release.current.name; @@ -1666,7 +1669,7 @@ exports.bundle = function (options) { }, function () { var packageLoader = project.project.getPackageLoader(); var downloaded = project.project._ensurePackagesExistOnDisk( - project.project.dependencies, { arch: arch, verbose: true }); + project.project.dependencies, { serverArch: serverArch, verbose: true }); if (_.keys(downloaded).length !== _.keys(project.project.dependencies).length) { @@ -1677,10 +1680,10 @@ exports.bundle = function (options) { var controlProgram = null; - var makeClientTarget = function (app) { + var makeClientTarget = function (app, webArch) { var client = new ClientTarget({ packageLoader: packageLoader, - arch: "browser" + arch: webArch }); client.make({ @@ -1695,7 +1698,7 @@ exports.bundle = function (options) { var makeBlankClientTarget = function () { var client = new ClientTarget({ packageLoader: packageLoader, - arch: "browser" + arch: "web.browser" }); client.make({ minify: buildOptions.minify, @@ -1708,7 +1711,7 @@ exports.bundle = function (options) { var makeServerTarget = function (app, clientTarget) { var targetOptions = { packageLoader: packageLoader, - arch: arch, + arch: serverArch, releaseName: releaseName }; if (clientTarget) @@ -1738,13 +1741,23 @@ exports.bundle = function (options) { appDir, exports.ignoreFiles); // Client - var client = makeClientTarget(app); - targets.client = client; + _.each(webArchs, function (arch) { + var client = makeClientTarget(app, arch); + targets[arch] = client; + }); + + // Create a browser client if one doesn't exist already. + var browserClient = targets["web.browser"]; + + if (! browserClient) { + browserClient = makeBlankClientTarget(app); + targets["web.browser"] = browserClient; + } // Server if (! options.hasCachedBundle) { - var server = makeServerTarget(app, client); - server.clientTarget = client; + var server = makeServerTarget(app, browserClient); + server.clientTarget = browserClient; targets.server = server; } } diff --git a/tools/commands-packages.js b/tools/commands-packages.js index 51249e1437..1f54fea654 100644 --- a/tools/commands-packages.js +++ b/tools/commands-packages.js @@ -186,12 +186,15 @@ main.registerCommand({ } // Warn the user if their package is not good for all architectures. - if (compileResult.unipackage.buildArchitectures() !== "browser+os") { + var allArchs = compileResult.unipackage.buildArchitectures().split('+'); + if (_.any(allArchs, function (arch) { + return arch.match(/^os\./); + })) { process.stdout.write( "\nWARNING: Your package contains binary code and is only compatible with " + archinfo.host() + " architecture.\n" + "Please use publish-for-arch to publish new builds of the package.\n\n"); - }; + } // We are only publishing one package, so we should close the connection, and // then exit with the previous error code. @@ -763,15 +766,18 @@ main.registerCommand({ var myBuilds = _.pluck( catalog.official.getAllBuilds(name, version), 'buildArchitectures'); - // This package does not have different builds, so we don't care. - if (_.isEqual(myBuilds, ["browser+os"])) { - return versionRecord; + // Does this package only have a cross-platform build? + if (myBuilds.length === 1) { + var allArches = myBuilds[0].split('+'); + if (!_.any(allArches, function (arch) { + return arch.match(/^os\./); + })) { + return versionRecord; + } } // This package is only available for some architectures. - var myStringBuilds = ""; - _.each(myBuilds, function (build) { - myStringBuilds = myStringBuilds + build.split('+')[1] + " "; - }); + // XXX show in a more human way? + var myStringBuilds = myBuilds.join(' '); return _.extend({ buildArchitectures: myStringBuilds }, versionRecord); }; diff --git a/tools/commands.js b/tools/commands.js index ea5e91f620..ec420d4e20 100644 --- a/tools/commands.js +++ b/tools/commands.js @@ -1308,7 +1308,7 @@ main.registerCommand({ }, function () { tmpTropo.maybeDownloadPackageForArchitectures( {packageName: toolPkg.package, version: toolPkg.constraint}, - [osArch], // XXX 'browser' too? + [osArch], // XXX 'web.browser' too? true); }); _.each(release.packages, function (pkgVersion, pkgName) { @@ -1317,7 +1317,7 @@ main.registerCommand({ }, function () { tmpTropo.maybeDownloadPackageForArchitectures( {packageName: pkgName, version: pkgVersion}, - [osArch], // XXX 'browser' too? + [osArch], // XXX 'web.browser' too? true); }); }); diff --git a/tools/compiler.js b/tools/compiler.js index 2e25c9acd0..390b777fd5 100644 --- a/tools/compiler.js +++ b/tools/compiler.js @@ -28,7 +28,7 @@ var compiler = exports; // end up as watched dependencies. (At least for now, packages only used in // target creation (eg minifiers and dev-bundle-fetcher) don't require you to // update BUILT_BY, though you will need to quit and rerun "meteor run".) -compiler.BUILT_BY = 'meteor/11'; +compiler.BUILT_BY = 'meteor/12'; // XXX where should this go? I'll make it a random utility function // for now @@ -457,11 +457,11 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader, // information. // - pathForSourceMap: If this file is to be included in a source map, // this is the name you should use for it in the map. - // - rootOutputPath: on browser targets, for resources such as + // - rootOutputPath: on web targets, for resources such as // stylesheet and static assets, this is the root URL that // will get prepended to the paths you pick for your output // files so that you get your own namespace, for example - // '/packages/foo'. null on non-browser targets + // '/packages/foo'. null on non-web targets // - fileOptions: any options passed to "api.add_files"; for // use by the plugin. The built-in "js" plugin uses the "bare" // option for files that shouldn't be wrapped in a closure. @@ -473,11 +473,11 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader, // file as a Buffer. If n is omitted you get the rest of the // file. // - appendDocument({ section: "head", data: "my markup" }) - // Browser targets only. Add markup to the "head" or "body" + // Web targets only. Add markup to the "head" or "body" // section of the document. // - addStylesheet({ path: "my/stylesheet.css", data: "my css", // sourceMap: "stringified json sourcemap"}) - // Browser targets only. Add a stylesheet to the + // Web targets only. Add a stylesheet to the // document. 'path' is a requested URL for the stylesheet that // may or may not ultimately be honored. (Meteor will add // appropriate tags to cause the stylesheet to be loaded. It @@ -498,10 +498,10 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader, // a closure, so that its vars are shared with other files // in the module. // - addAsset({ path: "my/image.png", data: Buffer }) - // Add a file to serve as-is over HTTP (browser targets) or + // Add a file to serve as-is over HTTP (web targets) or // to include as-is in the bundle (os targets). // This time `data` is a Buffer rather than a string. For - // browser targets, it will be served at the exact path you + // web targets, it will be served at the exact path you // request (concatenated with rootOutputPath). For server // targets, the file can be retrieved by passing path to // Assets.getText or Assets.getBinary. @@ -514,7 +514,7 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader, // line, column, and func are all optional. // // XXX for now, these handlers must only generate portable code - // (code that isn't dependent on the arch, other than 'browser' + // (code that isn't dependent on the arch, other than 'web' // vs 'os') -- they can look at the arch that is provided // but they can't rely on the running on that particular arch // (in the end, an arch-specific unibuild will be emitted only if @@ -572,9 +572,9 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader, return ret; }, appendDocument: function (options) { - if (! archinfo.matches(inputSourceArch.arch, "browser")) + if (! archinfo.matches(inputSourceArch.arch, "web")) throw new Error("Document sections can only be emitted to " + - "browser targets"); + "web targets"); if (options.section !== "head" && options.section !== "body") throw new Error("'section' must be 'head' or 'body'"); if (typeof options.data !== "string") @@ -586,9 +586,9 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader, }); }, addStylesheet: function (options) { - if (! archinfo.matches(inputSourceArch.arch, "browser")) + if (! archinfo.matches(inputSourceArch.arch, "web")) throw new Error("Stylesheets can only be emitted to " + - "browser targets"); + "web targets"); if (typeof options.data !== "string") throw new Error("'data' option to addStylesheet must be a string"); sourceIsWatched = true; @@ -605,8 +605,8 @@ var compileUnibuild = function (unipackage, inputSourceArch, packageLoader, throw new Error("'data' option to addJavaScript must be a string"); if (typeof options.sourcePath !== "string") throw new Error("'sourcePath' option must be supplied to addJavaScript. Consider passing inputPath."); - if (options.bare && ! archinfo.matches(inputSourceArch.arch, "browser")) - throw new Error("'bare' option may only be used for browser targets"); + if (options.bare && ! archinfo.matches(inputSourceArch.arch, "web")) + throw new Error("'bare' option may only be used for web targets"); sourceIsWatched = true; js.push({ source: options.data, diff --git a/tools/package-source.js b/tools/package-source.js index 1ddf15adb0..6d45967c16 100644 --- a/tools/package-source.js +++ b/tools/package-source.js @@ -92,6 +92,17 @@ var loadOrderSort = function (templateExtensions) { }; }; +// XXX We currently have a 1 to 1 mapping between 'where' and 'arch'. +// In the future, we may let people specify different 'where' and 'arch'. +var mapWhereToArch = function (where) { + if (where === 'server') { + return 'os'; + } else { + // Transform client.* into web.* + return 'web.' + where.split('.').slice(1).join('.'); + } +}; + /////////////////////////////////////////////////////////////////////////////// // SourceArch /////////////////////////////////////////////////////////////////////////////// @@ -200,7 +211,7 @@ var PackageSource = function () { // Path that will be prepended to the URLs of all resources emitted // by this package (assuming they don't end up getting - // concatenated). For non-browser targets, the only effect this will + // concatenated). For non-web targets, the only effect this will // have is to change the actual on-disk paths of the files in the // bundle, for those that care to open up the bundle and look (but // it's still nice to get it right). @@ -293,6 +304,10 @@ var PackageSource = function () { // overall package versions file (if one exists). In the future, we can make // this option transparent to the user in package.js. self.noVersionFile = false; + + // The list of where that we can target. Doesn't include 'client' because + // it is expanded into 'client.*'. + self.allWheres = ['server', 'client.browser', 'client.cordova']; }; @@ -699,16 +714,35 @@ _.extend(PackageSource.prototype, { } // source files used - var sources = {client: [], server: []}; + var sources = {}; // symbols exported - var exports = {client: [], server: []}; + var exports = {}; // packages used and implied (keys are 'package', 'unordered', and // 'weak'). an "implied" package is a package that will be used by a unibuild // which uses us. - var uses = {client: [], server: []}; - var implies = {client: [], server: []}; + var uses = {}; + var implies = {}; + + _.each(self.allWheres, function (where) { + sources[where] = []; + exports[where] = []; + uses[where] = []; + implies[where] = []; + }); + + // Iterates over the list of target archs and calls f(arch) for all archs + // that match an element of 'wheres'. + var forAllMatchingWheres = function (wheres, f) { + _.each(wheres, function (where) { + _.each(self.allWheres, function (matchWhere) { + if (archinfo.matches(matchWhere, where)) { + f(matchWhere); + } + }); + }); + }; // For this old-style, on_use/on_test/where-based package, figure // out its dependencies by calling its on_xxx functions and seeing @@ -717,14 +751,11 @@ _.extend(PackageSource.prototype, { // We have a simple strategy. Call its on_xxx handler with no // 'where', which is what happens when the package is added // directly to an app, and see what files it adds to the client - // and the server. Call the former the client version of the - // package, and the latter the server version. Then, when a - // package is used, include it in both the client and the server - // by default. This simple strategy doesn't capture even 10% of - // the complexity possible with on_use, on_test, and where, but + // and the server. When a package is used, include it in both the client + // and the server by default. This simple strategy doesn't capture even + // 10% of the complexity possible with on_use, on_test, and where, but // probably is sufficient for virtually all packages that actually - // exist in the field, if not every single - // one. #OldStylePackageSupport + // exist in the field, if not every single one. #OldStylePackageSupport if (fileAndDepLoader) { var toArray = function (x) { @@ -733,25 +764,23 @@ _.extend(PackageSource.prototype, { return x ? [x] : []; }; - var allWheres = ['client', 'server']; var toWhereArray = function (where) { if (!(where instanceof Array)) { - where = where ? [where] : allWheres; + where = where ? [where] : self.allWheres; } where = _.uniq(where); - var realWhere = _.intersection(where, allWheres); - if (realWhere.length !== where.length) { - var badWheres = _.difference(where, allWheres); - // avoid using _.each so as to not add more frames to skip - for (var i = 0; i < badWheres.length; ++i) { + _.each(where, function (inputWhere) { + var isMatch = _.any(_.map(self.allWheres, function (actualWhere) { + return archinfo.matches(actualWhere, inputWhere); + })); + if (! isMatch) { buildmessage.error( - "Invalid 'where' argument: '" + badWheres[i] + "'", + "Invalid 'where' argument: '" + inputWhere + "'", // skip toWhereArray in addition to the actual API function - {useMyCaller: 1}); - }; - // recover by using the real ones only - } - return realWhere; + {useMyCaller: 2}); + } + }); + return where; }; var api = { @@ -759,8 +788,9 @@ _.extend(PackageSource.prototype, { // used. Can also take literal package objects, if you have // anonymous packages you want to use (eg, app packages) // - // @param where 'client', 'server', or an array of those. - // The default is ['client', 'server']. + // @param where 'web', 'web.browser', 'web.cordova', 'server', + // or an array of those. + // The default is ['web', 'server']. // // options can include: // @@ -808,7 +838,7 @@ _.extend(PackageSource.prototype, { } _.each(names, function (name) { - _.each(where, function (w) { + forAllMatchingWheres(where, function (w) { uses[w].push(_.extend(utils.splitConstraint(name), { unordered: options.unordered || false, weak: options.weak || false @@ -825,7 +855,7 @@ _.extend(PackageSource.prototype, { where = toWhereArray(where); _.each(names, function (name) { - _.each(where, function (w) { + forAllMatchingWheres(where, function (w) { // We don't allow weak or unordered implies, since the main // purpose of imply is to provide imports and plugins. implies[w].push(utils.splitConstraint(name)); @@ -841,7 +871,7 @@ _.extend(PackageSource.prototype, { where = toWhereArray(where); _.each(paths, function (path) { - _.each(where, function (w) { + forAllMatchingWheres(where, function (w) { var source = {relPath: path}; if (fileOptions) source.fileOptions = fileOptions; @@ -877,8 +907,9 @@ _.extend(PackageSource.prototype, { // Export symbols from this package. // // @param symbols String (eg "Foo") or array of String - // @param where 'client', 'server', or an array of those. - // The default is ['client', 'server']. + // @param where 'web', 'server', 'web.browser', 'web.cordova' + // or an array of those. + // The default is ['web', 'server']. // @param options 'testOnly', boolean. export: function (symbols, where, options) { // Support `api.export("FooTest", {testOnly: true})` without @@ -900,7 +931,7 @@ _.extend(PackageSource.prototype, { // recover by ignoring return; } - _.each(where, function (w) { + forAllMatchingWheres(where, function (w) { exports[w].push({name: symbol, testOnly: !!options.testOnly}); }); }); @@ -918,7 +949,11 @@ _.extend(PackageSource.prototype, { // packages and any remaining handlers. It violates the // principle of least surprise to half-run a handler // and then continue. - sources = {client: [], server: []}; + sources = {}; + _.each(self.allWheres, function (where) { + sources[where] = []; + }); + fileAndDepLoader = null; self.pluginInfo = {}; npmDependencies = null; @@ -944,9 +979,9 @@ _.extend(PackageSource.prototype, { // For all implies and uses, fill in the unspecified dependencies from the // release. - _.each(['server', 'client'], function (label) { - uses[label] = _.map(uses[label], setFromRel); - implies[label] = _.map(implies[label], setFromRel); + _.each(self.allWheres, function (label) { + uses[label] = _.map(uses[label], setFromRel); + implies[label] = _.map(implies[label], setFromRel); }); }; @@ -983,9 +1018,10 @@ _.extend(PackageSource.prototype, { files.rm_recursive(path.join(self.sourceRoot, '.npm', f)); }); - // Create source architectures, one for the server and one for the client. - _.each(["browser", "os"], function (arch) { - var where = (arch === "browser") ? "client" : "server"; + // Create source architectures, one for the server and one for each web + // arch. + _.each(self.allWheres, function (where) { + var arch = mapWhereToArch(where); // Everything depends on the package 'meteor', which sets up // the basic environment) (except 'meteor' itself, and js-analyze @@ -1065,15 +1101,17 @@ _.extend(PackageSource.prototype, { self.sourceRoot = appDir; self.serveRoot = path.sep; - _.each(["client", "server"], function (archName) { + _.each(self.allWheres, function (where) { // Determine used packages var project = require('./project.js').project; var names = project.getConstraints(); - var arch = archName === "server" ? "os" : "browser"; + var arch = mapWhereToArch(where); + // XXX what about /client.browser/* etc, these directories could also + // be for specific client targets. // Create unibuild var sourceArch = new SourceArch(self, { - name: archName, + name: where, arch: arch, uses: _.map(names, utils.dealConstraint) }); @@ -1118,7 +1156,7 @@ _.extend(PackageSource.prototype, { }); var otherUnibuildRegExp = - (archName === "server" ? /^client\/$/ : /^server\/$/); + (where === "server" ? /^client\/$/ : /^server\/$/); // The paths that we've called checkForInfiniteRecursion on. var seenPaths = {}; @@ -1188,7 +1226,7 @@ _.extend(PackageSource.prototype, { // Special case: on the client, JavaScript files in a // `client/compatibility` directory don't get wrapped in a closure. - if (archName === "client" && relPath.match(/\.js$/)) { + if (archinfo.matches(arch, "web") && relPath.match(/\.js$/)) { var clientCompatSubstr = path.sep + 'client' + path.sep + 'compatibility' + path.sep; if ((path.sep + relPath).indexOf(clientCompatSubstr) !== -1) @@ -1198,7 +1236,7 @@ _.extend(PackageSource.prototype, { }); // Now look for assets for this unibuild. - var assetDir = archName === "client" ? "public" : "private"; + var assetDir = archinfo.matches(arch, "web") ? "public" : "private"; var assetDirs = readAndWatchDirectory('', { include: [new RegExp('^' + assetDir + '/$')] }); diff --git a/tools/project.js b/tools/project.js index 7cabd810d1..94cbf7a545 100644 --- a/tools/project.js +++ b/tools/project.js @@ -649,7 +649,7 @@ _.extend(Project.prototype, { var self = this; buildmessage.assertInCapture(); options = options || {}; - var arch = options.arch || archinfo.host(); + var serverArch = options.serverArch || archinfo.host(); var verbose = options.verbose || !self.muted; var downloadedPackages = {}; _.each(versions, function (version, name) { @@ -657,7 +657,7 @@ _.extend(Project.prototype, { try { var available = tropohouse.default.maybeDownloadPackageForArchitectures( packageVersionInfo, - ['browser', arch], + [serverArch], // XXX 'web.browser' too? verbose /* print downloading message */ ); downloadedPackages[name] = version; diff --git a/tools/selftest.js b/tools/selftest.js index 48336d3d7c..afe1cada73 100644 --- a/tools/selftest.js +++ b/tools/selftest.js @@ -339,10 +339,15 @@ _.extend(OutputLog.prototype, { // 'fake-mongod' stub process to be started instead of 'mongod'. The // tellMongo method then becomes available on Runs for controlling // the stub. +// - clients +// - browserstack: true if browserstack clients should be used +// - port: the port that the clients should run on var Sandbox = function (options) { var self = this; - options = options || {}; + // default options + options = _.extend({ clients: {} }, options); + self.root = files.mkdtemp(); self.warehouse = null; @@ -369,13 +374,13 @@ var Sandbox = function (options) { self.clients = [ new PhantomClient({ host: 'localhost', - port: 3000 + port: options.clients.port || 3000 })]; if (options.clients && options.clients.browserstack) { self.clients.push(new BrowserStackClient({ host: 'localhost', - port: 3000 + port: options.clients.port || 3000 })); } @@ -410,6 +415,7 @@ _.extend(Sandbox.prototype, { // }); testWithAllClients: function (f) { var self = this; + var argsArray = _.compact(_.toArray(arguments).slice(1)); if (self.clients.length) { console.log("running test with " + self.clients.length + " client(s)."); @@ -421,7 +427,7 @@ _.extend(Sandbox.prototype, { console.log("testing with " + client.name + "..."); f(new Run(self.execPath, { sandbox: self, - args: [], + args: argsArray, cwd: self.cwd, env: self._makeEnv(), fakeMongo: self.fakeMongo, @@ -528,7 +534,7 @@ _.extend(Sandbox.prototype, { }; self.write(to, contents); }, - + // Delete a file in the sandbox. 'filename' is as in write(). unlink: function (filename) { var self = this; @@ -725,7 +731,7 @@ _.extend(Sandbox.prototype, { // Insert into builds. Assume the package is available for all // architectures. stubCatalog.collections.builds.push({ - buildArchitectures: "browser+os", + buildArchitectures: "web.browser+os", versionId: versionRec._id, build: buildRec.build, _id: utils.randomToken() @@ -1566,5 +1572,6 @@ _.extend(exports, { fail: fail, expectEqual: expectEqual, expectThrows: expectThrows, - getToolsPackage: getToolsPackage + getToolsPackage: getToolsPackage, + execFileSync: execFileSync }); diff --git a/tools/tests/logs-mongo-auth.js b/tools/tests/logs-mongo-auth.js index 5aba7a3939..88751ad0d9 100644 --- a/tools/tests/logs-mongo-auth.js +++ b/tools/tests/logs-mongo-auth.js @@ -43,6 +43,7 @@ var logsOrMongoForApp = function (sandbox, command, appName, options) { run.waitSecs(commandTimeoutSecs); var expectSuccess = selftest.markStack(function () { + run.waitSecs(2); run.match(matchString); run.expectExit(0); }); diff --git a/tools/tests/old/test-bundler-assets.js b/tools/tests/old/test-bundler-assets.js index 75cf9620e7..e4c8d18177 100644 --- a/tools/tests/old/test-bundler-assets.js +++ b/tools/tests/old/test-bundler-assets.js @@ -48,7 +48,7 @@ var runTest = function () { }); var clientManifest = JSON.parse( fs.readFileSync( - path.join(tmpOutputDir, "programs", "client", "program.json") + path.join(tmpOutputDir, "programs", "web.browser", "program.json") ) ); @@ -59,7 +59,7 @@ var runTest = function () { return m.url === file[0]; }); assert(manifestItem); - var diskPath = path.join(tmpOutputDir, "programs", "client", + var diskPath = path.join(tmpOutputDir, "programs", "web.browser", manifestItem.path); assert(fs.existsSync(diskPath)); assert.strictEqual(fs.readFileSync(diskPath, "utf8"), file[1]); diff --git a/tools/tests/old/test-bundler-options.js b/tools/tests/old/test-bundler-options.js index 1c954296a6..1cc05b2325 100644 --- a/tools/tests/old/test-bundler-options.js +++ b/tools/tests/old/test-bundler-options.js @@ -37,7 +37,7 @@ var setAppDir = function (appDir) { var runTest = function () { var readManifest = function (tmpOutputDir) { return JSON.parse(fs.readFileSync( - path.join(tmpOutputDir, "programs", "client", "program.json"), + path.join(tmpOutputDir, "programs", "web.browser", "program.json"), "utf8")).manifest; }; diff --git a/tools/tropohouse.js b/tools/tropohouse.js index 937e9edc66..3cb7dd6469 100644 --- a/tools/tropohouse.js +++ b/tools/tropohouse.js @@ -116,7 +116,7 @@ _.extend(exports.Tropohouse.prototype, { throw e; } if (packageLinkTarget) { - // The symlink will be of the form '.VERSION.RANDOMTOKEN++browser+os', + // The symlink will be of the form '.VERSION.RANDOMTOKEN++web.browser+os', // so this strips off the part before the '++'. // XXX maybe we should just read the unipackage.json instead of // depending on the symlink? diff --git a/tools/unipackage.js b/tools/unipackage.js index 3ddafa6d4f..aaa66863a4 100644 --- a/tools/unipackage.js +++ b/tools/unipackage.js @@ -156,7 +156,7 @@ _.extend(Unibuild.prototype, { importStubServePath: isApp && '/packages/global-imports.js', prelinkFiles: self.prelinkFiles, packageVariables: self.packageVariables, - includeSourceMapInstructions: archinfo.matches(self.arch, "browser"), + includeSourceMapInstructions: archinfo.matches(self.arch, "web"), name: self.pkg.name || null }); @@ -307,7 +307,7 @@ _.extend(Unipackage.prototype, { _.pluck(self.unibuilds, 'arch').concat(self._toolArchitectures()) ).sort(); // Ensure that our buildArchitectures string does not look like - // browser+os+os.osx.x86_64 + // web+os+os.osx.x86_64 // This would happen if there is an 'os' unibuild but a platform-specific // tool (eg, in meteor-tool). This would confuse catalog.getBuildsForArches // into thinking that it would work for Linux, since the 'os' means @@ -338,7 +338,7 @@ _.extend(Unipackage.prototype, { }, // Return the unibuild of the package to use for a given target architecture - // (eg, 'os.linux.x86_64' or 'browser'), or throw an exception if that + // (eg, 'os.linux.x86_64' or 'web'), or throw an exception if that // packages can't be loaded under these circumstances. getUnibuildAtArch: function (arch) { var self = this;