File Coverage

blib/lib/Treex/Core/Run.pm
Criterion Covered Total %
statement 71 193 36.7
branch 8 56 14.2
condition 0 19 0.0
subroutine 19 30 63.3
pod 1 3 33.3
total 99 301 32.8


line stmt bran cond sub pod time code
1             package Treex::Core::Run;
2             $Treex::Core::Run::VERSION = '2.20210102';
3 1     1   140144 use 5.008;
  1         13  
4              
5 1     1   561 use Moose;
  1         474347  
  1         7  
6 1     1   8293 use Treex::Core::Common;
  1         4  
  1         6  
7 1     1   6993 use Treex::Core;
  1         4  
  1         63  
8 1     1   10 use MooseX::SemiAffordanceAccessor 0.09;
  1         31  
  1         10  
9             with 'MooseX::Getopt';
10              
11              
12             # TODO some of these modules might not be needed, check this
13 1     1   4741 use Cwd;
  1         2  
  1         81  
14 1     1   8 use File::Path;
  1         2  
  1         59  
15 1     1   7 use File::Temp qw(tempdir);
  1         2  
  1         47  
16 1     1   7 use File::Which;
  1         4  
  1         49  
17 1     1   7 use List::MoreUtils qw(first_index);
  1         2  
  1         12  
18 1     1   1350 use IO::Interactive;
  1         958  
  1         7  
19 1     1   36 use Time::HiRes;
  1         2  
  1         11  
20 1     1   180 use Readonly;
  1         3  
  1         47  
21 1     1   6 use POSIX;
  1         4  
  1         11  
22 1     1   2151 use Exporter;
  1         3  
  1         34  
23 1     1   502 use Sys::Hostname;
  1         1169  
  1         61  
24 1     1   8 use base 'Exporter';
  1         3  
  1         138  
25              
26 1     1   8 use File::Glob 'bsd_glob';
  1         3  
  1         3101  
