/* 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. */ /* eslint-env node */ /* globals target */ 'use strict'; var fs = require('fs'); var gulp = require('gulp'); var gutil = require('gulp-util'); var rename = require('gulp-rename'); var mkdirp = require('mkdirp'); var rimraf = require('rimraf'); var runSequence = require('run-sequence'); var stream = require('stream'); var exec = require('child_process').exec; var spawn = require('child_process').spawn; var streamqueue = require('streamqueue'); var merge = require('merge-stream'); var zip = require('gulp-zip'); var BUILD_DIR = 'build/'; var JSDOC_DIR = 'jsdoc/'; var L10N_DIR = 'l10n/'; var TEST_DIR = 'test/'; var EXTENSION_SRC_DIR = 'extensions/'; var GENERIC_DIR = BUILD_DIR + 'generic/'; var COMPONENTS_DIR = BUILD_DIR + 'components/'; var SINGLE_FILE_DIR = BUILD_DIR + 'singlefile/'; var MINIFIED_DIR = BUILD_DIR + 'minified/'; var FIREFOX_BUILD_DIR = BUILD_DIR + 'firefox/'; var COMMON_WEB_FILES = [ 'web/images/*.{png,svg,gif,cur}', 'web/debugger.js' ]; var builder = require('./external/builder/builder.js'); 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 stripCommentHeaders(content) { var notEndOfComment = '(?:[^*]|\\*(?!/))+'; var reg = new RegExp( '\n/\\* Copyright' + notEndOfComment + '\\*/\\s*' + '(?:/\\*' + notEndOfComment + '\\*/\\s*|//(?!#).*\n\\s*)*' + '\\s*\'use strict\';', 'g'); content = content.replace(reg, ''); return content; } function checkChromePreferencesFile(chromePrefsPath, webPrefsPath) { var chromePrefs = JSON.parse(fs.readFileSync(chromePrefsPath).toString()); var chromePrefsKeys = Object.keys(chromePrefs.properties); chromePrefsKeys.sort(); var webPrefs = JSON.parse(fs.readFileSync(webPrefsPath).toString()); var webPrefsKeys = Object.keys(webPrefs); webPrefsKeys.sort(); var telemetryIndex = chromePrefsKeys.indexOf('disableTelemetry'); if (telemetryIndex >= 0) { chromePrefsKeys.splice(telemetryIndex, 1); } else { console.log('Warning: disableTelemetry key not found in chrome prefs!'); return false; } if (webPrefsKeys.length !== chromePrefsKeys.length) { return false; } return webPrefsKeys.every(function (value, index) { return chromePrefsKeys[index] === value && chromePrefs.properties[value].default === webPrefs[value]; }); } 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(); }); var p2 = require('./external/builder/preprocessor2.js'); var ctx = { rootPath: __dirname, saveComments: 'copyright', defines: builder.merge(defines, { BUNDLE_VERSION: versionInfo.version, BUNDLE_BUILD: versionInfo.commit, BUNDLE_AMD_NAME: amdName, BUNDLE_JS_NAME: jsName, MAIN_FILE: isMainFile }) }; var templateContent = fs.readFileSync(filename).toString(); templateContent = templateContent.replace( /\/\/#expand\s+__BUNDLE__\s*\n/, function (all) { return bundleContent; }); bundleContent = null; templateContent = p2.preprocessPDFJSCode(ctx, templateContent); fs.writeFileSync(outfilename, templateContent); templateContent = null; } 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', 'firefox_print_service.js'); } else if (defines.CHROME) { files.push('chromecom.js', 'pdf_print_service.js'); } else if (defines.GENERIC) { files.push('pdf_print_service.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; } function checkFile(path) { try { var stat = fs.lstatSync(path); return stat.isFile(); } catch (e) { return false; } } function checkDir(path) { try { var stat = fs.lstatSync(path); return stat.isDirectory(); } catch (e) { return false; } } function getTempFile(prefix, suffix) { mkdirp.sync(BUILD_DIR + 'tmp/'); var bytes = require('crypto').randomBytes(6).toString('hex'); var path = BUILD_DIR + 'tmp/' + prefix + bytes + suffix; fs.writeFileSync(path, ''); return path; } function createTestSource(testsName) { var source = stream.Readable({ objectMode: true }); source._read = function () { console.log(); console.log('### Running ' + testsName + ' tests'); var PDF_TEST = process.env['PDF_TEST'] || 'test_manifest.json'; var PDF_BROWSERS = process.env['PDF_BROWSERS'] || 'resources/browser_manifests/browser_manifest.json'; if (!checkFile('test/' + PDF_BROWSERS)) { console.log('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.'); console.log('Copy and adjust the example in ' + 'test/resources/browser_manifests.'); this.emit('error', new Error('Missing manifest file')); return null; } var args = ['test.js']; switch (testsName) { case 'browser': args.push('--reftest', '--manifestFile=' + PDF_TEST); break; case 'browser (no reftest)': args.push('--manifestFile=' + PDF_TEST); break; case 'unit': args.push('--unitTest'); break; case 'font': args.push('--fontTest'); break; default: this.emit('error', new Error('Unknown name: ' + testsName)); return null; } args.push('--browserManifestFile=' + PDF_BROWSERS); var testProcess = spawn('node', args, {cwd: TEST_DIR, stdio: 'inherit'}); testProcess.on('close', function (code) { source.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('extension', function (done) { console.log(); console.log('### Building extensions'); runSequence('locale', 'firefox', 'chromium', done); }); 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('locale', function () { var VIEWER_LOCALE_OUTPUT = 'web/locale/'; var METADATA_OUTPUT = 'extensions/firefox/'; var EXTENSION_LOCALE_OUTPUT = 'extensions/firefox/locale/'; console.log(); console.log('### Building localization files'); rimraf.sync(EXTENSION_LOCALE_OUTPUT); mkdirp.sync(EXTENSION_LOCALE_OUTPUT); rimraf.sync(VIEWER_LOCALE_OUTPUT); mkdirp.sync(VIEWER_LOCALE_OUTPUT); var subfolders = fs.readdirSync(L10N_DIR); subfolders.sort(); var metadataContent = ''; var chromeManifestContent = ''; var viewerOutput = ''; var locales = []; for (var i = 0; i < subfolders.length; i++) { var locale = subfolders[i]; var path = L10N_DIR + locale; if (!checkDir(path)) { continue; } if (!/^[a-z][a-z]([a-z])?(-[A-Z][A-Z])?$/.test(locale)) { console.log('Skipping invalid locale: ' + locale); continue; } mkdirp.sync(EXTENSION_LOCALE_OUTPUT + '/' + locale); mkdirp.sync(VIEWER_LOCALE_OUTPUT + '/' + locale); locales.push(locale); chromeManifestContent += 'locale pdf.js ' + locale + ' locale/' + locale + '/\n'; if (checkFile(path + '/viewer.properties')) { viewerOutput += '[' + locale + ']\n' + '@import url(' + locale + '/viewer.properties)\n\n'; } if (checkFile(path + '/metadata.inc')) { var metadata = fs.readFileSync(path + '/metadata.inc').toString(); metadataContent += metadata; } } return merge([ createStringSource('metadata.inc', metadataContent) .pipe(gulp.dest(METADATA_OUTPUT)), createStringSource('chrome.manifest.inc', chromeManifestContent) .pipe(gulp.dest(METADATA_OUTPUT)), gulp.src(L10N_DIR + '/{' + locales.join(',') + '}' + '/{viewer,chrome}.properties', {base: L10N_DIR}) .pipe(gulp.dest(EXTENSION_LOCALE_OUTPUT)), createStringSource('locale.properties', viewerOutput) .pipe(gulp.dest(VIEWER_LOCALE_OUTPUT)), gulp.src(L10N_DIR + '/{' + locales.join(',') + '}' + '/viewer.properties', {base: L10N_DIR}) .pipe(gulp.dest(VIEWER_LOCALE_OUTPUT)) ]); }); gulp.task('cmaps', function () { var CMAP_INPUT = 'external/cmaps'; var VIEWER_CMAP_OUTPUT = 'external/bcmaps'; console.log(); console.log('### Building cmaps'); // Testing a file that usually present. if (!checkFile(CMAP_INPUT + '/UniJIS-UCS2-H')) { console.log('./external/cmaps has no cmap files, download them from:'); console.log(' https://github.com/adobe-type-tools/cmap-resources'); throw new Error('cmap files were not found'); } // Remove old bcmap files. fs.readdirSync(VIEWER_CMAP_OUTPUT).forEach(function (file) { if (/\.bcmap$/i.test(file)) { fs.unlinkSync(VIEWER_CMAP_OUTPUT + '/' + file); } }); var compressCmaps = require('./external/cmapscompress/compress.js').compressCmaps; compressCmaps(CMAP_INPUT, VIEWER_CMAP_OUTPUT, true); }); gulp.task('bundle', ['buildnumber'], function () { return createBundle(DEFINES).pipe(gulp.dest(BUILD_DIR)); }); function preprocessCSS(source, mode, defines, cleanup) { var outName = getTempFile('~preprocess', '.css'); var setup = { defines: defines, copy: [], preprocess: [], preprocessCSS: [ [mode, source, outName] ] }; builder.build(setup); var out = fs.readFileSync(outName).toString(); fs.unlinkSync(outName); if (cleanup) { // Strip out all license headers in the middle. var reg = /\n\/\* Copyright(.|\n)*?Mozilla Foundation(.|\n)*?\*\//g; out = out.replace(reg, ''); } var i = source.lastIndexOf('/'); return createStringSource(source.substr(i + 1), out); } function preprocessHTML(source, defines) { var outName = getTempFile('~preprocess', '.html'); var setup = { defines: defines, copy: [], preprocess: [ [source, outName] ], preprocessCSS: [] }; builder.build(setup); var out = fs.readFileSync(outName).toString(); fs.unlinkSync(outName); var i = source.lastIndexOf('/'); return createStringSource(source.substr(i + 1), out); } function preprocessJS(source, defines, cleanup) { var outName = getTempFile('~preprocess', '.js'); var setup = { defines: defines, copy: [], preprocess: [ [source, outName] ], preprocessCSS: [] }; builder.build(setup); var out = fs.readFileSync(outName).toString(); fs.unlinkSync(outName); if (cleanup) { out = stripCommentHeaders(out); } var i = source.lastIndexOf('/'); return createStringSource(source.substr(i + 1), out); } // Builds the generic production viewer that should be compatible with most // modern HTML5 browsers. gulp.task('generic', ['buildnumber', 'locale'], function () { console.log(); console.log('### Creating generic viewer'); var defines = builder.merge(DEFINES, {GENERIC: true}); rimraf.sync(GENERIC_DIR); return merge([ createBundle(defines).pipe(gulp.dest(GENERIC_DIR + 'build')), createWebBundle(defines).pipe(gulp.dest(GENERIC_DIR + 'web')), gulp.src(COMMON_WEB_FILES, {base: 'web/'}) .pipe(gulp.dest(GENERIC_DIR + 'web')), gulp.src('LICENSE').pipe(gulp.dest(GENERIC_DIR)), gulp.src([ 'external/webL10n/l10n.js', 'web/compatibility.js' ]).pipe(gulp.dest(GENERIC_DIR + 'web')), gulp.src([ 'web/locale/*/viewer.properties', 'web/locale/locale.properties' ], {base: 'web/'}).pipe(gulp.dest(GENERIC_DIR + 'web')), gulp.src(['external/bcmaps/*.bcmap', 'external/bcmaps/LICENSE'], {base: 'external/bcmaps'}) .pipe(gulp.dest(GENERIC_DIR + 'web/cmaps')), preprocessHTML('web/viewer.html', defines) .pipe(gulp.dest(GENERIC_DIR + 'web')), preprocessCSS('web/viewer.css', 'generic', defines, true) .pipe(gulp.dest(GENERIC_DIR + 'web')), gulp.src('web/compressed.tracemonkey-pldi-09.pdf') .pipe(gulp.dest(GENERIC_DIR + 'web')), ]); }); gulp.task('components', ['buildnumber'], function () { console.log(); console.log('### Creating generic components'); var defines = builder.merge(DEFINES, {COMPONENTS: true, GENERIC: true}); rimraf.sync(COMPONENTS_DIR); var COMPONENTS_IMAGES = [ 'web/images/annotation-*.svg', 'web/images/loading-icon.gif', 'web/images/shadow.png', 'web/images/texture.png', ]; return merge([ createWebBundle(defines).pipe(gulp.dest(COMPONENTS_DIR)), gulp.src(COMPONENTS_IMAGES).pipe(gulp.dest(COMPONENTS_DIR + 'images')), gulp.src('web/compatibility.js').pipe(gulp.dest(COMPONENTS_DIR)), preprocessCSS('web/pdf_viewer.css', 'components', defines, true) .pipe(gulp.dest(COMPONENTS_DIR)), ]); }); gulp.task('singlefile', ['buildnumber'], function () { console.log(); console.log('### Creating singlefile build'); var defines = builder.merge(DEFINES, {SINGLE_FILE: true}); var SINGLE_FILE_BUILD_DIR = SINGLE_FILE_DIR + 'build/'; rimraf.sync(SINGLE_FILE_DIR); return createBundle(defines).pipe(gulp.dest(SINGLE_FILE_BUILD_DIR)); }); gulp.task('minified-pre', ['buildnumber', 'locale'], function () { console.log(); console.log('### Creating minified viewer'); var defines = builder.merge(DEFINES, {MINIFIED: true, GENERIC: true}); rimraf.sync(MINIFIED_DIR); return merge([ createBundle(defines).pipe(gulp.dest(MINIFIED_DIR + 'build')), createWebBundle(defines).pipe(gulp.dest(MINIFIED_DIR + 'web')), gulp.src(COMMON_WEB_FILES, {base: 'web/'}) .pipe(gulp.dest(MINIFIED_DIR + 'web')), gulp.src([ 'web/locale/*/viewer.properties', 'web/locale/locale.properties' ], {base: 'web/'}).pipe(gulp.dest(MINIFIED_DIR + 'web')), gulp.src(['external/bcmaps/*.bcmap', 'external/bcmaps/LICENSE'], {base: 'external/bcmaps'}) .pipe(gulp.dest(MINIFIED_DIR + 'web/cmaps')), preprocessHTML('web/viewer.html', defines) .pipe(gulp.dest(MINIFIED_DIR + 'web')), preprocessCSS('web/viewer.css', 'minified', defines, true) .pipe(gulp.dest(MINIFIED_DIR + 'web')), gulp.src('web/compressed.tracemonkey-pldi-09.pdf') .pipe(gulp.dest(MINIFIED_DIR + 'web')), ]); }); gulp.task('firefox-pre', ['buildnumber', 'locale'], function () { console.log(); console.log('### Building Firefox extension'); var defines = builder.merge(DEFINES, {FIREFOX: true}); var FIREFOX_BUILD_CONTENT_DIR = FIREFOX_BUILD_DIR + '/content/', FIREFOX_EXTENSION_DIR = 'extensions/firefox/', FIREFOX_CONTENT_DIR = EXTENSION_SRC_DIR + '/firefox/content/'; // Clear out everything in the firefox extension build directory rimraf.sync(FIREFOX_BUILD_DIR); return merge([ createBundle(defines).pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR + 'build')), createWebBundle(defines).pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR + 'web')), gulp.src(COMMON_WEB_FILES, {base: 'web/'}) .pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR + 'web')), gulp.src(FIREFOX_EXTENSION_DIR + 'locale/**/*.properties', {base: FIREFOX_EXTENSION_DIR}) .pipe(gulp.dest(FIREFOX_BUILD_DIR)), gulp.src(['external/bcmaps/*.bcmap', 'external/bcmaps/LICENSE'], {base: 'external/bcmaps'}) .pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR + 'web/cmaps')), preprocessHTML('web/viewer.html', defines) .pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR + 'web')), preprocessCSS('web/viewer.css', 'firefox', defines, true) .pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR + 'web')), gulp.src(FIREFOX_CONTENT_DIR + 'PdfJs-stub.jsm') .pipe(rename('PdfJs.jsm')) .pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR)), gulp.src(FIREFOX_CONTENT_DIR + 'PdfJsTelemetry-addon.jsm') .pipe(rename('PdfJsTelemetry.jsm')) .pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR)), gulp.src(FIREFOX_EXTENSION_DIR + '**/*.{rdf,png,manifest}', {base: FIREFOX_EXTENSION_DIR}) .pipe(gulp.dest(FIREFOX_BUILD_DIR)), gulp.src(FIREFOX_EXTENSION_DIR + 'chrome/content.js', {base: FIREFOX_EXTENSION_DIR}) .pipe(gulp.dest(FIREFOX_BUILD_DIR)), gulp.src('LICENSE').pipe(gulp.dest(FIREFOX_BUILD_DIR)), gulp.src(FIREFOX_EXTENSION_DIR + 'tools/l10n.js') .pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR + '/web')), preprocessJS(FIREFOX_CONTENT_DIR + 'PdfStreamConverter.jsm', defines, true) .pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR)), preprocessJS(FIREFOX_CONTENT_DIR + 'PdfJsNetwork.jsm', defines, true) .pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR)), preprocessJS(FIREFOX_CONTENT_DIR + 'PdfjsContentUtils.jsm', defines, true) .pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR)), preprocessJS(FIREFOX_CONTENT_DIR + 'PdfjsChromeUtils.jsm', defines, true) .pipe(gulp.dest(FIREFOX_BUILD_CONTENT_DIR)), preprocessJS(FIREFOX_EXTENSION_DIR + 'bootstrap.js', defines, true) .pipe(gulp.dest(FIREFOX_BUILD_DIR)), ]); }); gulp.task('mozcentral-pre', ['buildnumber', 'locale'], function () { console.log(); console.log('### Building mozilla-central extension'); var defines = builder.merge(DEFINES, {MOZCENTRAL: true}); var MOZCENTRAL_DIR = BUILD_DIR + 'mozcentral/', MOZCENTRAL_EXTENSION_DIR = MOZCENTRAL_DIR + 'browser/extensions/pdfjs/', MOZCENTRAL_CONTENT_DIR = MOZCENTRAL_EXTENSION_DIR + 'content/', FIREFOX_EXTENSION_DIR = 'extensions/firefox/', MOZCENTRAL_L10N_DIR = MOZCENTRAL_DIR + 'browser/locales/en-US/pdfviewer/', FIREFOX_CONTENT_DIR = EXTENSION_SRC_DIR + '/firefox/content/'; // Clear out everything in the firefox extension build directory rimraf.sync(MOZCENTRAL_DIR); return merge([ createBundle(defines).pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + 'build')), createWebBundle(defines).pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + 'web')), gulp.src(COMMON_WEB_FILES, {base: 'web/'}) .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + 'web')), gulp.src(['external/bcmaps/*.bcmap', 'external/bcmaps/LICENSE'], {base: 'external/bcmaps'}) .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + 'web/cmaps')), preprocessHTML('web/viewer.html', defines) .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + 'web')), preprocessCSS('web/viewer.css', 'mozcentral', defines, true) .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + 'web')), gulp.src(FIREFOX_CONTENT_DIR + 'PdfJsTelemetry.jsm') .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR)), gulp.src(FIREFOX_EXTENSION_DIR + 'chrome-mozcentral.manifest') .pipe(rename('chrome.manifest')) .pipe(gulp.dest(MOZCENTRAL_EXTENSION_DIR)), gulp.src(FIREFOX_EXTENSION_DIR + 'locale/en-US/*.properties', {base: FIREFOX_EXTENSION_DIR}) .pipe(gulp.dest(MOZCENTRAL_L10N_DIR)), gulp.src(FIREFOX_EXTENSION_DIR + 'README.mozilla') .pipe(gulp.dest(MOZCENTRAL_EXTENSION_DIR)), gulp.src('LICENSE').pipe(gulp.dest(MOZCENTRAL_EXTENSION_DIR)), gulp.src(FIREFOX_EXTENSION_DIR + 'tools/l10n.js') .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + '/web')), preprocessJS(FIREFOX_CONTENT_DIR + 'PdfJs.jsm', defines, true) .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR)), preprocessJS(FIREFOX_CONTENT_DIR + 'PdfStreamConverter.jsm', defines, true) .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR)), preprocessJS(FIREFOX_CONTENT_DIR + 'PdfJsNetwork.jsm', defines, true) .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR)), preprocessJS(FIREFOX_CONTENT_DIR + 'PdfjsContentUtils.jsm', defines, true) .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR)), preprocessJS(FIREFOX_CONTENT_DIR + 'PdfjsChromeUtils.jsm', defines, true) .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR)), ]); }); gulp.task('chromium-pre', ['buildnumber', 'locale'], function () { console.log(); console.log('### Building Chromium extension'); var defines = builder.merge(DEFINES, {CHROME: true}); var CHROME_BUILD_DIR = BUILD_DIR + '/chromium/', CHROME_BUILD_CONTENT_DIR = CHROME_BUILD_DIR + '/content/'; // Clear out everything in the chrome extension build directory rimraf.sync(CHROME_BUILD_DIR); return merge([ createBundle(defines).pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + 'build')), createWebBundle(defines).pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + 'web')), gulp.src(COMMON_WEB_FILES, {base: 'web/'}) .pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + 'web')), gulp.src('external/webL10n/l10n.js') .pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + 'web')), gulp.src([ 'web/locale/*/viewer.properties', 'web/locale/locale.properties' ], {base: 'web/'}).pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + 'web')), gulp.src(['external/bcmaps/*.bcmap', 'external/bcmaps/LICENSE'], {base: 'external/bcmaps'}) .pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + 'web/cmaps')), preprocessHTML('web/viewer.html', defines) .pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + 'web')), preprocessCSS('web/viewer.css', 'chrome', defines, true) .pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + 'web')), gulp.src('LICENSE').pipe(gulp.dest(CHROME_BUILD_DIR)), gulp.src('extensions/chromium/*.{json,html,js,css,png}') .pipe(gulp.dest(CHROME_BUILD_DIR)), gulp.src('extensions/chromium/**/*.{json,html,js,css,png}', {base: 'extensions/chromium/'}) .pipe(gulp.dest(CHROME_BUILD_DIR)), ]); }); gulp.task('jsdoc', function (done) { console.log(); console.log('### Generating documentation (JSDoc)'); var JSDOC_FILES = [ 'src/doc_helper.js', 'src/display/api.js', 'src/display/global.js', 'src/shared/util.js', 'src/core/annotation.js' ]; var directory = BUILD_DIR + JSDOC_DIR; rimraf(directory, function () { mkdirp(directory, function () { var command = '"node_modules/.bin/jsdoc" -d ' + directory + ' ' + JSDOC_FILES.join(' '); exec(command, done); }); }); }); gulp.task('publish', ['generic'], function (done) { var version = JSON.parse( fs.readFileSync(BUILD_DIR + 'version.json').toString()).version; config.stableVersion = config.betaVersion; config.betaVersion = version; createStringSource(CONFIG_FILE, JSON.stringify(config, null, 2)) .pipe(gulp.dest('.')) .on('end', function () { var targetName = 'pdfjs-' + version + '-dist.zip'; gulp.src(BUILD_DIR + 'generic/**') .pipe(zip(targetName)) .pipe(gulp.dest(BUILD_DIR)) .on('end', function () { console.log('Built distribution file: ' + targetName); done(); }); }); }); gulp.task('test', function () { return streamqueue({ objectMode: true }, createTestSource('unit'), createTestSource('browser')); }); gulp.task('bottest', function () { return streamqueue({ objectMode: true }, createTestSource('unit'), createTestSource('font'), createTestSource('browser (no reftest)')); }); gulp.task('browsertest', function () { return createTestSource('browser'); }); gulp.task('browsertest-noreftest', function () { return createTestSource('browser (no reftest)'); }); gulp.task('unittest', function () { return createTestSource('unit'); }); gulp.task('fonttest', function () { return createTestSource('font'); }); gulp.task('botmakeref', function (done) { console.log(); console.log('### Creating reference images'); var PDF_BROWSERS = process.env['PDF_BROWSERS'] || 'resources/browser_manifests/browser_manifest.json'; if (!checkFile('test/' + PDF_BROWSERS)) { console.log('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.'); console.log('Copy and adjust the example in ' + 'test/resources/browser_manifests.'); done(new Error('Missing manifest file')); return; } var args = ['test.js', '--masterMode', '--noPrompts', '--browserManifestFile=' + PDF_BROWSERS]; var testProcess = spawn('node', args, {cwd: TEST_DIR, stdio: 'inherit'}); testProcess.on('close', function (code) { done(); }); }); gulp.task('unittestcli', function (done) { var args = ['JASMINE_CONFIG_PATH=test/unit/clitests.json']; var testProcess = spawn('node_modules/.bin/jasmine', args, {stdio: 'inherit'}); testProcess.on('close', function (code) { if (code !== 0) { done(new Error('Unit tests failed.')); return; } done(); }); }); gulp.task('lint', function (done) { console.log(); console.log('### Linting JS files'); // Ensure that we lint the Firefox specific *.jsm files too. var options = ['node_modules/eslint/bin/eslint', '--ext', '.js,.jsm', '.']; var esLintProcess = spawn('node', options, {stdio: 'inherit'}); esLintProcess.on('close', function (code) { if (code !== 0) { done(new Error('ESLint failed.')); return; } console.log(); console.log('### Checking UMD dependencies'); var umd = require('./external/umdutils/verifier.js'); var paths = { 'pdfjs': './src', 'pdfjs-web': './web', 'pdfjs-test': './test' }; if (!umd.validateFiles(paths)) { done(new Error('UMD check failed.')); return; } console.log(); console.log('### Checking supplemental files'); if (!checkChromePreferencesFile( 'extensions/chromium/preferences_schema.json', 'web/default_preferences.json')) { done(new Error('chromium/preferences_schema is not in sync.')); return; } console.log('files checked, no errors found'); done(); }); }); 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 require('./make.js'); 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); } }; });