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_GCC_VERSION_$(architecture)[1]) = 2 { 451 EnableBuildFeatures gcc2 ; 452 } 453 454 TARGET_PACKAGING_ARCH = $(savedArchitecture) ; 455} 456