Browse Source

- Re-organised the documentation.

- Modularised how the list of packages in a task is determined.
tags/2.07
Joey Hess 17 years ago
parent
commit
120eef15f2
7 changed files with 228 additions and 165 deletions
  1. +1
    -0
      Makefile
  2. +61
    -4
      README
  3. +2
    -0
      debian/changelog
  4. +1
    -0
      makedesc.pl
  5. +13
    -51
      tasks/README
  6. +148
    -109
      tasksel.pl
  7. +2
    -1
      tasksel.pod

+ 1
- 0
Makefile View File

@@ -32,6 +32,7 @@ updatetaskspo:
install:
install -d $(DESTDIR)/usr/bin $(DESTDIR)$(TASKDIR) \
$(DESTDIR)/usr/lib/tasksel/tests \
$(DESTDIR)/usr/lib/tasksel/packages \
$(DESTDIR)/usr/share/man/man8
install -m 755 tasksel.pl $(DESTDIR)/usr/bin/tasksel
install -m 755 tasksel-debconf $(DESTDIR)/usr/lib/tasksel/


+ 61
- 4
README View File

@@ -6,13 +6,70 @@ On startup, the tasksel program will read all *.desc files in
tasks will be presented in a simple list selection screen with their short
descriptions.

On exit, tasksel executes the appropriate apt-get command to install the
selected packages. If the -t option is given, then tasksel prints out the
command line to use to stdout instead. All other messages are printed to
stderr.
On exit, tasksel executes the appropriate command to install the selected
packages. If the -t option is given, then tasksel prints out the command
line to use to stdout instead. All other messages are printed to stderr.

To get a new task added, please file a bug report on tasksel.

Debian derived distributions can add a new .desc file to
/usr/share/tasksel/ to add additional tasks, or modify/divert
debian-tasks.desc to remove tasks.

The file format is a rfc-822 style stanza, with fields named Task, Section,
Description (which should include an extended descrition), Key, Depends,
and optional Test- and Source fields. Here is an example:

Task: desktop
Section: user
Relevance: 10
Description: Desktop environment
This task provides basic "desktop" software, including a variety
of session managers, file managers and web browsers. It incorporates
both the GNOME and KDE desktops, and provides a display manager
which lets the user choose between the two.
Key:
x-window-system-core
kde
gnome
Packages: task-fields

The Key field lists packages that are essential to the task. If those
packages are not available, then the task will not be available either. It
need not list all the packages in the task, if some only serve to make it
better when they are available.

The Packages field tells how to get a complete list of packages that are in
the task. In the example above, it uses the task-fields method, which is
built into tasksel, and looks for Task fields in the control data of available
packages, that list the name of the task. It's also possible to define
other methods, by adding programs to /usr/lib/tasksel/packages/. Then
list the name of the program as the value for the task field, and it will
be run and passed the name of the task, and should output a list of
packages in that task.

There is support for automatically installing tasks based on test programs.
If a task has a Test-* field, then a program in /usr/lib/tasksel/tests/
will be run. For example Test-lang fields cause /usr/lib/tasksel/tests/lang
to be run. The test is passed first the name of the tasks, and then the
contents of the field as parameters. The exit code of the test controls
what to do with the task:

0 - do not display, but do install task
1 - do not display task
2 - display task, marked for installation
3 - display task, not marked for installation

One use of these tests is in automatically selecting a language task
appropriate for the user's locale, and hiding the rest. The lang test
handles this by comparing the value of the Test-lang field of a task with
the locale setting. Tests could also be used for things like automatically
installing hardware support tasks on systems with the right hardware.

There is rudimentary support for tasks that depend on other tasks. If a
task has a Depends field, then it should only be installed if all the tasks
listed as dependencies are installed.

If a task is important enough that it should go near the top of its
section, give it a relevance of 9 or 10. If a task is not likely to be
used, give it a relevance of 1. Default is 5.

+ 2
- 0
debian/changelog View File

@@ -5,6 +5,8 @@ tasksel (2.07) UNRELEASED; urgency=low
- Document --new-install and long format switches.
- Add --task-packages switch, may be used by Synaptic.
- Add --task-desc switch. Closes: #259664
- Re-organised the documentation.
- Modularised how the list of packages in a task is determined.
* Translations
- Kęstutis Biliūnas
- Updated Lithuanian translation of tasks.


+ 1
- 0
makedesc.pl View File

@@ -98,6 +98,7 @@ sub processfile {
grep(/^test-(.*)/, keys %fields)) {
print OUT ucfirst($_).": ".$fields{$_}."\n" if length $fields{$_};
}
print OUT "Packages: task-fields\n";
print OUT "\n";
}



