Repository: common-workflow-language/cwltool

Issue:
An execution of a packed workflow fails
## Expected Behavior
cwltool executes a workflow after packing exactly the same way as a normal workflow.

## Actual Behavior
Workflow fails

## Steps to reproduce
```
cd /path/to/cwltool
cwltool --pack cwltool/schemas/v1.0/v1.0/count-lines6-wf.cwl > cwltool/schemas/v1.0/v1.0/count-lines6-wf-packed.cwl
cwltool  cwltool/schemas/v1.0/v1.0/count-lines6-wf-packed.cwl  cwltool/schemas/v1.0/v1.0/count-lines6-job.json
```

## Full Traceback
```
Resolved 'v1.0/v1.0/count-lines6-wf-packed.cwl' to 'file:///Users/anton/Programming/CWL/cwltool/cwltool/schemas/v1.0/v1.0/count-lines6-wf-packed.cwl'
Tool definition failed initialization:
Tool file contains graph of multiple objects, must specify one of #main, #wc3-tool.cwl
Traceback (most recent call last):
  File "/Users/anton/Programming/CWL/cwltool/cwltool/main.py", line 896, in main
    makeTool, make_tool_kwds)
  File "/Users/anton/Programming/CWL/cwltool/cwltool/load_tool.py", line 291, in make_tool
    urllib.parse.urldefrag(i["id"])[1] for i in resolveduri
cwltool.errors.WorkflowException: Tool file contains graph of multiple objects, must specify one of #main, #wc3-tool.cwl
```

## Your Environment
* cwltool version: 
1.0.20171107131604



Discussion:
If should run #main by default.  It used to do that and then was pulled out because it wasn't to spec, but I think it should do that and the spec should be updated.

Resolving patch:
diff --git a/cwltool/load_tool.py b/cwltool/load_tool.py
--- a/cwltool/load_tool.py
+++ b/cwltool/load_tool.py
@@ -281,10 +281,13 @@ def make_tool(document_loader,  # type: Loader
     """Make a Python CWL object."""
     resolveduri = document_loader.resolve_ref(uri)[0]
 
+    processobj = None
     if isinstance(resolveduri, list):
-        if len(resolveduri) == 1:
-            processobj = resolveduri[0]
-        else:
+        for obj in resolveduri:
+            if obj['id'].endswith('#main'):
+                processobj = obj
+                break
+        if not processobj:
             raise WorkflowException(
                 u"Tool file contains graph of multiple objects, must specify "
                 "one of #%s" % ", #".join(


Test patch:
diff --git a/tests/test_pack.py b/tests/test_pack.py
--- a/tests/test_pack.py
+++ b/tests/test_pack.py
@@ -1,22 +1,27 @@
 from __future__ import absolute_import
+
 import json
-import os
 import unittest
+
+import os
 from functools import partial
 
+import pytest
+from six import StringIO
+
 import cwltool.pack
-from cwltool.main import print_pack as print_pack
 import cwltool.workflow
 from cwltool.load_tool import fetch_document, validate_document
-from cwltool.main import makeRelative
+from cwltool.main import makeRelative, main, print_pack
 from cwltool.pathmapper import adjustDirObjs, adjustFileObjs
-
+from cwltool.utils import onWindows
 from .util import get_data
 
 
 class TestPack(unittest.TestCase):
+    maxDiff = None
+
     def test_pack(self):
-        self.maxDiff = None
 
         document_loader, workflowobj, uri = fetch_document(
             get_data("tests/wf/revsort.cwl"))
@@ -38,8 +43,6 @@ def test_pack(self):
     def test_pack_missing_cwlVersion(self):
         """Test to ensure the generated pack output is not missing
         the `cwlVersion` in case of single tool workflow and single step workflow"""
-        # Since diff is longer than 3174 characters
-        self.maxDiff = None
 
         # Testing single tool workflow
         document_loader, workflowobj, uri = fetch_document(
@@ -76,7 +79,6 @@ def test_pack_idempotence_workflow(self):
         self._pack_idempotently("tests/wf/count-lines1-wf.cwl")
 
     def _pack_idempotently(self, document):
-        self.maxDiff = None
         document_loader, workflowobj, uri = fetch_document(
             get_data(document))
         document_loader, avsc_names, processobj, metadata, uri = validate_document(
@@ -89,3 +91,28 @@ def _pack_idempotently(self, document):
             document_loader, workflowobj, uri)
         double_packed = json.loads(print_pack(document_loader, processobj, uri2, metadata))
         self.assertEqual(packed, double_packed)
+
+    @pytest.mark.skipif(onWindows(),
+                        reason="Instance of cwltool is used, on Windows it invokes a default docker container"
+                               "which is not supported on AppVeyor")
+    def test_packed_workflow_execution(self):
+        test_wf = "tests/wf/count-lines1-wf.cwl"
+        test_wf_job = "tests/wf/wc-job.json"
+        document_loader, workflowobj, uri = fetch_document(
+            get_data(test_wf))
+        document_loader, avsc_names, processobj, metadata, uri = validate_document(
+            document_loader, workflowobj, uri)
+        packed = json.loads(print_pack(document_loader, processobj, uri, metadata))
+        temp_packed_path = "/tmp/packedwf"
+        with open(temp_packed_path, 'w') as f:
+            json.dump(packed, f)
+        normal_output = StringIO()
+        packed_output = StringIO()
+        self.assertEquals(main(['--debug', get_data(temp_packed_path),
+                                get_data(test_wf_job)],
+                               stdout=packed_output), 0)
+        self.assertEquals(main([get_data(test_wf),
+                                get_data(test_wf_job)],
+                               stdout=normal_output), 0)
+        self.assertEquals(json.loads(packed_output.getvalue()), json.loads(normal_output.getvalue()))
+
diff --git a/tests/wf/wc-job.json b/tests/wf/wc-job.json
new file mode 100644
--- /dev/null
+++ b/tests/wf/wc-job.json
@@ -0,0 +1,6 @@
+{
+    "file1": {
+        "class": "File",
+        "location": "whale.txt"
+    }
+}
diff --git a/tests/wf/whale.txt b/tests/wf/whale.txt
new file mode 100644
--- /dev/null
+++ b/tests/wf/whale.txt
@@ -0,0 +1,16 @@
+Call me Ishmael. Some years ago--never mind how long precisely--having
+little or no money in my purse, and nothing particular to interest me on
+shore, I thought I would sail about a little and see the watery part of
+the world. It is a way I have of driving off the spleen and regulating
+the circulation. Whenever I find myself growing grim about the mouth;
+whenever it is a damp, drizzly November in my soul; whenever I find
+myself involuntarily pausing before coffin warehouses, and bringing up
+the rear of every funeral I meet; and especially whenever my hypos get
+such an upper hand of me, that it requires a strong moral principle to
+prevent me from deliberately stepping into the street, and methodically
+knocking people's hats off--then, I account it high time to get to sea
+as soon as I can. This is my substitute for pistol and ball. With a
+philosophical flourish Cato throws himself upon his sword; I quietly
+take to the ship. There is nothing surprising in this. If they but knew
+it, almost all men in their degree, some time or other, cherish very
+nearly the same feelings towards the ocean with me.