Skip to content

Commit 1acc77a

Browse files
committed
docs(documentation): Reuse build-manifest.json concept
1 parent 4bed03f commit 1acc77a

File tree

1 file changed

+49
-21
lines changed

1 file changed

+49
-21
lines changed

rfcs/0017-incremental-build.md

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ This shall enable the following workflow:
4646

4747
1. **Action:** Build is started
4848
1. Task A, Task B and Task C are executed in sequence, writing their results into individual writer stages.
49-
1. *Task outputs are written to a content-addressable store and the `cache-info.json` metadata is serialized to disk.*
49+
1. *Task outputs are written to a content-addressable store and the `build-manifest.json` metadata is serialized to disk.*
5050
1. Build finishes and the resources of all writer stages and the source reader are combined and written into the target output directory.
5151
* Resources present in later writer stages (and higher versions) are preferred over competing resources with the same path.
5252
1. **Action:** A source file is modified and a new build is triggered
53-
1. *The `cache-info.json` is read from disk, allowing the build to access cached content from the content-addressable store.*
53+
1. *The `build-manifest.json` is read from disk, allowing the build to access cached content from the content-addressable store.*
5454
1. The build determines which tasks need to be executed using the imported cache and information about the modified source file.
5555
* In this example, it is determined that Task A and Task C need to be executed since they requested the modified resource in their previous execution.
5656
1. Task A is executed. The output is written into a new **version** (v2) of the associated writer stage.
@@ -59,7 +59,7 @@ This shall enable the following workflow:
5959
* Task A can't access v1 of its writer stage. It can only access the combined resources of all previous writer stages.
6060
1. The `Project Build Cache` determines whether the resources produced in this latest execution of Task A are relevant for Task B. If yes, the content of those resources is compared to the cached content of the resources Task B has received during its last execution. In this example, the output of Task A is not relevant for Task B and it is skipped.
6161
1. Task C is called and has access to both versions (v1 and v2) of the writer stage of Task A. Allowing it to access all resources produced in all previous executions of Task A.
62-
1. *Task outputs are written to the content-addressable store and the `cache-info.json` is updated.*
62+
1. *Task outputs are written to the content-addressable store and the `build-manifest.json` is updated.*
6363
1. The build finishes. The combined resources of all writer stages and the source reader are written to the target output directory.
6464

6565
![Diagram illustrating an initial and a successive build leveraging the build cache](./resources/0017-incremental-build/Build_With_Cache.png)
@@ -72,14 +72,45 @@ Every project has its own cache metadata. This allows for reuse of a project's c
7272

7373
The cache consists of two main parts:
7474
1. A global **object store (the CAS)** where all file contents are stored, named by a hash of their content.
75-
2. A per-project `cache-info.json` file which acts as a lightweight **metadata index**, mapping logical file paths to their content hashes in the object store.
75+
2. A per-project `build-manifest.json` file which acts as a lightweight **metadata index**, mapping logical file paths to their content hashes in the object store.
7676

77-
#### cache-info.json
77+
#### build-manifest.json
7878

7979
````jsonc
8080
{
81-
"timestamp": 1734005532124,
8281
"cacheKey": "project-name-1.0.0-bb3a3262d893fcb9adf16bff63f",
82+
"buildManifest": {
83+
"manifestVersion": "1.0",
84+
"timestamp": "2025-11-24T13:43:24.612Z",
85+
"versions": {
86+
"builderVersion": "5.0.0",
87+
"projectVersion": "5.0.0",
88+
"fsVersion": "5.0.0"
89+
},
90+
"buildConfig": {
91+
"selfContained": false,
92+
"cssVariables": false,
93+
"jsdoc": false,
94+
"createBuildManifest": false,
95+
"outputStyle": "Default",
96+
"includedTasks": [],
97+
"excludedTasks": []
98+
},
99+
"version": "1.0.0",
100+
"namespace": "project/namespace",
101+
"tags": {
102+
"/resources/project/namespace/Component-dbg.js": {
103+
"ui5:IsDebugVariant": true
104+
},
105+
"/resources/project/namespace/Component.js": {
106+
"ui5:HasDebugVariant": true
107+
}
108+
}
109+
},
110+
"sourceMetadata": {
111+
// Map of source paths to their content hashes
112+
"/resources/project/namespace/Component.js": "d41d8cd98f00b204e9800998ecf8427e"
113+
},
83114
"taskCache": [{
84115
"taskName": "replaceCopyright",
85116
"resourceMetadata": {
@@ -102,14 +133,12 @@ The cache consists of two main parts:
102133
"/resources/project/namespace/Component.js": "c1c77edc5c689a471b12fe8ba79c51d1"
103134
}
104135
}
105-
}],
106-
"sourceMetadata": {
107-
// Map of source paths to their content hashes
108-
"/resources/project/namespace/Component.js": "d41d8cd98f00b204e9800998ecf8427e"
109-
}
136+
}]
110137
}
111138
````
112139

