[pacman-dev] [PATCH] Add support for multiple 'Architecture' values

Allan McRae allan at archlinux.org
Wed Apr 21 13:25:20 UTC 2021


From: Dan McGee <dan at archlinux.org>

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 at archlinux.org>
Patch-updated-by: Allan McRae <allan at archlinux.org>
Signed-off-by: Allan McRae <allan at archlinux.org>
---

Here is the link to Dan's original patch from 2012!
https://code.toofishes.net/cgit/dan/pacman.git/commit/?h=multiarch&id=673c7d08c30fc897f534bda24e88e806e27bea2b

Getting this patch to apply did not require much effort, so I left
Dan as the author. We guess the original patch did not get applied
because it got lost due to no-one pushing it for inclusion.

 doc/pacman.conf.5.asciidoc                    | 10 +++---
 lib/libalpm/alpm.h                            | 30 ++++++++++++-----
 lib/libalpm/handle.c                          | 30 +++++++++++++----
 lib/libalpm/handle.h                          |  2 +-
 lib/libalpm/trans.c                           | 21 ++++++++++--
 src/pacman/conf.c                             | 33 ++++++++++++-------
 src/pacman/conf.h                             |  4 +--
 src/pacman/pacman-conf.c                      |  4 +--
 src/pacman/pacman.c                           |  2 +-
 test/pacman/meson.build                       |  2 ++
 test/pacman/tests/multiple-architectires01.py | 14 ++++++++
 test/pacman/tests/multiple-architectires02.py | 16 +++++++++
 12 files changed, 127 insertions(+), 41 deletions(-)
 create mode 100644 test/pacman/tests/multiple-architectires01.py
 create mode 100644 test/pacman/tests/multiple-architectires02.py

diff --git a/doc/pacman.conf.5.asciidoc b/doc/pacman.conf.5.asciidoc
index 9bd31916..11ca1e4f 100644
--- a/doc/pacman.conf.5.asciidoc
+++ b/doc/pacman.conf.5.asciidoc
@@ -113,9 +113,9 @@ Options
 	general configuration options. Wildcards in the specified paths will get
 	expanded based on linkman:glob[7] rules.
 
-*Architecture =* auto | i686 | x86_64 | ...::
-	If set, pacman will only allow installation of packages of the given
-	architecture (e.g. 'i686', 'x86_64', etc). The special value 'auto' will
+*Architecture =* auto &| i686 &| x86_64 | ...::
+	If set, pacman will only allow installation of packages with the given
+	architectures (e.g. 'i686', 'x86_64', etc). The special value 'auto' will
 	use the system architecture, provided via ``uname -m''. If unset, no
 	architecture checks are made. *NOTE*: Packages with the special
 	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
 current section. This is often utilized in files specified using the 'Include'
 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
-even be used for different architectures.
+the `$arch` variable to the first (or only) value of the `Architecture` option,
+so the same mirrorfile can even be used for different architectures.
 
 *SigLevel =* ...::
 	Set the signature verification level for this repository. For more
diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
index 833df829..2e99d4d8 100644
--- a/lib/libalpm/alpm.h
+++ b/lib/libalpm/alpm.h
@@ -2016,25 +2016,37 @@ int alpm_option_remove_assumeinstalled(alpm_handle_t *handle, const alpm_depend_
 /** @} */
 
 
-/** @name Accessors for the configured architecture
- *
- * libalpm will only install packages that match the configured architecture.
- * The architecture does not need to match the physical architecture.
- * It can just be treated as a label.
+/** @name Accessors to the list of allowed architectures.
+ * libalpm will only install packages that match one of the configured
+ * architectures. The architectures do not need to match the physical
+   architecture. They can just be treated as a label.
  * @{
  */
 
 /** Returns the allowed package architecture.
  * @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 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 */
 /** @} */
 
diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c
index 7b8cb1da..46224a25 100644
--- a/lib/libalpm/handle.c
+++ b/lib/libalpm/handle.c
@@ -77,7 +77,7 @@ void _alpm_handle_free(alpm_handle_t *handle)
 	FREELIST(handle->hookdirs);
 	FREE(handle->logfile);
 	FREE(handle->lockfile);
-	FREE(handle->arch);
+	FREELIST(handle->architectures);
 	FREE(handle->gpgdir);
 	FREELIST(handle->noupgrade);
 	FREELIST(handle->noextract);
@@ -276,10 +276,10 @@ alpm_list_t SYMEXPORT *alpm_option_get_assumeinstalled(alpm_handle_t *handle)
 	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);
-	return handle->arch;
+	return handle->architectures;
 }
 
 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;
 }
 
-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);
+	if(handle->architectures) FREELIST(handle->architectures);
+	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);
-	if(handle->arch) FREE(handle->arch);
-	STRDUP(handle->arch, arch, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
+	handle->architectures = alpm_list_remove_str(handle->architectures, arch, &vdata);
+	if(vdata != NULL) {
+		FREE(vdata);
+		return 1;
+	}
 	return 0;
 }
 
diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h
index 2d8d0f9e..52dc2125 100644
--- a/lib/libalpm/handle.h
+++ b/lib/libalpm/handle.h
@@ -96,7 +96,7 @@ struct __alpm_handle_t {
 	alpm_list_t *assumeinstalled;   /* List of virtual packages used to satisfy dependencies */
 
 	/* 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 checkspace;          /* Check disk space before installing */
 	char *dbext;             /* Sync DB extension */
diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c
index c6ec7eea..939ab05a 100644
--- a/lib/libalpm/trans.c
+++ b/lib/libalpm/trans.c
@@ -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 *invalid = NULL;
 
-	const char *arch = handle->arch;
-	if(!arch) {
+	if(!handle->architectures) {
+		_alpm_log(handle, ALPM_LOG_DEBUG, "skipping architecture checks\n");
 		return NULL;
 	}
 	for(i = pkgs; i; i = i->next) {
 		alpm_pkg_t *pkg = i->data;
+		alpm_list_t *j;
+		int found = 0;
 		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;
 			const char *pkgname = pkg->name;
 			const char *pkgver = pkg->version;
diff --git a/src/pacman/conf.c b/src/pacman/conf.c
index cde96716..9578daa3 100644
--- a/src/pacman/conf.c
+++ b/src/pacman/conf.c
@@ -159,7 +159,7 @@ int config_free(config_t *oldconfig)
 	FREELIST(oldconfig->cachedirs);
 	free(oldconfig->xfercommand);
 	free(oldconfig->print_format);
-	free(oldconfig->arch);
+	FREELIST(oldconfig->architectures);
 	wordsplit_free(oldconfig->xfercommand_argv);
 	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) {
 		struct utsname un;
+		char *newarch;
 		uname(&un);
-		config->arch = strdup(un.machine);
-	} else {
-		config->arch = strdup(arch);
+		newarch = strdup(un.machine);
+		free(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;
 }
 
@@ -638,9 +641,12 @@ static int _parse_options(const char *key, char *value,
 		} else if(strcmp(key, "HookDir") == 0) {
 			setrepeatingoption(value, "HookDir", &(config->hookdirs));
 		} else if(strcmp(key, "Architecture") == 0) {
-			if(!config->arch) {
-				config_set_arch(value);
+			alpm_list_t *i, *arches = NULL;
+			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) {
 			/* don't overwrite a path specified on the command line */
 			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)
 {
-	if(c->arch == NULL && strstr(s, "$arch")) {
+	if(c->architectures == NULL && strstr(s, "$arch")) {
 		pm_printf(ALPM_LOG_ERROR,
 				_("mirror '%s' contains the '%s' variable, but no '%s' is defined.\n"),
 				s, "$arch", "Architecture");
 		return NULL;
 	}
 
-	if(c->arch) {
+	/* use first specified architecture */
+	if(c->architectures) {
 		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;
 		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");
 	}
 
-	alpm_option_set_arch(handle, config->arch);
+	alpm_option_set_architectures(handle, config->architectures);
 	alpm_option_set_checkspace(handle, config->checkspace);
 	alpm_option_set_usesyslog(handle, config->usesyslog);
 
diff --git a/src/pacman/conf.h b/src/pacman/conf.h
index 1b9fb337..316c8d0f 100644
--- a/src/pacman/conf.h
+++ b/src/pacman/conf.h
@@ -57,7 +57,6 @@ typedef struct __config_t {
 	unsigned short usesyslog;
 	unsigned short color;
 	unsigned short disable_dl_timeout;
-	char *arch;
 	char *print_format;
 	/* 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
@@ -70,6 +69,7 @@ typedef struct __config_t {
 	char *sysroot;
 	alpm_list_t *hookdirs;
 	alpm_list_t *cachedirs;
+	alpm_list_t *architectures;
 
 	unsigned short op_q_isfile;
 	unsigned short op_q_info;
@@ -244,7 +244,7 @@ int config_free(config_t *oldconfig);
 
 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 parseconfigfile(const char *file);
 int setdefaults(config_t *c);
diff --git a/src/pacman/pacman-conf.c b/src/pacman/pacman-conf.c
index f8fac75d..6ca21119 100644
--- a/src/pacman/pacman-conf.c
+++ b/src/pacman/pacman-conf.c
@@ -258,7 +258,7 @@ static void dump_config(void)
 	show_list_str("NoUpgrade", config->noupgrade);
 	show_list_str("NoExtract", config->noextract);
 
-	show_str("Architecture", config->arch);
+	show_list_str("Architecture", config->architectures);
 	show_str("XferCommand", config->xfercommand);
 
 	show_bool("UseSyslog", config->usesyslog);
@@ -364,7 +364,7 @@ static int list_directives(void)
 
 
 		} 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) {
 			show_str("XferCommand", config->xfercommand);
 
diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
index b2aabc08..7e810127 100644
--- a/src/pacman/pacman.c
+++ b/src/pacman/pacman.c
@@ -377,7 +377,7 @@ static int parsearg_global(int opt)
 {
 	switch(opt) {
 		case OP_ARCH:
-			config_set_arch(optarg);
+			config_add_architecture(strdup(optarg));
 			break;
 		case OP_ASK:
 			config->noask = 1;
diff --git a/test/pacman/meson.build b/test/pacman/meson.build
index 52ff9b9a..60722a8a 100644
--- a/test/pacman/meson.build
+++ b/test/pacman/meson.build
@@ -85,6 +85,8 @@ pacman_tests = [
   'tests/mode001.py',
   'tests/mode002.py',
   'tests/mode003.py',
+  'tests/multiple-architectires01.py',
+  'tests/multiple-architectires02.py',
   'tests/noupgrade-inverted.py',
   'tests/overwrite-files-match-negated.py',
   'tests/overwrite-files-match.py',
diff --git a/test/pacman/tests/multiple-architectires01.py b/test/pacman/tests/multiple-architectires01.py
new file mode 100644
index 00000000..39f3e1f7
--- /dev/null
+++ b/test/pacman/tests/multiple-architectires01.py
@@ -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")
diff --git a/test/pacman/tests/multiple-architectires02.py b/test/pacman/tests/multiple-architectires02.py
new file mode 100644
index 00000000..18625ec6
--- /dev/null
+++ b/test/pacman/tests/multiple-architectires02.py
@@ -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)
\ No newline at end of file
-- 
2.31.1


More information about the pacman-dev mailing list