Gradle Transform 输出路径解析

目录

1、transforms路径下首先是各个Transform Task的名字所代表的路径

2、${productFlavor}/${buildType}

3、release后面是 jars 或 folders

1)自定的 Transform Task (StripAarTransform)

2)对于 Proguard Transform Task 来说

3)  对于dex Transform Task 来说

4、folders|jars 后面是 ${OutputTypes}

1)自定的 Transform Task (StripAarTransform)

2)对于 Proguard Transform Task 来说

3)对于dex Transform Task 来说

5、OutputTypes 后面是 ${Scopes}

1)自定的 Transform Task (StripAarTransform)

2)对于 Proguard Transform Task 来说

3)  对于dex Transform Task 来说


无意中注意到,Transform task 的输出路径,好奇,这么奇怪的路径是怎么生成的?

比如:

Proguard (task 为:transformClassesAndResourcesWithProguardForDebug、transformClassesAndResourcesWithProguardForRelease)的输出路径:

build/intermediates/transforms/proguard/debug/jars/3/1f/main.jar

build/intermediates/transforms/proguard/release/jars/3/1f/main.jar

Gradle Transform 输出路径解析_第1张图片

 

先给个结论吧:

主要是由 TransformOutputProvider#getContentLocation(name, types, scopes, format) 中的参数决定的!

 

源码定义在:com.android.build.api.transform.TransformOutputProvider.java:

/**
 * The output of a transform.
 * 

* There is no direct access to a location to write. Instead, Transforms can ask to get the * location for given scopes, content-types and a format. */ public interface TransformOutputProvider { /** * Delete all content. This is useful when running in non-incremental mode * @throws IOException */ void deleteAll() throws IOException; /** * Returns the location of content for a given set of Scopes, Content Types, and Format. *

* If the format is {@link Format#DIRECTORY} then the result is the file location of the * directory.
* If the format is {@link Format#JAR} then the result is a file representing the jar to create. *

* Non of the directories or files are created by querying this method, and there is * no checks regarding the existence of content in this location. *

* In case of incremental processing of removed files, it is safe to query the method to get * the location of the files to removed. * * @param name a unique name for the content. For a given set of scopes/types/format it must * be unique. * @param types the content types associated with this content. * @param scopes the scopes associated with this content. * @param format the format of the content. * @return the location of the content. */ @NonNull File getContentLocation(@NonNull String name, @NonNull Set types, @NonNull Set scopes, @NonNull Format format); }

 

 

 

1、transforms路径下首先是各个Transform Task的名字所代表的路径

如:

transforms/dex

transforms/proguard

 

 

 

2、${productFlavor}/${buildType}

proguard下下面,紧接着的路径是:${productFlavor}/${buildType},没有productFlavor就省略,如:

transforms/proguard/qihoo/release/jars/3/1f/main.jar

transforms/proguard/qihoo/debug/jars/3/1f/main.jar

transforms/proguard/xiaomi/release/jars/3/1f/main.jar

transforms/proguard/xiaomi/debug/jars/3/1f/main.jar

 

 

3、release后面是 jars 或 folders

如:

release/folders

release/jars

如何确定是 jars 还是 folders?

 

先给结论:

jars 或 folders 是由 TransformOutputProvider#getContentLocation(name, types, scopes, format) 中的 format 决定的:

(1)Format.DIRECTORY ==> folders

(2)Format.JAR ==> jars

 

1)自定的 Transform Task (StripAarTransform)

在 Transform#transform() 中获取输出目录时:

@Override
void transform(Context context, Collection inputs, Collection referencedInputs,
               TransformOutputProvider outputProvider, boolean isIncremental) 
               throws IOException, TransformException, InterruptedException {
    inputs.each { transformInput ->
        // Bypass the directories
        transformInput.directoryInputs.each { directoryInput ->
            File dest = outputProvider.getContentLocation(
                    directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY);
            FileUtils.copyDirectory(directoryInput.file, dest)
        }

        // Filter the jars
        transformInput.jarInputs.each { jarInput ->
            File dest = outputProvider.getContentLocation(
                    destName, jarInput.contentTypes, jarInput.scopes, Format.JAR)
            FileUtils.copyFile(jarInput.file, dest)
        }
    }
}

