Come posso determinare le dipendenze CPAN prima di distribuire un progetto Perl?
-
04-07-2019 - |
Domanda
Qualcuno ha qualche suggerimento per un buon approccio alla ricerca di tutte le dipendenze CPAN che potrebbero essere sorte in un progetto di sviluppo su misura. Come tende ad accadere, il tuo ambiente di sviluppo locale raramente corrisponde a quello live e mentre costruisci sempre più progetti, tendi a costruire una libreria locale di moduli installati. Questi quindi ti portano a non notare necessariamente che il tuo ultimo progetto ha un requisito su un modulo non core. Poiché in genere è necessario impacchettare l'intero progetto per la distribuzione in un altro gruppo (nel nostro caso il nostro team operativo), è importante sapere quali moduli dovrebbero essere inclusi nel pacchetto.
Qualcuno ha qualche idea sul problema.
Grazie
Peter
Soluzione
Ho avuto questo problema da solo. Devel :: Modlist (come suggerito da questa risposta ) adotta un approccio dinamico. Riporta i moduli che sono stati effettivamente caricati durante una particolare esecuzione dello script. In questo modo i moduli vengono caricati con qualsiasi mezzo, ma potrebbero non soddisfare i requisiti condizionali. Cioè, se hai un codice come questo:
if ($some_condition) { require Some::Module }
e $ some_condition
sembrano essere falsi, Devel :: Modlist
non elencherà Some :: Module
come requisito.
Ho deciso di utilizzare invece Module :: ExtractUse . Esegue un'analisi statica, il che significa che catturerà sempre Some :: Module
nell'esempio sopra. D'altra parte, non può fare nulla per il codice come:
my $module = "Other::Module";
eval "use $module;";
Naturalmente, è possibile utilizzare entrambi gli approcci e quindi combinare le due liste.
Comunque, ecco la soluzione che mi è venuta in mente:
#! /usr/bin/perl
#---------------------------------------------------------------------
# Copyright 2008 Christopher J. Madsen <perl at cjmweb.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either the
# GNU General Public License or the Artistic License for more details.
#
# Recursively collect dependencies of Perl scripts
#---------------------------------------------------------------------
use strict;
use warnings;
use File::Spec ();
use Module::CoreList ();
use Module::ExtractUse ();
my %need;
my $core = $Module::CoreList::version{'5.008'};
# These modules have lots of dependencies. I don't need to see them now.
my %noRecurse = map { Ho avuto questo problema da solo. Devel :: Modlist (come suggerito da questa risposta ) adotta un approccio dinamico. Riporta i moduli che sono stati effettivamente caricati durante una particolare esecuzione dello script. In questo modo i moduli vengono caricati con qualsiasi mezzo, ma potrebbero non soddisfare i requisiti condizionali. Cioè, se hai un codice come questo:
if ($some_condition) { require Some::Module }
e $ some_condition
sembrano essere falsi, Devel :: Modlist
non elencherà Some :: Module
come requisito.
Ho deciso di utilizzare invece Module :: ExtractUse . Esegue un'analisi statica, il che significa che catturerà sempre Some :: Module
nell'esempio sopra. D'altra parte, non può fare nulla per il codice come:
my $module = "Other::Module";
eval "use $module;";
Naturalmente, è possibile utilizzare entrambi gli approcci e quindi combinare le due liste.
Comunque, ecco la soluzione che mi è venuta in mente:
perl finddeps.pl scriptToCheck.pl otherScriptToCheck.pl
Lo faresti come:
<*>
Stampa un elenco di tutti i moduli non core necessari per eseguire gli script elencati. (A meno che non facciano trucchi fantasiosi con il caricamento del modulo che impediscono a Module :: ExtractUse di vederli.)
=> 1 } qw(
Log::Log4perl
XML::Twig
);
foreach my $file (@ARGV) {
findDeps($file);
}
foreach my $module (sort keys %need) {
print " $module\n";
}
#---------------------------------------------------------------------
sub findDeps
{
my ($file) = @_;
my $p = Module::ExtractUse->new;
$p->extract_use($file);
foreach my $module ($p->array) {
next if exists $core->{$module};
next if $module =~ /^5[._\d]+/; # Ignore "use MIN-PERL-VERSION"
next if $module =~ /\$/; # Run-time specified module
if (++$need{$module} == 1 and not $noRecurse{$module}) {
my $path = findModule($module);
if ($path) { findDeps($path) }
else { warn "WARNING: Can't find $module\n" }
} # end if first use of $module
} # end foreach $module used
} # end findDeps
#---------------------------------------------------------------------
sub findModule
{
my ($module) = @_;
$module =~ s!::|\'!/!g;
$module .= '.pm';
foreach my $dir (@INC) {
my $path = File::Spec->catfile($dir, $module);
return $path if -f $path;
}
return;
} # end findModule
Lo faresti come:
<*>Stampa un elenco di tutti i moduli non core necessari per eseguire gli script elencati. (A meno che non facciano trucchi fantasiosi con il caricamento del modulo che impediscono a Module :: ExtractUse di vederli.)
Altri suggerimenti
Puoi utilizzare il servizio web online su deps.cpantesters.org che ti fornirà molte utili dipendenze dati. Tutti i moduli su CPAN hanno già il collegamento al sito delle dipendenze (sul lato destro della pagina del modulo).
In passato ho usato Devel :: Modlist che è ragionevolmente buono permettendo andare
perl -d:Modlist script.pl
Per ottenere un elenco dei moduli richiesti.
Ho un sistema di build basato su Make per tutte le mie applicazioni C / C ++ (sia su PC che per vari progetti integrati), e mentre adoro poter fare una build di alto livello su una macchina nuova e verificare tutto dipendenze sono presenti (controllo le mie toolchain per il controllo di revisione: D), sono stato frustrato di non fare lo stesso per i linguaggi interpretati che attualmente non hanno makefile nel mio sistema di compilazione.
Sono tentato di scrivere una sceneggiatura che:
- cerca nel mio repository di controllo di revisione i file con estensione .pl o .pm
- esegue
perl -d: Modlist
su di loro (grazie Vagnerr!) - concatenandolo all'elenco dei moduli richiesti
- e infine confrontandolo con l'elenco dei moduli installati.
Eseguirò quindi quello script come parte della mia build di livello superiore, in modo che chiunque costruisca qualcosa sappia se ha tutto ciò di cui ha bisogno per eseguire ogni script perl ottenuto dal controllo di revisione. Se c'è qualche script perl che non eseguono mai e non vogliono installare CPAN ciò che è richiesto per eseguirlo, dovrebbero rimuovere lo script indesiderato dal loro hard disk, quindi il controllo delle dipendenze non li trova. So come modificare un client perforce per escludere determinate sottodirectory quando si esegue una "sincronizzazione", dovrò capirlo per sovversione ...
Suggerirei di fare del controllo delle dipendenze un singolo script che cerca i file pl, al contrario di un singolo makefile per controllare le dipendenze per ogni script, o basato su un elenco di nomi di script codificato. Se scegli un metodo che richiede che l'utente esegua il controllo di uno script per le dipendenze, le persone dimenticheranno di eseguire quell'azione, poiché saranno in grado di eseguire lo script anche se non eseguono il controllo delle dipendenze.
Come ho detto, non ho ancora implementato quanto sopra, ma questa domanda mi ha spinto a provare a farlo. Ti riporterò indietro con la mia esperienza dopo aver finito.
Il modo "ovvio" - doloroso ma moderatamente efficace - è quello di installare una nuova build di Perl di base in una posizione fuori mano (non la userai in produzione), quindi provare a installare il tuo usando questa versione "vergine" di Perl. Troverai tutte le dipendenze mancanti. La prima volta, questo potrebbe essere doloroso. Dopo la prima volta, avrai già coperto la maggior parte delle dipendenze e sarà notevolmente meno doloroso.
Prendi in considerazione l'esecuzione del tuo repository locale di moduli CPAN, in modo da non dover scaricare sempre il codice. Considera anche come ripulire i moduli non aggiornati.
use Acme::Magic::Pony;
Scherzi a parte. Installerà automaticamente i moduli Perl se risultano mancanti. Vedi Acme :: Magic: : Pony pagina in CPAN.
È un " cavallo imbullonato " risposta ma ho l'abitudine di creare un file Bundle con tutte le mie dipendenze. Quindi quando vado in un nuovo ambiente, lo copio e lo installo.
Ad es. Ho un Baz.pm
package Bundle::Baz;
$VERSION = '0.1';
1;
__END__
=head1 NAME
Bundle::Baz
=head1 SYNOPSIS
perl -MCPAN -e 'install Bundle::Baz'
=head1 CONTENTS
# Baz's modules
XML::Twig
XML::Writer
Perl6::Say
Moose
Inseriscilo in ~ / .cpan / Bundle / (o ovunque risieda il tuo .cpan) e quindi installa 'Bundle :: Baz' come un normale modulo CPAN. In questo modo vengono installati tutti i moduli elencati in " = head1 INDICE " ;.
Ecco una funzione di quickie bash (usando l'eccellente ack ):
# find-perl-module-use <directory> (lib/ by default)
function find-perl-module-use() {
dir=${1:-lib}
ack '^\s*use\s+.*;\s* $dir | awk '{ print $2 }' | sed 's/();\?$\|;$//' | sort | uniq
ack '^\s*use\s+base\s+.*;\s* $dir | awk '{ print $3 }' | sed 's/();\?$\|;$//' | sort | uniq
}