Import Upstream version 28

This commit is contained in:
su-fang 2023-02-21 15:51:52 +08:00
commit c1f363234a
4 changed files with 537 additions and 0 deletions

85
cargo-auto-test Executable file
View File

@ -0,0 +1,85 @@
#!/bin/bash
# Run `cargo test` on an installed Debian crate, e.g. in an autopkgtest.
#
# Requires dev-dependencies to be installed. If you give extra flags such as
# "--all-features", then these features' dependencies must also be installed.
#
# This script also swallows some known false-positive test failures, so you
# don't have to set test_is_broken everywhere in your debcargo.toml configs.
set -e
pkg="$1"
ver="$2"
if [ -z "$pkg" -o -z "$ver" ]; then
echo >&2 "Usage: $0 <crate> <version> [<extra test flags, e.g. --all-features>]"
exit 2
fi
shift 2
pkgdir="/usr/share/cargo/registry/${pkg}-${ver}"
if [ ! -d "$pkgdir" ]; then
echo >&2 "crate directory not found: $pkgdir"
exit 1
fi
cd "$pkgdir"
rundir=$(mktemp -d)
# https://stackoverflow.com/a/14812383 inside "trap" avoids running handler twice
trap 'excode=$?; rm -rf "'"$rundir"'"; trap - EXIT' EXIT HUP INT QUIT PIPE TERM
# set envvars necessary for cargo-debian-wrapper
export DEB_CARGO_CRATE="${pkg}_${ver}"
export CARGO_HOME="$rundir/debian/cargo_home"
mkdir -p "$CARGO_HOME"
mkdir -p "$rundir/registry"
. <(dpkg-buildflags --export)
. <(dpkg-architecture -s)
RUST_TYPE="$(rustc --version --verbose | sed -ne 's/^host: //p')"
export DEB_HOST_RUST_TYPE="$RUST_TYPE"
export DEB_BUILD_RUST_TYPE="$RUST_TYPE"
export DEB_TARGET_RUST_TYPE="$RUST_TYPE"
export CARGO_TARGET_DIR="$rundir/target"
export PATH="/usr/share/cargo/bin:$PATH"
# run the test!
if [ -t 2 ]; then flags="--color=always"; export RUSTDOCFLAGS="--color=always"; fi
run_test() {
local n="$1"
shift
{ cargo test $flags "$@" 2>&1; echo "$?" > "$rundir/run-$n.ec"; } | tee "$rundir/run-$n.log"
sed -i -e 's/\x1b\[[0-9;]*m//g' "$rundir/run-$n.log" # rm ansi colour codes
local x="$(< "$rundir/run-$n.ec")"
if [ "$x" = "0" ]; then
return 0
fi
local e="$(egrep -o '^error\[E[0-9]+\]' "$rundir/run-$n.log" | sort -u | tr '\n' ',')"
# some crates like to update their old versions to depend on their new
# versions, e.g. rand-core. unfortunately this breaks cargo test, see
# https://github.com/rust-lang/cargo/issues/6819. so just ignore them.
if [ "$e" = "error[E0463],error[E0465]," ]; then
echo "cargo-auto-test: ignore test failure due to upstream #6819: $e"
return 0
fi
return "$x"
}
set +e
cargo prepare-debian "$rundir/registry" --link-from-system
run_test 0 "$@"
x="$?"
if [ "$x" = "0" ]; then exit 0; fi
if ! egrep -q '^error\[E0554\]' "$rundir/run-0.log"; then exit "$x"; fi
echo
echo "----------------------------------------------------------------"
echo "cargo-auto-test: re-running test with RUSTC_BOOTSTRAP due to E0554"
RUSTC_BOOTSTRAP=1 run_test 1 "$@"

219
cargo.pm Normal file
View File