所以,会产生folders、jars两个目录:

Gradle Transform 输出路径解析_第2张图片

 

2)对于 Proguard Transform Task 来说

在 Transform#transform() 中获取输出目录时:

com.android.build.gradle.internal.transforms.ProGuardTransform.java:

Set outputTypes = getOutputTypes();
Set scopes = getScopes();
File outFile = output.getContentLocation("main", outputTypes, scopes,
        asJar ? Format.JAR : Format.DIRECTORY);

 

 

 

在 com.android.tools.build:gradle:2.3.2 版本的时候,asJar 总是为true的。

所以 format = Format.JAR ==> jars

 

Gradle Transform 输出路径解析_第3张图片

 

3)对于dex Transform Task 来说

看看源码: com.android.build.gradle.internal.transforms.ProGuardTransform.java:

File outputDir = outputProvider.getContentLocation("main",
        getOutputTypes(),
        TransformManager.SCOPE_FULL_PROJECT,
        Format.DIRECTORY);

所以 format = Format.DIRECTORY ==> folders

 

Gradle Transform 输出路径解析_第4张图片

 

 

 

 

4、folders|jars 后面是 ${OutputTypes}

 

注意 OutputTypes 是一个数字的十六进制表示。

先给结论:

${OutputTypes} 是由 TransformOutputProvider#getContentLocation(name, types, scopes, format) 中的 types 决定的:

 

types 一般会取自 Transform#getOutputTypes() 的值,也不一定,看具体的参数吧。

但是注意:Transform#getOutputTypes() 如果子类没有复写的话,默认返回值等于Transform#getInputTypes() :

源码:com.android.build.api.transform.Transform.class:

/**
 * Returns the type(s) of data that is generated by the Transform. This may be more than
 * one type.
 *
 * 

The default implementation returns {@link #getInputTypes()}. * *

This must be of type {@link QualifiedContent.DefaultContentType} */ @NonNull public Set getOutputTypes() { return getInputTypes(); }

 

1)自定的 Transform Task (StripAarTransform)

@Override
void transform(Context context, Collection inputs, Collection referencedInputs,
               TransformOutputProvider outputProvider, boolean isIncremental) 
               throws IOException, TransformException, InterruptedException {
    inputs.each { transformInput ->
        // Bypass the directories
        transformInput.directoryInputs.each { directoryInput ->
            File dest = outputProvider.getContentLocation(
                    directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY);
            FileUtils.copyDirectory(directoryInput.file, dest)
        }

        // Filter the jars
        transformInput.jarInputs.each { jarInput ->
            File dest = outputProvider.getContentLocation(
                    destName, jarInput.contentTypes, jarInput.scopes, Format.JAR)
            FileUtils.copyFile(jarInput.file, dest)
        }
    }
}

上面的 types 不是来自 getOutputTypes() 的,而是来自实际输入文件的types,如:

(1)对于 folders: types = directoryInput.contentTypes, directoryInput的Types一般等于:TransformManager.CONTENT_CLASS,

TransformManager.CONTENT_CLASS 定义在:com.android.build.gradle.internal.pipeline.TransformManager.class 中:

public class TransformManager extends FilterableStreamCollection {
    public static final Set CONTENT_CLASS = ImmutableSet.of(CLASSES);
    public static final Set CONTENT_JARS = ImmutableSet.of(CLASSES, RESOURCES);
    public static final Set CONTENT_RESOURCES = ImmutableSet.of(RESOURCES);
    public static final Set CONTENT_NATIVE_LIBS = ImmutableSet.of(ExtendedContentType.NATIVE_LIBS);
    public static final Set CONTENT_DEX = ImmutableSet.of(ExtendedContentType.DEX);
    public static final Set CONTENT_JACK = ImmutableSet.of(JACK);
 }

CLASSES 定义在:com.android.build.api.transform.QualifiedContent.class 中:

