diff --git a/BUILD.gn b/BUILD.gn index 0e5df24286..247c322a97 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -278,17 +278,17 @@ run_node("deno_runtime_declaration") { run_node("bundle") { out_dir = "$target_gen_dir/bundle/" - sources = ts_sources + [ "rollup.config.js" ] outputs = [ out_dir + "main.js", out_dir + "main.js.map", ] + depfile = out_dir + "main.d" deps = [ ":deno_runtime_declaration", ":msg_ts", ] args = [ - "./node_modules/rollup/bin/rollup", + rebase_path("third_party/node_modules/rollup/bin/rollup", root_build_dir), "-c", rebase_path("rollup.config.js", root_build_dir), "-i", @@ -298,8 +298,6 @@ run_node("bundle") { "--sourcemapFile", rebase_path("."), "--silent", - "--environment", - "BASEPATH:" + rebase_path(".", root_build_dir), ] } diff --git a/rollup.config.js b/rollup.config.js index 2119430d8a..a648c64704 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,4 +1,6 @@ // Copyright 2018 the Deno authors. All rights reserved. MIT license. +import assert from "assert"; +import * as fs from "fs"; import path from "path"; import alias from "rollup-plugin-alias"; import { plugin as analyze } from "rollup-plugin-analyzer"; @@ -10,19 +12,20 @@ import { createFilter } from "rollup-pluginutils"; import typescript from "typescript"; import MagicString from "magic-string"; -const mockPath = path.join(__dirname, "js", "mock_builtin.js"); -const platformPath = path.join(__dirname, "js", "platform.ts"); -const tsconfig = path.join(__dirname, "tsconfig.json"); -const typescriptPath = `${ - process.env.BASEPATH -}/third_party/node_modules/typescript/lib/typescript.js`; +const mockPath = path.resolve(__dirname, "js/mock_builtin.js"); +const platformPath = path.resolve(__dirname, "js/platform.ts"); +const tsconfig = path.resolve(__dirname, "tsconfig.json"); +const typescriptPath = path.resolve( + __dirname, + "third_party/node_modules/typescript/lib/typescript.js" +); // We will allow generated modules to be resolvable by TypeScript based on // the current build path const tsconfigOverride = { compilerOptions: { paths: { - "*": ["*", path.join(process.cwd(), "*")] + "*": ["*", path.resolve("*")] } } }; @@ -50,7 +53,7 @@ function strings({ include, exclude } = {}) { if (!importee.startsWith("gen/")) { // this is a static asset which is located relative to the root of // the source project - return path.resolve(path.join(process.env.BASEPATH, importee)); + return path.resolve(path.join(__dirname, importee)); } // this is an asset which has been generated, therefore it will be // located within the build path @@ -122,15 +125,85 @@ function resolveGenerated() { name: "resolve-msg-generated", resolveId(importee) { if (importee.startsWith("gen/msg_generated")) { - const resolved = path.resolve( - path.join(process.cwd(), `${importee}.ts`) - ); - return resolved; + return path.resolve(`${importee}.ts`); } } }; } +function generateDepFile({ outputFile, sourceFiles = [], configFiles = [] }) { + let timestamp = Date.now(); + + // Save the depfile just before the node process exits. + process.once("beforeExit", () => + writeDepFile({ outputFile, sourceFiles, configFiles, timestamp }) + ); + + return { + name: "depfile", + load(sourceFile) { + // The 'globals' plugin adds generated files that don't exist on disk. + // Don't add them to the depfile. + if (/^[0-9a-f]{30}$/.test(sourceFile)) { + return; + } + sourceFiles.push(sourceFile); + // Remember the time stamp that we last resolved a dependency. + // We'll set the last modified time of the depfile to that. + timestamp = new Date(); + } + }; +} + +function writeDepFile({ outputFile, sourceFiles, configFiles, timestamp }) { + const buildDir = process.cwd(); + const outputDir = path.dirname(outputFile); + + // Assert that the discovered bundle inputs are files that exist on disk. + sourceFiles.forEach(f => fs.accessSync(f)); + // Since we also want to rebuild the bundle if rollup configuration or the the + // tooling changes (e.g. when typescript is updated), add the currently loaded + // node.js modules to the list of dependencies. + let inputs = [...sourceFiles, ...configFiles, ...Object.keys(require.cache)]; + // Deduplicate the list of inputs. + inputs = Array.from(new Set(inputs.map(f => path.resolve(f)))); + // Turn filenames into relative paths and format/escape them for a Makefile. + inputs = inputs.map(formatPath); + + // Build a list of output filenames and normalize those too. + const depFile = path.join( + outputDir, + path.basename(outputFile, path.extname(outputFile)) + ".d" + ); + const outputs = [outputFile, depFile].map(formatPath); + + // Generate depfile contents. + const depFileContent = [ + ...outputs.map(filename => `${filename}: ` + inputs.join(" ") + "\n\n"), + ...inputs.map(filename => `${filename}:\n`) + ].join(""); + + // Since we're writing the depfile when node's "beforeExit" hook triggers, + // it's getting written _after_ the regular outputs are saved to disk. + // Therefore, after writing the depfile, reset its timestamps to when we last + // discovered a dependency, which was certainly before the bundle was built. + fs.writeFileSync(depFile, depFileContent); + fs.utimesSync(depFile, timestamp, timestamp); + + // Renders path to make it suitable for a depfile. + function formatPath(filename) { + // Make the path relative to the root build directory. + filename = path.relative(buildDir, filename); + // Use forward slashes on Windows. + if (process.platform === "win32") { + filename = filename.replace(/\\/g, "/"); + } + // Escape spaces with a backslash. This is what rust and clang do too. + filename = filename.replace(/ /g, "\\ "); + return filename; + } +} + export default function makeConfig(commandOptions) { return { output: { @@ -243,7 +316,12 @@ export default function makeConfig(commandOptions) { // source-map-support, which is required by TypeScript to support source maps, requires Node.js Buffer // implementation. This needs to come at the end of the plugins because of the impact it has on // the existing runtime environment, which breaks other plugins and features of the bundler. - globals() + globals(), + + generateDepFile({ + outputFile: commandOptions.o, + configFiles: [commandOptions.c, tsconfig] + }) ] }; }