]> git.donarmstrong.com Git - debhelper.git/blob - Debian/Debhelper/Buildsystem.pm
Remove empty build directory parent dirs (up to source directory) too.
[debhelper.git] / Debian / Debhelper / Buildsystem.pm
1 # Defines debhelper buildsystem class interface and implementation
2 # of common functionality.
3 #
4 # Copyright: © 2008-2009 Modestas Vainius
5 # License: GPL-2+
6
7 package Debian::Debhelper::Buildsystem;
8
9 use strict;
10 use warnings;
11 use Cwd ();
12 use File::Spec;
13 use Debian::Debhelper::Dh_Lib;
14
15 # Cache DEB_BUILD_GNU_TYPE value. Performance hit of multiple
16 # invocations is noticable when listing buildsystems.
17 our $DEB_BUILD_GNU_TYPE = dpkg_architecture_value("DEB_BUILD_GNU_TYPE");
18
19 # Build system name. Defaults to the last component of the class
20 # name. Do not override this method unless you know what you are
21 # doing.
22 sub NAME {
23         my $this=shift;
24         my $class = ref($this) || $this;
25         if ($class =~ m/^.+::([^:]+)$/) {
26                 return $1;
27         }
28         else {
29                 error("ınvalid buildsystem class name: $class");
30         }
31 }
32
33 # Description of the build system to be shown to the users.
34 sub DESCRIPTION {
35         error("class lacking a DESCRIPTION");
36 }
37
38 # Default build directory. Can be overriden in the derived
39 # class if really needed.
40 sub DEFAULT_BUILD_DIRECTORY {
41         "obj-" . $DEB_BUILD_GNU_TYPE;
42 }
43
44 # Constructs a new build system object. Named parameters:
45 # - sourcedir-     specifies source directory (relative to the current (top)
46 #                  directory) where the sources to be built live. If not
47 #                  specified or empty, defaults to the current directory.
48 # - builddir -     specifies build directory to use. Path is relative to the
49 #                  source directory unless it starts with ./, then it is
50 #                  assumed to be relative to the top directory. If undef or
51 #                  empty, DEFAULT_BUILD_DIRECTORY relative to the source
52 #                  directory will be used. If not specified, in source build
53 #                  will be attempted.
54 # - build_step -   set this parameter to the name of the build step
55 #                  if you want the object to determine its is_buidable
56 #                  status automatically (with check_auto_buildable()).
57 #                  Do not pass this parameter if is_buildable flag should
58 #                  be forced to true or set this parameter to undef if
59 #                  is_buildable flag should be false.
60 # Derived class can override the constructor to initialize common object
61 # parameters and execute commands to configure build environment if
62 # is_buildable flag is set on the object.
63 sub new {
64         my ($class, %opts)=@_;
65
66         my $this = bless({ sourcedir => '.',
67                            builddir => undef,
68                            is_buildable => 1 }, $class);
69
70         if (exists $opts{sourcedir}) {
71                 # Get relative sourcedir abs_path (without symlinks)
72                 my $curdir = Cwd::getcwd();
73                 my $abspath = Cwd::abs_path($opts{sourcedir});
74                 if (! -d $abspath || $abspath !~ /^\Q$curdir\E/) {
75                         error("Invalid or non-existing path to the source directory: ".$opts{sourcedir});
76                 }
77                 $this->{sourcedir} = File::Spec->abs2rel($abspath, $curdir);
78         }
79         if (exists $opts{builddir}) {
80                 if ($opts{builddir}) {
81                         if ($opts{builddir} =~ m!^\./(.*)!) {
82                                 # Specified as relative to the current directory
83                                 $this->{builddir} = $1;
84                         }
85                         else {
86                                 # Specified as relative to the source directory
87                                 $this->{builddir} = $this->_canonpath($this->get_sourcepath($opts{builddir}));
88                         }
89                 }
90                 else {
91                         # Relative to the source directory by default
92                         $this->{builddir} = $this->get_sourcepath($this->DEFAULT_BUILD_DIRECTORY());
93                 }
94         }
95         if (exists $opts{build_step}) {
96                 if (defined $opts{build_step}) {
97                         $this->{is_buildable} = $this->check_auto_buildable($opts{build_step});
98                 }
99                 else {
100                         $this->{is_buildable} = 0;
101                 }
102         }
103         return $this;
104 }
105
106 # Test is_buildable flag of the object.
107 sub is_buildable {
108         my $this=shift;
109         return $this->{is_buildable};
110 }
111
112 # This instance method is called to check if the build system is capable
113 # to auto build a source package. Additional argument $step describes
114 # which operation the caller is going to perform (either configure,
115 # build, test, install or clean). You must override this method for the
116 # build system module to be ever picked up automatically. This method is
117 # used in conjuction with @Dh_Buildsystems::BUILDSYSTEMS.
118 #
119 # This method is supposed to be called with source root directory being
120 # working directory. Use $this->get_buildpath($path) method to get full
121 # path to the files in the build directory.
122 sub check_auto_buildable {
123         my $this=shift;
124         my ($step) = @_;
125         return 0;
126 }
127
128 # Derived class can call this method in its constructor
129 # to enforce in source building even if the user requested otherwise.
130 sub enforce_in_source_building {
131         my $this=shift;
132         if ($this->{builddir}) {
133                 # Do not emit warning unless the object is buildable.
134                 if ($this->is_buildable()) {
135                         warning("warning: " . $this->NAME() .
136                             " does not support building out of source tree. In source building enforced.");
137                 }
138                 $this->{builddir} = undef;
139         }
140 }
141
142 # Derived class can call this method in its constructor to enforce
143 # out of source building even if the user didn't request it.
144 sub enforce_out_of_source_building {
145         my ($this, $builddir) = @_;
146         if (!defined $this->{builddir}) {
147                 $this->{builddir} = ($builddir && $builddir ne ".") ? $builddir : $this->DEFAULT_BUILD_DIRECTORY();
148         }
149 }
150
151 # Enhanced version of File::Spec::canonpath. It collapses ..
152 # too so it may return invalid path if symlinks are involved.
153 # On the other hand, it does not need for the path to exist.
154 sub _canonpath {
155         my ($this, $path)=@_;
156         my @canon;
157         my $back=0;
158         for my $comp (split(m%/+%, $path)) {
159                 if ($comp eq '.') {
160                         next;
161                 }
162                 elsif ($comp eq '..') {
163                         if (@canon > 0) { pop @canon; }  else { $back++; }
164                 }
165                 else {
166                         push @canon, $comp;
167                 }
168         }
169         return (@canon + $back > 0) ? join('/', ('..')x$back, @canon) : '.';
170 }
171
172 # Given both $path and $base are relative to the same directory,
173 # converts and returns path of $path being relative the $base.
174 sub _rel2rel {
175         my ($this, $path, $base, $root)=@_;
176         $root = File::Spec->rootdir() if !defined $root;
177         
178         return File::Spec->abs2rel(
179             File::Spec->rel2abs($path, $root),
180             File::Spec->rel2abs($base, $root)
181         );
182 }
183
184 # Get path to the source directory
185 # (relative to the current (top) directory)
186 sub get_sourcedir {
187         my $this=shift;
188         return $this->{sourcedir};
189 }
190
191 # Convert path relative to the source directory to the path relative
192 # to the current (top) directory.
193 sub get_sourcepath {
194         my ($this, $path)=@_;
195         return File::Spec->catfile($this->get_sourcedir(), $path);
196 }
197
198 # Get path to the build directory if it was specified
199 # (relative to the current (top) directory). undef otherwise.
200 sub get_builddir {
201         my $this=shift;
202         return $this->{builddir};
203 }
204
205 # Convert path that is relative to the build directory to the path
206 # that is relative to the current (top) directory.
207 # If $path is not specified, always returns build directory path
208 # relative to the current (top) directory regardless if builddir was
209 # specified or not.
210 sub get_buildpath {
211         my ($this, $path)=@_;
212         my $builddir = $this->get_builddir() || $this->get_sourcedir();
213         if (defined $path) {
214                 return File::Spec->catfile($builddir, $path);
215         }
216         return $builddir;
217 }
218
219 # When given a relative path to the source directory, converts it
220 # to the path that is relative to the build directory. If $path is
221 # not given, returns a path to the source directory that is relative
222 # to the build directory.
223 sub get_source_rel2builddir {
224         my $this=shift;
225         my $path=shift;
226
227         my $dir = '.';
228         if ($this->get_builddir()) {
229                 $dir = $this->_rel2rel($this->get_sourcedir(), $this->get_builddir());
230         }
231         if (defined $path) {
232                 return File::Spec->catfile($dir, $path);
233         }
234         return $dir;
235 }
236
237 # When given a relative path to the build directory, converts it
238 # to the path that is relative to the source directory. If $path is
239 # not given, returns a path to the build directory that is relative
240 # to the source directory.
241 sub get_build_rel2sourcedir {
242         my $this=shift;
243         my $path=shift;
244
245         my $dir = '.';
246         if ($this->get_builddir()) {
247                 $dir = $this->_rel2rel($this->get_builddir(), $this->get_sourcedir());
248         }
249         if (defined $path) {
250                 return File::Spec->catfile($dir, $path);
251         }
252         return $dir;
253 }
254
255 # Creates a build directory.
256 sub mkdir_builddir {
257         my $this=shift;
258         if ($this->get_builddir()) {
259                 doit("mkdir", "-p", $this->get_builddir());
260         }
261 }
262
263 sub _cd {
264         my ($this, $dir)=@_;
265         if (! $dh{NO_ACT}) {
266                 verbose_print("cd $dir");
267                 chdir $dir or error("error: unable to chdir to $dir");
268         }
269 }
270
271 # Changes working directory to the source directory (if needed)
272 # calls doit(@_) and changes working directory back to the top
273 # directory.
274 sub doit_in_sourcedir {
275         my $this=shift;
276         if ($this->get_sourcedir() ne '.') {
277                 my $sourcedir = get_sourcedir();
278                 my $curdir = Cwd::getcwd();
279                 $this->_cd($sourcedir);
280                 doit(@_);
281                 $this->_cd($this->_rel2rel($curdir, $sourcedir, $curdir));
282         }
283         else {
284                 doit(@_);
285         }
286         return 1;
287 }
288
289 # Changes working directory to the build directory (if needed),
290 # calls doit(@_) and changes working directory back to the top
291 # directory.
292 sub doit_in_builddir {
293         my $this=shift;
294         if ($this->get_buildpath() ne '.') {
295                 my $buildpath = $this->get_buildpath();
296                 my $curdir = Cwd::getcwd();
297                 $this->_cd($buildpath);
298                 doit(@_);
299                 $this->_cd($this->_rel2rel($curdir, $buildpath, $curdir));
300         }
301         else {
302                 doit(@_);
303         }
304         return 1;
305 }
306
307 # In case of out of source tree building, whole build directory
308 # gets wiped (if it exists) and 1 is returned. If build directory
309 # had 2 or more levels, empty parent directories are also deleted.
310 # If build directory does not exist, nothing is done and 0 is returned.
311 sub rmdir_builddir {
312         my $this=shift;
313         if ($this->get_builddir()) {
314                 my $buildpath = $this->get_buildpath();
315                 if (-d $buildpath && ! $dh{NO_ACT}) {
316                         doit("rm", "-rf", $buildpath);
317                         # If build directory had 2 or more levels, delete empty
318                         # parent directories until the source directory level.
319                         my @spdir = File::Spec->splitdir($this->get_build_rel2sourcedir());
320                         my $peek;
321                         pop @spdir;
322                         while (($peek=pop(@spdir)) && $peek ne '.' && $peek ne '..') {
323                                 last if ! rmdir($this->get_sourcepath(File::Spec->catdir(@spdir, $peek)));
324                         }
325                 }
326                 return 1;
327         }
328         return 0;
329 }
330
331 # Instance method that is called before performing any step (see below).
332 # Action name is passed as an argument. Derived classes overriding this
333 # method should also call SUPER implementation of it.
334 sub pre_building_step {
335         my $this=shift;
336         my ($step)=@_;
337 }
338
339 # Instance method that is called after performing any step (see below).
340 # Action name is passed as an argument. Derived classes overriding this
341 # method should also call SUPER implementation of it.
342 sub post_building_step {
343         my $this=shift;
344         my ($step)=@_;
345 }
346
347 # The instance methods below provide support for configuring,
348 # building, testing, install and cleaning source packages.
349 # In case of failure, the method may just error() out.
350 #
351 # These methods should be overriden by derived classes to
352 # implement buildsystem specific steps needed to build the
353 # source. Arbitary number of custom step arguments might be
354 # passed. Default implementations do nothing.
355 sub configure {
356         my $this=shift;
357 }
358
359 sub build {
360         my $this=shift;
361 }
362
363 sub test {
364         my $this=shift;
365 }
366
367 # destdir parameter specifies where to install files.
368 sub install {
369         my $this=shift;
370         my $destdir=shift;
371 }
372
373 sub clean {
374         my $this=shift;
375 }
376
377 1;