public interface QualifiedContent {
    /**
     * The type of of the content.
     */
    enum DefaultContentType implements ContentType {
        /**
         * The content is compiled Java code. This can be in a Jar file or in a folder. If
         * in a folder, it is expected to in sub-folders matching package names.
         */
        CLASSES(0x01),
        /**
         * The content is standard Java resources.
         */
        RESOURCES(0x02);
    }
}

所以,TransformManager.CONTENT_CLASS ==> 1

Gradle Transform 输出路径解析_第5张图片

 

(2)对于 jars: types = jarInput.contentTypes, jarInput 的 Types一般等于:TransformManager.CONTENT_JARS,

由上面代码知:TransformManager.CONTENT_JARS 包含 CLASSES(1) 、RESOURCES(2) 、 CLASSES + RESOURCES(3)

所以,就要看具体 jarInput 的情况了:下图都是classes,所以都是“1”:

Gradle Transform 输出路径解析_第6张图片

 

2)对于 Proguard Transform Task 来说

看看源码:com.android.build.gradle.internal.transforms.ProGuardTransform.java:

Set outputTypes = getOutputTypes();
Set scopes = getScopes();
File outFile = output.getContentLocation("main", outputTypes, scopes,
        asJar ? Format.JAR : Format.DIRECTORY);

 

上面 outputTypes = getOutputTypes(),默认实现是 getInputTypes() :

看看源码: com.android.build.gradle.internal.transforms.ProGuardTransform.class:

 

public Set getInputTypes() {
    return TransformManager.CONTENT_JARS;
}

从上面的代码可知,TransformManager.CONTENT_JARS ==> CLASSES + RESOURCES = 3

这就解释了为什么Proguard的输出路径有 “3” 了。

 

Gradle Transform 输出路径解析_第7张图片

 

3)对于dex Transform Task 来说

看看源码: com.android.build.gradle.internal.transforms.ProGuardTransform.java:

 

File outputDir = outputProvider.getContentLocation("main",
        getOutputTypes(),
        TransformManager.SCOPE_FULL_PROJECT,
        Format.DIRECTORY);

上面 outputTypes = getOutputTypes(),且有复写的实现:

看看源码: com.android.build.gradle.internal.transforms.DexTransform.class:

 

@Override
public Set getOutputTypes() {
    return TransformManager.CONTENT_DEX;
}

从上文可以看到:

TransformManager.CONTENT_DEX ==> ExtendedContentType.DEX

 

其中DEX定义为:com.android.build.gradle.internal.pipeline.ExtendedContentType.class:

/**
 * Content types private to the Android Plugin.
 */
public enum ExtendedContentType implements ContentType {
    /**
     * The content is dex files.
     */
    DEX(0x1000),
    /**
     * Content is a native library.
     */
    NATIVE_LIBS(0x2000),
    /**
     * Instant Run '$override' classes, which contain code of new method bodies.
     *
     * 

This stream also contains the AbstractPatchesLoaderImpl class for applying HotSwap * changes. */ CLASSES_ENHANCED(0x4000), /** * The content is Jack library. * * This is zip file containing classes in jayce format. * If the library has been pre-dexed it will also contain the corresponding dex. */ JACK(0x8000), /** * The content is an artifact exported by the data binding compiler. */ DATA_BINDING(0x10000); }

所以:outputTypes = TransformManager.CONTENT_DEX ==> ExtendedContentType.DEX ==> 1000

验证一下对不对:

Gradle Transform 输出路径解析_第8张图片

 

 

 

5、OutputTypes 后面是 ${Scopes}

注意 Scopes 是一个数字的十六进制表示。

先给结论:

${Scopes} 是由 TransformOutputProvider#getContentLocation(name, types, scopes, format) 中的 scopes 决定的:

其值常常来自:Transform#getScopes() ,也不一定,看具体的参数值吧。

 

1)自定的 Transform Task (StripAarTransform)

