Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Very Odd Issue When Using pp to Create an .exe File Including Date::Calc

by perldigious (Priest)
on Jun 27, 2017 at 14:46 UTC ( #1193701=perlquestion: print w/replies, xml ) Need Help??

perldigious has asked for the wisdom of the Perl Monks concerning the following question:

I figured out how to fix an issue I first had shortly after I began programming in Perl and that has plagued me since, but I'm at a loss to explain how this could possibly matter. This is definitely one for Monks more knowledgeable than I, and especially (I presume) those familiar with the pp module. I'm using Strawberry Perl on Windows 7, and of course can provide more information than that if necessary. I simply enter pp -o my_name.exe my_script.pl on the command line.

When I have the following code and and am trying to use pp to make an .exe, it makes the .exe, but the .exe doesn't seem to do anything at all when run (it won't even pause on the my $pause_here = <STDIN> line here.

#!/usr/bin/perl use utf8; use 5.022; use strict; use List::Util qw(max); use Date::Cal +c qw(Delta_Days); say "FINISHED"; my $pause_here = <STDIN>;

However, when I do the following, it works just fine, makes the .exe, and the .exe runs and performs as expected. Yes, the only difference is the one newline after use 5.022.

#!/usr/bin/perl use utf8; use 5.022; use strict; use List::Util qw(max); use Date::Calc qw(Delta_Days); say "FINISHED"; my $pause_here = <STDIN>;

It's the use Date::Calc qw(Delta_Days) that always caused me trouble. Without it, and even with all the other use statements on a single line, it also all works perfectly fine.

#!/usr/bin/perl use utf8; use 5.022; use strict; use List::Util qw(max); say "FINISHED"; my $pause_here = <STDIN>;

Am I crazy? Can anyone else repeat this behavior or explain it?

Now I always put one use statement per line anyway, but I had this problem in several old scripts I wrote where I wasn't yet in that habit, and I never found this as the "fix" until now. This plagued me so much until now that I literally have written and rewritten (because of course it didn't work the first time I reinvented the wheel) my own days_diff subroutine and simple single day difference test of it to deal with this.

