http://qs321.pair.com?node_id=1082218


in reply to What are (popular) modules that access/modify @ARGV?

Hmmm,

My first thought was whether, or not, you could put B::Xref to use ... and then you wouldn't need to pre-construct a list of likely candidates (which will vary over time), because (with any luck and a following wind) you should be able to ascertain the information on-the-fly...

Justa thought...

A user level that continues to overstate my experience :-))
  • Comment on Re: What are (popular) modules that access/modify @ARGV?

Replies are listed 'Best First'.
Re^2: What are (popular) modules that access/modify @ARGV?
by frozenwithjoy (Priest) on Apr 14, 2014 at 16:42 UTC

    So, I took your suggestion and wrote the following subroutine. Much of it is just the process of comparing the module's path to the object being called to determine the module name. It works well if I put it in a script; however, when I put it in a module (such that it runs on import), running a script that calls the module essentially fork bombs my machine. Any suggestions on possible workarounds? When I get a chance tonight, I'll look at the B::Xref source and see if I can figure something out. Thanks.

    sub check_conflicts { my @xref_out = `perl -MO=Xref,-r $0 2> /dev/null`; @xref_out = grep { /ARGV/ } @xref_out; my %conflicts; for (@xref_out) { my ($module_path, $object ) = split /\s/; $module_path =~ s|/|::|g; my @object_path = split /::/, $object; for (0 .. $#object_path) { my $module_name = join "::", @object_path[0..$_]; if ($module_path =~ /$module_name\.pm/) { $conflicts{$module_name} = 1; last; } }; } say $_ for keys %conflicts; }

      I suspect (but don't know for sure) that your "bombs my machine" problem is due to the cyclic nature of: run script ... load module ... run B::Xref ... run script ... load module ... run B::Xref ... ad infinitum.

      There's some other (potential) issues, which include: you're currently finding all modules using @ARGV, not just the ones loaded before your module; splitting the output from B::Xref on whitespace when pathnames may contain whitespace; portability of /dev/null; assuming modules have a .pm extension.

      I created module PM::1082204::Log::Reproducible in ~/tmp/PM/1082204/Log/. This is intended to mirror your Log::Reproducible. Here's some notes:

      • I've used a BEGIN block, instead of a subroutine called from import(), so that there's no reliance on import() being called and the @ARGV checks are done first.
      • I've taken the code from the beginning of the calling script (up to, but not including, the use Log::Reproducible) and saved to a File::Temp temporary file. B::Xref operates on this temporary file which fixes the cyclic/infinite loop issue mentioned above.
      • I've used IPC::Open3 to deal with the /dev/null issue: STDERR output is accessible from <CERR> but that filehandle isn't read.
      • B::Xref discards a leading './' in the filename path but retains instances of embedded '/./'. I've handled this (e.g. /^(?:\.[\\\/]?)?(.*)$/) but there may be better ways to do this.
      • There's quite a few tweaks I could envisage you making to this; the code here should at least provide a framework as a starting point.

      Here's the PM::1082204::Log::Reproducible code:

      package PM::1082204::Log::Reproducible; use 5.010; use strict; use warnings; use autodie; use File::Temp (); use IPC::Open3; BEGIN { my $code = do { open my $fh, '<', $0; local $/; <$fh> }; my ($code_to_test) = $code =~ /(\A .*?) use \s+ @{[__PACKAGE__]}/s +x; my ($temp_fh, $temp_filename) = File::Temp::tempfile(); print $temp_fh $code_to_test; local(*CIN, *COUT, *CERR); my $cmd = "$^X -MO=Xref,-r $temp_filename"; my $pid = open3(\*CIN, \*COUT, \*CERR, $cmd); my $re = '(?:' . join('|' => map { /^(?:\.[\\\/]?)?(.*)$/; "\Q$1" +} @INC) . ')[\\\/]?(\S+?)(?:\.\S+)?\s'; my %argv_modules; for (<COUT>) { next unless /\@\s+ARGV/; (my $module) = /$re/; $module =~ s{[\\\/]}{::}g; ++$argv_modules{$module}; } waitpid $pid, 0; my @warn_modules = sort keys %argv_modules; if (@warn_modules) { warn "WARNING:\n", "Modules using'\@ARGV' before " . __PACKAGE__ . " loaded:\ +n"; warn "\t$_\n" for @warn_modules; } } 1;

      In addition, the Qwerty module, in ~/tmp/PM/1082204/Log/X X/, tests for pathnames with whitespace:

      package Qwerty; use 5.010; use strict; use warnings; sub xxx { say "ARGV not empty." if @ARGV; } 1;

      And, the ModuleWithoutPmExtension, in ~/tmp/, tests pretty much what it says:

      package ModuleWithoutPmExtension; sub test { print "I'm ", __PACKAGE__, "\n"; print "\@ARGV has elements.\n" if @ARGV; } 1;

      I ran the tests from ~/tmp. Here's pm_1082204_b_xref.pl:

      #!/usr/bin/env perl use strict; use warnings; BEGIN { require 'ModuleWithoutPmExtension'; ModuleWithoutPmExtension::test(); } use lib './../tmp/./PM/1082204/Log/X X'; use Qwerty; { use Getopt::Long; use PM::1082204::Log::Reproducible; } use Getopt::Std;

      Output:

      I'm ModuleWithoutPmExtension WARNING: Modules using'@ARGV' before PM::1082204::Log::Reproducible loaded: Getopt::Long ModuleWithoutPmExtension Qwerty

      Getopt::Std isn't in the list. If you load it earlier in the code, it does appear in the list.

      -- Ken

        Hey Ken,
        Thanks so much for this great and thorough suggestion. I incorporated your code into v0.8.2 of Log::Reproducible to check for potential conflicts along side another subroutine that checks for known conflicts (the list of which is currently empty, but I'm sure there are some out there). I really appreciate your contribution!

        Thanks again,
        Mike

Re^2: What are (popular) modules that access/modify @ARGV?
by frozenwithjoy (Priest) on Apr 14, 2014 at 10:22 UTC
    I'm really intrigued by this approach! I've got it figured out and partially written, but need to sleep. I'll post my attempt tomorrow. Thanks.