#!/usr/bin/perl -w # Copyright (c) 2004-2007 Matthew Seaman. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above # copyright notice, this list of conditions and the following # disclaimer. # # 2. Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials # provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # @(#) $Id: find-updated,v 1.18 2007/08/05 16:35:55 matthew Exp $ # # Scan through the ports tree, and pick out all ports containing files # updated after the date given on the command line. Optionally print # names of any ports in the cache that no-longer exist in the tree. # This program doesn't modify the cache at all, but it reads cache # data and produces output that cache-update can use. use strict; use warnings; use BerkeleyDB; use File::Find; use POSIX qw(strftime); use FreeBSD::Portindex::Config qw(read_config); use FreeBSD::Portindex::Port; use FreeBSD::Portindex::Tree; our %Config; our $pkgname = 'portindex'; $0 =~ s@.*/@@; # Script name for error messages # # Given a port origin, check for files with a more recent modification # time than the reference time. If one is found, add this path to the # list of paths to pass to cache-update. Also add any ports that # include an updated Makefile. Return once all files have been # checked. Don't check ./work or other WRKDIR type directories, and # ignore README.html as those are generated files. # sub look_for_updated_files ($$$$) { my $path = shift; my $refmtime = shift; my $updaters = shift; my $tree = shift; my $is_updated = 0; File::Find::find( { preprocess => sub { return grep { !/^(work|README\.html)/ } @_; }, wanted => sub { my ($mtime) = ( lstat($_) )[9]; if ( -f _ && $mtime > $refmtime ) { $is_updated++; map { $updaters->{$_}++ } @{ $tree->makefile_list($File::Find::name) }; warn "$0: ", strftime( '%Y.%m.%d.%H.%M.%S', localtime($mtime) ), " $File::Find::name\n" if $::Config{Verbose}; } } }, $path ); return $is_updated; } # # Scan through the port directories by reading the SUBDIR variables # out of port Makefiles. Remarkably sumilar to # FreeBSD::Portindex::Tree::make_describe() -- that function and this # one should be amalgamated somehow. # sub scan_makefiles ($$$$$) { my $path = shift; my $refmtime = shift; my $updaters = shift; my $allports = shift; my $tree = shift; my $pk; my $sd; my @subdirs; # Hmmm... Using make(1) to print out the value of the variable # (make -V SUBDIRS) takes about 200 times as long as just scanning # the Makefiles for definitions of the SUBDIR variable. Be picky # about the format of the SUBDIR assignment lines: SUBDIR is used # in some of the leaf Makefiles, but in a different style. chdir $path or do { warn "$0: Error. Can't change directory to $path -- $!\n"; return; }; open( MAKE, "/usr/bin/make -V PKGNAME -V SUBDIR|" ) or do { # If $path does not exist, or if there's no Makefile there, # then something has gone horribly wrong. Give up trying to # recurse at this directory level. warn "$0: Error. Can't run make at $path -- $!"; return; # Leave out this directory. }; chomp( $pk = ); # If this is set, it's a port chomp( $sd = ); close MAKE or do { # Even if the close() errors out, we've got this far, so # might as well carry on and try and process any output. warn "$0: Error. $path: ", ( $! ? "close failed -- $!\n" : "make -- bad exit status -- $?\n" ); }; # Note that we've been here. $allports->{$path}++; if ($pk) { # Keep track of which ports we've seen: anything we don't see # but that is known in the cache must be a deleted or # disconnected port. Anything we do see, but that isn't known # in the cache must be a new port. warn "$0: new port found at \'$path\'\n" if ( $::Config{Warnings} && !exists $allports->{$path} ); # This is a real port directory, not a subdir. $updaters->{$path}++ if look_for_updated_files( $path, $refmtime, $updaters, $tree ); } else { @subdirs = map { "${path}/$_" } split( ' ', $sd ); for my $subdir (@subdirs) { # Recursion means never having to obey a function # prototype. &scan_makefiles( $subdir, $refmtime, $updaters, $allports, $tree ); } } return; } # # Add all of the port origins known in the cache, but not visited # during scan_makefiles() to the list updated stuff (on the basis that # those must be ports that have been deleted) # sub deleted_ports($$$$) { my $updaters = shift; my $allports = shift; my $tree = shift; my $refmtime = shift; for my $origin ( keys %{$allports} ) { if ( $allports->{$origin} == 0 ) { # A port can be unhooked from the ports system, but the # directory left in the tree. This means the cache will # still contain the port data -- don't pass the name to # cache-update to recheck unless that directory contains # modified files. On the other hand, the port directory # can be removed completely, and cache-update should look # at that directory so that it will similarly modify the # cache. if ( !-d $origin ) { $updaters->{$origin}++; warn "$0: $origin has been deleted\n" if $::Config{Warnings}; } elsif ( look_for_updated_files( $origin, $refmtime, $updaters, $tree ) ) { $updaters->{$origin}++; warn "$0: $origin was updated but is not connected to ports tree\n" if $::Config{Warnings}; } } } return; } MAIN: { my $tree; my %updaters; my $reference_date; my %allports; read_config( \%Config ); $tree = FreeBSD::Portindex::Tree->new( -Env => { -Home => $Config{CacheDir}, }, -CacheFilename => $Config{CacheFilename}, ); # Redirect STDOUT if required if ( $Config{Output} ne '-' ) { open STDOUT, '>', $Config{Output} or die "$0: Can't open output \'$Config{Output}\' -- $!\n"; } $tree->port_origins( \%allports ); $tree->init_makefile_list(); # Check the standard ports makefiles in /usr/ports/Mk -- updates # to these generally affect a lot of ports. look_for_updated_files( "$Config{PortsDir}/Mk", $Config{ReferenceTime}, \%updaters, $tree ); # Scan through all ports, looking for files newer than the cut-off # date. scan_makefiles( $Config{PortsDir}, $Config{ReferenceTime}, \%updaters, \%allports, $tree ); # Add all of the deleted ports to the list of stuff to update deleted_ports( \%updaters, \%allports, $tree, $Config{ReferenceTime} ); print join( "\n", sort keys %updaters ), "\n" if %updaters; } __END__ =head1 NAME find-updated -- generate a list of those ports depending on files that have been modified since the given date/time. =head1 SYNOPSIS B [B<-hvq>] [B<-c> F] [B<-C> F] [B<-T> F] [B<-p> F] [B<-o> F] date =head1 DESCRIPTION B scans through the whole ports tree and checks each port origin and the F directory (or F<${PORTSDIR}/Mk> if C<$PORTSDIR> is set in the environment) for files modified after the date given on the command line. The output consists of a list of modified ports, printed one per line, suitable for feeding into B using plain format. The generated list of updated ports takes into account any master-slave relationships between ports and any ports that include Makefiles modified in the given period. Only file modification times are checked, not directories. Files named F or any F subdirectories are ignored. The date specification must be given strictly in the B-style of I with four digits for the year, and two each for the month, day of the month, hour (24 hour clock), minutes and seconds. The local timezone is assumed. To print out a date in the required format, use: date +%Y.%m.%d.%H.%M.%S B does a further level of checking by comparing the list of port origins obtained from the cache with all of the ports it scans for modifications. Any ports listed in the cache, but either not present in the ports tree (a I port), or not referenced from the sub-directory Fs and containing modified files (a I port) will be added to the output. B will recognise if the port has been deleted or not and modify the cache accordingly. =head2 Configuration Files B shares configuration files with B, B and B. Any configuration settings are taken from the following locations, where the later items on this list override the earlier: =over 8 =item * Built-in settings from the B perl module. =item * The system wide configuration file F =item * The per-user configuration file F<${HOME}/.portindexrc>. This file is ignored if the process is run as root. =item * The local configuration file, found in the current working directory of the B process F<./.portindexrc>. This file is ignored if the process is run as root. =item * The program command line. =back All of the configuration files are optional. A summary of the resultant configuration options including the effect of any command line settings is printed as part of the help text when B is invoked with the C<-h> option. =head1 OPTIONS =over 8 =item B<-h> =item B<--help> Print a brief usage message and a summary of the configuration settings after command line processing and then exit. =item B<-v> =item B<--verbose> Turn on verbose output printed to C. This is the default. =item B<-q> =item B<--quiet> =item B<--noverbose> Turn off verbose output to C. Using both the B<-v> amd B<-q> options together does not make any sense, but neither does it generate an error. The last mentioned of the two options will prevail. =item B<-c> F =item B<--cache-dir>=F The location of the B data cache, by default F. =item B<-C> F =item B<--cache-file>=F Berkeley DB Btree file containing the cached and post-processed values of a number of C variables for all of the ports in the tree. This file name will be relative to the cache directory (B<-c> option above) unless an absolute path is given. Defaults to F. =item B<-T> F =item B<--timestamp-file>=F A file within the cache directory whose modification time marks the last time that data was modified in or added to the cache. Defaults to F =item B<-p> F =item B<--ports-dir>=F The location of the ports tree. Almost always defaults to F unless C<$PORTSDIR> is set in the environment. =item B<-o> F =item B<--output>=F Filename to write the generated list of updated ports to. Setting this to F<-> means output to STDOUT, which is the default. =back =head1 FILES =over 16 =item F The default ports directory. =item F The location of the data cache. =item F Btree file containing cached C output. =item F<__db.001>, F<__db.002>, F<__db.003>, F<__db.004> Files used as part of the internal workings of BerkeleyDB, for memory pool management and DB locking. Will be recreated automatically if deleted. =item F This file contains the last time and date that the cache was updated or modified. =item F System-wide configuration file. =item F<${HOME}/.portindexrc> Per-user configuration file =item F<./.portindexrc> Local configuration file =back =head1 SEE ALSO L, L, L, L, L =head1 BUGS B does not distinguish ports that are I -- ie. that do not have an entry in the C list in the immediately superior category. Such ports will have an entry in the cache, but the default is not to include them in the resulting F. =cut # # That's All Folks! #