+ 13
- 51
tasks/README View File

@@ -3,10 +3,10 @@ verious general tasks a user might want to perform with a Debian system,
lists of packages that they might use to perform the tasks, and grouping
information to make related sets of tasks appear together.

The file format is a rfc-822 style stanza, with fields named Task, Section,
Description (which should include an extended descrition), Key, and
Packages. The Packages field should include the list of packages one per
line after it, indented by one space each, like so:
See the toplevel README for details about the file format. The files in
this directory are preprocessed and may include comments by prefixing any
line with a hash mark ("#"). Also, the Packages field is used in these files
to list the packages that are part of each task, like so:

Packages:
foo
@@ -14,15 +14,15 @@ Packages:
baz
...

When the task is selected, any of those packages that are available will be
selected.

The Key field is the same, but packages listed in it must be available or
the task should not be displayed at all. There's no need to list packages
in the Packages field if they are listed as Key.
List only real packages, not virtual packages.
Packages listed for different tasks (and within a single task)
should not conflict, or the results will be rather arbitrary.

Comments may appear in the file, by prefixing a line with a hash mark
('#').
Packages that are only available on some architectures, or that may not
be available on the user's installation media may still be listed. This
is no problem, they are simply ignored in those cases. Take care listing
such packages as Key however.

Care should be taken when adding new tasks to ensure that the new task is
suitably generic -- it should be something of value to a large number (at
@@ -35,38 +35,6 @@ appropriate test program. No more than 10 tasks should ever be displayed by
the program, so if there are more, the least-used tasks must be removed
when adding a new one.

There is support for automatically installing tasks based on test programs.
If a task has a Test-* field, then a program in /usr/lib/tasksel/tests/
will be run. For example Test-lang fields cause /usr/lib/tasksel/tests/lang
to be run. The test is passed first the name of the tasks, and then the
contents of the field as parameters. The exit code of the test controls
what to do with the task:

0 - do not display, but do install task
1 - do not display task
2 - display task, marked for installation
3 - display task, not marked for installation

One use of these tests is in automatically selecting a language task
appropriate for the user's locale, and hiding the rest. The lang test
handles this by comparing the value of the Test-lang field of a task with
the locale setting. Tests could also be used for things like automatically
installing hardware support tasks on systems with the right hardware.

There is rudimentary support for tasks that depend on other tasks. If a
task has a Depends field, then it should only be installed if all the tasks
listed as dependencies are installed.

Packages listed for different tasks (and within a single task)
should not conflict, or the results will be rather arbitrary.

Packages that are only available on some architectures, or that may not
be available on the user's installation media may still be listed. This
is no problem, they are simply ignored in those cases. Take care listing
such packages as Key however.

List only real packages, not virtual packages.

Users are given the opportunity to drill down and select/unselect
individual packages; the tasks they select only serve as a starting
point. So err on the side of listing too many packages, rather than too
@@ -74,13 +42,7 @@ few (but don't go overboard, since many users will not bother with
package-level selection at first).

Keep short descriptions short -- very short -- and to the point. Do not
include details about what individual packages a task includes, tasksel
will do that for you.

If a task is important enough that it should go near the top of its
section, give it a relevance of 9 or 10. If a task is not likely to be
used, give it a relevance of 1. Default is 5. (Low relevance tasks are also
candidates for removal..)
include details about what individual packages a task includes.

Debian developers aside from the tasksel maintenance team may take over
maintenance of tasks. Talk with the tasksel developers first, and then put


+ 148
- 109
tasksel.pl View File

@@ -10,6 +10,7 @@ textdomain('tasksel');

my $debconf_helper="/usr/lib/tasksel/tasksel-debconf";
my $testdir="/usr/lib/tasksel/tests";
my $packagesdir="/usr/lib/tasksel/packages";
my $descdir="/usr/share/tasksel";

sub warning {
@@ -70,21 +71,42 @@ sub read_task_desc {
return @ret;
}

# Given a task name, returns a list of all available packages in the task.
sub all_tasks {
map { read_task_desc($_) } list_task_descs();
}

# Given task hash, returns a list of all available packages in the task.
# If the aptitude_tasks parameter is true, then it does not expand tasks
# that aptitude knows about, and just returns aptitude task syntax for
# those.
sub task_packages {
my $task=shift;
my $aptitude_tasks=shift;
my @list;
local $/="\n\n";
open (AVAIL, "apt-cache dumpavail|");
while (<AVAIL>) {
if (/^Task: (.*)/m) {
my @tasks=split(", ", $1);
if (grep { $_ eq $task } @tasks) {
push @list, $1 if /^Package: (.*)/m;
if ($task->{packages} eq 'task-fields') {
# task-fields method one is built-in for speed.
if ($aptitude_tasks) {
return "~t".$task->{task};
}
else {
local $/="\n\n";
open (AVAIL, "apt-cache dumpavail|");
while (<AVAIL>) {
if (/^Task: (.*)/m) {
my @tasks=split(", ", $1);
if (grep { $_ eq $task->{task} } @tasks) {
push @list, $1 if /^Package: (.*)/m;
}
}
}
close AVAIL;
}
}
close AVAIL;
else {
# external method
@list=split "\n", `$packagesdir/$task->{packages} $task->{task}`;
}
return @list;
}

@@ -201,17 +223,10 @@ sub order_for_display {
} @_;
}

# Process command line options.
sub getopts {
my %ret;
Getopt::Long::Configure ("bundling");
if (! GetOptions(\%ret, "test|t", "required|r", "important|i",
"standard|s", "no-ui|n", "new-install", "list-tasks",
"task-packages=s@", "task-desc=s")) {
usage();
exit(1);
}
return %ret;
# Given a set of tasks and a name, returns the one with that name.
sub name_to_task {
my $name=shift;
return (grep { $_->{task} eq $name } @_)[0];
}

sub usage {
@@ -230,114 +245,138 @@ tasksel [options]; where options is any combination of:
});
}

my @aptitude_install;
my @tasks_to_install;
my %options=getopts();

if (@ARGV) {
if ($ARGV[0] eq "install") {
shift;
push @aptitude_install, map { "~t$_" } @ARGV;
# Process command line options and return them in a hash.
sub getopts {
my %ret;
Getopt::Long::Configure ("bundling");
if (! GetOptions(\%ret, "test|t", "required|r", "important|i",
"standard|s", "no-ui|n", "new-install", "list-tasks",
"task-packages=s@", "task-desc=s")) {
usage();
exit(1);
}
else {
# Special case apt-like syntax.
if (@ARGV && $ARGV[0] eq "install") {
shift @ARGV;
$ret{install} = shift @ARGV;
}
if (@ARGV) {
usage();
exit 1;
}
return %ret;
}

if (exists $options{"task-packages"}) {
foreach (@{$options{"task-packages"}}) {
print "$_\n" foreach task_packages($_);
}
exit(0);
}
elsif ($options{"task-desc"}) {
my $task=(grep { $_->{task} eq $options{"task-desc"} }
map { read_task_desc($_) } list_task_descs())[0];
if ($task) {
print dgettext("debian-tasks", join(" ",
@{$task->{description}}[1..$#{$task->{description}}]))."\n";
sub main {
my %options=getopts();

# Options that output stuff and don't need a full processed list of tasks.
if (exists $options{"task-packages"}) {
my @tasks=all_tasks();
foreach my $taskname (@{$options{"task-packages"}}) {
print "$_\n" foreach task_packages(grep { $_->{task} eq $taskname } @tasks);
}
exit(0);
}
else {
exit(1);
elsif ($options{"task-desc"}) {
my $task=name_to_task($options{"task-desc"}, all_tasks());
if ($task) {
print dgettext("debian-tasks", join(" ",
@{$task->{description}}[1..$#{$task->{description}}]))."\n";
exit(0);
}
else {
exit(1);
}
}
}

my @tasks=map { hide_dependent_tasks($_) } map { task_test($_) }
grep { task_avail($_) } map { read_task_desc($_) }
list_task_descs();

if ($options{"list-tasks"}) {
print $_->{task}."\t".$_->{shortdesc}."\n"
foreach order_for_display(grep { $_->{_display} } @tasks);
exit(0);
}

if (! $options{"new-install"}) {
# Don't install hidden tasks if this is not a new install.
map { $_->{_install} = 0 } grep { $_->{_display} == 0 } @tasks;
}
if ($options{"required"}) {
push @aptitude_install, "~prequired";
}
if ($options{"important"}) {
push @aptitude_install, "~pimportant";
}
if ($options{"standard"}) {
push @aptitude_install, "~pstandard";
}

my @list = order_for_display(grep { $_->{_display} == 1 } @tasks);
map { $_->{_install} = 0 } @list; # don't install displayed tasks unless selected
if (@list && ! $options{"no-ui"}) {
my $question="tasksel/tasks";
if ($options{"new-install"}) {
$question="tasksel/first";
# This is relatively expensive, get the full list of available tasks and
# mark them.
my @tasks=map { hide_dependent_tasks($_) } map { task_test($_) }
grep { task_avail($_) } all_tasks();
if ($options{"list-tasks"}) {
print $_->{task}."\t".$_->{shortdesc}."\n"
foreach order_for_display(grep { $_->{_display} } @tasks);
exit(0);
}
my @default = grep { $_->{_display} == 1 && $_->{_install} == 1 } @tasks;
my $tmpfile=`tempfile`;
chomp $tmpfile;
system($debconf_helper, $tmpfile, task_to_debconf(@list),
task_to_debconf(@default), $question);
open(IN, "<$tmpfile");
my $ret=<IN>;
chomp $ret;
close IN;
unlink $tmpfile;
map { $_->{_install} = 1 } debconf_to_task($ret, @tasks);
if (! $options{test} && $ret=~/manual package selection/) {
# Doing better than this calls for a way to queue stuff for
# install in aptitude and then enter interactive mode.
my $ret=system("aptitude") >> 8;
if ($ret != 0) {
error gettext("aptitude failed");
# Now work out what to tell aptitude to install.
my @aptitude_install;
if (! $options{"new-install"}) {
# Don't install hidden tasks if this is not a new install.
map { $_->{_install} = 0 } grep { $_->{_display} == 0 } @tasks;
}
if ($options{"required"}) {
push @aptitude_install, "~prequired";
}
if ($options{"important"}) {
push @aptitude_install, "~pimportant";
}
if ($options{"standard"}) {
push @aptitude_install, "~pstandard";
}
if ($options{"install"}) {
my $task=name_to_task($options{"install"}, @tasks);
$task->{_install} = 1 if $task;
}
# The interactive bit.
my @list = order_for_display(grep { $_->{_display} == 1 } @tasks);
if (@list && ! $options{"no-ui"} && ! $options{install}) {
map { $_->{_install} = 0 } @list; # don't install displayed tasks unless selected
my $question="tasksel/tasks";
if ($options{"new-install"}) {
$question="tasksel/first";
}
my @default = grep { $_->{_display} == 1 && $_->{_install} == 1 } @tasks;
my $tmpfile=`tempfile`;
chomp $tmpfile;
system($debconf_helper, $tmpfile, task_to_debconf(@list),
task_to_debconf(@default), $question);
open(IN, "<$tmpfile");
my $ret=<IN>;
chomp $ret;
close IN;
unlink $tmpfile;
map { $_->{_install} = 1 } debconf_to_task($ret, @tasks);
if (! $options{test} && $ret=~/manual package selection/) {
# Doing better than this calls for a way to queue stuff for
# install in aptitude and then enter interactive mode.
my $ret=system("aptitude") >> 8;
if ($ret != 0) {
error gettext("aptitude failed");
}
}
}
}

# Mark dependnent packages for install if their dependencies are met.
foreach my $task (@tasks) {
if (! $task->{_install} && exists $task->{depends} && length $task->{depends} ) {
$task->{_install} = 1;
foreach my $dep (split(', ', $task->{depends})) {
if (! grep { $_->{task} eq $dep && $_->{_install} } @tasks) {
$task->{_install} = 0;
# Mark dependnent packages for install if their dependencies are met.
foreach my $task (@tasks) {
if (! $task->{_install} && exists $task->{depends} && length $task->{depends} ) {
$task->{_install} = 1;
foreach my $dep (split(', ', $task->{depends})) {
if (! grep { $_->{task} eq $dep && $_->{_install} } @tasks) {
$task->{_install} = 0;
}
}
}
}
}

push @aptitude_install, map { "~t".$_->{task} } grep { $_->{_install} } @tasks;
# Add tasks to install.
push @aptitude_install, map { task_packages($_, 1) } grep { $_->{_install} } @tasks;

if (@aptitude_install) {
if ($options{test}) {
print "aptitude --without-recommends -y install ".join(" ", @aptitude_install)."\n";
}
else {
my $ret=system("aptitude", "--without-recommends", "-y", "install", @aptitude_install) >> 8;
if ($ret != 0) {
error gettext("aptitude failed");
# And finally, act on selected tasks.
if (@aptitude_install) {
if ($options{test}) {
print "aptitude --without-recommends -y install ".join(" ", @aptitude_install)."\n";
}
else {
my $ret=system("aptitude", "--without-recommends", "-y", "install", @aptitude_install) >> 8;
if ($ret != 0) {
error gettext("aptitude failed");
}
}
}
}

main();

+ 2
- 1
tasksel.pod View File

@@ -68,7 +68,8 @@ F</usr/share/tasksel/debian-tasks.desc>

=head1 AUTHOR

tasksel was written by Randolph Chung E<lt>tausq@debian.orgE<gt>.
tasksel was written by Randolph Chung E<lt>tausq@debian.orgE<gt>
and Joey Hess E<lt>joeyh@debiam.orgE<gt>

=head1 HISTORY



Loading…
Cancel
Save