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

com.android.build.gradle.internal.dsl.LintOptions.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.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

/**
 * DSL object for configuring lint options.
 */
public class LintOptions implements com.android.builder.model.LintOptions, Serializable {
    public static final String STDOUT = "stdout"
    public static final String STDERR = "stderr"
    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
    @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 LintOptions() {
    }

    public LintOptions(
            @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 com.android.builder.model.LintOptions create(@NonNull com.android.builder.model.LintOptions source) {
        return new LintOptions(
                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 issue ID 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 write informational messages
     * such as paths to report files written)
     */
    public boolean isQuiet() {
        return quiet
    }

    /**
     * Sets whether lint should be quiet (for example, not write informational messages
     * such as paths to report files written)
     */
    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
    }

    /** Returns whether lint should include explanations for issue errors. (Note that
     * HTML and XML reports intentionally do this unconditionally, ignoring this setting.) */
    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
    }

    /**
     * Returns whether lint should check for fatal errors during release builds. Default is true.
     * If issues with severity "fatal" are found, the release build is aborted.
     */
    @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
    }

    /** Whether we should write an text report. Default false. The location can be
     * controlled by {@link #getTextOutput()}. */
    @Override
    boolean getTextReport() {
        return textReport
    }

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

    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
    }

    /**
     * The optional path to where a text report should be written. The special value
     * "stdout" can be used to point to standard output.
     */
    @Override
    File getTextOutput() {
        return textOutput
    }

    /** Whether we should write an HTML report. Default true. The location can be
     * controlled by {@link #getHtmlOutput()}. */
    @Override
    boolean getHtmlReport() {
        return htmlReport
    }

    /** The optional path to where an HTML report should be written */
    @Override
    File getHtmlOutput() {
        return htmlOutput
    }

    /** Whether we should write an XML report. Default true. The location can be
     * controlled by {@link #getXmlOutput()}. */
    @Override
    boolean getXmlReport() {
        return xmlReport
    }

    /** The optional path to where an XML report should be written */
    @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(flags.isFatalOnly() ? STDERR: STDOUT)
                } else if (!output.isAbsolute() && !isStdOut(output) && !isStdErr(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 if (isStdErr(output)) {
                    writer = new PrintWriter(System.err, 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())
    }

    private static boolean isStdErr(@NonNull File output) {
        return STDERR.equals(output.getPath())
    }

    @NonNull
    private static File validateOutputFile(@NonNull File output) {
        if (isStdOut(output) || isStdErr(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())
    }

    /**
     * An optional map of severity overrides. The map maps from issue id's to the corresponding
     * severity to use, which must be "fatal", "error", "warning", or "ignore".
     *
     * @return a map of severity overrides, or null. The severities are one of the constants
     *  {@link #SEVERITY_FATAL}, {@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING},
     *  {@link #SEVERITY_INFORMATIONAL}, {@link #SEVERITY_IGNORE}
     */
    @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.

    /**
     * Adds the id to the set of issues to check.
     */
    public void check(String id) {
        check.add(id)
    }

    /**
     * Adds the ids to the set of issues to check.
     */
    public void check(String... ids) {
        for (String id : ids) {
            check(id);
        }
    }

    /**
     * Adds the id to the set of issues to enable.
     */
    public void enable(String id) {
        enable.add(id)
        def issue = new BuiltinIssueRegistry().getIssue(id)
        severities.put(id, issue != null ? issue.defaultSeverity : Severity.WARNING)
    }

    /**
     * Adds the ids to the set of issues to enable.
     */
    public void enable(String... ids) {
        for (String id : ids) {
            enable(id);
        }
    }

    /**
     * Adds the id to the set of issues to enable.
     */
    public void disable(String id) {
        disable.add(id)
        severities.put(id, Severity.IGNORE)
    }

    /**
     * Adds the ids to the set of issues to enable.
     */
    public void disable(String... ids) {
        for (String id : ids) {
            disable(id);
        }
    }

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

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

    /**
     * Adds a severity override for the given issues.
     */
    public void fatal(String id) {
        severities.put(id, Severity.FATAL)
    }

    /**
     * Adds a severity override for the given issues.
     */
    public void fatal(String... ids) {
        for (String id : ids) {
            fatal(id);
        }
    }

    /**
     * Adds a severity override for the given issues.
     */
    public void error(String id) {
        severities.put(id, Severity.ERROR)
    }

    /**
     * Adds a severity override for the given issues.
     */
    public void error(String... ids) {
        for (String id : ids) {
            error(id);
        }
    }

    /**
     * Adds a severity override for the given issues.
     */
    public void warning(String id) {
        severities.put(id, Severity.WARNING)
    }

    /**
     * Adds a severity override for the given issues.
     */
    public void warning(String... ids) {
        for (String id : ids) {
            warning(id);
        }
    }

    /**
     * Adds a severity override for the given issues.
     */
    public void ignore(String id) {
        severities.put(id, Severity.IGNORE)
    }

    /**
     * Adds a severity override for the given issues.
     */
    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 com.android.builder.model.LintOptions.SEVERITY_FATAL
            case Severity.ERROR:
                return com.android.builder.model.LintOptions.SEVERITY_ERROR
            case Severity.WARNING:
                return com.android.builder.model.LintOptions.SEVERITY_WARNING
            case Severity.INFORMATIONAL:
                return com.android.builder.model.LintOptions.SEVERITY_INFORMATIONAL
            case Severity.IGNORE:
            default:
                return com.android.builder.model.LintOptions.SEVERITY_IGNORE
        }
    }

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy