Merge branch 'library-refactor-downloads' into library-refactor

This commit is contained in:
Emily Stark
2014-03-31 09:09:32 -07:00
4 changed files with 171 additions and 55 deletions

View File

@@ -1784,12 +1784,10 @@ exports.bundle = function (options) {
// Pick up any additional targets in /programs
// Step 1: scan for targets and make a list. We will reload if you create a
// new subdir in 'programs', or create 'programs' itself.
var programsDir = path.join(appDir, 'programs');
var programs = [];
var programsSubdirs = watch.readAndWatchDirectory(watchSet, {
absPath: programsDir,
include: [/\/$/],
exclude: [/^\./]
var programsDir = project.getProgramsDirectory(appDir);
var programsSubdirs = project.getProgramsSubdirs(appDir, {
watchSet: watchSet
});
_.each(programsSubdirs, function (item) {
@@ -1882,7 +1880,7 @@ exports.bundle = function (options) {
// it
var pkg = packageCache.packageCache.
loadPackageAtPath(p.name, p.loadPath);
loadPackageAtPath(p.name, p.path);
var target;
switch (p.type) {
case "server":

View File

@@ -189,7 +189,7 @@ main.registerCommand({
var allPackages = project.getIndirectDependencies(options.appDir);
var messages = buildmessage.capture(function () {
_.forEach(allPackages, function (version, name) {
_.forEach(allPackages, function (versions, name) {
// Calling getPackage on the loader will return a unipackage object, which
// means that the package will be compiled/downloaded. That we throw the
// package variable away afterwards is immaterial.
@@ -602,7 +602,6 @@ main.registerCommand({
failed = true;
return;
}
console.log(constraint);
// If the version was specified, check that the version exists.
if ( constraint.versionConstraint !== "none") {
@@ -617,16 +616,16 @@ main.registerCommand({
}
}
// Check that the constraint is new. If we are already using the package at
// the same constraint, return from this function.
if (_.has(packages, constraint.name) &&
constraint.versionConstraint === packages[constraint.name] ) {
// the same constraint in the app, return from this function.
if (_.has(packages.appDeps, constraint.name) &&
packages.appDeps[constraint.name] === constraint.versionConstraint) {
process.stderr.write(constraint.name + " with version constraint " +
constraint.versionConstraint + " has already been added.\n");
failed = true;
}
// Add the package to our direct dependency constraints that we get from .meteor/packages.
packages[constraint.name] = constraint.versionConstraint;
packages.appDeps[constraint.name] = constraint.versionConstraint;
});
// If the user asked for invalid packages, then the user probably expects a
@@ -644,9 +643,12 @@ main.registerCommand({
// Call the constraint solver.
var constraintSolver = require('./constraint-solver.js');
var resolver = new constraintSolver.Resolver;
// Combine into one object mapping package name to list of
// constraints, to pass in to the constraint solver.
var allPackages = project.combineAppAndProgramDependencies(packages);
// XXX: constraint solver currently ignores versions, but it should not.
// XXX: this would also be the place to add no-update options.
var newVersions = resolver.resolve(packages);
var newVersions = resolver.resolve(allPackages);
if ( ! newVersions) {
// XXX: Better error handling.
process.stderr.write("Cannot resolve package dependencies.");
@@ -663,31 +665,36 @@ main.registerCommand({
messageLog.push("removed dependency on " + packageName);
});
// Install the new versions.
// Install the new versions. If all new versions were installed
// successfully, then `setDependencies` also records dependency
// changes in the .meteor/versions file.
//
// Makes sure we have enough builds of the package downloaded such that
// we can load a browser slice and a slice that will run on this
// system. (Later we may also need to download more builds to be able to
// deploy to another architecture.)
var downloaded = project.setDependencies(
options.appDir,
packages.appDeps,
newVersions
);
_.each(newVersions, function(version, packageName) {
if ( failed )
if (failed)
return;
if (_.has(versions, packageName) &&
versions[packageName] == version ) {
versions[packageName] === version) {
// Nothing changed. Skip this.
return;
}
}
// Make sure we have enough builds of the package downloaded such that
// we can load a browser slice and a slice that will run on this
// system. (Later we may also need to download more builds to be able to
// deploy to another architecture.)
var available = tropohouse.maybeDownloadPackageForArchitectures(
catalog.getVersion(packageName, version),
// XXX we also download the deploy arch now, because we don't run the
// constraint solver / downloader anywhere other than add-package yet.
['browser', archinfo.host(), XXX_DEPLOY_ARCH]);
if (! available) {
if (! downloaded[packageName] || downloaded[packageName] !== version) {
// XXX maybe we shouldn't be letting the constraint solver choose
// things that don't have the right arches?
process.stderr.write("Package " + packageName +
" has no compatible build for version " +
version);
version + "\n");
failed = true;
return;
}
@@ -700,7 +707,7 @@ main.registerCommand({
// If the previous versions file had this, then we are upgrading, if it did
// not, then we must be adding this package anew.
if ( _.has(versions, packageName )) {
if (_.has(versions, packageName)) {
messageLog.push("upgraded " + packageName + " from version " +
versions[packageName] +
" to version " + newVersions[packageName]);
@@ -713,9 +720,6 @@ main.registerCommand({
if (failed)
return 1;
// Record dependency changes.
project.rewriteDependencies(options.appDir, packages, newVersions);
// Show the user the messageLog of packages we added.
_.each(messageLog, function (msg) {
process.stdout.write(msg + "\n");
@@ -762,12 +766,12 @@ main.registerCommand({
_.each(options.args, function (packageName) {
// Check that we are using the package. We don't check if the package
// exists. You should be able to remove non-existent packages.
if (! _.has(packages, packageName)) {
if (! _.has(packages.appDeps, packageName)) {
process.stderr.write( packageName + " is not in this project \n");
}
// Remove the package from our dependency list.
delete packages[packageName];
delete packages.appDeps[packageName];
});
// Get the contents of our versions file. We need to pass them to the
@@ -780,7 +784,8 @@ main.registerCommand({
var resolver = new constraintSolver.Resolver;
// XXX: constraint solver currently ignores versions, but it should not.
// XXX: this would also be the place to add no-update options.
var newVersions = resolver.resolve(packages);
var newVersions = resolver.resolve(
project.combineAppAndProgramDependencies(packages));
if ( ! newVersions) {
// This should never really happen.
process.stderr.write("Cannot resolve package dependencies.");
@@ -798,7 +803,7 @@ main.registerCommand({
});
// Write the dependency files with the right versions
project.rewriteDependencies(options.appDir, packages, newVersions);
project.setDependencies(options.appDir, packages.appDeps, newVersions);
// Show the user the messageLog of everything we removed.
_.each(messageLog, function (msg) {
@@ -831,7 +836,7 @@ main.registerCommand({
var items = [];
if (options.using) {
var packages = project.getDirectDependencies(options.appDir);
_.each(packages, function (version, name) {
_.each(packages.appDeps, function (version, name) {
var versionInfo = catalog.getVersion(name, version);
if (!versionInfo) {
process.stderr.write("Cannot process package list. Unknown: " + name +
@@ -1362,16 +1367,9 @@ main.registerCommand({
// The catalog doesn't know about other programs in your app. Let's blow
// away their .build directories if they have them, and not rebuild
// them. Sort of hacky, but eh.
var programsDir = path.join(options.appDir, 'programs');
try {
var programs = fs.readdirSync(programsDir);
} catch (e) {
// OK if the programs directory doesn't exist; that'll just leave
// 'programs' empty.
if (e.code !== "ENOENT")
throw e;
}
_.each(programs, function (program) {
var programsDir = project.getProgramsDirectory(options.appDir);
var programsSubdirs = project.getProgramsSubdirs(options.appDir);
_.each(programsSubdirs, function (program) {
files.rm_recursive(path.join(programsDir, program, '.build'));
});
}

View File

@@ -28,7 +28,7 @@ var deepClone = function (v) {
// For some reason, _.map doesn't work in this context on Opera (weird test
// failures).
ret = [];
for (i = 0; i < v.length; i++)
for (var i = 0; i < v.length; i++)
ret[i] = deepClone(v[i]);
return ret;
}
@@ -244,7 +244,8 @@ var rejectExactDeps = function (deps) { return _.reject(deps, isExact); };
// converts dependencies from simple format to the structured format
var toStructuredDeps = function (dependencies) {
var structuredDeps = [];
_.each(dependencies, function (details, packageName) {
var addStructuredDep = function (packageName, details) {
// if details is null, it means 'no constraint'
if (typeof details === "string" || details === null) {
structuredDeps.push(_.extend(
@@ -253,6 +254,16 @@ var toStructuredDeps = function (dependencies) {
} else {
structuredDeps.push(_.extend({ packageName: packageName }, details));
}
};
_.each(dependencies, function (details, packageName) {
if (_.isArray(details)) {
_.each(details, function (constraint) {
addStructuredDep(packageName, constraint);
});
} else {
addStructuredDep(packageName, details);
}
});
return structuredDeps;
};

View File

@@ -3,6 +3,9 @@ var path = require('path');
var _ = require('underscore');
var files = require('./files.js');
var utils = require('./utils.js');
var tropohouse = require('./tropohouse.js');
var archinfo = require('./archinfo.js');
var watch = require('./watch.js');
var project = exports;
@@ -74,9 +77,64 @@ project.processPerConstraintLines = function(lines) {
};
// Read in the .meteor/packages file.
project.getProgramsDirectory = function (appDir) {
return path.join(appDir, "programs");
};
// Return the list of subdirectories containing programs in the
// app. Options can include:
// - watchSet: if provided, the app's programs directory will be added to it
project.getProgramsSubdirs = function (appDir, options) {
options = options || {};
var programsDir = project.getProgramsDirectory(appDir);
var readOptions = {
absPath: programsDir,
include: [/\/$/],
exclude: [/^\./]
};
if (options.watchSet) {
return watch.readAndWatchDirectory(options.watchSet, readOptions);
} else {
return watch.readDirectory(readOptions);
}
};
// Read direct dependencies from the .meteor/packages file and from
// programs in this app.
//
// Returns an object with keys:
// - appDeps: object mapping package names to version constraints
// - programsDeps: an object mapping program name to program deps,
// where program deps is an object mapping package names to version
// constraints.
project.getDirectDependencies = function(appDir) {
return project.processPerConstraintLines(getPackagesLines(appDir));
var appDeps = project.processPerConstraintLines(getPackagesLines(appDir));
var programsDeps = {};
var programsSubdirs = project.getProgramsSubdirs(appDir);
var PackageSource;
_.each(programsSubdirs, function (item) {
if (! PackageSource) {
PackageSource = require('./package-source.js');
}
var programName = item.substr(0, item.length - 1);
programsDeps[programName] = {};
var programSubdir = path.join(project.getProgramsDirectory(appDir), item);
var programSource = new PackageSource(programSubdir);
programSource.initFromPackageDir(programName, programSubdir);
_.each(programSource.slices, function (sourceSlice) {
_.each(sourceSlice.uses, function (use) {
programsDeps[programName][use["package"]] = use.constraint || "none";
});
});
});
return {
appDeps: appDeps,
programsDeps: programsDeps
};
};
// Get a list of constraints from the .meteor/versions file.
@@ -85,7 +143,7 @@ project.getIndirectDependencies = function(appDir) {
};
// Write the .meteor/versions file after running the constraint solver.
project.rewriteDependencies = function (appDir, deps, versions) {
var rewriteDependencies = function (appDir, deps, versions) {
// Rewrite the packages file. Do this first, since the versions file is
// derived from the packages file.
@@ -112,10 +170,58 @@ project.rewriteDependencies = function (appDir, deps, versions) {
lines.join(''), 'utf8');
};
// Call this after running the constraint solver. Downloads the
// necessary package builds and writes the .meteor/versions and
// .meteor/packages files with the results of the constraint solver.
//
// Only writes to .meteor/versions if all the requested versions were
// available from the package server.
//
// Returns an object whose keys are package names and values are
// versions that were successfully downloaded.
project.setDependencies = function (appDir, deps, versions) {
var downloadedPackages = {};
_.each(versions, function (version, name) {
var packageVersionInfo = { packageName: name, version: version };
// XXX error handling
var available = tropohouse.maybeDownloadPackageForArchitectures(
packageVersionInfo,
['browser', archinfo.host()]
);
if (available) {
downloadedPackages[name] = version;
}
});
if (_.keys(downloadedPackages).length === _.keys(versions).length) {
rewriteDependencies(appDir, deps, versions);
}
return downloadedPackages;
};
var meteorReleaseFilePath = function (appDir) {
return path.join(appDir, '.meteor', 'release');
};
// Helper function. Given an object `deps` as returned from
// `getDirectDependencies`, combine all the direct dependencies (for the
// app and its programs) into a single object mapping package name to a
// list of version constraints, which can be passed into the constraint
// solver.
project.combineAppAndProgramDependencies = function (deps) {
var allDeps = {};
_.each(deps.appDeps, function (constraint, packageName) {
allDeps[packageName] = [constraint];
});
_.each(deps.programsDeps, function (deps, programName) {
_.each(deps, function (constraint, packageName) {
allDeps[packageName] = allDeps[packageName] || [];
allDeps[packageName].push(constraint);
});
});
return allDeps;
};
// Run the constraint solver to determine the package versions to use.
//
@@ -127,20 +233,23 @@ project.generatePackageLoader = function (appDir) {
var versions = project.getIndirectDependencies(appDir);
var packages = project.getDirectDependencies(appDir);
// package name -> list of version constraints
var allPackages = project.combineAppAndProgramDependencies(packages);
// XXX: We are manually adding ctl here, but we should do this in a more
// principled manner.
var constraintSolver = require('./constraint-solver.js');
var resolver = new constraintSolver.Resolver;
// XXX: constraint solver currently ignores versions, but it should not.
var newVersions = resolver.resolve(
_.extend(packages, { "ctl" : "none" }));
_.extend(allPackages, { "ctl" : ["none"] }));
if ( ! newVersions) {
return { outcome: 'conflicting-versions' };
}
// Write out the new versions file.
// Download any necessary package builds and write out the new versions file.
delete packages["ctl"];
project.rewriteDependencies(appDir, packages, newVersions);
project.setDependencies(appDir, packages.appDeps, newVersions);
var newVersionsReform = {};
_.each(newVersions, function (version, name) {