diff --git a/.changeset/moody-trees-sing.md b/.changeset/moody-trees-sing.md new file mode 100644 index 000000000..4f18767d2 --- /dev/null +++ b/.changeset/moody-trees-sing.md @@ -0,0 +1,5 @@ +--- +'@sphinx-labs/plugins': patch +--- + +Avoid recompiling every single time we invoke the users script diff --git a/docs/cli-existing-project.md b/docs/cli-existing-project.md index a5723db38..de9a4afeb 100644 --- a/docs/cli-existing-project.md +++ b/docs/cli-existing-project.md @@ -189,7 +189,6 @@ ALCHEMY_API_KEY= Update your `foundry.toml` file to include a few settings required by Sphinx. We recommend putting them in `[profile.default]`. ``` -build_info = true extra_output = ['storageLayout'] fs_permissions = [{ access = "read-write", path = "./"}] ``` diff --git a/packages/plugins/src/cli/deploy.ts b/packages/plugins/src/cli/deploy.ts index 5da7139a1..71728cffb 100644 --- a/packages/plugins/src/cli/deploy.ts +++ b/packages/plugins/src/cli/deploy.ts @@ -104,10 +104,23 @@ export const deploy = async ( ) } - // Run the compiler. It's necessary to do this before we read any contract interfaces. + /** + * Run the compiler. It's necessary to do this before we read any contract interfaces. + * We force recompile and request the build info here. We need to build info to generate + * the compiler config. + * + * The current behavior of Foundry is to disregard the cache when compiling with the build info. + * This causes the users entire project to be rebuilt when the build info file is requested. So + * since we have to rebuild the entire project anyway, we also use force which will remove any + * previous build info files making our later logic for reading the build info files more performant. + * + * If/when Foundry fixes this so that the cache is used when the build info file is requested, we should + * remove force here for the best performance. + */ compile( silent, - false // Do not force re-compile. + true, // Force recompile + true // Generate build info ) const scriptFunctionCalldata = await parseScriptFunctionCalldata(sig) @@ -233,6 +246,10 @@ export const deploy = async ( // priority than `DAPP_BLOCK_GAS_LIMIT`. FOUNDRY_BLOCK_GAS_LIMIT: MAX_UINT64.toString(), ETH_FROM: safeAddress, + // We specify build info to be false so that calling the script does not cause the users entire + // project to be rebuilt if they have `build_info=true` defined in their foundry.toml file. + // We do need the build info, but that is generated when we compile at the beginning of the script. + FOUNDRY_BUILD_INFO: 'false', }) if (spawnOutput.code !== 0) { diff --git a/packages/plugins/src/cli/propose/index.ts b/packages/plugins/src/cli/propose/index.ts index 8c46a4e98..862fcfa88 100644 --- a/packages/plugins/src/cli/propose/index.ts +++ b/packages/plugins/src/cli/propose/index.ts @@ -136,6 +136,10 @@ export const buildNetworkConfigArray: BuildNetworkConfigArray = async ( // priority than `DAPP_BLOCK_GAS_LIMIT`. FOUNDRY_BLOCK_GAS_LIMIT: MAX_UINT64.toString(), ETH_FROM: safeAddress, + // We specify build info to be false so that calling the script does not cause the users entire + // project to be rebuilt if they have `build_info=true` defined in their foundry.toml file. + // We do need the build info, but that is generated when we compile at the beginning of the script. + FOUNDRY_BUILD_INFO: 'false', }) if (spawnOutput.code !== 0) { @@ -246,10 +250,23 @@ export const propose = async ( process.exit(1) } - // Run the compiler. It's necessary to do this before we read any contract interfaces. + /** + * Run the compiler. It's necessary to do this before we read any contract interfaces. + * We force recompile and request the build info here. We need to build info to generate + * the compiler config. + * + * The current behavior of Foundry is to disregard the cache when compiling with the build info. + * This causes the users entire project to be rebuilt when the build info file is requested. So + * since we have to rebuild the entire project anyway, we also use force which will remove any + * previous build info files making our later logic for reading the build info files more performant. + * + * If/when Foundry fixes this so that the cache is used when the build info file is requested, we should + * remove force here for the best performance. + */ compile( silent, - false // Do not force re-compile. + true, // Force recompile + true // Generate build info ) const scriptFunctionCalldata = await parseScriptFunctionCalldata(sig) diff --git a/packages/plugins/src/foundry/options.ts b/packages/plugins/src/foundry/options.ts index 8b54c791c..b85c791f0 100644 --- a/packages/plugins/src/foundry/options.ts +++ b/packages/plugins/src/foundry/options.ts @@ -25,12 +25,6 @@ export const resolvePaths = (outPath: string, buildInfoPath: string) => { } export const checkRequiredTomlOptions = (toml: FoundryToml) => { - if (toml.buildInfo !== true) { - throw new Error( - 'Missing required option in foundry.toml file:\nbuild_info = true\nPlease update your foundry.toml file and try again.' - ) - } - // Check if the user included the `storageLayout` option. Since foundry force recompiles after // changing the foundry.toml file, we can assume that the contract artifacts will contain the // necessary info as long as the config includes the expected options diff --git a/packages/plugins/src/foundry/utils/index.ts b/packages/plugins/src/foundry/utils/index.ts index d3de342bb..f286dd60e 100644 --- a/packages/plugins/src/foundry/utils/index.ts +++ b/packages/plugins/src/foundry/utils/index.ts @@ -300,7 +300,11 @@ export const getInitCodeWithArgsArray = ( * It's fine for recompilation to occur after running the user's Forge script because Foundry * automatically compiles the necessary contracts before executing it. */ -export const compile = (silent: boolean, force: boolean): void => { +export const compile = ( + silent: boolean, + force: boolean, + buildInfo: boolean +): void => { const forgeBuildArgs = ['build'] if (silent) { @@ -309,6 +313,9 @@ export const compile = (silent: boolean, force: boolean): void => { if (force) { forgeBuildArgs.push('--force') } + if (buildInfo) { + forgeBuildArgs.push('--build-info') + } // We use `spawnSync` to display the compilation process to the user as it occurs. Compiler errors // will be displayed to the user even if the `silent` flag is included. @@ -833,7 +840,12 @@ export const callForgeScriptFunction = async ( code: testCode, stdout: testOut, stderr: testErr, - } = await spawnAsync('forge', testScriptArgs) + } = await spawnAsync('forge', testScriptArgs, { + // We specify build info to be false so that calling the script does not cause the users entire + // project to be rebuilt if they have `build_info=true` defined in their foundry.toml file. + // We do need the build info, but that is generated when we compile at the beginning of the script. + FOUNDRY_BUILD_INFO: 'false', + }) if (testCode !== 0) { spinner?.stop() @@ -855,7 +867,12 @@ export const callForgeScriptFunction = async ( true ) - const { code, stdout, stderr } = await spawnAsync('forge', forgeScriptArgs) + const { code, stdout, stderr } = await spawnAsync('forge', forgeScriptArgs, { + // We specify build info to be false so that calling the script does not cause the users entire + // project to be rebuilt if they have `build_info=true` defined in their foundry.toml file. + // We do need the build info, but that is generated when we compile at the beginning of the script. + FOUNDRY_BUILD_INFO: 'false', + }) // For good measure, we still read the code and error if necessary but this is unlikely to be triggered if (code !== 0) { diff --git a/packages/plugins/src/sample-project/sample-foundry-config.ts b/packages/plugins/src/sample-project/sample-foundry-config.ts index 944b96748..849165fc8 100644 --- a/packages/plugins/src/sample-project/sample-foundry-config.ts +++ b/packages/plugins/src/sample-project/sample-foundry-config.ts @@ -23,7 +23,6 @@ export const fetchForgeConfig = ( ): string => `[profile.default] script = 'script' test = 'test' -build_info = true extra_output = ['storageLayout'] fs_permissions=[{access="read", path="./out"}, {access="read-write", path="./cache"}] allow_paths = ["../.."]