blob: 5fb9aa187fd598b75e8b4dc0e8b3c2247b9bc4cf [file] [log] [blame]
/*
* Copyright (C) 2011 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.tools.lint.checks;
import static com.android.SdkConstants.PROGUARD_CONFIG;
import static com.android.SdkConstants.PROJECT_PROPERTIES;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import java.io.File;
/**
* Check which looks for errors in Proguard files.
*/
public class ProguardDetector extends Detector {
private static final Implementation IMPLEMENTATION = new Implementation(ProguardDetector.class,
Scope.PROGUARD_SCOPE);
/** The main issue discovered by this detector */
public static final Issue WRONG_KEEP = Issue.create(
"Proguard", //$NON-NLS-1$
"Using obsolete ProGuard configuration",
"Using `-keepclasseswithmembernames` in a proguard config file is not " +
"correct; it can cause some symbols to be renamed which should not be.\n" +
"Earlier versions of ADT used to create proguard.cfg files with the " +
"wrong format. Instead of `-keepclasseswithmembernames` use " +
"`-keepclasseswithmembers`, since the old flags also implies " +
"\"allow shrinking\" which means symbols only referred to from XML and " +
"not Java (such as possibly CustomViews) can get deleted.",
Category.CORRECTNESS,
8,
Severity.FATAL,
IMPLEMENTATION)
.addMoreInfo(
"http://http://code.google.com/p/android/issues/detail?id=16384"); //$NON-NLS-1$
/** Finds ProGuard files that contain non-project specific configuration
* locally and suggests replacing it with an include path */
public static final Issue SPLIT_CONFIG = Issue.create(
"ProguardSplit", //$NON-NLS-1$
"Proguard.cfg file contains generic Android rules",
"Earlier versions of the Android tools bundled a single `proguard.cfg` file " +
"containing a ProGuard configuration file suitable for Android shrinking and " +
"obfuscation. However, that version was copied into new projects, which " +
"means that it does not continue to get updated as we improve the default " +
"ProGuard rules for Android.\n" +
"\n" +
"In the new version of the tools, we have split the ProGuard configuration " +
"into two halves:\n" +
"* A simple configuration file containing only project-specific flags, in " +
"your project\n" +
"* A generic configuration file containing the recommended set of ProGuard " +
"options for Android projects. This generic file lives in the SDK install " +
"directory which means that it gets updated along with the tools.\n" +
"\n" +
"In order for this to work, the proguard.config property in the " +
"`project.properties` file now refers to a path, so you can reference both " +
"the generic file as well as your own (and any additional files too).\n" +
"\n" +
"To migrate your project to the new setup, create a new `proguard-project.txt` file " +
"in your project containing any project specific ProGuard flags as well as " +
"any customizations you have made, then update your project.properties file " +
"to contain:\n" +
"`proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt`",
Category.CORRECTNESS,
3,
Severity.WARNING,
IMPLEMENTATION);
@Override
public void run(@NonNull Context context) {
String contents = context.getContents();
if (contents != null) {
if (context.isEnabled(WRONG_KEEP)) {
int index = contents.indexOf(
// Old pattern:
"-keepclasseswithmembernames class * {\n" + //$NON-NLS-1$
" public <init>(android."); //$NON-NLS-1$
if (index != -1) {
context.report(WRONG_KEEP,
Location.create(context.file, contents, index, index),
"Obsolete ProGuard file; use `-keepclasseswithmembers` instead of " +
"`-keepclasseswithmembernames`");
}
}
if (context.isEnabled(SPLIT_CONFIG)) {
int index = contents.indexOf("-keep public class * extends android.app.Activity");
if (index != -1) {
// Only complain if project.properties actually references this file;
// no need to bother the users who got a default proguard.cfg file
// when they created their projects but haven't actually hooked it up
// to shrinking & obfuscation.
File propertyFile = new File(context.file.getParentFile(), PROJECT_PROPERTIES);
if (!propertyFile.exists()) {
return;
}
String properties = context.getClient().readFile(propertyFile);
int i = properties.indexOf(PROGUARD_CONFIG);
if (i == -1) {
return;
}
// Make sure the entry isn't just commented out, such as
// # To enable ProGuard to shrink and obfuscate your code, uncomment this:
// #proguard.config=proguard.cfg
for (; i >= 0; i--) {
char c = properties.charAt(i);
if (c == '#') {
return;
}
if (c == '\n') {
break;
}
}
if (properties.contains(PROGUARD_CONFIG)) {
context.report(SPLIT_CONFIG,
Location.create(context.file, contents, index, index),
String.format(
"Local ProGuard configuration contains general Android " +
"configuration: Inherit these settings instead? " +
"Modify `project.properties` to define " +
"`proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:%1$s`" +
" and then keep only project-specific configuration here",
context.file.getName()));
}
}
}
}
}
@Override
public boolean appliesTo(@NonNull Context context, @NonNull File file) {
return true;
}
@NonNull
@Override
public Speed getSpeed() {
return Speed.FAST;
}
}