diff options
Diffstat (limited to 'lib/base-compiler.js')
-rw-r--r-- | lib/base-compiler.js | 267 |
1 files changed, 205 insertions, 62 deletions
diff --git a/lib/base-compiler.js b/lib/base-compiler.js index 0ebe7f005..059f5d0b0 100644 --- a/lib/base-compiler.js +++ b/lib/base-compiler.js @@ -725,13 +725,35 @@ export class BaseCompiler { }; } - async processRustMirOutput(outputFilename, output) { - const mirPath = this.getRustMirOutputFilename(outputFilename); + getRustMacroExpansionOutputFilename(inputFilename) { + return inputFilename.replace(path.extname(inputFilename), '.expanded.rs'); + } + + getRustHirOutputFilename(inputFilename) { + return inputFilename.replace(path.extname(inputFilename), '.hir'); + } + + getRustMirOutputFilename(outputFilename) { + return outputFilename.replace(path.extname(outputFilename), '.mir'); + } + + // Currently called for getting macro expansion and HIR. + // It returns the content of the output file created after using -Z unpretty=<unprettyOpt>. + // The outputFriendlyName is a free form string used in case of error. + async generateRustUnprettyOutput(inputFilename, options, unprettyOpt, outputFilename, outputFriendlyName){ + const execOptions = this.getDefaultExecOptions(); + + const rustcOptions = [...options]; + rustcOptions.splice(options.indexOf('-o', 2)); + rustcOptions.push(inputFilename, '-o', outputFilename, `-Zunpretty=${unprettyOpt}`); + + const output = await this.runCompiler(this.compiler.exe, rustcOptions, inputFilename, + execOptions); if (output.code !== 0) { - return [{text: 'Failed to run compiler to get Rust MIR'}]; + return [{text: `Failed to run compiler to get Rust ${outputFriendlyName}`}]; } - if (await fs.exists(mirPath)) { - const content = await fs.readFile(mirPath, 'utf-8'); + if (await fs.exists(outputFilename)) { + const content = await fs.readFile(outputFilename, 'utf-8'); return content.split('\n').map((line) => ({ text: line, })); @@ -739,25 +761,23 @@ export class BaseCompiler { return [{text: 'Internal error; unable to open output path'}]; } - async generateRustMacroExpansion(inputFilename) { - const execOptions = this.getDefaultExecOptions(); + async generateRustMacroExpansion(inputFilename, options) { const macroExpPath = this.getRustMacroExpansionOutputFilename(inputFilename); - const rustcOptions = [ - inputFilename, - '-o', - macroExpPath, - '-Zunpretty=expanded', - '--crate-type', - 'rlib', - ]; - - const output = await this.runCompiler(this.compiler.exe, rustcOptions, this.filename(inputFilename), - execOptions); + return this.generateRustUnprettyOutput(inputFilename, options, 'expanded', macroExpPath, 'Macro Expansion'); + } + + async generateRustHir(inputFilename, options) { + const hirPath = this.getRustHirOutputFilename(inputFilename); + return this.generateRustUnprettyOutput(inputFilename, options, 'hir-tree', hirPath, 'HIR'); + } + + async processRustMirOutput(outputFilename, output) { + const mirPath = this.getRustMirOutputFilename(outputFilename); if (output.code !== 0) { - return [{text: 'Failed to run compiler to get Rust Macro Expansion'}]; + return [{text: 'Failed to run compiler to get Rust MIR'}]; } - if (await fs.exists(macroExpPath)) { - const content = await fs.readFile(macroExpPath, 'utf-8'); + if (await fs.exists(mirPath)) { + const content = await fs.readFile(mirPath, 'utf-8'); return content.split('\n').map((line) => ({ text: line, })); @@ -769,14 +789,6 @@ export class BaseCompiler { return inputFilename.replace(path.extname(inputFilename), '.ll'); } - getGnatDebugOutputFilename(inputFilename) { - return inputFilename + '.dg'; - } - - getRustMacroExpansionOutputFilename(inputFilename) { - return inputFilename.replace(path.extname(inputFilename), '.expanded.rs'); - } - getOutputFilename(dirPath, outputFilebase, key) { let filename; if (key && key.backendOptions && key.backendOptions.customOutputFilename) { @@ -796,30 +808,80 @@ export class BaseCompiler { return this.getOutputFilename(dirPath, outputFilebase, key); } - async processGnatDebugOutput(inputFilename, output) { - const gnatDebugPath = this.getGnatDebugOutputFilename(inputFilename); + async processGnatDebugOutput(inputFilename, result) { + const contentDebugExpanded = []; + const contentDebugTree = []; + const keep_stdout = []; + + // stdout layout: + // + // ----- start + // everything here stays + // ... in stdout + // ... until : + // Source recreated from tree... <-\ + // everything here is | + // ... sent in expanded | this is optionnal + // ... pane... until : <-/ + // Tree created for ... <-\ + // everything after is | this is optionnal + // ... sent in Tree pane <-/ + // ----- EOF + const startOfExpandedCode = /^Source recreated from tree/; + const startOfTree = /^Tree created for/; + + let isInExpandedCode = false; + let isInTree = false; + + for (const obj of Object.values(result.stdout)) { + if (!isInExpandedCode && startOfExpandedCode.test(obj.text)) { + isInExpandedCode = true; + isInTree = false; + } + + if (!isInTree && startOfTree.test(obj.text)) { + isInExpandedCode = false; + isInTree = true; + } + + if (isInExpandedCode) { + contentDebugExpanded.push(obj); + } else if (isInTree){ + contentDebugTree.push(obj); + } else { + keep_stdout.push(obj); + } + } // Do not check compiler result before looking for expanded code. The - // compiler may exit with an error after the emission. This file is also + // compiler may exit with an error after the emission. This dump is also // very usefull to debug error message. - if (await fs.exists(gnatDebugPath)) { - const content = await fs.readFile(gnatDebugPath, 'utf-8'); - return content.split('\n').map((line) => ({ - text: line, - })); - } else { - // check for possible cause for missing file - if (output.code !== 0) { - return [{text: 'GNAT exited with an error and did not create the expanded code'}]; + + if (contentDebugExpanded.length === 0) + if (result.code !== 0) { + contentDebugExpanded.push({text: 'GNAT exited with an error and did not create the expanded code'}); } else { - return [{text: 'GNAT exited successfully but the expanded code is missing, something is wrong'}]; + contentDebugExpanded.push( + {text: 'GNAT exited successfully but the expanded code is missing, something is wrong'}); } - } + + if (contentDebugTree.length === 0) + if (result.code !== 0) { + contentDebugTree.push({text: 'GNAT exited with an error and did not create the Tree'}); + } else { + contentDebugTree.push({text: 'GNAT exited successfully but the Tree is missing, something is wrong'}); + } + + return { + stdout: keep_stdout, + tree: contentDebugTree, + expandedcode: contentDebugExpanded, + }; } /** * @returns {{filename_suffix: string, name: string, command_prefix: string}} - * `filename_suffix`: dump file name suffix if GCC default dump names is used + * `filename_suffix`: dump file name suffix if GCC default dump name is used * * `name`: the name to be displayed in the UI * @@ -899,6 +961,8 @@ export class BaseCompiler { const filesToWrite = []; for (let file of files) { + if (!file.filename) throw new Error('One of more files do not have a filename'); + const fullpath = this.getExtraFilepath(dirPath, file.filename); filesToWrite.push(fs.outputFile(fullpath, file.contents)); } @@ -907,6 +971,8 @@ export class BaseCompiler { } async writeAllFiles(dirPath, source, files, filters) { + if (!source) throw new Error(`File ${this.compileFilename} has no content or file is missing`); + const inputFilename = path.join(dirPath, this.compileFilename); await fs.writeFile(inputFilename, source); @@ -922,6 +988,8 @@ export class BaseCompiler { } async writeAllFilesCMake(dirPath, source, files, filters) { + if (!source) throw new Error('File CMakeLists.txt has no content or file is missing'); + const inputFilename = path.join(dirPath, 'CMakeLists.txt'); await fs.writeFile(inputFilename, source); @@ -973,9 +1041,14 @@ export class BaseCompiler { const buildResults = await this.loadPackageWithExecutable(key, dirPath); if (buildResults) return buildResults; - const compilationResult = await this.buildExecutableInFolder(key, dirPath); - if (compilationResult.code !== 0) { - return compilationResult; + let compilationResult; + try { + compilationResult = await this.buildExecutableInFolder(key, dirPath); + if (compilationResult.code !== 0) { + return compilationResult; + } + } catch (e) { + return this.handleUserError(e, dirPath); } await this.storePackageWithExecutable(key, dirPath, compilationResult); @@ -1178,10 +1251,12 @@ export class BaseCompiler { execOptions.ldPath = this.getSharedLibraryPathsAsLdLibraryPaths([]); const makeAst = backendOptions.produceAst && this.compiler.supportsAstView; - const makeGnatDebug = backendOptions.produceGnatDebug && this.compiler.supportsGnatDebugView; + const makeGnatDebug = backendOptions.produceGnatDebug && this.compiler.supportsGnatDebugViews; + const makeGnatDebugTree = backendOptions.produceGnatDebugTree && this.compiler.supportsGnatDebugViews; const makeIr = backendOptions.produceIr && this.compiler.supportsIrView; const makeRustMir = backendOptions.produceRustMir && this.compiler.supportsRustMirView; const makeRustMacroExp = backendOptions.produceRustMacroExp && this.compiler.supportsRustMacroExpView; + const makeRustHir = backendOptions.produceRustHir && this.compiler.supportsRustHirView; const makeGccDump = backendOptions.produceGccDump && backendOptions.produceGccDump.opened && this.compiler.supportsGccDump; @@ -1190,12 +1265,14 @@ export class BaseCompiler { asmResult, astResult, irResult, + rustHirResult, rustMacroExpResult, toolsResult, ] = await Promise.all([ this.runCompiler(this.compiler.exe, options, inputFilenameSafe, execOptions), (makeAst ? this.generateAST(inputFilename, options) : ''), (makeIr ? this.generateIR(inputFilename, options, filters) : ''), + (makeRustHir ? this.generateRustHir(inputFilename, options) : ''), (makeRustMacroExp ? this.generateRustMacroExpansion(inputFilename, options) : ''), Promise.all(this.runToolsOfType(tools, 'independent', this.getCompilationInfo(key, { inputFilename, @@ -1206,9 +1283,10 @@ export class BaseCompiler { // GNAT, GCC and rustc can produce their extra output files along // with the main compilation command. - const gnatDebugResult = (makeGnatDebug ? - await this.processGnatDebugOutput(inputFilenameSafe, asmResult) - : ''); + const gnatDebugResults = ((makeGnatDebug || makeGnatDebugTree)? + await this.processGnatDebugOutput(inputFilenameSafe, asmResult) + : ''); + const gccDumpResult = (makeGccDump ? await this.processGccDumpOutput(backendOptions.produceGccDump, asmResult, this.compiler.removeEmptyGccDump, @@ -1226,9 +1304,17 @@ export class BaseCompiler { asmResult.gccDumpOutput = gccDumpResult; } - if (this.compiler.supportsGnatDebugView && gnatDebugResult) { - asmResult.hasGnatDebugOutput = true; - asmResult.gnatDebugOutput = gnatDebugResult; + if (this.compiler.supportsGnatDebugViews && gnatDebugResults) { + asmResult.stdout = gnatDebugResults.stdout; + + if (makeGnatDebug && gnatDebugResults.expandedcode.length > 0) { + asmResult.hasGnatDebugOutput = true; + asmResult.gnatDebugOutput = gnatDebugResults.expandedcode; + } + if (makeGnatDebugTree && gnatDebugResults.tree.length > 0) { + asmResult.hasGnatDebugTreeOutput = true; + asmResult.gnatDebugTreeOutput = gnatDebugResults.tree; + } } asmResult.tools = toolsResult; @@ -1262,6 +1348,11 @@ export class BaseCompiler { asmResult.rustMacroExpOutput = rustMacroExpResult; } + if (rustHirResult) { + asmResult.hasRustHirOutput = true; + asmResult.rustHirOutput = rustHirResult; + } + return this.checkOutputFileAndDoPostProcess(asmResult, outputFilename, filters); } @@ -1289,6 +1380,17 @@ export class BaseCompiler { return result; } + handleUserError(error, dirPath) { + return { + dirPath, + okToCache: false, + code: -1, + asm: [{text: `<${error.message}>`}], + stdout: [], + stderr: [{text: `<${error.message}>` }], + }; + } + async doBuildstepAndAddToResult(result, name, command, args, execParams) { const stepResult = await this.doBuildstep(command, args, execParams); stepResult.step = name; @@ -1359,7 +1461,12 @@ export class BaseCompiler { let fullResult = await this.loadPackageWithExecutable(cacheKey, dirPath); if (!fullResult) { - const writeSummary = await this.writeAllFilesCMake(dirPath, cacheKey.source, files, cacheKey.filters); + let writeSummary; + try { + writeSummary = await this.writeAllFilesCMake(dirPath, cacheKey.source, files, cacheKey.filters); + } catch (e) { + return this.handleUserError(e, dirPath); + } const execParams = this.getDefaultExecOptions(); execParams.appHome = dirPath; @@ -1535,7 +1642,12 @@ export class BaseCompiler { const dirPath = await this.newTempDir(); - const writeSummary = await this.writeAllFiles(dirPath, source, files, filters); + let writeSummary; + try { + writeSummary = await this.writeAllFiles(dirPath, source, files, filters); + } catch (e) { + return this.handleUserError(e, dirPath); + } const inputFilename = writeSummary.inputFilename; const [result, optOutput] = await this.doCompilation( @@ -1679,24 +1791,55 @@ export class BaseCompiler { currentPassOutput: '<No pass selected>', syntaxHighlight: false, }; + const treeDumpsNotInPasses = []; + const selectedPasses = []; - if (opts.treeDump) selectedPasses.push('tree'); + if (opts.treeDump) { + selectedPasses.push('tree'); + + // Fake 2 lines as coming from -fdump-passes + // This allows the insertion of 'gimple' and 'original' + // tree dumps that are not really part of a tree pass. + treeDumpsNotInPasses.push ( + [ { text: 'tree-original: ON'}, + { + filename_suffix: 't.original', + name: 'original (tree)', + command_prefix: '-fdump-tree-original', + }, + ], + [ { text: 'tree-gimple: ON'}, + { + filename_suffix: 't.gimple', + name: 'gimple (tree)', + command_prefix: '-fdump-tree-gimple', + }, + ]); + } + if (opts.ipaDump) selectedPasses.push('ipa'); if (opts.rtlDump) selectedPasses.push('rtl'); + // Defaults to a custom file derived from output file name. Works when + // using the -fdump-tree-foo=FILE variant (!removeEmptyPasses). + // Will be overriden later if not. let dumpFileName = this.getGccDumpFileName(outputFilename); let passFound = false; const filtered_stderr = []; const toRemoveFromStderr = /^\s*((ipa|tree|rtl)-)|(\*)([\w-]+).*(ON|OFF)$/; - for (const obj of Object.values(result.stderr)) { - const selectizeObject = this.fromInternalGccDumpName(obj.text, selectedPasses); - if (selectizeObject){ + + const dumpPassesLines = treeDumpsNotInPasses.concat( + Object.values(result.stderr).map( + x => [x, this.fromInternalGccDumpName(x.text, selectedPasses)])); + + for (const [obj, selectizeObject] of dumpPassesLines) { + if (selectizeObject) { if (opts.pass && opts.pass.name === selectizeObject.name) passFound = true; - if (removeEmptyPasses){ + if (removeEmptyPasses) { const f = fs.readdirSync(rootDir).filter(fn => fn.endsWith(selectizeObject.filename_suffix)); // pass is enabled, but the dump hasn't produced anything: @@ -1711,7 +1854,7 @@ export class BaseCompiler { output.all.push(selectizeObject); } - if (!toRemoveFromStderr.test(obj.text)){ + if (!toRemoveFromStderr.test(obj.text)) { filtered_stderr.push(obj); } } |