def build(self, filename): # check the package has .desktop files print 'SOURCE\t', filename pkg = Package(filename) for b in self.cfg.get_package_blacklist(): if fnmatch.fnmatch(pkg.name, b): print 'IGNORE\t', filename, '\t', "package is blacklisted:", pkg.name return # set up state if not os.path.exists('./appstream'): os.makedirs('./appstream') if not os.path.exists('./icons'): os.makedirs('./icons') if not os.path.exists('./screenshot-cache'): os.makedirs('./screenshot-cache') if not os.path.exists('./screenshots'): os.makedirs('./screenshots') if not os.path.exists('./screenshots/source'): os.makedirs('./screenshots/source') for size in self.cfg.get_screenshot_thumbnail_sizes(): path = './screenshots/' + str(size[0]) + 'x' + str(size[1]) if not os.path.exists(path): os.makedirs(path) # remove tmp if os.path.exists('./tmp'): shutil.rmtree('./tmp') os.makedirs('./tmp') # decompress main file and search for desktop files package_decompress(pkg) files = [] for f in self.cfg.get_interesting_installed_files(): files.extend(glob.glob("./tmp" + f)) files.sort() # we only need to install additional files if we're not running on # the builders for c in self.cfg.get_package_data_list(): if fnmatch.fnmatch(pkg.name, c[0]): extra_files = glob.glob("./packages/%s*.rpm" % c[1]) for f in extra_files: extra_pkg = Package(f) print "INFO\tAdding extra package %s for %s" % (extra_pkg.name, pkg.name) package_decompress(extra_pkg) # open the AppStream file for writing xml_output_file = './appstream/' + pkg.name + '.xml' xml = open(xml_output_file, 'w') xml.write("<?xml version=\"1.0\"?>\n") xml.write("<applications version=\"0.1\">\n") # check for duplicate apps in the package application_ids = [] has_valid_content = False # process each desktop file in the original package for f in files: print 'PROCESS\t', f fi = Gio.file_new_for_path(f) info = fi.query_info('standard::content-type', 0, None) # create the right object depending on the content type content_type = info.get_content_type() if content_type == 'inode/symlink': continue if content_type == 'application/x-font-ttf': app = FontFile(pkg, self.cfg) elif content_type == 'application/x-font-otf': app = FontFile(pkg, self.cfg) elif content_type == 'application/x-desktop': app = DesktopFile(pkg, self.cfg) elif content_type == 'application/xml': app = InputMethodComponent(pkg, self.cfg) elif content_type == 'application/x-sqlite3': app = InputMethodTable(pkg, self.cfg) else: print 'IGNORE\t', f, '\t', "content type " + content_type + " not supported" continue # the ID is the filename app.set_id(f.split('/')[-1]) # application is blacklisted blacklisted = False for b in self.cfg.get_id_blacklist(): if fnmatch.fnmatch(app.app_id, b): print 'IGNORE\t', f, '\t', "application is blacklisted:", app.app_id blacklisted = True break if blacklisted: continue # packages that ship .desktop files in /usr/share/applications # *and* /usr/share/applications/kde4 do not need multiple entries if app.app_id in application_ids: print 'IGNORE\t', f, '\t', app.app_id, 'duplicate ID in package' continue application_ids.append(app.app_id) # parse desktop file if not app.parse_file(f): continue # do we have an AppData file? appdata_file = './tmp/usr/share/appdata/' + app.app_id + '.appdata.xml' appdata_extra_file = './appdata-extra/' + app.type_id + '/' + app.app_id + '.appdata.xml' if os.path.exists(appdata_file) and os.path.exists(appdata_extra_file): print 'DELETE\t', appdata_extra_file, 'as upstream AppData file exists' os.remove(appdata_extra_file) # just use the extra file in places of the missing upstream one if os.path.exists(appdata_extra_file): appdata_file = appdata_extra_file # need to extract details if os.path.exists(appdata_file): data = AppData() data.extract(appdata_file) # check AppData file validates if os.path.exists('/usr/bin/appdata-validate'): env = os.environ p = subprocess.Popen(['/usr/bin/appdata-validate', '--relax', appdata_file], cwd='.', env=env, stdout=subprocess.PIPE) p.wait() if p.returncode: for line in p.stdout: line = line.replace('\n', '') print 'WARNING\tAppData did not validate: ' + line # check the id matches if data.get_id() != app.app_id and data.get_id() != app.app_id_full: raise StandardError('The AppData id does not match: ' + app.app_id) # check the licence is okay if data.get_licence() not in self.cfg.get_content_licences(): raise StandardError('The AppData licence is not okay for ' + app.app_id + ': \'' + data.get_licence() + '\'') # if we have an override, use it for all languages tmp = data.get_names() if tmp: app.names = tmp # if we have an override, use it for all languages tmp = data.get_summaries() if tmp: app.comments = tmp # get optional bits tmp = data.get_url() if tmp: app.homepage_url = tmp tmp = data.get_project_group() if tmp: app.project_group = tmp app.descriptions = data.get_descriptions() # get screenshots tmp = data.get_screenshots() for image in tmp: print 'DOWNLOADING\t', image app.add_screenshot_url(image) elif app.requires_appdata: print 'IGNORE\t', f, '\t', app.app_id_full, 'requires AppData to be included' continue # use the homepage to filter out same more generic apps if not app.project_group: # GNOME project_urls = [ 'http*://*.gnome.org*', 'http://gnome-*.sourceforge.net/'] for m in project_urls: if fnmatch.fnmatch(app.homepage_url, m): app.project_group = "GNOME" # KDE project_urls = [ 'http*://*.kde.org*', 'http://*kde-apps.org/*' ] for m in project_urls: if fnmatch.fnmatch(app.homepage_url, m): app.project_group = "KDE" # XFCE project_urls = [ 'http://*xfce.org*' ] for m in project_urls: if fnmatch.fnmatch(app.homepage_url, m): app.project_group = "XFCE" # LXDE project_urls = [ 'http://lxde.org*', 'http://lxde.sourceforge.net/*' ] for m in project_urls: if fnmatch.fnmatch(app.homepage_url, m): app.project_group = "LXDE" # MATE project_urls = [ 'http://*mate-desktop.org*' ] for m in project_urls: if fnmatch.fnmatch(app.homepage_url, m): app.project_group = "MATE" # print that we auto-added it if app.project_group: print 'INFO\t', f, '\t', app.app_id, 'assigned', app.project_group # we got something useful if not has_valid_content: has_valid_content = True # Do not include apps without a name if not 'C' in app.names: print 'IGNORE\t', f, '\t', "no Name" continue # Do not include apps without a summary if not 'C' in app.comments: print 'IGNORE\t', f, '\t', "no Comment" continue # Do not include apps without an icon if not app.icon: print 'IGNORE\t', f, '\t', "Icon unspecified" continue # write content app.write(xml) # create AppStream XML xml.write("</applications>\n") xml.close() if not has_valid_content: os.remove(xml_output_file) # create AppStream icon tar if has_valid_content: output_file = "./appstream/%s-icons.tar" % pkg.name print 'WRITING\t', output_file tar = tarfile.open(output_file, "w") files = glob.glob("./icons/*.png") for f in files: tar.add(f, arcname=f.split('/')[-1]) tar.close() # remove tmp if not os.getenv('APPSTREAM_DEBUG'): shutil.rmtree('./tmp') shutil.rmtree('./icons')
def add_appdata_file(self): # do we have an AppData file? filename = './tmp/usr/share/appdata/' + self.app_id + '.appdata.xml' fn_extra = '../appdata-extra/' + self.type_id + '/' + self.app_id + '.appdata.xml' if os.path.exists(filename) and os.path.exists(fn_extra): self.log.write(LoggerItem.INFO, "deleting %s as upstream AppData file exists" % fn_extra) os.remove(fn_extra) # just use the extra file in places of the missing upstream one if os.path.exists(fn_extra): filename = fn_extra # need to extract details if not os.path.exists(filename): return False data = AppData() if not data.extract(filename): self.log.write(LoggerItem.WARNING, "AppData file '%s' could not be parsed" % filename) return False # check AppData file validates enable_validation = self.type_id != 'font' if enable_validation and os.path.exists('/usr/bin/appdata-validate'): env = os.environ p = subprocess.Popen(['/usr/bin/appdata-validate', '--relax', filename], cwd='.', env=env, stdout=subprocess.PIPE) p.wait() if p.returncode: for line in p.stdout: line = line.replace('\n', '').decode('utf-8') self.log.write(LoggerItem.WARNING, "AppData did not validate: %s" % line) # check the id matches if data.get_id() != self.app_id and data.get_id() != self.app_id_full: self.log.write(LoggerItem.WARNING, "The AppData id does not match: " + self.app_id) return False # check the licence is okay if data.get_licence() not in self.cfg.get_content_licences(): self.log.write(LoggerItem.WARNING, "The AppData licence is not okay for " + self.app_id + ': \'' + data.get_licence() + '\'') return False # if we have an override, use it for all languages tmp = data.get_names() if tmp: self.names = tmp # if we have an override, use it for all languages tmp = data.get_summaries() if tmp: self.comments = tmp # get metadata tmp = data.get_metadata() if tmp: # and extra packages we want to add in? if 'ExtraPackages' in tmp: for pkg in tmp['ExtraPackages'].split(','): if pkg not in self.pkgnames: self.pkgnames.append(pkg) del tmp['ExtraPackages'] self.metadata.update(tmp) # get optional bits tmp = data.get_urls() if tmp: for key in tmp: self.urls[key] = tmp[key] tmp = data.get_project_group() if tmp: self.project_group = tmp try: self.descriptions = data.get_descriptions() except StandardError, e: self.log.write(LoggerItem.WARNING, "failed to add description: %s" % str(e))