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