All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.build.gradle.internal.dsl.LintOptionsImpl.groovy Maven / Gradle / Ivy

There is a newer version: 2.3.0
Show newest version
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.build.gradle.internal.dsl

import com.android.tools.lint.checks.BuiltinIssueRegistry

import static com.android.builder.model.AndroidProject.FD_OUTPUTS

import com.android.annotations.NonNull
import com.android.annotations.Nullable;
import com.android.builder.model.LintOptions
import com.android.tools.lint.HtmlReporter
import com.android.tools.lint.LintCliClient
import com.android.tools.lint.LintCliFlags
import com.android.tools.lint.TextReporter
import com.android.tools.lint.XmlReporter
import com.android.tools.lint.detector.api.Severity
import com.google.common.collect.Maps
import com.google.common.collect.Sets
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputFile

import static com.android.SdkConstants.DOT_XML

public class LintOptionsImpl implements LintOptions, Serializable {
    public static final String STDOUT = "stdout"
    private static final long serialVersionUID = 1L;

    @Input
    private Set disable = Sets.newHashSet()
    @Input
    private Set enable = Sets.newHashSet()
    @Input
    private Set check = Sets.newHashSet()
    @Input
    private boolean abortOnError = true
    @Input
    private boolean absolutePaths = true
    @Input
    private boolean noLines
    @Input
    private boolean quiet = true
    @Input
    private boolean checkAllWarnings
    @Input
    private boolean ignoreWarnings
    @Input
    private boolean warningsAsErrors
    @Input
    private boolean showAll
    @Input
    private boolean checkReleaseBuilds = true;
    @Input
    private boolean explainIssues = true;
    @InputFile
    private File lintConfig
    @Input
    private boolean textReport
    @OutputFile
    private File textOutput
    @Input
    private boolean htmlReport = true
    @OutputFile
    private File htmlOutput
    @Input
    private boolean xmlReport = true
    @OutputFile
    private File xmlOutput

    private Map severities = Maps.newHashMap();

    public LintOptionsImpl() {
    }

    public LintOptionsImpl(
            @Nullable Set disable,
            @Nullable Set enable,
            @Nullable Set check,
            @Nullable File lintConfig,
            boolean textReport,
            @Nullable File textOutput,
            boolean htmlReport,
            @Nullable File htmlOutput,
            boolean xmlReport,
            @Nullable File xmlOutput,
            boolean abortOnError,
            boolean absolutePaths,
            boolean noLines,
            boolean quiet,
            boolean checkAllWarnings,
            boolean ignoreWarnings,
            boolean warningsAsErrors,
            boolean showAll,
            boolean explainIssues,
            boolean checkReleaseBuilds,
            @Nullable Map severityOverrides) {
        this.disable = disable
        this.enable = enable
        this.check = check
        this.lintConfig = lintConfig
        this.textReport = textReport
        this.textOutput = textOutput
        this.htmlReport = htmlReport
        this.htmlOutput = htmlOutput
        this.xmlReport = xmlReport
        this.xmlOutput = xmlOutput
        this.abortOnError = abortOnError
        this.absolutePaths = absolutePaths
        this.noLines = noLines
        this.quiet = quiet
        this.checkAllWarnings = checkAllWarnings
        this.ignoreWarnings = ignoreWarnings
        this.warningsAsErrors = warningsAsErrors
        this.showAll = showAll
        this.explainIssues = explainIssues
        this.checkReleaseBuilds = checkReleaseBuilds

        if (severityOverrides != null) {
            for (Map.Entry entry : severityOverrides.entrySet()) {
                severities.put(entry.key, convert(entry.value))
            }
        }
    }

    @NonNull
    static LintOptions create(@NonNull LintOptions source) {
        return new LintOptionsImpl(
                source.getDisable(),
                source.getEnable(),
                source.getCheck(),
                source.getLintConfig(),
                source.getTextReport(),
                source.getTextOutput(),
                source.getHtmlReport(),
                source.getHtmlOutput(),
                source.getXmlReport(),
                source.getXmlOutput(),
                source.isAbortOnError(),
                source.isAbsolutePaths(),
                source.isNoLines(),
                source.isQuiet(),
                source.isCheckAllWarnings(),
                source.isIgnoreWarnings(),
                source.isWarningsAsErrors(),
                source.isShowAll(),
                source.isExplainIssues(),
                source.isCheckReleaseBuilds(),
                source.getSeverityOverrides()
        )
    }

