@truffle/compile-solidity
Version:
Compiler helper and artifact manager for Solidity files
139 lines • 6.82 kB
JavaScript
;
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 });
exports.compileWithPragmaAnalysis = void 0;
const compilerSupplier_1 = require("./compilerSupplier");
const config_1 = __importDefault(require("@truffle/config"));
const semver_1 = __importDefault(require("semver"));
const profiler_1 = require("./profiler");
const run_1 = require("./run");
const reportSources_1 = require("./reportSources");
const os_1 = __importDefault(require("os"));
const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
const getSemverExpression = source => {
const result = source.match(/pragma solidity(.*);/);
return result && result[1] ? result[1].trim() : undefined;
};
const getSemverExpressions = sources => {
return sources
.map(source => getSemverExpression(source))
.filter(expression => expression);
};
const validateSemverExpressions = semverExpressions => {
for (const expression of semverExpressions) {
if (semver_1.default.validRange(expression) === null) {
const message = `Invalid semver expression (${expression}) found in ` +
`one of your contract's imports.`;
throw new Error(message);
}
}
};
// takes an array of versions and an array of semver expressions
// returns a version of the compiler or undefined if none can be found
const findNewestSatisfyingVersion = ({ solcReleases, semverExpressions }) => {
// releases are ordered from newest to oldest
return solcReleases.find(version => {
return semverExpressions.every(expression => semver_1.default.satisfies(version, expression));
});
};
const throwCompilerVersionNotFound = ({ path, semverExpressions }) => {
const message = `Could not find a single version of the Solidity compiler that ` +
`satisfies the following semver expressions obtained from your source ` +
`files' pragma statements: ${semverExpressions.join(" - ")}. ` +
`${os_1.default.EOL}Please check the pragma statements for ${path} and its imports.`;
throw new Error(message);
};
const compileWithPragmaAnalysis = ({ paths, options }) => __awaiter(void 0, void 0, void 0, function* () {
//don't compile if there's yul
const yulPath = paths.find(path => path.endsWith(".yul"));
if (yulPath !== undefined) {
throw new Error(`Paths to compile includes Yul source ${yulPath}. ` +
`Pragma analysis is not supported when compiling Yul.`);
}
const filteredPaths = paths.filter(path => path.endsWith(".sol") || path.endsWith(".json"));
if (filteredPaths.length === 0) {
return { compilations: [] };
}
// construct supplier options for fetching list of solc versions;
// enforce no Docker because listing Docker versions is slow (Docker Hub API
// paginates responses, with >500 pages at time of writing)
const supplierOptions = {
events: options.events,
solcConfig: Object.assign(Object.assign({}, options.compilers.solc), { docker: false })
};
const compilerSupplier = new compilerSupplier_1.CompilerSupplier(supplierOptions);
const { releases } = yield compilerSupplier.list();
// collect sources by the version of the Solidity compiler that they require
const versionsAndSources = {};
for (const path of filteredPaths) {
const source = (yield options.resolver.resolve(path)).body;
const parserVersion = findNewestSatisfyingVersion({
solcReleases: releases,
semverExpressions: [getSemverExpression(source)]
});
if (!parserVersion) {
const m = `Could not find a valid pragma expression in ${path}. To use the ` +
`"pragma" compiler setting your contracts must contain a pragma ` +
`expression.`;
throw new Error(m);
}
// allSources is of the format { [filename]: string }
const { allSources } = yield profiler_1.Profiler.requiredSourcesForSingleFile(options.with({
path,
base_path: options.contracts_directory,
resolver: options.resolver,
compiler: {
name: "solc",
version: parserVersion
},
compilers: {
solc: {
version: parserVersion
}
}
}));
// get an array of all the semver expressions in the sources
const semverExpressions = yield getSemverExpressions(Object.values(allSources));
// this really just validates the expressions from the contracts' imports
// as it has already determined the parser version for each contract
validateSemverExpressions(semverExpressions);
const newestSatisfyingVersion = findNewestSatisfyingVersion({
solcReleases: releases,
semverExpressions
});
if (!newestSatisfyingVersion) {
throwCompilerVersionNotFound({
path,
semverExpressions
});
}
versionsAndSources[newestSatisfyingVersion] = Object.assign(Object.assign({}, versionsAndSources[newestSatisfyingVersion]), allSources);
}
(0, reportSources_1.reportSources)({ paths: filteredPaths, options });
const compilations = [];
for (const compilerVersion in versionsAndSources) {
const compilationOptions = {
compilers: (0, cloneDeep_1.default)(options.compilers)
};
compilationOptions.compilers.solc.version = compilerVersion;
const config = config_1.default.default().with(compilationOptions);
const compilation = yield (0, run_1.run)(versionsAndSources[compilerVersion], config);
if (compilation && Object.keys(compilation.contracts).length > 0) {
compilations.push(compilation);
}
}
return { compilations };
});
exports.compileWithPragmaAnalysis = compileWithPragmaAnalysis;
//# sourceMappingURL=compileWithPragmaAnalysis.js.map