3535ACCEPTLIST_EN_RELAXED_RE = re .compile (r".*" ) # Accept anything
3636ACCEPTLIST_RE = ACCEPTLIST_EN_STRICT_RE
3737DEFAULT_CONTAINER_MSG = """We are on Microsoft Windows and not all components of this CWL description have a
38- container specified. This means that these steps will be executed in the default container,
38+ container specified. This means that these steps will be executed in the default container,
3939which is %s.
4040
4141Note, this could affect portability if this CWL description relies on non-POSIX features
@@ -116,17 +116,26 @@ def revmap_file(builder, outdir, f):
116116 if not split .scheme :
117117 outdir = file_uri (str (outdir ))
118118
119+ # builder.outdir is the inner (container/compute node) output directory
120+ # outdir is the outer (host/storage system) output directory
121+
119122 if "location" in f :
120123 if f ["location" ].startswith ("file://" ):
121124 path = convert_pathsep_to_unix (uri_file_path (f ["location" ]))
122125 revmap_f = builder .pathmapper .reversemap (path )
126+
123127 if revmap_f and not builder .pathmapper .mapper (revmap_f [0 ]).type .startswith ("Writable" ):
124128 f ["basename" ] = os .path .basename (path )
125- f ["location" ] = revmap_f [0 ]
129+ f ["location" ] = revmap_f [1 ]
126130 elif path == builder .outdir :
127131 f ["location" ] = outdir
128132 elif path .startswith (builder .outdir ):
129133 f ["location" ] = builder .fs_access .join (outdir , path [len (builder .outdir ) + 1 :])
134+ elif f ["location" ].startswith (outdir ):
135+ revmap_f = builder .pathmapper .reversemap (builder .fs_access .join (builder .outdir , f ["location" ][len (outdir ) + 1 :]))
136+ if revmap_f and not builder .pathmapper .mapper (revmap_f [0 ]).type .startswith ("Writable" ):
137+ f ["basename" ] = os .path .basename (path )
138+ f ["location" ] = revmap_f [1 ]
130139 return f
131140
132141 if "path" in f :
@@ -190,7 +199,7 @@ def __init__(self, toolpath_object, **kwargs):
190199 super (CommandLineTool , self ).__init__ (toolpath_object , ** kwargs )
191200 self .find_default_container = kwargs .get ("find_default_container" , None )
192201
193- def makeJobRunner (self , use_container = True ): # type: (Optional[bool]) -> JobBase
202+ def makeJobRunner (self , use_container = True , ** kwargs ): # type: (Optional[bool], **Any ) -> JobBase
194203 dockerReq , _ = self .get_requirement ("DockerRequirement" )
195204 if not dockerReq and use_container :
196205 if self .find_default_container :
@@ -216,7 +225,8 @@ def makeJobRunner(self, use_container=True): # type: (Optional[bool]) -> JobBas
216225
217226 def makePathMapper (self , reffiles , stagedir , ** kwargs ):
218227 # type: (List[Any], Text, **Any) -> PathMapper
219- return PathMapper (reffiles , kwargs ["basedir" ], stagedir )
228+ return PathMapper (reffiles , kwargs ["basedir" ], stagedir ,
229+ separateDirs = kwargs .get ("separateDirs" , True ))
220230
221231 def updatePathmap (self , outdir , pathmap , fn ):
222232 # type: (Text, PathMapper, Dict) -> None
@@ -325,9 +335,10 @@ def rm_pending_output_callback(output_callbacks, jobcachepending,
325335
326336 reffiles = copy .deepcopy (builder .files )
327337
328- j = self .makeJobRunner (kwargs . get ( "use_container" ) )
338+ j = self .makeJobRunner (** kwargs )
329339 j .builder = builder
330340 j .joborder = builder .job
341+ j .make_pathmapper = self .makePathMapper
331342 j .stdin = None
332343 j .stderr = None
333344 j .stdout = None
@@ -350,6 +361,7 @@ def rm_pending_output_callback(output_callbacks, jobcachepending,
350361 if "stagedir" in make_path_mapper_kwargs :
351362 make_path_mapper_kwargs = make_path_mapper_kwargs .copy ()
352363 del make_path_mapper_kwargs ["stagedir" ]
364+
353365 builder .pathmapper = self .makePathMapper (reffiles , builder .stagedir , ** make_path_mapper_kwargs )
354366 builder .requirements = j .requirements
355367
@@ -566,7 +578,12 @@ def collect_output(self, schema, builder, outdir, fs_access, compute_checksum=Tr
566578 elif gb .startswith ("/" ):
567579 raise WorkflowException ("glob patterns must not start with '/'" )
568580 try :
581+ prefix = fs_access .glob (outdir )
569582 r .extend ([{"location" : g ,
583+ "path" : fs_access .join (builder .outdir , g [len (prefix [0 ])+ 1 :]),
584+ "basename" : os .path .basename (g ),
585+ "nameroot" : os .path .splitext (os .path .basename (g ))[0 ],
586+ "nameext" : os .path .splitext (os .path .basename (g ))[1 ],
570587 "class" : "File" if fs_access .isfile (g ) else "Directory" }
571588 for g in fs_access .glob (fs_access .join (outdir , gb ))])
572589 except (OSError , IOError ) as e :
@@ -576,12 +593,14 @@ def collect_output(self, schema, builder, outdir, fs_access, compute_checksum=Tr
576593 raise
577594
578595 for files in r :
596+ rfile = files .copy ()
597+ revmap (rfile )
579598 if files ["class" ] == "Directory" :
580599 ll = builder .loadListing or (binding and binding .get ("loadListing" ))
581600 if ll and ll != "no_listing" :
582601 get_listing (fs_access , files , (ll == "deep_listing" ))
583602 else :
584- with fs_access .open (files ["location" ], "rb" ) as f :
603+ with fs_access .open (rfile ["location" ], "rb" ) as f :
585604 contents = b""
586605 if binding .get ("loadContents" ) or compute_checksum :
587606 contents = f .read (CONTENT_LIMIT )
@@ -625,28 +644,39 @@ def collect_output(self, schema, builder, outdir, fs_access, compute_checksum=Tr
625644 else :
626645 r = r [0 ]
627646
628- # Ensure files point to local references outside of the run environment
629- adjustFileObjs (r , cast ( # known bug in mypy
630- # https://github.com/python/mypy/issues/797
631- Callable [[Any ], Any ], revmap ))
632-
633647 if "secondaryFiles" in schema :
634648 with SourceLine (schema , "secondaryFiles" , WorkflowException ):
635649 for primary in aslist (r ):
636650 if isinstance (primary , dict ):
637- primary ["secondaryFiles" ] = []
651+ primary .setdefault ("secondaryFiles" , [])
652+ pathprefix = primary ["path" ][0 :primary ["path" ].rindex ("/" )+ 1 ]
638653 for sf in aslist (schema ["secondaryFiles" ]):
639654 if isinstance (sf , dict ) or "$(" in sf or "${" in sf :
640655 sfpath = builder .do_eval (sf , context = primary )
641- if isinstance (sfpath , string_types ):
642- sfpath = revmap ({"location" : sfpath , "class" : "File" })
656+ subst = False
643657 else :
644- sfpath = { "location" : substitute ( primary [ "location" ], sf ), "class" : "File" }
645-
658+ sfpath = sf
659+ subst = True
646660 for sfitem in aslist (sfpath ):
647- if fs_access .exists (sfitem ["location" ]):
661+ if isinstance (sfitem , string_types ):
662+ if subst :
663+ sfitem = {"path" : substitute (primary ["path" ], sfitem )}
664+ else :
665+ sfitem = {"path" : pathprefix + sfitem }
666+ if "path" in sfitem and "location" not in sfitem :
667+ revmap (sfitem )
668+ if fs_access .isfile (sfitem ["location" ]):
669+ sfitem ["class" ] = "File"
670+ primary ["secondaryFiles" ].append (sfitem )
671+ elif fs_access .isdir (sfitem ["location" ]):
672+ sfitem ["class" ] = "Directory"
648673 primary ["secondaryFiles" ].append (sfitem )
649674
675+ # Ensure files point to local references outside of the run environment
676+ adjustFileObjs (r , cast ( # known bug in mypy
677+ # https://github.com/python/mypy/issues/797
678+ Callable [[Any ], Any ], revmap ))
679+
650680 if not r and optional :
651681 r = None
652682
0 commit comments