Add support for multiple 'Architecture' values

This allows architecture to be multivalued. On x86-64 machines, this
could be something like:
    Architecture = x86-64-v3 x86-64

We use the first specified Architecture value in mirrorlist $arch
variable replacement, as this is backwards-compatible and sane.

Original-patch-by: Dan McGee <dan@archlinux.org>
Patch-updated-by: Allan McRae <allan@archlinux.org>
Signed-off-by: Allan McRae <allan@archlinux.org>
This commit is contained in:
Dan McGee 2021-04-21 22:47:13 +10:00 committed by Allan McRae
parent abdb4d7fa6
commit 3179db108a
12 changed files with 127 additions and 41 deletions

View file

@ -113,9 +113,9 @@ Options
general configuration options. Wildcards in the specified paths will get general configuration options. Wildcards in the specified paths will get
expanded based on linkman:glob[7] rules. expanded based on linkman:glob[7] rules.
*Architecture =* auto | i686 | x86_64 | ...:: *Architecture =* auto &| i686 &| x86_64 | ...::
If set, pacman will only allow installation of packages of the given If set, pacman will only allow installation of packages with the given
architecture (e.g. 'i686', 'x86_64', etc). The special value 'auto' will architectures (e.g. 'i686', 'x86_64', etc). The special value 'auto' will
use the system architecture, provided via ``uname -m''. If unset, no use the system architecture, provided via ``uname -m''. If unset, no
architecture checks are made. *NOTE*: Packages with the special architecture checks are made. *NOTE*: Packages with the special
architecture 'any' can always be installed, as they are meant to be architecture 'any' can always be installed, as they are meant to be
@ -252,8 +252,8 @@ number.
During parsing, pacman will define the `$repo` variable to the name of the During parsing, pacman will define the `$repo` variable to the name of the
current section. This is often utilized in files specified using the 'Include' current section. This is often utilized in files specified using the 'Include'
directive so all repositories can use the same mirrorfile. pacman also defines directive so all repositories can use the same mirrorfile. pacman also defines
the `$arch` variable to the value of `Architecture`, so the same mirrorfile can the `$arch` variable to the first (or only) value of the `Architecture` option,
even be used for different architectures. so the same mirrorfile can even be used for different architectures.
*SigLevel =* ...:: *SigLevel =* ...::
Set the signature verification level for this repository. For more Set the signature verification level for this repository. For more

View file

