]> git.proxmox.com Git - dh-cargo.git/blob - cargo.pm
Pass cargo-version not Debian-version to `cargo install`
[dh-cargo.git] / cargo.pm
1 # debhelper buildsystem for Rust crates using Cargo
2 #
3 # Josh Triplett <josh@joshtriplett.org>
4
5 package Debian::Debhelper::Buildsystem::cargo;
6
7 use strict;
8 use warnings;
9 use Cwd;
10 use Debian::Debhelper::Dh_Lib;
11 use Dpkg::Changelog::Debian;
12 use Dpkg::Control::Info;
13 use Dpkg::Version;
14 use JSON::PP;
15 use base 'Debian::Debhelper::Buildsystem';
16
17 sub DESCRIPTION {
18 "Rust Cargo"
19 }
20
21 sub cargo_version {
22 my $src = shift;
23 open(F, "cargo metadata --manifest-path $src --no-deps --format-version 1 |");
24 local $/;
25 my $json = JSON::PP->new;
26 my $manifest = $json->decode(<F>);
27 return %{@{%{$manifest}{'packages'}}[0]}{'version'} . "\n";
28 }
29
30 sub check_auto_buildable {
31 my $this = shift;
32 if (-f $this->get_sourcepath("Cargo.toml")) {
33 return 1;
34 }
35 return 0;
36 }
37
38 sub new {
39 my $class = shift;
40 my $this = $class->SUPER::new(@_);
41 $this->enforce_in_source_building();
42 return $this;
43 }
44
45 sub pre_building_step {
46 my $this = shift;
47 my $step = shift;
48
49 $this->{cargo_home} = Cwd::abs_path($this->get_sourcepath("debian/cargo_home"));
50 $this->{cargo_registry} = Cwd::abs_path($this->get_sourcepath("debian/cargo_registry"));
51
52 my $control = Dpkg::Control::Info->new();
53
54 my $source = $control->get_source();
55 my $crate = $source->{'X-Cargo-Crate'};
56 if (!$crate) {
57 $crate = $source->{Source};
58 $crate =~ s/^rust-//;
59 $crate =~ s/-[0-9]+(\.[0-9]+)*$//;
60 }
61 $this->{crate} = $crate;
62 my $changelog = Dpkg::Changelog::Debian->new(range => { count => 1 });
63 $changelog->load($this->get_sourcepath("debian/changelog"));
64 $this->{version} = Dpkg::Version->new(@{$changelog}[0]->get_version())->version();
65
66 my @packages = $control->get_packages();
67 $this->{libpkg} = 0;
68 $this->{binpkg} = 0;
69 $this->{featurepkg} = [];
70 foreach my $package (@packages) {
71 if ($package->{Package} =~ /^librust-.*-dev$/ && $package->{Architecture} eq 'all') {
72 if ($package->{Package} =~ /\+/) {
73 push(@{$this->{featurepkg}}, $package->{Package});
74 next;
75 }
76 if ($this->{libpkg}) {
77 error("Multiple Cargo lib packages found: " . $this->{libpkg} . " and " . $package->{Package});
78 }
79 $this->{libpkg} = $package->{Package};
80 } elsif ($package->{Architecture} ne 'all') {
81 $this->{binpkg} = $package->{Package};
82 }
83 }
84 if (!$this->{libpkg} && !$this->{binpkg}) {
85 error("Could not find any Cargo lib or bin packages to build.");
86 }
87 if (@{$this->{featurepkg}} && !$this->{libpkg}) {
88 error("Found feature packages but no lib package.");
89 }
90
91 my $parallel = $this->get_parallel();
92 $this->{j} = $parallel > 0 ? ["-j$parallel"] : [];
93
94 $this->SUPER::pre_building_step($step);
95 }
96
97 sub get_sources {
98 my $this=shift;
99 opendir(my $dirhandle, $this->get_sourcedir());
100 my @sources = grep { $_ ne '.' && $_ ne '..' && $_ ne '.git' && $_ ne 'debian' } readdir($dirhandle);
101 closedir($dirhandle);
102 @sources
103 }
104
105 sub configure {
106 my $this=shift;
107 # Create a fake local registry $this->{cargo_registry} with only our dependencies
108 my $crate = $this->{crate} . '-' . $this->{version};
109 my $registry = $this->{cargo_registry};
110 doit("mkdir", "-p", $this->{cargo_home}, $registry);
111 opendir(my $dirhandle, '/usr/share/cargo/registry');
112 my @crates = map { "/usr/share/cargo/registry/$_" } grep { $_ ne '.' && $_ ne '..' } readdir($dirhandle);
113 closedir($dirhandle);
114 if (@crates) {
115 doit("ln", "-st", "$registry", @crates);
116 }
117 # Handle the case of building the package with the same version of the
118 # package installed.
119 if (-l "$registry/$crate") {
120 unlink("$registry/$crate");
121 }
122 mkdir("$registry/$crate");
123 my @sources = $this->get_sources();
124 doit("cp", "-at", "$registry/$crate", @sources);
125 doit("cp", $this->get_sourcepath("debian/cargo-checksum.json"), "$registry/$crate/.cargo-checksum.json");
126
127 open(CONFIG, ">" . $this->{cargo_home} . "/config");
128 print(CONFIG qq{
129 [source.crates-io]
130 replace-with = "dh-cargo-registry"
131
132 [source.dh-cargo-registry]
133 directory = "$registry"
134 });
135 close(CONFIG);
136 }
137
138 sub test {
139 my $this=shift;
140 $ENV{'CARGO_HOME'} = $this->{cargo_home};
141 # Check that the thing compiles. This might fail if e.g. the package
142 # requires non-rust system dependencies and the maintainer didn't provide
143 # this additional information to debcargo.
144 doit("cargo", "build", "--verbose", "-Zavoid-dev-deps");
145 }
146
147 sub install {
148 my $this=shift;
149 $ENV{'CARGO_HOME'} = $this->{cargo_home};
150 my $crate = $this->{crate} . '-' . $this->{version};
151 if ($this->{libpkg}) {
152 my $target = $this->get_sourcepath("debian/" . $this->{libpkg} . "/usr/share/cargo/registry/$crate");
153 my @sources = $this->get_sources();
154 doit("mkdir", "-p", $target);
155 doit("cp", "-at", $target, @sources);
156 doit("rm", "-rf", "$target/target");
157 doit("cp", $this->get_sourcepath("debian/cargo-checksum.json"), "$target/.cargo-checksum.json");
158 }
159 foreach my $pkg (@{$this->{featurepkg}}) {
160 my $target = $this->get_sourcepath("debian/$pkg/usr/share/doc");
161 doit("mkdir", "-p", $target);
162 doit("ln", "-s", $this->{libpkg}, "$target/$pkg");
163 }
164 if ($this->{binpkg}) {
165 my $target = $this->get_sourcepath("debian/" . $this->{binpkg} . "/usr");
166 doit("cargo", "install", $this->{crate}, "--verbose", @{$this->{j}},
167 "--vers", cargo_version($this->get_sourcepath("Cargo.toml")),
168 "--root", $target);
169 doit("rm", "$target/.crates.toml");
170 }
171 }
172
173 sub clean {
174 my $this=shift;
175 $ENV{'CARGO_HOME'} = $this->{cargo_home};
176 doit("cargo", "clean", "--verbose");
177 doit("rm", "-rf", $this->{cargo_home}, $this->{cargo_registry});
178 }
179
180 1