blob: bdbf8b51a384ebe806e3fc112054dc29a1a4c5b0 [file] [log] [blame]
/*
* Copyright (C) 2016 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.gradle.tasks;
import static com.android.build.gradle.internal.scope.InternalArtifactType.MERGED_SHADERS;
import static com.android.build.gradle.internal.utils.HasConfigurableValuesKt.setDisallowChanges;
import static com.google.common.base.Preconditions.checkNotNull;
import com.android.annotations.NonNull;
import com.android.build.api.component.impl.ComponentPropertiesImpl;
import com.android.build.gradle.internal.LoggerWrapper;
import com.android.build.gradle.internal.SdkComponentsBuildService;
import com.android.build.gradle.internal.core.VariantDslInfo;
import com.android.build.gradle.internal.process.GradleProcessExecutor;
import com.android.build.gradle.internal.scope.InternalArtifactType;
import com.android.build.gradle.internal.services.BuildServicesKt;
import com.android.build.gradle.internal.tasks.NonIncrementalTask;
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction;
import com.android.builder.internal.compiler.DirectoryWalker;
import com.android.builder.internal.compiler.ShaderProcessor;
import com.android.ide.common.process.LoggedProcessOutputHandler;
import com.android.ide.common.process.ProcessOutputHandler;
import com.android.ide.common.workers.WorkerExecutorFacade;
import com.android.repository.Revision;
import com.android.utils.FileUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import javax.inject.Inject;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.FileTree;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.CacheableTask;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.SkipWhenEmpty;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.api.tasks.util.PatternSet;
import org.gradle.process.ExecOperations;
/**
* Task to compile Shaders.
*
* <p>TODO(b/124424292)
*
* <p>We can not use gradle worker in this task as we use {@link GradleProcessExecutor} for
* compiling shader files, which should not be serialized.
*/
@CacheableTask
public abstract class ShaderCompile extends NonIncrementalTask {
private static final PatternSet PATTERN_SET = new PatternSet()
.include("**/*." + ShaderProcessor.EXT_VERT)
.include("**/*." + ShaderProcessor.EXT_TESC)
.include("**/*." + ShaderProcessor.EXT_TESE)
.include("**/*." + ShaderProcessor.EXT_GEOM)
.include("**/*." + ShaderProcessor.EXT_FRAG)
.include("**/*." + ShaderProcessor.EXT_COMP);
@Internal
public Provider<Revision> getBuildToolInfoRevisionProvider() {
return getSdkBuildService()
.flatMap(SdkComponentsBuildService::getBuildToolsRevisionProvider);
}
@Input
public Provider<String> getBuildToolsVersion() {
return getBuildToolInfoRevisionProvider()
.map(rev -> Objects.requireNonNull(rev.toString()));
}
@Internal
public abstract Property<SdkComponentsBuildService> getSdkBuildService();
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
public abstract DirectoryProperty getSourceDir();
@NonNull
private List<String> defaultArgs = ImmutableList.of();
private Map<String, List<String>> scopedArgs = ImmutableMap.of();
@NonNull private final ExecOperations execOperations;
@Inject
public ShaderCompile(@NonNull ExecOperations execOperations) {
this.execOperations = execOperations;
}
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
@SkipWhenEmpty
public FileTree getSourceFiles() {
File sourceDirFile = getSourceDir().get().getAsFile();
FileTree src = null;
if (sourceDirFile.isDirectory()) {
src = getProject().files(sourceDirFile).getAsFileTree().matching(PATTERN_SET);
}
return src == null ? getProject().files().getAsFileTree() : src;
}
@Override
protected void doTaskAction() throws IOException {
// this is full run, clean the previous output
File destinationDir = getOutputDir().get().getAsFile();
FileUtils.cleanOutputDir(destinationDir);
try (WorkerExecutorFacade workers = getWorkerFacadeWithThreads(false)) {
compileAllShaderFiles(
getSourceDir().get().getAsFile(),
destinationDir,
defaultArgs,
scopedArgs,
getSdkBuildService().get().getNdkDirectoryProvider().get().getAsFile(),
new LoggedProcessOutputHandler(new LoggerWrapper(getLogger())),
workers);
}
}
/**
* Compiles all the shader files found in the given source folders.
*
* @param sourceFolder the source folder with the merged shaders
* @param outputDir the output dir in which to generate the output
* @throws IOException failed
*/
private void compileAllShaderFiles(
@NonNull File sourceFolder,
@NonNull File outputDir,
@NonNull List<String> defaultArgs,
@NonNull Map<String, List<String>> scopedArgs,
@NonNull File ndkLocation,
@NonNull ProcessOutputHandler processOutputHandler,
@NonNull WorkerExecutorFacade workers)
throws IOException {
checkNotNull(sourceFolder, "sourceFolder cannot be null.");
checkNotNull(outputDir, "outputDir cannot be null.");
Supplier<ShaderProcessor> processor =
() ->
new ShaderProcessor(
ndkLocation,
sourceFolder,
outputDir,
defaultArgs,
scopedArgs,
new GradleProcessExecutor(execOperations::exec),
processOutputHandler,
workers);
DirectoryWalker.builder()
.root(sourceFolder.toPath())
.extensions(
ShaderProcessor.EXT_VERT,
ShaderProcessor.EXT_TESC,
ShaderProcessor.EXT_TESE,
ShaderProcessor.EXT_GEOM,
ShaderProcessor.EXT_FRAG,
ShaderProcessor.EXT_COMP)
.action(processor)
.build()
.walk();
}
@OutputDirectory
public abstract DirectoryProperty getOutputDir();
@NonNull
@Input
public List<String> getDefaultArgs() {
return defaultArgs;
}
public void setDefaultArgs(@NonNull List<String> defaultArgs) {
this.defaultArgs = ImmutableList.copyOf(defaultArgs);
}
@NonNull
@Input
public Map<String, List<String>> getScopedArgs() {
return scopedArgs;
}
public void setScopedArgs(@NonNull Map<String, List<String>> scopedArgs) {
this.scopedArgs = ImmutableMap.copyOf(scopedArgs);
}
public static class CreationAction
extends VariantTaskCreationAction<ShaderCompile, ComponentPropertiesImpl> {
public CreationAction(@NonNull ComponentPropertiesImpl componentProperties) {
super(componentProperties);
}
@Override
@NonNull
public String getName() {
return computeTaskName("compile", "Shaders");
}
@Override
@NonNull
public Class<ShaderCompile> getType() {
return ShaderCompile.class;
}
@Override
public void handleProvider(@NonNull TaskProvider<ShaderCompile> taskProvider) {
super.handleProvider(taskProvider);
creationConfig
.getArtifacts()
.setInitialProvider(taskProvider, ShaderCompile::getOutputDir)
.withName("out")
.on(InternalArtifactType.SHADER_ASSETS.INSTANCE);
}
@Override
public void configure(@NonNull ShaderCompile task) {
super.configure(task);
final VariantDslInfo variantDslInfo = creationConfig.getVariantDslInfo();
setDisallowChanges(
task.getSdkBuildService(),
BuildServicesKt.getBuildService(
creationConfig.getServices().getBuildServiceRegistry(),
SdkComponentsBuildService.class));
creationConfig
.getArtifacts()
.setTaskInputToFinalProduct(MERGED_SHADERS.INSTANCE, task.getSourceDir());
task.setDefaultArgs(variantDslInfo.getDefaultGlslcArgs());
task.setScopedArgs(variantDslInfo.getScopedGlslcArgs());
}
}
}