diff options
Diffstat (limited to 'lib/base-compiler.js')
-rw-r--r-- | lib/base-compiler.js | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/lib/base-compiler.js b/lib/base-compiler.js index 5235d4975..cdac46dd0 100644 --- a/lib/base-compiler.js +++ b/lib/base-compiler.js @@ -36,6 +36,7 @@ import { CompilerArguments } from './compiler-arguments'; import { ClangParser, GCCParser } from './compilers/argument-parsers'; import { getDemanglerTypeByKey } from './demangler'; import * as exec from './exec'; +import { FormattingHandler } from './handlers/formatting'; import { InstructionSets } from './instructionsets'; import { languages } from './languages'; import { LlvmAstParser } from './llvm-ast'; @@ -79,6 +80,8 @@ export class BaseCompiler { this.llvmIr = new LlvmIrParser(this.compilerProps); this.llvmAst = new LlvmAstParser(this.compilerProps); + this.formatHandler = new FormattingHandler(this.env.ceProps); + this.toolchainPath = getToolchainPath(this.compiler.exe, this.compiler.options); this.possibleArguments = new CompilerArguments(this.compiler.id); @@ -287,6 +290,20 @@ export class BaseCompiler { return result; } + async runCompilerRawOutput(compiler, options, inputFilename, execOptions) { + if (!execOptions) { + execOptions = this.getDefaultExecOptions(); + } + + if (!execOptions.customCwd) { + execOptions.customCwd = path.dirname(inputFilename); + } + + const result = await this.exec(compiler, options, execOptions); + result.inputFilename = inputFilename; + return result; + } + parseCompilationOutput(result, inputFilename) { result.stdout = utils.parseOutput(result.stdout, inputFilename); result.stderr = utils.parseOutput(result.stderr, inputFilename); @@ -695,6 +712,88 @@ export class BaseCompiler { await this.runCompiler(this.compiler.exe, newOptions, this.filename(inputFilename), execOptions)); } + async generatePP(inputFilename, compilerOptions, rawPpOptions) { + // -E to dump preprocessor output, remove -o so it is dumped to stdout + compilerOptions = compilerOptions.concat(['-E']); + if (compilerOptions.includes('-o')) { + compilerOptions.splice(compilerOptions.indexOf('-o'), 2); + } + + let ppOptions = _.extend({ + 'filter-headers': false, + 'clang-format': false, + }, rawPpOptions); + + const execOptions = this.getDefaultExecOptions(); + // A higher max output is needed for when the user includes headers + execOptions.maxOutput = 1024 * 1024 * 1024; + let result = await this.runCompilerRawOutput(this.compiler.exe, compilerOptions, + this.filename(inputFilename), execOptions); + let output = result.stdout; + + let numberOfLinesFiltered = 0; + if (ppOptions['filter-headers']) { + [numberOfLinesFiltered, output] = this.filterPP(output); + } + if (ppOptions['clang-format']) { + output = await this.applyClangFormat(output); + } + + return { + numberOfLinesFiltered, + output: output, + }; + } + + filterPP(stdout) { + // Every compiler except Chibicc, as far as I've tested, outputs these line annotations + // Compiler test: https://godbolt.org/z/K7Pncjs4o + // Matching things like: + // # 4 "/app/example.cpp" + // # 11 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 2 3 4 + // #line 1816 "C:/WinSdk/Include/10.0.18362.0/ucrt\\corecrt.h" + // # 13 "" 3 + // regex test cases: https://regex101.com/r/9dOsUI/1 + let lines = stdout.split('\n'); + const ppLineRe = /^\s*#\s*(?:line)?\s*\d+\s*"((?:\\"|[^"])*)"/i; + let isInSourceRegion = true; + let numberOfLinesFiltered = 0; + let filteredLines = []; + for (const line of lines) { + let match = line.match(ppLineRe); + if (match === null) { + if (isInSourceRegion) { + filteredLines.push(line); + } else { + numberOfLinesFiltered++; + } + } else { + let path = match[1]; + if (path.trim() === '' || path === '<source>' || path.endsWith('.c') || path.endsWith('.cpp')) { + isInSourceRegion = true; + } else { + isInSourceRegion = false; + } + numberOfLinesFiltered++; + } + } + return [numberOfLinesFiltered, filteredLines.join('\n')]; + } + + async applyClangFormat(output) { + // Currently hard-coding llvm style + try { + let [stdout, stderr] = await this.formatHandler.internalFormat('clangformat', 'LLVM', output); + if (stderr) { + stdout += '\n/* clang-format stderr:\n' + stderr.trim() + '\n*/'; + } + return stdout; + } catch (err) { + logger.error('Internal formatter error', {err}); + return '/* <Error while running clang-format> */\n\n' + output; + } + } + async generateIR(inputFilename, options, filters) { // These options make Clang produce an IR const newOptions = _.filter(options, option => option !== '-fcolor-diagnostics') @@ -1249,6 +1348,7 @@ export class BaseCompiler { execOptions.ldPath = this.getSharedLibraryPathsAsLdLibraryPaths([]); const makeAst = backendOptions.produceAst && this.compiler.supportsAstView; + const makePp = typeof backendOptions.producePp === 'object' && this.compiler.supportsPpView; const makeGnatDebug = backendOptions.produceGnatDebug && this.compiler.supportsGnatDebugViews; const makeGnatDebugTree = backendOptions.produceGnatDebugTree && this.compiler.supportsGnatDebugViews; const makeIr = backendOptions.produceIr && this.compiler.supportsIrView; @@ -1262,6 +1362,7 @@ export class BaseCompiler { const [ asmResult, astResult, + ppResult, irResult, rustHirResult, rustMacroExpResult, @@ -1269,6 +1370,7 @@ export class BaseCompiler { ] = await Promise.all([ this.runCompiler(this.compiler.exe, options, inputFilenameSafe, execOptions), (makeAst ? this.generateAST(inputFilename, options) : ''), + (makePp ? this.generatePP(inputFilename, options, backendOptions.producePp) : ''), (makeIr ? this.generateIR(inputFilename, options, filters) : ''), (makeRustHir ? this.generateRustHir(inputFilename, options) : ''), (makeRustMacroExp ? this.generateRustMacroExpansion(inputFilename, options) : ''), @@ -1332,6 +1434,10 @@ export class BaseCompiler { asmResult.hasAstOutput = true; asmResult.astOutput = astResult; } + if (ppResult) { + asmResult.hasPpOutput = true; + asmResult.ppOutput = ppResult; + } if (irResult) { asmResult.hasIrOutput = true; asmResult.irOutput = irResult; @@ -2031,6 +2137,8 @@ but nothing was dumped. Possible causes are: this.compiler.version = version; this.compiler.fullVersion = fullVersion; this.compiler.supportsCfg = this.isCfgCompiler(version); + // all C/C++ compilers support -E + this.compiler.supportsPpView = (this.compiler.lang === 'c' || this.compiler.lang === 'c++'); this.compiler.supportsAstView = this.couldSupportASTDump(version); } |