compiler.ts 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. 'use strict';
  2. import * as vscode from 'vscode';
  3. import * as fs from 'fs';
  4. import * as path from 'path';
  5. import * as fsex from 'fs-extra';
  6. import * as artifactor from 'truffle-artifactor';
  7. import {SolcCompiler, compilerType} from './solcCompiler';
  8. import { errorsToDiagnostics } from './solErrorsToDiaganosticsClient';
  9. function outputErrorsToChannel(outputChannel: vscode.OutputChannel, errors: any) {
  10. errors.forEach(error => {
  11. outputChannel.appendLine(error.formattedMessage);
  12. });
  13. outputChannel.show();
  14. }
  15. export function compile(contracts: any,
  16. diagnosticCollection: vscode.DiagnosticCollection,
  17. buildDir: string, rootDir: string, sourceDir: string, excludePath?: string, singleContractFilePath?: string) {
  18. // Did we find any sol files after all?
  19. if (Object.keys(contracts).length === 0) {
  20. vscode.window.showWarningMessage('No solidity files (*.sol) found');
  21. return;
  22. }
  23. const solc = new SolcCompiler(vscode.workspace.rootPath);
  24. const outputChannel = vscode.window.createOutputChannel('solidity compilation');
  25. outputChannel.clear();
  26. outputChannel.show();
  27. vscode.window.setStatusBarMessage('Compilation started');
  28. const remoteCompiler = vscode.workspace.getConfiguration('solidity').get<string>('compileUsingRemoteVersion');
  29. const localCompiler = vscode.workspace.getConfiguration('solidity').get<string>('compileUsingLocalVersion');
  30. solc.intialiseCompiler(localCompiler, remoteCompiler).then(() => {
  31. const output = solc.compile(JSON.stringify(contracts));
  32. if (solc.currentCompilerType === compilerType.localFile) {
  33. outputChannel.appendLine("Compiling using local file: '" + solc.currentCompilerSetting + "', solidity version: " + solc.getVersion() );
  34. }
  35. if (solc.currentCompilerType === compilerType.localNode) {
  36. outputChannel.appendLine('Compiling using solidity from node_modules, solidity version: ' + solc.getVersion());
  37. }
  38. if (solc.currentCompilerType === compilerType.Remote) {
  39. outputChannel.appendLine("Compiling using remote version: '" + solc.currentCompilerSetting + "', solidity version: " + solc.getVersion() );
  40. }
  41. if (solc.currentCompilerType === compilerType.default) {
  42. outputChannel.appendLine('Compiling using default compiler, solidity version: ' + solc.getVersion() );
  43. }
  44. processCompilationOuput(output, outputChannel, diagnosticCollection, buildDir,
  45. sourceDir, excludePath, singleContractFilePath);
  46. }).catch( (reason: any) => {
  47. vscode.window.showWarningMessage(reason);
  48. });
  49. }
  50. function processCompilationOuput(outputString: any, outputChannel: vscode.OutputChannel, diagnosticCollection: vscode.DiagnosticCollection,
  51. buildDir: string, sourceDir: string, excludePath?: string, singleContractFilePath?: string) {
  52. const output = JSON.parse(outputString);
  53. if (Object.keys(output).length === 0) {
  54. vscode.window.showWarningMessage('No output by the compiler');
  55. return;
  56. }
  57. diagnosticCollection.clear();
  58. if (output.errors) {
  59. const errorWarningCounts = errorsToDiagnostics(diagnosticCollection, output.errors);
  60. outputErrorsToChannel(outputChannel, output.errors);
  61. if (errorWarningCounts.errors > 0) {
  62. vscode.window.showErrorMessage(`Compilation failed with ${errorWarningCounts.errors} errors`);
  63. if (errorWarningCounts.warnings > 0) {
  64. vscode.window.showWarningMessage(`Compilation had ${errorWarningCounts.warnings} warnings`);
  65. }
  66. } else if (errorWarningCounts.warnings > 0) {
  67. writeCompilationOutputToBuildDirectory(output, buildDir, sourceDir, excludePath, singleContractFilePath);
  68. vscode.window.showWarningMessage(`Compilation had ${errorWarningCounts.warnings} warnings`);
  69. vscode.window.showInformationMessage('Compilation completed succesfully!');
  70. }
  71. } else {
  72. writeCompilationOutputToBuildDirectory(output, buildDir, sourceDir, excludePath, singleContractFilePath);
  73. // outputChannel.hide();
  74. vscode.window.showInformationMessage('Compilation completed succesfully!');
  75. }
  76. }
  77. function ensureDirectoryExistence(filePath) {
  78. const dirname = path.dirname(filePath);
  79. if (fs.existsSync(dirname)) {
  80. return true;
  81. }
  82. ensureDirectoryExistence(dirname);
  83. fs.mkdirSync(dirname);
  84. }
  85. function writeCompilationOutputToBuildDirectory(output: any, buildDir: string, sourceDir: string,
  86. excludePath?: string, singleContractFilePath?: string) {
  87. const binPath = path.join(vscode.workspace.rootPath, buildDir);
  88. if (!fs.existsSync(binPath)) {
  89. fs.mkdirSync(binPath);
  90. }
  91. if (typeof singleContractFilePath !== 'undefined' && singleContractFilePath !== null) {
  92. const relativePath = path.relative(vscode.workspace.rootPath, singleContractFilePath);
  93. const dirName = path.dirname(path.join(binPath, relativePath));
  94. const outputCompilationPath = path.join(dirName, path.basename(singleContractFilePath, '.sol') + '-sol-output' + '.json');
  95. ensureDirectoryExistence(outputCompilationPath);
  96. fs.writeFileSync(outputCompilationPath, JSON.stringify(output, null, 4));
  97. } else {
  98. const dirName = binPath;
  99. const outputCompilationPath = path.join(dirName, 'compile-all-output' + '.json');
  100. ensureDirectoryExistence(outputCompilationPath);
  101. if (fs.existsSync(outputCompilationPath)) {
  102. fs.unlinkSync(outputCompilationPath);
  103. }
  104. fs.writeFileSync(outputCompilationPath, JSON.stringify(output, null, 4));
  105. }
  106. // iterate through all the sources,
  107. // find contracts and output them into the same folder structure to avoid collisions, named as the contract
  108. for (const source in output.contracts) {
  109. // TODO: ALL this validation to a method
  110. // Output only single contract compilation or all
  111. if (!singleContractFilePath || source === singleContractFilePath) {
  112. if (!excludePath || !source.startsWith(excludePath)) {
  113. // Output only source directory compilation or all (this will exclude external references)
  114. if (!sourceDir || source.startsWith(sourceDir)) {
  115. for (const contractName in output.contracts[source]) {
  116. if (output.contracts[source].hasOwnProperty(contractName)) {
  117. const contract = output.contracts[source][contractName];
  118. const relativePath = path.relative(vscode.workspace.rootPath, source);
  119. const dirName = path.dirname(path.join(binPath, relativePath));
  120. if (!fs.existsSync(dirName)) {
  121. fsex.mkdirsSync(dirName);
  122. }
  123. const contractAbiPath = path.join(dirName, contractName + '.abi');
  124. const contractBinPath = path.join(dirName, contractName + '.bin');
  125. const contractJsonPath = path.join(dirName, contractName + '.json');
  126. const truffleArtifactPath = path.join(dirName, contractName + '.sol.js');
  127. if (fs.existsSync(contractAbiPath)) {
  128. fs.unlinkSync(contractAbiPath);
  129. }
  130. if (fs.existsSync(contractBinPath)) {
  131. fs.unlinkSync(contractBinPath);
  132. }
  133. if (fs.existsSync(contractJsonPath)) {
  134. fs.unlinkSync(contractJsonPath);
  135. }
  136. if (fs.existsSync(truffleArtifactPath)) {
  137. fs.unlinkSync(truffleArtifactPath);
  138. }
  139. fs.writeFileSync(contractBinPath, contract.evm.bytecode.object);
  140. fs.writeFileSync(contractAbiPath, JSON.stringify(contract.abi));
  141. const shortJsonOutput = {
  142. abi : contract.abi,
  143. bytecode : contract.evm.bytecode.object,
  144. functionHashes : contract.evm.methodIdentifiers,
  145. gasEstimates : contract.evm.gasEstimates,
  146. };
  147. fs.writeFileSync(contractJsonPath, JSON.stringify(shortJsonOutput, null, 4));
  148. /*
  149. let contract_data = {
  150. contract_name: contractName,
  151. abi: output.contracts[source + ':' + contractName].interface,
  152. unlinked_binary: output.contracts[source + ':' + contractName].bytecode,
  153. };
  154. artifactor.save(contract_data, truffleArtifactPath);
  155. */
  156. }
  157. }
  158. }
  159. }
  160. }
  161. }
  162. }