Annotation of loncom/build/make_rpm.pl, revision 1.21

1.1       harris41    1: #!/usr/bin/perl
                      2: 
1.16      harris41    3: # -------------------------------------------------------- Documentation notice
                      4: # Run "perldoc ./make_rpm.pl" in order to best view the software documentation
                      5: # internalized in this program.
                      6: 
                      7: # --------------------------------------------------------- License Information
1.12      harris41    8: # The LearningOnline Network with CAPA
1.13      harris41    9: # make_rpm.pl - make RedHat package manager file (A CLEAN AND CONFIGURABLE WAY)
1.12      harris41   10: #
1.21    ! harris41   11: # $Id$
1.12      harris41   12: #
                     13: # Written by Scott Harrison, harris41@msu.edu
                     14: #
                     15: # Copyright Michigan State University Board of Trustees
                     16: #
1.21    ! harris41   17: # This file was written to help the LearningOnline Network with CAPA (LON-CAPA)
        !            18: # project.
1.12      harris41   19: #
                     20: # LON-CAPA is free software; you can redistribute it and/or modify
                     21: # it under the terms of the GNU General Public License as published by
                     22: # the Free Software Foundation; either version 2 of the License, or
                     23: # (at your option) any later version.
                     24: #
                     25: # LON-CAPA is distributed in the hope that it will be useful,
                     26: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     27: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     28: # GNU General Public License for more details.
                     29: #
                     30: # You should have received a copy of the GNU General Public License
                     31: # along with LON-CAPA; if not, write to the Free Software
                     32: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     33: #
                     34: # http://www.lon-capa.org/
                     35: #
                     36: # YEAR=2000
                     37: # 9/30,10/2,12/11,12/12,12/21 - Scott Harrison
                     38: # YEAR=2001
                     39: # 1/8,1/10,1/13,1/23,5/16 - Scott Harrison
                     40: # YEAR=2002
1.21    ! harris41   41: # 1/4,1/8,1/9,2/13,4/7,12/18 - Scott Harrison
1.12      harris41   42: #
                     43: ###
                     44: 
1.15      harris41   45: # make_rpm.pl automatically generate RPM software packages
                     46: # from a target image directory and file listing.  POD
                     47: # documentation is at the end of this file.
1.1       harris41   48: 
1.13      harris41   49: ###############################################################################
                     50: ##                                                                           ##
                     51: ## ORGANIZATION OF THIS PERL SCRIPT                                          ##
                     52: ##                                                                           ##
                     53: ## 1. Check to see if RPM builder application is available                   ##
1.16      harris41   54: ## 2. Read in command-line arguments                                         ##
                     55: ## 3. Generate temporary directories (subdirs of first command-line argument)##
1.13      harris41   56: ## 4. Initialize some variables                                              ##
1.16      harris41   57: ## 5. Create a stand-alone rpm building environment                          ##
1.13      harris41   58: ## 6. Perform variable initializations and customizations                    ##
                     59: ## 7. Print header information for .spec file                                ##
                     60: ## 8. Process file list and generate information                             ##
                     61: ## 9. Generate SRPM and BinaryRoot Makefiles                                 ##
                     62: ## 10. mirror copy (BinaryRoot) files under a temporary directory            ##
                     63: ## 11. roll everything into an rpm                                           ##
                     64: ## 12. clean everything up                                                   ##
1.15      harris41   65: ## 13. subroutines                                                           ##
                     66: ## 13a. find_info - recursively gather information from a directory          ##
                     67: ## 13b. grabtag - grab a tag from an XML string                              ##
1.13      harris41   68: ## 14. Plain Old Documentation                                               ##
                     69: ##                                                                           ##
                     70: ###############################################################################
                     71: 
1.21    ! harris41   72: my $VERSION = 2.0;
        !            73: 
1.13      harris41   74: use strict;
                     75: 
                     76: # ------------------------ Check to see if RPM builder application is available
                     77: 
1.21    ! harris41   78: unless (-e '/usr/lib/rpm/rpmrc') # part of the expected rpm software package
        !            79:   {
1.16      harris41   80:     print(<<END);
                     81: **** ERROR **** This script only works with a properly installed RPM builder
1.12      harris41   82: application.  
1.1       harris41   83: Cannot find /usr/lib/rpm/rpmrc, so cannot generate customized rpmrc file.
                     84: Script aborting.
                     85: END
1.16      harris41   86:     exit(1);
1.21    ! harris41   87:   }
1.1       harris41   88: 
1.16      harris41   89: # ---------------------------------------------- Read in command-line arguments
1.13      harris41   90: 
1.21    ! harris41   91: my ($tag,$version,$release,$configuration_files,$documentation_files,
1.13      harris41   92:     $pathprefix,$customize)=@ARGV;
1.16      harris41   93: @ARGV=(); # read standard input based on a pipe, not a command-line argument
1.18      harris41   94: 
                     95: # standardize pathprefix argument
                     96: $pathprefix=~s/\/$//; # OTHERWISE THE BEGINNING SLASH MIGHT BE REMOVED