@Override
void transform(Context context, Collection inputs, Collection referencedInputs,
               TransformOutputProvider outputProvider, boolean isIncremental) 
               throws IOException, TransformException, InterruptedException {
    inputs.each { transformInput ->
        // Bypass the directories
        transformInput.directoryInputs.each { directoryInput ->
            File dest = outputProvider.getContentLocation(
                    directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY);
            FileUtils.copyDirectory(directoryInput.file, dest)
        }

        // Filter the jars
        transformInput.jarInputs.each { jarInput ->
            File dest = outputProvider.getContentLocation(
                    destName, jarInput.contentTypes, jarInput.scopes, Format.JAR)
            FileUtils.copyFile(jarInput.file, dest)
        }
    }
}

上面的 scopes 不是来自 getScopes() 的,而是来自实际输入文件的 scopes,

scopes 定义在:com.android.build.api.transform.QualifiedContent.class:

 

/**
 * The scope of the content.
 *
 * 

* This indicates what the content represents, so that Transforms can apply to only part(s) * of the classes or resources that the build manipulates. */ enum Scope implements ScopeType { /** Only the project content */ PROJECT(0x01), /** Only the project's local dependencies (local jars) */ PROJECT_LOCAL_DEPS(0x02), /** Only the sub-projects. */ SUB_PROJECTS(0x04), /** Only the sub-projects's local dependencies (local jars). */ SUB_PROJECTS_LOCAL_DEPS(0x08), /** Only the external libraries */ EXTERNAL_LIBRARIES(0x10), /** Code that is being tested by the current variant, including dependencies */ TESTED_CODE(0x20), /** Local or remote dependencies that are provided-only */ PROVIDED_ONLY(0x40); }

结合际输入文件的 scopes,最终取值如下图所示:

Gradle Transform 输出路径解析_第9张图片

 

2)对于 Proguard Transform Task 来说

看看源码:com.android.build.gradle.internal.transforms.ProGuardTransform.java:

 

Set outputTypes = getOutputTypes();
Set scopes = getScopes();
File outFile = output.getContentLocation("main", outputTypes, scopes,
        asJar ? Format.JAR : Format.DIRECTORY);

上面 scopes = getScopes():

看看源码:com.android.build.gradle.internal.transforms.ProGuardTransform.java:

 

public Set getScopes() {
    if (variantType == VariantType.LIBRARY) {
        return Sets.immutableEnumSet(Scope.PROJECT, Scope.PROJECT_LOCAL_DEPS);
    }
    return TransformManager.SCOPE_FULL_PROJECT;
}

当Project为App时,scopes = TransformManager.SCOPE_FULL_PROJECT

TransformManager.SCOPE_FULL_PROJECT 定义在:com.android.build.gradle.internal.pipeline.TransformManager.java:

 

public static final Set SCOPE_FULL_PROJECT = Sets.immutableEnumSet(
        Scope.PROJECT,
        Scope.PROJECT_LOCAL_DEPS,
        Scope.SUB_PROJECTS,
        Scope.SUB_PROJECTS_LOCAL_DEPS,
        Scope.EXTERNAL_LIBRARIES);

Gradle Transform 输出路径解析_第10张图片

也就是,ProGuard 是将所有class与jar汇总输出一个jar包。计算 SCOPE_FULL_PROJECT 的元素之和:

scopes = TransformManager.SCOPE_FULL_PROJECT ==> 0x1F ==> 1f

这就解释了为什么Proguard的输出路径有“1f”了。

Gradle Transform 输出路径解析_第11张图片

 

3)对于dex Transform Task 来说

看看源码: com.android.build.gradle.internal.transforms.ProGuardTransform.java:

File outputDir = outputProvider.getContentLocation("main",
        getOutputTypes(),
        TransformManager.SCOPE_FULL_PROJECT,
        Format.DIRECTORY);

同 Proguard,scopes = TransformManager.SCOPE_FULL_PROJECT ==> 0x1F ==> 1f。

 

Gradle Transform 输出路径解析_第12张图片

 

 

 

 

 

 

你可能感兴趣的:(Transform,Proguard,Dex,Android,Java)