#!/usr/bin/python import getopt, sys, os import settings #__doc__="Usage: "+sys.argv[0]+" [...]" def verbose_system(command): print command return os.system(command) def list_configured_drivers(): #all .cfg files which end in .cfg as they should return [x[:-len('.cfg')] for x in os.listdir(settings.GLOBAL_CONF_DIR) if x[-len('.cfg'):]=='.cfg'] #read key=value file to dict def read_config(conf_file,defaults={}): conffile=open(conf_file) conf=defaults.copy() for line in conffile: if len(line): value=line[line.find("=")+1:] if value[-1]=='\n': value=value[:-1] conf[line[:line.find("=")]]=value return conf #returns dict of key=value config file def read_driver_config(driver_name): conffile=os.path.join(settings.GLOBAL_CONF_DIR,settings.DRIVER_DIR,driver_name+'.cfg') return read_config(conffile,{ #'name':None, #'executable':None, }) return configs #dict #read g-common config for a repo def read_repo_config(repo_location): hidden_conffile=os.path.join(repo_location,settings.MYDIR,'repo.cfg') return read_config(hidden_conffile) #sync a local repository's PACKAGES file def action_sync(repo_location,driver,remote_uri): if driver==None: repo_conf=read_repo_config(repo_location) driver=repo_conf['driver'] driver_conf=read_driver_config(driver) if remote_uri is None: remote_uri=repo_conf['uri'] if os.path.exists(repo_location): try: os.makedirs(os.path.join(repo_location,settings.MYDIR)) except: pass cfg_file=open(os.path.join(repo_location,settings.MYDIR,"repo.cfg"),"w") cfg_file.write('driver='+driver+'\n') cfg_file.write('uri='+remote_uri+'\n') cfg_file.close() return os.system(driver_conf['exec']+" "+repo_location+" sync "+remote_uri) #list categories in this repositorie def list_categories(repo_location): repo_conf=read_repo_config(repo_location) driver_conf=read_driver_config(repo_conf['driver']) return os.system(driver_conf['exec']+" "+repo_location+" list-categories") #idem ditto def list_packages(repo_location): repo_conf=read_repo_config(repo_location) driver_conf=read_driver_config(repo_conf['driver']) return os.system(driver_conf['exec']+" "+repo_location+" list-packages") def parse_manifest(manifest_file): manifest=open(manifest_file) digests={} for line in manifest: parts=line.split(' ') type=parts[0] file=parts[1] size=parts[2] hashes={} for i in range((len(parts)-3)/2): hashes[parts[3+i*2]]=parts[4+i*2] digests[file]=(type,file,size,hashes) manifest.close() return digests def generate_digest(dir,entry,type): import hashlib full_name=os.path.join(dir,entry) return (type,os.path.relpath(full_name,dir),os.path.getsize(full_name),{'SHA1':hashlib.sha1(open(full_name).read()).hexdigest()}) def write_manifest(manifest,filename): manifest_file=open(filename,'w') for file, data in manifest.items(): hashes=[key+' '+value for key,value in data[3].items()] manifest_file.write("%s %s %d %s\n" % (data[0], data[1], int(data[2]), ' '.join(hashes))) manifest_file.close() def populate_manifest(manifest_file,dir): import hashlib ebuild_file=settings.COMMON_EBUILD_FILE #get from settings ebuild_digest=hashlib.sha1(open(ebuild_file).read()).hexdigest() if os.path.exists(manifest_file): manifest=parse_manifest(manifest_file) else: manifest={} for entry in os.listdir(dir): full_entry=os.path.join(dir,entry) if not os.path.isfile(full_entry): #we don't deal with dirs and stuff continue elif entry[0]=='.': #hidden file, skip continue elif entry=='Manifest': #eh, don't process this continue elif entry[-len('.ebuild'):]=='.ebuild': #ends in .ebuild manifest[entry]=('EBUILD',entry,os.path.getsize(full_entry),{'SHA1':ebuild_digest}) elif entry=='ChangeLog' or entry=='metadata.xml': manifest[entry]=generate_digest(dir,entry,'MISC') else: raise NotImplementedError #can't deduce file type files_dir=os.path.join(dir,'files') if os.path.exists(files_dir): for entry in os.listdir(files_dir): full_entry=os.path.join(files_dir,entry) if not os.path.isfile(full_entry): #don't process dirs continue else: digest=generate_digest(files_dir,entry,'AUX') manifest[digest[1]]=digest write_manifest(manifest,manifest_file) #generate a tree of ebuilds... note that we only link ebuild files, instead of generating them #we will, however, generate metadata.xml and Manifest files def generate_tree(repo_location,generate_manifest,generate_metadata): import hashlib, subprocess repo_conf=read_repo_config(repo_location) driver_conf=read_driver_config(repo_conf['driver']) #clean repo visible_files=[x for x in os.listdir(repo_location) if x[0]!='.'] import shutil for dir in visible_files: try: shutil.rmtree(os.path.join(repo_location,dir)) except: pass #create directory structure packages_list_pipe=subprocess.Popen(driver_conf['exec']+' '+repo_location+' list-packages',shell=True,stdout=subprocess.PIPE) packages=[] for line in iter(packages_list_pipe.stdout.readline,''): if line=='': continue category=line[:line.find("/")] package=line[line.find("/")+1:line.find(" ")] version=line[line.find(" ")+1:-1] ebuild_dir=os.path.join(repo_location,category,package) packages.append(line) if not os.path.exists(ebuild_dir): #obvious race condition, but whatever os.makedirs(ebuild_dir) os.waitpid(packages_list_pipe.pid,0) if packages_list_pipe.returncode: return returncode os.makedirs(os.path.join(repo_location,'profiles')) #call driver generate-metadata to give it a chance to fill up the repo returncode=os.system(driver_conf['exec']+" "+repo_location+" generate-metadata") if returncode: return returncode ebuild_file=settings.COMMON_EBUILD_FILE #get from settings #write symlinks for line in packages: category=line[:line.find("/")] package=line[line.find("/")+1:line.find(" ")] version=line[line.find(" ")+1:-1] ebuild_dir=os.path.join(repo_location,category,package) package_file=package+'-'+version+'.ebuild' os.symlink(ebuild_file,os.path.join(ebuild_dir,package_file)) if generate_metadata: metadata_file=open(os.path.join(ebuild_dir,'metadata.xml'),'w') metadata_file.close() if generate_manifest: populate_manifest(os.path.join(ebuild_dir,'Manifest'),ebuild_dir) #write repo metadata if not os.path.exists(os.path.join(repo_location,'profiles','repo_name')): import string #make up a name #as a lucky guess, use the last part of the repo_location directory location repo_location_parts=os.path.split(repo_location) if len(repo_location_parts): #repo might be locaten in /... unlikely, but i was in a good mood while typing this #name chars must be in [a-zA-Z_-] and must not start with a - raw_name=repo_location_parts[-1] name=''.join([x for x in raw_name if x in string.ascii_letters+string.digits or x=='_' or x=='-']) if name[0]=='-': #name may not begin with a hyphen name=name[1:] else: name='common-repo' #fallback repo_name_file=open(os.path.join(repo_location,'profiles','repo_name'),'w') repo_name_file.write(name) repo_name_file.close() if not os.path.exists(os.path.join(repo_location,'profiles','categories')): categories_file=open(os.path.join(repo_location,'profiles','categories'),'w') categories_file.write('\n'.join(list(set([package[:package.find('/')] for package in packages])))) #now isn't that a readable oneliner categories_file.close() return 0 #list package details, in PMS's format def action_package(repo_location,package_name,version): repo_conf=read_repo_config(repo_location) driver_conf=read_driver_config(repo_conf['driver']) version_append='' if version: version_append=' '+version return os.system(driver_conf['exec']+" "+repo_location+" package "+package_name+version_append) #do one of the ebuild phases def exec_phase(repo_location,phase): repo_conf=read_repo_config(repo_location) driver_conf=read_driver_config(repo_conf['driver']) env=os.environ return os.system(driver_conf['exec']+" "+repo_location+" "+phase) def usage(): print __doc__ return 0 def main(): arguments=sys.argv[1:] #print options, arguments if len(arguments)<2: #we need at least a local repository location and an action usage() sys.exit(0) action=arguments[1] repo_location=os.path.abspath(arguments[0]) if action=='sync': if len(arguments)<2 or 'help' in arguments: print "The 'sync' action takes the following parameters:" print " * [driver]" print " * [remote_repository_uri]" sys.exit(1) driver=None remote_repo=None if len(arguments)>2: driver=arguments[2] if len(arguments)>3: remote_repo=arguments[3] return action_sync(repo_location,driver,remote_repo) elif action=='list-categories': return list_categories(repo_location) elif action=='list-packages': return list_packages(repo_location) elif action=='generate-tree': if '--without-manifest' in arguments: manifest=False else: manifest=True if '--without-metadata' in arguments: metadata=False else: metadata=True return generate_tree(repo_location,manifest,metadata) elif action=='package': if len(arguments)<3 or 'help' in arguments: print "The 'package' action takes the following parameters:" print " * category/package_name" print " * [version]" sys.exit(1) package_name=arguments[2] version=None if len(arguments)>3: #version version=arguments[3] return action_package(repo_location,package_name,version) elif action=='usage' or action=='help': return usage() elif action in settings.PMS_PHASES: return exec_phase(repo_location,action) else: return usage() if __name__ == "__main__": sys.exit(main())