android - 选择 Android 库的构建变体的 Gradle 脚本

标签 android eclipse gradle

我正在使用 Eclipse 并处理 Android 项目。由于多种原因,我无法使用 Android Studio。遗憾的是,Eclipse 无法处理 AAR 文件。经过长时间的斗争,我们决定让 Eclipse 项目成为 NON-GRADLE(从所有 Eclipse 项目中消除 gradle 性质)并准备一个带有特殊 gradle 脚本的依赖项列表。 为了能够与 Android 一起工作,我编写了以下 gradle 脚本,它执行以下操作:

  1. 搜索所有项目中的所有依赖项
  2. 将依赖项从 gradle 缓存复制到一个特殊的 Eclipse 项目“jars-from-gradle”

  3. 为所有项目编写“.classpath”以仅使用找到的库

这是 gradle 脚本:

apply plugin: "eclipse"

configurations {
    eclipseOnly {
        description = 'this is used only to build eclipse classpath'
    }
}

afterEvaluate{

  project.tasks['eclipseProject'].dependsOn(project.tasks['cleanEclipseProject'])
  project.tasks['eclipseClasspath'].dependsOn(project.tasks['cleanEclipseClasspath'])

  eclipse{

    File f = rootProject.ext.find("explodedAarsDir");
    if(f == null) {
        f = new File("${rootProject.projectDir}/jars-from-gradle/explodedAars/");
        rootProject.ext.set("explodedAarsDir", f)
        f.mkdirs();
    }
    f = rootProject.ext.find("globalDependenciesRepo");
    if(f == null) {
        f = new File("${rootProject.projectDir}/jars-from-gradle/libs");
        rootProject.ext.set("globalDependenciesRepo", f)
        f.mkdirs();
    }
    org.gradle.plugins.ide.eclipse.model.internal.FileReferenceFactory frf = rootProject.ext.find("fileReferenceFactory");
    if(frf == null) {
        frf = new org.gradle.plugins.ide.eclipse.model.internal.FileReferenceFactory();
        rootProject.ext.set("fileReferenceFactory", frf)
    }

    if(!rootProject.ext.has("eclipseFileMapping")) {
        rootProject.ext.set("eclipseFileMapping", new HashMap<File, File>())
    }
    Map<File, File> eclipseFileMapping = rootProject.ext.get("eclipseFileMapping")
    eclipseFileMapping.put(new File("bin/main"), new File("bin"))
    eclipseFileMapping.put(new File("bin/test"), new File("bin"))
    eclipseFileMapping.put(new File("${buildDir}/classes/java/main"), new File("${projectDir.parentFile}/${project.name}/bin"))
    eclipseFileMapping.put(new File("${buildDir}/classes/java/test"), new File("${projectDir.parentFile}/${project.name}/bin"))
    eclipseFileMapping.put(new File("${buildDir}/resources/main"), new File("${projectDir.parentFile}/${project.name}/bin"))
    eclipseFileMapping.put(new File("${buildDir}/resources/test"), new File("${projectDir.parentFile}/${project.name}/bin"))

    configurations {
      eclipsePlusConfig {
        description = "files to include into eclipse classpath"
        transitive = false
      }
      eclipseMinusConfig {
        description = "files to exclude from eclipse classpath"
        transitive = false
      }
    }

    project{
      setupEclipseProject()
    }

    classpath{
      defaultOutputDir = new File("${projectDir}/bin")
      plusConfigurations += [project.configurations.eclipseOnly]
      if(project.extensions.findByName("android") != null) {
          plusConfigurations += [project.configurations.compile]
          plusConfigurations += [project.configurations.runtime]
          project.eclipse.project{
              natures 'org.eclipse.andmore.AndroidNature'
              buildCommands.clear()
              buildCommand "org.eclipse.andmore.ResourceManagerBuilder"
              buildCommand "org.eclipse.andmore.PreCompilerBuilder"
              buildCommand "org.eclipse.jdt.core.javabuilder"
              buildCommand "org.eclipse.andmore.ApkBuilder"
              containers 'org.eclipse.andmore.DEPENDENCIES', 'org.eclipse.andmore.LIBRARIES', 'org.eclipse.andmore.ANDROID_FRAMEWORK'
          }
      } else {
          plusConfigurations += [project.configurations.compile]
          plusConfigurations += [project.configurations.runtime]
      }

      file {
        beforeMerged { classpath ->
           eclipse.classpath.sourceSets.each {
               println "    source set "+ it.getName()
           }
           eclipse.classpath.plusConfigurations.each{ processConf(it, "    ", "plus conf: ") }
           eclipse.classpath.minusConfigurations.each{ processConf(it, "    ", "minus conf: ") }

           eclipse.classpath.plusConfigurations.add(project.configurations['eclipsePlusConfig'])
           eclipse.classpath.minusConfigurations.add(project.configurations['eclipseMinusConfig'])
        }
        whenMerged { classpath ->
           List<org.gradle.plugins.ide.eclipse.model.ClasspathEntry> replacementEclipseClasspath = createEclipseReplacementClasspath(classpath);
           classpath.setEntries(replacementEclipseClasspath)
        }
        withXml { n ->
            Set<File> existingPaths = new HashSet<File>();
            def rootNode = n.asNode();
            for(int nodeIndex = 0; nodeIndex<rootNode.children().size(); nodeIndex++) {
               def chld = rootNode.children().get(nodeIndex);
               if("classpathentry".equals(chld.name())) {
                  removeGradleAttributes(chld);
                  chld.attributes().remove("output");
                  String kind = chld.attributes().get("kind");
                  for(Map.Entry entry : chld.attributes().entrySet()) {
                      if("path".equals(entry.key) || "sourcepath".equals(entry.key)) {
                        f = new File(entry.value);
                        if(f.toPath().isAbsolute()) {
                            String relativeName = rootProject.projectDir.toPath().relativize(f.toPath()).toString();
                            entry.value = "/"+ relativeName.replace('\\', '/');
                        }
                        if("path".equals(entry.key) && existingPaths.contains(f)) {
                            rootNode.children().remove(nodeIndex--);
                            break;
                        }
                        if(entry.value.startsWith("/")) {
                            if("src".equals(kind)) {
                                chld.attributes().put("combineaccessrules", "false");
                            }
                        }
                        existingPaths.add(f);
                      }
                  }
                  if("lib".equals(kind)) {
                      chld.attributes().put("exported", "true");
                  }
               }
            }
        }
      }
    }

    task prepareEclipse{
      doFirst{
        mkDirIfNotExists(new File("${projectDir}/src/main/java"))
        mkDirIfNotExists(new File("${projectDir}/src/main/resources"))
        mkDirIfNotExists(new File("${projectDir}/src/test/java"))
        mkDirIfNotExists(new File("${projectDir}/src/test/resources"))
      }
    }

    tasks['eclipseClasspath'].dependsOn(prepareEclipse)
  }
}

