UNPKG

6.83 kBPlain TextView Raw
1import chalk from "chalk";
2import fsExtra from "fs-extra";
3import path from "path";
4
5import {
6 getArtifactFromContractOutput,
7 saveArtifact,
8} from "../internal/artifacts";
9import {
10 SOLC_INPUT_FILENAME,
11 SOLC_OUTPUT_FILENAME,
12} from "../internal/constants";
13import { internalTask, task, types } from "../internal/core/config/config-env";
14import { BuidlerError } from "../internal/core/errors";
15import { ERRORS } from "../internal/core/errors-list";
16import { Compiler } from "../internal/solidity/compiler";
17import { getInputFromDependencyGraph } from "../internal/solidity/compiler/compiler-input";
18import { DependencyGraph } from "../internal/solidity/dependencyGraph";
19import { Resolver } from "../internal/solidity/resolver";
20import { glob } from "../internal/util/glob";
21import { getCompilersDir } from "../internal/util/global-dir";
22import { pluralize } from "../internal/util/strings";
23import { ResolvedBuidlerConfig, SolcInput } from "../types";
24
25import {
26 TASK_BUILD_ARTIFACTS,
27 TASK_COMPILE,
28 TASK_COMPILE_CHECK_CACHE,
29 TASK_COMPILE_COMPILE,
30 TASK_COMPILE_GET_COMPILER_INPUT,
31 TASK_COMPILE_GET_DEPENDENCY_GRAPH,
32 TASK_COMPILE_GET_RESOLVED_SOURCES,
33 TASK_COMPILE_GET_SOURCE_PATHS,
34 TASK_COMPILE_RUN_COMPILER,
35} from "./task-names";
36import { areArtifactsCached, cacheBuidlerConfig } from "./utils/cache";
37
38async function cacheSolcJsonFiles(
39 config: ResolvedBuidlerConfig,
40 input: any,
41 output: any
42) {
43 await fsExtra.ensureDir(config.paths.cache);
44
45 // TODO: This could be much better. It feels somewhat hardcoded
46 await fsExtra.writeFile(
47 path.join(config.paths.cache, SOLC_INPUT_FILENAME),
48 JSON.stringify(input, undefined, 2),
49 {
50 encoding: "utf8",
51 }
52 );
53
54 await fsExtra.writeFile(
55 path.join(config.paths.cache, SOLC_OUTPUT_FILENAME),
56 JSON.stringify(output, undefined, 2),
57 {
58 encoding: "utf8",
59 }
60 );
61}
62
63function isConsoleLogError(error: any): boolean {
64 return (
65 error.type === "TypeError" &&
66 typeof error.message === "string" &&
67 error.message.includes("log") &&
68 error.message.includes("type(library console)")
69 );
70}
71
72export default function () {
73 internalTask(TASK_COMPILE_GET_SOURCE_PATHS, async (_, { config }) => {
74 return glob(path.join(config.paths.sources, "**/*.sol"));
75 });
76
77 internalTask(
78 TASK_COMPILE_GET_RESOLVED_SOURCES,
79 async (_, { config, run }) => {
80 const resolver = new Resolver(config.paths.root);
81 const paths = await run(TASK_COMPILE_GET_SOURCE_PATHS);
82 return Promise.all(
83 paths.map((p: string) => resolver.resolveProjectSourceFile(p))
84 );
85 }
86 );
87
88 internalTask(
89 TASK_COMPILE_GET_DEPENDENCY_GRAPH,
90 async (_, { config, run }) => {
91 const resolver = new Resolver(config.paths.root);
92 const localFiles = await run(TASK_COMPILE_GET_RESOLVED_SOURCES);
93
94 return DependencyGraph.createFromResolvedFiles(resolver, localFiles);
95 }
96 );
97
98 internalTask(TASK_COMPILE_GET_COMPILER_INPUT, async (_, { config, run }) => {
99 const dependencyGraph: DependencyGraph = await run(
100 TASK_COMPILE_GET_DEPENDENCY_GRAPH
101 );
102
103 return getInputFromDependencyGraph(
104 dependencyGraph,
105 config.solc.optimizer,
106 config.solc.evmVersion
107 );
108 });
109
110 internalTask(TASK_COMPILE_RUN_COMPILER)
111 .addParam(
112 "input",
113 "The compiler standard JSON input",
114 undefined,
115 types.json
116 )
117 .setAction(async ({ input }: { input: SolcInput }, { config }) => {
118 const compilersCache = await getCompilersDir();
119 const compiler = new Compiler(config.solc.version, compilersCache);
120
121 return compiler.compile(input);
122 });
123
124 internalTask(TASK_COMPILE_COMPILE, async (_, { config, run }) => {
125 const input = await run(TASK_COMPILE_GET_COMPILER_INPUT);
126
127 console.log("Compiling...");
128 const output = await run(TASK_COMPILE_RUN_COMPILER, { input });
129
130 let hasErrors = false;
131 let hasConsoleLogErrors = false;
132 if (output.errors) {
133 for (const error of output.errors) {
134 hasErrors = hasErrors || error.severity === "error";
135 if (error.severity === "error") {
136 hasErrors = true;
137
138 if (isConsoleLogError(error)) {
139 hasConsoleLogErrors = true;
140 }
141
142 console.error(chalk.red(error.formattedMessage));
143 } else {
144 console.log("\n");
145 console.warn(chalk.yellow(error.formattedMessage));
146 }
147 }
148 }
149
150 if (hasConsoleLogErrors) {
151 console.error(
152 chalk.red(
153 `The console.log call you made isn’t supported. See https://buidler.dev/console-log for the list of supported methods.`
154 )
155 );
156 console.log();
157 }
158
159 if (hasErrors || !output.contracts) {
160 throw new BuidlerError(ERRORS.BUILTIN_TASKS.COMPILE_FAILURE);
161 }
162
163 await cacheSolcJsonFiles(config, input, output);
164
165 await cacheBuidlerConfig(config.paths, config.solc);
166
167 return output;
168 });
169
170 internalTask(TASK_COMPILE_CHECK_CACHE, async ({ force }, { config, run }) => {
171 if (force) {
172 return false;
173 }
174
175 const dependencyGraph: DependencyGraph = await run(
176 TASK_COMPILE_GET_DEPENDENCY_GRAPH
177 );
178
179 const sourceTimestamps = dependencyGraph
180 .getResolvedFiles()
181 .map((file) => file.lastModificationDate.getTime());
182
183 return areArtifactsCached(sourceTimestamps, config.solc, config.paths);
184 });
185
186 internalTask(TASK_BUILD_ARTIFACTS, async ({ force }, { config, run }) => {
187 const sources = await run(TASK_COMPILE_GET_SOURCE_PATHS);
188
189 if (sources.length === 0) {
190 console.log("No Solidity source file available.");
191 return;
192 }
193
194 const isCached: boolean = await run(TASK_COMPILE_CHECK_CACHE, { force });
195
196 if (isCached) {
197 console.log(
198 "All contracts have already been compiled, skipping compilation."
199 );
200 return;
201 }
202
203 const compilationOutput = await run(TASK_COMPILE_COMPILE);
204
205 if (compilationOutput === undefined) {
206 return;
207 }
208
209 await fsExtra.ensureDir(config.paths.artifacts);
210 let numberOfContracts = 0;
211
212 for (const file of Object.values<any>(compilationOutput.contracts)) {
213 for (const [contractName, contractOutput] of Object.entries(file)) {
214 const artifact = getArtifactFromContractOutput(
215 contractName,
216 contractOutput
217 );
218 numberOfContracts += 1;
219
220 await saveArtifact(config.paths.artifacts, artifact);
221 }
222 }
223
224 console.log(
225 "Compiled",
226 numberOfContracts,
227 pluralize(numberOfContracts, "contract"),
228 "successfully"
229 );
230 });
231
232 task(TASK_COMPILE, "Compiles the entire project, building all artifacts")
233 .addFlag("force", "Force compilation ignoring cache")
234 .setAction(async ({ force: force }: { force: boolean }, { run }) =>
235 run(TASK_BUILD_ARTIFACTS, { force })
236 );
237}