@ -0,0 +1,219 @@
# debhelper buildsystem for Rust crates using Cargo
#
# This builds Debian rust crates to be installed into a system-level
# crate registry in /usr/share/cargo/registry containing crates that
# can be used and Build-Depended upon by other Debian packages. The
# debcargo(1) tool will automatically generate Debian source packages
# that uses this buildsystem and packagers are not expected to use this
# directly which is why the documentation is poor.
#
# If you have a multi-language program such as firefox or librsvg that
# includes private Rust crates or libraries not exposed to others, you
# should instead use our cargo wrapper, in /usr/share/cargo/bin/cargo,
# which this script also uses. That file contains usage instructions.
# You then should define a Build-Depends on cargo and not dh-cargo.
# The Debian cargo package itself also uses the wrapper as part of its
# own build, which you can look at for a real usage example.
#
# Josh Triplett <josh@joshtriplett.org>
# Ximin Luo <infinity0@debian.org>
package Debian::Debhelper::Buildsystem::cargo;
use strict;
use warnings;
use Cwd;
use Debian::Debhelper::Dh_Lib;
use Dpkg::Changelog::Debian;
use Dpkg::Control::Info;
use Dpkg::Version;
use JSON::PP;
use base 'Debian::Debhelper::Buildsystem';
sub DESCRIPTION {
"Rust Cargo"
}
sub cargo_version {
my $src = shift;
open(F, "cargo metadata --manifest-path $src --no-deps --format-version 1 |");
local $/;
my $json = JSON::PP->new;
my $manifest = $json->decode(<F>);
return %{@{%{$manifest}{'packages'}}[0]}{'version'} . "";
}
sub deb_host_rust_type {
open(F, 'printf "include /usr/share/rustc/architecture.mk\n\
all:\n\
echo \$(DEB_HOST_RUST_TYPE)\n\
" | make --no-print-directory -sf - |');
$_ = <F>;
chomp;
return $_;
}
sub check_auto_buildable {
my $this = shift;
if (-f $this->get_sourcepath("Cargo.toml")) {
return 1;
}
return 0;
}
sub new {
my $class = shift;
my $this = $class->SUPER::new(@_);
$this->enforce_in_source_building();
return $this;
}
sub pre_building_step {
my $this = shift;
my $step = shift;
# Many files are coming from crates.io with incorrect timestamp
# See https://github.com/rust-lang/crates.io/issues/3859
complex_doit("find . ! -newermt 'jan 01, 2000' -exec touch -d@" . $ENV{SOURCE_DATE_EPOCH} . " {} +");
$this->{cargo_home} = Cwd::abs_path("debian/cargo_home");
$this->{host_rust_type} = deb_host_rust_type;
my $control = Dpkg::Control::Info->new();
my $source = $control->get_source();
my $crate = $source->{'X-Cargo-Crate'};
if (!$crate) {
$crate = $source->{Source};
$crate =~ s/^ru[sz]t-//;
$crate =~ s/-[0-9]+(\.[0-9]+)*$//;
}
$this->{crate} = $crate;
my $changelog = Dpkg::Changelog::Debian->new(range => { count => 1 });
$changelog->load("debian/changelog");
$this->{version} = Dpkg::Version->new(@{$changelog}[0]->get_version())->version();
my @packages = $control->get_packages();
$this->{libpkg} = 0;
$this->{binpkg} = 0;
$this->{featurepkg} = [];
foreach my $package (@packages) {
if ($package->{Package} =~ /^libru[sz]t-.*-dev$/) {
if ($package->{Package} =~ /\+/) {
push(@{$this->{featurepkg}}, $package->{Package});
next;
}
if ($this->{libpkg}) {
error("Multiple Cargo lib packages found: " . $this->{libpkg} . " and " . $package->{Package});
}
$this->{libpkg} = $package->{Package};
} elsif ($package->{Architecture} ne 'all') {
$this->{binpkg} = $package->{Package};
}
}
if (!$this->{libpkg} && !$this->{binpkg}) {
error("Could not find any Cargo lib or bin packages to build.");
}
if (@{$this->{featurepkg}} && !$this->{libpkg}) {
error("Found feature packages but no lib package.");
}
my $parallel = $this->get_parallel();
$this->{j} = $parallel > 0 ? ["-j$parallel"] : [];
$ENV{'CARGO_HOME'} = $this->{cargo_home};
$ENV{'DEB_CARGO_CRATE'} = $crate . '_' . $this->{version};
$ENV{'DEB_HOST_RUST_TYPE'} = $this->{host_rust_type};
$ENV{'DEB_HOST_GNU_TYPE'} = dpkg_architecture_value("DEB_HOST_GNU_TYPE");
$this->SUPER::pre_building_step($step);
}
sub get_sources {
my $this=shift;
opendir(my $dirhandle, '.');
my @sources = grep { $_ ne '.' && $_ ne '..' && $_ ne '.git' && $_ ne '.pc' && $_ ne 'debian' } readdir($dirhandle);
push @sources, 'debian/patches' if -d 'debian/patches';
closedir($dirhandle);
@sources
}
sub configure {
my $this=shift;
doit("cp", "debian/cargo-checksum.json",
".cargo-checksum.json");
if (defined $ENV{'CARGO_VENDOR_DIR'}) {
doit("/usr/share/cargo/bin/cargo", "prepare-debian", $ENV{'CARGO_VENDOR_DIR'});
doit("/usr/share/cargo/bin/dh-cargo-vendored-sources");
} else {
rm_files("Cargo.lock");
doit("/usr/share/cargo/bin/cargo", "prepare-debian", "debian/cargo_registry", "--link-from-system");
}
}
sub test {
my $this=shift;
my $cmd="build";
if (!defined $_[0]) {
# nop
} elsif ($_[0] eq "test") {
$cmd="test";
shift;
} elsif ($_[0] eq "build") {
shift;
}
# Check that the thing compiles. This might fail if e.g. the package
# requires non-rust system dependencies and the maintainer didn't provide
# this additional information to debcargo.
doit("/usr/share/cargo/bin/cargo", $cmd, @_);
# test generating Built-Using fields
doit("env", "CARGO_CHANNEL=debug", "/usr/share/cargo/bin/dh-cargo-built-using");
}
sub install {
my $this=shift;
my $destdir=shift;
my $crate = $this->{crate} . '-' . ($this->{version} =~ tr/~/-/r);
if ($this->{libpkg}) {
my $target = tmpdir($this->{libpkg}) . "/usr/share/cargo/registry/$crate";
my @sources = $this->get_sources();
install_dir($target);
doit("cp", "--parents", "-at", $target, @sources);
doit("rm", "-rf", "$target/target");
doit("cp", "debian/cargo-checksum.json", "$target/.cargo-checksum.json");
# prevent an ftpmaster auto-reject regarding files with old dates.
doit("touch", "-d@" . $ENV{SOURCE_DATE_EPOCH}, "$target/Cargo.toml");
}
foreach my $pkg (@{$this->{featurepkg}}) {
my $target = tmpdir($pkg) . "/usr/share/doc";
install_dir($target);
make_symlink_raw_target($this->{libpkg}, "$target/$pkg");
}
if ($this->{binpkg}) {
# Do the install
my $destdir = $ENV{'DESTDIR'} || tmpdir($this->{binpkg});
doit("env", "DESTDIR=$destdir",
"/usr/share/cargo/bin/cargo", "install", @_);
if (defined $ENV{'CARGO_VENDOR_DIR'}) {
if (-e "Cargo.lock") {
my $lockfile_dir=tmpdir($this->{binpkg}) . "/usr/share/doc/" . $this->{binpkg};
install_dir($lockfile_dir);
install_file("Cargo.lock", $lockfile_dir . "/Cargo.lock");
} else {
warning("Cargo.lock file not found!");
}
}
# generate Built-Using fields
doit("env", "/usr/share/cargo/bin/dh-cargo-built-using", $this->{binpkg});
}
}
sub clean {
my $this=shift;
doit("touch", "--no-create", "-d@" . $ENV{SOURCE_DATE_EPOCH}, ".cargo_vcs_info.json");
doit("/usr/share/cargo/bin/cargo", "clean", @_);
rm_files(".cargo-checksum.json");
doit("rm", "-rf", "debian/cargo_registry");
}
1

