#------------------------------------------------------------------------------- # vim:ts=8:sw=4:et # # Packaging rules for building RPMs and DEBs. # # The rules below can be used to automate the build of RPM and DEB packages from # a structured tree of package files. They are easily integrated into # Electric Commander and are maintained centrally by the packaging team. # # If you're editing this file, then indents are 4 spaces; if you're using vim, # then setting 'modelines' should take care of it for you. # Location of this Jamrules file (defines the variable PACKAGING) SubDir PACKAGING ; EXEC_SUB_TOKENS = $(OS:L)$(OSVER:EL)$(OSPLAT:EL) $(BUILD) $(TYPE:L) ; EXEC_TOKENS = PACKAGING p4-bin bin.$(EXEC_SUB_TOKENS[1]) $(EXEC_SUB_TOKENS[2]) $(EXEC_SUB_TOKENS[3]) ; EXEC ?= [ FSubDirPath $(EXEC_TOKENS) ] ; ALL_LOCATE_TARGET = $(EXEC) ; # Use rm -rf to clean directories. RMDIRFLAGS ?= -rf ; # How to build RPM packages RPMBUILD ?= rpmbuild ; RPMBUILDFLAGS ?= --quiet ; # How to build DEB packages. If we're doing a production build, then we # also want to sign the packages at build time. DPKGBUILD ?= dpkg-buildpackage ; if ! $(PRODUCTION) { # In development mode, we don't try to sign packages and only # build binaries DPKGBUILDFLAGS ?= -b -uc -us ; } # VersionFrom versionFile # # Load the version string from the named file. The named file should be # identical in format to p4/Version. # # Usage: VersionFrom ; # rule VersionFrom { # The rule is called from the location of the Version file, so we # can just use the current value of SEARCH_SOURCE to tell Jam # where to look for it when it needs it. SEARCH on $(<) ?= $(SEARCH_SOURCE) ; include $(<) ; } # A utility rule to simply return its arguments joined together into # a single string. # # Usage: VAR = [ FConcat token... ] ; # rule Fconcat { return $(<:J=" ") ; } # Replacement for Jambase's File rule, needed because the latter always sets # SEARCH even when it's already set and we want consumers of this ruleset # to be able to specify where to look for the file if they want to. # # Usage: CopyFile : ; # rule CopyFile { Depends files : $(<) ; Depends $(<) : $(>) ; SEARCH on $(>) ?= $(SEARCH_SOURCE) ; MODE on $(<) ?= $(FILEMODE) ; Chmod $(<) ; } actions CopyFile { $(CP) $(>) $(<) } # Add a new sed expression to the list that will be applied when # the target is copied using SedCopy. Shorthand for setting SED_EXPRESSIONS # on the target directly. # # Usage: SedRule : # rule SedRule { SED_EXPRESSIONS on $(<) += $(>) ; } # Copies a file from src to target using sed to apply any transformations in # $(SED_EXPRESSIONS) as it does so. For example: # # SedRule foo : s/bar/foo/ # SedCopy foofile : barfile # # Usage: SedCopy : # rule SedCopy { SEARCH on $(>) = $(SEARCH_SOURCE) ; Depends files : $(<) ; Depends $(<) : $(>) ; MODE on $(<) ?= $(FILEMODE) ; Chmod $(<) ; Clean clean : $(<) ; } # Copies a file using sed and applies the transformations in $(SED_EXPRESSIONS) # on the way. Uses piecemeal to ensure that the command is invoked separately # for every target. actions piecemeal SedCopy { $(SED) $(SEDFLAGS) -e "" -e"$(SED_EXPRESSIONS)" $(>) > $(<) } # Build a RPM package based on the given spec file. This rule is responsible # for creating the temporary build tree in which the package is built # and for copying the spec file into it. # # Sets the following target-specific variables used in the build # # RPMDIR - The path to the temporary build directory # ID_REL - The (RPM-friendly) release name. Passed to # rpmbuild as the macro 'p4rel' # ID_PATCH - The (RPM-friendly) changelist level. Passed to # rpmbuild as the macro 'p4change' # # Usage: RPM : ; # rule RPM { local _pkg_grist = package-$(1) ; # RPM : local _pkg = $(1:G=$(_pkg_grist)) ; # Make our build directory. local _builddir = [ FRPMBuildDir $(1) ] ; _builddir = [ FDirName $(EXEC) $(_builddir) ] ; # Create the subdirectories we need for RPM building for _d in BUILD RPMS SRPMS SOURCES SPECS ROOT { _d = [ FDirName $(_builddir) $(_d) ] ; _d = $(_d:G=dir) ; MkDir $(_d) ; Depends $(_pkg) : $(_d) ; } # Tell our action where to run the build. RPMDIR on $(_pkg) = [ FDirName $(EXEC) $(_builddir) ] ; # Ensure that 'jam clean' removes the build tree. RmDir clean : $(_builddir) ; Depends all : $(_pkg) ; Depends $(_pkg) : $(2) $(_builddir:G=dir) ; RPMFile $(1) : $(2) : SPECS ; ID_REL on $(_pkg) = [ Fconcat p4rel [ FRPMRelease $(RELEASE) ] ] ; ID_PATCH on $(_pkg) = [ Fconcat p4change $(PATCHLEVEL)$(SPECIAL:E) ] ; RpmBuild $(_pkg) : $(2) ; } # Action to build the RPM. Executes the rpmbuild command from the temporary # build location and defines the %{p4rel} and %{p4change} macros for use # within our spec files. actions piecemeal RpmBuild { cd $(RPMDIR) && $(RPMBUILD) $(RPMBUILDFLAGS) --define "$(ID_REL)" --define "$(ID_PATCH)" --define "_topdir `pwd`" --buildroot `pwd`/ROOT -bb SPECS/`basename $(2)` mv RPMS/*/*.rpm .. ; } # Function to compute the name of the RPM build directory based on: # # (a) the name of the package being built # (b) the RELEASE and PATCHLEVEL variables specified in the Version file # # Usage: VAR = [ FRPMBuildDir ] ; # rule FRPMBuildDir { # Same as Debian package build directory for now... local _d = [ FDebDirName $(1) ] ; d = $(_d:G=$(1)) ; return $(_d) ; } # Function to compute the RPM release identifier passed to rpmbuild as # %{p4rel}. RPM forbids the use of the '-' character (and possibly others) # within the release names, so here we convert all '-' characters to '.'. # # Requires: # (a) the name of the package being built # (b) the RELEASE and PATCHLEVEL variables from the Version file # # Usage: VAR = [ FRPMRelease ] ; # rule FRPMRelease { # Compute the ID_REL tag for this release in a format that # RPM likes and return it # Join all tokens in $(<) with '.'s, so we have only one string # to worry about local _rel = $(<:J=.) ; # Convert any '-' characters to '.'s. This is because rpm # uses a - to separate the release from the version. _rel = [ FSplitChar - : $(_rel) ] ; _rel = $(_rel:J=.) ; return $(_rel) ; } # Rule to include source files in the build of the specified RPM package and # place those files in a specific subdirectory of the temporary build tree # used to construct the RPM. # # This rule creates a dependency chain that looks like this: # # pkgname <-- source <-- source # # Where 'pkgname' is the package name - aka $(1). The middle target in # the example above is the copy of the source file in the temporary build # location. As that will have the same name as the source, we use Jam's # grist to disambiguate them. The package name is also gristed for a # similar reason. # # Note that the package name here is the name of the source package as # there could be several binary packages that come out of it. # # Usage: RPMFile : : # # For example: # # RPMFile foo : foo-src.tar.gz : SOURCES ; # rule RPMFile { # Grist package name with 'package' in case package name is also the # name of a binary. Quite likely. local _pkg = $(1:G=package>) ; local _builddir = [ FRPMBuildDir $(1) ] ; local _files = $(2) ; local _dir = $(3) ; local _cpflags ; if $(UNIX) { _cpflags = -p ; # to preserve times with cp -p } # for each target file, copy to target directory local _f ; for _f in $(_files) { local _d _t ; _t = $(_f:G=$(1)) ; # target _d = [ FDirName $(EXEC) $(_builddir) $(_dir) ] ; # directory CP on $(_t) = $(CP) $(_cpflags) ; MakeLocate $(_t) : $(_d) ; CopyFile $(_t) : $(_f) ; # Ideally the .rpm would depend on the files, but in fact # all we have here is the source package name. There may be more # than one .rpm produced from a single source. Depends $(_pkg) : $(_t) ; } } # Build the binary debian packages based on the named source package and the # specified configuration . The files specified here are not the # sources for the package, but the debian package config files (i.e. those # normally found in the debian directory. # # Note that for convenience, the debian/source/format file should be called # debian/source-format. It will be moved into the correct location as part # of the build. # # Note also that multiple binary packages may result from this build. Naming # them all here generates the correct dependencies. # # The main dependency graph generated looks like this: # # debfilename.deb <-- sourcefile <-- sourcefile # # Note that the debian/changelog file can be automatically generated as it # is copied using sed. The following variables within the file will be # substituted: # # PKG_VERSION - Replaced with the version string # PKG_DATE - Replaced with the current date # # Sets the following variables on the deb targets: # # BUILDDIR - Temporary build dir for this target # ID_REL - Release string # ID_PATCH - Changelist for this build # # The latter two come from the Version file. # # Usage : MkDeb : ... : # rule MkDeb { local _pkg = $(1).deb ; local _builddir = [ FDebDirName $(1) ] ; local _deb = [ FDebFilename $(2) ] ; # Tokens and path to build directory local _builddirTokens = $(_builddir) ; _builddir = [ FDirName $(EXEC) $(_builddir) ] ; # Tokens, grist and path to debian subdirectory thereof local _debdirTokens = $(_builddirTokens) debian ; local _debdirGrist = [ FGrist $(_builddirTokens) debian ] ; local _debdir = [ FDirName $(EXEC) $(_debdirTokens) ] ; MkDir $(_builddir) ; RmDir clean : $(_builddir) ; MkDir $(_debdir) ; # For each file, copy it from its current location to the # build tree. for _s in $(3) { local _s = [ FGristSourceFiles $(_s) ] ; local _t = $(_s:G=$(_debdirGrist)) ; local _targetdir = $(_debdir) ; # Special case. The file source-format has to be copied into # source/format. This saves us a level of Jamfiles which we'd # need to handle one, trivial, file. if ( $(_s:B) = source-format ) { local _td = [ FDirName $(_targetdir) source ] ; MkDir $(_td) ; _t = format ; _targetdir = $(_sd) ; } # Special case. The changelog file needs updating with the # build information. if ( $(_s:B) = changelog ) { local _pVer = [ FDebVersion ] ; MakeLocate $(_t) : $(_targetdir) ; SedRule $(_t) : "s/PKG_VERSION/$(_pVer)/" ; SedRule $(_t) : "s/PKG_DATE/`date -R`/" ; SedCopy $(_t) : $(_s) ; Depends $(_deb) : $(_t) ; continue ; } MakeLocate $(_t) : $(_targetdir) ; File $(_t) : $(_s) ; Depends $(_deb) : $(_t) ; } LOCATE on $(_deb) = $(EXEC) ; Depends all : $(_pkg) ; NotFile $(_pkg) ; Depends $(_pkg) : $(_deb) ; Clean clean : $(_deb) ; ID_REL on $(_deb) = [ Fconcat p4rel $(RELEASE:J=.) ] ; ID_PATCH on $(_deb) = [ Fconcat p4change $(PATCHLEVEL)$(SPECIAL:E) ] ; BUILDDIR on $(_deb) = $(_builddir) ; MkDeb1 $(_deb) ; } # Adds the files to the specified debian packages being built # and places them into the specified subdirectory in the temporary # build tree. # # srcname is the name of the source package, tgtdebnames are the name(s) # of the target debs to which these files will be added as part of # the build of the source package. # # If no target directory is specified, then the target is the root of the # build tree # # Usage: DebFile srcname : tgtdebnames : files... [ : target_directory ] ; # rule DebFile { local _pkg = $(1).deb ; local _deb = [ FDebFilename $(2) ] ; local _builddir = [ FDebDirName $(1) ] ; local _dir = $(4) ; local _cpflags ; if $(UNIX) { _cpflags = -p ; # to preserve times with cp -p } # for each target file, copy to named temp directory # create mini structure (bin, lib, include ) local _f ; for _f in $(3) { local _d _t ; local _target_grist = [ FGrist $(_builddir) $(_dir) ] ; _d = [ FDirName $(EXEC) $(_builddir) $(_dir) ] ; # directory _t = $(_f:G=$(_target_grist)) ; # target CP on $(_t) = $(CP) $(_cpflags) ; MakeLocate $(_t) : $(_d) ; CopyFile $(_t) : $(_f) ; Depends $(_deb) : $(_t) ; } } # Function - not for use in Jamfiles. # # Returns the platform name as used by dpkg-buildpackage # # Usage: VAR = [ FDebPlatform ] ; # rule FDebPlatform { local _plat = $(OSPLAT:L) ; switch $(_plat) { case x86_64 : _plat = amd64 ; } return $(_plat) ; } # Function - not for use in Jamfiles. # # Returns the version string in the format used by dpkg-buildpackage # # Usage: VAR = [ FDebVersion ] ; # rule FDebVersion { local _versionTokens = $(RELEASE[1]).$(RELEASE[2]) $(PATCHLEVEL) [ FSplitChar _ : $(RELEASE[3-]) ] ; return $(_versionTokens:J=-) ; } # Function - not for use in Jamfiles. # # Returns the list of source files generated by building the package # # Usage: FDebSourceFiles # rule FDebSourceFiles { local _version = [ FDebVersion ] ; local _plat = [ FDebPlatform ] ; local _extensions = _$(_plat).changes .dsc .tar.gz ; return $(<)_$(_version)$(_extensions) ; } # Function - not for use in Jamfiles. # # Builds the filename of the Debian package from the version data. In some # cases this won't be right - for example, a package that works on all # platforms - but it's close enough as it just means we might build again # when we don't really need to. # # Usage: [ FDebFilename ] # rule FDebFilename { local _plat = [ FDebPlatform ] ; local _version = [ FDebVersion ] ; local _name = $(<)_$(_version)_$(_plat).deb ; return $(_name) ; } # Function - not for use in Jamfiles. # # Computes the name of the build directory for this deb # # Usage: [ FDebDirName ] ; # rule FDebDirName { local _name ; if $(RELEASE[3]) { _name = $(<)-$(RELEASE[1]).$(RELEASE[2]).$(PATCHLEVEL)-$(RELEASE[3-]) ; } else { _name = $(<)-$(RELEASE[1]).$(RELEASE[2]).$(PATCHLEVEL) ; } return $(_name) ; } # Clean for directories as built-in clean seems to not work well for # cleaning up an entire tree. actions piecemeal together existing RmDir { $(RMDIR) $(RMDIRFLAGS) $(>) ; } # Action to build a Debian package. actions together MkDeb1 { cd $(BUILDDIR) && $(DPKGBUILD) $(DPKGBUILDFLAGS) ; } # Utility function to split a string at occurrences of a specified # character. Usage: [ FSplitChar : ] # # Return a list of tokens rule FSplitChar { local _char = $(<) ; local _regexp = $(_char)?([^$(_char)]*)(.*) ; local _tokens ; local _result = [ MATCH $(_regexp) : $(>) ] ; while $(_result) { _tokens = $(_tokens) $(_result[1]) ; _result = [ MATCH $(_regexp) : $(_result[2-]) ] ; } return $(_tokens) ; }