Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Perl syntax checking without `perl -c`

by kcott (Bishop)
on Dec 03, 2020 at 01:55 UTC ( #11124560=perlquestion: print w/replies, xml ) Need Help??

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

Very brief background: Some idiot dug a hole, cut through a main electrical cable, and killed the power to my $work (and many others in the vicinity). Most of our systems are back up and running but not all of those that I need. To continue working, I've download various files from out git repostory to my home computer. I'm making code changes here but do not have the full environment available to me. Getting that environment is problematic at best, perhaps not possible at all, while "recovery" procedures are in progress.

After making changes, typing 'perl -c ...' invariably gives me something like: "Can't locate SomeMod.pm in @INC ...".

I've checked perlrun, under "-c" it has:

"causes Perl to check the syntax of the program and then exit without executing it. Actually, it will execute any BEGIN, UNITCHECK, or CHECK blocks and any use statements: these are considered as occurring outside the execution of your program. INIT and END blocks, however, will be skipped."

The use statements will be my biggest problem; although, others may crop up here and there.

Does anyone know of something simple, like 'perl -c ...', that will just check the syntax without executing any part of the code?

I'm hoping the system will be fully operational within the next day or two. Anything that involves a substantial amount of work — e.g. setting up and configuring a framework; installing lots of modules; etc. — is probably not an option for me at the present; however, that could still be useful to know and have available for some future, similar situation.

— Ken

Replies are listed 'Best First'.
Re: Perl syntax checking without `perl -c`
by LanX (Cardinal) on Dec 03, 2020 at 02:15 UTC
    Hi Ken,

    > like 'perl -c ...', that will just check the syntax without executing any part of the code?

    the problem is that imported functions can change the syntax.

    Just think of something like sub foo (&) {} , which will only compile if it finds foo {...} later. That's the main reason why Perl is so hard to parse statically.

    > The use statements will be my biggest problem; although, others may crop up here and there.

    > "Can't locate SomeMod.pm in @INC ..."

    OK let's suppose the exported functions don't change the syntax.

    What you could do is pretending that "SomeMod.pm" was already loaded, by setting %INC in a BEGIN block.* No need to find a module which was already "loaded".

    But these are kind of hairy workarounds, do you really think it's worth going down that road?

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

    *) I seem to remember that perlrun allows you to inject a BEGIN without changing the actual code, just the way you start it.

      G'day Rolf,

      "the problem is that imported functions can change the syntax"

      You make a very good point; I hadn't considered that. I've only downloaded individual files (e.g. Whatever.pm) and these are all integrated into a much bigger framework; there will, in a number of places, be modules that change syntax.

      I really just wanted to get on with some work while the SysAdmin folks dealt with the disaster recovery. Setting up the environment — which not only involves code but also databases, web servers, etc. — would likely take substantially longer than the recovery (and, as I indicated above, depending on services affected, may not even be possible at this time).

      "But these are kind of hairy workarounds, do you really think it's worth going down that road?"

      No, I don't think so. :-)

      For now, I'll just rely on my editor's syntax highlighting and my own experience. In a day or two, I can upload all my changes and test in the full environment.

      Thanks for your feedback.

      — Ken

      Another workaround I sometimes use at work is to create a fakelib/ directory with contents created like
      echo 1 > SomeMod.pm

      Then include fakelib into your PERL5LIB (or run perl -I /path/to/fakelib).

      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

        Seconded; I've used basically the same thing letting flycheck run on my desktop working copy at $work so that it's perl -c check behaves even though I don't have the same perl environment available as the production machines where the code actually runs.

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

        Yeah, that's another good approach.

        I think it depends how many modules one needs to fake and often one needs them.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: Perl syntax checking without `perl -c`
