summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Andres Klode <jak@debian.org>2025-10-25 18:42:24 +0000
committerJulian Andres Klode <jak@debian.org>2025-10-25 18:42:24 +0000
commit2698c1e186accebefa320ebacde7ec3c40c66c29 (patch)
tree100d5321f364b389100928e927639e852c52ff48
parenta92a5ecc465055027d9fbb899f359ec7eab110bd (diff)
parent9d62eef571ffd1fd2df8b97884d3689fced27fb1 (diff)
Merge branch 'variants-submit' into 'main'
Merge Architecture variants from Ubuntu See merge request apt-team/apt!526
-rw-r--r--apt-pkg/acquire-item.cc54
-rw-r--r--apt-pkg/aptconfiguration.cc113
-rw-r--r--apt-pkg/aptconfiguration.h9
-rw-r--r--apt-pkg/cacheiterators.h10
-rw-r--r--apt-pkg/contrib/strutl.cc11
-rw-r--r--apt-pkg/contrib/strutl.h1
-rw-r--r--apt-pkg/deb/deblistparser.cc6
-rw-r--r--apt-pkg/deb/deblistparser.h4
-rw-r--r--apt-pkg/deb/debmetaindex.cc5
-rw-r--r--apt-pkg/init.cc17
-rw-r--r--apt-pkg/pkgcache.cc11
-rw-r--r--apt-pkg/pkgcache.h1
-rw-r--r--apt-pkg/pkgcachegen.cc29
-rw-r--r--apt-pkg/tagfile-keys.list1
-rw-r--r--apt-pkg/tagfile-order.c2
-rw-r--r--doc/examples/configure-index6
-rw-r--r--test/integration/framework11
-rwxr-xr-xtest/integration/test-architecture-variants244
-rw-r--r--test/integration/varianttable5
19 files changed, 520 insertions, 20 deletions
diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc
index c692b5281..84f61b8bf 100644
--- a/apt-pkg/acquire-item.cc
+++ b/apt-pkg/acquire-item.cc
@@ -34,13 +34,13 @@
#include <algorithm>
#include <array>
#include <cerrno>
-#include <ctime>
#include <chrono>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
+#include <fstream>
#include <iostream>
#include <memory>
#include <numeric>
@@ -1598,6 +1598,48 @@ void pkgAcqMetaClearSig::QueueIndexes(bool const verify) /*{{{*/
componentsSeen.emplace(std::move(component));
}
+ auto variants = APT::Configuration::getArchitectureVariants(true);
+ std::set<IndexTarget *> supplantedTargets;
+ std::set<std::string> seenVariants;
+ if (hasReleaseFile)
+ {
+ // Figure out the variants that are standalone and supplant 'lesser' variants
+ for (auto &variant : variants)
+ {
+ // Check if the variant is standalone, that is, in the Architectures: field.
+ // FIXME: We should support Standalone-Architecture-Variants field here
+ if (not TransactionManager->MetaIndexParser->IsArchitectureSupported(variant.name))
+ continue;
+
+ seenVariants.insert(variant.name);
+
+ // Iterate over all targets belonging to the architecture variant and then see if they
+ // supplant any other target. For example main/binary-amd64v3/Packages supplants main/binary-amd64/Packages
+ // but it won't supplant the contrib entry, or a Contents file.
+ for (auto &Target : IndexTargets)
+ {
+ if (Target.Option(IndexTarget::ARCHITECTURE) != variant.name)
+ continue;
+
+ for (auto &BaseTarget : IndexTargets)
+ {
+ if (BaseTarget.Option(IndexTarget::IDENTIFIER) == Target.Option(IndexTarget::IDENTIFIER) &&
+ BaseTarget.Option(IndexTarget::COMPONENT) == Target.Option(IndexTarget::COMPONENT) &&
+ // Variants we already saw can never be supplanted here, because they are ordered by preference
+ seenVariants.find(BaseTarget.Option(IndexTarget::ARCHITECTURE)) == seenVariants.end() &&
+ // BaseTarget is supplanted by Target on the architecture CPU
+ std::find(variant.supplants.begin(), variant.supplants.end(), BaseTarget.Option(IndexTarget::ARCHITECTURE)) != variant.supplants.end())
+ {
+ if (_config->FindB("Debug::Acquire::Variants", false))
+ std::clog << "Variant target " << Target.Description << " supplants target " << BaseTarget.Description << std::endl;
+ supplantedTargets.insert(&BaseTarget);
+ }
+ }
+ }
+ //
+ }
+ }
+
for (auto&& Target: IndexTargets)
{
// if we have seen a target which is created-by a target this one here is declared a
@@ -1608,6 +1650,14 @@ void pkgAcqMetaClearSig::QueueIndexes(bool const verify) /*{{{*/
new CleanupItem(Owner, TransactionManager, Target);
continue;
}
+
+ // If the target has been supplanted by a variant target, skip it.
+ if (supplantedTargets.find(&Target) != supplantedTargets.end())
+ {
+ new CleanupItem(Owner, TransactionManager, Target);
+ continue;
+ }
+
// all is an implementation detail. Users shouldn't use this as arch
// We need this support trickery here as e.g. Debian has binary-all files already,
// but arch:all packages are still in the arch:any files, so we would waste precious
@@ -3497,7 +3547,7 @@ pkgAcqArchive::pkgAcqArchive(pkgAcquire *const Owner, pkgSourceList *const Sourc
all repositories containing this file */
StoreFilename = QuoteString(Version.ParentPkg().Name(), "_:") + '_' +
QuoteString(Version.VerStr(), "_:") + '_' +
- QuoteString(Version.Arch(), "_:.") +
+ QuoteString(Version.ArchVariant().empty() ? Version.Arch() : Version.ArchVariant().data(), "_:.") +
'.' += flExtension(poolfilename);
Desc.URI = Index->ArchiveURI(poolfilename);
diff --git a/apt-pkg/aptconfiguration.cc b/apt-pkg/aptconfiguration.cc
index 96ff8b285..a0164c6c3 100644
--- a/apt-pkg/aptconfiguration.cc
+++ b/apt-pkg/aptconfiguration.cc
@@ -24,6 +24,8 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
+#include <fstream>
+#include <optional>
#include <string>
#include <vector>
#include <dirent.h>
@@ -313,6 +315,117 @@ bool Configuration::checkLanguage(std::string Lang, bool const All) {
return (std::find(langs.begin(), langs.end(), Lang) != langs.end());
}
/*}}}*/
+
+// getArchitectures - Return Vector of preferred Architectures /*{{{*/
+
+static std::set<std::string> const &getCpuFlags()
+{
+ static std::set<std::string> flags;
+ if (not flags.empty())
+ return flags;
+
+ std::ifstream table(_config->FindFile("Dir::proc::cpuinfo", "/proc/cpuinfo"));
+ for (std::string line; std::getline(table, line);)
+ {
+ if (not APT::String::Startswith(line, "flags\t"))
+ continue;
+
+ line = line.substr(line.find(':'));
+ line = line.substr(line.find_first_not_of(": "));
+ if (flags.empty())
+ for (auto &&flag : APT::String::Split(line))
+ flags.insert(std::move(flag));
+ else
+ {
+ // Remove flags not present in this CPU
+ auto split = APT::String::Split(line);
+ std::set<std::string> newSet{split.begin(), split.end()};
+ for (auto it = flags.begin(); it != flags.end();)
+ if (newSet.find(*it) == newSet.end())
+ it = flags.erase(it);
+ else
+ ++it;
+ }
+ }
+ return flags;
+}
+std::vector<Configuration::ArchitectureVariant> Configuration::getArchitectureVariants(bool cached)
+{
+ std::optional<std::vector<ArchitectureVariant>> static variants;
+ if (likely(cached) && variants)
+ return *variants;
+
+ variants.emplace();
+
+ auto const configuredVariants = _config->FindVector("APT::Architecture-Variants");
+ auto const architectures = getArchitectures(cached);
+ auto const autoDetect = configuredVariants.size() == 1 && configuredVariants.front() == "auto";
+ auto const autoCpuFlags = getCpuFlags();
+ auto tablepath = _config->FindFile("Dir::dpkg::varianttable", DPKG_DATADIR "/varianttable");
+ std::ifstream table(tablepath);
+ auto isAutoVariant = [&](auto &var)
+ {
+ bool res = true;
+ for (auto want : var.cpuflags)
+ if (autoCpuFlags.find(want) == autoCpuFlags.end())
+ {
+ if (_config->FindB("Debug::Acquire::Variants", false))
+ std::clog << "variant auto: " << var.name << ": missing: " << want << "\n";
+ res = false;
+ }
+ return res;
+ };
+ for (std::string line; std::getline(table, line);)
+ {
+ if (line[0] == '#' || line[0] == '\0')
+ continue;
+ auto cols = APT::String::Split(line);
+ ArchitectureVariant var{cols[0], cols[1], VectorizeString(cols[2], ',')};
+ // Skip disabled architectures.
+ if (std::find(architectures.begin(), architectures.end(), var.base) == architectures.end())
+ {
+ continue;
+ }
+ // See if this variant is configured or we detect it as auto
+ bool enabled = std::find(configuredVariants.begin(), configuredVariants.end(), cols[0]) != configuredVariants.end() || (autoDetect && isAutoVariant(var));
+ if (enabled)
+ variants->push_back(std::move(var));
+ }
+
+ if (not autoDetect)
+ for (auto &var : configuredVariants)
+ if (std::find_if(variants->begin(), variants->end(), [var](auto &v)
+ { return v.name == var; }) == variants->end())
+ {
+ if (var == "auto")
+ _error->Warning("Invalid value for APT::Architecture-Variants: auto cannot be combined with explicit variants");
+ else
+ _error->Warning("Invalid value for APT::Architecture-Variants: %s", var.c_str());
+ }
+
+ // Build the supplants table
+ for (auto var = variants->begin(); var != variants->end(); ++var)
+ {
+ var->supplants.push_back(var->base);
+ for (auto var2 = var + 1; var2 != variants->end(); ++var2)
+ if (var2->base == var->base)
+ var->supplants.push_back(var2->name);
+ }
+
+ // Order variants by preference
+ std::sort(variants->begin(), variants->end(), [&](auto &a, auto &b)
+ { return std::find(configuredVariants.begin(), configuredVariants.end(), a.name) < std::find(configuredVariants.begin(), configuredVariants.end(), b.name); });
+ if (_config->FindB("Debug::Acquire::Variants", false))
+ {
+ std::clog << "Variants enabled:";
+ for (auto &var : *variants)
+ std::clog << " " << var.name;
+ std::clog << std::endl;
+ }
+
+ return *variants;
+}
+ /*}}}*/
// getArchitectures - Return Vector of preferred Architectures /*{{{*/
std::vector<std::string> const Configuration::getArchitectures(bool const &Cached) {
std::vector<std::string> static archs;
diff --git a/apt-pkg/aptconfiguration.h b/apt-pkg/aptconfiguration.h
index 58c925b7f..c1979f3cc 100644
--- a/apt-pkg/aptconfiguration.h
+++ b/apt-pkg/aptconfiguration.h
@@ -131,6 +131,15 @@ namespace Configuration { /*{{{*/
/** \return Check usr is merged or produce error. */
APT_PUBLIC bool checkUsrMerged();
APT_PUBLIC std::string color(std::string const &colorName, std::string const &content = "");
+
+ struct ArchitectureVariant
+ {
+ std::string name;
+ std::string base;
+ std::vector<std::string> cpuflags;
+ std::vector<std::string> supplants{};
+ };
+ APT_HIDDEN std::vector<ArchitectureVariant> getArchitectureVariants(bool cached);
#endif
/*}}}*/
diff --git a/apt-pkg/cacheiterators.h b/apt-pkg/cacheiterators.h
index 45e45cc83..cc2179bf7 100644
--- a/apt-pkg/cacheiterators.h
+++ b/apt-pkg/cacheiterators.h
@@ -281,6 +281,16 @@ class APT_PUBLIC pkgCache::VerIterator : public Iterator<Version, VerIterator> {
{
return (static_cast<Version::Extra *>(Owner->Map.Data()) + S->d)->PhasedUpdatePercentage;
}
+ inline void ArchVariant(map_stringitem_t variant) const
+ {
+ (static_cast<Version::Extra *>(Owner->Map.Data()) + S->d)->ArchVariant = variant;
+ }
+ inline std::string_view ArchVariant() const
+ {
+ if (auto I = (static_cast<Version::Extra *>(Owner->Map.Data()) + S->d)->ArchVariant)
+ return Owner->ViewString(I);
+ return {};
+ }
inline bool PhasedUpdatePercentage(unsigned int percentage)
{
if (percentage > 100)
diff --git a/apt-pkg/contrib/strutl.cc b/apt-pkg/contrib/strutl.cc
index 651c985bd..b5200831a 100644
--- a/apt-pkg/contrib/strutl.cc
+++ b/apt-pkg/contrib/strutl.cc
@@ -138,6 +138,17 @@ size_t DisplayLength(string_view str)
return len;
}
+// Splits by whitespace. There may be continuous spans of whitespace - they
+// will be considered as one.
+std::vector<std::string> Split(std::string const &s)
+{
+ std::vector<std::string> vec;
+ std::istringstream iss(s);
+ iss.imbue(std::locale::classic());
+ for (std::string current_s; iss >> current_s;)
+ vec.push_back(current_s);
+ return vec;
+}
}
}
/*}}}*/
diff --git a/apt-pkg/contrib/strutl.h b/apt-pkg/contrib/strutl.h
index 79b7c8bfa..c22b2dda2 100644
--- a/apt-pkg/contrib/strutl.h
+++ b/apt-pkg/contrib/strutl.h
@@ -48,6 +48,7 @@ namespace APT {
APT_PUBLIC std::string Join(std::vector<std::string> list, const std::string_view &sep);
// Returns string display length honoring multi-byte characters
APT_PUBLIC size_t DisplayLength(std::string_view str);
+ APT_HIDDEN std::vector<std::string> Split(std::string const &str);
}
}
diff --git a/apt-pkg/deb/deblistparser.cc b/apt-pkg/deb/deblistparser.cc
index b62c1a84f..b98198430 100644
--- a/apt-pkg/deb/deblistparser.cc
+++ b/apt-pkg/deb/deblistparser.cc
@@ -215,6 +215,9 @@ bool debListParser::NewVersion(pkgCache::VerIterator &Ver)
Ver->NextInSource = G->VersionsInSource;
G->VersionsInSource = Ver.MapPointer();
+ if (auto ArchVar = Section.Find(pkgTagSection::Key::Architecture_Variant); not ArchVar.empty())
+ Ver.ArchVariant(StoreString(pkgCacheGenerator::MIXED, ArchVar));
+
Ver->MultiArch = ParseMultiArch(true);
// Archive Size
Ver->Size = Section.FindULL(pkgTagSection::Key::Size);
@@ -1017,6 +1020,9 @@ bool debListParser::SameVersion(uint32_t Hash, /*{{{*/
unsigned char MultiArch = ParseMultiArch(false);
if (MultiArch != Ver->MultiArch)
return false;
+
+ if (ArchVariant() != Ver.ArchVariant())
+ return false;
// for all practical proposes (we can check): same version
return true;
}
diff --git a/apt-pkg/deb/deblistparser.h b/apt-pkg/deb/deblistparser.h
index 2a9108775..7ab6800c6 100644
--- a/apt-pkg/deb/deblistparser.h
+++ b/apt-pkg/deb/deblistparser.h
@@ -99,6 +99,10 @@ class APT_HIDDEN debListParser : public pkgCacheListParser
{
return Section.Find(pkgTagSection::Key::SHA256);
}
+ std::string_view ArchVariant() const
+ {
+ return Section.Find(pkgTagSection::Key::Architecture_Variant);
+ }
#endif
};
diff --git a/apt-pkg/deb/debmetaindex.cc b/apt-pkg/deb/debmetaindex.cc
index c040b76b9..b85163654 100644
--- a/apt-pkg/deb/debmetaindex.cc
+++ b/apt-pkg/deb/debmetaindex.cc
@@ -1068,6 +1068,11 @@ class APT_HIDDEN debSLTypeDebian : public pkgSourceList::Type /*{{{*/
auto veryforeign = _config->FindVector("APT::BarbarianArchitectures");
Values.reserve(Values.size() + veryforeign.size());
std::move(veryforeign.begin(), veryforeign.end(), std::back_inserter(Values));
+ // Let's treat architecture variants as architectures for sources.list parsing
+ auto const &variants = APT::Configuration::getArchitectureVariants(true);
+ Values.reserve(Values.size() + variants.size());
+ for (auto const &var : variants)
+ Values.push_back(var.name);
}
// all is a very special architecture users shouldn't be concerned with explicitly
// but if the user does, do not override the choice
diff --git a/apt-pkg/init.cc b/apt-pkg/init.cc
index 61d45e37e..531cd258d 100644
--- a/apt-pkg/init.cc
+++ b/apt-pkg/init.cc
@@ -38,19 +38,6 @@ namespace APT {
APT_HIDDEN extern std::unordered_map<std::string, std::vector<std::string>> ArchToTupleMap;
}
-// Splits by whitespace. There may be continuous spans of whitespace - they
-// will be considered as one.
-static std::vector<std::string> split(std::string const & s)
-{
- std::vector<std::string> vec;
- std::istringstream iss(s);
- iss.imbue(std::locale::classic());
- for(std::string current_s; iss >> current_s; )
- vec.push_back(current_s);
- return vec;
-}
-
-
// pkgInitArchTupleMap - Initialize the architecture tuple map /*{{{*/
// ---------------------------------------------------------------------
/* This initializes */
@@ -67,7 +54,7 @@ static bool pkgInitArchTupleMap()
{
if (cpuline[0] == '#' || cpuline[0] == '\0')
continue;
- auto cpurow = split(cpuline);
+ auto cpurow = APT::String::Split(cpuline);
auto cpu = APT::String::Strip(cpurow.at(0));
cpus.emplace_back(cpu);
@@ -90,7 +77,7 @@ static bool pkgInitArchTupleMap()
if (tupleline[0] == '#' || tupleline[0] == '\0')
continue;
- std::vector<std::string> tuplerow = split(tupleline);
+ std::vector<std::string> tuplerow = APT::String::Split(tupleline);
auto tuple = APT::String::Strip(tuplerow.at(0));
auto arch = APT::String::Strip(tuplerow.at(1));
diff --git a/apt-pkg/pkgcache.cc b/apt-pkg/pkgcache.cc
index 4101fb4e5..3377facac 100644
--- a/apt-pkg/pkgcache.cc
+++ b/apt-pkg/pkgcache.cc
@@ -193,6 +193,17 @@ bool pkgCache::ReMap(bool const &Errorchecks)
list.append(",");
list.append(arch);
}
+ auto const variants = APT::Configuration::getArchitectureVariants(true);
+ if (not variants.empty())
+ {
+ list.append(";");
+ for (auto const &variant : variants)
+ {
+ if (list.back() != ';')
+ list.append(",");
+ list.append(variant.name);
+ }
+ }
if (_config->Find("APT::Architecture") != StrP + HeaderP->Architecture ||
list != StrP + HeaderP->GetArchitectures())
return _error->Error(_("The package cache was built for different architectures: %s vs %s"), StrP + HeaderP->GetArchitectures(), list.c_str());
diff --git a/apt-pkg/pkgcache.h b/apt-pkg/pkgcache.h
index 7a9db3d20..042d928e1 100644
--- a/apt-pkg/pkgcache.h
+++ b/apt-pkg/pkgcache.h
@@ -724,6 +724,7 @@ struct pkgCache::Version
struct pkgCache::Version::Extra
{
uint8_t PhasedUpdatePercentage;
+ map_stringitem_t ArchVariant;
};
#endif
/*}}}*/
diff --git a/apt-pkg/pkgcachegen.cc b/apt-pkg/pkgcachegen.cc
index 3358441ee..f3c3c10a0 100644
--- a/apt-pkg/pkgcachegen.cc
+++ b/apt-pkg/pkgcachegen.cc
@@ -104,12 +104,23 @@ bool pkgCacheGenerator::Start()
map_stringitem_t idxArchitectures;
std::vector<std::string> archs = APT::Configuration::getArchitectures();
- if (archs.size() > 1)
+ auto const &variants = APT::Configuration::getArchitectureVariants(true);
+ if (archs.size() > 1 || not variants.empty())
{
std::vector<std::string>::const_iterator a = archs.begin();
std::string list = *a;
for (++a; a != archs.end(); ++a)
list.append(",").append(*a);
+ if (not variants.empty())
+ {
+ list.append(";");
+ for (auto const &variant : variants)
+ {
+ if (list.back() != ';')
+ list.append(",");
+ list.append(variant.name);
+ }
+ }
idxArchitectures = WriteStringInMap(list);
if (unlikely(idxArchitectures == 0))
return false;
@@ -383,11 +394,20 @@ bool pkgCacheGenerator::MergeListVersion(ListParser &List, pkgCache::PkgIterator
auto Hash = List.VersionHash();
std::string_view ListSHA256;
+ std::string_view ListArchVariant;
bool const Debug = _config->FindB("Debug::pkgCacheGen", false);
auto DebList = dynamic_cast<debListParser *>(&List);
if (DebList != nullptr)
ListSHA256 = DebList->SHA256();
+ if (DebList != nullptr)
+ ListArchVariant = DebList->ArchVariant();
+
+ auto variants = _config->FindVector("APT::Architecture-Variants");
+ // Always add "no variant" at the back of the list. If it's already configured earlier, that wins
+ if (variants.empty() || variants.back() != "")
+ variants.push_back("");
+
if (Ver.end() == false)
{
/* We know the list is sorted so we use that fact in the search.
@@ -426,6 +446,13 @@ bool pkgCacheGenerator::MergeListVersion(ListParser &List, pkgCache::PkgIterator
if (VF.end() == true)
break;
}
+
+ // Variants are ordered by their index in the variant list.
+ if (std::find(variants.begin(), variants.end(), Ver.ArchVariant()) > std::find(variants.begin(), variants.end(), ListArchVariant))
+ {
+ Res = 1;
+ break;
+ }
}
// proceed with the next till we have either the right
// or we found another version (which will be lower)
diff --git a/apt-pkg/tagfile-keys.list b/apt-pkg/tagfile-keys.list
index d198ea0a5..8f5407354 100644
--- a/apt-pkg/tagfile-keys.list
+++ b/apt-pkg/tagfile-keys.list
@@ -81,3 +81,4 @@ Vcs-Svn
Version
### APPEND BELOW, sort in with next ABI break ###
Source-Version
+Architecture-Variant
diff --git a/apt-pkg/tagfile-order.c b/apt-pkg/tagfile-order.c
index 7cf188c51..dbd45d6ac 100644
--- a/apt-pkg/tagfile-order.c
+++ b/apt-pkg/tagfile-order.c
@@ -9,6 +9,7 @@ static const char *iTFRewritePackageOrder[] = {
"Package",
"Package-Type",
"Architecture",
+ "Architecture-Variant",
"Subarchitecture", // NO_KEY: Used only by d-i
"Version",
// "Revision", // Obsolete (warning in dpkg)
@@ -71,6 +72,7 @@ static const char *iTFRewriteSourceOrder[] = {
"Format",
"Binary",
"Architecture",
+ "Architecture-Variant",
"Version",
"Priority",
// "Class", // Obsolete alias for Priority, warning by dpkg
diff --git a/doc/examples/configure-index b/doc/examples/configure-index
index 1b050d0b9..f08026136 100644
--- a/doc/examples/configure-index
+++ b/doc/examples/configure-index
@@ -51,6 +51,7 @@ APT
Audit "<BOOL>"; // display audit messages
Architecture "<STRING>"; // debian architecture like amd64, i386, powerpc, armhf, mips, …
Architectures "<LIST>"; // a list of (foreign) debian architectures, defaults to: dpkg --print-foreign-architectures
+ Architecture-Variants "<STRING_OR_LIST>"; // a list of variants or the string auto
BarbarianArchitectures "<LIST>"; // a list of architectures considered too foreign to satisfy M-A:foreign
Build-Essential "<LIST>"; // list of package names
@@ -468,6 +469,9 @@ Dir "<DIR>"
Boot "<DIR>";
Usr "<DIR>";
Apport "<DIR>";
+ Proc "<DIR"> {
+ CpuInfo "<FILE>";
+ };
};
// Things that effect the APT dselect method
@@ -567,6 +571,7 @@ Debug
Acquire::Transaction "<BOOL>";
Acquire::Progress "<BOOL>";
Acquire::Retries "<BOOL>"; // Debugging for retries, especially delays
+ Acquire::Variants "<BOOL>"; // Debugging for architecture variants
aptcdrom "<BOOL>"; // Show found package files
IdentCdrom "<BOOL>";
acquire::netrc "<BOOL>"; // netrc parser
@@ -900,6 +905,7 @@ dir::filelistdir "<STRING>";
dir::dpkg::tupletable "<FILE>";
dir::dpkg::triplettable "<FILE>";
dir::dpkg::cputable "<FILE>";
+dir::dpkg::varianttable "<FILE>";
Rred::t "<BOOL>";
Rred::f "<BOOL>";
Rred::Compress "<STRING>";
diff --git a/test/integration/framework b/test/integration/framework
index d95a1799a..7780ef835 100644
--- a/test/integration/framework
+++ b/test/integration/framework
@@ -547,6 +547,7 @@ exec fakeroot gdb --quiet -ex run '${DPKG:-dpkg}' --args '${DPKG:-dpkg}' --root=
EOF
chmod +x "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/dpkg" "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/gdb-dpkg"
echo "Dir::Bin::dpkg \"${TMPWORKINGDIRECTORY}/rootdir/usr/bin/dpkg\";" > rootdir/etc/apt/apt.conf.d/99dpkg
+ echo "Dir::dpkg::varianttable \"${TESTDIRECTORY}/varianttable\";" > rootdir/etc/apt/apt.conf.d/99variants
# Set the solver for the test case.
if [ "$APT_SOLVER" ]; then
echo "APT::Solver \"$APT_SOLVER\";" >> aptconfig.conf
@@ -1033,10 +1034,12 @@ insertpackage() {
continue
fi
for arch in $(getarchitecturesfromcommalist "$ARCH"); do
+ local variant="${arch#*:}"
+ arch="${arch%:*}"
if [ "$arch" = 'none' ]; then
ARCHS="$(getarchitectures)"
else
- ARCHS="$arch"
+ ARCHS="$variant"
fi
for BUILDARCH in $ARCHS; do
local PPATH="aptarchive/dists/${RELEASE}/${DISTSECTION}/binary-${BUILDARCH}"
@@ -1054,8 +1057,9 @@ Size: 42"
fi
echo "Maintainer: Joe Sixpack <joe@example.org>"
test "$arch" = 'none' || echo "Architecture: $arch"
+ test "$arch" = "$variant" || echo "Architecture-Variant: $variant"
echo "Version: $VERSION
-Filename: pool/${DISTSECTION}/${NAME}/${NAME}_${VERSION}_${arch}.deb"
+Filename: pool/${DISTSECTION}/${NAME}/${NAME}_${VERSION}_${variant}.deb"
test -z "$DEPENDENCIES" || printf "%b\n" "$DEPENDENCIES"
echo "Description: $(printf '%s' "$DESCRIPTION" | head -n 1)"
echo "Description-md5: $(printf '%s' "$DESCRIPTION" | md5sum | cut -d' ' -f 1)"
@@ -1118,6 +1122,8 @@ insertinstalledpackage() {
local FILE='rootdir/var/lib/dpkg/status'
local INFO='rootdir/var/lib/dpkg/info'
for arch in $(getarchitecturesfromcommalist "$ARCH"); do
+ local variant="${arch#*:}"
+ arch="${arch%:*}"
echo "Package: $NAME
Status: $STATUS
Priority: $PRIORITY" >> "$FILE"
@@ -1128,6 +1134,7 @@ Priority: $PRIORITY" >> "$FILE"
Maintainer: Joe Sixpack <joe@example.org>
Version: $VERSION" >> "$FILE"
test "$arch" = 'none' || echo "Architecture: $arch" >> "$FILE"
+ test "$arch" = "$variant" || echo "Architecture-Variant: $variant" >> "$FILE"
test -z "$DEPENDENCIES" || printf "%b\n" "$DEPENDENCIES" >> "$FILE"
printf "%b\n" "Description: $DESCRIPTION" >> "$FILE"
echo >> "$FILE"
diff --git a/test/integration/test-architecture-variants b/test/integration/test-architecture-variants
new file mode 100755
index 000000000..e116720c9
--- /dev/null
+++ b/test/integration/test-architecture-variants
@@ -0,0 +1,244 @@
+#!/bin/sh
+set -e
+
+TESTDIR="$(readlink -f "$(dirname "$0")")"
+. "$TESTDIR/framework"
+setupenvironment
+configarchitecture 'amd64'
+
+msgmsg "Test basic ordering scenarios"
+
+echo 'APT::Architecture-Variants { "amd64v3"; "amd64v2" }' > 'rootdir/etc/apt/apt.conf.d/variants'
+insertpackage 'unstable' 'foo' 'amd64:amd64v2' '1'
+insertpackage 'unstable' 'foo' 'amd64' '1'
+insertpackage 'unstable' 'foo' 'amd64:amd64v3' '1'
+setupaptarchive --no-update
+
+testsuccessequal "" aptget update -qq
+
+testsuccessequal "foo:
+ Installed: (none)
+ Candidate: 1
+ Version table:
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v2 Packages
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages" aptcache policy foo
+testsuccessequal "'file:${TMPWORKINGDIRECTORY}/aptarchive/pool/main/foo/foo_1_amd64v3.deb' foo_1_amd64v3.deb 42 " apt install --print-uris -qqq foo
+
+echo 'APT::Architecture-Variants { "amd64v2"; "amd64v3" }' > 'rootdir/etc/apt/apt.conf.d/variants'
+testsuccessequal "foo:
+ Installed: (none)
+ Candidate: 1
+ Version table:
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v2 Packages
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages" aptcache policy foo
+testsuccessequal "'file:${TMPWORKINGDIRECTORY}/aptarchive/pool/main/foo/foo_1_amd64v2.deb' foo_1_amd64v2.deb 42 " apt install --print-uris -qqq foo
+echo 'APT::Architecture-Variants { "amd64v3" }' > 'rootdir/etc/apt/apt.conf.d/variants'
+testsuccessequal "foo:
+ Installed: (none)
+ Candidate: 1
+ Version table:
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages" aptcache policy foo
+testsuccessequal "'file:${TMPWORKINGDIRECTORY}/aptarchive/pool/main/foo/foo_1_amd64v3.deb' foo_1_amd64v3.deb 42 " apt install --print-uris -qqq foo
+
+
+echo 'APT::Architecture-Variants { "amd64v3"; "amd64v2" }' > 'rootdir/etc/apt/apt.conf.d/variants'
+
+msgmsg "Test upgrade calculation: amd64v3 updates to amd64"
+
+insertpackage 'unstable' 'foo' 'amd64' '2'
+insertinstalledpackage 'foo' 'amd64:amd64v3' '1'
+setupaptarchive --no-update
+
+testsuccessequal "" aptget update -qq
+testsuccessequal "foo:
+ Installed: 1
+ Candidate: 2
+ Version table:
+ 2 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages
+ *** 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v2 Packages
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages" aptcache policy foo
+testsuccessequal "'file:${TMPWORKINGDIRECTORY}/aptarchive/pool/main/foo/foo_2_amd64.deb' foo_2_amd64.deb 42 " apt dist-upgrade --print-uris -qqq
+
+msgmsg "Test upgrade calculation: amd64v3 updates to amd64v3 now"
+insertpackage 'unstable' 'foo' 'amd64:amd64v3' '2'
+setupaptarchive --no-update
+
+testsuccessequal "" aptget update -qq
+testsuccessequal "foo:
+ Installed: 1
+ Candidate: 2
+ Version table:
+ 2 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ 2 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages
+ *** 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v2 Packages
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages" aptcache policy foo
+testsuccessequal "'file:${TMPWORKINGDIRECTORY}/aptarchive/pool/main/foo/foo_2_amd64v3.deb' foo_2_amd64v3.deb 42 " apt dist-upgrade --print-uris -qqq
+
+msgmsg "Test standalone architecture variants"
+sed -i 's/^Architectures:.*/Architectures: all amd64 amd64v3/' aptarchive/dists/unstable/Release
+signreleasefiles
+testsuccessequal "" aptget update -qq
+testsuccessequal "foo:
+ Installed: 1
+ Candidate: 2
+ Version table:
+ 2 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ *** 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status" aptcache policy foo -o standalone=v3
+testsuccessequal "'file:${TMPWORKINGDIRECTORY}/aptarchive/pool/main/foo/foo_2_amd64v3.deb' foo_2_amd64v3.deb 42 " apt dist-upgrade --print-uris -qqq -o standalone=v3
+
+sed -i 's/^Architectures:.*/Architectures: all amd64 amd64v3 amd64v2/' aptarchive/dists/unstable/Release
+signreleasefiles
+testsuccessequal "" aptget update -qq
+testsuccessequal "foo:
+ Installed: 1
+ Candidate: 2
+ Version table:
+ 2 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ *** 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status" aptcache policy foo -o standalone=v3,v2
+testsuccessequal "'file:${TMPWORKINGDIRECTORY}/aptarchive/pool/main/foo/foo_2_amd64v3.deb' foo_2_amd64v3.deb 42 " apt dist-upgrade --print-uris -qqq -o standalone=v3,v2
+
+sed -i 's/^Architectures:.*/Architectures: all amd64 amd64v2/' aptarchive/dists/unstable/Release
+signreleasefiles
+testsuccessequal "" aptget update -qq
+# Notably v2 is now the standalone architecture so we need to fetch v3 and v2.
+testsuccessequal "foo:
+ Installed: 1
+ Candidate: 2
+ Version table:
+ 2 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ *** 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v2 Packages" aptcache policy foo -o standalone=v2
+testsuccessequal "'file:${TMPWORKINGDIRECTORY}/aptarchive/pool/main/foo/foo_2_amd64v3.deb' foo_2_amd64v3.deb 42 " apt dist-upgrade --print-uris -qqq -o standalone=v2
+
+# amd64v2 is preferred now
+sed -i 's/^Architectures:.*/Architectures: all amd64 amd64v3 amd64v2/' aptarchive/dists/unstable/Release
+echo 'APT::Architecture-Variants { "amd64v2"; "amd64v3" }' > 'rootdir/etc/apt/apt.conf.d/variants'
+signreleasefiles
+testsuccessequal "" aptget update -qq
+testsuccessequal "foo:
+ Installed: 1
+ Candidate: 2
+ Version table:
+ 2 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v2 Packages
+ *** 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v3 Packages
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status" aptcache policy foo -o standalone=v3,v2 -o pref=v2
+msgtest 'Test that amd64v2 is the candidate of the version 1'
+testsuccessequal --nomsg "'file:${TMPWORKINGDIRECTORY}/aptarchive/pool/main/foo/foo_1_amd64v2.deb' foo_1_amd64v2.deb 42 " apt dist-upgrade --print-uris -qqq -o standalone=v3,v2 -o pref=v2 foo=1
+
+msgmsg "Test automatic detection"
+echo "flags : lm" > rootdir/cpuinfo
+echo "flags : lm cmov cx8 fpu fxsr mmx syscall sse2 cx16 lahf_lm popcnt sse4_1 sse4_2 ssse3" >> rootdir/cpuinfo
+echo 'APT::Architecture-Variants "auto";' > 'rootdir/etc/apt/apt.conf.d/variants'
+echo 'Dir::Proc::CpuInfo "cpuinfo";'>> 'rootdir/etc/apt/apt.conf.d/variants'
+testsuccessequal "" aptget update -qq
+testsuccessequal "foo:
+ Installed: 1
+ Candidate: 2
+ Version table:
+ 2 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status" aptcache policy foo -o auto=intersection
+
+echo "flags : lm cmov cx8 fpu fxsr mmx syscall sse2 cx16 lahf_lm popcnt sse4_1 sse4_2 ssse3" > rootdir/cpuinfo
+echo 'APT::Architecture-Variants "auto";' > 'rootdir/etc/apt/apt.conf.d/variants'
+echo 'Dir::Proc::CpuInfo "cpuinfo";'>> 'rootdir/etc/apt/apt.conf.d/variants'
+testsuccessequal "" aptget update -qq
+testsuccessequal "foo:
+ Installed: 1
+ Candidate: 1
+ Version table:
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v2 Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status" aptcache policy foo -o auto=v2
+
+# Not a valid value for the auto field.
+echo 'APT::Architecture-Variants "invalid";' > 'rootdir/etc/apt/apt.conf.d/variants'
+echo 'Dir::Proc::CpuInfo "cpuinfo";'>> 'rootdir/etc/apt/apt.conf.d/variants'
+testwarningequal "W: Invalid value for APT::Architecture-Variants: invalid" aptget update -qq
+testwarningequal "foo:
+ Installed: 1
+ Candidate: 2
+ Version table:
+ 2 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+W: Invalid value for APT::Architecture-Variants: invalid" aptcache policy foo -o auto=invalid
+
+# The unqualified one takes precedent
+echo 'APT::Architecture-Variants "invalid";' > 'rootdir/etc/apt/apt.conf.d/variants'
+echo 'APT::Architecture-Variants:: "amd64v2";' >> 'rootdir/etc/apt/apt.conf.d/variants'
+echo 'Dir::Proc::CpuInfo "cpuinfo";'>> 'rootdir/etc/apt/apt.conf.d/variants'
+testwarningequal "Variants enabled:
+W: Invalid value for APT::Architecture-Variants: invalid" aptget update -qq -o debug::acquire::variants=1
+testwarningequal "foo:
+ Installed: 1
+ Candidate: 2
+ Version table:
+ 2 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+W: Invalid value for APT::Architecture-Variants: invalid" aptcache policy foo -o auto=invalid+v2
+
+
+echo 'APT::Architecture-Variants "auto,amd64v2";' > 'rootdir/etc/apt/apt.conf.d/variants'
+echo 'Dir::Proc::CpuInfo "cpuinfo";'>> 'rootdir/etc/apt/apt.conf.d/variants'
+testwarningequal "Variants enabled: amd64v2
+Variant target file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v2 Packages supplants target file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64 Packages
+W: Invalid value for APT::Architecture-Variants: auto cannot be combined with explicit variants" aptget update -qq -o debug::acquire::variants=1
+testwarningequal "foo:
+ Installed: 1
+ Candidate: 1
+ Version table:
+ 1 500
+ 500 file:${TMPWORKINGDIRECTORY}/aptarchive unstable/main amd64v2 Packages
+ *** 1 100
+ 100 ${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status
+W: Invalid value for APT::Architecture-Variants: auto cannot be combined with explicit variants" aptcache policy foo -o auto=auto,v2
diff --git a/test/integration/varianttable b/test/integration/varianttable
new file mode 100644
index 000000000..29cccba93
--- /dev/null
+++ b/test/integration/varianttable
@@ -0,0 +1,5 @@
+# <variant> <arch> <flags>
+amd64v4 amd64 lm,cmov,cx8,fpu,fxsr,mmx,syscall,sse2,cx16,lahf_lm,popcnt,sse4_1,sse4_2,ssse3,avx,avx2,bmi1,bmi2,f16c,fma,abm,movbe,xsave,avx512f,avx512bw,avx512cd,avx512dq,avx512vl
+amd64v3 amd64 lm,cmov,cx8,fpu,fxsr,mmx,syscall,sse2,cx16,lahf_lm,popcnt,sse4_1,sse4_2,ssse3,avx,avx2,bmi1,bmi2,f16c,fma,abm,movbe,xsave
+amd64v2 amd64 lm,cmov,cx8,fpu,fxsr,mmx,syscall,sse2,cx16,lahf_lm,popcnt,sse4_1,sse4_2,ssse3
+amd64v1 amd64 lm,cmov,cx8,fpu,fxsr,mmx,syscall,sse2