UNPKG

@nomiclabs/buidler

Version:

Buidler is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.

238 lines (198 loc) 6.83 kB
import chalk from "chalk"; import fsExtra from "fs-extra"; import path from "path"; import { getArtifactFromContractOutput, saveArtifact, } from "../internal/artifacts"; import { SOLC_INPUT_FILENAME, SOLC_OUTPUT_FILENAME, } from "../internal/constants"; import { internalTask, task, types } from "../internal/core/config/config-env"; import { BuidlerError } from "../internal/core/errors"; import { ERRORS } from "../internal/core/errors-list"; import { Compiler } from "../internal/solidity/compiler"; import { getInputFromDependencyGraph } from "../internal/solidity/compiler/compiler-input"; import { DependencyGraph } from "../internal/solidity/dependencyGraph"; import { Resolver } from "../internal/solidity/resolver"; import { glob } from "../internal/util/glob"; import { getCompilersDir } from "../internal/util/global-dir"; import { pluralize } from "../internal/util/strings"; import { ResolvedBuidlerConfig, SolcInput } from "../types"; import { TASK_BUILD_ARTIFACTS, TASK_COMPILE, TASK_COMPILE_CHECK_CACHE, TASK_COMPILE_COMPILE, TASK_COMPILE_GET_COMPILER_INPUT, TASK_COMPILE_GET_DEPENDENCY_GRAPH, TASK_COMPILE_GET_RESOLVED_SOURCES, TASK_COMPILE_GET_SOURCE_PATHS, TASK_COMPILE_RUN_COMPILER, } from "./task-names"; import { areArtifactsCached, cacheBuidlerConfig } from "./utils/cache"; async function cacheSolcJsonFiles( config: ResolvedBuidlerConfig, input: any, output: any ) { await fsExtra.ensureDir(config.paths.cache); // TODO: This could be much better. It feels somewhat hardcoded await fsExtra.writeFile( path.join(config.paths.cache, SOLC_INPUT_FILENAME), JSON.stringify(input, undefined, 2), { encoding: "utf8", } ); await fsExtra.writeFile( path.join(config.paths.cache, SOLC_OUTPUT_FILENAME), JSON.stringify(output, undefined, 2), { encoding: "utf8", } ); } function isConsoleLogError(error: any): boolean { return ( error.type === "TypeError" && typeof error.message === "string" && error.message.includes("log") && error.message.includes("type(library console)") ); } export default function () { internalTask(TASK_COMPILE_GET_SOURCE_PATHS, async (_, { config }) => { return glob(path.join(config.paths.sources, "**/*.sol")); }); internalTask( TASK_COMPILE_GET_RESOLVED_SOURCES, async (_, { config, run }) => { const resolver = new Resolver(config.paths.root); const paths = await run(TASK_COMPILE_GET_SOURCE_PATHS); return Promise.all( paths.map((p: string) => resolver.resolveProjectSourceFile(p)) ); } ); internalTask( TASK_COMPILE_GET_DEPENDENCY_GRAPH, async (_, { config, run }) => { const resolver = new Resolver(config.paths.root); const localFiles = await run(TASK_COMPILE_GET_RESOLVED_SOURCES); return DependencyGraph.createFromResolvedFiles(resolver, localFiles); } ); internalTask(TASK_COMPILE_GET_COMPILER_INPUT, async (_, { config, run }) => { const dependencyGraph: DependencyGraph = await run( TASK_COMPILE_GET_DEPENDENCY_GRAPH ); return getInputFromDependencyGraph( dependencyGraph, config.solc.optimizer, config.solc.evmVersion ); }); internalTask(TASK_COMPILE_RUN_COMPILER) .addParam( "input", "The compiler standard JSON input", undefined, types.json ) .setAction(async ({ input }: { input: SolcInput }, { config }) => { const compilersCache = await getCompilersDir(); const compiler = new Compiler(config.solc.version, compilersCache); return compiler.compile(input); }); internalTask(TASK_COMPILE_COMPILE, async (_, { config, run }) => { const input = await run(TASK_COMPILE_GET_COMPILER_INPUT); console.log("Compiling..."); const output = await run(TASK_COMPILE_RUN_COMPILER, { input }); let hasErrors = false; let hasConsoleLogErrors = false; if (output.errors) { for (const error of output.errors) { hasErrors = hasErrors || error.severity === "error"; if (error.severity === "error") { hasErrors = true; if (isConsoleLogError(error)) { hasConsoleLogErrors = true; } console.error(chalk.red(error.formattedMessage)); } else { console.log("\n"); console.warn(chalk.yellow(error.formattedMessage)); } } } if (hasConsoleLogErrors) { console.error( chalk.red( `The console.log call you made isn’t supported. See https://buidler.dev/console-log for the list of supported methods.` ) ); console.log(); } if (hasErrors || !output.contracts) { throw new BuidlerError(ERRORS.BUILTIN_TASKS.COMPILE_FAILURE); } await cacheSolcJsonFiles(config, input, output); await cacheBuidlerConfig(config.paths, config.solc); return output; }); internalTask(TASK_COMPILE_CHECK_CACHE, async ({ force }, { config, run }) => { if (force) { return false; } const dependencyGraph: DependencyGraph = await run( TASK_COMPILE_GET_DEPENDENCY_GRAPH ); const sourceTimestamps = dependencyGraph .getResolvedFiles() .map((file) => file.lastModificationDate.getTime()); return areArtifactsCached(sourceTimestamps, config.solc, config.paths); }); internalTask(TASK_BUILD_ARTIFACTS, async ({ force }, { config, run }) => { const sources = await run(TASK_COMPILE_GET_SOURCE_PATHS); if (sources.length === 0) { console.log("No Solidity source file available."); return; } const isCached: boolean = await run(TASK_COMPILE_CHECK_CACHE, { force }); if (isCached) { console.log( "All contracts have already been compiled, skipping compilation." ); return; } const compilationOutput = await run(TASK_COMPILE_COMPILE); if (compilationOutput === undefined) { return; } await fsExtra.ensureDir(config.paths.artifacts); let numberOfContracts = 0; for (const file of Object.values<any>(compilationOutput.contracts)) { for (const [contractName, contractOutput] of Object.entries(file)) { const artifact = getArtifactFromContractOutput( contractName, contractOutput ); numberOfContracts += 1; await saveArtifact(config.paths.artifacts, artifact); } } console.log( "Compiled", numberOfContracts, pluralize(numberOfContracts, "contract"), "successfully" ); }); task(TASK_COMPILE, "Compiles the entire project, building all artifacts") .addFlag("force", "Force compilation ignoring cache") .setAction(async ({ force: force }: { force: boolean }, { run }) => run(TASK_BUILD_ARTIFACTS, { force }) ); }