use utf8; use 5.022; use strict; use warnings; for (my $year=1970; $year<=2100; $year++) { for (my $month=1; $month<=12; $month++) { for (my $day=1; $day<=31; $day++) { next if ( ($month == 2 && ($day == 30 || $day == 31)) || ($month == 2 && $day == 29 && ($year%4 != 0 || ($year +%100 == 0 && $year%400 != 0))) || ($month == 4 && $day == 31) || ($month == 6 && $day == 31) || ($month == 9 && $day == 31) || ($month == 11 && $day == 31) ); my $date1 = $month. "/" . $day . "/" . $year; my $date2 = $month . "/" . ($day+1) . "/" . $year; $date2 = ($month+1) . "/1/" . $year if ( ($day+1 == 32) || ($month == 2 && ($day+1 == 30 || $day+1 == 31)) || ($month == 2 && $day+1 == 29 && ($year%4 != 0 || ($ye +ar%100 == 0 && $year%400 != 0))) || ($month == 4 && $day+1 == 31) || ($month == 6 && $day+1 == 31) || ($month == 9 && $day+1 == 31) || ($month == 11 && $day+1 == 31) ); $date2 = "1/1/" . ($year+1) if ($month+1 == 13 && $day+1 +== 32); my $difference = days_diff($date2, $date1); say "$date1\t$date2\t$difference" if ($difference != 1); } } } say "Hit Enter to quit."; my $pause = <STDIN>; sub days_diff { my ($month1, $day1, $year1) = split /\//, $_[0]; my ($month2, $day2, $year2) = split /\//, $_[1]; # The Date::Calc module function "Delta_Days" was used for this an +d worked great, but it would not compile in to an .exe using pp. # use Date::Calc qw(Delta_Days); # use is here for convenient copy +/paste # return Delta_Days($year2, $month2, $day2, $year1, $month1, $day1 +); # This crude (not precise over long timeframes or short ones spann +ing the end of February and March, but this was fine for the applicat +ion) method was used for a long time, # but it fails (in an unacceptable way) when subtracting a day tha +t falls on the 1st of the next month from a day that falls on the 31s +t the prior month (gives -0, not -1), # return sprintf("%.0f", ($year1-$year2)*365.25+($month1-$month2)* +365.25/12+($day1-$day2)); # Here a Gergorian calendar date to Julian day number conversion i +s used on both dates and then they are subtracted. # https://en.wikipedia.org/wiki/Julian_day#Converting_Julian_or_Gr +egorian_calendar_date_to_Julian_day_number use POSIX qw(floor); # use is here for convenient copy/paste my $a1 = floor((14-$month1)/12); my $a2 = floor((14-$month2)/12); my $y1 = $year1 + 4800 - $a1; my $y2 = $year2 + 4800 - $a2; my $m1 = $month1 + 12*$a1 - 3; my $m2 = $month2 + 12*$a2 - 3; my $JDN1 = $day1 + floor((153*$m1+2)/5) + 365*$y1 + floor($y1/4) - + floor($y1/100) + floor($y1/400) - 32045; my $JDN2 = $day2 + floor((153*$m2+2)/5) + 365*$y2 + floor($y2/4) - + floor($y2/100) + floor($y2/400) - 32045; return $JDN1-$JDN2; }

Just another Perl hooker - But the purist monks keep telling me I should do it for love, not money.

Replies are listed 'Best First'.
Re: Very Odd Issue When Using pp to Create an .exe File Including Date::Calc
by marto (Cardinal) on Jun 27, 2017 at 15:43 UTC

    I don't have access to a Windows machine right now, I usually recomend running pp with the -x flag:

    pp -x -o 1193701.exe 1193701.pl

    On Linux I created example exes using pp with and without -x, as you can see there's differences in what is being packaged:

    rwxrwxr-x 1 marto marto 149 Jun 27 16:31 1193701_oneline.pl -rwxrwxr-x 1 marto marto 148 Jun 27 16:31 1193701_twolines.pl -rwxr-xr-x 1 marto marto 3322431 Jun 27 16:32 oneline.exe -rwxr-xr-x 1 marto marto 3417012 Jun 27 16:32 oneline_x.exe -rwxr-xr-x 1 marto marto 3428235 Jun 27 16:34 twolines.exe -rwxr-xr-x 1 marto marto 3428929 Jun 27 16:34 twolines_x.exe

    Note the different sizes. Runtime:

    marto@netbook:~/perlmonks$ ./oneline.exe Can't locate Date/Calc.pm in @INC (you may need to install the Date::C +alc module) (@INC contains: /tmp/par-6d6172746f/cache-c93faf27fca57f9 +a6de63ed7af3cc18b152dad8a/inc/lib /tmp/par-6d6172746f/cache-c93faf27f +ca57f9a6de63ed7af3cc18b152dad8a/inc CODE(0x9ffa574) CODE(0x9ffa740)) +at script/1193701_oneline.pl line 4. BEGIN failed--compilation aborted at script/1193701_oneline.pl line 4. marto@netbook:~/perlmonks$ ./oneline_x.exe FINISHED marto@netbook:~/perlmonks$ ./twolines.exe FINISHED marto@netbook:~/perlmonks$ ./twolines_x.exe FINISHED

    Clearly, the one line of use... example isn't packaging Date::Calc when called without the -x option. The good thing about the executables created is that they are zip archives you can extract and examine the differences. If I get time later I'll do some more digging, but this should be a reasonable avenue for investigation.

    1193701_oneline.pl:

    #!/usr/bin/perl use utf8; use 5.022; use strict; use List::Util qw(max); use Date::Cal +c qw(Delta_Days); say "FINISHED"; my $pause_here = <STDIN>;

    1193701_twolines.pl

    #!/usr/bin/perl use utf8; use 5.022; use strict; use List::Util qw(max); use Date::Calc qw(Delta_Days); say "FINISHED"; my $pause_here = <STDIN>;

      Thanks for the tip marto, when I run pp -x -o my_name.exe my_script.pl it runs the script in my command line output and then ultimately the .exe does work (even with every use on one line). I'll take that advice about the -x switch for pp in the future.

      I didn't even know the .exe files created could be extracted like zip files, though I'm not sure I'm equipped with the know-how to go about debugging the differences and figuring out why one works over the other.

      This isn't a crucial issue or anything since I did figure out a way to make it work (now I have two ways thanks to your -x suggestion), it just made me turn my head sideways with a funny look that simply adding the newline fixed the problem... very odd to me, since I wouldn't have thought that should matter at all.

      Just another Perl hooker - But the purist monks keep telling me I should do it for love, not money.

        I'm mobile at the moment, so can't test further right now. However, a few things. Calling pp with -vvv this enables the highest level of verbose output. With the -L flag, provided with a file name this becomes the log for output, rather that stdout. See the documentation for various options. You could enable logging to discover the packaging process, compare the process for the different options and track down the reason from there. Similarly, extracting each exe into a directory will allow you to explore and compare the output. Comparing logs is probably easier :)

