Hello,
I am attempting to implement a task that follows the ExecSpec
, but rather than wait for the process to exit it should wait for a ready condition and proceed with build once ready. Ideally this process would remain active with the gradle daemon, baring that should cleanly exit at the end of the build.
I am most of the way there with this:
class DaemonExec extends AbstractExecTask<DaemonExec> implements ExecSpec {
private final DaemonExecActionFactory execActionFactory = objectFactory.newInstance(DaemonExecActionFactory)
private final DaemonExecAction execAction = execActionFactory.newDecoratedDaemonExecAction()
private final Property<ExecResult> execResult = objectFactory.property(ExecResult)
public DaemonExec() {
super(DaemonExec)
setExecAction(execAction)
}
@Inject
protected ObjectFactory getObjectFactory() {
throw new UnsupportedOperationException()
}
void processReadyWhen(Closure<Boolean> closure) {
execAction.readyWhen(closure)
}
@TaskAction
protected void exec() {
execAction.setExecutable(this.getExecutable())
execAction.setArgs(this.getArgs())
execAction.setWorkingDir(this.getWorkingDir())
execAction.setEnvironment(this.getEnvironment())
execResult.set(execAction.execute())
}
}
class DefaultDaemonExecAction extends DefaultExecHandleBuilder implements ExecAction, DaemonExecAction {
private static ExecResult waitForReady(final ExecHandle execHandle, final Closure<Boolean> readyCondition = null) {
if(readyCondition) {
final ReentrantLock lock = new ReentrantLock()
final Condition stateChanged = lock.newCondition()
lock.lock()
try {
//TODO stream stdout to readyCondition, unlock when returns true
while (!execHandle.getState().isTerminal()) {
try {
stateChanged.await()
} catch (InterruptedException e) {
execHandle.abort()
throw UncheckedException.throwAsUncheckedException(e)
}
}
} finally {
lock.unlock()
}
//TODO we need to figure out how to return a valid
// ExecResult which has not yet exited
}
return execHandle.start().waitForFinish()
}
private Closure<Boolean> readyCondition = null
@Inject
DefaultDaemonExecAction(PathToFileResolver fileResolver, Executor executor, BuildCancellationToken buildCancellationToken) {
super(fileResolver, executor, buildCancellationToken)
}
@Override
public ExecResult execute() {
final ExecHandle execHandle = build()
final ExecResult execResult = waitForReady(execHandle, readyCondition)
// TODO defer assertion until process exits
if (!isIgnoreExitValue()) {
execResult.assertNormalExitValue()
}
return execResult
}
@Override
void readyWhen(Closure<Boolean> closure) {
readyCondition = closure
}
}
So far so good, this works just like Exec
when no readyCondition
is used. The remaining implementation is uninteresting, but can be found at [1] if interested.
What is unclear to me is how I should go about returning an ExecResult
from waitForReady
, which has not yet exited. Is this even possible? I was hoping that by implementing the task in the same fashion as Exec
I could piggyback off the gradle daemon to handle the process thread.
Any ideas how to achieve this, or am I just opening up a bunch of footguns?
Cheers, and thanks for your time!
~Aaron
[1] - Full Source