    /**
     * Returns the set of issue id's to suppress. Callers are allowed to modify this collection.
     */
    @NonNull
    public Set getDisable() {
        return disable
    }

    /**
     * Sets the set of issue id's to suppress. Callers are allowed to modify this collection.
     * Note that these ids add to rather than replace the given set of ids.
     */
    public void setDisable(@Nullable Set ids) {
        disable.addAll(ids)
    }

    /**
     * Returns the set of issue id's to enable. Callers are allowed to modify this collection.
     * To enable a given issue, add the {@link com.android.tools.lint.detector.api.Issue#getId()} to the returned set.
     */
    @NonNull
    public Set getEnable() {
        return enable
    }

    /**
     * Sets the set of issue id's to enable. Callers are allowed to modify this collection.
     * Note that these ids add to rather than replace the given set of ids.
     */
    public void setEnable(@Nullable Set ids) {
        enable.addAll(ids)
    }

    /**
     * Returns the exact set of issues to check, or null to run the issues that are enabled
     * by default plus any issues enabled via {@link #getEnable} and without issues disabled
     * via {@link #getDisable}. If non-null, callers are allowed to modify this collection.
     */
    @Nullable
    public Set getCheck() {
        return check
    }

    /**
     * Sets the exact set of issues to check.
     * @param ids the set of issue id's to check
     */
    public void setCheck(@Nullable Set ids) {
        check.addAll(ids)
    }

    /** Whether lint should set the exit code of the process if errors are found */
    public boolean isAbortOnError() {
        return this.abortOnError
    }

    /** Sets whether lint should set the exit code of the process if errors are found */
    public void setAbortOnError(boolean abortOnError) {
        this.abortOnError = abortOnError
    }

    /**
     * Whether lint should display full paths in the error output. By default the paths
     * are relative to the path lint was invoked from.
     */
    public boolean isAbsolutePaths() {
        return absolutePaths
    }

    /**
     * Sets whether lint should display full paths in the error output. By default the paths
     * are relative to the path lint was invoked from.
     */
    public void setAbsolutePaths(boolean absolutePaths) {
        this.absolutePaths = absolutePaths
    }

    /**
     * Whether lint should include the source lines in the output where errors occurred
     * (true by default)
     */
    public boolean isNoLines() {
        return this.noLines
    }

    /**
     * Sets whether lint should include the source lines in the output where errors occurred
     * (true by default)
     */
    public void setNoLines(boolean noLines) {
        this.noLines = noLines
    }

    /**
     * Returns whether lint should be quiet (for example, not show progress dots for each analyzed
     * file)
     */
    public boolean isQuiet() {
        return quiet
    }

    /**
     * Sets whether lint should be quiet (for example, not show progress dots for each analyzed
     * file)
     */
    public void setQuiet(boolean quiet) {
        this.quiet = quiet
    }

    /** Returns whether lint should check all warnings, including those off by default */
    public boolean isCheckAllWarnings() {
        return checkAllWarnings
    }

    /** Sets whether lint should check all warnings, including those off by default */
    public void setCheckAllWarnings(boolean warnAll) {
        this.checkAllWarnings = warnAll
    }

    /** Returns whether lint will only check for errors (ignoring warnings) */
    public boolean isIgnoreWarnings() {
        return ignoreWarnings
    }

    /** Sets whether lint will only check for errors (ignoring warnings) */
    public void setIgnoreWarnings(boolean noWarnings) {
        this.ignoreWarnings = noWarnings
    }

    /** Returns whether lint should treat all warnings as errors */
    public boolean isWarningsAsErrors() {
        return warningsAsErrors
    }

    /** Sets whether lint should treat all warnings as errors */
    public void setWarningsAsErrors(boolean allErrors) {
        this.warningsAsErrors = allErrors
    }

    public boolean isExplainIssues() {
        return explainIssues
    }

    public void setExplainIssues(boolean explainIssues) {
        this.explainIssues = explainIssues
    }

    /**
     * Returns whether lint should include all output (e.g. include all alternate
     * locations, not truncating long messages, etc.)
     */
    public boolean isShowAll() {
        return showAll
    }

    /**
     * Sets whether lint should include all output (e.g. include all alternate
     * locations, not truncating long messages, etc.)
     */
    public void setShowAll(boolean showAll) {
        this.showAll = showAll
    }

    @Override
    public boolean isCheckReleaseBuilds() {
        return checkReleaseBuilds;
    }

