/* Copyright 2016 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* jshint node:true */ /* globals target */ 'use strict'; var fs = require('fs'); var gulp = require('gulp'); var gutil = require('gulp-util'); var rimraf = require('rimraf'); var stream = require('stream'); var exec = require('child_process').exec; var streamqueue = require('streamqueue'); var BUILD_DIR = 'build/'; var L10N_DIR = 'l10n/'; var makeFile = require('./make.js'); var stripCommentHeaders = makeFile.stripCommentHeaders; var builder = makeFile.builder; var CONFIG_FILE = 'pdfjs.config'; var config = JSON.parse(fs.readFileSync(CONFIG_FILE).toString()); var DEFINES = { PRODUCTION: true, // The main build targets: GENERIC: false, FIREFOX: false, MOZCENTRAL: false, CHROME: false, MINIFIED: false, SINGLE_FILE: false, COMPONENTS: false }; function createStringSource(filename, content) { var source = stream.Readable({ objectMode: true }); source._read = function () { this.push(new gutil.File({ cwd: '', base: '', path: filename, contents: new Buffer(content) })); this.push(null); }; return source; } function stripUMDHeaders(content) { var reg = new RegExp( 'if \\(typeof define === \'function\' && define.amd\\) \\{[^}]*' + '\\} else if \\(typeof exports !== \'undefined\'\\) \\{[^}]*' + '\\} else ', 'g'); return content.replace(reg, ''); } function bundle(filename, outfilename, pathPrefix, initFiles, amdName, defines, isMainFile, versionInfo) { // Reading UMD headers and building loading orders of modules. The // readDependencies returns AMD module names: removing 'pdfjs' prefix and // adding '.js' extensions to the name. var umd = require('./external/umdutils/verifier.js'); initFiles = initFiles.map(function (p) { return pathPrefix + p; }); var files = umd.readDependencies(initFiles).loadOrder.map(function (name) { return pathPrefix + name.replace(/^[\w\-]+\//, '') + '.js'; }); var crlfchecker = require('./external/crlfchecker/crlfchecker.js'); crlfchecker.checkIfCrlfIsPresent(files); var bundleContent = files.map(function (file) { var content = fs.readFileSync(file); // Prepend a newline because stripCommentHeaders only strips comments that // follow a line feed. The file where bundleContent is inserted already // contains a license header, so the header of bundleContent can be removed. content = stripCommentHeaders('\n' + content); // Removes AMD and CommonJS branches from UMD headers. content = stripUMDHeaders(content); return content; }).join(''); var jsName = amdName.replace(/[\-_\.\/]\w/g, function (all) { return all[1].toUpperCase(); }); // Avoiding double processing of the bundle file. var templateContent = fs.readFileSync(filename).toString(); var tmpFile = outfilename + '.tmp'; fs.writeFileSync(tmpFile, templateContent.replace( /\/\/#expand\s+__BUNDLE__\s*\n/, function (all) { return bundleContent; })); bundleContent = null; templateContent = null; // This just preprocesses the empty pdf.js file, we don't actually want to // preprocess everything yet since other build targets use this file. builder.preprocess(tmpFile, outfilename, builder.merge(defines, { BUNDLE_VERSION: versionInfo.version, BUNDLE_BUILD: versionInfo.commit, BUNDLE_AMD_NAME: amdName, BUNDLE_JS_NAME: jsName, MAIN_FILE: isMainFile })); fs.unlinkSync(tmpFile); } function createBundle(defines) { var versionJSON = JSON.parse( fs.readFileSync(BUILD_DIR + 'version.json').toString()); console.log(); console.log('### Bundling files into pdf.js'); var mainFiles = [ 'display/global.js' ]; var workerFiles = [ 'core/worker.js' ]; var mainAMDName = 'pdfjs-dist/build/pdf'; var workerAMDName = 'pdfjs-dist/build/pdf.worker'; var mainOutputName = 'pdf.js'; var workerOutputName = 'pdf.worker.js'; // Extension does not need network.js file. if (!defines.FIREFOX && !defines.MOZCENTRAL) { workerFiles.push('core/network.js'); } if (defines.SINGLE_FILE) { // In singlefile mode, all of the src files will be bundled into // the main pdf.js output. mainFiles = mainFiles.concat(workerFiles); workerFiles = null; // no need for worker file mainAMDName = 'pdfjs-dist/build/pdf.combined'; workerAMDName = null; mainOutputName = 'pdf.combined.js'; workerOutputName = null; } var state = 'mainfile'; var source = stream.Readable({ objectMode: true }); source._read = function () { var tmpFile; switch (state) { case 'mainfile': // 'buildnumber' shall create BUILD_DIR for us tmpFile = BUILD_DIR + '~' + mainOutputName + '.tmp'; bundle('src/pdf.js', tmpFile, 'src/', mainFiles, mainAMDName, defines, true, versionJSON); this.push(new gutil.File({ cwd: '', base: '', path: mainOutputName, contents: fs.readFileSync(tmpFile) })); fs.unlinkSync(tmpFile); state = workerFiles ? 'workerfile' : 'stop'; break; case 'workerfile': // 'buildnumber' shall create BUILD_DIR for us tmpFile = BUILD_DIR + '~' + workerOutputName + '.tmp'; bundle('src/pdf.js', tmpFile, 'src/', workerFiles, workerAMDName, defines, false, versionJSON); this.push(new gutil.File({ cwd: '', base: '', path: workerOutputName, contents: fs.readFileSync(tmpFile) })); fs.unlinkSync(tmpFile); state = 'stop'; break; case 'stop': this.push(null); break; } }; return source; } function createWebBundle(defines) { var versionJSON = JSON.parse( fs.readFileSync(BUILD_DIR + 'version.json').toString()); var template, files, outputName, amdName; if (defines.COMPONENTS) { amdName = 'pdfjs-dist/web/pdf_viewer'; template = 'web/pdf_viewer.component.js'; files = [ 'pdf_viewer.js', 'pdf_history.js', 'pdf_find_controller.js', 'download_manager.js' ]; outputName = 'pdf_viewer.js'; } else { amdName = 'pdfjs-dist/web/viewer'; outputName = 'viewer.js'; template = 'web/viewer.js'; files = ['app.js']; if (defines.FIREFOX || defines.MOZCENTRAL) { files.push('firefoxcom.js'); } else if (defines.CHROME) { files.push('chromecom.js', 'mozPrintCallback_polyfill.js'); } else if (defines.GENERIC) { files.push('mozPrintCallback_polyfill.js'); } } var source = stream.Readable({ objectMode: true }); source._read = function () { // 'buildnumber' shall create BUILD_DIR for us var tmpFile = BUILD_DIR + '~' + outputName + '.tmp'; bundle(template, tmpFile, 'web/', files, amdName, defines, false, versionJSON); this.push(new gutil.File({ cwd: '', base: '', path: outputName, contents: fs.readFileSync(tmpFile) })); fs.unlinkSync(tmpFile); this.push(null); }; return source; } gulp.task('default', function() { console.log('Available tasks:'); var tasks = Object.keys(gulp.tasks); tasks.sort(); tasks.forEach(function (taskName) { console.log(' ' + taskName); }); }); gulp.task('buildnumber', function (done) { console.log(); console.log('### Getting extension build number'); exec('git log --format=oneline ' + config.baseVersion + '..', function (err, stdout, stderr) { var buildNumber = 0; if (!err) { // Build number is the number of commits since base version buildNumber = stdout ? stdout.match(/\n/g).length : 0; } console.log('Extension build number: ' + buildNumber); var version = config.versionPrefix + buildNumber; exec('git log --format="%h" -n 1', function (err, stdout, stderr) { var buildCommit = ''; if (!err) { buildCommit = stdout.replace('\n', ''); } createStringSource('version.json', JSON.stringify({ version: version, build: buildNumber, commit: buildCommit }, null, 2)) .pipe(gulp.dest(BUILD_DIR)) .on('end', done); }); }); }); gulp.task('bundle-firefox', ['buildnumber'], function () { var defines = builder.merge(DEFINES, {FIREFOX: true}); return streamqueue({ objectMode: true }, createBundle(defines), createWebBundle(defines)) .pipe(gulp.dest(BUILD_DIR)); }); gulp.task('bundle-mozcentral', ['buildnumber'], function () { var defines = builder.merge(DEFINES, {MOZCENTRAL: true}); return streamqueue({ objectMode: true }, createBundle(defines), createWebBundle(defines)) .pipe(gulp.dest(BUILD_DIR)); }); gulp.task('bundle-chromium', ['buildnumber'], function () { var defines = builder.merge(DEFINES, {CHROME: true}); return streamqueue({ objectMode: true }, createBundle(defines), createWebBundle(defines)) .pipe(gulp.dest(BUILD_DIR)); }); gulp.task('bundle-singlefile', ['buildnumber'], function () { var defines = builder.merge(DEFINES, {SINGLE_FILE: true}); return createBundle(defines).pipe(gulp.dest(BUILD_DIR)); }); gulp.task('bundle-generic', ['buildnumber'], function () { var defines = builder.merge(DEFINES, {GENERIC: true}); return streamqueue({ objectMode: true }, createBundle(defines), createWebBundle(defines)) .pipe(gulp.dest(BUILD_DIR)); }); gulp.task('bundle-minified', ['buildnumber'], function () { var defines = builder.merge(DEFINES, {MINIFIED: true, GENERIC: true}); return streamqueue({ objectMode: true }, createBundle(defines), createWebBundle(defines)) .pipe(gulp.dest(BUILD_DIR)); }); gulp.task('bundle-components', ['buildnumber'], function () { var defines = builder.merge(DEFINES, {COMPONENTS: true, GENERIC: true}); return createWebBundle(defines).pipe(gulp.dest(BUILD_DIR)); }); gulp.task('bundle', ['buildnumber'], function () { return createBundle(DEFINES).pipe(gulp.dest(BUILD_DIR)); }); gulp.task('server', function (done) { console.log(); console.log('### Starting local server'); var WebServer = require('./test/webserver.js').WebServer; var server = new WebServer(); server.port = 8888; server.start(); }); gulp.task('clean', function(callback) { console.log(); console.log('### Cleaning up project builds'); rimraf(BUILD_DIR, callback); }); gulp.task('makefile', function () { var makefileContent = 'help:\n\tgulp\n\n'; var targetsNames = []; for (var i in target) { makefileContent += i + ':\n\tgulp ' + i + '\n\n'; targetsNames.push(i); } makefileContent += '.PHONY: ' + targetsNames.join(' ') + '\n'; return createStringSource('Makefile', makefileContent) .pipe(gulp.dest('.')); }); gulp.task('importl10n', function(done) { var locales = require('./external/importL10n/locales.js'); console.log(); console.log('### Importing translations from mozilla-aurora'); if (!fs.existsSync(L10N_DIR)) { fs.mkdirSync(L10N_DIR); } locales.downloadL10n(L10N_DIR, done); }); // Getting all shelljs registered tasks and register them with gulp var gulpContext = false; for (var taskName in global.target) { if (taskName in gulp.tasks) { continue; } var task = (function (shellJsTask) { return function () { gulpContext = true; try { shellJsTask.call(global.target); } finally { gulpContext = false; } }; })(global.target[taskName]); gulp.task(taskName, task); } Object.keys(gulp.tasks).forEach(function (taskName) { var oldTask = global.target[taskName] || function () { gulp.run(taskName); }; global.target[taskName] = function (args) { // The require('shelljs/make') import in make.js will try to execute tasks // listed in arguments, guarding with gulpContext if (gulpContext) { oldTask.call(global.target, args); } }; });