@ -2016,25 +2016,37 @@ int alpm_option_remove_assumeinstalled(alpm_handle_t *handle, const alpm_depend_
/** @} */ /** @} */
/** @name Accessors for the configured architecture /** @name Accessors to the list of allowed architectures.
* * libalpm will only install packages that match one of the configured
* libalpm will only install packages that match the configured architecture. * architectures. The architectures do not need to match the physical
* The architecture does not need to match the physical architecture. architecture. They can just be treated as a label.
* It can just be treated as a label.
* @{ * @{
*/ */
/** Returns the allowed package architecture. /** Returns the allowed package architecture.
* @param handle the context handle * @param handle the context handle
* @return the configured package architecture * @return the configured package architectures
*/ */
const char *alpm_option_get_arch(alpm_handle_t *handle); alpm_list_t *alpm_option_get_architectures(alpm_handle_t *handle);
/** Sets the allowed package architecture. /** Adds an allowed package architecture.
* @param handle the context handle * @param handle the context handle
* @param arch the architecture to set * @param arch the architecture to set
*/ */
int alpm_option_set_arch(alpm_handle_t *handle, const char *arch); int alpm_option_add_architecture(alpm_handle_t *handle, const char *arch);
/** Sets the allowed package architecture.
* @param handle the context handle
* @param arches the architecture to set
*/
int alpm_option_set_architectures(alpm_handle_t *handle, alpm_list_t *arches);
/** Removes an allowed package architecture.
* @param handle the context handle
* @param arch the architecture to remove
*/
int alpm_option_remove_architecture(alpm_handle_t *handle, const char *arch);
/* End of arch accessors */ /* End of arch accessors */
/** @} */ /** @} */

View file

@ -77,7 +77,7 @@ void _alpm_handle_free(alpm_handle_t *handle)
FREELIST(handle->hookdirs); FREELIST(handle->hookdirs);
FREE(handle->logfile); FREE(handle->logfile);
FREE(handle->lockfile); FREE(handle->lockfile);
FREE(handle->arch); FREELIST(handle->architectures);
FREE(handle->gpgdir); FREE(handle->gpgdir);
FREELIST(handle->noupgrade); FREELIST(handle->noupgrade);
FREELIST(handle->noextract); FREELIST(handle->noextract);
@ -276,10 +276,10 @@ alpm_list_t SYMEXPORT *alpm_option_get_assumeinstalled(alpm_handle_t *handle)
return handle->assumeinstalled; return handle->assumeinstalled;
} }
const char SYMEXPORT *alpm_option_get_arch(alpm_handle_t *handle) alpm_list_t SYMEXPORT *alpm_option_get_architectures(alpm_handle_t *handle)
{ {
CHECK_HANDLE(handle, return NULL); CHECK_HANDLE(handle, return NULL);
return handle->arch; return handle->architectures;
} }
int SYMEXPORT alpm_option_get_checkspace(alpm_handle_t *handle) int SYMEXPORT alpm_option_get_checkspace(alpm_handle_t *handle)
@ -720,11 +720,29 @@ int SYMEXPORT alpm_option_remove_assumeinstalled(alpm_handle_t *handle, const al
return 0; return 0;
} }
int SYMEXPORT alpm_option_set_arch(alpm_handle_t *handle, const char *arch) int SYMEXPORT alpm_option_add_architecture(alpm_handle_t *handle, const char *arch)
{
handle->architectures = alpm_list_add(handle->architectures, strdup(arch));
return 0;
}
int SYMEXPORT alpm_option_set_architectures(alpm_handle_t *handle, alpm_list_t *arches)
{ {
CHECK_HANDLE(handle, return -1); CHECK_HANDLE(handle, return -1);
if(handle->arch) FREE(handle->arch); if(handle->architectures) FREELIST(handle->architectures);
STRDUP(handle->arch, arch, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); handle->architectures = alpm_list_strdup(arches);
return 0;
}
int SYMEXPORT alpm_option_remove_architecture(alpm_handle_t *handle, const char *arch)
{
char *vdata = NULL;
CHECK_HANDLE(handle, return -1);
handle->architectures = alpm_list_remove_str(handle->architectures, arch, &vdata);
if(vdata != NULL) {
FREE(vdata);
return 1;
}
return 0; return 0;
} }

View file

