1RM_DIR ?= $(RM) -rf ;
2
3actions piecemeal together existing CleanDir
4{
5	$(RM_DIR) "$(>)"
6}
7
8
9rule Copy
10{
11	if $(2) {
12		SEARCH on $(2) += $(SEARCH_SOURCE) ;
13		Depends $(1) : <build>copyattr $(2) ;
14		Copy1 $(1) : <build>copyattr $(2) ;
15	}
16}
17
18
19actions Copy1
20{
21	$(HOST_ADD_BUILD_COMPATIBILITY_LIB_DIR) \
22	"$(2[1])" -d "$(2[2-])" "$(1)"
23}
24
25
26rule SymLink
27{
28	# SymLink <target> : <source> : <makeDefaultDependencies> ;
29	# Links <target> to <source>.
30	# <source> is the exact link contents. No binding is done.
31	# <makeDefaultDependencies> If true, <target> will be made a dependency
32	# of the `all' pseudo target, i.e. it will be made by default, and removed
33	# on `jam clean'.
34
35	local target = $(1) ;
36	local source = $(2) ;
37	local makeDefaultDependencies = $(3) ;
38	if ! $(makeDefaultDependencies) {
39		makeDefaultDependencies = true ;
40	}
41	LINKCONTENTS on $(target) = $(source) ;
42	SymLink1 $(target) ;
43	if $(makeDefaultDependencies) = true {
44		LocalDepends files : $(target) ;
45		LocalClean clean : $(target) ;
46	}
47}
48
49actions SymLink1
50{
51	$(RM) "$(1)" && $(LN) -s "$(LINKCONTENTS)" "$(1)"
52}
53
54rule RelSymLink
55{
56	# RelSymLink <link> : <link target> : <makeDefaultDependencies> ;
57	# Creates a relative symbolic link from <link> to <link target>.
58	# <link> and <link target> can be usual targets. They may have a grist
59	# and don't need to have any dirname. Their LOCATE variables are used to
60	# find their locations.
61	# <makeDefaultDependencies> If true (which is the default), <link> will be
62	# made a dependency of the `files' pseudo target, i.e. it will be made by
63	# default, and removed on `jam clean'.
64
65	local target = $(1) ;
66	local source = $(2) ;
67	local makeDefaultDependencies = $(3) ;
68	local targetDir = [ on $(target) FDirName $(LOCATE[1]) $(target:D) ] ;
69	local sourceDir = [ on $(source) FDirName $(LOCATE[1]) $(source:D) ] ;
70	local sourcePath = $(source:G=) ;
71	sourcePath = $(sourcePath:D=$(sourceDir)) ;
72	local targetDirComponents = [ FSplitPath $(targetDir) ] ;
73	local sourceComponents = [ FSplitPath $(sourcePath) ] ;
74
75	SymLink $(target)
76		: [ FRelPath $(targetDirComponents) : $(sourceComponents) ]
77		: $(makeDefaultDependencies) ;
78	NOUPDATE $(target) ;
79	Depends $(target) : $(source) ;
80}
81
82rule AbsSymLink
83{
84	# AbsSymLink <link> : <link target> : <link dir>
85	#			: <makeDefaultDependencies> ;
86	# Creates an absolute symbolic link from <link> to <link target>.
87	# <link> and <link target> must be usual targets. If <link dir> is
88	# given, then it is set as LOCATE directory on <link>.
89	# <makeDefaultDependencies> If true (which is the default), <link> will be
90	# made a dependency of the `files' pseudo target, i.e. it will be made by
91	# default, and removed on `jam clean'.
92
93	local makeDefaultDependencies = $(4) ;
94	if ! $(makeDefaultDependencies) {
95		makeDefaultDependencies = true ;
96	}
97
98	Depends $(1) : $(2) ;
99	if $(3) {
100		MakeLocate $(1) : $(3) ;
101	}
102	SEARCH on $(2) += $(SEARCH_SOURCE) ;
103	if $(makeDefaultDependencies) = true {
104		LocalDepends files : $(1) ;
105		LocalClean clean : $(1) ;
106	}
107}
108
109actions AbsSymLink
110{
111	target="$(2)"
112	case "$target" in
113		/*) ;;
114		*) target=`pwd`/"$target";;
115	esac
116	$(RM) "$(1)" && $(LN) -s "$target" "$(1)"
117}
118
119rule HaikuInstall installAndUninstall : dir : sources : installgrist
120	: installRule : targets
121{
122	# Usage: HaikuInstall <[ install [ and uninstall ] pseudotarget ]>
123	#	: <directory> : <sources to install> : [ <installgrist> ]
124	#	: [ <install rule> ] : [ <targets> ] ;
125
126	local install = $(installAndUninstall[1]) ;
127	install ?= install ;
128	local uninstall = $(installAndUninstall[2]) ;
129	uninstall ?= un$(install) ;
130	installgrist ?= $(INSTALLGRIST) ;
131	installRule ?= Install ;
132
133	targets ?= $(sources) ;
134	targets = $(targets:G=$(installgrist)) ;
135
136	NotFile $(install) ;
137	NotFile $(uninstall) ;
138	Depends $(install) : $(targets) ;
139	Clean $(uninstall) : $(targets) ;
140
141	SEARCH on $(sources) += $(SEARCH_SOURCE) ;
142	MakeLocate $(targets) : $(dir) ;
143
144	local source ;
145	for source in $(sources) {
146		local target = $(targets[1]) ;
147		targets = $(targets[2-]) ;
148
149		Depends $(target) : $(source) ;
150		$(installRule) $(target) : $(source) ;
151
152		if [ on $(target) return $(MODE) ] {
153			Chmod $(target) ;
154		}
155
156		if $(OWNER) && $(CHOWN) {
157			Chown $(target) ;
158			OWNER on $(target) = $(OWNER) ;
159		}
160
161		if $(GROUP) && $(CHGRP) {
162			Chgrp $(target) ;
163			GROUP on $(target) = $(GROUP) ;
164		}
165	}
166}
167
168rule InstallAbsSymLinkAdapter
169{
170	# InstallAbsSymLinkAdapter <link> : <link target>
171	if ! [ on $(2) return $(TARGET) ] {
172		TARGET on $(2) = [ on $(2) return $(SEARCH) ] ;
173	}
174	AbsSymLink $(1) : $(2) : : false ;
175}
176
177rule HaikuInstallAbsSymLink
178{
179	# Usage: HaikuInstallAbsSymLink <[ install [ and uninstall ] pseudotarget ]>
180	#							   : <directory> : <sources to install>
181	#							   : [ <installgrist> ] ;
182	HaikuInstall $(1) : $(2) : $(3) : $(4) : InstallAbsSymLinkAdapter ;
183}
184
185rule InstallRelSymLinkAdapter
186{
187	# InstallRelSymLinkAdapter <link> : <link target>
188	if ! [ on $(2) return $(TARGET) ] {
189		TARGET on $(2) = [ on $(2) return $(SEARCH) ] ;
190	}
191	RelSymLink $(1) : $(2) : false ;
192}
193
194rule HaikuInstallRelSymLink
195{
196	# Usage: HaikuInstallRelSymLink <[ install [ and uninstall ] pseudotarget ]>
197	#							   : <directory> : <sources to install>
198	#							   : [ <installgrist> ] ;
199	HaikuInstall $(1) : $(2) : $(3) : $(4) : InstallRelSymLinkAdapter ;
200}
201
202
203rule UnarchiveObjects
204{
205	# UnarchiveObjects <target objects> : <static object>
206
207	MakeLocateArch $(1) ;
208	Depends $(1) : $(2) ;
209	SEARCH on $(2) = $(SEARCH_SOURCE) ;
210}
211
212actions UnarchiveObjects
213{
214	( cd $(1[1]:D) && $(TARGET_AR_$(TARGET_PACKAGING_ARCH)) \
215		$(TARGET_UNARFLAGS_$(TARGET_PACKAGING_ARCH)) "$(2)" $(1:BS) )
216}
217
218
219rule ExtractArchive directory : entries : archiveFile : grist
220{
221	# ExtractArchive <directory> : <entries> : <archiveFile> [ : <grist> ]
222	#
223	# Extract the archive file target <archiveFile> to directory <directory>.
224	# The rule can be called multiple times for different <entries> for the same
225	# <directory> and <archiveFile> combo.
226	#
227	# <directory> - The directory into which to extract the archive file. The
228	#               directory is created by this rule and it is the target
229	#               that the extract action is associated with.
230	# <entries>   - The entries of the archive file one is interested in. The
231	#               rule always extracts the complete archive file, from the
232	#               given entries the rule creates targets (using <grist>)
233	#               representing the extracted entries. Those targets are
234	#               returned by the rule.
235	# <archiveFile> - The archive file target to extract.
236	# <grist>     - The grist used to create targets from <entries>. Defaults to
237	#               "extracted".
238
239	grist ?= extracted ;
240
241	# Turn the entries into targets to build.
242	local targets ;
243	local entry ;
244	for entry in $(entries) {
245		local target = $(entry:G=$(grist)) ;
246		targets += $(target) ;
247	}
248
249	LOCATE on $(targets) = $(directory:G=) ;
250	Depends $(targets) : $(directory) $(archiveFile) ;
251	NoUpdate $(targets) ;
252
253	# one-time initialization for the main target (the directory)
254	if ! [ on $(directory) return $(INITIALIZED) ] {
255		# make sure the parent dir exists
256		local parentDir = $(directory:PG=dir) ;
257		Depends $(directory) : $(parentDir) ;
258		MkDir $(parentDir) ;
259
260		NoUpdate $(directory) ;
261		Depends $(directory) : $(archiveFile) ;
262		switch $(archiveFile:S)
263		{
264			case .zip :
265				ExtractZipArchive1 $(directory) : $(archiveFile) ;
266
267			case .tgz :
268				ExtractTarArchive1 $(directory) : $(archiveFile) ;
269
270			case .hpkg :
271				Depends $(directory) : <build>package ;
272				ExtractHPKGArchive1 $(directory)
273					: <build>package $(archiveFile) ;
274
275			case "" :
276				Exit "ExtractArchive: No archive passed" ;
277
278			case * :
279				Exit "ExtractArchive: Unhandled archive extension:"
280					"$(archiveFile:S)" ;
281		}
282		INITIALIZED on $(directory) = 1 ;
283	}
284
285	return $(targets) ;
286}
287
288
289actions ExtractZipArchive1
290{
291	mkdir -p $(1)
292	unzip -q -u -o -d $(1) $(2)
293}
294
295
296actions ExtractTarArchive1
297{
298	mkdir -p $(1)
299	tar -C $(1) -xf $(2)
300}
301
302
303actions ExtractHPKGArchive1
304{
305	mkdir -p "$(1)"
306	$(HOST_ADD_BUILD_COMPATIBILITY_LIB_DIR) \
307	$(2[1]) extract -C "$(1)" "$(2[2])"
308}
309
310
311rule ObjectReference
312{
313	# ObjectReference <reference object> : <source object>
314	# Makes <reference object> refer to the same file as <source object>.
315	# The filenames must of course be identical.
316	# <source object> must have already been LOCATEd.
317
318	local ref = $(1) ;
319	local source = $(2) ;
320	if $(ref) != $(source) {
321		Depends $(ref) : $(source) ;
322		LOCATE on $(ref) = [ on $(source) return $(LOCATE) ] ;
323	}
324}
325
326rule ObjectReferences
327{
328	# ObjectReferences <source objects>
329	# Creates local references to <source objects>, i.e. identifiers with the
330	# current grist referring to the same files. <source objects> must have
331	# already been LOCATEd.
332
333	local source ;
334	for source in $(1) {
335		ObjectReference [ FGristFiles $(source) ] : $(source) ;
336	}
337}
338
339
340rule CopySetHaikuRevision target : source
341{
342	# CopySetHaikuRevision <target> : <source>
343	#
344	# Copy <source> to <target>, writing the Git revision of the working
345	# directory into the haiku revision section of <target>.
346	#
347	# <target> - Output file target. Gristed and located target.
348	# <source> - ELF object to be copied. Gristed and located target.
349
350	PropagateContainerUpdateTargetFlags $(target) : $(source) ;
351
352	HAIKU_TARGET_IS_EXECUTABLE on $(target) = [ on $(source)
353		return $(HAIKU_TARGET_IS_EXECUTABLE) ] ;
354
355	local revisionFile = [ DetermineHaikuRevision ] ;
356
357	Depends $(target)
358		: <build>copyattr <build>set_haiku_revision $(source) $(revisionFile) ;
359	CopySetHaikuRevision1 $(target)
360		: <build>copyattr <build>set_haiku_revision $(source) $(revisionFile) ;
361}
362
363
364actions CopySetHaikuRevision1
365{
366	export $(HOST_ADD_BUILD_COMPATIBILITY_LIB_DIR)
367
368	$(2[1]) --data $(2[3]) $(1) || exit 1
369
370	revision=0
371	if [ -n "$(2[4]:E=)" ]; then
372		revision="`cat $(2[4]:E=)`"
373	fi
374	$(2[2]) $(1) "$revision"
375}
376
377
378rule DetermineHaikuRevision
379{
380	# If existing, make the target depend on the $GIT_DIR/index file,
381	# so it gets updated when the revision changes due to commits or merges.
382	# If GIT_DIR is not set, fall back to .git/index.
383	local dotGit = <haiku-rootdir>.git ;
384	local revisionFile = <haiku-rootdir>haiku-revision ;
385	if ! [ on $(dotGit) return $(HAIKU_GIT_REVISION_DETERMINED) ] {
386		HAIKU_GIT_REVISION_DETERMINED on $(dotGit) = 1 ;
387		MakeLocate $(revisionFile) : $(HAIKU_BUILD_OUTPUT_DIR) ;
388		LocalClean clean : $(revisionFile) ;
389		if $(HAIKU_REVISION) {
390			DetermineHaikuRevision2 $(revisionFile) ;
391		} else if $(GIT_DIR) && $(GIT_DIR) != ".git" {
392			local gitIndex = <$(GIT_DIR)>index ;
393			SEARCH on $(gitIndex) = [ FDirName $(GIT_DIR) ] ;
394			Depends $(revisionFile) : $(gitIndex) ;
395			DetermineHaikuRevision1 $(revisionFile) : $(gitIndex) ;
396		} else if [ Glob [ FDirName $(HAIKU_TOP) .git ] : index ] {
397			local gitIndex = <haiku-rootdir-git>index ;
398			SEARCH on $(gitIndex) = [ FDirName $(HAIKU_TOP) .git ] ;
399			Depends $(revisionFile) : $(gitIndex) ;
400			DetermineHaikuRevision1 $(revisionFile) : $(gitIndex) ;
401		} else {
402			Exit "ERROR: Haiku revision could not be determined." ;
403		}
404	}
405
406	return $(revisionFile) ;
407}
408
409
410actions DetermineHaikuRevision1
411{
412	$(HAIKU_TOP)/build/scripts/determine_haiku_revision $(HAIKU_TOP) $(1)
413}
414
415
416actions DetermineHaikuRevision2
417{
418	echo $(HAIKU_REVISION) > $(1)
419}
420
421
422rule DataFileToSourceFile sourceFile : dataFile : dataVariable : sizeVariable
423{
424	sourceFile = [ FGristFiles $(sourceFile) ] ;
425	MakeLocateCommonPlatform $(sourceFile) ;
426
427	sizeVariable ?= $(dataVariable)Size ;
428
429	DATA_VARIABLE on $(sourceFile) = $(dataVariable) ;
430	SIZE_VARIABLE on $(sourceFile) = $(sizeVariable) ;
431
432	Depends $(sourceFile) : <build>data_to_source $(dataFile) ;
433	DataFileToSourceFile1 $(sourceFile) : <build>data_to_source $(dataFile) ;
434	LocalClean clean : $(sourceFile) ;
435}
436
437actions DataFileToSourceFile1
438{
439	$(HOST_ADD_BUILD_COMPATIBILITY_LIB_DIR) \
440	$(2[1]) $(DATA_VARIABLE) $(SIZE_VARIABLE) $(2[2]) $(1)
441}
442
443rule DownloadLocatedFile target : url : source
444{
445	# DownloadLocatedFile <target> : <url> [ : <source> ] ;
446	#
447	# <source> is an optional target that <target> will be made dependent on.
448	# Its resolved path can be used in <url> via '$source'.
449
450	URL on $(target) = $(url) ;
451
452	if $(source) {
453		Depends $(target) : $(source) ;
454	}
455
456	DownloadLocatedFile1 $(target) : $(source) ;
457}
458
459if $(HAIKU_NO_DOWNLOADS) = 1 {
460	actions DownloadLocatedFile1
461	{
462		source="$(2)"
463		echo "ERROR: Would need to download $(URL), but HAIKU_NO_DOWNLOADS is set!"
464		exit 1
465	}
466} else {
467	actions DownloadLocatedFile1
468	{
469		source="$(2)"
470		wget --retry-connrefused --retry-on-host-error --timeout 30 -O "$(1)" $(URL) || exit 1
471		touch "$(1)"
472	}
473}
474
475rule DownloadFile file : url : source
476{
477	# DownloadFile <file> : <url> [ : <source> ] ;
478	#
479	# <source> is an optional target that the target will be made dependent on.
480	# Its resolved path can be used in <url> via '$source'.
481
482	file = $(file:G=download) ;
483
484	# Request the download only once.
485	if [ on $(file) return $(HAIKU_FILE_DOWNLOAD) ] {
486		return $(file) ;
487	}
488
489	HAIKU_FILE_DOWNLOAD on $(file) = 1 ;
490
491	MakeLocate $(file) : $(HAIKU_DOWNLOAD_DIR) ;
492	DownloadLocatedFile $(file) : $(url) : $(source) ;
493
494	return $(file) ;
495}
496
497
498actions ChecksumFileSHA256
499{
500	$(HOST_SHA256) $(2) \
501		| $(HOST_EXTENDED_REGEX_SED) 's,([^[:space:]]*).*,\1,' > $(1)
502		# The sed part is only necessary for sha256sum, but it doesn't harm for
503		# sha256 either.
504}
505
506
507rule Sed target : source : substitutions : targetMap
508{
509	# Sed <target> : [ <source> ] : <substitutions> [ : <targetMap> ] ;
510	#
511	# Performs substitutions in a text file. If <source> is given, that is the
512	# input, otherwise the substitutions are performed in place on <target>. The
513	# caller is responsible for locating <target>, <source>, and any other used
514	# target.
515	#
516	# <target> - The target file.
517	# <source> - The source file. If not given, the substitutions are performed
518	#	in place on <target>. If given, a dependency of <target> to <source>
519	#	will be established.
520	# <substitutions> - List of substitutions to be performed. Each element
521	#	specifies a substitution. It's a partial sed "s" command of the form
522	#	"<pattern>,<replacement>".
523	# <targetMap> - A list of elements of the form "<variable>=<mappedTarget>".
524	#	<variable> specifies a name of a shell variable, <mappedTarget> a jam
525	#	target whose bound name will be assigned to the shell variable. The
526	#	variable can be used in <substitutions>. A dependency of <target> to
527	#	<mappedTarget> will be established.
528
529	# We need a temporary (shell) file to which we write the target variable
530	# mappings and the sed invocations. This is necessary, since multiple rule
531	# invocations are allowed for a target, so that we cannot use on-target
532	# variables.
533	local script = [ NextID ] ;
534	script = temp-sed-script-$(target:BS)-$(script) ;
535
536	# process the target variable mappings
537	local mappedTargets ;
538	local targetMapElement ;
539	for targetMapElement in $(targetMap) {
540		local split = [ Match ([^=]+)=(.*) : $(targetMapElement) ] ;
541		HAIKU_SED_SCRIPT_VARIABLE on $(script) += $(split[1]) ;
542		local mappedTarget = $(split[2]) ;
543		mappedTargets += $(mappedTarget) ;
544	}
545
546	HAIKU_SED_SCRIPT_SUBSTITUTIONS on $(script)
547		= "-e \"s,$(substitutions),g\"" ;
548	HAIKU_SED_SCRIPT_SOURCE on $(script) = $(source) ;
549	if $(source) {
550		HAIKU_SED_SCRIPT_SOURCE_ARGUMENTS on $(script) = ">" ;
551	} else {
552		HAIKU_SED_SCRIPT_SOURCE_ARGUMENTS on $(script) = -i ;
553	}
554
555	# build the script
556	MakeLocate $(script) : $(HAIKU_TMP_DIR) ;
557	Depends $(script) : $(mappedTargets) $(source) ;
558	SedCreateScript $(script) : $(mappedTargets) ;
559
560	# build the target
561	Depends $(target) : $(script) ;
562	Sed1 $(target) : $(script) ;
563	RmTemps $(target) : $(script) ;
564}
565
566
567actions SedCreateScript bind HAIKU_SED_SCRIPT_SOURCE
568{
569	set -o errexit
570
571	$(RM) "$(1)"
572	touch "$(1)"
573
574	set -- $(2)
575	for variable in "$(HAIKU_SED_SCRIPT_VARIABLE)" ; do
576		echo "$variable=\"$1\"" >> "$(1)"
577		shift
578	done
579
580	echo sed '$(HAIKU_SED_SCRIPT_SUBSTITUTIONS)' \
581		'"$(HAIKU_SED_SCRIPT_SOURCE)"' "$(HAIKU_SED_SCRIPT_SOURCE_ARGUMENTS)" \
582		'"$target"' >> "$(1)"
583}
584
585
586actions Sed1
587{
588	set -o errexit
589
590	target="$(1)"
591	. "$(2)"
592}
593
594
595rule StripFile target : source
596{
597	# Note: The caller is reponsible for matching TARGET_PACKAGING_ARCH with
598	# the architecture the target was built for.
599	STRIP on $(target) = $(HAIKU_STRIP_$(TARGET_PACKAGING_ARCH)) ;
600
601	PropagateContainerUpdateTargetFlags $(target) : $(source) ;
602
603	LocalClean clean : $(target) ;
604	Depends $(target) : $(source) <build>xres <build>copyattr ;
605	StripFile1 $(target) : $(source) <build>xres <build>copyattr ;
606}
607
608
609actions StripFile1
610{
611	export $(HOST_ADD_BUILD_COMPATIBILITY_LIB_DIR)
612	"$(STRIP)" -o "$(1)" "$(2[1])"
613	"$(2[2])" -o "$(1)" "$(2[1])"
614	"$(2[3])" "$(2[1])" "$(1)"
615}
616
617
618rule StripFiles files
619{
620	# Note: The caller is reponsible for matching TARGET_PACKAGING_ARCH with
621	# the architecture the targets were built for.
622	local strippedFiles ;
623	local file ;
624	for file in $(files) {
625		local strippedFile = $(file:G=stripped_$(file:G)) ;
626		# Place the stripped file in a "stripped" subdirectory of the file's
627		# location.
628		local location = [ on $(file) return $(LOCATE) ] ;
629		if ! $(location) {
630			location
631				= $(TARGET_COMMON_DEBUG_OBJECT_DIR_$(TARGET_PACKAGING_ARCH)) ;
632		}
633		MakeLocateArch $(strippedFile) : [ FDirName $(location) stripped ] ;
634		StripFile $(strippedFile) : $(file) ;
635		strippedFiles += $(strippedFile) ;
636	}
637
638	return $(strippedFiles) ;
639}
640