Browse Source

Caching and removing check compiler on init to help WSL users

Juan Blanco 10 months ago
parent
commit
524284b1be

+ 2 - 2
package.json

@@ -10,7 +10,7 @@
     "nethereum",
     "solhint"
   ],
-  "version": "0.0.72",
+  "version": "0.0.73",
   "publisher": "JuanBlanco",
   "engines": {
     "vscode": "^1.30.0"
@@ -465,4 +465,4 @@
       }
     ]
   }
-}
+}

BIN
solidity-0.0.71.vsix


BIN
solidity-0.0.73.vsix


+ 3 - 2
src/analysers/mythx/commands/analyzeContract.ts

@@ -7,6 +7,7 @@ import {  getFileContent } from '../utils/getFileContent';
 import { getAstData } from '../utils/getAstData';
 import { getContractName } from "../utils/getContractName";
 import {compileActiveContract} from '../../../compileActive';
+import {Compiler} from '../../../compiler';
 
 const { window } = vscode;
 
@@ -18,9 +19,9 @@ const contractNameOption: vscode.InputBoxOptions = {
     prompt: 'Contract Name: ',
 };
 
-export async function analyzeContract(diagnosticCollection: vscode.DiagnosticCollection, fileUri: vscode.Uri): Promise<void> {
+export async function analyzeContract(compiler: Compiler, diagnosticCollection: vscode.DiagnosticCollection, fileUri: vscode.Uri): Promise<void> {
     let contractName;
-    await compileActiveContract().then(async (compiledResults: string[]) => {
+    await compileActiveContract(compiler).then(async (compiledResults: string[]) => {
         if (!compiledResults) {
             throw new Error(`MythX error with compilation.`);
         }

+ 3 - 3
src/compileActive.ts

@@ -1,7 +1,7 @@
 'use strict';
 import * as vscode from 'vscode';
 import * as path from 'path';
-import {compile} from './compiler';
+import {Compiler} from './compiler';
 import {ContractCollection} from './model/contractsCollection';
 import { initialiseProject } from './projectService';
 import { formatPath } from './util';
@@ -13,7 +13,7 @@ export function initDiagnosticCollection(diagnostics: vscode.DiagnosticCollectio
     diagnosticCollection = diagnostics;
 }
 
-export function compileActiveContract(): Promise<Array<string>> {
+export function compileActiveContract(compiler: Compiler): Promise<Array<string>> {
     const editor = vscode.window.activeTextEditor;
 
     if (!editor) {
@@ -42,7 +42,7 @@ export function compileActiveContract(): Promise<Array<string>> {
     const contract = contractsCollection.addContractAndResolveImports(contractPath, contractCode, project);
     const packagesPath = formatPath(project.packagesDir);
 
-    return compile(contractsCollection.getDefaultContractsForCompilation(compilationOptimisation),
+    return compiler.compile(contractsCollection.getDefaultContractsForCompilation(compilationOptimisation),
             diagnosticCollection,
             project.projectPackage.build_dir,
             project.projectPackage.absoluletPath,

+ 3 - 3
src/compileAll.ts

@@ -2,12 +2,12 @@
 import * as vscode from 'vscode';
 import * as fs from 'fs';
 import * as path from 'path';
-import {compile} from './compiler';
+import {Compiler} from './compiler';
 import {ContractCollection} from './model/contractsCollection';
 import { initialiseProject } from './projectService';
 import { formatPath } from './util';
 
-export function compileAllContracts(diagnosticCollection: vscode.DiagnosticCollection) {
+export function compileAllContracts(compiler: Compiler, diagnosticCollection: vscode.DiagnosticCollection) {
 
     // Check if is folder, if not stop we need to output to a bin folder on rootPath
     if (vscode.workspace.workspaceFolders[0] === undefined) {
@@ -58,7 +58,7 @@ export function compileAllContracts(diagnosticCollection: vscode.DiagnosticColle
         });
         const sourceDirPath = formatPath(project.projectPackage.getSolSourcesAbsolutePath());
         const packagesPath = formatPath(project.packagesDir);
-        compile(contractsCollection.getDefaultContractsForCompilation(compilationOptimisation),
+        compiler.compile(contractsCollection.getDefaultContractsForCompilation(compilationOptimisation),
                 diagnosticCollection,
                 project.projectPackage.build_dir,
                 project.projectPackage.absoluletPath,

+ 266 - 257
src/compiler.ts

@@ -8,308 +8,317 @@ import * as https from 'https';
 import { SolcCompiler, compilerType } from './solcCompiler';
 import { errorsToDiagnostics } from './solErrorsToDiaganosticsClient';
 
-function outputErrorsToChannel(outputChannel: vscode.OutputChannel, errors: any) {
-    errors.forEach(error => {
-        outputChannel.appendLine(error.formattedMessage);
-    });
-    outputChannel.show();
-}
 
-let outputChannel: vscode.OutputChannel;
-let solc: SolcCompiler;
+export class Compiler {
+    private solcCachePath: string;
+    private outputChannel: vscode.OutputChannel;
+    private solc: SolcCompiler;
 
-export function initialiseSolidityCompilationOutput() {
-    outputChannel = vscode.window.createOutputChannel('solidity compilation');
-}
-export function outputCompilerInfoEnsuringInitialised() {
-    // initialise compiler outputs the information and validates existing settings
-    initialiseCompiler();
-}
-
-export function outputCompilerInfo() {
-    outputChannel.clear();
-    outputChannel.show();
-    outputChannel.appendLine('Retrieving compiler information:');
-    if (solc.currentCompilerType === compilerType.localFile) {
-        outputChannel.appendLine("Compiler using local file: '" + solc.currentCompilerSetting + "', solidity version: " + solc.getVersion());
+    constructor(solcCachePath: string) {
+        this.solcCachePath = solcCachePath;
+        this.outputChannel = vscode.window.createOutputChannel('solidity compilation');
     }
 
-    if (solc.currentCompilerType === compilerType.localNode) {
-        outputChannel.appendLine('Compiler using solidity from node_modules, solidity version: ' + solc.getVersion());
+    public outputCompilerInfoEnsuringInitialised() {
+        // initialise compiler outputs the information and validates existing settings
+        this.initialiseCompiler();
     }
 
-    if (solc.currentCompilerType === compilerType.Remote) {
-        outputChannel.appendLine("Compiler using remote version: '" + solc.currentCompilerSetting + "', solidity version: " + solc.getVersion());
+    public async selectRemoteVersion(target: vscode.ConfigurationTarget) {
+        const releases = await this.getSolcReleases();
+        const releasesToSelect: string[] = ['none', 'latest'];
+        // tslint:disable-next-line: forin
+        for (const release in releases) {
+            releasesToSelect.push(release);
+        }
+        vscode.window.showQuickPick(releasesToSelect).then((selected: string) => {
+            let updateValue = '';
+            if (selected !== 'none') {
+                if (selected === 'latest') {
+                    updateValue = selected;
+                } else {
+                    const value: string = releases[selected];
+                    if (value !== 'undefined') {
+                        updateValue = value.replace('soljson-', '');
+                        updateValue = updateValue.replace('.js', '');
+                    }
+                }
+            }
+            vscode.workspace.getConfiguration('solidity').update('compileUsingRemoteVersion', updateValue, target);
+        });
     }
 
-    if (solc.currentCompilerType === compilerType.default) {
-        outputChannel.appendLine('Compiler using default compiler (embedded on extension), solidity version: ' + solc.getVersion());
+    public getSolcReleases(): Promise<any> {
+        const url = 'https://solc-bin.ethereum.org/bin/list.json';
+        return new Promise((resolve, reject) => {
+            https.get(url, (res) => {
+                let body = '';
+                res.on('data', (chunk) => {
+                    body += chunk;
+                });
+                res.on('end', () => {
+                    try {
+                        const binList = JSON.parse(body);
+                        resolve(binList.releases);
+                    } catch (error) {
+                        reject(error.message);
+                    }
+                });
+            }).on('error', (error) => {
+                reject(error.message);
+            });
+        });
     }
-}
 
-export async function selectRemoteVersion(target: vscode.ConfigurationTarget) {
-    const releases = await getSolcReleases();
-    const releasesToSelect: string[] = ['none', 'latest'];
-    // tslint:disable-next-line: forin
-    for (const release in releases) {
-        releasesToSelect.push(release);
-    }
-    vscode.window.showQuickPick(releasesToSelect).then((selected: string) => {
-        let updateValue = '';
-        if (selected !== 'none') {
-            if (selected === 'latest') {
-                updateValue = selected;
-            } else {
-                const value: string = releases[selected];
-                if (value !== 'undefined') {
-                    updateValue = value.replace('soljson-', '');
-                    updateValue = updateValue.replace('.js', '');
-                }
+    public async outputSolcReleases() {
+        this.outputChannel.clear();
+        this.outputChannel.appendLine('Retrieving solc versions ..');
+        try {
+            const releases = await this.getSolcReleases();
+            // tslint:disable-next-line: forin
+            for (const release in releases) {
+                this.outputChannel.appendLine(release + ': ' + releases[release]);
             }
+        } catch (error) {
+            this.outputChannel.appendLine('Error:' + error);
         }
-        vscode.workspace.getConfiguration('solidity').update('compileUsingRemoteVersion', updateValue, target);
-    });
-}
+    }
 
-export function getSolcReleases(): Promise<any> {
-    const url = 'https://solc-bin.ethereum.org/bin/list.json';
-    return new Promise((resolve, reject) => {
-        https.get(url, (res) => {
-            let body = '';
-            res.on('data', (chunk) => {
-                body += chunk;
-            });
-            res.on('end', () => {
+    public async compile(contracts: any,
+        diagnosticCollection: vscode.DiagnosticCollection,
+        buildDir: string, rootDir: string, sourceDir: string, excludePath?: string, singleContractFilePath?: string): Promise<Array<string>> {
+        // Did we find any sol files after all?
+        if (Object.keys(contracts).length === 0) {
+            vscode.window.showWarningMessage('No solidity files (*.sol) found');
+            return;
+        }
+        return new Promise((resolve, reject) => {
+            this.initialiseCompiler().then(() => {
                 try {
-                    const binList = JSON.parse(body);
-                    resolve(binList.releases);
-                } catch (error) {
-                    reject(error.message);
+                    const output = this.solc.compile(JSON.stringify(contracts));
+                    resolve(this.processCompilationOutput(output, this.outputChannel, diagnosticCollection, buildDir,
+                        sourceDir, excludePath, singleContractFilePath));
+                } catch (reason) {
+                    vscode.window.showWarningMessage(reason);
+                    reject(reason);
                 }
+
             });
-        }).on('error', (error) => {
-            reject(error.message);
         });
-    });
-}
-
-export async function outputSolcReleases() {
-    outputChannel.clear();
-    outputChannel.appendLine('Retrieving solc versions ..');
-    try {
-        const releases = await getSolcReleases();
-        // tslint:disable-next-line: forin
-        for (const release in releases) {
-            outputChannel.appendLine(release + ': ' + releases[release]);
-        }
-    } catch (error) {
-        outputChannel.appendLine('Error:' + error);
     }
-}
 
-export function initialiseCompiler(): Promise<void> {
-    const rootPath = vscode.workspace.workspaceFolders[0].uri.fsPath;
-    if (typeof solc === 'undefined' || solc === null) {
-        solc = new SolcCompiler(rootPath);
-    }
-    outputChannel.clear();
-    outputChannel.show();
-    const remoteCompiler = vscode.workspace.getConfiguration('solidity').get<string>('compileUsingRemoteVersion');
-    const localCompiler = vscode.workspace.getConfiguration('solidity').get<string>('compileUsingLocalVersion');
-    const enableNodeCompiler = vscode.workspace.getConfiguration('solidity').get<boolean>('enableLocalNodeCompiler');
-    outputChannel.appendLine('Initialising compiler with settings:');
-    outputChannel.appendLine('Remote compiler: ' + remoteCompiler);
-    outputChannel.appendLine('Local compiler: ' + localCompiler);
-    outputChannel.appendLine('Node compiler enabled: ' + enableNodeCompiler);
-    outputChannel.appendLine('This may take a couple of seconds as we may need to download the solc binaries...');
-    return new Promise((resolve, reject) => {
-        solc.intialiseCompiler(localCompiler, remoteCompiler, enableNodeCompiler).then(() => {
-            outputCompilerInfo();
-            resolve();
-        }).catch((reason: any) => {
-            vscode.window.showWarningMessage(reason);
-            reject(reason);
+    private outputErrorsToChannel(outputChannel: vscode.OutputChannel, errors: any) {
+        errors.forEach(error => {
+            outputChannel.appendLine(error.formattedMessage);
         });
-    });
-}
+        outputChannel.show();
+    }
+
+    private outputCompilerInfo() {
+        this.outputChannel.clear();
+        this.outputChannel.show();
+        this.outputChannel.appendLine('Retrieving compiler information:');
+        if (this.solc.currentCompilerType === compilerType.localFile) {
+            this.outputChannel.appendLine("Compiler using local file: '" + this.solc.currentCompilerSetting + "', solidity version: " + this.solc.getVersion());
+        }
+
+        if (this.solc.currentCompilerType === compilerType.localNode) {
+            this.outputChannel.appendLine('Compiler using solidity from node_modules, solidity version: ' + this.solc.getVersion());
+        }
 
-export async function compile(contracts: any,
-    diagnosticCollection: vscode.DiagnosticCollection,
-    buildDir: string, rootDir: string, sourceDir: string, excludePath?: string, singleContractFilePath?: string): Promise<Array<string>> {
-    // Did we find any sol files after all?
-    if (Object.keys(contracts).length === 0) {
-        vscode.window.showWarningMessage('No solidity files (*.sol) found');
-        return;
+        if (this.solc.currentCompilerType === compilerType.Remote) {
+            this.outputChannel.appendLine("Compiler using remote version: '" + this.solc.currentCompilerSetting + "', solidity version: " + this.solc.getVersion());
+        }
+
+        if (this.solc.currentCompilerType === compilerType.default) {
+            this.outputChannel.appendLine('Compiler using default compiler (embedded on extension), solidity version: ' + this.solc.getVersion());
+        }
     }
-    return new Promise((resolve, reject) => {
-        initialiseCompiler().then(() => {
-            try {
-                const output = solc.compile(JSON.stringify(contracts));
-                resolve(processCompilationOutput(output, outputChannel, diagnosticCollection, buildDir,
-                    sourceDir, excludePath, singleContractFilePath));
-            } catch (reason) {
+
+    private initialiseCompiler(): Promise<void> {
+        const rootPath = vscode.workspace.workspaceFolders[0].uri.fsPath;
+        if (typeof this.solc === 'undefined' || this.solc === null) {
+            this.solc = new SolcCompiler(rootPath);
+            this.solc.setSolcCache(this.solcCachePath);
+        }
+        this.outputChannel.appendLine(this.solcCachePath);
+        this.outputChannel.clear();
+        this.outputChannel.show();
+        const remoteCompiler = vscode.workspace.getConfiguration('solidity').get<string>('compileUsingRemoteVersion');
+        const localCompiler = vscode.workspace.getConfiguration('solidity').get<string>('compileUsingLocalVersion');
+        const enableNodeCompiler = vscode.workspace.getConfiguration('solidity').get<boolean>('enableLocalNodeCompiler');
+        this.outputChannel.appendLine('Initialising compiler with settings:');
+        this.outputChannel.appendLine('Remote compiler: ' + remoteCompiler);
+        this.outputChannel.appendLine('Local compiler: ' + localCompiler);
+        this.outputChannel.appendLine('Node compiler enabled: ' + enableNodeCompiler);
+        this.outputChannel.appendLine('This may take a couple of seconds as we may need to download the solc binaries...');
+        return new Promise((resolve, reject) => {
+            this.solc.intialiseCompiler(localCompiler, remoteCompiler, enableNodeCompiler).then(() => {
+                this.outputCompilerInfo();
+                resolve();
+            }).catch((reason: any) => {
                 vscode.window.showWarningMessage(reason);
                 reject(reason);
-            }
-
+            });
         });
-    });
-}
-
-function processCompilationOutput(outputString: any, outputChannel: vscode.OutputChannel, diagnosticCollection: vscode.DiagnosticCollection,
-    buildDir: string, sourceDir: string, excludePath?: string, singleContractFilePath?: string): Array<string> {
-    const output = JSON.parse(outputString);
-    if (Object.keys(output).length === 0) {
-        const noOutputMessage = `No output by the compiler`;
-        vscode.window.showWarningMessage(noOutputMessage);
-        vscode.window.setStatusBarMessage(noOutputMessage);
-        outputChannel.appendLine(noOutputMessage);
-        return;
     }
 
-    diagnosticCollection.clear();
+    private processCompilationOutput(outputString: any, outputChannel: vscode.OutputChannel, diagnosticCollection: vscode.DiagnosticCollection,
+        buildDir: string, sourceDir: string, excludePath?: string, singleContractFilePath?: string): Array<string> {
+        const output = JSON.parse(outputString);
+        if (Object.keys(output).length === 0) {
+            const noOutputMessage = `No output by the compiler`;
+            vscode.window.showWarningMessage(noOutputMessage);
+            vscode.window.setStatusBarMessage(noOutputMessage);
+            outputChannel.appendLine(noOutputMessage);
+            return;
+        }
+
+        diagnosticCollection.clear();
 
-    if (output.errors) {
-        const errorWarningCounts = errorsToDiagnostics(diagnosticCollection, output.errors);
-        outputErrorsToChannel(outputChannel, output.errors);
+        if (output.errors) {
+            const errorWarningCounts = errorsToDiagnostics(diagnosticCollection, output.errors);
+            this.outputErrorsToChannel(outputChannel, output.errors);
 
-        if (errorWarningCounts.errors > 0) {
-            const compilationWithErrorsMessage = `Compilation failed with ${errorWarningCounts.errors} errors`;
-            vscode.window.showErrorMessage(compilationWithErrorsMessage);
-            vscode.window.setStatusBarMessage(compilationWithErrorsMessage);
-            outputChannel.appendLine(compilationWithErrorsMessage);
-            if (errorWarningCounts.warnings > 0) {
-                vscode.window.showWarningMessage(`Compilation had ${errorWarningCounts.warnings} warnings`);
+            if (errorWarningCounts.errors > 0) {
+                const compilationWithErrorsMessage = `Compilation failed with ${errorWarningCounts.errors} errors`;
+                vscode.window.showErrorMessage(compilationWithErrorsMessage);
+                vscode.window.setStatusBarMessage(compilationWithErrorsMessage);
+                outputChannel.appendLine(compilationWithErrorsMessage);
+                if (errorWarningCounts.warnings > 0) {
+                    vscode.window.showWarningMessage(`Compilation had ${errorWarningCounts.warnings} warnings`);
+                }
+            } else if (errorWarningCounts.warnings > 0) {
+                const files = this.writeCompilationOutputToBuildDirectory(output, buildDir, sourceDir, excludePath, singleContractFilePath);
+                const compilationWithWarningsMessage = `Compilation completed successfully!, with ${errorWarningCounts.warnings} warnings`;
+                vscode.window.showWarningMessage(compilationWithWarningsMessage);
+                vscode.window.setStatusBarMessage(compilationWithWarningsMessage);
+                outputChannel.appendLine(compilationWithWarningsMessage);
+                return files;
             }
-        } else if (errorWarningCounts.warnings > 0) {
-            const files = writeCompilationOutputToBuildDirectory(output, buildDir, sourceDir, excludePath, singleContractFilePath);
-            const compilationWithWarningsMessage = `Compilation completed successfully!, with ${errorWarningCounts.warnings} warnings`;
-            vscode.window.showWarningMessage(compilationWithWarningsMessage);
-            vscode.window.setStatusBarMessage(compilationWithWarningsMessage);
-            outputChannel.appendLine(compilationWithWarningsMessage);
+        } else {
+            const files = this.writeCompilationOutputToBuildDirectory(output, buildDir, sourceDir, excludePath, singleContractFilePath);
+            const compilationSuccessMessage = `Compilation completed successfully!`;
+            vscode.window.showInformationMessage(compilationSuccessMessage);
+            vscode.window.setStatusBarMessage(compilationSuccessMessage);
+            outputChannel.appendLine(compilationSuccessMessage);
             return files;
         }
-    } else {
-        const files = writeCompilationOutputToBuildDirectory(output, buildDir, sourceDir, excludePath, singleContractFilePath);
-        const compilationSuccessMessage = `Compilation completed successfully!`;
-        vscode.window.showInformationMessage(compilationSuccessMessage);
-        vscode.window.setStatusBarMessage(compilationSuccessMessage);
-        outputChannel.appendLine(compilationSuccessMessage);
-        return files;
     }
-}
-
-function ensureDirectoryExistence(filePath: string) {
-    const dirname = path.dirname(filePath);
-    if (fs.existsSync(dirname)) {
-        return true;
-    }
-    ensureDirectoryExistence(dirname);
-    fs.mkdirSync(dirname);
-}
-
-function writeCompilationOutputToBuildDirectory(output: any, buildDir: string, sourceDir: string,
-    excludePath?: string, singleContractFilePath?: string): Array<string> {
-    const rootPath = vscode.workspace.workspaceFolders[0].uri.fsPath;
-    const binPath = path.join(rootPath, buildDir);
-    const compiledFiles: Array<string> = new Array<string>();
 
-    if (!fs.existsSync(binPath)) {
-        fs.mkdirSync(binPath);
-    }
-
-    if (typeof singleContractFilePath !== 'undefined' && singleContractFilePath !== null) {
-        const relativePath = path.relative(rootPath, singleContractFilePath);
-        const dirName = path.dirname(path.join(binPath, relativePath));
-        const outputCompilationPath = path.join(dirName, path.basename(singleContractFilePath, '.sol') + '-solc-output' + '.json');
-        ensureDirectoryExistence(outputCompilationPath);
-        fs.writeFileSync(outputCompilationPath, JSON.stringify(output, null, 4));
-    } else {
-        const dirName = binPath;
-        const outputCompilationPath = path.join(dirName, 'solc-output-compile-all' + '.json');
-        ensureDirectoryExistence(outputCompilationPath);
-        if (fs.existsSync(outputCompilationPath)) {
-            fs.unlinkSync(outputCompilationPath);
+    private ensureDirectoryExistence(filePath: string) {
+        const dirname = path.dirname(filePath);
+        if (fs.existsSync(dirname)) {
+            return true;
         }
-        fs.writeFileSync(outputCompilationPath, JSON.stringify(output, null, 4));
+        this.ensureDirectoryExistence(dirname);
+        fs.mkdirSync(dirname);
     }
 
-    // iterate through all the sources,
-    // find contracts and output them into the same folder structure to avoid collisions, named as the contract
-    for (const source in output.contracts) {
-
-        // TODO: ALL this validation to a method
-
-        // Output only single contract compilation or all
-        if (!singleContractFilePath || source === singleContractFilePath) {
-
-            if (!excludePath || !source.startsWith(excludePath)) {
-                // Output only source directory compilation or all (this will exclude external references)
-                if (!sourceDir || source.startsWith(sourceDir)) {
-
-                    for (const contractName in output.contracts[source]) {
-                        if (output.contracts[source].hasOwnProperty(contractName)) {
-
-                            const contract = output.contracts[source][contractName];
-                            const relativePath = path.relative(rootPath, source);
-                            const dirName = path.dirname(path.join(binPath, relativePath));
-
-                            if (!fs.existsSync(dirName)) {
-                                fsex.mkdirsSync(dirName);
-                            }
+    private writeCompilationOutputToBuildDirectory(output: any, buildDir: string, sourceDir: string,
+        excludePath?: string, singleContractFilePath?: string): Array<string> {
+        const rootPath = vscode.workspace.workspaceFolders[0].uri.fsPath;
+        const binPath = path.join(rootPath, buildDir);
+        const compiledFiles: Array<string> = new Array<string>();
 
-                            const contractAbiPath = path.join(dirName, contractName + '.abi');
-                            const contractBinPath = path.join(dirName, contractName + '.bin');
-                            const contractJsonPath = path.join(dirName, contractName + '.json');
-
-                            if (fs.existsSync(contractAbiPath)) {
-                                fs.unlinkSync(contractAbiPath);
-                            }
+        if (!fs.existsSync(binPath)) {
+            fs.mkdirSync(binPath);
+        }
 
-                            if (fs.existsSync(contractBinPath)) {
-                                fs.unlinkSync(contractBinPath);
-                            }
+        if (typeof singleContractFilePath !== 'undefined' && singleContractFilePath !== null) {
+            const relativePath = path.relative(rootPath, singleContractFilePath);
+            const dirName = path.dirname(path.join(binPath, relativePath));
+            const outputCompilationPath = path.join(dirName, path.basename(singleContractFilePath, '.sol') + '-solc-output' + '.json');
+            this.ensureDirectoryExistence(outputCompilationPath);
+            fs.writeFileSync(outputCompilationPath, JSON.stringify(output, null, 4));
+        } else {
+            const dirName = binPath;
+            const outputCompilationPath = path.join(dirName, 'solc-output-compile-all' + '.json');
+            this.ensureDirectoryExistence(outputCompilationPath);
+            if (fs.existsSync(outputCompilationPath)) {
+                fs.unlinkSync(outputCompilationPath);
+            }
+            fs.writeFileSync(outputCompilationPath, JSON.stringify(output, null, 4));
+        }
 
-                            if (fs.existsSync(contractJsonPath)) {
-                                fs.unlinkSync(contractJsonPath);
+        // iterate through all the sources,
+        // find contracts and output them into the same folder structure to avoid collisions, named as the contract
+        for (const source in output.contracts) {
+
+            // TODO: ALL this validation to a method
+
+            // Output only single contract compilation or all
+            if (!singleContractFilePath || source === singleContractFilePath) {
+
+                if (!excludePath || !source.startsWith(excludePath)) {
+                    // Output only source directory compilation or all (this will exclude external references)
+                    if (!sourceDir || source.startsWith(sourceDir)) {
+
+                        for (const contractName in output.contracts[source]) {
+                            if (output.contracts[source].hasOwnProperty(contractName)) {
+
+                                const contract = output.contracts[source][contractName];
+                                const relativePath = path.relative(rootPath, source);
+                                const dirName = path.dirname(path.join(binPath, relativePath));
+
+                                if (!fs.existsSync(dirName)) {
+                                    fsex.mkdirsSync(dirName);
+                                }
+
+                                const contractAbiPath = path.join(dirName, contractName + '.abi');
+                                const contractBinPath = path.join(dirName, contractName + '.bin');
+                                const contractJsonPath = path.join(dirName, contractName + '.json');
+
+                                if (fs.existsSync(contractAbiPath)) {
+                                    fs.unlinkSync(contractAbiPath);
+                                }
+
+                                if (fs.existsSync(contractBinPath)) {
+                                    fs.unlinkSync(contractBinPath);
+                                }
+
+                                if (fs.existsSync(contractJsonPath)) {
+                                    fs.unlinkSync(contractJsonPath);
+                                }
+
+                                fs.writeFileSync(contractBinPath, contract.evm.bytecode.object);
+                                fs.writeFileSync(contractAbiPath, JSON.stringify(contract.abi));
+
+                                let version = '';
+                                try {
+                                    version = JSON.parse(contract.metadata).compiler.version;
+                                    // tslint:disable-next-line: no-empty
+                                } catch { } // i could do a check for string.empty but this catches (literally :) ) all scenarios
+
+                                const shortJsonOutput = {
+                                    contractName: contractName,
+                                    // tslint:disable-next-line:object-literal-sort-keys
+                                    abi: contract.abi,
+                                    metadata: contract.metadata,
+                                    bytecode: contract.evm.bytecode.object,
+                                    deployedBytecode: contract.evm.deployedBytecode.object,
+                                    sourceMap: contract.evm.bytecode.sourceMap,
+                                    deployedSourceMap: contract.evm.deployedBytecode.sourceMap,
+                                    sourcePath: source,
+                                    compiler: {
+                                        name: 'solc',
+                                        version: version,
+                                    },
+                                    ast: output.sources[source].ast,
+                                    functionHashes: contract.evm.methodIdentifiers,
+                                    gasEstimates: contract.evm.gasEstimates,
+
+                                };
+
+                                fs.writeFileSync(contractJsonPath, JSON.stringify(shortJsonOutput, null, 4));
+                                compiledFiles.push(contractJsonPath);
                             }
-
-                            fs.writeFileSync(contractBinPath, contract.evm.bytecode.object);
-                            fs.writeFileSync(contractAbiPath, JSON.stringify(contract.abi));
-
-                            let version = '';
-                            try {
-                                version = JSON.parse(contract.metadata).compiler.version;
-                                // tslint:disable-next-line: no-empty
-                            } catch { } // i could do a check for string.empty but this catches (literally :) ) all scenarios
-
-                            const shortJsonOutput = {
-                                contractName: contractName,
-                                // tslint:disable-next-line:object-literal-sort-keys
-                                abi: contract.abi,
-                                metadata: contract.metadata,
-                                bytecode: contract.evm.bytecode.object,
-                                deployedBytecode: contract.evm.deployedBytecode.object,
-                                sourceMap: contract.evm.bytecode.sourceMap,
-                                deployedSourceMap: contract.evm.deployedBytecode.sourceMap,
-                                sourcePath: source,
-                                compiler: {
-                                    name: 'solc',
-                                    version: version,
-                                },
-                                ast: output.sources[source].ast,
-                                functionHashes: contract.evm.methodIdentifiers,
-                                gasEstimates: contract.evm.gasEstimates,
-
-                            };
-
-                            fs.writeFileSync(contractJsonPath, JSON.stringify(shortJsonOutput, null, 4));
-                            compiledFiles.push(contractJsonPath);
                         }
                     }
                 }
             }
         }
+        return compiledFiles;
     }
-    return compiledFiles;
+
 }

+ 24 - 14
src/extension.ts

@@ -2,7 +2,7 @@
 import * as path from 'path';
 import * as vscode from 'vscode';
 import { compileAllContracts } from './compileAll';
-import { outputCompilerInfoEnsuringInitialised, initialiseSolidityCompilationOutput, initialiseCompiler, outputSolcReleases, selectRemoteVersion } from './compiler';
+import { Compiler } from './compiler';
 import { compileActiveContract, initDiagnosticCollection } from './compileActive';
 import {
     generateNethereumCodeSettingsFile, codeGenerateNethereumCQSCsharp, codeGenerateNethereumCQSFSharp, codeGenerateNethereumCQSVbNet,
@@ -18,14 +18,22 @@ import { formatDocument } from './formatter/prettierFormatter';
 
 let diagnosticCollection: vscode.DiagnosticCollection;
 let mythxDiagnostic: vscode.DiagnosticCollection;
+let compiler: Compiler;
 
 export async function activate(context: vscode.ExtensionContext) {
     const ws: WorkspaceFolder[] | undefined = workspace.workspaceFolders;
     diagnosticCollection = vscode.languages.createDiagnosticCollection('solidity');
-    initialiseSolidityCompilationOutput();
-
+    compiler = new Compiler(context.extensionPath);
     mythxDiagnostic = vscode.languages.createDiagnosticCollection('mythx');
-
+    
+    /*
+    const configuration = vscode.workspace.getConfiguration('solidity');
+    const cacheConfiguration = configuration.get<string>('solcCache');
+    if (typeof cacheConfiguration === 'undefined' || cacheConfiguration === null) {
+        configuration.update('solcCache', context.extensionPath, vscode.ConfigurationTarget.Global);
+    }*/
+
+    /* WSL is affected by this
     workspace.onDidChangeConfiguration(async (event) => {
         if (event.affectsConfiguration('solidity.enableLocalNodeCompiler') ||
             event.affectsConfiguration('solidity.compileUsingRemoteVersion') ||
@@ -33,19 +41,20 @@ export async function activate(context: vscode.ExtensionContext) {
             await initialiseCompiler();
         }
     });
+    */
 
     context.subscriptions.push(diagnosticCollection);
 
     initDiagnosticCollection(diagnosticCollection);
 
     context.subscriptions.push(vscode.commands.registerCommand('solidity.compile.active', async () => {
-        const compiledResults = await compileActiveContract();
+        const compiledResults = await compileActiveContract(compiler);
         autoCodeGenerateAfterCompilation(compiledResults, null, diagnosticCollection);
         return compiledResults;
     }));
 
     context.subscriptions.push(vscode.commands.registerCommand('solidity.compile', () => {
-        compileAllContracts(diagnosticCollection);
+        compileAllContracts(compiler, diagnosticCollection);
     }));
 
     context.subscriptions.push(vscode.commands.registerCommand('solidity.codegenCSharpProject', (args: any[]) => {
@@ -53,7 +62,7 @@ export async function activate(context: vscode.ExtensionContext) {
     }));
 
     context.subscriptions.push(vscode.commands.registerCommand('solidity.compileAndCodegenCSharpProject', async (args: any[]) => {
-        const compiledResults = await compileActiveContract();
+        const compiledResults = await compileActiveContract(compiler);
         compiledResults.forEach(file => {
             codeGenerateCQS(file, 0, args, diagnosticCollection);
         });
@@ -68,7 +77,7 @@ export async function activate(context: vscode.ExtensionContext) {
     }));
 
     context.subscriptions.push(vscode.commands.registerCommand('solidity.compileAndCodegenVbNetProject', async (args: any[]) => {
-        const compiledResults = await compileActiveContract();
+        const compiledResults = await compileActiveContract(compiler);
         compiledResults.forEach(file => {
             codeGenerateCQS(file, 1, args, diagnosticCollection);
         });
@@ -79,7 +88,7 @@ export async function activate(context: vscode.ExtensionContext) {
     }));
 
     context.subscriptions.push(vscode.commands.registerCommand('solidity.compileAndCodegenFSharpProject', async (args: any[]) => {
-        const compiledResults = await compileActiveContract();
+        const compiledResults = await compileActiveContract(compiler);
         compiledResults.forEach(file => {
             codeGenerateCQS(file, 3, args, diagnosticCollection);
         });
@@ -114,23 +123,23 @@ export async function activate(context: vscode.ExtensionContext) {
     }));
 
     context.subscriptions.push(vscode.commands.registerCommand('solidity.runMythx', async (fileUri: vscode.Uri) => {
-        analyzeContract(mythxDiagnostic, fileUri);
+        analyzeContract(compiler, mythxDiagnostic, fileUri);
     }));
 
     context.subscriptions.push(vscode.commands.registerCommand('solidity.compilerInfo', async () => {
-        await outputCompilerInfoEnsuringInitialised();
+        await compiler.outputCompilerInfoEnsuringInitialised();
     }));
 
     context.subscriptions.push(vscode.commands.registerCommand('solidity.solcReleases', async () => {
-        outputSolcReleases();
+        compiler.outputSolcReleases();
     }));
 
     context.subscriptions.push(vscode.commands.registerCommand('solidity.selectWorkspaceRemoteSolcVersion', async () => {
-        selectRemoteVersion(vscode.ConfigurationTarget.Workspace);
+        compiler.selectRemoteVersion(vscode.ConfigurationTarget.Workspace);
     }));
 
     context.subscriptions.push(vscode.commands.registerCommand('solidity.selectGlobalRemoteSolcVersion', async () => {
-        selectRemoteVersion(vscode.ConfigurationTarget.Global);
+        compiler.selectRemoteVersion(vscode.ConfigurationTarget.Global);
     }));
 
     context.subscriptions.push(
@@ -167,6 +176,7 @@ export async function activate(context: vscode.ExtensionContext) {
             // Notify the server about file changes to '.sol.js files contain in the workspace (TODO node, linter)
             // fileEvents: vscode.workspace.createFileSystemWatcher('**/.sol.js'),
         },
+        initializationOptions: context.extensionPath
     };
 
     let clientDisposable;

+ 1 - 0
src/linter/solhint.ts

@@ -70,6 +70,7 @@ class ValidationConfig {
 
         return {
             extends: extendsConfig,
+            plugins : this.fileConfig.plugins,
             rules: Object.assign(
                 ValidationConfig.DEFAULT_RULES,
                 this.ideRules,

+ 3 - 0
src/server.ts

@@ -60,6 +60,7 @@ let enableNodeCompiler = true;
 let solhintDefaultRules = {};
 let soliumDefaultRules = {};
 let validationDelay = 1500;
+let solcCachePath = '';
 
 // flags to avoid trigger concurrent validations (compiling is slow)
 let validatingDocument = false;
@@ -226,7 +227,9 @@ documents.listen(connection);
 
 connection.onInitialize((result): InitializeResult => {
     rootPath = result.rootPath;
+    solcCachePath = result.initializationOptions;
     solcCompiler = new SolcCompiler(rootPath);
+    solcCompiler.setSolcCache(solcCachePath);
 
     return {
         capabilities: {

+ 34 - 1
src/solcCompiler.ts

@@ -3,6 +3,7 @@ import { errorToDiagnostic } from './solErrorsToDiagnostics';
 import * as solc from 'solc';
 import * as fs from 'fs';
 import * as path from 'path';
+import * as https from 'https';
 import { ContractCollection } from './model/contractsCollection';
 import { initialiseProject } from './projectService';
 
@@ -20,6 +21,11 @@ export class SolcCompiler {
     public currentCompilerSetting: string;
     public enableNodeCompilerSetting: boolean;
     private localSolc: any;
+    private solcCachePath: string;
+
+    public setSolcCache(solcCachePath: string): void {
+        this.solcCachePath = solcCachePath;
+    }
 
     public getVersion(): string {
         return this.localSolc.version();
@@ -93,7 +99,7 @@ export class SolcCompiler {
                             // remote
                             if (typeof remoteInstallationVersion !== 'undefined' && remoteInstallationVersion !== null && remoteInstallationVersion !== '') {
                                 const solcService = this;
-                                solc.loadRemoteVersion(remoteInstallationVersion, function (err, solcSnapshot) {
+                                this.loadRemoteWasmVersion(remoteInstallationVersion, function (err, solcSnapshot) {
                                     if (err) {
                                         reject('There was an error loading the remote version: ' + remoteInstallationVersion);
                                     } else {
@@ -163,4 +169,31 @@ export class SolcCompiler {
         }
         return [];
     }
+
+    private loadRemoteWasmVersion (versionString, cb) {
+        const pathVersion = path.resolve(path.join(this.solcCachePath, 'soljson-' + versionString + '.js'));
+        if (fs.existsSync(pathVersion) && versionString !== 'latest') {
+            const solidityfile = require(pathVersion);
+                const solcConfigured = solc.setupMethods(solidityfile);
+                cb(null, solcConfigured);
+        } else {
+            const file = fs.createWriteStream(pathVersion);
+            // the files have a redirection.. so we are not using the wasm path for the time being until i check with Christian
+            const url = 'https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/soljson-' + versionString + '.js';
+            https.get(url, function (response) {
+                if (response.statusCode !== 200) {
+                cb(new Error('Error retrieving binary: ' + response.statusMessage));
+                } else {
+                response.pipe(file);
+                response.on('end', function () {
+                    const solidityfile = require(pathVersion);
+                    const solcConfigured = solc.setupMethods(solidityfile);
+                    cb(null, solcConfigured);
+                });
+                }
+            }).on('error', function (error) {
+                cb(error);
+            });
+        }
+    }
 }