HaikuPorts
  • Login
  • Preferences
  • Help/Guide
  • Wiki
  • Timeline
  • Roadmap
  • View Tickets
  • Search
  • Port Log
  • Blog

Context Navigation

  • Back to Ticket #451

Ticket #451: pep8.diff

File pep8.diff, 81.1 KB (added by jrabbit, 5 years ago)
  • haikuporter

     
    3030# -- HaikuPorts options ------------------------------------------------------- 
    3131 
    3232# location of haikuports.conf 
     33 
    3334haikuPortsConf = '/boot/common/etc/haikuports.conf' 
    3435 
    3536# create new type 'ShellType' for identifying lists of shell commands 
     37 
     38 
    3639class shell(list): 
    37         pass 
     40 
     41    pass 
     42 
     43 
    3844ShellType = shell 
    3945 
    4046# create new type 'StatusType' for identifying the port's status on a platform 
    4147#  the possible states are defined in the 'Config' class (reStatusType) 
     48 
     49 
    4250class status(str): 
    43         pass 
     51 
     52    pass 
     53 
     54 
    4455StatusType = status 
    4556 
    4657# allowed types of the /etc/haikuports.conf values 
     58 
    4759confTypes = {} 
    4860confTypes['PACKAGES_PATH'] = [types.StringType] 
    4961confTypes['PATCH_OPTIONS'] = [types.StringType] 
    5062 
    5163# allowed types of the BepFile values 
     64 
    5265bepTypes = {} 
    53 bepTypes['DESCRIPTION']  = [types.StringType, types.ListType] 
    54 bepTypes['HOMEPAGE']    = [types.StringType] 
    55 bepTypes['SRC_URI']              = [types.StringType, types.ListType] 
     66bepTypes['DESCRIPTION'] = [types.StringType, types.ListType] 
     67bepTypes['HOMEPAGE'] = [types.StringType] 
     68bepTypes['SRC_URI'] = [types.StringType, types.ListType] 
    5669bepTypes['CHECKSUM_MD5'] = [types.StringType] 
    57 bepTypes['REVISION']    = [types.IntType] 
     70bepTypes['REVISION'] = [types.IntType] 
    5871bepTypes['STATUS_HAIKU'] = [StatusType] 
    59 bepTypes['DEPEND']              = [types.StringType, types.ListType, types.NoneType] 
    60 bepTypes['BUILD']                = [ShellType] 
    61 bepTypes['INSTALL']              = [ShellType] 
    62 bepTypes['TEST']                = [ShellType] 
    63 bepTypes['MESSAGE']              = [types.StringType] 
    64 bepTypes['LICENSE']              = [types.StringType, types.ListType] 
    65 bepTypes['COPYRIGHT']    = [types.StringType, types.ListType] 
     72bepTypes['DEPEND'] = [types.StringType, types.ListType, types.NoneType] 
     73bepTypes['BUILD'] = [ShellType] 
     74bepTypes['INSTALL'] = [ShellType] 
     75bepTypes['TEST'] = [ShellType] 
     76bepTypes['MESSAGE'] = [types.StringType] 
     77bepTypes['LICENSE'] = [types.StringType, types.ListType] 
     78bepTypes['COPYRIGHT'] = [types.StringType, types.ListType] 
    6679 
    67                                                         # is field       #      default 
    68 bepDefaults = {}                        # required?      #      value 
    69 bepDefaults['DESCRIPTION']      = [True,                None            ] 
    70 bepDefaults['HOMEPAGE']         = [True,                None            ] 
    71 bepDefaults['SRC_URI']          = [True,                None            ] 
    72 bepDefaults['CHECKSUM_MD5']     = [False,               None            ] 
    73 bepDefaults['REVISION']         = [True,                None            ] 
    74 bepDefaults['WORKING']          = [False,               True            ] 
    75 bepDefaults['STATUS_HAIKU']     = [False,               'untested'      ] 
    76 bepDefaults['DEPEND']           = [False,               None            ] 
    77 bepDefaults['BUILD']            = [False,               shell()         ] 
    78 bepDefaults['INSTALL']          = [False,               shell()         ] 
    79 bepDefaults['TEST']                     = [False,               shell()         ] 
    80 bepDefaults['MESSAGE']          = [False,               None            ] 
    81 bepDefaults['LICENSE']          = [False,               None            ] 
    82 bepDefaults['COPYRIGHT']        = [False,               None            ] 
     80                            # is field.... #....default 
    8381 
     82bepDefaults = {}  # required?.... #....value 
     83bepDefaults['DESCRIPTION'] = [True, None] 
     84bepDefaults['HOMEPAGE'] = [True, None] 
     85bepDefaults['SRC_URI'] = [True, None] 
     86bepDefaults['CHECKSUM_MD5'] = [False, None] 
     87bepDefaults['REVISION'] = [True, None] 
     88bepDefaults['WORKING'] = [False, True] 
     89bepDefaults['STATUS_HAIKU'] = [False, 'untested'] 
     90bepDefaults['DEPEND'] = [False, None] 
     91bepDefaults['BUILD'] = [False, shell()] 
     92bepDefaults['INSTALL'] = [False, shell()] 
     93bepDefaults['TEST'] = [False, shell()] 
     94bepDefaults['MESSAGE'] = [False, None] 
     95bepDefaults['LICENSE'] = [False, None] 
     96bepDefaults['COPYRIGHT'] = [False, None] 
     97 
    8498# names of directories 
     99 
    85100paths = {} 
    86 paths['work']           = "work" 
    87 paths['patches']        = "patches" 
    88 paths['download']       = "download" 
    89 paths['distro']         = "distro" 
    90 paths['licenses']       = "licesnes" 
     101paths['work'] = 'work' 
     102paths['patches'] = 'patches' 
     103paths['download'] = 'download' 
     104paths['distro'] = 'distro' 
     105paths['licenses'] = 'licesnes' 
    91106 
    92107# regex to split bep filenames into port / version 
     108 
    93109regExp = {} 
    94 regExp['portname']              = '^(?P<name>[\w\-\+]+?)' 
    95 regExp['portversion']   = '(?P<version>[\w]*?[\d]+([\w\-\\.\+])*)' 
    96 regExp['portfullname']  = regExp['portname'] + '-' + regExp['portversion'] 
    97 regExp['bepfilename']   = regExp['portfullname'] + '\.bep$' 
     110regExp['portname'] = '^(?P<name>[\w\-\+]+?)' 
     111regExp['portversion'] = '(?P<version>[\w]*?[\d]+([\w\-\\.\+])*)' 
     112regExp['portfullname'] = regExp['portname'] + '-' + regExp['portversion' 
     113        ] 
     114regExp['bepfilename'] = regExp['portfullname'] + '\.bep$' 
    98115 
    99116# -- capture output of shell command ----------------------------------------- 
    100117 
     118 
    101119def getCommandOutput(command): 
    102         status, data = commands.getstatusoutput(command) 
    103         if os.WEXITSTATUS(status) != 0: 
    104                 sys.exit('\'%s\' failed with exit code %d' % (command, os.WEXITSTATUS(status))) 
    105         return data 
     120    (status, data) = commands.getstatusoutput(command) 
     121    if os.WEXITSTATUS(status) != 0: 
     122        sys.exit('\'%s\' failed with exit code %d' % (command, 
     123                 os.WEXITSTATUS(status))) 
     124    return data 
    106125 
     126 
    107127# -- Set up locale for thousands seperators ---------------------------------- 
     128 
     129 
    108130def _temp(lc=locale.localeconv()): 
    109         lc.update({'thousands_sep':',','grouping':[3,3,0]}) 
    110         return lc 
     131    lc.update({'thousands_sep': ',', 'grouping': [3, 3, 0]}) 
     132    return lc 
    111133 
     134 
    112135def FindDirectory(aDir, subDirPath): 
    113         result = commands.getoutput('finddir %s' % aDir) + '/' + subDirPath 
    114         return result 
     136    result = commands.getoutput('finddir %s' % aDir) + '/' + subDirPath 
     137    return result 
    115138 
    116139 
    117 locale.localeconv=_temp 
     140locale.localeconv = _temp 
    118141 
    119142# -- Main Program ------------------------------------------------------------ 
    120143 
     144 
    121145class HaikuPorter: 
    122         def __init__(self, options, args): 
    123                 # read global settings 
    124                 mainConfig = Config(haikuPortsConf) 
    125                 self.confKeys = mainConfig.getKeys() 
    126                 self.options = options 
    127146 
    128                 for key in self.confKeys: 
    129                         try: 
    130                                 if type(self.confKeys[key]) not in confTypes[key]: 
    131                                         sys.exit("Error: Invalid value type for " + key + ". Expecting " + str(confTypes[key])) 
    132                         except KeyError, e: 
    133                                 print "Warning: Unknown key label '" + key + "' in " + haikuPortsConf + ". Ignoring." 
     147    def __init__(self, options, args): 
    134148 
    135                 self.packagesPath = self.confKeys['PACKAGES_PATH'] 
    136                 if self.packagesPath[-1] == '/': 
    137                         # strip trailing '/' 
    138                         self.packagesPath = self.packagesPath[:-1] 
     149        # read global settings 
    139150 
    140                 # if requested, list all ports in the HaikuPorts tree 
    141                 if options.list: 
    142                         self.searchPorts(None) 
    143                         sys.exit() 
     151        mainConfig = Config(haikuPortsConf) 
     152        self.confKeys = mainConfig.getKeys() 
     153        self.options = options 
    144154 
    145                 # if requested, search for a port 
    146                 if options.search: 
    147                         if len(args) == 0: 
    148                                 print "You need to specifiy a search string." 
    149                                 print "Invoke '" + sys.argv[0] + " -h' for usage information." 
    150                                 sys.exit(1) 
     155        for key in self.confKeys: 
     156            try: 
     157                if type(self.confKeys[key]) not in confTypes[key]: 
     158                    sys.exit('Error: Invalid value type for ' + key 
     159                              + '. Expecting ' + str(confTypes[key])) 
     160            except KeyError, e: 
     161                print "Warning: Unknown key label '" + key + "' in "\ 
     162                     + haikuPortsConf + '. Ignoring.' 
    151163 
    152                         self.searchPorts(args[0]) 
    153                         sys.exit() 
     164        self.packagesPath = self.confKeys['PACKAGES_PATH'] 
     165        if self.packagesPath[-1] == '/': 
    154166 
    155                 # if requested, checkout or update ports tree 
    156                 if options.get: 
    157                         self.updatePortsTree() 
    158                         sys.exit() 
     167            # strip trailing '/' 
    159168 
    160                 # if requested, print the location of the haikuports source tree 
    161                 if options.tree: 
    162                         print self.packagesPath 
    163                         sys.exit() 
    164                          
    165                 # if requested, scan the ports tree for problems 
    166                 if options.lint: 
    167                         self.checkSourceTree() 
    168                         sys.exit() 
    169                          
    170                 # if there is no argument given, exit 
    171                 if len(args) == 0: 
    172                         print "You need to specifiy at least a port name." 
    173                         print "Invoke '" + sys.argv[0] + " -h' for usage information." 
    174                         sys.exit(1) 
    175                 else: 
    176                         port = args[0] 
    177                          
    178                 # split the argument into a port name and a version 
    179                 reWithVersion = re.compile(regExp['portfullname']) 
    180                 reWithoutVersion = re.compile(regExp['portname'] + '$') 
    181                 if reWithVersion.match(port):           # with version 
    182                         m = reWithVersion.match(port) 
    183                         self.portName = m.group("name") 
    184                         self.portVersion = m.group("version") 
    185                 elif reWithoutVersion.match(port):      # without version 
    186                         m = reWithoutVersion.match(port) 
    187                         self.portName = m.group("name") 
    188                         self.portVersion = None 
    189                 else:                                                           # invalid argument 
    190                         sys.exit("Error: Invalid port name " + port)                                             
     169            self.packagesPath = self.packagesPath[:-1] 
    191170 
    192                 # find the port in the HaikuPorts tree 
    193                 self.portCategory = self.getCategory(self.portName) 
    194                 if self.portCategory == None: 
    195                         sys.exit("Error: Port " + self.portName + " not found.") 
     171        # if requested, list all ports in the HaikuPorts tree 
    196172 
    197                 # create full paths for the directories 
    198                 self.portDir    = self.packagesPath + '/' + self.portCategory + '/' + self.portName 
    199                 self.downloadDir= self.portDir + '/' + paths['download'] 
    200                 self.workDir    = self.portDir + '/' + paths['work'] 
    201                 self.patchesDir = self.portDir + '/' + paths['patches'] 
    202                 self.distroDir  = self.portDir + '/' + paths['distro'] 
     173        if options.list: 
     174            self.searchPorts(None) 
     175            sys.exit() 
    203176 
    204                 # if the port version was not specified, list available versions 
    205                 if self.portVersion == None: 
    206                         versions = [] 
    207                         reBepFile = re.compile(regExp['bepfilename']) 
    208                         dirList = os.listdir(self.portDir) 
    209                         for item in dirList: 
    210                                 m = reBepFile.match(item) 
    211                                 if m: 
    212                                         versions.append([m.group("version"), item]) 
    213                         if len(versions) > 1: 
    214                                 print "Following versions of %s are available:" % (self.portName) 
    215                                 for version in versions: 
    216                                         print "  " + version[0] 
    217                                 sys.exit("Run haikuporter again, specifiying a port version") 
    218                         elif len(versions) == 1: 
    219                                 self.portVersion = versions[0][0] 
    220                         else: 
    221                                 sys.exit("Error: .bep files for " + self.portName +" not found.") 
     177        # if requested, search for a port 
    222178 
    223                 # show port description, if requested 
    224                 if options.about: 
    225                         self.printDescription() 
    226                         sys.exit() 
     179        if options.search: 
     180            if len(args) == 0: 
     181                print 'You need to specifiy a search string.' 
     182                print "Invoke '" + sys.argv[0]\ 
     183                     + " -h' for usage information." 
     184                sys.exit(1) 
    227185 
    228                 # read data from the bep file 
    229                 self.parseBepFile() 
     186            self.searchPorts(args[0]) 
     187            sys.exit() 
    230188 
    231                 # warn when the port is not stable on this platform 
    232                 self.platform = self.detectOS() 
    233                 if self.platform == None: 
    234                         sys.exit("Error: Unknown OS platform!") 
    235                 else: 
    236                         if self.bepKeys['STATUS_' + self.platform] != "stable": 
    237                                 print "Warning: This port is " + self.bepKeys['STATUS_' + self.platform] + " on this platform." 
    238                                 if not self.options.yes: 
    239                                         answer = raw_input("Continue (y/n + enter)? ") 
    240                                         if answer == '': 
    241                                                 sys.exit(1) 
    242                                         if answer[0].lower() == 'y': 
    243                                                 print " ok" 
    244                                         else: 
    245                                                 sys.exit(1) 
     189        # if requested, checkout or update ports tree 
    246190 
    247                 if self.bepKeys['MESSAGE']: 
    248                         print self.bepKeys['MESSAGE'] 
    249                         if not self.options.yes: 
    250                                 answer = raw_input("Continue (y/n + enter)? ") 
    251                                 if answer == '': 
    252                                         sys.exit(1) 
    253                                 if answer[0].lower() == 'y': 
    254                                         print " ok" 
    255                                 else: 
    256                                         sys.exit(1) 
     191        if options.get: 
     192            self.updatePortsTree() 
     193            sys.exit() 
    257194 
    258                 # clean the work dir and don't build when making a source archive 
    259                 if options.archive: 
    260                         options.build = False 
    261                         options.clean = True 
    262                         options.patch = True 
     195        # if requested, print the location of the haikuports source tree 
    263196 
    264                 # clean the work directory, if requested 
    265                 if options.clean: 
    266                         self.cleanWorkDirectory() 
     197        if options.tree: 
     198            print self.packagesPath 
     199            sys.exit() 
    267200 
    268                 # don't build when not patching 
    269                 if not options.patch: 
    270                         options.build = False 
     201        # if requested, scan the ports tree for problems 
    271202 
    272                 self.checkDependencies()        # check dependencies 
    273                 self.downloadSource()   # fetch sources 
    274                 self.checksumSource()   # calculate checksum 
    275                 self.unpackSource()             # unpack source archive 
    276                 if options.patch: 
    277                         self.patchSource()      # apply patches 
    278                 if options.build: 
    279                         self.buildPort()        # build 
    280                 if options.install: 
    281                         self.installPort()      # install 
    282                 if options.distro: 
    283                         self.makePackage()      # distro 
    284                 if options.archive: 
    285                         self.makePatchedArchive()       # create patched source archive 
    286                 if options.test: 
    287                         self.testPort()         # run tests 
     203        if options.lint: 
     204            self.checkSourceTree() 
     205            sys.exit() 
    288206 
    289         def printDescription(self): 
    290                 """Show port description""" 
     207        # if there is no argument given, exit 
    291208 
    292                 bepFilename = self.portDir + '/' + self.portName + '-' + self.portVersion + ".bep" 
     209        if len(args) == 0: 
     210            print 'You need to specifiy at least a port name.' 
     211            print "Invoke '" + sys.argv[0]\ 
     212                 + " -h' for usage information." 
     213            sys.exit(1) 
     214        else: 
     215            port = args[0] 
    293216 
    294                 reOptionValue   = re.compile('^(?P<key>[A-Z0-9_]*)\s*=\s*"(?P<value>.*)(?<!\\\\)"\s*$') 
    295                 reComment               = re.compile('^\s*#.*$') 
    296                 reEmptyLine             = re.compile('^\s*$') 
     217        # split the argument into a port name and a version 
    297218 
    298                 if not os.path.exists(bepFilename): 
    299                         sys.exit("Error: " + self.portName + " version " + self.portVersion + " not found.") 
     219        reWithVersion = re.compile(regExp['portfullname']) 
     220        reWithoutVersion = re.compile(regExp['portname'] + '$') 
     221        if reWithVersion.match(port):  # with version 
     222            m = reWithVersion.match(port) 
     223            self.portName = m.group('name') 
     224            self.portVersion = m.group('version') 
     225        elif reWithoutVersion.match(port): 
    300226 
    301                 fp = open(bepFilename) 
    302                 lineCount = 0 
     227                                              # without version 
    303228 
    304                 line = fp.readline() 
    305                 lineCount += 1 
     229            m = reWithoutVersion.match(port) 
     230            self.portName = m.group('name') 
     231            self.portVersion = None 
     232        else: 
    306233 
    307                 print '*'*80 
     234                                             # invalid argument 
    308235 
    309                 while line != "": 
    310                         # empty line or comment 
    311                         if reEmptyLine.match(line) or reComment.match(line): 
    312                                 pass 
    313                         # value option (single line) (check BEFORE list option) 
    314                         elif reOptionValue.match(line): 
    315                                 m = reOptionValue.match(line) 
    316                                 key = m.group("key") 
    317                                 value = m.group("value") 
    318                                  
    319                                 if 'DESCRIPTION' in key: 
    320                                         print key.capitalize()+":",value 
    321                                 elif 'HOMEPAGE' in key: 
    322                                         print key.capitalize()+":",value 
     236            sys.exit('Error: Invalid port name ' + port) 
    323237 
    324                         line = fp.readline() 
    325                         lineCount += 1 
     238        # find the port in the HaikuPorts tree 
    326239 
    327                 print '*'*80 
     240        self.portCategory = self.getCategory(self.portName) 
     241        if self.portCategory == None: 
     242            sys.exit('Error: Port ' + self.portName + ' not found.') 
    328243 
    329                 fp.close() 
     244        # create full paths for the directories 
    330245 
     246        self.portDir = self.packagesPath + '/' + self.portCategory + '/'\ 
     247             + self.portName 
     248        self.downloadDir = self.portDir + '/' + paths['download'] 
     249        self.workDir = self.portDir + '/' + paths['work'] 
     250        self.patchesDir = self.portDir + '/' + paths['patches'] 
     251        self.distroDir = self.portDir + '/' + paths['distro'] 
    331252 
    332         def setFlag(self, name): 
    333                 open('%s/%s-%s.%s' %(self.workDir, self.portName, self.portVersion, name), 'w').close() 
     253        # if the port version was not specified, list available versions 
    334254 
     255        if self.portVersion == None: 
     256            versions = [] 
     257            reBepFile = re.compile(regExp['bepfilename']) 
     258            dirList = os.listdir(self.portDir) 
     259            for item in dirList: 
     260                m = reBepFile.match(item) 
     261                if m: 
     262                    versions.append([m.group('version'), item]) 
     263            if len(versions) > 1: 
     264                print 'Following versions of %s are available:'\ 
     265                     % self.portName 
     266                for version in versions: 
     267                    print '  ' + version[0] 
     268                sys.exit('Run haikuporter again, specifiying a port version' 
     269                         ) 
     270            elif len(versions) == 1: 
     271                self.portVersion = versions[0][0] 
     272            else: 
     273                sys.exit('Error: .bep files for ' + self.portName 
     274                          + ' not found.') 
    335275 
    336         def checkFlag(self, name): 
    337                 if os.path.exists('%s/%s-%s.%s' %(self.workDir, self.portName, self.portVersion, name)): 
    338                         return True 
    339                 else: 
    340                         return False 
     276        # show port description, if requested 
    341277 
     278        if options.about: 
     279            self.printDescription() 
     280            sys.exit() 
    342281 
    343         def updatePortsTree(self): 
    344                 """Get/Update the port tree via svn""" 
    345                 print "Refreshing the port tree: " + self.packagesPath 
    346                 if os.path.exists(self.packagesPath + "/.svn"): 
    347                         check_call(['svn', 'update', self.packagesPath]) 
    348                 else: 
    349                         check_call(['svn', 'checkout', 'http://ports.haiku-files.org/svn/haikuports/trunk', self.packagesPath]) 
     282        # read data from the bep file 
    350283 
     284        self.parseBepFile() 
    351285 
    352         def validateBepFile(self, bepFilePath, exitOnError=True): 
    353                 """Validate the keys/values in a bep file""" 
    354                 bepConfig = Config(bepFilePath) 
    355                 self.bepKeys = bepConfig.getKeys() 
     286        # warn when the port is not stable on this platform 
    356287 
    357                 # check whether all required fields are present 
    358                 for key in bepDefaults: 
    359                         if key not in self.bepKeys and bepDefaults[key][0]: 
    360                                 print "Error: Required field '" + key + "' not present in " + bepFilePath 
    361                                 if exitOnError: 
    362                                         sys.exit(1) 
     288        self.platform = self.detectOS() 
     289        if self.platform == None: 
     290            sys.exit('Error: Unknown OS platform!') 
     291        else: 
     292            if self.bepKeys['STATUS_' + self.platform] != 'stable': 
     293                print 'Warning: This port is ' + self.bepKeys['STATUS_' 
     294                         + self.platform] + ' on this platform.' 
     295                if not self.options.yes: 
     296                    answer = raw_input('Continue (y/n + enter)? ') 
     297                    if answer == '': 
     298                        sys.exit(1) 
     299                    if answer[0].lower() == 'y': 
     300                        print ' ok' 
     301                    else: 
     302                        sys.exit(1) 
    363303 
    364                 # check validity of BepFile values 
    365                 for key in self.bepKeys: 
    366                         try: 
    367                                 if type(self.bepKeys[key]) not in bepTypes[key]: 
    368                                         print "Error: Invalid value type for " + key + ". Expecting " + str(bepTypes[key]) 
    369                                         if exitOnError: 
    370                                                 sys.exit(1) 
    371                         except KeyError, e: 
    372                                 print "Warning: Unknown key label '" + key 
     304        if self.bepKeys['MESSAGE']: 
     305            print self.bepKeys['MESSAGE'] 
     306            if not self.options.yes: 
     307                answer = raw_input('Continue (y/n + enter)? ') 
     308                if answer == '': 
     309                    sys.exit(1) 
     310                if answer[0].lower() == 'y': 
     311                    print ' ok' 
     312                else: 
     313                    sys.exit(1) 
    373314 
    374                 # Check for a valid license file 
    375                 if 'LICENSE' in self.bepKeys: 
    376                         fileList = [] 
    377                         bepLicense = [] 
    378                         if type(self.bepKeys['LICENSE']) == type(str()): 
    379                                 bepLicense.append(self.bepKeys['LICENSE']) 
    380                         else: 
    381                                 bepLicense = self.bepKeys['LICENSE'] 
    382                         for item in bepLicense: 
    383                                 dirname = FindDirectory('B_SYSTEM_DIRECTORY', 'data/licenses') 
    384                                 for filename in os.listdir(dirname): 
    385                                         fileList.append(filename) 
    386                                 haikuLicenseList = fileList 
    387                                 if item not in fileList: 
    388                                         fileList = [] 
    389                                         dirname = os.path.dirname(bepFilePath) + "/licenses" 
    390                                         print "Try looking in " + dirname 
    391                                         if os.path.exists(dirname): 
    392                                                 for filename in os.listdir(dirname): 
    393                                                         fileList.append(filename) 
    394                                 if item not in fileList: 
    395                                         print "\n######## Error: No match found for License " + item + " ########\n" 
    396                                         print "Valid license filenames included with Haiku are: " 
    397                                         print haikuLicenseList 
    398                                         print "\n" 
    399                                         if exitOnError: 
    400                                                 sys.exit(1) 
    401                                 else: 
    402                                         print "Matching License (" + item + ") found in " + dirname 
    403                                            
    404                 # TODO Disable warnings if an OPD file is found 
    405                 if not 'LICENSE' in self.bepKeys or not self.bepKeys['LICENSE']: 
    406                         print "Warning: No LICENSE found in bep file" 
     315        # clean the work dir and don't build when making a source archive 
    407316 
    408                 if not 'COPYRIGHT' in self.bepKeys or not self.bepKeys['COPYRIGHT']: 
    409                         print "Warning: No COPYRIGHT found in bep file" 
     317        if options.archive: 
     318            options.build = False 
     319            options.clean = True 
     320            options.patch = True 
    410321 
     322        # clean the work directory, if requested 
    411323 
    412         def parseBepFile(self): 
    413                 """Parse the BepFile of the specified port""" 
    414                 bepFilename = self.portDir + '/' + self.portName + "-" + self.portVersion + ".bep" 
    415                 if not os.path.exists(bepFilename): 
    416                         sys.exit("Error: " + self.portName + " version " + self.portVersion + " not found.") 
    417                  
    418                 self.validateBepFile(bepFilename) 
     324        if options.clean: 
     325            self.cleanWorkDirectory() 
    419326 
    420                 # set default values when not provided 
    421                 for key in bepDefaults: 
    422                         if key not in self.bepKeys: 
    423                                 self.bepKeys[key] = bepDefaults[key][1] 
     327        # don't build when not patching 
    424328 
    425                 # convert each None/string value into a list (with respectively 0 or 1 element) 
    426                 #       (simplifies implementation ahead) 
    427                 for key in bepTypes: 
    428                         if types.ListType in bepTypes[key]: 
    429                                 if self.bepKeys[key] == None: 
    430                                         self.bepKeys[key] = [] 
    431                                 elif type(self.bepKeys[key]) == types.StringType: 
    432                                         self.bepKeys[key] = [self.bepKeys[key]] 
     329        if not options.patch: 
     330            options.build = False 
    433331 
    434                 #for key in self.bepKeys: 
    435                 #       print key + " = " + str(self.bepKeys[key]) 
     332        self.checkDependencies()  # check dependencies 
     333        self.downloadSource()  # fetch sources 
     334        self.checksumSource()  # calculate checksum 
     335        self.unpackSource()  # unpack source archive 
     336        if options.patch: 
     337            self.patchSource()  # apply patches 
     338        if options.build: 
     339            self.buildPort()  # build 
     340        if options.install: 
     341            self.installPort()  # install 
     342        if options.distro: 
     343            self.makePackage()  # distro 
     344        if options.archive: 
     345            self.makePatchedArchive()  # create patched source archive 
     346        if options.test: 
     347            self.testPort()  # run tests 
    436348 
     349    def printDescription(self): 
     350        """Show port description""" 
    437351 
    438         def detectOS(self): 
    439                 """Detect the platform we're running on""" 
    440                 name = getCommandOutput('uname -s') 
    441                 if name == "Haiku": 
    442                         return name.upper() 
    443                 # Support for R5, R5bone, dano, and Zeta has been dropped. 
    444                 else: 
    445                         return None 
     352        bepFilename = self.portDir + '/' + self.portName + '-'\ 
     353             + self.portVersion + '.bep' 
    446354 
     355        reOptionValue = \ 
     356            re.compile('^(?P<key>[A-Z0-9_]*)\s*=\s*"(?P<value>.*)(?<!\\\\)"\s*$' 
     357                       ) 
     358        reComment = re.compile('^\s*#.*$') 
     359        reEmptyLine = re.compile('^\s*$') 
    447360 
    448         def searchPorts(self, regExp): 
    449                 """Search for a port in the HaikuPorts tree""" 
    450                 if regExp: 
    451                         reSearch = re.compile(regExp) 
    452                 hierarchy = [] 
    453                 os.chdir(self.packagesPath) 
    454                 dirList = os.listdir(self.packagesPath) 
    455                 for category in dirList: 
    456                         if os.path.isdir(category) and (category[0] != '.'): 
    457                                 subdirList = os.listdir(category) 
    458                                 # remove items starting with '.' 
    459                                 subdirList.sort() 
    460                                 for portName in subdirList: 
    461                                         if portName[0][0] != '.' and (not regExp or reSearch.search(portName)): 
    462                                                 print category + "/" + portName 
     361        if not os.path.exists(bepFilename): 
     362            sys.exit('Error: ' + self.portName + ' version ' 
     363                      + self.portVersion + ' not found.') 
    463364 
     365        fp = open(bepFilename) 
     366        lineCount = 0 
    464367 
    465         def getCategory(self, portName): 
    466                 """Find location of the specified port in the HaikuPorts tree""" 
    467                 hierarchy = [] 
    468                 os.chdir(self.packagesPath) 
    469                 dirList = os.listdir(self.packagesPath) 
    470                 for item in dirList: 
    471                         if os.path.isdir(item) and (item[0] != '.'): 
    472                                 subdirList = os.listdir(item) 
    473                                 # remove items starting with '.' 
    474                                 subdirList.sort() 
    475                                 while subdirList[0][0] == '.': 
    476                                         del subdirList[0] 
    477                                 # locate port 
    478                                 try: 
    479                                         if subdirList.index(portName) >= 0: 
    480                                                 # port was found in the category specified by 'item' 
    481                                                 return item 
    482                                 except ValueError: 
    483                                         pass 
    484                                 hierarchy.append([item, subdirList]) 
    485                 return None 
     368        line = fp.readline() 
     369        lineCount += 1 
    486370 
     371        print '*' * 80 
    487372 
    488         def cleanWorkDirectory(self): 
    489                 """Clean the working directory""" 
    490                 if os.path.exists(self.workDir): 
    491                         print "Cleaning work directory..." 
    492                         shutil.rmtree(self.workDir) 
     373        while line != '': 
    493374 
     375            # empty line or comment 
    494376 
    495         def checkDependencies(self): 
    496                 """Print the list of ports this one depends on""" 
    497                 if self.bepKeys['DEPEND'] == []: 
    498                         print "No dependencies" 
    499                         return 
    500                 print "This port depends on the following ports:" 
    501                 for item in self.bepKeys['DEPEND']: 
    502                         print "  " + item 
    503                 print "Please verify that you have these installed.", 
    504                 if not self.options.yes: 
    505                         answer = raw_input("Continue (y/n + enter)? ") 
    506                         if answer == '': 
    507                                 sys.exit(1) 
    508                         if answer[0].lower() == 'y': 
    509                                 print " ok" 
    510                         else: 
    511                                 sys.exit(1) 
     377            if reEmptyLine.match(line) or reComment.match(line): 
     378                pass 
     379            elif reOptionValue.match(line): 
    512380 
     381            # value option (single line) (check BEFORE list option) 
    513382 
    514         def downloadSource(self): 
    515                 """Fetch the source archive""" 
     383                m = reOptionValue.match(line) 
     384                key = m.group('key') 
     385                value = m.group('value') 
    516386 
    517                 for src_uri in self.bepKeys['SRC_URI']: 
    518                         # Examine the uri to determine if we need to perform a checkout instead of download 
    519                         if re.match('^cvs.*$|^svn.*$|^hg.*$|^git.*$|^bzr.*$', src_uri): 
    520                                 self.checkoutSource(src_uri) 
    521                                 return 
     387                if 'DESCRIPTION' in key: 
     388                    print key.capitalize() + ':', value 
     389                elif 'HOMEPAGE' in key: 
     390                    print key.capitalize() + ':', value 
    522391 
    523                         try: 
    524                                 # Need to make a request to get the actual uri in case it is an http redirect 
    525                                 uri_request = urllib2.urlopen(src_uri) 
    526                                 src_uri = uri_request.geturl() 
     392            line = fp.readline() 
     393            lineCount += 1 
    527394 
    528                                 self.src_local = src_uri[src_uri.rindex('/') + 1:] 
    529                                 fp = self.downloadDir + '/' + self.src_local 
    530                                 if os.path.isfile(fp): 
    531                                         print 'File already exists: ' + fp + '\nSkipping download ...' 
    532                                         return 
    533                                 else: 
    534                                         # create download dir and cd into it 
    535                                         if not os.path.exists(self.downloadDir): 
    536                                                 os.mkdir(self.downloadDir) 
     395        print '*' * 80 
    537396 
    538                                         os.chdir(self.downloadDir) 
     397        fp.close() 
    539398 
    540                                         print "\nDownloading: " + src_uri 
    541                                         check_call(['wget', '-c', '--tries=3', src_uri]) 
    542                                         # succesfully downloaded source archive 
    543                                         return 
    544                         except: 
    545                                 print "Warning: download error, trying next location." 
     399    def setFlag(self, name): 
     400        open('%s/%s-%s.%s' % (self.workDir, self.portName, 
     401             self.portVersion, name), 'w').close() 
    546402 
    547                 # failed to fetch source 
    548                 sys.exit("Error: Failed to download source package from all locations.") 
     403    def checkFlag(self, name): 
     404        if os.path.exists('%s/%s-%s.%s' % (self.workDir, self.portName, 
     405                          self.portVersion, name)): 
     406            return True 
     407        else: 
     408            return False 
    549409 
     410    def updatePortsTree(self): 
     411        """Get/Update the port tree via svn""" 
    550412 
    551         def checkoutSource(self, uri): 
    552                 """Parse the uri and execute the appropriate command to check the source out""" 
     413        print 'Refreshing the port tree: ' + self.packagesPath 
     414        if os.path.exists(self.packagesPath + '/.svn'): 
     415            check_call(['svn', 'update', self.packagesPath]) 
     416        else: 
     417            check_call(['svn', 'checkout', 
     418                       'http://ports.haiku-files.org/svn/haikuports/trunk' \ 
     419                       , self.packagesPath]) 
    553420 
    554                 if self.checkFlag('checkout') and not self.options.force: 
    555                         print "Source already checked out. Skipping ..." 
    556                         return 
     421    def validateBepFile(self, bepFilePath, exitOnError=True): 
     422        """Validate the keys/values in a bep file""" 
    557423 
    558                 # If the work dir exists we need to clean it out 
    559                 if os.path.exists(self.workDir): 
    560                         shutil.rmtree(self.workDir) 
     424        bepConfig = Config(bepFilePath) 
     425        self.bepKeys = bepConfig.getKeys() 
    561426 
    562                 print "Source checkout: " + uri 
     427        # check whether all required fields are present 
    563428 
    564                 # Attempt to parse a uri with a + in it. ex: hg+http://blah 
    565                 # Even if it doesn't find the 'type' it should extract 'real_uri' and 'rev' 
    566                 m = re.match('^((?P<type>\w*)\+)?(?P<real_uri>.+?)(#(?P<rev>.+))?$', uri) 
    567                 if not m or not m.group("real_uri"): 
    568                         sys.exit("Error: Couldn't parse repository URI") 
     429        for key in bepDefaults: 
     430            if key not in self.bepKeys and bepDefaults[key][0]: 
     431                print "Error: Required field '" + key\ 
     432                     + "' not present in " + bepFilePath 
     433                if exitOnError: 
     434                    sys.exit(1) 
    569435 
    570                 type = m.group("type") 
    571                 real_uri = m.group("real_uri") 
    572                 rev = m.group("rev") 
     436        # check validity of BepFile values 
    573437 
    574                 # Attempt to parse a uri without a + in it. ex: svn://blah 
    575                 # TODO improve the regex above to fallback to this pattern 
    576                 if not type: 
    577                         m = re.match("^(\w*).*$", real_uri) 
    578                         if m: 
    579                                 type = m.group(1) 
     438        for key in self.bepKeys: 
     439            try: 
     440                if type(self.bepKeys[key]) not in bepTypes[key]: 
     441                    print 'Error: Invalid value type for ' + key\ 
     442                         + '. Expecting ' + str(bepTypes[key]) 
     443                    if exitOnError: 
     444                        sys.exit(1) 
     445            except KeyError, e: 
     446                print "Warning: Unknown key label '" + key 
    580447 
    581                 if not type: 
    582                         sys.exit("Error: Couldn't determine repository type") 
     448        # Check for a valid license file 
    583449 
    584                 # Set the name of the directory to check out sources into 
    585                 checkoutDir = self.portName + "-" + self.portVersion 
     450        if 'LICENSE' in self.bepKeys: 
     451            fileList = [] 
     452            bepLicense = [] 
     453            if type(self.bepKeys['LICENSE']) == type(str()): 
     454                bepLicense.append(self.bepKeys['LICENSE']) 
     455            else: 
     456                bepLicense = self.bepKeys['LICENSE'] 
     457            for item in bepLicense: 
     458                dirname = FindDirectory('B_SYSTEM_DIRECTORY', 
     459                        'data/licenses') 
     460                for filename in os.listdir(dirname): 
     461                    fileList.append(filename) 
     462                haikuLicenseList = fileList 
     463                if item not in fileList: 
     464                    fileList = [] 
     465                    dirname = os.path.dirname(bepFilePath) + '/licenses' 
     466                    print 'Try looking in ' + dirname 
     467                    if os.path.exists(dirname): 
     468                        for filename in os.listdir(dirname): 
     469                            fileList.append(filename) 
     470                if item not in fileList: 
     471                    print '\n######## Error: No match found for License '\ 
     472                         + item + ' ########\n' 
     473                    print 'Valid license filenames included with Haiku are: ' 
     474                    print haikuLicenseList 
     475                    print '\n' 
     476                    if exitOnError: 
     477                        sys.exit(1) 
     478                else: 
     479                    print 'Matching License (' + item + ') found in '\ 
     480                         + dirname 
    586481 
    587                 # Start building the command to perform the checkout 
    588                 if type == 'cvs': 
    589                         # Chop off the leading cvs:// part of the uri 
    590                         real_uri = real_uri[real_uri.index("cvs://") + 6:] 
    591                         # Extract the cvs module from the uri and remove it from real_uri 
    592                         module = real_uri[real_uri.rfind('/') + 1:] 
    593                         real_uri = real_uri[:real_uri.rfind('/')] 
    594                         checkoutCommand = "cvs -d" + real_uri + " co -P" 
    595                         if rev: 
    596                                 # For CVS 'rev' specifies a date 
    597                                 checkoutCommand += " -D" + rev 
    598                         checkoutCommand += " -d " + checkoutDir + " " + module 
     482        # TODO Disable warnings if an OPD file is found 
    599483 
    600                 elif type == 'svn': 
    601                         checkoutCommand = "svn co --non-interactive --trust-server-cert" 
    602                         if rev: 
    603                                 checkoutCommand += " -r " + rev 
    604                         checkoutCommand += " " + real_uri + " " + checkoutDir 
     484        if not 'LICENSE' in self.bepKeys or not self.bepKeys['LICENSE']: 
     485            print 'Warning: No LICENSE found in bep file' 
    605486 
    606                 elif type == 'hg': 
    607                         checkoutCommand = "hg clone" 
    608                         if rev: 
    609                                 checkoutCommand += " -r " + rev 
    610                         checkoutCommand += " " + real_uri + " " + checkoutDir 
     487        if not 'COPYRIGHT' in self.bepKeys\ 
     488             or not self.bepKeys['COPYRIGHT']: 
     489            print 'Warning: No COPYRIGHT found in bep file' 
    611490 
    612                 elif type == 'bzr': 
    613                         #http://doc.bazaar.canonical.com/bzr-0.10/bzr_man.htm#bzr-branch-from-location-to-location 
    614                         checkoutCommand = "bzr checkout --lightweight" 
    615                         if rev: 
    616                                 checkoutCommand += " -r " + rev 
    617                         checkoutCommand += " " + real_uri + " " + checkoutDir    
     491    def parseBepFile(self): 
     492        """Parse the BepFile of the specified port""" 
    618493 
    619                 else: 
    620                         #TODO Skip the initial checkout if a rev is specified? 
    621                         checkoutCommand = "git clone " + real_uri + " " + checkoutDir 
    622                         if rev: 
    623                                 checkoutCommand += " && cd " + checkoutDir + " && git checkout " + rev 
     494        bepFilename = self.portDir + '/' + self.portName + '-'\ 
     495             + self.portVersion + '.bep' 
     496        if not os.path.exists(bepFilename): 
     497            sys.exit('Error: ' + self.portName + ' version ' 
     498                      + self.portVersion + ' not found.') 
    624499 
    625                 # create the work dir 
    626                 if not os.path.exists(self.workDir): 
    627                         os.mkdir(self.workDir) 
     500        self.validateBepFile(bepFilename) 
    628501 
    629                 try: 
    630                         check_call(checkoutCommand, shell=True, cwd=self.workDir) 
    631                 except (OSError, CalledProcessError), e: 
    632                         if self.prompt_installer(checkoutCommand.split()[0]): 
    633                                 check_call(checkoutCommand, shell=True, cwd=self.workDir) 
    634                         else: 
    635                                 sys.exit() 
    636                  
    637                 # Set the 'checkout' flag to signal that the checkout is complete 
    638                 # This also tells haikuporter not to attempt an unpack step 
    639                 self.setFlag('checkout') 
     502        # set default values when not provided 
    640503 
     504        for key in bepDefaults: 
     505            if key not in self.bepKeys: 
     506                self.bepKeys[key] = bepDefaults[key][1] 
    641507 
    642         def checksumSource(self):  
    643                 if self.bepKeys['CHECKSUM_MD5']: 
    644                         sys.stdout.write('Calculating checksum -') 
    645                         sys.stdout.flush() 
    646                         h = hashlib.md5() 
    647                         f = open(self.downloadDir + "/" + self.src_local, 'rb') 
    648                         while True: 
    649                                 d = f.read(16384) 
    650                                 if not d: 
    651                                         break 
    652                                 h.update(d) 
    653                         f.close() 
    654                         if h.hexdigest() == self.bepKeys['CHECKSUM_MD5']: 
    655                                 sys.stdout.write(' OK\n') 
    656                         else: 
    657                                 sys.stdout.write(' FAILED\n') 
    658                                 sys.exit(1) 
    659                         sys.stdout.flush() 
    660                 else: 
    661                         # The checkout flag only gets set when a source checkout is performed 
    662                         # If it exists we don't need to warn about the missing bep field 
    663                         if not self.checkFlag('checkout'): 
    664                                 print "Warning: CHECKSUM_MD5 key not found in bep file." 
     508        # convert each None/string value into a list 
     509        # (with respectively 0 or 1 element) 
     510        # ....(simplifies implementation ahead) 
    665511 
     512        for key in bepTypes: 
     513            if types.ListType in bepTypes[key]: 
     514                if self.bepKeys[key] == None: 
     515                    self.bepKeys[key] = [] 
     516                elif type(self.bepKeys[key]) == types.StringType: 
     517                    self.bepKeys[key] = [self.bepKeys[key]] 
    666518 
    667         def unpackSource(self): 
    668                 """Unpack the source archive (into the work directory)""" 
     519        # for key in self.bepKeys: 
     520        # ....print key + " = " + str(self.bepKeys[key]) 
    669521 
    670                 # If the source came from a vcs there is no unpack step 
    671                 if self.checkFlag('checkout'): 
    672                         return 
     522    def detectOS(self): 
     523        """Detect the platform we're running on""" 
    673524 
    674                 # create work dir 
    675                 if not os.path.exists(self.workDir): 
    676                         os.mkdir(self.workDir) 
     525        name = getCommandOutput('uname -s') 
     526        if name == 'Haiku': 
     527            return name.upper() 
     528        else: 
    677529 
    678                 # Check to see if the source archive was already unpacked. 
    679                 if self.checkFlag('unpack') and not self.options.force: 
    680                         return 
     530        # Support for R5, R5bone, dano, and Zeta has been dropped. 
    681531 
    682                 # unpack source archive 
    683                 print "Unpacking " + self.src_local 
    684                 archiveFullPath = self.downloadDir + "/" + self.src_local 
    685                 if tarfile.is_tarfile(archiveFullPath):  
    686                         tf = tarfile.open(self.downloadDir + "/" + self.src_local, 'r') 
    687                         tf.extractall(self.workDir) 
    688                         tf.close() 
    689                 elif zipfile.is_zipfile(archiveFullPath): 
    690                         zf = zipfile.ZipFile(self.downloadDir + "/" + self.src_local, 'r') 
    691                         zf.extractall(self.workDir) 
    692                         zf.close() 
    693                 elif archiveFullPath.split("/")[-1].split(".")[-1] == "xz": 
    694                         try: 
    695                                 Popen(['xz', '-d', '-k', archiveFullPath]) 
    696                         except (OSError, CalledProcessError), e: 
    697                                 #run the installoptionalsoftware prompt 
    698                                 if self.prompt_installer('xz'): 
    699                                         Popen(['xz', '-d', '-k', archiveFullPath]) 
    700                                 else: 
    701                                         sys.exit() 
    702                         tar = archiveFullPath[:-3] 
    703                         if tarfile.is_tarfile(tar):  
    704                                 tf = tarfile.open(tar, 'r') 
    705                                 tf.extractall(self.workDir) 
    706                                 tf.close() 
    707                 else: 
    708                         sys.exit("Error: Unrecognized archive type.") 
     532            return None 
    709533 
    710                 self.setFlag('unpack')   
     534    def searchPorts(self, regExp): 
     535        """Search for a port in the HaikuPorts tree""" 
    711536 
     537        if regExp: 
     538            reSearch = re.compile(regExp) 
     539        hierarchy = [] 
     540        os.chdir(self.packagesPath) 
     541        dirList = os.listdir(self.packagesPath) 
     542        for category in dirList: 
     543            if os.path.isdir(category) and category[0] != '.': 
     544                subdirList = os.listdir(category) 
    712545 
    713         def patchSource(self): 
    714                 """Apply the Haiku patches to the source directory""" 
     546                # remove items starting with '.' 
    715547 
    716                 # Check to see if the patch was already applied to the source. 
    717                 if self.checkFlag('patch') and not self.options.force: 
    718                         return 
     548                subdirList.sort() 
     549                for portName in subdirList: 
     550                    if portName[0][0] != '.' and (not regExp 
     551                             or reSearch.search(portName)): 
     552                        print category + '/' + portName 
    719553 
    720                 patchFilePath = self.patchesDir + '/' + self.portName + '-' + self.portVersion + '.patch' 
    721                 if os.path.exists(patchFilePath): 
    722                         patchOptions = "" 
    723                         if 'PATCH_OPTIONS' in self.confKeys: 
    724                                 patchOptions += self.confKeys['PATCH_OPTIONS'] 
    725                         check_call('patch -p0 ' + patchOptions + ' -i ' + patchFilePath, shell=True, cwd=self.workDir) 
    726                 else: 
    727                         print "No patching required" 
    728                 self.setFlag('patch') 
     554    def getCategory(self, portName): 
     555        """Find location of the specified port in the HaikuPorts tree""" 
    729556 
     557        hierarchy = [] 
     558        os.chdir(self.packagesPath) 
     559        dirList = os.listdir(self.packagesPath) 
     560        for item in dirList: 
     561            if os.path.isdir(item) and item[0] != '.': 
     562                subdirList = os.listdir(item) 
    730563 
    731         def buildPort(self): 
    732                 """Build the sources""" 
     564                # remove items starting with '.' 
    733565 
    734                 # Make sure the bep file for the package has a BUILD section. 
    735                 if (not 'BUILD' in self.bepKeys) or (not self.bepKeys['BUILD']): 
    736                         sys.exit('Error: Invalid bep file with no BUILD section.') 
     566                subdirList.sort() 
     567                while subdirList[0][0] == '.': 
     568                    del subdirList[0] 
    737569 
    738                 # Check to see if a previous build was already done. 
    739                 if self.checkFlag('build') and not self.options.force: 
    740                         return 
     570                # locate port 
    741571 
    742                 self.runCommandSequence(self.bepKeys['BUILD'], self.workDir) 
     572                try: 
     573                    if subdirList.index(portName) >= 0: 
    743574 
    744                 self.setFlag('build') 
     575                        # port was found in the category specified by 'item' 
    745576 
     577                        return item 
     578                except ValueError: 
     579                    pass 
     580                hierarchy.append([item, subdirList]) 
     581        return None 
    746582 
    747         def installPort(self): 
    748                 """Install the binaries onto the system""" 
     583    def cleanWorkDirectory(self): 
     584        """Clean the working directory""" 
    749585 
    750                 # Make sure the bep file for the package has an INSTALL section. 
    751                 if (not 'INSTALL' in self.bepKeys) or (not self.bepKeys['INSTALL']): 
    752                         sys.exit('Error: bep file has no INSTALL section.') 
     586        if os.path.exists(self.workDir): 
     587            print 'Cleaning work directory...' 
     588            shutil.rmtree(self.workDir) 
    753589 
    754                 # Check if port is python or perl as installing them with haikuporter may break haikuporter 
    755                 if ((self.portName == "python") or (self.portName == "perl")): 
    756                         sys.exit('Error: cannot install ' + self.portName + ' using haikuporter.  Build to a package instead.') 
    757                 print "Installing ..." 
     590    def checkDependencies(self): 
     591        """Print the list of ports this one depends on""" 
    758592 
    759                 self.runCommandSequence(self.bepKeys['INSTALL'], self.workDir) 
     593        if self.bepKeys['DEPEND'] == []: 
     594            print 'No dependencies' 
     595            return 
     596        print 'This port depends on the following ports:' 
     597        for item in self.bepKeys['DEPEND']: 
     598            print '  ' + item 
     599        print 'Please verify that you have these installed.', 
     600        if not self.options.yes: 
     601            answer = raw_input('Continue (y/n + enter)? ') 
     602            if answer == '': 
     603                sys.exit(1) 
     604            if answer[0].lower() == 'y': 
     605                print ' ok' 
     606            else: 
     607                sys.exit(1) 
    760608 
     609    def downloadSource(self): 
     610        """Fetch the source archive""" 
    761611 
    762         def testPort(self): 
    763                 """Test the resulting binaries""" 
     612        for src_uri in self.bepKeys['SRC_URI']: 
    764613 
    765                 # Make sure the bep file for the package has a TEST section. 
    766                 if (not 'TEST' in self.bepKeys) or (not self.bepKeys['TEST']): 
    767                         sys.exit('Error: bep file has no TEST section.') 
     614            # Examine the uri to determine if we need to perform a checkout instead of download 
    768615 
    769                 print "Testing ..." 
     616            if re.match('^cvs.*$|^svn.*$|^hg.*$|^git.*$|^bzr.*$', 
     617                        src_uri): 
     618                self.checkoutSource(src_uri) 
     619                return 
    770620 
    771                 self.runCommandSequence(self.bepKeys['TEST'], self.workDir) 
     621            try: 
    772622 
     623                # Need to make a request to get the actual uri in case it is an http redirect 
    773624 
    774         def generatePackageDescription(self, opdFile): 
    775                 """Create an OptionalPackageDescription file for inclusion in a package""" 
     625                uri_request = urllib2.urlopen(src_uri) 
     626                src_uri = uri_request.geturl() 
    776627 
    777                 print "Generating OptionalPackageDescription file ..." 
     628                self.src_local = src_uri[src_uri.rindex('/') + 1:] 
     629                fp = self.downloadDir + '/' + self.src_local 
     630                if os.path.isfile(fp): 
     631                    print 'File already exists: ' + fp\ 
     632                         + '\nSkipping download ...' 
     633                    return 
     634                else: 
    778635 
    779                 opd = open(opdFile, 'w') 
     636                    # create download dir and cd into it 
    780637 
    781                 opd.write('Package:\t' + self.portName + '\n'); 
    782                 opd.write('Version:\t' + self.portVersion + '\n') 
    783                 opd.write('URL:\t\t' + self.bepKeys['HOMEPAGE'] + '\n') 
     638                    if not os.path.exists(self.downloadDir): 
     639                        os.mkdir(self.downloadDir) 
    784640 
    785                 # These keys aren't mandatory so we need to check if they exist 
    786                 if self.bepKeys['LICENSE']: 
    787                         for license in self.bepKeys['LICENSE']: 
    788                                 opd.write('License:\t' + license + '\n') 
     641                    os.chdir(self.downloadDir) 
    789642 
    790                 if self.bepKeys['COPYRIGHT']: 
    791                         for copyright in self.bepKeys['COPYRIGHT']: 
    792                                 opd.write('Copyright:\t' + copyright + '\n') 
     643                    print '\nDownloading: ' + src_uri 
     644                    check_call(['wget', '-c', '--tries=3', src_uri]) 
    793645 
    794                 opd.close() 
     646                    # succesfully downloaded source archive 
    795647 
     648                    return 
     649            except: 
     650                print 'Warning: download error, trying next location.' 
    796651 
    797         def makePackage(self): 
    798                 """Create a package suitable for distribution""" 
     652        # failed to fetch source 
    799653 
    800                 print "Creating distribution package ..." 
     654        sys.exit('Error: Failed to download source package from all locations.' 
     655                 ) 
    801656 
    802                 # Make sure the bep file for the package has an INSTALL section. 
    803                 if (not 'INSTALL' in self.bepKeys) or (not self.bepKeys['INSTALL']): 
    804                         sys.exit('Error: bep file has no INSTALL section.') 
     657    def checkoutSource(self, uri): 
     658        """Parse the uri and execute the appropriate command to check the source out""" 
    805659 
    806                 # if the distro dir still exists from a previous run then remove it 
    807                 shutil.rmtree(self.distroDir, True) 
     660        if self.checkFlag('checkout') and not self.options.force: 
     661            print 'Source already checked out. Skipping ...' 
     662            return 
    808663 
    809                 # create distro dir 
    810                 if not os.path.exists(self.distroDir): 
    811                         os.mkdir(self.distroDir) 
     664        # If the work dir exists we need to clean it out 
    812665 
    813                 self.runCommandSequence(self.bepKeys['INSTALL'], self.workDir, True) 
     666        if os.path.exists(self.workDir): 
     667            shutil.rmtree(self.workDir) 
    814668 
    815                 if not os.path.exists(self.distroDir + '/boot'): 
    816                         sys.exit('Error: No installed files detected in packaging directory. Check bep file for correctness') 
     669        print 'Source checkout: ' + uri 
    817670 
    818                 opdFound = False 
    819                 for f in os.listdir(self.portDir): 
    820                         if 'licenses' in f: 
    821                                 shutil.copytree(self.portDir + '/' + f, self.distroDir + '/boot/common/data/licenses') 
     671        # Attempt to parse a uri with a + in it. ex: hg+http://blah 
     672        # Even if it doesn't find the 'type' it should extract 'real_uri' and 'rev' 
    822673 
    823                         if 'OptionalPackageDescription' in f: 
    824                                 opdFound = True 
    825                                 print "Copying OptionalPackageDescription to distro directory ..." 
    826                                 shutil.copyfile(self.portDir + '/' + f, self.distroDir + '/boot/.OptionalPackageDescription') 
     674        m = \ 
     675            re.match('^((?P<type>\w*)\+)?(?P<real_uri>.+?)(#(?P<rev>.+))?$' 
     676                     , uri) 
     677        if not m or not m.group('real_uri'): 
     678            sys.exit("Error: Couldn't parse repository URI") 
    827679 
    828                 if not opdFound: 
    829                         self.generatePackageDescription(self.distroDir + '/boot/.OptionalPackageDescription') 
     680        type = m.group('type') 
     681        real_uri = m.group('real_uri') 
     682        rev = m.group('rev') 
    830683 
    831                 # go to distro dir for making zip package 
    832                 os.chdir(self.distroDir) 
     684        # Attempt to parse a uri without a + in it. ex: svn://blah 
     685        # TODO improve the regex above to fallback to this pattern 
    833686 
    834                 package = self.portName + '-' + self.portVersion 
    835                  
    836                 # set haikuversion to HAIKUVERSION, if it is empty then don't add a dash. 
    837                 haikuversion = '' 
    838                 if os.environ.has_key('HAIKUVERSION'): 
    839                         haikuversion = '-' + os.environ['HAIKUVERSION'] 
     687        if not type: 
     688            m = re.match("^(\w*).*$", real_uri) 
     689            if m: 
     690                type = m.group(1) 
    840691 
    841                 gcc = getCommandOutput('setgcc') 
    842                 gcc = gcc.split(': ')[1].split('/') 
    843                 arch = '-' + gcc[0] 
    844                 gcc = '-' + gcc[1] 
     692        if not type: 
     693            sys.exit("Error: Couldn't determine repository type") 
    845694 
    846                 date = time.strftime("-%Y-%m-%d", time.localtime()) 
     695        # Set the name of the directory to check out sources into 
    847696 
    848                 zipFile = self.portDir + '/' + package + haikuversion + arch + gcc + date + '.zip' 
     697        checkoutDir = self.portName + '-' + self.portVersion 
    849698 
    850                 zipRootDir = self.distroDir + '/boot' 
     699        # Start building the command to perform the checkout 
    851700 
    852                 packageFiles = '' 
     701        if type == 'cvs': 
    853702 
    854                 # Add the files/dirs in self.distroDir/boot to the list of items to zip 
    855                 for f in os.listdir(zipRootDir): 
    856                         packageFiles += f + ' ' 
     703            # Chop off the leading cvs:// part of the uri 
    857704 
    858                 # Zip the package and save it in the root of the port dir 
    859                 check_call('zip -9ry ' + zipFile + ' ' + packageFiles + ' -x *.svn*', shell=True, cwd=zipRootDir) 
     705            real_uri = real_uri[real_uri.index('cvs://') + 6:] 
    860706 
    861                 # Clean up after ourselves 
    862                 shutil.rmtree(self.distroDir) 
     707            # Extract the cvs module from the uri and remove it from real_uri 
    863708 
    864                 print 'Package saved to: ' + zipFile 
     709            module = real_uri[real_uri.rfind('/') + 1:] 
     710            real_uri = real_uri[:real_uri.rfind('/')] 
     711            checkoutCommand = 'cvs -d' + real_uri + ' co -P' 
     712            if rev: 
    865713 
     714                # For CVS 'rev' specifies a date 
    866715 
    867         def makePatchedArchive(self): 
    868                 """Create a patched source archive""" 
     716                checkoutCommand += ' -D' + rev 
     717            checkoutCommand += ' -d ' + checkoutDir + ' ' + module 
     718        elif type == 'svn': 
    869719 
    870                 print "Creating patched source archive ..." 
     720            checkoutCommand = \ 
     721                'svn co --non-interactive --trust-server-cert' 
     722            if rev: 
     723                checkoutCommand += ' -r ' + rev 
     724            checkoutCommand += ' ' + real_uri + ' ' + checkoutDir 
     725        elif type == 'hg': 
    871726 
    872                 # Set the path and filename for the archive. 
    873                 date = time.strftime("-%Y-%m-%d", time.localtime()) 
    874                 archiveFile = self.portDir + '/' + self.portName + '-' + self.portVersion + '_haiku' + date + '.tar.xz' 
     727            checkoutCommand = 'hg clone' 
     728            if rev: 
     729                checkoutCommand += ' -r ' + rev 
     730            checkoutCommand += ' ' + real_uri + ' ' + checkoutDir 
     731        elif type == 'bzr': 
    875732 
    876                 sourceFiles = "" 
     733            # http://doc.bazaar.canonical.com/bzr-0.10/bzr_man.htm#bzr-branch-from-location-to-location 
    877734 
    878                 # Build the list of dirs to archive. 
    879                 # Since we don't know the name we have to iterate over the dir. 
    880                 for f in os.listdir(self.workDir): 
    881                         if os.path.isdir(self.workDir + '/' + f): 
    882                                 sourceFiles += ' ' + f 
     735            checkoutCommand = 'bzr checkout --lightweight' 
     736            if rev: 
     737                checkoutCommand += ' -r ' + rev 
     738            checkoutCommand += ' ' + real_uri + ' ' + checkoutDir 
     739        else: 
    883740 
    884                 # Make sure we found something to archive 
    885                 if not sourceFiles: 
    886                         sys.exit("Error: No source directories found in work dir.") 
     741            # TODO Skip the initial checkout if a rev is specified? 
    887742 
    888                 # Archive the package and save it in the root of the port dir. 
    889                 check_call('tar cJvf ' + archiveFile + ' ' + sourceFiles, shell=True, cwd=self.workDir) 
     743            checkoutCommand = 'git clone ' + real_uri + ' '\ 
     744                 + checkoutDir 
     745            if rev: 
     746                checkoutCommand += ' && cd ' + checkoutDir\ 
     747                     + ' && git checkout ' + rev 
    890748 
    891                 # Clean up after ourselves 
    892                 shutil.rmtree(self.workDir) 
     749        # create the work dir 
    893750 
    894                 print 'Archive saved to: ' + archiveFile 
     751        if not os.path.exists(self.workDir): 
     752            os.mkdir(self.workDir) 
    895753 
    896         def prompt_installer(self, name): 
    897                 apps = {'xz': 'XZ-Utils', 'git': 'Git', 'hg': 'mercurial', 
    898                 'cvs': 'cvs', 'bzr' : 'bazaar'} #map command to package 
    899                 if self.options.yes: 
    900                         check_call('installoptionalpackage' + " " + str(apps[name]), shell=True) 
    901                         return True 
    902                 response =  raw_input("Do you want to install %s? [Y/n]" % name) 
    903                 if response in ["y", "Y", "\n", "yes", '']: 
    904                         check_call('installoptionalpackage' + " " + str(apps[name]), shell=True) 
    905                         return True 
    906                 else: 
    907                         print "In order to install this package you need to run:" 
    908                         print "installoptionalpackage %s" % apps[name] 
    909                         print "later or let Haikuporter install it for you." 
    910                         return False 
     754        try: 
     755            check_call(checkoutCommand, shell=True, cwd=self.workDir) 
     756        except (OSError, CalledProcessError), e: 
     757            if self.prompt_installer(checkoutCommand.split()[0]): 
     758                check_call(checkoutCommand, shell=True, 
     759                           cwd=self.workDir) 
     760            else: 
     761                sys.exit() 
    911762 
    912         def runCommandSequence(self, rawCommandList, dir, packageMode=False): 
    913                 """Run a sequence of shell commands from a bep file""" 
     763        # Set the 'checkout' flag to signal that the checkout is complete 
     764        # This also tells haikuporter not to attempt an unpack step 
    914765 
    915                 # Convert rawCommandList to a string with a newline after each entry 
    916                 # Use 'set -e' as the first command so that the shell aborts immediately 
    917                 commandString = 'set -e\n' 
    918                 for command in rawCommandList: 
    919                         commandString += command + '\n' 
     766        self.setFlag('checkout') 
    920767 
    921                 shellEnv = None 
     768    def checksumSource(self): 
     769        if self.bepKeys['CHECKSUM_MD5']: 
     770            sys.stdout.write('Calculating checksum -') 
     771            sys.stdout.flush() 
     772            h = hashlib.md5() 
     773            f = open(self.downloadDir + '/' + self.src_local, 'rb') 
     774            while True: 
     775                d = f.read(16384) 
     776                if not d: 
     777                    break 
     778                h.update(d) 
     779            f.close() 
     780            if h.hexdigest() == self.bepKeys['CHECKSUM_MD5']: 
     781                sys.stdout.write(' OK\n') 
     782            else: 
     783                sys.stdout.write(' FAILED\n') 
     784                sys.exit(1) 
     785            sys.stdout.flush() 
     786        else: 
    922787 
    923                 if packageMode: 
    924                         shellEnv = os.environ 
    925                         shellEnv['DESTDIR'] = self.distroDir 
     788            # The checkout flag only gets set when a source checkout is performed 
     789            # If it exists we don't need to warn about the missing bep field 
    926790 
    927                 check_call(commandString, shell=True, cwd=dir, env=shellEnv) 
     791            if not self.checkFlag('checkout'): 
     792                print 'Warning: CHECKSUM_MD5 key not found in bep file.' 
    928793 
     794    def unpackSource(self): 
     795        """Unpack the source archive (into the work directory)""" 
    929796 
    930         def checkSourceTree(self): 
    931                 print "Checking HaikuPorts tree at: " + self.packagesPath 
     797        # If the source came from a vcs there is no unpack step 
    932798 
    933                 for category in os.listdir(self.packagesPath): 
    934                         categoryFullPath = self.packagesPath + '/' + category 
     799        if self.checkFlag('checkout'): 
     800            return 
    935801 
    936                         if os.path.isdir(categoryFullPath) and (category[0] != '.'): 
    937                                 print "Category: " + category 
     802        # create work dir 
    938803 
    939                                 for port in os.listdir(categoryFullPath): 
    940                                         portFullPath = categoryFullPath + '/' + port 
     804        if not os.path.exists(self.workDir): 
     805            os.mkdir(self.workDir) 
    941806 
    942                                         if os.path.isdir(portFullPath) and (port[0] != '.'): 
    943                                                 print "\tPort: " + port 
     807        # Check to see if the source archive was already unpacked. 
    944808 
    945                                                 for bep in os.listdir(portFullPath): 
    946                                                         bepFullPath = portFullPath + '/' + bep 
     809        if self.checkFlag('unpack') and not self.options.force: 
     810            return 
    947811 
    948                                                         if os.path.isfile(bepFullPath) and bep[-4:] == ".bep": 
    949                                                                 reWithVersion = re.compile(regExp['bepfilename']) 
    950                                                                 #reWithVersion = re.compile('^(?P<name>[a-z0-9\-_]*)-(?P<version>([\d]+[a-z\\.])*[\d]*)?$') 
    951                                                                 m = reWithVersion.match(bep) 
    952                                                                 if m and m.group("name") and m.group("version"):                # with version 
    953                                                                         print "\t\tName/Version: " + m.group("name") + "\t" + m.group("version") 
    954                                                                         self.validateBepFile(bepFullPath, False) 
    955                                                                 else:                                                           # invalid argument 
    956                                                                         print("Error: Couldn't parse port/version info: " + bep)         
     812        # unpack source archive 
    957813 
     814        print 'Unpacking ' + self.src_local 
     815        archiveFullPath = self.downloadDir + '/' + self.src_local 
     816        if tarfile.is_tarfile(archiveFullPath): 
     817            tf = tarfile.open(self.downloadDir + '/' + self.src_local, 
     818                              'r') 
     819            tf.extractall(self.workDir) 
     820            tf.close() 
     821        elif zipfile.is_zipfile(archiveFullPath): 
     822            zf = zipfile.ZipFile(self.downloadDir + '/' 
     823                                  + self.src_local, 'r') 
     824            zf.extractall(self.workDir) 
     825            zf.close() 
     826        elif archiveFullPath.split('/')[-1].split('.')[-1] == 'xz': 
     827            try: 
     828                Popen(['xz', '-d', '-k', archiveFullPath]) 
     829            except (OSError, CalledProcessError), e: 
     830 
     831                # run the installoptionalsoftware prompt 
     832 
     833                if self.prompt_installer('xz'): 
     834                    Popen(['xz', '-d', '-k', archiveFullPath]) 
     835                else: 
     836                    sys.exit() 
     837            tar = archiveFullPath[:-3] 
     838            if tarfile.is_tarfile(tar): 
     839                tf = tarfile.open(tar, 'r') 
     840                tf.extractall(self.workDir) 
     841                tf.close() 
     842        else: 
     843            sys.exit('Error: Unrecognized archive type.') 
     844 
     845        self.setFlag('unpack') 
     846 
     847    def patchSource(self): 
     848        """Apply the Haiku patches to the source directory""" 
     849 
     850        # Check to see if the patch was already applied to the source. 
     851 
     852        if self.checkFlag('patch') and not self.options.force: 
     853            return 
     854 
     855        patchFilePath = self.patchesDir + '/' + self.portName + '-'\ 
     856             + self.portVersion + '.patch' 
     857        if os.path.exists(patchFilePath): 
     858            patchOptions = '' 
     859            if 'PATCH_OPTIONS' in self.confKeys: 
     860                patchOptions += self.confKeys['PATCH_OPTIONS'] 
     861            check_call('patch -p0 ' + patchOptions + ' -i ' 
     862                        + patchFilePath, shell=True, cwd=self.workDir) 
     863        else: 
     864            print 'No patching required' 
     865        self.setFlag('patch') 
     866 
     867    def buildPort(self): 
     868        """Build the sources""" 
     869 
     870        # Make sure the bep file for the package has a BUILD section. 
     871 
     872        if not 'BUILD' in self.bepKeys or not self.bepKeys['BUILD']: 
     873            sys.exit('Error: Invalid bep file with no BUILD section.') 
     874 
     875        # Check to see if a previous build was already done. 
     876 
     877        if self.checkFlag('build') and not self.options.force: 
     878            return 
     879 
     880        self.runCommandSequence(self.bepKeys['BUILD'], self.workDir) 
     881 
     882        self.setFlag('build') 
     883 
     884    def installPort(self): 
     885        """Install the binaries onto the system""" 
     886 
     887        # Make sure the bep file for the package has an INSTALL section. 
     888 
     889        if not 'INSTALL' in self.bepKeys or not self.bepKeys['INSTALL']: 
     890            sys.exit('Error: bep file has no INSTALL section.') 
     891 
     892        # Check if port is python or perl as installing them with haikuporter may break haikuporter 
     893 
     894        if self.portName == 'python' or self.portName == 'perl': 
     895            sys.exit('Error: cannot install ' + self.portName 
     896                      + ' using haikuporter.  Build to a package instead.' 
     897                     ) 
     898        print 'Installing ...' 
     899 
     900        self.runCommandSequence(self.bepKeys['INSTALL'], self.workDir) 
     901 
     902    def testPort(self): 
     903        """Test the resulting binaries""" 
     904 
     905        # Make sure the bep file for the package has a TEST section. 
     906 
     907        if not 'TEST' in self.bepKeys or not self.bepKeys['TEST']: 
     908            sys.exit('Error: bep file has no TEST section.') 
     909 
     910        print 'Testing ...' 
     911 
     912        self.runCommandSequence(self.bepKeys['TEST'], self.workDir) 
     913 
     914    def generatePackageDescription(self, opdFile): 
     915        """Create an OptionalPackageDescription file for inclusion in a package""" 
     916 
     917        print 'Generating OptionalPackageDescription file ...' 
     918 
     919        opd = open(opdFile, 'w') 
     920 
     921        opd.write('Package:\t' + self.portName + '\n') 
     922        opd.write('Version:\t' + self.portVersion + '\n') 
     923        opd.write('URL:\t\t' + self.bepKeys['HOMEPAGE'] + '\n') 
     924 
     925        # These keys aren't mandatory so we need to check if they exist 
     926 
     927        if self.bepKeys['LICENSE']: 
     928            for license in self.bepKeys['LICENSE']: 
     929                opd.write('License:\t' + license + '\n') 
     930 
     931        if self.bepKeys['COPYRIGHT']: 
     932            for copyright in self.bepKeys['COPYRIGHT']: 
     933                opd.write('Copyright:\t' + copyright + '\n') 
     934 
     935        opd.close() 
     936 
     937    def makePackage(self): 
     938        """Create a package suitable for distribution""" 
     939 
     940        print 'Creating distribution package ...' 
     941 
     942        # Make sure the bep file for the package has an INSTALL section. 
     943 
     944        if not 'INSTALL' in self.bepKeys or not self.bepKeys['INSTALL']: 
     945            sys.exit('Error: bep file has no INSTALL section.') 
     946 
     947        # if the distro dir still exists from a previous run then remove it 
     948 
     949        shutil.rmtree(self.distroDir, True) 
     950 
     951        # create distro dir 
     952 
     953        if not os.path.exists(self.distroDir): 
     954            os.mkdir(self.distroDir) 
     955 
     956        self.runCommandSequence(self.bepKeys['INSTALL'], self.workDir, 
     957                                True) 
     958 
     959        if not os.path.exists(self.distroDir + '/boot'): 
     960            sys.exit('Error: No installed files detected in packaging directory. Check bep file for correctness' 
     961                     ) 
     962 
     963        opdFound = False 
     964        for f in os.listdir(self.portDir): 
     965            if 'licenses' in f: 
     966                shutil.copytree(self.portDir + '/' + f, self.distroDir 
     967                                 + '/boot/common/data/licenses') 
     968 
     969            if 'OptionalPackageDescription' in f: 
     970                opdFound = True 
     971                print 'Copying OptionalPackageDescription to distro directory ...' 
     972                shutil.copyfile(self.portDir + '/' + f, self.distroDir 
     973                                 + '/boot/.OptionalPackageDescription') 
     974 
     975        if not opdFound: 
     976            self.generatePackageDescription(self.distroDir 
     977                     + '/boot/.OptionalPackageDescription') 
     978 
     979        # go to distro dir for making zip package 
     980 
     981        os.chdir(self.distroDir) 
     982 
     983        package = self.portName + '-' + self.portVersion 
     984 
     985        # set haikuversion to HAIKUVERSION, if it is empty then don't add a dash. 
     986 
     987        haikuversion = '' 
     988        if 'HAIKUVERSION' in os.environ: 
     989            haikuversion = '-' + os.environ['HAIKUVERSION'] 
     990 
     991        gcc = getCommandOutput('setgcc') 
     992        gcc = gcc.split(': ')[1].split('/') 
     993        arch = '-' + gcc[0] 
     994        gcc = '-' + gcc[1] 
     995 
     996        date = time.strftime('-%Y-%m-%d', time.localtime()) 
     997 
     998        zipFile = self.portDir + '/' + package + haikuversion + arch\ 
     999             + gcc + date + '.zip' 
     1000 
     1001        zipRootDir = self.distroDir + '/boot' 
     1002 
     1003        packageFiles = '' 
     1004 
     1005        # Add the files/dirs in self.distroDir/boot to the list of items to zip 
     1006 
     1007        for f in os.listdir(zipRootDir): 
     1008            packageFiles += f + ' ' 
     1009 
     1010        # Zip the package and save it in the root of the port dir 
     1011 
     1012        check_call('zip -9ry ' + zipFile + ' ' + packageFiles 
     1013                    + ' -x *.svn*', shell=True, cwd=zipRootDir) 
     1014 
     1015        # Clean up after ourselves 
     1016 
     1017        shutil.rmtree(self.distroDir) 
     1018 
     1019        print 'Package saved to: ' + zipFile 
     1020 
     1021    def makePatchedArchive(self): 
     1022        """Create a patched source archive""" 
     1023 
     1024        print 'Creating patched source archive ...' 
     1025 
     1026        # Set the path and filename for the archive. 
     1027 
     1028        date = time.strftime('-%Y-%m-%d', time.localtime()) 
     1029        archiveFile = self.portDir + '/' + self.portName + '-'\ 
     1030             + self.portVersion + '_haiku' + date + '.tar.xz' 
     1031 
     1032        sourceFiles = '' 
     1033 
     1034        # Build the list of dirs to archive. 
     1035        # Since we don't know the name we have to iterate over the dir. 
     1036 
     1037        for f in os.listdir(self.workDir): 
     1038            if os.path.isdir(self.workDir + '/' + f): 
     1039                sourceFiles += ' ' + f 
     1040 
     1041        # Make sure we found something to archive 
     1042 
     1043        if not sourceFiles: 
     1044            sys.exit('Error: No source directories found in work dir.') 
     1045 
     1046        # Archive the package and save it in the root of the port dir. 
     1047 
     1048        check_call('tar cJvf ' + archiveFile + ' ' + sourceFiles, 
     1049                   shell=True, cwd=self.workDir) 
     1050 
     1051        # Clean up after ourselves 
     1052 
     1053        shutil.rmtree(self.workDir) 
     1054 
     1055        print 'Archive saved to: ' + archiveFile 
     1056 
     1057    def prompt_installer(self, name): 
     1058        apps = {  # map command to package 
     1059            'xz': 'XZ-Utils', 
     1060            'git': 'Git', 
     1061            'hg': 'mercurial', 
     1062            'cvs': 'cvs', 
     1063            'bzr': 'bazaar', 
     1064            } 
     1065        if self.options.yes: 
     1066            check_call('installoptionalpackage' + ' ' 
     1067                        + str(apps[name]), shell=True) 
     1068            return True 
     1069        response = raw_input('Do you want to install %s? [Y/n]' % name) 
     1070        if response in ['y', 'Y', '\n', 'yes', '']: 
     1071            check_call('installoptionalpackage' + ' ' 
     1072                        + str(apps[name]), shell=True) 
     1073            return True 
     1074        else: 
     1075            print 'In order to install this package you need to run installoptionalpackage %s later or let Haikuporter install it for you.'\ 
     1076                 % apps[name] 
     1077            return False 
     1078 
     1079    def runCommandSequence( 
     1080        self, 
     1081        rawCommandList, 
     1082        dir, 
     1083        packageMode=False, 
     1084        ): 
     1085        """Run a sequence of shell commands from a bep file""" 
     1086 
     1087        # Convert rawCommandList to a string with a newline after each entry 
     1088        # Use 'set -e' as the first command so that the shell aborts immediately 
     1089 
     1090        commandString = 'set -e\n' 
     1091        for command in rawCommandList: 
     1092            commandString += command + '\n' 
     1093 
     1094        shellEnv = None 
     1095 
     1096        if packageMode: 
     1097            shellEnv = os.environ 
     1098            shellEnv['DESTDIR'] = self.distroDir 
     1099 
     1100        check_call(commandString, shell=True, cwd=dir, env=shellEnv) 
     1101 
     1102    def checkSourceTree(self): 
     1103        print 'Checking HaikuPorts tree at: ' + self.packagesPath 
     1104 
     1105        for category in os.listdir(self.packagesPath): 
     1106            categoryFullPath = self.packagesPath + '/' + category 
     1107 
     1108            if os.path.isdir(categoryFullPath) and category[0] != '.': 
     1109                print 'Category: ' + category 
     1110 
     1111                for port in os.listdir(categoryFullPath): 
     1112                    portFullPath = categoryFullPath + '/' + port 
     1113 
     1114                    if os.path.isdir(portFullPath) and port[0] != '.': 
     1115                        print '\tPort: ' + port 
     1116 
     1117                        for bep in os.listdir(portFullPath): 
     1118                            bepFullPath = portFullPath + '/' + bep 
     1119 
     1120                            if os.path.isfile(bepFullPath) and bep[-4:]\ 
     1121                                 == '.bep': 
     1122                                reWithVersion = \ 
     1123                                    re.compile(regExp['bepfilename']) 
     1124 
     1125                                # reWithVersion = re.compile('^(?P<name>[a-z0-9\-_]*)-(?P<version>([\d]+[a-z\\.])*[\d]*)?$') 
     1126 
     1127                                m = reWithVersion.match(bep) 
     1128                                if m and m.group('name')\ 
     1129                                     and m.group('version'):  # with version 
     1130                                    print '\t\tName/Version: '\ 
     1131     + m.group('name') + '\t' + m.group('version') 
     1132                                    self.validateBepFile(bepFullPath, 
     1133        False) 
     1134                                else: 
     1135 
     1136                                                                     # invalid argument 
     1137 
     1138                                    print "Error: Couldn't parse port/version info: "\ 
     1139     + bep 
     1140 
     1141 
    9581142# -- /etc/haikuports.conf and *.bep parser ----------------------------------- 
    9591143 
     1144 
    9601145class Config: 
    961         def __init__(self, filename): 
    962                 # regular expressions for parsing the config file 
    963                 reOptionValue   = re.compile('^(?P<key>[A-Z0-9_]*)\s*=\s*"(?P<value>.*)(?<!\\\\)"\s*$') 
    964                 reOptionList    = re.compile('^(?P<key>[A-Z0-9_]*)\s*=\s*"(?P<item>.*)\s*$') 
    965                 reLastListItem  = re.compile('^\s+(?P<item>.*)(?<!\\\\)"\s*$') 
    966                 reListItem              = re.compile('^\s+(?P<item>.*)\s*$') 
    9671146 
    968                 reShellStart    = re.compile('^(?P<key>[A-Z_]*)\s*\{\s*$') 
    969                 reShellEnd              = re.compile('^\}\s*$') 
     1147    def __init__(self, filename): 
    9701148 
    971                 reComment               = re.compile('^\s*#.*$') 
    972                 reEmptyLine             = re.compile('^\s*$') 
     1149        # regular expressions for parsing the config file 
    9731150 
    974                 reNoneType              = re.compile('^\s*$') 
    975                 reIntType               = re.compile('^[0-9]+$') 
    976                 reBooleanType   = re.compile('^yes$|^no$') 
    977                 reStatusType    = re.compile('^broken$|^untested$|^unstable$|^stable$') 
     1151        reOptionValue = \ 
     1152            re.compile('^(?P<key>[A-Z0-9_]*)\s*=\s*"(?P<value>.*)(?<!\\\\)"\s*$' 
     1153                       ) 
     1154        reOptionList = \ 
     1155            re.compile('^(?P<key>[A-Z0-9_]*)\s*=\s*"(?P<item>.*)\s*$') 
     1156        reLastListItem = re.compile('^\s+(?P<item>.*)(?<!\\\\)"\s*$') 
     1157        reListItem = re.compile('^\s+(?P<item>.*)\s*$') 
    9781158 
    979                 self.options = {} 
     1159        reShellStart = re.compile('^(?P<key>[A-Z_]*)\s*\{\s*$') 
     1160        reShellEnd = re.compile('^\}\s*$') 
    9801161 
    981                 self.filename = filename 
     1162        reComment = re.compile('^\s*#.*$') 
     1163        reEmptyLine = re.compile('^\s*$') 
    9821164 
    983                 try: 
    984                         self.file = open(self.filename, 'rb') 
    985                 except: 
    986                         sys.exit("Error: Can't find config file: " + self.filename) 
     1165        reNoneType = re.compile('^\s*$') 
     1166        reIntType = re.compile('^[0-9]+$') 
     1167        reBooleanType = re.compile('^yes$|^no$') 
     1168        reStatusType = \ 
     1169            re.compile('^broken$|^untested$|^unstable$|^stable$') 
    9871170 
    988                 self.lineCount = 0 
    989                 self.nextLine() 
    990                 # TODO: store all options in a list? 
    991                 while self.line != "": 
    992                         # empty line or comment 
    993                         if reEmptyLine.match(self.line) or reComment.match(self.line): 
    994                                 pass 
    995                         # value option (single line) (check BEFORE list option) 
    996                         elif reOptionValue.match(self.line): 
    997                                 #print "[OV] " + self.line.strip('\n') 
    998                                 m = reOptionValue.match(self.line) 
    999                                 key = m.group("key") 
    1000                                 value = m.group("value") 
    1001                                 if reNoneType.match(value):                     # NoneType 
    1002                                         self.options[key] = None 
    1003                                 elif reIntType.match(value):            # IntType 
    1004                                         self.options[key] = int(value) 
    1005                                 elif reBooleanType.match(value):        # BooleanType 
    1006                                         if value == "yes": 
    1007                                                 self.options[key] = True 
    1008                                         else: 
    1009                                                 self.options[key] = False 
    1010                                 elif reStatusType.match(value):         # StatusType 
    1011                                         self.options[key] = status(value) 
    1012                                 else:                                                           # StringType 
    1013                                         self.options[key] = value 
    1014                         # list option (multiline) 
    1015                         elif reOptionList.match(self.line): 
    1016                                 #print "[OL] " + self.line.strip('\n') 
    1017                                 m = reOptionList.match(self.line) 
    1018                                 key = m.group("key") 
    1019                                 self.options[key] = [m.group("item")] 
    1020                                 self.nextLine() 
    1021                                 while self.line != "": 
    1022                                         # empty line or comment 
    1023                                         if reEmptyLine.match(self.line) or reComment.match(self.line): 
    1024                                                 pass 
    1025                                         # last list item (check BEFORE list item) 
    1026                                         elif reLastListItem.match(self.line): 
    1027                                                 #print "[LL] " + self.line.strip('\n') 
    1028                                                 m = reLastListItem.match(self.line) 
    1029                                                 self.options[key].append(m.group("item")) 
    1030                                                 break 
    1031                                         # list item 
    1032                                         elif reListItem.match(self.line): 
    1033                                                 #print "[LI] " + self.line.strip('\n') 
    1034                                                 m = reListItem.match(self.line) 
    1035                                                 self.options[key].append(m.group("item")) 
    1036                                         # unrecognized syntax 
    1037                                         else: 
    1038                                                 self.illegalSyntax() 
    1039                                         self.nextLine() 
    1040                         # shell commands (multiline) 
    1041                         elif reShellStart.match(self.line): 
    1042                                 #print "[SS] " + self.line.strip('\n') 
    1043                                 m = reShellStart.match(self.line) 
    1044                                 key = m.group("key") 
    1045                                 self.options[key] = shell() 
    1046                                 self.nextLine() 
    1047                                 while self.line != "": 
    1048                                         # empty line or comment 
    1049                                         if reEmptyLine.match(self.line) or reComment.match(self.line): 
    1050                                                 pass 
    1051                                         # last list item (check BEFORE list item) 
    1052                                         elif reShellEnd.match(self.line): 
    1053                                                 #print "[SE] " + self.line.strip('\n') 
    1054                                                 break 
    1055                                         # list item 
    1056                                         elif reListItem.match(self.line): 
    1057                                                 #print "[SI] " + self.line.strip('\n') 
    1058                                                 m = reListItem.match(self.line) 
    1059                                                 self.options[key].append(m.group("item")) 
    1060                                         # unrecognized syntax 
    1061                                         else: 
    1062                                                 self.illegalSyntax() 
    1063                                         self.nextLine() 
    1064                         # unrecognized syntax 
    1065                         else: 
    1066                                 self.illegalSyntax() 
    1067                         self.nextLine() 
    1068                 self.file.close() 
    1069                 #for key in self.options: 
    1070                 #       print key + " = " + str(self.options[key]) 
     1171        self.options = {} 
    10711172 
    1072         def nextLine(self): 
    1073                 self.line = self.file.readline() 
    1074                 self.lineCount = self.lineCount + 1 
     1173        self.filename = filename 
    10751174 
    1076         def illegalSyntax(self): 
    1077                 print "Error: Illegal syntax in " + self.filename + " at line " + str(self.lineCount) + ":" 
    1078                 print "  " + self.line 
    1079                 sys.exit(1) 
     1175        try: 
     1176            self.file = open(self.filename, 'rb') 
     1177        except: 
     1178            sys.exit("Error: Can't find config file: " + self.filename) 
    10801179 
    1081         def getKeys(self): 
    1082                 return self.options 
     1180        self.lineCount = 0 
     1181        self.nextLine() 
    10831182 
    1084         def valueOf(self, key): 
    1085                 try: 
    1086                         value = self.options[key] 
    1087                         return value 
    1088                 except KeyError:        # an unspecified option is the same as an empty one 
    1089                         return "" 
     1183        # TODO: store all options in a list? 
    10901184 
     1185        while self.line != '': 
     1186 
     1187            # empty line or comment 
     1188 
     1189            if reEmptyLine.match(self.line)\ 
     1190                 or reComment.match(self.line): 
     1191                pass 
     1192            elif reOptionValue.match(self.line): 
     1193 
     1194            # value option (single line) (check BEFORE list option) 
     1195                # print "[OV] " + self.line.strip('\n') 
     1196 
     1197                m = reOptionValue.match(self.line) 
     1198                key = m.group('key') 
     1199                value = m.group('value') 
     1200                if reNoneType.match(value):  # NoneType 
     1201                    self.options[key] = None 
     1202                elif reIntType.match(value): 
     1203 
     1204                                                    # IntType 
     1205 
     1206                    self.options[key] = int(value) 
     1207                elif reBooleanType.match(value): 
     1208 
     1209                                                    # BooleanType 
     1210 
     1211                    if value == 'yes': 
     1212                        self.options[key] = True 
     1213                    else: 
     1214                        self.options[key] = False 
     1215                elif reStatusType.match(value): 
     1216 
     1217                                                       # StatusType 
     1218 
     1219                    self.options[key] = status(value) 
     1220                else: 
     1221 
     1222                                                     # StringType 
     1223 
     1224                    self.options[key] = value 
     1225            elif reOptionList.match(self.line): 
     1226 
     1227            # list option (multiline) 
     1228                # print "[OL] " + self.line.strip('\n') 
     1229 
     1230                m = reOptionList.match(self.line) 
     1231                key = m.group('key') 
     1232                self.options[key] = [m.group('item')] 
     1233                self.nextLine() 
     1234                while self.line != '': 
     1235 
     1236                    # empty line or comment 
     1237 
     1238                    if reEmptyLine.match(self.line)\ 
     1239                         or reComment.match(self.line): 
     1240                        pass 
     1241                    elif reLastListItem.match(self.line): 
     1242 
     1243                    # last list item (check BEFORE list item) 
     1244                        # print "[LL] " + self.line.strip('\n') 
     1245 
     1246                        m = reLastListItem.match(self.line) 
     1247                        self.options[key].append(m.group('item')) 
     1248                        break 
     1249                    elif reListItem.match(self.line): 
     1250 
     1251                    # list item 
     1252                        # print "[LI] " + self.line.strip('\n') 
     1253 
     1254                        m = reListItem.match(self.line) 
     1255                        self.options[key].append(m.group('item')) 
     1256                    else: 
     1257 
     1258                    # unrecognized syntax 
     1259 
     1260                        self.illegalSyntax() 
     1261                    self.nextLine() 
     1262            elif reShellStart.match(self.line): 
     1263 
     1264            # shell commands (multiline) 
     1265                # print "[SS] " + self.line.strip('\n') 
     1266 
     1267                m = reShellStart.match(self.line) 
     1268                key = m.group('key') 
     1269                self.options[key] = shell() 
     1270                self.nextLine() 
     1271                while self.line != '': 
     1272 
     1273                    # empty line or comment 
     1274 
     1275                    if reEmptyLine.match(self.line)\ 
     1276                         or reComment.match(self.line): 
     1277                        pass 
     1278                    elif reShellEnd.match(self.line): 
     1279 
     1280                    # last list item (check BEFORE list item) 
     1281                        # print "[SE] " + self.line.strip('\n') 
     1282 
     1283                        break 
     1284                    elif reListItem.match(self.line): 
     1285 
     1286                    # list item 
     1287                        # print "[SI] " + self.line.strip('\n') 
     1288 
     1289                        m = reListItem.match(self.line) 
     1290                        self.options[key].append(m.group('item')) 
     1291                    else: 
     1292 
     1293                    # unrecognized syntax 
     1294 
     1295                        self.illegalSyntax() 
     1296                    self.nextLine() 
     1297            else: 
     1298 
     1299            # unrecognized syntax 
     1300 
     1301                self.illegalSyntax() 
     1302            self.nextLine() 
     1303        self.file.close() 
     1304 
     1305        # for key in self.options: 
     1306        # ....print key + " = " + str(self.options[key]) 
     1307 
     1308    def nextLine(self): 
     1309        self.line = self.file.readline() 
     1310        self.lineCount = self.lineCount + 1 
     1311 
     1312    def illegalSyntax(self): 
     1313        print 'Error: Illegal syntax in ' + self.filename + ' at line '\ 
     1314             + str(self.lineCount) + ':' 
     1315        print '  ' + self.line 
     1316        sys.exit(1) 
     1317 
     1318    def getKeys(self): 
     1319        return self.options 
     1320 
     1321    def valueOf(self, key): 
     1322        try: 
     1323            value = self.options[key] 
     1324            return value 
     1325        except KeyError: 
     1326 
     1327                            # an unspecified option is the same as an empty one 
     1328 
     1329            return '' 
     1330 
     1331 
    10911332# -- Command line argument parsing ------------------------------------------- 
    10921333 
    1093 parser = OptionParser(usage="usage: %prog [options] portname[-portversion]", 
    1094                                                 version="%prog " + info['version']) 
     1334parser = \ 
     1335    OptionParser(usage='usage: %prog [options] portname[-portversion]', 
     1336                 version='%prog ' + info['version']) 
    10951337 
    1096 parser.add_option("-l", "--list", 
    1097         action="store_true", dest="list", default=False, 
    1098         help="list available ports") 
     1338parser.add_option( 
     1339    '-l', 
     1340    '--list', 
     1341    action='store_true', 
     1342    dest='list', 
     1343    default=False, 
     1344    help='list available ports', 
     1345    ) 
    10991346 
    1100 parser.add_option("-a", "--about", 
    1101         action="store_true", dest="about", default=False, 
    1102         help="show description of the specified port") 
     1347parser.add_option( 
     1348    '-a', 
     1349    '--about', 
     1350    action='store_true', 
     1351    dest='about', 
     1352    default=False, 
     1353    help='show description of the specified port', 
     1354    ) 
    11031355 
    1104 parser.add_option("-s", "--search", 
    1105         action="store_true", dest="search", default=False, 
    1106         help="search for a port (regex)") 
     1356parser.add_option( 
     1357    '-s', 
     1358    '--search', 
     1359    action='store_true', 
     1360    dest='search', 
     1361    default=False, 
     1362    help='search for a port (regex)', 
     1363    ) 
    11071364 
    1108 parser.add_option("-p", "--nopatch", 
    1109         action="store_false", dest="patch", default=True, 
    1110         help="don't patch the sources, just download and unpack") 
     1365parser.add_option( 
     1366    '-p', 
     1367    '--nopatch', 
     1368    action='store_false', 
     1369    dest='patch', 
     1370    default=True, 
     1371    help="don't patch the sources, just download and unpack", 
     1372    ) 
    11111373 
    1112 parser.add_option("-b", "--nobuild", 
    1113         action="store_false", dest="build", default=True, 
    1114         help="don't build the port, just download, unpack and patch") 
     1374parser.add_option( 
     1375    '-b', 
     1376    '--nobuild', 
     1377    action='store_false', 
     1378    dest='build', 
     1379    default=True, 
     1380    help="don't build the port, just download, unpack and patch", 
     1381    ) 
    11151382 
    1116 parser.add_option("-i", "--install", 
    1117         action="store_true", dest="install", default=False, 
    1118         help="also install the port (the default is to only build)") 
    1119          
    1120 parser.add_option("-d", "--distro", 
    1121         action="store_true", dest="distro", default=False, 
    1122         help="make distribution package of the specified port (include download, unpack, patch, build)") 
     1383parser.add_option( 
     1384    '-i', 
     1385    '--install', 
     1386    action='store_true', 
     1387    dest='install', 
     1388    default=False, 
     1389    help='also install the port (the default is to only build)', 
     1390    ) 
    11231391 
    1124 parser.add_option("-c", "--clean", 
    1125         action="store_true", dest="clean", default=False, 
    1126         help="clean the working directory of the specified port") 
     1392parser.add_option( 
     1393    '-d', 
     1394    '--distro', 
     1395    action='store_true', 
     1396    dest='distro', 
     1397    default=False, 
     1398    help='make distribution package of the specified port (include download, unpack, patch, build)' 
     1399        , 
     1400    ) 
    11271401 
    1128 parser.add_option("-g", "--get", 
    1129         action="store_true", dest="get", default=False, 
    1130         help="get/update the ports tree") 
     1402parser.add_option( 
     1403    '-c', 
     1404    '--clean', 
     1405    action='store_true', 
     1406    dest='clean', 
     1407    default=False, 
     1408    help='clean the working directory of the specified port', 
     1409    ) 
    11311410 
    1132 parser.add_option("-f", "--force", 
    1133         action="store_true", dest="force", default=False, 
    1134         help="force to perform the steps (unpack, patch, build)") 
     1411parser.add_option( 
     1412    '-g', 
     1413    '--get', 
     1414    action='store_true', 
     1415    dest='get', 
     1416    default=False, 
     1417    help='get/update the ports tree', 
     1418    ) 
    11351419 
    1136 parser.add_option("-z", "--archive", 
    1137         action="store_true", dest="archive", default=False, 
    1138         help="Create a patched source archive as <package>_haiku.tar.xz") 
     1420parser.add_option( 
     1421    '-f', 
     1422    '--force', 
     1423    action='store_true', 
     1424    dest='force', 
     1425    default=False, 
     1426    help='force to perform the steps (unpack, patch, build)', 
     1427    ) 
    11391428 
    1140 parser.add_option("-t", "--tree", 
    1141         action="store_true", dest="tree", default=False, 
    1142         help="Print out the location of the haikuports source tree") 
     1429parser.add_option( 
     1430    '-z', 
     1431    '--archive', 
     1432    action='store_true', 
     1433    dest='archive', 
     1434    default=False, 
     1435    help='Create a patched source archive as <package>_haiku.tar.xz', 
     1436    ) 
    11431437 
    1144 parser.add_option("-y", "--yes", 
    1145         action="store_true", dest="yes", default=False, 
    1146         help="Answer yes to all questions") 
    1147          
    1148 parser.add_option("--test", 
    1149         action="store_true", dest="test", default=False, 
    1150         help="Run tests on resulting binaries") 
    1151          
    1152 parser.add_option("--lint", 
    1153         action="store_true", dest="lint", default=False, 
    1154         help="Scan the ports tree for problems") 
     1438parser.add_option( 
     1439    '-t', 
     1440    '--tree', 
     1441    action='store_true', 
     1442    dest='tree', 
     1443    default=False, 
     1444    help='Print out the location of the haikuports source tree', 
     1445    ) 
    11551446 
     1447parser.add_option( 
     1448    '-y', 
     1449    '--yes', 
     1450    action='store_true', 
     1451    dest='yes', 
     1452    default=False, 
     1453    help='Answer yes to all questions', 
     1454    ) 
     1455 
     1456parser.add_option('--test', action='store_true', dest='test', 
     1457                  default=False, help='Run tests on resulting binaries') 
     1458 
     1459parser.add_option('--lint', action='store_true', dest='lint', 
     1460                  default=False, help='Scan the ports tree for problems' 
     1461                  ) 
     1462 
    11561463(options, args) = parser.parse_args() 
    11571464 
    11581465haikuporter = HaikuPorter(options, args) 
    1159  

Download in other formats:

  • Original Format

Trac Powered

Powered by Trac 0.13dev-r10686
By Edgewall Software.

Visit the Trac open source project at
http://trac.edgewall.org/