|
| 1 | +import { createHash } from "node:crypto"; |
1 | 2 | import { WgslTemplateBuildError } from "./errors.js"; |
2 | 3 | import type { |
3 | 4 | SourceBuilder, |
@@ -188,6 +189,7 @@ export class StaticCodeGenerator implements CodeGenerator, SourceBuilder { |
188 | 189 |
|
189 | 190 | #buildGenerateIndexImpl( |
190 | 191 | repo: TemplateRepository<TemplatePass2>, |
| 192 | + templateImplementationHash: Map<string, string>, |
191 | 193 | includePathPrefix: string, |
192 | 194 | templateExt: string |
193 | 195 | ): string { |
@@ -238,7 +240,14 @@ std::string pass_as_string(T&& v) { |
238 | 240 | throw new Error(`Template name "${name}" does not end with the expected extension "${templateExt}"`); |
239 | 241 | } |
240 | 242 | const baseName = name.slice(0, -templateExt.length); |
241 | | - implContent.push(`#include "${includePathPrefix}generated/${baseName}.h"`); |
| 243 | + const hash = templateImplementationHash.get(name); |
| 244 | + if (!hash) { |
| 245 | + throw new WgslTemplateBuildError( |
| 246 | + `Missing hash for template implementation "${name}"`, |
| 247 | + "output-validation-failed" |
| 248 | + ); |
| 249 | + } |
| 250 | + implContent.push(`#include "${includePathPrefix}generated/${baseName}.h" // ${hash}`); |
242 | 251 | } |
243 | 252 |
|
244 | 253 | implContent.push(""); |
@@ -303,28 +312,36 @@ std::string pass_as_string(T&& v) { |
303 | 312 | options: SourceBuilderOptions |
304 | 313 | ): TemplateRepository<TemplateBuildResult> { |
305 | 314 | const result = new Map<string, TemplateBuildResult>(); |
| 315 | + const templateImplementationHash = new Map<string, string>(); |
306 | 316 |
|
307 | | - // STEP.1. Generate the index.h |
308 | | - result.set("index.h", this.#buildGenerateIndex(repo)); |
309 | | - |
310 | | - // STEP.2. Generate the string table if needed |
311 | | - if (this.#stringTable) { |
312 | | - result.set("string_table.h", this.#buildGenerateStringTable()); |
| 317 | + // STEP.1. Generate each template implementation |
| 318 | + for (const [name, template] of repo.templates) { |
| 319 | + if (!name.endsWith(options.templateExt)) { |
| 320 | + throw new Error(`Template name "${name}" does not end with the expected extension "${options.templateExt}"`); |
| 321 | + } |
| 322 | + const baseName = name.slice(0, -options.templateExt.length); |
| 323 | + const content = this.#buildTemplateImplementation(name, template); |
| 324 | + result.set(`generated/${baseName}.h`, content); |
| 325 | + templateImplementationHash.set(name, createHash("sha256").update(content).digest("hex")); |
313 | 326 | } |
314 | 327 |
|
315 | | - // STEP.3. Generate implementation index_impl.h |
| 328 | + // STEP.2. Generate implementation index_impl.h |
316 | 329 | result.set( |
317 | 330 | "index_impl.h", |
318 | | - this.#buildGenerateIndexImpl(repo, options.includePathPrefix ?? "", options.templateExt) |
| 331 | + this.#buildGenerateIndexImpl( |
| 332 | + repo, |
| 333 | + templateImplementationHash, |
| 334 | + options.includePathPrefix ?? "", |
| 335 | + options.templateExt |
| 336 | + ) |
319 | 337 | ); |
320 | 338 |
|
321 | | - // STEP.4. Generate each template implementation |
322 | | - for (const [name, template] of repo.templates) { |
323 | | - if (!name.endsWith(options.templateExt)) { |
324 | | - throw new Error(`Template name "${name}" does not end with the expected extension "${options.templateExt}"`); |
325 | | - } |
326 | | - const baseName = name.slice(0, -options.templateExt.length); |
327 | | - result.set(`generated/${baseName}.h`, this.#buildTemplateImplementation(name, template)); |
| 339 | + // STEP.3. Generate the index.h |
| 340 | + result.set("index.h", this.#buildGenerateIndex(repo)); |
| 341 | + |
| 342 | + // STEP.4. Generate the string table if needed |
| 343 | + if (this.#stringTable) { |
| 344 | + result.set("string_table.h", this.#buildGenerateStringTable()); |
328 | 345 | } |
329 | 346 |
|
330 | 347 | return { |
|
0 commit comments