@openzeppelin/cli
Version:
Command-line interface for the OpenZeppelin smart contract platform
168 lines • 9.64 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = __importDefault(require("path"));
const lodash_1 = require("lodash");
const glob_1 = __importDefault(require("glob"));
const util_1 = require("util");
const fs_extra_1 = require("fs-extra");
const fs_1 = require("fs");
const upgrades_1 = require("@openzeppelin/upgrades");
const SolidityContractsCompiler_1 = require("./SolidityContractsCompiler");
const SourcesGatherer_1 = require("./SourcesGatherer");
const solidity_1 = require("../../../utils/solidity");
const try_1 = require("../../../utils/try");
function compileProject(options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const projectCompiler = new SolidityProjectCompiler(Object.assign(Object.assign({}, options), { inputDir: options.inputDir || upgrades_1.Contracts.getLocalContractsDir(), outputDir: options.outputDir || upgrades_1.Contracts.getLocalBuildDir(), workingDir: options.workingDir || process.cwd() }));
yield projectCompiler.call();
return {
contracts: projectCompiler.contracts,
compilerVersion: projectCompiler.compilerVersion,
artifacts: projectCompiler.compilerOutput,
};
});
}
exports.compileProject = compileProject;
class SolidityProjectCompiler {
constructor(options = {}) {
this.contracts = [];
this.compilerOutput = [];
this.options = options;
}
get inputDir() {
return this.options.inputDir;
}
get outputDir() {
return this.options.outputDir;
}
get workingDir() {
return this.options.workingDir;
}
call() {
return __awaiter(this, void 0, void 0, function* () {
const roots = yield this.loadProjectSoliditySources();
this.contracts = yield this.loadSolidityDependencies(roots);
if (this.contracts.length === 0) {
upgrades_1.Loggy.noSpin(__filename, 'call', 'compile-contracts', 'No contracts found to compile.');
return;
}
this.compilerVersion = yield SolidityContractsCompiler_1.resolveCompilerVersion(this.contracts, this.options);
if (!this.shouldCompile()) {
upgrades_1.Loggy.noSpin(__filename, 'call', `compile-contracts`, 'Nothing to compile, all contracts are up to date.');
return;
}
upgrades_1.Loggy.spin(__filename, 'call', 'compile-contracts', `Compiling contracts with solc ${this.compilerVersion.version} (${this.compilerVersion.build})`);
this.compilerOutput = yield SolidityContractsCompiler_1.compileWith(this.compilerVersion, this.contracts, this.options);
yield this.writeOutput();
upgrades_1.Loggy.succeed('compile-contracts', `Compiled contracts with solc ${this.compilerVersion.version} (${this.compilerVersion.build})`);
});
}
loadProjectSoliditySources() {
return __awaiter(this, void 0, void 0, function* () {
const dir = this.inputDir;
if (!fs_1.existsSync(dir) || !fs_1.lstatSync(dir).isDirectory)
return [];
return util_1.promisify(glob_1.default)('**/*.sol', { cwd: dir, absolute: true });
});
}
loadSolidityDependencies(roots) {
return __awaiter(this, void 0, void 0, function* () {
const importFiles = yield SourcesGatherer_1.gatherSources(roots, this.workingDir);
return importFiles.map(file => ({
fileName: path_1.default.basename(file.name),
filePath: file.name,
source: file.content,
lastModified: try_1.tryFunc(() => fs_1.statSync(file.url).mtimeMs),
dependency: file.dependency,
}));
});
}
shouldCompile() {
if (this.options.force)
return true;
const artifacts = this.listArtifacts();
const artifactsWithMtimes = artifacts.map(artifact => ({
artifact,
mtime: fs_1.statSync(artifact).mtimeMs,
}));
// We pick a single artifact (the most recent one) to get the version it was compiled with
const latestArtifact = lodash_1.maxBy(artifactsWithMtimes, 'mtime');
const latestSchema = latestArtifact && fs_extra_1.readJsonSync(latestArtifact.artifact);
const artifactCompiledVersion = latestSchema && latestSchema.compiler.version;
const artifactSettings = latestSchema && lodash_1.pick(latestSchema.compiler, 'evmVersion', 'optimizer');
// Build current settings based on defaults
const currentSettings = Object.assign({ optimizer: SolidityContractsCompiler_1.DEFAULT_OPTIMIZER, evmVersion: SolidityContractsCompiler_1.defaultEVMVersion(this.compilerVersion.longVersion) }, lodash_1.omitBy(this.options, lodash_1.isUndefined));
// Gather artifacts vs sources modified times
const maxArtifactsMtimes = latestArtifact && latestArtifact.mtime;
const maxSourcesMtimes = lodash_1.max(this.contracts.map(({ lastModified }) => lastModified));
// Compile if there are no previous artifacts, or no mtimes could be collected for sources,
// or sources were modified after artifacts, or compiler version changed, or compiler settings changed
return (!maxArtifactsMtimes ||
!maxSourcesMtimes ||
maxArtifactsMtimes <= maxSourcesMtimes ||
!artifactCompiledVersion ||
!solidity_1.compilerVersionsMatch(artifactCompiledVersion, this.compilerVersion.longVersion) ||
!solidity_1.compilerSettingsMatch(currentSettings, artifactSettings));
}
writeOutput() {
return __awaiter(this, void 0, void 0, function* () {
// Create directory if not exists, or clear it of artifacts if it does,
// preserving networks deployment info
const networksInfo = {};
if (!fs_1.existsSync(this.outputDir)) {
fs_extra_1.ensureDirSync(this.outputDir);
}
else {
const artifacts = this.listArtifacts();
yield Promise.all(artifacts.map((filePath) => __awaiter(this, void 0, void 0, function* () {
const name = path_1.default.basename(filePath, '.json');
const schema = yield fs_extra_1.readJSON(filePath);
if (schema.networks)
networksInfo[name] = schema.networks;
yield fs_extra_1.unlink(filePath);
})));
}
// Check and warn for repeated artifacts
const artifactsByName = lodash_1.groupBy(this.compilerOutput, (artifact) => artifact.contractName);
const repeatedArtifacts = Object.keys(lodash_1.pickBy(artifactsByName, (artifacts) => artifacts.length > 1));
if (repeatedArtifacts.length > 0) {
for (const repeatedArtifact of repeatedArtifacts) {
upgrades_1.Loggy.noSpin.warn(__filename, 'writeOutput', `repeated-artifact-${repeatedArtifact}`, `There is more than one contract named ${repeatedArtifact}. The compiled artifact for only one of them will be generated.`);
}
}
// Delete repeated artifacts, sorting them by local first, assuming that artifacts from dependencies are less used
const sortedArtifactsByLocal = lodash_1.sortBy(this.compilerOutput, (artifact) => { var _a, _b; return _b = (_a = this.contracts.find(c => c.filePath === artifact.sourcePath)) === null || _a === void 0 ? void 0 : _a.dependency, (_b !== null && _b !== void 0 ? _b : ''); });
const uniqueArtifacts = lodash_1.uniqBy(sortedArtifactsByLocal, (artifact) => artifact.contractName);
// Write compiler output, saving networks info if present
yield Promise.all(uniqueArtifacts.map((data) => __awaiter(this, void 0, void 0, function* () {
const name = data.contractName;
const buildFileName = `${this.outputDir}/${name}.json`;
if (networksInfo[name])
Object.assign(data, { networks: networksInfo[name] });
yield fs_extra_1.writeJson(buildFileName, data, { spaces: 2 });
return { filePath: buildFileName, contractName: name };
})));
});
}
listArtifacts() {
if (!fs_1.existsSync(this.outputDir))
return [];
return fs_1.readdirSync(this.outputDir)
.map(fileName => path_1.default.resolve(this.outputDir, fileName))
.filter(fileName => !fs_1.lstatSync(fileName).isDirectory())
.filter(fileName => path_1.default.extname(fileName) === '.json');
}
}
//# sourceMappingURL=SolidityProjectCompiler.js.map