1rule FQualifiedBuildFeatureName features
2{
3	# FQualifiedBuildFeatureName <features>
4	#
5	# Prepends the name of the current target packaging architecture to the
6	# given feature names.
7
8	if $(features[1]) = "QUALIFIED" {
9		# We have been pre-supplied a qualified name.
10		return $(features[2]) ;
11	}
12
13	return $(TARGET_PACKAGING_ARCH):$(features) ;
14}
15
16
17rule FIsBuildFeatureEnabled feature
18{
19	# FIsBuildFeatureEnabled <feature> ;
20	# Returns whether the given build feature <feature> is enabled (if so: "1",
21	# if not: empty list).
22	#
23	# <feature> - The name of the build feature (all lower case).
24
25	feature = [ FQualifiedBuildFeatureName $(feature) ] ;
26	return $(HAIKU_BUILD_FEATURE_$(feature:U)_ENABLED) ;
27}
28
29
30rule FMatchesBuildFeatures specification
31{
32	# FMatchesBuildFeatures <specification> ;
33	# Returns whether the given build feature specification <specification>
34	# holds. <specification> consists of positive and negative build feature
35	# conditions. Conditions can be individual list elements and multiple
36	# conditions can be joined, separated by ",", in a single list element. The
37	# effect is the same; they are considered as single set of conditions.
38	# A positive condition does not start with a "!". It holds when the build
39	# feature named by the element is enabled.
40	# A negative condition starts with a "!". It holds when the build feature
41	# named by the string resulting from removing the leading "!" is not
42	# enabled.
43	# <specification> holds when it is not empty and
44	# * none of the negative conditions it contains hold and
45	# * if it contains any positive conditions, at least one one of them holds.
46	#
47	# <specification> - The build feature specification. A list of individual
48	#	conditions or conditions joined by ",".
49
50	local splitSpecification ;
51	local element ;
52	for element in $(specification) {
53		splitSpecification += [ FSplitString $(element) : "," ] ;
54	}
55	return [ FConditionsHold $(splitSpecification) : FIsBuildFeatureEnabled ] ;
56}
57
58
59rule FFilterByBuildFeatures list
60{
61	# FFilterByBuildFeatures <list> ;
62	# Filters the list annotated by build feature specifications and returns the
63	# resulting list. The list can be annotated in two different ways:
64	# * A single list element can be annotated by appending "@<specification>"
65	#   to it, with <specification> being a build feature specification (a
66	#   single comma-separated string). The element appears in the resulting
67	#   list, only if <specification> holds.
68	# * A sublist can be annotated by enclosing it in "<specification> @{" (two
69	#   separate list elements) and "}@", with <specification> being a build
70	#   feature specification (a single comma-separated string). The enclosed
71	#   sublist appears in the resulting list, only if <specification> holds.
72	# The sublist annotations can be nested. The annotations themselves don't
73	# appear in the resulting list.
74	#
75	# <list> - A list annotated with build feature specifications.
76
77	local filteredList ;
78
79	# Since we must look ahead one element to be able to decide whether an
80	# element is a regular list element or a features specification for a
81	# subsequent "@{". Hence we always process an element other than "@{" and
82	# "}@" in the next iteration. We append a dummy element to the list so we
83	# don't need special handling for the last element.
84	local evaluationStack = 1 ;
85	local previousElement ;
86	local element ;
87	for element in $(list) dummy {
88		local stackTop = $(evaluationStack[1]) ;
89		local processElement = $(previousElement) ;
90		switch $(element) {
91			case }@ :
92			{
93				# Pop the topmost specification off the stack.
94				evaluationStack = $(evaluationStack[2-]) ;
95				if ! $(evaluationStack) {
96					Exit "FFilterByBuildFeatures: Unbalanced @{ in: " $(list) ;
97				}
98
99				processElement = $(previousElement) ;
100				previousElement = ;
101			}
102			case @{ :
103			{
104				if ! $(previousElement) {
105					Exit "FFilterByBuildFeatures: No feature specification"
106						"after }@ in: " $(list) ;
107				}
108
109				if $(evaluationStack[1]) = 1
110					&& [ FMatchesBuildFeatures $(previousElement) ] {
111					evaluationStack = 1 $(evaluationStack) ;
112				} else {
113					evaluationStack = 0 $(evaluationStack) ;
114				}
115
116				processElement = ;
117				previousElement = ;
118			}
119			case * :
120			{
121				processElement = $(previousElement) ;
122				previousElement = $(element) ;
123			}
124		}
125
126		if $(processElement) && $(stackTop) = 1 {
127			local splitElement = [ Match "(.*)@([^@]*)" : $(processElement) ] ;
128			if $(splitElement) {
129				if [ FMatchesBuildFeatures $(splitElement[2]) ] {
130					filteredList += $(splitElement[1]) ;
131				}
132			} else {
133				filteredList += $(processElement) ;
134			}
135		}
136	}
137
138	if $(evaluationStack[2-]) {
139		Exit "FFilterByBuildFeatures: Unbalanced )@ in: " $(list) ;
140	}
141
142	return $(filteredList) ;
143}
144
145
146rule EnableBuildFeatures features : specification
147{
148	# EnableBuildFeatures <features> : <specification> ;
149	# Enables the build features <features>, if the build features specification
150	# <specification> holds. If <specification> is omitted, the features are
151	# enabled unconditionally.
152	# The rule enables a build feature by adding its given lower case name to
153	# the build variable HAIKU_BUILD_FEATURES and defining a build variable
154	# HAIKU_BUILD_FEATURE_<FEATURE>_ENABLED (<FEATURE> being the upper case name
155	# of the build feature) to "1".
156	#
157	# <features> - A list of build feature names (lower case).
158	# <specification> - An optional build features specification (cf.
159	#	FMatchesBuildFeatures).
160
161	features = [ FQualifiedBuildFeatureName $(features) ] ;
162
163	local feature ;
164	for feature in $(features) {
165		if ! $(HAIKU_BUILD_FEATURE_$(feature:U)_ENABLED)
166			&& ( ! $(specification)
167				|| [ FMatchesBuildFeatures $(specification) ] ) {
168			HAIKU_BUILD_FEATURES += $(feature) ;
169			HAIKU_BUILD_FEATURE_$(feature:U)_ENABLED = 1 ;
170		}
171	}
172}
173
174
175rule BuildFeatureObject feature
176{
177	# BuildFeatureObject <feature> ;
178	# Returns a unique object for the given build feature. It is used for
179	# attaching attribute values to it.
180
181	feature = [ FQualifiedBuildFeatureName $(feature) ] ;
182
183	local featureObject = $(HAIKU_BUILD_FEATURE_$(feature:U)) ;
184	if ! $(featureObject) {
185		featureObject = [ NewUniqueTarget ] ;
186		HAIKU_BUILD_FEATURE_$(feature:U) = $(featureObject) ;
187	}
188
189	return $(featureObject) ;
190}
191
192
193rule SetBuildFeatureAttribute feature : attribute : values : package
194{
195	# SetBuildFeatureAttribute <feature> : <attribute> : <values>
196	#	[ : <package> ] ;
197	# Sets attribute <attribute> of a build feature <feature> to value <values>.
198	# If <package> is specified, it names the package the attribute belongs to.
199
200	local featureObject = [ BuildFeatureObject $(feature) ] ;
201	HAIKU_ATTRIBUTE_$(attribute) on $(featureObject) = $(values) ;
202	if $(package) {
203		HAIKU_ATTRIBUTE_$(attribute):package on $(featureObject) = $(package) ;
204	}
205}
206
207
208rule BuildFeatureAttribute feature : attribute : flags
209{
210	# BuildFeatureAttribute <feature> : <attribute> [ : <flags> ] ;
211	# Returns the value of attribute <attribute> of a build feature <feature>.
212	# Flags can be a list of flags which influence the returned value. Currently
213	# only flag "path" is defined, which will convert the attribute value --
214	# which is assumed to be a list of (gristed) targets with a path relative to
215	# the extraction directory of the build feature archive files -- to paths.
216	# A typical example is the "headers" attribute, whose value can be used as
217	# dependency, but which must be converted to a path to be a valid headers
218	# search path.
219
220	local featureObject = [ BuildFeatureObject $(feature) ] ;
221	local values
222		= [ on $(featureObject) return $(HAIKU_ATTRIBUTE_$(attribute)) ] ;
223	if path in $(flags) {
224		# get the attribute's package and the corresponding extraction dir
225		local package
226			= [ BuildFeatureAttribute $(feature) : $(attribute):package ] ;
227		local directory ;
228		if $(package) {
229			directory = [ BuildFeatureAttribute $(feature)
230				: $(package):directory ] ;
231		}
232
233		# translate the values
234		local paths ;
235		local value ;
236		for value in $(values:G=) {
237			paths += [ FDirName $(directory) $(value) ] ;
238		}
239		values = $(paths) ;
240	}
241
242	return $(values) ;
243}
244
245
246rule ExtractBuildFeatureArchivesExpandValue value : fileName
247{
248	if ! $(value) {
249		return $(value) ;
250	}
251
252	# split the value
253	local splitValue ;
254	while $(value) {
255		local components = [ Match "([^%]*)(%[^%]*%)(.*)" : $(value) ] ;
256		if ! $(components) {
257			splitValue += $(value) ;
258			break ;
259		}
260
261		if $(components[1]) {
262			splitValue += $(components[1]) ;
263		}
264		splitValue += $(components[2]) ;
265		value = $(components[3]) ;
266	}
267
268	# reassemble the value, performing the substitutions
269	local %packageName% ;
270	local %portName% ;
271	local %packageFullVersion% ;
272	value = "" ;
273	while $(splitValue) {
274		local component = $(splitValue[1]) ;
275		splitValue = $(splitValue[2-]) ;
276		switch $(component) {
277			case %packageRevisionedName% :
278				splitValue = %packageName% "-" %packageFullVersion%
279					$(splitValue) ;
280
281			case %portRevisionedName% :
282				splitValue = %portName% "-" %packageFullVersion% $(splitValue) ;
283
284			case %*% :
285				if ! x$(%packageName%) {
286					# extract package name and version from file name
287					local splitName
288						= [ Match "([^-]*)-(.*).hpkg" : $(fileName) ] ;
289					if $(splitName) {
290						%packageName% = $(splitName[1]) ;
291						%packageFullVersion%
292							= [ Match "([^-]*-[^-]*)-.*" : $(splitName[2]) ] ;
293						if ! $(packageFullVersion%) {
294							packageFullVersion% = $(splitName[2]) ;
295						}
296					} else {
297						%packageName% = [ Match "(.*).hpkg" : $(fileName) ] ;
298						if ! $(%packageName%) {
299							%packageName% = $(fileName) ;
300						}
301						%packageFullVersion% = "" ;
302					}
303
304					# get the port name from the package name
305					splitName = [ FSplitPackageName $(%packageName%) ] ;
306					%portName% = $(splitName[1]) ;
307				}
308
309				value = "$(value)$($(component):E=)" ;
310
311			case * :
312				value = "$(value)$(component)" ;
313		}
314	}
315
316	return $(value) ;
317}
318
319
320rule ExtractBuildFeatureArchives feature : list
321{
322	# ExtractBuildFeatureArchives <feature> : <list> ;
323	# Downloads and extracts one or more archives for build feature <feature>
324	# and sets attributes for the build feature to extracted entries. The syntax
325	# for <list> is:
326	# "file:" <packageAlias> <packageName>
327	#    <attribute>: <value> ...
328	#    ...
329	# "file:" <packageAlias2> <packageName2>
330	# ...
331	#
332	# <packageAlias> specifies a short name for the archive (e.g. "base" for the
333	# base package, "devel" for the development package, etc.), <packageName>
334	# the unversioned name of the package (e.g. "libsolv_devel").
335	# <attribute> can be any name and <value> any relative path in the
336	# extraction directory. The following placeholders in <value> will be
337	# replaced with the respective value:
338	# * %packageName% is replaced with the name of the package as extracted from
339	#   the package file name (may differ from the specified package name e.g.
340	#   for bootstrap packages).
341	# * %portName% is replaced with the name of the port the package belongs to.
342	#   That is %packageName% with any well-known package type suffix ("_devel",
343	#   "_source", etc.) removed.
344	# * %packageFullVersion% is replaced with the the full version string of the
345	#   package (i.e. including the revision) as extracted frmo the package file
346	#   name.
347	# * %packageRevisionedName% is replaced with what
348	#   %packageName%-%packageFullVersion% would be replaced.
349	# * %portRevisionedName% is replaced with what
350	#   %portName%-%packageFullVersion% would be replaced.
351	#
352	# The attribute with the name "depends" will be handled specially. Its
353	# <value> specifies the name of a package the current package depends on
354	# (e.g. "devel" typically depends on "base"). If such a dependency is
355	# specified the current package will be extracted to the same directory as
356	# the package it depends on. The "depends" attribute must precede any other
357	# attribute for the package.
358	#
359	# The rule also sets the build feature attribute "<packageAlias>:directory"
360	# to the extraction directory for each package.
361
362	local qualifiedFeature = [ FQualifiedBuildFeatureName $(feature) ] ;
363	list = [ FFilterByBuildFeatures $(list) ] ;
364
365	while $(list) {
366		if $(list[1]) != file: {
367			Exit "ExtractBuildFeatureArchives: Expected \"file: ...\", got:"
368				$(list) ;
369		}
370
371		local package = $(list[2]) ;
372		local file = [ FetchPackage $(list[3]) ] ;
373		local fileName = $(file:BS) ;
374		list = $(list[4-]) ;
375
376		local directory = [ FDirName $(HAIKU_OPTIONAL_BUILD_PACKAGES_DIR)
377			$(fileName:B) ] ;
378		directory = $(directory:G=$(package)) ;
379
380		while $(list) {
381			local attribute = [ Match "(.*):" : $(list[1]) ] ;
382			if ! $(attribute) {
383				Exit "ExtractBuildFeatureArchives: Expected attribute, got:"
384					$(list) ;
385			}
386			if $(attribute) = file {
387				break ;
388			}
389
390			list = $(list[2-]) ;
391
392			local values ;
393
394			while $(list) {
395				switch $(list[1]) {
396					case *: :
397					{
398						break ;
399					}
400					case * :
401					{
402						values += [ ExtractBuildFeatureArchivesExpandValue
403							$(list[1]) : $(fileName) ] ;
404						list = $(list[2-]) ;
405					}
406				}
407			}
408
409			if $(attribute) = depends {
410				# Inherit the extraction directory (with a different grist) and
411				# depend on the extraction directory of the base package.
412				local basePackage = $(values[1]) ;
413				local baseDirectory = [ BuildFeatureAttribute $(feature)
414					: $(basePackage):directory ] ;
415				directory = $(baseDirectory:G=$(package)) ;
416				Depends $(directory) : $(directory:G=$(basePackage)) ;
417			} else {
418				SetBuildFeatureAttribute $(feature) : $(attribute)
419					: [ ExtractArchive $(directory)
420						: $(values) : $(file)
421						: extracted-$(qualifiedFeature)-$(package) ] ;
422				SetBuildFeatureAttribute $(feature) : $(attribute):package
423					: $(package) ;
424			}
425		}
426
427		SetBuildFeatureAttribute $(feature) : $(package):directory
428			: $(directory:G=) ;
429	}
430}
431
432
433rule InitArchitectureBuildFeatures architecture
434{
435	# InitArchitectureBuildFeatures <architecture> ;
436	#
437	# Enable the build features that can be derived directly from the
438	# architecture.
439
440	# The build feature rule use TARGET_PACKAGING_ARCH, so set that temporarily.
441	local savedArchitecture = $(TARGET_PACKAGING_ARCH) ;
442	TARGET_PACKAGING_ARCH = $(architecture) ;
443
444	# Add the target architecture as a build feature.
445	EnableBuildFeatures $(TARGET_ARCH_$(architecture)) ;
446
447	# For the primary architecture add the "primary" build feature.
448	if $(architecture) = $(TARGET_PACKAGING_ARCHS[1]) {
449		EnableBuildFeatures primary ;
450	}
451
452	# Add all secondary architectures as build features (with prefix).
453	EnableBuildFeatures secondary_$(TARGET_PACKAGING_ARCHS[2-]) ;
454
455	if $(TARGET_CC_IS_LEGACY_GCC_$(architecture)) = 1 {
456		EnableBuildFeatures gcc2 ;
457	}
458
459	TARGET_PACKAGING_ARCH = $(savedArchitecture) ;
460}
461