140+
The concept of a `build-manifest.json` has already been explored in [RFC 0011 Reuse Build Results](https://github.com/SAP/ui5-tooling/pull/612) and found an implementation for consumption of pre-built UI5 framework libraries in [UI5 3.0](https://github.com/UI5/cli/pull/612). The **buildManifest** object is based on that concept.
141+
113142
**cacheKey**
114143

115144
The cache key can be used to identify the cache for a project. It shall be based on the project's name and package version, as well as a SHA256 hash compiled from the following information:
@@ -141,25 +170,24 @@ The directory structure is flat and efficient. A global `cas/` directory stores
141170
│ ├── d41d8cd98f00b204e9899998ecf8427e (Content of another file)
142171
│ └── ... (all other unique file contents)
143172
144-
├── openui5-sample-app-0.5.0-bb0a3262d093fcb9acf16
145-
│ └── cache-info.json
146-
├── sap.m-1.132.0-SNAPSHOT-bb0a3262d093fcb9acf16
147-
│ └── cache-info.json
148-
└── sap.ui.core-1.132.0-SNAPSHOT-bb0a3262d093fcb9acf16
149-
└── cache-info.json
173+
└── manifests/
174+
├── @ui5/
175+
│ └── sample-app-0.5.0-bb3a3262d893fcb9adf16bff63f.json
176+
├── sap.m-1.142.0-xy3a3262d893fcb9adf16bff63f.json.json
177+
└── sap.ui.core-1.142.0-fh3a3262d893fcb3adf16bff63f.json
150178
```
151179

152-
Besides the `cas` directory, each project has its own directory named after its cache key. This directory contains only the `cache-info.json` file for that project.
180+
The `cas` directory contains files named by their SHA256 content hash. Each file contains the raw content of a resource produced during a build. Ideally a library like [`cacache`](https://www.npmjs.com/package/cacache) should be used to manage the content-addressable store.
153181

154-
All unique file contents from all projects and their builds are stored **once** in the global `cas` directory, named by their content hash.
182+
The `manifests` directory contains one build manifest file per project build cache. The filename is derived from the project's namespace, version and cache key.
155183

156184
![Diagram illustrating the creation of a build cache](./resources/0017-incremental-build/Create_Cache.png)
157185

158186
### Cache Import
159187

160188
Before building a project, UI5 Tooling shall scan for a cache directory with the respective cache key and import the cache if one is found.
161189

162-
The import process is very fast, as it only involves reading the lightweight `cache-info.json` file to populate the `Build Task Cache` instances with their metadata. When the build process needs to access a cached resource, it uses the metadata map to find the content hash and reads the corresponding file directly from the global `cas` store.
190+
The import process is very fast, as it only involves reading the lightweight `build-manifest.json` file to populate the `Build Task Cache` instances with their metadata. When the build process needs to access a cached resource, it uses the metadata map to find the content hash and reads the corresponding file directly from the global `cas` store.
163191

164192
This allows executing individual tasks and providing them with the results of all preceding tasks without the overhead of creating numerous file system readers or managing physical copies of files for each build stage.
165193

@@ -183,7 +211,7 @@ A mechanism to purge unused cache on disk is required. The cache can grow very l
183211

184212
This should probably use some sort of LRU-cache to purge unused cache entries dynamically. The same mechanism could be applied to the npm artifacts downloaded by UI5 Tooling.
185213

186-
To avoid slowing down core commands, the purge check should run as a non-blocking process after a successful ui5 build or ui5 serve command completes. This process checks if either of the configured thresholds (age or size) has been exceeded. If so, it proceeds with the purge.
214+
To avoid slowing down core commands, the purge check should run as a non-blocking process after a successful ui5 build or ui5 serve command completes. This process checks if either of the configured thresholds (age or size) has been exceeded. If so, it proceeds with the purge. Some of this functionality might be provided by the underlying content-addressable store library, such as cacache's internal garbage collection.
187215

188216
A dedicated command, such as `ui5 cache clean`, should be introduced in addition. This command allows users to manually trigger a cache purge, providing options to specify criteria such as maximum age or size for cache entries to be removed. Similarly, a command `ui5 cache verify` could be provided to check the integrity of the cache.
189217

0 commit comments

Comments
 (0)