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

先给个结论吧:

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

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

/**
 * The output of a transform.
 * <p>
 * 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.
     * <p>
     * If the format is {@link Format#DIRECTORY} then the result is the file location of the
     * directory.<br>
     * If the format is {@link Format#JAR} then the result is a file representing the jar to create.
     * <p>
     * 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.
     * <p>
     * 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<QualifiedContent.ContentType> types,
            @NonNull Set<? super QualifiedContent.Scope> 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<TransformInput> inputs, Collection<TransformInput> 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两个目录:

2)对于 Proguard Transform Task 来说

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

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

Set<ContentType> outputTypes = getOutputTypes();
Set<Scope> 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

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

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.
 *
 * <p>The default implementation returns {@link #getInputTypes()}.
 *
 * <p><strong>This must be of type {@link QualifiedContent.DefaultContentType}</strong>
 */
@NonNull
public Set<ContentType> getOutputTypes() {
    return getInputTypes();
}

1)自定的 Transform Task (StripAarTransform)

@Override
void transform(Context context, Collection<TransformInput> inputs, Collection<TransformInput> 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<ContentType> CONTENT_CLASS = ImmutableSet.of(CLASSES);
    public static final Set<ContentType> CONTENT_JARS = ImmutableSet.of(CLASSES, RESOURCES);
    public static final Set<ContentType> CONTENT_RESOURCES = ImmutableSet.of(RESOURCES);
    public static final Set<ContentType> CONTENT_NATIVE_LIBS = ImmutableSet.of(ExtendedContentType.NATIVE_LIBS);
    public static final Set<ContentType> CONTENT_DEX = ImmutableSet.of(ExtendedContentType.DEX);
    public static final Set<ContentType> 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

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

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

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

2)对于 Proguard Transform Task 来说

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

Set<ContentType> outputTypes = getOutputTypes();
Set<Scope> 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<ContentType> getInputTypes() {
    return TransformManager.CONTENT_JARS;
}

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

这就解释了为什么Proguard的输出路径有“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);

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

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

@Override
public Set<ContentType> 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.
     *
     * <p>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

验证一下对不对:

5、OutputTypes 后面是 ${Scopes}

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

先给结论:

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

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

1)自定的 Transform Task (StripAarTransform)

@Override
void transform(Context context, Collection<TransformInput> inputs, Collection<TransformInput> 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.
 *
 * <p>
 * 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,最终取值如下图所示:

2)对于 Proguard Transform Task 来说

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

Set<ContentType> outputTypes = getOutputTypes();
Set<Scope> 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<Scope> 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> SCOPE_FULL_PROJECT = Sets.immutableEnumSet(
        Scope.PROJECT,
        Scope.PROJECT_LOCAL_DEPS,
        Scope.SUB_PROJECTS,
        Scope.SUB_PROJECTS_LOCAL_DEPS,
        Scope.EXTERNAL_LIBRARIES);

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

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

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

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。

猜你喜欢

转载自blog.csdn.net/zhoaya188/article/details/82355965