@@ -530,8 +530,9 @@ class _SlicesDirInputSpec(FSLCommandInputSpec):
530530 desc = 'output every second axial slice rather than just 9 ortho slices' ,
531531 )
532532
533+ # Copy in_files from the original location to the runtime.cwd
533534 in_files = InputMultiPath (
534- File (exists = True ),
535+ File (exists = True , copyfile = False ),
535536 argstr = '%s' ,
536537 mandatory = True ,
537538 position = - 1 ,
@@ -568,6 +569,58 @@ class SlicesDir(FSLCommand):
568569 _cmd = 'slicesdir'
569570 input_spec = _SlicesDirInputSpec
570571 output_spec = _SlicesDirOutputSpec
572+ _short_basenames = None
573+ _short_outline = None
574+
575+ def _run_interface (self , runtime ):
576+ """Create symlinks with short names before running slicesdir.
577+
578+ ``slicesdir`` names its output files by replacing path separators in the input
579+ file paths with underscores. When input paths are long (e.g., in deep working
580+ directories), the derived output filenames can exceed the OS's 255-character
581+ filename limit (see https://github.com/PennLINC/xcp_d/issues/1545).
582+
583+ We work around this by creating symlinks with short names in the working
584+ directory, and passing those short names to ``slicesdir`` instead of the
585+ original long paths.
586+ """
587+ self ._short_basenames = []
588+ for i , f in enumerate (self .inputs .in_files ):
589+ ext = '.nii.gz' if f .endswith ('.nii.gz' ) else os .path .splitext (f )[1 ]
590+ basename = f'img{ i } { ext } '
591+ symlink_path = os .path .join (runtime .cwd , basename )
592+ if os .path .lexists (symlink_path ):
593+ os .unlink (symlink_path )
594+
595+ os .symlink (os .path .abspath (f ), symlink_path )
596+ self ._short_basenames .append (basename )
597+
598+ if isdefined (self .inputs .outline_image ):
599+ f = self .inputs .outline_image
600+ ext = '.nii.gz' if f .endswith ('.nii.gz' ) else os .path .splitext (f )[1 ]
601+ self ._short_outline = f'outline{ ext } '
602+ symlink_path = os .path .join (runtime .cwd , self ._short_outline )
603+ if os .path .lexists (symlink_path ):
604+ os .unlink (symlink_path )
605+
606+ os .symlink (os .path .abspath (f ), symlink_path )
607+
608+ runtime = super ()._run_interface (runtime )
609+ return runtime
610+
611+ def _format_arg (self , name , spec , value ):
612+ """Use short symlink basenames for in_files and outline_image.
613+
614+ This ensures the ``slicesdir`` command line uses short paths, producing
615+ short output filenames that won't exceed the OS filename length limit.
616+ """
617+ if name == 'in_files' and self ._short_basenames is not None :
618+ return ' ' .join (self ._short_basenames )
619+
620+ if name == 'outline_image' and self ._short_outline is not None :
621+ return spec .argstr % self ._short_outline
622+
623+ return super ()._format_arg (name , spec , value )
571624
572625 def _list_outputs (self ):
573626 """Create a Bunch which contains all possible files generated by running the interface.
@@ -586,13 +639,21 @@ def _list_outputs(self):
586639
587640 out_dir = os .path .abspath (os .path .join (os .getcwd (), 'slicesdir' ))
588641 outputs ['out_dir' ] = out_dir
642+
643+ # Use short basenames if available (set by _run_interface),
644+ # otherwise fall back to the original path-based naming.
645+ if self ._short_basenames is not None :
646+ in_basenames = self ._short_basenames
647+ else :
648+ in_basenames = [f .replace (os .sep , '_' ) for f in self .inputs .in_files ]
649+
589650 outputs ['out_files' ] = [
590651 self ._gen_fname (
591- basename = f . replace ( os . sep , '_' ) ,
652+ basename = name ,
592653 cwd = out_dir ,
593654 ext = self .inputs .out_extension ,
594655 )
595- for f in self . inputs . in_files
656+ for name in in_basenames
596657 ]
597658 temp_files = [
598659 'grota.png' ,
0 commit comments