diff --git a/packages/artifactor/test/contracts.js b/packages/artifactor/test/contracts.js index 80ae23dfe99..9103885d862 100644 --- a/packages/artifactor/test/contracts.js +++ b/packages/artifactor/test/contracts.js @@ -6,10 +6,10 @@ const path = require("path"); const fs = require("fs"); const Config = require("@truffle/config"); const requireNoCache = require("require-nocache")(module); -const Compile = require("@truffle/compile-solidity/legacy"); +const { Compile } = require("@truffle/compile-solidity"); const Ganache = require("ganache-core"); const Web3 = require("web3"); -const { promisify } = require("util"); +const { Shims } = require("@truffle/compile-common"); const tmp = require("tmp"); tmp.setGracefulCleanup(); @@ -19,14 +19,14 @@ describe("artifactor + require", () => { const web3 = new Web3(); web3.setProvider(provider); - before(() => web3.eth.net.getId().then((id) => (networkID = id))); + before(() => web3.eth.net.getId().then(id => (networkID = id))); before(async function () { this.timeout(20000); const sourcePath = path.join(__dirname, "Example.sol"); const sources = { - Example: fs.readFileSync(sourcePath, { encoding: "utf8" }), + Example: fs.readFileSync(sourcePath, { encoding: "utf8" }) }; const options = { @@ -37,22 +37,26 @@ describe("artifactor + require", () => { settings: { optimizer: { enabled: false, - runs: 200, - }, - }, - }, + runs: 200 + } + } + } }, logger: { log(stringToLog) { this.loggedStuff = this.loggedStuff + stringToLog; }, - loggedStuff: "", - }, + loggedStuff: "" + } }; config = Config.default().with(options); // Compile first - const result = await promisify(Compile)(sources, config); + const { compilations } = await Compile.sources({ + sources, + options: config + }); + const { contracts } = compilations[0]; // Clean up after solidity. Only remove solidity's listener, // which happens to be the first. @@ -61,14 +65,19 @@ describe("artifactor + require", () => { process.listeners("uncaughtException")[0] || (() => {}) ); - const compiled = Schema.normalize(result["Example"]); + const exampleContract = contracts.find( + contract => contract.contractName === "Example" + ); + const compiled = Schema.normalize( + Shims.NewToLegacy.forContract(exampleContract) + ); abi = compiled.abi; bytecode = compiled.bytecode; // Setup const tempDir = tmp.dirSync({ unsafeCleanup: true, - prefix: "tmp-test-contract-", + prefix: "tmp-test-contract-" }); const expectedFilepath = path.join(tempDir.name, "Example.json"); @@ -82,9 +91,9 @@ describe("artifactor + require", () => { bytecode, networks: { [`${networkID}`]: { - address: "0xe6e1652a0397e078f434d6dda181b218cfd42e01", - }, - }, + address: "0xe6e1652a0397e078f434d6dda181b218cfd42e01" + } + } }) .then(() => { const json = requireNoCache(expectedFilepath); @@ -94,11 +103,11 @@ describe("artifactor + require", () => { }); before(() => - web3.eth.getAccounts().then((_accounts) => { + web3.eth.getAccounts().then(_accounts => { accounts = _accounts; Example.defaults({ - from: accounts[0], + from: accounts[0] }); }) ); @@ -108,33 +117,33 @@ describe("artifactor + require", () => { assert(transactionHash, "transactionHash should be non-empty"); })); - it("should get and set values via methods and get values via .call", (done) => { + it("should get and set values via methods and get values via .call", done => { let example; Example.new(1, { gas: 3141592 }) - .then((instance) => { + .then(instance => { example = instance; return example.value.call(); }) - .then((value) => { + .then(value => { assert.equal(value.valueOf(), 1, "Starting value should be 1"); return example.setValue(5); }) .then(() => example.value.call()) - .then((value) => { + .then(value => { assert.equal(parseInt(value), 5, "Ending value should be five"); }) .then(done) .catch(done); }); - it("shouldn't synchronize constant functions", (done) => { + it("shouldn't synchronize constant functions", done => { let example; Example.new(5, { gas: 3141592 }) - .then((instance) => { + .then(instance => { example = instance; return example.getValue(); }) - .then((value) => { + .then(value => { assert.equal( value.valueOf(), 5, @@ -145,26 +154,26 @@ describe("artifactor + require", () => { .catch(done); }); - it("should allow BigNumbers as input parameters, and not confuse them as transaction objects", (done) => { + it("should allow BigNumbers as input parameters, and not confuse them as transaction objects", done => { // BigNumber passed on new() let example = null; Example.new("30", { gas: 3141592 }) - .then((instance) => { + .then(instance => { example = instance; return example.value.call(); }) - .then((value) => { + .then(value => { assert.equal(parseInt(value), 30, "Starting value should be 30"); // BigNumber passed in a transaction. return example.setValue("25", { gas: 3141592 }); }) .then(() => example.value.call()) - .then((value) => { + .then(value => { assert.equal(parseInt(value), 25, "Ending value should be twenty-five"); // BigNumber passed in a call. return example.parrot.call(865); }) - .then((parrotValue) => { + .then(parrotValue => { assert.equal( parseInt(parrotValue), 865, @@ -175,10 +184,10 @@ describe("artifactor + require", () => { .catch(done); }); - it("should return transaction hash, logs and receipt when using synchronised transactions", (done) => { + it("should return transaction hash, logs and receipt when using synchronised transactions", done => { let example = null; Example.new("1", { gas: 3141592 }) - .then((instance) => { + .then(instance => { example = instance; return example.triggerEvent(); }) @@ -213,17 +222,17 @@ describe("artifactor + require", () => { it("should trigger the fallback function when calling sendTransaction()", () => { let example = null; return Example.new("1", { gas: 3141592 }) - .then((instance) => { + .then(instance => { example = instance; return example.fallbackTriggered(); }) - .then((triggered) => { + .then(triggered => { assert( triggered === false, "Fallback should not have been triggered yet" ); return example.sendTransaction({ - value: web3.utils.toWei("1", "ether"), + value: web3.utils.toWei("1", "ether") }); }) .then( @@ -235,7 +244,7 @@ describe("artifactor + require", () => { }) ) ) - .then((balance) => { + .then(balance => { assert(balance === web3.utils.toWei("1", "ether")); }); }); @@ -243,11 +252,11 @@ describe("artifactor + require", () => { it("should trigger the fallback function when calling send() (shorthand notation)", () => { let example = null; return Example.new("1", { gas: 3141592 }) - .then((instance) => { + .then(instance => { example = instance; return example.fallbackTriggered(); }) - .then((triggered) => { + .then(triggered => { assert( triggered === false, "Fallback should not have been triggered yet" @@ -263,12 +272,12 @@ describe("artifactor + require", () => { }) ) ) - .then((balance) => { + .then(balance => { assert(balance === web3.utils.toWei("1", "ether")); }); }); - it("errors when setting an invalid provider", (done) => { + it("errors when setting an invalid provider", done => { try { Example.setProvider(null); assert.fail("setProvider() should have thrown an error"); @@ -278,15 +287,15 @@ describe("artifactor + require", () => { done(); }); - it("creates a network object when an address is set if no network specified", (done) => { + it("creates a network object when an address is set if no network specified", done => { const NewExample = contract({ abi, - bytecode, + bytecode }); NewExample.setProvider(provider); NewExample.defaults({ - from: accounts[0], + from: accounts[0] }); assert.equal(NewExample.network_id, null); @@ -307,29 +316,29 @@ describe("artifactor + require", () => { .catch(done); }); - it("doesn't error when calling .links() or .events() with no network configuration", (done) => { + it("doesn't error when calling .links() or .events() with no network configuration", done => { const eventABI = { anonymous: false, inputs: [ { indexed: true, name: "nameHash", - type: "bytes32", + type: "bytes32" }, { indexed: true, name: "releaseHash", - type: "bytes32", - }, + type: "bytes32" + } ], name: "PackageRelease", - type: "event", + type: "event" }; const MyContract = contract({ contractName: "MyContract", abi: [eventABI], - bytecode: "0x12345678", + bytecode: "0x12345678" }); MyContract.setNetwork(5); diff --git a/packages/box/test/box.js b/packages/box/test/box.js index b65b3984336..c59eee98d12 100644 --- a/packages/box/test/box.js +++ b/packages/box/test/box.js @@ -114,7 +114,7 @@ describe("@truffle/box Box", () => { utils.downloadBox.restore(); }); - it("calls the cleanup function if it is available", function(done) { + it("calls the cleanup function if it is available", function (done) { Box.unbox(TRUFFLE_BOX_DEFAULT, destination, {}, config).catch(() => { assert(cleanupCallback.called); done(); diff --git a/packages/compile-common/package.json b/packages/compile-common/package.json index 2f977663177..51f9bddf641 100644 --- a/packages/compile-common/package.json +++ b/packages/compile-common/package.json @@ -8,13 +8,17 @@ "scripts": { "build": "tsc", "prepare": "yarn build", + "test": "mocha -r ts-node/register test/*.ts", "watch": "tsc -w" }, "devDependencies": { "@types/fs-extra": "^8.1.0", + "@types/mocha": "^5.2.7", "typescript": "3.9.6" }, "dependencies": { + "mocha": "8.0.1", + "ts-node": "8.10.2", "@truffle/contract-schema": "^3.2.5", "@truffle/resolver": "^6.0.18", "fs-extra": "^8.1.0" diff --git a/packages/compile-common/src/index.ts b/packages/compile-common/src/index.ts index ec22da5bb0f..154dfee6547 100644 --- a/packages/compile-common/src/index.ts +++ b/packages/compile-common/src/index.ts @@ -1,2 +1,3 @@ -import { Profiler } from "./profiler"; -export { Profiler }; +export { Profiler } from "./profiler"; +export * as Shims from "./shims"; +export * from "./types"; diff --git a/packages/workflow-compile/shims.js b/packages/compile-common/src/shims/LegacyToNew.ts similarity index 64% rename from packages/workflow-compile/shims.js rename to packages/compile-common/src/shims/LegacyToNew.ts index cb72f42dd65..66531a730ab 100644 --- a/packages/workflow-compile/shims.js +++ b/packages/compile-common/src/shims/LegacyToNew.ts @@ -1,25 +1,11 @@ -const { multiPromisify } = require("./utils"); +import { Bytecode, CompiledContract, LinkReference } from "../types"; -function shimLegacy(method) { - return async config => { - const compile = multiPromisify(method); - - const [contracts, sourceIndexes, compilerInfo] = await compile(config); - - return { - contracts: shimContracts(contracts), - sourceIndexes, - compilerInfo - }; - }; -} - -function shimContracts(contracts) { +export function forContracts(contracts: any[]): CompiledContract[] { // convert to list - return Object.values(contracts).map(shimContract); + return Object.values(contracts).map(forContract); } -function shimContract(contract) { +export function forContract(contract: any): CompiledContract { const { contractName, contract_name, @@ -51,8 +37,8 @@ function shimContract(contract) { ast, abi, metadata, - bytecode: shimBytecode(bytecode), - deployedBytecode: shimBytecode(deployedBytecode), + bytecode: forBytecode(bytecode), + deployedBytecode: forBytecode(deployedBytecode), compiler, devdoc, userdoc, @@ -62,7 +48,7 @@ function shimContract(contract) { }; } -function shimBytecode(bytecode) { +export function forBytecode(bytecode: string): Bytecode { if (!bytecode) { return undefined; } @@ -70,7 +56,7 @@ function shimBytecode(bytecode) { return bytecode; } - const linkReferences = []; + const linkReferences: LinkReference[] = []; const bytes = bytecode .slice(2) // remove 0x prefix @@ -93,10 +79,3 @@ function shimBytecode(bytecode) { return { bytes, linkReferences }; } - -module.exports = { - shimLegacy, - shimContracts, - shimContract, - shimBytecode -}; diff --git a/packages/compile-common/src/shims/NewToLegacy.ts b/packages/compile-common/src/shims/NewToLegacy.ts new file mode 100644 index 00000000000..88d54b726c1 --- /dev/null +++ b/packages/compile-common/src/shims/NewToLegacy.ts @@ -0,0 +1,87 @@ +import { LinkReference, CompiledContract } from "../types"; + +export function forContract(contract: CompiledContract): any { + const { + contractName, + sourcePath, + source, + sourceMap, + deployedSourceMap, + legacyAST, + ast, + abi, + metadata, + bytecode, + deployedBytecode, + compiler, + devdoc, + userdoc, + immutableReferences + } = contract; + + return { + contract_name: contractName, + sourcePath, + source, + sourceMap, + deployedSourceMap, + legacyAST, + ast, + abi, + metadata, + bytecode: forBytecode(bytecode), + deployedBytecode: forBytecode(deployedBytecode), + unlinked_binary: forBytecode(bytecode), + compiler, + devdoc, + userdoc, + immutableReferences + }; +} + +export function forBytecode(bytecode: any): any { + if (!bytecode) { + return bytecode; + } + if (typeof bytecode === "string") { + return bytecode; + } + + let { bytes, linkReferences } = bytecode; + + linkReferences = linkReferences || []; + + // inline link references - start by flattening the offsets + const flattenedLinkReferences = linkReferences + // map each link ref to array of link refs with only one offset + .map(({ offsets, length, name }: LinkReference) => + offsets.map(offset => ({ offset, length, name })) + ) + // flatten + .reduce((a: object[], b: object[]) => [...a, ...b], []); + + // then overwite bytes with link reference + bytes = flattenedLinkReferences.reduce( + ( + bytes: string, + { offset, name, length }: { offset: number; name: string; length: number } + ) => { + // length is a byte offset + const characterLength = length * 2; + + let linkId = `__${name.slice(0, characterLength - 2)}`; + while (linkId.length < characterLength) { + linkId += "_"; + } + + const start = offset * 2; + + return `${bytes.substring(0, start)}${linkId}${bytes.substring( + start + characterLength + )}`; + }, + bytes + ); + + return `0x${bytes}`; +} diff --git a/packages/compile-common/src/shims/index.ts b/packages/compile-common/src/shims/index.ts new file mode 100644 index 00000000000..925fc69e483 --- /dev/null +++ b/packages/compile-common/src/shims/index.ts @@ -0,0 +1,2 @@ +export * as LegacyToNew from "./LegacyToNew"; +export * as NewToLegacy from "./NewToLegacy"; diff --git a/packages/compile-common/src/types.ts b/packages/compile-common/src/types.ts new file mode 100644 index 00000000000..8ae6a049a36 --- /dev/null +++ b/packages/compile-common/src/types.ts @@ -0,0 +1,65 @@ +import { ContractObject } from "@truffle/contract-schema/spec"; + +export type Compilation = { + sourceIndexes: string[]; + contracts: CompiledContract[]; + compiler: { + name: string | undefined; + version: string | undefined; + }; +}; + +export interface CompilerResult { + compilations: Compilation[]; +} + +export interface Bytecode { + bytes: string; + linkReferences: LinkReference[]; +} + +export interface LinkReference { + offsets: number[]; + name: string | null; // this will be the contractName of the library or some other identifier + length: number; +} + +export type CompiledContract = { + contractName: string; + sourcePath: string; + source: string; + sourceMap: string; + deployedSourceMap: string; + legacyAST: object; + ast: object; + abi: object[]; + metadata: string; + bytecode: Bytecode; + deployedBytecode: Bytecode; + compiler: { + name: string; + version: string; + }; + devdoc: object; + userdoc: object; + immutableReferences: object; + generatedSources: any; + deployedGeneratedSources: any; +}; + +export interface WorkflowCompileResult { + compilations: Compilation[]; + contracts: CompiledContract[]; +} + +export interface Compiler { + all: (options: object) => Promise; + necessary: (options: object) => Promise; + sources: ({ + sources, + options + }: { + sources: object; + options: object; + }) => Promise; +} diff --git a/packages/compile-common/test/shims.ts b/packages/compile-common/test/shims.ts new file mode 100644 index 00000000000..a3d5fcaae58 --- /dev/null +++ b/packages/compile-common/test/shims.ts @@ -0,0 +1,134 @@ +import assert from "assert"; +import { Shims, Bytecode } from "../src"; + +describe("Shims.NewToLegacy.forBytecode", () => { + it("handles undefined", () => { + assert.equal(Shims.NewToLegacy.forBytecode(undefined), undefined); + }); + + it("prepends 0x", () => { + const bytes = "ffffffff"; + + assert.equal( + Shims.NewToLegacy.forBytecode({ bytes, linkReferences: [] }), + `0x${bytes}` + ); + }); + + it("inlines an external link reference into underscores format", () => { + const bytecode = { + // 0 1 2 3 4 5 6 7 8 9 + bytes: "00000000000000000000", + linkReferences: [ + { + offsets: [1], + length: 8, + name: "hello" + } + ] + }; + + // 0 1 2 3 4 5 6 7 8 9 + const expected = "0x00__hello_________00"; + + assert.equal(Shims.NewToLegacy.forBytecode(bytecode), expected); + }); + + it("inlines a link reference with multiple offsets", () => { + const bytecode = { + // 0 1 2 3 4 5 6 7 8 9 + bytes: "00000000000000000000", + linkReferences: [ + { + offsets: [0, 5], + length: 4, + name: "hi" + } + ] + }; + + // 0 1 2 3 4 5 6 7 8 9 + const expected = "0x__hi____00__hi____00"; + + assert.equal(Shims.NewToLegacy.forBytecode(bytecode), expected); + }); + + it("inlines two different link references", () => { + const bytecode = { + // 0 1 2 3 4 5 6 7 8 9 + bytes: "00000000000000000000", + linkReferences: [ + { + offsets: [0], + length: 4, + name: "hi" + }, + { + offsets: [5], + length: 4, + name: "there" + } + ] + }; + + // 0 1 2 3 4 5 6 7 8 9 + const expected = "0x__hi____00__there_00"; + + assert.equal(Shims.NewToLegacy.forBytecode(bytecode), expected); + }); +}); + +describe("Shims.LegacyToNew.forBytecode", () => { + it("removes 0x", function () { + const bytes = "ffffffff"; + const expected = { + bytes, + linkReferences: [] + } as Bytecode; + + assert.deepEqual(Shims.LegacyToNew.forBytecode(`0x${bytes}`), expected); + }); + + it("externalizes a link reference in underscores format", function () { + // 0 1 2 3 4 5 6 7 8 9 + const bytecode = "0x00__hello_________00"; + + const expected = { + // 0 1 2 3 4 5 6 7 8 9 + bytes: "00000000000000000000", + linkReferences: [ + { + offsets: [1], + length: 8, + name: "hello" + } + ] + }; + + assert.deepEqual(Shims.LegacyToNew.forBytecode(bytecode), expected); + }); + + it("externalizes two different link references", function () { + // 0 1 2 3 4 5 6 7 8 9 + const bytecode = "0x__hi____00__there_00"; + + const expected = { + // 0 1 2 3 4 5 6 7 8 9 + bytes: "00000000000000000000", + linkReferences: [ + { + offsets: [0], + length: 4, + name: "hi" + }, + { + offsets: [5], + length: 4, + name: "there" + } + ] + }; + + assert.deepEqual(Shims.LegacyToNew.forBytecode(bytecode), expected); + }); +}); diff --git a/packages/compile-common/tsconfig.json b/packages/compile-common/tsconfig.json index 119715bd149..2b9eda32c98 100644 --- a/packages/compile-common/tsconfig.json +++ b/packages/compile-common/tsconfig.json @@ -12,6 +12,7 @@ "paths": { }, "rootDir": ".", + "types": ["mocha"], "typeRoots": [ "../../node_modules/@types/fs-extra" ] diff --git a/packages/compile-solidity/index.js b/packages/compile-solidity/index.js index db661f405ef..44256a73bc9 100644 --- a/packages/compile-solidity/index.js +++ b/packages/compile-solidity/index.js @@ -1,3 +1,128 @@ -// LATER -// module.exports = require("./new"); -module.exports = require("./legacy"); +const debug = require("debug")("compile"); // eslint-disable-line no-unused-vars +const path = require("path"); +const expect = require("@truffle/expect"); +const findContracts = require("@truffle/contract-sources"); +const Config = require("@truffle/config"); +const Profiler = require("./profiler"); +const CompilerSupplier = require("./compilerSupplier"); +const { run } = require("./run"); +const { normalizeOptions } = require("./legacy/options"); + +const Compile = { + // this takes an object with keys being the name and values being source + // material as well as an options object + async sources({ sources, options }) { + const compilation = await run(sources, normalizeOptions(options)); + return compilation.contracts.length > 0 + ? { compilations: [compilation] } + : { compilations: [] }; + }, + + async all(options) { + const paths = [ + ...new Set([ + ...(await findContracts(options.contracts_directory)), + ...(options.files || []) + ]) + ]; + + return await Compile.sourcesWithDependencies({ + paths, + options: Config.default().merge(options) + }); + }, + + async necessary(options) { + options.logger = options.logger || console; + + const paths = await Profiler.updated(options); + + return await Compile.sourcesWithDependencies({ + paths, + options: Config.default().merge(options) + }); + }, + + // this takes an array of paths and options + async sourcesWithDependencies({ paths, options }) { + options.logger = options.logger || console; + options.contracts_directory = options.contracts_directory || process.cwd(); + + expect.options(options, [ + "working_directory", + "contracts_directory", + "resolver" + ]); + + const config = Config.default().merge(options); + const { allSources, compilationTargets } = await Profiler.requiredSources( + config.with({ + paths, + base_path: options.contracts_directory, + resolver: options.resolver + }) + ); + + const hasTargets = compilationTargets.length; + + hasTargets + ? Compile.display(compilationTargets, options) + : Compile.display(allSources, options); + + // when there are no sources, don't call run + if (Object.keys(allSources).length === 0) { + return { compilations: [] }; + } + + options.compilationTargets = compilationTargets; + const { sourceIndexes, contracts, compiler } = await run( + allSources, + normalizeOptions(options) + ); + const { name, version } = compiler; + // returns CompilerResult - see @truffle/compile-common + return contracts.length > 0 + ? { + compilations: [ + { + sourceIndexes, + contracts, + compiler: { name, version } + } + ] + } + : { compilations: [] }; + }, + + display(paths, options) { + if (options.quiet !== true) { + if (!Array.isArray(paths)) { + paths = Object.keys(paths); + } + + const blacklistRegex = /^truffle\//; + + const sources = paths + .sort() + .map(contract => { + if (path.isAbsolute(contract)) { + contract = + "." + + path.sep + + path.relative(options.working_directory, contract); + } + if (contract.match(blacklistRegex)) return; + return contract; + }) + .filter(contract => contract); + options.events.emit("compile:sourcesToCompile", { + sourceFileNames: sources + }); + } + } +}; + +module.exports = { + Compile, + CompilerSupplier +}; diff --git a/packages/compile-solidity/legacy/index.js b/packages/compile-solidity/legacy/index.js index d66c3455028..177ea37009c 100644 --- a/packages/compile-solidity/legacy/index.js +++ b/packages/compile-solidity/legacy/index.js @@ -3,7 +3,7 @@ const path = require("path"); const expect = require("@truffle/expect"); const findContracts = require("@truffle/contract-sources"); const Config = require("@truffle/config"); -const Profiler = require("../profiler"); +const { Profiler } = require("@truffle/compile-common"); const CompilerSupplier = require("../compilerSupplier"); const { run } = require("../run"); const { normalizeOptions } = require("./options"); @@ -19,12 +19,16 @@ const { shimOutput } = require("./shims"); // quiet: false, // logger: console // } -const compile = function(sources, options, callback) { +const compile = function (sources, options, callback) { if (typeof options === "function") { callback = options; options = {}; } + if (Object.keys(sources).length === 0) { + return callback(null, [{}, []]); + } + // account for legacy settings options = normalizeOptions(options); @@ -37,8 +41,8 @@ const compile = function(sources, options, callback) { // contracts_directory: String. Directory where .sol files can be found. // quiet: Boolean. Suppress output. Defaults to false. // strict: Boolean. Return compiler warnings as errors. Defaults to false. -compile.all = function(options, callback) { - findContracts(options.contracts_directory, function(err, files) { +compile.all = function (options, callback) { + findContracts(options.contracts_directory, function (err, files) { if (err) return callback(err); options.paths = files; @@ -52,22 +56,22 @@ compile.all = function(options, callback) { // in the build directory to see what needs to be compiled. // quiet: Boolean. Suppress output. Defaults to false. // strict: Boolean. Return compiler warnings as errors. Defaults to false. -compile.necessary = function(options, callback) { +compile.necessary = function (options, callback) { options.logger = options.logger || console; - Profiler.updated(options, function(err, updated) { - if (err) return callback(err); - - if (updated.length === 0 && options.quiet !== true) { - return callback(null, [], {}); - } + Profiler.updated(options) + .then(updated => { + if (updated.length === 0 && options.quiet !== true) { + return callback(null, [], {}); + } - options.paths = updated; - compile.with_dependencies(options, callback); - }); + options.paths = updated; + compile.with_dependencies(options, callback); + }) + .catch(callback); }; -compile.with_dependencies = function(options, callback) { +compile.with_dependencies = function (options, callback) { var self = this; options.logger = options.logger || console; @@ -103,7 +107,7 @@ compile.with_dependencies = function(options, callback) { ); }; -compile.calculateCompiledSources = function(paths, options) { +compile.calculateCompiledSources = function (paths, options) { if (!Array.isArray(paths)) paths = Object.keys(paths); const blacklistRegex = /^truffle\//; diff --git a/packages/compile-solidity/legacy/shims.js b/packages/compile-solidity/legacy/shims.js index 07b607d1989..1e0f2efc0c3 100644 --- a/packages/compile-solidity/legacy/shims.js +++ b/packages/compile-solidity/legacy/shims.js @@ -1,101 +1,17 @@ -function shimOutput({ contracts: list, sourceIndexes, compilerInfo }) { +const { Shims } = require("@truffle/compile-common"); + +function shimOutput({ contracts: list, sourceIndexes, compiler }) { const contracts = list // get old format - .map(contract => shimContract(contract)) + .map(contract => Shims.NewToLegacy.forContract(contract)) // get pair .map(contract => ({ [contract.contract_name]: contract })) // merge pairs .reduce((a, b) => Object.assign({}, a, b), {}); - return [contracts, sourceIndexes, compilerInfo]; -} - -function shimContract(contract) { - const { - contractName, - sourcePath, - source, - sourceMap, - deployedSourceMap, - legacyAST, - ast, - abi, - metadata, - bytecode, - deployedBytecode, - compiler, - devdoc, - userdoc, - immutableReferences, - generatedSources, - deployedGeneratedSources - } = contract; - - return { - contract_name: contractName, - sourcePath, - source, - sourceMap, - deployedSourceMap, - legacyAST, - ast, - abi, - metadata, - bytecode: shimBytecode(bytecode), - deployedBytecode: shimBytecode(deployedBytecode), - unlinked_binary: shimBytecode(bytecode), - compiler, - devdoc, - userdoc, - immutableReferences, - generatedSources, - deployedGeneratedSources - }; -} - -function shimBytecode(bytecode) { - if (!bytecode) { - return bytecode; - } - if (typeof bytecode === "string") { - return bytecode; - } - - let { bytes, linkReferences } = bytecode; - - linkReferences = linkReferences || []; - - // inline link references - start by flattening the offsets - const flattenedLinkReferences = linkReferences - // map each link ref to array of link refs with only one offset - .map(({ offsets, length, name }) => - offsets.map(offset => ({ offset, length, name })) - ) - // flatten - .reduce((a, b) => [...a, ...b], []); - - // then overwite bytes with link reference - bytes = flattenedLinkReferences.reduce((bytes, { offset, name, length }) => { - // length is a byte offset - const characterLength = length * 2; - - let linkId = `__${name.slice(0, characterLength - 2)}`; - while (linkId.length < characterLength) { - linkId += "_"; - } - - const start = offset * 2; - - return `${bytes.substring(0, start)}${linkId}${bytes.substring( - start + characterLength - )}`; - }, bytes); - - return `0x${bytes}`; + return [contracts, sourceIndexes, compiler]; } module.exports = { - shimOutput, - shimContract, - shimBytecode + shimOutput }; diff --git a/packages/compile-solidity/new/index.js b/packages/compile-solidity/new/index.js deleted file mode 100644 index 05f13fe6408..00000000000 --- a/packages/compile-solidity/new/index.js +++ /dev/null @@ -1,123 +0,0 @@ -const debug = require("debug")("compile:new"); // eslint-disable-line no-unused-vars -const path = require("path"); -const expect = require("@truffle/expect"); -const findContracts = require("@truffle/contract-sources"); -const Config = require("@truffle/config"); -const Profiler = require("../profiler"); -const CompilerSupplier = require("../compilerSupplier"); -const { run } = require("../run"); -const { normalizeOptions } = require("../legacy/options"); - -// Most basic of the compile commands. Takes a hash of sources, where -// the keys are file or module paths and the values are the bodies of -// the contracts. Does not evaulate dependencies that aren't already given. -// -// Default options: -// { -// strict: false, -// quiet: false, -// logger: console -// } -const compile = async function(sources, options) { - return await run(sources, normalizeOptions(options)); -}; - -// contracts_directory: String. Directory where .sol files can be found. -// quiet: Boolean. Suppress output. Defaults to false. -// strict: Boolean. Return compiler warnings as errors. Defaults to false. -// files: Array. Explicit files to compile besides detected sources -compile.all = async function(options) { - const paths = [ - ...new Set([ - ...(await findContracts(options.contracts_directory)), - ...(options.files || []) - ]) - ]; - - return await compile.with_dependencies( - Config.default() - .merge(options) - .merge({ paths }) - ); -}; - -// contracts_directory: String. Directory where .sol files can be found. -// build_directory: String. Optional. Directory where .sol.js files can be found. Only required if `all` is false. -// all: Boolean. Compile all sources found. Defaults to true. If false, will compare sources against built files -// in the build directory to see what needs to be compiled. -// quiet: Boolean. Suppress output. Defaults to false. -// strict: Boolean. Return compiler warnings as errors. Defaults to false. -// files: Array. Explicit files to compile besides detected sources -compile.necessary = async function(options) { - options.logger = options.logger || console; - - const paths = await Profiler.updated(options); - - return await compile.with_dependencies( - Config.default() - .merge(options) - .merge({ paths }) - ); -}; - -compile.with_dependencies = async function(options) { - options.logger = options.logger || console; - options.contracts_directory = options.contracts_directory || process.cwd(); - - expect.options(options, [ - "paths", - "working_directory", - "contracts_directory", - "resolver" - ]); - - var config = Config.default().merge(options); - - const { allSources, required } = await new Promise((accept, reject) => { - Profiler.required_sources( - config.with({ - paths: options.paths, - base_path: options.contracts_directory, - resolver: options.resolver - }), - (err, allSources, required) => { - if (err) { - return reject(err); - } - - return accept({ allSources, required }); - } - ); - }); - - var hasTargets = required.length; - - hasTargets - ? this.display(required, options) - : this.display(allSources, options); - - options.compilationTargets = required; - return await compile(allSources, options); -}; - -compile.display = function(paths, options) { - if (options.quiet !== true) { - if (!Array.isArray(paths)) { - paths = Object.keys(paths); - } - - const blacklistRegex = /^truffle\//; - - paths.sort().forEach(contract => { - if (path.isAbsolute(contract)) { - contract = - "." + path.sep + path.relative(options.working_directory, contract); - } - if (contract.match(blacklistRegex)) return; - options.logger.log("> Compiling " + contract); - }); - } -}; - -compile.CompilerSupplier = CompilerSupplier; -module.exports = compile; diff --git a/packages/compile-solidity/profiler/index.js b/packages/compile-solidity/profiler/index.js index 8a28747bb2e..64a8e9ef533 100644 --- a/packages/compile-solidity/profiler/index.js +++ b/packages/compile-solidity/profiler/index.js @@ -5,35 +5,26 @@ const debug = require("debug")("compile:profiler"); const Common = require("@truffle/compile-common"); const { loadParser } = require("./loadParser"); const { shouldIncludePath } = require("./shouldIncludePath"); -const { polycallbackify } = require("./polycallbackify"); module.exports = { - updated: polycallbackify({ - asyncFunction: async options => { - const profiler = await new Common.Profiler({}); - return await profiler.updated(options); - } - }), + updated: async options => { + const profiler = await new Common.Profiler({}); + return await profiler.updated(options); + }, // Returns the minimal set of sources to pass to solc as compilations targets, // as well as the complete set of sources so solc can resolve the comp targets' imports. - required_sources: polycallbackify({ - asyncFunction: async options => { - // get parser - const parseImports = await loadParser(options); + requiredSources: async options => { + // get parser + const parseImports = await loadParser(options); - // generate profiler - const profiler = new Common.Profiler({ - parseImports, - shouldIncludePath - }); + // generate profiler + const profiler = new Common.Profiler({ + parseImports, + shouldIncludePath + }); - // invoke profiler - return await profiler.requiredSources(options); - }, - resultArgs: ({ allSources, compilationTargets }) => [ - allSources, - compilationTargets - ] - }) + // invoke profiler + return await profiler.requiredSources(options); + } }; diff --git a/packages/compile-solidity/profiler/polycallbackify.js b/packages/compile-solidity/profiler/polycallbackify.js deleted file mode 100644 index bfdf8874584..00000000000 --- a/packages/compile-solidity/profiler/polycallbackify.js +++ /dev/null @@ -1,31 +0,0 @@ -// convert an async function to one that maybe takes a callback -function polycallbackify({ - asyncFunction, - determineArgs = (options, callback) => ({ - args: [options], - callback - }), - resultArgs = result => [result] -}) { - return async (...rawArgs) => { - const { args, callback } = determineArgs(...rawArgs); - - try { - const result = await asyncFunction(...args); - - if (callback) { - callback(null, ...resultArgs(result)); - } else { - return result; - } - } catch (error) { - if (callback) { - callback(error); - } else { - throw error; - } - } - }; -} - -module.exports = { polycallbackify }; diff --git a/packages/compile-solidity/run.js b/packages/compile-solidity/run.js index 43f5bb837e5..1a7888e2abc 100644 --- a/packages/compile-solidity/run.js +++ b/packages/compile-solidity/run.js @@ -1,30 +1,31 @@ const debug = require("debug")("compile:run"); // eslint-disable-line no-unused-vars const OS = require("os"); const semver = require("semver"); - const CompileError = require("./compileerror"); const CompilerSupplier = require("./compilerSupplier"); +// this function returns a Compilation - legacy/index.js and ./index.js +// both check to make sure rawSources exist before calling this method +// however, there is a check here that returns null if no sources exist async function run(rawSources, options) { if (Object.keys(rawSources).length === 0) { - return { - contracts: [], - sourceIndexes: [], - compilerInfo: undefined - }; + return null; } + // Ensure sources have operating system independent paths // i.e., convert backslashes to forward slashes; things like C: are left intact. const { sources, targets, originalSourcePaths } = collectSources( rawSources, options.compilationTargets ); + // construct solc compiler input const compilerInput = prepareCompilerInput({ sources, targets, settings: options.compilers.solc.settings }); + // perform compilation const { compilerOutput, solcVersion } = await invokeCompiler({ compilerInput, @@ -51,6 +52,7 @@ async function run(rawSources, options) { } // success case + // returns Compilation - see @truffle/compile-common return { sourceIndexes: processSources({ compilerOutput, @@ -62,7 +64,7 @@ async function run(rawSources, options) { solcVersion, originalSourcePaths }), - compilerInfo: { + compiler: { name: "solc", version: solcVersion } diff --git a/packages/compile-solidity/test/compilerSupplier/index.js b/packages/compile-solidity/test/compilerSupplier/index.js index 9a09c81c9d2..f1fbc19de28 100644 --- a/packages/compile-solidity/test/compilerSupplier/index.js +++ b/packages/compile-solidity/test/compilerSupplier/index.js @@ -42,7 +42,7 @@ describe("CompilerSupplier", () => { }); }); - describe(`when the version is set to a specific word I can't write here`, () => { + describe(`using the setting below (n'ative)`, () => { // HACK: Because of how the test script is set up, the word 'native' cannot be a // part of the it/describe strings above and below beforeEach(() => { diff --git a/packages/compile-solidity/test/legacy/shims.js b/packages/compile-solidity/test/legacy/shims.js deleted file mode 100644 index 43b71871eda..00000000000 --- a/packages/compile-solidity/test/legacy/shims.js +++ /dev/null @@ -1,77 +0,0 @@ -const { assert } = require("chai"); - -const { shimBytecode } = require("@truffle/compile-solidity/legacy/shims"); - -describe("shimBytecode", () => { - it("handles undefined", function() { - assert.equal(shimBytecode(undefined), undefined); - }); - - it("prepends 0x", function() { - const bytes = "ffffffff"; - - assert.equal(shimBytecode({ bytes }), `0x${bytes}`); - }); - - it("inlines an external link reference into underscores format", function() { - const bytecode = { - // 0 1 2 3 4 5 6 7 8 9 - bytes: "00000000000000000000", - linkReferences: [ - { - offsets: [1], - length: 8, - name: "hello" - } - ] - }; - - // 0 1 2 3 4 5 6 7 8 9 - const expected = "0x00__hello_________00"; - - assert.equal(shimBytecode(bytecode), expected); - }); - - it("inlines a link reference with multiple offsets", function() { - const bytecode = { - // 0 1 2 3 4 5 6 7 8 9 - bytes: "00000000000000000000", - linkReferences: [ - { - offsets: [0, 5], - length: 4, - name: "hi" - } - ] - }; - - // 0 1 2 3 4 5 6 7 8 9 - const expected = "0x__hi____00__hi____00"; - - assert.equal(shimBytecode(bytecode), expected); - }); - - it("inlines two different link references", function() { - const bytecode = { - // 0 1 2 3 4 5 6 7 8 9 - bytes: "00000000000000000000", - linkReferences: [ - { - offsets: [0], - length: 4, - name: "hi" - }, - { - offsets: [5], - length: 4, - name: "there" - } - ] - }; - - // 0 1 2 3 4 5 6 7 8 9 - const expected = "0x__hi____00__there_00"; - - assert.equal(shimBytecode(bytecode), expected); - }); -}); diff --git a/packages/compile-solidity/test/test_JSparser.js b/packages/compile-solidity/test/test_JSparser.js index 63baa4378a0..7363495807d 100644 --- a/packages/compile-solidity/test/test_JSparser.js +++ b/packages/compile-solidity/test/test_JSparser.js @@ -1,7 +1,7 @@ const path = require("path"); const assert = require("assert"); const Resolver = require("@truffle/resolver"); -const compile = require("../index"); +const { Compile } = require("@truffle/compile-solidity"); const Config = require("@truffle/config"); describe("JSparser", () => { @@ -22,7 +22,7 @@ describe("JSparser", () => { working_directory: __dirname }; - it("resolves imports when using solcjs parser instead of docker [ @native ]", done => { + it("resolves imports when using solcjs parser instead of docker [ @native ]", async () => { options.compilers.solc.version = "0.4.22"; options.compilers.solc.docker = true; options.contracts_directory = path.join(__dirname, "./sources/v0.4.x"); @@ -31,24 +31,25 @@ describe("JSparser", () => { paths.push(path.join(__dirname, "./sources/v0.4.x/ComplexOrdered.sol")); paths.push(path.join(__dirname, "./sources/v0.4.x/InheritB.sol")); - options.paths = paths; options.resolver = new Resolver(options); const config = Config.default().merge(options); - compile.with_dependencies(config, (err, result) => { - if (err) return done(err); - - // This contract imports / inherits - assert( - result["ComplexOrdered"].contract_name === "ComplexOrdered", - "Should have compiled" - ); - done(); + const { compilations } = await Compile.sourcesWithDependencies({ + paths, + options: config + }); + const contractWasCompiled = compilations.some(compilation => { + return compilation.contracts.some(contract => { + return contract.contractName === "ComplexOrdered"; + }); }); + + // This contract imports / inherits + assert(contractWasCompiled, "Should have compiled"); }).timeout(20000); - it("properly throws when passed an invalid parser value", done => { + it("properly throws when passed an invalid parser value", async () => { options.compilers.solc.parser = "badParser"; options.contracts_directory = path.join(__dirname, "./sources/v0.5.x"); @@ -56,19 +57,18 @@ describe("JSparser", () => { paths.push(path.join(__dirname, "./sources/v0.5.x/ComplexOrdered.sol")); paths.push(path.join(__dirname, "./sources/v0.5.x/InheritB.sol")); - options.paths = paths; options.resolver = new Resolver(options); const config = Config.default().merge(options); - compile.with_dependencies(config, (err, result) => { - if (result) { - assert(false, "should have failed!"); - done(); - } - - assert(err.message.match(/(Unsupported parser)/)); - done(); - }); + try { + await Compile.sourcesWithDependencies({ + paths, + options: config + }); + assert(false, "this call should have failed!"); + } catch (error) { + assert(error.message.match(/(Unsupported parser)/)); + } }).timeout(3000); }); diff --git a/packages/compile-solidity/test/test_ordering.js b/packages/compile-solidity/test/test_ordering.js index 4177a27a2e8..128020ad4a2 100644 --- a/packages/compile-solidity/test/test_ordering.js +++ b/packages/compile-solidity/test/test_ordering.js @@ -1,7 +1,7 @@ const debug = require("debug")("compile:test:test_ordering"); const fs = require("fs"); const path = require("path"); -const compile = require("@truffle/compile-solidity/new"); +const { Compile } = require("@truffle/compile-solidity"); const CompilerSupplier = require("../compilerSupplier"); const assert = require("assert"); const { findOne } = require("./helpers"); @@ -27,7 +27,7 @@ let supplierOptions = { } }; -describe("Compile - solidity ^0.4.0", function() { +describe("Compile - solidity ^0.4.0", function () { this.timeout(5000); // solc let simpleOrderedSource = null; let complexOrderedSource = null; @@ -50,15 +50,15 @@ describe("Compile - solidity ^0.4.0", function() { quiet: true }; - before("get solc", async function() { + before("get solc", async function () { this.timeout(40000); const supplier = new CompilerSupplier(supplierOptions); ({ solc } = await supplier.load()); }); - describe("ABI Ordering", function() { - before("get code", function() { + describe("ABI Ordering", function () { + before("get code", function () { simpleOrderedSource = fs.readFileSync( path.join(__dirname, "./sources/v0.4.x/SimpleOrdered.sol"), "utf-8" @@ -75,7 +75,7 @@ describe("Compile - solidity ^0.4.0", function() { // Ordered.sol's methods are ordered semantically. // solc alphabetizes methods within a file (but interpolates imported methods). - it("Simple ABI should be out of source order when solc compiles it", function() { + it("Simple ABI should be out of source order when solc compiles it", function () { var alphabetic = ["andThird", "second", "theFirst"]; var input = { language: "Solidity", @@ -86,28 +86,31 @@ describe("Compile - solidity ^0.4.0", function() { var result = solc.compile(JSON.stringify(input)); result = JSON.parse(result); var abi = result.contracts["SimpleOrdered.sol"]["SimpleOrdered"].abi.map( - function(item) { + function (item) { return item.name; } ); assert.deepEqual(abi, alphabetic); }); - it("orders the simple ABI", async function() { - var expectedOrder = ["theFirst", "second", "andThird"]; - var sources = {}; + it("orders the simple ABI", async function () { + const expectedOrder = ["theFirst", "second", "andThird"]; + const sources = {}; sources["SimpleOrdered.sol"] = simpleOrderedSource; - const { contracts } = await compile(sources, compileOptions); + const { compilations } = await Compile.sources({ + sources, + options: compileOptions + }); - const SimpleOrdered = findOne("SimpleOrdered", contracts); - var abi = SimpleOrdered.abi.map(({ name }) => name); + const SimpleOrdered = findOne("SimpleOrdered", compilations[0].contracts); + const abi = SimpleOrdered.abi.map(({ name }) => name); assert.deepEqual(abi, expectedOrder); }); // Ordered.sol's methods are ordered semantically. // solc alphabetizes methods within a file (but interpolates imported methods). - it("Complex ABI should be out of source order when solc compiles it", function() { + it("Complex ABI should be out of source order when solc compiles it", function () { var alphabetic = [ "andThird", "second", @@ -131,13 +134,13 @@ describe("Compile - solidity ^0.4.0", function() { debug("result %o", result); var abi = result.contracts["ComplexOrdered.sol"][ "ComplexOrdered" - ].abi.map(function(item) { + ].abi.map(function (item) { return item.name; }); assert.deepEqual(abi, alphabetic); }); - it("orders the complex ABI", async function() { + it("orders the complex ABI", async function () { var expectedOrder = [ "LogB", "LogA", @@ -151,21 +154,30 @@ describe("Compile - solidity ^0.4.0", function() { sources["ComplexOrdered.sol"] = complexOrderedSource; sources["InheritB.sol"] = inheritedSource; - const { contracts } = await compile(sources, compileOptions); - const ComplexOrdered = findOne("ComplexOrdered", contracts); + const { compilations } = await Compile.sources({ + sources, + options: compileOptions + }); + const ComplexOrdered = findOne( + "ComplexOrdered", + compilations[0].contracts + ); var abi = ComplexOrdered.abi.map(({ name }) => name); assert.deepEqual(abi, expectedOrder); }); // Ported from `@truffle/solidity-utils` - it("orders the ABI of a contract without functions", async function() { + it("orders the ABI of a contract without functions", async function () { var sources = {}; // ComplexOrdered.sol includes contract `Empty` sources["ComplexOrdered.sol"] = complexOrderedSource; sources["InheritB.sol"] = inheritedSource; - const { contracts } = await compile(sources, compileOptions); - const Empty = findOne("Empty", contracts); + const { compilations } = await Compile.sources({ + sources, + options: compileOptions + }); + const Empty = findOne("Empty", compilations[0].contracts); assert.equal(Empty.abi.length, 0); }); }); diff --git a/packages/compile-solidity/test/test_supplier.js b/packages/compile-solidity/test/test_supplier.js index dcb9e5107cb..04def6fc05d 100644 --- a/packages/compile-solidity/test/test_supplier.js +++ b/packages/compile-solidity/test/test_supplier.js @@ -3,7 +3,7 @@ const fse = require("fs-extra"); const path = require("path"); const assert = require("assert"); const Resolver = require("@truffle/resolver"); -const compile = require("@truffle/compile-solidity/new"); +const { Compile } = require("@truffle/compile-solidity"); const Config = require("@truffle/config"); const { findOne } = require("./helpers"); @@ -24,7 +24,7 @@ describe("CompilerSupplier", function () { const options = { contracts_directory: "", solc: "", - quiet: true, + quiet: true }; before("get code", async function () { @@ -60,8 +60,14 @@ describe("CompilerSupplier", function () { it("compiles w/ default solc if no compiler specified (float)", async function () { const defaultOptions = Config.default().merge(options); - const { contracts } = await compile(version5PragmaSource, defaultOptions); - const Version5Pragma = findOne("Version5Pragma", contracts); + const { compilations } = await Compile.sources({ + sources: version5PragmaSource, + options: defaultOptions + }); + const Version5Pragma = findOne( + "Version5Pragma", + compilations[0].contracts + ); assert(Version5Pragma.contractName === "Version5Pragma"); }); @@ -70,13 +76,16 @@ describe("CompilerSupplier", function () { options.compilers = { solc: { version: "0.4.15", - settings: {}, - }, + settings: {} + } }; const config = new Config().with(options); - const { contracts } = await compile(oldPragmaPinSource, config); - const OldPragmaPin = findOne("OldPragmaPin", contracts); + const { compilations } = await Compile.sources({ + sources: oldPragmaPinSource, + options: config + }); + const OldPragmaPin = findOne("OldPragmaPin", compilations[0].contracts); assert(OldPragmaPin.contractName === "OldPragmaPin"); }); @@ -87,13 +96,19 @@ describe("CompilerSupplier", function () { options.compilers = { solc: { version: "0.4.16-nightly.2017.8.9+commit.81887bc7", - settings: {}, - }, + settings: {} + } }; const config = Config.default().merge(options); - const { contracts } = await compile(oldPragmaFloatSource, config); - const OldPragmaFloat = findOne("OldPragmaFloat", contracts); + const { compilations } = await Compile.sources({ + sources: oldPragmaFloatSource, + options: config + }); + const OldPragmaFloat = findOne( + "OldPragmaFloat", + compilations[0].contracts + ); assert(OldPragmaFloat.contractName === "OldPragmaFloat"); }); @@ -106,17 +121,20 @@ describe("CompilerSupplier", function () { options.compilers = { solc: { - version: pathToSolc, - }, + version: pathToSolc + } }; const localPathOptions = Config.default().merge(options); - const { contracts } = await compile( - version5PragmaSource, - localPathOptions + const { compilations } = await Compile.sources({ + sources: version5PragmaSource, + options: localPathOptions + }); + const Version5Pragma = findOne( + "Version5Pragma", + compilations[0].contracts ); - const Version5Pragma = findOne("Version5Pragma", contracts); assert(Version5Pragma.contractName === "Version5Pragma"); }); @@ -137,13 +155,16 @@ describe("CompilerSupplier", function () { if (await fse.exists(expectedCache)) await fse.unlink(expectedCache); options.compilers = { - solc: { version: "0.4.21" }, + solc: { version: "0.4.21" } }; const cachedOptions = Config.default().merge(options); // Run compiler, expecting solc to be downloaded and cached. - await compile(version4PragmaSource, cachedOptions); + await Compile.sources({ + sources: version4PragmaSource, + options: cachedOptions + }); assert(await fse.exists(expectedCache), "Should have cached compiler"); @@ -154,10 +175,13 @@ describe("CompilerSupplier", function () { // got accessed / ran ok. await waitSecond(); - const { contracts } = await compile(version4PragmaSource, cachedOptions); + const { compilations } = await Compile.sources({ + sources: version4PragmaSource, + options: cachedOptions + }); finalAccessTime = (await fse.stat(expectedCache)).atime.getTime(); - const NewPragma = findOne("NewPragma", contracts); + const NewPragma = findOne("NewPragma", compilations[0].contracts); assert(NewPragma.contractName === "NewPragma", "Should have compiled"); @@ -174,17 +198,20 @@ describe("CompilerSupplier", function () { it("compiles with native solc", async function () { options.compilers = { solc: { - version: "native", - }, + version: "native" + } }; const nativeSolcOptions = Config.default().merge(options); - const { contracts } = await compile( - versionLatestPragmaSource, - nativeSolcOptions - ); - const VersionLatestPragma = findOne("Version7Pragma", contracts); //update when necessary + const { compilations } = await Compile.sources({ + sources: versionLatestPragmaSource, + options: nativeSolcOptions + }); + const VersionLatestPragma = findOne( + "Version7Pragma", + compilations[0].contracts + ); //update when necessary assert(VersionLatestPragma.compiler.version.includes("0.7.")); //update when necessary assert( VersionLatestPragma.contractName === "Version7Pragma", //update when necessary @@ -196,19 +223,19 @@ describe("CompilerSupplier", function () { options.compilers = { solc: { version: "0.4.22", - docker: true, - }, + docker: true + } }; const dockerizedSolcOptions = Config.default().merge(options); const expectedVersion = "0.4.22+commit.4cb486ee.Linux.g++"; - const { contracts } = await compile( - version4PragmaSource, - dockerizedSolcOptions - ); - const NewPragma = findOne("NewPragma", contracts); + const { compilations } = await Compile.sources({ + sources: version4PragmaSource, + options: dockerizedSolcOptions + }); + const NewPragma = findOne("NewPragma", compilations[0].contracts); assert(NewPragma.compiler.version === expectedVersion); assert(NewPragma.contractName === "NewPragma", "Should have compiled"); @@ -227,24 +254,30 @@ describe("CompilerSupplier", function () { settings: { optimizer: { enabled: false, - runs: 200, - }, - }, - }, + runs: 200 + } + } + } }, quiet: true, solc: "", contracts_build_directory: path.join(__dirname, "./build"), contracts_directory: path.join(__dirname, "./sources/v0.4.x"), working_directory: __dirname, - paths: paths, + paths: paths }; options.resolver = new Resolver(options); options = Config.default().merge(options); - const { contracts } = await compile.with_dependencies(options); - const ComplexOrdered = findOne("ComplexOrdered", contracts); + const { compilations } = await Compile.sourcesWithDependencies({ + paths, + options + }); + const ComplexOrdered = findOne( + "ComplexOrdered", + compilations[0].contracts + ); // This contract imports / inherits assert( @@ -258,14 +291,17 @@ describe("CompilerSupplier", function () { solc: { version: undefined, docker: true, - settings: {}, - }, + settings: {} + } }; compileConfig = Config.default().merge(options); let error; try { - await compile(version4PragmaSource, compileConfig); + await Compile.sources({ + sources: version4PragmaSource, + options: compileConfig + }); } catch (err) { error = err; } @@ -281,14 +317,17 @@ describe("CompilerSupplier", function () { solc: { version: imageName, docker: true, - settings: {}, - }, + settings: {} + } }; compileConfig = Config.default().merge(options); let error; try { - await compile(version4PragmaSource, compileConfig); + await Compile.sources({ + sources: version4PragmaSource, + options: compileConfig + }); } catch (err) { error = err; } diff --git a/packages/compile-vyper/index.js b/packages/compile-vyper/index.js index 1b691f0c3d6..e9145feec0f 100644 --- a/packages/compile-vyper/index.js +++ b/packages/compile-vyper/index.js @@ -4,7 +4,7 @@ const fs = require("fs"); const colors = require("colors"); const minimatch = require("minimatch"); -const find_contracts = require("@truffle/contract-sources"); +const findContracts = require("@truffle/contract-sources"); const Profiler = require("@truffle/compile-solidity/profiler"); const compiler = { @@ -14,77 +14,21 @@ const compiler = { const VYPER_PATTERN = "**/*.{vy,v.py,vyper.py}"; -// -------- TODO: Common with @truffle/compile-solidity -------- - -const compile = {}; - -// contracts_directory: String. Directory where .sol files can be found. -// quiet: Boolean. Suppress output. Defaults to false. -// strict: Boolean. Return compiler warnings as errors. Defaults to false. -compile.all = function(options, callback) { - find_contracts(options.contracts_directory, function(err, files) { - if (err) return callback(err); - - options.paths = files; - compile.with_dependencies(options, callback); - }); -}; - -// contracts_directory: String. Directory where .sol files can be found. -// build_directory: String. Optional. Directory where .sol.js files can be found. Only required if `all` is false. -// all: Boolean. Compile all sources found. Defaults to true. If false, will compare sources against built files -// in the build directory to see what needs to be compiled. -// quiet: Boolean. Suppress output. Defaults to false. -// strict: Boolean. Return compiler warnings as errors. Defaults to false. -compile.necessary = function(options, callback) { - options.logger = options.logger || console; - - Profiler.updated(options, function(err, updated) { - if (err) return callback(err); - - if (updated.length === 0 && options.quiet !== true) { - return callback(null, [], {}); - } - - options.paths = updated; - compile.with_dependencies(options, callback); - }); -}; - -compile.display = function(paths, options) { - if (!Array.isArray(paths)) { - paths = Object.keys(paths); - } - - const sourceFileNames = paths.sort().map(contract => { - if (path.isAbsolute(contract)) { - return `.${path.sep}${path.relative( - options.working_directory, - contract - )}`; - } - - return contract; - }); - options.events.emit("compile:sourcesToCompile", sourceFileNames); -}; - -// -------- End of common with @truffle/compile-solidity -------- - // Check that vyper is available, save its version -function checkVyper(callback) { - exec("vyper --version", function(err, stdout, stderr) { - if (err) - return callback(`${colors.red("Error executing vyper:")}\n${stderr}`); - - compiler.version = stdout.trim(); - - callback(null); +function checkVyper() { + return new Promise((resolve, reject) => { + exec("vyper --version", function (err, stdout, stderr) { + if (err) { + return reject(`${colors.red("Error executing vyper:")}\n${stderr}`); + } + compiler.version = stdout.trim(); + resolve(); + }); }); } // Execute vyper for single source file -function execVyper(options, source_path, callback) { +function execVyper(options, sourcePath, callback) { const formats = ["abi", "bytecode", "bytecode_runtime"]; if ( options.compilers.vyper.settings && @@ -92,37 +36,37 @@ function execVyper(options, source_path, callback) { ) { formats.push("source_map"); } - const command = `vyper -f ${formats.join(",")} ${source_path}`; + const command = `vyper -f ${formats.join(",")} ${sourcePath}`; - exec(command, { maxBuffer: 600 * 1024 }, function(err, stdout, stderr) { + exec(command, { maxBuffer: 600 * 1024 }, function (err, stdout, stderr) { if (err) return callback( `${stderr}\n${colors.red( - `Compilation of ${source_path} failed. See above.` + `Compilation of ${sourcePath} failed. See above.` )}` ); var outputs = stdout.split(/\r?\n/); - const compiled_contract = outputs.reduce(function(contract, output, index) { + const compiledContract = outputs.reduce((contract, output, index) => { return Object.assign(contract, { [formats[index]]: output }); }, {}); - callback(null, compiled_contract); + callback(null, compiledContract); }); } -// compile all options.paths -function compileAll(options, callback) { +// compile all sources +async function compileAll({ sources, options }) { options.logger = options.logger || console; - compile.display(options.paths, options); + Compile.display(sources, options); const promises = []; - options.paths.forEach(sourcePath => { + sources.forEach(sourcePath => { promises.push( new Promise((resolve, reject) => { - execVyper(options, sourcePath, function(error, compiledContract) { + execVyper(options, sourcePath, function (error, compiledContract) { if (error) return reject(error); // remove first extension from filename @@ -139,14 +83,14 @@ function compileAll(options, callback) { const sourceContents = sourceBuffer.toString(); const contractDefinition = { - contract_name: contractName, + contractName: contractName, sourcePath: sourcePath, source: sourceContents, abi: compiledContract.abi, bytecode: compiledContract.bytecode, deployedBytecode: compiledContract.bytecode_runtime, sourceMap: compiledContract.source_map, - compiler: compiler + compiler }; resolve(contractDefinition); @@ -154,54 +98,96 @@ function compileAll(options, callback) { }) ); }); - Promise.all(promises) - .then(contracts => { - const result = contracts.reduce((result, contract) => { - result[contract.contract_name] = contract; - - return result; - }, {}); - - const compilerInfo = { name: "vyper", version: compiler.version }; - - callback(null, result, options.paths, compilerInfo); - }) - .catch(callback); + const contracts = await Promise.all(promises); + + const compilerInfo = { name: "vyper", version: compiler.version }; + return { + compilations: [ + { + compiler: compilerInfo, + contracts, + sourceIndexes: sources + } + ] + }; } -// Check that vyper is available then forward to internal compile function -function compileVyper(options, callback) { - // filter out non-vyper paths - options.paths = options.paths.filter(function(path) { - return minimatch(path, VYPER_PATTERN); - }); +const Compile = { + // Check that vyper is available then forward to internal compile function + async sources({ sources = [], options }) { + // filter out non-vyper paths + const vyperFiles = sources.filter(path => minimatch(path, VYPER_PATTERN)); - // no vyper files found, no need to check vyper - if (options.paths.length === 0) return callback(null, {}, []); + // no vyper files found, no need to check vyper + if (sources.length === 0) { + return { compilations: [] }; + } - checkVyper(function(err) { - if (err) return callback(err); + await checkVyper(); + return await compileAll({ + sources: vyperFiles, + options + }); + }, + + // contracts_directory: String. Directory where contract files can be found. + // quiet: Boolean. Suppress output. Defaults to false. + // strict: Boolean. Return compiler warnings as errors. Defaults to false. + async all(options) { + const fileSearchPattern = path.join( + options.contracts_directory, + VYPER_PATTERN + ); + const files = await findContracts(fileSearchPattern); + + return await Compile.sources({ + sources: files, + options + }); + }, + + // contracts_directory: String. Directory where contract files can be found. + // all: Boolean. Compile all sources found. Defaults to true. If false, will compare sources against built files + // in the build directory to see what needs to be compiled. + // quiet: Boolean. Suppress output. Defaults to false. + // strict: Boolean. Return compiler warnings as errors. Defaults to false. + async necessary(options) { + options.logger = options.logger || console; + const updated = await Profiler.updated(options); - return compileAll(options, callback); - }); -} + if (updated.length === 0 && options.quiet !== true) { + return { compilations: [] }; + } -// append .vy pattern to contracts_directory in options and return updated options -function updateContractsDirectory(options) { - return options.with({ - contracts_directory: path.join(options.contracts_directory, VYPER_PATTERN) - }); -} + // filter out only Vyper files + const updatedVyperPaths = updated.filter(path => { + return path.match(/\.vy$|\.v.py$|\.vyper.py$/); + }); + return await Compile.sources({ + sources: updatedVyperPaths, + options + }); + }, + + async display(paths, options) { + if (!Array.isArray(paths)) { + paths = Object.keys(paths); + } -// wrapper for compile.all. only updates contracts_directory to find .vy -compileVyper.all = function(options, callback) { - return compile.all(updateContractsDirectory(options), callback); + const sourceFileNames = paths.sort().map(contract => { + if (path.isAbsolute(contract)) { + return `.${path.sep}${path.relative( + options.working_directory, + contract + )}`; + } + + return contract; + }); + options.events.emit("compile:sourcesToCompile", { sourceFileNames }); + } }; -// wrapper for compile.necessary. only updates contracts_directory to find .vy -compileVyper.necessary = function(options, callback) { - return compile.necessary(updateContractsDirectory(options), callback); +module.exports = { + Compile }; - -compile.with_dependencies = compileVyper; -module.exports = compileVyper; diff --git a/packages/compile-vyper/test/test_compiler.js b/packages/compile-vyper/test/test_compiler.js index 8afead5a975..b405b09b30b 100644 --- a/packages/compile-vyper/test/test_compiler.js +++ b/packages/compile-vyper/test/test_compiler.js @@ -1,9 +1,9 @@ const path = require("path"); const assert = require("assert"); const Config = require("@truffle/config"); -const compile = require("../index"); +const { Compile } = require("../index"); -describe("vyper compiler", function() { +describe("vyper compiler", function () { this.timeout(20000); const defaultSettings = { @@ -13,82 +13,69 @@ describe("vyper compiler", function() { }; const config = new Config().merge(defaultSettings); - it("compiles vyper contracts", function(done) { - compile.all(config, function(err, contracts, paths) { - assert.equal(err, null, "Compiles without error"); - - paths.forEach(function(path) { - assert( - [".vy", ".v.py", ".vyper.py"].some( - extension => path.indexOf(extension) !== -1 - ), - "Paths have only vyper files" - ); - }); - - const hex_regex = /^[x0-9a-fA-F]+$/; + it("compiles vyper contracts", async function () { + const { compilations } = await Compile.all(config); + const { contracts, sourceIndexes } = compilations[0]; + sourceIndexes.forEach(path => { + assert( + [".vy", ".v.py", ".vyper.py"].some( + extension => path.indexOf(extension) !== -1 + ), + "Paths have only vyper files" + ); + }); - [ - contracts.VyperContract1, - contracts.VyperContract2, - contracts.VyperContract3 - ].forEach((contract, index) => { - assert.notEqual( - contract, - undefined, - `Compiled contracts have VyperContract${index + 1}` - ); - assert.equal( - contract.contract_name, - `VyperContract${index + 1}`, - "Contract name is set correctly" - ); + const hex_regex = /^[x0-9a-fA-F]+$/; - assert.notEqual( - contract.abi.indexOf("vyper_action"), - -1, - "ABI has function from contract present" - ); + contracts.forEach((contract, index) => { + assert.notEqual( + contract, + undefined, + `Compiled contracts have VyperContract${index + 1}` + ); + assert.equal( + contract.contractName, + `VyperContract${index + 1}`, + "Contract name is set correctly" + ); - assert( - hex_regex.test(contract.bytecode), - "Bytecode has only hex characters" - ); - assert( - hex_regex.test(contract.deployedBytecode), - "Deployed bytecode has only hex characters" - ); + assert.notEqual( + contract.abi.indexOf("vyper_action"), + -1, + "ABI has function from contract present" + ); - assert.equal( - contract.compiler.name, - "vyper", - "Compiler name set correctly" - ); - }); + assert( + hex_regex.test(contract.bytecode), + "Bytecode has only hex characters" + ); + assert( + hex_regex.test(contract.deployedBytecode), + "Deployed bytecode has only hex characters" + ); - done(); + assert.equal( + contract.compiler.name, + "vyper", + "Compiler name set correctly" + ); }); }); - it("skips solidity contracts", function(done) { - compile.all(config, function(err, contracts, paths) { - assert.equal(err, null, "Compiles without error"); + it("skips solidity contracts", async function () { + const { compilations } = await Compile.all(config); + const { contracts, sourceIndexes } = compilations[0]; - paths.forEach(function(path) { - assert.equal(path.indexOf(".sol"), -1, "Paths have no .sol files"); - }); - - assert.equal( - contracts.SolidityContract, - undefined, - "Compiled contracts have no SolidityContract" - ); - - done(); + sourceIndexes.forEach(path => { + assert.equal(path.indexOf(".sol"), -1, "Paths have no .sol files"); + }); + const noSolidityContract = contracts.every(contract => { + return contract.contractName !== "SolidityContract"; }); + assert(noSolidityContract, "Compiled contracts have no SolidityContract"); }); - describe("with external options set", function() { + describe("with external options set", function () { const configWithSourceMap = new Config().merge(defaultSettings).merge({ compilers: { vyper: { @@ -99,19 +86,14 @@ describe("vyper compiler", function() { } }); - it("compiles when sourceMap option set true", function(done) { - compile.all(configWithSourceMap, function(err, contracts) { - [ - contracts.VyperContract1, - contracts.VyperContract2, - contracts.VyperContract3 - ].forEach((contract, index) => { - assert( - contract.sourceMap, - `source map have to not be empty. ${index + 1}` - ); - }); - done(); + it("compiles when sourceMap option set true", async () => { + const { compilations } = await Compile.all(configWithSourceMap); + const { contracts } = compilations[0]; + contracts.forEach((contract, index) => { + assert( + contract.sourceMap, + `source map have to not be empty. ${index + 1}` + ); }); }); }); diff --git a/packages/core/index.js b/packages/core/index.js index dfb602225cb..e554877931f 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -1,10 +1,11 @@ require("source-map-support/register"); -var pkg = require("./package.json"); +const pkg = require("./package.json"); module.exports = { build: require("./lib/build"), create: require("./lib/commands/create/helpers"), - contracts: require("@truffle/workflow-compile"), + // TODO: update this to non-legacy the next breaking change + contracts: require("@truffle/workflow-compile/legacy"), package: require("./lib/package"), test: require("./lib/test"), version: pkg.version, diff --git a/packages/core/lib/build.js b/packages/core/lib/build.js index 4252d37c8bb..d9011473ab6 100644 --- a/packages/core/lib/build.js +++ b/packages/core/lib/build.js @@ -1,6 +1,6 @@ const fse = require("fs-extra"); const del = require("del"); -const Contracts = require("@truffle/workflow-compile"); +const WorkflowCompile = require("@truffle/workflow-compile"); const BuildError = require("./errors/builderror"); const { spawn } = require("child_process"); const spawnargs = require("spawn-args"); @@ -11,7 +11,7 @@ function CommandBuilder(command) { this.command = command; } -CommandBuilder.prototype.build = function(options, callback) { +CommandBuilder.prototype.build = function (options, callback) { console.log("Running `" + this.command + "`..."); const args = spawnargs(this.command); @@ -27,15 +27,15 @@ CommandBuilder.prototype.build = function(options, callback) { }) }); - cmd.stdout.on("data", function(data) { + cmd.stdout.on("data", function (data) { console.log(data.toString()); }); - cmd.stderr.on("data", function(data) { + cmd.stderr.on("data", function (data) { console.error(data); }); - cmd.on("close", function(code) { + cmd.on("close", function (code) { let error = null; if (code !== 0) { error = "Command exited with code " + code; @@ -45,7 +45,7 @@ CommandBuilder.prototype.build = function(options, callback) { }; const Build = { - clean: function(options, callback) { + clean: function (options, callback) { const destination = options.build_directory; const contracts_build_directory = options.contracts_build_directory; @@ -60,7 +60,7 @@ const Build = { }); }, - build: function(options, callback) { + build: function (options, callback) { expect.options(options, [ "build_directory", "working_directory", @@ -101,23 +101,23 @@ const Build = { clean = builder.clean; } - clean(options, function(err) { + clean(options, function (err) { if (err) return callback(err); // If necessary. This prevents errors due to the .sol.js files not existing. - Contracts.compile(options, function(err) { - if (err) return callback(err); - - if (builder) { - builder.build(options, function(err) { - if (typeof err === "string") { - return callback(new BuildError(err)); - } - return callback(err); - }); - } - return callback(); - }); + WorkflowCompile.compileAndSave(options) + .then(() => { + if (builder) { + builder.build(options, function (err) { + if (typeof err === "string") { + return callback(new BuildError(err)); + } + return callback(err); + }); + } + return callback(); + }) + .catch(callback); }); } }; diff --git a/packages/core/lib/command.js b/packages/core/lib/command.js index 1e9d629352c..95bc9d1ebec 100644 --- a/packages/core/lib/command.js +++ b/packages/core/lib/command.js @@ -68,7 +68,7 @@ class Command { return { name: chosenCommand, argv, - command, + command }; } @@ -138,7 +138,7 @@ class Command { analytics.send({ command: result.name ? result.name : "other", args: result.argv._, - version: bundled || "(unbundled) " + core, + version: bundled || "(unbundled) " + core }); } catch (err) { callback(err); diff --git a/packages/core/lib/commands/compile.js b/packages/core/lib/commands/compile.js index f7588307e9a..333962179e0 100644 --- a/packages/core/lib/commands/compile.js +++ b/packages/core/lib/commands/compile.js @@ -63,7 +63,7 @@ const command = { }, run: function (options, done) { const TruffleError = require("@truffle/error"); - const Contracts = require("@truffle/workflow-compile/new"); + const WorkflowCompile = require("@truffle/workflow-compile"); const Config = require("@truffle/config"); const config = Config.detect(options); @@ -87,7 +87,7 @@ const command = { ); } - Contracts.compile(config) + WorkflowCompile.compile(config) .then(async compilationOutput => { if (options.saveIntermediate) { // Get the filename the user provided to save the compilation results to @@ -102,7 +102,7 @@ const command = { ); } - return Contracts.save(config, compilationOutput.contracts); + return WorkflowCompile.save(config, compilationOutput); }) .then(() => done()) .catch(done); diff --git a/packages/core/lib/commands/exec.js b/packages/core/lib/commands/exec.js index 8ee7989474e..8ab739306ab 100644 --- a/packages/core/lib/commands/exec.js +++ b/packages/core/lib/commands/exec.js @@ -35,9 +35,9 @@ const command = { } ] }, - run: function(options, done) { + run: function (options, done) { const Config = require("@truffle/config"); - const Contracts = require("@truffle/workflow-compile"); + const WorkflowCompile = require("@truffle/workflow-compile"); const ConfigurationError = require("../errors/configurationerror"); const Require = require("@truffle/require"); const { Environment } = require("@truffle/environment"); @@ -74,10 +74,16 @@ const command = { // `--compile` if (options.c || options.compile) { - return Contracts.compile(config); + return WorkflowCompile.compile(config); } return; }) + .then(compilationOutput => { + // save artifacts if compilation took place + if (compilationOutput) { + return WorkflowCompile.save(config, compilationOutput); + } + }) .then(() => { return promisify(Require.exec.bind(Require))(config.with({ file })); }) diff --git a/packages/core/lib/commands/migrate.js b/packages/core/lib/commands/migrate.js index 2ed91c9e80f..89b08e3cef1 100644 --- a/packages/core/lib/commands/migrate.js +++ b/packages/core/lib/commands/migrate.js @@ -188,7 +188,7 @@ const command = { const Artifactor = require("@truffle/artifactor"); const Resolver = require("@truffle/resolver"); const Migrate = require("@truffle/migrate"); - const Contracts = require("@truffle/workflow-compile"); + const WorkflowCompile = require("@truffle/workflow-compile"); const { Environment } = require("@truffle/environment"); const Config = require("@truffle/config"); const { promisify } = require("util"); @@ -201,7 +201,7 @@ const command = { conf.compiler = "none"; } - Contracts.compile(conf) + WorkflowCompile.compileAndSave(conf) .then(async () => { await Environment.detect(conf); diff --git a/packages/core/lib/commands/opcode.js b/packages/core/lib/commands/opcode.js index 834258e13cb..897ca85e0ad 100644 --- a/packages/core/lib/commands/opcode.js +++ b/packages/core/lib/commands/opcode.js @@ -1,4 +1,4 @@ -var command = { +const command = { command: "opcode", description: "Print the compiled opcodes for a given contract", builder: { @@ -17,56 +17,55 @@ var command = { } ] }, - run: function(options, done) { - var Config = require("@truffle/config"); - var TruffleError = require("@truffle/error"); - var Contracts = require("@truffle/workflow-compile"); - var CodeUtils = require("@truffle/code-utils"); + run: function (options, done) { + const Config = require("@truffle/config"); + const TruffleError = require("@truffle/error"); + const WorkflowCompile = require("@truffle/workflow-compile"); + const CodeUtils = require("@truffle/code-utils"); if (options._.length === 0) { return done(new TruffleError("Please specify a contract name.")); } var config = Config.detect(options); - Contracts.compile(config, function(err) { - if (err) return done(err); - - var contractName = options._[0]; - var Contract; - try { - Contract = config.resolver.require(contractName); - } catch (e) { - return done( - new TruffleError( - 'Cannot find compiled contract with name "' + contractName + '"' - ) - ); - } + WorkflowCompile.compileAndSave(config) + .then(() => { + const contractName = options._[0]; + let Contract; + try { + Contract = config.resolver.require(contractName); + } catch (e) { + return done( + new TruffleError( + 'Cannot find compiled contract with name "' + contractName + '"' + ) + ); + } - var bytecode = Contract.deployedBytecode; - var numInstructions = Contract.deployedSourceMap.split(";").length; + let bytecode = Contract.deployedBytecode; + let numInstructions = Contract.deployedSourceMap.split(";").length; - if (options.creation) { - bytecode = Contract.bytecode; - numInstructions = Contract.sourceMap.split(";").length; - } - - var opcodes = CodeUtils.parseCode(bytecode, numInstructions); + if (options.creation) { + bytecode = Contract.bytecode; + numInstructions = Contract.sourceMap.split(";").length; + } + const opcodes = CodeUtils.parseCode(bytecode, numInstructions); - var indexLength = (opcodes.length + "").length; + const indexLength = (opcodes.length + "").length; - opcodes.forEach(function(opcode, index) { - var strIndex = index + ":"; + opcodes.forEach((opcode, index) => { + let strIndex = index + ":"; - while (strIndex.length < indexLength + 1) { - strIndex += " "; - } + while (strIndex.length < indexLength + 1) { + strIndex += " "; + } - console.log( - strIndex + " " + opcode.name + " " + (opcode.pushData || "") - ); - }); - }); + console.log( + strIndex + " " + opcode.name + " " + (opcode.pushData || "") + ); + }); + }) + .catch(done); } }; diff --git a/packages/core/lib/debug/compiler.js b/packages/core/lib/debug/compiler.js index d2dd8479096..092dc880b87 100644 --- a/packages/core/lib/debug/compiler.js +++ b/packages/core/lib/debug/compiler.js @@ -1,4 +1,4 @@ -const compile = require("@truffle/compile-solidity/new"); +const { Compile } = require("@truffle/compile-solidity"); class DebugCompiler { constructor(config) { @@ -8,14 +8,24 @@ class DebugCompiler { async compile(sources = undefined) { const compileConfig = this.config.with({ quiet: true }); - const { contracts, sourceIndexes } = sources - ? await compile(sources, compileConfig) //used by external.js - : await compile.all(compileConfig); + const { compilations } = sources + ? await Compile.sources({ sources, options: compileConfig }) //used by external.js + : await Compile.all(compileConfig); - return { contracts, sourceIndexes }; + return compilations.reduce( + (a, compilation) => { + a.contracts = a.contracts.concat(compilation.contracts); + a.sourceIndexes = a.sourceIndexes.concat(compilation.sourceIndexes); + return a; + }, + { + contracts: [], + sourceIndexes: [] + } + ); } } module.exports = { - DebugCompiler, + DebugCompiler }; diff --git a/packages/core/lib/test.js b/packages/core/lib/test.js index bdef731d6e8..b8660d30a91 100644 --- a/packages/core/lib/test.js +++ b/packages/core/lib/test.js @@ -6,7 +6,7 @@ const { createInterfaceAdapter } = require("@truffle/interface-adapter"); const Config = require("@truffle/config"); -const Contracts = require("@truffle/workflow-compile/new"); +const WorkflowCompile = require("@truffle/workflow-compile"); const Resolver = require("@truffle/resolver"); const TestRunner = require("./testing/TestRunner"); const TestResolver = require("./testing/TestResolver"); @@ -116,16 +116,27 @@ const Test = { await this.performInitialDeploy(config, testResolver); + const solidityCompilationOutput = compilations.reduce( + (a, compilation) => { + if (compilation.compiler && compilation.compiler.name === "solc") { + a.sourceIndexes = a.sourceIndexes.concat(compilation.sourceIndexes); + a.contracts = a.contracts.concat(compilation.contracts); + } + return a; + }, + { sourceIndexes: [], contracts: [] } + ); + await this.defineSolidityTests( mocha, testContracts, - compilations.solc.sourceIndexes, + solidityCompilationOutput.sourceIndexes, runner ); const debuggerCompilations = Codec.Compilations.Utils.shimArtifacts( - compilations.solc.contracts, - compilations.solc.sourceIndexes + solidityCompilationOutput.contracts, + solidityCompilationOutput.sourceIndexes ); //for stack traces, we'll need to set up a light-mode debugger... @@ -233,9 +244,9 @@ const Test = { } } // Compile project contracts and test contracts - const { contracts, compilations } = await Contracts.compile(compileConfig); - - await Contracts.save(compileConfig, contracts); + const { contracts, compilations } = await WorkflowCompile.compileAndSave( + compileConfig + ); return { contracts, diff --git a/packages/core/lib/testing/SolidityTest.js b/packages/core/lib/testing/SolidityTest.js index dd0bdbe4fc6..451d3d84fea 100644 --- a/packages/core/lib/testing/SolidityTest.js +++ b/packages/core/lib/testing/SolidityTest.js @@ -1,9 +1,9 @@ const TestCase = require("mocha/lib/test.js"); const Suite = require("mocha/lib/suite.js"); const Deployer = require("@truffle/deployer"); +const { Compile } = require("@truffle/compile-solidity"); +const { Shims } = require("@truffle/compile-common"); const RangeUtils = require("@truffle/compile-solidity/compilerSupplier/rangeUtils"); -const compile = require("@truffle/compile-solidity/new"); -const { shimContract } = require("@truffle/compile-solidity/legacy/shims"); const path = require("path"); const debug = require("debug")("lib:testing:soliditytest"); @@ -116,28 +116,31 @@ const SolidityTest = { ? "NewSafeSend.sol" : "OldSafeSend.sol"; - const { contracts } = await compile.with_dependencies( - runner.config.with({ - paths: [ - path.join(__dirname, "Assert.sol"), - path.join(__dirname, "AssertAddress.sol"), - path.join(__dirname, "AssertAddressArray.sol"), - path.join(__dirname, "AssertBalance.sol"), - path.join(__dirname, "AssertBool.sol"), - path.join(__dirname, "AssertBytes32.sol"), - path.join(__dirname, "AssertBytes32Array.sol"), - path.join(__dirname, "AssertGeneral.sol"), - path.join(__dirname, "AssertInt.sol"), - path.join(__dirname, "AssertIntArray.sol"), - path.join(__dirname, "AssertString.sol"), - path.join(__dirname, "AssertUint.sol"), - path.join(__dirname, "AssertUintArray.sol"), - "truffle/DeployedAddresses.sol", // generated by deployed.js - path.join(__dirname, SafeSend) - ], + const { compilations } = await Compile.sourcesWithDependencies({ + paths: [ + path.join(__dirname, "Assert.sol"), + path.join(__dirname, "AssertAddress.sol"), + path.join(__dirname, "AssertAddressArray.sol"), + path.join(__dirname, "AssertBalance.sol"), + path.join(__dirname, "AssertBool.sol"), + path.join(__dirname, "AssertBytes32.sol"), + path.join(__dirname, "AssertBytes32Array.sol"), + path.join(__dirname, "AssertGeneral.sol"), + path.join(__dirname, "AssertInt.sol"), + path.join(__dirname, "AssertIntArray.sol"), + path.join(__dirname, "AssertString.sol"), + path.join(__dirname, "AssertUint.sol"), + path.join(__dirname, "AssertUintArray.sol"), + "truffle/DeployedAddresses.sol", // generated by deployed.js + path.join(__dirname, SafeSend) + ], + options: runner.config.with({ quiet: true }) - ); + }); + const contracts = compilations.reduce((a, compilation) => { + return a.concat(compilation.contracts); + }, []); // Set network values. for (let contract of contracts) { @@ -145,7 +148,9 @@ const SolidityTest = { contract.default_network = config.default_network; } - await config.artifactor.saveAll(contracts.map(shimContract)); + await config.artifactor.saveAll( + contracts.map(Shims.NewToLegacy.forContract) + ); debug("compiled"); }, diff --git a/packages/core/test/config.js b/packages/core/test/config.js index dccf94ce154..dac95042aba 100644 --- a/packages/core/test/config.js +++ b/packages/core/test/config.js @@ -2,13 +2,13 @@ var assert = require("chai").assert; var fs = require("fs-extra"); var glob = require("glob"); var Box = require("@truffle/box"); -var Contracts = require("@truffle/workflow-compile"); +var WorkflowCompile = require("@truffle/workflow-compile"); var Ganache = require("ganache-core"); var provision = require("@truffle/provisioner"); var Resolver = require("@truffle/resolver"); var Artifactor = require("@truffle/artifactor"); -describe("config", function() { +describe("config", function () { var config; var customRPCConfig = { gas: 90000, @@ -32,17 +32,16 @@ describe("config", function() { }; }); - before("Compile contracts", function(done) { + before("Compile contracts", async function () { this.timeout(10000); - Contracts.compile( + await WorkflowCompile.compileAndSave( config.with({ quiet: true - }), - done + }) ); }); - after("Cleanup tmp files", function(done) { + after("Cleanup tmp files", function (done) { glob("tmp-*", (err, files) => { if (err) done(err); files.forEach(file => fs.removeSync(file)); @@ -50,7 +49,7 @@ describe("config", function() { }); }); - it("Provisioning contracts should set proper RPC values", function() { + it("Provisioning contracts should set proper RPC values", function () { var contract = config.resolver.require("MetaCoin.sol"); provision(contract, config); diff --git a/packages/core/test/ethpm.js b/packages/core/test/ethpm.js index 1af5c2a65f7..dd625c950a9 100644 --- a/packages/core/test/ethpm.js +++ b/packages/core/test/ethpm.js @@ -1,16 +1,16 @@ -var assert = require("chai").assert; -var Box = require("@truffle/box"); -var fs = require("fs-extra"); -var glob = require("glob"); -var path = require("path"); -var Contracts = require("@truffle/workflow-compile"); -var Package = require("../lib/package.js"); -var Blockchain = require("@truffle/blockchain-utils"); -var Ganache = require("ganache-core"); -var Resolver = require("@truffle/resolver"); -var Artifactor = require("@truffle/artifactor"); - -describe.skip("EthPM integration", function() { +const assert = require("chai").assert; +const Box = require("@truffle/box"); +const fs = require("fs-extra"); +const glob = require("glob"); +const path = require("path"); +const WorkflowCompile = require("@truffle/workflow-compile"); +const Package = require("../lib/package.js"); +const Blockchain = require("@truffle/blockchain-utils"); +const Ganache = require("ganache-core"); +const Resolver = require("@truffle/resolver"); +const Artifactor = require("@truffle/artifactor"); + +describe.skip("EthPM integration", function () { var config; var host; var registry; @@ -29,12 +29,12 @@ describe.skip("EthPM integration", function() { ); } - beforeEach("Create a Ganache provider and get a blockchain uri", function( + beforeEach("Create a Ganache provider and get a blockchain uri", function ( done ) { provider = Ganache.provider(); - Blockchain.asURI(provider, function(err, uri) { + Blockchain.asURI(provider, function (err, uri) { if (err) return done(err); blockchain_uri = uri; done(); @@ -42,9 +42,9 @@ describe.skip("EthPM integration", function() { }); // Super slow doing these in a beforeEach, but it ensures nothing conflicts. - beforeEach("Create a sandbox", function(done) { + beforeEach("Create a sandbox", function (done) { this.timeout(20000); - Box.sandbox(function(err, result) { + Box.sandbox(function (err, result) { if (err) return done(err); config = result; config.resolver = new Resolver(config); @@ -60,14 +60,14 @@ describe.skip("EthPM integration", function() { }); }); - beforeEach("Create a fake EthPM host and memory registry", function(done) { + beforeEach("Create a fake EthPM host and memory registry", function (done) { this.timeout(30000); // I've had varrying runtimes with this block, likely due to networking. GithubExamples.initialize( { blockchain: blockchain_uri }, - function(err, results) { + function (err, results) { if (err) return done(err); host = results.host; @@ -78,7 +78,7 @@ describe.skip("EthPM integration", function() { ); }); - after("Cleanup tmp files", function(done) { + after("Cleanup tmp files", function (done) { glob("tmp-*", (err, files) => { if (err) done(err); files.forEach(file => fs.removeSync(file)); @@ -103,10 +103,10 @@ describe.skip("EthPM integration", function() { // } // }); - it("successfully installs single dependency from EthPM", function(done) { + it("successfully installs single dependency from EthPM", async function () { this.timeout(30000); // Giving ample time for requests to time out. - Package.install( + await Package.install( config.with({ ethpm: { ipfs_host: host, @@ -114,29 +114,21 @@ describe.skip("EthPM integration", function() { provider: provider }, packages: ["owned"] - }), - function(err) { - if (err) return done(err); - - var expected_install_directory = path.resolve( - path.join(config.working_directory, "installed_contracts", "owned") - ); - - assertFile(path.join(expected_install_directory, "ethpm.json")); - assertFile( - path.join(expected_install_directory, "contracts", "owned.sol") - ); - - done(); - } + }) + ); + const expected_install_directory = path.resolve( + path.join(config.working_directory, "installed_contracts", "owned") ); + + assertFile(path.join(expected_install_directory, "ethpm.json")); + assertFile(path.join(expected_install_directory, "contracts", "owned.sol")); }); - it("successfully installs and provisions a package with dependencies from EthPM", function(done) { + it("successfully installs and provisions a package with dependencies from EthPM", async function () { this.timeout(30000); // Giving ample time for requests to time out. this.retries(2); - Package.install( + await Package.install( config.with({ ethpm: { ipfs_host: host, @@ -144,95 +136,75 @@ describe.skip("EthPM integration", function() { provider: provider }, packages: ["transferable"] - }), - function(err) { - if (err) return done(err); - - var expected_install_directory = path.resolve( - path.join(config.working_directory, "installed_contracts") - ); + }) + ); + const expected_install_directory = path.resolve( + path.join(config.working_directory, "installed_contracts") + ); - assertFile( - path.join(expected_install_directory, "transferable", "ethpm.json") - ); - assertFile( - path.join( - expected_install_directory, - "transferable", - "contracts", - "transferable.sol" - ) - ); - assertFile( - path.join(expected_install_directory, "owned", "ethpm.json") - ); - assertFile( - path.join( - expected_install_directory, - "owned", - "contracts", - "owned.sol" - ) - ); + assertFile( + path.join(expected_install_directory, "transferable", "ethpm.json") + ); + assertFile( + path.join( + expected_install_directory, + "transferable", + "contracts", + "transferable.sol" + ) + ); + assertFile(path.join(expected_install_directory, "owned", "ethpm.json")); + assertFile( + path.join(expected_install_directory, "owned", "contracts", "owned.sol") + ); - // Write a contract that uses transferable, so it will be compiled. - var contractSource = - "pragma solidity ^0.4.2; import 'transferable/transferable.sol'; contract MyContract {}"; + // Write a contract that uses transferable, so it will be compiled. + const contractSource = + "pragma solidity ^0.4.2; import 'transferable/transferable.sol'; contract MyContract {}"; - fs.writeFileSync( - path.join(config.contracts_directory, "MyContract.sol"), - contractSource, - "utf8" - ); + fs.writeFileSync( + path.join(config.contracts_directory, "MyContract.sol"), + contractSource, + "utf8" + ); - // Compile all contracts, then provision them and see if we get contracts from our dependencies. - Contracts.compile( - config.with({ - all: true, - quiet: true - }), - function(err, result) { - if (err) return done(err); - let { contracts } = result; - - assert.isNotNull(contracts["owned"]); - assert.isNotNull(contracts["transferable"]); - - fs.readdir(config.contracts_build_directory, function(err, files) { - if (err) return done(err); - - var found = [false, false]; - var search = ["owned", "transferable"]; - - search.forEach(function(contract_name, index) { - files.forEach(function(file) { - if (path.basename(file, ".json") === contract_name) { - found[index] = true; - } - }); - }); - - found.forEach(function(isFound, index) { - assert( - isFound, - "Could not find built binary with name '" + - search[index] + - "'" - ); - }); - - done(); - }); - } - ); - } + // Compile all contracts, then provision them and see if we get contracts from our dependencies. + const { contracts } = await WorkflowCompile.compileAndSave( + config.with({ + all: true, + quiet: true + }) ); + const contractNames = contracts.reduce((a, contract) => { + return a.concat(contract.contractName); + }, []); + assert.isNotNull(contractNames["owned"]); + assert.isNotNull(contractNames["transferable"]); + const files = fs.readdirSync(config.contracts_build_directory); + + const found = [false, false]; + const search = ["owned", "transferable"]; + + search.forEach((contract_name, index) => { + files.forEach(file => { + if (path.basename(file, ".json") === contract_name) { + found[index] = true; + } + }); + }); + + found.forEach((isFound, index) => { + assert( + isFound, + "Could not find built binary with name '" + search[index] + "'" + ); + }); }); // For each of these examples, sources exist. However, including sources isn't required. This test // treats the package as if it had no sources; to do so, we simply don't compile its code. // In addition, this package contains deployments. We need to make sure these deployments are available. - it("successfully installs and provisions a deployed package with network artifacts from EthPM, without compiling", function(done) { + it("successfully installs and provisions a deployed package with network artifacts from EthPM, without compiling", function (done) { this.timeout(30000); // Giving ample time for requests to time out. Package.install( @@ -244,7 +216,7 @@ describe.skip("EthPM integration", function() { }, packages: ["safe-math-lib"] }), - function(err) { + function (err) { if (err) return done(err); // Make sure we can resolve it. diff --git a/packages/core/test/lib/commands/compile.js b/packages/core/test/lib/commands/compile.js index 1642c1b2429..1ced2659836 100644 --- a/packages/core/test/lib/commands/compile.js +++ b/packages/core/test/lib/commands/compile.js @@ -1,6 +1,6 @@ const assert = require("chai").assert; const Box = require("@truffle/box"); -const Contracts = require("@truffle/workflow-compile"); +const WorkflowCompile = require("@truffle/workflow-compile"); const Artifactor = require("@truffle/artifactor"); const Resolver = require("@truffle/resolver"); const MemoryStream = require("memorystream"); @@ -12,7 +12,7 @@ let config; let output = ""; let memStream; -describe("compile", function() { +describe("compile", function () { before("Create a sandbox", async () => { config = await Box.sandbox("default"); config.resolver = new Resolver(config); @@ -29,7 +29,7 @@ describe("compile", function() { config.logger = { log: val => val && memStream.write(val) }; }); - after("Cleanup tmp files", function(done) { + after("Cleanup tmp files", function (done) { glob("tmp-*", (err, files) => { if (err) done(err); files.forEach(file => fs.removeSync(file)); @@ -39,51 +39,37 @@ describe("compile", function() { afterEach("Clear MemoryStream", () => (output = "")); - it("compiles all initial contracts", function(done) { + it("compiles all initial contracts", async function () { this.timeout(10000); - - Contracts.compile( + const { contracts } = await WorkflowCompile.compileAndSave( config.with({ all: false, quiet: true - }), - function(err, result) { - if (err) return done(err); - let { contracts } = result; - - assert.equal( - Object.keys(contracts).length, - 3, - "Didn't compile the expected number of contracts" - ); - done(); - } + }) + ); + assert.equal( + Object.keys(contracts).length, + 3, + "Didn't compile the expected number of contracts" ); }); - it("compiles no contracts after no updates", function(done) { + it("compiles no contracts after no updates", async function () { this.timeout(10000); - - Contracts.compile( + const { contracts } = await WorkflowCompile.compileAndSave( config.with({ all: false, quiet: true - }), - function(err, result) { - if (err) return done(err); - let { contracts } = result; - - assert.equal( - Object.keys(contracts).length, - 0, - "Compiled a contract even though we weren't expecting it" - ); - done(); - } + }) + ); + assert.equal( + Object.keys(contracts).length, + 0, + "Compiled a contract even though we weren't expecting it" ); }); - it("compiles updated contract and its ancestors", function(done) { + it("compiles updated contract and its ancestors", async function () { this.timeout(10000); var file_to_update = path.resolve( @@ -95,30 +81,23 @@ describe("compile", function() { var newTime = new Date().getTime(); fs.utimesSync(file_to_update, newTime, newTime); - Contracts.compile( + const { contracts } = await WorkflowCompile.compileAndSave( config.with({ all: false, quiet: true - }), - function(err, result) { - if (err) return done(err); - let { contracts } = result; - - assert.equal( - Object.keys(contracts).length, - 2, - "Expected MetaCoin and ConvertLib to be compiled" - ); - - // reset time - fs.utimesSync(file_to_update, stat.atime, stat.mtime); - - done(); - } + }) + ); + assert.equal( + Object.keys(contracts).length, + 2, + "Expected MetaCoin and ConvertLib to be compiled" ); + + // reset time + fs.utimesSync(file_to_update, stat.atime, stat.mtime); }); - it("compiling shouldn't create any network artifacts", function() { + it("compiling shouldn't create any network artifacts", function () { var contract = config.resolver.require("MetaCoin.sol"); assert.equal( Object.keys(contract.networks).length, @@ -127,15 +106,15 @@ describe("compile", function() { ); }); - describe("solc listing options", function() { + describe("solc listing options", function () { beforeEach(() => { memStream = new MemoryStream(); - memStream.on("data", function(data) { + memStream.on("data", function (data) { output += data.toString(); }); }); - it("prints a truncated list of solcjs versions", function(done) { + it("prints a truncated list of solcjs versions", function (done) { this.timeout(5000); const options = { @@ -145,7 +124,7 @@ describe("compile", function() { command.run(config.with(options), err => { if (err) return done(err); - memStream.on("end", function() { + memStream.on("end", function () { const arr = JSON.parse(output); assert(arr.length === 11); done(); @@ -155,7 +134,7 @@ describe("compile", function() { }); }); - it("prints a list of docker tags", function(done) { + it("prints a list of docker tags", function (done) { this.timeout(20000); const options = { @@ -165,7 +144,7 @@ describe("compile", function() { command.run(config.with(options), err => { if (err) return done(err); - memStream.on("end", function() { + memStream.on("end", function () { const arr = JSON.parse(output); assert(arr.length === 11); assert(typeof arr[0] === "string"); @@ -176,7 +155,7 @@ describe("compile", function() { }); }); - it("prints a full list of releases when --all is set", function(done) { + it("prints a full list of releases when --all is set", function (done) { this.timeout(5000); const options = { @@ -187,7 +166,7 @@ describe("compile", function() { command.run(config.with(options), err => { if (err) return done(err); - memStream.on("end", function() { + memStream.on("end", function () { const arr = JSON.parse(output); assert(arr.length > 11); assert(typeof arr[0] === "string"); @@ -198,29 +177,4 @@ describe("compile", function() { }); }); }); - - // TODO: Kept this as a comment because I'm confused if it applies. - // Since the binary and abi are updated with every compile, and they're not within - // the networks object anymore, it may not matter when that specific network changed. - - // it('compiles all contracts after multiple changes after a change in network', function(done) { - // this.timeout(10000); - // - // config.network = "secondary"; - // - // Contracts.compile(config.with({ - // all: false, - // quiet: true - // }), function(err, contracts) { - // if (err) return done(err); - // - // assert.equal(Object.keys(contracts).length, 3, "Expected all contracts to be compiled on a second network"); - // done(); - // }); - // }); - // - // it('contracts should now have two networks', function() { - // var contract = config.resolver.require("MetaCoin.sol"); - // assert.equal(contract.networks().length, 2, "Expected the contract to be managing two networks"); - // }); }).timeout(10000); diff --git a/packages/core/test/migrate.js b/packages/core/test/migrate.js index 497e0571d1b..ceffc168f3a 100644 --- a/packages/core/test/migrate.js +++ b/packages/core/test/migrate.js @@ -1,7 +1,7 @@ var assert = require("chai").assert; var Box = require("@truffle/box"); var Migrate = require("@truffle/migrate"); -var Contracts = require("@truffle/workflow-compile"); +var WorkflowCompile = require("@truffle/workflow-compile"); var Networks = require("../lib/networks"); var path = require("path"); var fs = require("fs-extra"); @@ -11,7 +11,7 @@ var Resolver = require("@truffle/resolver"); var Artifactor = require("@truffle/artifactor"); var Web3 = require("web3"); -describe("migrate", function() { +describe("migrate", function () { var config; before("Create a sandbox", async () => { @@ -35,15 +35,15 @@ describe("migrate", function() { }); } - before("Get accounts and network id of network one", function() { + before("Get accounts and network id of network one", function () { return createProviderAndSetNetworkConfig("primary"); }); - before("Get accounts and network id of network one", function() { + before("Get accounts and network id of network one", function () { return createProviderAndSetNetworkConfig("secondary"); }); - after("Cleanup tmp files", function(done) { + after("Cleanup tmp files", function (done) { glob("tmp-*", (err, files) => { if (err) done(err); files.forEach(file => fs.removeSync(file)); @@ -75,7 +75,9 @@ describe("migrate", function() { config.network = "primary"; - await Contracts.compile(config.with({ all: false, quiet: true })); + await WorkflowCompile.compileAndSave( + config.with({ all: false, quiet: true }) + ); await Migrate.run(config.with({ quiet: true })); @@ -169,7 +171,7 @@ describe("migrate", function() { "Migrations contract should have an address on secondary network" ); - Object.keys(networks["primary"]).forEach(function(contract_name) { + Object.keys(networks["primary"]).forEach(function (contract_name) { assert.notEqual( networks["secondary"][contract_name], networks["primary"][contract_name], diff --git a/packages/core/test/npm.js b/packages/core/test/npm.js index 9a46cf407d1..cc59a512a3d 100644 --- a/packages/core/test/npm.js +++ b/packages/core/test/npm.js @@ -5,9 +5,9 @@ var path = require("path"); var fse = require("fs-extra"); var Resolver = require("@truffle/resolver"); var Artifactor = require("@truffle/artifactor"); -var Contracts = require("@truffle/workflow-compile"); +var WorkflowCompile = require("@truffle/workflow-compile"); -describe("NPM integration", function() { +describe("NPM integration", function () { var config; var moduleSource = "pragma solidity ^0.5.0; import './ModuleDependency.sol'; contract Module {}"; @@ -53,7 +53,7 @@ describe("NPM integration", function() { ); }); - after("Cleanup tmp files", function(done) { + after("Cleanup tmp files", function (done) { glob("tmp-*", (err, files) => { if (err) done(err); files.forEach(file => fse.removeSync(file)); @@ -61,7 +61,7 @@ describe("NPM integration", function() { }); }); - it("successfully finds the correct source via Sources lookup", async function() { + it("successfully finds the correct source via Sources lookup", async function () { const { body } = await config.resolver.resolve( "fake_source/contracts/Module.sol", config.sources @@ -69,7 +69,7 @@ describe("NPM integration", function() { assert.equal(body, moduleSource); }); - it("errors when module does not exist from any source", async function() { + it("errors when module does not exist from any source", async function () { try { await config.resolver.resolve( "some_source/contracts/SourceDoesNotExist.sol", @@ -88,25 +88,18 @@ describe("NPM integration", function() { assert.fail("Source lookup should have errored but didn't"); }); - it("contract compiliation successfully picks up modules and their dependencies", function(done) { + it("successfully picks up modules and their dependencies during compilation", async function () { this.timeout(10000); - - Contracts.compile( + const { contracts } = await WorkflowCompile.compileAndSave( config.with({ quiet: true - }), - function(err, result) { - if (err) return done(err); - let { contracts } = result; - - var contractNames = Object.keys(contracts); - - assert.include(contractNames, "Parent"); - assert.include(contractNames, "Module"); - assert.include(contractNames, "ModuleDependency"); - - done(); - } + }) ); + const contractNames = contracts.reduce((a, contract) => { + return a.concat(contract.contractName); + }, []); + assert.include(contractNames, "Parent"); + assert.include(contractNames, "Module"); + assert.include(contractNames, "ModuleDependency"); }); }).timeout(10000); diff --git a/packages/core/test/profiler.js b/packages/core/test/profiler.js index 3637c72e463..0a652055017 100644 --- a/packages/core/test/profiler.js +++ b/packages/core/test/profiler.js @@ -8,7 +8,7 @@ var Artifactor = require("@truffle/artifactor"); // TOOD: Move this to @truffle/compile-solidity! -describe("profiler", function() { +describe("profiler", function () { var config; before("Create a sandbox", async () => { @@ -18,7 +18,7 @@ describe("profiler", function() { config.network = "development"; }); - after("Cleanup tmp files", function(done) { + after("Cleanup tmp files", function (done) { glob("tmp-*", (err, files) => { if (err) done(err); files.forEach(file => fs.removeSync(file)); @@ -26,19 +26,14 @@ describe("profiler", function() { }); }); - it("profiles example project successfully", function(done) { - Profiler.required_sources( + it("profiles example project successfully", async () => { + const { allSources, compilationTargets } = await Profiler.requiredSources( config.with({ paths: ["./ConvertLib.sol"], base_path: config.contracts_directory - }), - function(err, allSources, compilationTargets) { - if (err) return done(err); - - assert.equal(Object.keys(allSources).length, 3); - assert.equal(compilationTargets.length, 2); - done(); - } + }) ); + assert.equal(Object.keys(allSources).length, 3); + assert.equal(compilationTargets.length, 2); }); }).timeout(10000); diff --git a/packages/debugger/lib/session/index.js b/packages/debugger/lib/session/index.js index 409d7e2bae6..d3645704fe5 100644 --- a/packages/debugger/lib/session/index.js +++ b/packages/debugger/lib/session/index.js @@ -26,7 +26,7 @@ import solidity from "lib/solidity/selectors"; import rootSaga from "./sagas"; import reducer from "./reducers"; -import { shimBytecode } from "@truffle/compile-solidity/legacy/shims"; +import { Shims } from "@truffle/compile-common"; /** * Debugger Session @@ -158,10 +158,10 @@ export default class Session { //hopefully we can get rid of this step eventually, but not yet if (typeof binary === "object") { - binary = shimBytecode(binary); + binary = Shims.NewToLegacy.forBytecode(binary); } if (typeof deployedBinary === "object") { - deployedBinary = shimBytecode(deployedBinary); + deployedBinary = Shims.NewToLegacy.forBytecode(deployedBinary); } let primarySourceIndex; diff --git a/packages/debugger/test/helpers.js b/packages/debugger/test/helpers.js index 0583ad7fb1f..e994d971d11 100644 --- a/packages/debugger/test/helpers.js +++ b/packages/debugger/test/helpers.js @@ -3,7 +3,7 @@ const debug = debugModule("test:helpers"); import path from "path"; import fs from "fs-extra"; -import Contracts from "@truffle/workflow-compile"; +import WorkflowCompile from "@truffle/workflow-compile"; import Artifactor from "@truffle/artifactor"; import Web3 from "web3"; import Migrate from "@truffle/migrate"; @@ -34,8 +34,7 @@ export async function prepareContracts(provider, sources = {}, migrations) { }; await addContracts(config, sources); - let { contracts, files } = await compile(config); - let contractNames = Object.keys(contracts); + let { contractNames, files } = await compile(config); if (!migrations) { migrations = await defaultMigrations(contractNames); @@ -135,20 +134,38 @@ export async function defaultMigrations(contractNames) { } export async function compile(config) { - return new Promise(function (accept, reject) { - Contracts.compile( - config.with({ - all: true, - quiet: true - }), - function (err, result) { - if (err) return reject(err); - const { contracts, outputs } = result; - debug("result %O", result); - return accept({ contracts, files: outputs.solc }); + const { compilations } = await WorkflowCompile.compileAndSave( + config.with({ + all: true, + quiet: true + }) + ); + const collectedCompilationOutput = compilations.reduce( + (a, compilation) => { + if (compilation.compiler.name === "solc") { + for (const contract of compilation.contracts) { + a.contractNames = a.contractNames.concat(contract.contractName); + } + a.sourceIndexes = a.sourceIndexes.concat(compilation.sourceIndexes); } - ); - }); + return a; + }, + { contractNames: [], sourceIndexes: [] } + ); + const sourceIndexes = collectedCompilationOutput.sourceIndexes.filter( + (item, index) => { + return collectedCompilationOutput.sourceIndexes.indexOf(item) === index; + } + ); + const contractNames = collectedCompilationOutput.contractNames.filter( + (item, index) => { + return collectedCompilationOutput.contractNames.indexOf(item) === index; + } + ); + return { + contractNames, + files: sourceIndexes + }; } export async function migrate(config) { diff --git a/packages/decoder/lib/decoders.ts b/packages/decoder/lib/decoders.ts index 753cc8d58fc..3445926ba18 100644 --- a/packages/decoder/lib/decoders.ts +++ b/packages/decoder/lib/decoders.ts @@ -34,7 +34,7 @@ import { VariableNotFoundError } from "./errors"; //sorry for the untyped imports, but... -const { shimBytecode } = require("@truffle/compile-solidity/legacy/shims"); +const { Shims } = require("@truffle/compile-common"); const SolidityUtils = require("@truffle/solidity-utils"); /** @@ -73,8 +73,10 @@ export class WireDecoder { let deployedContext: Contexts.DecoderContext | undefined = undefined; let constructorContext: Contexts.DecoderContext | undefined = undefined; const compiler = compilation.compiler || contract.compiler; - const deployedBytecode = shimBytecode(contract.deployedBytecode); - const bytecode = shimBytecode(contract.bytecode); + const deployedBytecode = Shims.NewToLegacy.forBytecode( + contract.deployedBytecode + ); + const bytecode = Shims.NewToLegacy.forBytecode(contract.bytecode); if (deployedBytecode && deployedBytecode !== "0x") { deployedContext = Utils.makeContext(contract, node, compilation); this.contexts[deployedContext.context] = deployedContext; @@ -535,8 +537,10 @@ export class WireDecoder { */ public async forArtifact(artifact: Artifact): Promise { - const deployedBytecode = shimBytecode(artifact.deployedBytecode); - const bytecode = shimBytecode(artifact.bytecode); + const deployedBytecode = Shims.NewToLegacy.forBytecode( + artifact.deployedBytecode + ); + const bytecode = Shims.NewToLegacy.forBytecode(artifact.bytecode); const { compilation, contract } = this.compilations.reduce( (foundSoFar: DecoderTypes.CompilationAndContract, compilation) => { @@ -546,7 +550,7 @@ export class WireDecoder { const contractFound = compilation.contracts.find(contract => { if (bytecode) { return ( - shimBytecode(contract.bytecode) === bytecode && + Shims.NewToLegacy.forBytecode(contract.bytecode) === bytecode && contract.contractName === (artifact.contractName || artifact.contract_name) ); @@ -554,7 +558,8 @@ export class WireDecoder { //I'll just go by one of bytecode or deployedBytecode; //no real need to check both return ( - shimBytecode(contract.deployedBytecode) === deployedBytecode && + Shims.NewToLegacy.forBytecode(contract.deployedBytecode) === + deployedBytecode && contract.contractName === (artifact.contractName || artifact.contract_name) ); @@ -1147,7 +1152,9 @@ export class ContractInstanceDecoder { ) ); - const deployedBytecode = shimBytecode(this.contract.deployedBytecode); + const deployedBytecode = Shims.NewToLegacy.forBytecode( + this.contract.deployedBytecode + ); if (!deployedBytecode || deployedBytecode === "0x") { //if this contract does *not* have the deployedBytecode field, then the decoder core diff --git a/packages/decoder/lib/utils.ts b/packages/decoder/lib/utils.ts index e018d04f93b..2b5f40e7e54 100644 --- a/packages/decoder/lib/utils.ts +++ b/packages/decoder/lib/utils.ts @@ -9,7 +9,7 @@ import * as Codec from "@truffle/codec"; import * as Types from "./types"; //sorry for the untyped import, but... -const { shimBytecode } = require("@truffle/compile-solidity/legacy/shims"); +const { Shims } = require("@truffle/compile-common"); //NOTE: Definitely do not use this in real code! For tests only! //for convenience: invokes the nativize method on all the given variables, and changes them to @@ -39,7 +39,7 @@ export function makeContext( const bytecode = isConstructor ? contract.bytecode : contract.deployedBytecode; - const binary: string = shimBytecode(bytecode); + const binary: string = Shims.NewToLegacy.forBytecode(bytecode); const hash = Codec.Conversion.toHexString( Codec.Evm.Utils.keccak256({ type: "string", @@ -90,7 +90,9 @@ function contractKind( //PUSH20 followed by 20 0s, in which case we'll assume it's a library //(note: this will fail to detect libraries from before Solidity 0.4.20) if (contract.deployedBytecode) { - const deployedBytecode = shimBytecode(contract.deployedBytecode); + const deployedBytecode = Shims.NewToLegacy.forBytecode( + contract.deployedBytecode + ); const pushAddressInstruction = ( 0x60 + Codec.Evm.Utils.ADDRESS_SIZE - diff --git a/packages/deployer/test/await.js b/packages/deployer/test/await.js index e6e9cae797d..5648b825184 100644 --- a/packages/deployer/test/await.js +++ b/packages/deployer/test/await.js @@ -5,14 +5,14 @@ const assert = require("assert"); const Deployer = require("../index"); const utils = require("./helpers/utils"); -describe("Deployer (async / await)", function() { +describe("Deployer (async / await)", function () { let owner; let options; let networkId; const provider = ganache.provider(); const web3 = new Web3(provider); - beforeEach(async function() { + beforeEach(async function () { this.timeout(20000); networkId = await web3.eth.net.getId(); const accounts = await web3.eth.getAccounts(); @@ -29,23 +29,23 @@ describe("Deployer (async / await)", function() { network_id: networkId, provider: provider }; - await utils.compile(); }); - afterEach(() => utils.cleanUp()); - afterEach(() => deployer.finish()); + afterEach(async () => { + await utils.cleanUp(); + await deployer.finish(); + }); - it("single deploy()", async function() { + it("single deploy()", async function () { const Example = utils.getContract("Example", provider, networkId, owner); options.contracts = [Example]; deployer = new Deployer(options); - const migrate = async function() { + const migrate = async function () { await deployer.deploy(Example); }; - await deployer.start(); await deployer.then(migrate); @@ -56,7 +56,7 @@ describe("Deployer (async / await)", function() { assert((await example.id()) === "Example"); }); - it("deploy() with interdependent contracts", async function() { + it("deploy() with interdependent contracts", async function () { const Example = utils.getContract("Example", provider, networkId, owner); const UsesExample = utils.getContract( "UsesExample", @@ -68,8 +68,7 @@ describe("Deployer (async / await)", function() { options.contracts = [Example, UsesExample]; deployer = new Deployer(options); - - const migrate = async function() { + const migrate = async function () { await deployer.deploy(Example); await deployer.deploy(UsesExample, Example.address); }; @@ -87,7 +86,7 @@ describe("Deployer (async / await)", function() { assert((await usesExample.other()) === Example.address); }); - it("deployer.link", async function() { + it("deployer.link", async function () { const UsesLibrary = utils.getContract( "UsesLibrary", provider, @@ -104,7 +103,7 @@ describe("Deployer (async / await)", function() { deployer = new Deployer(options); - const migrate = async function() { + const migrate = async function () { await deployer.deploy(IsLibrary); deployer.link(IsLibrary, UsesLibrary); await deployer.deploy(UsesLibrary); diff --git a/packages/deployer/test/helpers/utils.js b/packages/deployer/test/helpers/utils.js index 5415ad8a470..5605d806a7c 100644 --- a/packages/deployer/test/helpers/utils.js +++ b/packages/deployer/test/helpers/utils.js @@ -1,5 +1,5 @@ const TruffleContract = require("@truffle/contract"); -const workflow = require("@truffle/workflow-compile"); +const WorkflowCompile = require("@truffle/workflow-compile"); const path = require("path"); const fs = require("fs-extra"); @@ -13,33 +13,30 @@ const utils = { buildDir: path.join(__dirname, "../build"), sourcesDir: path.join(__dirname, "../sources"), - compile: async function() { + compile: async function () { const config = { contracts_build_directory: utils.buildDir, contracts_directory: utils.sourcesDir }; - - return new Promise((accept, reject) => { - workflow.compile(config, err => (err ? reject(err) : accept())); - }); + await WorkflowCompile.compileAndSave(config); }, - evm_mine: function(web3) { - return new Promise(function(accept, reject) { + evm_mine: function (web3) { + return new Promise(function (accept, reject) { web3.currentProvider.send( { jsonrpc: "2.0", method: "evm_mine", id: new Date().getTime() }, - function(err, result) { + function (err, result) { err ? reject(err) : accept(result); } ); }); }, - startAutoMine: function(web3, interval) { + startAutoMine: function (web3, interval) { utils.miningId = setInterval(async () => { await utils.evm_mine(web3); }, interval); @@ -51,7 +48,7 @@ const utils = { cleanUp: () => fs.removeSync(utils.buildDir), - getContract: function(name, provider, networkId, account) { + getContract: function (name, provider, networkId, account) { const json = require(`../build/${name}`); const contract = TruffleContract(json); contract.setProvider(provider); diff --git a/packages/events/defaultSubscribers/compile.js b/packages/events/defaultSubscribers/compile.js index d2c52479a4d..8f7d9764c9b 100644 --- a/packages/events/defaultSubscribers/compile.js +++ b/packages/events/defaultSubscribers/compile.js @@ -1,31 +1,31 @@ const OS = require("os"); module.exports = { - initialization: function() { + initialization: function () { this.logger = console; }, handlers: { "compile:start": [ - function() { + function () { this.logger.log(OS.EOL + `Compiling your contracts...`); this.logger.log(`===========================`); } ], "compile:succeed": [ - function({ contractsBuildDirectory, compilersInfo }) { - if (Object.keys(compilersInfo).length > 0) { + function ({ contractsBuildDirectory, compilers }) { + if (compilers.length > 0) { this.logger.log(`> Artifacts written to ${contractsBuildDirectory}`); this.logger.log(`> Compiled successfully using:`); - const maxLength = Object.keys(compilersInfo) - .map(name => name.length) + const maxLength = compilers + .map(({ name }) => name.length) .reduce((max, length) => (length > max ? length : max), 0); - for (const name in compilersInfo) { - const padding = " ".repeat(maxLength - name.length); + for (const compiler of compilers) { + const padding = " ".repeat(maxLength - compiler.name.length); this.logger.log( - ` - ${name}:${padding} ${compilersInfo[name].version}` + ` - ${compiler.name}:${padding} ${compiler.version}` ); } } @@ -33,7 +33,7 @@ module.exports = { } ], "compile:sourcesToCompile": [ - function({ sourceFileNames }) { + function ({ sourceFileNames }) { if (!sourceFileNames) return; sourceFileNames.forEach(sourceFileName => this.logger.log("> Compiling " + sourceFileName) @@ -41,13 +41,13 @@ module.exports = { } ], "compile:warnings": [ - function({ warnings }) { + function ({ warnings }) { this.logger.log("> Compilation warnings encountered:"); this.logger.log(`${OS.EOL} ${warnings.join()}`); } ], "compile:nothingToCompile": [ - function() { + function () { this.logger.log( `> Everything is up to date, there is nothing to compile.` ); diff --git a/packages/external-compile/index.js b/packages/external-compile/index.js index 0296aa381b9..6d221d5300f 100644 --- a/packages/external-compile/index.js +++ b/packages/external-compile/index.js @@ -3,12 +3,13 @@ const debug = require("debug")("external-compile"); const { exec, execSync } = require("child_process"); const resolve = require("path").resolve; -const { callbackify, promisify } = require("util"); +const { promisify } = require("util"); const glob = promisify(require("glob")); const fs = require("fs"); const expect = require("@truffle/expect"); const Schema = require("@truffle/contract-schema"); const web3Utils = require("web3-utils"); +const { Shims } = require("@truffle/compile-common"); const DEFAULT_ABI = [ { @@ -84,7 +85,7 @@ function* bufferLines() { * run a command, forwarding data to arbitrary logger. * invokes callback when process exits, error on nonzero exit code. */ -const runCommand = promisify(function(command, options, callback) { +const runCommand = promisify(function (command, options, callback) { const { cwd, logger, input } = options; const child = exec(command, { cwd, input }); @@ -108,7 +109,7 @@ const runCommand = promisify(function(command, options, callback) { child.stdout.on("data", data => log(data.toString())); child.stderr.on("data", data => warn(data.toString())); - child.on("close", function(code) { + child.on("close", function (code) { // close streams to flush unterminated lines log(null); warn(null); @@ -147,11 +148,11 @@ function decodeContents(contents) { } async function processTargets(targets, cwd, logger) { - const contracts = {}; + const contracts = []; for (let target of targets) { let targetContracts = await processTarget(target, cwd, logger); - for (let [name, contract] of Object.entries(targetContracts)) { - contracts[name] = Schema.validate(contract); + for (let contract of Object.values(targetContracts)) { + contracts.push(Schema.validate(contract)); } } @@ -241,34 +242,64 @@ async function processTarget(target, cwd, logger) { } } -const compile = callbackify(async function(options) { - if (options.logger == null) { - options.logger = console; - } - - expect.options(options, ["compilers"]); - expect.options(options.compilers, ["external"]); - expect.options(options.compilers.external, ["command", "targets"]); - - const { command, targets } = options.compilers.external; - const cwd = - options.compilers.external.workingDirectory || - options.compilers.external.working_directory || // just in case - options.working_directory; - const logger = options.logger; - - debug("running compile command: %s", command); - await runCommand(command, { cwd, logger }); - - return await processTargets(targets, cwd, logger); -}); - -// required public interface -compile.all = compile; -compile.necessary = compile; +const Compile = { + async all(options) { + return await Compile.sources({ + // currently you cannot specify sources here + sources: undefined, + options + }); + }, + + async necessary(options) { + return await Compile.sources({ + // currently you cannot specify sources here + sources: undefined, + options + }); + }, + + // the `sources` argument here is currently unused as the user is + // responsible for dealing with compiling their sources + async sources({ sources, options }) { + if (options.logger == null) { + options.logger = console; + } -// specific exports -compile.DEFAULT_ABI = DEFAULT_ABI; -compile.processTarget = processTarget; + expect.options(options, ["compilers"]); + expect.options(options.compilers, ["external"]); + expect.options(options.compilers.external, ["command", "targets"]); + + const { command, targets } = options.compilers.external; + const cwd = + options.compilers.external.workingDirectory || + options.compilers.external.working_directory || // just in case + options.working_directory; + const logger = options.logger; + + debug("running compile command: %s", command); + await runCommand(command, { cwd, logger }); + + const contracts = await processTargets(targets, cwd, logger); + return { + compilations: [ + { + contracts: contracts.map(Shims.LegacyToNew.forContract), + // sourceIndexes is empty because we have no way of + // knowing for certain the source paths for the contracts + sourceIndexes: [], + compiler: { + name: "external", + version: undefined + } + } + ] + }; + } +}; -module.exports = compile; +module.exports = { + Compile, + DEFAULT_ABI, + processTarget +}; diff --git a/packages/external-compile/test/compilation.js b/packages/external-compile/test/compilation.js new file mode 100644 index 00000000000..f1a659a75bb --- /dev/null +++ b/packages/external-compile/test/compilation.js @@ -0,0 +1,30 @@ +const path = require("path"); +const assert = require("assert"); +const { Compile } = require("../"); + +describe("compile(options)", () => { + const options = { + compilers: { + external: { + command: "echo 'dummy command!'", + targets: [ + { + path: path.resolve("./test/sources/A.json") + } + ] + } + } + }; + + it("outputs an object with an array of compilations", async () => { + const result = await Compile.all(options); + assert(Array.isArray(result.compilations)); + }); + + it("returns contracts with bytecode in 'new' format (non-string)", async () => { + const result = await Compile.all(options); + const [{ contracts }] = result.compilations; + assert(typeof contracts[0].bytecode !== "string"); + assert(typeof contracts[0].bytecode === "object"); + }); +}); diff --git a/packages/external-compile/test/sources/A.json b/packages/external-compile/test/sources/A.json new file mode 100644 index 00000000000..f013a73be98 --- /dev/null +++ b/packages/external-compile/test/sources/A.json @@ -0,0 +1,205 @@ +{ + "contractName": "A", + "abi": [], + "metadata": "{\"compiler\":{\"version\":\"0.6.12+commit.27d51765\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"/Users/tyler/src/work/reproduce/2020/09/09/test/contracts/A.sol\":\"A\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"/Users/tyler/src/work/reproduce/2020/09/09/test/contracts/A.sol\":{\"keccak256\":\"0xe1ce9fb46a4d91606e1f4b2c0b8ef59f980e1df473d29a714fc4fe37901653eb\",\"urls\":[\"bzz-raw://11a00dc8a3664b30c6b884f80a4ccc8934668098b65bc137a95f2f25d174b4af\",\"dweb:/ipfs/QmdF7tpekfz6SoJCBiqqTAtqbKcdcQGEppA2ZApKBQGLeW\"]}},\"version\":1}", + "bytecode": "0x60806040526005600055348015601457600080fd5b50603f8060226000396000f3fe6080604052600080fdfea264697066735822122054305d3698403f70e4fdf176417f1869c05a4ea315553b29b8d92961c56e24f564736f6c634300060c0033", + "deployedBytecode": "0x6080604052600080fdfea264697066735822122054305d3698403f70e4fdf176417f1869c05a4ea315553b29b8d92961c56e24f564736f6c634300060c0033", + "immutableReferences": {}, + "sourceMap": "25:33:0:-:0;;;54:1;40:15;;25:33;;;;;;;;;;;;;;;;", + "deployedSourceMap": "25:33:0:-:0;;;;;", + "source": "pragma solidity ^0.6.0;\n\ncontract A {\n uint possum = 5;\n}\n\n", + "sourcePath": "/Users/tyler/src/work/reproduce/2020/09/09/test/contracts/A.sol", + "ast": { + "absolutePath": "/Users/tyler/src/work/reproduce/2020/09/09/test/contracts/A.sol", + "exportedSymbols": { + "A": [ + 5 + ] + }, + "id": 6, + "license": null, + "nodeType": "SourceUnit", + "nodes": [ + { + "id": 1, + "literals": [ + "solidity", + "^", + "0.6", + ".0" + ], + "nodeType": "PragmaDirective", + "src": "0:23:0" + }, + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 5, + "linearizedBaseContracts": [ + 5 + ], + "name": "A", + "nodeType": "ContractDefinition", + "nodes": [ + { + "constant": false, + "id": 4, + "mutability": "mutable", + "name": "possum", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 5, + "src": "40:15:0", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "40:4:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "value": { + "argumentTypes": null, + "hexValue": "35", + "id": 3, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "54:1:0", + "subdenomination": null, + "typeDescriptions": { + "typeIdentifier": "t_rational_5_by_1", + "typeString": "int_const 5" + }, + "value": "5" + }, + "visibility": "internal" + } + ], + "scope": 6, + "src": "25:33:0" + } + ], + "src": "0:60:0" + }, + "legacyAST": { + "absolutePath": "/Users/tyler/src/work/reproduce/2020/09/09/test/contracts/A.sol", + "exportedSymbols": { + "A": [ + 5 + ] + }, + "id": 6, + "license": null, + "nodeType": "SourceUnit", + "nodes": [ + { + "id": 1, + "literals": [ + "solidity", + "^", + "0.6", + ".0" + ], + "nodeType": "PragmaDirective", + "src": "0:23:0" + }, + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": null, + "fullyImplemented": true, + "id": 5, + "linearizedBaseContracts": [ + 5 + ], + "name": "A", + "nodeType": "ContractDefinition", + "nodes": [ + { + "constant": false, + "id": 4, + "mutability": "mutable", + "name": "possum", + "nodeType": "VariableDeclaration", + "overrides": null, + "scope": 5, + "src": "40:15:0", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 2, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "40:4:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "value": { + "argumentTypes": null, + "hexValue": "35", + "id": 3, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "54:1:0", + "subdenomination": null, + "typeDescriptions": { + "typeIdentifier": "t_rational_5_by_1", + "typeString": "int_const 5" + }, + "value": "5" + }, + "visibility": "internal" + } + ], + "scope": 6, + "src": "25:33:0" + } + ], + "src": "0:60:0" + }, + "compiler": { + "name": "solc", + "version": "0.6.12+commit.27d51765.Emscripten.clang" + }, + "networks": {}, + "schemaVersion": "3.2.4", + "updatedAt": "2020-09-10T17:52:07.249Z", + "devdoc": { + "kind": "dev", + "methods": {}, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + } +} diff --git a/packages/external-compile/test/test_targets.js b/packages/external-compile/test/test_targets.js index a9b65b14460..fe7ba319b40 100644 --- a/packages/external-compile/test/test_targets.js +++ b/packages/external-compile/test/test_targets.js @@ -24,12 +24,12 @@ describe("Compilation Targets", () => { const target = { properties: { - contractName, - }, + contractName + } }; const expected = { - [contractName]: { contractName, abi }, + [contractName]: { contractName, abi } }; const actual = await processTarget(target, cwd); @@ -46,11 +46,11 @@ describe("Compilation Targets", () => { const target = { properties: { - contractName, + contractName }, fileProperties: { - bytecode: bytecodeFile, - }, + bytecode: bytecodeFile + } }; const processed = await processTarget(target, cwd); @@ -67,11 +67,11 @@ describe("Compilation Targets", () => { const target = { properties: { - contractName, + contractName }, fileProperties: { - bytecode: bytecodeFile, - }, + bytecode: bytecodeFile + } }; const processed = await processTarget(target, cwd); @@ -88,8 +88,8 @@ describe("Compilation Targets", () => { const target = { fileProperties: { - contractName: contractNameFile, - }, + contractName: contractNameFile + } }; const processed = await processTarget(target, cwd); @@ -106,11 +106,11 @@ describe("Compilation Targets", () => { const target = { properties: { - contractName, + contractName }, fileProperties: { - bytecode: bytecodeFile, - }, + bytecode: bytecodeFile + } }; const processed = await processTarget(target, cwd); diff --git a/packages/truffle/test/scenarios/commands/exec.js b/packages/truffle/test/scenarios/commands/exec.js index 7222f016578..aa34cf36312 100644 --- a/packages/truffle/test/scenarios/commands/exec.js +++ b/packages/truffle/test/scenarios/commands/exec.js @@ -1,26 +1,26 @@ -var MemoryLogger = require("../memorylogger"); -var CommandRunner = require("../commandrunner"); -var fs = require("fs"); -var path = require("path"); -var assert = require("assert"); -var Server = require("../server"); -var Reporter = require("../reporter"); -var sandbox = require("../sandbox"); +const MemoryLogger = require("../memorylogger"); +const CommandRunner = require("../commandrunner"); +const fs = require("fs"); +const path = require("path"); +const assert = require("assert"); +const Server = require("../server"); +const Reporter = require("../reporter"); +const sandbox = require("../sandbox"); -describe("truffle exec [ @standalone ]", function() { - var config; - var project = path.join(__dirname, "../../sources/exec"); - var logger = new MemoryLogger(); +describe("truffle exec [ @standalone ]", function () { + let config; + const project = path.join(__dirname, "../../sources/exec"); + const logger = new MemoryLogger(); - before("set up the server", function(done) { + before("set up the server", function (done) { Server.start(done); }); - after("stop server", function(done) { + after("stop server", function (done) { Server.stop(done); }); - beforeEach("set up sandbox", function() { + beforeEach("set up sandbox", function () { this.timeout(10000); return sandbox.create(project).then(conf => { config = conf; @@ -32,7 +32,7 @@ describe("truffle exec [ @standalone ]", function() { }); }); - it("runs script after compiling", async function() { + it("runs script after compiling", async function () { this.timeout(30000); await CommandRunner.run("compile", config); assert( @@ -47,7 +47,7 @@ describe("truffle exec [ @standalone ]", function() { }); // Check accuracy of next test - it("errors when run without compiling", async function() { + it("errors when run without compiling", async function () { this.timeout(30000); try { await CommandRunner.run("exec script.js", config); @@ -57,7 +57,7 @@ describe("truffle exec [ @standalone ]", function() { } }); - it("succeeds when -c flag is set", async function() { + it("succeeds when -c flag is set", async function () { this.timeout(30000); await CommandRunner.run("exec -c script.js", config); const output = logger.contents(); diff --git a/packages/truffle/test/sources/exec/script.js b/packages/truffle/test/sources/exec/script.js index 21831d7abbb..ac389cdb0cc 100644 --- a/packages/truffle/test/sources/exec/script.js +++ b/packages/truffle/test/sources/exec/script.js @@ -1,8 +1,7 @@ -const Executable = artifacts.require('Executable'); +const Executable = artifacts.require("Executable"); -const exec = function(){ - Executable - .new() +const exec = function () { + Executable.new() .then(instance => instance.x()) .then(val => { console.log(parseInt(val)); @@ -11,4 +10,4 @@ const exec = function(){ .catch(err => process.exit(1)); }; -module.exports = exec; \ No newline at end of file +module.exports = exec; diff --git a/packages/workflow-compile/index.js b/packages/workflow-compile/index.js index db661f405ef..d4e520df73e 100644 --- a/packages/workflow-compile/index.js +++ b/packages/workflow-compile/index.js @@ -1,3 +1,114 @@ -// LATER -// module.exports = require("./new"); -module.exports = require("./legacy"); +const debug = require("debug")("workflow-compile"); +const fse = require("fs-extra"); +const { prepareConfig } = require("./utils"); +const { Shims } = require("@truffle/compile-common"); +const { + reportCompilationStarted, + reportNothingToCompile, + reportCompilationFinished +} = require("./reports"); + +const SUPPORTED_COMPILERS = { + solc: require("@truffle/compile-solidity").Compile, + vyper: require("@truffle/compile-vyper").Compile, + external: require("@truffle/external-compile").Compile +}; + +async function compile(config) { + // determine compiler(s) to use + // + const compilers = config.compiler + ? config.compiler === "none" + ? [] + : [config.compiler] + : Object.keys(config.compilers); + + // invoke compilers + // + const rawCompilations = await Promise.all( + compilers.map(async name => { + const Compile = SUPPORTED_COMPILERS[name]; + if (!Compile) throw new Error("Unsupported compiler: " + name); + + const compileMethod = + config.all === true || config.compileAll === true + ? Compile.all + : Compile.necessary; + + return await compileMethod(config); + }) + ); + + // collect results - rawCompilations is CompilerResult[] + // flatten the array and remove compilations without results + const compilations = rawCompilations.reduce((a, compilerResult) => { + compilerResult.compilations.forEach(compilation => { + if (compilation.contracts.length > 0) { + a = a.concat(compilation); + } + }); + return a; + }, []); + + const contracts = compilations.reduce((a, compilation) => { + return a.concat(compilation.contracts); + }, []); + + // return WorkflowCompileResult + return { contracts, compilations }; +} + +const Contracts = { + async compile(options) { + const config = prepareConfig(options); + + if (config.events) config.events.emit("compile:start"); + + const { contracts, compilations } = await compile(config); + + const compilers = compilations + .reduce((a, compilation) => { + return a.concat(compilation.compiler); + }, []) + .filter(compiler => compiler); + + if (contracts.length === 0 && config.events) { + config.events.emit("compile:nothingToCompile"); + } + + if (config.events) { + config.events.emit("compile:succeed", { + contractsBuildDirectory: config.contracts_build_directory, + compilers + }); + } + return { + contracts, + compilations + }; + }, + + async compileAndSave(options) { + const { contracts, compilations } = await this.compile(options); + await this.save(options, { contracts, compilations }); + return { + contracts, + compilations + }; + }, + + reportCompilationStarted, + reportCompilationFinished, + reportNothingToCompile, + + async save(options, { contracts, _compilations }) { + const config = prepareConfig(options); + + await fse.ensureDir(config.contracts_build_directory); + + const artifacts = contracts.map(Shims.NewToLegacy.forContract); + await config.artifactor.saveAll(artifacts); + } +}; + +module.exports = Contracts; diff --git a/packages/workflow-compile/new/index.js b/packages/workflow-compile/new/index.js deleted file mode 100644 index 6451a02dd9c..00000000000 --- a/packages/workflow-compile/new/index.js +++ /dev/null @@ -1,120 +0,0 @@ -const debug = require("debug")("workflow-compile:new"); -const fse = require("fs-extra"); -const { prepareConfig } = require("../utils"); -const { shimLegacy } = require("../shims"); -const { shimContract } = require("@truffle/compile-solidity/legacy/shims"); -const { - reportCompilationStarted, - reportNothingToCompile, - reportCompilationFinished, -} = require("../reports"); - -const SUPPORTED_COMPILERS = { - solc: { - compiler: require("@truffle/compile-solidity/new"), - }, - vyper: { - compiler: require("@truffle/compile-vyper"), - legacy: true, - }, - external: { - compiler: require("@truffle/external-compile"), - legacy: true, - }, -}; - -async function compile(config) { - // determine compiler(s) to use - // - - const compilers = config.compiler - ? config.compiler === "none" - ? [] - : [config.compiler] - : Object.keys(config.compilers); - - // invoke compilers - // - - const rawCompilations = await Promise.all( - compilers.map(async name => { - const { compiler, legacy } = SUPPORTED_COMPILERS[name] || {}; - if (!compiler) throw new Error("Unsupported compiler: " + name); - - const method = - config.all === true || config.compileAll === true - ? compiler.all - : compiler.necessary; - - const compile = legacy ? shimLegacy(method) : method; - - return { - [name]: await compile(config), - }; - }) - ); - - // collect results - // - - const compilations = rawCompilations.reduce( - (a, b) => Object.assign({}, a, b), - {} - ); - - const [compilerUsed] = Object.values(compilations) - .map(({ compilerInfo }) => compilerInfo) - .filter(compilerInfo => compilerInfo); - - const contracts = Object.values(compilations) - .map(({ contracts }) => contracts) - .reduce((a, b) => [...a, ...b], []); - - return { contracts, compilations, compilerUsed }; -} - -const Contracts = { - async compile(options) { - const config = prepareConfig(options); - - if (config.events) config.events.emit("compile:start"); - - const { contracts, compilations, compilerUsed } = await compile(config); - - if (compilerUsed) { - config.compilersInfo[compilerUsed.name] = { - version: compilerUsed.version, - }; - } - - if (contracts.length === 0 && config.events) { - config.events.emit("compile:nothingToCompile"); - } - - if (config.events) { - config.events.emit("compile:succeed", { - contractsBuildDirectory: config.contracts_build_directory, - compilersInfo: config.compilersInfo, - }); - } - return { - contracts, - compilations, - }; - }, - - reportCompilationStarted, - reportCompilationFinished, - reportNothingToCompile, - - async save(options, contracts) { - const config = prepareConfig(options); - - await fse.ensureDir(config.contracts_build_directory); - - const artifacts = contracts.map(shimContract); - await config.artifactor.saveAll(artifacts); - }, -}; - -module.exports = Contracts; diff --git a/packages/workflow-compile/test/index.js b/packages/workflow-compile/test/index.js index 05ba45c1880..d36e0f85436 100644 --- a/packages/workflow-compile/test/index.js +++ b/packages/workflow-compile/test/index.js @@ -1,4 +1,4 @@ -const Contracts = require("../new"); +const Contracts = require("../"); const assert = require("assert"); const { existsSync, removeSync } = require("fs-extra"); const { join } = require("path"); @@ -37,8 +37,7 @@ describe("Contracts.compile", () => { describe("when config.all is true", () => { it("recompiles all contracts in contracts_directory", async () => { // initial compile - const { contracts } = await Contracts.compile(config); - await Contracts.save(config, contracts); + const { contracts } = await Contracts.compileAndSave(config); let contractName = contracts[0].contractName; assert( @@ -47,11 +46,10 @@ describe("Contracts.compile", () => { // compile again config.all = true; - const { compilations } = await Contracts.compile(config); - await Contracts.save(config, contracts); + const { compilations } = await Contracts.compileAndSave(config); assert( - compilations.solc.sourceIndexes[0] === + compilations[0].sourceIndexes[0] === join( `${process.cwd()}/${config.contracts_directory}/${contractName}.sol` ) diff --git a/packages/workflow-compile/test/shims.js b/packages/workflow-compile/test/shims.js deleted file mode 100644 index 854850fb49f..00000000000 --- a/packages/workflow-compile/test/shims.js +++ /dev/null @@ -1,58 +0,0 @@ -const { assert } = require("chai"); - -const { shimBytecode } = require("@truffle/workflow-compile/shims"); - -describe("shimBytecode", () => { - it("removes 0x", function() { - const bytes = "ffffffff"; - const expected = { - bytes, - linkReferences: [] - }; - - assert.deepEqual(shimBytecode(`0x${bytes}`), expected); - }); - - it("externalizes a link reference in underscores format", function() { - // 0 1 2 3 4 5 6 7 8 9 - const bytecode = "0x00__hello_________00"; - - const expected = { - // 0 1 2 3 4 5 6 7 8 9 - bytes: "00000000000000000000", - linkReferences: [ - { - offsets: [1], - length: 8, - name: "hello" - } - ] - }; - - assert.deepEqual(shimBytecode(bytecode), expected); - }); - - it("externalizes two different link references", function() { - // 0 1 2 3 4 5 6 7 8 9 - const bytecode = "0x__hi____00__there_00"; - - const expected = { - // 0 1 2 3 4 5 6 7 8 9 - bytes: "00000000000000000000", - linkReferences: [ - { - offsets: [0], - length: 4, - name: "hi" - }, - { - offsets: [5], - length: 4, - name: "there" - } - ] - }; - - assert.deepEqual(shimBytecode(bytecode), expected); - }); -});