blob: ce14f68b874e35521d2e8fc5fe994ce4afaf452e [file] [log] [blame]
/*
* Copyright (C) 2015 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.build.api.transform;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.api.transform.QualifiedContent.ContentType;
import com.android.build.api.transform.QualifiedContent.Scope;
import com.android.build.api.variant.VariantInfo;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.gradle.api.Incubating;
/**
* A Transform that processes intermediary build artifacts.
*
* <p>For each added transform, a new task is created. The action of adding a transform takes
* care of handling dependencies between the tasks. This is done based on what the transform
* processes. The output of the transform becomes consumable by other transforms and these
* tasks get automatically linked together.
*
* <p>The Transform indicates what it applies to (content, scope) and what it generates (content).
*
* <p>A transform receives input as a collection {@link TransformInput}, which is composed of
* {@link JarInput}s and {@link DirectoryInput}s.
* Both provide information about the {@link Scope}s and {@link ContentType}s associated with their
* particular content.
*
* <p>The output is handled by {@link TransformOutputProvider} which allows creating new self-contained
* content, each associated with their own Scopes and Content Types.
* The content handled by TransformInput/Output is managed by the transform system, and their
* location is not configurable.
*
* <p>It is best practice to write into as many outputs as Jar/Folder Inputs have been received by the
* transform. Combining all the inputs into a single output prevents downstream transform from
* processing limited scopes.
*
* <p>While it's possible to differentiate different Content Types by file extension, it's not possible
* to do so for Scopes. Therefore if a transform request a Scope but the only available Output
* contains more than the requested Scope, the build will fail.<br>
* If a transform request a single content type but the only available content includes more than
* the requested type, the input file/folder will contain all the files of all the types, but
* the transform should only read, process and output the type(s) it requested.
*
*
* <p>Additionally, a transform can indicate secondary inputs/outputs. These are not handled by
* upstream or downstream transforms, and are not restricted by type handled by transform. They can
* be anything.
*
* <p>It's up to each transform to manage where these files are, and to make sure that these files
* are generated before the transform is called. This is done through additional parameters
* when register the transform.
*
* <p>These secondary inputs/outputs allow a transform to read but not process any content. This
* can be achieved by having {@link #getScopes()} return an empty list and use
* {@link #getReferencedScopes()} to indicate what to read instead.
*/
@SuppressWarnings("MethodMayBeStatic")
public abstract class Transform {
/**
* Returns the unique name of the transform.
*
* <p>This is associated with the type of work that the transform does. It does not have to be
* unique per variant.
*/
@NonNull
public abstract String getName();
/**
* Whether this transform should be applied to a given variant.
*
* @since 3.4
* @return true if the transform should be applied to a given variant, false otherwise.
* @param variant information about the current variant.
*/
@Incubating
public boolean applyToVariant(@NonNull VariantInfo variant) {
return true;
}
/**
* Returns the type(s) of data that is consumed by the Transform. This may be more than
* one type.
*
* <strong>This must be of type {@link QualifiedContent.DefaultContentType}</strong>
*/
@NonNull
public abstract Set<ContentType> getInputTypes();
/**
* 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();
}
/**
* Returns the scope(s) of the Transform. This indicates which scopes the transform consumes.
*/
@NonNull
public abstract Set<? super Scope> getScopes();
/**
* Returns the referenced scope(s) for the Transform. These scopes are not consumed by
* the Transform. They are provided as inputs, but are still available as inputs for
* other Transforms to consume.
*
* <p>The default implementation returns an empty Set.
*/
@NonNull
public Set<? super Scope> getReferencedScopes() {
return ImmutableSet.of();
}
/**
* Returns a list of additional file(s) that this Transform needs to run. Preferably, use
* {@link #getSecondaryFiles()} API which allow eah secondary file to indicate if changes
* can be handled incrementally or not. This API will treat all additional file change as
* a non incremental event.
*
* <p>Changes to files returned in this list will trigger a new execution of the Transform
* even if the qualified-content inputs haven't been touched.
*
* <p>Any changes to these files will trigger a non incremental execution.
*
* <p>The default implementation returns an empty collection.
*
* @deprecated replaced by {@link #getSecondaryFiles()}
*/
@Deprecated
@NonNull
public Collection<File> getSecondaryFileInputs() {
return ImmutableList.of();
}
/**
* Returns a list of additional file(s) that this Transform needs to run.
*
* <p>Changes to files returned in this list will trigger a new execution of the Transform
* even if the qualified-content inputs haven't been touched.
*
* <p>Each secondary input has the ability to be declared as necessitating a non incremental
* execution in case of change. This Transform can therefore declare which secondary file
* changes it supports in incremental mode.
*
* <p>The default implementation returns an empty collection.
*/
@NonNull
public Collection<SecondaryFile> getSecondaryFiles() {
return ImmutableList.of();
}
/**
* Returns a list of additional (out of streams) file(s) that this Transform creates.
*
* <p>These File instances can only represent files, not directories. For directories, use
* {@link #getSecondaryDirectoryOutputs()}
*
*
* <p>Changes to files returned in this list will trigger a new execution of the Transform
* even if the qualified-content inputs haven't been touched.
*
* <p>Changes to these output files force a non incremental execution.
*
* <p>The default implementation returns an empty collection.
*/
@NonNull
public Collection<File> getSecondaryFileOutputs() {
return ImmutableList.of();
}
/**
* Returns a list of additional (out of streams) directory(ies) that this Transform creates.
*
* <p>These File instances can only represent directories. For files, use
* {@link #getSecondaryFileOutputs()}
*
* <p>Changes to directories returned in this list will trigger a new execution of the Transform
* even if the qualified-content inputs haven't been touched.
*
* <p>Changes to these output directories force a non incremental execution.
*
* <p>The default implementation returns an empty collection.
*/
@NonNull
public Collection<File> getSecondaryDirectoryOutputs() {
return ImmutableList.of();
}
/**
* Returns a map of non-file input parameters using a unique identifier as the map key.
*
* <p>Changes to values returned in this map will trigger a new execution of the Transform
* even if the content inputs haven't been touched.
*
* <p>Changes to these values force a non incremental execution.
*
* <p>The default implementation returns an empty Map.
*/
@NonNull
public Map<String, Object> getParameterInputs() {
return ImmutableMap.of();
}
/**
* Returns whether the Transform can perform incremental work.
*
* <p>If it does, then the TransformInput may contain a list of changed/removed/added files, unless
* something else triggers a non incremental run.
*/
public abstract boolean isIncremental();
/**
* @deprecated replaced by {@link #transform(TransformInvocation)}.
*/
@Deprecated
@SuppressWarnings("UnusedParameters")
public void transform(
@NonNull Context context,
@NonNull Collection<TransformInput> inputs,
@NonNull Collection<TransformInput> referencedInputs,
@Nullable TransformOutputProvider outputProvider,
boolean isIncremental) throws IOException, TransformException, InterruptedException {
}
/**
* Executes the Transform.
*
* <p>The inputs are packaged as an instance of {@link TransformInvocation}
* <ul>
* <li>The <var>inputs</var> collection of {@link TransformInput}. These are the inputs
* that are consumed by this Transform. A transformed version of these inputs must
* be written into the output. What is received is controlled through
* {@link #getInputTypes()}, and {@link #getScopes()}.</li>
* <li>The <var>referencedInputs</var> collection of {@link TransformInput}. This is
* for reference only and should be not be transformed. What is received is controlled
* through {@link #getReferencedScopes()}.</li>
* </ul>
*
* A transform that does not want to consume anything but instead just wants to see the content
* of some inputs should return an empty set in {@link #getScopes()}, and what it wants to
* see in {@link #getReferencedScopes()}.
*
* <p>Even though a transform's {@link Transform#isIncremental()} returns true, this method may
* be receive <code>false</code> in <var>isIncremental</var>. This can be due to
* <ul>
* <li>a change in secondary files ({@link #getSecondaryFiles()},
* {@link #getSecondaryFileOutputs()}, {@link #getSecondaryDirectoryOutputs()})</li>
* <li>a change to a non file input ({@link #getParameterInputs()})</li>
* <li>an unexpected change to the output files/directories. This should not happen unless
* tasks are improperly configured and clobber each other's output.</li>
* <li>a file deletion that the transform mechanism could not match to a previous input.
* This should not happen in most case, except in some cases where dependencies have
* changed.</li>
* </ul>
* In such an event, when <var>isIncremental</var> is false, the inputs will not have any
* incremental change information:
* <ul>
* <li>{@link JarInput#getStatus()} will return {@link Status#NOTCHANGED} even though
* the file may be added/changed.</li>
* <li>{@link DirectoryInput#getChangedFiles()} will return an empty map even though
* some files may be added/changed.</li>
* </ul>
*
* @param transformInvocation the invocation object containing the transform inputs.
* @throws IOException if an IO error occurs.
* @throws InterruptedException
* @throws TransformException Generic exception encapsulating the cause.
*/
public void transform(@NonNull TransformInvocation transformInvocation)
throws TransformException, InterruptedException, IOException {
// Just delegate to old method, for code that uses the old API.
//noinspection deprecation
transform(transformInvocation.getContext(), transformInvocation.getInputs(),
transformInvocation.getReferencedInputs(),
transformInvocation.getOutputProvider(),
transformInvocation.isIncremental());
}
/**
* Returns if this transform's outputs should be cached. Please read {@link
* org.gradle.api.tasks.CacheableTask} Javadoc if you would like to make your transform
* cacheable.
*/
public boolean isCacheable() {
return false;
}
}