File:  [LON-CAPA] / loncom / build / make_rpm.pl
Revision 1.15: download - view: text, annotated - select for diffs
Sat Feb 16 19:53:46 2002 UTC (22 years, 4 months ago) by harris41
Branches: MAIN
CVS tags: HEAD
cleaning up documentation

    1: #!/usr/bin/perl
    2: 
    3: # The LearningOnline Network with CAPA
    4: # make_rpm.pl - make RedHat package manager file (A CLEAN AND CONFIGURABLE WAY)
    5: #
    6: # $Id: make_rpm.pl,v 1.15 2002/02/16 19:53:46 harris41 Exp $
    7: #
    8: # Written by Scott Harrison, harris41@msu.edu
    9: #
   10: # Copyright Michigan State University Board of Trustees
   11: #
   12: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
   13: #
   14: # LON-CAPA is free software; you can redistribute it and/or modify
   15: # it under the terms of the GNU General Public License as published by
   16: # the Free Software Foundation; either version 2 of the License, or
   17: # (at your option) any later version.
   18: #
   19: # LON-CAPA is distributed in the hope that it will be useful,
   20: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   21: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   22: # GNU General Public License for more details.
   23: #
   24: # You should have received a copy of the GNU General Public License
   25: # along with LON-CAPA; if not, write to the Free Software
   26: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   27: #
   28: # http://www.lon-capa.org/
   29: #
   30: # YEAR=2000
   31: # 9/30,10/2,12/11,12/12,12/21 - Scott Harrison
   32: # YEAR=2001
   33: # 1/8,1/10,1/13,1/23,5/16 - Scott Harrison
   34: # YEAR=2002
   35: # 1/4,1/8,1/9,2/13 - Scott Harrison
   36: #
   37: ###
   38: 
   39: # make_rpm.pl automatically generate RPM software packages
   40: # from a target image directory and file listing.  POD
   41: # documentation is at the end of this file.
   42: 
   43: ###############################################################################
   44: ##                                                                           ##
   45: ## ORGANIZATION OF THIS PERL SCRIPT                                          ##
   46: ##                                                                           ##
   47: ## 1. Check to see if RPM builder application is available                   ##
   48: ## 2. Read in arguments                                                      ##
   49: ## 3. Generate temporary directories                                         ##
   50: ## 4. Initialize some variables                                              ##
   51: ## 5. Create a standalone rpm building environment                           ##
   52: ## 6. Perform variable initializations and customizations                    ##
   53: ## 7. Print header information for .spec file                                ##
   54: ## 8. Process file list and generate information                             ##
   55: ## 9. Generate SRPM and BinaryRoot Makefiles                                 ##
   56: ## 10. mirror copy (BinaryRoot) files under a temporary directory            ##
   57: ## 11. roll everything into an rpm                                           ##
   58: ## 12. clean everything up                                                   ##
   59: ## 13. subroutines                                                           ##
   60: ## 13a. find_info - recursively gather information from a directory          ##
   61: ## 13b. grabtag - grab a tag from an XML string                              ##
   62: ## 14. Plain Old Documentation                                               ##
   63: ##                                                                           ##
   64: ###############################################################################
   65: 
   66: use strict;
   67: 
   68: # ------------------------ Check to see if RPM builder application is available
   69: 
   70: unless (-e '/usr/lib/rpm/rpmrc') {
   71:     print <<END;
   72: ERROR: This script only works with a properly installed RPM builder
   73: application.  
   74: Cannot find /usr/lib/rpm/rpmrc, so cannot generate customized rpmrc file.
   75: Script aborting.
   76: END
   77: }
   78: 
   79: # ----------------------------------------------------------- Read in arguments
   80: 
   81: my ($tag,$version,$configuration_files,$documentation_files,
   82:     $pathprefix,$customize)=@ARGV;
   83: @ARGV=();
   84: 
   85: if (!$version) {
   86:     print <<END;
   87: See "perldoc make_rpm.pl" for more information.
   88: 
   89: Usage: 
   90:            <STDIN> | perl make_rpm.pl <TAG> <VERSION> [CONFIGURATION_FILES]
   91:            [DOCUMENTATION_FILES] [PATHPREFIX] [CUSTOMIZATION_XML]
   92: 
   93: Standard input provides the list of files to work with.
   94: TAG, required descriptive tag.  For example, a kerberos software
   95: package might be tagged as "krb4".
   96: VERSION, required version.  Needed to generate version information
   97: for the RPM.  This should be in the format N.M where N and M are
   98: integers.
   99: CONFIGURATION_FILES, optional comma-separated listing of files to
  100: be treated as configuration files by RPM (and thus subject to saving
  101: during RPM upgrades).
  102: DOCUMENTATION_FILES, optional comma-separated listing of files to be
  103: treated as documentation files by RPM (and thus subject to being
  104: placed in the /usr/doc/RPM-NAME directory during RPM installation).
  105: PATHPREFIX, optional path to be removed from file listing.  This
  106: is in case you are building an RPM from files elsewhere than
  107: root-level.  Note, this still depends on a root directory hierarchy
  108: after PATHPREFIX.
  109: CUSTOMIZATION_XML, allows for customizing various pieces of information such
  110: as vendor, summary, name, copyright, group, autoreqprov, requires, prereq,
  111: description, and pre-installation scripts (see more in the POD;
  112: perldoc make_rpml.pl).
  113: END
  114:     exit;
  115: }
  116: 
  117: mkdir $tag,0755;
  118: mkdir "$tag/BuildRoot",0755;
  119: mkdir "$tag/SOURCES",0755;
  120: mkdir "$tag/SPECS",0755;
  121: mkdir "$tag/BUILD",0755;
  122: mkdir "$tag/SRPMS",0755;
  123: mkdir "$tag/RPMS",0755;
  124: mkdir "$tag/RPMS/i386",0755;
  125: 
  126: # -------------------------------------------------------- Initialize variables
  127: 
  128: my $file;
  129: my $binaryroot="$tag/BinaryRoot";
  130: my ($type,$size,$octalmode,$user,$group);
  131: 
  132: my $currentdir=`pwd`; chop $currentdir; my $invokingdir=$currentdir;
  133: $currentdir.="/$tag";
  134: 
  135: # -------------------------------- Create a standalone rpm building environment
  136: 
  137: open (IN,'</usr/lib/rpm/rpmrc') or die('Cannot open /usr/lib/rpm/rpmrc');
  138: my @lines=<IN>;
  139: close IN;
  140: 
  141: open (RPMRC,">$tag/SPECS/rpmrc");
  142: foreach my $line (@lines) {
  143:     if ($line=~/^macrofiles/) {
  144: 	chop $line;
  145: 	$line.=":$currentdir/SPECS/rpmmacros\n";
  146:     }
  147:     print RPMRC $line;
  148: }
  149: close RPMRC;
  150: 
  151: open (RPMMACROS,">$tag/SPECS/rpmmacros");
  152: print RPMMACROS <<END;
  153: \%_topdir $currentdir
  154: \%__spec_install_post    \\
  155:     /usr/lib/rpm/brp-strip \\
  156:     /usr/lib/rpm/brp-strip-comment-note \\
  157: \%{nil}
  158: END
  159: close RPMMACROS;
  160: 
  161: # ------------------------- Perform variable initializations and customizations
  162: 
  163: my $cu;
  164: if ($customize) {
  165:     open (IN,"<$customize") or die("Cannot open $customize");
  166:     my @clines=(<IN>);
  167:     $cu=join('',@clines);
  168:     close IN;
  169: }
  170: my $tv; # tag value variable for storing retrievals from $cu
  171: 
  172: # (Sure. We could use HTML::TokeParser here.. but that wouldn't be fun now,
  173: # would it?)
  174: my $name=$tag;
  175: # read in name from customization if available
  176: $tv=grabtag('name',$cu,1); $name=$tv if $tv;
  177: $name=~s/\<tag \/\>/$tag/g;
  178: 
  179: # okay.. now we can generate this needed directory
  180: mkdir "$tag/SOURCES/$name-$version",0755;
  181: 
  182: my $requires="";
  183: # read in relevant requires info from customization file (if applicable)
  184: # note that "PreReq: item" controls order of CD-ROM installation (if you
  185: # are making a customized CD-ROM)
  186: # "Requires: item" just enforces dependencies from the command-line invocation
  187: $tv=grabtag('requires',$cu,1); $requires=$tv if $tv;
  188: # do more require processing here
  189: $requires=~s/\s*\<\/item\>\s*//g;
  190: $requires=~s/\s*\<item\>\s*/\n/g;
  191: $requires=~s/^\s+//s;
  192: 
  193: my $summary="Files for the $name software package.";
  194: # read in summary from customization if available
  195: $tv=grabtag('summary',$cu,1); $summary=$tv if $tv;
  196: $summary=~s/\<tag \/\>/$tag/g;
  197: 
  198: my $autoreqprov='no';
  199: # read in autoreqprov from customization if available
  200: $tv=grabtag('autoreqprov',$cu,1); $autoreqprov=$tv if $tv;
  201: 
  202: my $copyright="not specified here";
  203: # read in copyright from customization if available
  204: $tv=grabtag('copyright',$cu,1); $copyright=$tv if $tv;
  205: $copyright=~s/\<tag \/\>/$tag/g;
  206: 
  207: open (SPEC,">$tag/SPECS/$name-$version.spec");
  208: 
  209: my $vendor='Me';
  210: # read in vendor from customization if available
  211: $tv=grabtag('vendor',$cu,1); $vendor=$tv if $tv;
  212: $vendor=~s/\<tag \/\>/$tag/g;
  213: 
  214: my $description="$name software package";
  215: # read in description from customization if available
  216: $tv=grabtag('description',$cu,0); $description=$tv if $tv;
  217: $description=~s/\<tag \/\>/$tag/g;
  218: 
  219: my $pre="";
  220: # read in pre-installation script if available
  221: $tv=grabtag('pre',$cu,0); $pre=$tv if $tv;
  222: $pre=~s/\<tag \/\>/$tag/g;
  223: 
  224: # ------------------------------------- Print header information for .spec file
  225: 
  226: print SPEC <<END;
  227: Summary: $summary
  228: Name: $name
  229: Version: $version
  230: Release: 1
  231: Vendor: $vendor
  232: BuildRoot: $currentdir/BuildRoot
  233: Copyright: $copyright
  234: Group: Utilities/System
  235: Source: $name-$version.tar.gz
  236: AutoReqProv: $autoreqprov
  237: $requires
  238: # requires: filesystem
  239: \%description
  240: $description
  241: 
  242: \%prep
  243: \%setup
  244: 
  245: \%build
  246: rm -Rf "$currentdir/BuildRoot"
  247: 
  248: \%install
  249: make ROOT="\$RPM_BUILD_ROOT" SOURCE="$currentdir/BinaryRoot" directories
  250: make ROOT="\$RPM_BUILD_ROOT" SOURCE="$currentdir/BinaryRoot" files
  251: make ROOT="\$RPM_BUILD_ROOT" SOURCE="$currentdir/BinaryRoot" links
  252: 
  253: \%pre
  254: $pre
  255: 
  256: \%post
  257: \%postun
  258: 
  259: \%files
  260: END
  261: 
  262: # ------------------------------------ Process file list and gather information
  263: 
  264: my %BinaryRootMakefile;
  265: my %Makefile;
  266: my %dotspecfile;
  267: 
  268: foreach $file (<>) {
  269:     chop $file;
  270:     my $comment="";
  271:     if ($file=~/\s+\#(.*)$/) {
  272: 	$file=~s/\s+\#(.*)$//;
  273: 	$comment=$1;
  274:     }
  275:     my $directive="";
  276:     if ($comment=~/config\(noreplace\)/) {
  277: 	$directive="\%config(noreplace) ";
  278:     }
  279:     elsif ($comment=~/config/) {
  280: 	$directive="\%config ";
  281:     }
  282:     elsif ($comment=~/doc/) {
  283: 	$directive="\%doc";
  284:     }
  285:     if (($type,$size,$octalmode,$user,$group)=find_info($file)) {
  286: 	$octalmode="0" . $octalmode if length($octalmode)<4;
  287: 	if ($pathprefix) {
  288: 	    $file=~s/^$pathprefix//;
  289: 	}
  290: 	if ($type eq "files") {
  291: 	    push @{$BinaryRootMakefile{$type}},"\tinstall -D -m $octalmode ".
  292: 		"$pathprefix$file $binaryroot$file\n";
  293: 	    push @{$Makefile{$type}},"\tinstall -D -m $octalmode ".
  294: 		"\$(SOURCE)$file \$(ROOT)$file\n";
  295: 	    push @{$dotspecfile{$type}},"$directive\%attr($octalmode,$user,".
  296: 		"$group) $file\n";
  297: 	}
  298: 	elsif ($type eq "directories") {
  299: 	    push @{$BinaryRootMakefile{$type}},"\tinstall -m $octalmode -d ".
  300: 		"$binaryroot$file\n";
  301: 	    push @{$Makefile{$type}},"\tinstall -m $octalmode -d ".
  302: 		"\$(SOURCE)$file \$(ROOT)$file\n";
  303: 	    push @{$dotspecfile{$type}},"\%dir \%attr($octalmode,$user,".
  304: 		"$group) $file\n";
  305: 	}
  306: 	elsif ($type eq "links") {
  307: 	    my $link=$size; # I use the size variable to pass the link value
  308:                             # from the subroutine find_info
  309: 	    $link=~s/^$pathprefix//;
  310: 	    push @{$BinaryRootMakefile{$type}},
  311: 	         "\tln -s $link $binaryroot$file\n";
  312: 	    push @{$Makefile{$type}},"\tln -s $link \$(ROOT)$file\n";
  313: 	    push @{$dotspecfile{$type}},"\%attr(-,$user,$group) $file\n";
  314: 	}
  315:     }
  316: }
  317: 
  318: # -------------------------------------- Generate SRPM and BinaryRoot Makefiles
  319: 
  320: open OUTS, ">$tag/SOURCES/$name-$version/Makefile";
  321: open OUTB, ">$tag/BinaryRootMakefile";
  322: foreach $type ("directories","files","links") {
  323:     print OUTS "$type\:\n";
  324:     print OUTS join("",@{$Makefile{$type}}) if $Makefile{$type};
  325:     print OUTS "\n";
  326:     print OUTB "$type\:\n";
  327:     print OUTB join("",@{$BinaryRootMakefile{$type}})
  328: 	if $BinaryRootMakefile{$type};
  329:     print OUTB "\n";
  330:     print SPEC join("",@{$dotspecfile{$type}}) if $dotspecfile{$type};
  331: }
  332: close OUTB;
  333: close OUTS;
  334: 
  335: close SPEC;
  336: 
  337: # ------------------ mirror copy (BinaryRoot) files under a temporary directory
  338: 
  339: `make -f $tag/BinaryRootMakefile directories`;
  340: `make -f $tag/BinaryRootMakefile files`;
  341: `make -f $tag/BinaryRootMakefile links`;
  342: 
  343: # ------------------------------------------------- roll everything into an RPM
  344: 
  345: my $command="cd $currentdir/SOURCES; tar czvf $name-$version.tar.gz ".
  346:     "$name-$version";
  347: print `$command`;
  348: $command="cd $currentdir/SPECS; rpm --rcfile=./rpmrc -ba ".
  349:     "$name-$version.spec; cd ../RPMS/i386; cp ".
  350:     "$name-$version-1.i386.rpm $invokingdir/.";
  351: print `$command`;
  352: 
  353: # --------------------------------------------------------- clean everything up
  354: 
  355: print `cd $invokingdir; rm -Rf $tag`;
  356: 
  357: # ----------------------------------------------------------------- SUBROUTINES
  358: # ----- Subroutine: find_info - recursively gather information from a directory
  359: sub find_info {
  360:     my ($file)=@_;
  361:     my $line;
  362:     if (($line=`find $file -type f -prune`)=~/^$file\n/) {
  363: 	$line=`find $file -type f -prune -printf "\%s\t\%m\t\%u\t\%g"`;
  364: 	return ("files",split(/\t/,$line));
  365:     }
  366:     elsif (($line=`find $file -type d -prune`)=~/^$file\n/) {
  367: 	$line=`find $file -type d -prune -printf "\%s\t\%m\t\%u\t\%g"`;
  368: 	return ("directories",split(/\t/,$line));
  369:     }
  370:     elsif (($line=`find $file -type l -prune`)=~/^$file\n/) {
  371: 	$line=`find $file -type l -prune -printf "\%l\t\%m\t\%u\t\%g"`;
  372: 	return ("links",split(/\t/,$line));
  373:     }
  374:     die("**** ERROR **** $file is neither a directory, soft link, or file");
  375: }
  376: 
  377: # ------------------------- Subroutine: grabtag - grab a tag from an xml string
  378: sub grabtag {
  379:     my ($tag,$text,$clean)=@_;
  380:     # meant to be quick and dirty as opposed to a formal state machine parser
  381:     my $value;
  382:     $cu=~/\<$tag\>(.*?)\<\/$tag\>/s; 
  383:     $value=$1; $value=~s/^\s+//;
  384:     if ($clean) {
  385: 	$value=~s/\n\s/ /g;
  386: 	$value=~s/\s\n/ /g;
  387: 	$value=~s/\n/ /g;
  388: 	$value=~s/\s+$//;
  389:     }
  390:     return $value;
  391: }
  392: 
  393: # ----------------------------------------------------- Plain Old Documentation
  394: 
  395: =head1 NAME
  396: 
  397: make_rpm.pl - automatically generate an RPM software package
  398: 
  399: =head1 SYNOPSIS
  400: 
  401: Usage: <TAG> <VERSION> [CONFIGURATION_FILES]
  402:  [DOCUMENTATION_FILES] [PATHPREFIX] [CUSTOMIZATION_XML]
  403: 
  404: Standard input provides the list of files to work with.
  405: 
  406: TAG, required descriptive tag.  For example, a kerberos software
  407: package might be tagged as "krb4".
  408: 
  409: VERSION, required version.  Needed to generate version information
  410: for the RPM.  This should be in the format N.M where N and M are
  411: integers.
  412: 
  413: CONFIGURATION_FILES, optional comma-separated listing of files to
  414: be treated as configuration files by RPM (and thus subject to saving
  415: during RPM upgrades).
  416: 
  417: DOCUMENTATION_FILES, optional comma-separated listing of files to be
  418: treated as documentation files by RPM (and thus subject to being
  419: placed in the /usr/doc/RPM-NAME directory during RPM installation).
  420: 
  421: PATHPREFIX, optional path to be removed from file listing.  This
  422: is in case you are building an RPM from files elsewhere than
  423: root-level.  Note, this still depends on a root directory hierarchy
  424: after PATHPREFIX.
  425: 
  426: CUSTOMIZATION_XML, allows for customizing various pieces of information such
  427: as vendor, summary, name, copyright, group, autoreqprov, requires, prereq,
  428: description, and pre-installation scripts (see more in the POD;
  429: perldoc make_rpml.pl).
  430: 
  431: Examples:
  432: 
  433: [prompt] find notreallyrootdir | perl make_rpm.pl makemoney 3.1 '' \
  434:     '/usr/doc/man/man3/makemoney.3' notreallyrootdir
  435:  would generate makemoney-3.1-1.i386.rpm
  436: 
  437: [prompt] find /usr/local/bin | perl make_rpm.pl mybinfiles 1.0
  438:  would generate mybinfiles-1.0-1.i386.rpm
  439: 
  440: [prompt] find romeo | perl make_rpm.pl romeo 1.0 '' '' '' customize.xml
  441:  would generate romeo with customizations from customize.xml.
  442: 
  443: The CUSTOMIZATION_XML argument represents a way to customize the
  444: numerous variables associated with RPMs.  This argument represents
  445: a file name.  (Parsing is done in an unsophisticated fashion using
  446: regular expressions.)  Here are example contents of such a file:
  447: 
  448:  <vendor>
  449:  Laboratory for Instructional Technology Education, Division of
  450:  Science and Mathematics Education, Michigan State University.
  451:  </vendor>
  452:  <summary>Files for the <tag /> component of LON-CAPA</summary>
  453:  <name>LON-CAPA-<tag /></name>
  454:  <copyright>Michigan State University patents may apply.</copyright>
  455:  <group>Utilities/System</group>
  456:  <AutoReqProv>no</AutoReqProv>
  457:  <requires tag='setup'>
  458:  <item>PreReq: setup</item>
  459:  <item>PreReq: passwd</item>
  460:  <item>PreReq: util-linux</item>
  461:  </requires>
  462:  <requires tag='base'>
  463:  <item>PreReq: LON-CAPA-setup</item>
  464:  <item>PreReq: apache</item>
  465:  <item>PreReq: /etc/httpd/conf/access.conf</item>
  466:  </requires>
  467:  <requires>
  468:  <item>Requires: LON-CAPA-base</item>
  469:  </requires>
  470:  <description>
  471:  This package is automatically generated by the make_rpm.pl perl
  472:  script (written by the LON-CAPA development team, www.lon-capa.org,
  473:  Scott Harrison). This implements the <tag /> component for LON-CAPA.
  474:  For more on the LON-CAPA project, visit http://www.lon-capa.org/.
  475:  </description>
  476:  <pre>
  477:  echo "***********************************************************************"
  478:  echo "LON-CAPA  LearningOnline with CAPA"
  479:  echo "http://www.lon-capa.org/"
  480:  echo " "
  481:  echo "Laboratory for Instructional Technology Education"
  482:  echo "Michigan State University"
  483:  echo " "
  484:  echo "** Michigan State University patents may apply **"
  485:  echo " "
  486:  echo "This installation assumes an installation of Redhat 6.2"
  487:  echo " "
  488:  echo "The server computer should be currently connected to the ethernet"
  489:  echo " "
  490:  echo "The files in this package are only those for the <tag /> component."
  491:  echo "Configuration files are sometimes part of the LON-CAPA-base RPM."
  492:  echo "***********************************************************************"
  493:  </pre>
  494: 
  495: =head1 DESCRIPTION
  496: 
  497: Automatically generate an RPM software package from a list of files.
  498: 
  499: This script builds the RPM in a very clean and configurable fashion.
  500: (Finally!  Making RPMs outside of /usr/src/redhat without a zillion
  501: file intermediates left over!)
  502: 
  503: This script generates and then deletes temporary
  504: files needed to build an RPM with.
  505: It works cleanly and independently from pre-existing
  506: directory trees such as /usr/src/redhat/*.
  507: 
  508: The script is also simple.  It accepts four kinds of information,
  509: two of which are mandatory:
  510: 
  511: =over 4
  512: 
  513: =item *
  514: 
  515: a list of files that are to be part of the software package;
  516: 
  517: =item * 
  518: 
  519: the location of these files;
  520: 
  521: =item *
  522: 
  523: (optional) a descriptive tag and a version tag;
  524: 
  525: =item *
  526: 
  527: and (optional) an XML file that defines the additional metadata
  528: associated with the RPM software package.
  529: 
  530: =back
  531: 
  532: The following items are initially and temporarily generated during the
  533: construction of an RPM:
  534: 
  535: =over 4
  536: 
  537: =item *
  538: 
  539: RPM .spec file
  540: 
  541: =item *
  542: 
  543: RPM Makefile
  544: 
  545: =item *
  546: 
  547: SourceRoot
  548: 
  549: =back
  550: 
  551: A resulting .rpm file is generated.
  552: 
  553: make_rpm.pl is compatible with both rpm version 3.* and rpm version 4.*.
  554: 
  555: =head1 README
  556: 
  557: Automatically generate an RPM software package from a list of files.
  558: 
  559: This script builds the RPM in a very clean and configurable fashion.
  560: (Making RPMs the simple and right way!)
  561: 
  562: This script generates and then deletes temporary
  563: files (and binary root directory tree) to build an RPM with.
  564: It is designed to work cleanly and independently from pre-existing
  565: directory trees such as /usr/src/redhat/*.
  566: 
  567: =head1 PREREQUISITES
  568: 
  569: This script requires the C<strict> module.
  570: 
  571: =pod OSNAMES
  572: 
  573: Linux
  574: 
  575: =pod SCRIPT CATEGORIES
  576: 
  577: UNIX/System Administration
  578: 
  579: =cut

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