27              
28             our @EXPORT_OK = q(treex);
29              
30             has 'save' => (
31             traits => ['Getopt'],
32             cmd_aliases => 's',
33             is => 'rw',
34             isa => 'Bool',
35             default => 0,
36             documentation => 'save all documents',
37             );
38              
39             has 'quiet' => (
40             traits => ['Getopt'],
41             cmd_aliases => 'q',
42             is => 'rw',
43             isa => 'Bool',
44             default => 0,
45             trigger => sub { Treex::Core::Log::log_set_error_level('FATAL'); },
46             documentation => q{Warning, info and debug messages are suppressed. Only fatal errors are reported.},
47             );
48              
49             has 'cleanup' => (
50             traits => ['Getopt'],
51             is => 'rw', isa => 'Bool', default => 0,
52             documentation => q{Delete all temporary files.},
53             );
54              
55             has 'error_level' => (
56             traits => ['Getopt'],
57             cmd_aliases => 'e',
58             is => 'rw', isa => 'ErrorLevel', default => 'INFO',
59             trigger => sub { Treex::Core::Log::log_set_error_level( $_[1] ); },
60             documentation => q{Possible values: ALL, DEBUG, INFO, WARN, FATAL},
61             );
62              
63             has 'lang' => (
64             traits => ['Getopt'],
65             cmd_aliases => [ 'language', 'L' ],
66             is => 'rw', isa => 'Str',
67             documentation => q{shortcut for adding "Util::SetGlobal language=xy" at the beginning of the scenario},
68             );
69              
70             has 'selector' => (
71             traits => ['Getopt'],
72             cmd_aliases => 'S',
73             is => 'rw', isa => 'Str',
74             documentation => q{shortcut for adding "Util::SetGlobal selector=xy" at the beginning of the scenario},
75             );
76              
77             has 'tokenize' => (
78             traits => ['Getopt'],
79             cmd_aliases => 't',
80             is => 'rw', isa => 'Bool',
81             documentation => q{shortcut for adding "Read::Sentences W2A::Tokenize" at the beginning of the scenario (or W2A::XY::Tokenize if used with --lang=xy)},
82             );
83              
84              
85             # treex -h should not print "Unknown option: h" before the usage.
86             #has 'help' => (
87             # traits => ['Getopt'],
88             # cmd_aliases => 'h',
89             # is => 'ro', isa => 'Bool', default => 0,
90             # documentation => q{Print usage info},
91             #);
92              
93             has 'filenames' => (
94             traits => ['NoGetopt'],
95             is => 'rw',
96             isa => 'ArrayRef[Str]',
97             documentation => 'treex file names',
98             );
99              
100             has 'scenario' => (
101             traits => ['NoGetopt'],
102             is => 'rw',
103             isa => 'Treex::Core::Scenario',
104             predicate => '_has_scenario',
105             documentation => 'scenario object',
106             );
107              
108              
109             has 'watch' => (
110             traits => ['Getopt'],
111             is => 'ro',
112             isa => 'Str',
113             documentation => 're-run when the given file is changed TODO better doc',
114             );
115              
116             has 'dump_scenario' => (
117             traits => ['Getopt'],
118             cmd_aliases => 'd',
119             is => 'rw',
120             isa => 'Bool',
121             default => 0,
122             documentation => 'Just dump (print to STDOUT) the given scenario and exit.',
123             );
124              
125             has 'dump_required_files' => (
126             traits => ['Getopt'],
127             is => 'rw',
128             isa => 'Bool',
129             default => 0,
130             documentation => 'Just dump (print to STDOUT) files required by the given scenario and exit.',
131             );
132              
133             has 'cache' => (
134             traits => ['Getopt'],
135             is => 'rw',
136             isa => 'Str',
137             default => "",
138             documentation => 'Use cache. Required memory is specified in format memcached,loading. Numbers are in GB.',
139             );
140              
141             has version => (
142             traits => ['Getopt'],
143             is => 'ro',
144             isa => 'Bool',
145             default => 0,
146             cmd_aliases => 'v',
147             documentation => q(Print treex and perl version),
148             trigger => sub {
149             print get_version();
150             exit();
151             },
152             );
153              
154             #
155             # Parallel head execution options
156             # TODO move them to Treex::Core::Parallel::Head
157              
158             has 'forward_error_level' => (
159             traits => ['Getopt'],
160             cmd_aliases => 'E',
161             is => 'rw', isa => 'ErrorLevel', default => 'WARN',
162             documentation => q{messages with this level or higher will be forwarded from the distributed jobs to the main STDERR},
163             );
164              
165              
166             has 'parallel' => (
167             traits => ['Getopt'],
168             cmd_aliases => 'p',
169             is => 'rw',
170             isa => 'Bool',
171             default => 0,
172             documentation => 'Parallelize the task on SGE cluster (using qsub).',
173             );
174              
175             has 'jobs' => (
176             traits => ['Getopt'],
177             cmd_aliases => 'j',
178             is => 'ro',
179             isa => 'Int',
180             default => 10,
181             documentation => 'Number of jobs for parallelization, default 10. Requires -p.',
182             );
183              
184              
185             has 'local' => (
186             traits => ['Getopt'],
187             is => 'ro',
188             isa => 'Bool',
189             documentation => 'Run jobs locally (might help with multi-core machines). Requires -p.',
190             );
191              
192             has 'priority' => (
193             traits => ['Getopt'],
194             is => 'ro',
195             isa => 'Int',
196             default => -100,
197             documentation => 'Priority for qsub, an integer in the range -1023 to 0 (or 1024 for admins), default=-100. Requires -p.',
198             );
199              
200             has 'mem' => (
201             traits => ['Getopt'],
202             cmd_aliases => [ 'm', 'memory' ],
203             is => 'ro',
204             isa => 'Str',
205             default => '2G',
206             documentation => 'How much memory should be allocated for cluster jobs, default=2G. Requires -p. '
207             . 'Translates to "qsub -hard -l mem_free=$mem -l h_vmem=2*$mem -l act_mem_free=$mem". '
208             . 'Use --mem=0 and --qsub to set your own SGE settings (e.g. if act_mem_free is not available).',
209             );
210              
211             has 'name' => (
212             traits => ['Getopt'],
213             is => 'ro',
214             isa => 'Str',
215             default => '',
216             documentation => 'Prefix of submitted jobs. Requires -p. '
217             . 'Translates to "qsub -N $name-jobname".',
218             );
219              
220             has 'queue' => (
221             traits => ['Getopt'],
222             is => 'ro',
223             isa => 'Str',
224             default => '',
225             documentation => 'SGE queue. Translates to "qsub -q $queue".',
226             );
227              
228             has 'qsub' => (
229             traits => ['Getopt'],
230             is => 'ro',
231             isa => 'Str',
232             default => '',
233             documentation => 'Additional parameters passed to qsub. Requires -p. '
234             . 'See --priority and --mem. You can use e.g. --qsub="-q *@p*,*@s*" to use just machines p* and s*. '
235             . 'Or e.g. --qsub="-q *@!(twi*|pan*)" to skip twi* and pan* machines.',
236             );
237              
238             has 'workdir' => (
239             is => 'rw',
240             traits => ['Getopt'],
241             isa => 'Str',
242             default => './{NNN}-cluster-run-{XXXXX}',
243             documentation => 'working directory for temporary files in parallelized processing; ' .
244             'one can create automatic directories by using patterns: ' .
245             '{NNN} is replaced by an ordinal number with so many leading zeros to have length of the number of Ns, ' .
246             '{XXXX} is replaced by a random string, whose length is the same as the number of Xs (min. 4). ' .
247             'If not specified, directories such as 001-cluster-run, 002-cluster-run etc. are created',
248             );
249              
250             has 'sge_job_numbers' => (
251             is => 'rw',
252             traits => ['NoGetopt'],
253             documentation => 'list of numbers of jobs executed on sge',
254             default => sub { [] },
255             );
256              
257             has 'survive' => (
258             traits => ['Getopt'],
259             is => 'rw',
260             isa => 'Bool',
261             default => 0,
262             documentation => 'Continue collecting jobs\' outputs even if some of them crashed (risky, use with care!).',
263             );
264              
265              
266             #
267             # Parallel node/worker execution options
268             # TODO move them to Treex::Core::Parallel::Node
269              
270             has 'jobindex' => (
271             traits => ['Getopt'],
272             is => 'ro',
273             isa => 'Int',
274             documentation => 'Not to be used manually. If number of jobs is set to J and modulo set to M, only I-th files fulfilling I mod J == M are processed.',
275             );
276              
277             has 'outdir' => (
278             traits => ['Getopt'],
279             is => 'ro',
280             isa => 'Str',
281             documentation => 'Not to be used manually. Dictory for collecting standard and error outputs in parallelized processing.',
282             );
283              
284             has 'server' => (
285             traits => ['Getopt'],
286             is => 'ro',
287             isa => 'Str',
288             default => '',
289             documentation => 'Not to be used manually. Used to point parallel jobs to the head.',
290             );
291              
292             #
293             #
294             #
295              
296             sub _usage_format {
297 0     0   0 return "usage: %c %o scenario [-- treex_files]\nscenario is a sequence of blocks or *.scen files\noptions:";
298             }
299              
300             #gets info about version of treex and perl
301             sub get_version {
302 0     0 0 0 my $perl_v = $^V;
303 0         0 my $perl_x = $^X;
304 0   0     0 my $treex_v = $Treex::Core::Run::VERSION || 'DEV';
305 0         0 my $treex_x = which('treex');
306              
307             # File::Which::which sometimes fails to found treex.
308 0 0       0 if ( !defined $treex_x ) {
309 0         0 chomp( $treex_x = `which treex 2> /dev/null` );
310 0   0     0 $treex_x ||= '<treex not found in $PATH>';
311             }
312 0         0 my $version_string = <<"VERSIONS";
313             Treex version: $treex_v from $treex_x
314             Perl version: $perl_v from $perl_x
315             VERSIONS
316 0         0 return $version_string;
317             }
318              
319             sub BUILD {
320              
321             # more complicated tests on consistency of options will be place here
322 0     0 0 0 my ($self) = @_;
323              
324 0         0 return;
325             }
326              
327              
328             sub _execute {
329 0     0   0 my ($self) = @_;
330              
331 0 0 0     0 if ( $self->dump_scenario || $self->dump_required_files ) {
332              
333             # If someone wants to run treex -d My::Block my_scen.scen
334 0         0 my $scen_str = $self->_construct_scenario_string_with_quoted_whitespace();
335 0         0 $self->set_scenario( Treex::Core::Scenario->new( scenario_string => $scen_str, runner => $self ) );
336              
337             # TODO: Do it properly - perhaps, add a Scenario option to not load all the blocks.
338             # We cannot create the real scenario instance without loading all the blocks
339             # However, since r6307 some Scenario's functions were changed to methods, so we must create a dummy instance.
340              
341             #my @block_items = Treex::Core::Scenario::parse_scenario_string($scen_str);
342             #my @block_items = $dummy_scenario->parse_scenario_string($scen_str);
343              
344 0 0       0 if ( $self->dump_scenario ) {
345 0         0 print "# Full Scenario generated by 'treex --dump_scenario' on " . localtime() . "\n";
346 0         0 print $self->scenario->construct_scenario_string( multiline => 1 ), "\n";
347             }
348              
349 0 0       0 if ( $self->dump_required_files ) {
350 0         0 print "# Required files generated by 'treex --dump_required_files' on " . localtime() . "\n";
351 0         0 print join "\n", $self->scenario->get_required_files(), "\n";
352             }
353 0         0 exit;
354             }
355              
356 0 0       0 if ( $self->dump_required_files ) {
357              
358 0         0 my $scen_str = join ' ', @{ $self->extra_argv };
  0         0  
359 0         0 $self->set_scenario( Treex::Core::Scenario->new( scenario_string => $scen_str, runner => $self ) );
360              
361 0         0 print "# Required files generated by 'treex --dump_required_files' on " . localtime() . "\n";
362 0         0 print join "\n", $self->scenario->get_required_files(), "\n";
363 0         0 exit;
364             }
365 0         0 my $done = 0;
366 0         0 my $time;
367 0         0 my $watch = $self->watch;
368              
369 0 0       0 if ( defined $watch ) {
370 0 0       0 log_fatal "Watch file '$watch' does not exists" if !-f $watch;
371 0         0 $time = ( stat $watch )[9];
372             }
373              
374 0         0 while ( !$done ) {
375              
376 0         0 $self->_execute_scenario();
377              
378 0         0 $done = 1;
379 0         0 my $info_written = 0;
380             WATCH_CHANGE:
381 0   0     0 while ( defined $watch && -f $watch ) {
382 0         0 my $new_time = ( stat $watch )[9];
383 0 0       0 if ( $new_time > $time ) {
384 0         0 $time = $new_time;
385 0         0 $done = 0;
386 0         0 last WATCH_CHANGE;
387             }
388 0 0       0 if ( !$info_written ) {
389 0         0 log_info "Watching '$watch' file. Touch it to re-run, delete to quit.";
390 0         0 $info_written = 1;
391             }
392 0         0 sleep 1;
393             }
394             }
395 0         0 return;
396             }
397              
398             my %READER_FOR = (
399             'treex' => 'Treex',
400             'treex.gz' => 'Treex',
401             'txt' => 'Text',
402             'txt.gz' => 'Text',
403             'streex' => 'Treex',
404             'mrg' => 'PennMrg',
405             'mrg.gz' => 'PennMrg',
406             'tag' => 'CdtTag',
407              
408             # TODO:
409             # conll => 'Conll',
410             # plsgz => 'Plsgz',
411             # tmt
412             );
413              
414             sub _get_reader_name_for {
415 13     13   6297 my $self = shift;
416 13         47 my @names = @_;
417 13         63 my $base_re = join( '|', keys %READER_FOR );
418 13         117 my $re = qr{\.($base_re)$};
419 13         30 my @extensions;
420             my $first;
421              
422 13         29 foreach my $name (@names) {
423 21 100       134 if ( $name =~ /$re/ ) {
424 17         45 my $current = $1;
425 17         43 $current =~ s/\.gz$//;
426 17 100       46 if ( !defined $first ) {
427 9         15 $first = $current;
428             }
429 17 100       37 if ( $current ne $first ) {
430 1         17 log_fatal 'All files (' . join( ',', @names ) . ') must have the same extension' . "\n" .
431             " current = $current\n" .
432             " first = $first\n" .
433             " curname = $name";
434             }
435 16         35 push @extensions, $current;
436             }
437             else {
438 4         21 log_fatal 'Files (' . join( ',', @names ) . ') must have extensions';
439             }
440             }
441 8         21 my $r = $READER_FOR{$first};
442 8 100       27 log_fatal "There is no DocumentReader implemented for extension '$first'" if !$r;
443 7         62 return "Read::$r";
444             }
445              
446             # This is where the main work is done. It is overridden in parallel execution.
447             sub _execute_scenario {
448 0     0     my ($self) = @_;
449              
450 0           log_info "Local (single-process) execution.";
451              
452 0           $self->_init_scenario();
453              
454 0           my $scenario = $self->scenario;
455              
456 0           my $runnin_started = time;
457 0           $scenario->run();
458              
459 0           log_info "Running the scenario took " . ( time - $runnin_started ) . " seconds";
460              
461 0           return;
462             }
463              
464             # Parameters can contain whitespaces that should be preserved
465             sub _construct_scenario_string_with_quoted_whitespace {
466 0     0     my ($self) = @_;
467 0           my @arguments;
468 0           foreach my $arg ( @{ $self->extra_argv } ) {
  0            
469 0 0         if ( $arg =~ /([^=\s]+)=(.*\s.*)$/ ) {
470 0           my ( $name, $value ) = ( $1, $2 );
471 0           $value =~ s/'/\\'/g;
472 0           push @arguments, qq($name='$value');
473             }
474             else {
475 0           push @arguments, $arg;
476             }
477             }
478 0           return join ' ', @arguments;
479             }
480              
481             sub _init_scenario {
482 0     0     my ($self) = @_;
483              
484 0           my $scen_str = $self->_construct_scenario_string_with_quoted_whitespace();
485              
486             # some command line options are just shortcuts for blocks; the blocks are added to the scenario now
487 0 0         if ( $self->filenames ) {
488 0           my $reader = $self->_get_reader_name_for( @{ $self->filenames } );
  0            
489 0           log_info "Block $reader added to the beginning of the scenario.";
490 0           $scen_str = "$reader from=" . join( ',', @{ $self->filenames } ) . " $scen_str";
  0            
491             }
492              
493 0 0         if ( $self->save ) {
494 0           log_info "Block Write::Treex added to the end of the scenario.";
495 0           $scen_str .= ' Write::Treex';
496             }
497              
498 0 0         if ( $self->tokenize ) {
499 0           my $tokenizer = 'W2A::Tokenize';
500 0           my $lang = $self->lang;
501 0 0 0       if ($lang && $lang ne 'all'){
502 0           my $module = 'Treex::Block::W2A::' . uc($lang) . '::Tokenize';
503 0 0         if (eval "use $module;1"){
504 0           $tokenizer = 'W2A::' . uc($lang) . '::Tokenize';
505             }
506             }
507 0           $scen_str = "Read::Sentences $tokenizer $scen_str";
508             }
509              
510 0 0         if ( $self->lang ) {
511 0           $scen_str = 'Util::SetGlobal language=' . $self->lang . " $scen_str";
512             }
513              
514 0 0         if ( $self->selector ) {
515 0           $scen_str = 'Util::SetGlobal selector=' . $self->selector . " $scen_str";
516             }
517              
518 0           my $loading_started = time;
519 0 0         if ( $self->_has_scenario ) {
520 0           $self->scenario->restart();
521             }
522             else {
523 0           $self->set_scenario( Treex::Core::Scenario->new( from_string => $scen_str, runner => $self ) );
524 0           $self->scenario->load_blocks;
525             }
526              
527 0           my $loading_ended = time;
528 0           log_info "Loading the scenario took " . ( $loading_ended - $loading_started ) . " seconds";
529              
530 0           return;
531             }
532              
533              
534             # A factory subroutine, creating the right Treex object for the job.
535             # (local single-process: Treex::Core::Run, parallel processing head: Treex::Parallel::Head,
536             # parallel processing worker node: Treex::Parallel::Node)
537             sub treex {
538              
539             # ref to array of arguments, or a string containing all arguments as on the command line
540 0     0 1   my $arguments = shift;
541              
542 0 0 0       if ( ref($arguments) eq 'ARRAY' && scalar @$arguments > 0 ) {
    0 0        
543 0     0     my $idx = first_index { $_ eq '--' } @$arguments;
  0            
544 0           my %args = ( argv => $arguments );
545 0 0         if ( $idx != -1 ) {
546 0           $args{filenames} = [ splice @$arguments, $idx + 1 ];
547 0           pop @$arguments; # delete "--"
548             }
549 0           my $runner;
550              
551 0 0   0     if (any { $_ =~ /^--jobindex/ } @$arguments){
  0 0          
552 0           require Treex::Core::Parallel::Node;
553 0           $runner = Treex::Core::Parallel::Node->new_with_options( \%args );
554             }
555 0     0     elsif (any { $_ =~ /^(--parallel|-p|-pj\d+)$/ } @$arguments){
556 0           require Treex::Core::Parallel::Head;
557 0           $runner = Treex::Core::Parallel::Head->new_with_options( \%args );
558             }
559             else {
560 0           $runner = Treex::Core::Run->new_with_options( \%args );
561             }
562 0           $runner->_execute();
563              
564             }
565              
566             elsif ( defined $arguments && ref($arguments) ne 'ARRAY' ) {
567 0 0         treex( [ grep { defined $_ && $_ ne '' } split( /\s/, $arguments ) ] );
  0            
568             }
569              
570             else {
571 0           treex('--help');
572              
573             #log_fatal 'Unspecified arguments for running treex.';
574             }
575 0           return;
576             }
577              
578             1;
579              
580             __END__
581              
582             =head2 --watch option
583              
584             SYNOPSIS:
585             touch timestamp.file
586             treex --watch=timestamp.file my.scen & # or without & and open another terminal
587             # after all documents are processed, treex is still running, watching timestamp.file
588             # you can modify any modules/blocks and then
589             touch timestamp.file
590             # All modified modules will be reloaded (the number of reloaded modules is printed).
591             # The document reader is restarted, so it starts reading the first file again.
592             # To exit this "watching loop" either rm timestamp.file or press Ctrl^C.
593              
594             BENEFITS:
595             * much faster development cycles (e.g. most time of en-cs translation is spent on loading)
596             * Now I have some non-deterministic problems with loading NER::Stanford
597             - using --watch I get it loaded on all jobs once and then I don't have to reload it.
598              
599             TODO:
600             * modules are just reloaded, no constructors are called yet
601              
602              
603             =for Pod::Coverage BUILD get_version
604              
605             =encoding utf-8
606              
607             =head1 NAME
608              
609             Treex::Core::Run + treex - applying Treex blocks and/or scenarios on data
610              
611             =head1 VERSION
612              
613             version 2.20210102
614              
615             =head1 SYNOPSIS
616              
617             In bash:
618              
619             > treex myscenario.scen -- data/*.treex
620             > treex My::Block1 My::Block2 -- data/*.treex
621              
622             In Perl:
623              
624             use Treex::Core::Run q(treex);
625             treex([qw(myscenario.scen -- data/*.treex)]);
626             treex([qw(My::Block1 My::Block2 -- data/*.treex)]);
627              
628             =head1 DESCRIPTION
629              
630             C<Treex::Core::Run> allows to apply a block, a scenario, or their mixture on a
631             set of data files. It is designed to be used primarily from bash command line,
632             using a thin front-end script called C<treex>. However, the same list of
633             arguments can be passed by an array reference to the function C<treex()>
634             imported from C<Treex::Core::Run>.
635              
636             Note that this module supports distributed processing (Linux-only!), simply by
637             adding the switch C<-p>. The C<treex> method then creates a
638             C<Treex::Core::Parallel::Head> object, which extends C<Treex::Core::Run>
639             by providing parallel processing functionality.
640              
641             Then there are two ways to process the data in a parallel fashion. By
642             default, SGE cluster\'s C<qsub> is expected to be available. If you have no
643             cluster but want to make the computation parallelized at least on a multicore
644             machine, add the C<--local> switch.
645              
646             =head1 SUBROUTINES
647              
648             =over 4
649              
650             =item treex
651              
652             create new runner and runs scenario given in parameters
653              
654             =back
655              
656             =head1 USAGE
657              
658             Can't locate Treex/Core/Run.pm in @INC (you may need to install the Treex::Core::Run module) (@INC contains: /home/martin/perl5/lib/perl5/5.26.1/x86_64-linux-gnu-thread-multi /home/martin/perl5/lib/perl5/5.26.1 /home/martin/perl5/lib/perl5/x86_64-linux-gnu-thread-multi /home/martin/perl5/lib/perl5 /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.26.1 /usr/local/share/perl/5.26.1 /usr/lib/x86_64-linux-gnu/perl5/5.26 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl/5.26 /usr/share/perl/5.26 /home/martin/perl5/lib/perl5/5.26.0 /home/martin/perl5/lib/perl5/5.26.0/x86_64-linux-gnu-thread-multi /usr/local/lib/site_perl /usr/lib/x86_64-linux-gnu/perl-base) at bin/treex line 5.
659             BEGIN failed--compilation aborted at bin/treex line 5.
660              
661             =head1 AUTHORS
662              
663             Zdeněk Žabokrtský <zabokrtsky@ufal.mff.cuni.cz>
664              
665             Martin Popel <popel@ufal.mff.cuni.cz>
666              
667             Martin Majliš
668              
669             Ondřej Dušek <odusek@ufal.mff.cuni.cz>
670              
671             =head1 COPYRIGHT AND LICENSE
672              
673             Copyright © 2011-2014 by Institute of Formal and Applied Linguistics, Charles University in Prague
674              
675             This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.