by stevieb (Canon) on Dec 06, 2020 at 19:14 UTC

    Went one step further and wrote Syntax::Check, which comes with its own syncheck binary:

    $ syncheck t/data/test.pl t/data/test.pl syntax OK

    Add --verbose|-v to get output of the name of the temporary lib dir and all modules it skipped. Use --keep|-k to keep the temporary directory around after program execution.

    Uses PPI, so if that's not installed, installation of this distribution may take a bit of time.

      I had a look in both the POD and the source: couldn't see anything that dealt with "Can't use bareword ("syntactic_sugar") ..." which I first mentioned in "Re: Perl syntax checking without `perl -c` [general reply to all]".

      I checked and found that I did have PPI so I thought I'd install it. More than 45 minutes later, and it's still installing (I have neither a slow machine nor a slow internet connection). The issue seems to involve the "Module::Installed::Tiny dependency tree" — that doesn't look very Tiny to me.

      I have to go back to $work now. The installation is still running. I can't wait for it any longer.

      Update: I checked back periodically. It looks like the installation hung after about 1hr 10mins.

      — Ken

        The issue seems to involve the "Module::Installed::Tiny dependency tree" that doesn't look very Tiny to me.

        Sheesh! I never noticed that. The deps are ALL related to test prereqs.

        I liked the premise of the distribution, so I wrote my own. Module::Installed. It not only checks single modules, but I added a function that will list all includes in a file and whether or not they are installed or not. There are no non-core deps. PPI is required to use the includes_installed() function, but we load it dynamically only if it's available:

        use warnings; use strict; use Data::Dumper; use Module::Installed qw(module_installed includes_installed); my $module = 'PPI'; my $file = '/home/spek/repos/module-installed/t/data/test.pl'; my $statement = module_installed($module) ? "is installed" : "isn't installed"; print "$module $statement\n\n"; print "Checking includes in file $file\n\n"; my $includes = includes_installed($file); print Dumper $includes;

        Output:

        PPI is installed Checking includes in file /home/spek/repos/module-installed/t/data/tes +t.pl $VAR1 = { 'Carp' => 1, 'Not::Installed' => 0, 'strict' => 1, 'warnings' => 1, 'Load::Fail' => 0, 'Exporter' => 1, 'Data::Dumper' => 1 };

        I just finished a very large Perl project that I landed a little while ago. It's nice to be back in the Perl mindset, hammering out all manner of weird stuff. I'll take a look at the "bareword" issue in Syntax::Check later this afternoon.

        Update: Holy crap, I just realized I've hit the 50 mark in published CPAN distributions!

Re: Perl syntax checking without `perl -c`
by Discipulus (Abbot) on Dec 03, 2020 at 07:38 UTC
    Hello kcott,

    I'm sorry for you. You can give a try to Compiler::Lexer and Compiler::Parser as shown here

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
      As shown to coredump , heh
Re: Perl syntax checking without `perl -c`
by Anonymous Monk on Dec 03, 2020 at 13:31 UTC
    For a really crude workaround, you could hook into the require subsystem to return empty files for missing modules:
    # workaround.pm BEGIN { push @INC, sub { my ($self, $filename) = @_; return \"1;"; }; } 1;
    $ perl -Mworkaround -MFoo::Bar -E' say "still alive"; ' still alive

      No need to hack it up yourself when there's a module for that: Acme::Anything.


      🦛

      This is not "crude" and actually a good solution; that often times you definitely want things to fail if prerequisite modules are not found. And if you're requiring libs over a network mounted drive, you're gonna have a bad time.

        I just can't understand why someone capable of giving such a valuable contribution disguises himself as the Anonymous Monk.

        Greetings,
        -jo

        $gryYup$d0ylprbpriprrYpkJl2xyl~rzg??P~5lp2hyl0p$
