blob: 478c39f9d863d2d757ef60e4302ee725608ffbeb [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.tools.idea.gradle.eclipse;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.repository.GradleCoordinate;
import com.android.ide.common.repository.SdkMavenRepository;
import com.android.resources.ResourceFolderType;
import com.android.sdklib.AndroidVersion;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
import static com.android.SdkConstants.*;
import static com.android.tools.idea.gradle.eclipse.GradleImport.*;
import static java.io.File.separator;
import static java.io.File.separatorChar;
public abstract class ImportModule implements Comparable<ImportModule> {
@SuppressWarnings("SpellCheckingInspection")
private static final String SHERLOCK_DEP = "com.actionbarsherlock:actionbarsherlock:4.4.0@aar";
private static final String PLAY_SERVICES_DEP = "com.google.android.gms:play-services:+";
public static final String SUPPORT_GROUP_ID = "com.android.support";
public static final String APPCOMPAT_ARTIFACT = "appcompat-v7";
public static final String SUPPORT_ARTIFACT = "support-v4";
public static final String GRIDLAYOUT_ARTIFACT = "gridlayout-v7";
@SuppressWarnings("SpellCheckingInspection")
public static final String MEDIA_ROUTER_ARTIFACT = "mediarouter-v7";
protected final GradleImport myImporter;
protected final List<GradleCoordinate> myDependencies = Lists.newArrayList();
protected final List<GradleCoordinate> myTestDependencies = Lists.newArrayList();
protected final List<File> myJarDependencies = Lists.newArrayList();
protected final List<File> myTestJarDependencies = Lists.newArrayList();
protected List<GradleCoordinate> myReplaceWithDependencies;
private String myModuleName;
public ImportModule(@NonNull GradleImport importer) {
myImporter = importer;
}
protected static File getTestJarOutputRelativePath(File jar) {
return new File(LIBS_FOLDER, jar.getName());
}
private static void recordCopiedFile(@NonNull Set<File> copied, @NonNull File file) throws IOException {
copied.add(file);
copied.add(file.getCanonicalFile());
}
protected abstract boolean isLibrary();
protected abstract boolean isApp();
protected abstract boolean isAndroidLibrary();
protected abstract boolean isAndroidProject();
protected abstract boolean isJavaLibrary();
protected abstract boolean isNdkProject();
@NonNull
protected abstract AndroidVersion getCompileSdkVersion();
@NonNull
protected abstract AndroidVersion getMinSdkVersion();
@NonNull
protected abstract AndroidVersion getTargetSdkVersion();
@Nullable
protected abstract String getAddOn();
@NonNull
public abstract File getDir();
@NonNull
protected abstract String getOriginalName();
@NonNull
protected abstract List<File> getSourcePaths();
@NonNull
protected abstract List<File> getJarPaths();
@NonNull
protected abstract List<File> getTestJarPaths();
@NonNull
protected abstract List<File> getNativeLibs();
@NonNull
protected abstract File resolveFile(@NonNull File file);
@NonNull
protected abstract File getCanonicalModuleDir();
@NonNull
protected abstract List<File> getLocalProguardFiles();
@NonNull
protected abstract List<File> getSdkProguardFiles();
@NonNull
protected abstract String getLanguageLevel();
@NonNull
protected abstract List<ImportModule> getDirectDependencies();
@NonNull
protected abstract List<ImportModule> getAllDependencies();
@Nullable
protected abstract String getPackage();
@Nullable
protected abstract File getLintXml();
@Nullable
protected abstract File getOutputDir();
@Nullable
protected abstract File getManifestFile();
@Nullable
protected abstract File getResourceDir();
@Nullable
protected abstract File getAssetsDir();
@Nullable
protected abstract File getNativeSources();
@Nullable
protected abstract String getNativeModuleName();
@Nullable
protected abstract File getInstrumentationDir();
@Nullable
protected abstract Charset getFileEncoding(@NonNull File file);
@Nullable
protected abstract Charset getProjectEncoding(@NonNull File file);
public void initialize() {
initDependencies();
initReplaceWithDependency();
}
protected void initDependencies() {
}
@Nullable
public GradleCoordinate getLatestVersion(String artifact) {
int compileVersion = GradleImport.CURRENT_COMPILE_VERSION;
AndroidVersion version = getCompileSdkVersion();
if (version != AndroidVersion.DEFAULT) {
compileVersion = version.getFeatureLevel();
}
// If you're using for example android-14, you still need support libraries
// from version 18 (earliest version where we have all the libs in the m2 repository)
if (compileVersion < 18) {
compileVersion = 18;
}
String compileVersionString = Integer.toString(compileVersion);
if (myImporter.getSdkLocation() != null) {
@SuppressWarnings("UnnecessaryLocalVariable") String filter = compileVersionString;
GradleCoordinate max =
SdkMavenRepository.ANDROID.getHighestInstalledVersion(myImporter.getSdkLocation(), SUPPORT_GROUP_ID, artifact, filter, true);
if (max != null) {
return max;
}
}
String coordinate = SUPPORT_GROUP_ID + ':' + artifact + ':' + compileVersionString + ".+";
return GradleCoordinate.parseCoordinateString(coordinate);
}
@Nullable
protected GradleCoordinate getAppCompatDependency() {
return getLatestVersion(APPCOMPAT_ARTIFACT);
}
@Nullable
protected GradleCoordinate getSupportLibDependency() {
return getLatestVersion(SUPPORT_ARTIFACT);
}
@Nullable
protected GradleCoordinate getGridLayoutDependency() {
return getLatestVersion(GRIDLAYOUT_ARTIFACT);
}
@Nullable
protected GradleCoordinate getMediaRouterDependency() {
return getLatestVersion(MEDIA_ROUTER_ARTIFACT);
}
@SuppressWarnings("SpellCheckingInspection")
@Nullable
GradleCoordinate guessDependency(@NonNull File jar) {
// Make guesses based on library. For now, we do simple name checks, but
// later consider looking at jar contents, md5 sums etc, especially to
// pick up exact version numbers of popular libraries.
// (This list was generated by just looking at some existing projects
// and seeing which .jar files they depended on and then consulting available
// gradle dependencies via http://gradleplease.appspot.com/ )
String name = jar.getName().toLowerCase(Locale.US);
if (name.equals("android-support-v4.jar")) {
myImporter.markJarHandled(jar);
return getSupportLibDependency();
}
else if (name.equals("android-support-v7-gridlayout.jar")) {
myImporter.markJarHandled(jar);
return getGridLayoutDependency();
}
else if (name.equals("android-support-v7-appcompat.jar")) {
myImporter.markJarHandled(jar);
return getAppCompatDependency();
}
else if (name.equals("com_actionbarsherlock.jar") || name.equalsIgnoreCase("actionbarsherlock.jar")) {
myImporter.markJarHandled(jar);
return GradleCoordinate.parseCoordinateString(SHERLOCK_DEP);
}
else if (name.equals("guava.jar") || name.startsWith("guava-")) {
myImporter.markJarHandled(jar);
String version = getVersion(jar, "guava-", name, "18.0");
if (version.startsWith("r")) { // really old versions
version = "15.0";
}
return GradleCoordinate.parseCoordinateString("com.google.guava:guava:" + version);
}
else if (name.startsWith("joda-time")) {
myImporter.markJarHandled(jar);
// Convert joda-time-2.1 jar into joda-time:joda-time:2.1 etc
String version = getVersion(jar, "joda-time-", name, "2.7");
return GradleCoordinate.parseCoordinateString("joda-time:joda-time:" + version);
}
else if (name.startsWith("robotium-solo-")) {
myImporter.markJarHandled(jar);
String version = getVersion(jar, "robotium-solo-", name, "5.3.1");
return GradleCoordinate.parseCoordinateString("com.jayway.android.robotium:robotium-solo:" + version);
}
else if (name.startsWith("protobuf-java-")) {
myImporter.markJarHandled(jar);
String version = getVersion(jar, "protobuf-java-", name, "2.6.1");
return GradleCoordinate.parseCoordinateString("com.google.protobuf:protobuf-java:" + version);
}
else if (name.startsWith("gson-")) {
myImporter.markJarHandled(jar);
String version = getVersion(jar, "gson-", name, "2.3.1");
return GradleCoordinate.parseCoordinateString("com.google.code.gson:gson:" + version);
}
else if (name.startsWith("google-http-client-gson-")) {
myImporter.markJarHandled(jar);
return GradleCoordinate.parseCoordinateString("com.google.http-client:google-http-client-gson:1.20.0");
}
else if (name.startsWith("svg-android")) {
myImporter.markJarHandled(jar);
return GradleCoordinate.parseCoordinateString("com.github.japgolly.android:svg-android:2.0.6");
}
else if (name.equals("gcm.jar")) {
myImporter.markJarHandled(jar);
return GradleCoordinate.parseCoordinateString(PLAY_SERVICES_DEP);
}
// TODO: Consider other libraries if and when they get Gradle dependencies:
// analytics, volley, ...
return null;
}
private String getVersion(File jar, String prefix, String jarName, String defaultVersion) {
if (jarName.matches(prefix + "([\\d\\.]+)\\.jar")) {
String version = jarName.substring(prefix.length(), jarName.length() - 4);
if (!defaultVersion.equals(version)) {
myImporter.getSummary().reportGuessedVersion(jar);
}
return version;
}
return defaultVersion;
}
/**
* See if this is a library that looks like a known dependency; if so, just
* use a dependency instead of the library
*/
@SuppressWarnings("SpellCheckingInspection")
private void initReplaceWithDependency() {
if (isLibrary() && myImporter.isReplaceLibs()) {
String pkg = getPackage();
if (pkg != null) {
if (pkg.equals("com.actionbarsherlock")) {
myReplaceWithDependencies = Arrays.asList(GradleCoordinate.parseCoordinateString(SHERLOCK_DEP), getSupportLibDependency());
}
else if (pkg.equals("android.support.v7.gridlayout")) {
myReplaceWithDependencies = Collections.singletonList(getGridLayoutDependency());
}
else if (pkg.equals("com.google.android.gms")) {
myReplaceWithDependencies = Collections.singletonList(GradleCoordinate.parseCoordinateString(PLAY_SERVICES_DEP));
}
else if (pkg.equals("android.support.v7.appcompat")) {
myReplaceWithDependencies = Collections.singletonList(getAppCompatDependency());
}
else if (pkg.equals("android.support.v7.mediarouter")) {
myReplaceWithDependencies = Collections.singletonList(getMediaRouterDependency());
}
if (myReplaceWithDependencies != null) {
myImporter.getSummary().reportReplacedLib(getOriginalName(), myReplaceWithDependencies);
}
}
}
}
public boolean isReplacedWithDependency() {
return myReplaceWithDependencies != null && !myReplaceWithDependencies.isEmpty();
}
public List<GradleCoordinate> getReplaceWithDependencies() {
return myReplaceWithDependencies;
}
public String getModuleName() {
if (myModuleName == null) {
if (myImporter.isGradleNameStyle() && !myImporter.isImportIntoExisting() && myImporter.getModuleCount() == 1) {
myModuleName = "app";
return myModuleName;
}
String string = getOriginalName();
// Strip whitespace and characters which can pose a problem when the module
// name is referenced as a module name in Gradle (Groovy) files
StringBuilder sb = new StringBuilder(string.length());
for (int i = 0, n = string.length(); i < n; i++) {
char c = string.charAt(i);
if (Character.isJavaIdentifierPart(c)) {
sb.append(c);
}
}
String moduleName = sb.toString();
if (!moduleName.isEmpty() && !Character.isJavaIdentifierStart(moduleName.charAt(0))) {
moduleName = '_' + moduleName;
}
if (myImporter.isGradleNameStyle() && !moduleName.isEmpty()) {
moduleName = Character.toLowerCase(moduleName.charAt(0)) + moduleName.substring(1);
}
myModuleName = moduleName;
}
return myModuleName;
}
public void setModuleName(String name) {
myModuleName = name;
}
public void pickUniqueName(@NonNull File projectDir) {
assert projectDir.exists() : projectDir;
String preferredName = getModuleName();
// If the name ends with a number, strip that off and increment from it.
// In other words if the module name is "foo", we test "foo2", "foo3", and so on.
// But if the name already is "module49", we don't do "module492", ... we try "module50"
int length = preferredName.length();
int lastDigit = length;
for (int i = length - 1; i >= 1; i--) { // 1: name cannot start with a digit!
if (!Character.isDigit(preferredName.charAt(i))) {
break;
}
else {
lastDigit = i;
}
}
int startingNumber = 2;
if (lastDigit < length) {
startingNumber = Integer.parseInt(preferredName.substring(lastDigit)) + 1;
preferredName = preferredName.substring(0, lastDigit);
}
for (int i = startingNumber; ; i++) {
String name = preferredName + i;
if (!(new File(projectDir, name)).exists()) {
myModuleName = name;
break;
}
}
}
public String getModuleReference() {
String moduleName = getModuleName();
File file = new File(moduleName);
StringBuilder builder = new StringBuilder(moduleName.length() + 1);
while (file != null) {
builder.insert(0, file.getName());
builder.insert(0, ':');
file = file.getParentFile();
}
return builder.toString();
}
protected File getJarOutputRelativePath(File jar) {
if (jar.isAbsolute()) {
File relative;
try {
relative = GradleImport.computeRelativePath(getCanonicalModuleDir(), jar);
}
catch (IOException ioe) {
relative = null;
}
if (relative != null) {
jar = relative;
}
else {
jar = new File(LIBS_FOLDER, jar.getName());
}
}
return jar;
}
public void copyInto(@NonNull File destDir) throws IOException {
ImportSummary summary = myImporter.getSummary();
Set<File> copied = Sets.newHashSet();
final File main = new File(destDir, FD_SOURCES + separator + FD_MAIN);
myImporter.mkdirs(main);
if (isAndroidProject()) {
File srcManifest = getManifestFile();
if (srcManifest != null && srcManifest.exists()) {
File destManifest = new File(main, ANDROID_MANIFEST_XML);
myImporter.copyTextFile(this, srcManifest, destManifest);
summary.reportMoved(this, srcManifest, destManifest);
recordCopiedFile(copied, srcManifest);
}
File srcRes = getResourceDir();
if (srcRes != null && srcRes.exists()) {
File destRes = new File(main, FD_RES);
myImporter.mkdirs(destRes);
myImporter.copyDir(srcRes, destRes, new GradleImport.CopyHandler() {
@Override
public boolean handle(@NonNull File source, @NonNull File dest, boolean updateEncoding, @Nullable ImportModule sourceModule)
throws IOException {
// Resource files in non-value folders should use only lower case characters
if (hasUpperCaseExtension(dest) && !isIgnoredFile(source)) {
File parentFile = source.getParentFile();
if (parentFile != null) {
ResourceFolderType folderType = ResourceFolderType.getFolderType(parentFile.getName());
if (folderType != ResourceFolderType.VALUES) {
String name = dest.getName();
int dot = name.indexOf('.');
if (dot != -1) {
name = name.substring(0, dot) + name.substring(dot).toLowerCase(Locale.US);
File destParent = dest.getParentFile();
dest = destParent != null ? new File(destParent, name) : new File(name);
if (updateEncoding && isTextFile(source)) {
myImporter.copyTextFile(sourceModule, source, dest);
} else {
Files.copy(source, dest);
}
if (sourceModule != null) {
// Just use the names rather than the full paths to make it clear that this was just
// a file renaming (even though there is also a move happening for all resources including
// these. In other words, instead of displaying
// * res/drawable-hdpi/other_icon.PNG => app/src/main/res/drawable-hdpi/other_icon.png
// we display
// * other_icon.PNG => app/src/main/res/drawable-hdpi/other_icon.png
myImporter.getSummary().reportMoved(sourceModule, new File(source.getName()), new File(name));
}
return true;
}
}
}
}
return false;
}
}, true, this);
summary.reportMoved(this, srcRes, destRes);
recordCopiedFile(copied, srcRes);
}
File srcAssets = getAssetsDir();
if (srcAssets != null && srcAssets.exists()) {
File destAssets = new File(main, FD_ASSETS);
myImporter.mkdirs(destAssets);
myImporter.copyDir(srcAssets, destAssets, null, false, null);
summary.reportMoved(this, srcAssets, destAssets);
recordCopiedFile(copied, srcAssets);
}
File lintXml = getLintXml();
if (lintXml != null) {
File destLintXml = new File(destDir, lintXml.getName());
myImporter.copyTextFile(this, lintXml, destLintXml);
summary.reportMoved(this, lintXml, destLintXml);
recordCopiedFile(copied, lintXml);
}
}
for (final File src : getSourcePaths()) {
final File srcJava = resolveFile(src);
File destJava = new File(main, FD_JAVA);
if (srcJava.isDirectory()) {
// Merge all the separate source folders into a single one; they aren't allowed
// to contain source file conflicts anyway
myImporter.mkdirs(destJava);
}
else {
destJava = new File(main, srcJava.getName());
}
myImporter.copyDir(srcJava, destJava, new GradleImport.CopyHandler() {
// Handle moving .rs/.rsh/.fs files to main/rs/ and .aidl files to the
// corresponding aidl package under main/aidl
@Override
public boolean handle(@NonNull File source, @NonNull File dest, boolean updateEncoding, @Nullable ImportModule sourceModule)
throws IOException {
String sourcePath = source.getPath();
if (sourcePath.endsWith(DOT_AIDL)) {
File aidlDir = new File(main, FD_AIDL);
File relative = GradleImport.computeRelativePath(srcJava, source);
if (relative == null) {
relative = GradleImport.computeRelativePath(srcJava.getCanonicalFile(), source);
}
if (relative != null) {
File destAidl = new File(aidlDir, relative.getPath());
myImporter.mkdirs(destAidl.getParentFile());
myImporter.copyTextFile(ImportModule.this, source, destAidl);
myImporter.getSummary().reportMoved(ImportModule.this, source, destAidl);
return true;
}
}
else if (sourcePath.endsWith(DOT_RS) ||
sourcePath.endsWith(DOT_RSH) ||
sourcePath.endsWith(DOT_FS)) {
// Copy to flattened rs dir
// TODO: Ensure the file names are unique!
File destRs = new File(main, FD_RENDERSCRIPT + separator +
source.getName());
myImporter.mkdirs(destRs.getParentFile());
myImporter.copyTextFile(ImportModule.this, source, destRs);
myImporter.getSummary().reportMoved(ImportModule.this, source, destRs);
return true;
} else if (!sourcePath.endsWith(DOT_JAVA)
&& !sourcePath.endsWith(DOT_CLASS) // in case Eclipse built .class into source dir
&& !sourcePath.endsWith(DOT_JAR)
&& !sourcePath.equals("package.html") // leave docs with their code
&& !sourcePath.equals("overview.html")
&& source.isFile()) {
// Move resources over to the resource folder
File resourceDir = new File(main, FD_JAVA_RES);
File relative = GradleImport.computeRelativePath(srcJava, source);
if (relative == null) {
relative = GradleImport.computeRelativePath(srcJava.getCanonicalFile(), source);
}
if (relative != null) {
File destResource = new File(resourceDir, relative.getPath());
myImporter.mkdirs(destResource.getParentFile());
Files.copy(source, destResource);
myImporter.getSummary().reportMoved(ImportModule.this, source, destResource);
return true;
}
}
return false;
}
}, true, this);
summary.reportMoved(this, srcJava, destJava);
recordCopiedFile(copied, srcJava);
}
for (File jar : getJarPaths()) {
File srcJar = resolveFile(jar);
File destJar = new File(destDir, getJarOutputRelativePath(jar).getPath());
if (destJar.getParentFile() != null) {
myImporter.mkdirs(destJar.getParentFile());
}
Files.copy(srcJar, destJar);
summary.reportMoved(this, srcJar, destJar);
recordCopiedFile(copied, srcJar);
}
for (File lib : getNativeLibs()) {
File srcLib = resolveFile(lib);
String abi = lib.getParentFile().getName();
File destLib =
new File(destDir, FD_SOURCES + separator + FD_MAIN + separator + "jniLibs" + separator + abi + separator + lib.getName());
if (destLib.getParentFile() != null) {
myImporter.mkdirs(destLib.getParentFile());
}
Files.copy(srcLib, destLib);
summary.reportMoved(this, srcLib, destLib);
recordCopiedFile(copied, srcLib);
}
File jni = getNativeSources();
if (jni != null) {
File srcJni = resolveFile(jni);
File destJni = new File(destDir, FD_SOURCES + separator + FD_MAIN + separator + "jni");
myImporter.copyDir(srcJni, destJni, null, true, this);
summary.reportMoved(this, srcJni, destJni);
recordCopiedFile(copied, srcJni);
}
File instrumentation = getInstrumentationDir();
if (instrumentation != null) {
final File test = new File(destDir, FD_SOURCES + separator + FD_TEST);
myImporter.mkdirs(test);
// We should NOT copy the Android manifest file. Don't mark it as "ignored"
// either since we'll pull everything we need out of it and put it into the
// Gradle file.
recordCopiedFile(copied, new File(instrumentation, ANDROID_MANIFEST_XML));
File srcRes = new File(instrumentation, FD_RES);
if (srcRes.isDirectory()) {
File destRes = new File(test, FD_RES);
myImporter.mkdirs(destRes);
myImporter.copyDir(srcRes, destRes, null, true, this);
summary.reportMoved(this, srcRes, destRes);
recordCopiedFile(copied, srcRes);
}
File srcJava = new File(instrumentation, FD_SOURCES);
if (srcJava.isDirectory()) {
File destRes = new File(test, FD_JAVA);
myImporter.mkdirs(destRes);
myImporter.copyDir(srcJava, destRes, null, true, this);
summary.reportMoved(this, srcJava, destRes);
recordCopiedFile(copied, srcJava);
}
for (File jar : getTestJarPaths()) {
File srcJar = resolveFile(jar);
File destJar = new File(destDir, getTestJarOutputRelativePath(jar).getPath());
if (destJar.exists()) {
continue;
}
if (destJar.getParentFile() != null) {
myImporter.mkdirs(destJar.getParentFile());
}
Files.copy(srcJar, destJar);
summary.reportMoved(this, srcJar, destJar);
recordCopiedFile(copied, srcJar);
}
}
if (isAndroidProject()) {
for (File srcProguard : getLocalProguardFiles()) {
File destProguard = new File(destDir, srcProguard.getName());
if (!destProguard.exists()) {
myImporter.copyTextFile(this, srcProguard, destProguard);
summary.reportMoved(this, srcProguard, destProguard);
recordCopiedFile(copied, srcProguard);
}
else {
myImporter.reportWarning(this, destProguard, "Local proguard config file name is not unique");
}
}
}
reportIgnored(copied);
}
private static boolean hasUpperCaseExtension(@NonNull File file) {
String path = file.getPath();
int index = path.lastIndexOf(separatorChar);
// Can be -1 (if this is just a file name, with no path); the below still works since we start from the next char (0)
index = path.indexOf('.', index + 1);
if (index == -1) {
return false; // no extension in the file name
}
index++;
for (; index < path.length(); index++) {
if (Character.isUpperCase(path.charAt(index))) {
return true;
}
}
return false;
}
private void reportIgnored(Set<File> copied) throws IOException {
File canonicalDir = getCanonicalModuleDir();
// Ignore output folder (if not under bin/ as usual)
File outputDir = getOutputDir();
if (outputDir != null) {
copied.add(resolveFile(outputDir).getCanonicalFile());
}
// These files are either not useful (bin, gen) or already handled (project metadata files)
copied.add(new File(canonicalDir, BIN_FOLDER));
copied.add(new File(canonicalDir, GEN_FOLDER));
copied.add(new File(canonicalDir, ECLIPSE_DOT_CLASSPATH));
copied.add(new File(canonicalDir, ECLIPSE_DOT_PROJECT));
copied.add(new File(canonicalDir, FN_PROJECT_PROPERTIES));
copied.add(new File(canonicalDir, FN_PROJECT_PROPERTIES));
copied.add(new File(canonicalDir, FN_LOCAL_PROPERTIES));
copied.add(new File(canonicalDir, LIBS_FOLDER));
copied.add(new File(canonicalDir, ".settings"));
copied.add(new File(canonicalDir, ".cproject"));
if (isNdkProject()) {
// TODO: Also resolve NDK_OUT in the Makefile in case a custom location is used
copied.add(new File(canonicalDir, "obj"));
}
reportIgnored(canonicalDir, copied, 0);
}
/**
* Report ignored files. Returns true if the file (and all its children) were
* ignored too.
*/
private boolean reportIgnored(@NonNull File file, @NonNull Set<File> copied, int depth) throws IOException {
if (depth > 0 && copied.contains(file)) {
return true;
}
boolean ignore = true;
boolean isDirectory = file.isDirectory();
if (isDirectory) {
// Don't recursively list contents of .git etc
if (depth == 1) {
if (GradleImport.isIgnoredFile(file)) {
return false;
}
}
File[] files = file.listFiles();
if (files != null) {
for (File child : files) {
ignore &= reportIgnored(child, copied, depth + 1);
}
}
}
else {
ignore = false;
}
if (depth > 0 && !ignore) {
File relative = GradleImport.computeRelativePath(getCanonicalModuleDir(), file);
if (relative == null) {
relative = file;
}
String path = relative.getPath();
if (isDirectory) {
path += separator;
}
myImporter.getSummary().reportIgnored(getOriginalName(), path);
}
return ignore;
}
public List<File> getJarDependencies() {
return myJarDependencies;
}
public List<GradleCoordinate> getDependencies() {
return myDependencies;
}
public List<File> getTestJarDependencies() {
return myTestJarDependencies;
}
public List<GradleCoordinate> getTestDependencies() {
return myTestDependencies;
}
@Nullable
public File computeProjectRelativePath(@NonNull File file) throws IOException {
return GradleImport.computeRelativePath(getCanonicalModuleDir(), file);
}
protected abstract boolean dependsOn(@NonNull ImportModule other);
protected abstract boolean dependsOnLibrary(@NonNull String pkg);
/**
* Strip out .jar file dependencies (on files in libs/) that correspond
* to code pulled in from a library dependency
*/
void removeJarDependencies() {
// For each module, remove any .jar files in its path that
// provided b
ListIterator<File> iterator = getJarPaths().listIterator();
while (iterator.hasNext()) {
File jar = iterator.next();
if (myImporter.isJarHandled(jar)) {
iterator.remove();
}
else {
String pkg = jar.getName();
if (pkg.endsWith(DOT_JAR)) {
pkg = pkg.substring(0, pkg.length() - DOT_JAR.length());
}
pkg = pkg.replace('-', '.');
if (dependsOnLibrary(pkg)) {
iterator.remove();
}
}
}
}
// Sort by dependency order
@Override
public int compareTo(@NonNull ImportModule other) {
if (dependsOn(other)) {
return 1;
}
else if (other.dependsOn(this)) {
return -1;
}
else {
return getOriginalName().compareTo(other.getOriginalName());
}
}
}