The previous attempt was wrong, as it wasn't correctly rebuilding dependents if a breaking TypeScript API change was made. The root cause for that is the split of `devtools_entrypoint` and `devtools_module`, which we need for bundling. Unfortunately, we also can't introduce granular GN targets for only `.d.ts` files, since TypeScript generates all outputs in 1 go. Therefore, it is not possible to split that up into multiple scripts, which is required if we want to introduce targets with outputs for only `.d.ts`. Instead, we should still reset timestamps for `devtools_module`, but then we always rebuild `devtools_entrypoint`. By doing that, a breaking API change in a `devtools_module` would trigger its corresponding `devtools_entrypoint` to change, which will ensure that all its dependents also change. However, the next layer of `devtools_module` will then detect that it doesn't change, hence introducing the performance improvement. So while we are still doing a bit too much work in theory, in practice this change already removes a whole bunch of unnecessary work. I think that is a step in the right direction and this should result in deterministic builds as well. DISABLE_THIRD_PARTY_CHECK=Update TypeScript infrastructure R=jacktfranklin@chromium.org CC=marijnh@gmail.com Bug: 1237438 Change-Id: Ib8ea10ee8df263f0dddf0918bc1732fd696f1105 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3107130 Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org> Commit-Queue: Jack Franklin <jacktfranklin@chromium.org> Auto-Submit: Tim van der Lippe <tvanderlippe@chromium.org> Reviewed-by: Jack Franklin <jacktfranklin@chromium.org>
DevTools build system templates
The DevTools build system contains several templates to integrate files in the build system. Below you can find an overview of which template/combination of templates you need to use and when.
Entrypoints and modules
The buildsystem has a concept of "entrypoints" and "modules".
An entrypoint is a file that exports all symbols that are considered the public API of that particular component.
For example, front_end/common/ exposes its public API in front_end/common/common.js.
Therefore, the common.js file is considered the entrypoint.
Rule: all entrypoints use the same name in their filename as the name of the folder.
All other files in a particular component are considered implementation details and therefore part of the "module".
Rule: an entrypoint only exports symbols that are considered the public API of that component. The public API includes functions/classes/etc... that are intended to be used by other modules
For example, for front_end/common/:
- The entrypoint is
front_end/common/common.ts - All other files such as
front_end/common/implementation_detail.tsare part of the "module"
devtools_entrypoint and devtools_module
The two templates devtools_entrypoint and devtools_module implement the respective roles of entrypoints/modules.
The devtools_entrypoint includes an entrypoint, which is the entrypoint filename.
The devtools_module contains a list of files that are considered the implementation of the component.
Example file for front_end/my_module/BUILD.gn:
import("../../scripts/build/ninja/devtools_entrypoint.gni")
import("../../scripts/build/ninja/devtools_module.gni")
devtools_module("my_module") {
sources = [
"implementation_detail.ts",
"some_other_file.ts",
]
deps = [
"../other_dependency:bundle",
]
}
devtools_entrypoint("bundle") {
entrypoint = "my_module.ts"
deps = [
":my_module",
]
}
Rule:
devtools_moduleis named the same name as the folder it resides in
Rule:
devtools_entrypointsis named "bundle", as in a release build it bundles with Rollup
Legacy: Non-TypeScript entrypoints
Not all modules are currently typechecked only by TypeScript. There are two other options, related to the legacy Closure Compiler.
Typescriptified modules (e.g. TypeScript + Closure Compiler)
Any module that is fully typescriptified (e.g. a JavaScript file that is typechecked by both TypeScript and Compiler Compiler) can be imported from both TypeScript modules and Closure Compile-checked modules.
Rule: Any typescriptified module can never contain an entrypoint or source file in their implementation that is TypeScript-authored. In practice this rule comes down to: you can only have a TypeScript-authored implementation file if the corresponding entrypoint is also TypeScript-authored.
As such, the following layout is used for TypeScriptified modules:
import("../../scripts/build/ninja/devtools_entrypoint.gni")
import("../../scripts/build/ninja/devtools_module.gni")
devtools_module("my_module") {
sources = [
"implementation_detail.js",
"some_other_file.js",
]
deps = [
"../other_dependency:bundle",
]
}
devtools_entrypoint("bundle") {
entrypoint = "my_module.js"
deps = [
":my_module",
]
}
Here, both the entrypoint and the sources of the module are all JavaScript files. All of these files are typechecked by Closure Compiler and TypeScript.
GRD file generation
To make sure that files are loaded in Chromium, DevTools generates a GRD file that includes all files that are allowed to be loaded by the backend. To generate the GRD, there are numerous variables that list all kinds of files.
Entrypoints
All entrypoints are listed in grd_files_release_sources specified in /config/gni/devtools_grd_files.gni.
Rule: in both release and debug builds, entrypoints are always included in the GRD file
Module implementation files
All implementation files for components are listed in grd_files_debug_sources specified in /config/gni/devtools_grd_files.gni.
Rule: the implementation files are only present in the GRD file in a debug build, because the release build bundles all files into the respective entrypoint