Repository: common-workflow-language/cwltool

Issue:
Cannot run a nested workflow step using `--single-process`
Given the following workflow, I expect to be able to run `stepY` of the nested workflow using `--single-process step1/stepX/stepY` but this does not work.

```
I'm sorry, I couldn't load this CWL file, try again with --debug for more information.
The error was: Step file:///Users/jdidion/projects/dxCompiler/test/cwl_conformance/workflows/count-lines17-wf.cwl.json#count-lines17-wf/step1/stepX/stepY was not found
```

Additionally, the output of `--print-targets` appears to be wrong:

```
INFO  Output targets:
  count_output
INFO  Input targets:
  file1
INFO  steps targets:
  step1
INFO step1 Output targets:
  step1/count_output
INFO step1 Input targets:
  step1/file1
INFO step1 steps targets:
  step1/stepX
INFO stepX Output targets:
  stepX/wc_output
INFO stepX Input targets:
  stepX/file1
INFO stepX steps targets:
  stepX/stepY
INFO stepY Output targets:
  stepY/output
INFO stepY Input targets:
  stepY/file1
  step1/stepZ
INFO stepZ Output targets:
  stepZ/output
INFO stepZ Input targets:
  stepZ/file1
```

FYI this is count-lines17-wf.cwl conformance test packed with `cwlpack --add-ids --json`, but I have modified the step names to make it a bit less confusing to debug.

```json
{
  "class": "Workflow",
  "cwlVersion": "v1.2",
  "id": "count-lines17-wf",
  "inputs": [
    {
      "id": "file1",
      "type": "File"
    }
  ],
  "outputs": [
    {
      "id": "count_output",
      "outputSource": "step1/count_output",
      "type": "int"
    }
  ],
  "requirements": [
    {
      "class": "SubworkflowFeatureRequirement"
    },
    {
      "class": "InlineJavascriptRequirement"
    }
  ],
  "steps": [
    {
      "id": "step1",
      "in": [
        {
          "id": "file1",
          "source": "file1"
        }
      ],
      "out": [
        "count_output"
      ],
      "run": {
        "class": "Workflow",
        "id": "count-lines17-wf.cwl@step_step1@run",
        "inputs": [
          {
            "id": "file1",
            "type": "File"
          }
        ],
        "outputs": [
          {
            "id": "count_output",
            "outputSource": "stepZ/output",
            "type": "int"
          }
        ],
        "requirements": [
          {
            "class": "SubworkflowFeatureRequirement"
          },
          {
            "class": "InlineJavascriptRequirement"
          }
        ],
        "steps": [
          {
            "id": "stepX",
            "in": [
              {
                "id": "file1",
                "source": "file1"
              }
            ],
            "out": [
              "wc_output"
            ],
            "run": {
              "class": "Workflow",
              "id": "count-lines17-wf.cwl@step_step1@run@step_stepX@run",
              "inputs": [
                {
                  "id": "file1",
                  "type": "File"
                }
              ],
              "outputs": [
                {
                  "id": "wc_output",
                  "outputSource": "stepY/output",
                  "type": "File"
                }
              ],
              "requirements": [
                {
                  "class": "SubworkflowFeatureRequirement"
                },
                {
                  "class": "InlineJavascriptRequirement"
                }
              ],
              "steps": [
                {
                  "id": "stepY",
                  "in": [
                    {
                      "id": "file1",
                      "source": "file1"
                    }
                  ],
                  "out": [
                    "output"
                  ],
                  "run": {
                    "baseCommand": [
                      "wc",
                      "-l"
                    ],
                    "class": "CommandLineTool",
                    "id": "count-lines17-wf.cwl@step_step1@run@step_stepX@run@step_stepY@run",
                    "inputs": [
                      {
                        "id": "file1",
                        "type": "File"
                      }
                    ],
                    "outputs": [
                      {
                        "id": "output",
                        "outputBinding": {
                          "glob": "output"
                        },
                        "type": "File"
                      }
                    ],
                    "requirements": [
                      {
                        "class": "InlineJavascriptRequirement"
                      }
                    ],
                    "stdin": "$(inputs.file1.path)",
                    "stdout": "output"
                  }
                }
              ]
            }
          },
          {
            "id": "stepZ",
            "in": [
              {
                "id": "file1",
                "source": "stepX/wc_output"
              }
            ],
            "out": [
              "output"
            ],
            "run": {
              "class": "ExpressionTool",
              "expression": "$({'output': parseInt(inputs.file1.contents)})",
              "id": "count-lines17-wf.cwl@step_step1@run@step_stepZ@run",
              "inputs": [
                {
                  "id": "file1",
                  "loadContents": true,
                  "type": "File"
                }
              ],
              "outputs": [
                {
                  "id": "output",
                  "type": "int"
                }
              ],
              "requirements": [
                {
                  "class": "InlineJavascriptRequirement"
                }
              ]
            }
          }
        ]
      }
    }
  ]
}
```