173
dh-cargo-built-using Executable file
View File

@ -0,0 +1,173 @@
#!/bin/sh
# Generates Built-Using after a successful cargo build.
# Run this in the package top-level directory where debian/ is.
set -e
DEB_HOST_RUST_TYPE="${DEB_HOST_RUST_TYPE:-$(printf "include /usr/share/rustc/architecture.mk\nall:\n\techo \$(DEB_HOST_RUST_TYPE)\n" | make --no-print-directory -sf -)}"
CARGO_REGISTRY="${CARGO_REGISTRY:-debian/cargo_registry}"
CARGO_CHANNEL="${CARGO_CHANNEL:-release}"
# useful for testing:
# CARGO_REGISTRY="$HOME/.cargo/registry/src/github.com-1ecc6299db9ec823" DEB_HOST_RUST_TYPE="." CARGO_CHANNEL=debug
CARGO_TARGET_DIR="target/$DEB_HOST_RUST_TYPE/$CARGO_CHANNEL"
CARGO_TARGET_DIR_ABS="$(readlink -f "$CARGO_TARGET_DIR")"
CPRIGHT_FORMAT="https://www.debian.org/doc/packaging-manuals/copyright-format/1.0"
SRCLEFT_LICENSES="$(echo GPL LGPL AGPL GFDL MPL CDDL CPL Artistic Perl QPL | tr ' ' '\n')"
pkg_has_srcleft_license() {
local pkg="$1"
local ver="$2"
local f="/usr/share/doc/$pkg/copyright"
if ! sed -nre 's ^Format: (.*) \1 gp' "$f" | grep -qiw "$CPRIGHT_FORMAT"; then
echo >&2 "$0: abort: Not in machine-readable format: $f"
echo 2
elif sed -nre 's ^X-Binary-Requires-Source: (.*) \1 gp' "$f" | grep -qiw yes; then
echo 1
elif sed -nre 's ^License: (.*) \1 gp' "$f" | grep -qiwF "$SRCLEFT_LICENSES"; then
echo 1
else
echo 0
fi
}
dep_files_to_pkgs() {
xargs -r dpkg -S \
| sed -nre 's (.*): .* \1 gp' \
| xargs -r dpkg-query --show \
| while read pkg ver; do echo "$pkg $ver $(pkg_has_srcleft_license "${pkg%:*}" "$ver")"; done
# pkg_has_srcleft_license should be accurate for all rust crates, no need to give a $containing_crate
# this is due to nature of crate copyright info, and the debian rust packaging policy
}
rust_dep_files() {
cat "$CARGO_TARGET_DIR/deps"/*.d \
| sed -nre 's ^\S*/('"$CARGO_REGISTRY"'/[^/]*)/.* '"$(readlink -f "$PWD")/"'\1 gp' \
| sort -u \
| xargs -r readlink -f
}
rust_libs() {
{ which rustc; rust_dep_files; } | dep_files_to_pkgs
}
gcc_default_searchdirs() {
gcc -print-search-dirs \
| sed -nre 's ^libraries: (.*) \1 gp' \
| tr ':' '\n' \
| sed -e 's ^= '"$(gcc -print-sysroot)"' g' \
| xargs readlink -m 2>/dev/null # suppress errors caused by early pipe closure
}
rust_search_lib() {
local lib="$1"
{
cat
# rust does not actually search normal paths when linking static libs
# - see https://github.com/rust-lang/rust/issues/43118
# - see also `fn link_rlib` in back/link.rs which calls
# `pub fn find_library` in back/archive.rs which generates the error message
#gcc_default_searchdirs
} | while read searchdir; do
#echo >&2 "searching $searchdir for static lib $lib"
local f="$(readlink -m "$searchdir/lib${lib}.a")"
if test -f "$f"; then
printf "%s\n" "$f"
break
fi
done
}
native_libs() {
ls -1d "$CARGO_TARGET_DIR/build"/*/output 2>/dev/null | while read output; do
sed -nre 's ^cargo:rustc-link-lib=static=(.*) \1 '"$output"' gp' "$output"
done | while read lib output; do
local containing_crate="$(basename "$(dirname "$output")")"
test -n "$lib" || continue
local libfile="$(sed -nre 's ^cargo:rustc-link-search=native=(.*) \1 gp' "$output" | rust_search_lib "$lib")"
local srcleft=""
test -n "$libfile" || { echo >&2 "$0: abort: could not find static lib '$lib'; rustc should have failed already?"; exit 1; }
echo >&2 "$0: found static lib $lib at $libfile"
if [ "${libfile#$CARGO_TARGET_DIR_ABS/}" != "$libfile" ]; then
# static library source code embedded in crate
local srcstat="$(sed -nre 's ^dh-cargo:deb-built-using='"$lib"'=([01]=.*) \1 gp' "$output")"
case "$srcstat" in
0=*|1=*)
srcleft="${srcstat%%=*}"
libfile="${srcstat#*=}"
if [ "$(readlink -f "$libfile")" = "$(readlink -f "$PWD")" ]; then
# Note that this exception only applies in the case that where you are building
# the Debian package for $containing_crate itself. In the case where you are
# building a Debian package for crate X depending on $containing_crate, the
# latter still has to output the dh-cargo:deb-built-using in their build.rs so
# that the Debian package for crate X can correctly set Built-Using themselves.
echo >&2 "$0: static library derived from $libfile which is the top-level crate being built, no need to add Built-Using"
continue
fi
;;
*)
echo >&2 "$0: abort: could not determine source-distribution conditions of ${libfile#$CARGO_TARGET_DIR_ABS/}."
echo >&2 "You must patch build.rs of ${containing_crate%-*} to output 'println!(\"dh-cargo:deb-built-using=$lib=\$s={}\", env::var(\"CARGO_MANIFEST_DIR\").unwrap());' where:"
echo >&2 "- \$s is 1 if the license(s) of the included static libs require source distribution alongside binaries, otherwise 0"
exit 1
;;
esac
fi
local wpkg="$(dpkg -S "$(readlink -f "$libfile")")"
test -n "$wpkg" || { echo >&2 "$0: abort: could not find Debian package for file $libfile"; exit 1; }
local pkgstat="$(echo "$wpkg" | sed -nre 's (.*): .* \1 gp' | xargs -r dpkg-query --show)"
local pkg="$(echo "$pkgstat" | cut -f1)"
local ver="$(echo "$pkgstat" | cut -f2)"
# static library source code embedded in crate (from earlier)
if [ -n "$srcleft" ]; then
echo "$pkg $ver $srcleft"
# static libraries from another Debian package
elif sed -nre 's ^dh-cargo:deb-built-using='"$lib"'=0~=(.*) \1 gp' "$output" | { echo "${pkg%:*} $ver" | grep -qExf /dev/fd/3; } 3<&0; then
echo "$pkg $ver 0"
elif sed -nre 's ^dh-cargo:deb-built-using='"$lib"'=1~=(.*) \1 gp' "$output" | { echo "${pkg%:*} $ver" | grep -qExf /dev/fd/3; } 3<&0; then
echo "$pkg $ver 1"
else
# guess the conditions based on the whole d/copyright file
# this loses granularity, e.g. gcc is mostly distributed as GPL-3 but the libbacktrace portion is BSD-3
# to retain granularity the crate package maintainer should patch build.rs as suggested
echo >&2 "$0: warning: guessing source-distribution conditions of $libfile, this may be inaccurate."
echo >&2 "$0: warning: patch build.rs to suppress the above warning"
srcleft="$(pkg_has_srcleft_license "${pkg%:*}" "$ver")"
if [ "$srcleft" -gt 1 ]; then
echo >&2 "You must patch build.rs of ${containing_crate%-*} to output 'dh-cargo:deb-built-using=$lib=\$s~=\$PAT' where:"
echo >&2 "- \$s is 1 if the license(s) of the included static libs require source distribution alongside binaries, otherwise 0"
echo >&2 "- \$PAT is an egrep pattern matching the \"\$pkg \$ver\" combinations that satisfy \$s"
echo >&2 " for example '$pkg .*' matches the currently-relevant package, $pkg $ver"
exit 1
fi
echo "$pkg $ver $srcleft"
fi
done
}
output() {
local binpkg="$1"
if [ -z "$binpkg" ]; then
cat
else
local built_using=""
local built_using_x=""
while read pkg ver srcleft; do
local src="$(dpkg-query -f '${source:Package}' --show "$pkg")"
local srcver="$(dpkg-query -f '${source:Version}' --show "$pkg")"
case "$srcleft" in
2) exit 1;;
1) built_using="${built_using}$src (= $srcver), ";;
esac
built_using_x="${built_using_x}$src (= $srcver), "
done
echo "cargo:Built-Using=${built_using%, }" >> "debian/$binpkg.substvars"
echo "cargo:X-Cargo-Built-Using=${built_using_x%, }" >> "debian/$binpkg.substvars"
fi
}
native_libs="$(native_libs)" # capture output outside of pipe so set -e works
{
rust_libs
test -z "$native_libs" || echo "$native_libs"
} | LC_COLLATE=C.utf-8 sort -u | output "$@"

60
dh-cargo-vendored-sources Executable file
View File

@ -0,0 +1,60 @@
#!/usr/bin/perl
#
# Checks that the current package's Vendored-Sources-Rust field is up-to-date
use strict;
use warnings;
use Dpkg::Control::Info;
use JSON::PP;
unless (defined $ENV{CARGO_VENDOR_DIR}) {
print STDERR "CARGO_VENDOR_DIR environment variable not defined, aborting\n";
exit 1;
}
my $vendor_dir = $ENV{CARGO_VENDOR_DIR};
sub cargo_info {
my $src = shift;
open(F, "cargo metadata --manifest-path $src --no-deps --format-version 1 |");
local $/;
my $json = JSON::PP->new;
my $manifest = $json->decode(<F>);
close(F);
my $crate = %{@{%{$manifest}{'packages'}}[0]}{'name'};
my $version = %{@{%{$manifest}{'packages'}}[0]}{'version'};
return "$crate\@$version";
}
my @unsorted_expected = ();
# We operate under the assumption of a flat vendor directory with one crate per subdir,
# as is generated by `cargo vendor`.
opendir my $dh, $vendor_dir or die "Cannot open directory: $!";
my @crates = readdir $dh;
foreach my $crate (@crates) {
next if $crate eq ".";
next if $crate eq "..";
next unless -d "$vendor_dir/$crate";
next unless -e "$vendor_dir/$crate/Cargo.toml";
push(@unsorted_expected, cargo_info("$vendor_dir/$crate/Cargo.toml"));
}
closedir $dh;
my @expected = sort @unsorted_expected;
my $control = Dpkg::Control::Info->new();
my $source = $control->get_source();
my $actual_raw = $source->get_custom_field('Vendored-Sources-Rust');
my @actual = sort(split(/\s*,\s*/, $actual_raw // ""));
my $expected_formatted = join(', ', @expected);
my $actual_formatted = join(', ', @actual);
if ($expected_formatted ne $actual_formatted) {
print STDERR "Mismatched XS-Vendored-Sources-Rust field. Expected:\n";
print STDERR "XS-Vendored-Sources-Rust: $expected_formatted\n";
exit 2;
}