Alternatives framework (1/2) (WIP).
This commit is contained in:
246
ALTERNATIVES
Normal file
246
ALTERNATIVES
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
The XBPS alternatives framework
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
Package metadata stored in packages (props.plist)
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
The 'nvi' pkg declares an alternative group named 'vi':
|
||||||
|
|
||||||
|
...
|
||||||
|
<key>alternatives</key>
|
||||||
|
<dict>
|
||||||
|
<key>vi</key>
|
||||||
|
<array>
|
||||||
|
<string>/usr/bin/ex:/usr/bin/nvi</string>
|
||||||
|
<string>/usr/bin/vi:/usr/bin/nvi</string>
|
||||||
|
<string>/usr/share/man/man1/ex.1:/usr/share/man/man1/nvi.1</string>
|
||||||
|
<string>/usr/share/man/man1/vi.1:/usr/share/man/man1/nvi.1</string>
|
||||||
|
</array>
|
||||||
|
...
|
||||||
|
</dict>
|
||||||
|
...
|
||||||
|
|
||||||
|
The strings need to follow the <symlink>:<target> convention, delimited by
|
||||||
|
the ':' character.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
Package metadata stored in metadir (metadir/alternatives-0.48.plist)
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
/var/db/xbps/alternatives-0.48.plist (dict):
|
||||||
|
|
||||||
|
3 packages can provide the 'vi' alternative group: ex-vi, nvi or vim.
|
||||||
|
The entry order determines the priority (first entry always wins).
|
||||||
|
|
||||||
|
After running `xbps-install -Sy nvi ex-vi vim' the order is the following:
|
||||||
|
|
||||||
|
...
|
||||||
|
<key>vi</key>
|
||||||
|
<array>
|
||||||
|
<string>nvi</string>
|
||||||
|
<string>ex-vi</string>
|
||||||
|
<string>vim</string>
|
||||||
|
</array>
|
||||||
|
...
|
||||||
|
...
|
||||||
|
|
||||||
|
If the 'nvi' pkg is removed, and there's no alternative set for `vi',
|
||||||
|
the 'ex-vi' pkg now becomes the default alternative:
|
||||||
|
|
||||||
|
...
|
||||||
|
<key>vi</key>
|
||||||
|
<array>
|
||||||
|
<string>ex-vi</string>
|
||||||
|
<string>vim</string>
|
||||||
|
</array>
|
||||||
|
...
|
||||||
|
...
|
||||||
|
|
||||||
|
The user now decides that 'vi' should be provided by the 'vim' pkg,
|
||||||
|
so that the matching entry is put into the head:
|
||||||
|
|
||||||
|
...
|
||||||
|
<key>vi</key>
|
||||||
|
<array>
|
||||||
|
<string>vim</string>
|
||||||
|
<string>ex-vi</string>
|
||||||
|
</array>
|
||||||
|
...
|
||||||
|
...
|
||||||
|
|
||||||
|
When no packages provide an alternative group, the alternative group and its associated symlinks
|
||||||
|
will be completely removed.
|
||||||
|
|
||||||
|
|
||||||
|
--------------------
|
||||||
|
xbps-alternatives(1)
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
- Listing all available alternatives and current state:
|
||||||
|
|
||||||
|
$ xbps-alternatives -l
|
||||||
|
libGL
|
||||||
|
- libGL (current)
|
||||||
|
- /usr/lib/libGL.so -> /usr/lib/libGL-mesa.so
|
||||||
|
- /usr/lib/libGL.so.1 -> /usr/lib/libGL-mesa.so.1
|
||||||
|
- /usr/lib/xorg/modules/extensions/libglx.so -> /usr/lib/xorg/modules/extensions/libglx-xorg.so
|
||||||
|
- nvidia-libs
|
||||||
|
- /usr/lib/libGL.so -> /usr/lib/libGL-nvidia.so.1
|
||||||
|
- /usr/lib/libGL.so.1 -> /usr/lib/libGL-nvidia.so.1
|
||||||
|
- /usr/lib/xorg/modules/extensions/libglx.so -> /usr/lib/xorg/modules/extensions/libglx-nvidia.so
|
||||||
|
- catalyst-libs
|
||||||
|
- /usr/lib/libGL.so -> /usr/lib/libGL-fglrx.so.1
|
||||||
|
- /usr/lib/libGL.so.1 -> /usr/lib/libGL-fglrx.so.1
|
||||||
|
- /usr/lib/xorg/modules/extensions/libglx.so -> /usr/lib/xorg/modules/extensions/libglx-fglrx.so
|
||||||
|
ntpd
|
||||||
|
- openntpd (current)
|
||||||
|
- /etc/sv/ntpd -> /etc/sv/openntpd
|
||||||
|
- /usr/bin/ntpd -> /usr/bin/openntpd
|
||||||
|
- /usr/share/man/man1/ntpd.1 -> /usr/share/man/man1/openntpd.1
|
||||||
|
- ntp
|
||||||
|
- /etc/sv/ntpd -> /etc/sv/isc-ntpd
|
||||||
|
- /usr/bin/ntpd -> /usr/bin/isc-ntpd
|
||||||
|
- /usr/share/man/man1/ntpd.1 -> /usr/share/man/man1/isc-ntpd.1
|
||||||
|
- busybox
|
||||||
|
- /etc/sv/ntpd -> /etc/sv/busybox-ntpd
|
||||||
|
- /usr/bin/ntpd -> /usr/bin/busybox
|
||||||
|
sort
|
||||||
|
- coreutils (current)
|
||||||
|
- /usr/bin/sort -> /usr/bin/coreutils-sort
|
||||||
|
- /usr/share/man/man1/sort.1 -> /usr/share/man/man1/coreutils-sort.1
|
||||||
|
- busybox
|
||||||
|
- /usr/bin/sort -> /usr/bin/busybox
|
||||||
|
vi
|
||||||
|
- nvi (current)
|
||||||
|
- /usr/bin/ex -> /usr/bin/nvi
|
||||||
|
- /usr/bin/vi -> /usr/bin/nvi
|
||||||
|
- /usr/share/man/man1/ex.1 -> /usr/share/man/man1/nvi.1
|
||||||
|
- /usr/share/man/man1/vi.1 -> /usr/share/man/man1/nvi.1
|
||||||
|
- vim
|
||||||
|
- /usr/bin/ex -> /usr/bin/vim-ex
|
||||||
|
- /usr/bin/vi -> /usr/bin/vim
|
||||||
|
- /usr/share/man/man1/ex.1 -> /usr/share/man/man1/vim-ex.1
|
||||||
|
- /usr/share/man/man1/vi.1 -> /usr/share/man/man1/vim.1
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
- Listing available alternative groups of a package:
|
||||||
|
|
||||||
|
$ xbps-alternatives -l busybox
|
||||||
|
ntpd
|
||||||
|
- /etc/sv/ntpd -> /etc/sv/busybox-ntpd
|
||||||
|
- /usr/bin/ntpd -> /usr/bin/busybox
|
||||||
|
sort
|
||||||
|
- /usr/bin/sort -> /usr/bin/busybox
|
||||||
|
...
|
||||||
|
|
||||||
|
- Apply all alternative groups specified by the 'nvi' package (declares one group: vi):
|
||||||
|
|
||||||
|
$ xbps-alternatives -s nvi
|
||||||
|
Switched 'vi' alternatives group to 'nvi'.
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/bin/ex -> /usr/bin/nvi
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/bin/vi -> /usr/bin/nvi
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/share/man/man1/ex.1 -> /usr/share/man/man1/nvi.1
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/share/man/man1/vi.1 -> /usr/share/man/man1/nvi.1
|
||||||
|
$
|
||||||
|
|
||||||
|
- Creating a specific alternative group of a package containing multiple groups:
|
||||||
|
|
||||||
|
$ xbps-alternatives -s busybox -g ntpd
|
||||||
|
Switched 'ntpd' alternatives group to 'busybox'.
|
||||||
|
Creating 'ntpd' alternatives group symlink: /etc/sv/ntpd -> /etc/sv/busybox-ntpd
|
||||||
|
Creating 'ntpd' alternatives group symlink: /usr/bin/ntpd -> /usr/bin/busybox
|
||||||
|
$
|
||||||
|
|
||||||
|
---------------
|
||||||
|
xbps-install(1)
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Installing a package with an alternatives group for the first time:
|
||||||
|
|
||||||
|
$ xbps-install -Syv vim
|
||||||
|
Name Action Version New version Download size
|
||||||
|
vim install - 1.0_1 -
|
||||||
|
|
||||||
|
Free space on disk: 49GB
|
||||||
|
|
||||||
|
[*] Downloading binary packages
|
||||||
|
|
||||||
|
[*] Verifying package integrity
|
||||||
|
vim-1.0_1: verifying SHA256 hash...
|
||||||
|
|
||||||
|
[*] Running transaction tasks
|
||||||
|
vim-1.0_1: unpacking ...
|
||||||
|
vim-1.0_1: unpacked file `./usr/bin/vim' (0 bytes)
|
||||||
|
vim-1.0_1: unpacked file `./usr/share/man/man1/vim.1' (0 bytes)
|
||||||
|
vim-1.0_1: registered 'vi' alternatives group
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/bin/vi -> /usr/bin/vim
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/bin/ex -> /usr/bin/vim
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/bin/view -> /usr/bin/vim
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/share/man/man1/ex.1 -> /usr/share/man/man1/vim.1
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/share/man/man1/vi.1 -> /usr/share/man/man1/vim.1
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/share/man/man1/view.1 -> /usr/share/man/man1/vim.1
|
||||||
|
|
||||||
|
[*] Configuring unpacked packages
|
||||||
|
vim-1.0_1: configuring ...
|
||||||
|
vim-1.0_1: installed successfully.
|
||||||
|
|
||||||
|
0 downloaded, 1 installed, 0 updated, 1 configured, 0 removed.
|
||||||
|
$
|
||||||
|
|
||||||
|
Installing a package that provides a 'vi' alternatives group (nvi):
|
||||||
|
|
||||||
|
$ xbps-install -Syv nvi
|
||||||
|
Name Action Version New version Download size
|
||||||
|
nvi install - 1.0_1 -
|
||||||
|
|
||||||
|
Free space on disk: 49GB
|
||||||
|
|
||||||
|
[*] Downloading binary packages
|
||||||
|
|
||||||
|
[*] Verifying package integrity
|
||||||
|
nvi-1.0_1: verifying SHA256 hash...
|
||||||
|
|
||||||
|
[*] Running transaction tasks
|
||||||
|
nvi-1.0_1: unpacking ...
|
||||||
|
nvi-1.0_1: unpacked file `./usr/bin/nvi' (0 bytes)
|
||||||
|
nvi-1.0_1: unpacked file `./usr/share/man/man1/nvi.1' (0 bytes)
|
||||||
|
nvi-1.0_1: registered 'vi' alternatives group
|
||||||
|
|
||||||
|
[*] Configuring unpacked packages
|
||||||
|
nvi-1.0_1: configuring ...
|
||||||
|
nvi-1.0_1: installed successfully.
|
||||||
|
|
||||||
|
0 downloaded, 1 installed, 0 updated, 1 configured, 0 removed.
|
||||||
|
$
|
||||||
|
|
||||||
|
Removing a package that was the default provider of the 'vi' alternatives group (vim):
|
||||||
|
|
||||||
|
$ xbps-remove -yv vim
|
||||||
|
Name Action Version New version Download size
|
||||||
|
vim remove 1.0_1 - -
|
||||||
|
|
||||||
|
Free space on disk: 49GB
|
||||||
|
|
||||||
|
Removing `vim-1.0_1' ...
|
||||||
|
Removing 'vi' alternatives group symlink: /usr/bin/vi
|
||||||
|
Removing 'vi' alternatives group symlink: /usr/bin/ex
|
||||||
|
Removing 'vi' alternatives group symlink: /usr/bin/view
|
||||||
|
Removing 'vi' alternatives group symlink: /usr/share/man/man1/ex.1
|
||||||
|
Removing 'vi' alternatives group symlink: /usr/share/man/man1/vi.1
|
||||||
|
Removing 'vi' alternatives group symlink: /usr/share/man/man1/view.1
|
||||||
|
vim-1.0_1: unregistered 'vi' alternatives group
|
||||||
|
Switched 'vi' alternatives group to 'nvi'
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/bin/vi -> /usr/bin/nvi
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/bin/ex -> /usr/bin/nvi
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/bin/view -> /usr/bin/nvi
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/share/man/man1/ex.1 -> /usr/share/man/man1/nvi.1
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/share/man/man1/vi.1 -> /usr/share/man/man1/nvi.1
|
||||||
|
Creating 'vi' alternatives group symlink: /usr/share/man/man1/view.1 -> /usr/share/man/man1/nvi.1
|
||||||
|
Removed file `/usr/share/man/man1/vim.1'
|
||||||
|
Removed file `/usr/bin/vim'
|
||||||
|
Removed `vim-1.0_1' successfully.
|
||||||
|
|
||||||
|
0 downloaded, 0 installed, 0 updated, 0 configured, 1 removed.
|
||||||
|
$
|
@ -102,6 +102,9 @@ usage(void)
|
|||||||
" -s --desc Short description (max 80 characters).\n"
|
" -s --desc Short description (max 80 characters).\n"
|
||||||
" -t --tags A list of tags/categories (blank separated list).\n"
|
" -t --tags A list of tags/categories (blank separated list).\n"
|
||||||
" -V --version Prints XBPS release version.\n"
|
" -V --version Prints XBPS release version.\n"
|
||||||
|
" --alternatives List of available alternatives this pkg provides.\n"
|
||||||
|
" This expects a blank separated list of <name>:<symlink>:<target>, e.g\n"
|
||||||
|
" 'vi:/usr/bin/vi:/usr/bin/vim foo:/usr/bin/foo:/usr/bin/blah'.\n"
|
||||||
" --build-options A string with the used build options.\n"
|
" --build-options A string with the used build options.\n"
|
||||||
" --compression Compression format: none, gzip, bzip2, xz (default).\n"
|
" --compression Compression format: none, gzip, bzip2, xz (default).\n"
|
||||||
" --shlib-provides List of provided shared libraries (blank separated list,\n"
|
" --shlib-provides List of provided shared libraries (blank separated list,\n"
|
||||||
@ -162,6 +165,72 @@ out:
|
|||||||
xbps_object_release(array);
|
xbps_object_release(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
process_one_alternative(const char *altgrname, const char *val)
|
||||||
|
{
|
||||||
|
xbps_dictionary_t d;
|
||||||
|
xbps_array_t a;
|
||||||
|
char *altfiles;
|
||||||
|
bool alloc = false;
|
||||||
|
|
||||||
|
if ((d = xbps_dictionary_get(pkg_propsd, "alternatives")) == NULL) {
|
||||||
|
d = xbps_dictionary_create();
|
||||||
|
assert(d);
|
||||||
|
alloc = true;
|
||||||
|
}
|
||||||
|
if ((a = xbps_dictionary_get(d, altgrname)) == NULL) {
|
||||||
|
a = xbps_array_create();
|
||||||
|
assert(a);
|
||||||
|
}
|
||||||
|
altfiles = strchr(val, ':') + 1;
|
||||||
|
assert(altfiles);
|
||||||
|
|
||||||
|
xbps_array_add_cstring(a, altfiles);
|
||||||
|
xbps_dictionary_set(d, altgrname, a);
|
||||||
|
xbps_dictionary_set(pkg_propsd, "alternatives", d);
|
||||||
|
|
||||||
|
if (alloc) {
|
||||||
|
xbps_object_release(a);
|
||||||
|
xbps_object_release(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
process_dict_of_arrays(const char *key, const char *val)
|
||||||
|
{
|
||||||
|
char *altgrname, *args, *p, *saveptr;
|
||||||
|
|
||||||
|
assert(key);
|
||||||
|
|
||||||
|
if (val == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args = strdup(val);
|
||||||
|
assert(args);
|
||||||
|
|
||||||
|
if (strchr(args, ' ') == NULL) {
|
||||||
|
altgrname = strtok(args, ":");
|
||||||
|
assert(altgrname);
|
||||||
|
process_one_alternative(altgrname, val);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((p = strtok_r(args, " ", &saveptr)); p;
|
||||||
|
(p = strtok_r(NULL, " ", &saveptr))) {
|
||||||
|
char *b;
|
||||||
|
|
||||||
|
b = strdup(p);
|
||||||
|
assert(b);
|
||||||
|
altgrname = strtok(b, ":");
|
||||||
|
assert(altgrname);
|
||||||
|
process_one_alternative(altgrname, p);
|
||||||
|
free(b);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
free(args);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
process_file(const char *file, const char *key)
|
process_file(const char *file, const char *key)
|
||||||
{
|
{
|
||||||
@ -615,6 +684,7 @@ main(int argc, char **argv)
|
|||||||
{ "shlib-requires", required_argument, NULL, '1' },
|
{ "shlib-requires", required_argument, NULL, '1' },
|
||||||
{ "build-options", required_argument, NULL, '2' },
|
{ "build-options", required_argument, NULL, '2' },
|
||||||
{ "compression", required_argument, NULL, '3' },
|
{ "compression", required_argument, NULL, '3' },
|
||||||
|
{ "alternatives", required_argument, NULL, '4' },
|
||||||
{ NULL, 0, NULL, 0 }
|
{ NULL, 0, NULL, 0 }
|
||||||
};
|
};
|
||||||
struct archive *ar;
|
struct archive *ar;
|
||||||
@ -624,7 +694,7 @@ main(int argc, char **argv)
|
|||||||
const char *conflicts, *deps, *homepage, *license, *maint, *bwith;
|
const char *conflicts, *deps, *homepage, *license, *maint, *bwith;
|
||||||
const char *provides, *pkgver, *replaces, *reverts, *desc, *ldesc;
|
const char *provides, *pkgver, *replaces, *reverts, *desc, *ldesc;
|
||||||
const char *arch, *config_files, *mutable_files, *version;
|
const char *arch, *config_files, *mutable_files, *version;
|
||||||
const char *buildopts, *shlib_provides, *shlib_requires;
|
const char *buildopts, *shlib_provides, *shlib_requires, *alternatives;
|
||||||
const char *compression, *tags = NULL, *srcrevs = NULL;
|
const char *compression, *tags = NULL, *srcrevs = NULL;
|
||||||
char *pkgname, *binpkg, *tname, *p, cwd[PATH_MAX-1];
|
char *pkgname, *binpkg, *tname, *p, cwd[PATH_MAX-1];
|
||||||
bool quiet = false, preserve = false;
|
bool quiet = false, preserve = false;
|
||||||
@ -634,7 +704,7 @@ main(int argc, char **argv)
|
|||||||
arch = conflicts = deps = homepage = license = maint = compression = NULL;
|
arch = conflicts = deps = homepage = license = maint = compression = NULL;
|
||||||
provides = pkgver = replaces = reverts = desc = ldesc = bwith = NULL;
|
provides = pkgver = replaces = reverts = desc = ldesc = bwith = NULL;
|
||||||
buildopts = config_files = mutable_files = shlib_provides = NULL;
|
buildopts = config_files = mutable_files = shlib_provides = NULL;
|
||||||
shlib_requires = NULL;
|
alternatives = shlib_requires = NULL;
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
|
||||||
if (optarg && strcmp(optarg, "") == 0)
|
if (optarg && strcmp(optarg, "") == 0)
|
||||||
@ -716,6 +786,9 @@ main(int argc, char **argv)
|
|||||||
case '3':
|
case '3':
|
||||||
compression = optarg;
|
compression = optarg;
|
||||||
break;
|
break;
|
||||||
|
case '4':
|
||||||
|
alternatives = optarg;
|
||||||
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
@ -798,6 +871,7 @@ main(int argc, char **argv)
|
|||||||
process_array("reverts", reverts);
|
process_array("reverts", reverts);
|
||||||
process_array("shlib-provides", shlib_provides);
|
process_array("shlib-provides", shlib_provides);
|
||||||
process_array("shlib-requires", shlib_requires);
|
process_array("shlib-requires", shlib_requires);
|
||||||
|
process_dict_of_arrays("alternatives", alternatives);
|
||||||
|
|
||||||
/* save cwd */
|
/* save cwd */
|
||||||
memset(&cwd, 0, sizeof(cwd));
|
memset(&cwd, 0, sizeof(cwd));
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
*
|
*
|
||||||
* This header documents the full API for the XBPS Library.
|
* This header documents the full API for the XBPS Library.
|
||||||
*/
|
*/
|
||||||
#define XBPS_API_VERSION "20150603"
|
#define XBPS_API_VERSION "20151018"
|
||||||
|
|
||||||
#ifndef XBPS_VERSION
|
#ifndef XBPS_VERSION
|
||||||
#define XBPS_VERSION "UNSET"
|
#define XBPS_VERSION "UNSET"
|
||||||
@ -96,6 +96,12 @@
|
|||||||
*/
|
*/
|
||||||
#define XBPS_PKGDB "pkgdb-0.38.plist"
|
#define XBPS_PKGDB "pkgdb-0.38.plist"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def XBPS_ALTERNATIVES
|
||||||
|
* Filename for the package alternatives database.
|
||||||
|
*/
|
||||||
|
#define XBPS_ALTERNATIVES "alternatives-0.48.plist"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def XBPS_PKGPROPS
|
* @def XBPS_PKGPROPS
|
||||||
* Filename for package metadata property list.
|
* Filename for package metadata property list.
|
||||||
@ -280,6 +286,11 @@ extern "C" {
|
|||||||
* - XBPS_STATE_INVALID_DEP: package has an invalid dependency.
|
* - XBPS_STATE_INVALID_DEP: package has an invalid dependency.
|
||||||
* - XBPS_STATE_SHOW_INSTALL_MSG: package must show a post-install message.
|
* - XBPS_STATE_SHOW_INSTALL_MSG: package must show a post-install message.
|
||||||
* - XBPS_STATE_SHOW_REMOVE_MSG: package must show a pre-remove message.
|
* - XBPS_STATE_SHOW_REMOVE_MSG: package must show a pre-remove message.
|
||||||
|
* - XBPS_STATE_ALTGROUP_ADDED: package has registered an alternative group.
|
||||||
|
* - XBPS_STATE_ALTGROUP_REMOVED: package has unregistered an alternative group.
|
||||||
|
* - XBPS_STATE_ALTGROUP_SWITCHED: alternative group has been switched.
|
||||||
|
* - XBPS_STATE_ALTGROUP_LINK_ADDED: link added by an alternative group.
|
||||||
|
* - XBPS_STATE_ALTGROUP_LINK_REMOVED: link removed by an alternative group.
|
||||||
* - XBPS_STATE_UNPACK_FILE_PRESERVED: package unpack preserved a file.
|
* - XBPS_STATE_UNPACK_FILE_PRESERVED: package unpack preserved a file.
|
||||||
* - XBPS_STATE_PKGDB: pkgdb upgrade in progress.
|
* - XBPS_STATE_PKGDB: pkgdb upgrade in progress.
|
||||||
* - XBPS_STATE_PKGDB_DONE: pkgdb has been upgraded successfully.
|
* - XBPS_STATE_PKGDB_DONE: pkgdb has been upgraded successfully.
|
||||||
@ -328,7 +339,12 @@ typedef enum xbps_state {
|
|||||||
XBPS_STATE_UNPACK_FILE_PRESERVED,
|
XBPS_STATE_UNPACK_FILE_PRESERVED,
|
||||||
XBPS_STATE_PKGDB,
|
XBPS_STATE_PKGDB,
|
||||||
XBPS_STATE_PKGDB_DONE,
|
XBPS_STATE_PKGDB_DONE,
|
||||||
XBPS_STATE_TRANS_ADDPKG
|
XBPS_STATE_TRANS_ADDPKG,
|
||||||
|
XBPS_STATE_ALTGROUP_ADDED,
|
||||||
|
XBPS_STATE_ALTGROUP_REMOVED,
|
||||||
|
XBPS_STATE_ALTGROUP_SWITCHED,
|
||||||
|
XBPS_STATE_ALTGROUP_LINK_ADDED,
|
||||||
|
XBPS_STATE_ALTGROUP_LINK_REMOVED
|
||||||
} xbps_state_t;
|
} xbps_state_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -514,6 +530,7 @@ struct xbps_handle {
|
|||||||
*/
|
*/
|
||||||
xbps_dictionary_t pkgdb_revdeps;
|
xbps_dictionary_t pkgdb_revdeps;
|
||||||
xbps_dictionary_t vpkgd;
|
xbps_dictionary_t vpkgd;
|
||||||
|
xbps_dictionary_t alternatives;
|
||||||
/**
|
/**
|
||||||
* @var pkgdb
|
* @var pkgdb
|
||||||
*
|
*
|
||||||
@ -629,6 +646,10 @@ void xbps_dbg_printf_append(struct xbps_handle *, const char *, ...);
|
|||||||
void xbps_error_printf(const char *, ...);
|
void xbps_error_printf(const char *, ...);
|
||||||
void xbps_warn_printf(const char *, ...);
|
void xbps_warn_printf(const char *, ...);
|
||||||
|
|
||||||
|
int xbps_alternatives_flush(struct xbps_handle *xhp);
|
||||||
|
int xbps_alternatives_register(struct xbps_handle *, xbps_dictionary_t);
|
||||||
|
int xbps_alternatives_unregister(struct xbps_handle *, xbps_dictionary_t);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the XBPS library with the following steps:
|
* Initialize the XBPS library with the following steps:
|
||||||
*
|
*
|
||||||
|
@ -42,6 +42,7 @@ OBJS += plist.o plist_find.o plist_match.o archive.o
|
|||||||
OBJS += plist_remove.o plist_fetch.o util.o util_hash.o
|
OBJS += plist_remove.o plist_fetch.o util.o util_hash.o
|
||||||
OBJS += repo.o repo_pkgdeps.o repo_sync.o
|
OBJS += repo.o repo_pkgdeps.o repo_sync.o
|
||||||
OBJS += rpool.o cb_util.o proplib_wrapper.o
|
OBJS += rpool.o cb_util.o proplib_wrapper.o
|
||||||
|
OBJS += package_alternatives.o
|
||||||
OBJS += $(EXTOBJS) $(COMPAT_SRCS)
|
OBJS += $(EXTOBJS) $(COMPAT_SRCS)
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
|
280
lib/package_alternatives.c
Normal file
280
lib/package_alternatives.c
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 2015 Juan Romero Pardines.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "xbps_api_impl.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Alternatives framework for xbps.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
left(const char *str)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
p = strdup(str);
|
||||||
|
len = strlen(p) - strlen(strchr(p, ':'));
|
||||||
|
p[len] = '\0';
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
right(const char *str)
|
||||||
|
{
|
||||||
|
return strchr(str, ':') + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static int
|
||||||
|
make_symlink(const char *target, const char *link)
|
||||||
|
{
|
||||||
|
char *t, *l, *tdir, *ldir;
|
||||||
|
|
||||||
|
tdir = strdup(target);
|
||||||
|
assert(tdir);
|
||||||
|
ldir = strdup(link);
|
||||||
|
assert(ldir);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
xbps_alternatives_init(struct xbps_handle *xhp)
|
||||||
|
{
|
||||||
|
char *plist;
|
||||||
|
|
||||||
|
if (xbps_object_type(xhp->alternatives) == XBPS_TYPE_DICTIONARY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
plist = xbps_xasprintf("%s/%s", xhp->metadir, XBPS_ALTERNATIVES);
|
||||||
|
xhp->alternatives = xbps_dictionary_internalize_from_file(plist);
|
||||||
|
free(plist);
|
||||||
|
|
||||||
|
if (xhp->alternatives == NULL)
|
||||||
|
xhp->alternatives = xbps_dictionary_create();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
xbps_alternatives_flush(struct xbps_handle *xhp)
|
||||||
|
{
|
||||||
|
char *plist;
|
||||||
|
|
||||||
|
if (xbps_object_type(xhp->alternatives) != XBPS_TYPE_DICTIONARY)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* ... and then write dictionary to disk */
|
||||||
|
plist = xbps_xasprintf("%s/%s", xhp->metadir, XBPS_ALTERNATIVES);
|
||||||
|
if (!xbps_dictionary_externalize_to_file(xhp->alternatives, plist)) {
|
||||||
|
free(plist);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
free(plist);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
remove_symlinks(struct xbps_handle *xhp, xbps_array_t a, const char *grname)
|
||||||
|
{
|
||||||
|
unsigned int i, cnt;
|
||||||
|
|
||||||
|
cnt = xbps_array_count(a);
|
||||||
|
for (i = 0; i < cnt; i++) {
|
||||||
|
xbps_string_t str;
|
||||||
|
char *l, *lnk;
|
||||||
|
|
||||||
|
str = xbps_array_get(a, i);
|
||||||
|
l = left(xbps_string_cstring_nocopy(str));
|
||||||
|
assert(l);
|
||||||
|
lnk = xbps_xasprintf("%s%s", xhp->rootdir, l);
|
||||||
|
xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_LINK_REMOVED, 0, NULL,
|
||||||
|
"Removing '%s' alternatives group symlink: %s", grname, l);
|
||||||
|
unlink(lnk);
|
||||||
|
free(lnk);
|
||||||
|
free(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
create_symlinks(struct xbps_handle *xhp, xbps_array_t a, const char *grname)
|
||||||
|
{
|
||||||
|
unsigned int i, cnt;
|
||||||
|
|
||||||
|
cnt = xbps_array_count(a);
|
||||||
|
for (i = 0; i < cnt; i++) {
|
||||||
|
xbps_string_t str;
|
||||||
|
char *l, *lnk;
|
||||||
|
const char *tgt;
|
||||||
|
|
||||||
|
str = xbps_array_get(a, i);
|
||||||
|
l = left(xbps_string_cstring_nocopy(str));
|
||||||
|
assert(l);
|
||||||
|
tgt = right(xbps_string_cstring_nocopy(str));
|
||||||
|
assert(tgt);
|
||||||
|
lnk = xbps_xasprintf("%s%s", xhp->rootdir, l);
|
||||||
|
xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_LINK_ADDED, 0, NULL,
|
||||||
|
"Creating '%s' alternatives group symlink: %s -> %s", grname, l, tgt);
|
||||||
|
unlink(lnk);
|
||||||
|
symlink(tgt, lnk);
|
||||||
|
free(lnk);
|
||||||
|
free(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
xbps_alternatives_unregister(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
|
||||||
|
{
|
||||||
|
xbps_array_t allkeys;
|
||||||
|
xbps_dictionary_t alternatives;
|
||||||
|
const char *pkgver;
|
||||||
|
char *pkgname;
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
alternatives = xbps_dictionary_get(pkgd, "alternatives");
|
||||||
|
if (!xbps_dictionary_count(alternatives))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
xbps_alternatives_init(xhp);
|
||||||
|
|
||||||
|
xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
|
||||||
|
if ((pkgname = xbps_pkg_name(pkgver)) == NULL)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
allkeys = xbps_dictionary_all_keys(alternatives);
|
||||||
|
for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) {
|
||||||
|
xbps_array_t array;
|
||||||
|
xbps_object_t keysym;
|
||||||
|
const char *first = NULL, *keyname;
|
||||||
|
|
||||||
|
keysym = xbps_array_get(allkeys, i);
|
||||||
|
keyname = xbps_dictionary_keysym_cstring_nocopy(keysym);
|
||||||
|
|
||||||
|
array = xbps_dictionary_get(xhp->alternatives, keyname);
|
||||||
|
if (array == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
xbps_array_get_cstring_nocopy(array, 0, &first);
|
||||||
|
if (strcmp(pkgname, first) == 0) {
|
||||||
|
/* this pkg is the current alternative for this group */
|
||||||
|
rv = remove_symlinks(xhp,
|
||||||
|
xbps_dictionary_get(alternatives, keyname),
|
||||||
|
keyname);
|
||||||
|
if (rv != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_REMOVED, 0, NULL,
|
||||||
|
"%s: unregistered '%s' alternatives group", pkgver, keyname);
|
||||||
|
xbps_remove_string_from_array(array, pkgname);
|
||||||
|
if (xbps_array_count(array) == 0) {
|
||||||
|
xbps_dictionary_remove(xhp->alternatives, keyname);
|
||||||
|
} else {
|
||||||
|
xbps_dictionary_t curpkgd;
|
||||||
|
|
||||||
|
first = NULL;
|
||||||
|
xbps_array_get_cstring_nocopy(array, 0, &first);
|
||||||
|
curpkgd = xbps_pkgdb_get_pkg(xhp, first);
|
||||||
|
assert(curpkgd);
|
||||||
|
xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_SWITCHED, 0, NULL,
|
||||||
|
"Switched '%s' alternatives group to '%s'", keyname, first);
|
||||||
|
alternatives = xbps_dictionary_get(curpkgd, "alternatives");
|
||||||
|
rv = create_symlinks(xhp,
|
||||||
|
xbps_dictionary_get(alternatives, keyname),
|
||||||
|
keyname);
|
||||||
|
if (rv != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
xbps_object_release(allkeys);
|
||||||
|
free(pkgname);
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
xbps_alternatives_register(struct xbps_handle *xhp, xbps_dictionary_t pkgd)
|
||||||
|
{
|
||||||
|
xbps_array_t allkeys;
|
||||||
|
xbps_dictionary_t alternatives;
|
||||||
|
const char *pkgver;
|
||||||
|
char *pkgname;
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
alternatives = xbps_dictionary_get(pkgd, "alternatives");
|
||||||
|
if (!xbps_dictionary_count(alternatives))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
xbps_alternatives_init(xhp);
|
||||||
|
|
||||||
|
xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
|
||||||
|
pkgname = xbps_pkg_name(pkgver);
|
||||||
|
if (pkgname == NULL)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
allkeys = xbps_dictionary_all_keys(alternatives);
|
||||||
|
for (unsigned int i = 0; i < xbps_array_count(allkeys); i++) {
|
||||||
|
xbps_array_t array;
|
||||||
|
xbps_object_t keysym;
|
||||||
|
const char *keyname;
|
||||||
|
bool alloc = false;
|
||||||
|
|
||||||
|
keysym = xbps_array_get(allkeys, i);
|
||||||
|
keyname = xbps_dictionary_keysym_cstring_nocopy(keysym);
|
||||||
|
|
||||||
|
array = xbps_dictionary_get(xhp->alternatives, keyname);
|
||||||
|
if (array == NULL) {
|
||||||
|
alloc = true;
|
||||||
|
array = xbps_array_create();
|
||||||
|
}
|
||||||
|
xbps_array_add_cstring(array, pkgname);
|
||||||
|
xbps_dictionary_set(xhp->alternatives, keyname, array);
|
||||||
|
xbps_set_cb_state(xhp, XBPS_STATE_ALTGROUP_ADDED, 0, NULL,
|
||||||
|
"%s: registered '%s' alternatives group", pkgver, keyname);
|
||||||
|
if (alloc) {
|
||||||
|
/* apply alternatives for this group */
|
||||||
|
rv = create_symlinks(xhp,
|
||||||
|
xbps_dictionary_get(alternatives, keyname),
|
||||||
|
keyname);
|
||||||
|
xbps_object_release(array);
|
||||||
|
if (rv != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xbps_object_release(allkeys);
|
||||||
|
free(pkgname);
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
@ -318,6 +318,10 @@ xbps_remove_pkg(struct xbps_handle *xhp, const char *pkgver, bool update)
|
|||||||
if ((rv = xbps_cb_message(xhp, pkgd, "remove-msg")) != 0)
|
if ((rv = xbps_cb_message(xhp, pkgd, "remove-msg")) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
/* unregister alternatives */
|
||||||
|
if ((rv = xbps_alternatives_unregister(xhp, pkgd)) != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If updating a package, we just need to execute the current
|
* If updating a package, we just need to execute the current
|
||||||
* pre-remove action target and we are done. Its files will be
|
* pre-remove action target and we are done. Its files will be
|
||||||
|
@ -622,6 +622,14 @@ xbps_unpack_binary_pkg(struct xbps_handle *xhp, xbps_dictionary_t pkg_repod)
|
|||||||
"%s: [unpack] failed to set state to unpacked: %s",
|
"%s: [unpack] failed to set state to unpacked: %s",
|
||||||
pkgver, strerror(rv));
|
pkgver, strerror(rv));
|
||||||
}
|
}
|
||||||
|
/* register alternatives */
|
||||||
|
if ((rv = xbps_alternatives_register(xhp, pkg_repod)) != 0) {
|
||||||
|
xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FAIL,
|
||||||
|
rv, pkgver,
|
||||||
|
"%s: [unpack] failed to register alternatives: %s",
|
||||||
|
pkgver, strerror(rv));
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (pkg_fd != -1)
|
if (pkg_fd != -1)
|
||||||
close(pkg_fd);
|
close(pkg_fd);
|
||||||
|
@ -350,6 +350,10 @@ xbps_transaction_commit(struct xbps_handle *xhp)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* flush changes to the alternatives framework */
|
||||||
|
if ((rv = xbps_alternatives_flush(xhp)) != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
/* if there are no packages to install or update we are done */
|
/* if there are no packages to install or update we are done */
|
||||||
if (!xbps_dictionary_get(xhp->transd, "total-update-pkgs") &&
|
if (!xbps_dictionary_get(xhp->transd, "total-update-pkgs") &&
|
||||||
!xbps_dictionary_get(xhp->transd, "total-install-pkgs"))
|
!xbps_dictionary_get(xhp->transd, "total-install-pkgs"))
|
||||||
|
Reference in New Issue
Block a user