Discussion:

Resolving patch:
diff --git a/cwltool/main.py b/cwltool/main.py
--- a/cwltool/main.py
+++ b/cwltool/main.py
@@ -959,7 +959,9 @@ def print_targets(
                 process = make_tool(cast(CommentedMap, cmap(run)), loading_context)
             else:
                 process = run
-            print_targets(process, stdout, loading_context, shortname(t["id"]) + "/")
+            print_targets(
+                process, stdout, loading_context, f"{prefix}{shortname(t['id'])}/"
+            )
 
 
 def main(
diff --git a/cwltool/subgraph.py b/cwltool/subgraph.py
--- a/cwltool/subgraph.py
+++ b/cwltool/subgraph.py
@@ -17,7 +17,7 @@
 from ruamel.yaml.comments import CommentedMap, CommentedSeq
 
 from .context import LoadingContext
-from .load_tool import load_tool
+from .load_tool import load_tool, make_tool
 from .process import Process
 from .utils import CWLObjectType, aslist
 from .workflow import Workflow, WorkflowStep
@@ -64,18 +64,19 @@ def find_step(
 ) -> Tuple[Optional[CWLObjectType], Optional[WorkflowStep]]:
     """Find the step (raw dictionary and WorkflowStep) for a given step id."""
     for st in steps:
-        if st.tool["id"] == stepid:
+        st_tool_id = st.tool["id"]
+        if st_tool_id == stepid:
             return st.tool, st
-        if stepid.startswith(st.tool["id"]):
-            run: Union[str, Process] = st.tool["run"]
+        if stepid.startswith(st_tool_id):
+            run: Union[str, Process, CWLObjectType] = st.tool["run"]
             if isinstance(run, Workflow):
                 result, st2 = find_step(
                     run.steps, stepid[len(st.tool["id"]) + 1 :], loading_context
                 )
                 if result:
                     return result, st2
-            elif isinstance(run, str):
-                process = load_tool(run, loading_context)
+            elif isinstance(run, CommentedMap) and run["class"] == "Workflow":
+                process = make_tool(run, loading_context)
                 if isinstance(process, Workflow):
                     suffix = stepid[len(st.tool["id"]) + 1 :]
                     prefix = process.tool["id"]
@@ -84,9 +85,26 @@ def find_step(
                     else:
                         sep = "#"
                     adj_stepid = f"{prefix}{sep}{suffix}"
-                    result2, st3 = find_step(process.steps, adj_stepid, loading_context)
+                    result2, st3 = find_step(
+                        process.steps,
+                        adj_stepid,
+                        loading_context,
+                    )
                     if result2:
                         return result2, st3
+            elif isinstance(run, str):
+                process = load_tool(run, loading_context)
+                if isinstance(process, Workflow):
+                    suffix = stepid[len(st.tool["id"]) + 1 :]
+                    prefix = process.tool["id"]
+                    if "#" in prefix:
+                        sep = "/"
+                    else:
+                        sep = "#"
+                    adj_stepid = f"{prefix}{sep}{suffix}"
+                    result3, st4 = find_step(process.steps, adj_stepid, loading_context)
+                    if result3:
+                        return result3, st4
     return None, None
 
 


Test patch:
diff --git a/tests/subgraph/count-lines17-wf.cwl.json b/tests/subgraph/count-lines17-wf.cwl.json
new file mode 100644
--- /dev/null
+++ b/tests/subgraph/count-lines17-wf.cwl.json
@@ -0,0 +1,183 @@
+{
+  "class": "Workflow",
+  "cwlVersion": "v1.2",
+  "id": "count-lines17-wf",
+  "inputs": [
+    {
+      "id": "file1",
+      "type": "File"
+    }
+  ],
+  "outputs": [
+    {
+      "id": "count_output",
+      "outputSource": "step1/count_output",
+      "type": "int"
+    }
+  ],
+  "requirements": [
+    {
+      "class": "SubworkflowFeatureRequirement"
+    },
+    {
+      "class": "InlineJavascriptRequirement"
+    }
+  ],
+  "steps": [
+    {
+      "id": "step1",
+      "in": [
+        {
+          "id": "file1",
+          "source": "file1"
+        }
+      ],
+      "out": [
+        "count_output"
+      ],
+      "run": {
+        "class": "Workflow",
+        "id": "count-lines17-wf.cwl@step_step1@run",
+        "inputs": [
+          {
+            "id": "file1",
+            "type": "File"
+          }
+        ],
+        "outputs": [
+          {
+            "id": "count_output",
+            "outputSource": "stepZ/output",
+            "type": "int"
+          }
+        ],
+        "requirements": [
+          {
+            "class": "SubworkflowFeatureRequirement"
+          },
+          {
+            "class": "InlineJavascriptRequirement"
+          }
+        ],
+        "steps": [
+          {
+            "id": "stepX",
+            "in": [
+              {
+                "id": "file1",
+                "source": "file1"
+              }
+            ],
+            "out": [
+              "wc_output"
+            ],
+            "run": {
+              "class": "Workflow",
+              "id": "count-lines17-wf.cwl@step_step1@run@step_stepX@run",
+              "inputs": [
+                {
+                  "id": "file1",
+                  "type": "File"
+                }
+              ],
+              "outputs": [
+                {
+                  "id": "wc_output",
+                  "outputSource": "stepY/output",
+                  "type": "File"
+                }
+              ],
+              "requirements": [
+                {
+                  "class": "SubworkflowFeatureRequirement"
+                },
+                {
+                  "class": "InlineJavascriptRequirement"
+                }
+              ],
+              "steps": [
+                {
+                  "id": "stepY",
+                  "in": [
+                    {
+                      "id": "file1",
+                      "source": "file1"
+                    }
+                  ],
+                  "out": [
+                    "output"
+                  ],
+                  "run": {
+                    "baseCommand": [
+                      "wc",
+                      "-l"
+                    ],
+                    "class": "CommandLineTool",
+                    "id": "count-lines17-wf.cwl@step_step1@run@step_stepX@run@step_stepY@run",
+                    "inputs": [
+                      {
+                        "id": "file1",
+                        "type": "File"
+                      }
+                    ],
+                    "outputs": [
+                      {
+                        "id": "output",
+                        "outputBinding": {
+                          "glob": "output"
+                        },
+                        "type": "File"
+                      }
+                    ],
+                    "requirements": [
+                      {
+                        "class": "InlineJavascriptRequirement"
+                      }
+                    ],
+                    "stdin": "$(inputs.file1.path)",
+                    "stdout": "output"
+                  }
+                }
+              ]
+            }
+          },
+          {
+            "id": "stepZ",
+            "in": [
+              {
+                "id": "file1",
+                "source": "stepX/wc_output"
+              }
+            ],
+            "out": [
+              "output"
+            ],
+            "run": {
+              "class": "ExpressionTool",
+              "expression": "$({'output': parseInt(inputs.file1.contents)})",
+              "id": "count-lines17-wf.cwl@step_step1@run@step_stepZ@run",
+              "inputs": [
+                {
+                  "id": "file1",
+                  "loadContents": true,
+                  "type": "File"
+                }
+              ],
+              "outputs": [
+                {
+                  "id": "output",
+                  "type": "int"
+                }
+              ],
+              "requirements": [
+                {
+                  "class": "InlineJavascriptRequirement"
+                }
+              ]
+            }
+          }
+        ]
+      }
+    }
+  ]
+}
diff --git a/tests/test_subgraph.py b/tests/test_subgraph.py
--- a/tests/test_subgraph.py
+++ b/tests/test_subgraph.py
@@ -224,6 +224,23 @@ def test_single_process_packed_subwf_step() -> None:
     )
 
 
+def test_single_process_subwf_subwf_inline_step() -> None:
+    """Test --single-process on an inline sub-sub-workflow step."""
+    err_code, stdout, stderr = get_main_output(
+        [
+            "--single-process",
+            "step1/stepX/stepY",
+            get_data("tests/subgraph/count-lines17-wf.cwl.json"),
+            get_data("tests/wf/wc-job.json"),
+        ]
+    )
+    assert err_code == 0
+    assert (
+        json.loads(stdout)["output"]["checksum"]
+        == "sha1$3596ea087bfdaf52380eae441077572ed289d657"
+    )
+
+
 def test_single_step_subwf_step() -> None:
     """Inherit reqs and hints --single-step on sub-workflow step."""
     err_code, stdout, stderr = get_main_output(
@@ -308,3 +325,15 @@ def test_print_targets_embedded_reqsinherit() -> None:
         ]
     )
     assert err_code == 0
+
+
+def test_print_targets_embedded_sub_subwfs() -> None:
+    """Confirm that --print-targets works with inline sub-sub-workflows."""
+    err_code, stdout, stderr = get_main_output(
+        [
+            "--print-targets",
+            get_data("tests/subgraph/count-lines17-wf.cwl.json"),
+        ]
+    )
+    assert err_code == 0
+    assert "step1/stepX/stepY" in stdout