1

I am having trouble including Perl modules that have to be fetched from a Perforce repository. I am including the modules by reading their text from Perforce using P4 print.

For people who are not familiar with Perforce, its a code versioning tool. I cannot just read the file from that path directly as if it was a mapped drive, so I need to run this command.

p4 print is equivalent to cat except the path is special and only command p4 can access files from the Perforce workspace.

BEGIN {
  push @INC, (
    sub {
      open my $fh, "p4 print -q //sw/pvt/shashikanths/perl/mylib/ReadElf.pm |";
      return $fh;
    },
    sub {
      open my $fh1, "p4 print -q //sw/pvt/shashikanths/perl/mylib/SimpleLogger.pm |";
      return $fh1;
    },
  );
}

BEGIN {
  push @INC, sub {
    open my $fh2, "p4 print -q //sw/pvt/shashikanths/perl/mylib/Table.pm |";
    return $fh2;
  }
}

use ReadElf;
use SimpleLogger;
use Table;

Always only the first file ReadElf.pm is included. I tried concatenating all three files to a single file, and also using a BEGIN block for each file individually. None of them works.

So when I try to access modules from SimpleLogger I get undefined subroutine error.

Borodin
  • 126,100
  • 9
  • 70
  • 144
  • 1
    As I understand `@INC`, you add the path of a directory containing files, not files or file text directly. Other SO questions which might be handy reading: [1](http://stackoverflow.com/questions/9313277/regarding-begin-push-in-perl), [2](http://stackoverflow.com/questions/185114/how-do-i-use-a-perl-module-in-a-directory-not-in-inc). – rutter Nov 05 '13 at 23:02
  • 1
    `use` should be lower cased. – choroba Nov 05 '13 at 23:03
  • 1
    @rutter, `@INC` can also contain coderefs. This is one of the deeper bits of magic in Perl, and isn't frequently used. He's just not doing it quite right. – cjm Nov 06 '13 at 07:35

1 Answers1

7

I apologize, I have only just understood what you are doing. Pushing subroutine references onto @INC is an arcane part of Perl functionality that few are aware of.

The documentation for require says this:

You can also insert hooks into the import facility by putting Perl code directly into the @INC array. There are three forms of hooks: subroutine references, array references, and blessed objects.

Subroutine references are the simplest case. When the inclusion system walks through @INC and encounters a subroutine, this subroutine gets called with two parameters, the first a reference to itself, and the second the name of the file to be included (e.g., "Foo/Bar.pm"). The subroutine should return either nothing or else a list of up to three values in the following order:

1 - A filehandle, from which the file will be read.

2 - A reference to a subroutine. If there is no filehandle (previous item), then this subroutine is expected to generate one line of source code per call, writing the line into $_ and returning 1, then finally at end of file returning 0. If there is a filehandle, then the subroutine will be called to act as a simple source filter, with the line as read in $_ . Again, return 1 for each valid line, and 0 after all lines have been returned.

3 - Optional state for the subroutine. The state is passed in as $_[1] . A reference to the subroutine itself is passed in as $_[0].

The problem is that you are ignoring the parameters to the subroutines you are pushing onto @INC. The second parameter will be the name of the module file that perl is trying to load, i.e. ReadElf.pm, SimpleLogger.pm or Table.pm. Perl finds the first entry in @INC that returns anything, and that is always the first subroutine, which fetches ReadElf.pm and returns a file handle to read from it.

To load any one of these, as long as they are all in the same place in the Perforce repository, you can write

BEGIN {
  push @INC, sub {
    my ($self, $module) = @_;
    my $file = "//sw/pvt/shashikanths/perl/mylib/$module";
    open my $fh, "p4 print -q $file |";
    return $fh;
  }
}

This works by building the full path to the Perforce copy of the module and returning a file handle that will allow Perl to read it.

You really should check first that the file specified by $file exists, and return nothing if not. Otherwise p4 is being run unnecessarily, and the subroutine returns a file handle regardless of whether the module was fetched successfully.

Community
  • 1
  • 1
Borodin
  • 126,100
  • 9
  • 70
  • 144
  • Sorry 'use' is spelled correctly in lower case. I typed the code manually here, hence upper case 'U'. This code works perfectly when I push just one file. So passing a subroutine is not a problem. The subroutine just read the file and returns a file handle which is appeneded to INC. Not sure why it doesnt work when I push multiple files at once or with multiple BEGIN block. – user2103630 Nov 05 '13 at 23:37
  • 3
    @user2103630: You should *never* type code into Stack Overflow. *Always* copy and paste from the live code that isn't working for you. – Borodin Nov 06 '13 at 00:08