summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS22
-rw-r--r--src/cmdline/cmdline_action.cc58
-rw-r--r--src/cmdline/cmdline_util.cc25
-rw-r--r--src/cmdline/cmdline_util.h6
-rw-r--r--src/generic/apt/apt.cc4
-rw-r--r--src/generic/apt/matching/match.cc2
-rw-r--r--src/generic/apt/tasks.cc292
-rw-r--r--src/generic/apt/tasks.h59
-rw-r--r--src/pkg_grouppolicy.cc44
9 files changed, 387 insertions, 125 deletions
diff --git a/NEWS b/NEWS
index 0ddadb52..9d7b824b 100644
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,28 @@ earlier releases.
files greater than several gigabytes in size -- using the
'unsigned long long' type to store the file size.
+ * [all]: 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.
+
- Minor bugs:
* [all]: Changelog downloading is restored
diff --git a/src/cmdline/cmdline_action.cc b/src/cmdline/cmdline_action.cc
index e5c73938..a499d19a 100644
--- a/src/cmdline/cmdline_action.cc
+++ b/src/cmdline/cmdline_action.cc
@@ -469,39 +469,37 @@ bool cmdline_applyaction(string s,
string sourcestr, package;
- // Handle task installation. Won't work if tasksel isn't installed.
- if(task_list->find(s)!=task_list->end())
- {
- task t=(*task_list)[s];
-
- printf(_("Note: selecting the task \"%s: %s\" for installation\n"),
- s.c_str(), t.shortdesc.c_str());
-
- for(pkgCache::PkgIterator pkg=(*apt_cache_file)->PkgBegin();
- !pkg.end(); ++pkg)
- {
- std::set<std::string> *tasks = get_tasks(pkg);
-
- for(std::set<std::string>::iterator i = tasks->begin();
- i!=tasks->end(); ++i)
- if(*i==s)
- rval=cmdline_applyaction(action, pkg,
- seen_virtual_packages,
- to_install, to_hold, to_remove, to_purge,
- verbose, source,
- sourcestr,
- policy, arch_only,
- allow_auto,
- term_metrics) && rval;
- }
-
- // break out.
- return rval;
- }
-
if(!cmdline_parse_source(s, source, package, sourcestr))
return false;
+ // Handle task installation. Won't work if tasksel isn't installed.
+ aptitude::apt::task task;
+ string arch;
+ if(cmdline_parse_task(package, task, arch) == true)
+ {
+ printf(_("Note: selecting the task \"%s: %s\" for installation\n"),
+ task.name.c_str(), cw::util::transcode(task.shortdesc).c_str());
+
+ pkgset pkgset;
+ aptitude::apt::get_task_packages(&pkgset, task, arch);
+ for(pkgset::iterator pkg = pkgset.begin();
+ pkg != pkgset.end();
+ ++pkg)
+ {
+ rval = cmdline_applyaction(action, *pkg,
+ seen_virtual_packages,
+ to_install, to_hold, to_remove, to_purge,
+ verbose, source,
+ sourcestr,
+ policy, arch_only,
+ allow_auto,
+ term_metrics) && rval;
+ }
+
+ // break out.
+ return rval;
+ }
+
// This is harmless for other commands, but it won't make sense.
if(source == cmdline_version_version &&
action != cmdline_install &&
diff --git a/src/cmdline/cmdline_util.cc b/src/cmdline/cmdline_util.cc
index 813aceb2..1165e3e4 100644
--- a/src/cmdline/cmdline_util.cc
+++ b/src/cmdline/cmdline_util.cc
@@ -38,7 +38,7 @@
#include <generic/apt/matching/match.h>
#include <generic/apt/matching/parse.h>
#include <generic/apt/matching/pattern.h>
-
+#include <generic/apt/tasks.h>
// System includes:
#include <apt-pkg/error.h>
@@ -252,6 +252,29 @@ bool cmdline_parse_source(const string &input,
return true;
}
+bool cmdline_parse_task(string pattern,
+ aptitude::apt::task &task,
+ string &arch)
+{
+ const string::size_type archfound = pattern.find_last_of(':');
+ arch = "native";
+ if(archfound != string::npos)
+ {
+ arch = pattern.substr(archfound+1);
+ pattern.erase(archfound);
+ }
+
+ if(pattern[pattern.length() - 1] != '^')
+ return false;
+ pattern.erase(pattern.length() - 1);
+
+ const aptitude::apt::task *t = aptitude::apt::find_task(pattern);
+ if(t == NULL)
+ return _error->Error(_("Couldn't find task '%s'"), pattern.c_str());
+
+ task = *t;
+ return true;
+}
namespace
{
diff --git a/src/cmdline/cmdline_util.h b/src/cmdline/cmdline_util.h
index 3a5dea68..9b88c393 100644
--- a/src/cmdline/cmdline_util.h
+++ b/src/cmdline/cmdline_util.h
@@ -29,7 +29,7 @@
#include <generic/apt/download_manager.h>
#include <generic/apt/matching/pattern.h>
#include <generic/apt/matching/match.h> // For structural_match.
-
+#include <generic/apt/tasks.h>
// System includes:
#include <apt-pkg/srcrecords.h>
@@ -94,6 +94,10 @@ bool cmdline_parse_source(const string &input,
string &package,
string &sourcestr);
+bool cmdline_parse_task(std::string pattern,
+ aptitude::apt::task &task,
+ std::string &arch);
+
/** Run the given download and post-download commands using the
* standard command-line UI. Runs the preparation routine, the
* actual download, and the post-download commands.
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
diff --git a/src/pkg_grouppolicy.cc b/src/pkg_grouppolicy.cc
index 66588138..97875c08 100644
--- a/src/pkg_grouppolicy.cc
+++ b/src/pkg_grouppolicy.cc
@@ -1069,14 +1069,25 @@ void pkg_grouppolicy_task::init_section_descriptions()
class task_subtree:public pkg_subtree
{
char relevance[2];
+ void set_relevance(int _relevance)
+ {
+ relevance[0]='z'-_relevance;
+ relevance[1]=0;
+ }
+
public:
task_subtree(const std::wstring &_name, const std::wstring &_description,
desc_signal *_info_signal,
int _relevance)
:pkg_subtree(_name, _description, _info_signal)
{
- relevance[0]='z'-_relevance;
- relevance[1]=0;
+ set_relevance(_relevance);
+ }
+
+ task_subtree(const aptitude::apt::task &_task, desc_signal *_info_signal)
+ : pkg_subtree(_task.shortdesc, _task.longdesc, _info_signal)
+ {
+ set_relevance(_task.relevance);
}
const char *tag() const {return relevance;}
@@ -1085,7 +1096,9 @@ public:
void pkg_grouppolicy_task::add_package(const pkgCache::PkgIterator &pkg,
pkg_subtree *root)
{
- set<string> *tasks=get_tasks(pkg);
+ using aptitude::apt::task_list;
+
+ set<string> *tasks = aptitude::apt::get_tasks(pkg);
eassert(tasks);
@@ -1097,19 +1110,17 @@ void pkg_grouppolicy_task::add_package(const pkgCache::PkgIterator &pkg,
if(found==task_children.end())
{
- string section;
- map<string,task>::iterator taskfound=task_list->find(*i);
+ string section = "unknown";
+ aptitude::apt::task *task = aptitude::apt::find_task(*i);
+ bool taskfound = (task != NULL);
pkg_subtree *newtree, *sectiontree;
- if(taskfound==task_list->end())
- section="unknown";
- else
- {
- if(!taskfound->second.keys_present())
+ if(taskfound == true)
+ {
+ if(task->keys_present() == false)
continue; // skip to the next task of this package.
-
- section=taskfound->second.section;
- }
+ section = task->section;
+ }
if(!tasks_subtree)
{
@@ -1141,11 +1152,8 @@ void pkg_grouppolicy_task::add_package(const pkgCache::PkgIterator &pkg,
else
sectiontree=sectionfound->second;
- if(taskfound!=task_list->end())
- newtree=new task_subtree(cw::util::transcode(taskfound->second.shortdesc),
- cw::util::transcode(taskfound->second.longdesc),
- get_desc_sig(),
- taskfound->second.relevance);
+ if(taskfound == true)
+ newtree=new task_subtree(*task, get_desc_sig());
else
newtree=new task_subtree(cw::util::transcode(*i), L"",
get_desc_sig(), 5);