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

I am attempting to distribute a script to a number of Windows machines that may or may not have Perl installed on them by using the PAR::Packer module. More specifically, the pp application that comes with the module. The program initially reads a list of settings from a configuration file and then attempts to perform some checks on the machine based on these configuration settings. After debugging, I packaged the script and moved it and the configuration file to a test PC. The script ran perfectly, except that it did not read one of the configuration settings from the file. Since I couldn't seem to find any way to debug the PAR exe, I insatlled ActivePerl 5.8 on the machine and copied the script to the machine. Using perl -d, the script executed flawlessly, reading the configuration file just as it should. The PAR exe still would not work. The offending code is as follows:

my @configFiles = <.\\*.conf>; foreach(@configFiles) { open CONFIG_FILE, "<:via(File::BOM)", $_; while(<CONFIG_FILE>) { if(/^backup path\s*=/) { (undef, $backupPath) = split(/\s*=\s*/,$_); } elsif(/^activation code\s*=/) { (undef, $activationCode) = split(/\s*=\s*/,$_); print "activation code: $activationCode\n"; } elsif(/^directory\s*=/) { (undef, $searchDirectory) = split(/\s*=\s*/,$_); } elsif(/^file\s*=/) { (undef, $searchFilename) = split(/\s*=\s*/,$_); } elsif(/^failure pattern\s*=/) { my (undef, $pattern) = split(/\s*=\s*/,$_); push(@failurePatterns, $pattern); } elsif(/^warning pattern\s*=/) { my (undef, $pattern) = split(/\s*=\s*/,$_); push(@warningPatterns, $pattern); } elsif(/^file age\s*=/) { (undef, $fileAge) = split(/\s*=\s*/,$_); } elsif(/^date in file\s*=/) { (undef, $dateInFile) = split(/\s*=\s*/,$_); } elsif(/^start from\s*=/) { (undef, $startFrom) = split(/\s*=\s*/,$_); } elsif(/^stop line\s*=/) { (undef, $stopLine) = split(/\s*=\s*/,$_); } elsif(/^date pattern\s*=/) { (undef, $datePattern) = split(/\s*=\s*/,$_); } elsif(/^database\s*=/) { my (undef, $pattern) = split(/\s*=\s*/,$_); push(@dbList, $pattern); } } }

Specifically, the offending line is (apparently): } elsif(/^activation code\s*=/) {

I would like to know if there is any way to debug a PAR exe so that I can (hopefully) see what's going on that it doesn't find that specific setting. Barring that, I would welcome any suggestions as to why this particular bit of code isn't working.

Replies are listed 'Best First'.
Re: Debugging PAR packaged programs
by Herkum (Parson) on Dec 09, 2008 at 14:50 UTC

    God that is offensive code! No wonder you are having problems. Have you considered a module to manage your config files. cpan:Config::Auto is something that comes to mind.

    Basically, if you are going to do something like this(ie: something that has been done already). Try and find a module that does it for you and maybe you can avoid these sorts of issues.

      This is not always possible in certain environments, Herkum, due to the local political climate. Knowing how to write something correctly given the tools available is legitimate, even if a module out there does it cleaner. The other thing about modules is the lack of maintenance and occasional setup errors that turn folks off to the whole experience.

        Absolutely true, and mind-numbingly aggravating. I hear "only use core" all the time, or maybe "only use what you can find via yum in the official repositories for this antiquated CentOS version we're using." It's like telling me to only run my car only with what was in it when I bought it, or maybe using the resources available at the dinky little gas station around the corner from my apartment.

        It takes little research to discover what modules are available and actively maintained. Unfortunately, that's not enough to satisfy a corporate policy that fears the outside world. Pardon me, I need to go find a brick wall to smash my head against for a few minutes.

        Oh, that original could would be at least a little cleaner if it just split each line into a key/value pair and stored the results in a config hash. It wouldn't necessarily solve the problem of the missing value, but it would be easier to debug by reducing lines of code.

        Now about that brick wall ...

        It may not always be possible but that is not the case here. It is bad code, and the programmer needs to fix it. The easiest way for them would be for them to use a module. It would solve two issues, it makes them more flexible to new solutions, and there is a good chance that it will work better than anything that is home grown.

        It is easy to say its corporate politics that prevents me from doing 'blah' and continue doing what you are doing.

        I find that it leads to people who think they know more than they do, writing more code than they should and introducing bugs because of things they have not thought of before... And more than likely, they just did not ask!

Re: Debugging PAR packaged programs
by Bloodnok (Vicar) on Dec 09, 2008 at 14:16 UTC
    A little more context would be helpful e.g. (parts of) the config file & the errors/warnings you're 'seeing' ... .oO(are strictures enabled in the script?)

    A user level that continues to overstate my experience :-))
Re: Debugging PAR packaged programs
by dirtdart (Beadle) on Dec 09, 2008 at 14:27 UTC

    The config file is pretty simple:

    activation code= 4960256a directory = C:\\Documents and settings\\DrewB\\Local Settings\\Applica +tion Data\\Windows NT\\NTBackup\\data file=*.log date in file = 720 file age = 720 date pattern=Backup completed on (?<month>\d+)\/(?<day>\d+)\/(?<year>\ +d+).+?(?<hour>\d+):(?<minute>\d+)\s*(?<mark>AM|PM)

    I'm not actually getting any perl errors. However, when it gets to the following line:

        my $cmdLine = "java -cp $CPATH com.nable.server.edf.GenericApp.EDFGenericApp $activationCode EDF_SERVICEID_1050000:$dateFail EDF_SERVICEID_1050001:$failures EDF_SERVICEID_1050002:$fileFailCount EDF_SERVICEID_1050003:$warnings";

    The $activationCode is empty. I placed several print "$activationCode\n" lines in different places in the code and from what I can tell, it's never getting read out of the file as the one in the original code I posted never even fires.

      Nothing immediately obvious comes to mind. In cases like this, often the simplest solution is to instrument the code with some simple debug statements.
      while(<CONFIG_FILE>) { print("Line: $_\n"); if(/^backup path\s*=/) { (undef, $backupPath) = split(/\s*=\s*/,$_); } elsif(/^activation code\s*=/) { (undef, $activationCode) = split(/\s*=\s*/,$_); print "activation code: $activationCode\n"; } # etc...

      If you see the "activation code=" line, but your following $activationCode print doesn't show, then you know you have a parsing problem. If you never see the "activation code=" line at all, perhaps you're somehow missing reading the first line of the file.

      Anyway, even in the absence of external modules, this ugly code could have been made cleaner and less error prone with something like this:

      my %config; foreach (<CONFIG_FILE>) { unless (/^\s*(.+?)\s*=\s*(.+?)\s*$/) { chomp($_); warn "Invalid config format: `$_'"; next; } $config{$1} = $2; }

      This way, you don't pollute your global namespace with configuration variables, and you now have a 1:1 mapping between configuration variable names and hash keys, which has got to be easier to remember and maintain.

      You should still definitely have additional error checking and some validation checks after the file is read.