blob: 3bf7b8d9899271b8711d59ef181dc7116896e114 [file] [log] [blame]
/*
* 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;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.gradle.BasePlugin;
import com.android.builder.model.AndroidProject;
import com.android.builder.model.Variant;
import com.android.tools.lint.LintCliClient;
import com.android.tools.lint.LintCliFlags;
import com.android.tools.lint.Warning;
import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.client.api.LintRequest;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Project;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class LintGradleClient extends LintCliClient {
private final AndroidProject mModelProject;
private final String mVariantName;
private final BasePlugin mPlugin;
private List<File> mCustomRules = Lists.newArrayList();
public LintGradleClient(
@NonNull IssueRegistry registry,
@NonNull LintCliFlags flags,
@NonNull BasePlugin plugin,
@NonNull AndroidProject modelProject,
@Nullable String variantName) {
super(flags);
mPlugin = plugin;
mModelProject = modelProject;
mVariantName = variantName;
mRegistry = registry;
}
@NonNull
public BasePlugin getPlugin() {
return mPlugin;
}
public void setCustomRules(List<File> customRules) {
mCustomRules = customRules;
}
@Override
public List<File> findRuleJars(@NonNull Project project) {
return mCustomRules;
}
@Override
protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
// Should not be called by lint since we supply an explicit set of projects
// to the LintRequest
throw new IllegalStateException();
}
@Override
public File getSdkHome() {
File sdkHome = mPlugin.getSdkDirectory();
if (sdkHome != null) {
return sdkHome;
}
return super.getSdkHome();
}
@Override
@NonNull
protected LintRequest createLintRequest(@NonNull List<File> files) {
return new LintGradleRequest(this, mModelProject, mPlugin, mVariantName, files);
}
/** Run lint with the given registry and return the resulting warnings */
@NonNull
public List<Warning> run(@NonNull IssueRegistry registry) throws IOException {
run(registry, Collections.<File>emptyList());
return mWarnings;
}
/**
* Given a list of results from separate variants, merge them into a single
* list of warnings, and mark their
* @param warningMap a map from variant to corresponding warnings
* @param project the project model
* @return a merged list of issues
*/
@NonNull
public static List<Warning> merge(
@NonNull Map<Variant,List<Warning>> warningMap,
@NonNull AndroidProject project) {
// Easy merge?
if (warningMap.size() == 1) {
return warningMap.values().iterator().next();
}
int maxCount = 0;
for (List<Warning> warnings : warningMap.values()) {
int size = warnings.size();
maxCount = Math.max(size, maxCount);
}
if (maxCount == 0) {
return Collections.emptyList();
}
int totalVariantCount = project.getVariants().size();
List<Warning> merged = Lists.newArrayListWithExpectedSize(2 * maxCount);
// Map fro issue to message to line number to file name to canonical warning
Map<Issue,Map<String, Map<Integer, Map<String, Warning>>>> map =
Maps.newHashMapWithExpectedSize(2 * maxCount);
for (Map.Entry<Variant,List<Warning>> entry : warningMap.entrySet()) {
Variant variant = entry.getKey();
List<Warning> warnings = entry.getValue();
for (Warning warning : warnings) {
Map<String,Map<Integer,Map<String,Warning>>> messageMap = map.get(warning.issue);
if (messageMap == null) {
messageMap = Maps.newHashMap();
map.put(warning.issue, messageMap);
}
Map<Integer, Map<String, Warning>> lineMap = messageMap.get(warning.message);
if (lineMap == null) {
lineMap = Maps.newHashMap();
messageMap.put(warning.message, lineMap);
}
Map<String, Warning> fileMap = lineMap.get(warning.line);
if (fileMap == null) {
fileMap = Maps.newHashMap();
lineMap.put(warning.line, fileMap);
}
String fileName = warning.file != null ? warning.file.getName() : "<unknown>";
Warning canonical = fileMap.get(fileName);
if (canonical == null) {
canonical = warning;
fileMap.put(fileName, canonical);
canonical.variants = Sets.newHashSet();
canonical.gradleProject = project;
}
merged.add(canonical);
canonical.variants.add(variant);
}
}
// Clear out variants on any nodes that don't define all
for (Warning warning : merged) {
if (warning.variants != null && warning.variants.size() == totalVariantCount) {
// If this error is present in all variants, just clear it out
warning.variants = null;
}
}
Collections.sort(merged);
return merged;
}
}