From 94a0a3c7fb184d0cc3d0a26068595d25813412e9 Mon Sep 17 00:00:00 2001 From: galargh Date: Thu, 9 Jan 2025 20:04:14 +0100 Subject: [PATCH 01/53] wip: implement the compilation job and artifacts caches --- pnpm-lock.yaml | 12 ++- v-next/hardhat/package.json | 4 +- .../solidity/build-system/build-system.ts | 102 +++++++++++++++++- .../solidity/build-system/cache.ts | 71 ++++++++++++ .../solidity/hook-handlers/hre.ts | 4 +- .../src/types/solidity/build-system.ts | 28 ++++- 6 files changed, 205 insertions(+), 16 deletions(-) create mode 100644 v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9945a9ee8e..1bb19a4d1d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2028,8 +2028,8 @@ importers: specifier: ^5.18.1 version: 5.30.0 adm-zip: - specifier: ^0.4.16 - version: 0.4.16 + specifier: ^0.5.16 + version: 0.5.16 chalk: specifier: ^5.3.0 version: 5.4.1 @@ -2071,7 +2071,7 @@ importers: specifier: workspace:^ version: link:../hardhat-test-utils '@types/adm-zip': - specifier: ^0.5.5 + specifier: ^0.5.7 version: 0.5.7 '@types/debug': specifier: ^4.1.4 @@ -4779,6 +4779,10 @@ packages: resolution: {integrity: sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==} engines: {node: '>=0.3.0'} + adm-zip@0.5.16: + resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==} + engines: {node: '>=12.0'} + aes-js@3.0.0: resolution: {integrity: sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==} @@ -10663,6 +10667,8 @@ snapshots: adm-zip@0.4.16: {} + adm-zip@0.5.16: {} + aes-js@3.0.0: {} aes-js@4.0.0-beta.5: {} diff --git a/v-next/hardhat/package.json b/v-next/hardhat/package.json index b8b84a2654..90ee232953 100644 --- a/v-next/hardhat/package.json +++ b/v-next/hardhat/package.json @@ -65,7 +65,7 @@ "@eslint-community/eslint-plugin-eslint-comments": "^4.3.0", "@ignored/hardhat-vnext-node-test-reporter": "workspace:^3.0.0-next.15", "@nomicfoundation/hardhat-test-utils": "workspace:^", - "@types/adm-zip": "^0.5.5", + "@types/adm-zip": "^0.5.7", "@types/debug": "^4.1.4", "@types/node": "^20.14.9", "@types/semver": "^7.5.8", @@ -91,7 +91,7 @@ "@ignored/hardhat-vnext-zod-utils": "workspace:^3.0.0-next.15", "@nomicfoundation/solidity-analyzer": "^0.1.0", "@sentry/node": "^5.18.1", - "adm-zip": "^0.4.16", + "adm-zip": "^0.5.16", "chalk": "^5.3.0", "debug": "^4.1.1", "enquirer": "^2.3.0", diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index 1822a877d7..b2cc70cc71 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -9,6 +9,7 @@ import type { GetCompilationJobsOptions, CompileBuildInfoOptions, RunCompilationJobOptions, + EmitArtifactsOptions, } from "../../../../types/solidity/build-system.js"; import type { CompilationJob } from "../../../../types/solidity/compilation-job.js"; import type { @@ -24,6 +25,7 @@ import { assertHardhatInvariant } from "@ignored/hardhat-vnext-errors"; import { getAllDirectoriesMatching, getAllFilesMatching, + isDirectory, readJsonFile, remove, writeUtf8File, @@ -44,6 +46,7 @@ import { getContractArtifact, getDuplicatedContractNamesDeclarationFile, } from "./artifacts.js"; +import { Cache } from "./cache.js"; import { CompilationJobImplementation } from "./compilation-job.js"; import { downloadConfiguredCompilers, getCompiler } from "./compiler/index.js"; import { buildDependencyGraph } from "./dependency-graph-building.js"; @@ -67,11 +70,21 @@ export interface SolidityBuildSystemOptions { export class SolidityBuildSystemImplementation implements SolidityBuildSystem { readonly #options: SolidityBuildSystemOptions; + readonly #compilerOutputCache: Cache; + readonly #artifactsCache: Cache; readonly #defaultConcurrency = Math.max(os.cpus().length - 1, 1); #downloadedCompilers = false; constructor(options: SolidityBuildSystemOptions) { this.#options = options; + this.#compilerOutputCache = new Cache( + options.cachePath, + "hardhat.core.solidity.build-system.compiler-output", + ); + this.#artifactsCache = new Cache( + options.cachePath, + "hardhat.core.solidity.build-system.artifacts", + ); } public async getRootFilePaths(): Promise { @@ -115,11 +128,14 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { const compilationJobs = [...new Set(compilationJobsPerFile.values())]; - // TODO: Filter the compilation jobs based on the cache - + const runCompilationJobOptions: RunCompilationJobOptions = { + force: options?.force, + quiet: options?.quiet, + }; const results: CompilerOutput[] = await pMap( compilationJobs, - (compilationJob) => this.runCompilationJob(compilationJob), + (compilationJob) => + this.runCompilationJob(compilationJob, runCompilationJobOptions), { concurrency: options?.concurrency ?? this.#defaultConcurrency, // An error when running the compiler is not a compilation failure, but @@ -139,11 +155,16 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { if (isSuccessfulBuild) { log("Emitting artifacts of successful build"); + const emitArtifactsOptions: EmitArtifactsOptions = { + force: options?.force, + quiet: options?.quiet, + }; await Promise.all( compilationJobs.map(async (compilationJob, i) => { const artifactsPerFile = await this.emitArtifacts( compilationJob, results[i], + emitArtifactsOptions, ); contractArtifactsGeneratedByCompilationJob.set( @@ -316,6 +337,17 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { compilationJob: CompilationJob, options?: RunCompilationJobOptions, ): Promise { + const buildId = compilationJob.getBuildId(); + + if (options?.force !== true) { + const cachedCompilerOutput = + await this.#compilerOutputCache.getJson(buildId); + if (cachedCompilerOutput !== undefined) { + log(`Using cached compiler output for build ${buildId}`); + return cachedCompilerOutput; + } + } + await this.#downloadConfiguredCompilers(options?.quiet); let numberOfFiles = 0; @@ -336,7 +368,15 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { "The long version of the compiler should match the long version of the compilation job", ); - return compiler.compile(compilationJob.getSolcInput()); + const compilerOutput = await compiler.compile( + compilationJob.getSolcInput(), + ); + + if (!this.#hasCompilationErrors(compilerOutput)) { + await this.#compilerOutputCache.setJson(buildId, compilerOutput); + } + + return compilerOutput; } public async remapCompilerError( @@ -373,8 +413,43 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { public async emitArtifacts( compilationJob: CompilationJob, compilerOutput: CompilerOutput, + options?: EmitArtifactsOptions, ): Promise> { const result = new Map(); + const buildId = compilationJob.getBuildId(); + + if (options?.force !== true) { + const cachedFiles = await this.#artifactsCache.getFiles( + buildId, + this.#options.artifactsPath, + ); + if (cachedFiles !== undefined) { + log(`Using cached artifacts for build ${buildId}`); + for (const filePath of cachedFiles) { + const relativePath = path.relative( + this.#options.artifactsPath, + filePath, + ); + if ( + path.dirname(relativePath) === "build-info" || + path.basename(relativePath) === "artifacts.d.ts" + ) { + continue; + } + if (await isDirectory(filePath)) { + result.set(relativePath, []); + } else { + const publicSourceName = path.dirname(relativePath); + const paths = result.get(publicSourceName) ?? []; + paths.push(filePath); + result.set(publicSourceName, paths); + } + } + return result; + } + } + + const filesToCache: string[] = []; // We emit the artifacts for each root file, first emitting one artifact // for each contract, and then one declaration file for the entire file, @@ -434,11 +509,20 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { artifactsDeclarationFilePath, artifactsDeclarationFile, ); + + if (paths.length === 0) { + filesToCache.push( + path.join(this.#options.artifactsPath, publicSourceName), + ); + } else { + filesToCache.push(...paths); + } + filesToCache.push(artifactsDeclarationFilePath); } // Once we have emitted all the contract artifacts and its declaration // file, we emit the build info file and its output file. - const buildInfoId = compilationJob.getBuildId(); + const buildInfoId = buildId; const buildInfoPath = path.join( this.#options.artifactsPath, @@ -478,6 +562,14 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { })(), ]); + filesToCache.push(buildInfoPath, buildInfoOutputPath); + + await this.#artifactsCache.setFiles( + buildId, + this.#options.artifactsPath, + filesToCache, + ); + return result; } diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts new file mode 100644 index 0000000000..456e401620 --- /dev/null +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -0,0 +1,71 @@ +import path from "node:path"; + +import { assertHardhatInvariant } from "@ignored/hardhat-vnext-errors"; +import { + exists, + readJsonFile, + writeJsonFile, +} from "@ignored/hardhat-vnext-utils/fs"; +import AdmZip from "adm-zip"; + +export class Cache { + readonly #basePath: string; + readonly #namespace: string; + + constructor(basePath: string, namespace: string) { + this.#basePath = basePath; + this.#namespace = namespace; + } + + #getPath(key: string): string { + return path.join(this.#basePath, this.#namespace, key); + } + + public async setJson(key: string, value: T): Promise { + const filePath = this.#getPath(key); + await writeJsonFile(filePath, value); + } + + public async getJson(key: string): Promise { + const filePath = this.#getPath(key); + return (await exists(filePath)) ? readJsonFile(filePath) : undefined; + } + + public async setFiles( + key: string, + rootPath: string, + filePaths: string[], + ): Promise { + const zipFilePath = this.#getPath(key); + const zip = new AdmZip(); + for (const filePath of filePaths) { + zip.addLocalFile( + filePath, + path.dirname(path.relative(rootPath, filePath)), + ); + } + const zipFileCreated = await zip.writeZipPromise(zipFilePath, { + overwrite: true, + }); + assertHardhatInvariant( + zipFileCreated, + `Failed to create zip file ${zipFilePath}`, + ); + } + + public async getFiles( + key: string, + rootPath: string, + ): Promise { + const zipFilePath = this.#getPath(key); + if (await exists(zipFilePath)) { + const zip = new AdmZip(zipFilePath); + zip.extractAllTo(rootPath, true); + const filePaths = zip + .getEntries() + .map((entry) => path.join(rootPath, entry.entryName)); + return filePaths; + } + return undefined; + } +} diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/hook-handlers/hre.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/hook-handlers/hre.ts index f50592d26e..29dacae291 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/hook-handlers/hre.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/hook-handlers/hre.ts @@ -3,6 +3,7 @@ import type { BuildOptions, CompilationJobCreationError, CompileBuildInfoOptions, + EmitArtifactsOptions, FileBuildResult, GetCompilationJobsOptions, RunCompilationJobOptions, @@ -70,9 +71,10 @@ class LazySolidityBuildSystem implements SolidityBuildSystem { public async emitArtifacts( compilationJob: CompilationJob, compilerOutput: CompilerOutput, + options?: EmitArtifactsOptions, ): Promise> { const buildSystem = await this.#getBuildSystem(); - return buildSystem.emitArtifacts(compilationJob, compilerOutput); + return buildSystem.emitArtifacts(compilationJob, compilerOutput, options); } public async cleanupArtifacts(rootFilePaths: string[]): Promise { diff --git a/v-next/hardhat/src/types/solidity/build-system.ts b/v-next/hardhat/src/types/solidity/build-system.ts index f10aaa5b4f..5bd065b0b5 100644 --- a/v-next/hardhat/src/types/solidity/build-system.ts +++ b/v-next/hardhat/src/types/solidity/build-system.ts @@ -62,12 +62,34 @@ export type GetCompilationJobsOptions = Omit< * The options of the `runCompilationJob` method. */ export interface RunCompilationJobOptions { + /** + * If `true`, this option foces the build system to rerun the compilation job, + * even if its output is cached. + */ + force?: boolean; + /** * If `true`, the compilation process doesn't print any output. */ quiet?: boolean; } +/** + * The options of the `emitArtifacts` method. + */ +export interface EmitArtifactsOptions { + /** + * If `true`, this option foces the build system to recreate the artifacts, + * even if they are cached. + */ + force?: boolean; + + /** + * If `true`, the emit process doesn't print any output. + */ + quiet?: boolean; +} + /** * The options of the `compileBuildInfo` method. */ @@ -147,11 +169,6 @@ export type FileBuildResult = export interface CacheHitFileBuildResult { type: FileBuildResultType.CACHE_HIT; - // TODO: Should we remove this? It is a buildId of an already existing build - // info. - // NOTE: The buildId and contractArtifactsGenerated are useful when one uses - // the build system programatically and wants to find out what artifacts - // were generated for a given file, and with what configuration. buildId: string; contractArtifactsGenerated: string[]; } @@ -257,6 +274,7 @@ export interface SolidityBuildSystem { emitArtifacts( compilationJob: CompilationJob, compilerOutput: CompilerOutput, + options?: EmitArtifactsOptions, ): Promise>; /** From 86358787a391e474859c87dcb12624aae27f9c5c Mon Sep 17 00:00:00 2001 From: galargh Date: Fri, 10 Jan 2025 16:16:45 +0100 Subject: [PATCH 02/53] wip: try moving the artifacts higher --- .../solidity/build-system/build-system.ts | 114 ++++++++++++------ .../solidity/build-system/cache.ts | 10 +- 2 files changed, 84 insertions(+), 40 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index b2cc70cc71..b72232a093 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -132,10 +132,12 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { force: options?.force, quiet: options?.quiet, }; - const results: CompilerOutput[] = await pMap( + const results: Array = await pMap( compilationJobs, - (compilationJob) => - this.runCompilationJob(compilationJob, runCompilationJobOptions), + async (compilationJob) => + (await this.#artifactsCache.has(compilationJob.getBuildId())) + ? undefined + : this.runCompilationJob(compilationJob, runCompilationJobOptions), { concurrency: options?.concurrency ?? this.#defaultConcurrency, // An error when running the compiler is not a compilation failure, but @@ -145,7 +147,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { ); const isSuccessfulBuild = results.every( - (result) => !this.#hasCompilationErrors(result), + (result) => result === undefined || !this.#hasCompilationErrors(result), ); const contractArtifactsGeneratedByCompilationJob: Map< @@ -161,11 +163,28 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { }; await Promise.all( compilationJobs.map(async (compilationJob, i) => { - const artifactsPerFile = await this.emitArtifacts( - compilationJob, - results[i], - emitArtifactsOptions, - ); + const result = results[i]; + let artifactsPerFile; + if (result === undefined) { + const cachedFiles = await this.#artifactsCache.getFiles( + compilationJob.getBuildId(), + this.#options.artifactsPath, + ); + + assertHardhatInvariant( + cachedFiles !== undefined, + "We checked if the compilation job was cached before", + ); + + artifactsPerFile = + await this.#groupEmitArtifactsResults(cachedFiles); + } else { + artifactsPerFile = await this.emitArtifacts( + compilationJob, + result, + emitArtifactsOptions, + ); + } contractArtifactsGeneratedByCompilationJob.set( compilationJob, @@ -177,7 +196,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { const resultsMap: Map = new Map(); - for (let i = 0; i < results.length; i++) { + for (let i = 0; i < compilationJobs.length; i++) { const compilationJob = compilationJobs[i]; const result = results[i]; @@ -192,15 +211,19 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { const buildId = compilationJob.getBuildId(); - const errors = await Promise.all( - (result.errors ?? []).map((error) => - this.remapCompilerError(compilationJob, error, true), - ), - ); + const errors = + result !== undefined + ? await Promise.all( + (result.errors ?? []).map((error) => + this.remapCompilerError(compilationJob, error, true), + ), + ) + : []; this.#printSolcErrorsAndWarnings(errors); - const successfulResult = !this.#hasCompilationErrors(result); + const successfulResult = + result === undefined || !this.#hasCompilationErrors(result); for (const [publicSourceName, root] of compilationJob.dependencyGraph .getRoots() @@ -215,6 +238,17 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { continue; } + if (result === undefined) { + resultsMap.set(formatRootPath(publicSourceName, root), { + type: FileBuildResultType.CACHE_HIT, + buildId, + contractArtifactsGenerated: + contractArtifactsGenerated.get(publicSourceName) ?? [], + }); + + continue; + } + resultsMap.set(formatRootPath(publicSourceName, root), { type: FileBuildResultType.BUILD_SUCCESS, buildId, @@ -410,6 +444,32 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { }; } + async #groupEmitArtifactsResults( + filePaths: string[], + ): Promise> { + const result = new Map(); + + for (const filePath of filePaths) { + const relativePath = path.relative(this.#options.artifactsPath, filePath); + if ( + path.dirname(relativePath) === "build-info" || + path.basename(relativePath) === "artifacts.d.ts" + ) { + continue; + } + if (await isDirectory(filePath)) { + result.set(relativePath, []); + } else { + const publicSourceName = path.dirname(relativePath); + const paths = result.get(publicSourceName) ?? []; + paths.push(filePath); + result.set(publicSourceName, paths); + } + } + + return result; + } + public async emitArtifacts( compilationJob: CompilationJob, compilerOutput: CompilerOutput, @@ -425,27 +485,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { ); if (cachedFiles !== undefined) { log(`Using cached artifacts for build ${buildId}`); - for (const filePath of cachedFiles) { - const relativePath = path.relative( - this.#options.artifactsPath, - filePath, - ); - if ( - path.dirname(relativePath) === "build-info" || - path.basename(relativePath) === "artifacts.d.ts" - ) { - continue; - } - if (await isDirectory(filePath)) { - result.set(relativePath, []); - } else { - const publicSourceName = path.dirname(relativePath); - const paths = result.get(publicSourceName) ?? []; - paths.push(filePath); - result.set(publicSourceName, paths); - } - } - return result; + return this.#groupEmitArtifactsResults(cachedFiles); } } diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts index 456e401620..267f954c55 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -21,6 +21,10 @@ export class Cache { return path.join(this.#basePath, this.#namespace, key); } + public async has(key: string): Promise { + return exists(this.#getPath(key)); + } + public async setJson(key: string, value: T): Promise { const filePath = this.#getPath(key); await writeJsonFile(filePath, value); @@ -28,7 +32,7 @@ export class Cache { public async getJson(key: string): Promise { const filePath = this.#getPath(key); - return (await exists(filePath)) ? readJsonFile(filePath) : undefined; + return (await this.has(key)) ? readJsonFile(filePath) : undefined; } public async setFiles( @@ -57,8 +61,8 @@ export class Cache { key: string, rootPath: string, ): Promise { - const zipFilePath = this.#getPath(key); - if (await exists(zipFilePath)) { + if (await this.has(key)) { + const zipFilePath = this.#getPath(key); const zip = new AdmZip(zipFilePath); zip.extractAllTo(rootPath, true); const filePaths = zip From 6e89b99ad5f5b66b5214885c0719391750b23817 Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 14:53:53 +0100 Subject: [PATCH 03/53] chore: add version parameter to the cache constructor --- .../builtin-plugins/solidity/build-system/build-system.ts | 2 ++ .../internal/builtin-plugins/solidity/build-system/cache.ts | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index b72232a093..5378a8fe4c 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -80,10 +80,12 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { this.#compilerOutputCache = new Cache( options.cachePath, "hardhat.core.solidity.build-system.compiler-output", + "v1", ); this.#artifactsCache = new Cache( options.cachePath, "hardhat.core.solidity.build-system.artifacts", + "v1", ); } diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts index 267f954c55..574cdc6bd2 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -11,14 +11,16 @@ import AdmZip from "adm-zip"; export class Cache { readonly #basePath: string; readonly #namespace: string; + readonly #version: string; - constructor(basePath: string, namespace: string) { + constructor(basePath: string, namespace: string, version: string) { this.#basePath = basePath; this.#namespace = namespace; + this.#version = version; } #getPath(key: string): string { - return path.join(this.#basePath, this.#namespace, key); + return path.join(this.#basePath, this.#namespace, this.#version, key); } public async has(key: string): Promise { From 1a755bcaf51f8936819a5b4e90eb0875de9475da Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 14:54:39 +0100 Subject: [PATCH 04/53] chore: remove the setFiles/getFiles methods from the cache interface --- .../solidity/build-system/cache.ts | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts index 574cdc6bd2..6b0ee8a1da 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -1,12 +1,10 @@ import path from "node:path"; -import { assertHardhatInvariant } from "@ignored/hardhat-vnext-errors"; import { exists, readJsonFile, writeJsonFile, } from "@ignored/hardhat-vnext-utils/fs"; -import AdmZip from "adm-zip"; export class Cache { readonly #basePath: string; @@ -36,42 +34,4 @@ export class Cache { const filePath = this.#getPath(key); return (await this.has(key)) ? readJsonFile(filePath) : undefined; } - - public async setFiles( - key: string, - rootPath: string, - filePaths: string[], - ): Promise { - const zipFilePath = this.#getPath(key); - const zip = new AdmZip(); - for (const filePath of filePaths) { - zip.addLocalFile( - filePath, - path.dirname(path.relative(rootPath, filePath)), - ); - } - const zipFileCreated = await zip.writeZipPromise(zipFilePath, { - overwrite: true, - }); - assertHardhatInvariant( - zipFileCreated, - `Failed to create zip file ${zipFilePath}`, - ); - } - - public async getFiles( - key: string, - rootPath: string, - ): Promise { - if (await this.has(key)) { - const zipFilePath = this.#getPath(key); - const zip = new AdmZip(zipFilePath); - zip.extractAllTo(rootPath, true); - const filePaths = zip - .getEntries() - .map((entry) => path.join(rootPath, entry.entryName)); - return filePaths; - } - return undefined; - } } From a622510cba35eafa47ccbea49c7153510bc2b71e Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 15:02:18 +0100 Subject: [PATCH 05/53] chore: remove the artifacts cache --- .../solidity/build-system/build-system.ts | 85 +++---------------- .../src/types/solidity/build-system.ts | 6 -- 2 files changed, 10 insertions(+), 81 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index 5378a8fe4c..f62bb05d7c 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -71,7 +71,6 @@ export interface SolidityBuildSystemOptions { export class SolidityBuildSystemImplementation implements SolidityBuildSystem { readonly #options: SolidityBuildSystemOptions; readonly #compilerOutputCache: Cache; - readonly #artifactsCache: Cache; readonly #defaultConcurrency = Math.max(os.cpus().length - 1, 1); #downloadedCompilers = false; @@ -82,11 +81,6 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { "hardhat.core.solidity.build-system.compiler-output", "v1", ); - this.#artifactsCache = new Cache( - options.cachePath, - "hardhat.core.solidity.build-system.artifacts", - "v1", - ); } public async getRootFilePaths(): Promise { @@ -134,12 +128,11 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { force: options?.force, quiet: options?.quiet, }; - const results: Array = await pMap( + const results: CompilerOutput[] = await pMap( compilationJobs, async (compilationJob) => - (await this.#artifactsCache.has(compilationJob.getBuildId())) - ? undefined - : this.runCompilationJob(compilationJob, runCompilationJobOptions), + (await this.#compilerOutputCache.getJson(compilationJob.getBuildId())) ?? + this.runCompilationJob(compilationJob, runCompilationJobOptions), { concurrency: options?.concurrency ?? this.#defaultConcurrency, // An error when running the compiler is not a compilation failure, but @@ -149,7 +142,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { ); const isSuccessfulBuild = results.every( - (result) => result === undefined || !this.#hasCompilationErrors(result), + (result) => !this.#hasCompilationErrors(result), ); const contractArtifactsGeneratedByCompilationJob: Map< @@ -160,33 +153,16 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { if (isSuccessfulBuild) { log("Emitting artifacts of successful build"); const emitArtifactsOptions: EmitArtifactsOptions = { - force: options?.force, quiet: options?.quiet, }; await Promise.all( compilationJobs.map(async (compilationJob, i) => { const result = results[i]; - let artifactsPerFile; - if (result === undefined) { - const cachedFiles = await this.#artifactsCache.getFiles( - compilationJob.getBuildId(), - this.#options.artifactsPath, - ); - - assertHardhatInvariant( - cachedFiles !== undefined, - "We checked if the compilation job was cached before", - ); - - artifactsPerFile = - await this.#groupEmitArtifactsResults(cachedFiles); - } else { - artifactsPerFile = await this.emitArtifacts( - compilationJob, - result, - emitArtifactsOptions, - ); - } + const artifactsPerFile = await this.emitArtifacts( + compilationJob, + result, + emitArtifactsOptions, + ); contractArtifactsGeneratedByCompilationJob.set( compilationJob, @@ -240,17 +216,6 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { continue; } - if (result === undefined) { - resultsMap.set(formatRootPath(publicSourceName, root), { - type: FileBuildResultType.CACHE_HIT, - buildId, - contractArtifactsGenerated: - contractArtifactsGenerated.get(publicSourceName) ?? [], - }); - - continue; - } - resultsMap.set(formatRootPath(publicSourceName, root), { type: FileBuildResultType.BUILD_SUCCESS, buildId, @@ -475,24 +440,11 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { public async emitArtifacts( compilationJob: CompilationJob, compilerOutput: CompilerOutput, - options?: EmitArtifactsOptions, + _options?: EmitArtifactsOptions, ): Promise> { const result = new Map(); const buildId = compilationJob.getBuildId(); - if (options?.force !== true) { - const cachedFiles = await this.#artifactsCache.getFiles( - buildId, - this.#options.artifactsPath, - ); - if (cachedFiles !== undefined) { - log(`Using cached artifacts for build ${buildId}`); - return this.#groupEmitArtifactsResults(cachedFiles); - } - } - - const filesToCache: string[] = []; - // We emit the artifacts for each root file, first emitting one artifact // for each contract, and then one declaration file for the entire file, // which defines their types and augments the ArtifactMap type. @@ -551,15 +503,6 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { artifactsDeclarationFilePath, artifactsDeclarationFile, ); - - if (paths.length === 0) { - filesToCache.push( - path.join(this.#options.artifactsPath, publicSourceName), - ); - } else { - filesToCache.push(...paths); - } - filesToCache.push(artifactsDeclarationFilePath); } // Once we have emitted all the contract artifacts and its declaration @@ -604,14 +547,6 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { })(), ]); - filesToCache.push(buildInfoPath, buildInfoOutputPath); - - await this.#artifactsCache.setFiles( - buildId, - this.#options.artifactsPath, - filesToCache, - ); - return result; } diff --git a/v-next/hardhat/src/types/solidity/build-system.ts b/v-next/hardhat/src/types/solidity/build-system.ts index 5bd065b0b5..0707ac0dac 100644 --- a/v-next/hardhat/src/types/solidity/build-system.ts +++ b/v-next/hardhat/src/types/solidity/build-system.ts @@ -78,12 +78,6 @@ export interface RunCompilationJobOptions { * The options of the `emitArtifacts` method. */ export interface EmitArtifactsOptions { - /** - * If `true`, this option foces the build system to recreate the artifacts, - * even if they are cached. - */ - force?: boolean; - /** * If `true`, the emit process doesn't print any output. */ From 7a20c123e3a4fce1169da7bb5fdac2defb5eb63c Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 15:05:10 +0100 Subject: [PATCH 06/53] chore: remove compiler output cache from run compilation job --- .../solidity/build-system/build-system.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index f62bb05d7c..e9a83d3d0c 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -338,17 +338,6 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { compilationJob: CompilationJob, options?: RunCompilationJobOptions, ): Promise { - const buildId = compilationJob.getBuildId(); - - if (options?.force !== true) { - const cachedCompilerOutput = - await this.#compilerOutputCache.getJson(buildId); - if (cachedCompilerOutput !== undefined) { - log(`Using cached compiler output for build ${buildId}`); - return cachedCompilerOutput; - } - } - await this.#downloadConfiguredCompilers(options?.quiet); let numberOfFiles = 0; @@ -373,10 +362,6 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { compilationJob.getSolcInput(), ); - if (!this.#hasCompilationErrors(compilerOutput)) { - await this.#compilerOutputCache.setJson(buildId, compilerOutput); - } - return compilerOutput; } From 7c4f198c3a658c3cb1fbb5295a13b96f49cab9c1 Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 15:08:15 +0100 Subject: [PATCH 07/53] chore: add saving to cache to the build function --- .../solidity/build-system/build-system.ts | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index e9a83d3d0c..80ecd4a3e7 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -130,9 +130,26 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { }; const results: CompilerOutput[] = await pMap( compilationJobs, - async (compilationJob) => - (await this.#compilerOutputCache.getJson(compilationJob.getBuildId())) ?? - this.runCompilationJob(compilationJob, runCompilationJobOptions), + async (compilationJob) => { + const buildId = compilationJob.getBuildId(); + + if (runCompilationJobOptions?.force !== true) { + const cachedCompilerOutput = + await this.#compilerOutputCache.getJson(buildId); + if (cachedCompilerOutput !== undefined) { + log(`Using cached compiler output for build ${buildId}`); + return cachedCompilerOutput; + } + } + + const compilerOutput = await this.runCompilationJob(compilationJob, runCompilationJobOptions); + + if (!this.#hasCompilationErrors(compilerOutput)) { + await this.#compilerOutputCache.setJson(buildId, compilerOutput); + } + + return compilerOutput; + }, { concurrency: options?.concurrency ?? this.#defaultConcurrency, // An error when running the compiler is not a compilation failure, but From 100579ecf6d98410a34b3ef51c70bc7a670e760f Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 15:46:56 +0100 Subject: [PATCH 08/53] feat: add cache cleanup function --- v-next/hardhat-utils/src/fs.ts | 44 +++++++++++++++ .../solidity/build-system/build-system.ts | 39 +++++++++++--- .../solidity/build-system/cache.ts | 53 ++++++++++++++----- .../src/types/solidity/build-system.ts | 1 + 4 files changed, 117 insertions(+), 20 deletions(-) diff --git a/v-next/hardhat-utils/src/fs.ts b/v-next/hardhat-utils/src/fs.ts index f38224e3fa..ccc4525722 100644 --- a/v-next/hardhat-utils/src/fs.ts +++ b/v-next/hardhat-utils/src/fs.ts @@ -407,6 +407,50 @@ export async function getChangeTime(absolutePath: string): Promise { } } +/** + * Retrieves the last access time of a file or directory's properties. + * + * @param absolutePath The absolute path to the file or directory. + * @returns The time of the last access as a Date object. + * @throws FileNotFoundError if the path does not exist. + * @throws FileSystemAccessError for any other error. + */ +export async function getAccessTime(absolutePath: string): Promise { + try { + const stats = await fsPromises.stat(absolutePath); + return stats.atime; + } catch (e) { + ensureError(e); + if (e.code === "ENOENT") { + throw new FileNotFoundError(absolutePath, e); + } + + throw new FileSystemAccessError(e.message, e); + } +} + +/** + * Retrieves the size of a file. + * + * @param absolutePath The absolute path to the file. + * @returns The size of the file in bytes. + * @throws FileNotFoundError if the path does not exist. + * @throws FileSystemAccessError for any other error. + */ +export async function getSize(absolutePath: string): Promise { + try { + const stats = await fsPromises.stat(absolutePath); + return stats.size; + } catch (e) { + ensureError(e); + if (e.code === "ENOENT") { + throw new FileNotFoundError(absolutePath, e); + } + + throw new FileSystemAccessError(e.message, e); + } +} + /** * Checks if a file or directory exists. * diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index 80ecd4a3e7..777901852c 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -60,6 +60,11 @@ import { SolcConfigSelector } from "./solc-config-selection.js"; const log = debug("hardhat:core:solidity:build-system"); +interface CompilationResult { + compilerOutput: CompilerOutput, + cached: boolean +} + export interface SolidityBuildSystemOptions { readonly solidityConfig: SolidityConfig; readonly projectRoot: string; @@ -128,7 +133,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { force: options?.force, quiet: options?.quiet, }; - const results: CompilerOutput[] = await pMap( + const results: CompilationResult[] = await pMap( compilationJobs, async (compilationJob) => { const buildId = compilationJob.getBuildId(); @@ -138,7 +143,10 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { await this.#compilerOutputCache.getJson(buildId); if (cachedCompilerOutput !== undefined) { log(`Using cached compiler output for build ${buildId}`); - return cachedCompilerOutput; + return { + compilerOutput: cachedCompilerOutput, + cached: true, + }; } } @@ -148,7 +156,10 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { await this.#compilerOutputCache.setJson(buildId, compilerOutput); } - return compilerOutput; + return { + compilerOutput, + cached: false, + }; }, { concurrency: options?.concurrency ?? this.#defaultConcurrency, @@ -158,8 +169,10 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { }, ); + void this.#compilerOutputCache.clean(); + const isSuccessfulBuild = results.every( - (result) => !this.#hasCompilationErrors(result), + (result) => !this.#hasCompilationErrors(result.compilerOutput), ); const contractArtifactsGeneratedByCompilationJob: Map< @@ -177,7 +190,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { const result = results[i]; const artifactsPerFile = await this.emitArtifacts( compilationJob, - result, + result.compilerOutput, emitArtifactsOptions, ); @@ -209,7 +222,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { const errors = result !== undefined ? await Promise.all( - (result.errors ?? []).map((error) => + (result.compilerOutput.errors ?? []).map((error) => this.remapCompilerError(compilationJob, error, true), ), ) @@ -218,7 +231,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { this.#printSolcErrorsAndWarnings(errors); const successfulResult = - result === undefined || !this.#hasCompilationErrors(result); + result === undefined || !this.#hasCompilationErrors(result.compilerOutput); for (const [publicSourceName, root] of compilationJob.dependencyGraph .getRoots() @@ -233,6 +246,18 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { continue; } + if (result.cached) { + resultsMap.set(formatRootPath(publicSourceName, root), { + type: FileBuildResultType.CACHE_HIT, + buildId, + contractArtifactsGenerated: + contractArtifactsGenerated.get(publicSourceName) ?? [], + warnings: errors, + }); + + continue; + } + resultsMap.set(formatRootPath(publicSourceName, root), { type: FileBuildResultType.BUILD_SUCCESS, buildId, diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts index 6b0ee8a1da..b0a8270b7b 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -2,36 +2,63 @@ import path from "node:path"; import { exists, + getAccessTime, + getAllFilesMatching, + getSize, readJsonFile, + remove, writeJsonFile, } from "@ignored/hardhat-vnext-utils/fs"; export class Cache { - readonly #basePath: string; - readonly #namespace: string; - readonly #version: string; + readonly #path: string; + readonly #maxAgeMs: number = 0; + readonly #maxSize: number = 0; constructor(basePath: string, namespace: string, version: string) { - this.#basePath = basePath; - this.#namespace = namespace; - this.#version = version; - } - - #getPath(key: string): string { - return path.join(this.#basePath, this.#namespace, this.#version, key); + this.#path = path.join(basePath, namespace, version); } public async has(key: string): Promise { - return exists(this.#getPath(key)); + return exists(path.join(this.#path, key)); } public async setJson(key: string, value: T): Promise { - const filePath = this.#getPath(key); + const filePath = path.join(this.#path, key); await writeJsonFile(filePath, value); } public async getJson(key: string): Promise { - const filePath = this.#getPath(key); + const filePath = path.join(this.#path, key); return (await this.has(key)) ? readJsonFile(filePath) : undefined; } + + public async clean(): Promise { + const files = await getAllFilesMatching(this.#path); + const fileInfos = await Promise.all( + files.map(async (file) => ({ + file, + atimeMs: (await getAccessTime(file)).getTime(), + size: await getSize(file), + })), + ); + + const sortedFileInfos = fileInfos.sort((a, b) => a.atimeMs - b.atimeMs); + + let size = sortedFileInfos.reduce((acc, fileInfo) => acc + fileInfo.size, 0); + const minAtimeMs = (new Date(0 - this.#maxAgeMs)).getTime(); + + const filesToRemove: string[] = []; + + for (const fileInfo of sortedFileInfos) { + if (fileInfo.atimeMs < minAtimeMs || size > this.#maxSize) { + filesToRemove.push(fileInfo.file); + size -= fileInfo.size; + } else { + break; + } + } + + await Promise.all(filesToRemove.map(async (file) => remove(file))); + } } diff --git a/v-next/hardhat/src/types/solidity/build-system.ts b/v-next/hardhat/src/types/solidity/build-system.ts index 0707ac0dac..a6a4fb38d5 100644 --- a/v-next/hardhat/src/types/solidity/build-system.ts +++ b/v-next/hardhat/src/types/solidity/build-system.ts @@ -165,6 +165,7 @@ export interface CacheHitFileBuildResult { type: FileBuildResultType.CACHE_HIT; buildId: string; contractArtifactsGenerated: string[]; + warnings: CompilerOutputError[]; } export interface SuccessfulFileBuildResult { From 0787fcc37df860daee6ef8d226e641e589548750 Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 15:57:22 +0100 Subject: [PATCH 09/53] chore: clean cache only after all the writes are complete --- .../solidity/build-system/build-system.ts | 69 +++++++++++-------- .../solidity/build-system/cache.ts | 7 +- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index 777901852c..0b69bd6c3f 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -61,8 +61,9 @@ import { SolcConfigSelector } from "./solc-config-selection.js"; const log = debug("hardhat:core:solidity:build-system"); interface CompilationResult { - compilerOutput: CompilerOutput, - cached: boolean + compilationJob: CompilationJob; + compilerOutput: CompilerOutput; + cached: boolean; } export interface SolidityBuildSystemOptions { @@ -144,19 +145,20 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { if (cachedCompilerOutput !== undefined) { log(`Using cached compiler output for build ${buildId}`); return { + compilationJob, compilerOutput: cachedCompilerOutput, cached: true, }; } } - const compilerOutput = await this.runCompilationJob(compilationJob, runCompilationJobOptions); - - if (!this.#hasCompilationErrors(compilerOutput)) { - await this.#compilerOutputCache.setJson(buildId, compilerOutput); - } + const compilerOutput = await this.runCompilationJob( + compilationJob, + runCompilationJobOptions, + ); return { + compilationJob, compilerOutput, cached: false, }; @@ -169,12 +171,27 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { }, ); - void this.#compilerOutputCache.clean(); - - const isSuccessfulBuild = results.every( + const uncachedResults = results.filter((result) => !result.cached); + const uncachedSuccessfulResults = uncachedResults.filter( (result) => !this.#hasCompilationErrors(result.compilerOutput), ); + // NOTE: We're not waiting for the writes and clean to finish because we + // will only care about the result of these operations in subsequent runs + void Promise.all( + uncachedSuccessfulResults.map(async (result) => { + return this.#compilerOutputCache.setJson( + result.compilationJob.getBuildId(), + result.compilerOutput, + ); + }), + ).then(() => { + return this.#compilerOutputCache.clean(); + }); + + const isSuccessfulBuild = + uncachedResults.length === uncachedSuccessfulResults.length; + const contractArtifactsGeneratedByCompilationJob: Map< CompilationJob, ReadonlyMap @@ -204,12 +221,9 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { const resultsMap: Map = new Map(); - for (let i = 0; i < compilationJobs.length; i++) { - const compilationJob = compilationJobs[i]; - const result = results[i]; - + for (const result of results) { const contractArtifactsGenerated = isSuccessfulBuild - ? contractArtifactsGeneratedByCompilationJob.get(compilationJob) + ? contractArtifactsGeneratedByCompilationJob.get(result.compilationJob) : new Map(); assertHardhatInvariant( @@ -217,25 +231,24 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { "We emitted contract artifacts for all the jobs if the build was successful", ); - const buildId = compilationJob.getBuildId(); + const buildId = result.compilationJob.getBuildId(); - const errors = - result !== undefined - ? await Promise.all( - (result.compilerOutput.errors ?? []).map((error) => - this.remapCompilerError(compilationJob, error, true), - ), - ) - : []; + const errors = await Promise.all( + (result.compilerOutput.errors ?? []).map((error) => + this.remapCompilerError(result.compilationJob, error, true), + ), + ); this.#printSolcErrorsAndWarnings(errors); const successfulResult = - result === undefined || !this.#hasCompilationErrors(result.compilerOutput); + result === undefined || + !this.#hasCompilationErrors(result.compilerOutput); - for (const [publicSourceName, root] of compilationJob.dependencyGraph - .getRoots() - .entries()) { + for (const [ + publicSourceName, + root, + ] of result.compilationJob.dependencyGraph.getRoots().entries()) { if (!successfulResult) { resultsMap.set(formatRootPath(publicSourceName, root), { type: FileBuildResultType.BUILD_FAILURE, diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts index b0a8270b7b..17a0f53183 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -45,8 +45,11 @@ export class Cache { const sortedFileInfos = fileInfos.sort((a, b) => a.atimeMs - b.atimeMs); - let size = sortedFileInfos.reduce((acc, fileInfo) => acc + fileInfo.size, 0); - const minAtimeMs = (new Date(0 - this.#maxAgeMs)).getTime(); + let size = sortedFileInfos.reduce( + (acc, fileInfo) => acc + fileInfo.size, + 0, + ); + const minAtimeMs = new Date(0 - this.#maxAgeMs).getTime(); const filesToRemove: string[] = []; From ed3328ae27469bb555ddab29914e4d2a5be7739f Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 15:58:20 +0100 Subject: [PATCH 10/53] chore: remove unused has method from cache --- .../internal/builtin-plugins/solidity/build-system/cache.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts index 17a0f53183..c0c6cf7c21 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -19,10 +19,6 @@ export class Cache { this.#path = path.join(basePath, namespace, version); } - public async has(key: string): Promise { - return exists(path.join(this.#path, key)); - } - public async setJson(key: string, value: T): Promise { const filePath = path.join(this.#path, key); await writeJsonFile(filePath, value); @@ -30,7 +26,7 @@ export class Cache { public async getJson(key: string): Promise { const filePath = path.join(this.#path, key); - return (await this.has(key)) ? readJsonFile(filePath) : undefined; + return (await exists(filePath)) ? readJsonFile(filePath) : undefined; } public async clean(): Promise { From cd0adef9b6598c5b0bdfe31ee912fbcec9d24422 Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 16:00:35 +0100 Subject: [PATCH 11/53] chore: move cache value type declaration to the constructor --- .../solidity/build-system/build-system.ts | 10 +++++----- .../builtin-plugins/solidity/build-system/cache.ts | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index 0b69bd6c3f..ed77b6ef3e 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -46,7 +46,7 @@ import { getContractArtifact, getDuplicatedContractNamesDeclarationFile, } from "./artifacts.js"; -import { Cache } from "./cache.js"; +import { ObjectCache } from "./cache.js"; import { CompilationJobImplementation } from "./compilation-job.js"; import { downloadConfiguredCompilers, getCompiler } from "./compiler/index.js"; import { buildDependencyGraph } from "./dependency-graph-building.js"; @@ -76,13 +76,13 @@ export interface SolidityBuildSystemOptions { export class SolidityBuildSystemImplementation implements SolidityBuildSystem { readonly #options: SolidityBuildSystemOptions; - readonly #compilerOutputCache: Cache; + readonly #compilerOutputCache: ObjectCache; readonly #defaultConcurrency = Math.max(os.cpus().length - 1, 1); #downloadedCompilers = false; constructor(options: SolidityBuildSystemOptions) { this.#options = options; - this.#compilerOutputCache = new Cache( + this.#compilerOutputCache = new ObjectCache( options.cachePath, "hardhat.core.solidity.build-system.compiler-output", "v1", @@ -141,7 +141,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { if (runCompilationJobOptions?.force !== true) { const cachedCompilerOutput = - await this.#compilerOutputCache.getJson(buildId); + await this.#compilerOutputCache.get(buildId); if (cachedCompilerOutput !== undefined) { log(`Using cached compiler output for build ${buildId}`); return { @@ -180,7 +180,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { // will only care about the result of these operations in subsequent runs void Promise.all( uncachedSuccessfulResults.map(async (result) => { - return this.#compilerOutputCache.setJson( + return this.#compilerOutputCache.set( result.compilationJob.getBuildId(), result.compilerOutput, ); diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts index c0c6cf7c21..8b1f67fdfd 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -10,7 +10,7 @@ import { writeJsonFile, } from "@ignored/hardhat-vnext-utils/fs"; -export class Cache { +export class ObjectCache { readonly #path: string; readonly #maxAgeMs: number = 0; readonly #maxSize: number = 0; @@ -19,12 +19,12 @@ export class Cache { this.#path = path.join(basePath, namespace, version); } - public async setJson(key: string, value: T): Promise { + public async set(key: string, value: T): Promise { const filePath = path.join(this.#path, key); await writeJsonFile(filePath, value); } - public async getJson(key: string): Promise { + public async get(key: string): Promise { const filePath = path.join(this.#path, key); return (await exists(filePath)) ? readJsonFile(filePath) : undefined; } From 73ba8e0e3d0b851580f059f3b71f578731393946 Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 16:01:13 +0100 Subject: [PATCH 12/53] chore: remove unused function from build system implementation --- .../solidity/build-system/build-system.ts | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index ed77b6ef3e..93f7697be2 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -25,7 +25,6 @@ import { assertHardhatInvariant } from "@ignored/hardhat-vnext-errors"; import { getAllDirectoriesMatching, getAllFilesMatching, - isDirectory, readJsonFile, remove, writeUtf8File, @@ -451,32 +450,6 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { }; } - async #groupEmitArtifactsResults( - filePaths: string[], - ): Promise> { - const result = new Map(); - - for (const filePath of filePaths) { - const relativePath = path.relative(this.#options.artifactsPath, filePath); - if ( - path.dirname(relativePath) === "build-info" || - path.basename(relativePath) === "artifacts.d.ts" - ) { - continue; - } - if (await isDirectory(filePath)) { - result.set(relativePath, []); - } else { - const publicSourceName = path.dirname(relativePath); - const paths = result.get(publicSourceName) ?? []; - paths.push(filePath); - result.set(publicSourceName, paths); - } - } - - return result; - } - public async emitArtifacts( compilationJob: CompilationJob, compilerOutput: CompilerOutput, From b224e1a52f12d6c5676a4b550b0265fb3515b7ba Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 16:13:09 +0100 Subject: [PATCH 13/53] chore: simplify successful result check --- .../builtin-plugins/solidity/build-system/build-system.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index 93f7697be2..0ab194de9e 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -240,9 +240,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { this.#printSolcErrorsAndWarnings(errors); - const successfulResult = - result === undefined || - !this.#hasCompilationErrors(result.compilerOutput); + const successfulResult = !this.#hasCompilationErrors(result.compilerOutput); for (const [ publicSourceName, From 41bddca59a06f05f576391fb080ef5da1ab02853 Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 16:22:19 +0100 Subject: [PATCH 14/53] feat: make getBuildId and getSolcInput on CompilationJob async --- .../solidity/build-system/artifacts.ts | 14 ++++---- .../solidity/build-system/build-system.ts | 33 ++++++++++++------- .../solidity/build-system/compilation-job.ts | 12 +++---- .../src/types/solidity/compilation-job.ts | 4 +-- .../solidity/build-system/build-system.ts | 8 ++--- 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/artifacts.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/artifacts.ts index 1a2e6914cd..c7e902c69f 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/artifacts.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/artifacts.ts @@ -104,9 +104,9 @@ declare module "@ignored/hardhat-vnext/types/artifacts" { }`; } -export function getBuildInfo( +export async function getBuildInfo( compilationJob: CompilationJob, -): SolidityBuildInfo { +): Promise { const publicSourceNameMap = Object.fromEntries( [...compilationJob.dependencyGraph.getRoots().entries()].map( ([publicSourceName, root]) => [publicSourceName, root.sourceName], @@ -115,23 +115,23 @@ export function getBuildInfo( const buildInfo: Required = { _format: "hh3-sol-build-info-1", - id: compilationJob.getBuildId(), + id: await compilationJob.getBuildId(), solcVersion: compilationJob.solcConfig.version, solcLongVersion: compilationJob.solcLongVersion, publicSourceNameMap, - input: compilationJob.getSolcInput(), + input: await compilationJob.getSolcInput(), }; return buildInfo; } -export function getBuildInfoOutput( +export async function getBuildInfoOutput( compilationJob: CompilationJob, compilerOutput: CompilerOutput, -): SolidityBuildInfoOutput { +): Promise { const buildInfoOutput: Required = { _format: "hh3-sol-build-info-output-1", - id: compilationJob.getBuildId(), + id: await compilationJob.getBuildId(), output: compilerOutput, }; diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index 0ab194de9e..cb15fbb162 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -129,6 +129,12 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { const compilationJobs = [...new Set(compilationJobsPerFile.values())]; + await Promise.all( + compilationJobs.map(async (compilationJob) => + compilationJob.getBuildId(), + ), + ); + const runCompilationJobOptions: RunCompilationJobOptions = { force: options?.force, quiet: options?.quiet, @@ -136,7 +142,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { const results: CompilationResult[] = await pMap( compilationJobs, async (compilationJob) => { - const buildId = compilationJob.getBuildId(); + const buildId = await compilationJob.getBuildId(); if (runCompilationJobOptions?.force !== true) { const cachedCompilerOutput = @@ -180,7 +186,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { void Promise.all( uncachedSuccessfulResults.map(async (result) => { return this.#compilerOutputCache.set( - result.compilationJob.getBuildId(), + await result.compilationJob.getBuildId(), result.compilerOutput, ); }), @@ -230,7 +236,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { "We emitted contract artifacts for all the jobs if the build was successful", ); - const buildId = result.compilationJob.getBuildId(); + const buildId = await result.compilationJob.getBuildId(); const errors = await Promise.all( (result.compilerOutput.errors ?? []).map((error) => @@ -240,7 +246,9 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { this.#printSolcErrorsAndWarnings(errors); - const successfulResult = !this.#hasCompilationErrors(result.compilerOutput); + const successfulResult = !this.#hasCompilationErrors( + result.compilerOutput, + ); for (const [ publicSourceName, @@ -280,7 +288,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { if (options?.quiet !== true) { if (isSuccessfulBuild) { - this.#printCompilationResult(compilationJobs); + await this.#printCompilationResult(compilationJobs); } } @@ -411,7 +419,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { ); const compilerOutput = await compiler.compile( - compilationJob.getSolcInput(), + await compilationJob.getSolcInput(), ); return compilerOutput; @@ -454,7 +462,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { _options?: EmitArtifactsOptions, ): Promise> { const result = new Map(); - const buildId = compilationJob.getBuildId(); + const buildId = await compilationJob.getBuildId(); // We emit the artifacts for each root file, first emitting one artifact // for each contract, and then one declaration file for the entire file, @@ -484,7 +492,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { ); const artifact = getContractArtifact( - compilationJob.getBuildId(), + await compilationJob.getBuildId(), publicSourceName, root.sourceName, contractName, @@ -536,7 +544,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { // concurrently, and keep their lifetimes sperated and small. await Promise.all([ (async () => { - const buildInfo = getBuildInfo(compilationJob); + const buildInfo = await getBuildInfo(compilationJob); await writeUtf8File( buildInfoPath, @@ -546,7 +554,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { ); })(), (async () => { - const buildInfoOutput = getBuildInfoOutput( + const buildInfoOutput = await getBuildInfoOutput( compilationJob, compilerOutput, ); @@ -749,7 +757,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { } } - #printCompilationResult(compilationJobs: CompilationJob[]) { + async #printCompilationResult(compilationJobs: CompilationJob[]) { const jobsPerVersionAndEvmVersion = new Map< string, Map @@ -757,8 +765,9 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { for (const job of compilationJobs) { const solcVersion = job.solcConfig.version; + const solcInput = await job.getSolcInput(); const evmVersion = - job.getSolcInput().settings.evmVersion ?? + solcInput.settings.evmVersion ?? `Check solc ${solcVersion}'s doc for its default evm version`; let jobsPerVersion = jobsPerVersionAndEvmVersion.get(solcVersion); diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index 268bf15fa4..c469deb3ec 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -33,23 +33,23 @@ export class CompilationJobImplementation implements CompilationJob { this.#remappings = remappings; } - public getSolcInput(): CompilerInput { + public async getSolcInput(): Promise { if (this.#solcInput === undefined) { - this.#solcInput = this.#buildSolcInput(); + this.#solcInput = await this.#buildSolcInput(); } return this.#solcInput; } - public getBuildId(): string { + public async getBuildId(): Promise { if (this.#buildId === undefined) { - this.#buildId = this.#computeBuildId(); + this.#buildId = await this.#computeBuildId(); } return this.#buildId; } - #buildSolcInput(): CompilerInput { + async #buildSolcInput(): Promise { const sources: { [sourceName: string]: { content: string } } = {}; // we sort the files so that we always get the same compilation input @@ -108,7 +108,7 @@ export class CompilationJobImplementation implements CompilationJob { }; } - #computeBuildId(): string { + async #computeBuildId(): Promise { // NOTE: We type it this way so that this stop compiling if we ever change // the format of the BuildInfo type. const format: BuildInfo["_format"] = "hh3-sol-build-info-1"; diff --git a/v-next/hardhat/src/types/solidity/compilation-job.ts b/v-next/hardhat/src/types/solidity/compilation-job.ts index fab052de6e..c1c0d523e5 100644 --- a/v-next/hardhat/src/types/solidity/compilation-job.ts +++ b/v-next/hardhat/src/types/solidity/compilation-job.ts @@ -25,7 +25,7 @@ export interface CompilationJob { /** * Returns the solc input to be used. */ - getSolcInput(): CompilerInput; + getSolcInput(): Promise; /** * Returns the build id of the compilation job. @@ -37,5 +37,5 @@ export interface CompilationJob { * While deterministic, it shouldn't be expected to be stable across different * versions of Hardhat. */ - getBuildId(): string; + getBuildId(): Promise; } diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts index dbeb83dda2..3c09378abb 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -33,15 +33,11 @@ async function emitArtifacts(solidity: SolidityBuildSystem): Promise { const buildIds = new Set(); for (const compilationJob of compilationJobs.values()) { - const buildId = compilationJob.getBuildId(); + const buildId = await compilationJob.getBuildId(); if (!buildIds.has(buildId)) { buildIds.add(buildId); const buildInfoOutput = await readJsonFile( - path.join( - artifactsPath, - "build-info", - `${compilationJob.getBuildId()}.output.json`, - ), + path.join(artifactsPath, "build-info", `${buildId}.output.json`), ); await solidity.emitArtifacts(compilationJob, buildInfoOutput.output); } From 96096b25f8a7c4989913ee8a72c3e53129194ab3 Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 16:34:05 +0100 Subject: [PATCH 15/53] chore: add helper functions for getting part of solc inputs --- .../solidity/build-system/compilation-job.ts | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index c469deb3ec..924524088d 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -5,6 +5,7 @@ import type { SolcConfig } from "../../../../types/config.js"; import type { CompilationJob } from "../../../../types/solidity/compilation-job.js"; import type { CompilerInput } from "../../../../types/solidity/compiler-io.js"; import type { DependencyGraph } from "../../../../types/solidity/dependency-graph.js"; +import type { ResolvedFile } from "../../../../types/solidity.js"; import { createNonCryptographicHashId } from "@ignored/hardhat-vnext-utils/crypto"; @@ -20,6 +21,8 @@ export class CompilationJobImplementation implements CompilationJob { #buildId: string | undefined; #solcInput: CompilerInput | undefined; + #solcInputWithoutSources: Omit | undefined; + #resolvedFiles: ResolvedFile[] | undefined; constructor( dependencyGraph: DependencyGraphImplementation, @@ -35,7 +38,7 @@ export class CompilationJobImplementation implements CompilationJob { public async getSolcInput(): Promise { if (this.#solcInput === undefined) { - this.#solcInput = await this.#buildSolcInput(); + this.#solcInput = this.#buildSolcInput(); } return this.#solcInput; @@ -49,13 +52,31 @@ export class CompilationJobImplementation implements CompilationJob { return this.#buildId; } - async #buildSolcInput(): Promise { + #getSolcInputWithoutSources(): Omit { + if (this.#solcInputWithoutSources === undefined) { + this.#solcInputWithoutSources = this.#buildSolcInputWithoutSources(); + } + + return this.#solcInputWithoutSources; + } + + #getResolvedFiles(): ResolvedFile[] { + if (this.#resolvedFiles === undefined) { + // we sort the files so that we always get the same compilation input + this.#resolvedFiles = [...this.dependencyGraph.getAllFiles()].sort( + (a, b) => a.sourceName.localeCompare(b.sourceName), + ); + } + + return this.#resolvedFiles; + } + + #buildSolcInput(): CompilerInput { + const solcInputWithoutSources = this.#getSolcInputWithoutSources(); + const sources: { [sourceName: string]: { content: string } } = {}; - // we sort the files so that we always get the same compilation input - const resolvedFiles = [...this.dependencyGraph.getAllFiles()].sort((a, b) => - a.sourceName.localeCompare(b.sourceName), - ); + const resolvedFiles = this.#getResolvedFiles(); for (const file of resolvedFiles) { sources[file.sourceName] = { @@ -63,6 +84,13 @@ export class CompilationJobImplementation implements CompilationJob { }; } + return { + ...solcInputWithoutSources, + sources, + }; + } + + #buildSolcInputWithoutSources(): Omit { const settings = this.solcConfig.settings; const rootsOutputSelection: CompilerInput["settings"]["outputSelection"] = @@ -96,7 +124,6 @@ export class CompilationJobImplementation implements CompilationJob { return { language: "Solidity", - sources, settings: { ...settings, evmVersion: From 0f4974a46295a6ffbf1fb8c4fc70445cf4d159b9 Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 16:42:57 +0100 Subject: [PATCH 16/53] feat: make hash generation asynchronous --- v-next/hardhat-utils/src/crypto.ts | 17 ++++++++++------- v-next/hardhat-utils/test/crypto.ts | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/v-next/hardhat-utils/src/crypto.ts b/v-next/hardhat-utils/src/crypto.ts index 02366f1f80..ff8889c2a0 100644 --- a/v-next/hardhat-utils/src/crypto.ts +++ b/v-next/hardhat-utils/src/crypto.ts @@ -1,5 +1,3 @@ -import { createHash } from "node:crypto"; - import { keccak256 as keccak256Impl } from "ethereum-cryptography/keccak"; /** @@ -17,17 +15,22 @@ export async function keccak256(bytes: Uint8Array): Promise { * * This function is primarily intended for generating unique identifiers from * a given input string. - * It uses the MD5 hash algorithm, which is not cryptographically secure, but + * It uses the SHA-1 hash algorithm, which is not cryptographically secure, but * is sufficient for this use case as long as the input is not generated by an * attacker. * - * Note: The exact algorithm used (MD5) is not crucial for the function's + * Note: The exact algorithm used (SHA-1) is not crucial for the function's * purpose of generating unique identifiers, and could be replaced if needed. * * @param data The input string to be hashed. - * @returns The MD5 hash of the input string, represented as a + * @returns The SHA-1 hash of the input string, represented as a * hexadecimal string. */ -export function createNonCryptographicHashId(data: string): string { - return createHash("md5").update(data).digest("hex"); +export async function createNonCryptographicHashId(data: string): Promise { + const message = new TextEncoder().encode(data); + const buffer = await crypto.subtle.digest("SHA-1", message); + const array = Array.from(new Uint8Array(buffer)); + return array + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); } diff --git a/v-next/hardhat-utils/test/crypto.ts b/v-next/hardhat-utils/test/crypto.ts index 77e4aca079..27f4dbb8db 100644 --- a/v-next/hardhat-utils/test/crypto.ts +++ b/v-next/hardhat-utils/test/crypto.ts @@ -16,9 +16,9 @@ describe("crypto", () => { }); describe("createNonCryptographicHashId", () => { - it("Should create a non-cryptographic hash-based identifier", () => { + it("Should create a non-cryptographic hash-based identifier", async () => { assert.equal( - createNonCryptographicHashId("hello"), + await createNonCryptographicHashId("hello"), "5d41402abc4b2a76b9719d911017c592", ); }); From 9b7198828a903d88e3274a2904c490f8aada11c4 Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 16:46:21 +0100 Subject: [PATCH 17/53] feat: use hashed sources in the build id hash input --- .../solidity/build-system/compilation-job.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index 924524088d..5a02d66d2d 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -140,13 +140,24 @@ export class CompilationJobImplementation implements CompilationJob { // the format of the BuildInfo type. const format: BuildInfo["_format"] = "hh3-sol-build-info-1"; + const sources: { [sourceName: string]: { hash: string } } = {}; + const resolvedFiles = this.#getResolvedFiles(); + + await Promise.all( + resolvedFiles.map(async (file) => { + sources[file.sourceName] = { + hash: await createNonCryptographicHashId(file.content.text), + }; + }), + ); + // The preimage should include all the information that makes this // compilation job unique, and as this is used to identify the build info // file, it also includes its format string. const preimage = format + this.solcLongVersion + - JSON.stringify(this.getSolcInput()) + + JSON.stringify(this.#getSolcInputWithoutSources()) + JSON.stringify(this.solcConfig); return createNonCryptographicHashId(preimage); From b48e0b93033146f91bfbb77f1b2c3927a586ad91 Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 16:59:35 +0100 Subject: [PATCH 18/53] feat: cache resolved file content hashes --- .../solidity/build-system/compilation-job.ts | 7 +++++-- .../src/types/solidity/resolved-file.ts | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index 5a02d66d2d..6066560f2c 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -5,7 +5,10 @@ import type { SolcConfig } from "../../../../types/config.js"; import type { CompilationJob } from "../../../../types/solidity/compilation-job.js"; import type { CompilerInput } from "../../../../types/solidity/compiler-io.js"; import type { DependencyGraph } from "../../../../types/solidity/dependency-graph.js"; -import type { ResolvedFile } from "../../../../types/solidity.js"; +import { + getResolvedFileContentHash, + type ResolvedFile, +} from "../../../../types/solidity.js"; import { createNonCryptographicHashId } from "@ignored/hardhat-vnext-utils/crypto"; @@ -146,7 +149,7 @@ export class CompilationJobImplementation implements CompilationJob { await Promise.all( resolvedFiles.map(async (file) => { sources[file.sourceName] = { - hash: await createNonCryptographicHashId(file.content.text), + hash: await getResolvedFileContentHash(file), }; }), ); diff --git a/v-next/hardhat/src/types/solidity/resolved-file.ts b/v-next/hardhat/src/types/solidity/resolved-file.ts index be8512f956..abac38858b 100644 --- a/v-next/hardhat/src/types/solidity/resolved-file.ts +++ b/v-next/hardhat/src/types/solidity/resolved-file.ts @@ -1,3 +1,5 @@ +import { createNonCryptographicHashId } from "@ignored/hardhat-vnext-utils/crypto"; + /** * The representation of an npm package. */ @@ -111,3 +113,19 @@ export interface FileContent { */ versionPragmas: string[]; } + +const contentHashCache = new Map(); + +export async function getResolvedFileContentHash( + file: ResolvedFile, +): Promise { + const cachedContentHash = contentHashCache.get(file.sourceName); + if (cachedContentHash !== undefined) { + return cachedContentHash; + } + + const contentHash = await createNonCryptographicHashId(file.content.text); + contentHashCache.set(file.sourceName, contentHash); + + return contentHash; +} From f724079f1ee75c118cbe667a46b68b05ca40adac Mon Sep 17 00:00:00 2001 From: galargh Date: Tue, 28 Jan 2025 17:03:16 +0100 Subject: [PATCH 19/53] fix: include sources in the compilation job id --- .../builtin-plugins/solidity/build-system/compilation-job.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index 6066560f2c..66234cb880 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -161,6 +161,7 @@ export class CompilationJobImplementation implements CompilationJob { format + this.solcLongVersion + JSON.stringify(this.#getSolcInputWithoutSources()) + + JSON.stringify(sources) JSON.stringify(this.solcConfig); return createNonCryptographicHashId(preimage); From 0d6e34635b187f1f61599f6b3eaf9a5ca3cad2c8 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 00:13:46 +0100 Subject: [PATCH 20/53] chore: put the source content hash cache on compilation job --- .../solidity/build-system/compilation-job.ts | 30 ++++++++++++++----- .../src/types/solidity/resolved-file.ts | 18 ----------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index 66234cb880..5097aa4ea5 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -5,10 +5,7 @@ import type { SolcConfig } from "../../../../types/config.js"; import type { CompilationJob } from "../../../../types/solidity/compilation-job.js"; import type { CompilerInput } from "../../../../types/solidity/compiler-io.js"; import type { DependencyGraph } from "../../../../types/solidity/dependency-graph.js"; -import { - getResolvedFileContentHash, - type ResolvedFile, -} from "../../../../types/solidity.js"; +import type { ResolvedFile } from "../../../../types/solidity.js"; import { createNonCryptographicHashId } from "@ignored/hardhat-vnext-utils/crypto"; @@ -27,6 +24,8 @@ export class CompilationJobImplementation implements CompilationJob { #solcInputWithoutSources: Omit | undefined; #resolvedFiles: ResolvedFile[] | undefined; + static readonly #sourceContentHashCache = new Map(); + constructor( dependencyGraph: DependencyGraphImplementation, solcConfig: SolcConfig, @@ -138,6 +137,23 @@ export class CompilationJobImplementation implements CompilationJob { }; } + async #getSourceContentHash(file: ResolvedFile): Promise { + const cachedSourceContentHash = + CompilationJobImplementation.#sourceContentHashCache.get(file.sourceName); + if (cachedSourceContentHash !== undefined) { + return cachedSourceContentHash; + } + + const sourceContentHash = await createNonCryptographicHashId( + file.content.text, + ); + CompilationJobImplementation.#sourceContentHashCache.set( + file.sourceName, + sourceContentHash, + ); + return sourceContentHash; + } + async #computeBuildId(): Promise { // NOTE: We type it this way so that this stop compiling if we ever change // the format of the BuildInfo type. @@ -149,7 +165,7 @@ export class CompilationJobImplementation implements CompilationJob { await Promise.all( resolvedFiles.map(async (file) => { sources[file.sourceName] = { - hash: await getResolvedFileContentHash(file), + hash: await this.#getSourceContentHash(file), }; }), ); @@ -161,8 +177,8 @@ export class CompilationJobImplementation implements CompilationJob { format + this.solcLongVersion + JSON.stringify(this.#getSolcInputWithoutSources()) + - JSON.stringify(sources) - JSON.stringify(this.solcConfig); + JSON.stringify(sources); + JSON.stringify(this.solcConfig); return createNonCryptographicHashId(preimage); } diff --git a/v-next/hardhat/src/types/solidity/resolved-file.ts b/v-next/hardhat/src/types/solidity/resolved-file.ts index abac38858b..be8512f956 100644 --- a/v-next/hardhat/src/types/solidity/resolved-file.ts +++ b/v-next/hardhat/src/types/solidity/resolved-file.ts @@ -1,5 +1,3 @@ -import { createNonCryptographicHashId } from "@ignored/hardhat-vnext-utils/crypto"; - /** * The representation of an npm package. */ @@ -113,19 +111,3 @@ export interface FileContent { */ versionPragmas: string[]; } - -const contentHashCache = new Map(); - -export async function getResolvedFileContentHash( - file: ResolvedFile, -): Promise { - const cachedContentHash = contentHashCache.get(file.sourceName); - if (cachedContentHash !== undefined) { - return cachedContentHash; - } - - const contentHash = await createNonCryptographicHashId(file.content.text); - contentHashCache.set(file.sourceName, contentHash); - - return contentHash; -} From 7ebad19444c213687c98581593cc22528e797f22 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 01:02:31 +0100 Subject: [PATCH 21/53] test: make build system tests easier to change --- .../solidity/example-project/.gitignore | 2 + .../example-project/artifacts/artifacts.d.ts | 0 .../89eed7b31e13eec3ee71467386d39108.json | 38 -- ...eed7b31e13eec3ee71467386d39108.output.json | 65 --- .../c6d3d46c7bda8aac0e4258c216012f50.json | 106 ----- ...d3d46c7bda8aac0e4258c216012f50.output.json | 442 ------------------ .../artifacts/contracts/A.sol/A.json | 13 - .../artifacts/contracts/A.sol/artifacts.d.ts | 27 -- .../artifacts/contracts/B.sol/B.json | 13 - .../artifacts/contracts/B.sol/artifacts.d.ts | 27 -- .../artifacts/contracts/C.sol/C.json | 13 - .../artifacts/contracts/C.sol/C2.json | 13 - .../artifacts/contracts/C.sol/artifacts.d.ts | 43 -- .../artifacts/contracts/D.sol/artifacts.d.ts | 0 .../artifacts/contracts/E.sol/artifacts.d.ts | 0 .../contracts/NoImports.sol/NoImports.json | 13 - .../contracts/NoImports.sol/artifacts.d.ts | 27 -- .../UserRemappedImport.sol/artifacts.d.ts | 0 .../solidity/build-system/build-system.ts | 76 +-- 19 files changed, 49 insertions(+), 869 deletions(-) create mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/.gitignore delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/artifacts.d.ts delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/89eed7b31e13eec3ee71467386d39108.json delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/89eed7b31e13eec3ee71467386d39108.output.json delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/c6d3d46c7bda8aac0e4258c216012f50.json delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/c6d3d46c7bda8aac0e4258c216012f50.output.json delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/A.sol/A.json delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/A.sol/artifacts.d.ts delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/B.sol/B.json delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/B.sol/artifacts.d.ts delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/C.sol/C.json delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/C.sol/C2.json delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/C.sol/artifacts.d.ts delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/D.sol/artifacts.d.ts delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/E.sol/artifacts.d.ts delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/NoImports.sol/NoImports.json delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/NoImports.sol/artifacts.d.ts delete mode 100644 v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/UserRemappedImport.sol/artifacts.d.ts diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/.gitignore b/v-next/hardhat/test/fixture-projects/solidity/example-project/.gitignore new file mode 100644 index 0000000000..e7f801166c --- /dev/null +++ b/v-next/hardhat/test/fixture-projects/solidity/example-project/.gitignore @@ -0,0 +1,2 @@ +artifacts/ +cache/ diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/artifacts.d.ts b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/artifacts.d.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/89eed7b31e13eec3ee71467386d39108.json b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/89eed7b31e13eec3ee71467386d39108.json deleted file mode 100644 index 34f07408f8..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/89eed7b31e13eec3ee71467386d39108.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "_format": "hh3-sol-build-info-1", - "id": "89eed7b31e13eec3ee71467386d39108", - "solcVersion": "0.7.1", - "solcLongVersion": "0.7.1+commit.f4a555be", - "publicSourceNameMap": { - "contracts/NoImports.sol": "contracts/NoImports.sol" - }, - "input": { - "language": "Solidity", - "sources": { - "contracts/NoImports.sol": { - "content": "// SPDX-License-Identifier: SEE LICENSE IN LICENSE\npragma solidity ^0.7.0;\n\ncontract NoImports {}\n" - } - }, - "settings": { - "evmVersion": "istanbul", - "outputSelection": { - "*": { - "": ["ast"] - }, - "contracts/NoImports.sol": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ] - } - }, - "remappings": [ - "remapped/=npm/@openzeppelin/contracts@5.1.0/access/", - "@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.1.0/" - ] - } - } -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/89eed7b31e13eec3ee71467386d39108.output.json b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/89eed7b31e13eec3ee71467386d39108.output.json deleted file mode 100644 index e9bd41454f..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/89eed7b31e13eec3ee71467386d39108.output.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "_format": "hh3-sol-build-info-output-1", - "id": "89eed7b31e13eec3ee71467386d39108", - "output": { - "contracts": { - "contracts/NoImports.sol": { - "NoImports": { - "abi": [], - "evm": { - "bytecode": { - "linkReferences": {}, - "object": "6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220622ff1f92fb75a340477cc69a6f9fcbbd9518f40d01c4ed0fc96c42f94e05c5a64736f6c63430007010033", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x3F DUP1 PUSH1 0x1D PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 PUSH3 0x2FF1F9 0x2F 0xB7 GAS CALLVALUE DIV PUSH24 0xCC69A6F9FCBBD9518F40D01C4ED0FC96C42F94E05C5A6473 PUSH16 0x6C634300070100330000000000000000 ", - "sourceMap": "76:21:0:-:0;;;;;;;;;;;;;;;;;;;" - }, - "deployedBytecode": { - "immutableReferences": {}, - "linkReferences": {}, - "object": "6080604052600080fdfea2646970667358221220622ff1f92fb75a340477cc69a6f9fcbbd9518f40d01c4ed0fc96c42f94e05c5a64736f6c63430007010033", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 PUSH3 0x2FF1F9 0x2F 0xB7 GAS CALLVALUE DIV PUSH24 0xCC69A6F9FCBBD9518F40D01C4ED0FC96C42F94E05C5A6473 PUSH16 0x6C634300070100330000000000000000 ", - "sourceMap": "76:21:0:-:0;;;;;" - }, - "methodIdentifiers": {} - }, - "metadata": "{\"compiler\":{\"version\":\"0.7.1+commit.f4a555be\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/NoImports.sol\":\"NoImports\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.1.0/\",\":remapped/=npm/@openzeppelin/contracts@5.1.0/access/\"]},\"sources\":{\"contracts/NoImports.sol\":{\"keccak256\":\"0x3a28c66ec59da4cb822584ed8f391ddaff26410fb89766d49d577497823bc5ea\",\"license\":\"SEE LICENSE IN LICENSE\",\"urls\":[\"bzz-raw://fb9c7244739857b5c2b14000e0cdf717a521671457ed04d73bf4dfce002cf454\",\"dweb:/ipfs/QmZH2Ky7fy8Q87bsf7g9wcMnM1qNJQLzjcyQSG8nX84yPY\"]}},\"version\":1}" - } - } - }, - "sources": { - "contracts/NoImports.sol": { - "ast": { - "absolutePath": "contracts/NoImports.sol", - "exportedSymbols": { "NoImports": [2] }, - "id": 3, - "license": "SEE LICENSE IN LICENSE", - "nodeType": "SourceUnit", - "nodes": [ - { - "id": 1, - "literals": ["solidity", "^", "0.7", ".0"], - "nodeType": "PragmaDirective", - "src": "51:23:0" - }, - { - "abstract": false, - "baseContracts": [], - "contractDependencies": [], - "contractKind": "contract", - "fullyImplemented": true, - "id": 2, - "linearizedBaseContracts": [2], - "name": "NoImports", - "nodeType": "ContractDefinition", - "nodes": [], - "scope": 3, - "src": "76:21:0" - } - ], - "src": "51:47:0" - }, - "id": 0 - } - } - } -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/c6d3d46c7bda8aac0e4258c216012f50.json b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/c6d3d46c7bda8aac0e4258c216012f50.json deleted file mode 100644 index cf02c33710..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/c6d3d46c7bda8aac0e4258c216012f50.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "_format": "hh3-sol-build-info-1", - "id": "c6d3d46c7bda8aac0e4258c216012f50", - "solcVersion": "0.8.22", - "solcLongVersion": "0.8.22+commit.4fc1097e", - "publicSourceNameMap": { - "contracts/A.sol": "contracts/A.sol", - "contracts/B.sol": "contracts/B.sol", - "contracts/C.sol": "contracts/C.sol", - "contracts/D.sol": "contracts/D.sol", - "contracts/E.sol": "contracts/E.sol", - "contracts/UserRemappedImport.sol": "contracts/UserRemappedImport.sol" - }, - "input": { - "language": "Solidity", - "sources": { - "contracts/A.sol": { - "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\npragma solidity *;\n\nimport \"./B.sol\";\n\ncontract A is B {}\n" - }, - "contracts/B.sol": { - "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract B {}\n" - }, - "contracts/C.sol": { - "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.0;\n\nimport \"./B.sol\";\n\ncontract C {}\n\ncontract C2 {}\n" - }, - "contracts/D.sol": { - "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.22;\n\nimport \"./C.sol\";\n" - }, - "contracts/E.sol": { - "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.22;\n\nimport \"./C.sol\";\n" - }, - "contracts/UserRemappedImport.sol": { - "content": "// SPDX-License-Identifier: SEE LICENSE IN LICENSE\npragma solidity ^0.8.0;\n\nimport \"remapped/Ownable.sol\";\n" - }, - "npm/@openzeppelin/contracts@5.1.0/access/Ownable.sol": { - "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nabstract contract Ownable {}\n" - } - }, - "settings": { - "evmVersion": "shanghai", - "outputSelection": { - "*": { - "": ["ast"] - }, - "contracts/A.sol": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ] - }, - "contracts/B.sol": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ] - }, - "contracts/C.sol": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ] - }, - "contracts/D.sol": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ] - }, - "contracts/E.sol": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ] - }, - "contracts/UserRemappedImport.sol": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ] - } - }, - "remappings": [ - "remapped/=npm/@openzeppelin/contracts@5.1.0/access/", - "@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.1.0/" - ] - } - } -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/c6d3d46c7bda8aac0e4258c216012f50.output.json b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/c6d3d46c7bda8aac0e4258c216012f50.output.json deleted file mode 100644 index 6ee52b10f9..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/build-info/c6d3d46c7bda8aac0e4258c216012f50.output.json +++ /dev/null @@ -1,442 +0,0 @@ -{ - "_format": "hh3-sol-build-info-output-1", - "id": "c6d3d46c7bda8aac0e4258c216012f50", - "output": { - "contracts": { - "contracts/A.sol": { - "A": { - "abi": [], - "evm": { - "bytecode": { - "functionDebugData": {}, - "generatedSources": [], - "linkReferences": {}, - "object": "6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea2646970667358221220fa80377a08a97ad93ccb004c8ba5b1944699d70ad3012de9351c0bca77d0196664736f6c63430008160033", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xE JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x3E DUP1 PUSH1 0x1A PUSH0 CODECOPY PUSH0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 STATICCALL DUP1 CALLDATACOPY PUSH27 0x8A97AD93CCB004C8BA5B1944699D70AD3012DE9351C0BCA77D019 PUSH7 0x64736F6C634300 ADDMOD AND STOP CALLER ", - "sourceMap": "102:18:0:-:0;;;;;;;;;;;;;;;;;;;" - }, - "deployedBytecode": { - "functionDebugData": {}, - "generatedSources": [], - "immutableReferences": {}, - "linkReferences": {}, - "object": "60806040525f80fdfea2646970667358221220fa80377a08a97ad93ccb004c8ba5b1944699d70ad3012de9351c0bca77d0196664736f6c63430008160033", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 STATICCALL DUP1 CALLDATACOPY PUSH27 0x8A97AD93CCB004C8BA5B1944699D70AD3012DE9351C0BCA77D019 PUSH7 0x64736F6C634300 ADDMOD AND STOP CALLER ", - "sourceMap": "102:18:0:-:0;;;;;" - }, - "methodIdentifiers": {} - }, - "metadata": "{\"compiler\":{\"version\":\"0.8.22+commit.4fc1097e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/A.sol\":\"A\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.1.0/\",\":remapped/=npm/@openzeppelin/contracts@5.1.0/access/\"]},\"sources\":{\"contracts/A.sol\":{\"keccak256\":\"0x0b6baab6b8f8c894f2ae3b4a30aa03687f5e1b5efdc79fc746d7001bc61ac2ae\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://4745a08025dd5ecdc31adc7db51210d16b9034dc41f2ed7241f98961c687e905\",\"dweb:/ipfs/QmYGbMN118xuYMR3p2YjyTMAn781mWPZXgBtG2jsYDYcPW\"]},\"contracts/B.sol\":{\"keccak256\":\"0xe3bfba24c7ab781173f217b2a770a1bf762950f485d589fef5066493a9781575\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://0265cd6edebc0e6ce4d1ed1107e40d1d0a79ad6ffc532269a9ac648a850e03b6\",\"dweb:/ipfs/QmaGSjd8xq5KotjZW1CWSRNEzXY4kWrBpDeGw16cuqNgpY\"]},\"npm/@openzeppelin/contracts@5.1.0/access/Ownable.sol\":{\"keccak256\":\"0x8bb13fdfb1cc614eed7de55fe3b8deb01cf864a125a43b0bb083f029698b834d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://98a23f280b79fe5ee104c3748e3caf90c846d8dd41c9e1b333f500ec94c6dbea\",\"dweb:/ipfs/QmTD9zsov5nahn894F69rvUKcEphBXGR51f8Qy4Z67BLyx\"]}},\"version\":1}" - } - }, - "contracts/B.sol": { - "B": { - "abi": [], - "evm": { - "bytecode": { - "functionDebugData": {}, - "generatedSources": [], - "linkReferences": {}, - "object": "6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea26469706673582212203a770298304d45a01cb3629a2f032da51fb0fe9d9c0b8148d8aa64f0dd779c8564736f6c63430008160033", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xE JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x3E DUP1 PUSH1 0x1A PUSH0 CODECOPY PUSH0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 GASPRICE PUSH24 0x298304D45A01CB3629A2F032DA51FB0FE9D9C0B8148D8AA PUSH5 0xF0DD779C85 PUSH5 0x736F6C6343 STOP ADDMOD AND STOP CALLER ", - "sourceMap": "118:13:1:-:0;;;;;;;;;;;;;;;;;;;" - }, - "deployedBytecode": { - "functionDebugData": {}, - "generatedSources": [], - "immutableReferences": {}, - "linkReferences": {}, - "object": "60806040525f80fdfea26469706673582212203a770298304d45a01cb3629a2f032da51fb0fe9d9c0b8148d8aa64f0dd779c8564736f6c63430008160033", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 GASPRICE PUSH24 0x298304D45A01CB3629A2F032DA51FB0FE9D9C0B8148D8AA PUSH5 0xF0DD779C85 PUSH5 0x736F6C6343 STOP ADDMOD AND STOP CALLER ", - "sourceMap": "118:13:1:-:0;;;;;" - }, - "methodIdentifiers": {} - }, - "metadata": "{\"compiler\":{\"version\":\"0.8.22+commit.4fc1097e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/B.sol\":\"B\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.1.0/\",\":remapped/=npm/@openzeppelin/contracts@5.1.0/access/\"]},\"sources\":{\"contracts/B.sol\":{\"keccak256\":\"0xe3bfba24c7ab781173f217b2a770a1bf762950f485d589fef5066493a9781575\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://0265cd6edebc0e6ce4d1ed1107e40d1d0a79ad6ffc532269a9ac648a850e03b6\",\"dweb:/ipfs/QmaGSjd8xq5KotjZW1CWSRNEzXY4kWrBpDeGw16cuqNgpY\"]},\"npm/@openzeppelin/contracts@5.1.0/access/Ownable.sol\":{\"keccak256\":\"0x8bb13fdfb1cc614eed7de55fe3b8deb01cf864a125a43b0bb083f029698b834d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://98a23f280b79fe5ee104c3748e3caf90c846d8dd41c9e1b333f500ec94c6dbea\",\"dweb:/ipfs/QmTD9zsov5nahn894F69rvUKcEphBXGR51f8Qy4Z67BLyx\"]}},\"version\":1}" - } - }, - "contracts/C.sol": { - "C": { - "abi": [], - "evm": { - "bytecode": { - "functionDebugData": {}, - "generatedSources": [], - "linkReferences": {}, - "object": "6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea264697066735822122066220fc59f0e456bfeed804b45a091ea9b64289092ec824766fee8af886447de64736f6c63430008160033", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xE JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x3E DUP1 PUSH1 0x1A PUSH0 CODECOPY PUSH0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 PUSH7 0x220FC59F0E456B INVALID 0xED DUP1 0x4B GASLIMIT LOG0 SWAP2 0xEA SWAP12 PUSH5 0x289092EC82 SELFBALANCE PUSH7 0xFEE8AF886447DE PUSH5 0x736F6C6343 STOP ADDMOD AND STOP CALLER ", - "sourceMap": "83:13:2:-:0;;;;;;;;;;;;;;;;;;;" - }, - "deployedBytecode": { - "functionDebugData": {}, - "generatedSources": [], - "immutableReferences": {}, - "linkReferences": {}, - "object": "60806040525f80fdfea264697066735822122066220fc59f0e456bfeed804b45a091ea9b64289092ec824766fee8af886447de64736f6c63430008160033", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 PUSH7 0x220FC59F0E456B INVALID 0xED DUP1 0x4B GASLIMIT LOG0 SWAP2 0xEA SWAP12 PUSH5 0x289092EC82 SELFBALANCE PUSH7 0xFEE8AF886447DE PUSH5 0x736F6C6343 STOP ADDMOD AND STOP CALLER ", - "sourceMap": "83:13:2:-:0;;;;;" - }, - "methodIdentifiers": {} - }, - "metadata": "{\"compiler\":{\"version\":\"0.8.22+commit.4fc1097e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/C.sol\":\"C\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.1.0/\",\":remapped/=npm/@openzeppelin/contracts@5.1.0/access/\"]},\"sources\":{\"contracts/B.sol\":{\"keccak256\":\"0xe3bfba24c7ab781173f217b2a770a1bf762950f485d589fef5066493a9781575\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://0265cd6edebc0e6ce4d1ed1107e40d1d0a79ad6ffc532269a9ac648a850e03b6\",\"dweb:/ipfs/QmaGSjd8xq5KotjZW1CWSRNEzXY4kWrBpDeGw16cuqNgpY\"]},\"contracts/C.sol\":{\"keccak256\":\"0xc59a3c8bafbab383c5b64ed325f6a3a61ec9fe36d5a375f2723cfd272c819675\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://294d208fbc2235f9e93c60f0caca7723bf4a08397cc5932783a89d72a2319c42\",\"dweb:/ipfs/QmUn6fonDgB3rZ84UjUNnY65vRm28qPTMKknFcVY6r7E28\"]},\"npm/@openzeppelin/contracts@5.1.0/access/Ownable.sol\":{\"keccak256\":\"0x8bb13fdfb1cc614eed7de55fe3b8deb01cf864a125a43b0bb083f029698b834d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://98a23f280b79fe5ee104c3748e3caf90c846d8dd41c9e1b333f500ec94c6dbea\",\"dweb:/ipfs/QmTD9zsov5nahn894F69rvUKcEphBXGR51f8Qy4Z67BLyx\"]}},\"version\":1}" - }, - "C2": { - "abi": [], - "evm": { - "bytecode": { - "functionDebugData": {}, - "generatedSources": [], - "linkReferences": {}, - "object": "6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea2646970667358221220c72aea65f1253bb034ad646e28ff9b53b7a7effdf03dfd58726d59b7083d8d9c64736f6c63430008160033", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xE JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x3E DUP1 PUSH1 0x1A PUSH0 CODECOPY PUSH0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 0xC7 0x2A 0xEA PUSH6 0xF1253BB034AD PUSH5 0x6E28FF9B53 0xB7 0xA7 0xEF REVERT CREATE RETURNDATASIZE REVERT PC PUSH19 0x6D59B7083D8D9C64736F6C6343000816003300 ", - "sourceMap": "98:14:2:-:0;;;;;;;;;;;;;;;;;;;" - }, - "deployedBytecode": { - "functionDebugData": {}, - "generatedSources": [], - "immutableReferences": {}, - "linkReferences": {}, - "object": "60806040525f80fdfea2646970667358221220c72aea65f1253bb034ad646e28ff9b53b7a7effdf03dfd58726d59b7083d8d9c64736f6c63430008160033", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 0xC7 0x2A 0xEA PUSH6 0xF1253BB034AD PUSH5 0x6E28FF9B53 0xB7 0xA7 0xEF REVERT CREATE RETURNDATASIZE REVERT PC PUSH19 0x6D59B7083D8D9C64736F6C6343000816003300 ", - "sourceMap": "98:14:2:-:0;;;;;" - }, - "methodIdentifiers": {} - }, - "metadata": "{\"compiler\":{\"version\":\"0.8.22+commit.4fc1097e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/C.sol\":\"C2\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.1.0/\",\":remapped/=npm/@openzeppelin/contracts@5.1.0/access/\"]},\"sources\":{\"contracts/B.sol\":{\"keccak256\":\"0xe3bfba24c7ab781173f217b2a770a1bf762950f485d589fef5066493a9781575\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://0265cd6edebc0e6ce4d1ed1107e40d1d0a79ad6ffc532269a9ac648a850e03b6\",\"dweb:/ipfs/QmaGSjd8xq5KotjZW1CWSRNEzXY4kWrBpDeGw16cuqNgpY\"]},\"contracts/C.sol\":{\"keccak256\":\"0xc59a3c8bafbab383c5b64ed325f6a3a61ec9fe36d5a375f2723cfd272c819675\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://294d208fbc2235f9e93c60f0caca7723bf4a08397cc5932783a89d72a2319c42\",\"dweb:/ipfs/QmUn6fonDgB3rZ84UjUNnY65vRm28qPTMKknFcVY6r7E28\"]},\"npm/@openzeppelin/contracts@5.1.0/access/Ownable.sol\":{\"keccak256\":\"0x8bb13fdfb1cc614eed7de55fe3b8deb01cf864a125a43b0bb083f029698b834d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://98a23f280b79fe5ee104c3748e3caf90c846d8dd41c9e1b333f500ec94c6dbea\",\"dweb:/ipfs/QmTD9zsov5nahn894F69rvUKcEphBXGR51f8Qy4Z67BLyx\"]}},\"version\":1}" - } - } - }, - "sources": { - "contracts/A.sol": { - "ast": { - "absolutePath": "contracts/A.sol", - "exportedSymbols": { "A": [6], "B": [10], "Ownable": [27] }, - "id": 7, - "license": "UNLICENSED", - "nodeType": "SourceUnit", - "nodes": [ - { - "id": 1, - "literals": ["solidity", "^", "0.8", ".0"], - "nodeType": "PragmaDirective", - "src": "39:23:0" - }, - { - "id": 2, - "literals": ["solidity", "*"], - "nodeType": "PragmaDirective", - "src": "63:18:0" - }, - { - "absolutePath": "contracts/B.sol", - "file": "./B.sol", - "id": 3, - "nameLocation": "-1:-1:-1", - "nodeType": "ImportDirective", - "scope": 7, - "sourceUnit": 11, - "src": "83:17:0", - "symbolAliases": [], - "unitAlias": "" - }, - { - "abstract": false, - "baseContracts": [ - { - "baseName": { - "id": 4, - "name": "B", - "nameLocations": ["116:1:0"], - "nodeType": "IdentifierPath", - "referencedDeclaration": 10, - "src": "116:1:0" - }, - "id": 5, - "nodeType": "InheritanceSpecifier", - "src": "116:1:0" - } - ], - "canonicalName": "A", - "contractDependencies": [], - "contractKind": "contract", - "fullyImplemented": true, - "id": 6, - "linearizedBaseContracts": [6, 10], - "name": "A", - "nameLocation": "111:1:0", - "nodeType": "ContractDefinition", - "nodes": [], - "scope": 7, - "src": "102:18:0", - "usedErrors": [], - "usedEvents": [] - } - ], - "src": "39:82:0" - }, - "id": 0 - }, - "contracts/B.sol": { - "ast": { - "absolutePath": "contracts/B.sol", - "exportedSymbols": { "B": [10], "Ownable": [27] }, - "id": 11, - "license": "UNLICENSED", - "nodeType": "SourceUnit", - "nodes": [ - { - "id": 8, - "literals": ["solidity", "^", "0.8", ".0"], - "nodeType": "PragmaDirective", - "src": "39:23:1" - }, - { - "absolutePath": "npm/@openzeppelin/contracts@5.1.0/access/Ownable.sol", - "file": "@openzeppelin/contracts/access/Ownable.sol", - "id": 9, - "nameLocation": "-1:-1:-1", - "nodeType": "ImportDirective", - "scope": 11, - "sourceUnit": 28, - "src": "64:52:1", - "symbolAliases": [], - "unitAlias": "" - }, - { - "abstract": false, - "baseContracts": [], - "canonicalName": "B", - "contractDependencies": [], - "contractKind": "contract", - "fullyImplemented": true, - "id": 10, - "linearizedBaseContracts": [10], - "name": "B", - "nameLocation": "127:1:1", - "nodeType": "ContractDefinition", - "nodes": [], - "scope": 11, - "src": "118:13:1", - "usedErrors": [], - "usedEvents": [] - } - ], - "src": "39:93:1" - }, - "id": 1 - }, - "contracts/C.sol": { - "ast": { - "absolutePath": "contracts/C.sol", - "exportedSymbols": { - "B": [10], - "C": [14], - "C2": [15], - "Ownable": [27] - }, - "id": 16, - "license": "UNLICENSED", - "nodeType": "SourceUnit", - "nodes": [ - { - "id": 12, - "literals": ["solidity", "^", "0.8", ".0"], - "nodeType": "PragmaDirective", - "src": "39:23:2" - }, - { - "absolutePath": "contracts/B.sol", - "file": "./B.sol", - "id": 13, - "nameLocation": "-1:-1:-1", - "nodeType": "ImportDirective", - "scope": 16, - "sourceUnit": 11, - "src": "64:17:2", - "symbolAliases": [], - "unitAlias": "" - }, - { - "abstract": false, - "baseContracts": [], - "canonicalName": "C", - "contractDependencies": [], - "contractKind": "contract", - "fullyImplemented": true, - "id": 14, - "linearizedBaseContracts": [14], - "name": "C", - "nameLocation": "92:1:2", - "nodeType": "ContractDefinition", - "nodes": [], - "scope": 16, - "src": "83:13:2", - "usedErrors": [], - "usedEvents": [] - }, - { - "abstract": false, - "baseContracts": [], - "canonicalName": "C2", - "contractDependencies": [], - "contractKind": "contract", - "fullyImplemented": true, - "id": 15, - "linearizedBaseContracts": [15], - "name": "C2", - "nameLocation": "107:2:2", - "nodeType": "ContractDefinition", - "nodes": [], - "scope": 16, - "src": "98:14:2", - "usedErrors": [], - "usedEvents": [] - } - ], - "src": "39:74:2" - }, - "id": 2 - }, - "contracts/D.sol": { - "ast": { - "absolutePath": "contracts/D.sol", - "exportedSymbols": { - "B": [10], - "C": [14], - "C2": [15], - "Ownable": [27] - }, - "id": 19, - "license": "UNLICENSED", - "nodeType": "SourceUnit", - "nodes": [ - { - "id": 17, - "literals": ["solidity", "^", "0.8", ".22"], - "nodeType": "PragmaDirective", - "src": "39:24:3" - }, - { - "absolutePath": "contracts/C.sol", - "file": "./C.sol", - "id": 18, - "nameLocation": "-1:-1:-1", - "nodeType": "ImportDirective", - "scope": 19, - "sourceUnit": 16, - "src": "65:17:3", - "symbolAliases": [], - "unitAlias": "" - } - ], - "src": "39:44:3" - }, - "id": 3 - }, - "contracts/E.sol": { - "ast": { - "absolutePath": "contracts/E.sol", - "exportedSymbols": { - "B": [10], - "C": [14], - "C2": [15], - "Ownable": [27] - }, - "id": 22, - "license": "UNLICENSED", - "nodeType": "SourceUnit", - "nodes": [ - { - "id": 20, - "literals": ["solidity", "^", "0.8", ".22"], - "nodeType": "PragmaDirective", - "src": "39:24:4" - }, - { - "absolutePath": "contracts/C.sol", - "file": "./C.sol", - "id": 21, - "nameLocation": "-1:-1:-1", - "nodeType": "ImportDirective", - "scope": 22, - "sourceUnit": 16, - "src": "65:17:4", - "symbolAliases": [], - "unitAlias": "" - } - ], - "src": "39:44:4" - }, - "id": 4 - }, - "contracts/UserRemappedImport.sol": { - "ast": { - "absolutePath": "contracts/UserRemappedImport.sol", - "exportedSymbols": { "Ownable": [27] }, - "id": 25, - "license": "SEE LICENSE IN LICENSE", - "nodeType": "SourceUnit", - "nodes": [ - { - "id": 23, - "literals": ["solidity", "^", "0.8", ".0"], - "nodeType": "PragmaDirective", - "src": "51:23:5" - }, - { - "absolutePath": "npm/@openzeppelin/contracts@5.1.0/access/Ownable.sol", - "file": "remapped/Ownable.sol", - "id": 24, - "nameLocation": "-1:-1:-1", - "nodeType": "ImportDirective", - "scope": 25, - "sourceUnit": 28, - "src": "76:30:5", - "symbolAliases": [], - "unitAlias": "" - } - ], - "src": "51:56:5" - }, - "id": 5 - }, - "npm/@openzeppelin/contracts@5.1.0/access/Ownable.sol": { - "ast": { - "absolutePath": "npm/@openzeppelin/contracts@5.1.0/access/Ownable.sol", - "exportedSymbols": { "Ownable": [27] }, - "id": 28, - "license": "MIT", - "nodeType": "SourceUnit", - "nodes": [ - { - "id": 26, - "literals": ["solidity", "^", "0.8", ".20"], - "nodeType": "PragmaDirective", - "src": "102:24:6" - }, - { - "abstract": true, - "baseContracts": [], - "canonicalName": "Ownable", - "contractDependencies": [], - "contractKind": "contract", - "fullyImplemented": true, - "id": 27, - "linearizedBaseContracts": [27], - "name": "Ownable", - "nameLocation": "146:7:6", - "nodeType": "ContractDefinition", - "nodes": [], - "scope": 28, - "src": "128:28:6", - "usedErrors": [], - "usedEvents": [] - } - ], - "src": "102:55:6" - }, - "id": 6 - } - } - } -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/A.sol/A.json b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/A.sol/A.json deleted file mode 100644 index e3c9ad2f2c..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/A.sol/A.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "_format": "hh3-artifact-1", - "contractName": "A", - "sourceName": "contracts/A.sol", - "abi": [], - "bytecode": "0x6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea2646970667358221220fa80377a08a97ad93ccb004c8ba5b1944699d70ad3012de9351c0bca77d0196664736f6c63430008160033", - "deployedBytecode": "0x60806040525f80fdfea2646970667358221220fa80377a08a97ad93ccb004c8ba5b1944699d70ad3012de9351c0bca77d0196664736f6c63430008160033", - "linkReferences": {}, - "deployedLinkReferences": {}, - "immutableReferences": {}, - "inputSourceName": "contracts/A.sol", - "buildInfoId": "c6d3d46c7bda8aac0e4258c216012f50" -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/A.sol/artifacts.d.ts b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/A.sol/artifacts.d.ts deleted file mode 100644 index 0e6a29083a..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/A.sol/artifacts.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -// This file was autogenerated by Hardhat-viem, do not edit it. -// prettier-ignore -// tslint:disable -// eslint-disable -// biome-ignore format: see above - -export interface A$Type { - readonly _format: "hh3-artifact-1"; - readonly contractName: "A"; - readonly sourceName: "contracts/A.sol"; - readonly abi: []; - readonly bytecode: "0x6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea2646970667358221220fa80377a08a97ad93ccb004c8ba5b1944699d70ad3012de9351c0bca77d0196664736f6c63430008160033"; - readonly deployedBytecode: "0x60806040525f80fdfea2646970667358221220fa80377a08a97ad93ccb004c8ba5b1944699d70ad3012de9351c0bca77d0196664736f6c63430008160033"; - readonly linkReferences: {}; - readonly deployedLinkReferences: {}; - readonly immutableReferences: {}; - readonly inputSourceName: "contracts/A.sol"; - readonly buildInfoId: "c6d3d46c7bda8aac0e4258c216012f50"; -} - -import "@ignored/hardhat-vnext/types/artifacts"; -declare module "@ignored/hardhat-vnext/types/artifacts" { - interface ArtifactMap { - ["A"]: A$Type; - ["contracts/A.sol:A"]: A$Type; - } -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/B.sol/B.json b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/B.sol/B.json deleted file mode 100644 index 24a8a545d1..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/B.sol/B.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "_format": "hh3-artifact-1", - "contractName": "B", - "sourceName": "contracts/B.sol", - "abi": [], - "bytecode": "0x6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea26469706673582212203a770298304d45a01cb3629a2f032da51fb0fe9d9c0b8148d8aa64f0dd779c8564736f6c63430008160033", - "deployedBytecode": "0x60806040525f80fdfea26469706673582212203a770298304d45a01cb3629a2f032da51fb0fe9d9c0b8148d8aa64f0dd779c8564736f6c63430008160033", - "linkReferences": {}, - "deployedLinkReferences": {}, - "immutableReferences": {}, - "inputSourceName": "contracts/B.sol", - "buildInfoId": "c6d3d46c7bda8aac0e4258c216012f50" -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/B.sol/artifacts.d.ts b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/B.sol/artifacts.d.ts deleted file mode 100644 index ace4596050..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/B.sol/artifacts.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -// This file was autogenerated by Hardhat-viem, do not edit it. -// prettier-ignore -// tslint:disable -// eslint-disable -// biome-ignore format: see above - -export interface B$Type { - readonly _format: "hh3-artifact-1"; - readonly contractName: "B"; - readonly sourceName: "contracts/B.sol"; - readonly abi: []; - readonly bytecode: "0x6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea26469706673582212203a770298304d45a01cb3629a2f032da51fb0fe9d9c0b8148d8aa64f0dd779c8564736f6c63430008160033"; - readonly deployedBytecode: "0x60806040525f80fdfea26469706673582212203a770298304d45a01cb3629a2f032da51fb0fe9d9c0b8148d8aa64f0dd779c8564736f6c63430008160033"; - readonly linkReferences: {}; - readonly deployedLinkReferences: {}; - readonly immutableReferences: {}; - readonly inputSourceName: "contracts/B.sol"; - readonly buildInfoId: "c6d3d46c7bda8aac0e4258c216012f50"; -} - -import "@ignored/hardhat-vnext/types/artifacts"; -declare module "@ignored/hardhat-vnext/types/artifacts" { - interface ArtifactMap { - ["B"]: B$Type; - ["contracts/B.sol:B"]: B$Type; - } -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/C.sol/C.json b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/C.sol/C.json deleted file mode 100644 index a23a024f01..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/C.sol/C.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "_format": "hh3-artifact-1", - "contractName": "C", - "sourceName": "contracts/C.sol", - "abi": [], - "bytecode": "0x6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea264697066735822122066220fc59f0e456bfeed804b45a091ea9b64289092ec824766fee8af886447de64736f6c63430008160033", - "deployedBytecode": "0x60806040525f80fdfea264697066735822122066220fc59f0e456bfeed804b45a091ea9b64289092ec824766fee8af886447de64736f6c63430008160033", - "linkReferences": {}, - "deployedLinkReferences": {}, - "immutableReferences": {}, - "inputSourceName": "contracts/C.sol", - "buildInfoId": "c6d3d46c7bda8aac0e4258c216012f50" -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/C.sol/C2.json b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/C.sol/C2.json deleted file mode 100644 index e2f029ca4a..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/C.sol/C2.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "_format": "hh3-artifact-1", - "contractName": "C2", - "sourceName": "contracts/C.sol", - "abi": [], - "bytecode": "0x6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea2646970667358221220c72aea65f1253bb034ad646e28ff9b53b7a7effdf03dfd58726d59b7083d8d9c64736f6c63430008160033", - "deployedBytecode": "0x60806040525f80fdfea2646970667358221220c72aea65f1253bb034ad646e28ff9b53b7a7effdf03dfd58726d59b7083d8d9c64736f6c63430008160033", - "linkReferences": {}, - "deployedLinkReferences": {}, - "immutableReferences": {}, - "inputSourceName": "contracts/C.sol", - "buildInfoId": "c6d3d46c7bda8aac0e4258c216012f50" -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/C.sol/artifacts.d.ts b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/C.sol/artifacts.d.ts deleted file mode 100644 index 721623b710..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/C.sol/artifacts.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -// This file was autogenerated by Hardhat-viem, do not edit it. -// prettier-ignore -// tslint:disable -// eslint-disable -// biome-ignore format: see above - -export interface C$Type { - readonly _format: "hh3-artifact-1"; - readonly contractName: "C"; - readonly sourceName: "contracts/C.sol"; - readonly abi: []; - readonly bytecode: "0x6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea264697066735822122066220fc59f0e456bfeed804b45a091ea9b64289092ec824766fee8af886447de64736f6c63430008160033"; - readonly deployedBytecode: "0x60806040525f80fdfea264697066735822122066220fc59f0e456bfeed804b45a091ea9b64289092ec824766fee8af886447de64736f6c63430008160033"; - readonly linkReferences: {}; - readonly deployedLinkReferences: {}; - readonly immutableReferences: {}; - readonly inputSourceName: "contracts/C.sol"; - readonly buildInfoId: "c6d3d46c7bda8aac0e4258c216012f50"; -} - -export interface C2$Type { - readonly _format: "hh3-artifact-1"; - readonly contractName: "C2"; - readonly sourceName: "contracts/C.sol"; - readonly abi: []; - readonly bytecode: "0x6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea2646970667358221220c72aea65f1253bb034ad646e28ff9b53b7a7effdf03dfd58726d59b7083d8d9c64736f6c63430008160033"; - readonly deployedBytecode: "0x60806040525f80fdfea2646970667358221220c72aea65f1253bb034ad646e28ff9b53b7a7effdf03dfd58726d59b7083d8d9c64736f6c63430008160033"; - readonly linkReferences: {}; - readonly deployedLinkReferences: {}; - readonly immutableReferences: {}; - readonly inputSourceName: "contracts/C.sol"; - readonly buildInfoId: "c6d3d46c7bda8aac0e4258c216012f50"; -} - -import "@ignored/hardhat-vnext/types/artifacts"; -declare module "@ignored/hardhat-vnext/types/artifacts" { - interface ArtifactMap { - ["C"]: C$Type; - ["C2"]: C2$Type; - ["contracts/C.sol:C"]: C$Type; - ["contracts/C.sol:C2"]: C2$Type; - } -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/D.sol/artifacts.d.ts b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/D.sol/artifacts.d.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/E.sol/artifacts.d.ts b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/E.sol/artifacts.d.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/NoImports.sol/NoImports.json b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/NoImports.sol/NoImports.json deleted file mode 100644 index 91c6e70839..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/NoImports.sol/NoImports.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "_format": "hh3-artifact-1", - "contractName": "NoImports", - "sourceName": "contracts/NoImports.sol", - "abi": [], - "bytecode": "0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220622ff1f92fb75a340477cc69a6f9fcbbd9518f40d01c4ed0fc96c42f94e05c5a64736f6c63430007010033", - "deployedBytecode": "0x6080604052600080fdfea2646970667358221220622ff1f92fb75a340477cc69a6f9fcbbd9518f40d01c4ed0fc96c42f94e05c5a64736f6c63430007010033", - "linkReferences": {}, - "deployedLinkReferences": {}, - "immutableReferences": {}, - "inputSourceName": "contracts/NoImports.sol", - "buildInfoId": "89eed7b31e13eec3ee71467386d39108" -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/NoImports.sol/artifacts.d.ts b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/NoImports.sol/artifacts.d.ts deleted file mode 100644 index 24bde4aebe..0000000000 --- a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/NoImports.sol/artifacts.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -// This file was autogenerated by Hardhat-viem, do not edit it. -// prettier-ignore -// tslint:disable -// eslint-disable -// biome-ignore format: see above - -export interface NoImports$Type { - readonly _format: "hh3-artifact-1"; - readonly contractName: "NoImports"; - readonly sourceName: "contracts/NoImports.sol"; - readonly abi: []; - readonly bytecode: "0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220622ff1f92fb75a340477cc69a6f9fcbbd9518f40d01c4ed0fc96c42f94e05c5a64736f6c63430007010033"; - readonly deployedBytecode: "0x6080604052600080fdfea2646970667358221220622ff1f92fb75a340477cc69a6f9fcbbd9518f40d01c4ed0fc96c42f94e05c5a64736f6c63430007010033"; - readonly linkReferences: {}; - readonly deployedLinkReferences: {}; - readonly immutableReferences: {}; - readonly inputSourceName: "contracts/NoImports.sol"; - readonly buildInfoId: "89eed7b31e13eec3ee71467386d39108"; -} - -import "@ignored/hardhat-vnext/types/artifacts"; -declare module "@ignored/hardhat-vnext/types/artifacts" { - interface ArtifactMap { - ["NoImports"]: NoImports$Type; - ["contracts/NoImports.sol:NoImports"]: NoImports$Type; - } -} diff --git a/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/UserRemappedImport.sol/artifacts.d.ts b/v-next/hardhat/test/fixture-projects/solidity/example-project/artifacts/contracts/UserRemappedImport.sol/artifacts.d.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts index 3c09378abb..f0979bc4e2 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -1,3 +1,4 @@ +import type { SolidityUserConfig } from "../../../../../src/types/config.js"; import type { HardhatRuntimeEnvironment } from "../../../../../src/types/hre.js"; import type { SolidityBuildInfoOutput, @@ -6,12 +7,13 @@ import type { import assert from "node:assert/strict"; import path from "node:path"; -import { beforeEach, describe, it } from "node:test"; +import { before, beforeEach, describe, it } from "node:test"; import { exists, getAllFilesMatching, readJsonFile, + remove, } from "@ignored/hardhat-vnext-utils/fs"; import { getTmpDir, @@ -51,35 +53,53 @@ describe( skip: process.env.HARDHAT_DISABLE_SLOW_TESTS === "true", }, () => { - let artifactsPath: string; + let actualArtifactsPath: string; + let expectedArtifactsPath: string; let hre: HardhatRuntimeEnvironment; useFixtureProject("solidity/example-project"); + const solidityUserConfig: SolidityUserConfig = { + profiles: { + default: { + compilers: [ + { + version: "0.8.22", + }, + { + version: "0.7.1", + }, + ], + }, + }, + remappings: ["remapped/=npm/@openzeppelin/contracts@5.1.0/access/"], + }; + + before(async () => { + expectedArtifactsPath = path.join(process.cwd(), "artifacts"); + const expectedCachePath = path.join(process.cwd(), "cache"); + await remove(expectedArtifactsPath); + await remove(expectedCachePath); + hre = await createHardhatRuntimeEnvironment({ + paths: { + artifacts: expectedArtifactsPath, + cache: expectedCachePath, + }, + solidity: solidityUserConfig, + }); + await hre.tasks.getTask("compile").run({ quiet: false }); + }); + beforeEach(async () => { const tmpDir = await getTmpDir("solidity-build-system-implementation"); - artifactsPath = path.join(tmpDir, "artifacts"); - const cachePath = path.join(tmpDir, "cache"); + actualArtifactsPath = path.join(tmpDir, "artifacts"); + const actualCachePath = path.join(tmpDir, "cache"); hre = await createHardhatRuntimeEnvironment({ paths: { - artifacts: artifactsPath, - cache: cachePath, - }, - solidity: { - profiles: { - default: { - compilers: [ - { - version: "0.8.22", - }, - { - version: "0.7.1", - }, - ], - }, - }, - remappings: ["remapped/=npm/@openzeppelin/contracts@5.1.0/access/"], + artifacts: actualArtifactsPath, + cache: actualCachePath, }, + solidity: solidityUserConfig, }); }); @@ -87,8 +107,6 @@ describe( it("should successfully emit the artifacts", async () => { await emitArtifacts(hre.solidity); - const expectedArtifactsPath = path.join(process.cwd(), "artifacts"); - const expectedArtifactPaths = await getAllFilesMatching( expectedArtifactsPath, (f) => f.endsWith(".json"), @@ -100,7 +118,7 @@ describe( expectedArtifactPath, ); const actualArtifactPath = path.join( - artifactsPath, + actualArtifactsPath, relativeArtifactPath, ); assert.ok( @@ -124,9 +142,9 @@ describe( beforeEach(async () => { await emitArtifacts(hre.solidity); - artifactPathsBefore = await getAllFilesMatching(artifactsPath); + artifactPathsBefore = await getAllFilesMatching(actualArtifactsPath); duplicatedContractNamesDeclarationFilePath = path.join( - artifactsPath, + actualArtifactsPath, "artifacts.d.ts", ); }); @@ -137,7 +155,7 @@ describe( ); const artifactPathsAfter = await getAllFilesMatching( - artifactsPath, + actualArtifactsPath, (f) => f !== duplicatedContractNamesDeclarationFilePath, ); @@ -158,7 +176,7 @@ describe( await hre.solidity.cleanupArtifacts(rootFilePathsToCleanUp); const artifactPathsAfter = await getAllFilesMatching( - artifactsPath, + actualArtifactsPath, (f) => f !== duplicatedContractNamesDeclarationFilePath, ); @@ -172,7 +190,7 @@ describe( await hre.solidity.cleanupArtifacts([]); const artifactPathsAfter = await getAllFilesMatching( - artifactsPath, + actualArtifactsPath, (f) => f !== duplicatedContractNamesDeclarationFilePath, ); From ded19e23c11329d04ab7a038ed0ef559d1079141 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 01:03:11 +0100 Subject: [PATCH 22/53] chore: simplify the compiler cache namespace --- .../builtin-plugins/solidity/build-system/build-system.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index cb15fbb162..31b788d161 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -83,7 +83,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { this.#options = options; this.#compilerOutputCache = new ObjectCache( options.cachePath, - "hardhat.core.solidity.build-system.compiler-output", + "compiler-output", "v1", ); } From 6386662c850bf38537d4780e1e147c18556e5796 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 01:03:28 +0100 Subject: [PATCH 23/53] fix: make compilation job build id deterministic again --- .../solidity/build-system/compilation-job.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index 5097aa4ea5..0303a9aac9 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -170,6 +170,10 @@ export class CompilationJobImplementation implements CompilationJob { }), ); + const sortedSources = Object.fromEntries( + Object.entries(sources).sort((a, b) => a[0].localeCompare(b[0])), + ); + // The preimage should include all the information that makes this // compilation job unique, and as this is used to identify the build info // file, it also includes its format string. @@ -177,7 +181,7 @@ export class CompilationJobImplementation implements CompilationJob { format + this.solcLongVersion + JSON.stringify(this.#getSolcInputWithoutSources()) + - JSON.stringify(sources); + JSON.stringify(sortedSources); JSON.stringify(this.solcConfig); return createNonCryptographicHashId(preimage); From 9a32f7014bf060d66e31f967db1d0cfa37fed1e6 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 01:03:47 +0100 Subject: [PATCH 24/53] fix: set sane defaults for cache options --- .../internal/builtin-plugins/solidity/build-system/cache.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts index 8b1f67fdfd..bec0c0e56b 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -12,8 +12,8 @@ import { export class ObjectCache { readonly #path: string; - readonly #maxAgeMs: number = 0; - readonly #maxSize: number = 0; + readonly #maxAgeMs: number = 7 * 24 * 60 * 60 * 1000; // 1 week + readonly #maxSize: number = 2 * 1024 * 1024 * 1024; // 2 GB constructor(basePath: string, namespace: string, version: string) { this.#path = path.join(basePath, namespace, version); From ff5bfd60c43281c2bd47ea1acbd29dc169a7d20b Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 09:12:53 +0100 Subject: [PATCH 25/53] chore: fix lint in utils --- v-next/hardhat-utils/src/crypto.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/v-next/hardhat-utils/src/crypto.ts b/v-next/hardhat-utils/src/crypto.ts index ff8889c2a0..024fcc9219 100644 --- a/v-next/hardhat-utils/src/crypto.ts +++ b/v-next/hardhat-utils/src/crypto.ts @@ -26,11 +26,11 @@ export async function keccak256(bytes: Uint8Array): Promise { * @returns The SHA-1 hash of the input string, represented as a * hexadecimal string. */ -export async function createNonCryptographicHashId(data: string): Promise { +export async function createNonCryptographicHashId( + data: string, +): Promise { const message = new TextEncoder().encode(data); const buffer = await crypto.subtle.digest("SHA-1", message); const array = Array.from(new Uint8Array(buffer)); - return array - .map((b) => b.toString(16).padStart(2, "0")) - .join(""); + return array.map((b) => b.toString(16).padStart(2, "0")).join(""); } From b28e802f22d3c393a7dd2cf5474a8046c6f806e7 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 09:15:39 +0100 Subject: [PATCH 26/53] chore: revert the changes to hardhat dependencies --- pnpm-lock.yaml | 12 +++--------- v-next/hardhat/package.json | 4 ++-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1bb19a4d1d..9945a9ee8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2028,8 +2028,8 @@ importers: specifier: ^5.18.1 version: 5.30.0 adm-zip: - specifier: ^0.5.16 - version: 0.5.16 + specifier: ^0.4.16 + version: 0.4.16 chalk: specifier: ^5.3.0 version: 5.4.1 @@ -2071,7 +2071,7 @@ importers: specifier: workspace:^ version: link:../hardhat-test-utils '@types/adm-zip': - specifier: ^0.5.7 + specifier: ^0.5.5 version: 0.5.7 '@types/debug': specifier: ^4.1.4 @@ -4779,10 +4779,6 @@ packages: resolution: {integrity: sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==} engines: {node: '>=0.3.0'} - adm-zip@0.5.16: - resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==} - engines: {node: '>=12.0'} - aes-js@3.0.0: resolution: {integrity: sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==} @@ -10667,8 +10663,6 @@ snapshots: adm-zip@0.4.16: {} - adm-zip@0.5.16: {} - aes-js@3.0.0: {} aes-js@4.0.0-beta.5: {} diff --git a/v-next/hardhat/package.json b/v-next/hardhat/package.json index 90ee232953..b8b84a2654 100644 --- a/v-next/hardhat/package.json +++ b/v-next/hardhat/package.json @@ -65,7 +65,7 @@ "@eslint-community/eslint-plugin-eslint-comments": "^4.3.0", "@ignored/hardhat-vnext-node-test-reporter": "workspace:^3.0.0-next.15", "@nomicfoundation/hardhat-test-utils": "workspace:^", - "@types/adm-zip": "^0.5.7", + "@types/adm-zip": "^0.5.5", "@types/debug": "^4.1.4", "@types/node": "^20.14.9", "@types/semver": "^7.5.8", @@ -91,7 +91,7 @@ "@ignored/hardhat-vnext-zod-utils": "workspace:^3.0.0-next.15", "@nomicfoundation/solidity-analyzer": "^0.1.0", "@sentry/node": "^5.18.1", - "adm-zip": "^0.5.16", + "adm-zip": "^0.4.16", "chalk": "^5.3.0", "debug": "^4.1.1", "enquirer": "^2.3.0", From 8660ad07ffa21f013b78618ca2d4dc4bf60e87b4 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 09:18:18 +0100 Subject: [PATCH 27/53] test: fix the hash creation tests after algorithm change --- v-next/hardhat-utils/test/crypto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v-next/hardhat-utils/test/crypto.ts b/v-next/hardhat-utils/test/crypto.ts index 27f4dbb8db..ac990f9b20 100644 --- a/v-next/hardhat-utils/test/crypto.ts +++ b/v-next/hardhat-utils/test/crypto.ts @@ -19,7 +19,7 @@ describe("crypto", () => { it("Should create a non-cryptographic hash-based identifier", async () => { assert.equal( await createNonCryptographicHashId("hello"), - "5d41402abc4b2a76b9719d911017c592", + "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d", ); }); }); From b13cae7e7c8b67881f9efb161abb2f5cbc44fa67 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 09:56:10 +0100 Subject: [PATCH 28/53] chore: revert changes to the getSolcInput signature --- .../builtin-plugins/solidity/build-system/artifacts.ts | 2 +- .../builtin-plugins/solidity/build-system/build-system.ts | 4 ++-- .../builtin-plugins/solidity/build-system/compilation-job.ts | 2 +- v-next/hardhat/src/types/solidity/compilation-job.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/artifacts.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/artifacts.ts index c7e902c69f..a49120315d 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/artifacts.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/artifacts.ts @@ -119,7 +119,7 @@ export async function getBuildInfo( solcVersion: compilationJob.solcConfig.version, solcLongVersion: compilationJob.solcLongVersion, publicSourceNameMap, - input: await compilationJob.getSolcInput(), + input: compilationJob.getSolcInput(), }; return buildInfo; diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index 31b788d161..a93195298e 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -419,7 +419,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { ); const compilerOutput = await compiler.compile( - await compilationJob.getSolcInput(), + compilationJob.getSolcInput(), ); return compilerOutput; @@ -765,7 +765,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { for (const job of compilationJobs) { const solcVersion = job.solcConfig.version; - const solcInput = await job.getSolcInput(); + const solcInput = job.getSolcInput(); const evmVersion = solcInput.settings.evmVersion ?? `Check solc ${solcVersion}'s doc for its default evm version`; diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index 0303a9aac9..f0fddf2d98 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -38,7 +38,7 @@ export class CompilationJobImplementation implements CompilationJob { this.#remappings = remappings; } - public async getSolcInput(): Promise { + public getSolcInput(): CompilerInput { if (this.#solcInput === undefined) { this.#solcInput = this.#buildSolcInput(); } diff --git a/v-next/hardhat/src/types/solidity/compilation-job.ts b/v-next/hardhat/src/types/solidity/compilation-job.ts index c1c0d523e5..d2d2aca951 100644 --- a/v-next/hardhat/src/types/solidity/compilation-job.ts +++ b/v-next/hardhat/src/types/solidity/compilation-job.ts @@ -25,7 +25,7 @@ export interface CompilationJob { /** * Returns the solc input to be used. */ - getSolcInput(): Promise; + getSolcInput(): CompilerInput; /** * Returns the build id of the compilation job. From 059385151a1d39afe15dec6397daa29716dd6e43 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 10:08:28 +0100 Subject: [PATCH 29/53] chore: remove unnecessary loops and recomputations --- .../solidity/build-system/build-system.ts | 18 ++++++------------ .../hardhat/src/types/solidity/build-system.ts | 6 ------ 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index a93195298e..b5def11689 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -136,7 +136,6 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { ); const runCompilationJobOptions: RunCompilationJobOptions = { - force: options?.force, quiet: options?.quiet, }; const results: CompilationResult[] = await pMap( @@ -144,7 +143,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { async (compilationJob) => { const buildId = await compilationJob.getBuildId(); - if (runCompilationJobOptions?.force !== true) { + if (options?.force !== true) { const cachedCompilerOutput = await this.#compilerOutputCache.get(buildId); if (cachedCompilerOutput !== undefined) { @@ -208,16 +207,15 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { quiet: options?.quiet, }; await Promise.all( - compilationJobs.map(async (compilationJob, i) => { - const result = results[i]; + results.map(async (result) => { const artifactsPerFile = await this.emitArtifacts( - compilationJob, + result.compilationJob, result.compilerOutput, emitArtifactsOptions, ); contractArtifactsGeneratedByCompilationJob.set( - compilationJob, + result.compilationJob, artifactsPerFile, ); }), @@ -418,11 +416,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { "The long version of the compiler should match the long version of the compilation job", ); - const compilerOutput = await compiler.compile( - compilationJob.getSolcInput(), - ); - - return compilerOutput; + return compiler.compile(compilationJob.getSolcInput()); } public async remapCompilerError( @@ -492,7 +486,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { ); const artifact = getContractArtifact( - await compilationJob.getBuildId(), + buildId, publicSourceName, root.sourceName, contractName, diff --git a/v-next/hardhat/src/types/solidity/build-system.ts b/v-next/hardhat/src/types/solidity/build-system.ts index a6a4fb38d5..b324b94c42 100644 --- a/v-next/hardhat/src/types/solidity/build-system.ts +++ b/v-next/hardhat/src/types/solidity/build-system.ts @@ -62,12 +62,6 @@ export type GetCompilationJobsOptions = Omit< * The options of the `runCompilationJob` method. */ export interface RunCompilationJobOptions { - /** - * If `true`, this option foces the build system to rerun the compilation job, - * even if its output is cached. - */ - force?: boolean; - /** * If `true`, the compilation process doesn't print any output. */ From 1179386b703453ce9aa6ac8ba933a3db4e3cf08a Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 11:01:01 +0100 Subject: [PATCH 30/53] test: implement new fs utils tests --- v-next/hardhat-utils/test/fs.ts | 64 +++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/v-next/hardhat-utils/test/fs.ts b/v-next/hardhat-utils/test/fs.ts index b08aed91b0..7d70dd17f4 100644 --- a/v-next/hardhat-utils/test/fs.ts +++ b/v-next/hardhat-utils/test/fs.ts @@ -26,6 +26,8 @@ import { writeJsonFile, writeUtf8File, readBinaryFile, + getAccessTime, + getSize, } from "../src/fs.js"; import { useTmpDir } from "./helpers/fs.js"; @@ -735,6 +737,68 @@ describe("File system utils", () => { }); }); + describe("getAccessTime", () => { + it("Should return the access time of a file", async () => { + const filePath = path.join(getTmpDir(), "file.txt"); + await createFile(filePath); + + const stats = await fsPromises.stat(filePath); + + assert.equal( + stats.atime.getTime(), + (await getAccessTime(filePath)).getTime(), + ); + }); + + it("Should throw FileNotFoundError if the file doesn't exist", async () => { + const filePath = path.join(getTmpDir(), "not-exists.txt"); + + await assert.rejects(getAccessTime(filePath), { + name: "FileNotFoundError", + message: `File ${filePath} not found`, + }); + }); + + it("Should throw FileSystemAccessError if a different error is thrown", async () => { + const invalidPath = "\0"; + + await assert.rejects(getAccessTime(invalidPath), { + name: "FileSystemAccessError", + }); + }); + }); + + describe("getSize", () => { + it("Should return the size of a file", async () => { + const filePath = path.join(getTmpDir(), "file.txt"); + await createFile(filePath); + + const stats = await fsPromises.stat(filePath); + + assert.equal( + stats.size, + (await getSize(filePath)), + ); + }); + + it("Should throw FileNotFoundError if the file doesn't exist", async () => { + const filePath = path.join(getTmpDir(), "not-exists.txt"); + + await assert.rejects(getSize(filePath), { + name: "FileNotFoundError", + message: `File ${filePath} not found`, + }); + }); + + it("Should throw FileSystemAccessError if a different error is thrown", async () => { + const invalidPath = "\0"; + + await assert.rejects(getSize(invalidPath), { + name: "FileSystemAccessError", + }); + }); + }); + describe("exists", () => { it("Should return true if the file exists", async () => { const filePath = path.join(getTmpDir(), "file.txt"); From 0a52ab76ff28110d3003ede4eb6a9645cb1959f0 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 11:06:12 +0100 Subject: [PATCH 31/53] fix: the build id should include solc config --- .../builtin-plugins/solidity/build-system/compilation-job.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index f0fddf2d98..f2b2d459ab 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -181,8 +181,8 @@ export class CompilationJobImplementation implements CompilationJob { format + this.solcLongVersion + JSON.stringify(this.#getSolcInputWithoutSources()) + - JSON.stringify(sortedSources); - JSON.stringify(this.solcConfig); + JSON.stringify(sortedSources) + + JSON.stringify(this.solcConfig); return createNonCryptographicHashId(preimage); } From 27d6a2d1e0bab23d4b03505da607cbe5e55a3fef Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 15:47:07 +0100 Subject: [PATCH 32/53] chore: add a utility clear cache function to the compilation job --- .../builtin-plugins/solidity/build-system/compilation-job.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index f2b2d459ab..a6dbacec38 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -186,4 +186,9 @@ export class CompilationJobImplementation implements CompilationJob { return createNonCryptographicHashId(preimage); } + + // NOTE: This method is used internally to clear the cache in between tests. + public static clearCache(): void { + CompilationJobImplementation.#sourceContentHashCache.clear(); + } } From e55db65641bb70440b4dd5e11a11e6ce66d191b2 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 15:47:59 +0100 Subject: [PATCH 33/53] test: implement compilation job build id tests --- .../solidity/build-system/compilation-job.ts | 307 ++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts new file mode 100644 index 0000000000..da77afc477 --- /dev/null +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -0,0 +1,307 @@ +import type { Remapping } from "../../../../../src/internal/builtin-plugins/solidity/build-system/resolver/types.js"; +import type { SolcConfig } from "../../../../../src/types/config.js"; +import type { + NpmPackageResolvedFile, + ProjectResolvedFile, +} from "../../../../../src/types/solidity.js"; + +import assert from "node:assert/strict"; +import { before, beforeEach, describe, it } from "node:test"; + +import { CompilationJobImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/compilation-job.js"; +import { DependencyGraphImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/dependency-graph.js"; +import { ResolvedFileType } from "../../../../../src/types/solidity.js"; + +describe("CompilationJobImplementation", () => { + let dependencyGraph: DependencyGraphImplementation; + let rootFile: ProjectResolvedFile; + let npmDependencyFile: NpmPackageResolvedFile; + let projectDependencyFile: ProjectResolvedFile; + let solcConfig: SolcConfig; + let solcLongVersion: string; + let remappings: Remapping[]; + let compilationJob: CompilationJobImplementation; + + before(() => { + dependencyGraph = new DependencyGraphImplementation(); + rootFile = { + type: ResolvedFileType.PROJECT_FILE, + sourceName: "root.sol", + fsPath: "root.sol", + content: { + text: "contract Root {}", + importPaths: [], + versionPragmas: [], + }, + }; + npmDependencyFile = { + type: ResolvedFileType.NPM_PACKGE_FILE, + sourceName: "npm:dependency/1.0.0/dependency.sol", + fsPath: "dependency.sol", + package: { + name: "dependency", + version: "1.0.0", + rootFsPath: "dependency", + rootSourceName: "dependency.sol", + }, + content: { + text: "contract Dependency {}", + importPaths: [], + versionPragmas: [], + }, + }; + projectDependencyFile = { + type: ResolvedFileType.PROJECT_FILE, + sourceName: "dependency.sol", + fsPath: "dependency.sol", + content: { + text: "contract Dependency {}", + importPaths: [], + versionPragmas: [], + }, + }; + dependencyGraph.addRootFile(rootFile.sourceName, rootFile); + dependencyGraph.addDependency(rootFile, npmDependencyFile); + dependencyGraph.addDependency(rootFile, projectDependencyFile); + solcConfig = { + version: "0.8.0", + settings: {}, + }; + solcLongVersion = "0.8.0-c7dfd78"; + remappings = []; + compilationJob = new CompilationJobImplementation( + dependencyGraph, + solcConfig, + solcLongVersion, + remappings, + ); + }); + + beforeEach(() => { + CompilationJobImplementation.clearCache(); + }); + + describe("getBuildId", () => { + describe("should change when", () => { + it("the solc long version changes", async () => { + const newCompilationJob = new CompilationJobImplementation( + dependencyGraph, + solcConfig, + "0.8.0-df193b1", + remappings, + ); + assert.notEqual( + await compilationJob.getBuildId(), + await newCompilationJob.getBuildId(), + ); + }); + it("the settings change", async () => { + const newCompilationJob = new CompilationJobImplementation( + dependencyGraph, + { + ...solcConfig, + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + solcLongVersion, + remappings, + ); + assert.notEqual( + await compilationJob.getBuildId(), + await newCompilationJob.getBuildId(), + ); + }); + it("the remappings change", async () => { + const newCompilationJob = new CompilationJobImplementation( + dependencyGraph, + solcConfig, + solcLongVersion, + [ + { + context: "test", + prefix: "test", + target: "test", + }, + ], + ); + assert.notEqual( + await compilationJob.getBuildId(), + await newCompilationJob.getBuildId(), + ); + }); + it("there is an additional root file in the dependency graph", async () => { + const newDependencyGraph = new DependencyGraphImplementation(); + const newRootFile = { + ...rootFile, + sourceName: "newRoot.sol", + }; + newDependencyGraph.addRootFile(rootFile.sourceName, rootFile); + newDependencyGraph.addDependency(rootFile, npmDependencyFile); + newDependencyGraph.addDependency(rootFile, projectDependencyFile); + newDependencyGraph.addRootFile("newRoot.sol", newRootFile); + const newCompilationJob = new CompilationJobImplementation( + newDependencyGraph, + solcConfig, + solcLongVersion, + remappings, + ); + assert.notEqual( + await compilationJob.getBuildId(), + await newCompilationJob.getBuildId(), + ); + }); + it("there is an additional dependency in the dependency graph", async () => { + const newDependencyGraph = new DependencyGraphImplementation(); + const newDependencyFile = { + ...projectDependencyFile, + sourceName: "newDependency.sol", + }; + newDependencyGraph.addRootFile(rootFile.sourceName, rootFile); + newDependencyGraph.addDependency(rootFile, npmDependencyFile); + newDependencyGraph.addDependency(rootFile, projectDependencyFile); + newDependencyGraph.addDependency(rootFile, newDependencyFile); + const newCompilationJob = new CompilationJobImplementation( + newDependencyGraph, + solcConfig, + solcLongVersion, + remappings, + ); + assert.notEqual( + await compilationJob.getBuildId(), + await newCompilationJob.getBuildId(), + ); + }); + it("the content of one of the root files changes", async () => { + const newDependencyGraph = new DependencyGraphImplementation(); + const newRootFile = { + ...rootFile, + content: { + ...rootFile.content, + text: "contract NewRoot {}", + }, + }; + newDependencyGraph.addRootFile(newRootFile.sourceName, newRootFile); + newDependencyGraph.addDependency(newRootFile, npmDependencyFile); + newDependencyGraph.addDependency(newRootFile, projectDependencyFile); + const newCompilationJob = new CompilationJobImplementation( + newDependencyGraph, + solcConfig, + solcLongVersion, + remappings, + ); + assert.notEqual( + await compilationJob.getBuildId(), + await newCompilationJob.getBuildId(), + ); + }); + it("the content of one of the dependencies changes", async () => { + const newDependencyGraph = new DependencyGraphImplementation(); + const newDependencyFile = { + ...npmDependencyFile, + content: { + ...npmDependencyFile.content, + text: "contract NewDependency {}", + }, + }; + newDependencyGraph.addRootFile(rootFile.sourceName, rootFile); + newDependencyGraph.addDependency(rootFile, newDependencyFile); + newDependencyGraph.addDependency(rootFile, projectDependencyFile); + const newCompilationJob = new CompilationJobImplementation( + newDependencyGraph, + solcConfig, + solcLongVersion, + remappings, + ); + assert.notEqual( + compilationJob.getBuildId(), + newCompilationJob.getBuildId(), + ); + }); + it("the source name of one of the root files changes", async () => { + const newDependencyGraph = new DependencyGraphImplementation(); + const newRootFile = { + ...rootFile, + sourceName: "newRoot.sol", + }; + newDependencyGraph.addRootFile(newRootFile.sourceName, newRootFile); + newDependencyGraph.addDependency(newRootFile, npmDependencyFile); + newDependencyGraph.addDependency(newRootFile, projectDependencyFile); + const newCompilationJob = new CompilationJobImplementation( + newDependencyGraph, + solcConfig, + solcLongVersion, + remappings, + ); + assert.notEqual( + await compilationJob.getBuildId(), + await newCompilationJob.getBuildId(), + ); + }); + it("the source name of one of the dependencies changes", async () => { + const newDependencyGraph = new DependencyGraphImplementation(); + const newDependencyFile = { + ...npmDependencyFile, + sourceName: "npm:dependency/1.0.0/newDependency.sol", + }; + newDependencyGraph.addRootFile(rootFile.sourceName, rootFile); + newDependencyGraph.addDependency(rootFile, newDependencyFile); + newDependencyGraph.addDependency(rootFile, projectDependencyFile); + const newCompilationJob = new CompilationJobImplementation( + newDependencyGraph, + solcConfig, + solcLongVersion, + remappings, + ); + assert.notEqual( + await compilationJob.getBuildId(), + await newCompilationJob.getBuildId(), + ); + }); + }); + describe("should not change when", () => { + it("the version of one of the dependencies changes without it being reflected in the source name", async () => { + const newDependencyGraph = new DependencyGraphImplementation(); + const newDependencyFile = { + ...npmDependencyFile, + package: { + ...npmDependencyFile.package, + version: "2.0.0", + }, + }; + newDependencyGraph.addRootFile(rootFile.sourceName, rootFile); + newDependencyGraph.addDependency(rootFile, newDependencyFile); + newDependencyGraph.addDependency(rootFile, projectDependencyFile); + const newCompilationJob = new CompilationJobImplementation( + newDependencyGraph, + solcConfig, + solcLongVersion, + remappings, + ); + assert.equal( + await compilationJob.getBuildId(), + await newCompilationJob.getBuildId(), + ); + }); + it("the order of the sources changes", async () => { + const newDependencyGraph = new DependencyGraphImplementation(); + newDependencyGraph.addRootFile(rootFile.sourceName, rootFile); + newDependencyGraph.addDependency(rootFile, projectDependencyFile); + newDependencyGraph.addDependency(rootFile, npmDependencyFile); + const newCompilationJob = new CompilationJobImplementation( + newDependencyGraph, + solcConfig, + solcLongVersion, + remappings, + ); + assert.equal( + await compilationJob.getBuildId(), + await newCompilationJob.getBuildId(), + ); + }); + }); + }); +}); From 0b59516c23575df288b999736ed6fcf420c9b151 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 16:40:44 +0100 Subject: [PATCH 34/53] chore: make object cache more suitable for testing --- .../solidity/build-system/cache.ts | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts index bec0c0e56b..2b8ec1e1c9 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -12,11 +12,19 @@ import { export class ObjectCache { readonly #path: string; - readonly #maxAgeMs: number = 7 * 24 * 60 * 60 * 1000; // 1 week - readonly #maxSize: number = 2 * 1024 * 1024 * 1024; // 2 GB + readonly #defaultMaxAgeMs: number; + readonly #defaultMaxSize: number; - constructor(basePath: string, namespace: string, version: string) { + constructor( + basePath: string, + namespace: string, + version: string, + defaultMaxAgeMs: number = 7 * 24 * 60 * 60 * 1000, // 1 week + defaultMaxSize: number = 2 * 1024 * 1024 * 1024, // 2 GB + ) { this.#path = path.join(basePath, namespace, version); + this.#defaultMaxAgeMs = defaultMaxAgeMs; + this.#defaultMaxSize = defaultMaxSize; } public async set(key: string, value: T): Promise { @@ -29,7 +37,10 @@ export class ObjectCache { return (await exists(filePath)) ? readJsonFile(filePath) : undefined; } - public async clean(): Promise { + public async clean(maxAgeMs?: number, maxSize?: number): Promise { + maxAgeMs ??= this.#defaultMaxAgeMs; + maxSize ??= this.#defaultMaxSize; + const files = await getAllFilesMatching(this.#path); const fileInfos = await Promise.all( files.map(async (file) => ({ @@ -45,12 +56,12 @@ export class ObjectCache { (acc, fileInfo) => acc + fileInfo.size, 0, ); - const minAtimeMs = new Date(0 - this.#maxAgeMs).getTime(); + const minAtimeMs = new Date().getTime() - maxAgeMs; const filesToRemove: string[] = []; for (const fileInfo of sortedFileInfos) { - if (fileInfo.atimeMs < minAtimeMs || size > this.#maxSize) { + if (fileInfo.atimeMs < minAtimeMs || size > maxSize) { filesToRemove.push(fileInfo.file); size -= fileInfo.size; } else { From b8eff7603a7edd4e80ffed296e58ac12b48609fc Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 16:41:04 +0100 Subject: [PATCH 35/53] test: implement object cache tests --- .../solidity/build-system/cache.ts | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts new file mode 100644 index 0000000000..90abad8232 --- /dev/null +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts @@ -0,0 +1,107 @@ +import assert from "node:assert/strict"; +import { randomUUID } from "node:crypto"; +import path from "node:path"; +import { beforeEach, describe, it } from "node:test"; + +import { getAllFilesMatching, getSize } from "@ignored/hardhat-vnext-utils/fs"; +import { useTmpDir } from "@nomicfoundation/hardhat-test-utils"; + +import { ObjectCache } from "../../../../../src/internal/builtin-plugins/solidity/build-system/cache.js"; + +describe("ObjectCache", () => { + let cache: ObjectCache; + let cachePath: string; + let testKey: string; + let testValue: string; + + useTmpDir("object-cache"); + + beforeEach(async () => { + cachePath = path.join(process.cwd(), "cache"); + cache = new ObjectCache(cachePath, "test", "v1"); + testKey = randomUUID(); + testValue = "testValue"; + await cache.set(testKey, testValue); + }); + + describe("get", () => { + it("should return undefined if the key is not present", async () => { + assert.equal(await cache.get(randomUUID()), undefined); + }); + it("should return the value if the key is present", async () => { + assert.equal(await cache.get(testKey), testValue); + }); + it("should return undefined if the key is present under a different namespace", async () => { + const newCache = new ObjectCache(cachePath, "newTest", "v1"); + assert.equal(await newCache.get(testKey), undefined); + }); + it("should return undefined if the key is present under a different version", async () => { + const newCache = new ObjectCache(cachePath, "test", "v2"); + assert.equal(await newCache.get(testKey), undefined); + }); + }); + + describe("set", () => { + it("should set the value if the key is not present", async () => { + const randomKey = randomUUID(); + assert.equal(await cache.get(randomKey), undefined); + await cache.set(randomKey, "randomValue"); + assert.equal(await cache.get(randomKey), "randomValue"); + }); + it("should overwrite the value if the key is present", async () => { + const randomKey = randomUUID(); + await cache.set(randomKey, "randomValue"); + assert.equal(await cache.get(randomKey), "randomValue"); + await cache.set(randomKey, "newRandomValue"); + assert.equal(await cache.get(randomKey), "newRandomValue"); + }); + }); + + describe("clean", () => { + it("should remove nothing with the default settings", async () => { + const filesBefore = await getAllFilesMatching(cachePath); + assert.notDeepEqual(filesBefore, []); + await cache.clean(); + const filesAfter = await getAllFilesMatching(cachePath); + assert.deepEqual(filesAfter, filesBefore); + }); + + it("should remove everything with the max age set to 0", async () => { + const filesBefore = await getAllFilesMatching(cachePath); + assert.notDeepEqual(filesBefore, []); + await cache.clean(0); + const filesAfter = await getAllFilesMatching(cachePath); + assert.deepEqual(filesAfter, []); + }); + + it("should remove everything with the max size set to 0", async () => { + const filesBefore = await getAllFilesMatching(cachePath); + assert.notDeepEqual(filesBefore, []); + await cache.clean(undefined, 0); + const filesAfter = await getAllFilesMatching(cachePath); + assert.deepEqual(filesAfter, []); + }); + + it("should remove something with the max age set to some value", async () => { + const filesBefore = await getAllFilesMatching(cachePath); + assert.notDeepEqual(filesBefore, []); + // NOTE: If the test ends up being flaky, we should increase the timeout + await new Promise((resolve) => setTimeout(resolve, 10)); + cache.set(randomUUID(), testValue); + await cache.clean(10); + const filesAfter = await getAllFilesMatching(cachePath); + assert.notDeepEqual(filesAfter, []); + assert.notDeepEqual(filesAfter, filesBefore); + }); + + it("should remove something with the max size set to some value", async () => { + const filesBefore = await getAllFilesMatching(cachePath); + assert.notDeepEqual(filesBefore, []); + cache.set(randomUUID(), testValue); + await cache.clean(undefined, (await getSize(filesBefore[0])) * 1.5); + const filesAfter = await getAllFilesMatching(cachePath); + assert.notDeepEqual(filesAfter, []); + assert.notDeepEqual(filesAfter, filesBefore); + }); + }); +}); From d14b38551776e4889cc24f6284d2346121aa58ad Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 16:42:34 +0100 Subject: [PATCH 36/53] chore: fix linting of the fs tests --- v-next/hardhat-utils/test/fs.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/v-next/hardhat-utils/test/fs.ts b/v-next/hardhat-utils/test/fs.ts index 7d70dd17f4..3c87e5067a 100644 --- a/v-next/hardhat-utils/test/fs.ts +++ b/v-next/hardhat-utils/test/fs.ts @@ -775,10 +775,7 @@ describe("File system utils", () => { const stats = await fsPromises.stat(filePath); - assert.equal( - stats.size, - (await getSize(filePath)), - ); + assert.equal(stats.size, await getSize(filePath)); }); it("Should throw FileNotFoundError if the file doesn't exist", async () => { From 8f8a033a389bdfac9a8ad039bafb8ba5bd9786c0 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 18:12:44 +0100 Subject: [PATCH 37/53] test: implement more build system tests --- .../solidity/build-system/build-system.ts | 243 +++++++++++++++--- 1 file changed, 203 insertions(+), 40 deletions(-) diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts index f0979bc4e2..a5b93e18dc 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -1,5 +1,4 @@ -import type { SolidityUserConfig } from "../../../../../src/types/config.js"; -import type { HardhatRuntimeEnvironment } from "../../../../../src/types/hre.js"; +import type { SolidityConfig } from "../../../../../src/types/config.js"; import type { SolidityBuildInfoOutput, SolidityBuildSystem, @@ -7,7 +6,7 @@ import type { import assert from "node:assert/strict"; import path from "node:path"; -import { before, beforeEach, describe, it } from "node:test"; +import { before, beforeEach, describe, it, mock } from "node:test"; import { exists, @@ -20,13 +19,13 @@ import { useFixtureProject, } from "@nomicfoundation/hardhat-test-utils"; -import { createHardhatRuntimeEnvironment } from "../../../../../src/hre.js"; +import { SolidityBuildSystemImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/build-system.js"; async function emitArtifacts(solidity: SolidityBuildSystem): Promise { const rootFilePaths = await solidity.getRootFilePaths(); const compilationJobs = await solidity.getCompilationJobs(rootFilePaths, { mergeCompilationJobs: true, - quiet: false, + quiet: true, }); assert.ok(compilationJobs instanceof Map, "compilationJobs should be a Map"); @@ -54,58 +53,69 @@ describe( }, () => { let actualArtifactsPath: string; + let actualCachePath: string; let expectedArtifactsPath: string; - let hre: HardhatRuntimeEnvironment; + let expectedCachePath: string; + let solidity: SolidityBuildSystemImplementation; useFixtureProject("solidity/example-project"); - const solidityUserConfig: SolidityUserConfig = { + const solidityConfig: SolidityConfig = { profiles: { default: { compilers: [ { version: "0.8.22", + settings: {}, }, { version: "0.7.1", + settings: {}, }, ], + overrides: {}, }, }, + dependenciesToCompile: [], remappings: ["remapped/=npm/@openzeppelin/contracts@5.1.0/access/"], }; before(async () => { expectedArtifactsPath = path.join(process.cwd(), "artifacts"); - const expectedCachePath = path.join(process.cwd(), "cache"); + expectedCachePath = path.join(process.cwd(), "cache"); await remove(expectedArtifactsPath); await remove(expectedCachePath); - hre = await createHardhatRuntimeEnvironment({ - paths: { - artifacts: expectedArtifactsPath, - cache: expectedCachePath, - }, - solidity: solidityUserConfig, + solidity = new SolidityBuildSystemImplementation({ + solidityConfig, + projectRoot: process.cwd(), + soliditySourcesPaths: [path.join(process.cwd(), "contracts")], + artifactsPath: expectedArtifactsPath, + cachePath: expectedCachePath, + }); + const rootFilePaths = await solidity.getRootFilePaths(); + await solidity.build(rootFilePaths, { + force: true, + mergeCompilationJobs: true, + quiet: true, }); - await hre.tasks.getTask("compile").run({ quiet: false }); }); beforeEach(async () => { const tmpDir = await getTmpDir("solidity-build-system-implementation"); actualArtifactsPath = path.join(tmpDir, "artifacts"); - const actualCachePath = path.join(tmpDir, "cache"); - hre = await createHardhatRuntimeEnvironment({ - paths: { - artifacts: actualArtifactsPath, - cache: actualCachePath, - }, - solidity: solidityUserConfig, + actualCachePath = path.join(tmpDir, "cache"); + solidity = new SolidityBuildSystemImplementation({ + solidityConfig, + projectRoot: process.cwd(), + soliditySourcesPaths: [path.join(process.cwd(), "contracts")], + artifactsPath: actualArtifactsPath, + cachePath: actualCachePath, }); }); describe("emitArtifacts", () => { it("should successfully emit the artifacts", async () => { - await emitArtifacts(hre.solidity); + await emitArtifacts(solidity); const expectedArtifactPaths = await getAllFilesMatching( expectedArtifactsPath, @@ -137,12 +147,13 @@ describe( }); describe("cleanupArtifacts", () => { - let artifactPathsBefore: string[]; + let actualArtifactPathsBefore: string[]; let duplicatedContractNamesDeclarationFilePath: string; beforeEach(async () => { - await emitArtifacts(hre.solidity); - artifactPathsBefore = await getAllFilesMatching(actualArtifactsPath); + await emitArtifacts(solidity); + actualArtifactPathsBefore = + await getAllFilesMatching(actualArtifactsPath); duplicatedContractNamesDeclarationFilePath = path.join( actualArtifactsPath, "artifacts.d.ts", @@ -150,60 +161,212 @@ describe( }); it("should clean up no artifacts when given all root file paths", async () => { - await hre.solidity.cleanupArtifacts( - await hre.solidity.getRootFilePaths(), - ); + await solidity.cleanupArtifacts(await solidity.getRootFilePaths()); - const artifactPathsAfter = await getAllFilesMatching( + const actualArtifactPathsAfter = await getAllFilesMatching( actualArtifactsPath, (f) => f !== duplicatedContractNamesDeclarationFilePath, ); assert.deepEqual( - artifactPathsAfter, - artifactPathsBefore, + actualArtifactPathsAfter, + actualArtifactPathsBefore, "No artifacts should be cleaned up", ); }); it("should not clean up some of the artifacts when given a subset of all root file paths", async () => { - const rootFilePaths = await hre.solidity.getRootFilePaths(); + const rootFilePaths = await solidity.getRootFilePaths(); const rootFilePathsToCleanUp = rootFilePaths.slice( 0, rootFilePaths.length - 1, ); - await hre.solidity.cleanupArtifacts(rootFilePathsToCleanUp); + await solidity.cleanupArtifacts(rootFilePathsToCleanUp); - const artifactPathsAfter = await getAllFilesMatching( + const actualArtifactPathsAfter = await getAllFilesMatching( actualArtifactsPath, (f) => f !== duplicatedContractNamesDeclarationFilePath, ); assert.ok( - artifactPathsBefore.length > artifactPathsAfter.length, + actualArtifactPathsBefore.length > actualArtifactPathsAfter.length, "Some artifacts should be cleaned up", ); }); it("should clean up all the artifacts when given no root file paths", async () => { - await hre.solidity.cleanupArtifacts([]); + await solidity.cleanupArtifacts([]); - const artifactPathsAfter = await getAllFilesMatching( + const actualArtifactPathsAfter = await getAllFilesMatching( actualArtifactsPath, (f) => f !== duplicatedContractNamesDeclarationFilePath, ); assert.ok( - artifactPathsBefore.length > 0, + actualArtifactPathsBefore.length > 0, "There should be some artifacts to clean up", ); assert.deepEqual( - artifactPathsAfter, + actualArtifactPathsAfter, [], "All artifacts should be cleaned up", ); }); }); + + describe("build", () => { + let expectedArtifactPaths: string[]; + let expectedCachePaths: string[]; + + before(async () => { + expectedArtifactPaths = ( + await getAllFilesMatching(expectedArtifactsPath) + ).map((f) => path.relative(expectedArtifactsPath, f)); + expectedCachePaths = (await getAllFilesMatching(expectedCachePath)).map( + (f) => path.relative(expectedCachePath, f), + ); + }); + + it("should build the project deterministically", async () => { + const rootFilePaths = await solidity.getRootFilePaths(); + await solidity.build(rootFilePaths, { + force: true, + mergeCompilationJobs: true, + quiet: true, + }); + + const actualArtifactPaths = ( + await getAllFilesMatching(actualArtifactsPath) + ).map((f) => path.relative(actualArtifactsPath, f)); + const actualCachePaths = ( + await getAllFilesMatching(actualCachePath) + ).map((f) => path.relative(actualCachePath, f)); + + assert.deepEqual( + actualArtifactPaths, + expectedArtifactPaths, + "Artifacts should be the same", + ); + assert.deepEqual( + actualCachePaths, + expectedCachePaths, + "Cache should be the same", + ); + }); + + it("should not recompile the project when given the same input as in the previous call", async () => { + const rootFilePaths = await solidity.getRootFilePaths(); + await solidity.build(rootFilePaths, { + force: true, + mergeCompilationJobs: true, + quiet: true, + }); + + const runCompilationJobSpy = mock.method(solidity, "runCompilationJob"); + + await solidity.build(rootFilePaths, { + force: false, + mergeCompilationJobs: true, + quiet: true, + }); + + assert.equal(runCompilationJobSpy.mock.callCount(), 0); + }); + + it("should recompile the project when given the same input as in the previous call but the force flag is set", async () => { + const rootFilePaths = await solidity.getRootFilePaths(); + await solidity.build(rootFilePaths, { + force: true, + mergeCompilationJobs: true, + quiet: true, + }); + + const runCompilationJobSpy = mock.method(solidity, "runCompilationJob"); + + await solidity.build(rootFilePaths, { + force: true, + mergeCompilationJobs: true, + quiet: true, + }); + + assert.equal(runCompilationJobSpy.mock.callCount(), 2); + }); + + it("should not recompile the project when the input changed but the generated compilation jobs are the subset of the previous ones", async () => { + const rootFilePaths = await solidity.getRootFilePaths(); + await solidity.build(rootFilePaths, { + force: true, + mergeCompilationJobs: true, + quiet: true, + }); + + const runCompilationJobSpy = mock.method(solidity, "runCompilationJob"); + + await solidity.build( + rootFilePaths.filter((f) => path.basename(f) !== "NoImports.sol"), + { + force: false, + mergeCompilationJobs: true, + quiet: true, + }, + ); + + assert.equal(runCompilationJobSpy.mock.callCount(), 0); + }); + + it("should recompile the project when the input changed and the generated compilation jobs changed", async () => { + const rootFilePaths = await solidity.getRootFilePaths(); + await solidity.build(rootFilePaths, { + force: true, + mergeCompilationJobs: true, + quiet: true, + }); + + const runCompilationJobSpy = mock.method(solidity, "runCompilationJob"); + + await solidity.build( + rootFilePaths.filter((f) => path.basename(f) !== "A.sol"), + { + force: false, + mergeCompilationJobs: true, + quiet: true, + }, + ); + + assert.equal(runCompilationJobSpy.mock.callCount(), 1); + }); + + it("should not recompile the project when the input is the same as in one of the previous calls", async () => { + const rootFilePaths = await solidity.getRootFilePaths(); + await solidity.build(rootFilePaths, { + force: true, + mergeCompilationJobs: true, + quiet: true, + }); + + await solidity.build( + rootFilePaths.filter((f) => path.basename(f) !== "A.sol"), + { + force: false, + mergeCompilationJobs: true, + quiet: true, + }, + ); + + const runCompilationJobSpy = mock.method(solidity, "runCompilationJob"); + + await solidity.build( + rootFilePaths.filter((f) => path.basename(f) !== "A.sol"), + { + force: false, + mergeCompilationJobs: true, + quiet: true, + }, + ); + + assert.equal(runCompilationJobSpy.mock.callCount(), 0); + }); + }); }, ); From 135d975b17f239bf4f2f364d51270993232fc560 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 18:23:49 +0100 Subject: [PATCH 38/53] chore: fix tests after merging v-next --- .../solidity/build-system/build-system.ts | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts index a5b93e18dc..036485475d 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -20,6 +20,7 @@ import { } from "@nomicfoundation/hardhat-test-utils"; import { SolidityBuildSystemImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/build-system.js"; +import { HookManagerImplementation } from "../../../../../src/internal/core/hook-manager.js"; async function emitArtifacts(solidity: SolidityBuildSystem): Promise { const rootFilePaths = await solidity.getRootFilePaths(); @@ -85,13 +86,16 @@ describe( expectedCachePath = path.join(process.cwd(), "cache"); await remove(expectedArtifactsPath); await remove(expectedCachePath); - solidity = new SolidityBuildSystemImplementation({ - solidityConfig, - projectRoot: process.cwd(), - soliditySourcesPaths: [path.join(process.cwd(), "contracts")], - artifactsPath: expectedArtifactsPath, - cachePath: expectedCachePath, - }); + solidity = new SolidityBuildSystemImplementation( + new HookManagerImplementation(process.cwd(), []), + { + solidityConfig, + projectRoot: process.cwd(), + soliditySourcesPaths: [path.join(process.cwd(), "contracts")], + artifactsPath: expectedArtifactsPath, + cachePath: expectedCachePath, + }, + ); const rootFilePaths = await solidity.getRootFilePaths(); await solidity.build(rootFilePaths, { force: true, @@ -104,13 +108,16 @@ describe( const tmpDir = await getTmpDir("solidity-build-system-implementation"); actualArtifactsPath = path.join(tmpDir, "artifacts"); actualCachePath = path.join(tmpDir, "cache"); - solidity = new SolidityBuildSystemImplementation({ - solidityConfig, - projectRoot: process.cwd(), - soliditySourcesPaths: [path.join(process.cwd(), "contracts")], - artifactsPath: actualArtifactsPath, - cachePath: actualCachePath, - }); + solidity = new SolidityBuildSystemImplementation( + new HookManagerImplementation(process.cwd(), []), + { + solidityConfig, + projectRoot: process.cwd(), + soliditySourcesPaths: [path.join(process.cwd(), "contracts")], + artifactsPath: actualArtifactsPath, + cachePath: actualCachePath, + }, + ); }); describe("emitArtifacts", () => { From c2de298a85fbe70fa2e9cd0798662597f21b20e5 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 18:33:11 +0100 Subject: [PATCH 39/53] fix: provide implicitly required context for the hook manager --- .../solidity/build-system/build-system.ts | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts index 036485475d..e20b3e1914 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -1,4 +1,5 @@ import type { SolidityConfig } from "../../../../../src/types/config.js"; +import type { HookContext } from "../../../../../src/types/hooks.js"; import type { SolidityBuildInfoOutput, SolidityBuildSystem, @@ -47,7 +48,7 @@ async function emitArtifacts(solidity: SolidityBuildSystem): Promise { } // NOTE: This test is slow because solidity compilers are downloaded. -describe( +describe.only( "SolidityBuildSystemImplementation", { skip: process.env.HARDHAT_DISABLE_SLOW_TESTS === "true", @@ -86,16 +87,16 @@ describe( expectedCachePath = path.join(process.cwd(), "cache"); await remove(expectedArtifactsPath); await remove(expectedCachePath); - solidity = new SolidityBuildSystemImplementation( - new HookManagerImplementation(process.cwd(), []), - { - solidityConfig, - projectRoot: process.cwd(), - soliditySourcesPaths: [path.join(process.cwd(), "contracts")], - artifactsPath: expectedArtifactsPath, - cachePath: expectedCachePath, - }, - ); + const hooks = new HookManagerImplementation(process.cwd(), []); + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- We don't care about hooks in this context + hooks.setContext({} as HookContext); + solidity = new SolidityBuildSystemImplementation(hooks, { + solidityConfig, + projectRoot: process.cwd(), + soliditySourcesPaths: [path.join(process.cwd(), "contracts")], + artifactsPath: expectedArtifactsPath, + cachePath: expectedCachePath, + }); const rootFilePaths = await solidity.getRootFilePaths(); await solidity.build(rootFilePaths, { force: true, @@ -108,16 +109,16 @@ describe( const tmpDir = await getTmpDir("solidity-build-system-implementation"); actualArtifactsPath = path.join(tmpDir, "artifacts"); actualCachePath = path.join(tmpDir, "cache"); - solidity = new SolidityBuildSystemImplementation( - new HookManagerImplementation(process.cwd(), []), - { - solidityConfig, - projectRoot: process.cwd(), - soliditySourcesPaths: [path.join(process.cwd(), "contracts")], - artifactsPath: actualArtifactsPath, - cachePath: actualCachePath, - }, - ); + const hooks = new HookManagerImplementation(process.cwd(), []); + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- We don't care about hooks in this context + hooks.setContext({} as HookContext); + solidity = new SolidityBuildSystemImplementation(hooks, { + solidityConfig, + projectRoot: process.cwd(), + soliditySourcesPaths: [path.join(process.cwd(), "contracts")], + artifactsPath: actualArtifactsPath, + cachePath: actualCachePath, + }); }); describe("emitArtifacts", () => { From 35b8e963fff38697332d7c88bfeaa0e8e2a518e2 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 18:35:58 +0100 Subject: [PATCH 40/53] chore: revert the addition of emit artifacts options --- .../solidity/build-system/build-system.ts | 6 ------ .../builtin-plugins/solidity/hook-handlers/hre.ts | 4 +--- v-next/hardhat/src/types/solidity/build-system.ts | 11 ----------- 3 files changed, 1 insertion(+), 20 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index 1d9ecbc28e..bc9649ef31 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -10,7 +10,6 @@ import type { GetCompilationJobsOptions, CompileBuildInfoOptions, RunCompilationJobOptions, - EmitArtifactsOptions, } from "../../../../types/solidity/build-system.js"; import type { CompilationJob } from "../../../../types/solidity/compilation-job.js"; import type { @@ -206,15 +205,11 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { if (isSuccessfulBuild) { log("Emitting artifacts of successful build"); - const emitArtifactsOptions: EmitArtifactsOptions = { - quiet: options?.quiet, - }; await Promise.all( results.map(async (result) => { const artifactsPerFile = await this.emitArtifacts( result.compilationJob, result.compilerOutput, - emitArtifactsOptions, ); contractArtifactsGeneratedByCompilationJob.set( @@ -463,7 +458,6 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { public async emitArtifacts( compilationJob: CompilationJob, compilerOutput: CompilerOutput, - _options?: EmitArtifactsOptions, ): Promise> { const result = new Map(); const buildId = await compilationJob.getBuildId(); diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/hook-handlers/hre.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/hook-handlers/hre.ts index 6e9212a61e..28e1aa44fb 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/hook-handlers/hre.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/hook-handlers/hre.ts @@ -6,7 +6,6 @@ import type { BuildOptions, CompilationJobCreationError, CompileBuildInfoOptions, - EmitArtifactsOptions, FileBuildResult, GetCompilationJobsOptions, RunCompilationJobOptions, @@ -76,10 +75,9 @@ class LazySolidityBuildSystem implements SolidityBuildSystem { public async emitArtifacts( compilationJob: CompilationJob, compilerOutput: CompilerOutput, - options?: EmitArtifactsOptions, ): Promise> { const buildSystem = await this.#getBuildSystem(); - return buildSystem.emitArtifacts(compilationJob, compilerOutput, options); + return buildSystem.emitArtifacts(compilationJob, compilerOutput); } public async cleanupArtifacts(rootFilePaths: string[]): Promise { diff --git a/v-next/hardhat/src/types/solidity/build-system.ts b/v-next/hardhat/src/types/solidity/build-system.ts index b324b94c42..bb1cf09671 100644 --- a/v-next/hardhat/src/types/solidity/build-system.ts +++ b/v-next/hardhat/src/types/solidity/build-system.ts @@ -68,16 +68,6 @@ export interface RunCompilationJobOptions { quiet?: boolean; } -/** - * The options of the `emitArtifacts` method. - */ -export interface EmitArtifactsOptions { - /** - * If `true`, the emit process doesn't print any output. - */ - quiet?: boolean; -} - /** * The options of the `compileBuildInfo` method. */ @@ -263,7 +253,6 @@ export interface SolidityBuildSystem { emitArtifacts( compilationJob: CompilationJob, compilerOutput: CompilerOutput, - options?: EmitArtifactsOptions, ): Promise>; /** From 7647d5420d10a223d15734ff8a90562477965cbe Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 18:40:33 +0100 Subject: [PATCH 41/53] chore: remove async from a method that doesn't need it --- .../builtin-plugins/solidity/build-system/build-system.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index bc9649ef31..af74eae4ed 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -291,7 +291,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { if (options?.quiet !== true) { if (isSuccessfulBuild) { - await this.#printCompilationResult(compilationJobs); + this.#printCompilationResult(compilationJobs); } } @@ -755,7 +755,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { } } - async #printCompilationResult(compilationJobs: CompilationJob[]) { + #printCompilationResult(compilationJobs: CompilationJob[]) { const jobsPerVersionAndEvmVersion = new Map< string, Map From 5ce2a3d8bfa68fc1d61008bfdccfaa66beb2fad0 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 29 Jan 2025 18:45:19 +0100 Subject: [PATCH 42/53] chore: remove forgotten describe.only --- .../builtin-plugins/solidity/build-system/build-system.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts index e20b3e1914..3b6e048f72 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -48,7 +48,7 @@ async function emitArtifacts(solidity: SolidityBuildSystem): Promise { } // NOTE: This test is slow because solidity compilers are downloaded. -describe.only( +describe( "SolidityBuildSystemImplementation", { skip: process.env.HARDHAT_DISABLE_SLOW_TESTS === "true", From c14cf21dc33fae6fca6d150037997467f778ea44 Mon Sep 17 00:00:00 2001 From: galargh Date: Thu, 30 Jan 2025 16:21:32 +0100 Subject: [PATCH 43/53] chore: turn resolved files into classes --- .../solidity/build-system/debug-utils.ts | 2 +- .../resolver/dependency-resolver.ts | 47 +++++++++---------- .../solidity/build-system/root-paths-utils.ts | 2 +- .../src/types/solidity/resolved-file.ts | 47 +++++++++++++++---- .../solidity/build-system/compilation-job.ts | 24 ++++------ .../solidity/build-system/dependency-graph.ts | 9 ++-- .../resolver/dependency-resolver.ts | 7 ++- .../solidity/build-system/root-paths-utils.ts | 30 ++++++------ .../build-system/solc-config-selection.ts | 8 ++-- 9 files changed, 96 insertions(+), 80 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/debug-utils.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/debug-utils.ts index 537859c13d..62d02e11e6 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/debug-utils.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/debug-utils.ts @@ -16,7 +16,7 @@ export function printDependencyGraphAndRemappingsSummary( const rootRepresentations: string[] = []; for (const [rootFile, resolvedFile] of roots.entries()) { - if (resolvedFile.type === ResolvedFileType.NPM_PACKGE_FILE) { + if (resolvedFile.type === ResolvedFileType.NPM_PACKAGE_FILE) { rootRepresentations.push(`- ${rootFile} -> ${resolvedFile.sourceName} ${resolvedFile.fsPath}`); } else { diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts index 86deff59fa..e5b70180d6 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts @@ -2,8 +2,6 @@ import type { Remapping, Resolver } from "./types.js"; import type { ResolvedNpmPackage, ResolvedFile, - ProjectResolvedFile, - NpmPackageResolvedFile, FileContent, } from "../../../../../types/solidity/resolved-file.js"; @@ -26,7 +24,11 @@ import { shortenPath } from "@ignored/hardhat-vnext-utils/path"; import { ResolutionError, resolve } from "@ignored/hardhat-vnext-utils/resolve"; import { analyze } from "@nomicfoundation/solidity-analyzer"; -import { ResolvedFileType } from "../../../../../types/solidity/resolved-file.js"; +import { + ProjectResolvedFile, + NpmPackageResolvedFile, + ResolvedFileType, +} from "../../../../../types/solidity/resolved-file.js"; import { AsyncMutex } from "../../../../core/async-mutex.js"; import { @@ -234,12 +236,11 @@ export class ResolverImplementation implements Resolver { trueCaseFsPath, ); - const resolvedFile: ProjectResolvedFile = { - type: ResolvedFileType.PROJECT_FILE, + const resolvedFile = new ProjectResolvedFile({ sourceName, fsPath: fsPathWithTheRightCasing, content: await readFileContent(fsPathWithTheRightCasing), - }; + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); @@ -320,13 +321,12 @@ export class ResolverImplementation implements Resolver { const fsPath = path.join(npmPackage.rootFsPath, trueCaseFsPath); - const resolvedFile: NpmPackageResolvedFile = { - type: ResolvedFileType.NPM_PACKGE_FILE, + const resolvedFile = new NpmPackageResolvedFile({ sourceName, fsPath, content: await readFileContent(fsPath), package: npmPackage, - }; + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); @@ -357,7 +357,7 @@ export class ResolverImplementation implements Resolver { importPath, ); - if (from.type === ResolvedFileType.NPM_PACKGE_FILE) { + if (from.type === ResolvedFileType.NPM_PACKAGE_FILE) { if (!directImport.startsWith(from.package.rootSourceName)) { throw new HardhatError( HardhatError.ERRORS.SOLIDITY.ILLEGAL_PACKAGE_IMPORT, @@ -388,7 +388,7 @@ export class ResolverImplementation implements Resolver { directImport, }); - case ResolvedFileType.NPM_PACKGE_FILE: + case ResolvedFileType.NPM_PACKAGE_FILE: return this.#resolveImportFromNpmPackageFile({ from, importPath, @@ -802,12 +802,11 @@ export class ResolverImplementation implements Resolver { const fsPath = path.join(this.#projectRoot, fsPathWithinTheProject); - const resolvedFile: ProjectResolvedFile = { - type: ResolvedFileType.PROJECT_FILE, + const resolvedFile = new ProjectResolvedFile({ sourceName, fsPath, content: await readFileContent(fsPath), - }; + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); @@ -864,13 +863,12 @@ export class ResolverImplementation implements Resolver { relativeFileFsPath, ); - const resolvedFile: NpmPackageResolvedFile = { - type: ResolvedFileType.NPM_PACKGE_FILE, + const resolvedFile = new NpmPackageResolvedFile({ sourceName, fsPath, content: await readFileContent(fsPath), package: remapping.targetNpmPackage, - }; + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); @@ -917,13 +915,12 @@ export class ResolverImplementation implements Resolver { const filePath = path.join(from.package.rootFsPath, relativePath); - const resolvedFile: NpmPackageResolvedFile = { - type: ResolvedFileType.NPM_PACKGE_FILE, + const resolvedFile = new NpmPackageResolvedFile({ sourceName, fsPath: filePath, content: await readFileContent(filePath), package: from.package, - }; + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); @@ -972,13 +969,12 @@ export class ResolverImplementation implements Resolver { const fsPath = path.join(from.package.rootFsPath, relativeFsPath); - const resolvedFile: NpmPackageResolvedFile = { - type: ResolvedFileType.NPM_PACKGE_FILE, + const resolvedFile = new NpmPackageResolvedFile({ sourceName, fsPath, content: await readFileContent(fsPath), package: from.package, - }; + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); @@ -1032,13 +1028,12 @@ export class ResolverImplementation implements Resolver { fsPathWithinThePackage, ); - const resolvedFile: NpmPackageResolvedFile = { - type: ResolvedFileType.NPM_PACKGE_FILE, + const resolvedFile = new NpmPackageResolvedFile({ sourceName, fsPath, content: await readFileContent(fsPath), package: importedPackage, - }; + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/root-paths-utils.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/root-paths-utils.ts index f57c7e4202..9ec792e316 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/root-paths-utils.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/root-paths-utils.ts @@ -71,7 +71,7 @@ export function formatRootPath( publicSourceName: string, rootFile: ResolvedFile, ): string { - if (rootFile.type !== ResolvedFileType.NPM_PACKGE_FILE) { + if (rootFile.type !== ResolvedFileType.NPM_PACKAGE_FILE) { return publicSourceName; } diff --git a/v-next/hardhat/src/types/solidity/resolved-file.ts b/v-next/hardhat/src/types/solidity/resolved-file.ts index be8512f956..1cc5ec2178 100644 --- a/v-next/hardhat/src/types/solidity/resolved-file.ts +++ b/v-next/hardhat/src/types/solidity/resolved-file.ts @@ -35,14 +35,10 @@ export interface ResolvedNpmPackage { */ export enum ResolvedFileType { PROJECT_FILE = "PROJECT_FILE", - NPM_PACKGE_FILE = "NPM_PACKAGE_FILE", + NPM_PACKAGE_FILE = "NPM_PACKAGE_FILE", } -/** - * A file that's part of the Hardhat project (i.e. not installed through npm). - */ -export interface ProjectResolvedFile { - type: ResolvedFileType.PROJECT_FILE; +interface ProjectResolvedFileOptions { /** * The source name of a project files is its relative path from the Hardhat * project root. @@ -61,11 +57,24 @@ export interface ProjectResolvedFile { } /** - * A file that's part of an npm package. + * A file that's part of the Hardhat project (i.e. not installed through npm). */ -export interface NpmPackageResolvedFile { - type: ResolvedFileType.NPM_PACKGE_FILE; +export class ProjectResolvedFile { + public readonly type: ResolvedFileType.PROJECT_FILE = + ResolvedFileType.PROJECT_FILE; + + public readonly sourceName: string; + public readonly fsPath: string; + public readonly content: FileContent; + + constructor(options: ProjectResolvedFileOptions) { + this.sourceName = options.sourceName; + this.fsPath = options.fsPath; + this.content = options.content; + } +} +interface NpmPackageResolvedFileOptions { /** * The source of an npm package file is `npm/@/`. */ @@ -87,6 +96,26 @@ export interface NpmPackageResolvedFile { package: ResolvedNpmPackage; } +/** + * A file that's part of an npm package. + */ +export class NpmPackageResolvedFile { + public readonly type: ResolvedFileType.NPM_PACKAGE_FILE = + ResolvedFileType.NPM_PACKAGE_FILE; + + public readonly sourceName: string; + public readonly fsPath: string; + public readonly content: FileContent; + public readonly package: ResolvedNpmPackage; + + constructor(options: NpmPackageResolvedFileOptions) { + this.sourceName = options.sourceName; + this.fsPath = options.fsPath; + this.content = options.content; + this.package = options.package; + } +} + /** * The resolult of resolving a file or import using a Resolver. */ diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts index da77afc477..24a4f482c2 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -1,16 +1,15 @@ import type { Remapping } from "../../../../../src/internal/builtin-plugins/solidity/build-system/resolver/types.js"; import type { SolcConfig } from "../../../../../src/types/config.js"; -import type { - NpmPackageResolvedFile, - ProjectResolvedFile, -} from "../../../../../src/types/solidity.js"; import assert from "node:assert/strict"; import { before, beforeEach, describe, it } from "node:test"; import { CompilationJobImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/compilation-job.js"; import { DependencyGraphImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/dependency-graph.js"; -import { ResolvedFileType } from "../../../../../src/types/solidity.js"; +import { + NpmPackageResolvedFile, + ProjectResolvedFile, +} from "../../../../../src/types/solidity.js"; describe("CompilationJobImplementation", () => { let dependencyGraph: DependencyGraphImplementation; @@ -24,8 +23,7 @@ describe("CompilationJobImplementation", () => { before(() => { dependencyGraph = new DependencyGraphImplementation(); - rootFile = { - type: ResolvedFileType.PROJECT_FILE, + rootFile = new ProjectResolvedFile({ sourceName: "root.sol", fsPath: "root.sol", content: { @@ -33,9 +31,8 @@ describe("CompilationJobImplementation", () => { importPaths: [], versionPragmas: [], }, - }; - npmDependencyFile = { - type: ResolvedFileType.NPM_PACKGE_FILE, + }); + npmDependencyFile = new NpmPackageResolvedFile({ sourceName: "npm:dependency/1.0.0/dependency.sol", fsPath: "dependency.sol", package: { @@ -49,9 +46,8 @@ describe("CompilationJobImplementation", () => { importPaths: [], versionPragmas: [], }, - }; - projectDependencyFile = { - type: ResolvedFileType.PROJECT_FILE, + }); + projectDependencyFile = new ProjectResolvedFile({ sourceName: "dependency.sol", fsPath: "dependency.sol", content: { @@ -59,7 +55,7 @@ describe("CompilationJobImplementation", () => { importPaths: [], versionPragmas: [], }, - }; + }); dependencyGraph.addRootFile(rootFile.sourceName, rootFile); dependencyGraph.addDependency(rootFile, npmDependencyFile); dependencyGraph.addDependency(rootFile, projectDependencyFile); diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/dependency-graph.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/dependency-graph.ts index ee83bb60bc..9e10e84ce9 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/dependency-graph.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/dependency-graph.ts @@ -1,5 +1,3 @@ -import type { ProjectResolvedFile } from "../../../../../src/types/solidity.js"; - import assert from "node:assert/strict"; import path from "node:path"; import { beforeEach, describe, it } from "node:test"; @@ -8,11 +6,10 @@ import { HardhatError } from "@ignored/hardhat-vnext-errors"; import { assertThrowsHardhatError } from "@nomicfoundation/hardhat-test-utils"; import { DependencyGraphImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/dependency-graph.js"; -import { ResolvedFileType } from "../../../../../src/types/solidity.js"; +import { ProjectResolvedFile } from "../../../../../src/types/solidity.js"; function createProjectResolvedFile(sourceName: string): ProjectResolvedFile { - return { - type: ResolvedFileType.PROJECT_FILE, + return new ProjectResolvedFile({ sourceName, fsPath: path.join(process.cwd(), sourceName), content: { @@ -20,7 +17,7 @@ function createProjectResolvedFile(sourceName: string): ProjectResolvedFile { importPaths: [], versionPragmas: [], }, - }; + }); } describe("DependencyGraphImplementation", () => { diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts index dbacf0e952..437eee831f 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts @@ -65,7 +65,7 @@ function assertNpmPackageResolvedFile( filePathFromPackageRoot: string, ): asserts resolvedFile is NpmPackageResolvedFile { assert.ok( - resolvedFile.type === ResolvedFileType.NPM_PACKGE_FILE, + resolvedFile.type === ResolvedFileType.NPM_PACKAGE_FILE, `Resolved file ${resolvedFile.fsPath} is not an npm file`, ); @@ -345,7 +345,10 @@ describe("Resolver", () => { "hardhat/console.sol", ); - assert.deepEqual(consoleSol.type, ResolvedFileType.NPM_PACKGE_FILE); + assert.deepEqual( + consoleSol.type, + ResolvedFileType.NPM_PACKAGE_FILE, + ); assert.deepEqual(consoleSol.package, { name: "@ignored/hardhat-vnext", version: "local", // The test considers it part of the monorepo, because it's the same package diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/root-paths-utils.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/root-paths-utils.ts index 7f5747a19b..00d2d2afa1 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/root-paths-utils.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/root-paths-utils.ts @@ -11,7 +11,10 @@ import { npmModuleToNpmRootPath, parseRootPath, } from "../../../../../src/internal/builtin-plugins/solidity/build-system/root-paths-utils.js"; -import { ResolvedFileType } from "../../../../../src/types/solidity.js"; +import { + NpmPackageResolvedFile, + ProjectResolvedFile, +} from "../../../../../src/types/solidity.js"; interface TestRootPath { rootPath: string; @@ -31,8 +34,7 @@ const testRootPaths: TestRootPath[] = [ isNpm: true, npmModule: "ethers", publicSourceName: "ethers", - resolvedFile: { - type: ResolvedFileType.NPM_PACKGE_FILE, + resolvedFile: new NpmPackageResolvedFile({ sourceName: "ethers", fsPath: "/Users/root/node_modules/ethers/index.js", content: { @@ -46,7 +48,7 @@ const testRootPaths: TestRootPath[] = [ rootFsPath: "/Users/root/node_modules/ethers", rootSourceName: "ethers", }, - }, + }), }, { rootPath: "npm:@openzeppelin/contracts", @@ -56,8 +58,7 @@ const testRootPaths: TestRootPath[] = [ isNpm: true, npmModule: "@openzeppelin/contracts", publicSourceName: "@openzeppelin/contracts", - resolvedFile: { - type: ResolvedFileType.NPM_PACKGE_FILE, + resolvedFile: new NpmPackageResolvedFile({ sourceName: "@openzeppelin/contracts", fsPath: "/Users/root/node_modules/@openzeppelin/contracts/index.js", content: { @@ -71,7 +72,7 @@ const testRootPaths: TestRootPath[] = [ rootFsPath: "/Users/root/node_modules/@openzeppelin/contracts", rootSourceName: "@openzeppelin/contracts", }, - }, + }), }, { rootPath: "npm:@openzeppelin/contracts/token/ERC20/ERC20.sol", @@ -81,8 +82,7 @@ const testRootPaths: TestRootPath[] = [ isNpm: true, npmModule: "@openzeppelin/contracts/token/ERC20/ERC20.sol", publicSourceName: "@openzeppelin/contracts/token/ERC20/ERC20.sol", - resolvedFile: { - type: ResolvedFileType.NPM_PACKGE_FILE, + resolvedFile: new NpmPackageResolvedFile({ sourceName: "@openzeppelin/contracts/token/ERC20/ERC20.sol", fsPath: "/Users/root/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol", @@ -97,7 +97,7 @@ const testRootPaths: TestRootPath[] = [ rootFsPath: "/Users/root/node_modules/@openzeppelin/contracts", rootSourceName: "@openzeppelin/contracts", }, - }, + }), }, { rootPath: "/Users/root/contracts/Contract.sol", @@ -107,8 +107,7 @@ const testRootPaths: TestRootPath[] = [ isNpm: false, npmModule: undefined, publicSourceName: "/Users/root/contracts/Contract.sol", - resolvedFile: { - type: ResolvedFileType.PROJECT_FILE, + resolvedFile: new ProjectResolvedFile({ sourceName: "/Users/root/contracts/Contract.sol", fsPath: "/Users/root/contracts/Contract.sol", content: { @@ -116,7 +115,7 @@ const testRootPaths: TestRootPath[] = [ importPaths: [], versionPragmas: [], }, - }, + }), }, { rootPath: "C:\\Users\\root\\contracts\\Contract.sol", @@ -126,8 +125,7 @@ const testRootPaths: TestRootPath[] = [ isNpm: false, npmModule: undefined, publicSourceName: "C:\\Users\\root\\contracts\\Contract.sol", - resolvedFile: { - type: ResolvedFileType.PROJECT_FILE, + resolvedFile: new ProjectResolvedFile({ sourceName: "C:\\Users\\root\\contracts\\Contract.sol", fsPath: "C:\\Users\\root\\contracts\\Contract.sol", content: { @@ -135,7 +133,7 @@ const testRootPaths: TestRootPath[] = [ importPaths: [], versionPragmas: [], }, - }, + }), }, ]; diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/solc-config-selection.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/solc-config-selection.ts index 77682f2c57..d6f03bfa4a 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/solc-config-selection.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/solc-config-selection.ts @@ -1,5 +1,4 @@ import type { SolidityBuildProfileConfig } from "../../../../../src/types/config.js"; -import type { ProjectResolvedFile } from "../../../../../src/types/solidity.js"; import assert from "node:assert/strict"; import path from "node:path"; @@ -11,16 +10,15 @@ import { assertRejectsWithHardhatError } from "@nomicfoundation/hardhat-test-uti import { DependencyGraphImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/dependency-graph.js"; import { SolcConfigSelector } from "../../../../../src/internal/builtin-plugins/solidity/build-system/solc-config-selection.js"; import { + ProjectResolvedFile, CompilationJobCreationErrorReason, - ResolvedFileType, } from "../../../../../src/types/solidity.js"; function createProjectResolvedFile( sourceName: string, versionPragmas: string[], ): ProjectResolvedFile { - return { - type: ResolvedFileType.PROJECT_FILE, + return new ProjectResolvedFile({ sourceName, fsPath: path.join(process.cwd(), sourceName), content: { @@ -28,7 +26,7 @@ function createProjectResolvedFile( importPaths: [], versionPragmas, }, - }; + }); } describe("SolcConfigSelector", () => { From 222a6c8ffac7502d5d32529511421fe302eca096 Mon Sep 17 00:00:00 2001 From: galargh Date: Thu, 30 Jan 2025 16:37:35 +0100 Subject: [PATCH 44/53] chore: replace static source content hash with per resolved file one --- .../solidity/build-system/compilation-job.ts | 26 +------------- .../src/types/solidity/resolved-file.ts | 24 +++++++++++-- .../solidity/build-system/compilation-job.ts | 36 +++++++++---------- 3 files changed, 39 insertions(+), 47 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index a6dbacec38..735e91bc0b 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -24,8 +24,6 @@ export class CompilationJobImplementation implements CompilationJob { #solcInputWithoutSources: Omit | undefined; #resolvedFiles: ResolvedFile[] | undefined; - static readonly #sourceContentHashCache = new Map(); - constructor( dependencyGraph: DependencyGraphImplementation, solcConfig: SolcConfig, @@ -137,23 +135,6 @@ export class CompilationJobImplementation implements CompilationJob { }; } - async #getSourceContentHash(file: ResolvedFile): Promise { - const cachedSourceContentHash = - CompilationJobImplementation.#sourceContentHashCache.get(file.sourceName); - if (cachedSourceContentHash !== undefined) { - return cachedSourceContentHash; - } - - const sourceContentHash = await createNonCryptographicHashId( - file.content.text, - ); - CompilationJobImplementation.#sourceContentHashCache.set( - file.sourceName, - sourceContentHash, - ); - return sourceContentHash; - } - async #computeBuildId(): Promise { // NOTE: We type it this way so that this stop compiling if we ever change // the format of the BuildInfo type. @@ -165,7 +146,7 @@ export class CompilationJobImplementation implements CompilationJob { await Promise.all( resolvedFiles.map(async (file) => { sources[file.sourceName] = { - hash: await this.#getSourceContentHash(file), + hash: await file.getContentHash(), }; }), ); @@ -186,9 +167,4 @@ export class CompilationJobImplementation implements CompilationJob { return createNonCryptographicHashId(preimage); } - - // NOTE: This method is used internally to clear the cache in between tests. - public static clearCache(): void { - CompilationJobImplementation.#sourceContentHashCache.clear(); - } } diff --git a/v-next/hardhat/src/types/solidity/resolved-file.ts b/v-next/hardhat/src/types/solidity/resolved-file.ts index 1cc5ec2178..1ef6e542ff 100644 --- a/v-next/hardhat/src/types/solidity/resolved-file.ts +++ b/v-next/hardhat/src/types/solidity/resolved-file.ts @@ -1,3 +1,5 @@ +import { createNonCryptographicHashId } from "@ignored/hardhat-vnext-utils/crypto"; + /** * The representation of an npm package. */ @@ -38,6 +40,20 @@ export enum ResolvedFileType { NPM_PACKAGE_FILE = "NPM_PACKAGE_FILE", } +abstract class BaseResolvedFile { + public abstract readonly content: FileContent; + + #contentHash?: string; + + public async getContentHash(): Promise { + if (this.#contentHash === undefined) { + this.#contentHash = await createNonCryptographicHashId(this.content.text); + } + + return this.#contentHash; + } +} + interface ProjectResolvedFileOptions { /** * The source name of a project files is its relative path from the Hardhat @@ -59,7 +75,7 @@ interface ProjectResolvedFileOptions { /** * A file that's part of the Hardhat project (i.e. not installed through npm). */ -export class ProjectResolvedFile { +export class ProjectResolvedFile extends BaseResolvedFile { public readonly type: ResolvedFileType.PROJECT_FILE = ResolvedFileType.PROJECT_FILE; @@ -68,6 +84,8 @@ export class ProjectResolvedFile { public readonly content: FileContent; constructor(options: ProjectResolvedFileOptions) { + super(); + this.sourceName = options.sourceName; this.fsPath = options.fsPath; this.content = options.content; @@ -99,7 +117,7 @@ interface NpmPackageResolvedFileOptions { /** * A file that's part of an npm package. */ -export class NpmPackageResolvedFile { +export class NpmPackageResolvedFile extends BaseResolvedFile { public readonly type: ResolvedFileType.NPM_PACKAGE_FILE = ResolvedFileType.NPM_PACKAGE_FILE; @@ -109,6 +127,8 @@ export class NpmPackageResolvedFile { public readonly package: ResolvedNpmPackage; constructor(options: NpmPackageResolvedFileOptions) { + super(); + this.sourceName = options.sourceName; this.fsPath = options.fsPath; this.content = options.content; diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts index 24a4f482c2..a383d4bf82 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -2,7 +2,7 @@ import type { Remapping } from "../../../../../src/internal/builtin-plugins/soli import type { SolcConfig } from "../../../../../src/types/config.js"; import assert from "node:assert/strict"; -import { before, beforeEach, describe, it } from "node:test"; +import { beforeEach, describe, it } from "node:test"; import { CompilationJobImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/compilation-job.js"; import { DependencyGraphImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/dependency-graph.js"; @@ -21,7 +21,7 @@ describe("CompilationJobImplementation", () => { let remappings: Remapping[]; let compilationJob: CompilationJobImplementation; - before(() => { + beforeEach(() => { dependencyGraph = new DependencyGraphImplementation(); rootFile = new ProjectResolvedFile({ sourceName: "root.sol", @@ -73,10 +73,6 @@ describe("CompilationJobImplementation", () => { ); }); - beforeEach(() => { - CompilationJobImplementation.clearCache(); - }); - describe("getBuildId", () => { describe("should change when", () => { it("the solc long version changes", async () => { @@ -131,10 +127,10 @@ describe("CompilationJobImplementation", () => { }); it("there is an additional root file in the dependency graph", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newRootFile = { + const newRootFile = new ProjectResolvedFile({ ...rootFile, sourceName: "newRoot.sol", - }; + }); newDependencyGraph.addRootFile(rootFile.sourceName, rootFile); newDependencyGraph.addDependency(rootFile, npmDependencyFile); newDependencyGraph.addDependency(rootFile, projectDependencyFile); @@ -152,10 +148,10 @@ describe("CompilationJobImplementation", () => { }); it("there is an additional dependency in the dependency graph", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newDependencyFile = { + const newDependencyFile = new ProjectResolvedFile({ ...projectDependencyFile, sourceName: "newDependency.sol", - }; + }); newDependencyGraph.addRootFile(rootFile.sourceName, rootFile); newDependencyGraph.addDependency(rootFile, npmDependencyFile); newDependencyGraph.addDependency(rootFile, projectDependencyFile); @@ -173,13 +169,13 @@ describe("CompilationJobImplementation", () => { }); it("the content of one of the root files changes", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newRootFile = { + const newRootFile = new ProjectResolvedFile({ ...rootFile, content: { ...rootFile.content, text: "contract NewRoot {}", }, - }; + }); newDependencyGraph.addRootFile(newRootFile.sourceName, newRootFile); newDependencyGraph.addDependency(newRootFile, npmDependencyFile); newDependencyGraph.addDependency(newRootFile, projectDependencyFile); @@ -196,13 +192,13 @@ describe("CompilationJobImplementation", () => { }); it("the content of one of the dependencies changes", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newDependencyFile = { + const newDependencyFile = new NpmPackageResolvedFile({ ...npmDependencyFile, content: { ...npmDependencyFile.content, text: "contract NewDependency {}", }, - }; + }); newDependencyGraph.addRootFile(rootFile.sourceName, rootFile); newDependencyGraph.addDependency(rootFile, newDependencyFile); newDependencyGraph.addDependency(rootFile, projectDependencyFile); @@ -219,10 +215,10 @@ describe("CompilationJobImplementation", () => { }); it("the source name of one of the root files changes", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newRootFile = { + const newRootFile = new ProjectResolvedFile({ ...rootFile, sourceName: "newRoot.sol", - }; + }); newDependencyGraph.addRootFile(newRootFile.sourceName, newRootFile); newDependencyGraph.addDependency(newRootFile, npmDependencyFile); newDependencyGraph.addDependency(newRootFile, projectDependencyFile); @@ -239,10 +235,10 @@ describe("CompilationJobImplementation", () => { }); it("the source name of one of the dependencies changes", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newDependencyFile = { + const newDependencyFile = new NpmPackageResolvedFile({ ...npmDependencyFile, sourceName: "npm:dependency/1.0.0/newDependency.sol", - }; + }); newDependencyGraph.addRootFile(rootFile.sourceName, rootFile); newDependencyGraph.addDependency(rootFile, newDependencyFile); newDependencyGraph.addDependency(rootFile, projectDependencyFile); @@ -261,13 +257,13 @@ describe("CompilationJobImplementation", () => { describe("should not change when", () => { it("the version of one of the dependencies changes without it being reflected in the source name", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newDependencyFile = { + const newDependencyFile = new NpmPackageResolvedFile({ ...npmDependencyFile, package: { ...npmDependencyFile.package, version: "2.0.0", }, - }; + }); newDependencyGraph.addRootFile(rootFile.sourceName, rootFile); newDependencyGraph.addDependency(rootFile, newDependencyFile); newDependencyGraph.addDependency(rootFile, projectDependencyFile); From b1377d3c1fa8c8095fc0eb59ea6bacfb4063202f Mon Sep 17 00:00:00 2001 From: galargh Date: Thu, 30 Jan 2025 20:54:18 +0100 Subject: [PATCH 45/53] chore: move resolved file interface implementations out of types --- .../solidity/build-system/resolved-file.ts | 66 +++++++++++++++ .../resolver/dependency-resolver.ts | 81 ++++++++++--------- .../src/types/solidity/resolved-file.ts | 72 ++++------------- .../solidity/build-system/compilation-job.ts | 30 ++++--- .../solidity/build-system/dependency-graph.ts | 6 +- .../solidity/build-system/root-paths-utils.ts | 18 ++--- .../build-system/solc-config-selection.ts | 9 +-- 7 files changed, 161 insertions(+), 121 deletions(-) create mode 100644 v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolved-file.ts diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolved-file.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolved-file.ts new file mode 100644 index 0000000000..1cb6e10d1c --- /dev/null +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolved-file.ts @@ -0,0 +1,66 @@ +import type { + FileContent, + NpmPackageResolvedFile, + ProjectResolvedFile, + ResolvedNpmPackage, +} from "../../../../types/solidity.js"; + +import { createNonCryptographicHashId } from "@ignored/hardhat-vnext-utils/crypto"; + +import { ResolvedFileType } from "../../../../types/solidity.js"; + +export class ProjectResolvedFileImplementation implements ProjectResolvedFile { + public readonly type: ResolvedFileType.PROJECT_FILE = + ResolvedFileType.PROJECT_FILE; + + public readonly sourceName: string; + public readonly fsPath: string; + public readonly content: FileContent; + + #contentHash?: string; + + constructor(options: Omit) { + this.sourceName = options.sourceName; + this.fsPath = options.fsPath; + this.content = options.content; + } + + public async getContentHash(): Promise { + if (this.#contentHash === undefined) { + this.#contentHash = await createNonCryptographicHashId(this.content.text); + } + + return this.#contentHash; + } +} + +export class NpmPackageResolvedFileImplementation + implements NpmPackageResolvedFile +{ + public readonly type: ResolvedFileType.NPM_PACKAGE_FILE = + ResolvedFileType.NPM_PACKAGE_FILE; + + public readonly sourceName: string; + public readonly fsPath: string; + public readonly content: FileContent; + public readonly package: ResolvedNpmPackage; + + #contentHash?: string; + + constructor( + options: Omit, + ) { + this.sourceName = options.sourceName; + this.fsPath = options.fsPath; + this.content = options.content; + this.package = options.package; + } + + public async getContentHash(): Promise { + if (this.#contentHash === undefined) { + this.#contentHash = await createNonCryptographicHashId(this.content.text); + } + + return this.#contentHash; + } +} diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts index e5b70180d6..f26d2069fa 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts @@ -3,6 +3,8 @@ import type { ResolvedNpmPackage, ResolvedFile, FileContent, + ProjectResolvedFile, + NpmPackageResolvedFile, } from "../../../../../types/solidity/resolved-file.js"; import path from "node:path"; @@ -24,12 +26,12 @@ import { shortenPath } from "@ignored/hardhat-vnext-utils/path"; import { ResolutionError, resolve } from "@ignored/hardhat-vnext-utils/resolve"; import { analyze } from "@nomicfoundation/solidity-analyzer"; -import { - ProjectResolvedFile, - NpmPackageResolvedFile, - ResolvedFileType, -} from "../../../../../types/solidity/resolved-file.js"; +import { ResolvedFileType } from "../../../../../types/solidity/resolved-file.js"; import { AsyncMutex } from "../../../../core/async-mutex.js"; +import { + NpmPackageResolvedFileImplementation, + ProjectResolvedFileImplementation, +} from "../resolved-file.js"; import { applyValidRemapping, @@ -236,7 +238,7 @@ export class ResolverImplementation implements Resolver { trueCaseFsPath, ); - const resolvedFile = new ProjectResolvedFile({ + const resolvedFile = new ProjectResolvedFileImplementation({ sourceName, fsPath: fsPathWithTheRightCasing, content: await readFileContent(fsPathWithTheRightCasing), @@ -321,12 +323,13 @@ export class ResolverImplementation implements Resolver { const fsPath = path.join(npmPackage.rootFsPath, trueCaseFsPath); - const resolvedFile = new NpmPackageResolvedFile({ - sourceName, - fsPath, - content: await readFileContent(fsPath), - package: npmPackage, - }); + const resolvedFile: NpmPackageResolvedFile = + new NpmPackageResolvedFileImplementation({ + sourceName, + fsPath, + content: await readFileContent(fsPath), + package: npmPackage, + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); @@ -802,7 +805,7 @@ export class ResolverImplementation implements Resolver { const fsPath = path.join(this.#projectRoot, fsPathWithinTheProject); - const resolvedFile = new ProjectResolvedFile({ + const resolvedFile = new ProjectResolvedFileImplementation({ sourceName, fsPath, content: await readFileContent(fsPath), @@ -863,12 +866,13 @@ export class ResolverImplementation implements Resolver { relativeFileFsPath, ); - const resolvedFile = new NpmPackageResolvedFile({ - sourceName, - fsPath, - content: await readFileContent(fsPath), - package: remapping.targetNpmPackage, - }); + const resolvedFile: NpmPackageResolvedFile = + new NpmPackageResolvedFileImplementation({ + sourceName, + fsPath, + content: await readFileContent(fsPath), + package: remapping.targetNpmPackage, + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); @@ -915,12 +919,13 @@ export class ResolverImplementation implements Resolver { const filePath = path.join(from.package.rootFsPath, relativePath); - const resolvedFile = new NpmPackageResolvedFile({ - sourceName, - fsPath: filePath, - content: await readFileContent(filePath), - package: from.package, - }); + const resolvedFile: NpmPackageResolvedFile = + new NpmPackageResolvedFileImplementation({ + sourceName, + fsPath: filePath, + content: await readFileContent(filePath), + package: from.package, + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); @@ -969,12 +974,13 @@ export class ResolverImplementation implements Resolver { const fsPath = path.join(from.package.rootFsPath, relativeFsPath); - const resolvedFile = new NpmPackageResolvedFile({ - sourceName, - fsPath, - content: await readFileContent(fsPath), - package: from.package, - }); + const resolvedFile: NpmPackageResolvedFile = + new NpmPackageResolvedFileImplementation({ + sourceName, + fsPath, + content: await readFileContent(fsPath), + package: from.package, + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); @@ -1028,12 +1034,13 @@ export class ResolverImplementation implements Resolver { fsPathWithinThePackage, ); - const resolvedFile = new NpmPackageResolvedFile({ - sourceName, - fsPath, - content: await readFileContent(fsPath), - package: importedPackage, - }); + const resolvedFile: NpmPackageResolvedFile = + new NpmPackageResolvedFileImplementation({ + sourceName, + fsPath, + content: await readFileContent(fsPath), + package: importedPackage, + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); diff --git a/v-next/hardhat/src/types/solidity/resolved-file.ts b/v-next/hardhat/src/types/solidity/resolved-file.ts index 1ef6e542ff..4c34910c25 100644 --- a/v-next/hardhat/src/types/solidity/resolved-file.ts +++ b/v-next/hardhat/src/types/solidity/resolved-file.ts @@ -1,5 +1,3 @@ -import { createNonCryptographicHashId } from "@ignored/hardhat-vnext-utils/crypto"; - /** * The representation of an npm package. */ @@ -40,21 +38,12 @@ export enum ResolvedFileType { NPM_PACKAGE_FILE = "NPM_PACKAGE_FILE", } -abstract class BaseResolvedFile { - public abstract readonly content: FileContent; - - #contentHash?: string; - - public async getContentHash(): Promise { - if (this.#contentHash === undefined) { - this.#contentHash = await createNonCryptographicHashId(this.content.text); - } - - return this.#contentHash; - } -} +/** + * A file that's part of the Hardhat project (i.e. not installed through npm). + */ +export interface ProjectResolvedFile { + type: ResolvedFileType.PROJECT_FILE; -interface ProjectResolvedFileOptions { /** * The source name of a project files is its relative path from the Hardhat * project root. @@ -70,29 +59,19 @@ interface ProjectResolvedFileOptions { * The file contents. */ content: FileContent; + + /** + * Return the non-cryptographic hash id of the file contents. + */ + getContentHash(): Promise; } /** - * A file that's part of the Hardhat project (i.e. not installed through npm). + * A file that's part of an npm package. */ -export class ProjectResolvedFile extends BaseResolvedFile { - public readonly type: ResolvedFileType.PROJECT_FILE = - ResolvedFileType.PROJECT_FILE; - - public readonly sourceName: string; - public readonly fsPath: string; - public readonly content: FileContent; - - constructor(options: ProjectResolvedFileOptions) { - super(); +export interface NpmPackageResolvedFile { + type: ResolvedFileType.NPM_PACKAGE_FILE; - this.sourceName = options.sourceName; - this.fsPath = options.fsPath; - this.content = options.content; - } -} - -interface NpmPackageResolvedFileOptions { /** * The source of an npm package file is `npm/@/`. */ @@ -112,28 +91,11 @@ interface NpmPackageResolvedFileOptions { * The package this file belongs to. */ package: ResolvedNpmPackage; -} -/** - * A file that's part of an npm package. - */ -export class NpmPackageResolvedFile extends BaseResolvedFile { - public readonly type: ResolvedFileType.NPM_PACKAGE_FILE = - ResolvedFileType.NPM_PACKAGE_FILE; - - public readonly sourceName: string; - public readonly fsPath: string; - public readonly content: FileContent; - public readonly package: ResolvedNpmPackage; - - constructor(options: NpmPackageResolvedFileOptions) { - super(); - - this.sourceName = options.sourceName; - this.fsPath = options.fsPath; - this.content = options.content; - this.package = options.package; - } + /** + * Return the non-cryptographic hash id of the file contents. + */ + getContentHash(): Promise; } /** diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts index a383d4bf82..cf0628b1c0 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -1,5 +1,9 @@ import type { Remapping } from "../../../../../src/internal/builtin-plugins/solidity/build-system/resolver/types.js"; import type { SolcConfig } from "../../../../../src/types/config.js"; +import type { + NpmPackageResolvedFile, + ProjectResolvedFile, +} from "../../../../../src/types/solidity.js"; import assert from "node:assert/strict"; import { beforeEach, describe, it } from "node:test"; @@ -7,9 +11,9 @@ import { beforeEach, describe, it } from "node:test"; import { CompilationJobImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/compilation-job.js"; import { DependencyGraphImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/dependency-graph.js"; import { - NpmPackageResolvedFile, - ProjectResolvedFile, -} from "../../../../../src/types/solidity.js"; + NpmPackageResolvedFileImplementation, + ProjectResolvedFileImplementation, +} from "../../../../../src/internal/builtin-plugins/solidity/build-system/resolved-file.js"; describe("CompilationJobImplementation", () => { let dependencyGraph: DependencyGraphImplementation; @@ -23,7 +27,7 @@ describe("CompilationJobImplementation", () => { beforeEach(() => { dependencyGraph = new DependencyGraphImplementation(); - rootFile = new ProjectResolvedFile({ + rootFile = new ProjectResolvedFileImplementation({ sourceName: "root.sol", fsPath: "root.sol", content: { @@ -32,7 +36,7 @@ describe("CompilationJobImplementation", () => { versionPragmas: [], }, }); - npmDependencyFile = new NpmPackageResolvedFile({ + npmDependencyFile = new NpmPackageResolvedFileImplementation({ sourceName: "npm:dependency/1.0.0/dependency.sol", fsPath: "dependency.sol", package: { @@ -47,7 +51,7 @@ describe("CompilationJobImplementation", () => { versionPragmas: [], }, }); - projectDependencyFile = new ProjectResolvedFile({ + projectDependencyFile = new ProjectResolvedFileImplementation({ sourceName: "dependency.sol", fsPath: "dependency.sol", content: { @@ -127,7 +131,7 @@ describe("CompilationJobImplementation", () => { }); it("there is an additional root file in the dependency graph", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newRootFile = new ProjectResolvedFile({ + const newRootFile = new ProjectResolvedFileImplementation({ ...rootFile, sourceName: "newRoot.sol", }); @@ -148,7 +152,7 @@ describe("CompilationJobImplementation", () => { }); it("there is an additional dependency in the dependency graph", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newDependencyFile = new ProjectResolvedFile({ + const newDependencyFile = new ProjectResolvedFileImplementation({ ...projectDependencyFile, sourceName: "newDependency.sol", }); @@ -169,7 +173,7 @@ describe("CompilationJobImplementation", () => { }); it("the content of one of the root files changes", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newRootFile = new ProjectResolvedFile({ + const newRootFile = new ProjectResolvedFileImplementation({ ...rootFile, content: { ...rootFile.content, @@ -192,7 +196,7 @@ describe("CompilationJobImplementation", () => { }); it("the content of one of the dependencies changes", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newDependencyFile = new NpmPackageResolvedFile({ + const newDependencyFile = new NpmPackageResolvedFileImplementation({ ...npmDependencyFile, content: { ...npmDependencyFile.content, @@ -215,7 +219,7 @@ describe("CompilationJobImplementation", () => { }); it("the source name of one of the root files changes", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newRootFile = new ProjectResolvedFile({ + const newRootFile = new ProjectResolvedFileImplementation({ ...rootFile, sourceName: "newRoot.sol", }); @@ -235,7 +239,7 @@ describe("CompilationJobImplementation", () => { }); it("the source name of one of the dependencies changes", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newDependencyFile = new NpmPackageResolvedFile({ + const newDependencyFile = new NpmPackageResolvedFileImplementation({ ...npmDependencyFile, sourceName: "npm:dependency/1.0.0/newDependency.sol", }); @@ -257,7 +261,7 @@ describe("CompilationJobImplementation", () => { describe("should not change when", () => { it("the version of one of the dependencies changes without it being reflected in the source name", async () => { const newDependencyGraph = new DependencyGraphImplementation(); - const newDependencyFile = new NpmPackageResolvedFile({ + const newDependencyFile = new NpmPackageResolvedFileImplementation({ ...npmDependencyFile, package: { ...npmDependencyFile.package, diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/dependency-graph.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/dependency-graph.ts index 9e10e84ce9..0d73941f4f 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/dependency-graph.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/dependency-graph.ts @@ -1,3 +1,5 @@ +import type { ProjectResolvedFile } from "../../../../../src/types/solidity.js"; + import assert from "node:assert/strict"; import path from "node:path"; import { beforeEach, describe, it } from "node:test"; @@ -6,10 +8,10 @@ import { HardhatError } from "@ignored/hardhat-vnext-errors"; import { assertThrowsHardhatError } from "@nomicfoundation/hardhat-test-utils"; import { DependencyGraphImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/dependency-graph.js"; -import { ProjectResolvedFile } from "../../../../../src/types/solidity.js"; +import { ProjectResolvedFileImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/resolved-file.js"; function createProjectResolvedFile(sourceName: string): ProjectResolvedFile { - return new ProjectResolvedFile({ + return new ProjectResolvedFileImplementation({ sourceName, fsPath: path.join(process.cwd(), sourceName), content: { diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/root-paths-utils.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/root-paths-utils.ts index 00d2d2afa1..edad9f04ed 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/root-paths-utils.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/root-paths-utils.ts @@ -4,6 +4,10 @@ import type { ResolvedFile } from "../../../../../src/types/solidity.js"; import assert from "node:assert/strict"; import { describe, it } from "node:test"; +import { + NpmPackageResolvedFileImplementation, + ProjectResolvedFileImplementation, +} from "../../../../../src/internal/builtin-plugins/solidity/build-system/resolved-file.js"; import { formatRootPath, isNpmParsedRootPath, @@ -11,10 +15,6 @@ import { npmModuleToNpmRootPath, parseRootPath, } from "../../../../../src/internal/builtin-plugins/solidity/build-system/root-paths-utils.js"; -import { - NpmPackageResolvedFile, - ProjectResolvedFile, -} from "../../../../../src/types/solidity.js"; interface TestRootPath { rootPath: string; @@ -34,7 +34,7 @@ const testRootPaths: TestRootPath[] = [ isNpm: true, npmModule: "ethers", publicSourceName: "ethers", - resolvedFile: new NpmPackageResolvedFile({ + resolvedFile: new NpmPackageResolvedFileImplementation({ sourceName: "ethers", fsPath: "/Users/root/node_modules/ethers/index.js", content: { @@ -58,7 +58,7 @@ const testRootPaths: TestRootPath[] = [ isNpm: true, npmModule: "@openzeppelin/contracts", publicSourceName: "@openzeppelin/contracts", - resolvedFile: new NpmPackageResolvedFile({ + resolvedFile: new NpmPackageResolvedFileImplementation({ sourceName: "@openzeppelin/contracts", fsPath: "/Users/root/node_modules/@openzeppelin/contracts/index.js", content: { @@ -82,7 +82,7 @@ const testRootPaths: TestRootPath[] = [ isNpm: true, npmModule: "@openzeppelin/contracts/token/ERC20/ERC20.sol", publicSourceName: "@openzeppelin/contracts/token/ERC20/ERC20.sol", - resolvedFile: new NpmPackageResolvedFile({ + resolvedFile: new NpmPackageResolvedFileImplementation({ sourceName: "@openzeppelin/contracts/token/ERC20/ERC20.sol", fsPath: "/Users/root/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol", @@ -107,7 +107,7 @@ const testRootPaths: TestRootPath[] = [ isNpm: false, npmModule: undefined, publicSourceName: "/Users/root/contracts/Contract.sol", - resolvedFile: new ProjectResolvedFile({ + resolvedFile: new ProjectResolvedFileImplementation({ sourceName: "/Users/root/contracts/Contract.sol", fsPath: "/Users/root/contracts/Contract.sol", content: { @@ -125,7 +125,7 @@ const testRootPaths: TestRootPath[] = [ isNpm: false, npmModule: undefined, publicSourceName: "C:\\Users\\root\\contracts\\Contract.sol", - resolvedFile: new ProjectResolvedFile({ + resolvedFile: new ProjectResolvedFileImplementation({ sourceName: "C:\\Users\\root\\contracts\\Contract.sol", fsPath: "C:\\Users\\root\\contracts\\Contract.sol", content: { diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/solc-config-selection.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/solc-config-selection.ts index d6f03bfa4a..249349ccf1 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/solc-config-selection.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/solc-config-selection.ts @@ -1,4 +1,5 @@ import type { SolidityBuildProfileConfig } from "../../../../../src/types/config.js"; +import type { ProjectResolvedFile } from "../../../../../src/types/solidity.js"; import assert from "node:assert/strict"; import path from "node:path"; @@ -8,17 +9,15 @@ import { HardhatError } from "@ignored/hardhat-vnext-errors"; import { assertRejectsWithHardhatError } from "@nomicfoundation/hardhat-test-utils"; import { DependencyGraphImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/dependency-graph.js"; +import { ProjectResolvedFileImplementation } from "../../../../../src/internal/builtin-plugins/solidity/build-system/resolved-file.js"; import { SolcConfigSelector } from "../../../../../src/internal/builtin-plugins/solidity/build-system/solc-config-selection.js"; -import { - ProjectResolvedFile, - CompilationJobCreationErrorReason, -} from "../../../../../src/types/solidity.js"; +import { CompilationJobCreationErrorReason } from "../../../../../src/types/solidity.js"; function createProjectResolvedFile( sourceName: string, versionPragmas: string[], ): ProjectResolvedFile { - return new ProjectResolvedFile({ + return new ProjectResolvedFileImplementation({ sourceName, fsPath: path.join(process.cwd(), sourceName), content: { From b1d6cadb034add79d7749e4630f59713734c919e Mon Sep 17 00:00:00 2001 From: galargh Date: Thu, 30 Jan 2025 21:10:07 +0100 Subject: [PATCH 46/53] test: add a 1ms sleep before cleaning cache --- .../test/internal/builtin-plugins/solidity/build-system/cache.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts index 90abad8232..de38c3587b 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts @@ -69,6 +69,7 @@ describe("ObjectCache", () => { it("should remove everything with the max age set to 0", async () => { const filesBefore = await getAllFilesMatching(cachePath); assert.notDeepEqual(filesBefore, []); + await new Promise((resolve) => setTimeout(resolve, 1)); await cache.clean(0); const filesAfter = await getAllFilesMatching(cachePath); assert.deepEqual(filesAfter, []); From 0dbc01682854625fcfb7ca7408a7e524d7adb2d6 Mon Sep 17 00:00:00 2001 From: galargh Date: Thu, 30 Jan 2025 21:14:46 +0100 Subject: [PATCH 47/53] chore: fix lint --- .../resolver/dependency-resolver.ts | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts index f26d2069fa..dc50eb3cd7 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts @@ -238,11 +238,12 @@ export class ResolverImplementation implements Resolver { trueCaseFsPath, ); - const resolvedFile = new ProjectResolvedFileImplementation({ - sourceName, - fsPath: fsPathWithTheRightCasing, - content: await readFileContent(fsPathWithTheRightCasing), - }); + const resolvedFile: ProjectResolvedFile = + new ProjectResolvedFileImplementation({ + sourceName, + fsPath: fsPathWithTheRightCasing, + content: await readFileContent(fsPathWithTheRightCasing), + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); @@ -805,11 +806,12 @@ export class ResolverImplementation implements Resolver { const fsPath = path.join(this.#projectRoot, fsPathWithinTheProject); - const resolvedFile = new ProjectResolvedFileImplementation({ - sourceName, - fsPath, - content: await readFileContent(fsPath), - }); + const resolvedFile: ProjectResolvedFile = + new ProjectResolvedFileImplementation({ + sourceName, + fsPath, + content: await readFileContent(fsPath), + }); this.#resolvedFileBySourceName.set(sourceName, resolvedFile); From 28fe32e0b9e3395f193b9e2d8c106152af63da4a Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 5 Feb 2025 09:50:41 +0100 Subject: [PATCH 48/53] chore: address feedback fromt the review --- v-next/hardhat-utils/src/crypto.ts | 3 +-- v-next/hardhat-utils/src/fs.ts | 2 +- v-next/hardhat-utils/test/fs.ts | 8 ++++---- .../builtin-plugins/solidity/build-system/cache.ts | 4 ++-- .../builtin-plugins/solidity/build-system/cache.ts | 7 +++++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/v-next/hardhat-utils/src/crypto.ts b/v-next/hardhat-utils/src/crypto.ts index 024fcc9219..db80e2efa8 100644 --- a/v-next/hardhat-utils/src/crypto.ts +++ b/v-next/hardhat-utils/src/crypto.ts @@ -31,6 +31,5 @@ export async function createNonCryptographicHashId( ): Promise { const message = new TextEncoder().encode(data); const buffer = await crypto.subtle.digest("SHA-1", message); - const array = Array.from(new Uint8Array(buffer)); - return array.map((b) => b.toString(16).padStart(2, "0")).join(""); + return Buffer.from(buffer).toString("hex"); } diff --git a/v-next/hardhat-utils/src/fs.ts b/v-next/hardhat-utils/src/fs.ts index ccc4525722..e3f3eb4997 100644 --- a/v-next/hardhat-utils/src/fs.ts +++ b/v-next/hardhat-utils/src/fs.ts @@ -437,7 +437,7 @@ export async function getAccessTime(absolutePath: string): Promise { * @throws FileNotFoundError if the path does not exist. * @throws FileSystemAccessError for any other error. */ -export async function getSize(absolutePath: string): Promise { +export async function getFileSize(absolutePath: string): Promise { try { const stats = await fsPromises.stat(absolutePath); return stats.size; diff --git a/v-next/hardhat-utils/test/fs.ts b/v-next/hardhat-utils/test/fs.ts index 3c87e5067a..e77d580b26 100644 --- a/v-next/hardhat-utils/test/fs.ts +++ b/v-next/hardhat-utils/test/fs.ts @@ -27,7 +27,7 @@ import { writeUtf8File, readBinaryFile, getAccessTime, - getSize, + getFileSize, } from "../src/fs.js"; import { useTmpDir } from "./helpers/fs.js"; @@ -775,13 +775,13 @@ describe("File system utils", () => { const stats = await fsPromises.stat(filePath); - assert.equal(stats.size, await getSize(filePath)); + assert.equal(stats.size, await getFileSize(filePath)); }); it("Should throw FileNotFoundError if the file doesn't exist", async () => { const filePath = path.join(getTmpDir(), "not-exists.txt"); - await assert.rejects(getSize(filePath), { + await assert.rejects(getFileSize(filePath), { name: "FileNotFoundError", message: `File ${filePath} not found`, }); @@ -790,7 +790,7 @@ describe("File system utils", () => { it("Should throw FileSystemAccessError if a different error is thrown", async () => { const invalidPath = "\0"; - await assert.rejects(getSize(invalidPath), { + await assert.rejects(getFileSize(invalidPath), { name: "FileSystemAccessError", }); }); diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts index 2b8ec1e1c9..312385857e 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -4,7 +4,7 @@ import { exists, getAccessTime, getAllFilesMatching, - getSize, + getFileSize, readJsonFile, remove, writeJsonFile, @@ -46,7 +46,7 @@ export class ObjectCache { files.map(async (file) => ({ file, atimeMs: (await getAccessTime(file)).getTime(), - size: await getSize(file), + size: await getFileSize(file), })), ); diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts index de38c3587b..4e6afd1175 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts @@ -3,7 +3,10 @@ import { randomUUID } from "node:crypto"; import path from "node:path"; import { beforeEach, describe, it } from "node:test"; -import { getAllFilesMatching, getSize } from "@ignored/hardhat-vnext-utils/fs"; +import { + getAllFilesMatching, + getFileSize, +} from "@ignored/hardhat-vnext-utils/fs"; import { useTmpDir } from "@nomicfoundation/hardhat-test-utils"; import { ObjectCache } from "../../../../../src/internal/builtin-plugins/solidity/build-system/cache.js"; @@ -99,7 +102,7 @@ describe("ObjectCache", () => { const filesBefore = await getAllFilesMatching(cachePath); assert.notDeepEqual(filesBefore, []); cache.set(randomUUID(), testValue); - await cache.clean(undefined, (await getSize(filesBefore[0])) * 1.5); + await cache.clean(undefined, (await getFileSize(filesBefore[0])) * 1.5); const filesAfter = await getAllFilesMatching(cachePath); assert.notDeepEqual(filesAfter, []); assert.notDeepEqual(filesAfter, filesBefore); From 1da1e5c07c07bf1aa2a65244bc725877814684c3 Mon Sep 17 00:00:00 2001 From: galargh Date: Wed, 5 Feb 2025 10:02:27 +0100 Subject: [PATCH 49/53] test: fix cache tests in which the access time order matters --- .../internal/builtin-plugins/solidity/build-system/cache.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts index 4e6afd1175..3cd5fe8013 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts @@ -89,7 +89,7 @@ describe("ObjectCache", () => { it("should remove something with the max age set to some value", async () => { const filesBefore = await getAllFilesMatching(cachePath); assert.notDeepEqual(filesBefore, []); - // NOTE: If the test ends up being flaky, we should increase the timeout + // NOTE: We're waiting a little so that the file's atime is different await new Promise((resolve) => setTimeout(resolve, 10)); cache.set(randomUUID(), testValue); await cache.clean(10); @@ -101,6 +101,8 @@ describe("ObjectCache", () => { it("should remove something with the max size set to some value", async () => { const filesBefore = await getAllFilesMatching(cachePath); assert.notDeepEqual(filesBefore, []); + // NOTE: We're waiting a little so that the file's atime is different + await new Promise((resolve) => setTimeout(resolve, 10)); cache.set(randomUUID(), testValue); await cache.clean(undefined, (await getFileSize(filesBefore[0])) * 1.5); const filesAfter = await getAllFilesMatching(cachePath); From 5bdb5a9f738d515ce67c95b6af411bd6063f214c Mon Sep 17 00:00:00 2001 From: galargh Date: Thu, 6 Feb 2025 15:19:34 +0100 Subject: [PATCH 50/53] chore: address the pr review feedback collectively --- .../solidity/build-system/build-system.ts | 43 +++++++++++++------ .../solidity/build-system/cache.ts | 12 ++++-- .../solidity/build-system/compilation-job.ts | 2 + 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts index af74eae4ed..ccfffada20 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/build-system.ts @@ -131,6 +131,8 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { const compilationJobs = [...new Set(compilationJobsPerFile.values())]; + // NOTE: We precompute the build ids in parallel here, which are cached + // internally in each compilation job await Promise.all( compilationJobs.map(async (compilationJob) => compilationJob.getBuildId(), @@ -182,18 +184,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { (result) => !this.#hasCompilationErrors(result.compilerOutput), ); - // NOTE: We're not waiting for the writes and clean to finish because we - // will only care about the result of these operations in subsequent runs - void Promise.all( - uncachedSuccessfulResults.map(async (result) => { - return this.#compilerOutputCache.set( - await result.compilationJob.getBuildId(), - result.compilerOutput, - ); - }), - ).then(() => { - return this.#compilerOutputCache.clean(); - }); + this.#cacheCompilationResults(uncachedSuccessfulResults); const isSuccessfulBuild = uncachedResults.length === uncachedSuccessfulResults.length; @@ -719,6 +710,34 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem { return `${error.type}: ${error.message}`.replace(/[:\s]*$/g, "").trim(); } + /** This function caches the compilation results in the compiler output cache. + * + * Please note that it does **NOT** wait for the writes/clean to finish. This + * is because we don't want to hold up the compilation process. + * + * We accept that some of the writes might fail. This is only safe because + * the write operation first writes to a temporary file, and only once that + * is done, it moves the file to the final location. + * + * If we notice that the amount of write failures is too high, or that the + * cache is not getting cleaned up often enough, we should reconsider this + * approach. + * + * @param compilationResults + */ + #cacheCompilationResults(compilationResults: CompilationResult[]): void { + void Promise.all( + compilationResults.map(async (result) => { + return this.#compilerOutputCache.set( + await result.compilationJob.getBuildId(), + result.compilerOutput, + ); + }), + ).then(() => { + return this.#compilerOutputCache.clean(); + }); + } + #printSolcErrorsAndWarnings(errors?: CompilerOutputError[]): void { if (errors === undefined) { return; diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts index 312385857e..5545665ebd 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -5,6 +5,7 @@ import { getAccessTime, getAllFilesMatching, getFileSize, + move, readJsonFile, remove, writeJsonFile, @@ -28,12 +29,17 @@ export class ObjectCache { } public async set(key: string, value: T): Promise { - const filePath = path.join(this.#path, key); - await writeJsonFile(filePath, value); + const filePath = path.join(this.#path, `${key}.json`); + const tmpPath = `${filePath}.tmp`; + + // NOTE: We are writing to a temporary file first because the value might + // be large and we don't want to end up with corrupted files in the cache. + await writeJsonFile(tmpPath, value); + await move(tmpPath, filePath); } public async get(key: string): Promise { - const filePath = path.join(this.#path, key); + const filePath = path.join(this.#path, `${key}.json`); return (await exists(filePath)) ? readJsonFile(filePath) : undefined; } diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index 79df6facbd..4d1e237832 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -145,6 +145,8 @@ export class CompilationJobImplementation implements CompilationJob { }), ); + // NOTE: We need to sort the sources because the sources map might be + // populated out of order which does affect serialisation. const sortedSources = Object.fromEntries( Object.entries(sources).sort((a, b) => a[0].localeCompare(b[0])), ); From b31d9a79b246b129cc3a0277b0d9d7ec23220627 Mon Sep 17 00:00:00 2001 From: galargh Date: Thu, 6 Feb 2025 15:31:10 +0100 Subject: [PATCH 51/53] test: fix tests by awaiting the cache set operation --- .../internal/builtin-plugins/solidity/build-system/cache.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts index 3cd5fe8013..65dffbcb6f 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts @@ -91,7 +91,7 @@ describe("ObjectCache", () => { assert.notDeepEqual(filesBefore, []); // NOTE: We're waiting a little so that the file's atime is different await new Promise((resolve) => setTimeout(resolve, 10)); - cache.set(randomUUID(), testValue); + await cache.set(randomUUID(), testValue); await cache.clean(10); const filesAfter = await getAllFilesMatching(cachePath); assert.notDeepEqual(filesAfter, []); @@ -103,7 +103,7 @@ describe("ObjectCache", () => { assert.notDeepEqual(filesBefore, []); // NOTE: We're waiting a little so that the file's atime is different await new Promise((resolve) => setTimeout(resolve, 10)); - cache.set(randomUUID(), testValue); + await cache.set(randomUUID(), testValue); await cache.clean(undefined, (await getFileSize(filesBefore[0])) * 1.5); const filesAfter = await getAllFilesMatching(cachePath); assert.notDeepEqual(filesAfter, []); From cbe10ee0c75f4fa17b8e8353ffa04fea4321437f Mon Sep 17 00:00:00 2001 From: galargh Date: Thu, 6 Feb 2025 15:38:05 +0100 Subject: [PATCH 52/53] feat: always remove all .tmp files from cache during clean --- .../builtin-plugins/solidity/build-system/cache.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts index 5545665ebd..bdf52b3728 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/solidity/build-system/cache.ts @@ -47,7 +47,9 @@ export class ObjectCache { maxAgeMs ??= this.#defaultMaxAgeMs; maxSize ??= this.#defaultMaxSize; - const files = await getAllFilesMatching(this.#path); + const files = await getAllFilesMatching(this.#path, (file) => + file.endsWith(".json"), + ); const fileInfos = await Promise.all( files.map(async (file) => ({ file, @@ -64,7 +66,10 @@ export class ObjectCache { ); const minAtimeMs = new Date().getTime() - maxAgeMs; - const filesToRemove: string[] = []; + const filesToRemove: string[] = await getAllFilesMatching( + this.#path, + (file) => file.endsWith(".json.tmp"), + ); for (const fileInfo of sortedFileInfos) { if (fileInfo.atimeMs < minAtimeMs || size > maxSize) { From ec80460acf8df33d45cb2e1c46fb12b170ef1547 Mon Sep 17 00:00:00 2001 From: galargh Date: Fri, 7 Feb 2025 10:07:29 +0100 Subject: [PATCH 53/53] test: make the waits the same in all the cache clean tests --- .../internal/builtin-plugins/solidity/build-system/cache.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts index 65dffbcb6f..4d87a7d7e4 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/solidity/build-system/cache.ts @@ -72,7 +72,8 @@ describe("ObjectCache", () => { it("should remove everything with the max age set to 0", async () => { const filesBefore = await getAllFilesMatching(cachePath); assert.notDeepEqual(filesBefore, []); - await new Promise((resolve) => setTimeout(resolve, 1)); + // NOTE: We're waiting a little so that the file's atime is different + await new Promise((resolve) => setTimeout(resolve, 10)); await cache.clean(0); const filesAfter = await getAllFilesMatching(cachePath); assert.deepEqual(filesAfter, []);