xref: /haiku/build/jam/MiscRules (revision 204dee708a999d5a71d0cb9497650ee7cef85d0a)
1
2rule SetupObjectsDir
3{
4	# SetupObjectsDir
5	#
6	# Internal rule used to set up the *{LOCATE,SEARCH}*_{TARGET,SOURCE}
7	# variables for the current directory.
8
9	local relPath = [ FDirName $(SUBDIR_TOKENS[2-]) ] ;
10	if $(relPath) = . {
11		relPath = ;
12	}
13	COMMON_PLATFORM_LOCATE_TARGET =
14		[ FDirName $(HAIKU_COMMON_PLATFORM_OBJECT_DIR) $(relPath) ] ;
15
16	local var ;
17	for var in COMMON_ARCH COMMON_DEBUG DEBUG_$(HAIKU_DEBUG_LEVELS) {
18		HOST_$(var)_LOCATE_TARGET
19			= [ FDirName $(HOST_$(var)_OBJECT_DIR) $(relPath) ] ;
20		TARGET_$(var)_LOCATE_TARGET
21			= [ FDirName $(TARGET_$(var)_OBJECT_DIR) $(relPath) ] ;
22	}
23
24	LOCATE_TARGET = $(COMMON_PLATFORM_LOCATE_TARGET) ;
25	LOCATE_SOURCE = $(LOCATE_TARGET) ;
26	SEARCH_SOURCE = $(SUBDIR) $(LOCATE_SOURCE)
27		$(HOST_COMMON_DEBUG_LOCATE_TARGET)		# Also add the standard output
28		$(TARGET_COMMON_DEBUG_LOCATE_TARGET)	# dirs for generated sources.
29	;
30}
31
32rule SetupFeatureObjectsDir feature
33{
34	# SetupFeatureObjectsDir <feature>
35	#
36	# Updates the *{LOCATE,SEARCH}*_{TARGET,SOURCE} variables for the current
37	# directory appending a <feature> to each of them. Note that it resets
38	# the LOCATE_TARGET, LOCATE_SOURCE, SEARCH_SOURCE (!) variables. I.e. it
39	# should be invoked before customizing these variables further (e.g. like
40	# adding additional source directories to SEARCH_SOURCE).
41
42	COMMON_PLATFORM_LOCATE_TARGET
43		= [ FDirName $(COMMON_PLATFORM_LOCATE_TARGET) $(feature) ] ;
44
45	local var ;
46	for var in COMMON_ARCH COMMON_DEBUG DEBUG_$(HAIKU_DEBUG_LEVELS) {
47		HOST_$(var)_LOCATE_TARGET
48			= [ FDirName $(HOST_$(var)_LOCATE_TARGET) $(feature) ] ;
49		TARGET_$(var)_LOCATE_TARGET
50			= [ FDirName $(TARGET_$(var)_LOCATE_TARGET) $(feature) ] ;
51	}
52
53	LOCATE_TARGET = [ FDirName $(LOCATE_TARGET) $(feature) ] ;
54	LOCATE_SOURCE = $(LOCATE_TARGET) ;
55	SEARCH_SOURCE = $(SUBDIR) $(LOCATE_SOURCE)
56		$(HOST_COMMON_DEBUG_LOCATE_TARGET)		# Also add the standard output
57		$(TARGET_COMMON_DEBUG_LOCATE_TARGET)	# dirs for generated sources.
58	;
59}
60
61rule SubIncludeGPL
62{
63	# SubInclude rule that can be used to conditionally include GPL licensed
64	# add-ons
65	if $(HAIKU_INCLUDE_GPL_ADDONS) = 1 {
66		SubInclude $(1) ;
67	}
68}
69
70
71# pragma mark - MakeLocate variants
72
73
74rule MakeLocateCommonPlatform
75{
76	# The file is shared between all target platforms.
77	MakeLocate $(1) : $(COMMON_PLATFORM_LOCATE_TARGET) ;
78}
79
80rule MakeLocatePlatform
81{
82	# The file is specific for the target platform, but
83	# architecture independent. Usually the right rule for generated
84	# sources, though sometimes sources can be architecture specific.
85	local files = $(1) ;
86	local file ;
87	for file in $(files) {
88		if [ on $(file) return $(PLATFORM) ] = host {
89			MakeLocate $(file) : $(HOST_COMMON_ARCH_LOCATE_TARGET) ;
90		} else {
91			MakeLocate $(file) : $(TARGET_COMMON_ARCH_LOCATE_TARGET) ;
92		}
93	}
94}
95
96rule MakeLocateArch
97{
98	# The file is platform+architecture specific, but is debug
99	# level independent. This is usually the right rule for generated
100	# architecture specific data or source files.
101	local files = $(1) ;
102	local file ;
103	for file in $(files) {
104		if [ on $(file) return $(PLATFORM) ] = host {
105			MakeLocate $(file) : $(HOST_COMMON_DEBUG_LOCATE_TARGET) ;
106		} else {
107			MakeLocate $(file) : $(TARGET_COMMON_DEBUG_LOCATE_TARGET) ;
108		}
109	}
110}
111
112rule MakeLocateDebug
113{
114	# The file is platform+architecture+debug level specific.
115	# That's what should be used for compiled code.
116	local files = $(1) ;
117	local file ;
118	for file in $(files) {
119		on $(file) {
120			if $(PLATFORM) = host {
121				MakeLocate $(file) : $(HOST_DEBUG_$(DEBUG)_LOCATE_TARGET) ;
122			} else {
123				MakeLocate $(file) : $(TARGET_DEBUG_$(DEBUG)_LOCATE_TARGET) ;
124			}
125		}
126	}
127}
128
129
130# pragma mark - Deferred SubIncludes
131
132
133# The variable used to collect the deferred SubIncludes.
134HAIKU_DEFERRED_SUB_INCLUDES = ;
135
136rule DeferredSubInclude params : jamfile : scope
137{
138	# DeferredSubInclude <subdir tokens> [ : <jamfile name>  [ : <scope> ] ] ;
139	#
140	# Takes the same directory tokens parameter as SubInclude plus an optional
141	# alternative Jamfile name. The the subdirectory referred to by
142	# <subdir tokens> will be included when ExecuteDeferredSubIncludes is
143	# invoked, i.e. at the end of the root Jamfile. The <jamfile name> parameter
144	# specifies the name of the Jamfile to include. By default it is "Jamfile".
145	# The <scope> parameter can be "global" (default) or "local", specifying
146	# whether the alternative Jamfile name shall also be used for subdirectories.
147
148	HAIKU_DEFERRED_SUB_INCLUDES += "/" $(params) ;
149	if $(jamfile) {
150		SetConfigVar JAMFILE : $(params) : $(jamfile) : $(scope) ;
151	}
152}
153
154rule ExecuteDeferredSubIncludes
155{
156	# ExecuteDeferredSubIncludes ;
157	#
158	# Performs the deferred SubIncludes scheduled by DeferredSubInclude.
159
160	local tokensList = $(HAIKU_DEFERRED_SUB_INCLUDES) ;
161	while $(tokensList) {
162		# chop off leading "/"
163		tokensList = $(tokensList[2-]) ;
164
165		# get the tokens for the next include
166		local tokens ;
167		while $(tokensList) && $(tokensList[1]) != "/" {
168			tokens += $(tokensList[1]) ;
169			tokensList = $(tokensList[2-]) ;
170		}
171
172		# perform the include
173		if $(tokens) {
174			SubInclude $(tokens) ;
175		}
176	}
177}
178
179rule HaikuSubInclude tokens
180{
181	# HaikuSubInclude <tokens> ;
182	#
183	# Current subdir relative SubInclude.
184	# <tokens> - subdir tokens specifying the subdirectory to be include
185	#            (relative to the current subdir)
186
187	if $(tokens) {
188		SubInclude HAIKU_TOP $(SUBDIR_TOKENS) $(tokens) ;
189	}
190}
191
192
193# pragma mark - Unique IDs/targets
194
195
196# private to NextID; incremented with each NextID invocation
197HAIKU_NEXT_ID = 0 ;
198
199rule NextID
200{
201	# NextID ;
202
203	local result = $(HAIKU_NEXT_ID:J=) ;
204	HAIKU_NEXT_ID = [ AddNumAbs $(HAIKU_NEXT_ID) : 1 ] ;
205	return $(result) ;
206}
207
208rule NewUniqueTarget basename
209{
210	# NewUniqueTarget [ basename ] ;
211
212	local id = [ NextID ] ;
213	return $(basename[1]:E=_target:G=unique!target)_$(id) ;
214}
215
216
217# pragma mark - RunCommandLine
218
219
220rule RunCommandLine commandLine
221{
222	# RunCommandLine <commandLine>
223	#
224	# Creates a pseudo target that, when made by jam, causes the supplied shell
225	# command line to be executed. Elements of <commandLine> with the prefix ":"
226	# are replaced by the rule. After stripping the prefix such a string specifies
227	# a build system target and the finally executed command line will contain
228	# a path to the target instead.
229	# The pseudo target will depend on all targets thus identified. Each
230	# invocation of this rule creates a different pseudo target, which is
231	# returned to the caller.
232
233	# collect the targets in the command line and replace them by $targetX*
234	# variables
235	local substitutedCommandLine ;
236	local targets ;
237
238	local targetVarName = target ;
239	local i ;
240	for i in $(commandLine)  {
241		# targets are marked by the ":" prefix
242		local target = [ Match ^:(.*) : $(i) ] ;
243		if $(target) {
244			targets += $(target) ;
245			targetVarName = $(targetVarName)X ;
246			i = "$"$(targetVarName) ;
247		}
248
249		substitutedCommandLine += $(i) ;
250	}
251
252	# define the "run" target
253	local run = [ NewUniqueTarget run ] ;
254	COMMAND_LINE on $(run) = $(substitutedCommandLine) ;
255	NotFile $(run) ;
256	Always $(run) ;
257	Depends $(run) : $(targets) ;
258	RunCommandLine1 $(run) : $(targets) ;
259
260	return $(run) ;
261}
262
263actions RunCommandLine1 {
264	target=target;
265	for t in $(2) ; do
266		target=${target}X
267		eval "${target}=${t}"
268	done
269	$(HOST_ADD_BUILD_COMPATIBILITY_LIB_DIR)
270	"$(COMMAND_LINE)"
271}
272
273
274#pragma mark - DefineBuildProfile
275
276
277rule DefineBuildProfile name : type : path {
278	# DefineBuildProfile <name> : <type> [ : <path> ]
279	#
280	# Makes a build profile known. Build profiles can be used to define
281	# different sets of settings for Haiku images/installations. For each
282	# profile the default actions "build", "update", and "mount" (the latter
283	# only for disks or image types) will be available (i.e. can be specified
284	# as second parameter on the jam command line). They will build an image
285	# or installation, update only given targets, respectively just mount the
286	# image or disk using the bfs_shell.
287	#
288	# <name> - The name of the build profile.
289	# <type> - The type of the build profile. Must be one of "image" (plain
290	#          disk image), "anyboot-image" (custom disk image that can be
291	#          written to CD or disk device), "cd-image" (ISO CD image),
292	#          "vmware-image" (VMware disk image), "disk" (actual partition
293	#          or hard disk device), "install" (installation in a directory),
294	#          or "custom" (user-defined).
295	# <path> - The path associated with the profile. Depending on the profile
296	#          type, this is the path to the disk image/VMware image, hard
297	#          disk/partition device, or the installation directory. If the
298	#          parameter is omitted, the value of the HAIKU[_VMWARE]_IMAGE_NAME,
299	#          HAIKU_IMAGE_DIR, respectively HAIKU_INSTALL_DIR or their default
300	#          values will be used instead.
301
302	if [ on $(name) return $(HAIKU_BUILD_PROFILE_SPECIFIED) ] {
303		Exit "ERROR: Build profile \"$(name)\" defined twice!" ;
304	}
305	HAIKU_BUILD_PROFILE_SPECIFIED on $(name) = 1 ;
306
307	if ! $(HAIKU_BUILD_PROFILE) || $(HAIKU_BUILD_PROFILE) != $(name) {
308		return ;
309	}
310
311	HAIKU_BUILD_PROFILE_DEFINED = 1 ;
312
313	# split path into directory path and name
314	local targetDir = $(path:D) ;
315	local targetName = $(path:BS) ;
316
317	# Jam's path splitting produces an empty string, if a component doesn't
318	# exist. That's a little unhandy for checks.
319	if $(targetDir) = "" {
320		targetDir = ;
321	}
322	if $(targetName) = "" {
323		targetName = ;
324	}
325
326	targetDir ?= $(HAIKU_IMAGE_DIR) ;
327	targetDir ?= $(HAIKU_DEFAULT_IMAGE_DIR) ;
328
329	# "disk" is "image" with HAIKU_DONT_CLEAR_IMAGE
330	if $(type) = "disk" {
331		type = "image" ;
332		HAIKU_DONT_CLEAR_IMAGE = 1 ;
333	}
334
335	local buildTarget ;
336	local startOffset ;
337
338	switch $(type) {
339		case "anyboot-image" : {
340			targetName ?= $(HAIKU_ANYBOOT_NAME) ;
341			targetName ?= $(HAIKU_DEFAULT_ANYBOOT_NAME) ;
342			HAIKU_ANYBOOT_DIR = $(targetDir) ;
343			HAIKU_ANYBOOT_NAME = $(targetName) ;
344			buildTarget = haiku-anyboot-image ;
345		}
346
347		case "cd-image" : {
348			targetName ?= $(HAIKU_CD_NAME) ;
349			targetName ?= $(HAIKU_DEFAULT_CD_NAME) ;
350			HAIKU_CD_DIR = $(targetDir) ;
351			HAIKU_CD_NAME = $(targetName) ;
352			buildTarget = haiku-cd ;
353		}
354
355		case "image" : {
356			targetName ?= $(HAIKU_IMAGE_NAME) ;
357			targetName ?= $(HAIKU_DEFAULT_IMAGE_NAME) ;
358			HAIKU_IMAGE_DIR = $(targetDir) ;
359			HAIKU_IMAGE_NAME = $(targetName) ;
360			buildTarget = haiku-image ;
361		}
362
363		case "vmware-image" : {
364			targetName ?= $(HAIKU_VMWARE_IMAGE_NAME) ;
365			targetName ?= $(HAIKU_DEFAULT_VMWARE_IMAGE_NAME) ;
366			HAIKU_IMAGE_DIR = $(targetDir) ;
367			HAIKU_VMWARE_IMAGE_NAME = $(targetName) ;
368			buildTarget = haiku-vmware-image ;
369			startOffset = --start-offset 65536 ;
370		}
371
372		case "install" : {
373			path ?= $(HAIKU_INSTALL_DIR) ;
374			path ?= $(HAIKU_DEFAULT_INSTALL_DIR) ;
375			HAIKU_INSTALL_DIR = $(path) ;
376			buildTarget = install-haiku ;
377		}
378
379		case "custom" : {
380			# user-defined -- don't do anything
381			return 1 ;
382		}
383
384		case * : {
385			Exit "Unsupported build profile type: " $(type) ;
386		}
387	}
388
389	switch $(HAIKU_BUILD_PROFILE_ACTION) {
390		case "build" : {
391			JAM_TARGETS = $(buildTarget) ;
392		}
393
394		case "update" : {
395			JAM_TARGETS = $(buildTarget) ;
396			SetUpdateHaikuImageOnly 1 ;
397			HAIKU_PACKAGES_UPDATE_ONLY = 1 ;
398			HAIKU_INCLUDE_IN_IMAGE on $(HAIKU_BUILD_PROFILE_PARAMETERS) = 1 ;
399			HAIKU_INCLUDE_IN_PACKAGES on $(HAIKU_BUILD_PROFILE_PARAMETERS) = 1 ;
400		}
401
402		case "update-all" : {
403			JAM_TARGETS = $(buildTarget) ;
404			SetUpdateHaikuImageOnly 1 ;
405			HAIKU_PACKAGES_UPDATE_ONLY = 1 ;
406			HAIKU_INCLUDE_IN_IMAGE = 1 ;
407			HAIKU_INCLUDE_IN_PACKAGES = 1 ;
408		}
409
410		case "update-packages" : {
411			JAM_TARGETS = $(buildTarget) ;
412			SetUpdateHaikuImageOnly 1 ;
413			HAIKU_UPDATE_ALL_PACKAGES = 1 ;
414		}
415
416		case "mount" : {
417			if $(type) in "install" "cd-image" {
418				Exit "Build action \"mount\" not supported for profile type"
419					"\"$(type)\"." ;
420			}
421
422			local commandLine = :<build>bfs_shell $(startOffset)
423				\"$(targetName:D=$(targetDir))\" ;
424			JAM_TARGETS = [ RunCommandLine $(commandLine) ] ;
425		}
426	}
427
428	return 1 ;
429}
430
431
432# pragma mark - Build Features
433
434
435rule FIsBuildFeatureEnabled feature
436{
437	# FIsBuildFeatureEnabled <feature> ;
438	# Returns whether the given build feature <feature> is enabled (if so: "1",
439	# if not: empty list).
440	#
441	# <feature> - The name of the build feature (all lower case).
442
443	if $(feature) in $(HAIKU_BUILD_FEATURES) {
444		return 1 ;
445	}
446
447	return ;
448}
449
450
451rule FMatchesBuildFeatures specification
452{
453	# FMatchesBuildFeatures <specification> ;
454	# Returns whether the given build feature specification <specification>
455	# holds. <specification> consists of positive and negative build feature
456	# conditions. Conditions can be individual list elements and multiple
457	# conditions can be joined, separated by ",", in a single list element. The
458	# effect is the same; they are considered as single set of conditions.
459	# A positive condition does not start with a "!". It holds when the build
460	# feature named by the element is enabled.
461	# A negative condition starts with a "!". It holds when the build feature
462	# named by the string resulting from removing the leading "!" is not
463	# enabled.
464	# <specification> holds when it is not empty and
465	# * none of the negative conditions it contains hold and
466	# * if it contains any positive conditions, at least one one of them holds.
467	#
468	# <specification> - The build feature specification. A list of individual
469	#	conditions or conditions joined by ",".
470
471	local splitSpecification ;
472	local element ;
473	for element in $(specification) {
474		splitSpecification += [ FSplitString $(element) : "," ] ;
475	}
476	return [ FSetConditionsHold $(splitSpecification)
477		: $(HAIKU_BUILD_FEATURES) ] ;
478}
479
480
481rule FFilterByBuildFeatures list
482{
483	# FFilterByBuildFeatures <list> ;
484	# Filters the list annotated by build feature specifications and returns the
485	# resulting list. The list can be annotated in two different ways:
486	# * A single list element can be annotated by appending "@<specification>"
487	#   to it, with <specification> being a build feature specification (a
488	#   single comma-separated string). The element appears in the resulting
489	#   list, only if <specification> holds.
490	# * A sublist can be annotated by enclosing it in "<specification> @{" (two
491	#   separate list elements) and "}@", with <specification> being a build
492	#   feature specification (a single comma-separated string). The enclosed
493	#   sublist appears in the resulting list, only if <specification> holds.
494	# The sublist annotations can be nested. The annotations themselves don't
495	# appear in the resulting list.
496	#
497	# <list> - A list annotated with build feature specifications.
498
499	local filteredList ;
500
501	# Since we must look ahead one element to be able to decide whether an
502	# element is a regular list element or a features specification for a
503	# subsequent "@{". Hence we always process an element other than "@{" and
504	# "}@" in the next iteration. We append a dummy element to the list so we
505	# don't need special handling for the last element.
506	local evaluationStack = 1 ;
507	local previousElement ;
508	local element ;
509	for element in $(list) dummy {
510		local stackTop = $(evaluationStack[1]) ;
511		local processElement = $(previousElement) ;
512		switch $(element) {
513			case }@ :
514			{
515				# Pop the topmost specificaton off the stack.
516				evaluationStack = $(evaluationStack[2-]) ;
517				if ! $(evaluationStack) {
518					Exit "FFilterByBuildFeatures: Unbalanced @( in: " $(list) ;
519				}
520
521				processElement = $(previousElement) ;
522				previousElement = ;
523			}
524			case @{ :
525			{
526				if ! $(previousElement) {
527					Exit "FFilterByBuildFeatures: No feature specification"
528						"after )@ in: " $(list) ;
529				}
530
531				if $(evaluationStack[1]) = 1
532					&& [ FMatchesBuildFeatures $(previousElement) ] {
533					evaluationStack = 1 $(evaluationStack) ;
534				} else {
535					evaluationStack = 0 $(evaluationStack) ;
536				}
537
538				processElement = ;
539				previousElement = ;
540			}
541			case * :
542			{
543				processElement = $(previousElement) ;
544				previousElement = $(element) ;
545			}
546		}
547
548		if $(processElement) && $(stackTop) = 1 {
549			local splitElement = [ Match "(.*)@([^@]*)" : $(processElement) ] ;
550			if $(splitElement) {
551				if [ FMatchesBuildFeatures $(splitElement[2]) ] {
552					filteredList += $(splitElement[1]) ;
553				}
554			} else {
555				filteredList += $(processElement) ;
556			}
557		}
558	}
559
560	if $(evaluationStack[2-]) {
561		Exit "FFilterByBuildFeatures: Unbalanced )@ in: " $(list) ;
562	}
563
564	return $(filteredList) ;
565}
566
567
568rule EnableBuildFeatures features : specification
569{
570	# EnableBuildFeatures <features> : <specification> ;
571	# Enables the build features <features>, if the build features specification
572	# <specification> holds. If <specification> is omitted, the features are
573	# enabled unconditionally.
574	# The rule enables a build feature by adding its given lower case name to
575	# the build variable HAIKU_BUILD_FEATURES and defining a build variable
576	# HAIKU_BUILD_FEATURE_<FEATURE>_ENABLED (<FEATURE> being the upper case name
577	# of the build feature) to "1".
578	#
579	# <features> - A list of build feature names (lower case).
580	# <specification> - An optional build features specification (cf.
581	#	FMatchesBuildFeatures).
582
583	if ! $(specification)
584		|| [ FMatchesBuildFeatures $(specification)
585			: $(HAIKU_BUILD_FEATURES) ] {
586		local feature ;
587		for feature in $(features) {
588			HAIKU_BUILD_FEATURES += $(feature) ;
589			HAIKU_BUILD_FEATURE_$(feature:U)_ENABLED = 1 ;
590		}
591	}
592}
593
594
595rule BuildFeatureObject feature
596{
597	# BuildFeatureObject <feature> ;
598	# Returns a unique object for the given build feature. It is used for
599	# attaching attribute values to it.
600
601	local featureObject = $(HAIKU_BUILD_FEATURE_$(feature:U)) ;
602	if ! $(featureObject) {
603		featureObject = [ NewUniqueTarget ] ;
604		HAIKU_BUILD_FEATURE_$(feature:U) = $(featureObject) ;
605	}
606
607	return $(featureObject) ;
608}
609
610
611rule SetBuildFeatureAttribute feature : attribute : values : package
612{
613	# SetBuildFeatureAttribute <feature> : <attribute> : <values>
614	#	[ : <package> ] ;
615	# Sets attribute <attribute> of a build feature <feature> to value <values>.
616	# If <package> is specified, it names the package the attribute belongs to.
617
618	local featureObject = [ BuildFeatureObject $(feature) ] ;
619	HAIKU_ATTRIBUTE_$(attribute) on $(featureObject) = $(values) ;
620	if $(package) {
621		HAIKU_ATTRIBUTE_$(attribute):package on $(featureObject) = $(package) ;
622	}
623}
624
625
626rule BuildFeatureAttribute feature : attribute : flags
627{
628	# BuildFeatureAttribute <feature> : <attribute> [ : <flags> ] ;
629	# Returns the value of attribute <attribute> of a build feature <feature>.
630	# Flags can be a list of flags which influence the returned value. Currently
631	# only flag "path" is defined, which will convert the attribute value --
632	# which is assumed to be a list of (gristed) targets with a path relative to
633	# the extraction directory of the build feature archive files -- to paths.
634	# A typical example is the "headers" attribute, whose value can be used as
635	# dependency, but which must be converted to a path to be a valid headers
636	# search path.
637
638	local featureObject = [ BuildFeatureObject $(feature) ] ;
639	local values
640		= [ on $(featureObject) return $(HAIKU_ATTRIBUTE_$(attribute)) ] ;
641	if path in $(flags) {
642		# get the attribute's package and the corresponding extraction dir
643		local package
644			= [ BuildFeatureAttribute $(feature) : $(attribute):package ] ;
645		local directory ;
646		if $(package) {
647			directory = [ BuildFeatureAttribute $(feature)
648				: $(package):directory ] ;
649		}
650
651		# translate the values
652		local paths ;
653		local value ;
654		for value in $(values:G=) {
655			paths += [ FDirName $(directory) $(value) ] ;
656		}
657		values = $(paths) ;
658	}
659
660	return $(values) ;
661}
662
663
664rule ExtractBuildFeatureArchives feature : list
665{
666	# ExtractBuildFeatureArchives <feature> : <list> ;
667	# Downloads and extracts one or more archives for build feature <feature>
668	# and sets attributes for the build feature to extracted entries. The syntax
669	# for <list> is:
670	# "file:" <packageName> <url>
671	#    <attribute>: <value> ...
672	#    ...
673	# "file:" <packageName2> <url2>
674	# ...
675	#
676	# <packageName> specifies a short name for the archive (e.g. "base" for the
677	# base package, "devel" for the development package, etc.), <url> the URL
678	# from which to download the file. <attribute> and be any name and <value>
679	# any relative path in the extraction directory.
680	#
681	# The attribute with the name "depends" will be handled specially. Its
682	# <value> specifies the name of a package the current package depends on
683	# (e.g. "devel" typically depends on "base"). If such a dependency is
684	# specified the current package will be extracted to the same directory as
685	# the package it depends on. The "depends" attribute must precede any other
686	# attribute for the package.
687	#
688	# The rule also sets the build feature attribute "<packageName>:directory"
689	# to the extraction directory for each package.
690
691	while $(list) {
692		if $(list[1]) != file: {
693			Exit "ExtractBuildFeatureArchives: Expected \"file: ...\", got:"
694				$(list) ;
695		}
696
697		local package = $(list[2]) ;
698		local url = $(list[3]) ;
699		local fileName = $(url:BS) ;
700		list = $(list[4-]) ;
701
702		local file = [ DownloadFile $(fileName) : $(url) ] ;
703
704		local directory = [ FDirName $(HAIKU_OPTIONAL_BUILD_PACKAGES_DIR)
705			$(fileName:B) ] ;
706		directory = $(directory:G=$(package)) ;
707
708		while $(list) {
709			local attribute = [ Match "(.*):" : $(list[1]) ] ;
710			if ! $(attribute) {
711				Exit "ExtractBuildFeatureArchives: Expected attribute, got:"
712					$(list) ;
713			}
714			if $(attribute) = file {
715				break ;
716			}
717
718			list = $(list[2-]) ;
719
720			local values ;
721
722			while $(list) {
723				switch $(list[1]) {
724					case *: :
725					{
726						break ;
727					}
728					case * :
729					{
730						values += $(list[1]) ;
731						list = $(list[2-]) ;
732					}
733				}
734			}
735
736			if $(attribute) = depends {
737				# Inherit the extraction directory (with a different grist) and
738				# depend on the extraction directory of the base package.
739				local basePackage = $(values[1]) ;
740				local baseDirectory = [ BuildFeatureAttribute $(feature)
741					: $(basePackage):directory ] ;
742				directory = $(baseDirectory:G=$(package)) ;
743				Depends $(directory) : $(directory:G=$(basePackage)) ;
744			} else {
745				SetBuildFeatureAttribute $(feature) : $(attribute)
746					: [ ExtractArchive $(directory)
747						: $(values) : $(file)
748						: extracted-$(feature)-$(package) ] ;
749				SetBuildFeatureAttribute $(feature) : $(attribute):package
750					: $(package) ;
751			}
752		}
753
754		SetBuildFeatureAttribute $(feature) : $(package):directory
755			: $(directory:G=) ;
756	}
757}
758