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