Re: Very Odd Issue When Using pp to Create an .exe File Including Date::Calc
by swl (Vicar) on Jun 28, 2017 at 04:04 UTC

    pp uses Module::ScanDeps to find dependencies, and it looks like it is not properly parsing lines containing /use\s+\d/

    Using the current version of Module::ScanDeps:

    perl -MModule::ScanDeps -e"print $Module::ScanDeps::VERSION" 1.23

    and shortening the code somewhat:

    use utf8; use strict; use List::Util qw(max); use Date::Calc qw(Delta_ +Days); use 42;

    And then running it through scandeps.bat (in this case the code is in use_on_one_line_42_at_end.pl).

    scandeps use_on_one_line_42_at_end.pl 'Portable' => '1.22', 'File::Which' => '1.21', 'CryptX' => '0.044', 'Portable::CPAN' => '1.22', 'Portable::Config' => '1.22', 'Portable::HomeDir' => '1.22', 'Portable::LoadYaml' => '1.22', 'Portable::minicpan' => '1.22', 'Portable::FileSpec' => '1.22', 'File::HomeDir' => '1.00', 'File::HomeDir::Darwin::Carbon' => '1.00', 'File::HomeDir::Darwin::Cocoa' => '1.00', 'File::HomeDir::FreeDesktop' => '1.00', 'File::HomeDir::MacOS9' => '1.00', 'File::HomeDir::Test' => '1.00', 'File::HomeDir::Windows' => '1.00', 'File::HomeDir::Unix' => '1.00', 'File::HomeDir::Darwin' => '1.00', 'File::HomeDir::Driver' => '1.00', 'Math::BigInt::GMP' => '1.6003', 'Math::BigInt::LTM' => '0.044',

    The above also does not list List::Util, but that can be done using the -B flag in the scandeps.pl call

    In fact, the issue also manifests in one-liners as these two are also missing Date::Calc in their outputs:

    scandeps -e"use 5.022; use Date::Calc" scandeps -e"use Date::Calc; use 5.022"

    But on further checking, it is triggered for any value >= 5.01

    These list Date::Calc and Date::Calc::PP

    scandeps -e"use 5.009; use Date::Calc" scandeps -e"use 4; use Date::Calc"

    This does not:

    scandeps -e"use 5.01; use Date::Calc"

    And for general reference:

    scandeps -e"use 5.009;" No modules found!

    It can also be reproduced using other modules. This one-liner does not list Text::CSV_XS.

    scandeps -e"use 5.01; use Text::CSV_XS"

    It is probably worth raising as a Module::ScanDeps issue.

      Wow, well done swl, I'm glad someone dug in to sniff out where/what the issue was (I seriously doubt I'm up to the task at my capability level, I'm just one of those Perl "lusers" :-) ). I'd give you all my ++ for the day on this if I could.

      Just another Perl hooker - But the purist monks keep telling me I should do it for love, not money.

        No worries perldigious.

        As haukex noted, Module::ScanDeps is a regexp based parser so it's perhaps not surprising that it misses some things when parsing perl code.

        That said, I had a quick look at the Module::ScanDeps code and it's probably in scan_line where it does an early return while processing semicolon separated chunks and it finds a chunk where a perl version >= 5.9.5 is being used (currently line 812). When it finds a matching chunk it returns feature.pm and thus stops processing the rest of the line. It should probably add feature.pm to %found and call next instead.

        There looks to be a similar issue in the following block where it handles pragmas.

        If I get a chance I'll submit a pull request to the github repo, but others are welcome to beat me to it as it won't be today.

Re: Very Odd Issue When Using pp to Create an .exe File Including Date::Calc
by karlgoethebier (Abbot) on Jun 27, 2017 at 15:48 UTC
    "...This crude not precise over long timeframes or short ones..."

    What about DateTime? It provides something like $dt->delta_days( $datetime ) etc.

    Regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

    perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

      Thanks for the suggestion karlgoethebier, though I do have a way to make Date::Calc work as it should anyway. I was just really confused by how something as seemingly inconsequential as listing all the use statements on one line vs. separate lines could make the .exe either fail or work respectively with no other difference... very odd to me, since it didn't seem like it should ever matter.

      Just another Perl hooker - But the purist monks keep telling me I should do it for love, not money.
        ... listing all the use statements on one line vs. separate lines could make the .exe either fail or work respectively with no other difference... very odd to me, since it didn't seem like it should ever matter.

        Perl is very hard to parse statically, maybe even impossible (although PPI does a decent job, considering). So quite a few tools resort to doing a simple line-by-line/regex parse of Perl source files to find out simple things, hoping that the authors of the code stuck to some common styles of writing Perl. For example:

        • The PAUSE indexer parses package ...; lines (and there is a simple trick to hide packages from the indexer),
        • some tools like ExtUtils::MakeMaker parse the our $VERSION = ...; line in modules (see the doc of VERSION_FROM and ExtUtils::MM_Unix->parse_version, Update 2: and see this node for even more modules that do a static parse on module versions), or
        • pp uses Module::ScanDeps which uses a "static-scanning heuristic" (I take that to mean regular expressions) to find the dependencies - and indeed swl has analysed the issue there a bit further.

        Updated: Added a few minor specifics.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1193701]
Approved by haukex
Front-paged by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (1)
As of 2021-09-26 01:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?