1.3       harris41   97: 
1.21    ! harris41   98: if (!$version) # version should be defined and string length greater than zero
        !            99:   {
1.16      harris41  100:     print(<<END);
1.13      harris41  101: See "perldoc make_rpm.pl" for more information.
                    102: 
1.15      harris41  103: Usage: 
1.21    ! harris41  104:            <STDIN> | perl make_rpm.pl <TAG> <VERSION> <RELEASE>
        !           105: 	   [CONFIGURATION_FILES] [DOCUMENTATION_FILES] [PATHPREFIX]
        !           106: 	   [CUSTOMIZATION_XML]
1.15      harris41  107: 
1.13      harris41  108: Standard input provides the list of files to work with.
                    109: TAG, required descriptive tag.  For example, a kerberos software
1.16      harris41  110: package might be tagged as "krb4". (This value is also used in
                    111: the generation of a temporary directory; you cannot have
                    112: a pre-existing directory named ./TAG.)
1.13      harris41  113: VERSION, required version.  Needed to generate version information
1.16      harris41  114: for the RPM.  It is recommended that this be in the format N.M where N and
1.21    ! harris41  115: M are integers.  For example, 0.3, 4.1, and 8.9 are all valid version numbers.
        !           116: RELEASE, required release number.  Needed to generate release information
        !           117: for the RPM.  This is typically an integer, but can also be given descriptive
        !           118: text such as 'rh7' or 'mandrake8a'.
1.13      harris41  119: CONFIGURATION_FILES, optional comma-separated listing of files to
                    120: be treated as configuration files by RPM (and thus subject to saving
                    121: during RPM upgrades).
                    122: DOCUMENTATION_FILES, optional comma-separated listing of files to be
                    123: treated as documentation files by RPM (and thus subject to being
                    124: placed in the /usr/doc/RPM-NAME directory during RPM installation).
                    125: PATHPREFIX, optional path to be removed from file listing.  This
                    126: is in case you are building an RPM from files elsewhere than
                    127: root-level.  Note, this still depends on a root directory hierarchy
                    128: after PATHPREFIX.
                    129: CUSTOMIZATION_XML, allows for customizing various pieces of information such
                    130: as vendor, summary, name, copyright, group, autoreqprov, requires, prereq,
1.16      harris41  131: description, and pre-installation scripts (see more in the POD,
                    132: "perldoc make_rpm.pl").
1.13      harris41  133: END
1.16      harris41  134:     exit(1);
1.21    ! harris41  135:   }
1.1       harris41  136: 
1.16      harris41  137: # ----- Generate temporary directories (subdirs of first command-line argument)
                    138: 
                    139: # Do some error-checking related to important first command-line argument.
1.21    ! harris41  140: if ($tag=~/[^\w-]/) # non-alphanumeric characters cause problems
        !           141:   {
1.16      harris41  142:     print(<<END);
                    143: **** ERROR **** Invalid tag name "$tag"
                    144: (The first command-line argument must be alphanumeric characters without
                    145: spaces.)
                    146: END
                    147:     exit(1);
1.21    ! harris41  148:   }
        !           149: if (-e $tag) # do not overwrite or conflict with existing data
        !           150:   {
1.16      harris41  151:     print(<<END);
                    152: **** ERROR **** a file or directory "./$tag" already exists
                    153: (This program needs to generate a temporary directory named "$tag".)
                    154: END
                    155:     exit(1);
1.21    ! harris41  156:   }
1.16      harris41  157: 
                    158: print('Generating temporary directory ./'.$tag."\n");
                    159: mkdir($tag,0755) or die("**** ERROR **** cannot generate $tag directory\n");
                    160: mkdir("$tag/BuildRoot",0755);
                    161: mkdir("$tag/SOURCES",0755);
                    162: mkdir("$tag/SPECS",0755);
                    163: mkdir("$tag/BUILD",0755);
                    164: mkdir("$tag/SRPMS",0755);
                    165: mkdir("$tag/RPMS",0755);
                    166: mkdir("$tag/RPMS/i386",0755);
1.1       harris41  167: 
1.13      harris41  168: # -------------------------------------------------------- Initialize variables
                    169: 
1.1       harris41  170: my $file;
1.16      harris41  171: my $binaryroot=$tag.'/BinaryRoot';
1.1       harris41  172: my ($type,$size,$octalmode,$user,$group);
                    173: 
1.16      harris41  174: my $currentdir=`pwd`; chomp($currentdir); my $invokingdir=$currentdir;
                    175: $currentdir.='/'.$tag;
1.1       harris41  176: 
1.16      harris41  177: # ------------------------------- Create a stand-alone rpm building environment
1.13      harris41  178: 
1.16      harris41  179: print('Creating stand-alone rpm build environment.'."\n");
                    180: open(IN,'</usr/lib/rpm/rpmrc') or die('Cannot open /usr/lib/rpm/rpmrc'."\n");
1.13      harris41  181: my @lines=<IN>;
1.16      harris41  182: close(IN);
1.1       harris41  183: 
1.16      harris41  184: open(RPMRC,">$tag/SPECS/rpmrc");
1.21    ! harris41  185: foreach my $line (@lines)
        !           186:   {
        !           187:     if ($line=~/^macrofiles/)
        !           188:       {
1.16      harris41  189: 	chomp($line);
1.11      harris41  190: 	$line.=":$currentdir/SPECS/rpmmacros\n";
1.21    ! harris41  191:       }
1.16      harris41  192:     print(RPMRC $line);
1.21    ! harris41  193:   }
1.16      harris41  194: close(RPMRC);
1.1       harris41  195: 
1.16      harris41  196: open(RPMMACROS,">$tag/SPECS/rpmmacros");
                    197: print(RPMMACROS <<END);
1.1       harris41  198: \%_topdir $currentdir
                    199: \%__spec_install_post    \\
                    200:     /usr/lib/rpm/brp-strip \\
                    201:     /usr/lib/rpm/brp-strip-comment-note \\
                    202: \%{nil}
                    203: END
1.16      harris41  204: close(RPMMACROS);
1.1       harris41  205: 
1.13      harris41  206: # ------------------------- Perform variable initializations and customizations
                    207: 
1.16      harris41  208: my $cu=''; # string that holds customization XML file contents
1.21    ! harris41  209: if (length($customize)>0)
        !           210:   {
1.16      harris41  211:     print('Reading in XML-formatted customizations from '.$customize."\n");
                    212:     open(IN,"<$customize") or
                    213:     (
                    214:      print(`cd $invokingdir; rm -Rf $tag`) and
                    215:      die('Cannot open customization file "'.$customize.'"'."\n")
                    216:     );
1.13      harris41  217:     my @clines=(<IN>);
                    218:     $cu=join('',@clines);
1.16      harris41  219:     close(IN);
1.21    ! harris41  220:   }
1.13      harris41  221: 
1.16      harris41  222: # tv - temporary variable (if it exists inside the XML document) then use it,
                    223: # otherwise don't overwrite existing values of variables
                    224: my $tv='';
                    225: 
                    226: # (Sure. We could use HTML::TokeParser here... but that wouldn't be fun now,
