Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,16 @@
import org.gradle.api.file.FileCollection;
import org.gradle.api.internal.TaskExecutionHistory;
import org.gradle.api.internal.changedetection.TaskArtifactState;
import org.gradle.api.internal.file.collections.FileCollectionAdapter;
import org.gradle.api.internal.file.collections.MinimalFileSet;
import org.gradle.api.internal.tasks.cache.TaskCacheKey;
import org.gradle.api.tasks.incremental.IncrementalTaskInputs;

import java.io.File;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;

class NoHistoryArtifactState implements TaskArtifactState, TaskExecutionHistory {
public boolean isUpToDate(Collection<String> messages) {
Expand Down Expand Up @@ -55,6 +61,47 @@ public void finished() {
}

public FileCollection getOutputFiles() {
throw new UnsupportedOperationException();
return EmptyFileCollection.INSTANCE;
}


private static class EmptyFileCollection extends FileCollectionAdapter implements Serializable {

private static final long serialVersionUID = -5671359228892430322L;

private static final FileCollection INSTANCE = new EmptyFileCollection();

private EmptyFileCollection() {
super(EmptyFileSet.INSTANCE);
}

private Object readResolve() {
return INSTANCE;
}
}

private static class EmptyFileSet implements MinimalFileSet, Serializable {

private static final long serialVersionUID = 8533471127662131385L;

private static final Set<File> EMPTY_FILE_SET = Collections.emptySet();
private static final MinimalFileSet INSTANCE = new EmptyFileSet();

private EmptyFileSet() {
}

@Override
public Set<File> getFiles() {
return EMPTY_FILE_SET;
}

@Override
public String getDisplayName() {
return "empty file collection";
}

private Object readResolve() {
return INSTANCE;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2016 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.api.internal.tasks.execution;

import org.gradle.api.internal.TaskInternal;
import org.gradle.api.internal.changedetection.TaskArtifactStateRepository;
import org.gradle.api.internal.tasks.TaskExecuter;
import org.gradle.api.internal.tasks.TaskExecutionContext;
import org.gradle.api.internal.tasks.TaskStateInternal;
import org.gradle.util.Clock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ResolveTaskArtifactStateTaskExecuter implements TaskExecuter {
private static final Logger LOGGER = LoggerFactory.getLogger(ResolveTaskArtifactStateTaskExecuter.class);

private final TaskExecuter executer;
private final TaskArtifactStateRepository repository;

public ResolveTaskArtifactStateTaskExecuter(TaskArtifactStateRepository repository, TaskExecuter executer) {
this.executer = executer;
this.repository = repository;
}

@Override
public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
Clock clock = new Clock();
context.setTaskArtifactState(repository.getStateFor(task));
LOGGER.info("Putting task artifact state for {} into context took {}.", task, clock.getTime());
try {
executer.execute(task, state, context);
} finally {
context.setTaskArtifactState(null);
LOGGER.debug("Removed task artifact state for {} from context.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

import org.gradle.api.execution.internal.TaskInputsListener;
import org.gradle.api.file.FileCollection;
import org.gradle.api.internal.TaskInputsInternal;
import org.gradle.api.internal.TaskInternal;
import org.gradle.api.internal.changedetection.TaskArtifactState;
import org.gradle.api.internal.file.FileCollectionInternal;
import org.gradle.api.internal.tasks.TaskExecuter;
import org.gradle.api.internal.tasks.TaskExecutionContext;
Expand All @@ -26,6 +28,9 @@
import org.gradle.api.logging.Logging;
import org.gradle.internal.Cast;

import java.io.File;
import java.util.Set;

/**
* A {@link TaskExecuter} which skips tasks whose source file collection is empty.
*/
Expand All @@ -40,15 +45,49 @@ public SkipEmptySourceFilesTaskExecuter(TaskInputsListener taskInputsListener, T
}

public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
FileCollection sourceFiles = task.getInputs().getSourceFiles();
if (task.getInputs().getHasSourceFiles() && sourceFiles.isEmpty()) {
LOGGER.info("Skipping {} as it has no source files.", task);
state.upToDate();
taskInputsListener.onExecute(task, Cast.cast(FileCollectionInternal.class, sourceFiles));
return;
} else {
taskInputsListener.onExecute(task, Cast.cast(FileCollectionInternal.class, task.getInputs().getFiles()));
TaskInputsInternal taskInputs = task.getInputs();
if (taskInputs.getHasSourceFiles()) {
FileCollection sourceFiles = taskInputs.getSourceFiles();
if (sourceFiles.isEmpty()) {
state.skipped("SKIPPED");
TaskArtifactState taskArtifactState = context.getTaskArtifactState();
FileCollection outputFiles = taskArtifactState.getExecutionHistory().getOutputFiles();
if (outputFiles.isEmpty()) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Skipping {} as it has no source files and no previous output files.", task);
}
} else {
Set<File> outputFileSet = outputFiles.getFiles();
boolean deletedFiles = false;
for (File file : outputFileSet) {
if (file.isFile()) {
if (file.delete()) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Deleted stale output file '{}'.", file.getAbsolutePath());
}
deletedFiles = true;
} else {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Failed to delete stale output file '{}'.", file.getAbsolutePath());
}
}
}
}
if (deletedFiles) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Cleaned previous output of {} as it has no source files.", task);
}
// uncomment the line below if you think the info that previous output has been
// cleaned vs. a simple SKIPPED is worthwhile.
//state.skipped("CLEANED");
}
}
taskInputsListener.onExecute(task, Cast.cast(FileCollectionInternal.class, sourceFiles));
return;
}
}

taskInputsListener.onExecute(task, Cast.cast(FileCollectionInternal.class, taskInputs.getFiles()));
executer.execute(task, state, context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import org.gradle.api.internal.TaskInternal;
import org.gradle.api.internal.changedetection.TaskArtifactState;
import org.gradle.api.internal.changedetection.TaskArtifactStateRepository;
import org.gradle.api.internal.tasks.TaskExecuter;
import org.gradle.api.internal.tasks.TaskExecutionContext;
import org.gradle.api.internal.tasks.TaskStateInternal;
Expand All @@ -36,17 +35,15 @@
public class SkipUpToDateTaskExecuter implements TaskExecuter {
private static final Logger LOGGER = LoggerFactory.getLogger(SkipUpToDateTaskExecuter.class);
private final TaskExecuter executer;
private final TaskArtifactStateRepository repository;

public SkipUpToDateTaskExecuter(TaskArtifactStateRepository repository, TaskExecuter executer) {
public SkipUpToDateTaskExecuter(TaskExecuter executer) {
this.executer = executer;
this.repository = repository;
}

public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
LOGGER.debug("Determining if {} is up-to-date", task);
Clock clock = new Clock();
TaskArtifactState taskArtifactState = repository.getStateFor(task);
TaskArtifactState taskArtifactState = context.getTaskArtifactState();
try {
List<String> messages = LOGGER.isInfoEnabled() ? new ArrayList<String>() : null;
if (taskArtifactState.isUpToDate(messages)) {
Expand All @@ -57,7 +54,6 @@ public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionCon
logOutOfDateMessages(messages, task, clock.getTime());

task.getOutputs().setHistory(taskArtifactState.getExecutionHistory());
context.setTaskArtifactState(taskArtifactState);

taskArtifactState.beforeTask();
try {
Expand All @@ -67,7 +63,6 @@ public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionCon
}
} finally {
task.getOutputs().setHistory(null);
context.setTaskArtifactState(null);
}
} finally {
taskArtifactState.finished();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter;
import org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter;
import org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter;
import org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter;
import org.gradle.api.internal.tasks.execution.SkipCachedTaskExecuter;
import org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter;
import org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter;
Expand Down Expand Up @@ -87,18 +88,20 @@ TaskExecuter createTaskExecuter(TaskArtifactStateRepository repository, TaskOutp
return new ExecuteAtMostOnceTaskExecuter(
new SkipOnlyIfTaskExecuter(
new SkipTaskWithNoActionsExecuter(
new SkipEmptySourceFilesTaskExecuter(
taskInputsListener,
new ValidatingTaskExecuter(
new SkipUpToDateTaskExecuter(
repository,
createSkipCachedExecuterIfNecessary(
startParameter,
gradle.getTaskCaching(),
packer,
new PostExecutionAnalysisTaskExecuter(
new ExecuteActionsTaskExecuter(
listenerManager.getBroadcaster(TaskActionListener.class)
new ResolveTaskArtifactStateTaskExecuter(
repository,
new SkipEmptySourceFilesTaskExecuter(
taskInputsListener,
new ValidatingTaskExecuter(
new SkipUpToDateTaskExecuter(
createSkipCachedExecuterIfNecessary(
startParameter,
gradle.getTaskCaching(),
packer,
new PostExecutionAnalysisTaskExecuter(
new ExecuteActionsTaskExecuter(
listenerManager.getBroadcaster(TaskActionListener.class)
)
)
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2016 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.api.internal.changedetection.changes

import org.gradle.api.file.FileCollection
import spock.lang.Specification
import spock.lang.Subject

@Subject(NoHistoryArtifactState)
class NoHistoryArtifactStateTest extends Specification {

def 'outputFiles singleton works as expected'() {
when: 'a NoHistoryArtifactState is created'
NoHistoryArtifactState instance = new NoHistoryArtifactState()

then: 'retrieving its outputFiles returns a value'
FileCollection outputFiles = instance.outputFiles
outputFiles != null

and: 'deserialized outputFiles all point to the same instance'
outputFiles.is(writeAndRead(outputFiles))

and: 'deserialized outputFiles.files Set<File> points to the same instance'
Set<File> fileSet = outputFiles.files
fileSet.is(writeAndRead(fileSet))

and: 'that instance is actually the immutable Collections.emptySet() singleton'
fileSet.is(Collections.emptySet())
}

private static <T> T writeAndRead(T instance) {
ByteArrayOutputStream bos = new ByteArrayOutputStream()
ObjectOutputStream oos = new ObjectOutputStream(bos)
oos.writeObject(instance)
oos.close()
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray())
ObjectInputStream ois = new ObjectInputStream(bis)
return (T) ois.readObject()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2014 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.api.internal.tasks.execution

import org.gradle.api.Action
import org.gradle.api.Task
import org.gradle.api.internal.TaskInternal
import org.gradle.api.internal.TaskOutputsInternal
import org.gradle.api.internal.changedetection.TaskArtifactState
import org.gradle.api.internal.changedetection.TaskArtifactStateRepository
import org.gradle.api.internal.tasks.TaskExecuter
import org.gradle.api.internal.tasks.TaskExecutionContext
import org.gradle.api.internal.tasks.TaskStateInternal
import spock.lang.Specification
import spock.lang.Subject

@Subject(ResolveTaskArtifactStateTaskExecuter)
public class ResolveTaskArtifactStateTaskExecuterTest extends Specification {
final delegate = Mock(TaskExecuter)
final outputs = Mock(TaskOutputsInternal)
final task = Mock(TaskInternal)
final taskState = Mock(TaskStateInternal)
final taskContext = Mock(TaskExecutionContext)
final repository = Mock(TaskArtifactStateRepository)
final taskArtifactState = Mock(TaskArtifactState)
final Action<Task> action = Mock(Action)

final executer = new ResolveTaskArtifactStateTaskExecuter(repository, delegate)

def 'taskContext is initialized and cleaned as expected'() {
when:
executer.execute(task, taskState, taskContext);

then: 'taskContext is initialized with task artifact state'
1 * repository.getStateFor(task) >> taskArtifactState
1 * taskContext.setTaskArtifactState(taskArtifactState)

then: 'delegate is executed'
1 * delegate.execute(task, taskState, taskContext)

then: 'task artifact state is removed from taskContext'
1 * taskContext.setTaskArtifactState(null)

and: 'nothing else'
0 * _
}
}
Loading