From 133ca624eb22687d13c42efa62676b4906beca39 Mon Sep 17 00:00:00 2001 From: Peter Kjellerstedt Date: Wed, 8 Jan 2020 04:44:49 +0100 Subject: oe-pkgdata-browser: Rename from pkgdataui.py (From OE-Core rev: 2171ad1525f1fb0c13174a1bb77128c99b5dac35) Signed-off-by: Peter Kjellerstedt Signed-off-by: Richard Purdie --- scripts/oe-pkgdata-browser | 241 ++++++++++++++++++++++++++++ scripts/oe-pkgdata-browser.glade | 335 +++++++++++++++++++++++++++++++++++++++ scripts/pkgdataui.glade | 335 --------------------------------------- scripts/pkgdataui.py | 241 ---------------------------- 4 files changed, 576 insertions(+), 576 deletions(-) create mode 100755 scripts/oe-pkgdata-browser create mode 100644 scripts/oe-pkgdata-browser.glade delete mode 100644 scripts/pkgdataui.glade delete mode 100755 scripts/pkgdataui.py (limited to 'scripts') diff --git a/scripts/oe-pkgdata-browser b/scripts/oe-pkgdata-browser new file mode 100755 index 0000000000..09544fff82 --- /dev/null +++ b/scripts/oe-pkgdata-browser @@ -0,0 +1,241 @@ +#! /usr/bin/env python3 + +import os, sys, enum, ast + +scripts_path = os.path.dirname(os.path.realpath(__file__)) +lib_path = scripts_path + '/lib' +sys.path = sys.path + [lib_path] + +import scriptpath +bitbakepath = scriptpath.add_bitbake_lib_path() +if not bitbakepath: + print("Unable to find bitbake by searching parent directory of this script or PATH") + sys.exit(1) +import bb + +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk, GObject + +RecipeColumns = enum.IntEnum("RecipeColumns", {"Recipe": 0}) +PackageColumns = enum.IntEnum("PackageColumns", {"Package": 0, "Size": 1}) +FileColumns = enum.IntEnum("FileColumns", {"Filename": 0, "Size": 1}) + +import time +def timeit(f): + def timed(*args, **kw): + ts = time.time() + print ("func:%r calling" % f.__name__) + result = f(*args, **kw) + te = time.time() + print ('func:%r args:[%r, %r] took: %2.4f sec' % \ + (f.__name__, args, kw, te-ts)) + return result + return timed + +def human_size(nbytes): + import math + suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + human = nbytes + rank = 0 + if nbytes != 0: + rank = int((math.log10(nbytes)) / 3) + rank = min(rank, len(suffixes) - 1) + human = nbytes / (1000.0 ** rank) + f = ('%.2f' % human).rstrip('0').rstrip('.') + return '%s %s' % (f, suffixes[rank]) + +def load(filename, suffix=None): + from configparser import ConfigParser + from itertools import chain + + parser = ConfigParser() + if suffix: + parser.optionxform = lambda option: option.replace("_" + suffix, "") + with open(filename) as lines: + lines = chain(("[fake]",), lines) + parser.read_file(lines) + + # TODO extract the data and put it into a real dict so we can transform some + # values to ints? + return parser["fake"] + +def find_pkgdata(): + import subprocess + output = subprocess.check_output(("bitbake", "-e"), universal_newlines=True) + for line in output.splitlines(): + if line.startswith("PKGDATA_DIR="): + return line.split("=", 1)[1].strip("\'\"") + # TODO exception or something + return None + +def packages_in_recipe(pkgdata, recipe): + """ + Load the recipe pkgdata to determine the list of runtime packages. + """ + data = load(os.path.join(pkgdata, recipe)) + packages = data["PACKAGES"].split() + return packages + +def load_runtime_package(pkgdata, package): + return load(os.path.join(pkgdata, "runtime", package), suffix=package) + +def recipe_from_package(pkgdata, package): + data = load(os.path.join(pkgdata, "runtime", package), suffix=package) + return data["PN"] + +def summary(data): + s = "" + s += "{0[PKG]} {0[PKGV]}-{0[PKGR]}\n{0[LICENSE]}\n{0[SUMMARY]}\n".format(data) + + return s + + +class PkgUi(): + def __init__(self, pkgdata): + self.pkgdata = pkgdata + self.current_recipe = None + self.recipe_iters = {} + self.package_iters = {} + + builder = Gtk.Builder() + builder.add_from_file(os.path.join(os.path.dirname(__file__), "oe-pkgdata-browser.glade")) + + self.window = builder.get_object("window") + self.window.connect("delete-event", Gtk.main_quit) + + self.recipe_store = builder.get_object("recipe_store") + self.recipe_view = builder.get_object("recipe_view") + self.package_store = builder.get_object("package_store") + self.package_view = builder.get_object("package_view") + + # Somehow resizable does not get set via builder xml + package_name_column = builder.get_object("package_name_column") + package_name_column.set_resizable(True) + file_name_column = builder.get_object("file_name_column") + file_name_column.set_resizable(True) + + self.recipe_view.get_selection().connect("changed", self.on_recipe_changed) + self.package_view.get_selection().connect("changed", self.on_package_changed) + + self.package_store.set_sort_column_id(PackageColumns.Package, Gtk.SortType.ASCENDING) + builder.get_object("package_size_column").set_cell_data_func(builder.get_object("package_size_cell"), lambda column, cell, model, iter, data: cell.set_property("text", human_size(model[iter][PackageColumns.Size]))) + + self.label = builder.get_object("label1") + self.depends_label = builder.get_object("depends_label") + self.recommends_label = builder.get_object("recommends_label") + self.suggests_label = builder.get_object("suggests_label") + self.provides_label = builder.get_object("provides_label") + + self.depends_label.connect("activate-link", self.on_link_activate) + self.recommends_label.connect("activate-link", self.on_link_activate) + self.suggests_label.connect("activate-link", self.on_link_activate) + + self.file_store = builder.get_object("file_store") + self.file_store.set_sort_column_id(FileColumns.Filename, Gtk.SortType.ASCENDING) + self.files_view = builder.get_object("files_scrollview") + self.files_label = builder.get_object("files_label") + + self.load_recipes() + + self.recipe_view.set_cursor(Gtk.TreePath.new_first()) + + self.window.show() + + def on_link_activate(self, label, url_string): + from urllib.parse import urlparse + url = urlparse(url_string) + if url.scheme == "package": + package = url.path + recipe = recipe_from_package(self.pkgdata, package) + + it = self.recipe_iters[recipe] + path = self.recipe_store.get_path(it) + self.recipe_view.set_cursor(path) + self.recipe_view.scroll_to_cell(path) + + self.on_recipe_changed(self.recipe_view.get_selection()) + + it = self.package_iters[package] + path = self.package_store.get_path(it) + self.package_view.set_cursor(path) + self.package_view.scroll_to_cell(path) + + return True + else: + return False + + def on_recipe_changed(self, selection): + self.package_store.clear() + self.package_iters = {} + + (model, it) = selection.get_selected() + if not it: + return + + recipe = model[it][RecipeColumns.Recipe] + for package in packages_in_recipe(self.pkgdata, recipe): + # TODO also show PKG after debian-renaming? + data = load_runtime_package(self.pkgdata, package) + # TODO stash data to avoid reading in on_package_changed + self.package_iters[package] = self.package_store.append([package, int(data["PKGSIZE"])]) + + def on_package_changed(self, selection): + self.label.set_text("") + self.file_store.clear() + self.depends_label.hide() + self.recommends_label.hide() + self.suggests_label.hide() + + (model, it) = selection.get_selected() + if it is None: + return + + package = model[it][PackageColumns.Package] + data = load_runtime_package(self.pkgdata, package) + + self.label.set_text(summary(data)) + + files = ast.literal_eval(data["FILES_INFO"]) + if files: + self.files_label.set_text("{0} files take {1}.".format(len(files), human_size(int(data["PKGSIZE"])))) + self.files_view.show() + for filename, size in files.items(): + self.file_store.append([filename, size]) + else: + self.files_view.hide() + self.files_label.set_text("This package has no files.") + + def update_deps(field, prefix, label, clickable=True): + if field in data: + l = [] + for name, version in bb.utils.explode_dep_versions2(data[field]).items(): + if clickable: + l.append("{0} {1}".format(name, " ".join(version))) + else: + l.append("{0} {1}".format(name, " ".join(version))) + label.set_markup(prefix + ", ".join(l)) + label.show() + else: + label.hide() + update_deps("RDEPENDS", "Depends: ", self.depends_label) + update_deps("RRECOMMENDS", "Recommends: ", self.recommends_label) + update_deps("RSUGGESTS", "Suggests: ", self.suggests_label) + update_deps("RPROVIDES", "Provides: ", self.provides_label, clickable=False) + + def load_recipes(self): + for recipe in sorted(os.listdir(pkgdata)): + if os.path.isfile(os.path.join(pkgdata, recipe)): + self.recipe_iters[recipe] = self.recipe_store.append([recipe]) + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description='pkgdata browser') + parser.add_argument('-p', '--pkgdata', help="Optional location of pkgdata") + + args = parser.parse_args() + pkgdata = args.pkgdata if args.pkgdata else find_pkgdata() + # TODO assert pkgdata is a directory + window = PkgUi(pkgdata) + Gtk.main() diff --git a/scripts/oe-pkgdata-browser.glade b/scripts/oe-pkgdata-browser.glade new file mode 100644 index 0000000000..04e987b975 --- /dev/null +++ b/scripts/oe-pkgdata-browser.glade @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + Package Data Browser + accessories-dictionary + True + + + True + False + 4 + 4 + 4 + 4 + vertical + 4 + + + False + pkgdata_store + 1 + + + + 0 + + + + + False + True + 0 + + + + + True + True + 200 + True + + + True + True + in + 100 + + + True + True + recipe_store + 0 + True + False + + + + + + fixed + Recipe + + + + 0 + + + + + + + + + False + True + + + + + True + True + 200 + True + + + True + True + in + 100 + + + True + True + package_store + 0 + False + + + + + + True + autosize + Package + 0 + + + + 0 + + + + + + + True + autosize + Size + 1 + + + + + + + + + + False + True + + + + + True + False + 4 + vertical + 4 + + + True + False + 0 + label + + + False + True + 0 + + + + + True + False + 0 + depends_label + True + False + + + False + True + 1 + + + + + True + False + 0 + recs_label + True + False + + + False + True + 2 + + + + + True + False + 0 + suggests_label + True + False + + + False + True + 3 + + + + + True + False + 0 + provides_label + True + False + + + False + True + 4 + + + + + True + False + 0 + files_label + end + + + False + True + 5 + + + + + True + True + in + + + True + True + file_store + True + 0 + False + + + + + + Name + True + 0 + + + rgba(0,0,0,0) + + + 0 + + + + + + + Size + True + 1 + + + + 1 + + + + + + + + + True + True + 6 + + + + + True + True + + + + + True + True + + + + + True + True + 1 + + + + + + diff --git a/scripts/pkgdataui.glade b/scripts/pkgdataui.glade deleted file mode 100644 index 04e987b975..0000000000 --- a/scripts/pkgdataui.glade +++ /dev/null @@ -1,335 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - False - Package Data Browser - accessories-dictionary - True - - - True - False - 4 - 4 - 4 - 4 - vertical - 4 - - - False - pkgdata_store - 1 - - - - 0 - - - - - False - True - 0 - - - - - True - True - 200 - True - - - True - True - in - 100 - - - True - True - recipe_store - 0 - True - False - - - - - - fixed - Recipe - - - - 0 - - - - - - - - - False - True - - - - - True - True - 200 - True - - - True - True - in - 100 - - - True - True - package_store - 0 - False - - - - - - True - autosize - Package - 0 - - - - 0 - - - - - - - True - autosize - Size - 1 - - - - - - - - - - False - True - - - - - True - False - 4 - vertical - 4 - - - True - False - 0 - label - - - False - True - 0 - - - - - True - False - 0 - depends_label - True - False - - - False - True - 1 - - - - - True - False - 0 - recs_label - True - False - - - False - True - 2 - - - - - True - False - 0 - suggests_label - True - False - - - False - True - 3 - - - - - True - False - 0 - provides_label - True - False - - - False - True - 4 - - - - - True - False - 0 - files_label - end - - - False - True - 5 - - - - - True - True - in - - - True - True - file_store - True - 0 - False - - - - - - Name - True - 0 - - - rgba(0,0,0,0) - - - 0 - - - - - - - Size - True - 1 - - - - 1 - - - - - - - - - True - True - 6 - - - - - True - True - - - - - True - True - - - - - True - True - 1 - - - - - - diff --git a/scripts/pkgdataui.py b/scripts/pkgdataui.py deleted file mode 100755 index f6b23fc0be..0000000000 --- a/scripts/pkgdataui.py +++ /dev/null @@ -1,241 +0,0 @@ -#! /usr/bin/env python3 - -import os, sys, enum, ast - -scripts_path = os.path.dirname(os.path.realpath(__file__)) -lib_path = scripts_path + '/lib' -sys.path = sys.path + [lib_path] - -import scriptpath -bitbakepath = scriptpath.add_bitbake_lib_path() -if not bitbakepath: - print("Unable to find bitbake by searching parent directory of this script or PATH") - sys.exit(1) -import bb - -import gi -gi.require_version('Gtk', '3.0') -from gi.repository import Gtk, Gdk, GObject - -RecipeColumns = enum.IntEnum("RecipeColumns", {"Recipe": 0}) -PackageColumns = enum.IntEnum("PackageColumns", {"Package": 0, "Size": 1}) -FileColumns = enum.IntEnum("FileColumns", {"Filename": 0, "Size": 1}) - -import time -def timeit(f): - def timed(*args, **kw): - ts = time.time() - print ("func:%r calling" % f.__name__) - result = f(*args, **kw) - te = time.time() - print ('func:%r args:[%r, %r] took: %2.4f sec' % \ - (f.__name__, args, kw, te-ts)) - return result - return timed - -def human_size(nbytes): - import math - suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] - human = nbytes - rank = 0 - if nbytes != 0: - rank = int((math.log10(nbytes)) / 3) - rank = min(rank, len(suffixes) - 1) - human = nbytes / (1000.0 ** rank) - f = ('%.2f' % human).rstrip('0').rstrip('.') - return '%s %s' % (f, suffixes[rank]) - -def load(filename, suffix=None): - from configparser import ConfigParser - from itertools import chain - - parser = ConfigParser() - if suffix: - parser.optionxform = lambda option: option.replace("_" + suffix, "") - with open(filename) as lines: - lines = chain(("[fake]",), lines) - parser.read_file(lines) - - # TODO extract the data and put it into a real dict so we can transform some - # values to ints? - return parser["fake"] - -def find_pkgdata(): - import subprocess - output = subprocess.check_output(("bitbake", "-e"), universal_newlines=True) - for line in output.splitlines(): - if line.startswith("PKGDATA_DIR="): - return line.split("=", 1)[1].strip("\'\"") - # TODO exception or something - return None - -def packages_in_recipe(pkgdata, recipe): - """ - Load the recipe pkgdata to determine the list of runtime packages. - """ - data = load(os.path.join(pkgdata, recipe)) - packages = data["PACKAGES"].split() - return packages - -def load_runtime_package(pkgdata, package): - return load(os.path.join(pkgdata, "runtime", package), suffix=package) - -def recipe_from_package(pkgdata, package): - data = load(os.path.join(pkgdata, "runtime", package), suffix=package) - return data["PN"] - -def summary(data): - s = "" - s += "{0[PKG]} {0[PKGV]}-{0[PKGR]}\n{0[LICENSE]}\n{0[SUMMARY]}\n".format(data) - - return s - - -class PkgUi(): - def __init__(self, pkgdata): - self.pkgdata = pkgdata - self.current_recipe = None - self.recipe_iters = {} - self.package_iters = {} - - builder = Gtk.Builder() - builder.add_from_file(os.path.join(os.path.dirname(__file__), "pkgdataui.glade")) - - self.window = builder.get_object("window") - self.window.connect("delete-event", Gtk.main_quit) - - self.recipe_store = builder.get_object("recipe_store") - self.recipe_view = builder.get_object("recipe_view") - self.package_store = builder.get_object("package_store") - self.package_view = builder.get_object("package_view") - - # Somehow resizable does not get set via builder xml - package_name_column = builder.get_object("package_name_column") - package_name_column.set_resizable(True) - file_name_column = builder.get_object("file_name_column") - file_name_column.set_resizable(True) - - self.recipe_view.get_selection().connect("changed", self.on_recipe_changed) - self.package_view.get_selection().connect("changed", self.on_package_changed) - - self.package_store.set_sort_column_id(PackageColumns.Package, Gtk.SortType.ASCENDING) - builder.get_object("package_size_column").set_cell_data_func(builder.get_object("package_size_cell"), lambda column, cell, model, iter, data: cell.set_property("text", human_size(model[iter][PackageColumns.Size]))) - - self.label = builder.get_object("label1") - self.depends_label = builder.get_object("depends_label") - self.recommends_label = builder.get_object("recommends_label") - self.suggests_label = builder.get_object("suggests_label") - self.provides_label = builder.get_object("provides_label") - - self.depends_label.connect("activate-link", self.on_link_activate) - self.recommends_label.connect("activate-link", self.on_link_activate) - self.suggests_label.connect("activate-link", self.on_link_activate) - - self.file_store = builder.get_object("file_store") - self.file_store.set_sort_column_id(FileColumns.Filename, Gtk.SortType.ASCENDING) - self.files_view = builder.get_object("files_scrollview") - self.files_label = builder.get_object("files_label") - - self.load_recipes() - - self.recipe_view.set_cursor(Gtk.TreePath.new_first()) - - self.window.show() - - def on_link_activate(self, label, url_string): - from urllib.parse import urlparse - url = urlparse(url_string) - if url.scheme == "package": - package = url.path - recipe = recipe_from_package(self.pkgdata, package) - - it = self.recipe_iters[recipe] - path = self.recipe_store.get_path(it) - self.recipe_view.set_cursor(path) - self.recipe_view.scroll_to_cell(path) - - self.on_recipe_changed(self.recipe_view.get_selection()) - - it = self.package_iters[package] - path = self.package_store.get_path(it) - self.package_view.set_cursor(path) - self.package_view.scroll_to_cell(path) - - return True - else: - return False - - def on_recipe_changed(self, selection): - self.package_store.clear() - self.package_iters = {} - - (model, it) = selection.get_selected() - if not it: - return - - recipe = model[it][RecipeColumns.Recipe] - for package in packages_in_recipe(self.pkgdata, recipe): - # TODO also show PKG after debian-renaming? - data = load_runtime_package(self.pkgdata, package) - # TODO stash data to avoid reading in on_package_changed - self.package_iters[package] = self.package_store.append([package, int(data["PKGSIZE"])]) - - def on_package_changed(self, selection): - self.label.set_text("") - self.file_store.clear() - self.depends_label.hide() - self.recommends_label.hide() - self.suggests_label.hide() - - (model, it) = selection.get_selected() - if it is None: - return - - package = model[it][PackageColumns.Package] - data = load_runtime_package(self.pkgdata, package) - - self.label.set_text(summary(data)) - - files = ast.literal_eval(data["FILES_INFO"]) - if files: - self.files_label.set_text("{0} files take {1}.".format(len(files), human_size(int(data["PKGSIZE"])))) - self.files_view.show() - for filename, size in files.items(): - self.file_store.append([filename, size]) - else: - self.files_view.hide() - self.files_label.set_text("This package has no files.") - - def update_deps(field, prefix, label, clickable=True): - if field in data: - l = [] - for name, version in bb.utils.explode_dep_versions2(data[field]).items(): - if clickable: - l.append("{0} {1}".format(name, " ".join(version))) - else: - l.append("{0} {1}".format(name, " ".join(version))) - label.set_markup(prefix + ", ".join(l)) - label.show() - else: - label.hide() - update_deps("RDEPENDS", "Depends: ", self.depends_label) - update_deps("RRECOMMENDS", "Recommends: ", self.recommends_label) - update_deps("RSUGGESTS", "Suggests: ", self.suggests_label) - update_deps("RPROVIDES", "Provides: ", self.provides_label, clickable=False) - - def load_recipes(self): - for recipe in sorted(os.listdir(pkgdata)): - if os.path.isfile(os.path.join(pkgdata, recipe)): - self.recipe_iters[recipe] = self.recipe_store.append([recipe]) - -if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser(description='pkgdata browser') - parser.add_argument('-p', '--pkgdata', help="Optional location of pkgdata") - - args = parser.parse_args() - pkgdata = args.pkgdata if args.pkgdata else find_pkgdata() - # TODO assert pkgdata is a directory - window = PkgUi(pkgdata) - Gtk.main() -- cgit v1.2.3-54-g00ecf