    public void setCheckReleaseBuilds(boolean checkReleaseBuilds) {
        this.checkReleaseBuilds = checkReleaseBuilds
    }

    /**
     * Returns the default configuration file to use as a fallback
     */
    public File getLintConfig() {
        return lintConfig
    }

    @Override
    boolean getTextReport() {
        return textReport
    }

    void setTextReport(boolean textReport) {
        this.textReport = textReport
    }

    void setTextOutput(@NonNull File textOutput) {
        this.textOutput = textOutput
    }

    void setHtmlReport(boolean htmlReport) {
        this.htmlReport = htmlReport
    }

    void setHtmlOutput(@NonNull File htmlOutput) {
        this.htmlOutput = htmlOutput
    }

    void setXmlReport(boolean xmlReport) {
        this.xmlReport = xmlReport
    }

    void setXmlOutput(@NonNull File xmlOutput) {
        this.xmlOutput = xmlOutput
    }

    @Override
    File getTextOutput() {
        return textOutput
    }

    @Override
    boolean getHtmlReport() {
        return htmlReport
    }

    @Override
    File getHtmlOutput() {
        return htmlOutput
    }

    @Override
    boolean getXmlReport() {
        return xmlReport
    }

    @Override
    File getXmlOutput() {
        return xmlOutput
    }

    /**
     * Sets the default config file to use as a fallback. This corresponds to a {@code lint.xml}
     * file with severities etc to use when a project does not have more specific information.
     */
    public void setLintConfig(@NonNull File lintConfig) {
        this.lintConfig = lintConfig
    }

    public void syncTo(
            @NonNull LintCliClient client,
            @NonNull LintCliFlags flags,
            @Nullable String variantName,
            @Nullable Project project,
            boolean report) {
        if (disable != null) {
            flags.getSuppressedIds().addAll(disable)
        }
        if (enable != null) {
            flags.getEnabledIds().addAll(enable)
        }
        if (check != null && !check.isEmpty()) {
            flags.setExactCheckedIds(check)
        }
        flags.setSetExitCode(this.abortOnError)
        flags.setFullPath(absolutePaths)
        flags.setShowSourceLines(!noLines)
        flags.setQuiet(quiet)
        flags.setCheckAllWarnings(checkAllWarnings)
        flags.setIgnoreWarnings(ignoreWarnings)
        flags.setWarningsAsErrors(warningsAsErrors)
        flags.setShowEverything(showAll)
        flags.setDefaultConfiguration(lintConfig)
        flags.setSeverityOverrides(severities)
        flags.setExplainIssues(explainIssues)

        if (report || flags.isFatalOnly() && this.abortOnError) {
            if (textReport || flags.isFatalOnly()) {
                File output = textOutput
                if (output == null) {
                    output = new File(STDOUT)
                } else if (!output.isAbsolute() && !isStdOut(output)) {
                    output = project.file(output.getPath())
                }
                output = validateOutputFile(output)

                Writer writer
                File file = null
                boolean closeWriter
                if (isStdOut(output)) {
                    writer = new PrintWriter(System.out, true)
                    closeWriter = false
                } else {
                    file = output
                    try {
                        writer = new BufferedWriter(new FileWriter(output))
                    } catch (IOException e) {
                        throw new GradleException("Text invalid argument.", e)
                    }
                    closeWriter = true
                }
                flags.getReporters().add(new TextReporter(client, flags, file, writer,
                        closeWriter))
            }
            if (htmlReport) {
                File output = htmlOutput
                if (output == null || flags.isFatalOnly()) {
                    output = createOutputPath(project, variantName, ".html", flags.isFatalOnly())
                } else if (!output.isAbsolute()) {
                    output = project.file(output.getPath())
                }
                output = validateOutputFile(output)
                try {
                    flags.getReporters().add(new HtmlReporter(client, output))
                } catch (IOException e) {
                    throw new GradleException("HTML invalid argument.", e)
                }
            }
            if (xmlReport) {
                File output = xmlOutput
                if (output == null || flags.isFatalOnly()) {
                    output = createOutputPath(project, variantName, DOT_XML, flags.isFatalOnly())
                } else if (!output.isAbsolute()) {
                    output = project.file(output.getPath())
                }
                output = validateOutputFile(output)
                try {
                    flags.getReporters().add(new XmlReporter(client, output))
                } catch (IOException e) {
                    throw new GradleException("XML invalid argument.", e)
                }
            }
        }
    }

