Skip to content
Open
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 @@ -68,7 +68,8 @@ public Statement transform(ProcessNodeV1 node) {
args(
closureX(null, node.exec),
constX(sgh.getSourceText(node.exec)),
constX(node.type)
constX(node.type),
sgh.getVariableRefs(node.exec)
)
))
)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ public Statement transform(ProcessNodeV2 node) {
args(
closureX(node.exec),
constX(sgh.getSourceText(node.exec)),
constX(node.type)
constX(node.type),
sgh.getVariableRefs(node.exec)
)
))
)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
import org.codehaus.groovy.ast.Variable;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.control.SourceUnit;

import static nextflow.script.ast.ASTUtils.*;
import static org.codehaus.groovy.ast.tools.GeneralUtils.*;

/**
Expand All @@ -41,6 +43,76 @@ public ScriptToGroovyHelper(SourceUnit sourceUnit) {
this.sourceUnit = sourceUnit;
}

/**
* Get the list of variable references in a statement.
*
* This method is used to collect references to task ext
* properties (e.g. `task.ext.args`) in the process body, so that
* they are included in the task hash.
*
* These properties are typically used like inputs, but are not
* explicitly declared, so they must be identified by their usage.
*
* The resulting list expression should be provided as the fourth
* argument of the BodyDef constructor.
*
* @param node
*/
public Expression getVariableRefs(Statement node) {
var refs = new VariableRefCollector().collect(node).stream()
.map(name -> createX("nextflow.script.TokenValRef", constX(name)))
.toList();

return listX(refs);
}

private class VariableRefCollector extends CodeVisitorSupport {

private Set<String> variableRefs;

public Set<String> collect(Statement node) {
variableRefs = new HashSet<>();
visit(node);
return variableRefs;
}

@Override
public void visitPropertyExpression(PropertyExpression node) {
if( !isPropertyChain(node) ) {
super.visitPropertyExpression(node);
return;
}

var name = asPropertyChain(node);
if( name.startsWith("task.ext.") )
variableRefs.add(name);
}

private static boolean isPropertyChain(PropertyExpression node) {
var target = node.getObjectExpression();
while( target instanceof PropertyExpression pe )
target = pe.getObjectExpression();
return target instanceof VariableExpression;
}

private static String asPropertyChain(PropertyExpression node) {
var builder = new StringBuilder();
builder.append(node.getPropertyAsString());

var target = node.getObjectExpression();
while( target instanceof PropertyExpression pe ) {
builder.append('.');
builder.append(pe.getPropertyAsString());
target = pe.getObjectExpression();
}

builder.append('.');
builder.append(target.getText());

return builder.reverse().toString();
}
Comment on lines +98 to +113
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reverse in last statement is reversing the full stringbuilder getting something like ksat.txe.sgra. I think you wanted to just reverse the order of elements of a list of strings and join with . later.

Suggested change
private static String asPropertyChain(PropertyExpression node) {
var builder = new StringBuilder();
builder.append(node.getPropertyAsString());
var target = node.getObjectExpression();
while( target instanceof PropertyExpression pe ) {
builder.append('.');
builder.append(pe.getPropertyAsString());
target = pe.getObjectExpression();
}
builder.append('.');
builder.append(target.getText());
return builder.reverse().toString();
}
private static String asPropertyChain(PropertyExpression node) {
var list = new ArrayList<String>();
list.add(node.getPropertyAsString());
var target = node.getObjectExpression();
while( target instanceof PropertyExpression pe ) {
list.add(pe.getPropertyAsString());
target = pe.getObjectExpression();
}
list.add(target.getText());
Collections.reverse(list);
return String.join(".", list);
}

}

/**
* Transform an expression into a lazy expression by
* wrapping it in a closure if it references variables.
Expand Down
30 changes: 30 additions & 0 deletions tests/checks/task-ext.nf/.checks
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
set -e

#
# initial run
#
$NXF_RUN -c ../../task-ext.config --value foo | tee .stdout

[[ `grep INFO .nextflow.log | grep -c 'Submitted process'` == 1 ]] || false

grep 'args = --some-param foo' .stdout


#
# resumed run (same value)
#
$NXF_RUN -c ../../task-ext.config --value foo -resume | tee .stdout

[[ `grep INFO .nextflow.log | grep -c 'Cached process'` == 1 ]] || false

grep 'args = --some-param foo' .stdout


#
# resumed run (new value)
#
$NXF_RUN -c ../../task-ext.config --value bar -resume | tee .stdout

[[ `grep INFO .nextflow.log | grep -c 'Submitted process'` == 1 ]] || false

grep 'args = --some-param bar' .stdout
10 changes: 10 additions & 0 deletions tests/task-ext.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

params {
value = 'value'
}

process {
withName: 'HELLO' {
ext.args = "--some-param ${params.value}"
}
}
13 changes: 13 additions & 0 deletions tests/task-ext.nf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

process HELLO {
debug true

script:
"""
echo "args = ${task.ext.args}"
"""
}

workflow {
HELLO()
}
Loading