@ -96,7 +96,7 @@ struct __alpm_handle_t {
alpm_list_t *assumeinstalled; /* List of virtual packages used to satisfy dependencies */ alpm_list_t *assumeinstalled; /* List of virtual packages used to satisfy dependencies */
/* options */ /* options */
char *arch; /* Architecture of packages we should allow */ alpm_list_t *architectures; /* Architectures of packages we should allow */
int usesyslog; /* Use syslog instead of logfile? */ /* TODO move to frontend */ int usesyslog; /* Use syslog instead of logfile? */ /* TODO move to frontend */
int checkspace; /* Check disk space before installing */ int checkspace; /* Check disk space before installing */
char *dbext; /* Sync DB extension */ char *dbext; /* Sync DB extension */

View file

@ -71,14 +71,29 @@ static alpm_list_t *check_arch(alpm_handle_t *handle, alpm_list_t *pkgs)
alpm_list_t *i; alpm_list_t *i;
alpm_list_t *invalid = NULL; alpm_list_t *invalid = NULL;
const char *arch = handle->arch; if(!handle->architectures) {
if(!arch) { _alpm_log(handle, ALPM_LOG_DEBUG, "skipping architecture checks\n");
return NULL; return NULL;
} }
for(i = pkgs; i; i = i->next) { for(i = pkgs; i; i = i->next) {
alpm_pkg_t *pkg = i->data; alpm_pkg_t *pkg = i->data;
alpm_list_t *j;
int found = 0;
const char *pkgarch = alpm_pkg_get_arch(pkg); const char *pkgarch = alpm_pkg_get_arch(pkg);
if(pkgarch && strcmp(pkgarch, arch) && strcmp(pkgarch, "any")) {
/* always allow non-architecture packages and those marked "any" */
if(!pkgarch || strcmp(pkgarch, "any") == 0) {
continue;
}
for(j = handle->architectures; j; j = j->next) {
if(strcmp(pkgarch, j->data) == 0) {
found = 1;
break;
}
}
if(!found) {
char *string; char *string;
const char *pkgname = pkg->name; const char *pkgname = pkg->name;
const char *pkgver = pkg->version; const char *pkgver = pkg->version;

View file

@ -159,7 +159,7 @@ int config_free(config_t *oldconfig)
FREELIST(oldconfig->cachedirs); FREELIST(oldconfig->cachedirs);
free(oldconfig->xfercommand); free(oldconfig->xfercommand);
free(oldconfig->print_format); free(oldconfig->print_format);
free(oldconfig->arch); FREELIST(oldconfig->architectures);
wordsplit_free(oldconfig->xfercommand_argv); wordsplit_free(oldconfig->xfercommand_argv);
free(oldconfig); free(oldconfig);
@ -394,16 +394,19 @@ cleanup:
} }
int config_set_arch(const char *arch) int config_add_architecture(char *arch)
{ {
if(strcmp(arch, "auto") == 0) { if(strcmp(arch, "auto") == 0) {
struct utsname un; struct utsname un;
char *newarch;
uname(&un); uname(&un);
config->arch = strdup(un.machine); newarch = strdup(un.machine);
} else { free(arch);
config->arch = strdup(arch); arch = newarch;
} }
pm_printf(ALPM_LOG_DEBUG, "config: arch: %s\n", config->arch);
pm_printf(ALPM_LOG_DEBUG, "config: arch: %s\n", arch);
config->architectures = alpm_list_add(config->architectures, arch);
return 0; return 0;
} }
@ -638,9 +641,12 @@ static int _parse_options(const char *key, char *value,
} else if(strcmp(key, "HookDir") == 0) { } else if(strcmp(key, "HookDir") == 0) {
setrepeatingoption(value, "HookDir", &(config->hookdirs)); setrepeatingoption(value, "HookDir", &(config->hookdirs));
} else if(strcmp(key, "Architecture") == 0) { } else if(strcmp(key, "Architecture") == 0) {
if(!config->arch) { alpm_list_t *i, *arches = NULL;
config_set_arch(value); setrepeatingoption(value, "Architecture", &arches);
for(i = arches; i; i = alpm_list_next(i)) {
config_add_architecture(i->data);
} }
alpm_list_free(arches);
} else if(strcmp(key, "DBPath") == 0) { } else if(strcmp(key, "DBPath") == 0) {
/* don't overwrite a path specified on the command line */ /* don't overwrite a path specified on the command line */
if(!config->dbpath) { if(!config->dbpath) {
@ -751,17 +757,20 @@ static int _parse_options(const char *key, char *value,
static char *replace_server_vars(config_t *c, config_repo_t *r, const char *s) static char *replace_server_vars(config_t *c, config_repo_t *r, const char *s)
{ {
if(c->arch == NULL && strstr(s, "$arch")) { if(c->architectures == NULL && strstr(s, "$arch")) {
pm_printf(ALPM_LOG_ERROR, pm_printf(ALPM_LOG_ERROR,
_("mirror '%s' contains the '%s' variable, but no '%s' is defined.\n"), _("mirror '%s' contains the '%s' variable, but no '%s' is defined.\n"),
s, "$arch", "Architecture"); s, "$arch", "Architecture");
return NULL; return NULL;
} }
if(c->arch) { /* use first specified architecture */
if(c->architectures) {
char *temp, *replaced; char *temp, *replaced;
alpm_list_t *i = config->architectures;
const char *arch = i->data;
replaced = strreplace(s, "$arch", c->arch); replaced = strreplace(s, "$arch", arch);
temp = replaced; temp = replaced;
replaced = strreplace(temp, "$repo", r->name); replaced = strreplace(temp, "$repo", r->name);
@ -893,7 +902,7 @@ static int setup_libalpm(void)
pm_printf(ALPM_LOG_WARNING, _("no '%s' configured\n"), "XferCommand"); pm_printf(ALPM_LOG_WARNING, _("no '%s' configured\n"), "XferCommand");
} }
alpm_option_set_arch(handle, config->arch); alpm_option_set_architectures(handle, config->architectures);
alpm_option_set_checkspace(handle, config->checkspace); alpm_option_set_checkspace(handle, config->checkspace);
alpm_option_set_usesyslog(handle, config->usesyslog); alpm_option_set_usesyslog(handle, config->usesyslog);

View file

@ -57,7 +57,6 @@ typedef struct __config_t {
unsigned short usesyslog; unsigned short usesyslog;
unsigned short color; unsigned short color;
unsigned short disable_dl_timeout; unsigned short disable_dl_timeout;
char *arch;
char *print_format; char *print_format;
/* unfortunately, we have to keep track of paths both here and in the library /* unfortunately, we have to keep track of paths both here and in the library
* because they can come from both the command line or config file, and we * because they can come from both the command line or config file, and we
@ -70,6 +69,7 @@ typedef struct __config_t {
char *sysroot; char *sysroot;
alpm_list_t *hookdirs; alpm_list_t *hookdirs;
alpm_list_t *cachedirs; alpm_list_t *cachedirs;
alpm_list_t *architectures;
unsigned short op_q_isfile; unsigned short op_q_isfile;
unsigned short op_q_info; unsigned short op_q_info;
@ -244,7 +244,7 @@ int config_free(config_t *oldconfig);
void config_repo_free(config_repo_t *repo); void config_repo_free(config_repo_t *repo);
int config_set_arch(const char *arch); int config_add_architecture(char *arch);
int parseconfig(const char *file); int parseconfig(const char *file);
int parseconfigfile(const char *file); int parseconfigfile(const char *file);
int setdefaults(config_t *c); int setdefaults(config_t *c);

View file

@ -258,7 +258,7 @@ static void dump_config(void)
show_list_str("NoUpgrade", config->noupgrade); show_list_str("NoUpgrade", config->noupgrade);
show_list_str("NoExtract", config->noextract); show_list_str("NoExtract", config->noextract);
show_str("Architecture", config->arch); show_list_str("Architecture", config->architectures);
show_str("XferCommand", config->xfercommand); show_str("XferCommand", config->xfercommand);
show_bool("UseSyslog", config->usesyslog); show_bool("UseSyslog", config->usesyslog);
@ -364,7 +364,7 @@ static int list_directives(void)
} else if(strcasecmp(i->data, "Architecture") == 0) { } else if(strcasecmp(i->data, "Architecture") == 0) {
show_str("Architecture", config->arch); show_list_str("Architecture", config->architectures);
} else if(strcasecmp(i->data, "XferCommand") == 0) { } else if(strcasecmp(i->data, "XferCommand") == 0) {
show_str("XferCommand", config->xfercommand); show_str("XferCommand", config->xfercommand);

View file

@ -377,7 +377,7 @@ static int parsearg_global(int opt)
{ {
switch(opt) { switch(opt) {
case OP_ARCH: case OP_ARCH:
config_set_arch(optarg); config_add_architecture(strdup(optarg));
break; break;
case OP_ASK: case OP_ASK:
config->noask = 1; config->noask = 1;

View file

@ -85,6 +85,8 @@ pacman_tests = [
'tests/mode001.py', 'tests/mode001.py',
'tests/mode002.py', 'tests/mode002.py',
'tests/mode003.py', 'tests/mode003.py',
'tests/multiple-architectures01.py',
'tests/multiple-architectures02.py',
'tests/noupgrade-inverted.py', 'tests/noupgrade-inverted.py',
'tests/overwrite-files-match-negated.py', 'tests/overwrite-files-match-negated.py',
'tests/overwrite-files-match.py', 'tests/overwrite-files-match.py',

View file

@ -0,0 +1,14 @@
self.description = "Install a package (multiple Architecture options, wrong)"
p = pmpkg("dummy")
p.files = ["bin/dummy",
"usr/man/man1/dummy.1"]
p.arch = 'i686'
self.addpkg(p)
self.option["Architecture"] = ['i586', 'i486', 'i386']
self.args = "-U %s" % p.filename()
self.addrule("PACMAN_RETCODE=1")
self.addrule("!PKG_EXIST=dummy")

View file

@ -0,0 +1,16 @@
self.description = "Install a package (multiple Architecture options)"
p = pmpkg("dummy")
p.files = ["bin/dummy",
"usr/man/man1/dummy.1"]
p.arch = 'i486'
self.addpkg(p)
self.option["Architecture"] = ['i586', 'i486', 'i386']
self.args = "-U %s" % p.filename()
self.addrule("PACMAN_RETCODE=0")
self.addrule("PKG_EXIST=dummy")
for f in p.files:
self.addrule("FILE_EXIST=%s" % f)