List<org.gradle.plugins.ide.eclipse.model.ClasspathEntry> createEclipseReplacementClasspath(org.gradle.plugins.ide.eclipse.model.Classpath eclipseClasspath) {
    Map<String, org.gradle.plugins.ide.eclipse.model.ClasspathEntry> replacementEclipseClasspathAsMap = new HashMap<String, org.gradle.plugins.ide.eclipse.model.ClasspathEntry>(); 
    eclipseClasspath.entries.each{ clspthentry ->
        dumpClassPathEntry(clspthentry)
        if (clspthentry instanceof org.gradle.plugins.ide.eclipse.model.Library) {
            org.gradle.plugins.ide.eclipse.model.Library library = clspthentry;
            String moduleId = library.getModuleVersion().toString();
            String groupId = null;
            String artifactId = null;
            String artifactVersion = null;
            int index = moduleId.indexOf(":");
            if(index >= 0) {
                groupId = moduleId.substring(0, index);
                String tmp = moduleId.substring(++index);
                index = tmp.indexOf(":")
                if(index >= 0) {
                    artifactId = tmp.substring(0, index);
                    artifactVersion = tmp.substring(++index);
                }
            }
            moduleId = moduleId.replaceAll(":", "-");
            println("    classpath entry found: moduleId="+ moduleId);
            if (library.getPath().endsWith(".aar")) {
                explodeAarJarFiles(moduleId, groupId, artifactId, artifactVersion, library, replacementEclipseClasspathAsMap);
            } else {
                copyLibraryFromGradleCache(moduleId, groupId, artifactId, artifactVersion, library, replacementEclipseClasspathAsMap)
            }
        } else {
            replacementEclipseClasspathAsMap.put(clspthentry.kind+ "_"+ clspthentry.path, clspthentry);
        }
    }
    List<org.gradle.plugins.ide.eclipse.model.ClasspathEntry> replacementEclipseClasspath = new ArrayList<org.gradle.plugins.ide.eclipse.model.ClasspathEntry>();
    replacementEclipseClasspath.addAll(replacementEclipseClasspathAsMap.values());
    List<String> KINDS = new ArrayList<String>();
    KINDS.add('src');
    KINDS.add('con');
    KINDS.add('lib');
    KINDS.add('output');
    Collections.sort(replacementEclipseClasspath, new Comparator<org.gradle.plugins.ide.eclipse.model.ClasspathEntry>() {

        private int detectKindIndex(String entryKind) {
            for(int i = 0; i<KINDS.size(); i++) {
                if(KINDS[i].equals(entryKind)) {
                    return i;
                }
            }
            return KINDS.size();
        }

        public int compare(org.gradle.plugins.ide.eclipse.model.ClasspathEntry entry1, org.gradle.plugins.ide.eclipse.model.ClasspathEntry entry2) {
            int kindDiff = detectKindIndex(entry1.getKind()) - detectKindIndex(entry2.getKind());
            if(kindDiff != 0) {
                return kindDiff;
            }
            if(entry1 instanceof org.gradle.plugins.ide.eclipse.model.ProjectDependency) {
                if(!(entry2 instanceof org.gradle.plugins.ide.eclipse.model.ProjectDependency)) {
                    return 11;
                }
            } else if(entry2 instanceof org.gradle.plugins.ide.eclipse.model.ProjectDependency) {
                return -1;
            }
            return entry1.path.compareTo(entry2.path);
        }
    });
    return replacementEclipseClasspath;
}

