diff --git a/gulpfile.js b/gulpfile.js index 60a644bc97..92d74ab86b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -12,7 +12,6 @@ var madge = require('madge'); var merge = require('merge'); var merge2 = require('merge2'); var path = require('path'); -var Q = require('q'); var gulpTraceur = require('./tools/transpiler/gulp-traceur'); var clean = require('./tools/build/clean'); @@ -35,11 +34,19 @@ var bundler = require('./tools/build/bundle'); var replace = require('gulp-replace'); var insert = require('gulp-insert'); -// dynamic require in build.broccoli.tools so we can bootstrap TypeScript compilation -function missingDynamicBroccoli() { - throw new Error('ERROR: build.broccoli.tools task should have been run before using broccoli'); + +// dynamic require in build.tools so we can bootstrap TypeScript compilation +function throwToolsBuildMissingError() { + throw new Error('ERROR: build.tools task should have been run before using angularBuilder'); } -var getBroccoli = missingDynamicBroccoli; + +var angularBuilder = { + rebuildBrowserDevTree: throwToolsBuildMissingError, + rebuildBrowserProdTree: throwToolsBuildMissingError, + rebuildNodeTree: throwToolsBuildMissingError, + rebuildDartTree: throwToolsBuildMissingError, + cleanup: function() {} +}; // Note: when DART_SDK is not found, all gulp tasks ending with `.dart` will be skipped. @@ -240,7 +247,7 @@ gulp.task('build/clean.docs', clean(gulp, gulpPlugins, { // transpile gulp.task('build/tree.dart', ['build.broccoli.tools'], function() { - return getBroccoli().forDartTree().buildOnce(); + return angularBuilder.rebuildDartTree(); }); // ------------ @@ -255,13 +262,6 @@ gulp.task('build/pubspec.dart', pubget.subDir(gulp, gulpPlugins, { command: DART_SDK.PUB })); -// ------------ -// linknodemodules - -gulp.task('build/linknodemodules.js.cjs', linknodemodules(gulp, gulpPlugins, { - dir: CONFIG.dest.js.cjs -})); - // ------------ // dartanalyzer @@ -464,44 +464,19 @@ gulp.task('test.unit.dart/ci', function (done) { karma.start({configFile: __dirname + '/karma-dart.conf.js', singleRun: true, reporters: ['dots'], browsers: getBrowsersFromCLI()}, done); }); -gulp.task('test.unit.cjs/ci', function () { - return gulp.src(CONFIG.test.js.cjs).pipe(jasmine({includeStackTrace: true, timeout: 1000})); -}); -gulp.task('test.unit.cjs', ['build.js.cjs'], function () { - //Run tests once - runSequence('test.unit.cjs/ci', function() {}); -}); -function runNodeJasmineTests() { - var doneDeferred = Q.defer(); - var jasmineProcess = fork('./tools/traceur-jasmine', ['dist/js/cjs/angular2/test/**/*_spec.js'], { + +gulp.task('test.unit.cjs/ci', function(done) { + fork('./tools/traceur-jasmine', ['dist/js/cjs/angular2/test/**/*_spec.js'], { stdio: 'inherit' + }).on('close', function (exitCode) { + done(exitCode); }); - - jasmineProcess.on('close', function (code) { - doneDeferred.resolve(); - }); - - return doneDeferred.promise; -} - -gulp.task('test.unit.cjs/ci', runNodeJasmineTests); - -gulp.task('test.unit.cjs', ['build.broccoli.tools'], function (done) { - //Run tests once - var nodeBroccoliBuilder = getBroccoli().forNodeTree(); +}); - nodeBroccoliBuilder.doBuild().then(function() { - gulp.start('build/linknodemodules.js.cjs'); - return runNodeJasmineTests(); - }).then(function() { - //Watcher to transpile file changed - gulp.watch('modules/**', function(event) { - console.log("fs changes detected", event); - nodeBroccoliBuilder.doBuild().then(runNodeJasmineTests); - }); - }); +gulp.task('test.unit.cjs', ['test.unit.cjs/ci'], function () { + gulp.watch('modules/**', ['test.unit.cjs/ci']); }); @@ -636,12 +611,9 @@ gulp.task('build.broccoli.tools', function() { }); gulp.task('broccoli.js.dev', ['build.broccoli.tools'], function() { - return getBroccoli().forDevTree().buildOnce(); + return angularBuilder.rebuildBrowserDevTree(); }); -gulp.task('broccoli.js.prod', ['build.broccoli.tools'], function() { - return getBroccoli().forProdTree().buildOnce(); -}); gulp.task('build.js.dev', function(done) { runSequence( @@ -652,19 +624,27 @@ gulp.task('build.js.dev', function(done) { ); }); -gulp.task('build.js.prod', ['broccoli.js.prod']); +gulp.task('build.js.prod', ['build.broccoli.tools'], function() { + return angularBuilder.rebuildBrowserProdTree(); +}); -gulp.task('broccoli.js.cjs', ['build.broccoli.tools'], function() { - return getBroccoli().forNodeTree().buildOnce(); -}); -gulp.task('build.js.cjs', function(done) { - runSequence( - 'broccoli.js.cjs', - 'build/linknodemodules.js.cjs', - done - ); + +var firstBuildJsCjs = true; + +gulp.task('build.js.cjs', ['build.broccoli.tools'], function() { + return angularBuilder.rebuildNodeTree().then(function() { + if (firstBuildJsCjs) { + firstBuildJsCjs = false; + console.log('creating node_modules symlink hack'); + // linknodemodules is all sync + linknodemodules(gulp, gulpPlugins, { + dir: CONFIG.dest.js.cjs + })(); + } + }); }); + var bundleConfig = { paths: { "*": "dist/js/prod/es6/*.es6", @@ -789,3 +769,20 @@ gulp.task('build/css.dart', function() { }); gulp.task('build.material', ['build.js.dev', 'build/css.js.dev']); + + +gulp.task('cleanup.builder', function() { + angularBuilder.cleanup(); +}); + + +// register cleanup listener for ctrl+c/kill used to quit any persistent task (autotest or serve tasks) +process.on('SIGINT', function() { + gulp.start('cleanup.builder'); + process.exit(); +}); + +// register cleanup listener for all non-persistent tasks +process.on('beforeExit', function() { + gulp.start('cleanup.builder'); +}); diff --git a/tools/broccoli/angular_builder.ts b/tools/broccoli/angular_builder.ts new file mode 100644 index 0000000000..dab5656735 --- /dev/null +++ b/tools/broccoli/angular_builder.ts @@ -0,0 +1,98 @@ +var broccoli = require('broccoli'); +var fse = require('fs-extra'); +var makeBrowserTree = require('./trees/browser_tree'); +var makeNodeTree = require('./trees/node_tree'); +var makeDartTree = require('./trees/dart_tree'); +var path = require('path'); +var printSlowTrees = require('broccoli-slow-trees'); +var Q = require('q'); + + +/** + * BroccoliBuilder facade for all of our build pipelines. + */ +export class AngularBuilder { + private nodeBuilder: BroccoliBuilder; + private browserDevBuilder: BroccoliBuilder; + private browserProdBuilder: BroccoliBuilder; + private dartBuilder: BroccoliBuilder; + + + constructor(private outputPath: string) {} + + + public rebuildBrowserDevTree(): Promise { + this.browserDevBuilder = this.browserDevBuilder || this.makeBrowserDevBuilder(); + return this.rebuild(this.browserDevBuilder); + } + + + public rebuildBrowserProdTree(): Promise { + this.browserProdBuilder = this.browserProdBuilder || this.makeBrowserProdBuilder(); + return this.rebuild(this.browserProdBuilder); + } + + + public rebuildNodeTree(): Promise { + this.nodeBuilder = this.nodeBuilder || this.makeNodeBuilder(); + return this.rebuild(this.nodeBuilder); + } + + + public rebuildDartTree(): Promise { + this.dartBuilder = this.dartBuilder || this.makeDartBuilder(); + return this.rebuild(this.dartBuilder); + } + + + cleanup(): Promise { + return Q.all([ + this.nodeBuilder && this.nodeBuilder.cleanup(), + this.browserDevBuilder && this.browserDevBuilder.cleanup(), + this.browserProdBuilder && this.browserProdBuilder.cleanup() + ]); + } + + + private makeBrowserDevBuilder(): BroccoliBuilder { + let tree = makeBrowserTree({name: 'dev', typeAssertions: true}, + path.join(this.outputPath, 'js', 'dev')); + return new broccoli.Builder(tree); + } + + + private makeBrowserProdBuilder(): BroccoliBuilder { + let tree = makeBrowserTree({name: 'prod', typeAssertions: false}, + path.join(this.outputPath, 'js', 'prod')); + return new broccoli.Builder(tree); + } + + + private makeNodeBuilder(): BroccoliBuilder { + let tree = makeNodeTree(path.join(this.outputPath, 'js', 'cjs')); + return new broccoli.Builder(tree); + } + + + private makeDartBuilder(): BroccoliBuilder { + let tree = makeDartTree(path.join(this.outputPath, 'dart')); + return new broccoli.Builder(tree); + } + + + private rebuild(builder) { + return builder.build() + .then((result) => { printSlowTrees(result.graph); }) + .catch((err) => { + console.error(err.toString()); + // Should show file and line/col if present + if (err.file) { + console.error('File: ' + err.file); + } + if (err.stack) { + console.error(err.stack); + } + throw err; + }); + } +} diff --git a/tools/broccoli/broccoli.d.ts b/tools/broccoli/broccoli.d.ts index 6eaa199def..dba123831a 100644 --- a/tools/broccoli/broccoli.d.ts +++ b/tools/broccoli/broccoli.d.ts @@ -73,7 +73,7 @@ interface BroccoliBuilder { * Cleans up the whole build tree by calling `.cleanup()` method on all trees that are part of the * pipeline. */ - cleanup(); + cleanup(): Promise; } diff --git a/tools/broccoli/broccoli_builder.ts b/tools/broccoli/broccoli_builder.ts deleted file mode 100644 index e332152744..0000000000 --- a/tools/broccoli/broccoli_builder.ts +++ /dev/null @@ -1,89 +0,0 @@ -var broccoli = require('broccoli'); -var destCopy = require('./broccoli-dest-copy'); -var fse = require('fs-extra'); -var makeBrowserTree = require('./trees/browser_tree'); -var makeNodeTree = require('./trees/node_tree'); -var makeDartTree = require('./trees/dart_tree'); -var path = require('path'); -var printSlowTrees = require('broccoli-slow-trees'); - -/** - * Helper for building with broccoli. - */ -export class BroccoliBuilder { - private builder; - private broccoliExecuted: {[s: string]: boolean} = {}; - - // Named constructors - static forDevTree(): BroccoliBuilder { - return new BroccoliBuilder(makeBrowserTree({name: 'dev', typeAssertions: true}), - path.join('js', 'dev')); - } - static forNodeTree(): BroccoliBuilder { - return new BroccoliBuilder(makeNodeTree(), path.join('js', 'cjs')); - } - static forProdTree(): BroccoliBuilder { - return new BroccoliBuilder(makeBrowserTree({name: 'prod', typeAssertions: false}), - path.join('js', 'prod')); - } - static forDartTree(): BroccoliBuilder { return new BroccoliBuilder(makeDartTree(), 'dart'); } - - constructor(tree, private outputRoot: string) { - if (this.broccoliExecuted[outputRoot]) { - throw new Error('The broccoli task can be called only once for outputRoot ' + outputRoot); - } - this.broccoliExecuted[outputRoot] = true; - - var distPath = path.join('dist', outputRoot); - - // We do a clean build every time to avoid stale outputs. - // Broccoli's cache folders allow it to remain incremental without reading this dir. - fse.removeSync(distPath); - - tree = destCopy(tree, 'dist'); - - this.builder = new broccoli.Builder(tree); - } - - doBuild(): Promise { - return this.builder.build() - .then(hash => - { - console.log('=== Stats for %s (total: %ds) ===', this.outputRoot, - Math.round(hash.totalTime / 1000000) / 1000); - printSlowTrees(hash.graph); - }) - .catch(err => { - console.log(err.toString()); - // Should show file and line/col if present - if (err.file) { - console.error('File: ' + err.file); - } - if (err.stack) { - console.error(err.stack); - } - throw err; - }); - } - - buildOnce(): Promise { - // es6-promise doesn't have finally() - return (this.doBuild()) - .finally(() => - { - this.time('Build cleanup', () => this.builder.cleanup()); - console.log('=== Done building %s ===', this.outputRoot); - }) - .catch(err => { - console.error('\nBuild failed'); - process.exit(1); - }); - } - - time(label, work) { - var start = Date.now(); - work(); - var duration = Date.now() - start; - console.log("%s: %dms", label, duration); - } -} diff --git a/tools/broccoli/trees/multi_copy.ts b/tools/broccoli/multi_copy.ts similarity index 93% rename from tools/broccoli/trees/multi_copy.ts rename to tools/broccoli/multi_copy.ts index e9d9cf272b..7d482c58f4 100644 --- a/tools/broccoli/trees/multi_copy.ts +++ b/tools/broccoli/multi_copy.ts @@ -1,5 +1,5 @@ -/// -/// +/// +/// import Writer = require('broccoli-writer'); import fs = require('fs'); import fsx = require('fs-extra'); diff --git a/tools/broccoli/trees/browser_tree.ts b/tools/broccoli/trees/browser_tree.ts index d66dbca67f..01fdd7310b 100644 --- a/tools/broccoli/trees/browser_tree.ts +++ b/tools/broccoli/trees/browser_tree.ts @@ -1,5 +1,6 @@ 'use strict'; +var destCopy = require('../broccoli-dest-copy'); var Funnel = require('broccoli-funnel'); var flatten = require('broccoli-flatten'); var htmlReplace = require('../html-replace'); @@ -10,10 +11,11 @@ var stew = require('broccoli-stew'); var ts2dart = require('../broccoli-ts2dart'); var traceurCompiler = require('../traceur'); + var projectRootDir = path.normalize(path.join(__dirname, '..', '..', '..')); -module.exports = function makeBrowserTree(options) { +module.exports = function makeBrowserTree(options, destinationPath) { var modulesTree = new Funnel( 'modules', {include: ['**/**'], exclude: ['**/*.cjs', 'benchmarks/e2e_test/**'], destDir: '/'}); @@ -113,8 +115,7 @@ module.exports = function makeBrowserTree(options) { es5Tree = mergeTrees([es5Tree, htmlTree]); - return mergeTrees([ - stew.mv(es6Tree, 'js/' + options.name + '/es6'), - stew.mv(es5Tree, 'js/' + options.name + '/es5') - ]); + var mergedTree = mergeTrees([stew.mv(es6Tree, '/es6'), stew.mv(es5Tree, '/es5')]); + + return destCopy(mergedTree, destinationPath); }; diff --git a/tools/broccoli/trees/dart_tree.ts b/tools/broccoli/trees/dart_tree.ts index 48f4386781..a891643a57 100644 --- a/tools/broccoli/trees/dart_tree.ts +++ b/tools/broccoli/trees/dart_tree.ts @@ -1,7 +1,8 @@ /// 'use strict'; -import {MultiCopy} from './multi_copy'; +import {MultiCopy} from './../multi_copy'; +var destCopy = require('../broccoli-dest-copy'); var Funnel = require('broccoli-funnel'); var glob = require('glob'); var mergeTrees = require('broccoli-merge-trees'); @@ -137,12 +138,11 @@ function getDocsTree() { return mergeTrees([licenses, mdTree, docs]); } -module.exports = function makeDartTree() { +module.exports = function makeDartTree(destinationPath) { var sourceTree = mergeTrees([getSourceTree(), getHtmlSourcesTree()]); sourceTree = fixDartFolderLayout(sourceTree); - var mergedResult = mergeTrees([sourceTree, getTemplatedPubspecsTree(), getDocsTree()]); + var dartTree = mergeTrees([sourceTree, getTemplatedPubspecsTree(), getDocsTree()]); - // Move the tree under the 'dart' folder. - return stew.mv(mergedResult, 'dart'); + return destCopy(dartTree, destinationPath); }; diff --git a/tools/broccoli/trees/node_tree.ts b/tools/broccoli/trees/node_tree.ts index 02b4524d52..772f8bcd56 100644 --- a/tools/broccoli/trees/node_tree.ts +++ b/tools/broccoli/trees/node_tree.ts @@ -1,5 +1,6 @@ 'use strict'; +var destCopy = require('../broccoli-dest-copy'); var Funnel = require('broccoli-funnel'); var mergeTrees = require('broccoli-merge-trees'); var path = require('path'); @@ -13,7 +14,7 @@ var TypescriptCompiler = require('../typescript'); var projectRootDir = path.normalize(path.join(__dirname, '..', '..', '..')); -module.exports = function makeNodeTree() { +module.exports = function makeNodeTree(destinationPath) { // list of npm packages that this build will create var outputPackages = ['angular2', 'benchpress', 'rtts_assert']; @@ -112,5 +113,5 @@ module.exports = function makeNodeTree() { nodeTree = mergeTrees([nodeTree, typescriptTree], { overwrite: true }); nodeTree = mergeTrees([nodeTree, docs, packageJsons]); - return stew.mv(nodeTree, 'js/cjs'); + return destCopy(nodeTree, destinationPath); };