1# -*- coding: utf-8 -*- 2# 3# Copyright 2013 Oliver Tappe 4# Copyright 2013 Haiku, Inc. 5# Distributed under the terms of the MIT License. 6 7# -- Modules ------------------------------------------------------------------ 8 9import os 10import re 11 12from .ConfigParser import ConfigParser 13from .Options import getOption 14from .RecipeTypes import Extendable, MachineArchitecture, YesNo 15from .Utils import sysExit 16 17 18def which(program): 19 def isExecutable(path): 20 return os.path.isfile(path) and os.access(path, os.X_OK) 21 22 path, _ = os.path.split(program) 23 if path: 24 if isExecutable(program): 25 return program 26 else: 27 for path in os.environ["PATH"].split(os.pathsep): 28 path = path.strip('"') 29 candidate = os.path.join(path, program) 30 if isExecutable(candidate): 31 return candidate 32 33 return None 34 35# ----------------------------------------------------------------------------- 36 37# allowed types of the configuration file values 38haikuportsAttributes = { 39 'ALLOW_UNTESTED': { 40 'type': YesNo, 41 'required': False, 42 'default': False, 43 'extendable': Extendable.NO, 44 'indexable': False, 45 'setAttribute': 'allowUntested', 46 }, 47 'ALLOW_UNSAFE_SOURCES': { 48 'type': YesNo, 49 'required': False, 50 'default': False, 51 'extendable': Extendable.NO, 52 'indexable': False, 53 'setAttribute': 'allowUnsafeSources' 54 }, 55 'CREATE_SOURCE_PACKAGES': { 56 'type': YesNo, 57 'required': False, 58 'default': False, 59 'extendable': Extendable.NO, 60 'indexable': False, 61 'setAttribute': 'createSourcePackages', 62 }, 63 'CROSS_DEVEL_PACKAGE': { 64 'type': bytes, 65 'required': False, 66 'default': None, 67 'extendable': Extendable.NO, 68 'indexable': False, 69 'optionAttribute': 'crossDevelPackage', 70 'setAttribute': 'crossDevelPackage', 71 }, 72 'CROSS_TOOLS': { 73 'type': bytes, 74 'required': False, 75 'default': None, 76 'extendable': Extendable.NO, 77 'indexable': False, 78 'optionAttribute': 'crossTools', 79 'setAttribute': 'crossTools', 80 }, 81 'DOWNLOAD_IN_PORT_DIRECTORY': { 82 'type': YesNo, 83 'required': False, 84 'default': False, 85 'extendable': Extendable.NO, 86 'indexable': False, 87 'setAttribute': 'downloadInPortDirectory', 88 }, 89 'DOWNLOAD_MIRROR': { 90 'type': bytes, 91 'required': False, 92 'default': 'https://ports-mirror.haiku-os.org', 93 'extendable': Extendable.NO, 94 'indexable': False, 95 'setAttribute': 'downloadMirror', 96 }, 97 'LICENSES_DIRECTORY': { 98 'type': bytes, 99 'required': False, 100 'default': None, 101 'extendable': Extendable.NO, 102 'indexable': False, 103 'optionAttribute': 'licensesDirectory', 104 'setAttribute': 'licensesDirectory', 105 }, 106 'MIMESET_COMMAND': { 107 'type': bytes, 108 'required': False, 109 'default': None, 110 'extendable': Extendable.NO, 111 'indexable': False, 112 'optionAttribute': 'commandMimeset', 113 'setAttribute': 'mimesetCommand', 114 }, 115 'REPORTING_URI': { 116 'type': bytes, 117 'required': False, 118 'default': None, 119 'extendable': Extendable.NO, 120 'indexable': False, 121 'optionAttribute': 'reportingURI', 122 'setAttribute': 'reportingURI', 123 }, 124 'OUTPUT_DIRECTORY': { 125 'type': bytes, 126 'required': False, 127 'default': None, 128 'extendable': Extendable.NO, 129 'indexable': False, 130 'setAttribute': 'outputDirectory', 131 }, 132 'PACKAGES_PATH': { 133 'type': bytes, 134 'required': False, 135 'default': None, 136 'extendable': Extendable.NO, 137 'indexable': False, 138 'setAttribute': 'packagesPath', 139 }, 140 'PACKAGE_COMMAND': { 141 'type': bytes, 142 'required': False, 143 'default': None, 144 'extendable': Extendable.NO, 145 'indexable': False, 146 'optionAttribute': 'commandPackage', 147 'setAttribute': 'packageCommand', 148 }, 149 'PACKAGE_REPO_COMMAND': { 150 'type': bytes, 151 'required': False, 152 'default': None, 153 'extendable': Extendable.NO, 154 'indexable': False, 155 'optionAttribute': 'commandPackageRepo', 156 'setAttribute': 'packageRepoCommand', 157 }, 158 'PACKAGER': { 159 'type': bytes, 160 'required': True, 161 'default': None, 162 'extendable': Extendable.NO, 163 'indexable': False, 164 'setAttribute': 'packager', 165 }, 166 'REPOSITORY_PATH': { 167 'type': bytes, 168 'required': False, 169 'default': None, 170 'extendable': Extendable.NO, 171 'indexable': False, 172 'setAttribute': 'repositoryPath', 173 }, 174 'SECONDARY_CROSS_DEVEL_PACKAGES': { 175 'type': list, 176 'required': False, 177 'default': [], 178 'extendable': Extendable.NO, 179 'indexable': False, 180 'optionAttribute': 'secondaryCrossDevelPackages', 181 'setAttribute': 'secondaryCrossDevelPackages', 182 }, 183 'SECONDARY_CROSS_TOOLS': { 184 'type': list, 185 'required': False, 186 'default': [], 187 'extendable': Extendable.NO, 188 'indexable': False, 189 }, 190 'SECONDARY_TARGET_ARCHITECTURES': { 191 'type': list, 192 'required': False, 193 'default': [], 194 'extendable': Extendable.NO, 195 'indexable': False, 196 'setAttribute': 'secondaryArchitectures', 197 }, 198 'SOURCEFORGE_MIRROR': { 199 'type': bytes, 200 'required': False, 201 'default': None, 202 'extendable': Extendable.NO, 203 'indexable': False, 204 'optionAttribute': 'sourceforgeMirror', 205 'setAttribute': 'sourceforgeMirror', 206 }, 207 'TARGET_ARCHITECTURE': { 208 'type': MachineArchitecture, 209 'required': False, 210 'default': None, 211 'extendable': Extendable.NO, 212 'indexable': False, 213 'setAttribute': 'targetArchitecture', 214 }, 215 'TREE_PATH': { 216 'type': bytes, 217 'required': True, 218 'default': None, 219 'extendable': Extendable.NO, 220 'indexable': False, 221 'setAttribute': 'treePath', 222 }, 223 'SYSTEM_MIME_DB': { 224 'type': bytes, 225 'required': False, 226 'default': None, 227 'extendable': Extendable.NO, 228 'indexable': False, 229 'optionAttribute': 'systemMimeDB', 230 'setAttribute': 'systemMimeDB', 231 }, 232 'VENDOR': { 233 'type': bytes, 234 'required': False, 235 'default': 'Haiku Project', 236 'extendable': Extendable.NO, 237 'indexable': False, 238 'setAttribute': 'vendor', 239 }, 240} 241 242 243# -- Configuration class ------------------------------------------------------ 244 245class Configuration(object): 246 configuration = None 247 248 def __init__(self): 249 self.treePath = None 250 self.isCrossBuildRepository = False 251 self.targetArchitecture = None 252 self.secondaryArchitectures = None 253 self.packager = None 254 self.packagerName = None 255 self.packagerEmail = None 256 self.allowUntested = False 257 self.allowUnsafeSources = False 258 self.createSourcePackages = True 259 self.downloadInPortDirectory = False 260 self.packageCommand = None 261 self.packageRepoCommand = None 262 self.mimesetCommand = None 263 self.reportingURI = None 264 self.systemMimeDB = None 265 self.licensesDirectory = None 266 self.crossTools = None 267 self.secondaryCrossTools = {} 268 self.crossDevelPackage = None 269 self.secondaryCrossDevelPackages = None 270 self.outputDirectory = None 271 self.repositoryPath = None 272 self.vendor = None 273 self.packagesPath = None 274 self.sourceforgeMirror = None 275 276 self._readConfigurationFile() 277 278 if not self.outputDirectory: 279 self.outputDirectory = self.treePath 280 if not self.packagesPath: 281 self.packagesPath = os.path.join(self.outputDirectory, 'packages') 282 if not self.repositoryPath: 283 self.repositoryPath = os.path.join(self.outputDirectory, 'repository') 284 285 @staticmethod 286 def init(): 287 Configuration.configuration = Configuration() 288 289 @staticmethod 290 def getTreePath(): 291 return Configuration.configuration.treePath 292 293 @staticmethod 294 def isCrossBuildRepository(): 295 return Configuration.configuration.isCrossBuildRepository 296 297 @staticmethod 298 def getTargetArchitecture(): 299 return Configuration.configuration.targetArchitecture 300 301 @staticmethod 302 def getSecondaryTargetArchitectures(): 303 return Configuration.configuration.secondaryArchitectures 304 305 @staticmethod 306 def getPackager(): 307 return Configuration.configuration.packager 308 309 @staticmethod 310 def getPackagerName(): 311 return Configuration.configuration.packagerName 312 313 @staticmethod 314 def getPackagerEmail(): 315 return Configuration.configuration.packagerEmail 316 317 @staticmethod 318 def shallAllowUntested(): 319 return Configuration.configuration.allowUntested 320 321 @staticmethod 322 def shallAllowUnsafeSources(): 323 return Configuration.configuration.allowUnsafeSources 324 325 @staticmethod 326 def shallCreateSourcePackages(): 327 return Configuration.configuration.createSourcePackages 328 329 @staticmethod 330 def shallDownloadInPortDirectory(): 331 return Configuration.configuration.downloadInPortDirectory 332 333 @staticmethod 334 def getPackageCommand(): 335 if Configuration.configuration.packageCommand is None: 336 return which("package") 337 return Configuration.configuration.packageCommand 338 339 @staticmethod 340 def getPackageRepoCommand(): 341 if Configuration.configuration.packageRepoCommand is None: 342 return which("package_repo") 343 return Configuration.configuration.packageRepoCommand 344 345 @staticmethod 346 def getMinisignCommand(): 347 return which("minisign") 348 349 @staticmethod 350 def getMimesetCommand(): 351 if Configuration.configuration.mimesetCommand is None: 352 return which("mimeset") 353 return Configuration.configuration.mimesetCommand 354 355 @staticmethod 356 def getReportingURI(): 357 return Configuration.configuration.reportingURI 358 359 @staticmethod 360 def getSystemMimeDbDirectory(): 361 return Configuration.configuration.systemMimeDB 362 363 @staticmethod 364 def getLicensesDirectory(): 365 return Configuration.configuration.licensesDirectory 366 367 @staticmethod 368 def getCrossToolsDirectory(): 369 return Configuration.configuration.crossTools 370 371 @staticmethod 372 def getSecondaryCrossToolsDirectory(architecture): 373 return Configuration.configuration.secondaryCrossTools.get(architecture) 374 375 @staticmethod 376 def getCrossDevelPackage(): 377 return Configuration.configuration.crossDevelPackage 378 379 @staticmethod 380 def getSecondaryCrossDevelPackage(architecture): 381 return Configuration.configuration.secondaryCrossDevelPackages.get( 382 architecture) 383 384 @staticmethod 385 def getOutputDirectory(): 386 return Configuration.configuration.outputDirectory 387 388 @staticmethod 389 def getRepositoryPath(): 390 return Configuration.configuration.repositoryPath 391 392 @staticmethod 393 def getPackagesPath(): 394 return Configuration.configuration.packagesPath 395 396 @staticmethod 397 def getDownloadMirror(): 398 return Configuration.configuration.downloadMirror 399 400 @staticmethod 401 def getSourceforgeMirror(): 402 return Configuration.configuration.sourceforgeMirror 403 404 @staticmethod 405 def getVendor(): 406 return Configuration.configuration.vendor 407 408 def _readConfigurationFile(self): 409 # Find the configuration file. It may be 410 # * specified on the command line, 411 # * in the current directory, 412 # * '~/config/settings/haikuports.conf', or 413 # * '/system/settings/haikuports.conf'. 414 haikuportsConf = getOption('configFile') 415 if not haikuportsConf: 416 haikuportsConf = 'haikuports.conf' 417 if not os.path.exists(haikuportsConf): 418 haikuportsConf = (os.path.expanduser('~') 419 + '/config/settings/haikuports.conf') 420 if not os.path.exists(haikuportsConf): 421 haikuportsConf = '/system/settings/haikuports.conf' 422 423 if not os.path.exists(haikuportsConf): 424 sysExit("Unable to find haikuports.conf in known search paths.\n" 425 + u"See haikuports-sample.conf for more information") 426 427 configParser = ConfigParser(haikuportsConf, haikuportsAttributes, {}) 428 configurationValue = configParser.getEntriesForExtension('') 429 430 # check whether all required values are present 431 for key in haikuportsAttributes.keys(): 432 if 'optionAttribute' in haikuportsAttributes[key]: 433 optionAttribute = haikuportsAttributes[key]['optionAttribute'] 434 optionValue = getOption(optionAttribute) 435 if optionValue: 436 configurationValue[key] = optionValue 437 438 if key not in configurationValue: 439 if haikuportsAttributes[key]['required']: 440 sysExit("Required value '" + key + u"' not present in " 441 + haikuportsConf) 442 443 # set default value, as no other value has been provided 444 if haikuportsAttributes[key]['default'] is not None: 445 configurationValue[key] \ 446 = haikuportsAttributes[key]['default'] 447 448 if ('setAttribute' in haikuportsAttributes[key] 449 and key in configurationValue): 450 setAttribute = haikuportsAttributes[key]['setAttribute'] 451 setattr(self, setAttribute, configurationValue[key]) 452 453 self.treePath = self.treePath.rstrip('/') 454 455 # determine if we are using a cross-build repository 456 self.isCrossBuildRepository = os.path.exists(self.treePath + '/.cross') 457 if self.isCrossBuildRepository and not self.targetArchitecture: 458 sysExit('For a cross-build repository, TARGET_ARCHITECTURE ' 459 'needs to be set in ' + haikuportsConf) 460 461 # split packager into name and email: 462 m = re.match(r'^\s*(?P<name>.+?)\s*<(?P<email>.+?)>$', self.packager) 463 if not m: 464 sysExit("Couldn't parse name/email from PACKAGER value " 465 + self.packager) 466 self.packagerName = m.group('name') 467 self.packagerEmail = m.group('email') 468 469 # get the secondary cross tools and devel packages 470 if self.secondaryArchitectures: 471 crossTools = configurationValue.get('SECONDARY_CROSS_TOOLS') 472 if crossTools: 473 if len(crossTools) != len(self.secondaryArchitectures): 474 sysExit('A cross-tools directory must be specified for ' 475 'each secondary architecture') 476 for architecture, tools \ 477 in zip(self.secondaryArchitectures, crossTools): 478 self.secondaryCrossTools[architecture] = tools 479 480 if self.secondaryCrossDevelPackages: 481 crossDevelPackages = self.secondaryCrossDevelPackages 482 self.secondaryCrossDevelPackages = {} 483 if len(crossDevelPackages) != len(self.secondaryArchitectures): 484 sysExit('A cross-tools devel pacakge must be specified for ' 485 'each secondary architecture') 486 for architecture, package \ 487 in zip(self.secondaryArchitectures, crossDevelPackages): 488 self.secondaryCrossDevelPackages[architecture] = package 489 else: 490 self.secondaryCrossDevelPackages = {} 491 else: 492 self.secondaryCrossTools = {} 493 self.secondaryCrossDevelPackages = {} 494