diff options
6 files changed, 191 insertions, 12 deletions
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java index f57fb9c..4f427a2 100644 --- a/lint/cli/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java +++ b/lint/cli/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java @@ -215,6 +215,10 @@ public abstract class AbstractCheckTest extends SdkTestCase { return true; } + if (issue == IssueRegistry.LINT_ERROR || issue == IssueRegistry.PARSER_ERROR) { + return !ignoreSystemErrors(); + } + return false; } @@ -230,10 +234,14 @@ public abstract class AbstractCheckTest extends SdkTestCase { return null; } + protected boolean ignoreSystemErrors() { + return true; + } + public class TestLintClient extends Main { private StringWriter mWriter = new StringWriter(); - TestLintClient() { + public TestLintClient() { mReporters.add(new TextReporter(this, mWriter, false)); } @@ -279,9 +287,14 @@ public abstract class AbstractCheckTest extends SdkTestCase { } @Override - public void report(Context context, Issue issue, Severity severity, Location location, - String message, Object data) { - if (issue == IssueRegistry.LINT_ERROR) { + public void report( + @NonNull Context context, + @NonNull Issue issue, + @NonNull Severity severity, + @Nullable Location location, + @NonNull String message, + @Nullable Object data) { + if (ignoreSystemErrors() && (issue == IssueRegistry.LINT_ERROR)) { return; } diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/ProjectTest.java b/lint/cli/src/test/java/com/android/tools/lint/client/api/ProjectTest.java new file mode 100644 index 0000000..4f3c9ef --- /dev/null +++ b/lint/cli/src/test/java/com/android/tools/lint/client/api/ProjectTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 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.client.api; + +import com.android.tools.lint.checks.AbstractCheckTest; +import com.android.tools.lint.checks.UnusedResourceDetector; +import com.android.tools.lint.detector.api.Detector; + +import java.io.File; +import java.util.Arrays; + +public class ProjectTest extends AbstractCheckTest { + @Override + protected boolean ignoreSystemErrors() { + return false; + } + + public void testCycle() throws Exception { + // Ensure that a cycle in library project dependencies doesn't cause + // infinite directory traversal + File master = getProjectDir("MasterProject", + // Master project + "multiproject/main-manifest.xml=>AndroidManifest.xml", + "multiproject/main.properties=>project.properties", + "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java" + ); + File library = getProjectDir("LibraryProject", + // Library project + "multiproject/library-manifest.xml=>AndroidManifest.xml", + "multiproject/main.properties=>project.properties", // RECURSIVE - points to self + "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java", + "multiproject/strings.xml=>res/values/strings.xml" + ); + + assertEquals("" + + "MasterProject/project.properties: Error: Circular library dependencies; check your project.properties files carefully [LintError]\n" + + "1 errors, 0 warnings\n", + + checkLint(Arrays.asList(master, library))); + } + + @Override + protected Detector getDetector() { + return new UnusedResourceDetector(); + } +} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/CircularDependencyException.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/CircularDependencyException.java new file mode 100644 index 0000000..337eb27 --- /dev/null +++ b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/CircularDependencyException.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 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.client.api; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.android.tools.lint.detector.api.Location; +import com.android.tools.lint.detector.api.Project; +import com.google.common.annotations.Beta; + +/** + * Exception thrown when there is a circular dependency, such as a circular dependency + * of library mProject references + * <p> + * <b>NOTE: This is not a public or final API; if you rely on this be prepared + * to adjust your code for the next tools release.</b> + */ +@Beta +public class CircularDependencyException extends RuntimeException { + @Nullable + private Project mProject; + + @Nullable + private Location mLocation; + + CircularDependencyException(@NonNull String message) { + super(message); + } + + /** + * Returns the associated project, if any + * + * @return the associated project, if any + */ + @Nullable + public Project getProject() { + return mProject; + } + + /** + * Sets the associated project, if any + * + * @param project the associated project, if any + */ + public void setProject(@Nullable Project project) { + mProject = project; + } + + /** + * Returns the associated location, if any + * + * @return the associated location, if any + */ + @Nullable + public Location getLocation() { + return mLocation; + } + + /** + * Sets the associated location, if any + * + * @param location the associated location, if any + */ + public void setLocation(@Nullable Location location) { + mLocation = location; + } +} diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintClient.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintClient.java index 7843aa1..9afef42 100644 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintClient.java +++ b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintClient.java @@ -39,6 +39,7 @@ import com.android.utils.StdLogger; import com.android.utils.StdLogger.Level; import com.google.common.annotations.Beta; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.google.common.io.Files; import org.w3c.dom.Document; @@ -54,6 +55,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -573,6 +575,8 @@ public abstract class LintClient { return project; } + private Set<File> mProjectDirs = Sets.newHashSet(); + /** * Create a project for the given directory * @param dir the root directory of the project @@ -581,6 +585,11 @@ public abstract class LintClient { */ @NonNull protected Project createProject(@NonNull File dir, @NonNull File referenceDir) { + if (mProjectDirs.contains(dir)) { + throw new CircularDependencyException( + "Circular library dependencies; check your project.properties files carefully"); + } + mProjectDirs.add(dir); return Project.create(this, dir, referenceDir); } diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintDriver.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintDriver.java index a92a9a2..0ff3a9a 100644 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintDriver.java +++ b/lint/libs/lint_api/src/main/java/com/android/tools/lint/client/api/LintDriver.java @@ -283,7 +283,16 @@ public class LintDriver { mCanceled = false; mScope = scope; - Collection<Project> projects = computeProjects(files); + Collection<Project> projects; + try { + projects = computeProjects(files); + } catch (CircularDependencyException e) { + Context context = new Context(this, e.getProject(), null, e.getLocation().getFile()); + mCurrentProject = e.getProject(); + context.report(IssueRegistry.LINT_ERROR, e.getLocation(), e.getMessage(), null); + mCurrentProject = null; + return; + } if (projects.isEmpty()) { mClient.log(null, "No projects found for %1$s", files.toString()); return; diff --git a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Project.java b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Project.java index c31a499..df27b2f 100644 --- a/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Project.java +++ b/lint/libs/lint_api/src/main/java/com/android/tools/lint/detector/api/Project.java @@ -32,6 +32,7 @@ import static com.android.SdkConstants.VALUE_TRUE; import com.android.SdkConstants; import com.android.annotations.NonNull; import com.android.annotations.Nullable; +import com.android.tools.lint.client.api.CircularDependencyException; import com.android.tools.lint.client.api.Configuration; import com.android.tools.lint.client.api.LintClient; import com.android.tools.lint.client.api.SdkInfo; @@ -104,7 +105,7 @@ public class Project { @NonNull public static Project create( @NonNull LintClient client, - @NonNull File dir, + @NonNull File dir, @NonNull File referenceDir) { return new Project(client, dir, referenceDir); } @@ -182,12 +183,18 @@ public class Project { } } - Project libraryPrj = client.getProject(libraryDir, libraryReferenceDir); - mDirectLibraries.add(libraryPrj); - // By default, we don't report issues in inferred library projects. - // The driver will set report = true for those library explicitly - // requested. - libraryPrj.setReportIssues(false); + try { + Project libraryPrj = client.getProject(libraryDir, libraryReferenceDir); + mDirectLibraries.add(libraryPrj); + // By default, we don't report issues in inferred library projects. + // The driver will set report = true for those library explicitly + // requested. + libraryPrj.setReportIssues(false); + } catch (CircularDependencyException e) { + e.setProject(this); + e.setLocation(Location.create(propFile)); + throw e; + } } } finally { Closeables.closeQuietly(is); |