1

I'm using Getopt::Long::Subcommand to parse the command line arguments.

But I found that in some of Perl versions. This module has incorrect behavior.

For example if I run ./ViewBS BisNonConvRate --sample bis_WT.tab.gz,WT. I expect the output as below:

Subcommand: BisNonConvRate
Sample list: bis_WT.tab.gz,WT

In perl-v5.18.2, it works fine. I got the output I expected. But in perl-v5.24.1, it does NOT. I got the output shown as below:

Subcommand: BisNonConvRate
Sample list:

I checked version of Getopt::Long::Subcommand. Both of them are the same version (0.102).

Could anyone help on this issue? Thanks in advance.

Here is how I configure the perl environment:

perlbrew install perl==5.24.1 ## or perl==5.18.2
perlbrew use perl-5.24.1
perlbrew install-cpanm
cpanm Getopt::Long::Subcommand
cpanm Bio::DB::HTS::Tabix
cpanm Bio::SeqIO

Here is my code using Getopt::Long::Subcommand in 'ViewBS':

#!/usr/bin/env perl 
#!/usr/bin/perl --

####################################################################
# ViewBS - Tools for exploring and visualizing bisulfite sequencing 
# (BS-seq) data. 
####################################################################
use strict;
use warnings;

use Getopt::Long::Subcommand;
use Pod::Usage;
use File::Basename;
use Cwd qw(abs_path);
use Benchmark;
my $t_stt = Benchmark->new;

#use FindBin;
#use lib "$FindBin::Bin";
$|++;    # Do not buffer output
#$| = 1; # Do not buffer output 

#Self written libraries.
## Package written by Shanshan Huang
use lib dirname(abs_path $0) . '/lib';  #include seft written packages in @INC.

my $main_path = dirname(abs_path $0);

my %opts;   ## store the values for common parameters
my %opts_subcmd; ### store the values for sub command parameters

### Return hash structure, with these keys: success, subcommand (array of str) by GetOptions
my $resOpt = &processCMD();  

my ($sub_cmd) = @{$resOpt->{subcommand}}; 
print "\nSubcommand: $sub_cmd\n";

if($sub_cmd eq "BisNonConvRate"){
    print "Sample list: @{$opts_subcmd{sample}}\n";
}

sub processCMD{
    $resOpt = GetOptions(   # Return hash structure, with these keys: success, subcommand (array of str)
        ## common options recognized by all subcommands
        options => {
        'help|h|?'       => \$opts{help},
        'version|v'      => \$opts{version},
        'verbose'        => \$opts{verbose},
        'outdir:s'       => \$opts_subcmd{outdir},
        'prefix:s'       => \$opts_subcmd{prefix},
        'minDepth:s'     => \$opts_subcmd{minDepth},
        'maxDepth:s'     => \$opts_subcmd{maxDepth},
        'context:s'      => \@{$opts_subcmd{context}},    # context
        'flank:i'        => \$opts_subcmd{flank},
        'height:f'       => \$opts_subcmd{height},  ## figure size: height
        'width:f'        => \$opts_subcmd{width},   ## figure size: width
        'height2:f'       => \$opts_subcmd{height2}, ## for histogram
        'width2:f'        => \$opts_subcmd{width2},  ## for histogram
        'random_region:i' => \$opts_subcmd{random_region}, ## For heatmap error. annot allocate vector of size
        'methodAverage'  => \$opts_subcmd{methodAverage},  ## Calculate the average methylation level rather than the weighted methylated level.
         },
         subcommands => {
             MethGeno => {       
                 summary => 'Plot methylation information across the chromsome.',
                 # subcommand-specific options
                 options => {
            # mandatory arguments
                'genomeLength|g:s'       => \$opts_subcmd{genomeLength},
            'sample:s'               => \@{$opts_subcmd{sample}},
            # Optional arguments
            'win:i'                  => \$opts_subcmd{win},
                    'step:i'                 => \$opts_subcmd{step},       
            'minLength:s'            => \$opts_subcmd{minLength},  # cutoff for minimum length of chromosome.
            'split:s'                => \$opts_subcmd{'split'},      #
                 }
            },
        MethOverRegion => {
        summary => 'Plot average distribution of methylation over your defined regions.',
        options => {
            # mandatory arguments
            'region:s'              => \$opts_subcmd{region},
                    'sample:s'               => \@{$opts_subcmd{sample}},
            # Optional arguments
            'binLength:i'        => \$opts_subcmd{binLength},
            'binNumber:i'            => \$opts_subcmd{binNumber},
                'minLength:i'        => \$opts_subcmd{minLength},
            'maxLength:i'            => \$opts_subcmd{maxLength},
                    'type:s'                 => \$opts_subcmd{type},
                'regionName:s'           => \$opts_subcmd{regionName},
                #'legendTitle:s'          => \$opts_subcmd{legendTitle},
        }
        },
        MethHeatmap => {
                summary => 'Generate heatmap for a given regions.',
                options => {
                    # mandatory arguments
                    'regions:s'              => \$opts_subcmd{region},
                    'sample:s'               => \@{$opts_subcmd{sample}},
             # Optional arguments
                    'merge'                 => \$opts_subcmd{merge},   ### if --merge is true, then methylation level of different will be generated in one file rather than one file for each context
                    'cluster_cols:s'         => \$opts_subcmd{cluster_cols},
            'cluster_rows:s'         => \$opts_subcmd{cluster_rows},
                }
            },
        MethOneRegion => {
                summary => 'Generate heatmap for a given regions.',
                options => {
            'regions:s'              => \$opts_subcmd{region},
                'sample:s'               => \@{$opts_subcmd{sample}},
             }
        },
            MethCoverage => {
                summary => 'Generate statistics of read coverage of the provided samples.',
                options => {
        'reference:s'        => \$opts_subcmd{reference},
                'sample:s'               => \@{$opts_subcmd{sample}},
        'outdir:s'               => \$opts_subcmd{outdir},
                 }
            },
        MethLevDist => {
                summary => 'Generate distribution of methylation levels for the provided samples.',
                options => {
                'sample:s'               => \@{$opts_subcmd{sample}},
        'regions:s'              => \$opts_subcmd{region},
        'binMethLev:s'       => \$opts_subcmd{binMethLev},
                 }
            },
        GlobalMethLev => {
                summary => 'Generate global (bulk) methylation levels for the provided samples.',
                options => {
                'sample:s'               => \@{$opts_subcmd{sample}},
                 }
            },
        BisNonConvRate => {
                summary => 'Estimate non-conversion rate of BS-seq data.',
                options => {
                'sample:s'               => \@{$opts_subcmd{sample}},
            'chrom:s'               => \$opts_subcmd{chrom},
                 }
            },
        }
    );
    #print "UUXXX", join("\t", $resOpt->{subcommand}), "\n";
    push @{$opts_subcmd{subcommand}}, @{$resOpt->{subcommand}} if exists $resOpt->{subcommand};
    return $resOpt;
}

