From 50d8703c785898d06463513896e37cbe7cedd471 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Mon, 28 Jul 2025 12:06:00 +0200 Subject: Move split() to APT::String::Split() This splits based upon contiguous whitespace as separators --- apt-pkg/contrib/strutl.cc | 11 +++++++++++ apt-pkg/contrib/strutl.h | 1 + apt-pkg/init.cc | 17 ++--------------- 3 files changed, 14 insertions(+), 15 deletions(-) (limited to 'apt-pkg') 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 Split(std::string const &s) +{ + std::vector 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 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 Split(std::string const &str); } } 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> ArchToTupleMap; } -// Splits by whitespace. There may be continuous spans of whitespace - they -// will be considered as one. -static std::vector split(std::string const & s) -{ - std::vector 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 tuplerow = split(tupleline); + std::vector tuplerow = APT::String::Split(tupleline); auto tuple = APT::String::Strip(tuplerow.at(0)); auto arch = APT::String::Strip(tuplerow.at(1)); -- cgit v1.2.3-70-g09d2 From 9d62eef571ffd1fd2df8b97884d3689fced27fb1 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Tue, 7 Jan 2025 17:39:32 +0100 Subject: Implement architecture variants Architecture variants are children of an architecture that share the same ABI but are optimized for different ISA levels. They are available in Ubuntu 25.10 and newer, and not supported in Debian or other distributions. A deb built for a variant contains the Architecture-Variant field, and the Architecture field points to the baseline, for example: Architecture: amd64 Architecture-Variant: amd64v3 However, the apt-get indextargets command reports the variant in the Architecture: field, and most of the code in APT presents the variant as the architecture. There are two types of variants: 1. Standalone variants are recorded in the Architectures field of the Release file as if they were a real architecture: Architectures: amd64 amd64v3 Standalone architecture variants only fetch the standalone architecture variant's Packages file. To do this, this patch changes the code such that the variants indextargets "supplant" the base targets. This may have complicated outcomes on the apt-get indextargets command. 2. Other variants can only be identified by their files being recorded with hashes in the Release file. APT fetches both the base architecture's as well as the variant's Packages file. Variants are configured in the APT::Architecture-Variants list. Image builders may want to build specific variant images using APT::Architecture-Variants { "amd64v3"; } But this commit also implements an automatic discovery mechanism using the varianttable and /proc/cpuinfo. APT::Architecture-Variants "auto"; --- apt-pkg/acquire-item.cc | 54 +++++- apt-pkg/aptconfiguration.cc | 113 +++++++++++++ apt-pkg/aptconfiguration.h | 9 + apt-pkg/cacheiterators.h | 10 ++ apt-pkg/deb/deblistparser.cc | 6 + apt-pkg/deb/deblistparser.h | 4 + apt-pkg/deb/debmetaindex.cc | 5 + apt-pkg/pkgcache.cc | 11 ++ apt-pkg/pkgcache.h | 1 + apt-pkg/pkgcachegen.cc | 29 +++- apt-pkg/tagfile-keys.list | 1 + apt-pkg/tagfile-order.c | 2 + doc/examples/configure-index | 6 + test/integration/framework | 11 +- test/integration/test-architecture-variants | 244 ++++++++++++++++++++++++++++ test/integration/varianttable | 5 + 16 files changed, 506 insertions(+), 5 deletions(-) create mode 100755 test/integration/test-architecture-variants create mode 100644 test/integration/varianttable (limited to 'apt-pkg') 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 #include #include -#include #include #include #include #include #include #include +#include #include #include #include @@ -1598,6 +1598,48 @@ void pkgAcqMetaClearSig::QueueIndexes(bool const verify) /*{{{*/ componentsSeen.emplace(std::move(component)); } + auto variants = APT::Configuration::getArchitectureVariants(true); + std::set supplantedTargets; + std::set 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 #include #include +#include +#include #include #include #include @@ -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 const &getCpuFlags() +{ + static std::set 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 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::getArchitectureVariants(bool cached) +{ + std::optional> 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 const Configuration::getArchitectures(bool const &Cached) { std::vector 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 cpuflags; + std::vector supplants{}; + }; + APT_HIDDEN std::vector 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 { { return (static_cast(Owner->Map.Data()) + S->d)->PhasedUpdatePercentage; } + inline void ArchVariant(map_stringitem_t variant) const + { + (static_cast(Owner->Map.Data()) + S->d)->ArchVariant = variant; + } + inline std::string_view ArchVariant() const + { + if (auto I = (static_cast(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/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/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 archs = APT::Configuration::getArchitectures(); - if (archs.size() > 1) + auto const &variants = APT::Configuration::getArchitectureVariants(true); + if (archs.size() > 1 || not variants.empty()) { std::vector::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(&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 ""; // display audit messages Architecture ""; // debian architecture like amd64, i386, powerpc, armhf, mips, … Architectures ""; // a list of (foreign) debian architectures, defaults to: dpkg --print-foreign-architectures + Architecture-Variants ""; // a list of variants or the string auto BarbarianArchitectures ""; // a list of architectures considered too foreign to satisfy M-A:foreign Build-Essential ""; // list of package names @@ -468,6 +469,9 @@ Dir "" Boot ""; Usr ""; Apport ""; + Proc " { + CpuInfo ""; + }; }; // Things that effect the APT dselect method @@ -567,6 +571,7 @@ Debug Acquire::Transaction ""; Acquire::Progress ""; Acquire::Retries ""; // Debugging for retries, especially delays + Acquire::Variants ""; // Debugging for architecture variants aptcdrom ""; // Show found package files IdentCdrom ""; acquire::netrc ""; // netrc parser @@ -900,6 +905,7 @@ dir::filelistdir ""; dir::dpkg::tupletable ""; dir::dpkg::triplettable ""; dir::dpkg::cputable ""; +dir::dpkg::varianttable ""; Rred::t ""; Rred::f ""; Rred::Compress ""; 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 " 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 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 @@ +# +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 -- cgit v1.2.3-70-g09d2