dpkg-genbuildinfo.pl 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. #!/usr/bin/perl
  2. #
  3. # dpkg-genbuildinfo
  4. #
  5. # Copyright © 1996 Ian Jackson
  6. # Copyright © 2000,2001 Wichert Akkerman
  7. # Copyright © 2003-2013 Yann Dirson <dirson@debian.org>
  8. # Copyright © 2006-2016 Guillem Jover <guillem@debian.org>
  9. # Copyright © 2014 Niko Tyni <ntyni@debian.org>
  10. # Copyright © 2014-2015 Jérémy Bobbio <lunar@debian.org>
  11. #
  12. # This program is free software; you can redistribute it and/or modify
  13. # it under the terms of the GNU General Public License as published by
  14. # the Free Software Foundation; either version 2 of the License, or
  15. # (at your option) any later version.
  16. #
  17. # This program is distributed in the hope that it will be useful,
  18. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. # GNU General Public License for more details.
  21. #
  22. # You should have received a copy of the GNU General Public License
  23. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  24. use strict;
  25. use warnings;
  26. use Cwd;
  27. use File::Basename;
  28. use POSIX qw(:fcntl_h strftime);
  29. use Dpkg ();
  30. use Dpkg::Gettext;
  31. use Dpkg::Checksums;
  32. use Dpkg::ErrorHandling;
  33. use Dpkg::Arch qw(get_build_arch);
  34. use Dpkg::Build::Types;
  35. use Dpkg::BuildFlags;
  36. use Dpkg::BuildProfiles qw(get_build_profiles);
  37. use Dpkg::Control::Info;
  38. use Dpkg::Control::Fields;
  39. use Dpkg::Control;
  40. use Dpkg::Changelog::Parse;
  41. use Dpkg::Deps;
  42. use Dpkg::Dist::Files;
  43. use Dpkg::Util qw(:list);
  44. use Dpkg::File;
  45. use Dpkg::Version;
  46. use Dpkg::Vendor qw(get_current_vendor run_vendor_hook);
  47. textdomain('dpkg-dev');
  48. my $controlfile = 'debian/control';
  49. my $changelogfile = 'debian/changelog';
  50. my $changelogformat;
  51. my $fileslistfile = 'debian/files';
  52. my $uploadfilesdir = '..';
  53. my $outputfile;
  54. my $stdout = 0;
  55. my $admindir = $Dpkg::ADMINDIR;
  56. my $always_include_path = 0;
  57. my @build_profiles = get_build_profiles();
  58. my $buildinfo_format = '0.1';
  59. my $buildinfo_id;
  60. my $buildinfo;
  61. my $checksums = Dpkg::Checksums->new();
  62. my %archadded;
  63. my @archvalues;
  64. # There is almost the same function in dpkg-checkbuilddeps, they probably
  65. # should be factored out.
  66. sub parse_status {
  67. my $status = shift;
  68. my $facts = Dpkg::Deps::KnownFacts->new();
  69. my %depends;
  70. my @essential_pkgs;
  71. local $/ = '';
  72. open my $status_fh, '<', $status or syserr(g_('cannot open %s'), $status);
  73. while (<$status_fh>) {
  74. next unless /^Status: .*ok installed$/m;
  75. my ($package) = /^Package: (.*)$/m;
  76. my ($version) = /^Version: (.*)$/m;
  77. my ($arch) = /^Architecture: (.*)$/m;
  78. my ($multiarch) = /^Multi-Arch: (.*)$/m;
  79. $facts->add_installed_package($package, $version, $arch, $multiarch);
  80. if (/^Essential: yes$/m) {
  81. push @essential_pkgs, $package;
  82. }
  83. if (/^Provides: (.*)$/m) {
  84. my $provides = deps_parse($1, reduce_arch => 1, union => 1);
  85. next if not defined $provides;
  86. deps_iterate($provides, sub {
  87. my $dep = shift;
  88. $facts->add_provided_package($dep->{package}, $dep->{relation},
  89. $dep->{version}, $package);
  90. });
  91. }
  92. if (/^(?:Pre-)?Depends: (.*)$/m) {
  93. my $depends = $1;
  94. foreach (split /,\s*/, $depends) {
  95. push @{$depends{"$package:$arch"}}, $_;
  96. }
  97. }
  98. }
  99. close $status_fh;
  100. return ($facts, \%depends, \@essential_pkgs);
  101. }
  102. sub append_deps {
  103. my $pkgs = shift;
  104. foreach my $dep_str (@_) {
  105. next unless $dep_str;
  106. my $deps = deps_parse($dep_str, reduce_restrictions => 1,
  107. build_dep => 1,
  108. build_profiles => \@build_profiles);
  109. # We add every sub-dependencies as we cannot know which package in
  110. # an OR dependency has been effectively used.
  111. deps_iterate($deps, sub {
  112. push @{$pkgs},
  113. $_[0]->{package} . (defined $_[0]->{archqual} ? ':' . $_[0]->{archqual} : '');
  114. 1
  115. });
  116. }
  117. }
  118. sub collect_installed_builddeps {
  119. my $control = shift;
  120. my ($facts, $depends, $essential_pkgs) = parse_status("$admindir/status");
  121. my %seen_pkgs;
  122. my @unprocessed_pkgs;
  123. # Parse essential packages list.
  124. append_deps(\@unprocessed_pkgs,
  125. @{$essential_pkgs},
  126. run_vendor_hook('builtin-build-depends'),
  127. $control->get_source->{'Build-Depends'});
  128. if (build_has_any(BUILD_ARCH_DEP)) {
  129. append_deps(\@unprocessed_pkgs,
  130. $control->get_source->{'Build-Depends-Arch'});
  131. }
  132. if (build_has_any(BUILD_ARCH_INDEP)) {
  133. append_deps(\@unprocessed_pkgs,
  134. $control->get_source->{'Build-Depends-Indep'});
  135. }
  136. my $installed_deps = Dpkg::Deps::AND->new();
  137. while (my $pkg_name = shift @unprocessed_pkgs) {
  138. next if $seen_pkgs{$pkg_name};
  139. $seen_pkgs{$pkg_name} = 1;
  140. my $required_architecture;
  141. if ($pkg_name =~ /\A(.*):(.*)\z/) {
  142. $pkg_name = $1;
  143. my $arch = $2;
  144. $required_architecture = $arch if $arch !~ /\A(?:all|any|native)\Z/
  145. }
  146. my $pkg;
  147. my $qualified_pkg_name;
  148. foreach my $installed_pkg (@{$facts->{pkg}->{$pkg_name}}) {
  149. if (!defined $required_architecture ||
  150. $required_architecture eq $installed_pkg->{architecture}) {
  151. $pkg = $installed_pkg;
  152. $qualified_pkg_name = $pkg_name . ':' . $installed_pkg->{architecture};
  153. last;
  154. }
  155. }
  156. if (defined $pkg) {
  157. my $version = $pkg->{version};
  158. my $architecture = $pkg->{architecture};
  159. my $new_deps_str = defined $depends->{$qualified_pkg_name} ? deps_concat(@{$depends->{$qualified_pkg_name}}) : '';
  160. my $new_deps = deps_parse($new_deps_str);
  161. if (!defined $required_architecture) {
  162. $installed_deps->add(Dpkg::Deps::Simple->new("$pkg_name (= $version)"));
  163. } else {
  164. $installed_deps->add(Dpkg::Deps::Simple->new("$qualified_pkg_name (= $version)"));
  165. # Dependencies of foreign packages are also foreign packages
  166. # (or Arch:all) so we need to qualify them as well. We figure
  167. # out if the package is actually foreign by searching for an
  168. # installed package of the right architecture.
  169. deps_iterate($new_deps, sub {
  170. my $dep = shift;
  171. $dep->{archqual} //= $architecture
  172. if any { $_[0]->{architecture} eq $architecture }, @{$facts->{pkg}->{$dep->{package}}};
  173. 1;
  174. });
  175. }
  176. # We add every sub-dependencies as we cannot know which package
  177. # in an OR dependency has been effectively used.
  178. deps_iterate($new_deps, sub {
  179. push @unprocessed_pkgs,
  180. $_[0]->{package} . (defined $_[0]->{archqual} ? ':' . $_[0]->{archqual} : '');
  181. 1
  182. });
  183. } elsif (defined $facts->{virtualpkg}->{$pkg_name}) {
  184. # virtual package: we cannot know for sure which implementation
  185. # is the one that has been used, so let's add them all...
  186. foreach my $provided (@{$facts->{virtualpkg}->{$pkg_name}}) {
  187. my ($provided_by, $provided_rel, $provided_ver) = @{$provided};
  188. push @unprocessed_pkgs, $provided_by;
  189. }
  190. }
  191. # else: it is a package in an OR dependency that has been otherwise
  192. # satisfied.
  193. }
  194. $installed_deps->simplify_deps(Dpkg::Deps::KnownFacts->new());
  195. $installed_deps->sort();
  196. $installed_deps = "\n" . $installed_deps->output();
  197. $installed_deps =~ s/, /,\n/g;
  198. return $installed_deps;
  199. }
  200. my @env_whitelist = (
  201. # Toolchain.
  202. qw(CC CPP CXX OBJC OBJCXX PC FC M2C AS LD AR RANLIB MAKE AWK LEX YACC),
  203. # Toolchain flags.
  204. qw(CFLAGS CPPFLAGS CXXFLAGS OBJCFLAGS OBJCXXFLAGS GCJFLAGS FFLAGS
  205. LDFLAGS ARFLAGS MAKEFLAGS),
  206. # Dynamic linker, see ld(1).
  207. qw(LD_LIBRARY_PATH),
  208. # Locale, see locale(1).
  209. qw(LANG LC_ALL LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY
  210. LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
  211. LC_IDENTIFICATION),
  212. # Build flags, see dpkg-buildpackage(1).
  213. qw(DEB_BUILD_OPTIONS DEB_BUILD_PROFILES),
  214. # DEB_flag_{SET,STRIP,APPEND,PREPEND} will be recorded after being merged
  215. # with system config and user config.
  216. qw(DEB_VENDOR), # See deb-vendor(1).
  217. qw(DPKG_ORIGINS_DIR), # See Dpkg::Vendor(3).
  218. # See <https://reproducible-builds.org/specs/source-date-epoch>.
  219. qw(SOURCE_DATE_EPOCH),
  220. );
  221. sub cleansed_environment {
  222. # Consider only whitelisted variables which are not supposed to leak
  223. # local user information.
  224. my %env = map { $_ => $ENV{$_} } grep { exists $ENV{$_} } @env_whitelist;
  225. # Record flags from dpkg-buildflags.
  226. my $bf = Dpkg::BuildFlags->new();
  227. $bf->load_system_config();
  228. $bf->load_user_config();
  229. $bf->load_environment_config();
  230. foreach my $flag ($bf->list()) {
  231. next if $bf->get_origin($flag) eq 'vendor';
  232. # We do not need to record *_{STRIP,APPEND,PREPEND} as they
  233. # have been used already to compute the above value.
  234. $env{"DEB_${flag}_SET"} = $bf->get($flag);
  235. }
  236. return join "\n", map { $_ . '="' . ($env{$_} =~ s/"/\\"/gr) . '"' }
  237. sort keys %env;
  238. }
  239. sub version {
  240. printf g_("Debian %s version %s.\n"), $Dpkg::PROGNAME, $Dpkg::PROGVERSION;
  241. printf g_('
  242. This is free software; see the GNU General Public License version 2 or
  243. later for copying conditions. There is NO warranty.
  244. ');
  245. }
  246. sub usage {
  247. printf g_(
  248. 'Usage: %s [<option>...]')
  249. . "\n\n" . g_(
  250. "Options:
  251. --build=<type>[,...] specify the build <type>: full, source, binary,
  252. any, all (default is \'full\').
  253. -c<control-file> get control info from this file.
  254. -l<changelog-file> get per-version info from this file.
  255. -f<files-list-file> get .deb files list from this file.
  256. -F<changelog-format> force changelog format.
  257. -O[<buildinfo-file>] write to stdout (or <buildinfo-file>).
  258. -u<upload-files-dir> directory with files (default is '..').
  259. --buildinfo-id=<id> specify the buildinfo id for the output file.
  260. --always-include-path always include Build-Path.
  261. --admindir=<directory> change the administrative directory.
  262. -?, --help show this help message.
  263. --version show the version.
  264. "), $Dpkg::PROGNAME;
  265. }
  266. while (@ARGV) {
  267. $_ = shift @ARGV ;
  268. if (m/^--build=(.*)$/) {
  269. set_build_type_from_options($1, $_);
  270. } elsif (m/^-c(.*)$/) {
  271. $controlfile = $1;
  272. } elsif (m/^-l(.*)$/) {
  273. $changelogfile = $1;
  274. } elsif (m/^-f(.*)$/) {
  275. $fileslistfile = $1;
  276. } elsif (m/^-F([0-9a-z]+)$/) {
  277. $changelogformat = $1;
  278. } elsif (m/^-u(.*)$/) {
  279. $uploadfilesdir = $1;
  280. } elsif (m/^-O$/) {
  281. $stdout = 1;
  282. } elsif (m/^-O(.*)$/) {
  283. $outputfile = $1;
  284. } elsif (m/^--buildinfo-id=(.*)$/) {
  285. $buildinfo_id = $1;
  286. } elsif (m/^--always-include-path$/) {
  287. $always_include_path = 1;
  288. } elsif (m/^--admindir=(.*)$/) {
  289. $admindir = $1;
  290. } elsif (m/^-(?:\?|-help)$/) {
  291. usage();
  292. exit(0);
  293. } elsif (m/^--version$/) {
  294. version();
  295. exit(0);
  296. } else {
  297. usageerr(g_("unknown option '%s'"), $_);
  298. }
  299. }
  300. my $control = Dpkg::Control::Info->new($controlfile);
  301. my $fields = Dpkg::Control->new(type => CTRL_FILE_BUILDINFO);
  302. my $dist = Dpkg::Dist::Files->new();
  303. # Retrieve info from the current changelog entry.
  304. my %options = (file => $changelogfile);
  305. $options{changelogformat} = $changelogformat if $changelogformat;
  306. my $changelog = changelog_parse(%options);
  307. # Retrieve info from the former changelog entry to handle binNMUs.
  308. $options{count} = 1;
  309. $options{offset} = 1;
  310. my $prev_changelog = changelog_parse(%options);
  311. my $sourceversion = $changelog->{'Binary-Only'} ?
  312. $prev_changelog->{'Version'} : $changelog->{'Version'};
  313. my $binaryversion = $changelog->{'Version'};
  314. # Include .dsc if available.
  315. my $spackage = $changelog->{'Source'};
  316. (my $sversion = $sourceversion) =~ s/^\d+://;
  317. if (build_has_any(BUILD_SOURCE)) {
  318. my $dsc = "${spackage}_${sversion}.dsc";
  319. $checksums->add_from_file("$uploadfilesdir/$dsc", key => $dsc);
  320. }
  321. my $dist_count = 0;
  322. $dist_count = $dist->load($fileslistfile) if -e $fileslistfile;
  323. if (build_has_any(BUILD_BINARY)) {
  324. error(g_('binary build with no binary artifacts found; .buildinfo is meaningless'))
  325. if $dist_count == 0;
  326. foreach my $file ($dist->get_files()) {
  327. # Make us a bit idempotent.
  328. next if $file->{filename} =~ m/\.buildinfo$/;
  329. my $path = "$uploadfilesdir/$file->{filename}";
  330. $checksums->add_from_file($path, key => $file->{filename});
  331. if (defined $file->{package_type} and $file->{package_type} =~ m/^u?deb$/) {
  332. push @archvalues, $file->{arch}
  333. if defined $file->{arch} and not $archadded{$file->{arch}}++;
  334. }
  335. }
  336. }
  337. $fields->{'Format'} = $buildinfo_format;
  338. $fields->{'Source'} = $spackage;
  339. $fields->{'Binary'} = join(' ', map { $_->{'Package'} } $control->get_packages());
  340. # Avoid overly long line by splitting over multiple lines.
  341. if (length($fields->{'Binary'}) > 980) {
  342. $fields->{'Binary'} =~ s/(.{0,980}) /$1\n/g;
  343. }
  344. $fields->{'Architecture'} = join ' ', sort @archvalues;
  345. $fields->{'Version'} = $binaryversion;
  346. if ($changelog->{'Binary-Only'}) {
  347. $fields->{'Source'} .= ' (' . $sourceversion . ')';
  348. $fields->{'Binary-Only-Changes'} =
  349. $changelog->{'Changes'} . "\n\n"
  350. . ' -- ' . $changelog->{'Maintainer'}
  351. . ' ' . $changelog->{'Date'};
  352. }
  353. $fields->{'Build-Origin'} = get_current_vendor();
  354. $fields->{'Build-Architecture'} = get_build_arch();
  355. my $cwd = cwd();
  356. if ($always_include_path) {
  357. $fields->{'Build-Path'} = $cwd;
  358. } else {
  359. # Only include the build path if its root path is considered acceptable
  360. # by the vendor.
  361. foreach my $root_path (run_vendor_hook('builtin-system-build-paths')) {
  362. if (index($cwd, $root_path) == 0) {
  363. $fields->{'Build-Path'} = $cwd;
  364. last;
  365. }
  366. }
  367. }
  368. $checksums->export_to_control($fields);
  369. $fields->{'Installed-Build-Depends'} = collect_installed_builddeps($control);
  370. $fields->{'Environment'} = "\n" . cleansed_environment();
  371. # Generate the buildinfo filename.
  372. if ($stdout) {
  373. # Nothing to do.
  374. } elsif (defined $outputfile) {
  375. $buildinfo = basename($outputfile);
  376. } else {
  377. if (not defined $buildinfo_id) {
  378. require Digest::MD5;
  379. my $buildinfo_contents = $fields->output();
  380. my $timestamp = strftime('%Y%m%dT%H%M%Sz', gmtime);
  381. my $buildinfo_md5 = Digest::MD5::md5_hex($buildinfo_contents);
  382. $buildinfo_id = "$timestamp-" . substr($buildinfo_md5, 0, 8);
  383. }
  384. $buildinfo = "${spackage}_${sversion}_${buildinfo_id}.buildinfo";
  385. $outputfile = "$uploadfilesdir/$buildinfo";
  386. }
  387. # Write out the generated .buildinfo file.
  388. if ($stdout) {
  389. $fields->output(\*STDOUT);
  390. } else {
  391. my $section = $control->get_source->{'Section'} || '-';
  392. my $priority = $control->get_source->{'Priority'} || '-';
  393. # Obtain a lock on debian/control to avoid simultaneous updates
  394. # of debian/files when parallel building is in use
  395. my $lockfh;
  396. my $lockfile = 'debian/control';
  397. $lockfile = $controlfile if not -e $lockfile;
  398. sysopen $lockfh, $lockfile, O_WRONLY
  399. or syserr(g_('cannot write %s'), $lockfile);
  400. file_lock($lockfh, $lockfile);
  401. $dist = Dpkg::Dist::Files->new();
  402. $dist->load($fileslistfile) if -e $fileslistfile;
  403. $dist->add_file($buildinfo, $section, $priority);
  404. $dist->save("$fileslistfile.new");
  405. rename "$fileslistfile.new", $fileslistfile
  406. or syserr(g_('install new files list file'));
  407. # Release the lock
  408. close $lockfh or syserr(g_('cannot close %s'), $lockfile);
  409. $fields->save("$outputfile.new");
  410. rename "$outputfile.new", $outputfile
  411. or syserr(g_("cannot install output buildinfo file '%s'"), $outputfile);
  412. }
  413. 1;