void copyLibraryFromGradleCache(String moduleId, String groupId, String artifactId, String artifactVersion, org.gradle.plugins.ide.eclipse.model.Library library, Map<String, org.gradle.plugins.ide.eclipse.model.Library> replacementEclipseClasspathAsMap) {
    String artifactIdAndVersion = artifactId + "-"+ artifactVersion;
    int fileSuffixIndex = -1;
    if(artifactId != null) {
        fileSuffixIndex = library.getPath().lastIndexOf(artifactIdAndVersion);
    }
    if(fileSuffixIndex >= 0) {
        fileSuffixIndex += artifactIdAndVersion.length();
    } else {
        fileSuffixIndex = library.getPath().lastIndexOf(".");
    }
    if(moduleId == null || fileSuffixIndex <= 0) {
        println("      non-movable library found: "+ library.getPath())
        replacementEclipseClasspathAsMap.put(moduleId, library);
    } else {
        File targetGroupFolder = null; 
        if (groupId==null || groupId.trim().length()==0) {
            targetGroupFolder = new File(globalDependenciesRepo.getAbsolutePath());
        } else {
            targetGroupFolder = new File(globalDependenciesRepo.getAbsolutePath(), groupId);
            if(!targetGroupFolder.exists()){
                targetGroupFolder.mkdirs()
            }
        }
        String fileSuffix = library.getPath().substring(fileSuffixIndex);
        String targetFileName = artifactIdAndVersion;
        println("      target filename: "+ targetGroupFolder+ " -> "+ targetFileName)
        java.nio.file.Path targetFile = java.nio.file.Paths.get(targetGroupFolder.getAbsolutePath(), targetFileName + fileSuffix);
        java.nio.file.Path sourceFile = java.nio.file.Paths.get(library.getPath());
        if(sourceFile.toFile().exists() && !sourceFile.toFile().isDirectory()) {
            java.nio.file.Files.copy(sourceFile, targetFile, java.nio.file.StandardCopyOption.REPLACE_EXISTING, java.nio.file.StandardCopyOption.COPY_ATTRIBUTES);
            library.setPath(targetFile.toString());
        }
        if(library.getSourcePath() != null) {
            java.nio.file.Path sourceSourceFile = java.nio.file.Paths.get(library.getSourcePath().getPath());
            if(sourceFile.toFile().exists() && !sourceFile.toFile().isDirectory()) {
                java.nio.file.Path sourceTargetFile = java.nio.file.Paths.get(targetGroupFolder.getAbsolutePath(), targetFileName + "_source"+ fileSuffix);
                println("      copying source file: "+ sourceSourceFile + " into "+ sourceTargetFile);
                java.nio.file.Files.copy(sourceSourceFile, sourceTargetFile, java.nio.file.StandardCopyOption.REPLACE_EXISTING, java.nio.file.StandardCopyOption.COPY_ATTRIBUTES);
                //println( "          TROLOLO "+ rootProject.projectDir.toPath().relativize(sourceTargetFile) );
                library.setSourcePath(fileReferenceFactory.fromFile(sourceTargetFile.toFile()));
            }
        }
        replacementEclipseClasspathAsMap.put(moduleId + "_"+ targetFileName + fileSuffix, library);
    }
}

