From f2f9bd2e7ac1372073644a4e30a5d99e8c57fbb1 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 10 Apr 2018 13:44:15 +0200 Subject: Revert "Merge pull request #493 from hysz/features/deployer/multipleCodebaseSupport" This reverts commit 70d403e6f8c56bc70e6d3471a770b9bbff5d72e7, reversing changes made to 073bf738ddb271b6b4158798baf4cac3cb0608e9. --- packages/deployer/src/cli.ts | 37 +++-------- packages/deployer/src/compiler.ts | 107 ++++++++++---------------------- packages/deployer/src/utils/compiler.ts | 65 +++---------------- packages/deployer/src/utils/contract.ts | 4 +- packages/deployer/src/utils/encoder.ts | 4 +- packages/deployer/src/utils/types.ts | 19 +----- 6 files changed, 59 insertions(+), 177 deletions(-) (limited to 'packages/deployer/src') diff --git a/packages/deployer/src/cli.ts b/packages/deployer/src/cli.ts index 7b32187c4..f1cf56416 100644 --- a/packages/deployer/src/cli.ts +++ b/packages/deployer/src/cli.ts @@ -11,7 +11,7 @@ import * as yargs from 'yargs'; import { commands } from './commands'; import { constants } from './utils/constants'; import { consoleReporter } from './utils/error_reporter'; -import { CliOptions, CompilerOptions, ContractDirectory, DeployerOptions } from './utils/types'; +import { CliOptions, CompilerOptions, DeployerOptions } from './utils/types'; const DEFAULT_OPTIMIZER_ENABLED = false; const DEFAULT_CONTRACTS_DIR = path.resolve('src/contracts'); @@ -27,7 +27,7 @@ const DEFAULT_CONTRACTS_LIST = '*'; */ async function onCompileCommandAsync(argv: CliOptions): Promise { const opts: CompilerOptions = { - contractDirs: getContractDirectoriesFromList(argv.contractDirs), + contractsDir: argv.contractsDir, networkId: argv.networkId, optimizerEnabled: argv.shouldOptimize, artifactsDir: argv.artifactsDir, @@ -45,7 +45,7 @@ async function onDeployCommandAsync(argv: CliOptions): Promise { const web3Wrapper = new Web3Wrapper(provider); const networkId = await web3Wrapper.getNetworkIdAsync(); const compilerOpts: CompilerOptions = { - contractDirs: getContractDirectoriesFromList(argv.contractDirs), + contractsDir: argv.contractsDir, networkId, optimizerEnabled: argv.shouldOptimize, artifactsDir: argv.artifactsDir, @@ -67,29 +67,6 @@ async function onDeployCommandAsync(argv: CliOptions): Promise { const deployerArgs = deployerArgsString.split(','); await commands.deployAsync(argv.contract as string, deployerArgs, deployerOpts); } -/** - * Creates a set of contracts to compile. - * @param contractDirectoriesList Comma separated list of contract directories - * @return Set of contract directories - */ -function getContractDirectoriesFromList(contractDirectoriesList: string): Set { - const directories = new Set(); - const possiblyNamespacedDirectories = contractDirectoriesList.split(','); - _.forEach(possiblyNamespacedDirectories, namespacedDirectory => { - const directoryComponents = namespacedDirectory.split(':'); - if (directoryComponents.length === 1) { - const directory = { namespace: '', path: directoryComponents[0] }; - directories.add(directory); - } else if (directoryComponents.length === 2) { - const directory = { namespace: directoryComponents[0], path: directoryComponents[1] }; - directories.add(directory); - } else { - throw new Error(`Unable to parse contracts directory: '${namespacedDirectory}'`); - } - }); - - return directories; -} /** * Creates a set of contracts to compile. * @param contracts Comma separated list of contracts to compile @@ -101,7 +78,8 @@ function getContractsSetFromList(contracts: string): Set { } const contractsArray = contracts.split(','); _.forEach(contractsArray, contractName => { - specifiedContracts.add(contractName); + const fileName = `${contractName}${constants.SOLIDITY_FILE_EXTENSION}`; + specifiedContracts.add(fileName); }); return specifiedContracts; } @@ -126,11 +104,10 @@ function deployCommandBuilder(yargsInstance: any) { (() => { const identityCommandBuilder = _.identity; return yargs - .option('contract-dirs', { + .option('contracts-dir', { type: 'string', default: DEFAULT_CONTRACTS_DIR, - description: - "comma separated list of contract directories.\nTo avoid filename clashes, directories should be prefixed with a namespace as follows: 'namespace:/path/to/dir'.", + description: 'path of contracts directory to compile', }) .option('network-id', { type: 'number', diff --git a/packages/deployer/src/compiler.ts b/packages/deployer/src/compiler.ts index e3ecc6c72..ba360cb57 100644 --- a/packages/deployer/src/compiler.ts +++ b/packages/deployer/src/compiler.ts @@ -1,4 +1,4 @@ -import { AbiType, ContractAbi, MethodAbi } from '@0xproject/types'; +import { ContractAbi } from '@0xproject/types'; import { logUtils, promisify } from '@0xproject/utils'; import * as ethUtil from 'ethereumjs-util'; import * as fs from 'fs'; @@ -11,8 +11,6 @@ import solc = require('solc'); import { binPaths } from './solc/bin_paths'; import { - constructContractId, - constructUniqueSourceFileId, createDirIfDoesNotExistAsync, findImportIfExist, getContractArtifactIfExistsAsync, @@ -25,14 +23,11 @@ import { fsWrapper } from './utils/fs_wrapper'; import { CompilerOptions, ContractArtifact, - ContractDirectory, - ContractIdToSourceFileId, ContractNetworkData, ContractNetworks, - ContractSourceDataByFileId, + ContractSourceData, ContractSources, ContractSpecificSourceData, - FunctionNameToSeenCount, } from './utils/types'; import { utils } from './utils/utils'; @@ -44,22 +39,20 @@ const SOLC_BIN_DIR = path.join(__dirname, '..', '..', 'solc_bin'); * to artifact files. */ export class Compiler { - private _contractDirs: Set; + private _contractsDir: string; private _networkId: number; private _optimizerEnabled: boolean; private _artifactsDir: string; // This get's set in the beggining of `compileAsync` function. It's not called from a constructor, but it's the only public method of that class and could as well be. private _contractSources!: ContractSources; private _specifiedContracts: Set = new Set(); - private _contractSourceDataByFileId: ContractSourceDataByFileId = {}; - + private _contractSourceData: ContractSourceData = {}; /** * Recursively retrieves Solidity source code from directory. * @param dirPath Directory to search. - * @param contractBaseDir Base contracts directory of search tree. - * @return Mapping of sourceFilePath to the contract source. + * @return Mapping of contract fileName to contract source. */ - private static async _getContractSourcesAsync(dirPath: string, contractBaseDir: string): Promise { + private static async _getContractSourcesAsync(dirPath: string): Promise { let dirContents: string[] = []; try { dirContents = await fsWrapper.readdirAsync(dirPath); @@ -75,18 +68,14 @@ export class Compiler { encoding: 'utf8', }; const source = await fsWrapper.readFileAsync(contentPath, opts); - if (!_.startsWith(contentPath, contractBaseDir)) { - throw new Error(`Expected content path '${contentPath}' to begin with '${contractBaseDir}'`); - } - const sourceFilePath = contentPath.slice(contractBaseDir.length); - sources[sourceFilePath] = source; - logUtils.log(`Reading ${sourceFilePath} source...`); + sources[fileName] = source; + logUtils.log(`Reading ${fileName} source...`); } catch (err) { logUtils.log(`Could not find file at ${contentPath}`); } } else { try { - const nestedSources = await Compiler._getContractSourcesAsync(contentPath, contractBaseDir); + const nestedSources = await Compiler._getContractSourcesAsync(contentPath); sources = { ...sources, ...nestedSources, @@ -104,7 +93,7 @@ export class Compiler { * @return An instance of the Compiler class. */ constructor(opts: CompilerOptions) { - this._contractDirs = opts.contractDirs; + this._contractsDir = opts.contractsDir; this._networkId = opts.networkId; this._optimizerEnabled = opts.optimizerEnabled; this._artifactsDir = opts.artifactsDir; @@ -116,49 +105,25 @@ export class Compiler { public async compileAsync(): Promise { await createDirIfDoesNotExistAsync(this._artifactsDir); await createDirIfDoesNotExistAsync(SOLC_BIN_DIR); - this._contractSources = {}; - const contractIdToSourceFileId: ContractIdToSourceFileId = {}; - const contractDirs = Array.from(this._contractDirs.values()); - for (const contractDir of contractDirs) { - const sources = await Compiler._getContractSourcesAsync(contractDir.path, contractDir.path); - _.forIn(sources, (source, sourceFilePath) => { - const sourceFileId = constructUniqueSourceFileId(contractDir.namespace, sourceFilePath); - // Record the file's source and data - if (!_.isUndefined(this._contractSources[sourceFileId])) { - throw new Error(`Found duplicate source files with ID '${sourceFileId}'`); - } - this._contractSources[sourceFileId] = source; - // Create a mapping between the contract id and its source file id - const contractId = constructContractId(contractDir.namespace, sourceFilePath); - if (!_.isUndefined(contractIdToSourceFileId[contractId])) { - throw new Error(`Found duplicate contract with ID '${contractId}'`); - } - contractIdToSourceFileId[contractId] = sourceFileId; - }); - } + this._contractSources = await Compiler._getContractSourcesAsync(this._contractsDir); _.forIn(this._contractSources, this._setContractSpecificSourceData.bind(this)); - const specifiedContractIds = this._specifiedContracts.has(ALL_CONTRACTS_IDENTIFIER) - ? _.keys(contractIdToSourceFileId) + const fileNames = this._specifiedContracts.has(ALL_CONTRACTS_IDENTIFIER) + ? _.keys(this._contractSources) : Array.from(this._specifiedContracts.values()); - await Promise.all( - _.map(specifiedContractIds, async contractId => - this._compileContractAsync(contractIdToSourceFileId[contractId]), - ), - ); + for (const fileName of fileNames) { + await this._compileContractAsync(fileName); + } } /** * Compiles contract and saves artifact to artifactsDir. - * @param sourceFileId Unique ID of the source file. + * @param fileName Name of contract with '.sol' extension. */ - private async _compileContractAsync(sourceFileId: string): Promise { + private async _compileContractAsync(fileName: string): Promise { if (_.isUndefined(this._contractSources)) { throw new Error('Contract sources not yet initialized'); } - if (_.isUndefined(this._contractSourceDataByFileId[sourceFileId])) { - throw new Error(`Contract source for ${sourceFileId} not yet initialized`); - } - const contractSpecificSourceData = this._contractSourceDataByFileId[sourceFileId]; - const currentArtifactIfExists = await getContractArtifactIfExistsAsync(this._artifactsDir, sourceFileId); + const contractSpecificSourceData = this._contractSourceData[fileName]; + const currentArtifactIfExists = await getContractArtifactIfExistsAsync(this._artifactsDir, fileName); const sourceHash = `0x${contractSpecificSourceData.sourceHash.toString('hex')}`; const sourceTreeHash = `0x${contractSpecificSourceData.sourceTreeHash.toString('hex')}`; @@ -197,17 +162,16 @@ export class Compiler { } const solcInstance = solc.setupMethods(requireFromString(solcjs, compilerBinFilename)); - logUtils.log(`Compiling ${sourceFileId} with Solidity v${solcVersion}...`); - const source = this._contractSources[sourceFileId]; + logUtils.log(`Compiling ${fileName} with Solidity v${solcVersion}...`); + const source = this._contractSources[fileName]; const input = { - [sourceFileId]: source, + [fileName]: source, }; const sourcesToCompile = { sources: input, }; - const compiled = solcInstance.compile(sourcesToCompile, Number(this._optimizerEnabled), importPath => - findImportIfExist(this._contractSources, sourceFileId, importPath), + findImportIfExist(this._contractSources, importPath), ); if (!_.isUndefined(compiled.errors)) { @@ -229,11 +193,11 @@ export class Compiler { }); } } - const contractName = path.basename(sourceFileId, constants.SOLIDITY_FILE_EXTENSION); - const contractIdentifier = `${sourceFileId}:${contractName}`; + const contractName = path.basename(fileName, constants.SOLIDITY_FILE_EXTENSION); + const contractIdentifier = `${fileName}:${contractName}`; if (_.isUndefined(compiled.contracts[contractIdentifier])) { throw new Error( - `Contract ${contractName} not found in ${sourceFileId}. Please make sure your contract has the same name as it's file name`, + `Contract ${contractName} not found in ${fileName}. Please make sure your contract has the same name as it's file name`, ); } const abi: ContractAbi = JSON.parse(compiled.contracts[contractIdentifier].interface); @@ -243,7 +207,6 @@ export class Compiler { const sourceMapRuntime = compiled.contracts[contractIdentifier].srcmapRuntime; const sources = _.keys(compiled.sources); const updated_at = Date.now(); - const contractNetworkData: ContractNetworkData = { solc_version: solcVersion, keccak256: sourceHash, @@ -280,30 +243,28 @@ export class Compiler { const artifactString = utils.stringifyWithFormatting(newArtifact); const currentArtifactPath = `${this._artifactsDir}/${contractName}.json`; await fsWrapper.writeFileAsync(currentArtifactPath, artifactString); - logUtils.log(`${sourceFileId} artifact saved!`); + logUtils.log(`${fileName} artifact saved!`); } /** * Gets contract dependendencies and keccak256 hash from source. * @param source Source code of contract. - * @param fileId FileId of the contract source file. * @return Object with contract dependencies and keccak256 hash of source. */ - private _setContractSpecificSourceData(source: string, fileId: string): void { - if (!_.isUndefined(this._contractSourceDataByFileId[fileId])) { + private _setContractSpecificSourceData(source: string, fileName: string): void { + if (!_.isUndefined(this._contractSourceData[fileName])) { return; } const sourceHash = ethUtil.sha3(source); const solcVersionRange = parseSolidityVersionRange(source); - const dependencies = parseDependencies(source, fileId); - const sourceTreeHash = this._getSourceTreeHash(fileId, sourceHash, dependencies); - this._contractSourceDataByFileId[fileId] = { + const dependencies = parseDependencies(source); + const sourceTreeHash = this._getSourceTreeHash(fileName, sourceHash, dependencies); + this._contractSourceData[fileName] = { dependencies, solcVersionRange, sourceHash, sourceTreeHash, }; } - /** * Gets the source tree hash for a file and its dependencies. * @param fileName Name of contract file. @@ -315,7 +276,7 @@ export class Compiler { const dependencySourceTreeHashes = _.map(dependencies, dependency => { const source = this._contractSources[dependency]; this._setContractSpecificSourceData(source, dependency); - const sourceData = this._contractSourceDataByFileId[dependency]; + const sourceData = this._contractSourceData[dependency]; return this._getSourceTreeHash(dependency, sourceData.sourceHash, sourceData.dependencies); }); const sourceTreeHashesBuffer = Buffer.concat([sourceHash, ...dependencySourceTreeHashes]); diff --git a/packages/deployer/src/utils/compiler.ts b/packages/deployer/src/utils/compiler.ts index 600495693..d5137d394 100644 --- a/packages/deployer/src/utils/compiler.ts +++ b/packages/deployer/src/utils/compiler.ts @@ -1,4 +1,3 @@ -import { AbiType, ContractAbi, MethodAbi } from '@0xproject/types'; import { logUtils } from '@0xproject/utils'; import * as _ from 'lodash'; import * as path from 'path'; @@ -6,48 +5,8 @@ import * as solc from 'solc'; import { constants } from './constants'; import { fsWrapper } from './fs_wrapper'; -import { ContractArtifact, ContractSources, FunctionNameToSeenCount } from './types'; +import { ContractArtifact, ContractSources } from './types'; -/** - * Constructs a system-wide unique identifier for a source file. - * @param directoryNamespace Namespace of the source file's root contract directory. - * @param sourceFilePath Path to a source file, relative to contractBaseDir. - * @return sourceFileId A system-wide unique identifier for the source file. - */ -export function constructUniqueSourceFileId(directoryNamespace: string, sourceFilePath: string): string { - const namespacePrefix = !_.isEmpty(directoryNamespace) ? `/${directoryNamespace}` : ''; - const sourceFilePathNoLeadingSlash = sourceFilePath.replace(/^\/+/g, ''); - const sourceFileId = `${namespacePrefix}/${sourceFilePathNoLeadingSlash}`; - return sourceFileId; -} -/** - * Constructs a system-wide unique identifier for a dependency file. - * @param dependencyFilePath Path from a sourceFile to a dependency. - * @param contractBaseDir Base contracts directory of search tree. - * @return sourceFileId A system-wide unique identifier for the source file. - */ -export function constructDependencyFileId(dependencyFilePath: string, sourceFilePath: string): string { - if (_.startsWith(dependencyFilePath, '/')) { - // Path of the form /namespace/path/to/dependency.sol - return dependencyFilePath; - } else { - // Dependency is relative to the source file: ./dependency.sol, ../../some/path/dependency.sol, etc. - // Join the two paths to construct a valid source file id: /namespace/path/to/dependency.sol - return path.join(path.dirname(sourceFilePath), dependencyFilePath); - } -} -/** - * Constructs a system-wide unique identifier for a contract. - * @param directoryNamespace Namespace of the source file's root contract directory. - * @param sourceFilePath Path to a source file, relative to contractBaseDir. - * @return sourceFileId A system-wide unique identifier for contract. - */ -export function constructContractId(directoryNamespace: string, sourceFilePath: string): string { - const namespacePrefix = !_.isEmpty(directoryNamespace) ? `${directoryNamespace}:` : ''; - const sourceFileName = path.basename(sourceFilePath, constants.SOLIDITY_FILE_EXTENSION); - const contractId = `${namespacePrefix}${sourceFileName}`; - return contractId; -} /** * Gets contract data on network or returns if an artifact does not exist. * @param artifactsDir Path to the artifacts directory. @@ -123,10 +82,9 @@ export function getNormalizedErrMsg(errMsg: string): string { /** * Parses the contract source code and extracts the dendencies * @param source Contract source code - * @param sourceFilePath File path of the source code. * @return List of dependendencies */ -export function parseDependencies(source: string, sourceFileId: string): string[] { +export function parseDependencies(source: string): string[] { // TODO: Use a proper parser const IMPORT_REGEX = /(import\s)/; const DEPENDENCY_PATH_REGEX = /"([^"]+)"/; // Source: https://github.com/BlockChainCompany/soljitsu/blob/master/lib/shared.js @@ -137,8 +95,8 @@ export function parseDependencies(source: string, sourceFileId: string): string[ const dependencyMatch = line.match(DEPENDENCY_PATH_REGEX); if (!_.isNull(dependencyMatch)) { const dependencyPath = dependencyMatch[1]; - const dependencyId = constructDependencyFileId(dependencyPath, sourceFileId); - dependencies.push(dependencyId); + const basenName = path.basename(dependencyPath); + dependencies.push(basenName); } } }); @@ -149,19 +107,14 @@ export function parseDependencies(source: string, sourceFileId: string): string[ * Callback to resolve dependencies with `solc.compile`. * Throws error if contractSources not yet initialized. * @param contractSources Source codes of contracts. - * @param sourceFileId ID of the source file. - * @param importPath Path of dependency source file. + * @param importPath Path to an imported dependency. * @return Import contents object containing source code of dependency. */ -export function findImportIfExist( - contractSources: ContractSources, - sourceFileId: string, - importPath: string, -): solc.ImportContents { - const dependencyFileId = constructDependencyFileId(importPath, sourceFileId); - const source = contractSources[dependencyFileId]; +export function findImportIfExist(contractSources: ContractSources, importPath: string): solc.ImportContents { + const fileName = path.basename(importPath); + const source = contractSources[fileName]; if (_.isUndefined(source)) { - throw new Error(`Contract source not found for ${dependencyFileId}`); + throw new Error(`Contract source not found for ${fileName}`); } const importContents: solc.ImportContents = { contents: source, diff --git a/packages/deployer/src/utils/contract.ts b/packages/deployer/src/utils/contract.ts index e8dd5218a..9b7baac11 100644 --- a/packages/deployer/src/utils/contract.ts +++ b/packages/deployer/src/utils/contract.ts @@ -1,9 +1,11 @@ import { schemas, SchemaValidator } from '@0xproject/json-schemas'; -import { AbiType, ContractAbi, EventAbi, FunctionAbi, MethodAbi, TxData } from '@0xproject/types'; +import { ContractAbi, EventAbi, FunctionAbi, MethodAbi, TxData } from '@0xproject/types'; import { promisify } from '@0xproject/utils'; import * as _ from 'lodash'; import * as Web3 from 'web3'; +import { AbiType } from './types'; + export class Contract implements Web3.ContractInstance { public address: string; public abi: ContractAbi; diff --git a/packages/deployer/src/utils/encoder.ts b/packages/deployer/src/utils/encoder.ts index 806efbbca..4f62662e1 100644 --- a/packages/deployer/src/utils/encoder.ts +++ b/packages/deployer/src/utils/encoder.ts @@ -1,7 +1,9 @@ -import { AbiDefinition, AbiType, ContractAbi, DataItem } from '@0xproject/types'; +import { AbiDefinition, ContractAbi, DataItem } from '@0xproject/types'; import * as _ from 'lodash'; import * as web3Abi from 'web3-eth-abi'; +import { AbiType } from './types'; + export const encoder = { encodeConstructorArgsFromAbi(args: any[], abi: ContractAbi): string { const constructorTypes: string[] = []; diff --git a/packages/deployer/src/utils/types.ts b/packages/deployer/src/utils/types.ts index 1a866b873..7d131f5ce 100644 --- a/packages/deployer/src/utils/types.ts +++ b/packages/deployer/src/utils/types.ts @@ -18,11 +18,6 @@ export interface ContractNetworks { [key: number]: ContractNetworkData; } -export interface ContractDirectory { - path: string; - namespace: string; -} - export interface ContractNetworkData { solc_version: string; optimizer_enabled: boolean; @@ -45,7 +40,7 @@ export interface SolcErrors { export interface CliOptions extends yargs.Arguments { artifactsDir: string; - contractDirs: string; + contractsDir: string; jsonrpcUrl: string; networkId: number; shouldOptimize: boolean; @@ -56,7 +51,7 @@ export interface CliOptions extends yargs.Arguments { } export interface CompilerOptions { - contractDirs: Set; + contractsDir: string; networkId: number; optimizerEnabled: boolean; artifactsDir: string; @@ -83,11 +78,7 @@ export interface ContractSources { [key: string]: string; } -export interface ContractIdToSourceFileId { - [key: string]: string; -} - -export interface ContractSourceDataByFileId { +export interface ContractSourceData { [key: string]: ContractSpecificSourceData; } @@ -107,8 +98,4 @@ export interface Token { swarmHash: string; } -export interface FunctionNameToSeenCount { - [key: string]: number; -} - export type DoneCallback = (err?: Error) => void; -- cgit v1.2.3