1#!/bin/sh
2set -o errexit
3
4# The first argument is the shell script that initializes the variables:
5# sourceDir
6# outputDir
7# tmpDir
8# addBuildCompatibilityLibDir
9# systemPackages - lists of the hpkg packages copied/updated into /system/packages
10# otherPackages - lists of the hpkg packages copied/updated into other (optional) places
11# repositories - all repository files
12# downloadDir
13# The following are only for image types:
14# installDir
15# isImage
16# imagePath
17# imageSize
18# imageLabel
19# resolvePackageDependencies
20# noDownloads
21# updateAllPackages
22# updateOnly
23# dontClearImage
24# isVMwareImage
25#
26# addattr
27# copyattr
28# getPackageDependencies
29# package
30# rc
31# rmAttrs
32# unzip
33# The following are only for image types:
34# bfsShell
35# fsShellCommand
36# makebootable
37# resattr
38# vmdkimage
39# The following is only for cd types:
40# generate_attribute_stores
41# isCD
42#
43if [ $# -gt 0 ]; then
44	. $1
45	shift
46fi
47
48if [ ! $isCD ]; then
49	# If the haiku image path is a symlink resolve it now (makebootable needs the
50	# path of the actual device path under Linux).
51	normalizedImagePath=''
52	if readlink -f "$imagePath" > /dev/null 2>&1 ; then
53		normalizedImagePath=$(readlink -f "$imagePath")
54	elif realpath "$imagePath" > /dev/null 2>&1 ; then
55		normalizedImagePath=$(realpath "$imagePath")
56	elif greadlink -f "$imagePath" > /dev/null 2>&1 ; then
57		normalizedImagePath=$(greadlink -f "$imagePath")
58	fi
59	if [ -n "$normalizedImagePath" ]; then
60		imagePath="$normalizedImagePath"
61	fi
62fi
63
64# this adds the build library dir to LD_LIBRARY_PATH
65eval "$addBuildCompatibilityLibDir"
66
67# map the shell commands
68if [ $isCD ]; then
69	outputDir=$tmpDir/cdsource
70
71	sPrefix=
72	tPrefix="$outputDir/"
73	cd=cd
74	scd=:
75	cp="$copyattr -d"
76	copyAttrs="$copyattr"
77	ln=ln
78	mkdir=mkdir
79	rm=rm
80	mkindex="mkindex -d $tPrefix"
81elif [ $isImage ]; then
82	# If FIFOs are used for the communication with the FS shell, prepare them.
83	if $fsShellCommand --uses-fifos; then
84		fifoBasePath=/tmp/build_haiku_image-$$-fifo
85		toFSShellFifo=${fifoBasePath}-to-shell
86		fromFSShellFifo=${fifoBasePath}-from-shell
87
88		rm -f $toFSShellFifo $fromFSShellFifo
89		mkfifo $toFSShellFifo $fromFSShellFifo
90
91		# Open the FIFOs such that they are ready for the fsShellCommand. This
92		# also makes sure that they remain open until this script exits. When we
93		# exit while the FS shell is still running and waiting for commands,
94		# closing of our file descriptors will break the FIFOs and the FS shell
95		# will exit, too.
96		# Note: A bit of trickery is needed since opening one end blocks until
97		# someone opens the other end.
98		sleep 3<$fromFSShellFifo 1 &
99		exec 6>$fromFSShellFifo 3<$fromFSShellFifo
100		sleep 5<$toFSShellFifo 1 &
101		exec 4>$toFSShellFifo 5<$toFSShellFifo
102
103		# Remove the FIFO files again -- we have the open FDs, so they can
104		# still be used and this makes sure they won't hang around any further.
105		rm -f $toFSShellFifo $fromFSShellFifo
106
107		# Remap the fsShellCommand and bfsShell such that they don't inherit the
108		# wrong FDs. For both fsShellCommand and bfsShell FD 3 is the input from
109		# the respectively other program, FD 4 is the output to it.
110		actualFSShellCommand="$fsShellCommand"
111		actualBFSShell="$bfsShell"
112
113		fsShellCommandWrapper()
114		{
115			$actualFSShellCommand 5>&- 6>&- "$@"
116		}
117
118		bfsShellWrapper()
119		{
120			$actualBFSShell 3>&5 4<&6 "$@"
121		}
122
123		fsShellCommand=fsShellCommandWrapper
124		bfsShell=bfsShellWrapper
125	fi
126
127	# set up the other commands
128	sPrefix=:
129	tPrefix=/myfs/
130	cd="$fsShellCommand cd"
131	scd="$fsShellCommand cd"
132	cp="$fsShellCommand cp -f"
133	copyAttrs="$fsShellCommand cp -a"
134	ln="$fsShellCommand ln"
135	mkdir="$fsShellCommand mkdir"
136	rm="$fsShellCommand rm"
137	mkindex="$fsShellCommand mkindex"
138else
139	sPrefix=
140	# TODO: This should come from the environment.
141	tPrefix="$installDir/"
142	cd=cd
143	scd=:
144	cp="$copyattr -d"
145	copyAttrs="$copyattr"
146	ln=ln
147	mkdir=mkdir
148	rm=rm
149	mkindex="mkindex -d $tPrefix"
150fi
151
152
153is_in_list()
154{
155	local element
156	for element in $2; do
157		if [ "$1" = "$element" ]; then
158			return 0
159		fi
160	done
161	return 1
162}
163
164
165extractFile()
166{
167	# extractFile <archive> <directory> <extractedSubDir>
168	archiveFile=$1
169	targetExtractedDir=$2
170	extractedSubDir=$3
171
172	extractDir=$tmpDir/extract
173	$rmAttrs -rf "$extractDir"
174	mkdir -p "$extractDir"
175
176	case "$archiveFile" in
177		*.zip)
178			echo "Extracting $archiveFile ..."
179			$unzip -q -d "$extractDir" "$archiveFile"
180			;;
181		*.tgz|*.tar.gz)
182			echo "Extracting $archiveFile ..."
183			tar -C "$extractDir" -xf "$archiveFile"
184			;;
185		*.hpkg)
186			echo "Extracting $archiveFile ..."
187			if [ -n "$extractedSubDir" ]; then
188				$package extract -C "$extractDir" "$archiveFile" \
189					"$extractedSubDir"
190			else
191				$package extract -C "$extractDir" "$archiveFile"
192			fi
193			;;
194		*)
195			echo "Unhandled archive extension in build_haiku_image" \
196				"extractFile()"
197			exit 1
198			;;
199	esac
200
201	if [ -f $extractDir/.OptionalPackageDescription ]; then
202		cat $extractDir/.OptionalPackageDescription >> $copyrightsFile
203		echo >> $copyrightsFile
204		rm $extractDir/.OptionalPackageDescription
205	fi
206
207	$cp -r "${sPrefix}$extractDir/$extractedSubDir/." \
208		"${tPrefix}$targetExtractedDir"
209
210	$rmAttrs -rf "$extractDir"
211}
212
213
214downloadFile()
215{
216	url=$1
217	path=$2
218
219	if [ ! -f "$path" ]; then
220		if [ "$noDownloads" = "1" ]; then
221			echo "ERROR: Would need to download $url, but HAIKU_NO_DOWNLOADS "
222				"is set!"
223			exit 1
224		fi
225		wget -O "$path" "$url"
226	fi
227}
228
229
230packageFileName()
231{
232	$package info -f "%fileName%" "$1"
233}
234
235
236mkdir -p $tmpDir
237copyrightsFile=$tmpDir/copyrights
238$rmAttrs -f $copyrightsFile
239
240if [ $isCD ]; then
241	# setup output dir
242	$rmAttrs -rf "$outputDir"
243	mkdir -p "$outputDir"
244fi
245
246# create the image and mount it
247if [ $isImage ]; then
248	echo
249
250	imageOffsetFlags=
251	if [ $isVMwareImage ]; then
252		imageOffsetFlags="--start-offset 65536"
253	fi
254
255	if [ ! $updateOnly ]; then
256		echo "Creating image ..."
257
258		imageFlags="-i${imageSize}M"
259		if [ ! "$dontClearImage" ]; then
260			imageFlags="$imageFlags -c"
261		fi
262
263		if [ $isVMwareImage ]; then
264			$vmdkimage -h 64k $imageFlags "$imagePath"
265		else
266			$createImage $imageFlags "$imagePath"
267		fi
268
269		$bfsShell --initialize $imageOffsetFlags "$imagePath" \
270			"$imageLabel" "block_size 2048"
271		$makebootable $imageOffsetFlags "$imagePath"
272	fi
273
274	$bfsShell -n $imageOffsetFlags "$imagePath" > /dev/null &
275	sleep 1
276
277	# Close FDs 5 and 6. Those represent the pipe ends that are used by the
278	# FS shell. Closing them in the shell process makes sure an unexpected death
279	# of the FS shell causes writing to/reading from the other ends to fail
280	# immediately.
281	exec 5>&- 6>&-
282
283	# bail out, if mounting fails
284	$cd .
285fi
286
287
288# Clean out the old packages directory, if updating all packages.
289if [ -n "$updateAllPackages" ]; then
290	echo "Removing old packages ..."
291	$rm -rf "${tPrefix}system/packages"
292	$mkdir -p "${tPrefix}system/packages"
293fi
294
295if [ ! $updateOnly ] && [ ! $isCD ]; then
296echo "Creating filesystem indices..."
297	$mkindex -t string Audio:Album
298	$mkindex -t string Audio:Artist
299	$mkindex -t string Media:Genre
300	$mkindex -t string Media:Title
301	$mkindex -t string MAIL:account
302	$mkindex -t string MAIL:cc
303	$mkindex -t string MAIL:chain
304	$mkindex -t string MAIL:from
305	$mkindex -t string MAIL:name
306	$mkindex -t string MAIL:pending_chain
307	$mkindex -t string MAIL:priority
308	$mkindex -t string MAIL:reply
309	$mkindex -t string MAIL:status
310	$mkindex -t string MAIL:subject
311	$mkindex -t string MAIL:thread
312	$mkindex -t string MAIL:to
313	$mkindex -t string META:address
314	$mkindex -t string META:city
315	$mkindex -t string META:company
316	$mkindex -t string META:county
317	$mkindex -t string META:email
318	$mkindex -t string META:fax
319	$mkindex -t string META:group
320	$mkindex -t string META:hphone
321	$mkindex -t string META:name
322	$mkindex -t string META:nickname
323	$mkindex -t string META:state
324	$mkindex -t string META:url
325	$mkindex -t string META:wphone
326	$mkindex -t string META:zip
327
328	$mkindex -t int32 Media:Rating
329	$mkindex -t int32 Media:Year
330	$mkindex -t int32 MAIL:account_id
331	$mkindex -t int32 MAIL:read
332	$mkindex -t int32 MAIL:when
333	$mkindex -t int32 MAIL:draft
334	$mkindex -t int32 MAIL:flags
335fi
336
337echo "Populating image ..."
338while [ $# -gt 0 ]; do
339	. $1
340	shift
341done
342
343
344# resolve package dependencies
345if [ -n "$resolvePackageDependencies" ]; then
346	echo "Resolving package dependencies ..."
347
348	packageUrls=`$getPackageDependencies $repositories -- $systemPackages`
349	for packageUrl in $packageUrls; do
350		packageFileName=`basename $packageUrl`
351		if [ "$otherPackages" != "${otherPackages#*$packageFileName}" ]; then
352			echo "ERROR: $packageFileName is a dependency of a package installed in /system/packages," \
353				"but it is in another (i.e. unactivated) package directory!"
354			exit 1
355		fi
356		packageFilePath="$downloadDir/$packageFileName"
357		downloadFile $packageUrl "$packageFilePath"
358		$cp "${sPrefix}$packageFilePath" "${tPrefix}system/packages"
359		systemPackages="$systemPackages $packageFilePath"
360	done
361
362	# validate dependencies of optional packages
363	packageUrls=`$getPackageDependencies $repositories -- $systemPackages $otherPackages`
364	packageFileNames=""
365	for packageUrl in $packageUrls; do
366		packageFileNames="$packageFileNames `basename $packageUrl`"
367	done
368	if [ ! -z "$packageFileNames" ]; then
369		echo "ERROR: Some of the unactivated (i.e. optional) packages have the following unmet dependencies:"
370		echo $packageFileNames
371		exit 1
372	fi
373fi
374
375# install default settings for packages
376for packageFile in $systemPackages; do
377	if $package list -p $packageFile | grep -E '^settings/' > /dev/null; then
378		extractFile $packageFile system/settings settings
379	fi
380done
381
382
383if [ $isCD ]; then
384	# generate the attribute stores
385	echo "Generating attribute stores ..."
386	$generate_attribute_stores "$tPrefix"
387
388	echo "Copying boot image ..."
389	$cp "$cdBootFloppy" "$outputDir"
390
391	if [ $(which xorriso) ]; then
392		# build the iso image using xorriso
393		echo "Building CD image..."
394		xorriso -as mkisofs -r -b `basename $cdBootFloppy` \
395			-V "$cdLabel" -o "$cdImagePath" "$tPrefix"
396	else
397		echo "you need xorriso to create a CD image."
398		exit 1
399	fi
400
401	# cleanup output dir
402	$rmAttrs -rf "$outputDir"
403fi
404
405# unmount
406if [ $isImage ]; then
407	echo "Unmounting ..."
408	$fsShellCommand sync
409	$fsShellCommand quit
410fi
411