You can also get a copy of the file here: https://github.com/xie186/ViewBS/tree/66edfa79c35b404539ca3cdf4afcfd9c1bd20c6d/data/chk_issue25

xie186
  • 487
  • 4
  • 9
  • Works fine on my Ubuntu 18.10 laptop with perl version 5.29.3, `Getopt::Long::Subcommand` version 0.102. (I get output: `Sample list: bis_WT.tab.gz,WT`) – Håkon Hægland Feb 26 '19 at 15:44

1 Answers1

3

Getopt::Long::Subcommand relies on Getopt::Long. Either Getopt::Long::Subcommand or your specific needs are incompatible with Getopt::Long 2.43 (inclusive) to 2.50 (exclusive).

The simple solution is to upgrade to the latest version of Getopt::Long.

I filed a ticket requesting the addition of a dependency on Getopt::Long 2.50.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Work fine after upgrading Getopt::Long to version 2.50. Could you please tell me how you identify this problem. I want to learn how to trouble shoot similar problem in the future. Thank you. – xie186 Feb 26 '19 at 17:22
  • I first uncommented out all the existing logging statements in the module. It showed differences in what what left in `@ARGV` after `Getopt::Long::GetOptions` returned. The fact that both the failing and passing tests happened using the same version of Getopt::Long::Subcommand pointed to an external problem, at least a problem with an interaction with something external to ::Subcommand. [continued] – ikegami Feb 26 '19 at 17:43
  • It could be differences in `perl` itself, but it could also be in a module that comes with Perl such Getopt::Long. Because we were dealing with fresh installs of Perl, `cpan` would have installed the latest version of dependencies that didn't come with Perl. – ikegami Feb 26 '19 at 17:44
  • So I tried installing different versions of Getopt::Long with Perl 5.22 to find out what version worked and which ones didn't. Your program started working with 2.50 again, and the `CHANGES` file Getopt::Long 2.50 indicated it fixes a couple of bugs. – ikegami Feb 26 '19 at 17:47
  • Having a solution, I didn't really care to find out what the actual problem was. If I wanted to debug that, I'd start by logging the parameters passed to `Getopt::Long::GetOptions` (including `@ARGV`) and the values returned (including `@ARGV`). From those, I'd construct a small test program, which I'd reduce until nothing else can be removed without removing the error. This would normally give a pretty good idea where to look. – ikegami Feb 26 '19 at 17:51
  • If not, then it provides the easiest call to trace because it has the least amount of distractions. **Reducing the problem is always key.** For example, you posted 130 lines of code, when you could have demonstrated your problem in 5-10 lines. That means that 95% of what you posted is irrelevant, and should have been left out as such. – ikegami Feb 26 '19 at 21:02
  • I've released Getopt::Long::Subcommand 0.103 that depends on Getopt::Long >= 2.50. Thanks @ikegami! – Perl Ancar Feb 27 '19 at 10:39
  • Thank you @ikegami. – xie186 Feb 27 '19 at 15:41