void explodeAarJarFiles(String moduleId, String groupId, String artifactId, String artifactVersion, org.gradle.plugins.ide.eclipse.model.Library aarLibrary, Map<String, org.gradle.plugins.ide.eclipse.model.Library> replacementEclipseClasspathAsMap) {
    File aarFile = new File(aarLibrary.getPath());
    println("    exploding AAR dependency: "+ aarFile.getAbsolutePath());
    File targetFolder = new File(explodedAarsDir, moduleId);
    println("      target folder: "+ targetFolder.getAbsolutePath());
    if (targetFolder.exists()) {
        println("        target folder exists. deleting ");
        project.delete(files(targetFolder))
    }
    if (!targetFolder.mkdirs()) {
        throw new RuntimeException("Cannot create folder: ${targetFolder.getAbsolutePath()}");
    }
    try {
        if(aarLibrary.getSourcePath() != null) {
            java.nio.file.Path sourceSourceFile = java.nio.file.Paths.get(aarLibrary.getSourcePath().getPath());
            if(sourceSourceFile.toFile().exists() && !sourceSourceFile.toFile().isDirectory()) {
                String sourceFileExt = sourceSourceFile.toString();
                int extensionIndex = sourceFileExt.lastIndexOf(".");
                if(extensionIndex >= 0) {
                    sourceFileExt = sourceFileExt.substring(extensionIndex);
                } else {
                    sourceFileExt = ".jar";
                }
                java.nio.file.Path sourceTargetFile = java.nio.file.Paths.get(targetFolder.toString(), moduleId+ "_source"+ sourceFileExt);
                println("      copying source file: "+ sourceSourceFile + " into "+ sourceTargetFile);
                java.nio.file.Files.copy(sourceSourceFile, sourceTargetFile, java.nio.file.StandardCopyOption.REPLACE_EXISTING, java.nio.file.StandardCopyOption.COPY_ATTRIBUTES);
                aarLibrary.setSourcePath(fileReferenceFactory.fromFile(sourceTargetFile.toFile()));
            }
        }
        java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(aarFile);
        zipFile.entries().each{ fileInsideAar -> 
            if (fileInsideAar.getName().endsWith(".jar")) {
                String targetName = moduleId+ "_"+ fileInsideAar.getName().replace('/', '_').replace('\\', '_');
                println("          jar inside aar: "+ fileInsideAar.getName());
                println("          copying to: "+ targetName);
                File targetFile = new File(targetFolder, targetName);
                int index = 1;
                while (targetFile.exists()) {
                    targetFile = new File(targetFolder, format("${targetName}_${++index}"));
                }
                try {
                    InputStream inputStream = zipFile.getInputStream(fileInsideAar);
                    java.nio.file.Files.copy(inputStream, targetFile.toPath());
                    org.gradle.plugins.ide.eclipse.model.Library library = new org.gradle.plugins.ide.eclipse.model.Library(fileReferenceFactory.fromFile(targetFile));
                    library.setSourcePath(aarLibrary.getSourcePath())
                    replacementEclipseClasspathAsMap.put(targetFile.getName(), library);
                } catch (IOException e) {
                    throw new RuntimeException(
                            "Cannot write entry to file: ${e.getMessage()}: ${targetFile.getAbsolutePath()}", e);
                }
            }
        };
    } catch (IOException e) {
        throw new RuntimeException(
               "Cannot explode aar: ${e.getMessage()}: ${aarFile.getAbsolutePath()}", e);
    }
}

