source: rpmbuild-bot/rpmbuild-bot.sh@ 732

Last change on this file since 732 was 732, checked in by dmik, 9 years ago

rpmbuild-bot: Fix "empty macro body" warning.

File size: 14.0 KB
Line 
1#!/bin/sh
2
3#
4# rpmbuild-bot.sh: RPM Build Bot version 1.0.1.
5#
6# Author: Dmitriy Kuminov <coding@dmik.org>
7#
8# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
9# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
10#
11# History
12# -------
13#
14# 1.0.1 [01.04.2016]
15# - Fix bad variable name error.
16# - Understand .CMD extension in REXX wrapper.
17# 1.0 [01.04.2016]
18# - Initial version.
19#
20# Synopsis
21# --------
22#
23# This script performs a build of RPM packages from a given .spec file in
24# controlled environment to guarantee consistent results when building RPMs
25# different machines. It uses a separate file rpmbuild-bot-env.sh located
26# in the same directory to set up the environment and control the build
27# process. The main purpose of this script is to build RPM packages for a
28# specific distribution channel maintaining distribution-siecific rules.
29#
30# Usage
31# -----
32#
33# > rpmbuild-bot.sh SPEC [ upload[=REPO] | test[=MODE] ] [-f]
34#
35# MYAPP is the name of the RPM package spec file (extension is optional,
36# .spec is assumed). The spec file is searched in the SPECS directory of the
37# rpmbuild tree (usually $USER/rpmbuild/SPECS, rpmbuild --eval='%_specdir'
38# will show the exact location). This may be overriden by giving a spec file
39# with a path specification.
40#
41# The second argument defines the command to perform. The default command is
42# "build". The following sections will describe each command.
43#
44# Building packages
45# -----------------
46#
47# The "build" is the main command which is used to generate packages for
48# distribution. This command does the following:
49#
50# 1. Build all RPM packages for all architectures specified in
51# $RPMBUILD_BOT_ARCH_LIST. The packages are stored in the RPMS
52# directory of the rpmbuild tree.
53# 2. Build the source RPM. Stored in the SRPMS directory.
54# 3. Create a ZIP package from the RPMs for the architecture specified
55# last in $RPMBUILD_BOT_ARCH_LIST.
56#
57# The build process for each architecture is stored in a log file in the logs
58# directory of the rpmbuild tree (`rpmbuild --eval='%_topdir'/logs`). Creation
59# of the source RPM and ZIP files is also logged, into separate log files.
60#
61# The "build" command will fail if the log directory contains files from a
62# successfull run of another "build" command for this package. This is to
63# prevent overwriting successfully built packages that are not yet uploaded to
64# the distribution repository (see the "upload" command). You should either
65# run the "upload" command or use the -f option to force removal of these
66# log files and the corresponding package files if you are absolutely sure they
67# should be discarded.
68#
69# Doing test builds
70# -----------------
71#
72# The "test" command is used to build packages for one architecture for testing
73# purposes. In this more, neither the source RPM nor the ZIP file is created.
74# Also, a special option is automatically passed to rpmbuild to clear the %dist
75# variable to indicate that this is a local, non-distribution build. Such
76# packages will always be "older" than the corresponding builds with %dist
77# so that they will be automatically updated on the next yum update to the
78# %dist ones. The packages built in "test" mode are NOT intended for
79# distribution, they are meant only for local testing before performing the
80# full build of everything.
81#
82# It is possible to configure which steps of the build to perform using the MODE
83# argument to the "test" command which may take one of the following values:
84#
85# prep Execute the %prep section of the spec file only.
86# build Execute the %build section only (requres %prep to be executed
87# before). May be run multiple times.
88# install Execute the %install sectuion only (requires "prep" and "build"
89# to be executed before). May be run multiple times.
90# pack Create the RPM packages (requires "prep", "build" and "install"
91# to be executed before). Note that this step will also execute
92# the %clean section so that a new "build" + "install" execution is
93# necessary for "pack" to succeed.
94#
95# When no MODE argument is given, all steps are executed in a proper order.
96#
97# The results of the "test" command are stored in a log file in the logs/test
98# directory of the rpmbuild tree. The previous log file, if any, is given a .bak
99# extension (the previous .bak file will be deleted).
100#
101# Uploading packages
102# ------------------
103#
104# The "upload" command will upload the packages built with the "build"
105# command to the official distribution channel configured in
106# rpmbuild-bot-env.sh. The REPO argument specifies one of the configured
107# repositories. When REPO is not given, the default repository is used
108# (usually experimental or similar).
109#
110# Note that the "upload" command needs log files from the "build" command
111# and will fail otherwise.
112#
113# Upon successful completion, the "upload" command will remove all uploaded
114# RPM and ZIP packages and will move all "build" log files to logs/archive.
115#
116# Return value
117# ------------
118#
119# The rpmbuild-bot.sh script will return a zero exit code upon successful
120# completion and non-zero otherwise. The log files should be inspected to
121# check for a reason of the failure.
122#
123
124#
125# Helpers.
126#
127
128run()
129{
130 "$@"
131 local rc=$?
132 if test $rc != 0 ; then
133 echo "ERROR: The following command failed with error code $rc:"
134 echo $@
135 exit $rc
136 fi
137}
138
139log_run()
140{
141 log="$1"
142 shift
143 "$@" >"$log" 2>&1
144 local rc=$?
145 if test $rc != 0 ; then
146 echo "ERROR: The following command failed with error code $rc:"
147 echo $@
148 echo "You will find more information in file '$log'."
149 echo "Here are the last 10 lines of output:"
150 echo ""
151 tail "$log" -n 10
152 exit $rc
153 fi
154}
155
156warn()
157{
158 echo "WARNING: $1"
159}
160
161die()
162{
163 echo "ERROR: $1"
164 exit 1
165}
166
167check_dir_var()
168{
169 eval local val="\$$1"
170 [ -n "$val" ] || die "$1 is empty."
171 [ -d "$val" ] || die "$1 is '$val', not a valid directory."
172}
173
174usage()
175{
176 echo "Usage:"
177 sed -n -e "s/rpmbuild-bot.sh/${0##*/}/g" -e 's/^# > / /p' < "$0"
178 exit 255
179}
180
181build_cmd()
182{
183 # Check settings.
184 test -n "$RPMBUILD_BOT_ARCH_LIST" || die "RPMBUILD_BOT_ARCH_LIST is empty."
185
186 local base_arch="${RPMBUILD_BOT_ARCH_LIST##* }"
187
188 echo "Spec file: $spec_full"
189 echo "Targets: $RPMBUILD_BOT_ARCH_LIST + SRPM + ZIP ($base_arch)"
190
191 if [ -f "$spec_list" ] ; then
192 if [ -z "$force" ] ; then
193 die "File '$spec_list' already exists.
194This file indicates a successful build that was not yet uploaded.
195Either run the '"'upload'"' command to upload the generated RPM and ZIP
196packages to the distribution repository or use the -f option to
197force their removal if you are sure they should be discarded."
198 fi
199
200 echo "Detected successful build in $spec_list, cleaning up due to -f option..."
201 while read f; do
202 echo "Removing $f..."
203 rm -f "$f"
204 done < "$spec_list"
205
206 echo "Removing $log_base.*.log and .list files..."
207 rm -f "$log_base".*.log "$log_base".*.list "$log_base".list
208 fi
209
210 # Generate RPMs.
211 for arch in $RPMBUILD_BOT_ARCH_LIST ; do
212 echo "Creating RPMs for '$arch' target (logging to $log_base.$arch.log)..."
213 log_run "$log_base.$arch.log" rpmbuild.exe --target=$arch -bb "$spec_full"
214 done
215
216 # Generate SRPM.
217 echo "Creating SRPM (logging to $log_base.srpm.log)..."
218 log_run "$log_base.srpm.log" rpmbuild -bs "$spec_full"
219
220 # Find SRPM file name in the log.
221 local src_rpm=`grep "^Wrote: \+.*\.src\.rpm$" "$log_base.srpm.log" | sed -e "s#^Wrote: \+##g"`
222 [ -n "$src_rpm" ] || die "Cannot find .src.rpm file name in '$log_base.srpm.log'."
223
224 # Find package version.
225 local ver_full="${src_rpm%.src.rpm}"
226 local ver_full="${ver_full##*/${spec_name}-}"
227 [ -n "$ver_full" ] || die "Cannot deduce package version from '$src_rpm'."
228
229 # Find all RPM packages for the base arch (note the quotes around `` - it's to preserve multi-line result).
230 local rpms="`grep "^Wrote: \+.*\.\($base_arch\.rpm\|noarch\.rpm\)$" "$log_base.$base_arch.log" | sed -e "s#^Wrote: \+##g"`"
231 [ -n "$rpms" ] || die "Cannot find .$base_arch.rpm/.noarch.rpm file names in '$log_base.base_arch.log'."
232
233 local ver_full_zip=`echo $ver_full | tr . _`
234 local zip="$zip_dir/$spec_name-$ver_full_zip.zip"
235
236 # Generate ZIP.
237 echo "Creating ZIP (logging to $log_base.zip.log)..."
238 create_zip()
239 {(
240 run cd "$zip_dir"
241 rm -r "@unixroot" 2> /dev/null
242 # Note no quoters around $rpms - it's to split at EOL.
243 for f in $rpms ; do
244 echo "Unpacking $f..."
245 run rpm2cpio "$f" | cpio -idm
246 done
247 rm -f "$zip" 2> /dev/null
248 echo "Creating '$zip'..."
249 run zip -mry9 "$zip" "@unixroot"
250 )}
251 log_run "$log_base.zip.log" create_zip
252
253 local ver_list="$log_base.$ver_full.list"
254
255 # Generate list of all generated packages for further reference.
256 echo "Creating list file ($ver_list)..."
257 echo "$src_rpm" > "$ver_list"
258 echo "$zip" >> "$ver_list"
259 # Save base arch RPMs.
260 for f in "$rpms" ; do
261 echo "$f" >> "$ver_list"
262 done
263 # Save other arch RPMs.
264 for arch in ${RPMBUILD_BOT_ARCH_LIST%${base_arch}} ; do
265 rpms="`grep "^Wrote: \+.*\.$arch\.rpm$" "$log_base.$arch.log" | sed -e "s#^Wrote: \+##g"`"
266 [ -n "$rpms" ] || die "Cannot find .$arch.rpm file names in '$log_base.arch.log'."
267 for f in $rpms ; do
268 echo "$f" >> "$ver_list"
269 done
270 done
271
272 # Everything succeeded. Symlink the list file so that "upload" can find it.
273 run ln -s "${ver_list##*/}" "$log_base.list"
274}
275
276test_cmd()
277{
278 echo "Spec file: $spec_full"
279
280 local base_arch="${RPMBUILD_BOT_ARCH_LIST##* }"
281 local cmds=
282
283 [ -z "$command_arg" ] && command_arg="all"
284
285 case "$command_arg" in
286 all)
287 cmds="$cmds -bb"
288 ;;
289 prep)
290 cmds="$cmds -bp --short-circuit"
291 ;;
292 build)
293 cmds="$cmds -bc --short-circuit"
294 ;;
295 install)
296 cmds="$cmds -bi --short-circuit"
297 ;;
298 pack)
299 cmds="$cmds -bb --short-circuit"
300 ;;
301 *)
302 die "Invalid test build sub-command '$command_arg'."
303 ;;
304 esac
305
306 local log_file="$log_dir/test/$spec_name.log"
307
308 if [ -f "$log_file" ] ; then
309 rm -f "$log_file.bak"
310 run mv "$log_file" "$log_file.bak"
311 fi
312
313 echo "Doing test RPM build for '$base_arch' target (logging to $log_file)..."
314 log_run "$log_file" rpmbuild.exe "--define=dist %nil" --target=$base_arch $cmds $spec_full
315
316 # Show the generated RPMs when appropriate.
317 if [ "$command_arg" = "all" -o "$command_arg" = "pack" ] ; then
318 local rpms=`grep "^Wrote: \+.*\.\($base_arch\.rpm\|noarch\.rpm\)$" "$log_file" | sed -e "s#^Wrote: \+##g"`
319 if [ -n "$rpms" ] ; then
320 echo "Successfully generated the following RPMs:"
321 for f in "$rpms" ; do
322 echo "$f"
323 done
324 else
325 warn "Cannot find .$base_arch.rpm/.noarch.rpm file names in '$log_file'."
326 fi
327 fi
328}
329
330upload_cmd()
331{
332 # Check settings.
333 test -n "$RPMBUILD_BOT_UPLOAD_REPO_LIST" || die "RPMBUILD_BOT_UPLOAD_REPO_LIST is empty."
334
335 local repo="$command_arg"
336 [ -z "$repo" ] && repo="${RPMBUILD_BOT_UPLOAD_REPO_LIST%% *}"
337
338 check_dir_var "RPMBUILD_BOT_UPLOAD_${repo}_DIR"
339
340 eval local base="\$RPMBUILD_BOT_UPLOAD_${repo}_DIR"
341
342 [ -f "$spec_list" ] || die \
343"File '$spec_list' is not found.
344You man need to build the packages using the 'build' command."
345
346 while read f; do
347 case "$f" in
348 *.src.rpm)
349 eval local d="$RPMBUILD_BOT_UPLOAD_REPO_LAYOUT_srpm"
350 ;;
351 *.*.rpm)
352 local arch="${f%.rpm}"
353 arch="${arch##*.}"
354 [ -n "$arch" ] || die "No arch spec in file name '$f' in '$spec_list'."
355 eval d="$RPMBUILD_BOT_UPLOAD_REPO_LAYOUT_rpm"
356 ;;
357 *.zip)
358 eval d="$RPMBUILD_BOT_UPLOAD_REPO_LAYOUT_zip"
359 ;;
360 *)
361 die "Unsupported file name '$f' in '$spec_list'."
362 ;;
363 esac
364 [ -d "$d" ] || die "'$d' is not a directory."
365 [ -f "$d/${f##*/}" -a -z "$force" ] && die \
366"File '$d/${f##*/}' already exists.
367Use the -f option to force uploading if you are sure the existing
368packages in the repository should be discarded."
369 echo "Copying $f to $d..."
370 run cp -p "$f" "$d"
371 done < "$spec_list"
372
373 # On success, delete the uploaded packages and archive log files.
374 while read f; do
375 echo "Removing $f..."
376 rm -f "$f"
377 done < "$spec_list"
378
379 # Note: versioned .list file will remain in archive forever (for further reference).
380 echo "Removing old '$spec_name' logs from $log_dir/archive..."
381 rm -f "$log_dir/archive/$spec_name".*.log "$log_dir/archive/$spec_name".list
382 echo "Moving '$spec_name' logs to $log_dir/archive..."
383 run mv "$log_base".*.log "$log_base".*.list "$log_base".list "$log_dir/archive/"
384}
385
386#
387# Main.
388#
389
390# Parse command line.
391while [ -n "$1" ] ; do
392 case "$1" in
393 -*)
394 options="$*"
395 while [ -n "$1" ] ; do
396 case "$1" in
397 -f) force="-f"
398 ;;
399 *) usage
400 ;;
401 esac
402 shift
403 done
404 break
405 ;;
406 *)
407 if [ -z "$spec" ] ; then
408 spec="$1"
409 else
410 command="$1"
411 fi
412 ;;
413 esac
414 shift
415done
416
417[ -n "$spec" ] || usage
418[ -z "$command" ] && command="build"
419
420command_name="${command%=*}"
421command_arg="${command#*=}"
422[ "$command_name" = "$command_arg" ] && command_arg=
423
424case "$command_name" in
425 build|upload|test)
426 ;;
427 *) usage
428 ;;
429esac
430
431# Query all rpmbuild macros in a single run as it may be slow.
432eval `rpmbuild.exe --eval='rpmbuild_dir="%_topdir" ; spec_dir="%_specdir"' | tr '\\\' /`
433
434log_dir="$rpmbuild_dir/logs"
435zip_dir="$rpmbuild_dir/zip"
436
437spec=`echo $spec | tr '\\\' /`
438
439spec_name="${spec##*/}"
440
441if [ "$spec_name" = "$spec" ] ; then
442 # No path information, use SPECS
443 spec_full="$spec_dir/${spec_name%.spec}.spec"
444else
445 # Has some path, use it.
446 spec_full="${spec%.spec}.spec"
447fi
448
449spec_name="${spec_name%.spec}"
450
451[ -f "$spec_full" ] || die "Spec file '$spec_full' is not found."
452
453# Prepare some (non-rpmbuild-standard) directories.
454run mkdir -p "$log_dir"
455run mkdir -p "$log_dir/archive"
456run mkdir -p "$log_dir/test"
457run mkdir -p "$zip_dir"
458
459[ -z "$command" ] && command="build"
460
461command_name="${command%=*}"
462comand_arg="${command#*=}"
463[ "$command_name" = "$command_arg" ] && command_arg=
464
465log_base="$log_dir/$spec_name"
466spec_list="$log_base.list"
467
468echo "Package: $spec_name"
469echo "Command: $command $options"
470
471# Set up the rpmbuild-bot environment.
472. "${0%%.sh}-env.sh"
473
474run eval "${command_name}_cmd"
475
476echo "All done."
477
478exit 0
Note: See TracBrowser for help on using the repository browser.