1.13      harris41  227: # would it?)
                    228: my $name=$tag;
                    229: # read in name from customization if available
                    230: $tv=grabtag('name',$cu,1); $name=$tv if $tv;
                    231: $name=~s/\<tag \/\>/$tag/g;
                    232: 
1.16      harris41  233: # (When in doubt, be paranoid about overwriting things.)
1.21    ! harris41  234: if (-e "$name-$version-1.i386.rpm")
        !           235:   {
1.16      harris41  236:     print(`cd $invokingdir; rm -Rf $tag`); # clean temporary filespace in use
                    237:     die("**** ERROR **** $name-$version-1.i386.rpm already exists.\n");
1.21    ! harris41  238:   }
1.13      harris41  239: 
1.16      harris41  240: my $requires='';
1.13      harris41  241: # read in relevant requires info from customization file (if applicable)
                    242: # note that "PreReq: item" controls order of CD-ROM installation (if you
                    243: # are making a customized CD-ROM)
                    244: # "Requires: item" just enforces dependencies from the command-line invocation
                    245: $tv=grabtag('requires',$cu,1); $requires=$tv if $tv;
                    246: # do more require processing here
                    247: $requires=~s/\s*\<\/item\>\s*//g;
                    248: $requires=~s/\s*\<item\>\s*/\n/g;
                    249: $requires=~s/^\s+//s;
                    250: 
1.16      harris41  251: my $summary='Files for the '.$name.' software package.';
1.13      harris41  252: # read in summary from customization if available
                    253: $tv=grabtag('summary',$cu,1); $summary=$tv if $tv;
                    254: $summary=~s/\<tag \/\>/$tag/g;
                    255: 
1.14      harris41  256: my $autoreqprov='no';
                    257: # read in autoreqprov from customization if available
                    258: $tv=grabtag('autoreqprov',$cu,1); $autoreqprov=$tv if $tv;
                    259: 
1.13      harris41  260: my $copyright="not specified here";
                    261: # read in copyright from customization if available
                    262: $tv=grabtag('copyright',$cu,1); $copyright=$tv if $tv;
                    263: $copyright=~s/\<tag \/\>/$tag/g;
                    264: 
1.21    ! harris41  265: my $rpmgroup="Utilities/System";
        !           266: # read in copyright from customization if available
        !           267: $tv=grabtag('group',$cu,1); $rpmgroup=$tv if $tv;
        !           268: $rpmgroup=~s/\<tag \/\>/$tag/g;
1.13      harris41  269: 
                    270: my $vendor='Me';
                    271: # read in vendor from customization if available
                    272: $tv=grabtag('vendor',$cu,1); $vendor=$tv if $tv;
                    273: $vendor=~s/\<tag \/\>/$tag/g;
                    274: 
                    275: my $description="$name software package";
                    276: # read in description from customization if available
                    277: $tv=grabtag('description',$cu,0); $description=$tv if $tv;
                    278: $description=~s/\<tag \/\>/$tag/g;
                    279: 
1.16      harris41  280: my $pre='';
1.13      harris41  281: # read in pre-installation script if available
                    282: $tv=grabtag('pre',$cu,0); $pre=$tv if $tv;
                    283: $pre=~s/\<tag \/\>/$tag/g;
1.1       harris41  284: 
1.13      harris41  285: # ------------------------------------- Print header information for .spec file
1.19      harris41  286: print('Print header information for .spec file'."\n");
1.12      harris41  287: 
1.21    ! harris41  288: open(SPEC,">$tag/SPECS/$name-$version.spec");
1.16      harris41  289: print(SPEC <<END);
1.13      harris41  290: Summary: $summary
                    291: Name: $name
1.1       harris41  292: Version: $version
1.21    ! harris41  293: Release: $release
1.12      harris41  294: Vendor: $vendor
1.1       harris41  295: BuildRoot: $currentdir/BuildRoot
1.13      harris41  296: Copyright: $copyright
1.21    ! harris41  297: Group: $rpmgroup
1.13      harris41  298: Source: $name-$version.tar.gz
1.14      harris41  299: AutoReqProv: $autoreqprov
1.7       harris41  300: $requires
1.1       harris41  301: # requires: filesystem
                    302: \%description
1.13      harris41  303: $description
1.1       harris41  304: 
                    305: \%prep
                    306: \%setup
                    307: 
                    308: \%build
                    309: rm -Rf "$currentdir/BuildRoot"
                    310: 
                    311: \%install
                    312: make ROOT="\$RPM_BUILD_ROOT" SOURCE="$currentdir/BinaryRoot" directories
                    313: make ROOT="\$RPM_BUILD_ROOT" SOURCE="$currentdir/BinaryRoot" files
                    314: make ROOT="\$RPM_BUILD_ROOT" SOURCE="$currentdir/BinaryRoot" links
                    315: 
                    316: \%pre
1.13      harris41  317: $pre
1.1       harris41  318: 
                    319: \%post
                    320: \%postun
                    321: 
                    322: \%files
                    323: END
                    324: 
1.13      harris41  325: # ------------------------------------ Process file list and gather information
1.19      harris41  326: print('Process standard input file list and gather information.'."\n");
1.13      harris41  327: 
                    328: my %BinaryRootMakefile;
                    329: my %Makefile;
                    330: my %dotspecfile;
                    331: 
