From 77d0f171546ed989292cb4983f9fdbe7429c475d Mon Sep 17 00:00:00 2001 From: guinux Date: Fri, 8 Nov 2013 15:21:38 +0100 Subject: [PATCH] add AUR support :D --- gui/manager.ui | 40 +++- pamac-daemon.py | 43 +++- pamac-manager.py | 185 +++++++++++++---- pamac-updater.py | 44 +++- pamac.pot | 201 ++++++++++-------- pamac/aur.py | 161 +++++++++++++++ pamac/transaction.py | 483 +++++++++++++++++++++++++++++++++++-------- 7 files changed, 908 insertions(+), 249 deletions(-) create mode 100644 pamac/aur.py diff --git a/gui/manager.ui b/gui/manager.ui index 9f6a683..0847b11 100644 --- a/gui/manager.ui +++ b/gui/manager.ui @@ -1,5 +1,5 @@ - + @@ -248,10 +248,27 @@ 0 + + + Search in AUR + True + True + False + False + 0 + True + + + False + True + 1 + + True True + out True @@ -282,7 +299,7 @@ True True - 1 + 2 @@ -511,6 +528,19 @@ + + + True + autosize + Repo + True + 0 + + + + + + True @@ -537,7 +567,7 @@ True True - + True True @@ -624,7 +654,7 @@ - + True True @@ -689,7 +719,7 @@ - + True True diff --git a/pamac-daemon.py b/pamac-daemon.py index b535b63..22aac01 100755 --- a/pamac-daemon.py +++ b/pamac-daemon.py @@ -9,7 +9,7 @@ import re import pyalpm from multiprocessing import Process -from pamac import config, common +from pamac import config, common, aur # i18n import gettext @@ -159,7 +159,7 @@ class PamacDBusService(dbus.service.Object): elif event == 'ALPM_EVENT_UPGRADE_START': string = _('Upgrading {pkgname}').format(pkgname = tupel[1].name) action = string+'...' - action_long = '{} ({} -> {})\n'.format(string, tupel[1].version, tupel[0].version) + action_long = '{} ({} => {})\n'.format(string, tupel[1].version, tupel[0].version) icon = '/usr/share/pamac/icons/24x24/status/package-update.png' elif event == 'ALPM_EVENT_UPGRADE_DONE': formatted_event = 'Upgraded {pkgname} ({oldversion} -> {newversion})'.format(pkgname = tupel[1].name, oldversion = tupel[1].version, newversion = tupel[0].version) @@ -167,7 +167,7 @@ class PamacDBusService(dbus.service.Object): elif event == 'ALPM_EVENT_DOWNGRADE_START': string = _('Downgrading {pkgname}').format(pkgname = tupel[1].name) action = string+'...' - action_long = '{} ({} -> {})'.format(string, tupel[1].version, tupel[0].version) + action_long = '{} ({} => {})'.format(string, tupel[1].version, tupel[0].version) icon = '/usr/share/pamac/icons/24x24/status/package-add.png' elif event == 'ALPM_EVENT_DOWNGRADE_DONE': formatted_event = 'Downgraded {pkgname} ({oldversion} -> {newversion})'.format(pkgname = tupel[1].name, oldversion = tupel[1].version, newversion = tupel[0].version) @@ -315,7 +315,10 @@ class PamacDBusService(dbus.service.Object): icon = '/usr/share/pamac/icons/24x24/status/package-download.png' if self.total_size > 0: percent = round((_transferred+self.already_transferred)/self.total_size, 2) - target = '{transferred}/{size}'.format(transferred = common.format_size(_transferred+self.already_transferred), size = common.format_size(self.total_size)) + if _transferred+self.already_transferred <= self.total_size: + target = '{transferred}/{size}'.format(transferred = common.format_size(_transferred+self.already_transferred), size = common.format_size(self.total_size)) + else: + target = '' else: percent = round(_transferred/_total, 2) target = '' @@ -388,10 +391,22 @@ class PamacDBusService(dbus.service.Object): updates += 1 if not updates: for pkg in self.handle.get_localdb().pkgcache: - candidate = pyalpm.sync_newversion(pkg, self.handle.get_syncdbs()) - if candidate: - if not candidate.name in _ignorepkgs: + if not pkg.name in _ignorepkgs: + candidate = pyalpm.sync_newversion(pkg, self.handle.get_syncdbs()) + if candidate: updates += 1 + else: + sync_pkg = None + for db in self.handle.get_syncdbs(): + sync_pkg = db.get_pkg(pkg.name) + if sync_pkg: + break + if not sync_pkg: + aur_pkg = aur.infos(pkg.name) + if aur_pkg: + comp = pyalpm.vercmp(aur_pkg.version, pkg.version) + if comp == 1: + updates += 1 self.EmitAvailableUpdates(updates) @dbus.service.method('org.manjaro.pamac', 'b', 's', async_callbacks=('success', 'nosuccess')) @@ -602,15 +617,21 @@ class PamacDBusService(dbus.service.Object): @dbus.service.method('org.manjaro.pamac', '', 'a(ss)') def To_Remove(self): liste = [] - for pkg in self.t.to_remove: - liste.append((pkg.name, pkg.version)) + try: + for pkg in self.t.to_remove: + liste.append((pkg.name, pkg.version)) + except: + pass return liste @dbus.service.method('org.manjaro.pamac', '', 'a(ssi)') def To_Add(self): liste = [] - for pkg in self.t.to_add: - liste.append((pkg.name, pkg.version, pkg.download_size)) + try: + for pkg in self.t.to_add: + liste.append((pkg.name, pkg.version, pkg.download_size)) + except: + pass return liste @dbus.service.method('org.manjaro.pamac', '', 's', async_callbacks=('success', 'nosuccess')) diff --git a/pamac-manager.py b/pamac-manager.py index a4e5a3a..ed1462b 100755 --- a/pamac-manager.py +++ b/pamac-manager.py @@ -9,7 +9,7 @@ import pyalpm import dbus from time import strftime, localtime -from pamac import config, common, transaction +from pamac import config, common, transaction, aur # i18n import gettext @@ -23,25 +23,30 @@ interface = transaction.interface interface.add_from_file('/usr/share/pamac/gui/manager.ui') ManagerWindow = interface.get_object("ManagerWindow") -details_list = interface.get_object('details_list') deps_list = interface.get_object('deps_list') +details_list = interface.get_object('details_list') files_textview = interface.get_object('files_textview') +deps_scrolledwindow = interface.get_object('deps_scrolledwindow') files_scrolledwindow = interface.get_object('files_scrolledwindow') +details_scrolledwindow = interface.get_object('details_scrolledwindow') name_label = interface.get_object('name_label') desc_label = interface.get_object('desc_label') link_label = interface.get_object('link_label') licenses_label = interface.get_object('licenses_label') search_entry = interface.get_object('search_entry') +search_aur_button = interface.get_object('search_aur_button') search_list = interface.get_object('search_list') search_selection = interface.get_object('search_treeview_selection') packages_list_treeview = interface.get_object('packages_list_treeview') state_column = interface.get_object('state_column') name_column = interface.get_object('name_column') version_column = interface.get_object('version_column') +repo_column = interface.get_object('repo_column') size_column = interface.get_object('size_column') state_rendererpixbuf = interface.get_object('state_rendererpixbuf') name_renderertext = interface.get_object('name_renderertext') version_renderertext = interface.get_object('version_renderertext') +repo_renderertext = interface.get_object('repo_renderertext') size_renderertext = interface.get_object('size_renderertext') list_selection = interface.get_object('list_treeview_selection') groups_list = interface.get_object('groups_list') @@ -84,6 +89,8 @@ def state_column_display_func(column, cell, treemodel, treeiter, data): pixbuf = installed_icon elif treemodel[treeiter][0].name in transaction.to_add: pixbuf = to_install_icon + elif treemodel[treeiter][0] in transaction.to_build: + pixbuf = to_install_icon else: pixbuf = uninstalled_icon cell.set_property("pixbuf", pixbuf) @@ -97,7 +104,7 @@ def state_column_sort_func(treemodel, treeiter1, treeiter2, data): num2 = 1 else: num2 = 0 - return num2 - num1 + return num1 - num2 def name_column_display_func(column, cell, treemodel, treeiter, data): if treemodel[treeiter][0] == _('No package found'): @@ -124,15 +131,44 @@ def version_column_display_func(column, cell, treemodel, treeiter, data): def version_column_sort_func(treemodel, treeiter1, treeiter2, data): return pyalpm.vercmp(treemodel[treeiter1][0].version, treemodel[treeiter2][0].version) -def size_column_display_func(column, cell, treemodel, treeiter, data): +def repo_column_display_func(column, cell, treemodel, treeiter, data): if treemodel[treeiter][0] == _('No package found'): cell.set_property("text", '') else: + cell.set_property("text", treemodel[treeiter][0].db.name) + +def repo_column_sort_func(treemodel, treeiter1, treeiter2, data): + servers = list(config.pacman_conf.repos.keys()) + servers.insert(0, 'local') + # display AUR at last + if treemodel[treeiter1][0].db.name == 'AUR': + num1 = 99 + else: + num1 = servers.index(treemodel[treeiter1][0].db.name) + # display AUR at last + if treemodel[treeiter2][0].db.name == 'AUR': + num2 = 99 + else: + num2 = servers.index(treemodel[treeiter2][0].db.name) + return num1 - num2 + +def size_column_display_func(column, cell, treemodel, treeiter, data): + if treemodel[treeiter][0] == _('No package found'): + cell.set_property("text", '') + elif treemodel[treeiter][0].isize: cell.set_property("text", common.format_size(treemodel[treeiter][0].isize)) + else: + cell.set_property("text", '') def size_column_sort_func(treemodel, treeiter1, treeiter2, data): - num1 = treemodel[treeiter1][0].isize - num2 = treemodel[treeiter2][0].isize + if treemodel[treeiter1][0].isize: + num1 = treemodel[treeiter1][0].isize + else: + num1 = 0 + if treemodel[treeiter2][0].isize: + num2 = treemodel[treeiter2][0].isize + else: + num2 = 0 return num1 - num2 def update_lists(): @@ -215,28 +251,35 @@ def get_repo_list(repo): repos_dict[repo].append([pkg]) return repos_dict[repo] -def search_pkgs(search_string): +def search_pkgs(data_tupel): global search_dict - if search_string in search_dict.keys(): - return search_dict[search_string] + search_string = data_tupel[0] + search_aur = data_tupel[1] + if (search_string, search_aur) in search_dict.keys(): + return search_dict[(search_string, search_aur)] else: - search_dict[search_string] = Gtk.ListStore(object) + search_dict[(search_string, search_aur)] = Gtk.ListStore(object) names_list = [] for pkg in transaction.localdb.search(*search_string.split()): if not pkg.name in names_list: names_list.append(pkg.name) - search_dict[search_string].append([pkg]) + search_dict[(search_string, search_aur)].append([pkg]) for db in transaction.syncdbs: for pkg in db.search(*search_string.split()): if not pkg.name in names_list: names_list.append(pkg.name) - search_dict[search_string].append([pkg]) + search_dict[(search_string, search_aur)].append([pkg]) + if search_aur: + for pkg in aur.search(*search_string.split()): + if not pkg.name in names_list: + names_list.append(pkg.name) + search_dict[(search_string, search_aur)].append([pkg]) if not names_list: - search_dict[search_string].append([_('No package found')]) + search_dict[(search_string, search_aur)].append([_('No package found')]) else: if not search_string in [row[0] for row in search_list]: search_list.append([search_string]) - return search_dict[search_string] + return search_dict[(search_string, search_aur)] def get_uninstalled_pkgs(): pkgs_list = [] @@ -266,6 +309,7 @@ def refresh_packages_list(liststore): state_column.set_sort_indicator(False) name_column.set_sort_indicator(True) version_column.set_sort_indicator(False) + repo_column.set_sort_indicator(False) size_column.set_sort_indicator(False) packages_list_treeview.thaw_child_notify() ManagerWindow.get_window().set_cursor(None) @@ -356,14 +400,20 @@ def handle_error(error): if response: transaction.ErrorDialog.hide() transaction.progress_buffer.delete(transaction.progress_buffer.get_start_iter(),transaction.progress_buffer.get_end_iter()) + transaction.Release() transaction.get_handle() transaction.update_dbs() transaction.to_add.clear() transaction.to_remove.clear() + transaction.to_update.clear() transaction.to_load.clear() + transaction.to_build.clear() def handle_reply(reply): - if reply: + if transaction.to_build: + transaction.build_next() + elif reply: + transaction.Release() transaction.ProgressCloseButton.set_visible(True) transaction.action_icon.set_from_icon_name('dialog-information', Gtk.IconSize.BUTTON) transaction.progress_label.set_text(str(reply)) @@ -374,7 +424,7 @@ def handle_reply(reply): transaction.ProgressWindow.hide() while Gtk.events_pending(): Gtk.main_iteration() - error = transaction.sysupgrade(True) + error = transaction.sysupgrade() ManagerWindow.get_window().set_cursor(None) if error: handle_error(error) @@ -383,9 +433,12 @@ def handle_reply(reply): transaction.update_dbs() transaction.to_add.clear() transaction.to_remove.clear() + transaction.to_update.clear() + transaction.to_load.clear() + transaction.to_build.clear() global search_dict global groups_dict - global states_dict + global states_dict global repos_dict search_dict = {} groups_dict = {} @@ -411,6 +464,9 @@ def on_TransCancelButton_clicked(*args): while Gtk.events_pending(): Gtk.main_iteration() transaction.Release() + transaction.to_update.clear() + # do it because deps are also added in to_build when check_to_build + transaction.to_build.clear() if current_filter[0]: refresh_packages_list(current_filter[0](current_filter[1])) @@ -420,12 +476,25 @@ def on_ProgressCloseButton_clicked(*args): Gtk.main_iteration() transaction.progress_buffer.delete(transaction.progress_buffer.get_start_iter(),transaction.progress_buffer.get_end_iter()) ManagerWindow.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) - error = transaction.sysupgrade(True) + while Gtk.events_pending(): + Gtk.main_iteration() + error = transaction.sysupgrade() ManagerWindow.get_window().set_cursor(None) if error: handle_error(error) def on_ProgressCancelButton_clicked(*args): + ManagerWindow.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) + while Gtk.events_pending(): + Gtk.main_iteration() + transaction.progress_buffer.delete(transaction.progress_buffer.get_start_iter(),transaction.progress_buffer.get_end_iter()) + transaction.cancel_download = True + if transaction.build_proc: + if transaction.build_proc.poll() is None: + transaction.build_proc.kill() + transaction.build_proc.wait() + # do it because deps are also added in to_build when check_to_build + transaction.to_build.clear() transaction.Interrupt() ManagerWindow.get_window().set_cursor(None) transaction.ProgressWindow.hide() @@ -440,8 +509,8 @@ def on_search_entry_activate(widget): ManagerWindow.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) while Gtk.events_pending(): Gtk.main_iteration() - current_filter = (search_pkgs, search_entry.get_text()) - refresh_packages_list(search_pkgs(search_entry.get_text())) + current_filter = (search_pkgs, (search_entry.get_text(), search_aur_button.get_active())) + refresh_packages_list(search_pkgs((search_entry.get_text(), search_aur_button.get_active()))) def mark_to_install(widget, pkg): transaction.to_add.add(pkg.name) @@ -452,12 +521,12 @@ def mark_to_reinstall(widget, pkg): def mark_to_remove(widget, pkg): transaction.to_remove.add(pkg.name) -def mark_to_unselect(widget, pkg): +def mark_to_deselect(widget, pkg): transaction.to_remove.discard(pkg.name) transaction.to_add.discard(pkg.name) def select_optdeps(widget, pkg, optdeps): - transaction.choose_label.set_markup(_('{pkgname} has {number} uninstalled optional deps.\nPlease choose the one(s) you want to install:').format(pkgname = pkg.name, number = str(len(optdeps)))) + transaction.choose_label.set_markup('{}'.format(_('{pkgname} has {number} uninstalled optional deps.\nPlease choose those you would like to install:').format(pkgname = pkg.name, number = str(len(optdeps))))) transaction.choose_list.clear() for long_string in optdeps: transaction.choose_list.append([False, long_string]) @@ -480,10 +549,10 @@ def on_list_treeview_button_press_event(treeview, event): if liststore[treeiter][0] != _('No package found') and not liststore[treeiter][0].name in config.holdpkg: right_click_menu = Gtk.Menu() if liststore[treeiter][0].name in transaction.to_add | transaction.to_remove: - item = Gtk.ImageMenuItem(_('Unselect')) + item = Gtk.ImageMenuItem(_('Deselect')) item.set_image(Gtk.Image.new_from_stock('gtk-undo', Gtk.IconSize.MENU)) item.set_always_show_image(True) - item.connect('activate', mark_to_unselect, liststore[treeiter][0]) + item.connect('activate', mark_to_deselect, liststore[treeiter][0]) right_click_menu.append(item) elif liststore[treeiter][0].db.name == 'local': item = Gtk.ImageMenuItem(_('Remove')) @@ -545,10 +614,18 @@ def on_list_treeview_selection_changed(treeview): set_deps_list(liststore[treeiter][0], "local") set_details_list(liststore[treeiter][0], "local") set_files_list(liststore[treeiter][0]) + deps_scrolledwindow.set_visible(True) + details_scrolledwindow.set_visible(True) files_scrolledwindow.set_visible(True) + elif liststore[treeiter][0].db.name == 'AUR': + deps_scrolledwindow.set_visible(False) + details_scrolledwindow.set_visible(False) + files_scrolledwindow.set_visible(False) else: set_deps_list(liststore[treeiter][0], "sync") set_details_list(liststore[treeiter][0], "sync") + deps_scrolledwindow.set_visible(True) + details_scrolledwindow.set_visible(True) files_scrolledwindow.set_visible(False) def on_search_treeview_selection_changed(widget): @@ -560,8 +637,8 @@ def on_search_treeview_selection_changed(widget): ManagerWindow.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) while Gtk.events_pending(): Gtk.main_iteration() - current_filter = (search_pkgs, search_list[line][0]) - refresh_packages_list(search_pkgs(search_list[line][0])) + current_filter = (search_pkgs, (search_list[line][0], search_aur_button.get_active())) + refresh_packages_list(search_pkgs((search_list[line][0], search_aur_button.get_active()))) def on_groups_treeview_selection_changed(widget): global current_filter @@ -602,19 +679,19 @@ def on_repos_treeview_selection_changed(widget): def on_list_treeview_row_activated(treeview, treeiter, column): liststore = treeview.get_model() if not liststore[treeiter][0] == _('No package found'): - if not liststore[treeiter][0].name in config.holdpkg: - if liststore[treeiter][0].db.name == 'local': - if liststore[treeiter][0].name in transaction.to_add: - transaction.to_add.discard(liststore[treeiter][0].name) - elif liststore[treeiter][0].name in transaction.to_remove: - transaction.to_remove.discard(liststore[treeiter][0].name) - else: - transaction.to_remove.add(liststore[treeiter][0].name) - else: - if liststore[treeiter][0].name in transaction.to_add: - transaction.to_add.discard(liststore[treeiter][0].name) - else: - transaction.to_add.add(liststore[treeiter][0].name) + if liststore[treeiter][0].name in transaction.to_add: + transaction.to_add.discard(liststore[treeiter][0].name) + elif liststore[treeiter][0] in transaction.to_build: + transaction.to_build.remove(liststore[treeiter][0]) + elif liststore[treeiter][0].name in transaction.to_remove: + transaction.to_remove.discard(liststore[treeiter][0].name) + elif liststore[treeiter][0].db.name == 'local': + if not liststore[treeiter][0].name in config.holdpkg: + transaction.to_remove.add(liststore[treeiter][0].name) + elif liststore[treeiter][0].db.name == 'AUR': + transaction.to_build.append(liststore[treeiter][0]) + else: + transaction.to_add.add(liststore[treeiter][0].name) while Gtk.events_pending(): Gtk.main_iteration() @@ -639,6 +716,8 @@ def on_notebook1_switch_page(notebook, page, page_num): def on_manager_valid_button_clicked(*args): ManagerWindow.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) + while Gtk.events_pending(): + Gtk.main_iteration() error = transaction.run() ManagerWindow.get_window().set_cursor(None) if error: @@ -647,12 +726,15 @@ def on_manager_valid_button_clicked(*args): def on_manager_cancel_button_clicked(*args): transaction.to_add.clear() transaction.to_remove.clear() + transaction.to_build.clear() if current_filter[0]: refresh_packages_list(current_filter[0](current_filter[1])) def on_refresh_item_activate(*args): ManagerWindow.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) - transaction.refresh(False) + while Gtk.events_pending(): + Gtk.main_iteration() + transaction.refresh() def on_local_item_activate(*args): @@ -678,6 +760,8 @@ def on_package_open_button_clicked(*args): for path in packages_paths: transaction.to_load.add(path) ManagerWindow.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) + while Gtk.events_pending(): + Gtk.main_iteration() error = transaction.run() ManagerWindow.get_window().set_cursor(None) if error: @@ -696,6 +780,7 @@ def on_state_column_clicked(column): state_column.set_sort_indicator(True) name_column.set_sort_indicator(False) version_column.set_sort_indicator(False) + repo_column.set_sort_indicator(False) size_column.set_sort_indicator(False) liststore.set_sort_func(0, state_column_sort_func, None) @@ -704,6 +789,7 @@ def on_name_column_clicked(column): state_column.set_sort_indicator(False) name_column.set_sort_indicator(True) version_column.set_sort_indicator(False) + repo_column.set_sort_indicator(False) size_column.set_sort_indicator(False) liststore.set_sort_func(0, name_column_sort_func, None) @@ -712,14 +798,25 @@ def on_version_column_clicked(column): state_column.set_sort_indicator(False) name_column.set_sort_indicator(False) version_column.set_sort_indicator(True) + repo_column.set_sort_indicator(False) size_column.set_sort_indicator(False) liststore.set_sort_func(0, version_column_sort_func, None) +def on_repo_column_clicked(column): + liststore = packages_list_treeview.get_model() + state_column.set_sort_indicator(False) + name_column.set_sort_indicator(False) + version_column.set_sort_indicator(False) + repo_column.set_sort_indicator(True) + size_column.set_sort_indicator(False) + liststore.set_sort_func(0, repo_column_sort_func, None) + def on_size_column_clicked(column): liststore = packages_list_treeview.get_model() state_column.set_sort_indicator(False) name_column.set_sort_indicator(False) version_column.set_sort_indicator(False) + repo_column.set_sort_indicator(False) size_column.set_sort_indicator(True) liststore.set_sort_func(0, size_column_sort_func, None) @@ -752,6 +849,7 @@ signals = {'on_ManagerWindow_delete_event' : on_ManagerWindow_delete_event, 'on_state_column_clicked' : on_state_column_clicked, 'on_name_column_clicked' : on_name_column_clicked, 'on_version_column_clicked' : on_version_column_clicked, + 'on_repo_column_clicked' : on_repo_column_clicked, 'on_size_column_clicked' : on_size_column_clicked} def config_dbus_signals(): @@ -773,13 +871,18 @@ else: state_column.set_cell_data_func(state_rendererpixbuf, state_column_display_func) name_column.set_cell_data_func(name_renderertext, name_column_display_func) version_column.set_cell_data_func(version_renderertext, version_column_display_func) + repo_column.set_cell_data_func(repo_renderertext, repo_column_display_func) size_column.set_cell_data_func(size_renderertext, size_column_display_func) transaction.get_handle() transaction.update_dbs() + # now localdb is defined, populate make_depends + for name in transaction.base_devel: + if not pyalpm.find_satisfier(transaction.localdb.pkgcache, name): + transaction.make_depends.add(name) update_lists() ManagerWindow.show_all() ManagerWindow.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) - transaction.refresh(False) + transaction.refresh() while Gtk.events_pending(): Gtk.main_iteration() Gtk.main() diff --git a/pamac-updater.py b/pamac-updater.py index 7aa3004..41d6605 100644 --- a/pamac-updater.py +++ b/pamac-updater.py @@ -5,7 +5,7 @@ from gi.repository import Gtk, Gdk import pyalpm import dbus -from pamac import config, common, transaction +from pamac import config, common, transaction, aur # i18n import gettext @@ -24,33 +24,43 @@ update_top_label = interface.get_object('update_top_label') update_bottom_label = interface.get_object('update_bottom_label') UpdaterApplyButton = interface.get_object('UpdaterApplyButton') -update_top_label.set_markup(_('Your system is up-to-date')) +update_top_label.set_markup('{}'.format(_('Your system is up-to-date'))) update_bottom_label.set_markup('') UpdaterApplyButton.set_sensitive(False) def have_updates(): + while Gtk.events_pending(): + Gtk.main_iteration() + UpdaterWindow.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) + while Gtk.events_pending(): + Gtk.main_iteration() do_syncfirst, updates = transaction.get_updates() update_listore.clear() update_top_label.set_justify(Gtk.Justification.CENTER) if not updates: update_bottom_label.set_markup('') - update_top_label.set_markup(_('Your system is up-to-date')) + update_top_label.set_markup('{}'.format(_('Your system is up-to-date'))) UpdaterApplyButton.set_sensitive(False) else: UpdaterApplyButton.set_sensitive(True) dsize = 0 for pkg in updates: pkgname = pkg.name+' '+pkg.version - update_listore.append([pkgname, common.format_size(pkg.size)]) + if pkg.size: + size_str = common.format_size(pkg.size) + else: + size_str = '' + update_listore.append([pkgname, size_str]) dsize += pkg.download_size if dsize == 0: update_bottom_label.set_markup('') else: - update_bottom_label.set_markup(_('Total download size: ')+common.format_size(dsize)) + update_bottom_label.set_markup('{} {}'.format(_('Total download size:'), common.format_size(dsize))) if len(updates) == 1: - update_top_label.set_markup(_('1 available update')) + update_top_label.set_markup('{}'.format(_('1 available update'))) else: - update_top_label.set_markup(_('{number} available updates').format(number = len(updates))) + update_top_label.set_markup('{}'.format('{number} available updates'.format(number = len(updates)))) + UpdaterWindow.get_window().set_cursor(None) def handle_error(error): UpdaterWindow.get_window().set_cursor(None) @@ -68,7 +78,10 @@ def handle_error(error): transaction.update_dbs() def handle_reply(reply): - if reply: + if transaction.to_build: + transaction.build_next() + elif reply: + transaction.Release() transaction.ProgressCloseButton.set_visible(True) transaction.action_icon.set_from_icon_name('dialog-information', Gtk.IconSize.BUTTON) transaction.progress_label.set_text(str(reply)) @@ -105,6 +118,9 @@ def on_TransCancelButton_clicked(*args): while Gtk.events_pending(): Gtk.main_iteration() transaction.Release() + transaction.to_update.clear() + # do it because deps are also added in to_build when check_to_build + transaction.to_build.clear() def on_ProgressCloseButton_clicked(*args): UpdaterWindow.get_window().set_cursor(None) @@ -115,6 +131,8 @@ def on_ProgressCloseButton_clicked(*args): have_updates() def on_ProgressCancelButton_clicked(*args): + # do it because deps are also added in to_build when check_to_build + transaction.to_build.clear() transaction.Interrupt() UpdaterWindow.get_window().set_cursor(None) transaction.ProgressWindow.hide() @@ -122,16 +140,20 @@ def on_ProgressCancelButton_clicked(*args): Gtk.main_iteration() def on_Updater_ApplyButton_clicked(*args): + while Gtk.events_pending(): + Gtk.main_iteration() UpdaterWindow.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) while Gtk.events_pending(): Gtk.main_iteration() - transaction.sysupgrade(False) + transaction.sysupgrade(show_updates = False) def on_Updater_RefreshButton_clicked(*args): while Gtk.events_pending(): Gtk.main_iteration() UpdaterWindow.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) - transaction.refresh(False) + while Gtk.events_pending(): + Gtk.main_iteration() + transaction.refresh() def on_Updater_CloseButton_clicked(*args): transaction.StopDaemon() @@ -170,5 +192,5 @@ else: UpdaterWindow.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH)) while Gtk.events_pending(): Gtk.main_iteration() - transaction.refresh(False) + transaction.refresh() Gtk.main() diff --git a/pamac.pot b/pamac.pot index 77777a2..5f530da 100644 --- a/pamac.pot +++ b/pamac.pot @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: pamac 0.9\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-10-18 20:35+0200\n" +"POT-Creation-Date: 2013-03-30 15:09+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,6 +16,10 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +#: data/polkit/org.manjaro.pamac.policy:10 +msgid "Authentication is required" +msgstr "" + #: pamac-daemon.py:114 msgid "Checking dependencies" msgstr "" @@ -117,199 +121,195 @@ msgstr "" msgid "Refreshing {repo}" msgstr "" -#: pamac-daemon.py:313 +#: pamac-daemon.py:313 pamac/transaction.py:465 #, python-brace-format msgid "Downloading {pkgname}" msgstr "" -#: pamac-daemon.py:480 pamac-install.py:81 +#: pamac-daemon.py:493 pamac-install.py:81 #, python-brace-format msgid "{pkgname} is not a valid path or package name" msgstr "" -#: pamac-daemon.py:589 +#: pamac-daemon.py:602 #, python-brace-format msgid "" "The transaction cannot be performed because it needs to remove {pkgname1} " "which is a locked package" msgstr "" -#: pamac-daemon.py:596 pamac/transaction.py:230 +#: pamac-daemon.py:609 pamac/transaction.py:346 msgid "Nothing to do" msgstr "" -#: pamac-daemon.py:654 +#: pamac-daemon.py:673 msgid "Transaction successfully finished" msgstr "" -#: pamac-daemon.py:658 pamac-daemon.py:666 +#: pamac-daemon.py:677 pamac-daemon.py:685 msgid "Authentication failed" msgstr "" -#: pamac-manager.py:74 pamac-manager.py:103 pamac-manager.py:104 -#: pamac-manager.py:119 pamac-manager.py:128 pamac-manager.py:227 -#: pamac-manager.py:472 pamac-manager.py:533 pamac-manager.py:595 +#: pamac-manager.py:79 pamac-manager.py:110 pamac-manager.py:111 +#: pamac-manager.py:126 pamac-manager.py:135 pamac-manager.py:156 +#: pamac-manager.py:278 pamac-manager.py:549 pamac-manager.py:611 +#: pamac-manager.py:681 msgid "No package found" msgstr "" -#: pamac-manager.py:143 pamac-manager.py:195 +#: pamac-manager.py:179 pamac-manager.py:239 msgid "local" msgstr "" -#: pamac-manager.py:145 pamac-manager.py:178 pamac-manager.py:284 +#: pamac-manager.py:181 pamac-manager.py:222 pamac-manager.py:336 msgid "Installed" msgstr "" -#: pamac-manager.py:145 pamac-manager.py:181 +#: pamac-manager.py:181 pamac-manager.py:225 msgid "Uninstalled" msgstr "" -#: pamac-manager.py:145 pamac-manager.py:184 +#: pamac-manager.py:181 pamac-manager.py:228 msgid "Orphans" msgstr "" -#: pamac-manager.py:145 pamac-manager.py:164 pamac/transaction.py:353 +#: pamac-manager.py:181 pamac-manager.py:200 pamac/transaction.py:637 msgid "To install" msgstr "" -#: pamac-manager.py:145 pamac-manager.py:169 pamac/transaction.py:371 +#: pamac-manager.py:181 pamac-manager.py:211 pamac/transaction.py:618 msgid "To remove" msgstr "" -#: pamac-manager.py:274 +#: pamac-manager.py:326 msgid "Licenses" msgstr "" -#: pamac-manager.py:279 +#: pamac-manager.py:331 msgid "Depends On" msgstr "" -#: pamac-manager.py:287 +#: pamac-manager.py:339 msgid "Optional Deps" msgstr "" -#: pamac-manager.py:290 +#: pamac-manager.py:342 msgid "Required By" msgstr "" -#: pamac-manager.py:292 +#: pamac-manager.py:344 msgid "Provides" msgstr "" -#: pamac-manager.py:294 +#: pamac-manager.py:346 msgid "Replaces" msgstr "" -#: pamac-manager.py:296 +#: pamac-manager.py:348 msgid "Conflicts With" msgstr "" -#: pamac-manager.py:301 +#: pamac-manager.py:353 msgid "Repository" msgstr "" -#: pamac-manager.py:303 gui/manager.ui:340 +#: pamac-manager.py:355 gui/manager.ui:357 msgid "Groups" msgstr "" -#: pamac-manager.py:305 +#: pamac-manager.py:357 msgid "Compressed Size" msgstr "" -#: pamac-manager.py:306 +#: pamac-manager.py:358 msgid "Download Size" msgstr "" -#: pamac-manager.py:308 +#: pamac-manager.py:360 msgid "Installed Size" msgstr "" -#: pamac-manager.py:309 +#: pamac-manager.py:361 msgid "Packager" msgstr "" -#: pamac-manager.py:310 +#: pamac-manager.py:362 msgid "Architecture" msgstr "" -#: pamac-manager.py:313 +#: pamac-manager.py:365 msgid "Install Date" msgstr "" -#: pamac-manager.py:315 +#: pamac-manager.py:367 msgid "Explicitly installed" msgstr "" -#: pamac-manager.py:317 +#: pamac-manager.py:369 msgid "Installed as a dependency for another package" msgstr "" -#: pamac-manager.py:319 +#: pamac-manager.py:371 msgid "Unknown" msgstr "" -#: pamac-manager.py:320 +#: pamac-manager.py:372 msgid "Install Reason" msgstr "" -#: pamac-manager.py:325 +#: pamac-manager.py:377 msgid "Signatures" msgstr "" -#: pamac-manager.py:329 +#: pamac-manager.py:381 msgid "Backup files" msgstr "" -#: pamac-manager.py:452 +#: pamac-manager.py:529 #, python-brace-format msgid "" -"{pkgname} has {number} uninstalled optional deps.\n" -"Please choose the one(s) you want to install:" +"{pkgname} has {number} uninstalled optional deps.\n" +"Please choose those you would like to install:" msgstr "" -#: pamac-manager.py:475 -msgid "Unselect" +#: pamac-manager.py:552 +msgid "Deselect" msgstr "" -#: pamac-manager.py:481 +#: pamac-manager.py:558 msgid "Remove" msgstr "" -#: pamac-manager.py:487 +#: pamac-manager.py:565 msgid "Reinstall" msgstr "" -#: pamac-manager.py:500 +#: pamac-manager.py:578 msgid "Install optional deps" msgstr "" -#: pamac-manager.py:506 +#: pamac-manager.py:584 msgid "Install" msgstr "" -#: pamac-manager.py:519 +#: pamac-manager.py:597 msgid "Install with optional deps" msgstr "" -#: pamac-manager.py:754 pamac-updater.py:159 pamac-install.py:110 +#: pamac-manager.py:861 pamac-updater.py:181 pamac-install.py:110 msgid "Pamac is already running" msgstr "" -#: pamac-updater.py:27 pamac-updater.py:37 -msgid "Your system is up-to-date" +#: pamac-updater.py:27 pamac-updater.py:42 pamac-tray.py:20 +msgid "Your system is up-to-date" msgstr "" -#: pamac-updater.py:49 pamac/transaction.py:389 -msgid "Total download size: " +#: pamac-updater.py:58 pamac/transaction.py:661 +msgid "Total download size:" msgstr "" -#: pamac-updater.py:51 -msgid "1 available update" -msgstr "" - -#: pamac-updater.py:53 -#, python-brace-format -msgid "{number} available updates" +#: pamac-updater.py:60 pamac-tray.py:18 +msgid "1 available update" msgstr "" #: pamac-tray.py:17 @@ -317,15 +317,7 @@ msgstr "" msgid "{number} available updates" msgstr "" -#: pamac-tray.py:18 -msgid "1 available update" -msgstr "" - -#: pamac-tray.py:20 -msgid "Your system is up-to-date" -msgstr "" - -#: pamac-tray.py:30 pamac-tray.py:85 pamac-tray.py:95 gui/updater.ui:24 +#: pamac-tray.py:30 pamac-tray.py:87 pamac-tray.py:97 gui/updater.ui:24 msgid "Update Manager" msgstr "" @@ -343,34 +335,53 @@ msgid "" "Please update your system first" msgstr "" -#: pamac/transaction.py:151 +#: pamac/transaction.py:176 #, python-brace-format msgid "" -"{pkgname} is provided by {number} packages.\n" -"Please choose the one(s) you want to install:" +"{pkgname} is provided by {number} packages.\n" +"Please choose those you would like to install:" msgstr "" -#: pamac/transaction.py:199 +#: pamac/transaction.py:224 msgid "Refreshing" msgstr "" -#: pamac/transaction.py:271 +#: pamac/transaction.py:297 +#, python-brace-format +msgid "{pkgname} depends on {dependname} but it is not installable" +msgstr "" + +#: pamac/transaction.py:301 +#, python-brace-format +msgid "Failed to get {pkgname} archive from AUR" +msgstr "" + +#: pamac/transaction.py:503 +#, python-brace-format +msgid "Building {pkgname}" +msgstr "" + +#: pamac/transaction.py:519 msgid "Preparing" msgstr "" -#: pamac/transaction.py:351 -msgid "Transaction Summary" +#: pamac/transaction.py:616 +msgid "Transaction Summary" msgstr "" -#: pamac/transaction.py:359 -msgid "To reinstall" -msgstr "" - -#: pamac/transaction.py:365 +#: pamac/transaction.py:624 msgid "To downgrade" msgstr "" -#: pamac/transaction.py:378 +#: pamac/transaction.py:631 +msgid "To build" +msgstr "" + +#: pamac/transaction.py:644 +msgid "To reinstall" +msgstr "" + +#: pamac/transaction.py:652 msgid "To update" msgstr "" @@ -400,43 +411,51 @@ msgstr "" msgid "Install local packages" msgstr "" -#: gui/manager.ui:294 +#: gui/manager.ui:253 +msgid "Search in AUR" +msgstr "" + +#: gui/manager.ui:311 msgid "Search" msgstr "" -#: gui/manager.ui:387 gui/manager.ui:479 +#: gui/manager.ui:404 gui/manager.ui:496 msgid "State" msgstr "" -#: gui/manager.ui:434 +#: gui/manager.ui:451 msgid "Repos" msgstr "" -#: gui/manager.ui:492 +#: gui/manager.ui:509 msgid "Name" msgstr "" -#: gui/manager.ui:505 +#: gui/manager.ui:522 msgid "Version" msgstr "" -#: gui/manager.ui:518 +#: gui/manager.ui:535 +msgid "Repo" +msgstr "" + +#: gui/manager.ui:548 msgid "Size" msgstr "" -#: gui/manager.ui:620 +#: gui/manager.ui:650 msgid "Infos" msgstr "" -#: gui/manager.ui:684 +#: gui/manager.ui:714 msgid "Deps" msgstr "" -#: gui/manager.ui:753 +#: gui/manager.ui:783 msgid "Details" msgstr "" -#: gui/manager.ui:782 +#: gui/manager.ui:812 msgid "Files" msgstr "" diff --git a/pamac/aur.py b/pamac/aur.py new file mode 100644 index 0000000..0b19f80 --- /dev/null +++ b/pamac/aur.py @@ -0,0 +1,161 @@ +#! /usr/bin/python3 +# -*- coding:utf-8 -*- + +import os +import urllib +import Namcap +import requests +import tarfile + +# i18n +import gettext +import locale +locale.bindtextdomain('pamac', '/usr/share/locale') +gettext.bindtextdomain('pamac', '/usr/share/locale') +gettext.textdomain('pamac') +_ = gettext.gettext + +aur_url = 'http://aur.archlinux.org' +rpc_url = aur_url + '/rpc.php' +srcpkgdir = '/tmp/pamac' + +class AURPkg(): + def __init__(self, pkginfo): + self.db = FakeDB() + self.isize = None + self.size = None + self.download_size = 0 + keys = pkginfo.keys() + if 'URL' in keys: + self.url = pkginfo['URL'] + if 'URLPath' in keys: + self.tarpath = pkginfo['URLPath'] + if 'name' in keys: + self.name = pkginfo['name'] + else: + self.name = pkginfo['Name'] + if 'version' in keys: + self.version = pkginfo['version'] + else: + self.version = pkginfo['Version'] + if 'desc' in keys: + self.desc = pkginfo['desc'] + else: + self.desc = pkginfo['Description'] + if 'licenses' in keys: + self.licenses = pkginfo['licenses'] + elif 'License' in keys: + self.licenses = [pkginfo['License']] + else: + self.licenses = [] + if 'source' in keys: + self.source = pkginfo['source'] + else: + self.source = [] + if 'orig_depends' in keys: + self.depends = pkginfo['orig_depends'] + else: + self.depends = [] + #~ if 'orig_optdepends' in keys: + #~ self.optdepends = pkginfo['orig_optdepends'] + #~ else: + #~ self.optdepends = [] + #~ if 'orig_provides' in keys: + #~ self.provides = pkginfo['orig_provides'] + #~ else: + #~ self.provides = [] + if 'orig_makedepends' in keys: + self.makedepends = pkginfo['orig_makedepends'] + else: + self.makedepends = [] + #~ if 'replaces' in keys: + #~ self.replaces = pkginfo['replaces'] + #~ else: + #~ self.replaces = [] + #~ if 'conflicts' in keys: + #~ self.conflicts = pkginfo['conflicts'] + #~ else: + #~ self.conflicts = [] + #~ if 'groups' in keys: + #~ self.groups = pkginfo['groups'] + #~ else: + #~ self.groups = [] + + def __repr__(self): + return '{}-{}'.format(self.name, self.version) + +class FakeDB(): + def __init__(self): + self.name = 'AUR' + +def get_pkgs(pkgbuild_path): + pkgbuild_info = Namcap.package.load_from_pkgbuild(pkgbuild_path) + pkgs = [] + if pkgbuild_info.is_split: + for infos in pkgbuild_info.subpackages: + pkg = AURPkg(infos) + pkgs.append(pkg) + else: + pkg = AURPkg(pkgbuild_info) + pkgs.append(pkg) + return pkgs + +def search(args): + spec = {'type':'search', 'arg':args} + try: + r = requests.get(rpc_url, params = spec) + r.raise_for_status() + except Exception as e: + print(e) + return [] + else: + results_dict = r.json() + pkgs = [] + for result in results_dict['results']: + pkgs.append(AURPkg(result)) + return pkgs + +def infos(pkgname): + spec = {'type':'info', 'arg':pkgname} + try: + r = requests.get(rpc_url, params = spec) + r.raise_for_status() + except Exception as e: + print(e) + return [] + else: + results_dict = r.json() + result = results_dict['results'] + if result: + pkg = AURPkg(result) + return pkg + else: + print('failed to get infos about {} from AUR'.format(pkgname)) + return None + +def get_extract_tarball(pkg): + try: + r = requests.get(aur_url + pkg.tarpath) + r.raise_for_status() + except Exception as e: + print(e) + return None + else: + if not os.path.exists(srcpkgdir): + os.makedirs(srcpkgdir) + tarpath = os.path.join(srcpkgdir, os.path.basename(pkg.tarpath)) + try: + with open(tarpath, 'wb') as f: + f.write(r.content) + except Exception as e: + print(e) + return None + else: + try: + tar = tarfile.open(tarpath) + tar.extractall(path = srcpkgdir) + except Exception as e: + print(e) + return None + else: + return os.path.join(srcpkgdir, pkg.name) diff --git a/pamac/transaction.py b/pamac/transaction.py index 82f506f..567a557 100644 --- a/pamac/transaction.py +++ b/pamac/transaction.py @@ -2,18 +2,33 @@ # -*- coding:utf-8 -*- import pyalpm +from gi.repository import Gtk, GObject +import subprocess +import os +import fnmatch +#import requests +#from ftplib import FTP +#from urllib.parse import urlparse import dbus -from gi.repository import Gtk from dbus.mainloop.glib import DBusGMainLoop -from pamac import config, common +from pamac import config, common, aur to_remove = set() to_add = set() to_update = set() to_load = set() +to_build = [] +cancel_download = False +build_proc = None +make_depends = set() +base_devel = ('autoconf', 'automake', 'binutils', 'bison', 'fakeroot', + 'file', 'findutils', 'flex', 'gawk', 'gcc', 'gettext', + 'grep', 'groff', 'gzip', 'libtool', 'm4', 'make', 'patch', + 'pkg-config', 'sed', 'sudo', 'texinfo', 'util-linux', 'which') +build_depends = set() handle = None -syncdbs =None +syncdbs = None localdb = None # i18n @@ -46,8 +61,8 @@ progress_label = interface.get_object('progresslabel2') action_icon = interface.get_object('action_icon') ProgressCancelButton = interface.get_object('ProgressCancelButton') ProgressCloseButton = interface.get_object('ProgressCloseButton') -progress_textview = interface.get_object('progress_textview') progress_expander = interface.get_object('progress_expander') +progress_textview = interface.get_object('progress_textview') progress_buffer = progress_textview.get_buffer() @@ -95,6 +110,16 @@ def config_dbus_signals(): bus.add_signal_receiver(log_error, dbus_interface = "org.manjaro.pamac", signal_name = "EmitLogError") bus.add_signal_receiver(log_warning, dbus_interface = "org.manjaro.pamac", signal_name = "EmitLogWarning") +def write_to_buffer(fd, condition): + if condition == GObject.IO_IN: # if there's something interesting to read + line = fd.readline().decode(encoding='UTF-8') + print(line.rstrip('\n')) + progress_buffer.insert_at_cursor(line) + progress_bar.pulse() + return True # FUNDAMENTAL, otherwise the callback isn't recalled + else: + return False # Raised an error: exit + def action_handler(action): progress_label.set_text(action) @@ -148,7 +173,7 @@ def log_warning(msg): def choose_provides(data): virtual_dep = str(data[1]) providers = data[0] - choose_label.set_markup(_('{pkgname} is provided by {number} packages.\nPlease choose the one(s) you want to install:').format(pkgname = virtual_dep, number = str(len(providers)))) + choose_label.set_markup('{}'.format(_('{pkgname} is provided by {number} packages.\nPlease choose those you would like to install:').format(pkgname = virtual_dep, number = str(len(providers))))) choose_list.clear() for name in providers: choose_list.append([False, str(name)]) @@ -195,11 +220,11 @@ def get_syncpkg(name): if pkg: return pkg -def refresh(force_update): - progress_label.set_text(_('Refreshing')+'...') - action_icon.set_from_file('/usr/share/pamac/icons/24x24/status/refresh-cache.png') - progress_bar.set_text('') - progress_bar.set_fraction(0) +def refresh(force_update = False): + action_handler(_('Refreshing')+'...') + icon_handler('/usr/share/pamac/icons/24x24/status/refresh-cache.png') + target_handler('') + percent_handler(0) ProgressCancelButton.set_visible(True) ProgressCloseButton.set_visible(False) while Gtk.events_pending(): @@ -209,30 +234,121 @@ def refresh(force_update): def init_transaction(**options): return Init(dbus.Dictionary(options, signature='sb')) +def check_to_build(): + global to_build + global to_add + global make_depends + global build_depends + already_checked = set() + build_order = [] + i = 0 + error = '' + while i < len(to_build): + pkg = to_build[i] + # if current pkg is not in build_order add it at the end of the list + if not pkg.name in build_order: + build_order.append(pkg.name) + # download end extract tarball from AUR + srcdir = aur.get_extract_tarball(pkg) + if srcdir: + # get PKGBUILD and parse it to create a new pkg object with makedeps and deps + new_pkgs = aur.get_pkgs(srcdir + '/PKGBUILD') + for new_pkg in new_pkgs: + print('checking', new_pkg.name) + # check if some makedeps must be installed + for makedepend in new_pkg.makedepends: + if not makedepend in already_checked: + if not pyalpm.find_satisfier(localdb.pkgcache, makedepend): + print('found make dep:',makedepend) + for db in syncdbs: + provider = pyalpm.find_satisfier(db.pkgcache, makedepend) + if provider: + break + if provider: + make_depends.add(provider.name) + already_checked.add(makedepend) + # check if some deps must be installed or built + for depend in new_pkg.depends: + if not depend in already_checked: + if not pyalpm.find_satisfier(localdb.pkgcache, depend): + print('found dep:',depend) + for db in syncdbs: + provider = pyalpm.find_satisfier(db.pkgcache, depend) + if provider: + break + if provider: + # current dep need to be installed + build_depends.add(provider.name) + already_checked.add(depend) + else: + # current dep need to be built + if not depend in build_order: + # get infos about it + dep_pkg = aur.infos(depend) + if dep_pkg: + # add it in to_build so it will be checked + to_build.append(dep_pkg) + # add it in build_order before pkg + index = build_order.index(pkg.name) + build_order.insert(index, dep_pkg.name) + else: + if error: + error += '\n' + error += _('{pkgname} depends on {dependname} but it is not installable').format(pkgname = pkg.name, dependname = depend) + else: + if error: + error += '\n' + error += _('Failed to get {pkgname} archive from AUR').format(pkgname = pkg.name) + i += 1 + if error: + return error + # add pkgname in make_depends and build_depends in to_add + for name in make_depends: + to_add.add(name) + for name in build_depends: + to_add.add(name) + # reorder to_build following build_order + to_build.sort(key = lambda pkg: build_order.index(pkg.name)) + print('order:', build_order) + print('to build:',to_build) + print('makedeps:',make_depends) + print('builddeps:',build_depends) + return error + def run(): - if to_add | to_remove | to_load: + if to_add or to_remove or to_load or to_build: error = '' - trans_flags = {'cascade' : True} - error += init_transaction(**trans_flags) + if to_build: + # check if packages in to_build have deps or makedeps which need to be install first + error += check_to_build() if not error: - for name in to_add: - error += Add(name) - for name in to_remove: - error += Remove(name) - for path in to_load: - error += Load(path) + if to_add or to_remove or to_load: + trans_flags = {'cascade' : True} + error += init_transaction(**trans_flags) + if not error: + for name in to_add: + error += Add(name) + for name in to_remove: + error += Remove(name) + for path in to_load: + error += Load(path) + if not error: + error += prepare(**trans_flags) if not error: - error += prepare(False, **trans_flags) + set_transaction_sum() + ConfDialog.show_all() + while Gtk.events_pending(): + Gtk.main_iteration() if error: Release() return(error) else: return (_('Nothing to do')) -def prepare(show_updates, **trans_flags): - global to_add +def prepare(**trans_flags): error = '' ret = Prepare() + # ret type is a(ass) so [([''], '')] if ret[0][0]: # providers are emitted Release() for item in ret: @@ -251,36 +367,173 @@ def prepare(show_updates, **trans_flags): error = str(ret[0][1]) elif ret[0][1]: # an error is emitted error = str(ret[0][1]) - if not error: - set_transaction_sum(show_updates) - if show_updates: - ConfDialog.show_all() - while Gtk.events_pending(): - Gtk.main_iteration() - else: - if len(transaction_sum) != 0: - ConfDialog.show_all() - while Gtk.events_pending(): - Gtk.main_iteration() - else: - finalize() return(error) -def finalize(): - global progress_buffer - progress_label.set_text(_('Preparing')+'...') - action_icon.set_from_file('/usr/share/pamac/icons/24x24/status/package-setup.png') - progress_bar.set_text('') - progress_bar.set_fraction(0) - progress_buffer.delete(progress_buffer.get_start_iter(), progress_buffer.get_end_iter()) +def check_finished_build(data): + global to_build + global build_proc + path = data[0] + pkg = data[1] + if build_proc.poll() is None: + return True + else: + built = [] + # parse again PKGBUILD to have new pkg objects in case of a pkgver() function + # was used so pkgver was changed during build process + new_pkgs = aur.get_pkgs(path + '/PKGBUILD') + # find built packages + for new_pkg in new_pkgs: + for item in os.listdir(path): + if os.path.isfile(os.path.join(path, item)): + if fnmatch.fnmatch(item, '{}-{}-*.pkg.tar*'.format(new_pkg.name, new_pkg.version)): + built.append(os.path.join(path, item)) + break + if built: + print('successfully built:', built) + build_proc = None + to_build_pkgs = to_build.copy() + for to_build_pkg in to_build_pkgs: + if pkg.name == to_build_pkg.name: + to_build.remove(pkg) + # install built packages + error = '' + error += init_transaction() + if not error: + for pkg_path in built: + error += Load(pkg_path) + if not error: + error += prepare() + if not error: + if To_Remove(): + set_transaction_sum() + ConfDialog.show_all() + while Gtk.events_pending(): + Gtk.main_iteration() + else: + finalize() + if error: + Release() + return False + +def download(url_list, path): + def write_file(chunk): + nonlocal transferred + nonlocal f + if cancel_download: + if ftp: + ftp.quit() + raise Exception('Download cancelled') + return + f.write(chunk) + transferred += len(chunk) + if total_size > 0: + percent = round(transferred/total_size, 2) + percent_handler(percent) + if transferred <= total_size: + target = '{transferred}/{size}'.format(transferred = common.format_size(transferred), size = common.format_size(total_size)) + else: + target = '' + target_handler(target) + while Gtk.events_pending(): + Gtk.main_iteration() + + global cancel_download + cancel_download = False + ftp = None + total_size = 0 + transferred = 0 + icon_handler('/usr/share/pamac/icons/24x24/status/package-download.png') ProgressCancelButton.set_visible(True) ProgressCloseButton.set_visible(False) - try: - Commit() - except dbus.exceptions.DBusException as e: - Release() + parsed_urls = [] + for url in url_list: + url_components = urlparse(url) + if url_components.scheme: + parsed_urls.append(url_components) + print(parsed_urls) + for url_components in parsed_urls: + if url_components.scheme == 'http': + total_size += int(requests.get(url).headers['Content-Length']) + elif url_components.scheme == 'ftp': + ftp = FTP(url_components.netloc) + ftp.login('anonymous', '') + total_size += int(ftp.size(url_components.path)) + print(total_size) + for url_components in parsed_urls: + filename = url_components.path.split('/')[-1] + print(filename) + action = _('Downloading {pkgname}').format(pkgname = filename)+'...' + action_long = action+'\n' + action_handler(action) + action_long_handler(action_long) + ProgressWindow.show() + while Gtk.events_pending(): + Gtk.main_iteration() + with open(os.path.join(path, filename), 'wb') as f: + if url_components.scheme == 'http': + try: + r = requests.get(url, stream = True) + for chunk in r.iter_content(1024): + if cancel_download: + raise Exception('Download cancelled') + break + else: + write_file(chunk) + except Exception as e: + print(e) + cancel_download = False + elif url_components.scheme == 'ftp': + try: + ftp = FTP(url_components.netloc) + ftp.login('anonymous', '') + ftp.retrbinary('RETR '+url_components.path, write_file, blocksize=1024) + except Exception as e: + print(e) + cancel_download = False + +def build_next(): + global build_proc + pkg = to_build[0] + path = os.path.join(aur.srcpkgdir, pkg.name) + new_pkgs = aur.get_pkgs(path + '/PKGBUILD') + # sources are identicals for splitted packages + # (not complete) download(new_pkgs[0].source, path) + icon_handler('/usr/share/pamac/icons/24x24/status/package-setup.png') + target_handler('') + action = _('Building {pkgname}').format(pkgname = pkg.name)+'...' + action_handler(action) + action_long_handler(action+'\n') + ProgressCancelButton.set_visible(True) + ProgressCloseButton.set_visible(False) + progress_expander.set_expanded(True) + ProgressWindow.show() while Gtk.events_pending(): Gtk.main_iteration() + build_proc = subprocess.Popen(["makepkg", "-cf"], cwd = path, stdout = subprocess.PIPE, stderr=subprocess.STDOUT) + GObject.io_add_watch(build_proc.stdout, GObject.IO_IN, write_to_buffer) + GObject.timeout_add(500, check_finished_build, (path, pkg)) + +def finalize(): + if To_Add() or To_Remove(): + global progress_buffer + action_handler(_('Preparing')+'...') + icon_handler('/usr/share/pamac/icons/24x24/status/package-setup.png') + target_handler('') + percent_handler(0) + progress_buffer.delete(progress_buffer.get_start_iter(), progress_buffer.get_end_iter()) + ProgressCancelButton.set_visible(True) + ProgressCloseButton.set_visible(False) + try: + Commit() + except dbus.exceptions.DBusException as e: + Release() + while Gtk.events_pending(): + Gtk.main_iteration() + elif to_build: + # packages in to_build have no deps or makedeps + # so we build and install the first one + # the next ones will be built by the caller + build_next() def get_updates(): do_syncfirst = False @@ -309,16 +562,25 @@ def get_updates(): return do_syncfirst, list_first result = [] for pkg in localdb.pkgcache: - candidate = pyalpm.sync_newversion(pkg, syncdbs) - if candidate: - if not candidate.name in _ignorepkgs: + if not pkg.name in _ignorepkgs: + candidate = pyalpm.sync_newversion(pkg, syncdbs) + if candidate: result.append(candidate) + else: + if not get_syncpkg(pkg.name): + aur_pkg = aur.infos(pkg.name) + if aur_pkg: + comp = pyalpm.vercmp(aur_pkg.version, pkg.version) + if comp == 1: + result.append(aur_pkg) return do_syncfirst, result def get_transaction_sum(): - transaction_dict = {'to_remove': [], 'to_install': [], 'to_update': [], 'to_reinstall': [], 'to_downgrade': []} - to_remove = sorted(To_Remove()) - for name, version in to_remove: + transaction_dict = {'to_remove': [], 'to_build': [], 'to_install': [], 'to_update': [], 'to_reinstall': [], 'to_downgrade': []} + for pkg in to_build: + transaction_dict['to_build'].append(pkg.name+' '+pkg.version) + _to_remove = sorted(To_Remove()) + for name, version in _to_remove: transaction_dict['to_remove'].append(name+' '+version) others = sorted(To_Add()) for name, version, dsize in others: @@ -333,82 +595,123 @@ def get_transaction_sum(): transaction_dict['to_downgrade'].append((name+' '+version, dsize)) else: transaction_dict['to_install'].append((name+' '+version, dsize)) - #~ if transaction_dict['to_install']: - #~ print('To install:', [name for name, size in transaction_dict['to_install']]) - #~ if transaction_dict['to_reinstall']: - #~ print('To reinstall:', [name for name, size in transaction_dict['to_reinstall']]) - #~ if transaction_dict['to_downgrade']: - #~ print('To downgrade:', [name for name, size in transaction_dict['to_downgrade']]) - #~ if transaction_dict['to_remove']: - #~ print('To remove:', [name for name in transaction_dict['to_remove']]) - #~ if transaction_dict['to_update']: - #~ print('To update:', [name for name, size in transaction_dict['to_update']]) + if transaction_dict['to_build']: + print('To build:', [name for name in transaction_dict['to_build']]) + if transaction_dict['to_install']: + print('To install:', [name for name, size in transaction_dict['to_install']]) + if transaction_dict['to_reinstall']: + print('To reinstall:', [name for name, size in transaction_dict['to_reinstall']]) + if transaction_dict['to_downgrade']: + print('To downgrade:', [name for name, size in transaction_dict['to_downgrade']]) + if transaction_dict['to_remove']: + print('To remove:', [name for name in transaction_dict['to_remove']]) + if transaction_dict['to_update']: + print('To update:', [name for name, size in transaction_dict['to_update']]) return transaction_dict -def set_transaction_sum(show_updates): +def set_transaction_sum(show_updates = True): + dsize = 0 transaction_sum.clear() transaction_dict = get_transaction_sum() - sum_top_label.set_markup(_('Transaction Summary')) - if transaction_dict['to_install']: - transaction_sum.append([_('To install')+':', transaction_dict['to_install'][0][0]]) + sum_top_label.set_markup('{}'.format(_('Transaction Summary'))) + if transaction_dict['to_remove']: + transaction_sum.append([_('To remove')+':', transaction_dict['to_remove'][0]]) i = 1 - while i < len(transaction_dict['to_install']): - transaction_sum.append([' ', transaction_dict['to_install'][i][0]]) - i += 1 - if transaction_dict['to_reinstall']: - transaction_sum.append([_('To reinstall')+':', transaction_dict['to_reinstall'][0][0]]) - i = 1 - while i < len(transaction_dict['to_reinstall']): - transaction_sum.append([' ', transaction_dict['to_reinstall'][i][0]]) + while i < len(transaction_dict['to_remove']): + transaction_sum.append(['', transaction_dict['to_remove'][i]]) i += 1 if transaction_dict['to_downgrade']: transaction_sum.append([_('To downgrade')+':', transaction_dict['to_downgrade'][0][0]]) i = 1 while i < len(transaction_dict['to_downgrade']): - transaction_sum.append([' ', transaction_dict['to_downgrade'][i][0]]) + transaction_sum.append(['', transaction_dict['to_downgrade'][i][0]]) + dsize += transaction_dict['to_downgrade'][i][1] i += 1 - if transaction_dict['to_remove']: - transaction_sum.append([_('To remove')+':', transaction_dict['to_remove'][0]]) + if transaction_dict['to_build']: + transaction_sum.append([_('To build')+':', transaction_dict['to_build'][0]]) i = 1 - while i < len(transaction_dict['to_remove']): - transaction_sum.append([' ', transaction_dict['to_remove'][i]]) + while i < len(transaction_dict['to_build']): + transaction_sum.append(['', transaction_dict['to_build'][i]]) + i += 1 + if transaction_dict['to_install']: + transaction_sum.append([_('To install')+':', transaction_dict['to_install'][0][0]]) + i = 1 + while i < len(transaction_dict['to_install']): + transaction_sum.append(['', transaction_dict['to_install'][i][0]]) + dsize += transaction_dict['to_install'][i][1] + i += 1 + if transaction_dict['to_reinstall']: + transaction_sum.append([_('To reinstall')+':', transaction_dict['to_reinstall'][0][0]]) + i = 1 + while i < len(transaction_dict['to_reinstall']): + transaction_sum.append(['', transaction_dict['to_reinstall'][i][0]]) + dsize += transaction_dict['to_reinstall'][i][1] i += 1 if show_updates: if transaction_dict['to_update']: transaction_sum.append([_('To update')+':', transaction_dict['to_update'][0][0]]) i = 1 while i < len(transaction_dict['to_update']): - transaction_sum.append([' ', transaction_dict['to_update'][i][0]]) + transaction_sum.append(['', transaction_dict['to_update'][i][0]]) + dsize += transaction_dict['to_update'][i][1] i += 1 - dsize = 0 - for nameversion, size in transaction_dict['to_install'] + transaction_dict['to_update'] + transaction_dict['to_reinstall'] + transaction_dict['to_downgrade']: - dsize += size if dsize == 0: sum_bottom_label.set_markup('') else: - sum_bottom_label.set_markup(_('Total download size: ')+common.format_size(dsize)) + sum_bottom_label.set_markup('{} {}'.format(_('Total download size:'), common.format_size(dsize))) -def sysupgrade(show_updates): +def sysupgrade(show_updates = True): global to_update global to_add global to_remove do_syncfirst, updates = get_updates() if updates: - to_update = set([pkg.name for pkg in updates]) + to_update.clear() to_add.clear() to_remove.clear() + for pkg in updates: + if pkg.db.name == 'AUR': + to_build.append(pkg) + else: + to_update.add(pkg.name) error = '' if do_syncfirst: error += init_transaction() if not error: for name in to_update: error += Add(name) + if not error: + error += prepare() else: - error += init_transaction() - if not error: - error += Sysupgrade() + if to_build: + # check if packages in to_build have deps or makedeps which need to be install first + # grab errors differently here to not break regular updates + _error = check_to_build() + if to_update or to_add: + error += init_transaction() + if not error: + if to_update: + error += Sysupgrade() + _error = '' + for name in to_add: + _error += Add(name) + if _error: + print(_error) + if not error: + error += prepare() if not error: - error += prepare(show_updates) + set_transaction_sum(show_updates = show_updates) + if show_updates: + ConfDialog.show_all() + while Gtk.events_pending(): + Gtk.main_iteration() + else: + if len(transaction_sum) != 0: + ConfDialog.show_all() + while Gtk.events_pending(): + Gtk.main_iteration() + else: + finalize() if error: Release() return error