    private static boolean isStdOut(@NonNull File output) {
        return STDOUT.equals(output.getPath())
    }

    @NonNull
    private static File validateOutputFile(@NonNull File output) {
        if (isStdOut(output)) {
            return output
        }

        File parent = output.parentFile
        if (!parent.exists()) {
            parent.mkdirs()
        }

        output = output.getAbsoluteFile()
        if (output.exists()) {
            boolean delete = output.delete()
            if (!delete) {
                throw new GradleException("Could not delete old " + output)
            }
        }
        if (output.getParentFile() != null && !output.getParentFile().canWrite()) {
            throw new GradleException("Cannot write output file " + output)
        }

        return output
    }

    private static File createOutputPath(
            @NonNull Project project,
            @NonNull String variantName,
            @NonNull String extension,
            boolean fatalOnly) {
        StringBuilder base = new StringBuilder()
        base.append(FD_OUTPUTS)
        base.append(File.separator)
        base.append("lint-results")
        if (variantName != null) {
            base.append("-")
            base.append(variantName)
        }
        if (fatalOnly) {
            base.append("-fatal")
        }
        base.append(extension)
        return new File(project.buildDir, base.toString())
    }

    @Override
    @Nullable
    public Map getSeverityOverrides() {
        if (severities == null || severities.isEmpty()) {
            return null
        }

        Map map =
                Maps.newHashMapWithExpectedSize(severities.size())
        for (Map.Entry entry : severities.entrySet()) {
            map.put(entry.key, convert(entry.value))
        }

        return map
    }

    // -- DSL Methods.

    public void check(String id) {
        check.add(id)
    }

    public void check(String... ids) {
        for (String id : ids) {
            check(id);
        }
    }

    public void enable(String id) {
        enable.add(id)
        def issue = new BuiltinIssueRegistry().getIssue(id)
        severities.put(id, issue != null ? issue.defaultSeverity : Severity.WARNING)
    }

    public void enable(String... ids) {
        for (String id : ids) {
            enable(id);
        }
    }

    public void disable(String id) {
        disable.add(id)
        severities.put(id, Severity.IGNORE)
    }

    public void disable(String... ids) {
        for (String id : ids) {
            disable(id);
        }
    }

    // For textOutput 'stdout' (normally a file)
    void textOutput(String textOutput) {
        this.textOutput = new File(textOutput)
    }

    // For textOutput file()
    void textOutput(File textOutput) {
        this.textOutput = textOutput;
    }

    public void fatal(String id) {
        severities.put(id, Severity.FATAL)
    }

    public void fatal(String... ids) {
        for (String id : ids) {
            fatal(id);
        }
    }

    public void error(String id) {
        severities.put(id, Severity.ERROR)
    }

    public void error(String... ids) {
        for (String id : ids) {
            error(id);
        }
    }

    public void warning(String id) {
        severities.put(id, Severity.WARNING)
    }

    public void warning(String... ids) {
        for (String id : ids) {
            warning(id);
        }
    }

    public void ignore(String id) {
        severities.put(id, Severity.IGNORE)
    }

    public void ignore(String... ids) {
        for (String id : ids) {
            ignore(id);
        }
    }

    // Without these qualifiers, Groovy compilation will fail with "Apparent variable
    // 'SEVERITY_FATAL' was found in a static scope but doesn't refer to a local variable,
    // static field or class"
    @SuppressWarnings("UnnecessaryQualifiedReference")
    private static int convert(Severity s) {
        switch (s) {
            case Severity.FATAL:
                return LintOptions.SEVERITY_FATAL
            case Severity.ERROR:
                return LintOptions.SEVERITY_ERROR
            case Severity.WARNING:
                return LintOptions.SEVERITY_WARNING
            case Severity.INFORMATIONAL:
                return LintOptions.SEVERITY_INFORMATIONAL
            case Severity.IGNORE:
            default:
                return LintOptions.SEVERITY_IGNORE
        }
    }

    @SuppressWarnings("UnnecessaryQualifiedReference")
    private static Severity convert(int s) {
        switch (s) {
            case LintOptions.SEVERITY_FATAL:
                return Severity.FATAL
            case LintOptions.SEVERITY_ERROR:
                return Severity.ERROR
            case LintOptions.SEVERITY_WARNING:
                return Severity.WARNING
            case LintOptions.SEVERITY_INFORMATIONAL:
                return Severity.INFORMATIONAL
            case LintOptions.SEVERITY_IGNORE:
            default:
                return Severity.IGNORE
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy