| // Copyright (c) 2017, Jared Wyles |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // * Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above copyright |
| // notice, this list of conditions and the following disclaimer in the |
| // documentation and/or other materials provided with the distribution. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| // POSSIBILITY OF SUCH DAMAGE. |
| |
| import _ from 'underscore'; |
| |
| import { logger } from '../logger'; |
| import * as utils from '../utils'; |
| |
| export class BaseParser { |
| static hasSupport(options, forOption) { |
| return _.keys(options).find(option => option.includes(forOption)); |
| } |
| |
| static parseLines(stdout, optionRegex) { |
| let previousOption = false; |
| let options = {}; |
| |
| utils.eachLine(stdout, line => { |
| const match = line.match(optionRegex); |
| if (!match) { |
| if (previousOption && (line.trim().length > 0)) { |
| if (options[previousOption].description.endsWith('-')) |
| options[previousOption].description += line.trim(); |
| else { |
| if (options[previousOption].description.length > 0) |
| options[previousOption].description += ' ' + line.trim(); |
| else |
| options[previousOption].description = line.trim(); |
| } |
| } else { |
| previousOption = false; |
| } |
| return; |
| } |
| |
| if (match) previousOption = match[1]; |
| if (previousOption) { |
| options[previousOption] = { |
| description: match[2].trim(), |
| timesused: 0, |
| }; |
| } |
| }); |
| |
| return options; |
| } |
| |
| static async getOptions(compiler, helpArg) { |
| const optionFinder = /^\s*(--?[\d+,<=>[\]a-z|-]*)\s*(.*)/i; |
| const result = await compiler.execCompilerCached(compiler.compiler.exe, [helpArg]); |
| const options = result.code === 0 |
| ? BaseParser.parseLines(result.stdout + result.stderr, optionFinder) : {}; |
| compiler.possibleArguments.populateOptions(options); |
| return options; |
| } |
| |
| static parse(compiler) { |
| return compiler; |
| } |
| } |
| |
| export class GCCParser extends BaseParser { |
| static async setCompilerSettingsFromOptions(compiler, options) { |
| const keys = _.keys(options); |
| logger.debug(`gcc-like compiler options: ${keys.join(' ')}`); |
| if (BaseParser.hasSupport(options, '-masm=')) { |
| // -masm= may be available but unsupported by the compiler. |
| const res = await compiler.execCompilerCached(compiler.compiler.exe, |
| ['-fsyntax-only', '--target-help', '-masm=intel']); |
| if (res.code === 0) { |
| compiler.compiler.intelAsm = '-masm=intel'; |
| compiler.compiler.supportsIntel = true; |
| } |
| } |
| if (BaseParser.hasSupport(options, '-fdiagnostics-color')) { |
| if (compiler.compiler.options) compiler.compiler.options += ' '; |
| compiler.compiler.options += '-fdiagnostics-color=always'; |
| } |
| // This check is not infallible, but takes care of Rust and Swift being picked up :) |
| if (_.find(keys, key => key.startsWith('-fdump-'))) { |
| compiler.compiler.supportsGccDump = true; |
| |
| // By default, consider the compiler to be a regular GCC (eg. gcc, |
| // g++) and do the extra work of filtering out enabled pass that did |
| // not produce anything. |
| compiler.compiler.removeEmptyGccDump = true; |
| } |
| } |
| |
| static async parse(compiler) { |
| const results = await Promise.all([ |
| GCCParser.getOptions(compiler, '-fsyntax-only --target-help'), |
| GCCParser.getOptions(compiler, '-fsyntax-only --help=common'), |
| GCCParser.getOptions(compiler, '-fsyntax-only --help=optimizers'), |
| ]); |
| const options = Object.assign({}, ...results); |
| await this.setCompilerSettingsFromOptions(compiler, options); |
| return compiler; |
| } |
| |
| static async getOptions(compiler, helpArg) { |
| const optionFinder = /^\s*(--?[\d+,<=>[\]a-z|-]*)\s*(.*)/i; |
| const result = await compiler.execCompilerCached(compiler.compiler.exe, helpArg.split(' ')); |
| const options = result.code === 0 |
| ? BaseParser.parseLines(result.stdout + result.stderr, optionFinder) : {}; |
| compiler.possibleArguments.populateOptions(options); |
| return options; |
| } |
| } |
| |
| export class ClangParser extends BaseParser { |
| static async setCompilerSettingsFromOptions(compiler, options) { |
| logger.debug(`clang-like compiler options: ${_.keys(options).join(' ')}`); |
| if (BaseParser.hasSupport(options, '-fsave-optimization-record')) { |
| compiler.compiler.optArg = '-fsave-optimization-record'; |
| compiler.compiler.supportsOptOutput = true; |
| } |
| if (BaseParser.hasSupport(options, '-fcolor-diagnostics')) { |
| if (compiler.compiler.options) compiler.compiler.options += ' '; |
| compiler.compiler.options += '-fcolor-diagnostics'; |
| } |
| if (BaseParser.hasSupport(options, '-emit-llvm')) { |
| compiler.compiler.supportsIrView = true; |
| compiler.compiler.irArg = ['-Xclang', '-emit-llvm', '-fsyntax-only']; |
| } |
| if (BaseParser.hasSupport(options, '-fno-crash-diagnostics')) { |
| if (compiler.compiler.options) compiler.compiler.options += ' '; |
| compiler.compiler.options += '-fno-crash-diagnostics'; |
| } |
| } |
| |
| static async parse(compiler) { |
| const options = await ClangParser.getOptions(compiler, '--help'); |
| await this.setCompilerSettingsFromOptions(compiler, options); |
| return compiler; |
| } |
| } |
| |
| export class PascalParser extends BaseParser { |
| static async parse(compiler) { |
| await PascalParser.getOptions(compiler, '-help'); |
| return compiler; |
| } |
| } |
| |
| export class ISPCParser extends BaseParser { |
| static async setCompilerSettingsFromOptions(compiler, options) { |
| if (BaseParser.hasSupport(options, '--x86-asm-syntax')) { |
| compiler.compiler.intelAsm = '--x86-asm-syntax=intel'; |
| compiler.compiler.supportsIntel = true; |
| } |
| } |
| |
| static async parse(compiler) { |
| const options = await ISPCParser.getOptions(compiler, '--help'); |
| await this.setCompilerSettingsFromOptions(compiler, options); |
| return compiler; |
| } |
| |
| static async getOptions(compiler, helpArg) { |
| const result = await compiler.execCompilerCached(compiler.compiler.exe, [helpArg]); |
| const optionFinder = /^\s*\[(--?[\d\s()+,/<=>a-z{|}-]*)]\s*(.*)/i; |
| const options = result.code === 0 |
| ? BaseParser.parseLines(result.stdout + result.stderr, optionFinder) : {}; |
| compiler.possibleArguments.populateOptions(options); |
| return options; |
| } |
| } |
| |
| export class JavaParser extends BaseParser { |
| static async parse(compiler) { |
| await JavaParser.getOptions(compiler, '-help'); |
| return compiler; |
| } |
| } |
| |
| export class KotlinParser extends BaseParser { |
| static async parse(compiler) { |
| await KotlinParser.getOptions(compiler, '-help'); |
| return compiler; |
| } |
| } |
| |
| export class ScalaParser extends BaseParser { |
| static async parse(compiler) { |
| await ScalaParser.getOptions(compiler, '-help'); |
| return compiler; |
| } |
| } |
| |
| export class VCParser extends BaseParser { |
| static async parse(compiler) { |
| await VCParser.getOptions(compiler, '/help'); |
| return compiler; |
| } |
| |
| static parseLines(stdout, optionRegex) { |
| let previousOption = false; |
| let options = {}; |
| |
| const matchLine = (line) => { |
| if (line.startsWith('/?')) return; |
| |
| const match = line.match(optionRegex); |
| if (!match) { |
| if (previousOption && (line.trim().length > 0)) { |
| if (options[previousOption].description.endsWith(':')) |
| options[previousOption].description += ' ' + line.trim(); |
| else { |
| if (options[previousOption].description.length > 0) |
| options[previousOption].description += ', ' + line.trim(); |
| else |
| options[previousOption].description = line.trim(); |
| } |
| } else { |
| previousOption = false; |
| } |
| return; |
| } |
| |
| if (match) previousOption = match[1]; |
| if (previousOption) { |
| options[previousOption] = { |
| description: match[2].trim(), |
| timesused: 0, |
| }; |
| } |
| }; |
| |
| utils.eachLine(stdout, line => { |
| if (line.length === 0) return; |
| if (line.includes('C/C++ COMPILER OPTIONS')) return; |
| if (/^\s+-.*-$/.test(line)) return; |
| |
| let col1; |
| let col2; |
| if ((line.length > 39) && (line[40] === '/')) { |
| col1 = line.substr(0, 39); |
| col2 = line.substr(40); |
| } else { |
| col1 = line; |
| col2 = ''; |
| } |
| |
| if (col1) matchLine(col1); |
| if (col2) matchLine(col2); |
| }); |
| |
| return options; |
| } |
| |
| static async getOptions(compiler, helpArg) { |
| const result = await compiler.execCompilerCached(compiler.compiler.exe, [helpArg]); |
| const optionFinder = /^\s*(\/[\w#+,.:<=>[\]{|}-]*)\s*(.*)/i; |
| const options = result.code === 0 |
| ? this.parseLines(result.stdout, optionFinder) : {}; |
| compiler.possibleArguments.populateOptions(options); |
| return options; |
| } |
| } |
| |
| export class RustParser extends BaseParser { |
| static async setCompilerSettingsFromOptions(compiler, options) { |
| if (BaseParser.hasSupport(options, '--color')) { |
| if (compiler.compiler.options) compiler.compiler.options += ' '; |
| compiler.compiler.options += '--color=always'; |
| } |
| } |
| |
| static async parse(compiler) { |
| const results = await Promise.all([ |
| RustParser.getOptions(compiler, '--help'), |
| RustParser.getOptions(compiler, '-C help'), |
| RustParser.getOptions(compiler, '--help -v'), |
| ]); |
| const options = Object.assign({}, ...results); |
| await this.setCompilerSettingsFromOptions(compiler, options); |
| return compiler; |
| } |
| |
| static async getOptions(compiler, helpArg) { |
| const result = await compiler.execCompilerCached(compiler.compiler.exe, helpArg.split(' ')); |
| let options = {}; |
| if (result.code === 0) { |
| if (helpArg === '-C help') { |
| const optionFinder = /^\s*(-c\s*[\d=a-z-]*)\s--\s(.*)/i; |
| |
| options = BaseParser.parseLines(result.stdout + result.stderr, optionFinder); |
| } else { |
| const optionFinder = /^\s*(--?[\d+,<=>[\]a-z|-]*)\s*(.*)/i; |
| |
| options = BaseParser.parseLines(result.stdout + result.stderr, optionFinder); |
| } |
| } |
| compiler.possibleArguments.populateOptions(options); |
| return options; |
| } |
| } |
| |
| export class MrustcParser extends BaseParser { |
| static async parse(compiler) { |
| await MrustcParser.getOptions(compiler, '--help'); |
| return compiler; |
| } |
| } |
| |
| export class NimParser extends BaseParser { |
| static async parse(compiler) { |
| await NimParser.getOptions(compiler, '-help'); |
| return compiler; |
| } |
| } |
| |
| export class CrystalParser extends BaseParser { |
| static async parse(compiler) { |
| await CrystalParser.getOptions(compiler, 'build'); |
| return compiler; |
| } |
| } |