Re: Perl syntax checking without `perl -c`
by davido (Cardinal) on Dec 03, 2020 at 23:24 UTC

    Another solution is to insert a subroutine into @INC that checks a list of modules to fake (rather than a full filesystem approach). If the required module is in your list, the @INC sub instantiates a stub, or simply modifies %INC to fake that the module is already loaded. See require for how to do this. I remember doing it a few years ago but haven't retained that recollection. :)

    I may have dived into this a bit in a Perl Conference talk in 2017: Spilling the @INC.

    Update: So despite my grasping for words in the talk it does look like around 22 minutes in I begin discussing how to stick a subref into @INC that manufactures modules and a subroutine within the module (which isn't strictly necessary).


    Dave

Re: Perl syntax checking without `perl -c` [general reply to all]
by kcott (Bishop) on Dec 04, 2020 at 08:16 UTC

    Thanks and ++ to everyone who responded; there were a lot of posts and I won't be replying individually.

    I tried variations on some of the fake module suggestions. After faking multiple modules, I finally got rid of the "Can't locate SomeMod.pm in @INC ..." messages; however, I then started getting messages like "Can't use bareword ("syntactic_sugar") ...".

    There were a lot of interesting takes on that general principle; and even a movie (thanks Dave). I'll keep these in mind: they may be useful at some later time but, unfortunately, not at the present.

    It looks like disaster recovery is moving along nicely: hopefully, I'll be back to normal operations early next week.

    — Ken

        "Hence you'll need to fake-export them too. ;)"

        Yes, I was aware of that and even considered doing it. The main criteria, when thinking about doing that, were: how long that would take; how long disaster recovery would take; and, what useful coding I would not be doing while doing this instead.

        I tend not to make too many mistakes. Mostly they involve keyboard errors: not pressing or releasing the shift key fast enough; hitting an adjacent key by mistake; and the like. E.g.

        my @x = qw{a b c}: # slow release of shift: got ':' instead of ';' my $y - $x[2]; # hit adjacent key: got '-' instead of '='

        Thankfully these are pretty rare and easily fixed.

        Other types of errors, such as logic errors, are not syntax errors and are only found when testing.

        — Ken

Re: Perl syntax checking without `perl -c`
by stevieb (Canon) on Dec 05, 2020 at 18:26 UTC

    For fun, I put together a script that fakes out all of the libraries that are used by creating a fake library directory with mock lib files. The script is a wrapper for perl -c which hides the fact that the real includes are unavailable. It uses the magical PPI to seek out the includes from the target script.

    syntax_check script:

    #!/usr/bin/env perl use warnings; use strict; use feature 'say'; use File::Path qw(make_path); use PPI; if (! @ARGV) { say "USAGE: ./syntax_check perl_script.pl"; exit; } my $file = $ARGV[0]; die "Can't open file '$file': $!" if ! -f $file; my $lib_dir = 'test_lib/'; if (! -d $lib_dir) { mkdir $lib_dir or die $!; } my $doc = PPI::Document->new($file); my $includes = $doc->find('PPI::Statement::Include'); for my $include (@$includes) { my $module = $include->module; my $package = $module; # Skip pragmas if ($module eq lc $module) { say "Skipping assumed pragma '$module'"; next; } $module =~ s|::|/|g; if (my ($dir, $file) = $module =~ m|^(.*)/(.*)$|) { $file .= '.pm'; my $path = "$dir/$file"; # Skip includes that are actually available if (exists $INC{$path}) { say "Skipping available module '$package'"; next; } if (! -d "$lib_dir/$dir") { make_path("$lib_dir/$dir") or die $!; } if (! -f "$lib_dir/$path") { open my $wfh, '>', "$lib_dir/$path" or die $!; print $wfh '1;'; close $wfh or die $!; } } } say ''; `perl -I$lib_dir -c $file`;

    Script we're going to run perl -c against:

    use strict; use warnings; use Data::Dumper; use Test::One; use Multiple::Levels::Two; print "Compiled and ready...\n";

    Run it:

    spek@scelia ~/scratch $ ./syntax_check script.pl Skipping assumed pragma 'strict' Skipping assumed pragma 'warnings' Skipping available module 'Data::Dumper' script.pl syntax OK

    Running the script without the wrapper:

    spek@scelia ~/scratch $ perl -c script.pl Can't locate Test/One.pm in @INC (you may need to install the Test::On +e module) (@INC contains: /home/spek/perl5/perlbrew/perls/perl-5.26.1 +/lib/site_perl/5.26.1/x86_64-linux /home/spek/perl5/perlbrew/perls/pe +rl-5.26.1/lib/site_perl/5.26.1 /home/spek/perl5/perlbrew/perls/perl-5 +.26.1/lib/5.26.1/x86_64-linux /home/spek/perl5/perlbrew/perls/perl-5. +26.1/lib/5.26.1) at script.pl line 5. BEGIN failed--compilation aborted at script.pl line 5.

    I have no idea how reliable it will be in practice, but it may be worth a shot. I was careless in variable naming and such, as I'm very limited in time right now.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (8)
As of 2021-01-25 17:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Notices?