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