1.21    ! harris41  332: foreach my $file (<>)
        !           333:   {
1.16      harris41  334:     chomp($file);
1.4       harris41  335:     my $comment="";
1.21    ! harris41  336:     if ($file=~/\s+\#(.*)$/)
        !           337:       {
1.4       harris41  338: 	$file=~s/\s+\#(.*)$//;
                    339: 	$comment=$1;
1.21    ! harris41  340:       }
1.13      harris41  341:     my $directive="";
1.21    ! harris41  342:     if ($comment=~/config\(noreplace\)/)
        !           343:       {
1.13      harris41  344: 	$directive="\%config(noreplace) ";
1.21    ! harris41  345:       }
        !           346:     elsif ($comment=~/config/)
        !           347:       {
1.13      harris41  348: 	$directive="\%config ";
1.21    ! harris41  349:       }
        !           350:     elsif ($comment=~/doc/)
        !           351:       {
1.13      harris41  352: 	$directive="\%doc";
1.21    ! harris41  353:       }
        !           354:     if (($type,$size,$octalmode,$user,$group)=find_info($file))
        !           355:       {
1.1       harris41  356: 	$octalmode="0" . $octalmode if length($octalmode)<4;
1.21    ! harris41  357: 	if ($pathprefix)
        !           358:           {
1.1       harris41  359: 	    $file=~s/^$pathprefix//;
1.21    ! harris41  360: 	  }
        !           361: 	if ($type eq "files")
        !           362:           {
1.16      harris41  363: 	    push(@{$BinaryRootMakefile{$type}},"\tinstall -D -m $octalmode ".
                    364: 		 "$pathprefix$file $binaryroot$file\n");
                    365: 	    push(@{$Makefile{$type}},"\tinstall -D -m $octalmode ".
                    366: 		 "\$(SOURCE)$file \$(ROOT)$file\n");
                    367: 	    push(@{$dotspecfile{$type}},"$directive\%attr($octalmode,$user,".
                    368: 		 "$group) $file\n");
1.21    ! harris41  369: 	  }
        !           370: 	elsif ($type eq "directories")
        !           371:           {
1.16      harris41  372: 	    push(@{$BinaryRootMakefile{$type}},"\tinstall -m $octalmode -d ".
                    373: 		 "$binaryroot$file\n");
                    374: 	    push(@{$Makefile{$type}},"\tinstall -m $octalmode -d ".
                    375: 		 "\$(SOURCE)$file \$(ROOT)$file\n");
                    376: 	    push(@{$dotspecfile{$type}},"\%dir \%attr($octalmode,$user,".
                    377: 		 "$group) $file\n");
1.21    ! harris41  378: 	  }
        !           379: 	elsif ($type eq "links")
        !           380:           {
1.12      harris41  381: 	    my $link=$size; # I use the size variable to pass the link value
                    382:                             # from the subroutine find_info
1.1       harris41  383: 	    $link=~s/^$pathprefix//;
1.16      harris41  384: 	    push(@{$BinaryRootMakefile{$type}},
                    385: 	         "\tln -s $link $binaryroot$file\n");
                    386: 	    push(@{$Makefile{$type}},"\tln -s $link \$(ROOT)$file\n");
                    387: 	    push(@{$dotspecfile{$type}},"\%attr(-,$user,$group) $file\n");
1.21    ! harris41  388: 	  }
        !           389:       }
        !           390:   }
1.1       harris41  391: 
1.13      harris41  392: # -------------------------------------- Generate SRPM and BinaryRoot Makefiles
1.19      harris41  393: print('Generate SRPM and BinaryRoot Makefiles.'."\n");
1.13      harris41  394: 
1.16      harris41  395: # Generate a much needed directory.
                    396: # This directory is meant to hold all source code information
                    397: # necessary for converting .src.rpm files into .i386.rpm files.
                    398: mkdir("$tag/SOURCES/$name-$version",0755);
                    399: 
                    400: open(OUTS,">$tag/SOURCES/$name-$version/Makefile");
                    401: open(OUTB, ">$tag/BinaryRootMakefile");
1.21    ! harris41  402: foreach $type ("directories","files","links")
        !           403:   {
1.16      harris41  404:     print(OUTS "$type\:\n");
                    405:     print(OUTS join("",@{$Makefile{$type}})) if $Makefile{$type};
                    406:     print(OUTS "\n");
                    407:     print(OUTB "$type\:\n");
                    408:     print(OUTB join("",@{$BinaryRootMakefile{$type}}))
1.13      harris41  409: 	if $BinaryRootMakefile{$type};
1.16      harris41  410:     print(OUTB "\n");
                    411:     print(SPEC join("",@{$dotspecfile{$type}})) if $dotspecfile{$type};
1.21    ! harris41  412:   }
1.16      harris41  413: close(OUTB);
                    414: close(OUTS);
1.1       harris41  415: 
1.16      harris41  416: close(SPEC);
1.1       harris41  417: 
1.13      harris41  418: # ------------------ mirror copy (BinaryRoot) files under a temporary directory
1.19      harris41  419: print('Mirror copy (BinaryRoot) files.'."\n");
1.1       harris41  420: 
                    421: `make -f $tag/BinaryRootMakefile directories`;
                    422: `make -f $tag/BinaryRootMakefile files`;
                    423: `make -f $tag/BinaryRootMakefile links`;
                    424: 
1.13      harris41  425: # ------------------------------------------------- roll everything into an RPM
1.16      harris41  426: print('Build a tarball and then run the rpm -ba command.'."\n");
1.13      harris41  427: my $command="cd $currentdir/SOURCES; tar czvf $name-$version.tar.gz ".
                    428:     "$name-$version";
1.16      harris41  429: print(`$command`);
1.21    ! harris41  430: 
        !           431: # ----------------------------------------- Define commands to be executed.
        !           432: # command1a works for rpm version <=4.0.2
        !           433: # command1b works for rpm version >4.0.4
        !           434: 
        !           435: my $arch = 'i386';
        !           436: 
        !           437: my $command1a="cd $currentdir/SPECS; rpm --rcfile=./rpmrc --target=$arch -ba ".
        !           438:     "$name-$version.spec";
        !           439: 
        !           440: my $rpmcommand = 'rpm';
        !           441: if (`rpmbuild --version`) {$rpmcommand = 'rpmbuild';}
        !           442: my $command1b="cd $currentdir/SPECS; $rpmcommand --rcfile=./rpmrc ".
        !           443:     "-ba --target $arch ".
        !           444:     "$name-$version.spec";
        !           445: 
        !           446: # ---------------------------------------------- Run the "rpm -ba" command.
        !           447: # The strategy here is to...try one approach, and then the other.
        !           448: print "$command1a\n";
        !           449: print (`$command1a`);
        !           450: if ($?!=0)
        !           451:   {
        !           452:     print(<<END);
        !           453: **** WARNING **** RPM compilation failed for rpm version 4.0.2 command syntax
        !           454: (...trying another command syntax...)
        !           455: END
        !           456:     print "$command1b\n";
        !           457:     print (`$command1b`);
        !           458:     if ($?!=0)
        !           459:       {
        !           460:    	print(<<END);
        !           461: **** ERROR **** RPM compilation failed for rpm version 4.0.4 command syntax
        !           462: (...no more syntax choices to try...)
        !           463: END
        !           464:         exit(1);
        !           465:       }
        !           466:   }
        !           467: 
        !           468: # -------------------------------------------------------- Retrieve binary rpm.
        !           469: $command="cd $currentdir/RPMS/$arch; cp -v ".
        !           470:     "$name-$version-$release.$arch.rpm $invokingdir/.";
1.16      harris41  471: print(`$command`);
1.13      harris41  472: 
                    473: # --------------------------------------------------------- clean everything up
1.16      harris41  474: print('Removing temporary ./'.$tag.' directory'."\n");
                    475: print(`cd $invokingdir; rm -Rf $tag`);
1.13      harris41  476: 
1.16      harris41  477: # -------------------------------------------------------- Yeah! We're all done
                    478: print('Success. Script complete.'."\n");
1.1       harris41  479: 
1.15      harris41  480: # ----------------------------------------------------------------- SUBROUTINES
1.13      harris41  481: # ----- Subroutine: find_info - recursively gather information from a directory
1.21    ! harris41  482: sub find_info
        !           483:   {
1.1       harris41  484:     my ($file)=@_;
1.16      harris41  485:     my $line='';
1.21    ! harris41  486:     my $safefile = $file;
        !           487:     $safefile =~ s/\+/\\+/g; # Better regular expression matching.
        !           488:     if (($line=`find $file -type f -prune`)=~/^$safefile\n/)
        !           489:       {
1.1       harris41  490: 	$line=`find $file -type f -prune -printf "\%s\t\%m\t\%u\t\%g"`;
1.16      harris41  491: 	return("files",split(/\t/,$line));
1.21    ! harris41  492:       }
        !           493:     elsif (($line=`find $file -type d -prune`)=~/^$safefile\n/)
        !           494:       {
1.1       harris41  495: 	$line=`find $file -type d -prune -printf "\%s\t\%m\t\%u\t\%g"`;
1.16      harris41  496: 	return("directories",split(/\t/,$line));
1.21    ! harris41  497:       }
        !           498:     elsif (($line=`find $file -type l -prune`)=~/^$safefile\n/)
        !           499:       {
1.6       harris41  500: 	$line=`find $file -type l -prune -printf "\%l\t\%m\t\%u\t\%g"`;
1.16      harris41  501: 	return("links",split(/\t/,$line));
1.21    ! harris41  502:       }
1.16      harris41  503:     die("**** ERROR **** $file is neither a directory, soft link, or file.\n");
1.21    ! harris41  504:   }
1.13      harris41  505: 
                    506: # ------------------------- Subroutine: grabtag - grab a tag from an xml string
1.21    ! harris41  507: sub grabtag
        !           508:   {
1.13      harris41  509:     my ($tag,$text,$clean)=@_;
                    510:     # meant to be quick and dirty as opposed to a formal state machine parser
1.16      harris41  511:     my $value='';
1.13      harris41  512:     $cu=~/\<$tag\>(.*?)\<\/$tag\>/s; 
                    513:     $value=$1; $value=~s/^\s+//;
1.21    ! harris41  514:     if ($clean==1)
        !           515:       {
1.13      harris41  516: 	$value=~s/\n\s/ /g;
                    517: 	$value=~s/\s\n/ /g;
                    518: 	$value=~s/\n/ /g;
                    519: 	$value=~s/\s+$//;
1.21    ! harris41  520:       }
1.16      harris41  521:     return($value);
1.21    ! harris41  522:   }
1.13      harris41  523: 
                    524: # ----------------------------------------------------- Plain Old Documentation
1.20      harris41  525: 
                    526: =pod
1.13      harris41  527: 
                    528: =head1 NAME
                    529: 
1.16      harris41  530: make_rpm.pl - cleanly generate an rpm in a simple one-line command
1.13      harris41  531: 
                    532: =head1 SYNOPSIS
                    533: 
1.21    ! harris41  534: Usage: <STDIN> | make_rpm.pl <TAG> <VERSION> <RELEASE>
1.16      harris41  535:        [CONFIGURATION_FILES] [DOCUMENTATION_FILES]
                    536:        [PATHPREFIX] [CUSTOMIZATION_XML]
                    537: 
                    538: =head2 The standard input stream
                    539: 
                    540: I<STDIN>, the standard input stream, provides the list of files to work
                    541: with.  This list of file names must give the complete filesystem
                    542: path starting from '/'.
                    543: 
                    544: =over 4
                    545: 
                    546: =item * For instance, the following is invalid:
                    547: 
                    548:  romeodir/file1.txt # ** INVALID! ** missing leading filesystem path
                    549:  romeodir/file2.txt
                    550:  romeodir/file3.txt
                    551: 
                    552: =item * Whereas, the following is valid:
                    553: 
                    554:  /home/joe/romeodir/file1.txt
                    555:  /home/joe/romeodir/file2.txt
                    556:  /home/joe/romeodir/file3.txt
                    557: 
                    558: =item * In terms of the B<find> command,
                    559: 
                    560:  "find romeodir | perl make_rpm.pl [COMMAND-LINE ARGUMENTS]"
                    561: 
                    562: is incorrect, whereas
                    563: 
                    564:  "find /home/joe/romeodir |perl make_rpm.pl [COMMAND-LINE ARGUMENTS]"
                    565: 
                    566: or
                    567: 
                    568:  "find `pwd`/romeodir |perl make_rpm.pl [COMMAND-LINE ARGUMENTS]"
                    569: 
                    570: is correct.
                    571: 
                    572: =back
                    573: 
                    574: The standard input stream can also
                    575: specify configuration files and documentation files through
                    576: '#'-style commenting.
                    577: 
                    578: For example, the following file listing encodes some of these directives:
                    579: 
                    580:  /home/joe/romeodir/buildloc/etc/romeo/user.conf # config(noreplace)
                    581:  /home/joe/romeodir/buildloc/etc/romeo/juliet.conf # config
                    582:  /home/joe/romeodir/buildloc/doc/man/man.1/romeo.1 # doc
                    583:  /home/joe/romeodir/buildloc/doc/man/man.1/romeo_talks.1 # doc
                    584:  /home/joe/romeodir/buildloc/usr/local/bin/where_art_thou
                    585:  /home/joe/romeodir/buildloc/usr/local/bin/romeo_talks
                    586: 
                    587: The I<config> directive controls how files are replaced
                    588: and/or backed up when a user attempts to install (B<rpm -i>) the F<.rpm>
                    589: file generated by B<make_rpm.pl>.  The I<doc> directive controls how a
                    590: given file is placed inside special documentation directories
                    591: on the filesystem during rpm installation (B<rpm -i>).
                    592: (If you want to learn more on how the B<rpm> tool gives configuration and
                    593: documentation files special treatment, you should read about "Directives"
                    594: in Edward Bailey's well-known "Maximum RPM" book available online
                    595: at http://www.rpm.org/max-rpm/s1-rpm-inside-files-list-directives.html.)
1.13      harris41  596: 
1.16      harris41  597: =head2 Description of command-line arguments
1.13      harris41  598: 
1.16      harris41  599: I<TAG> ($tag), B<required> descriptive tag.  For example, a kerberos software
1.13      harris41  600: package might be tagged as "krb4".
                    601: 
1.16      harris41  602: I<VERSION> ($version), B<required> version.  Needed to generate version
                    603: information for the RPM.  This should be in the format N.M where N and M are
1.13      harris41  604: integers.
                    605: 
1.21    ! harris41  606: I<RELEASE> ($release), B<required> release number.  Needed to generate release
        !           607: information for the RPM.  This is typically an integer, but can also be given
        !           608: descriptive text such as 'rh7' or 'mandrake8a'.
        !           609: 
1.16      harris41  610: I<CONFIGURATION_FILES>, B<optional> comma-separated listing of files to
1.13      harris41  611: be treated as configuration files by RPM (and thus subject to saving
1.16      harris41  612: during RPM upgrades).  Configuration files can also be specified in
                    613: the standard input stream (as described in L<"The standard input stream">).
1.13      harris41  614: 
1.16      harris41  615: I<DOCUMENTATION_FILES>, B<optional> comma-separated listing of files to be
1.13      harris41  616: treated as documentation files by RPM (and thus subject to being
1.16      harris41  617: placed in the F</usr/doc/RPM-NAME> directory during RPM installation).
                    618: Documentation files can also be specified in
                    619: the standard input stream (as described in L<"The standard input stream">).
1.13      harris41  620: 
1.16      harris41  621: I<PATHPREFIX>, B<optional> path to be removed from file listing.  This
1.13      harris41  622: is in case you are building an RPM from files elsewhere than
                    623: root-level.  Note, this still depends on a root directory hierarchy
                    624: after PATHPREFIX.
                    625: 
1.16      harris41  626: I<CUSTOMIZATION_XML>, B<optional> filename where XML-ish information exists.
                    627: Allows for customizing various pieces of information such
1.13      harris41  628: as vendor, summary, name, copyright, group, autoreqprov, requires, prereq,
1.16      harris41  629: description, and pre-installation scripts
                    630: (see L<"Customizing descriptive data of your RPM software package">).
1.13      harris41  631: 
1.16      harris41  632: =head2 Examples
1.13      harris41  633: 
1.16      harris41  634:  bash$ find /notreallyrootdir | perl make_rpm.pl \
1.21    ! harris41  635:        makemoney 3.1 1 '' \
1.16      harris41  636:        '/usr/doc/man/man3/makemoney.3' \
                    637:        /notreallyrootdir
1.13      harris41  638:  would generate makemoney-3.1-1.i386.rpm
                    639: 
1.16      harris41  640:  bash$ find /usr/local/bin | \
1.21    ! harris41  641:        perl make_rpm.pl mybinfiles 1.0 2
        !           642:  would generate mybinfiles-1.0-2.i386.rpm
        !           643: 
        !           644:  bash$ echo '/sycamore33/tree.txt' | \
        !           645:        perl make_rpm.pl sycamore 1.0 2 '' '' '/sycamore33'
        !           646:  would generate sycamore-1.0-2.i386.rpm
        !           647:  Note that the generated sycamore rpm would install files at the root
        !           648:  '/' directory (not underneath /sycamore33); thus a file named
        !           649:  '/tree.txt' is created, NOT '/sycamore33/tree.txt'.
1.13      harris41  650: 
1.16      harris41  651:  bash$ find /home/joe/romeodir/buildloc | \
                    652:        perl make_rpm.pl romeo \
1.21    ! harris41  653:        1.0 3 '' '' '/home/joe/romeodir/buildloc' customize.xml
1.13      harris41  654:  would generate romeo with customizations from customize.xml.
1.21    ! harris41  655:  Note that the generated romeo rpm would install files at the root
        !           656:  '/' directory (not underneath /home/joe/romedir/buildloc).
1.13      harris41  657: 
1.16      harris41  658: The I<CUSTOMIZATION_XML> argument represents a way to customize the
1.13      harris41  659: numerous variables associated with RPMs.  This argument represents
                    660: a file name.  (Parsing is done in an unsophisticated fashion using
1.16      harris41  661: regular expressions.)  See
                    662: L<"Customizing descriptive data of your RPM software package">.
                    663: 
                    664: =head1 Customizing descriptive data of your RPM software package
                    665: 
                    666: RPMS can be (and often are) packaged with descriptive data 
                    667: describing authorship, dependencies, descriptions, etc.
                    668: 
                    669: The following values can be tagged inside an XML file
                    670: (specified by the 6th command-line argument)
                    671: and made part of the RPM package information
                    672: (B<rpm -qi E<lt>package-nameE<gt>>).
                    673: 
                    674: =over 4
                    675: 
                    676: =item * vendor
                    677: 
                    678: =item * summary
                    679: 
                    680: =item * name
                    681: 
                    682: (overrides the <TAG> argument value; see 
                    683: L<"Description of command-line arguments>)
                    684: 
                    685: =item * copyright
                    686: 
                    687: =item * group
                    688: 
                    689: (the software package group;
                    690: e.g. Applications/System, User Interface/X, Development/Libraries,
                    691: etc.)
                    692: 
                    693: =item * requires
                    694: 
                    695: Contains all the dependency information (see the example below).
                    696: 
                    697: =item * description
                    698: 
                    699: =item * pre
                    700: 
                    701: Commands to be executed prior to software package installation.
                    702: 
                    703: =back
                    704: 
                    705: Here is an example (note that B<make_rpm.pl> automatically substitutes
                    706: any "<tag />" string with the first command-line argument described
                    707: in L<"Description of command-line arguments">):
1.13      harris41  708: 
                    709:  <vendor>
                    710:  Laboratory for Instructional Technology Education, Division of
                    711:  Science and Mathematics Education, Michigan State University.
                    712:  </vendor>
                    713:  <summary>Files for the <tag /> component of LON-CAPA</summary>
                    714:  <name>LON-CAPA-<tag /></name>
                    715:  <copyright>Michigan State University patents may apply.</copyright>
                    716:  <group>Utilities/System</group>
                    717:  <AutoReqProv>no</AutoReqProv>
                    718:  <requires tag='setup'>
                    719:  <item>PreReq: setup</item>
                    720:  <item>PreReq: passwd</item>
                    721:  <item>PreReq: util-linux</item>
                    722:  </requires>
                    723:  <requires tag='base'>
                    724:  <item>PreReq: LON-CAPA-setup</item>
                    725:  <item>PreReq: apache</item>
                    726:  <item>PreReq: /etc/httpd/conf/access.conf</item>
                    727:  </requires>
                    728:  <requires>
                    729:  <item>Requires: LON-CAPA-base</item>
                    730:  </requires>
                    731:  <description>
                    732:  This package is automatically generated by the make_rpm.pl perl
                    733:  script (written by the LON-CAPA development team, www.lon-capa.org,
                    734:  Scott Harrison). This implements the <tag /> component for LON-CAPA.
                    735:  For more on the LON-CAPA project, visit http://www.lon-capa.org/.
                    736:  </description>
                    737:  <pre>
1.16      harris41  738:  echo "************************************************************"
1.13      harris41  739:  echo "LON-CAPA  LearningOnline with CAPA"
                    740:  echo "http://www.lon-capa.org/"
                    741:  echo " "
                    742:  echo "Laboratory for Instructional Technology Education"
                    743:  echo "Michigan State University"
                    744:  echo " "
                    745:  echo "** Michigan State University patents may apply **"
                    746:  echo " "
                    747:  echo "This installation assumes an installation of Redhat 6.2"
                    748:  echo " "
1.16      harris41  749:  echo "The files in this package are for the <tag /> component."
                    750:  echo "***********************************************************"
1.13      harris41  751:  </pre>
                    752: 
                    753: =head1 DESCRIPTION
                    754: 
                    755: Automatically generate an RPM software package from a list of files.
                    756: 
1.16      harris41  757: B<make_rpm.pl> builds the RPM in a very clean and configurable fashion.
                    758: (Finally!  Making RPMs outside of F</usr/src/redhat> without a zillion
1.15      harris41  759: file intermediates left over!)
1.13      harris41  760: 
1.16      harris41  761: B<make_rpm.pl> generates and then deletes temporary
1.15      harris41  762: files needed to build an RPM with.
                    763: It works cleanly and independently from pre-existing
1.16      harris41  764: directory trees such as F</usr/src/redhat/*>.
1.13      harris41  765: 
1.16      harris41  766: Input to the script is simple.  B<make_rpm.pl> accepts five kinds of
                    767: information, three of which are mandatory:
1.15      harris41  768: 
                    769: =over 4
                    770: 
                    771: =item *
                    772: 
1.16      harris41  773: (required) a list of files that are to be part of the software package;
1.15      harris41  774: 
1.16      harris41  775: =item *
                    776: 
                    777: (required) the absolute filesystem location of these files
                    778: (see L<"The standard input stream">);
                    779: 
                    780: =item *
1.15      harris41  781: 
1.21    ! harris41  782: (required) a descriptive tag, version tag, and release tag for the naming of
        !           783: the RPM software package;
1.15      harris41  784: 
                    785: =item *
                    786: 
1.16      harris41  787: (optional) documentation and configuration files;
1.15      harris41  788: 
                    789: =item *
                    790: 
                    791: and (optional) an XML file that defines the additional metadata
                    792: associated with the RPM software package.
                    793: 
                    794: =back
                    795: 
1.16      harris41  796: A temporary directory named $tag (first argument described in
                    797: L<"Description of command-line arguments">) is
                    798: 
                    799: =over 4
                    800: 
                    801: =item *
                    802: 
                    803: generated under the directory from which you run B<make_rpm.pl>.
                    804: 
                    805: For example, user "joe" running
                    806: 
1.21    ! harris41  807:  cat file_list.txt | make_rpm.pl krb4 1.0 1
1.16      harris41  808: 
                    809: would temporarily generate F</home/joe/krb4/>.
                    810: 
                    811: =item *
                    812: 
                    813: F</home/joe/krb4/> is deleted after the *.rpm
                    814: file is generated.
                    815: 
                    816: =back
                    817: 
                    818: The RPM will typically be named $name-$version.i386.rpm
                    819: where $name=$tag.  (The $name can be overridden in the customization
                    820: XML file; see
                    821: L<"Customizing descriptive data of your RPM software package">.)
                    822: 
                    823: Here are some of the items are generated inside
                    824: the $tag directory during the construction of an RPM:
                    825: 
                    826: =over 4
                    827: 
                    828: =item *
                    829: 
                    830: RPM .spec file (F<./$tag/SPECS/$name-$version.spec>)
                    831: 
                    832: =item *
                    833: 
                    834: RPM Makefile (F<./$tag/SOURCES/$name-$version/Makefile>)
                    835: 
                    836: This is the Makefile that is called by the rpm
                    837: command in building the .i386.rpm from the .src.rpm.
                    838: The following directories are generated and/or used:
1.15      harris41  839: 
                    840: =over 4
                    841: 
                    842: =item *
                    843: 
1.16      harris41  844: SOURCE directory: F<./$tag/BinaryRoot/>
                    845: 
                    846: =item *
                    847: 
                    848: TARGET directory: F<./$tag/BuildRoot/>
                    849: 
                    850: =back
                    851: 
                    852: =item *
                    853: 
                    854: BinaryRootMakefile (F<./$tag/BinaryRootMakefile>)
                    855: 
                    856: This is the Makefile that this script creates and calls
                    857: to build the F<$tag/BinaryRoot/> directory from the existing
                    858: filesystem.
                    859: The following directories are generated and/or used:
                    860: 
                    861: =over 4
1.15      harris41  862: 
                    863: =item *
                    864: 
1.16      harris41  865: SOURCE directory: / (your entire filesystem)
1.15      harris41  866: 
                    867: =item *
                    868: 
1.16      harris41  869: TARGET directory: F<./$tag/BinaryRoot/>
1.15      harris41  870: 
                    871: =back
1.13      harris41  872: 
1.16      harris41  873: =back
                    874: 
                    875: The final output of B<make_rpm.pl> is a binary F<.rpm> file.
                    876: The F<./tag> directory is deleted (along with the F<.src.rpm>
                    877: file).  The typical file name generated by B<make_rpm.pl> is
                    878: F<$tag-$version.i386.rpm>.
1.13      harris41  879: 
1.16      harris41  880: B<make_rpm.pl> is compatible with either rpm version 3.* or rpm version 4.*.
1.15      harris41  881: 
1.13      harris41  882: =head1 README
                    883: 
                    884: Automatically generate an RPM software package from a list of files.
                    885: 
1.16      harris41  886: B<make_rpm.pl> builds the RPM in a very clean and configurable fashion.
                    887: (Making RPMs "the simple way" in a one-line command.)
1.13      harris41  888: 
1.16      harris41  889: B<make_rpm.pl> generates and then deletes temporary
1.13      harris41  890: files (and binary root directory tree) to build an RPM with.
                    891: It is designed to work cleanly and independently from pre-existing
                    892: directory trees such as /usr/src/redhat/*.
                    893: 
                    894: =head1 PREREQUISITES
                    895: 
                    896: This script requires the C<strict> module.
                    897: 
1.16      harris41  898: =head1 AUTHOR
                    899: 
                    900:  Scott Harrison
                    901:  harris41@msu.edu
                    902: 
                    903: Please let me know how/if you are finding this script useful and
                    904: any/all suggestions.  -Scott
                    905: 
                    906: =head1 LICENSE
                    907: 
                    908: Written by Scott Harrison, harris41@msu.edu
                    909: 
                    910: Copyright Michigan State University Board of Trustees
                    911: 
                    912: This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                    913: 
                    914: This is free software; you can redistribute it and/or modify
                    915: it under the terms of the GNU General Public License as published by
                    916: the Free Software Foundation; either version 2 of the License, or
                    917: (at your option) any later version.
                    918: 
                    919: This file is distributed in the hope that it will be useful,
                    920: but WITHOUT ANY WARRANTY; without even the implied warranty of
                    921: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                    922: GNU General Public License for more details.
                    923: 
                    924: The GNU Public License is available for review at
                    925: http://www.gnu.org/copyleft/gpl.html.
                    926: 
                    927: For information on the LON-CAPA project, please visit
                    928: http://www.lon-capa.org/.
                    929: 
                    930: =head1 OSNAMES
1.13      harris41  931: 
1.15      harris41  932: Linux
1.13      harris41  933: 
1.16      harris41  934: =head1 SCRIPT CATEGORIES
1.13      harris41  935: 
1.17      harris41  936: UNIX/System_administration
1.13      harris41  937: 
                    938: =cut
1.16      harris41  939: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>