1# -*- coding: utf-8 -*- 2# 3# Copyright 2007-2011 Brecht Machiels 4# Copyright 2009-2010 Chris Roberts 5# Copyright 2009-2011 Scott McCreary 6# Copyright 2009 Alexander Deynichenko 7# Copyright 2009 HaikuBot (aka RISC) 8# Copyright 2010-2011 Jack Laxson (Jrabbit) 9# Copyright 2011 Ingo Weinhold 10# Copyright 2013 Oliver Tappe 11# Distributed under the terms of the MIT License. 12 13# -- Modules ------------------------------------------------------------------ 14 15import os 16import re 17import sys 18import traceback 19from subprocess import check_call 20 21from .BuildPlatform import buildPlatform 22from .Configuration import Configuration 23from .DependencyAnalyzer import DependencyAnalyzer 24from .Options import getOption 25from .PackageRepository import PackageRepository 26from .Policy import Policy 27from .RecipeAttributes import getRecipeFormatVersion 28from .RecipeTypes import MachineArchitecture 29from .Repository import Repository 30from .Utils import (ensureCommandIsAvailable, haikuportsRepoUrl, info, sysExit, 31 warn) 32 33# -- Main Class --------------------------------------------------------------- 34 35class Main(object): 36 def __init__(self, options, args): 37 self.options = options 38 try: 39 self.run(args) 40 except BaseException as exception: 41 if getOption('debug'): 42 traceback.print_exc() 43 elif type(exception).__name__ == "SystemExit": 44 if type(exception.code).__name__ != "int": 45 print(exception.code) 46 else: 47 print(exception) 48 exit(1) 49 50 def run(self, args): 51 52 self.policy = Policy(self.options.strictPolicy) 53 54 self.repository = None 55 56 # read global settings 57 Configuration.init() 58 59 self.treePath = Configuration.getTreePath() 60 self.outputDirectory = Configuration.getOutputDirectory() 61 self.packagesPath = Configuration.getPackagesPath() 62 self.repositoryPath = Configuration.getRepositoryPath() 63 64 self.packageRepositories = [self.packagesPath] 65 if not self.options.noSystemPackages \ 66 and self.options.systemPackagesDirectory is not None: 67 self.packageRepositories.append( 68 self.options.systemPackagesDirectory) 69 70 # if requested, checkout or update ports tree 71 if self.options.get: 72 self._updatePortsTree() 73 return 74 75 # create path where built packages will be collected 76 if not os.path.exists(self.packagesPath): 77 os.mkdir(self.packagesPath) 78 79 self._checkFormatVersions() 80 81 # determine if haikuporter has just been invoked for a short-term 82 # command 83 self.shallowInitIsEnough = (self.options.lint or self.options.tree 84 or self.options.get or self.options.list 85 or self.options.portsForFiles 86 or self.options.portsForPackages 87 or self.options.listPackages 88 or self.options.listDependencies 89 or self.options.search 90 or self.options.searchPackages 91 or self.options.about 92 or self.options.location 93 or self.options.buildMaster 94 or self.options.repositoryUpdate 95 or self.options.prunePackageRepository 96 or self.options.createPackageRepository 97 or self.options.why 98 or self.options.analyzeDependencies 99 or self.options.checkPackageRepositoryConsistency 100 or self.options.checkRepositoryConsistency 101 or self.options.checkPortsReleases) 102 103 # init build platform 104 buildPlatform.init(self.treePath, self.outputDirectory, 105 self.packagesPath, self.shallowInitIsEnough) 106 107 # set up the global variables we'll inherit to the shell 108 self._initGlobalShellVariables() 109 110 if self.options.buildMaster: 111 from .BuildMaster import BuildMaster 112 self.buildMaster = BuildMaster(self.treePath, self.packagesPath, 113 self.options) 114 115 self.options.allDependencies = True 116 self.options.noPackageObsoletion = True 117 self.options.ignoreMessages = True 118 119 if self.options.repositoryUpdate \ 120 or self.options.checkRepositoryConsistency: 121 self._createRepositoryIfNeeded(self.options.quiet) 122 123 if self.options.checkRepositoryConsistency: 124 self.repository.checkRepositoryConsistency(self.options.verbose) 125 return 126 127 if self.options.prunePackageRepository \ 128 or self.options.createPackageRepository \ 129 or self.options.checkPackageRepositoryConsistency: 130 131 self.options.noPackageObsoletion = True 132 self._createRepositoryIfNeeded(True) 133 134 packageRepository = PackageRepository(self.packagesPath, 135 self.repository, self.options.quiet, self.options.verbose) 136 137 if self.options.prunePackageRepository: 138 packageRepository.prune() 139 140 if self.options.checkPackageRepositoryConsistency: 141 packageRepository.checkPackageRepositoryConsistency() 142 143 if self.options.createPackageRepository: 144 packageRepository.createPackageRepository( 145 self.options.createPackageRepository) 146 return 147 148 # if requested, print the location of the haikuports source tree 149 if self.options.tree: 150 print(self.treePath) 151 return 152 153 # if requested, scan the ports tree for problems 154 if self.options.lint: 155 if (not buildPlatform.isHaiku 156 and Configuration.getLicensesDirectory() is None): 157 sysExit('LICENSES_DIRECTORY must be set in configuration on ' 158 'this build platform!') 159 self._createRepositoryIfNeeded(True) 160 if not args: 161 self._checkSourceTree("") 162 else: 163 self._checkSourceTree(args[0]) 164 return 165 166 # if requested, list all ports in the HaikuPorts tree 167 if self.options.list or self.options.listPackages: 168 self._createRepositoryIfNeeded(True) 169 if self.options.list: 170 allNames = self.repository.searchPorts(None, 171 self.options.printFilenames) 172 else: 173 allNames = self.repository.searchPackages(None, 174 self.options.printFilenames) 175 176 for name in sorted(allNames): 177 print(name) 178 return 179 180 # if requested, search for a port 181 if self.options.search or self.options.searchPackages: 182 if not args: 183 sysExit('You need to specify a search string.\n' 184 u"Invoke '" + sys.argv[0] + u" -h' for usage " 185 u"information.") 186 self._createRepositoryIfNeeded(True) 187 188 for arg in args: 189 if self.options.search: 190 portNames = self.repository.searchPorts(arg) 191 for portName in portNames: 192 versions = self.repository.portVersionsByName[portName] 193 portID = portName + '-' + versions[0] 194 port = self.repository.allPorts[portID] 195 if self.options.printRaw: 196 print(portName) 197 else: 198 print(port.category + '::' + portName) 199 else: 200 packageNames = self.repository.searchPackages(arg, 201 self.options.printFilenames) 202 for packageName in packageNames: 203 print(packageName) 204 return 205 206 # if requested, print the ports related to the supplied files 207 if self.options.portsForFiles: 208 self._createRepositoryIfNeeded(True) 209 210 if self.options.activeVersionsOnly: 211 allPorts = self.repository.activePorts 212 else: 213 allPorts = self.repository.allPorts.values() 214 215 files = [arg if os.path.isabs(arg) \ 216 else os.path.join(self.treePath, arg) for arg in args] 217 218 for port in allPorts: 219 if port.referencesFiles(files): 220 print(port.versionedName) 221 222 return 223 224 # if requested, print the ports producing the supplied packages 225 if self.options.portsForPackages: 226 self._createRepositoryIfNeeded(True) 227 228 ports = set() 229 for port in self.repository.allPorts.values(): 230 try: 231 port.parseRecipeFileIfNeeded() 232 except: 233 continue 234 235 for package in port.packages: 236 if package.hpkgName in args: 237 ports.add(port.versionedName) 238 239 print('\n'.join(sorted(ports))) 240 return 241 242 if self.options.location: 243 if not args: 244 sysExit('You need to specify a search string.\n' 245 u"Invoke '" + sys.argv[0] + u" -h' for usage " 246 u"information.") 247 # Provide the installed location of a port (for quick editing) 248 self._createRepositoryIfNeeded(True) 249 portNames = self.repository.searchPorts(args[0]) 250 for portName in portNames: 251 versions = self.repository.portVersionsByName[portName] 252 portID = portName + '-' + versions[0] 253 port = self.repository.allPorts[portID] 254 print(os.path.join(self.treePath, port.category, portName)) 255 return 256 257 if self.options.portsfile: 258 # read portslist from file and convert into list of requires 259 with open(self.options.portsfile, 'r') as portsFile: 260 ports = [p.strip() for p in portsFile.readlines()] 261 ports = [p for p in ports if len(p) > 0] 262 portsfileAsRequires = [] 263 for port in ports: 264 portSpec = self._splitPortSpecIntoNameVersionAndRevision(port) 265 if portSpec['version']: 266 portsfileAsRequires.append(portSpec['name'] + ' ==' 267 + portSpec['version']) 268 else: 269 portsfileAsRequires.append(portSpec['name']) 270 if not portsfileAsRequires: 271 sysExit("The given ports-file doesn't contain any ports.") 272 self.shellVariables['portsfileAsRequires'] \ 273 = '\n'.join(portsfileAsRequires) 274 275 self._createRepositoryIfNeeded(self.options.quiet, self.options.verbose) 276 277 if self.options.analyzeDependencies: 278 DependencyAnalyzer(self.repository).printDependencies() 279 return 280 281 # if requested, check for newer upstream releases 282 if self.options.checkPortsReleases: 283 self._createRepositoryIfNeeded(True) 284 if not args: 285 self._checkPortsReleases("") 286 else: 287 self._checkPortsReleases(args[0]) 288 return 289 290 bootstrapPorts = set() 291 292 # if a ports-file has been given, read port specifications from it 293 # and build them all (as faked requires of a specific meta port, such 294 # that their runtime requires get pulled in, too) 295 self.portSpecs = [] 296 self.builtPortIDs = set() 297 if self.options.portsfile: 298 # pretend the meta port responsible for building a list of ports 299 # has been specified on the cmdline 300 metaPortSpec = 'meta_portsfile-1' 301 if metaPortSpec not in self.repository.allPorts: 302 sysExit("no recipe found for '%s'" % metaPortSpec) 303 self.portSpecs.append( 304 self._splitPortSpecIntoNameVersionAndRevision(metaPortSpec)) 305 elif self.options.doBootstrap: 306 # first untangle and build all ports with circular dependencies 307 dependencyAnalyzer = DependencyAnalyzer(self.repository) 308 portsToBuild = dependencyAnalyzer.getBuildOrderForBootstrap() 309 print('Untangling the ports with circular dependencies gave this:') 310 print(" " + "\n ".join(portsToBuild)) 311 print('After that, all other available ports will be built, too') 312 portsNotYetBuilt = [] 313 for portId in portsToBuild: 314 port = self.repository.allPorts[portId] 315 mainPackage = port.mainPackage 316 if (mainPackage 317 and os.path.exists( 318 self.packagesPath + '/' + mainPackage.hpkgName)): 319 print('skipping port %s, since its main package already ' 320 'exists' % portId) 321 continue 322 portsNotYetBuilt.append(portId) 323 bootstrapPorts.add(portId) 324 # add all other ports, such that all available ports will be built 325 for portId in self.repository.allPorts.keys(): 326 if portId not in bootstrapPorts: 327 port = self.repository.allPorts[portId] 328 mainPackage = port.mainPackage 329 if (mainPackage 330 and os.path.exists( 331 self.packagesPath + '/' + mainPackage.hpkgName)): 332 print('skipping port %s, since its main package ' 333 'already exists' % portId) 334 continue 335 portsNotYetBuilt.append(portId) 336 # add all ports as if they were given on the cmdline 337 self.portSpecs = [ 338 self._splitPortSpecIntoNameVersionAndRevision(port) 339 for port in portsNotYetBuilt 340 ] 341 else: 342 # if there is no argument given, exit 343 if not args: 344 sysExit('You need to specify a search string.\nInvoke ' 345 u"'" + sys.argv[0] + u" -h' for usage information.") 346 self.portSpecs = [ 347 self._splitPortSpecIntoNameVersionAndRevision(port) 348 for port in args 349 ] 350 351 # don't build or package when not patching 352 if not self.options.patch: 353 self.options.build = False 354 self.options.package = False 355 356 # collect all available ports and validate each specified port 357 allPorts = self.repository.allPorts 358 portVersionsByName = self.repository.portVersionsByName 359 for portSpec in self.portSpecs: 360 361 # validate name of port 362 portName = portSpec['name'] 363 if portName not in portVersionsByName: 364 # for cross-build repository, try with target arch added 365 portNameFound = False 366 if Configuration.isCrossBuildRepository(): 367 nameWithTargetArch \ 368 = (portName + '_' 369 + self.shellVariables['targetArchitecture']) 370 if nameWithTargetArch in portVersionsByName: 371 portName = nameWithTargetArch 372 portNameFound = True 373 374 # it might actually be a package name 375 if not portNameFound: 376 portName = self.repository.getPortNameForPackageName( 377 portName) 378 if not portName: 379 if self.options.buildMaster: 380 self.buildMaster.addSkipped(portSpec['name'], 381 'not found in repository') 382 continue 383 384 sysExit(portSpec['name'] + ' not found in repository') 385 386 portSpec['name'] = portName 387 388 # use specific version if given, otherwise use the highest buildable 389 # version 390 if portSpec['version']: 391 portID = portSpec['name'] + '-' + portSpec['version'] 392 else: 393 version = self.repository.getActiveVersionOf(portSpec['name'], 394 True) 395 if not version: 396 if self.options.buildMaster: 397 self.buildMaster.addSkipped(portSpec['name'], 398 'no version of ' + portSpec['name'] 399 + ' can be built') 400 continue 401 else: 402 sysExit('No version of ' + portSpec['name'] 403 + ' can be built') 404 portID = portSpec['name'] + '-' + version 405 406 if portID not in allPorts: 407 if self.options.buildMaster: 408 self.buildMaster.addSkipped(portID, 'not found in tree') 409 continue 410 411 sysExit(portID + ' not found in tree.') 412 413 port = allPorts[portID] 414 415 # show port description, if requested 416 if self.options.about: 417 try: 418 port.parseRecipeFile(False) 419 except: 420 pass 421 port.printDescription() 422 continue 423 424 if self.options.listDependencies: 425 self._listDependencies(port) 426 continue 427 428 if not self._validateMainPort(port, portSpec['revision']): 429 continue 430 431 portSpec['id'] = portID 432 433 if self.options.about or self.options.listDependencies: 434 return 435 436 if self.options.why: 437 # find out about why another port is required 438 port = allPorts[self.portSpecs[0]['id']] 439 whySpec = self._splitPortSpecIntoNameVersionAndRevision( 440 self.options.why) 441 if not whySpec['version']: 442 whySpec['version'] \ 443 = self.repository.getActiveVersionOf(whySpec['name'], 444 False) 445 whyID = whySpec['name'] + '-' + whySpec['version'] 446 if whyID not in allPorts: 447 sysExit(whyID + ' not found in tree.') 448 requiredPort = allPorts[whyID] 449 self._validateMainPort(requiredPort) 450 port.whyIsPortRequired(self.packagesPath, requiredPort) 451 return 452 453 # do whatever's needed to the list of ports 454 for portSpec in self.portSpecs: 455 if 'id' not in portSpec: 456 continue 457 458 port = allPorts[portSpec['id']] 459 460 if self.options.clean: 461 port.cleanWorkDirectory() 462 elif self.options.purge: 463 port.purge() 464 elif ((self.options.build and portSpec['id'] not in bootstrapPorts) 465 or self.options.test) and self.options.allDependencies: 466 try: 467 self._buildMainPort(port, self.options.test) 468 except SystemExit as exception: 469 if not self.options.buildMaster: 470 raise 471 else: 472 self.buildMaster.addSkipped(port, str(exception)) 473 474 elif self.options.extractPatchset: 475 port.extractPatchset() 476 else: 477 self._buildPort(port, True, self.options.test) 478 479 # show summary of policy violations 480 if Policy.violationsByPort: 481 print('Summary of policy violations in this session:') 482 for portName in sorted(Policy.violationsByPort.keys()): 483 print('Policy violations of %s:' + portName) 484 for violation in Policy.violationsByPort[portName]: 485 print('\t' + violation) 486 487 if self.options.buildMaster: 488 if self.options.display: 489 from .Display import DisplayContext 490 with DisplayContext() as ctxt: 491 self.buildMaster.runBuilds(ctxt.stdscr) 492 else: 493 self.buildMaster.runBuilds() 494 495 def _listDependencies(self, port): 496 print('-' * 70) 497 print('dependencies of ' + port.versionedName) 498 499 presentDependencyPackages = [] 500 buildDependencies = port.resolveDependencies( 501 self.packageRepositories, self.options.test, 502 presentDependencyPackages) 503 504 print('packages already present:') 505 presentDependencyPackageNames = [os.path.basename(package) 506 for package in presentDependencyPackages] 507 for name in sorted(presentDependencyPackageNames): 508 print("\t" + name) 509 print('') 510 511 print('packages that need to be built:') 512 for dependency in buildDependencies: 513 packageInfoFileName = os.path.basename(dependency) 514 packageID = packageInfoFileName[:packageInfoFileName.rindex('.')] 515 try: 516 portID = self.repository.getPortIdForPackageId(packageID) 517 print("\t" + packageID + ' -> ' + portID) 518 519 except KeyError: 520 sysExit('Inconsistency: ' + port.versionedName 521 + ' requires ' + packageID 522 + ' but no corresponding port was found!') 523 524 def _validateMainPort(self, port, revision=None): 525 """Parse the recipe file for the given port and get any required 526 confirmations""" 527 528 # read data from the recipe file 529 port.parseRecipeFile(True) 530 531 # if a specific revision has been given, check if this port matches it 532 if revision and port.revision != revision: 533 sysExit((u"Port %s isn't available in revision %s (found revision " 534 + '%s instead)') 535 % (port.versionedName, revision, port.revision)) 536 537 # warn when the port is not buildable on this architecture 538 if not port.isBuildableOnTargetArchitecture(): 539 status = port.statusOnTargetArchitecture 540 message = 'Port {} is {} on this architecture.'.format( 541 port.versionedName, status) 542 warn(message) 543 if self.options.buildMaster: 544 self.buildMaster.addSkipped(port, message) 545 return False 546 547 if not self.options.yes: 548 answer = input('Continue (y/n + enter)? ') 549 if answer == '': 550 sys.exit(1) 551 if answer[0].lower() == 'y': 552 print(' ok') 553 else: 554 sys.exit(1) 555 556 if not self.options.ignoreMessages and port.recipeKeys['MESSAGE']: 557 print(port.recipeKeys['MESSAGE']) 558 if not self.options.yes: 559 answer = raw_input('Continue (y/n + enter)? ') 560 if answer == '': 561 sys.exit(1) 562 if answer[0].lower() == 'y': 563 print(' ok') 564 else: 565 sys.exit(1) 566 567 return True 568 569 def _buildMainPort(self, port, testPort): 570 """Build the given port with all its dependencies""" 571 572 if port.versionedName in self.builtPortIDs: 573 return 574 575 self._setupForPossiblyObsoletePort(port) 576 577 print('=' * 70) 578 print(port.category + '::' + port.versionedName) 579 print('=' * 70) 580 581 allPorts = self.repository.allPorts 582 583 buildDependencies = None 584 presentDependencyPackages = None 585 if self.options.buildMaster: 586 presentDependencyPackages = [] 587 try: 588 buildDependencies = port.resolveDependencies( 589 self.packageRepositories, False, presentDependencyPackages) 590 except Exception as exception: 591 self.buildMaster.addSkipped(port, 592 'resolving build dependencies failed: {}'.format(exception)) 593 return 594 else: 595 buildDependencies = port.resolveDependencies( 596 self.packageRepositories, testPort) 597 598 print('The following build dependencies were found:') 599 for dependency in buildDependencies: 600 print('\t' + dependency) 601 602 requiredPortsToBuild = [] 603 requiredPortIDs = set() 604 requiredPackageIDs = set() 605 for dependency in buildDependencies: 606 packageInfoFileName = os.path.basename(dependency) 607 packageID = packageInfoFileName[:packageInfoFileName.rindex('.')] 608 if self.options.buildMaster and packageID not in requiredPackageIDs: 609 requiredPackageIDs.add(packageID) 610 611 try: 612 portID = self.repository.getPortIdForPackageId(packageID) 613 if portID not in requiredPortIDs: 614 requiredPort = allPorts[portID] 615 if ((getOption('createSourcePackagesForBootstrap') 616 or getOption('createSourcePackages')) 617 and (not requiredPort.sourcePackage 618 or requiredPort.sourcePackageExists( 619 self.packagesPath))): 620 continue 621 requiredPortsToBuild.append(requiredPort) 622 requiredPortIDs.add(portID) 623 except KeyError: 624 sysExit('Inconsistency: ' + port.versionedName 625 + ' requires ' + packageID 626 + ' but no corresponding port was found!') 627 628 if requiredPortsToBuild: 629 if port in requiredPortsToBuild: 630 sysExit('Port ' + port.versionedName + ' depends on itself') 631 632 print('The following required ports will be built first:') 633 for requiredPort in requiredPortsToBuild: 634 print('\t' + requiredPort.category + '::' 635 + requiredPort.versionedName) 636 for requiredPort in requiredPortsToBuild: 637 if self.options.buildMaster: 638 requiredPort.parseRecipeFile(True) 639 try: 640 self._buildMainPort(requiredPort, False) 641 except SystemExit as exception: 642 self.buildMaster.addSkipped(port, 643 'Skipping ' + port.versionedName + ', dependency ' 644 + requiredPort.versionedName 645 + ' cannot be built: ' + str(exception)) 646 sysExit('Dependency of ' + port.versionedName 647 + ' cannot be built') 648 else: 649 self._buildPort(requiredPort, True, False) 650 651 if self.options.buildMaster: 652 self.buildMaster.schedule(port, requiredPackageIDs, 653 presentDependencyPackages) 654 self.builtPortIDs.add(port.versionedName) 655 else: 656 self._buildPort(port, False, testPort) 657 658 def _buildPort(self, port, parseRecipe, testPort): 659 """Build a single port""" 660 661 if port.versionedName in self.builtPortIDs: 662 return 663 664 targetPath = self._setupForPossiblyObsoletePort(port) 665 666 print('-' * 70) 667 print(port.category + '::' + port.versionedName) 668 print('\t' + port.recipeFilePath) 669 print('-' * 70) 670 671 # pass-on options to port 672 port.forceOverride = self.options.force 673 port.beQuiet = self.options.quiet 674 port.avoidChroot = not self.options.chroot 675 676 if parseRecipe: 677 port.parseRecipeFile(True) 678 679 if testPort and port.checkFlag('build'): 680 self._testPort(port) 681 return 682 683 if not port.isMetaPort: 684 port.downloadSource() 685 port.unpackSource() 686 port.populateAdditionalFiles() 687 if self.options.patch: 688 port.patchSource() 689 690 if self.options.build: 691 port.build(self.packagesPath, self.options.package, targetPath) 692 693 if testPort: 694 self._testPort(port) 695 696 self.builtPortIDs.add(port.versionedName) 697 698 def _setupForPossiblyObsoletePort(self, port): 699 # HPKGs are usually written into the 'packages' directory, but when 700 # an obsolete port (one that's not in the repository) is being built, 701 # its packages are stored into the .obsolete subfolder of the packages 702 # directory. 703 targetPath = self.packagesPath 704 activeVersion = self.repository.getActiveVersionOf(port.name) 705 if port.version != activeVersion: 706 targetPath += '/.obsolete' 707 if not os.path.exists(targetPath): 708 os.makedirs(targetPath) 709 710 warn('building obsolete port, packages will be put in {}'.format( 711 targetPath)) 712 713 # make sure the correct dependencyInfo-file has been created 714 self.repository.supportBackwardsCompatibility(port.name, 715 port.version) 716 717 return targetPath 718 719 def _testPort(self, port): 720 """Build a single port""" 721 722 print('-' * 70) 723 print('TESTING ' + port.category + '::' + port.versionedName) 724 print('-' * 70) 725 726 # pass-on options to port 727 port.beQuiet = self.options.quiet 728 729 port.test(self.packagesPath) 730 731 def _initGlobalShellVariables(self): 732 # get the target haiku version and architecture 733 targetArchitecture = buildPlatform.targetArchitecture 734 if Configuration.isCrossBuildRepository(): 735 targetHaikuPackage = Configuration.getCrossDevelPackage() 736 if not targetHaikuPackage: 737 if not buildPlatform.isHaiku: 738 sysExit('On this platform a haiku cross devel package ' 739 'must be specified (via --cross-devel-package)') 740 targetHaikuPackage = ('/boot/system/develop/cross/' 741 + 'haiku_cross_devel_sysroot_%s.hpkg') \ 742 % targetArchitecture 743 else: 744 if (not buildPlatform.isHaiku and not self.shallowInitIsEnough 745 and not (getOption('createSourcePackagesForBootstrap') 746 or getOption('createSourcePackages'))): 747 sysExit('Native building not supported on this platform ' 748 '(%s)' % buildPlatform.name) 749 750 self.shellVariables = { 751 'haikuVersion': 'r1~alpha1', # just a dummy value for compatibility with old recipes 752 'buildArchitecture': buildPlatform.architecture, 753 'targetArchitecture': targetArchitecture, 754 'jobs': str(self.options.jobs), 755 } 756 if self.options.jobs > 1: 757 self.shellVariables['jobArgs'] = '-j' + str(self.options.jobs) 758 if self.options.quiet: 759 self.shellVariables['quiet'] = '1' 760 761 if Configuration.isCrossBuildRepository(): 762 self.shellVariables['isCrossRepository'] = 'true' 763 764 buildMachineTriple = buildPlatform.machineTriple 765 targetMachineTriple \ 766 = MachineArchitecture.getTripleFor(targetArchitecture) 767 768 # If build- and target machine triple are the same, force a 769 # cross-build by faking the build-machine triple as something 770 # different (which is still being treated identically by the actual 771 # build process). 772 if buildMachineTriple == targetMachineTriple: 773 buildMachineTriple += '_build' 774 775 self.shellVariables['buildMachineTriple'] = buildMachineTriple 776 self.shellVariables['buildMachineTripleAsName'] \ 777 = buildMachineTriple.replace('-', '_') 778 self.shellVariables['targetArchitecture'] = targetArchitecture 779 self.shellVariables['targetMachineTriple'] = targetMachineTriple 780 self.shellVariables['targetMachineTripleAsName'] \ 781 = targetMachineTriple.replace('-', '_') 782 else: 783 self.shellVariables['isCrossRepository'] = 'false' 784 785 def _createRepositoryIfNeeded(self, quiet=False, verbose=False): 786 """create/update repository""" 787 if self.repository: 788 return 789 self.repository = Repository(self.treePath, 790 self.outputDirectory, self.repositoryPath, 791 self.packagesPath, self.shellVariables, self.policy, 792 self.options.preserveFlags, quiet, verbose) 793 794 def _updatePortsTree(self): 795 """Get/Update the port tree via git""" 796 print('Refreshing the port tree: %s' % self.treePath) 797 ensureCommandIsAvailable('git') 798 if os.path.exists(self.treePath + '/.git'): 799 check_call(['git', 'pull'], cwd=self.treePath) 800 else: 801 check_call(['git', 'clone', haikuportsRepoUrl, self.treePath]) 802 803 def _splitPortSpecIntoNameVersionAndRevision(self, portSpecString): 804 elements = portSpecString.split('-') 805 if len(elements) < 1 or len(elements) > 3: 806 sysExit('Invalid port specifier ' + portSpecString) 807 808 return { 809 'specifier': portSpecString, 810 'name': elements[0], 811 'version': elements[1] if len(elements) > 1 else None, 812 'revision': elements[2] if len(elements) > 2 else None, 813 } 814 815 def _getCategory(self, portName): 816 """Find location of the specified port in the HaikuPorts tree""" 817 hierarchy = [] 818 dirList = os.listdir(self.treePath) 819 for item in dirList: 820 if os.path.isdir(item) and item[0] != '.' and '-' in item: 821 subdirList = os.listdir(item) 822 # remove items starting with '.' 823 subdirList.sort() 824 while subdirList[0][0] == '.': 825 del subdirList[0] 826 827 # locate port 828 try: 829 if subdirList.index(portName) >= 0: 830 # port was found in the category specified by 'item' 831 return item 832 except ValueError: 833 pass 834 hierarchy.append([item, subdirList]) 835 return None 836 837 def _checkSourceTree(self, portArgument): 838 if portArgument: 839 info('Checking ports of: ' + portArgument) 840 841 allPorts = self.repository.allPorts 842 portVersionsByName = self.repository.portVersionsByName 843 844 if portArgument in allPorts: 845 # Full port name / ver 846 port = allPorts[portArgument] 847 print('%s [%s]' % (portArgument, port.category)) 848 port.validateRecipeFile(True) # exit 1 if fail 849 return 850 elif portArgument in portVersionsByName: 851 # Base port name 852 somethingFailed = False 853 for version in portVersionsByName[portArgument]: 854 portID = portArgument + '-' + version 855 port = allPorts[portID] 856 print('%s [%s]' % (portID, port.category)) 857 try: 858 port.validateRecipeFile(True) 859 except SystemExit as e: 860 somethingFailed = True 861 print(e.code) 862 if somethingFailed: 863 sys.exit(1) 864 else: 865 # Unknown 866 sysExit('%s is not a known port!' % portArgument) 867 868 else: 869 info('Checking HaikuPorts tree at: ' + self.treePath) 870 allPorts = self.repository.allPorts 871 portVersionsByName = self.repository.portVersionsByName 872 somethingFailed = False 873 for portName in sorted(portVersionsByName.keys(), key=str.lower): 874 for version in portVersionsByName[portName]: 875 portID = portName + '-' + version 876 port = allPorts[portID] 877 print('%s [%s]' % (portID, port.category)) 878 try: 879 port.validateRecipeFile(True) 880 except SystemExit as e: 881 print(e.code) 882 somethingFailed = True 883 if somethingFailed: 884 sys.exit(1) 885 886 def _checkFormatVersions(self): 887 # Read the format versions used by the tree and stop if they don't 888 # match the ones supported by this instance of haikuporter. 889 formatVersionsFile = self.treePath + '/FormatVersions' 890 recipeFormatVersion = 0 891 if os.path.exists(formatVersionsFile): 892 with open(formatVersionsFile, 'r') as f: 893 formatVersions = f.read() 894 recipeFormatVersionMatch = re.search('^RecipeFormatVersion=(.+?)$', 895 formatVersions, 896 flags=re.MULTILINE) 897 if recipeFormatVersionMatch: 898 try: 899 recipeFormatVersion = int(recipeFormatVersionMatch.group(1)) 900 except ValueError: 901 pass 902 903 if recipeFormatVersion > getRecipeFormatVersion(): 904 sysExit('The version of the recipe file format used in the ports ' 905 'tree is newer than the one supported by haikuporter.\n' 906 'Please upgrade haikuporter.') 907 if recipeFormatVersion < getRecipeFormatVersion(): 908 sysExit('The version of the recipe file format used in the ports ' 909 'tree is older than the one supported by haikuporter.\n' 910 'Please upgrade the ports tree.') 911 912 def _checkPortsReleases(self, portArgument): 913 self._createRepositoryIfNeeded(True) 914 if portArgument: 915 print('Checking for newer release for port: ' + portArgument) 916 917 allPorts = self.repository.allPorts 918 portVersionsByName = self.repository.portVersionsByName 919 920 if portArgument in allPorts: 921 # Full port name / ver 922 port = allPorts[portArgument] 923 print('%s [%s]' % (portArgument, port.category)) 924 port.checkPortReleases() 925 return 926 elif portArgument in portVersionsByName: 927 # Base port name 928 version = self.repository.getActiveVersionOf(portArgument) 929 if not version: 930 sysExit('%s does not have an active version!' % portArgument) 931 portID = portArgument + '-' + version 932 port = allPorts[portID] 933 print('%s [%s]' % (portID, port.category)) 934 port.checkPortReleases() 935 else: 936 # Unknown 937 sysExit('%s is not a known port!' % portArgument) 938 939 else: 940 print('Checking for newer release for ports from tree at: ' + self.treePath) 941 allPorts = self.repository.allPorts 942 portVersionsByName = self.repository.portVersionsByName 943 somethingFailed = False 944 for portName in sorted(portVersionsByName.keys(), key=str.lower): 945 version = self.repository.getActiveVersionOf(portName) 946 if not version: 947 continue 948 portID = portName + '-' + version 949 port = allPorts[portID] 950 print('%s [%s]' % (portID, port.category)) 951 port.checkPortReleases() 952