diff --git a/packages/compile-common/src/sources.ts b/packages/compile-common/src/sources.ts index 09117b81e0a..9056d3573bc 100644 --- a/packages/compile-common/src/sources.ts +++ b/packages/compile-common/src/sources.ts @@ -7,19 +7,22 @@ import * as path from "path"; * * @param originalSources - { [originalSourcePath]: contents } * @param originalTargets - originalSourcePath[] + * @param baseDirectory - a directory to remove as a prefix + * @param replacement - what to replace it with * @return { sources, targets, originalSourcePaths } */ export function collectSources( originalSources: Sources, originalTargets: string[] = [], - contractsDirectory: string = "" //only used by Vyper atm + baseDirectory: string = "", + replacement: string = "/" ): CollectedSources { const mappedResults = Object.entries(originalSources) .map(([originalSourcePath, contents]) => ({ originalSourcePath, contents, sourcePath: getPortableSourcePath( - removeRootDirectory(originalSourcePath, contractsDirectory) + replaceRootDirectory(originalSourcePath, baseDirectory, replacement) ) })) .map(({ originalSourcePath, sourcePath, contents }) => ({ @@ -73,12 +76,16 @@ function getPortableSourcePath(sourcePath: string): string { return replacement; } -function removeRootDirectory(sourcePath: string, rootDirectory: string): string { - //make sure root directory doesn't end in a separator - if (rootDirectory.endsWith(path.sep)) { - rootDirectory = rootDirectory.slice(0, -1); //remove last character +function replaceRootDirectory( + sourcePath: string, + rootDirectory: string, + replacement: string +): string { + //make sure root directory ends in a separator + if (!rootDirectory.endsWith(path.sep)) { + rootDirectory = rootDirectory + path.sep; } return sourcePath.startsWith(rootDirectory) - ? sourcePath.slice(rootDirectory.length) //remove prefix + ? replacement + sourcePath.slice(rootDirectory.length) //remove prefix : sourcePath; } diff --git a/packages/compile-solidity/run.js b/packages/compile-solidity/run.js index 5ab02893f07..b1105835840 100644 --- a/packages/compile-solidity/run.js +++ b/packages/compile-solidity/run.js @@ -14,11 +14,18 @@ async function run(rawSources, options, language = "Solidity") { // Ensure sources have operating system independent paths // i.e., convert backslashes to forward slashes; things like C: are left intact. + // we also strip the project root (to avoid it appearing in metadata) + // and replace it with "project://" const { sources, targets, originalSourcePaths - } = Common.Sources.collectSources(rawSources, options.compilationTargets); + } = Common.Sources.collectSources( + rawSources, + options.compilationTargets, + options.working_directory, + "project://" + ); // construct solc compiler input const compilerInput = prepareCompilerInput({ diff --git a/packages/compile-solidity/test/test_metadata.js b/packages/compile-solidity/test/test_metadata.js new file mode 100644 index 00000000000..94c9f615ae4 --- /dev/null +++ b/packages/compile-solidity/test/test_metadata.js @@ -0,0 +1,72 @@ +const debug = require("debug")("compile:test:test_metadata"); +const fs = require("fs"); +const path = require("path"); +const { Compile } = require("@truffle/compile-solidity"); +const CompilerSupplier = require("../compilerSupplier"); +const assert = require("assert"); +const { findOne } = require("./helpers"); +const workingDirectory = "/home/fakename/truffleproject"; +const compileOptions = { + working_directory: workingDirectory, + compilers: { + solc: { + version: "0.4.25", + settings: { + optimizer: { + enabled: false, + runs: 200 + } + } + } + }, + quiet: true +}; +const supplierOptions = { + solcConfig: compileOptions.compilers.solc, + events: { + emit: () => {} + } +}; + +describe("Compile - solidity ^0.4.0", function () { + this.timeout(5000); // solc + let source = null; + let solc = null; // gets loaded via supplier + + before("get solc", async function () { + this.timeout(40000); + + const supplier = new CompilerSupplier(supplierOptions); + ({ solc } = await supplier.load()); + }); + + describe("Metadata", function () { + before("get code", function () { + source = fs.readFileSync( + path.join(__dirname, "./sources/v0.4.x/SimpleOrdered.sol"), + "utf-8" + ); + }); + + it("does not include absolute paths in metadata", async function () { + const sourcePath = `${workingDirectory}/contracts/SimpleOrdered.sol`; + const sources = { [sourcePath]: source }; + + const { compilations } = await Compile.sources({ + sources, + options: compileOptions + }); + + const SimpleOrdered = findOne("SimpleOrdered", compilations[0].contracts); + const metadata = JSON.parse(SimpleOrdered.metadata); + const metadataSources = Object.keys(metadata.sources); + const metadataTargets = Object.keys(metadata.settings.compilationTarget); + const metadataPaths = metadataSources.concat(metadataTargets); + debug("metadataPaths: %O", metadataPaths); + assert(metadataPaths.every( + sourcePath => sourcePath.startsWith("project://") && + !sourcePath.includes(workingDirectory) + )); + }); + }); +}); diff --git a/packages/compile-vyper/vyper-json.js b/packages/compile-vyper/vyper-json.js index a9789da83c1..30e11e65603 100644 --- a/packages/compile-vyper/vyper-json.js +++ b/packages/compile-vyper/vyper-json.js @@ -12,6 +12,10 @@ const partition = require("lodash.partition"); function compileJson({ sources: rawSources, options, version, command }) { const compiler = { name: "vyper", version }; + //in order to better support absolute Vyper imports, we pretend that + //the contracts directory is the root directory. note this means that + //if an imported source from somewhere other than FS uses an absolute + //import to refer to its own project root, it won't work. But, oh well. const { sources, targets, @@ -33,11 +37,6 @@ function compileJson({ sources: rawSources, options, version, command }) { : sourcePath => !sourcePath.endsWith(".json") ); - //in order to better support absolute Vyper imports, we pretend that - //the contracts directory is the root directory. note this means that - //if an imported source from somewhere other than FS uses an absolute - //import to refer to its own project root, it won't work. But, oh well. - const properSources = Object.assign( {}, ...properSourcePaths.map(sourcePath => ({