void removeGradleAttributes(Node node) {
    for(int i = 0; i<node.children().size(); i++) {
        Node attrs = node.children().get(i);
        if("attributes".equals(attrs.name())) {
            for(int j = 0; j<attrs.children().size(); j++) {
                Node attr = attrs.children().get(j);
                boolean isGradleAttr = false;
                for(Map.Entry entry : attr.attributes().entrySet()) {
                    if(entry.key.toLowerCase().contains("gradle") || entry.value.toLowerCase().contains("gradle")) {
                        isGradleAttr = true;
                    }
                }
                if(isGradleAttr) {
                    attrs.remove(attr);
                    j--;
                }
            }
            if(attrs.children().size()==0) {
                node.remove(attrs);
            }
        }
    }
}

void mkDirIfNotExists(File file) {
    if(!file.exists()) {
        file.mkdir()
    }
}

void processConf(org.gradle.api.internal.artifacts.configurations.DefaultConfiguration cnf, String startIndent, String prefix) {
    println(startIndent + prefix + cnf.name+ ", path: "+ cnf.path)
    StringBuilder indent = new StringBuilder();
    for(int i = 0; i<startIndent.length(); i++) {
        indent.append(" ");
    }
    indent.append("  ");

    cnf.dependencies.each{ dep ->
        maskDependencyIfNeeded(indent.toString(), dep)
    }
    cnf.allDependencies.each{ dep ->
        maskDependencyIfNeeded(indent.toString(), dep)
    }
}

void maskDependencyIfNeeded(String indent, org.gradle.api.internal.artifacts.dependencies.AbstractDependency dep) {
    if(dep instanceof org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency) {
        boolean needToExcludeDep = false;
        Set<File> maskedDepFiles = new HashSet<File>();
        dep.files.each{ depFile ->
            File f = findMaskedFile(depFile, null, true);
            if(f != null) {
              maskedDepFiles.add(f)
              needToExcludeDep = true;
              println(indent.toString()+ "  mask dep file "+ depFile+ " -> "+ f)
            }
        }
        if(needToExcludeDep) {
            project.configurations['eclipseMinusConfig'].dependencies.add(dep)
            if(!maskedDepFiles.isEmpty()) {
                org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency newDep = new org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency(dep.targetComponentId, project.files(maskedDepFiles))
                project.configurations['eclipsePlusConfig'].dependencies.add(newDep)
            }
        }
    }
}

