package Pcore::Core::Env; use Pcore -class; use Config; use File::Spec qw[]; ## no critic qw[Modules::ProhibitEvilModules] needed to find system temp dir use Cwd qw[]; ## no critic qw[Modules::ProhibitEvilModules] use Pcore::Dist; use Pcore::Core::Env::Share; use Fcntl qw[LOCK_EX SEEK_SET]; use Pcore::Util::Scalar qw[is_ref]; has is_par => ( init_arg => undef ); # process run from PAR distribution has main_dist => ( init_arg => undef ); # Maybe [ InstanceOf ['Pcore::Dist'] ], main dist has pcore => ( init_arg => undef ); # InstanceOf ['Pcore::Dist'], pcore dist has dists => ( init_arg => undef ); # HashRef, registered dists. index has dists_order => ( init_arg => undef ); # ArrayRef, registered dists in load order has cli => ( init_arg => undef ); # HashRef, parsed CLI data has share => ( init_arg => undef ); # InstanceOf ['Pcore::Core::Env::Share'], share object has user_cfg_path => ( is => 'lazy', init_arg => undef ); has user_cfg => ( is => 'lazy', init_arg => undef ); # $HOME/.pcore/pcore.yaml config has PCORE_SHARE_DIR => (); has USER_DIR => (); # OS user profile dir has PCORE_USER_DIR => (); # USER_DIR/.pcore, pcore profile dir has PCORE_USER_BUILD_DIR => (); # USER_DIR/.pcore/.build, pcore build dir has INLINE_DIR => (); has START_DIR => (); has SCRIPT_DIR => (); has SCRIPT_NAME => (); has TEMP_DIR => (); # OS temp dir has DATA_DIR => (); has SCANDEPS => (); has DAEMONIZE => (); has UID => (); has GID => (); # create $ENV object $ENV = __PACKAGE__->new; ## no critic qw[Variables::RequireLocalizedPunctuationVars] _normalize_inc(); $ENV->BUILD1; _configure_inc(); # TODO - remove??? check under windows sub _normalize_inc { my @inc; my $inc_index; # index @INC, resolve @INC paths, remove duplicates, preserve REF items for my $inc_path (@INC) { if ( ref $inc_path ) { push @inc, $inc_path; next; } # ignore non-exists path next if !-d $inc_path; $inc_path = P->path($inc_path)->to_abs->{path}; # ignore already added path if ( !exists $inc_index->{$inc_path} ) { $inc_index->{$inc_path} = 1; push @inc, $inc_path; } } @INC = @inc; ## no critic qw[Variables::RequireLocalizedPunctuationVars] return; } sub _configure_inc { my @inc; my $inc_index; # index @INC, resolve @INC paths, remove duplicates, preserve Ref items for my $inc_path (@INC) { if ( is_ref $inc_path ) { push @inc, $inc_path; next; } # ignore already added path if ( !exists $inc_index->{$inc_path} ) { $inc_index->{$inc_path} = 1; push @inc, $inc_path; } } # not for PAR if ( !$ENV->{is_par} ) { my $is_module_build_test = 0; # $ENV->dist && exists $inc_index->{ $ENV->dist->{root} . '/blib/lib' } ? 1 : 0; # add dist lib and WORKSPACE to @INC only if we are int on the PAR archive and not in the Module::Build testing environment # under Module::Build dist lib is already added and WORKSPACE is not added to emulate clean CPAN installation if ( !$is_module_build_test ) { my $dist_lib_path; # detect dist lib path if ( $ENV->dist && !exists $inc_index->{ $ENV->dist->{root} . '/lib' } && -d $ENV->dist->{root} . '/lib' ) { $dist_lib_path = $ENV->dist->{root} . '/lib'; $inc_index->{$dist_lib_path} = 1; } # find and add other dist libs to @INC if ( $ENV{WORKSPACE} && -d $ENV{WORKSPACE} ) { for my $dir ( reverse sort { $a cmp $b } P->file->read_dir( $ENV{WORKSPACE}, full_path => 1 )->@* ) { if ( !exists $inc_index->{qq[$dir/lib]} && -d qq[$dir/lib/] && Pcore::Dist->dir_is_dist_root($dir) ) { $inc_index->{qq[$dir/lib]} = 1; unshift @inc, qq[$dir/lib]; } } } # register dist lib path in @INC, dist lib path is always on top of other dists unshift @inc, $dist_lib_path if $dist_lib_path; } } @INC = @inc; ## no critic qw[Variables::RequireLocalizedPunctuationVars] return; } sub _init_inline ($self) { if ( $self->{is_par} ) { $INC{'Inline.pm'} = $INC{'Pcore/Core/Env.pm'}; ## no critic qw[Variables::RequireLocalizedPunctuationVars] require XSLoader; *Inline::import = sub { my $caller = caller; XSLoader::load $caller; return; }; } else { require Inline; Inline->import( config => ( directory => $self->{INLINE_DIR}, autoname => 0, clean_after_build => 1, # clean up the current build area if the build was successful clean_build_area => 1, # clean up the old build areas within the entire Inline directory force_build => 0, # build (compile) the source code every time the program is run build_noisy => 0, # dump build messages to the terminal rather than be silent about all the build details ) ); } return; } sub BUILD ( $self, $args ) { $self->{is_par} = $ENV{PAR_TEMP} ? 1 : 0; $self->{USER_DIR} = $ENV{HOME} || $ENV{USERPROFILE}; $self->{PCORE_USER_DIR} = "$self->{USER_DIR}/.pcore"; mkdir $self->{PCORE_USER_DIR} || die qq[Error creating user dir "$self->{PCORE_USER_DIR}"] if !-d $self->{PCORE_USER_DIR}; $self->{PCORE_USER_BUILD_DIR} = "$self->{PCORE_USER_DIR}/.build"; mkdir $self->{PCORE_USER_BUILD_DIR} || die qq[Error creating user dir "$self->{PCORE_USER_BUILD_DIR}"] if !-d $self->{PCORE_USER_BUILD_DIR}; if ( !$self->{is_par} ) { $self->{INLINE_DIR} = "$self->{PCORE_USER_BUILD_DIR}/$Config{version}-$Config{archname}"; mkdir $self->{INLINE_DIR} || die qq[Error creating "$self->{INLINE_DIR}"] if !-d $self->{INLINE_DIR}; } # find Pcore share dir my $pcore_path = $INC{'Pcore.pm'}; # remove "/Pcore.pm" substr $pcore_path, -9, 9, $EMPTY; if ( -d "$pcore_path/../share" ) { # remove "/lib" substr $pcore_path, -4, 4, $EMPTY; $self->{PCORE_SHARE_DIR} = "$pcore_path/share"; } elsif ( -d "$pcore_path/auto/share/dist/Pcore" ) { $self->{PCORE_SHARE_DIR} = "$pcore_path/auto/share/dist/Pcore"; } else { die q[Pcore share dir can't be found.]; } $self->_init_inline; return; } sub BUILD1 ($self) { $self->{USER_DIR} = P->path( $self->{USER_DIR} )->{path}; $self->{PCORE_USER_DIR} = P->path( $self->{PCORE_USER_DIR} )->{path}; $self->{PCORE_USER_BUILD_DIR} = P->path( $self->{PCORE_USER_BUILD_DIR} )->{path}; $self->{INLINE_DIR} = P->path( $self->{INLINE_DIR} )->{path} if $self->{INLINE_DIR}; # init share $self->{share} = Pcore::Core::Env::Share->new; $self->{START_DIR} = P->path->to_abs->{path}; if ( $Pcore::SCRIPT_PATH eq '-e' || $Pcore::SCRIPT_PATH eq '-' ) { $self->{SCRIPT_NAME} = '-e'; $self->{SCRIPT_DIR} = $self->{START_DIR}; } else { die qq[Cannot find current script "$Pcore::SCRIPT_PATH"] if !-f $Pcore::SCRIPT_PATH; my $path = P->path($Pcore::SCRIPT_PATH)->to_abs; $self->{SCRIPT_NAME} = $path->{filename}; $self->{SCRIPT_DIR} = $path->{dirname}; } $self->{SCRIPT_PATH} = "$self->{SCRIPT_DIR}/$self->{SCRIPT_NAME}"; $self->{TEMP_DIR} = P->path( File::Spec->tmpdir )->{path}; # find main dist if ( $self->{is_par} ) { $self->{main_dist} = Pcore::Dist->new( $ENV{PAR_TEMP} ); } else { $self->{main_dist} = Pcore::Dist->new( $self->{SCRIPT_DIR} ); } # register main dist if ( $self->{main_dist} ) { $self->{main_dist}->{is_main} = 1; $self->register_dist( $self->{main_dist} ); } # set DATA_DIR if ( my $dist = $self->{main_dist} ) { if ( $self->{is_par} ) { $self->{DATA_DIR} = $self->{SCRIPT_DIR}; } else { $self->{DATA_DIR} = P->path("$dist->{root}/data")->{path}; mkdir $self->{DATA_DIR} || die qq[Can't create "$self->{DATA_DIR}"] if !-d $self->{DATA_DIR}; } } else { $self->{DATA_DIR} = $self->{START_DIR}; } # init pcore dist, required for register pcore resources during bootstrap if ( $self->{main_dist} && $self->{main_dist}->is_pcore ) { $self->{pcore} = $self->{main_dist}; } else { $self->{pcore} = Pcore::Dist->new('Pcore.pm'); $self->register_dist( $self->{pcore} ); } # scan deps if ( !$self->{is_par} && defined( my $dist = $self->{main_dist} ) ) { if ( $dist->par_cfg && exists $dist->par_cfg->{ $self->{SCRIPT_NAME} } && !$dist->par_cfg->{ $self->{SCRIPT_NAME} }->{disabled} ) { $self->set_scandeps("$dist->{share_dir}/pardeps-$self->{SCRIPT_NAME}-@{[$^V->normal]}-$Config{archname}.json"); } } return; } sub set_scandeps ( $self, $path ) { $self->{SCANDEPS} = $path; return; } sub _build_user_cfg_path ($self) { return "$self->{PCORE_USER_DIR}/pcore.yaml" } sub _build_user_cfg ($self) { P->file->copy( $ENV->{share}->get('/Pcore/dist-tmpl/pcore.yaml'), $self->user_cfg_path, umask => 'rw' ) if !-f $self->user_cfg_path; return P->cfg->read( $self->user_cfg_path ); } sub register_dist ( $self, $dist ) { # create dist object $dist = Pcore::Dist->new($dist) if !is_ref $dist; # dist was not found die qq[Invlaid Pcore -dist pragma usage, "$dist" is not a Pcore dist main module] if !$dist; # dist is already registered return if exists $self->{dists}->{ $dist->name }; # add dist to the dists index $self->{dists}->{ $dist->name } = $dist; push $self->{dists_order}->@*, $dist; # register dist share lib $self->{share}->register_lib( $dist->name, $dist->{share_dir} ); $Pcore::Core::L10N::PROCESSED = 0; return; } sub dist ( $self, $dist_name = undef ) { if ($dist_name) { return $self->{dists}->{ $dist_name =~ s/::/-/smgr }; } else { return $self->{main_dist}; } } END { if ( $ENV->{SCANDEPS} ) { require Cpanel::JSON::XS; ## no critic qw[Modules::ProhibitEvilModules] my ( $fh, $index ); if ( -f $ENV->{SCANDEPS} ) { open $fh, '+<:raw', $ENV->{SCANDEPS} or die $!; ## no critic qw[InputOutput::RequireBriefOpen] flock $fh, LOCK_EX or die; local $/; my $deps = Cpanel::JSON::XS->new->utf8->decode(<$fh>); $index->@{ $deps->@* } = (); } else { open $fh, '>:raw', $ENV->{SCANDEPS} or die $!; ## no critic qw[InputOutput::RequireBriefOpen] flock $fh, LOCK_EX or die; } my ( $updated, $embedded_packages ); for my $module ( sort keys %INC ) { next if $INC{$module} eq '(embedded)'; if ( !exists $index->{$module} ) { if ( $INC{$module} !~ /\Q$module\E\z/sm ) { $embedded_packages->{$module} = $INC{$module}; } else { $updated = 1; $index->{$module} = undef; say "new dependency found: $module"; } } } # find real module for embedded modules if ($embedded_packages) { for my $embedded_package ( keys $embedded_packages->%* ) { my $added; for my $module ( keys %INC ) { if ( $INC{$module} eq $embedded_packages->{$embedded_package} ) { # embedded package is already added if ( exists $index->{$module} ) { # say "$module ---> $embedded_package"; $added = 1; last; } } } if ( !$added ) { $updated = 1; $index->{$embedded_package} = undef; say qq[new deps found: $embedded_package]; } } } # store deps if ($updated) { truncate $fh, 0 or die; seek $fh, 0, SEEK_SET or die; print {$fh} Cpanel::JSON::XS->new->utf8->canonical->pretty->encode( [ sort keys $index->%* ] ); } close $fh or die; } } 1; ## -----SOURCE FILTER LOG BEGIN----- ## ## PerlCritic profile "pcore-script" policy violations: ## +------+----------------------+----------------------------------------------------------------------------------------------------------------+ ## | Sev. | Lines | Policy | ## |======+======================+================================================================================================================| ## | 3 | | Subroutines::ProhibitExcessComplexity | ## | | 210 | * Subroutine "BUILD1" with high complexity score (23) | ## | | 335 | * Subroutine "END" with high complexity score (23) | ## |------+----------------------+----------------------------------------------------------------------------------------------------------------| ## | 3 | 346 | Variables::RequireInitializationForLocalVars - "local" variable not initialized | ## |------+----------------------+----------------------------------------------------------------------------------------------------------------| ## | 3 | 386 | ControlStructures::ProhibitDeepNests - Code structure is deeply nested | ## |------+----------------------+----------------------------------------------------------------------------------------------------------------| ## | 2 | 413 | ValuesAndExpressions::ProhibitLongChainsOfMethodCalls - Found method-call chain of length 5 | ## +------+----------------------+----------------------------------------------------------------------------------------------------------------+ ## ## -----SOURCE FILTER LOG END----- __END__ =pod =encoding utf8 =head1 NAME Pcore::Core::Env =head1 SYNOPSIS =head1 DESCRIPTION =head1 ATTRIBUTES =head1 METHODS =head1 SEE ALSO =cut