rule FQualifiedBuildFeatureName features{# FQualifiedBuildFeatureName <features>## Prepends the name of the current target packaging architecture to the# given feature names.if $(features[1]) = "QUALIFIED" {# We have been pre-supplied a qualified name.return $(features[2]) ;}return $(TARGET_PACKAGING_ARCH):$(features) ;}rule FIsBuildFeatureEnabled feature{# FIsBuildFeatureEnabled <feature> ;# Returns whether the given build feature <feature> is enabled (if so: "1",# if not: empty list).## <feature> - The name of the build feature (all lower case).feature = [ FQualifiedBuildFeatureName $(feature) ] ;return $(HAIKU_BUILD_FEATURE_$(feature:U)_ENABLED) ;}rule FMatchesBuildFeatures specification{# FMatchesBuildFeatures <specification> ;# Returns whether the given build feature specification <specification># holds. <specification> consists of positive and negative build feature# conditions. Conditions can be individual list elements and multiple# conditions can be joined, separated by ",", in a single list element. The# effect is the same; they are considered as single set of conditions.# A positive condition does not start with a "!". It holds when the build# feature named by the element is enabled.# A negative condition starts with a "!". It holds when the build feature# named by the string resulting from removing the leading "!" is not# enabled.# <specification> holds when it is not empty and# * none of the negative conditions it contains hold and# * if it contains any positive conditions, at least one one of them holds.## <specification> - The build feature specification. A list of individual# conditions or conditions joined by ",".local splitSpecification ;local element ;for element in $(specification) {splitSpecification += [ FSplitString $(element) : "," ] ;}return [ FConditionsHold $(splitSpecification) : FIsBuildFeatureEnabled ] ;}rule FFilterByBuildFeatures list{# FFilterByBuildFeatures <list> ;# Filters the list annotated by build feature specifications and returns the# resulting list. The list can be annotated in two different ways:# * A single list element can be annotated by appending "@<specification>"# to it, with <specification> being a build feature specification (a# single comma-separated string). The element appears in the resulting# list, only if <specification> holds.# * A sublist can be annotated by enclosing it in "<specification> @{" (two# separate list elements) and "}@", with <specification> being a build# feature specification (a single comma-separated string). The enclosed# sublist appears in the resulting list, only if <specification> holds.# The sublist annotations can be nested. The annotations themselves don't# appear in the resulting list.## <list> - A list annotated with build feature specifications.local filteredList ;# Since we must look ahead one element to be able to decide whether an# element is a regular list element or a features specification for a# subsequent "@{". Hence we always process an element other than "@{" and# "}@" in the next iteration. We append a dummy element to the list so we# don't need special handling for the last element.local evaluationStack = 1 ;local previousElement ;local element ;for element in $(list) dummy {local stackTop = $(evaluationStack[1]) ;local processElement = $(previousElement) ;switch $(element) {case }@ :{# Pop the topmost specification off the stack.evaluationStack = $(evaluationStack[2-]) ;if ! $(evaluationStack) {Exit "FFilterByBuildFeatures: Unbalanced @{ in: " $(list) ;}processElement = $(previousElement) ;previousElement = ;}case @{ :{if ! $(previousElement) {Exit "FFilterByBuildFeatures: No feature specification""after }@ in: " $(list) ;}if $(evaluationStack[1]) = 1&& [ FMatchesBuildFeatures $(previousElement) ] {evaluationStack = 1 $(evaluationStack) ;} else {evaluationStack = 0 $(evaluationStack) ;}processElement = ;previousElement = ;}case * :{processElement = $(previousElement) ;previousElement = $(element) ;}}if $(processElement) && $(stackTop) = 1 {local splitElement = [ Match "(.*)@([^@]*)" : $(processElement) ] ;if $(splitElement) {if [ FMatchesBuildFeatures $(splitElement[2]) ] {filteredList += $(splitElement[1]) ;}} else {filteredList += $(processElement) ;}}}if $(evaluationStack[2-]) {Exit "FFilterByBuildFeatures: Unbalanced )@ in: " $(list) ;}return $(filteredList) ;}rule EnableBuildFeatures features : specification{# EnableBuildFeatures <features> : <specification> ;# Enables the build features <features>, if the build features specification# <specification> holds. If <specification> is omitted, the features are# enabled unconditionally.# The rule enables a build feature by adding its given lower case name to# the build variable HAIKU_BUILD_FEATURES and defining a build variable# HAIKU_BUILD_FEATURE_<FEATURE>_ENABLED (<FEATURE> being the upper case name# of the build feature) to "1".## <features> - A list of build feature names (lower case).# <specification> - An optional build features specification (cf.# FMatchesBuildFeatures).features = [ FQualifiedBuildFeatureName $(features) ] ;local feature ;for feature in $(features) {if ! $(HAIKU_BUILD_FEATURE_$(feature:U)_ENABLED)&& ( ! $(specification)|| [ FMatchesBuildFeatures $(specification) ] ) {HAIKU_BUILD_FEATURES += $(feature) ;HAIKU_BUILD_FEATURE_$(feature:U)_ENABLED = 1 ;}}}rule BuildFeatureObject feature{# BuildFeatureObject <feature> ;# Returns a unique object for the given build feature. It is used for# attaching attribute values to it.feature = [ FQualifiedBuildFeatureName $(feature) ] ;local featureObject = $(HAIKU_BUILD_FEATURE_$(feature:U)) ;if ! $(featureObject) {featureObject = [ NewUniqueTarget ] ;HAIKU_BUILD_FEATURE_$(feature:U) = $(featureObject) ;}return $(featureObject) ;}rule SetBuildFeatureAttribute feature : attribute : values : package{# SetBuildFeatureAttribute <feature> : <attribute> : <values># [ : <package> ] ;# Sets attribute <attribute> of a build feature <feature> to value <values>.# If <package> is specified, it names the package the attribute belongs to.local featureObject = [ BuildFeatureObject $(feature) ] ;HAIKU_ATTRIBUTE_$(attribute) on $(featureObject) = $(values) ;if $(package) {HAIKU_ATTRIBUTE_$(attribute):package on $(featureObject) = $(package) ;}}rule BuildFeatureAttribute feature : attribute : flags{# BuildFeatureAttribute <feature> : <attribute> [ : <flags> ] ;# Returns the value of attribute <attribute> of a build feature <feature>.# Flags can be a list of flags which influence the returned value. Currently# only flag "path" is defined, which will convert the attribute value --# which is assumed to be a list of (gristed) targets with a path relative to# the extraction directory of the build feature archive files -- to paths.# A typical example is the "headers" attribute, whose value can be used as# dependency, but which must be converted to a path to be a valid headers# search path.local featureObject = [ BuildFeatureObject $(feature) ] ;local values= [ on $(featureObject) return $(HAIKU_ATTRIBUTE_$(attribute)) ] ;if path in $(flags) {# get the attribute's package and the corresponding extraction dirlocal package= [ BuildFeatureAttribute $(feature) : $(attribute):package ] ;local directory ;if $(package) {directory = [ BuildFeatureAttribute $(feature): $(package):directory ] ;}# translate the valueslocal paths ;local value ;for value in $(values:G=) {paths += [ FDirName $(directory) $(value) ] ;}values = $(paths) ;}return $(values) ;}rule ExtractBuildFeatureArchivesExpandValue value : fileName{if ! $(value) {return $(value) ;}# split the valuelocal splitValue ;while $(value) {local components = [ Match "([^%]*)(%[^%]*%)(.*)" : $(value) ] ;if ! $(components) {splitValue += $(value) ;break ;}if $(components[1]) {splitValue += $(components[1]) ;}splitValue += $(components[2]) ;value = $(components[3]) ;}# reassemble the value, performing the substitutionslocal %packageName% ;local %portName% ;local %packageFullVersion% ;value = "" ;while $(splitValue) {local component = $(splitValue[1]) ;splitValue = $(splitValue[2-]) ;switch $(component) {case %packageRevisionedName% :splitValue = %packageName% "-" %packageFullVersion%$(splitValue) ;case %portRevisionedName% :splitValue = %portName% "-" %packageFullVersion% $(splitValue) ;case %*% :if ! x$(%packageName%) {# extract package name and version from file namelocal splitName= [ Match "([^-]*)-(.*).hpkg" : $(fileName) ] ;if $(splitName) {%packageName% = $(splitName[1]) ;%packageFullVersion%= [ Match "([^-]*-[^-]*)-.*" : $(splitName[2]) ] ;if ! $(packageFullVersion%) {packageFullVersion% = $(splitName[2]) ;}} else {%packageName% = [ Match "(.*).hpkg" : $(fileName) ] ;if ! $(%packageName%) {%packageName% = $(fileName) ;}%packageFullVersion% = "" ;}# get the port name from the package namesplitName = [ FSplitPackageName $(%packageName%) ] ;%portName% = $(splitName[1]) ;}value = "$(value)$($(component):E=)" ;case * :value = "$(value)$(component)" ;}}return $(value) ;}rule ExtractBuildFeatureArchives feature : list{# ExtractBuildFeatureArchives <feature> : <list> ;# Downloads and extracts one or more archives for build feature <feature># and sets attributes for the build feature to extracted entries. The syntax# for <list> is:# "file:" <packageAlias> <packageName># <attribute>: <value> ...# ...# "file:" <packageAlias2> <packageName2># ...## <packageAlias> specifies a short name for the archive (e.g. "base" for the# base package, "devel" for the development package, etc.), <packageName># the unversioned name of the package (e.g. "libsolv_devel").# <attribute> can be any name and <value> any relative path in the# extraction directory. The following placeholders in <value> will be# replaced with the respective value:# * %packageName% is replaced with the name of the package as extracted from# the package file name (may differ from the specified package name e.g.# for bootstrap packages).# * %portName% is replaced with the name of the port the package belongs to.# That is %packageName% with any well-known package type suffix ("_devel",# "_source", etc.) removed.# * %packageFullVersion% is replaced with the the full version string of the# package (i.e. including the revision) as extracted frmo the package file# name.# * %packageRevisionedName% is replaced with what# %packageName%-%packageFullVersion% would be replaced.# * %portRevisionedName% is replaced with what# %portName%-%packageFullVersion% would be replaced.## The attribute with the name "depends" will be handled specially. Its# <value> specifies the name of a package the current package depends on# (e.g. "devel" typically depends on "base"). If such a dependency is# specified the current package will be extracted to the same directory as# the package it depends on. The "depends" attribute must precede any other# attribute for the package.## The rule also sets the build feature attribute "<packageAlias>:directory"# to the extraction directory for each package.local qualifiedFeature = [ FQualifiedBuildFeatureName $(feature) ] ;list = [ FFilterByBuildFeatures $(list) ] ;while $(list) {if $(list[1]) != file: {Exit "ExtractBuildFeatureArchives: Expected \"file: ...\", got:"$(list) ;}local package = $(list[2]) ;local file = [ FetchPackage $(list[3]) ] ;local fileName = $(file:BS) ;list = $(list[4-]) ;local directory = [ FDirName $(HAIKU_OPTIONAL_BUILD_PACKAGES_DIR)$(fileName:B) ] ;directory = $(directory:G=$(package)) ;while $(list) {local attribute = [ Match "(.*):" : $(list[1]) ] ;if ! $(attribute) {Exit "ExtractBuildFeatureArchives: Expected attribute, got:"$(list) ;}if $(attribute) = file {break ;}list = $(list[2-]) ;local values ;while $(list) {switch $(list[1]) {case *: :{break ;}case * :{values += [ ExtractBuildFeatureArchivesExpandValue$(list[1]) : $(fileName) ] ;list = $(list[2-]) ;}}}if $(attribute) = depends {# Inherit the extraction directory (with a different grist) and# depend on the extraction directory of the base package.local basePackage = $(values[1]) ;local baseDirectory = [ BuildFeatureAttribute $(feature): $(basePackage):directory ] ;directory = $(baseDirectory:G=$(package)) ;Depends $(directory) : $(directory:G=$(basePackage)) ;} else {SetBuildFeatureAttribute $(feature) : $(attribute): [ ExtractArchive $(directory): $(values) : $(file): extracted-$(qualifiedFeature)-$(package) ] ;SetBuildFeatureAttribute $(feature) : $(attribute):package: $(package) ;}}SetBuildFeatureAttribute $(feature) : $(package):directory: $(directory:G=) ;}}rule InitArchitectureBuildFeatures architecture{# InitArchitectureBuildFeatures <architecture> ;## Enable the build features that can be derived directly from the# architecture.# The build feature rule use TARGET_PACKAGING_ARCH, so set that temporarily.local savedArchitecture = $(TARGET_PACKAGING_ARCH) ;TARGET_PACKAGING_ARCH = $(architecture) ;# Add the target architecture as a build feature.EnableBuildFeatures $(TARGET_ARCH_$(architecture)) ;# For the primary architecture add the "primary" build feature.if $(architecture) = $(TARGET_PACKAGING_ARCHS[1]) {EnableBuildFeatures primary ;}# Add all secondary architectures as build features (with prefix).EnableBuildFeatures secondary_$(TARGET_PACKAGING_ARCHS[2-]) ;if $(TARGET_CC_IS_LEGACY_GCC_$(architecture)) = 1 {EnableBuildFeatures gcc2 ;}TARGET_PACKAGING_ARCH = $(savedArchitecture) ;}