diff --git a/cwltool/command_line_tool.py b/cwltool/command_line_tool.py index eb7bf9e23..ec478fafe 100644 --- a/cwltool/command_line_tool.py +++ b/cwltool/command_line_tool.py @@ -611,10 +611,16 @@ def _initialworkdir(self, j: Optional[JobBase], builder: Builder) -> None: continue et: CWLObjectType = {} + writable = t.get("writable", False) + et["writable"] = writable if isinstance(entry, Mapping) and entry.get("class") in ( "File", "Directory", ): + if writable and "secondaryFiles" in entry: + secFiles = cast(MutableSequence[CWLObjectType], entry["secondaryFiles"]) + for sf in secFiles: + sf["writable"] = writable et["entry"] = cast(CWLOutputType, entry) else: if isinstance(entry, str): @@ -650,7 +656,6 @@ def _initialworkdir(self, j: Optional[JobBase], builder: Builder) -> None: et["entryname"] = entryname_field else: et["entryname"] = None - et["writable"] = t.get("writable", False) ls.append(et) else: # Expression, must return a Dirent, File, Directory @@ -700,13 +705,14 @@ def _initialworkdir(self, j: Optional[JobBase], builder: Builder) -> None: ) if t2.get("entryname") or t2.get("writable"): - t2 = copy.deepcopy(t2) - t2entry = cast(CWLObjectType, t2["entry"]) + t2copy = copy.deepcopy(t2) + t2entry = cast(CWLObjectType, t2copy["entry"]) if t2.get("entryname"): - t2entry["basename"] = t2["entryname"] - t2entry["writable"] = t2.get("writable") + t2entry["basename"] = t2copy["entryname"] + t2entry["writable"] = t2copy.get("writable") + t2["entry"] = t2entry - ls[i] = cast(CWLObjectType, t2["entry"]) + ls[i] = t2["entry"] for i, t3 in enumerate(ls): if t3.get("class") not in ("File", "Directory"): @@ -753,14 +759,21 @@ def _initialworkdir(self, j: Optional[JobBase], builder: Builder) -> None: for entry in ls: if "basename" in entry: basename = cast(str, entry["basename"]) - entry["dirname"] = os.path.join(builder.outdir, os.path.dirname(basename)) + dirname = os.path.join(builder.outdir, os.path.dirname(basename)) + entry["dirname"] = dirname entry["basename"] = os.path.basename(basename) + if "secondaryFiles" in entry: + for sec_file in cast( + MutableSequence[CWLObjectType], entry["secondaryFiles"] + ): + sec_file["dirname"] = dirname normalizeFilesDirs(entry) self.updatePathmap( cast(Optional[str], entry.get("dirname")) or builder.outdir, cast(PathMapper, builder.pathmapper), entry, ) + if "listing" in entry: def remove_dirname(d: CWLObjectType) -> None: diff --git a/cwltool/pathmapper.py b/cwltool/pathmapper.py index 774414f0a..d09641fd8 100644 --- a/cwltool/pathmapper.py +++ b/cwltool/pathmapper.py @@ -111,13 +111,13 @@ def visit( copy: bool = False, staged: bool = False, ) -> None: + if obj["location"] in self._pathmap: + return stagedir = cast(Optional[str], obj.get("dirname")) or stagedir tgt = os.path.join( stagedir, cast(str, obj["basename"]), ) - if obj["location"] in self._pathmap: - return if obj["class"] == "Directory": location = cast(str, obj["location"]) if location.startswith("file://"): diff --git a/tests/iwdr_writable_secfile-inputs.json b/tests/iwdr_writable_secfile-inputs.json new file mode 100644 index 000000000..f49270436 --- /dev/null +++ b/tests/iwdr_writable_secfile-inputs.json @@ -0,0 +1,12 @@ +{ + "example_file_with_secondary": { + "class": "File", + "location": "2.fasta", + "secondaryFiles": [ + { + "class": "File", + "location": "2.fastq" + } + ] + } +} diff --git a/tests/iwdr_writable_secfile.cwl b/tests/iwdr_writable_secfile.cwl new file mode 100644 index 000000000..98e72eac4 --- /dev/null +++ b/tests/iwdr_writable_secfile.cwl @@ -0,0 +1,23 @@ +cwlVersion: v1.2 +class: CommandLineTool + +requirements: + InlineJavascriptRequirement: {} + InitialWorkDirRequirement: + listing: + - entry: $(inputs.example_file_with_secondary) + writable: true +inputs: + example_file_with_secondary: + type: File + secondaryFiles: + - pattern: "^.fastq" + required: true +outputs: + same_file: + type: stdout + +baseCommand: cat +arguments: + - $(inputs.example_file_with_secondary.path) + - $(inputs.example_file_with_secondary.path.split('.')[0]+'.fastq') diff --git a/tests/test_iwdr.py b/tests/test_iwdr.py index 8d4138cb7..52da1fb5b 100644 --- a/tests/test_iwdr.py +++ b/tests/test_iwdr.py @@ -376,3 +376,16 @@ def test_iwdr_permutations_singularity_inplace( ) == 0 ) + + +def test_iwdr_writable_secondaryfiles(tmp_path: Path) -> None: + """Confirm that a writable primary file and its secondary files are still co-located.""" + err_code, _, _ = get_main_output( + [ + "--outdir", + str(tmp_path), + get_data("tests/iwdr_writable_secfile.cwl"), + get_data("tests/iwdr_writable_secfile-inputs.json"), + ] + ) + assert err_code == 0