1#!/usr/bin/perl
2###########################################################################
3# Makefile for API Sanity Checker
4# Install/remove the tool for GNU/Linux, FreeBSD and Mac OS X
5#
6# Copyright (C) 2009-2010 The Linux Foundation
7# Copyright (C) 2009-2011 Institute for System Programming, RAS
8# Copyright (C) 2011-2013 ROSA Lab
9#
10# Written by Andrey Ponomarenko
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 or the GNU Lesser
14# General Public License as published by the Free Software Foundation.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# and the GNU Lesser General Public License along with this program.
23# If not, see <http://www.gnu.org/licenses/>.
24###########################################################################
25use Getopt::Long;
26Getopt::Long::Configure ("posix_default", "no_ignore_case");
27use File::Path qw(mkpath rmtree);
28use File::Spec qw(catfile file_name_is_absolute);
29use File::Copy qw(copy);
30use File::Basename qw(dirname);
31use Cwd qw(abs_path);
32use File::Find;
33use Config;
34use strict;
35
36my $TOOL_SNAME = "api-sanity-checker";
37my $ARCHIVE_DIR = abs_path(dirname($0));
38
39my $ABICC = "/usr/local/bin/abi-compliance-checker.pl";
40my $ABICC_VERSION = "1.98.7";
41
42my $HELP_MSG = "
43NAME:
44  Makefile for API Sanity Checker
45
46DESCRIPTION:
47  Install $TOOL_SNAME command and private modules.
48
49USAGE:
50  sudo perl $0 -install -prefix /usr
51  sudo perl $0 -update -prefix /usr
52  sudo perl $0 -remove -prefix /usr
53
54OPTIONS:
55  -h|-help
56      Print this help.
57
58  --prefix=PREFIX
59      Install files in PREFIX [/usr/local].
60
61  -install
62      Command to install the tool.
63
64  -update
65      Command to update existing installation.
66
67  -remove
68      Command to remove the tool.
69
70EXTRA OPTIONS:
71  --destdir=DESTDIR
72      This option is for maintainers to build
73      RPM or DEB packages inside the build root.
74      The environment variable DESTDIR is also
75      supported.
76\n";
77
78if(not @ARGV)
79{
80    print $HELP_MSG;
81    exit(0);
82}
83
84my ($PREFIX, $DESTDIR, $Help, $Install, $Update, $Remove);
85
86GetOptions(
87    "h|help!" => \$Help,
88    "prefix=s" => \$PREFIX,
89    "destdir=s" => \$DESTDIR,
90    "install!" => \$Install,
91    "update!" => \$Update,
92    "remove!" => \$Remove
93) or exit(1);
94
95sub scenario()
96{
97    if($Help)
98    {
99        print $HELP_MSG;
100        exit(0);
101    }
102    if(not $Install and not $Update and not $Remove)
103    {
104        print STDERR "ERROR: command is not selected (-install, -update or -remove)\n";
105        exit(1);
106    }
107    if(my $Version = `$ABICC -dumpversion`)
108    {
109        if(cmpVersions($Version, $ABICC_VERSION)<0)
110        {
111            print STDERR "ERROR: requires $ABICC_VERSION or newer version of \'$ABICC\'\n";
112            exit(1);
113        }
114    }
115    else
116    {
117        print STDERR "ERROR: cannot find \'$ABICC\'\n";
118        exit(1);
119    }
120    if($PREFIX ne "/") {
121        $PREFIX=~s/[\/]+\Z//g;
122    }
123    if(not $PREFIX)
124    { # default prefix
125        if($Config{"osname"}!~/win/i) {
126            $PREFIX = "/usr/local";
127        }
128    }
129    if(my $Var = $ENV{"DESTDIR"})
130    {
131        print "Using DESTDIR environment variable\n";
132        $DESTDIR = $Var;
133    }
134    if($DESTDIR)
135    {
136        if($DESTDIR ne "/") {
137            $DESTDIR=~s/[\/]+\Z//g;
138        }
139        if(not isAbs($DESTDIR))
140        {
141            print STDERR "ERROR: destdir is not absolute path\n";
142            exit(1);
143        }
144        if(not -d $DESTDIR)
145        {
146            print STDERR "ERROR: you should create destdir directory first\n";
147            exit(1);
148        }
149        $PREFIX = $DESTDIR.$PREFIX;
150        if(not -d $PREFIX)
151        {
152            print STDERR "ERROR: you should create installation directory first (destdir + prefix):\n  mkdir -p $PREFIX\n";
153            exit(1);
154        }
155    }
156    else
157    {
158        if(not isAbs($PREFIX))
159        {
160            print STDERR "ERROR: prefix is not absolute path\n";
161            exit(1);
162        }
163        if(not -d $PREFIX)
164        {
165            print STDERR "ERROR: you should create prefix directory first\n";
166            exit(1);
167        }
168    }
169
170    print "INSTALL PREFIX: $PREFIX\n";
171
172    # paths
173    my $EXE_PATH = catFile($PREFIX, "bin");
174    my $MODULES_PATH = catFile($PREFIX, "share", $TOOL_SNAME);
175    my $REL_PATH = catFile("..", "share", $TOOL_SNAME);
176    my $TOOL_PATH = catFile($EXE_PATH, $TOOL_SNAME);
177
178    if(not -w $PREFIX)
179    {
180        print STDERR "ERROR: you should be root\n";
181        exit(1);
182    }
183    if($Remove or $Update)
184    {
185        if(-e $EXE_PATH."/".$TOOL_SNAME)
186        { # remove executable
187            print "-- Removing $TOOL_PATH\n";
188            unlink($EXE_PATH."/".$TOOL_SNAME);
189        }
190
191        if(-d $MODULES_PATH)
192        { # remove modules
193            print "-- Removing $MODULES_PATH\n";
194            rmtree($MODULES_PATH);
195        }
196    }
197    if($Install or $Update)
198    {
199        if(-e $EXE_PATH."/".$TOOL_SNAME or -e $MODULES_PATH)
200        { # check installed
201            if(not $Remove)
202            {
203                print STDERR "ERROR: you should remove old version first (`perl $0 -remove --prefix=$PREFIX`)\n";
204                exit(1);
205            }
206        }
207
208        # configure
209        my $Content = readFile($ARCHIVE_DIR."/".$TOOL_SNAME.".pl");
210        if($DESTDIR) { # relative path
211            $Content=~s/MODULES_INSTALL_PATH/$REL_PATH/;
212        }
213        else { # absolute path
214            $Content=~s/MODULES_INSTALL_PATH/$MODULES_PATH/;
215        }
216
217        # copy executable
218        print "-- Installing $TOOL_PATH\n";
219        mkpath($EXE_PATH);
220        writeFile($EXE_PATH."/".$TOOL_SNAME, $Content);
221        chmod(0775, $EXE_PATH."/".$TOOL_SNAME);
222
223        if($Config{"osname"}=~/win/i) {
224            writeFile($EXE_PATH."/".$TOOL_SNAME.".cmd", "\@perl \"$TOOL_PATH\" \%*");
225        }
226
227        # copy modules
228        if(-d $ARCHIVE_DIR."/modules")
229        {
230                print "-- Installing $MODULES_PATH\n";
231                mkpath($MODULES_PATH);
232                copyDir($ARCHIVE_DIR."/modules", $MODULES_PATH);
233        }
234
235        # check PATH
236        my $Warn = "WARNING: your PATH variable doesn't include \'$EXE_PATH\'\n";
237
238        if($Config{"osname"}=~/win/i)
239        {
240            if($ENV{"PATH"}!~/(\A|[:;])\Q$EXE_PATH\E[\/\\]?(\Z|[:;])/i) {
241                print $Warn;
242            }
243        }
244        else
245        {
246            if($ENV{"PATH"}!~/(\A|[:;])\Q$EXE_PATH\E[\/\\]?(\Z|[:;])/) {
247                print $Warn;
248            }
249        }
250    }
251    exit(0);
252}
253
254sub cmpVersions($$)
255{ # compare two versions in dotted-numeric format
256    my ($V1, $V2) = @_;
257    return 0 if($V1 eq $V2);
258    my @V1Parts = split(/\./, $V1);
259    my @V2Parts = split(/\./, $V2);
260    for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++)
261    {
262        return -1 if(int($V1Parts[$i]) < int($V2Parts[$i]));
263        return 1 if(int($V1Parts[$i]) > int($V2Parts[$i]));
264    }
265    return -1 if($#V1Parts < $#V2Parts);
266    return 1 if($#V1Parts > $#V2Parts);
267    return 0;
268}
269
270sub catFile(@) {
271    return File::Spec->catfile(@_);
272}
273
274sub isAbs($) {
275    return File::Spec->file_name_is_absolute($_[0]);
276}
277
278sub copyDir($$)
279{
280    my ($From, $To) = @_;
281    my %Files;
282    find(\&wanted, $From);
283    sub wanted {
284        $Files{$File::Find::dir."/$_"} = 1 if($_ ne ".");
285    }
286    foreach my $Path (sort keys(%Files))
287    {
288        my $Inst = $Path;
289        $Inst=~s/\A\Q$ARCHIVE_DIR\E/$To/;
290        if(-d $Path)
291        { # directories
292            mkpath($Inst);
293        }
294        else
295        { # files
296            mkpath(dirname($Inst));
297            copy($Path, $Inst);
298        }
299    }
300}
301
302sub readFile($)
303{
304    my $Path = $_[0];
305    return "" if(not $Path or not -f $Path);
306    open(FILE, $Path) || die ("can't open file \'$Path\': $!\n");
307    local $/ = undef;
308    my $Content = <FILE>;
309    close(FILE);
310    return $Content;
311}
312
313sub writeFile($$)
314{
315    my ($Path, $Content) = @_;
316    return if(not $Path);
317    open(FILE, ">".$Path) || die ("can't open file \'$Path\': $!\n");
318    print FILE $Content;
319    close(FILE);
320}
321
322scenario();