File findMaskedFile(File f, String postfix, boolean initiallyUnmasked) {
  if(f != null) {
      for(Map.Entry<File, File> efm : eclipseFileMapping) {
           boolean masked = false;
           if(initiallyUnmasked) {
               if(efm.key.equals(f)) {
                   masked = true;
               }
           } else {
               if(efm.value.equals(f)) {
                   masked = true;
               }
           }
           if(masked) {
               if(postfix != null) {
                   return new File(efm.value, postfix)
               } else {
                   return efm.value;
               }
           }
      }
      return findMaskedFile(f.parentFile, postfix==null ? f.name : f.name + File.pathSeparator+ postfix, initiallyUnmasked);
  }
  return null;
}

void dumpClassPathEntry(org.gradle.plugins.ide.eclipse.model.ClasspathEntry clspthentry) {
    if("output".equals(clspthentry.kind)) {
        // the clspthentry is instance of org.gradle.plugins.ide.eclipse.model.Output
        println("  output: "+ clspthentry.path)
    } else if("src".equals(clspthentry.kind)) {
        if(clspthentry instanceof org.gradle.plugins.ide.eclipse.model.ProjectDependency) {
             // the clspthentry is instance of org.gradle.plugins.ide.eclipse.model.ProjectDependency
             println("  project: exported="+ clspthentry.exported+ "; path="+ clspthentry.path)
        } else {
             // the clspthentry is instance of org.gradle.plugins.ide.eclipse.model.SourceFolder
             println("  src folder: "+ clspthentry.name+ " ("+ clspthentry.dir+ ") -> output: "+ clspthentry.output)
             if(clspthentry.excludes != null && clspthentry.excludes.size()>0) {
                 println("    excludes:")
                 clspthentry.excludes.each{ excl ->
                     println("      "+ excl)
                 }
             }
             if(clspthentry.includes != null && clspthentry.includes.size()>0) {
                 println("    includes:")
                 clspthentry.includes.each{ incl ->
                     println("      "+ incl)
                 }
             }
         }
    } else if("con".equals(clspthentry.kind)) {
        //the clspthentry is instance of org.gradle.plugins.ide.eclipse.model.Container
        println("  con: exported="+ clspthentry.exported+ "; path="+ clspthentry.path)
    } else if("lib".equals(clspthentry.kind)) {
        //the clspthentry is instance of org.gradle.plugins.ide.eclipse.model.Library
        println("  lib: file="+ clspthentry.library.path)
    } else {
        println("  UNKNOWN "+ clspthentry.kind+ " -> "+ clspthentry.getClass().getName())
    }
}

// Gradle adds all custom sourceSets to eclipse's linkedResources. We do not need them in eclipse project, but we do not understand how and when the source is linked.
// So, JUST HACK IT: clear the linked resourcces after evaluating the project!
// But gradle is such a misterious thing! just clearing does not help. We need to put something there
// so lets put the existing linked resource, but with relative path :(
void setupEclipseProject() {
  if(project.name.contains("-android")) {
    project.eclipse.project{

        linkedResource name: 'AndroidManifest.xml', type: '1', location: 'PROJECT_LOC/src/main/AndroidManifest.xml'
        linkedResource name: 'android-java', type: '2', location: 'PARENT-1-PROJECT_LOC/assets/build/android/java'
        linkedResource name: 'res', type: '2', location: 'PROJECT_LOC/src/main/res'
    }
  }
}

我知道这不是 gradle 编程的顶峰,但它确实有效。 此脚本的主要问题是它要求所有依赖项都属于“编译”类型,但对于 Android,这是过时的。新的依赖类型是“api”和“implementation”。 现在 'compile' 刚刚被弃用,但恐怕它会完全消失。

有一个简单而丑陋的解决方案:使用“eclipseOnly”类型的依赖项复制所有非标准依赖项。上面的脚本中使用了此解决方案。这行得通,但真的很难看,因为我们必须修改所有项目中的 gradle 脚本。但我的目标不是触及所有项目。现在脚本包含在单独的文件“eclipseHelper.gradle”中,并包含在根项目中,如下所示:

subprojects{
   apply from: "${rootProject.projectDir}/eclipseHelper.gradle"
}

我想要实现的是将 android 的特定类型的依赖项添加到 Eclipse 中。首先,我从依赖项中排除了所有“发布”变体:

def androidExtension = project.extensions.findByName("android")
  if (androidExtension != null) {
      android.variantFilter { variant ->
          def names = variant.flavors*.name
          def buildTypeName = variant.buildType.name
          // if buildtype is required for filtering use
          // the above field
          if (variant.name.contains("elease")) {
              variant.ignore = true
          }
      }
   }

其次,我尝试将 Android 变体配置添加到 Eclipse 类路径中('plusConfigurations'):

def androidExtension = project.extensions.findByName("android")
    if (androidExtension != null) {
      boolean applicationBuild = rootProject.hasProperty("applicationBuild")
      if (androidExtension.getClass().getName().contains("LibraryExtension")){
          android.libraryVariants.all { variant ->
              eclipse.classpath.plusConfigurations += variant.compileConfiguration
              eclipse.classpath.plusConfigurations += variant.runtimeConfiguration
          }
      } else {
          android.applicationVariants.all { variant ->
              eclipse.classpath.plusConfigurations += variant.compileConfiguration
              eclipse.classpath.plusConfigurations += variant.runtimeConfiguration
          }
      }
    }

我们的项目中有一些 Android 库,这些库是用不同的风格构建的。所以我得到了以下异常:

org.gradle.internal.component.AmbiguousVariantSelectionException: More than one variant of project :proj1-android matches the consumer a
ttributes:
  - Configuration ':proj1-android:debugApiElements' variant android-aidl:
      - Found artifactType 'android-aidl' but wasn't required.
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found compatible value 'debug'.
      - Found com.android.build.api.attributes.VariantAttr 'debug' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Aar' and found compatible value 'Aar'.
      - Required org.gradle.usage 'java-api' and found compatible value 'java-api'.
  - ...
  - ...
  - Configuration ':proj1-android:debugApiElements' variant jar:
      - Found artifactType 'jar' but wasn't required.
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found compatible value 'debug'.
      - Found com.android.build.api.attributes.VariantAttr 'debug' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Aar' and found compatible value 'Aar'.
      - Required org.gradle.usage 'java-api' and found compatible value 'java-api'.

有人可以帮助我制定任何解决方案吗?

  1. 从集合中选择任何配置(“调试”)
  2. 完全跳过子项目的所有配置,将其作为项目依赖包含到.classpath中

提前致谢, 安德烈

最佳答案

我也遇到了同样的问题。 找到了几个解决方案,但它们不是最终的。

https://github.com/greensopinion/gradle-android-eclipse

https://github.com/GinVavilon/gradle-android-eclipse

Google 已经在 android.tools.build 3.4 中实现了数据绑定(bind)和 androidx,我正在尝试根据这些条款自定义我的项目。

关于android - 选择 Android 库的构建变体的 Gradle 脚本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54868172/

相关文章:

java - Android 帐户管理器在设置中删除选项菜单

android - 错误:无法下载ecj.jar(org.eclipse.jdt.core.compiler:ecj:4.4):无缓存版本可用于离线模式

android - 使用 ACTION_GET_CONTENT 在 Android on 2.1 上选取图片和视频

android - 高 Assets 加载时间

java - Eclipse 3.5+ - 注释处理器 : Generated classes cannot be imported

java - 从对话框中选择项目后如何告诉应用程序做什么?

intellij-idea - 我可以让 Gradle 的 Idea 插件解析为 'newest' 版本的依赖项吗?

unit-testing - Gradle jacoco 代码覆盖率 - 然后在 Jenkins 中发布/显示

android - 添加两个用户来解析对象和查询

java - Eclipse 帮助,导入无法识别