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 if ! $(specification) 159 || [ FMatchesBuildFeatures $(specification) ] { 160 local feature ; 161 for feature in $(features) { 162 HAIKU_BUILD_FEATURES += $(feature) ; 163 HAIKU_BUILD_FEATURE_$(feature:U)_ENABLED = 1 ; 164 } 165 } 166} 167 168 169rule BuildFeatureObject feature 170{ 171 # BuildFeatureObject <feature> ; 172 # Returns a unique object for the given build feature. It is used for 173 # attaching attribute values to it. 174 175 feature = [ FQualifiedBuildFeatureName $(feature) ] ; 176 177 local featureObject = $(HAIKU_BUILD_FEATURE_$(feature:U)) ; 178 if ! $(featureObject) { 179 featureObject = [ NewUniqueTarget ] ; 180 HAIKU_BUILD_FEATURE_$(feature:U) = $(featureObject) ; 181 } 182 183 return $(featureObject) ; 184} 185 186 187rule SetBuildFeatureAttribute feature : attribute : values : package 188{ 189 # SetBuildFeatureAttribute <feature> : <attribute> : <values> 190 # [ : <package> ] ; 191 # Sets attribute <attribute> of a build feature <feature> to value <values>. 192 # If <package> is specified, it names the package the attribute belongs to. 193 194 local featureObject = [ BuildFeatureObject $(feature) ] ; 195 HAIKU_ATTRIBUTE_$(attribute) on $(featureObject) = $(values) ; 196 if $(package) { 197 HAIKU_ATTRIBUTE_$(attribute):package on $(featureObject) = $(package) ; 198 } 199} 200 201 202rule BuildFeatureAttribute feature : attribute : flags 203{ 204 # BuildFeatureAttribute <feature> : <attribute> [ : <flags> ] ; 205 # Returns the value of attribute <attribute> of a build feature <feature>. 206 # Flags can be a list of flags which influence the returned value. Currently 207 # only flag "path" is defined, which will convert the attribute value -- 208 # which is assumed to be a list of (gristed) targets with a path relative to 209 # the extraction directory of the build feature archive files -- to paths. 210 # A typical example is the "headers" attribute, whose value can be used as 211 # dependency, but which must be converted to a path to be a valid headers 212 # search path. 213 214 local featureObject = [ BuildFeatureObject $(feature) ] ; 215 local values 216 = [ on $(featureObject) return $(HAIKU_ATTRIBUTE_$(attribute)) ] ; 217 if path in $(flags) { 218 # get the attribute's package and the corresponding extraction dir 219 local package 220 = [ BuildFeatureAttribute $(feature) : $(attribute):package ] ; 221 local directory ; 222 if $(package) { 223 directory = [ BuildFeatureAttribute $(feature) 224 : $(package):directory ] ; 225 } 226 227 # translate the values 228 local paths ; 229 local value ; 230 for value in $(values:G=) { 231 paths += [ FDirName $(directory) $(value) ] ; 232 } 233 values = $(paths) ; 234 } 235 236 return $(values) ; 237} 238 239 240rule ExtractBuildFeatureArchivesExpandValue value : fileName 241{ 242 if ! $(value) { 243 return $(value) ; 244 } 245 246 # split the value 247 local splitValue ; 248 while $(value) { 249 local components = [ Match "([^%]*)(%[^%]*%)(.*)" : $(value) ] ; 250 if ! $(components) { 251 splitValue += $(value) ; 252 break ; 253 } 254 255 if $(components[1]) { 256 splitValue += $(components[1]) ; 257 } 258 splitValue += $(components[2]) ; 259 value = $(components[3]) ; 260 } 261 262 # reassemble the value, performing the substitutions 263 local %packageName% ; 264 local %portName% ; 265 local %packageFullVersion% ; 266 value = "" ; 267 while $(splitValue) { 268 local component = $(splitValue[1]) ; 269 splitValue = $(splitValue[2-]) ; 270 switch $(component) { 271 case %packageRevisionedName% : 272 splitValue = %packageName% "-" %packageFullVersion% 273 $(splitValue) ; 274 275 case %portRevisionedName% : 276 splitValue = %portName% "-" %packageFullVersion% $(splitValue) ; 277 278 case %*% : 279 if ! x$(%packageName%) { 280 # extract package name and version from file name 281 local splitName 282 = [ Match "([^-]*)-(.*).hpkg" : $(fileName) ] ; 283 if $(splitName) { 284 %packageName% = $(splitName[1]) ; 285 %packageFullVersion% 286 = [ Match "([^-]*-[^-]*)-.*" : $(splitName[2]) ] ; 287 if ! $(packageFullVersion%) { 288 packageFullVersion% = $(splitName[2]) ; 289 } 290 } else { 291 %packageName% = [ Match "(.*).hpkg" : $(fileName) ] ; 292 if ! $(%packageName%) { 293 %packageName% = $(fileName) ; 294 } 295 %packageFullVersion% = "" ; 296 } 297 298 # get the port name from the package name 299 splitName = [ FSplitPackageName $(%packageName%) ] ; 300 %portName% = $(splitName[1]) ; 301 } 302 303 value = "$(value)$($(component):E=)" ; 304 305 case * : 306 value = "$(value)$(component)" ; 307 } 308 } 309 310 return $(value) ; 311} 312 313 314rule ExtractBuildFeatureArchives feature : list 315{ 316 # ExtractBuildFeatureArchives <feature> : <list> ; 317 # Downloads and extracts one or more archives for build feature <feature> 318 # and sets attributes for the build feature to extracted entries. The syntax 319 # for <list> is: 320 # "file:" <packageAlias> <packageName> 321 # <attribute>: <value> ... 322 # ... 323 # "file:" <packageAlias2> <packageName2> 324 # ... 325 # 326 # <packageAlias> specifies a short name for the archive (e.g. "base" for the 327 # base package, "devel" for the development package, etc.), <packageName> 328 # the unversioned name of the package (e.g. "libsolv_devel"). 329 # <attribute> can be any name and <value> any relative path in the 330 # extraction directory. The following placeholders in <value> will be 331 # replaced with the respective value: 332 # * %packageName% is replaced with the name of the package as extracted from 333 # the package file name (may differ from the specified package name e.g. 334 # for bootstrap packages). 335 # * %portName% is replaced with the name of the port the package belongs to. 336 # That is %packageName% with any well-known package type suffix ("_devel", 337 # "_source", etc.) removed. 338 # * %packageFullVersion% is replaced with the the full version string of the 339 # package (i.e. including the revision) as extracted frmo the package file 340 # name. 341 # * %packageRevisionedName% is replaced with what 342 # %packageName%-%packageFullVersion% would be replaced. 343 # * %portRevisionedName% is replaced with what 344 # %portName%-%packageFullVersion% would be replaced. 345 # 346 # The attribute with the name "depends" will be handled specially. Its 347 # <value> specifies the name of a package the current package depends on 348 # (e.g. "devel" typically depends on "base"). If such a dependency is 349 # specified the current package will be extracted to the same directory as 350 # the package it depends on. The "depends" attribute must precede any other 351 # attribute for the package. 352 # 353 # The rule also sets the build feature attribute "<packageAlias>:directory" 354 # to the extraction directory for each package. 355 356 local qualifiedFeature = [ FQualifiedBuildFeatureName $(feature) ] ; 357 list = [ FFilterByBuildFeatures $(list) ] ; 358 359 while $(list) { 360 if $(list[1]) != file: { 361 Exit "ExtractBuildFeatureArchives: Expected \"file: ...\", got:" 362 $(list) ; 363 } 364 365 local package = $(list[2]) ; 366 local file = [ FetchPackage $(list[3]) ] ; 367 local fileName = $(file:BS) ; 368 list = $(list[4-]) ; 369 370 local directory = [ FDirName $(HAIKU_OPTIONAL_BUILD_PACKAGES_DIR) 371 $(fileName:B) ] ; 372 directory = $(directory:G=$(package)) ; 373 374 while $(list) { 375 local attribute = [ Match "(.*):" : $(list[1]) ] ; 376 if ! $(attribute) { 377 Exit "ExtractBuildFeatureArchives: Expected attribute, got:" 378 $(list) ; 379 } 380 if $(attribute) = file { 381 break ; 382 } 383 384 list = $(list[2-]) ; 385 386 local values ; 387 388 while $(list) { 389 switch $(list[1]) { 390 case *: : 391 { 392 break ; 393 } 394 case * : 395 { 396 values += [ ExtractBuildFeatureArchivesExpandValue 397 $(list[1]) : $(fileName) ] ; 398 list = $(list[2-]) ; 399 } 400 } 401 } 402 403 if $(attribute) = depends { 404 # Inherit the extraction directory (with a different grist) and 405 # depend on the extraction directory of the base package. 406 local basePackage = $(values[1]) ; 407 local baseDirectory = [ BuildFeatureAttribute $(feature) 408 : $(basePackage):directory ] ; 409 directory = $(baseDirectory:G=$(package)) ; 410 Depends $(directory) : $(directory:G=$(basePackage)) ; 411 } else { 412 SetBuildFeatureAttribute $(feature) : $(attribute) 413 : [ ExtractArchive $(directory) 414 : $(values) : $(file) 415 : extracted-$(qualifiedFeature)-$(package) ] ; 416 SetBuildFeatureAttribute $(feature) : $(attribute):package 417 : $(package) ; 418 } 419 } 420 421 SetBuildFeatureAttribute $(feature) : $(package):directory 422 : $(directory:G=) ; 423 } 424} 425 426 427rule InitArchitectureBuildFeatures architecture 428{ 429 # InitArchitectureBuildFeatures <architecture> ; 430 # 431 # Enable the build features that can be derived directly from the 432 # architecture. 433 434 # The build feature rule use TARGET_PACKAGING_ARCH, so set that temporarily. 435 local savedArchitecture = $(TARGET_PACKAGING_ARCH) ; 436 TARGET_PACKAGING_ARCH = $(architecture) ; 437 438 # Add the target architecture as a build feature. 439 EnableBuildFeatures $(TARGET_ARCH_$(architecture)) ; 440 441 # For the primary architecture add the "primary" build feature. 442 if $(architecture) = $(TARGET_PACKAGING_ARCHS[1]) { 443 EnableBuildFeatures primary ; 444 } 445 446 # Add all secondary architectures as build features (with prefix). 447 EnableBuildFeatures secondary_$(TARGET_PACKAGING_ARCHS[2-]) ; 448 449 if $(TARGET_GCC_VERSION_$(architecture)[1]) = 2 { 450 EnableBuildFeatures gcc2 ; 451 } 452 453 TARGET_PACKAGING_ARCH = $(savedArchitecture) ; 454} 455