diff options
author | Daniel Hartwig <mandyke@gmail.com> | 2012-05-24 19:38:17 +0800 |
---|---|---|
committer | Daniel Hartwig <mandyke@gmail.com> | 2012-05-26 18:31:54 +0800 |
commit | 31308ec2213de43bc2bee2e1c1467153ac2068fe (patch) | |
tree | 9dba90089dd6a42bfc64e0501e38f6fa8902de72 /src/generic | |
parent | e35827b4e688556a6f1666075db872205fd842c9 (diff) | |
download | aptitude-31308ec2213de43bc2bee2e1c1467153ac2068fe.tar.gz |
Update to tasks support: task packages, multi-arch, syntax
Task packages (introduced with tasksel 3.0) are
meta-packages which define the dependencies of tasks. The
packages themselves have always worked but the 'tasks'
grouping policy and '?task' search term did not support
them. This update corrects for this.
As a result of this change to tasksel all Debian tasks now
function exactly like meta-packages. (Closes: #382631)
The syntax for installing tasks from the command line has
been updated. It now supports specifying an arch and
requires the same syntax as apt-utils ('^' must be the last
part of the name). Examples:
# aptitude install gnome-desktop^
# aptitude install ssh-server^:armel
This avoids ambiguity that may arise when a task and
package have the same name.
Closes: #382631
Diffstat (limited to 'src/generic')
-rw-r--r-- | src/generic/apt/apt.cc | 4 | ||||
-rw-r--r-- | src/generic/apt/matching/match.cc | 2 | ||||
-rw-r--r-- | src/generic/apt/tasks.cc | 292 | ||||
-rw-r--r-- | src/generic/apt/tasks.h | 59 |
4 files changed, 282 insertions, 75 deletions
diff --git a/src/generic/apt/apt.cc b/src/generic/apt/apt.cc index aa010d37..d534b436 100644 --- a/src/generic/apt/apt.cc +++ b/src/generic/apt/apt.cc @@ -366,7 +366,7 @@ void apt_close_cache() else LOG_TRACE(logger, "No global dependency resolver manager exists; none deleted."); - reset_tasks(); + aptitude::apt::reset_tasks(); LOG_TRACE(logger, "Tasks reset."); @@ -484,7 +484,7 @@ void apt_load_cache(OpProgress *progress_bar, bool do_initselections, apt_undos->clear_items(); LOG_TRACE(logger, "Loading task information."); - load_tasks(*progress_bar); + aptitude::apt::load_tasks(*progress_bar); LOG_TRACE(logger, "Loading tags."); #ifndef HAVE_EPT load_tags(*progress_bar); diff --git a/src/generic/apt/matching/match.cc b/src/generic/apt/matching/match.cc index 44430ab7..6e3a1763 100644 --- a/src/generic/apt/matching/match.cc +++ b/src/generic/apt/matching/match.cc @@ -1672,7 +1672,7 @@ namespace aptitude { pkgCache::PkgIterator pkg(target.get_package_iterator(cache)); - std::set<string> *l = get_tasks(pkg); + std::set<string> *l = aptitude::apt::get_tasks(pkg); if(!l) return NULL; diff --git a/src/generic/apt/tasks.cc b/src/generic/apt/tasks.cc index b3930471..6c268b09 100644 --- a/src/generic/apt/tasks.cc +++ b/src/generic/apt/tasks.cc @@ -1,6 +1,22 @@ // tasks.cc // -// Copyright 2001 Daniel Burrows +// Copyright (C) 2001 Daniel Burrows +// Copyright (C) 2012 Daniel Hartwig +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// #include "tasks.h" #include "apt.h" @@ -12,19 +28,35 @@ #include <apt-pkg/tagfile.h> #include <cwidget/generic/util/eassert.h> -#include <errno.h> +#include <cwidget/generic/util/transcode.h> +#include <errno.h> #include <ctype.h> #include <map> #include <vector> +#include <iterator> #include <algorithm> #include <sstream> +namespace cw = cwidget; + using namespace std; +namespace aptitude { +namespace apt { + +// Stores the various tasks. map<string, task> *task_list=new map<string, task>; +task *find_task(const std::string &name) +{ + if(task_list->find(name) == task_list->end()) + return NULL; + + return &((*task_list)[name]); +} + // This is an array indexed by package ID, managed by load_tasks. // (as usual, it's initialized to NULL) set<string> *tasks_by_package; @@ -38,6 +70,86 @@ std::set<std::string> *get_tasks(const pkgCache::PkgIterator &pkg) return tasks_by_package+pkg->ID; } +// Based on task_packages in tasksel.pl. +bool get_task_packages(std::set<pkgCache::PkgIterator> * const pkgset, + const task &task, const string &arch) +{ + // key packages are always included + for(set<string>::const_iterator it = task.keys.begin(); + it != task.keys.end(); + ++it) + { + pkgCache::PkgIterator pkg = (*apt_cache_file)->FindPkg(*it, arch); + if(pkg.end() != false) + pkgset->insert(pkg); + } + + if(task.packages.empty() == true) + { + // only key + } + else if(task.packages[0] == "task-fields") + { + // task-fields method is built-in for speed + for(pkgCache::GrpIterator grp = (*apt_cache_file)->GrpBegin(); + grp.end() == false; + ++grp) + { + pkgCache::PkgIterator pkg = grp.FindPkg(arch); + if(pkg.end() == true) + continue; + set<string> *tasks = get_tasks(pkg); + if(tasks->find(task.name) != tasks->end()) + pkgset->insert(pkg); + } + } + else if(task.packages[0] == "list") + { + // list method is build-in for speed + for(vector<string>::const_iterator it = task.packages.begin() + 1; + it != task.packages.end(); + ++it) + { + const pkgCache::PkgIterator pkg = (*apt_cache_file)->FindPkg(*it, arch); + if(pkg.end() != false) + pkgset->insert(pkg); + } + } + else + { + // TODO: Handle other methods. One option is to just call + // 'tasksel --task-packages' and process the list of search + // patterns returned. + return _error->Warning(_("Unhandled packages method in task %s: %s"), + task.name.c_str(), + task.packages[0].c_str()); + } + + return true; +} + +bool is_task_package(const pkgCache::PkgIterator &pkg) +{ + // TODO: Enable this optimization on Debian systems. + // if(pkg.Section() != NULL && strcmp(pkg.Section(), "tasks") == 0) + // return true; + set<string> *pkg_tasks = get_tasks(pkg); + for(set<string>::const_iterator it = pkg_tasks->begin(); + it != pkg_tasks->end(); + ++it) + { + if(task_list->find(*it) != task_list->end()) + { + const task t = (*task_list)[*it]; + if(t.packages.empty() == true + && t.keys.find(pkg.Name()) != t.keys.end()) + return true; + } + } + + return false; +} + /** \brief Add any tasks found in the given version-file pointer to * the tasks of the given package. */ @@ -48,9 +160,6 @@ static void append_tasks(const pkgCache::PkgIterator &pkg, // tasks structure. eassert(tasks_by_package); - if(strcmp(pkg.Name(), "kdeadmin") == 0) - eassert(pkg.Name()); - set<string> &task_set=tasks_by_package[pkg->ID]; if(apt_package_records) @@ -126,7 +235,6 @@ bool task::keys_present() { keys_present_cache=false; return false; - break; } } } @@ -227,6 +335,98 @@ static string rfc822dgettext(string textdomain, string msgid) return msgstr; } +static void set_task_description(task &task, const wstring &desc) +{ + const wstring::size_type newline = desc.find(L'\n'); + task.shortdesc = wstring(desc, 0, newline); + task.longdesc = wstring(L"\n ") + wstring(desc, newline+1); +} + +static void read_task_desc(const string &filename, OpProgress &prog) +{ + FileFd fd; + + fd.Open(filename, FileFd::ReadOnly); + if(!fd.IsOpen()) + return; + + const unsigned long long file_size = fd.Size(); + unsigned long long amt = 0; + prog.SubProgress(file_size); + + pkgTagFile tagfile(&fd); + pkgTagSection section; + const string taskdomain("debian-tasks"); + + while(tagfile.Step(section)) + { + task newtask; + const string taskname(section.FindS("Task")); + + if(!taskname.empty()) + { + newtask.name = taskname; + newtask.section = section.FindS("Section"); + newtask.relevance = section.FindI("Relevance", 5); + + istringstream keyss(section.FindS("Key")); + for(istream_iterator<string> key(keyss); + key != istream_iterator<string>(); + ++key) + { + newtask.keys.insert(*key); + + pkgCache::GrpIterator grp = (*apt_cache_file)->FindGrp(*key); + if(grp.end() == true) + continue; + + for(pkgCache::PkgIterator pkg = grp.PackageList(); + pkg.end() == false; + pkg = pkg.Group().NextPkg(pkg)) + tasks_by_package[pkg->ID].insert(taskname); + } + + istringstream packagess(section.FindS("Packages")); + copy(istream_iterator<string>(packagess), + istream_iterator<string>(), + back_inserter(newtask.packages)); + + string desc = section.FindS("Description"); + if(desc.empty() == false) + { + desc = rfc822dgettext(taskdomain, desc); + set_task_description(newtask, cw::util::transcode(desc)); + } + else + { + for(set<string>::const_iterator key = newtask.keys.begin(); + key != newtask.keys.end(); + ++key) + { + const pkgCache::PkgIterator pkg((*apt_cache_file)->FindPkg(*key)); + if(pkg.end() == true) + continue; + const pkgCache::VerIterator ver(pkg.VersionList()); + if(ver.end() == true) + continue; + + const wstring desc(get_long_description(ver, apt_package_records)); + if(desc.empty() == true) + continue; + + set_task_description(newtask, desc); + break; + } + } + + (*task_list)[taskname] = newtask; + } + + amt += section.size(); + prog.Progress(amt); + } +} + void load_tasks(OpProgress &progress) { // Build a list for each package of the tasks that package belongs to. @@ -262,71 +462,28 @@ void load_tasks(OpProgress &progress) ++i) append_tasks(i->first.ParentPkg(), i->second); - FileFd task_file; - // Load the task descriptions: - task_file.Open("/usr/share/tasksel/debian-tasks.desc", FileFd::ReadOnly); - - if(!task_file.IsOpen()) + const char *descdirs[] = + {"/usr/share/tasksel/descs", + "/usr/local/share/tasksel/descs", + NULL}; + vector<string> descfiles; + for(const char **it = descdirs; *it != NULL; ++it) { - _error->Discard(); - - // Allow the task file not to exist (eg, the user might not have - // tasksel installed) - if(errno!=ENOENT) - _error->Errno("load_tasks", - _("Unable to open /usr/share/tasksel/debian-tasks.desc")); - - return; + if(DirectoryExists(*it) == false) + continue; + const vector<string> v = + GetListOfFilesInDir(*it, "desc", false, false); + copy(v.begin(), v.end(), back_inserter(descfiles)); } - - int file_size=task_file.Size(); - int amt=0; - progress.OverallProgress(0, file_size, 1, _("Reading task descriptions")); - - pkgTagFile tagfile(&task_file); - pkgTagSection section; - string taskdomain="debian-tasks"; - - while(tagfile.Step(section)) + for(vector<string>::const_iterator it = descfiles.begin(); + it != descfiles.end(); + ++it) { - task newtask; - string desc; - string taskname=section.FindS("Task"); - - if(!taskname.empty()) - { - istringstream keystr(section.FindS("Key")); - - keystr >> ws; - - while(!keystr.eof()) - { - string s; - - keystr >> ws >> s >> ws; - - newtask.keys.insert(s); - } - - newtask.name=taskname; - newtask.section=section.FindS("Section"); - newtask.relevance=section.FindI("Relevance", 5); - - desc=section.FindS("Description"); - - string::size_type newline=desc.find('\n'); - newtask.shortdesc=dgettext(taskdomain.c_str(), string(desc, 0, newline).c_str()); - newtask.longdesc=string("\n "); - newtask.longdesc+=rfc822dgettext(taskdomain, string(desc, newline+1)); - - (*task_list)[taskname]=newtask; - } - - amt+=section.size(); - progress.OverallProgress(amt, file_size, 1, _("Reading task descriptions")); + progress.OverallProgress(it - descfiles.begin(), descfiles.size(), 1, + _("Reading task descriptions")); + read_task_desc(*it, progress); } - progress.OverallProgress(file_size, file_size, 1, _("Reading task descriptions")); progress.Done(); } @@ -337,3 +494,6 @@ void reset_tasks() delete[] tasks_by_package; tasks_by_package=NULL; } + +} +} diff --git a/src/generic/apt/tasks.h b/src/generic/apt/tasks.h index 7d439db5..d89fa496 100644 --- a/src/generic/apt/tasks.h +++ b/src/generic/apt/tasks.h @@ -1,12 +1,31 @@ // tasks.h -*-c++-*- // -// Copyright 2001 Daniel Burrows +// Copyright (C) 2001 Daniel Burrows +// Copyright (C) 2012 Daniel Hartwig // +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// + +#ifndef TASKS_H +#define TASKS_H + +#include <apt-pkg/pkgcache.h> #include <string> #include <set> #include <map> -#include <apt-pkg/pkgcache.h> /** \brief Handles parsing the list of tasks and getting the task of a given * package. @@ -16,6 +35,9 @@ class OpProgress; +namespace aptitude { +namespace apt { + class task { private: @@ -31,15 +53,21 @@ public: std::string name; std::string section; - std::string shortdesc; - std::string longdesc; + std::wstring shortdesc; + std::wstring longdesc; std::set<std::string> keys; + std::vector<std::string> packages; bool keys_present(); int relevance; }; +// Stores the various tasks. +extern std::map<std::string, task> *task_list; + +task *find_task(const std::string &name); + /** \brief Get the set of tasks associated with the given package. * * The caller should not delete this set; it's managed internally by @@ -47,8 +75,22 @@ public: */ std::set<std::string> *get_tasks(const pkgCache::PkgIterator &pkg); -// Stores the various tasks. -extern std::map<std::string, task> *task_list; +bool get_task_packages(std::set<pkgCache::PkgIterator> * const pkgset, + const task &task, + const std::string &arch); + +/** \brief Returns \b true if the given package is a task package. + * + * A task package is one that appears in the Key stanza of a task + * definition which has no Packages stanza. For example, the package + * task-ssh-server in the following definition is a task package: + * + * Task: ssh-server + * Section: server + * Key: + * task-ssh-server + */ +bool is_task_package(const pkgCache::PkgIterator &pkg); // (re)loads in the current list of available tasks. Necessary after a // cache reload, for obvious reasons. apt_reload_cache will call this. @@ -58,3 +100,8 @@ void load_tasks(OpProgress &progress); // Since the task list contains package iterators, we have to do something // in case they're still hanging around. void reset_tasks(); + +} +} + +#endif |