/* * Copyright 2015 the original author or authors. * * 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 org.gradle.language.base.internal.model; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.commons.collections.comparators.BooleanComparator; import org.gradle.api.Task; import org.gradle.api.tasks.TaskContainer; import org.gradle.internal.service.ServiceRegistry; import org.gradle.language.base.LanguageSourceSet; import org.gradle.language.base.internal.JointCompileTaskConfig; import org.gradle.language.base.internal.LanguageSourceSetInternal; import org.gradle.language.base.internal.SourceTransformTaskConfig; import org.gradle.language.base.internal.registry.LanguageTransform; import org.gradle.language.base.internal.registry.LanguageTransformContainer; import org.gradle.platform.base.internal.BinarySpecInternal; import java.util.*; import static org.apache.commons.lang.StringUtils.capitalize; /** * Creates source 'transformation' tasks based on the available {@link LanguageTransform}s. * * This class is a basic and somewhat hacky placeholder for true dependency-aware source handling: * - Source sets should be able to depend on other source sets, resulting in the correct task dependencies and inputs * - Joint-compilation should only be used in the case where sources are co-dependent. * * Currently we use joint-compilation when: * - We have a language transform that supports joint-compilation * - Binary is flagged with {@link BinarySpecInternal#hasCodependentSources()}. */ public class BinarySourceTransformations { private final TaskContainer tasks; private final Iterable<LanguageTransform<?, ?>> prioritizedTransforms; private final ServiceRegistry serviceRegistry; public BinarySourceTransformations(TaskContainer tasks, LanguageTransformContainer transforms, ServiceRegistry serviceRegistry) { this.tasks = tasks; this.prioritizedTransforms = prioritize(transforms); this.serviceRegistry = serviceRegistry; } public void createTasksFor(BinarySpecInternal binary) { Set<LanguageSourceSetInternal> sourceSetsToCompile = getSourcesToCompile(binary); for (LanguageTransform<?, ?> languageTransform : prioritizedTransforms) { if (!languageTransform.applyToBinary(binary)) { continue; } LanguageSourceSetInternal sourceSetToCompile; while ((sourceSetToCompile = findSourceFor(languageTransform, sourceSetsToCompile)) != null) { sourceSetsToCompile.remove(sourceSetToCompile); final SourceTransformTaskConfig taskConfig = languageTransform.getTransformTask(); String taskName = getTransformTaskName(languageTransform, taskConfig, binary, sourceSetToCompile); Task task = tasks.create(taskName, taskConfig.getTaskType()); taskConfig.configureTask(task, binary, sourceSetToCompile, serviceRegistry); task.dependsOn(sourceSetToCompile); binary.getTasks().add(task); if (binary.hasCodependentSources() && taskConfig instanceof JointCompileTaskConfig) { JointCompileTaskConfig jointCompileTaskConfig = (JointCompileTaskConfig) taskConfig; Iterator<LanguageSourceSetInternal> candidateSourceSets = sourceSetsToCompile.iterator(); while (candidateSourceSets.hasNext()) { LanguageSourceSetInternal candidate = candidateSourceSets.next(); if (jointCompileTaskConfig.canTransform(candidate)) { jointCompileTaskConfig.configureAdditionalTransform(task, candidate); candidateSourceSets.remove(); } } } } } // Should really fail here if sourcesToCompile is not empty: no transform for this source set in this binary } private Iterable<LanguageTransform<?, ?>> prioritize(LanguageTransformContainer languageTransforms) { List<LanguageTransform<?, ?>> prioritized = Lists.newArrayList(languageTransforms); Collections.sort(prioritized, new Comparator<LanguageTransform<?, ?>>() { @Override public int compare(LanguageTransform<?, ?> o1, LanguageTransform<?, ?> o2) { boolean joint1 = o1.getTransformTask() instanceof JointCompileTaskConfig; boolean joint2 = o2.getTransformTask() instanceof JointCompileTaskConfig; return new BooleanComparator(true).compare(joint1, joint2); } }); return prioritized; } private Set<LanguageSourceSetInternal> getSourcesToCompile(BinarySpecInternal binary) { LinkedHashSet<LanguageSourceSetInternal> sourceSets = Sets.newLinkedHashSet(); for (LanguageSourceSet languageSourceSet : binary.getInputs()) { LanguageSourceSetInternal languageSourceSetInternal = (LanguageSourceSetInternal) languageSourceSet; if (languageSourceSetInternal.getMayHaveSources()) { sourceSets.add(languageSourceSetInternal); } } return sourceSets; } private String getTransformTaskName(LanguageTransform<?, ?> transform, SourceTransformTaskConfig taskConfig, BinarySpecInternal binary, LanguageSourceSetInternal sourceSetToCompile) { if (binary.hasCodependentSources() && taskConfig instanceof JointCompileTaskConfig) { return taskConfig.getTaskPrefix() + capitalize(binary.getProjectScopedName()) + capitalize(transform.getClass().getSimpleName()); } return taskConfig.getTaskPrefix() + capitalize(binary.getProjectScopedName()) + capitalize(sourceSetToCompile.getProjectScopedName()); } private LanguageSourceSetInternal findSourceFor(LanguageTransform<?, ?> languageTransform, Set<LanguageSourceSetInternal> sourceSetsToCompile) { for (LanguageSourceSetInternal candidate : sourceSetsToCompile) { if (languageTransform.getSourceSetType().isInstance(candidate)) { return candidate; } } return null; } }