The R Project SVN R

Rev

Rev 27902 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 27902 Rev 28080
1
#! @PERL@
1
#! @PERL@
2
#-*- perl -*-
2
#-*- perl -*-
3
# Copyright (C) 2000-2004 R Development Core Team
3
# Copyright (C) 2000-2004 R Development Core Team
4
#
4
#
5
# This program is free software; you can redistribute it and/or modify
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2, or (at your option)
7
# the Free Software Foundation; either version 2, or (at your option)
8
# any later version.
8
# any later version.
9
#
9
#
10
# This program is distributed in the hope that it will be useful, but
10
# This program is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the GNU
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the GNU
13
# General Public License for more details.
13
# General Public License for more details.
14
#
14
#
15
# A copy of the GNU General Public License is available via WWW at
15
# A copy of the GNU General Public License is available via WWW at
16
# http://www.gnu.org/copyleft/gpl.html.	 You can also obtain it by
16
# http://www.gnu.org/copyleft/gpl.html.	 You can also obtain it by
17
# writing to the Free Software Foundation, Inc., 59 Temple Place,
17
# writing to the Free Software Foundation, Inc., 59 Temple Place,
18
# Suite 330, Boston, MA  02111-1307  USA.
18
# Suite 330, Boston, MA  02111-1307  USA.
19
 
19
 
20
# Send any bug reports to r-bugs@r-project.org
20
# Send any bug reports to r-bugs@r-project.org
21
 
21
 
22
## <FIXME>
22
## <FIXME>
23
## This is not portable: has Unix-style file paths and system().  (But
23
## This is not portable: has Unix-style file paths and system().  (But
24
## we need to call tar/gzip and zip, respectively, so it is not clear
24
## we need to call tar/gzip and zip, respectively, so it is not clear
25
## how platform-independent the code can be.)
25
## how platform-independent the code can be.)
26
## </FIXME>
26
## </FIXME>
27
 
27
 
28
use Cwd;
28
use Cwd;
29
use File::Basename;
29
use File::Basename;
30
use File::Compare;
30
use File::Compare;
31
use File::Find;
31
use File::Find;
32
use File::Path;
32
use File::Path;
33
use File::Copy;
33
use File::Copy;
34
use Getopt::Long;
34
use Getopt::Long;
35
use IO::File;
35
use IO::File;
36
use R::Dcf;
36
use R::Dcf;
37
use R::Logfile;
37
use R::Logfile;
38
use R::Rd;
38
use R::Rd;
39
use R::Utils;
39
use R::Utils;
40
use R::Vars;
40
use R::Vars;
41
use Text::DelimMatch;
41
use Text::DelimMatch;
42
use Text::Wrap;
42
use Text::Wrap;
43
 
43
 
44
## don't buffer output
44
## don't buffer output
45
$| = 1;
45
$| = 1;
46
 
46
 
47
my $revision = ' $Revision: 1.73 $ ';
47
my $revision = ' $Revision: 1.74 $ ';
48
my $version;
48
my $version;
49
my $name;
49
my $name;
50
$revision =~ / ([\d\.]*) /;
50
$revision =~ / ([\d\.]*) /;
51
$version = $1;
51
$version = $1;
52
($name = $0) =~ s|.*/||;
52
($name = $0) =~ s|.*/||;
53
 
53
 
54
R::Vars::error("R_HOME", "R_CMD");
54
R::Vars::error("R_HOME", "R_CMD");
55
 
55
 
56
my $WINDOWS = ($R::Vars::OSTYPE eq "windows");
56
my $WINDOWS = ($R::Vars::OSTYPE eq "windows");
57
 
57
 
58
my @exclude_patterns = R::Utils::get_exclude_patterns();
58
my @exclude_patterns = R::Utils::get_exclude_patterns();
59
 
59
 
60
my @known_options = ("help|h", "version|v", "binary", "no-docs",
60
my @known_options = ("help|h", "version|v", "binary", "no-docs",
61
		     "use-zip", "use-zip-help", "use-zip-data",
61
		     "use-zip", "use-zip-help", "use-zip-data",
62
		     "force", "no-vignettes");
62
		     "force", "no-vignettes");
63
 
63
 
64
if($WINDOWS) {
64
if($WINDOWS) {
65
    die "Please set TMPDIR to a valid temporary directory\n"
65
    die "Please set TMPDIR to a valid temporary directory\n"
66
	unless (-e ${R::Vars::TMPDIR});
66
	unless (-e ${R::Vars::TMPDIR});
67
    @known_options = ("help|h", "version|v", "binary", "docs:s", "auto-zip",
67
    @known_options = ("help|h", "version|v", "binary", "docs:s", "auto-zip",
68
		      "use-zip", "use-zip-help", "use-zip-data", 
68
		      "use-zip", "use-zip-help", "use-zip-data", 
69
		      "force", "no-vignettes");
69
		      "force", "no-vignettes");
70
}
70
}
71
 
71
 
72
GetOptions(@known_options) or usage();
72
GetOptions(@known_options) or usage();
73
 
73
 
74
R_version("R add-on package builder", $version) if $opt_version;
74
R_version("R add-on package builder", $version) if $opt_version;
75
usage() if $opt_help;
75
usage() if $opt_help;
76
 
76
 
77
## Use system default unless explicitly specified otherwise.
77
## Use system default unless explicitly specified otherwise.
78
$ENV{"R_DEFAULT_PACKAGES"} = "";
78
$ENV{"R_DEFAULT_PACKAGES"} = "";
79
 
79
 
80
my $startdir = R_cwd();
80
my $startdir = R_cwd();
81
my $R_platform = R_getenv("R_PLATFORM", "unknown-binary");
81
my $R_platform = R_getenv("R_PLATFORM", "unknown-binary");
82
my $tar = R_getenv("TAR", "tar");
82
my $tar = R_getenv("TAR", "tar");
83
 
83
 
84
my $INSTALL_opts = "";
84
my $INSTALL_opts = "";
85
$INSTALL_opts .= " --use-zip" if $opt_use_zip;
85
$INSTALL_opts .= " --use-zip" if $opt_use_zip;
86
$INSTALL_opts .= " --use-zip-data" if $opt_use_zip_data;
86
$INSTALL_opts .= " --use-zip-data" if $opt_use_zip_data;
87
$INSTALL_opts .= " --use-zip-help" if $opt_use_zip_help;
87
$INSTALL_opts .= " --use-zip-help" if $opt_use_zip_help;
88
if($WINDOWS) {
88
if($WINDOWS) {
89
    $INSTALL_opts .= " --docs=$opt_docs" if $opt_docs;
89
    $INSTALL_opts .= " --docs=$opt_docs" if $opt_docs;
90
    $INSTALL_opts .= " --auto-zip" if $opt_auto_zip;
90
    $INSTALL_opts .= " --auto-zip" if $opt_auto_zip;
91
} else {
91
} else {
92
    $INSTALL_opts .= " --no-docs" if $opt_no_docs;
92
    $INSTALL_opts .= " --no-docs" if $opt_no_docs;
93
}
93
}
94
## <FIXME>
94
## <FIXME>
95
## Once we have a 'global' log file, use $log->warning() instead of just
95
## Once we have a 'global' log file, use $log->warning() instead of just
96
## print().
96
## print().
97
if(!$opt_binary && $INSTALL_opts ne "") {
97
if(!$opt_binary && $INSTALL_opts ne "") {
98
    print "** Options '$INSTALL_opts' for '--binary' ignored\n";
98
    print "** Options '$INSTALL_opts' for '--binary' ignored\n";
99
}
99
}
100
## </FIXME>
100
## </FIXME>
101
 
101
 
102
## This is the main loop over all packages to be checked.
102
## This is the main loop over all packages to be checked.
103
foreach my $pkg (@ARGV) {
103
foreach my $pkg (@ARGV) {
104
    my $is_bundle = 0;
104
    my $is_bundle = 0;
105
    $pkg =~ s/\/$//;
105
    $pkg =~ s/\/$//;
106
    my $pkgname = basename($pkg);
106
    my $pkgname = basename($pkg);
107
    chdir($startdir);
107
    chdir($startdir);
108
 
108
 
109
    my $log = new R::Logfile();
109
    my $log = new R::Logfile();
110
 
110
 
111
    my $description;
111
    my $description;
112
    $log->checking("for file '$pkg/DESCRIPTION'");
112
    $log->checking("for file '$pkg/DESCRIPTION'");
113
    if(-r "$pkg/DESCRIPTION") {
113
    if(-r "$pkg/DESCRIPTION") {
114
	$description = new R::Dcf("$pkg/DESCRIPTION");
114
	$description = new R::Dcf("$pkg/DESCRIPTION");
115
	$log->result("OK");
115
	$log->result("OK");
116
    }
116
    }
117
    else {
117
    else {
118
	$log->result("NO");
118
	$log->result("NO");
119
	exit(1);
119
	exit(1);
120
    }
120
    }
121
 
121
 
122
    if($opt_binary) {
122
    if($opt_binary) {
123
	my $libdir = "${R::Vars::TMPDIR}/Rbuild.$$";
123
	my $libdir = "${R::Vars::TMPDIR}/Rbuild.$$";
124
	mkdir("$libdir", 0755)
124
	mkdir("$libdir", 0755)
125
	    or die "Cannot create directory '$libdir'\n";
125
	    or die "Cannot create directory '$libdir'\n";
126
 
126
 
127
	if(system("${R::Vars::R_CMD} INSTALL -l '$libdir' " .
127
	if(R_system("${R::Vars::R_CMD} INSTALL -l '$libdir' " .
128
		  "$INSTALL_opts '$pkg'")) {
128
		    "$INSTALL_opts '$pkg'")) {
129
	    $log->error("installation failed");
129
	    $log->error("installation failed");
130
	}
130
	}
131
	print("\n");
131
	print("\n");
132
	chdir($libdir);
132
	chdir($libdir);
133
 
133
 
134
	my $pkgs = $pkgname;
134
	my $pkgs = $pkgname;
135
	## JG:  topLevelFiles will be files that sit in the top level of
135
	## JG:  topLevelFiles will be files that sit in the top level of
136
	## a zip/tarball along with any included directories.
136
	## a zip/tarball along with any included directories.
137
	my $topLevelFiles;
137
	my $topLevelFiles;
138
	if($description->{"Contains"}) {
138
	if($description->{"Contains"}) {
139
	    $log->message("looks like '${pkg}' is a package bundle");
139
	    $log->message("looks like '${pkg}' is a package bundle");
140
	    $is_bundle = 1;
140
	    $is_bundle = 1;
141
	    my @bundlepkgs = split(/\s+/, $description->{"Contains"});
141
	    my @bundlepkgs = split(/\s+/, $description->{"Contains"});
142
	    $pkgs = join(" ", @bundlepkgs);
142
	    $pkgs = join(" ", @bundlepkgs);
143
	    ## JG: Get a copy of the bundle's DESCRIPTION file and add
143
	    ## JG: Get a copy of the bundle's DESCRIPTION file and add
144
	    ## it into the zip archive.
144
	    ## it into the zip archive.
145
	    $log->message("copying $pkg/DESCRIPTION");
145
	    $log->message("copying $pkg/DESCRIPTION");
146
	    copy("$startdir/$pkg/DESCRIPTION", "DESCRIPTION");
146
	    copy("$startdir/$pkg/DESCRIPTION", "DESCRIPTION");
147
	    $topLevelFiles = "DESCRIPTION";
147
	    $topLevelFiles = "DESCRIPTION";
148
	}
148
	}
149
 
149
 
150
	if($WINDOWS) {
150
	if($WINDOWS) {
151
	    my $filename = "${pkgname}_" . $description->{"Version"};
151
	    my $filename = "${pkgname}_" . $description->{"Version"};
152
	    $log->message("building '$filename.zip'");
152
	    $log->message("building '$filename.zip'");
153
	    system("rm -f $startdir/$filename.zip");
153
	    R_system("rm -f $startdir/$filename.zip");
154
	    system("zip -r9X $startdir/$filename.zip " .
154
	    R_system("zip -r9X $startdir/$filename.zip " .
155
		   "$pkgs $topLevelFiles");
155
		     "$pkgs $topLevelFiles");
156
	    chdir($startdir);
156
	    chdir($startdir);
157
	} else {
157
	} else {
158
	    ## <FIXME>
158
	    ## <FIXME>
159
	    ## As R CMD INSTALL recursively copies all of 'inst', we at
159
	    ## As R CMD INSTALL recursively copies all of 'inst', we at
160
	    ## least need to make sure that CVS subdirs are excluded.
160
	    ## least need to make sure that CVS subdirs are excluded.
161
	    ## It is not clear whether more patterns should be excluded,
161
	    ## It is not clear whether more patterns should be excluded,
162
	    ## and we also need to fix this under Windows.
162
	    ## and we also need to fix this under Windows.
163
	    my $exclude = "${R::Vars::TMPDIR}/Rbuild-exclude.$$";
163
	    my $exclude = "${R::Vars::TMPDIR}/Rbuild-exclude.$$";
164
	    open(EXCLUDE, "> $exclude")
164
	    open(EXCLUDE, "> $exclude")
165
		or die "Cannot write to '$exclude'\n";
165
		or die "Cannot write to '$exclude'\n";
166
	    sub findExcludeFiles {
166
	    sub findExcludeFiles {
167
		print EXCLUDE "$File::Find::name\n" if /^CVS$/;
167
		print EXCLUDE "$File::Find::name\n" if /^CVS$/;
168
	    }
168
	    }
169
	    foreach my $p (split(/\s+/, $pkgs)) {
169
	    foreach my $p (split(/\s+/, $pkgs)) {
170
		find(\&findExcludeFiles, "$p");
170
		find(\&findExcludeFiles, "$p");
171
	    }
171
	    }
172
	    close(EXCLUDE);
172
	    close(EXCLUDE);
173
 
173
 
174
	    my $filename = "${pkgname}_" . $description->{"Version"} .
174
	    my $filename = "${pkgname}_" . $description->{"Version"} .
175
		"_R_${R_platform}.tar";
175
		"_R_${R_platform}.tar";
176
	    $log->message("building '$filename.gz'");
176
	    $log->message("building '$filename.gz'");
177
	    system("$tar chXf $exclude $startdir/$filename " .
177
	    system("$tar chXf $exclude $startdir/$filename " .
178
		   "$pkgs $topLevelFiles");
178
		   "$pkgs $topLevelFiles");
179
	    chdir($startdir);
179
	    chdir($startdir);
180
	    system("gzip -9f $filename");
180
	    system("gzip -9f $filename");
181
	    unlink($exclude);
181
	    unlink($exclude);
182
	    ## </FIXME>
182
	    ## </FIXME>
183
	}
183
	}
184
 
184
 
185
	rmtree($libdir);
185
	rmtree($libdir);
186
    }
186
    }
187
    else {
187
    else {
188
	if($description->{"Contains"}) {
188
	if($description->{"Contains"}) {
189
	    $log->message("looks like '${pkg}' is a package bundle");
189
	    $log->message("looks like '${pkg}' is a package bundle");
190
	    $is_bundle = 1;
190
	    $is_bundle = 1;
191
	    my @bundlepkgs = split(/\s+/, $description->{"Contains"});
191
	    my @bundlepkgs = split(/\s+/, $description->{"Contains"});
192
	    foreach my $ppkg (@bundlepkgs) {
192
	    foreach my $ppkg (@bundlepkgs) {
193
		$log->message("preparing '$ppkg' in bundle '$pkg':");
193
		$log->message("preparing '$ppkg' in bundle '$pkg':");
194
		$log->setstars("**");
194
		$log->setstars("**");
195
		chdir($startdir);
195
		chdir($startdir);
196
		prepare_pkg("$pkg/$ppkg", $is_bundle, $description, $log);
196
		prepare_pkg("$pkg/$ppkg", $is_bundle, $description, $log);
197
		$log->setstars("*");
197
		$log->setstars("*");
198
	    }
198
	    }
199
	}
199
	}
200
	else {
200
	else {
201
	    $is_bundle = 0;
201
	    $is_bundle = 0;
202
	    chdir($startdir);
202
	    chdir($startdir);
203
	    $log->message("preparing '$pkg':");
203
	    $log->message("preparing '$pkg':");
204
	    prepare_pkg("$pkg", $is_bundle, $description, $log);
204
	    prepare_pkg("$pkg", $is_bundle, $description, $log);
205
	}
205
	}
206
 
206
 
207
	chdir($startdir);
207
	chdir($startdir);
208
 
208
 
209
	$log->message("removing junk files");
209
	$log->message("removing junk files");
210
	find(\&deleteJunkFiles, $pkg);
210
	find(\&deleteJunkFiles, $pkg);
211
 
211
 
212
	my $exclude = "${R::Vars::TMPDIR}/Rbuild-exclude.$$";
212
	my $exclude = "${R::Vars::TMPDIR}/Rbuild-exclude.$$";
213
	open(EXCLUDE, "> $exclude")
213
	open(EXCLUDE, "> $exclude")
214
	  or die "Cannot write to '$exclude'\n";
214
	  or die "Cannot write to '$exclude'\n";
215
	binmode EXCLUDE if $WINDOWS;
215
	binmode EXCLUDE if $WINDOWS;
216
	## <NOTE>
216
	## <NOTE>
217
	## For bundles, the .Rbuildignore mechanism is not consistent
217
	## For bundles, the .Rbuildignore mechanism is not consistent
218
	## between build and check: the latter always works on a per
218
	## between build and check: the latter always works on a per
219
	## package basis.  Maybe fix this once we know whether we want
219
	## package basis.  Maybe fix this once we know whether we want
220
	## to keep bundles ...
220
	## to keep bundles ...
221
	if(-f "$pkg/.Rbuildignore") {
221
	if(-f "$pkg/.Rbuildignore") {
222
	    open(RBUILDIGNORE, "$pkg/.Rbuildignore");
222
	    open(RBUILDIGNORE, "$pkg/.Rbuildignore");
223
	    while(<RBUILDIGNORE>) {
223
	    while(<RBUILDIGNORE>) {
224
		chop;
224
		chop;
225
		push(@exclude_patterns, $_) if $_;
225
		push(@exclude_patterns, $_) if $_;
226
	    }
226
	    }
227
	    close(RBUILDIGNORE);
227
	    close(RBUILDIGNORE);
228
	}
228
	}
229
	## </NOTE>
229
	## </NOTE>
230
	sub findExcludeFiles {
230
	sub findExcludeFiles {
231
	    print EXCLUDE "$File::Find::name\n" if(-d $_ && /^check$/);
231
	    print EXCLUDE "$File::Find::name\n" if(-d $_ && /^check$/);
232
	    print EXCLUDE "$File::Find::name\n" if(-d $_ && /^chm$/);
232
	    print EXCLUDE "$File::Find::name\n" if(-d $_ && /^chm$/);
233
	    print EXCLUDE "$File::Find::name\n" if(-d $_ && /[Oo]ld$/);
233
	    print EXCLUDE "$File::Find::name\n" if(-d $_ && /[Oo]ld$/);
234
	    print EXCLUDE "$File::Find::name\n" if /^GNUMakefile$/;
234
	    print EXCLUDE "$File::Find::name\n" if /^GNUMakefile$/;
235
	    print EXCLUDE "$File::Find::name\n" if /^CVS$/;
235
	    print EXCLUDE "$File::Find::name\n" if /^CVS$/;
236
	    if($WINDOWS) {
236
	    if($WINDOWS) {
237
		## exclude stray dependency files
237
		## exclude stray dependency files
238
		print EXCLUDE "$File::Find::name\n" if /\.d$/;
238
		print EXCLUDE "$File::Find::name\n" if /\.d$/;
239
		print EXCLUDE "$File::Find::name\n" if /^Makedeps$/;
239
		print EXCLUDE "$File::Find::name\n" if /^Makedeps$/;
240
	    }
240
	    }
241
	    my $filename = $File::Find::name;
241
	    my $filename = $File::Find::name;
242
	    $filename =~ s/^[^\/]*\///;
242
	    $filename =~ s/^[^\/]*\///;
243
	    foreach my $p (@exclude_patterns) {
243
	    foreach my $p (@exclude_patterns) {
244
		if($WINDOWS) {
244
		if($WINDOWS) {
245
		    ## Argh: Windows is case-honoring but not
245
		    ## Argh: Windows is case-honoring but not
246
		    ## case-insensitive ...
246
		    ## case-insensitive ...
247
		    print EXCLUDE "$File::Find::name\n"
247
		    print EXCLUDE "$File::Find::name\n"
248
			if($filename =~	/$p/i);
248
			if($filename =~	/$p/i);
249
		}
249
		}
250
		else {
250
		else {
251
		    if($filename =~ /$p/) {
251
		    if($filename =~ /$p/) {
252
			## Seems that the Tar '-X' option uses exclude
252
			## Seems that the Tar '-X' option uses exclude
253
                        ## *shell* patterns, where '*', '?', and '[...]'
253
                        ## *shell* patterns, where '*', '?', and '[...]'
254
                        ## are the usual shell wildcards and '\' escapes
254
                        ## are the usual shell wildcards and '\' escapes
255
			## them.  Hence we need to escape the wildchard
255
			## them.  Hence we need to escape the wildchard
256
			## characters in file names.  On Windows, the
256
			## characters in file names.  On Windows, the
257
			## first two are invalid (and hence rejected by
257
			## first two are invalid (and hence rejected by
258
			## R CMD check), and the last two do not need
258
			## R CMD check), and the last two do not need
259
			## escaping.
259
			## escaping.
260
			$filename = "$File::Find::name";
260
			$filename = "$File::Find::name";
261
			$filename =~ s/\[/\\\[/g;
261
			$filename =~ s/\[/\\\[/g;
262
			$filename =~ s/\]/\\\]/g;
262
			$filename =~ s/\]/\\\]/g;
263
			print EXCLUDE "$filename\n";
263
			print EXCLUDE "$filename\n";
264
		    }
264
		    }
265
		}
265
		}
266
	    }
266
	    }
267
	}
267
	}
268
	chdir("$pkg/..");
268
	chdir("$pkg/..");
269
	find(\&findExcludeFiles, "$pkgname");
269
	find(\&findExcludeFiles, "$pkgname");
270
	close(EXCLUDE);
270
	close(EXCLUDE);
271
 
271
 
272
	my $filename = "${pkgname}_" . $description->{"Version"} . ".tar";
272
	my $filename = "${pkgname}_" . $description->{"Version"} . ".tar";
273
	$log->message("building '$filename.gz'");
273
	$log->message("building '$filename.gz'");
274
	my $filepath = "$startdir/$filename";
274
	my $filepath = "$startdir/$filename";
275
	if($WINDOWS) {
275
	if($WINDOWS) {
276
	    ## workaround for paths in Cygwin tar
276
	    ## workaround for paths in Cygwin tar
277
	    $filepath =~ s+^([A-Za-x]):+/cygdrive/\1+;
277
	    $filepath =~ s+^([A-Za-x]):+/cygdrive/\1+;
278
	}
278
	}
279
	## <FIXME>
279
	## <FIXME>
280
	## Is option '-h' portable?
280
	## Is option '-h' portable?
281
	system("$tar chf $filepath $pkgname");
281
	R_system("$tar chf $filepath $pkgname");
282
	## </FIXME>
282
	## </FIXME>
283
	my $tmpdir = "${R::Vars::TMPDIR}/Rbuild.$$";
283
	my $tmpdir = "${R::Vars::TMPDIR}/Rbuild.$$";
284
	## (Same as $libdir for building a binary package.)
284
	## (Same as $libdir for building a binary package.)
285
	rmtree($tmpdir) if(-d $tmpdir);
285
	rmtree($tmpdir) if(-d $tmpdir);
286
	mkdir("$tmpdir", 0755)
286
	mkdir("$tmpdir", 0755)
287
	    or die "Cannot create directory '$tmpdir'\n";
287
	    or die "Cannot create directory '$tmpdir'\n";
288
	chdir($tmpdir);
288
	chdir($tmpdir);
289
	system("$tar xhf $filepath");
289
	R_system("$tar xhf $filepath");
290
	## Remove exclude files.
290
	## Remove exclude files.
291
	open(EXCLUDE, "< $exclude");
291
	open(EXCLUDE, "< $exclude");
292
	while(<EXCLUDE>) {
292
	while(<EXCLUDE>) {
293
	    rmtree(glob($_));
293
	    rmtree(glob($_));
294
	}
294
	}
295
	close(EXCLUDE);
295
	close(EXCLUDE);
296
	unlink($exclude);
296
	unlink($exclude);
297
	## Fix permissions.
297
	## Fix permissions.
298
	sub fix_permissions {
298
	sub fix_permissions {
299
	    ## Note that when called via File::Find::find, $_ holds the
299
	    ## Note that when called via File::Find::find, $_ holds the
300
	    ## file name within the current directory.
300
	    ## file name within the current directory.
301
	    if(-d $_) {
301
	    if(-d $_) {
302
		## Directories should really be mode 00755 if possible.
302
		## Directories should really be mode 00755 if possible.
303
		chmod(00755, $_);
303
		chmod(00755, $_);
304
	    }
304
	    }
305
	    elsif(-f $_) {
305
	    elsif(-f $_) {
306
		## Files should be readable by everyone, and writable
306
		## Files should be readable by everyone, and writable
307
		## only for user.  This leaves a bit of uncertainty
307
		## only for user.  This leaves a bit of uncertainty
308
		## about the execute bits.
308
		## about the execute bits.
309
		chmod(((stat $_)[2] | 00644) & 00755, $_);
309
		chmod(((stat $_)[2] | 00644) & 00755, $_);
310
	    }
310
	    }
311
	}
311
	}
312
	find(\&fix_permissions, "${pkgname}") if(!$WINDOWS);
312
	find(\&fix_permissions, "${pkgname}") if(!$WINDOWS);
313
	## Add build stamp to the DESCRIPTION file.
313
	## Add build stamp to the DESCRIPTION file.
314
	my @lines = &read_lines(&file_path($pkgname, "DESCRIPTION"));
314
	my @lines = &read_lines(&file_path($pkgname, "DESCRIPTION"));
315
	@lines = grep(!/^\s*$/, @lines); # Remove blank lines.
315
	@lines = grep(!/^\s*$/, @lines); # Remove blank lines.
316
	my $user_name;
316
	my $user_name;
317
	if($WINDOWS) {
317
	if($WINDOWS) {
318
	    $user_name = Win32::LoginName();
318
	    $user_name = Win32::LoginName();
319
	}
319
	}
320
	else {
320
	else {
321
	    $user_name = (getpwuid($<))[0];
321
	    $user_name = (getpwuid($<))[0];
322
	}
322
	}
323
	my $fh = new IO::File(&file_path($pkgname, "DESCRIPTION"), "w");
323
	my $fh = new IO::File(&file_path($pkgname, "DESCRIPTION"), "w");
324
	$fh->print(join("\n", @lines), "\n");
324
	$fh->print(join("\n", @lines), "\n");
325
	$fh->print("Packaged: ",
325
	$fh->print("Packaged: ",
326
		   scalar(localtime()), "; ",
326
		   scalar(localtime()), "; ",
327
		   $user_name, "\n");
327
		   $user_name, "\n");
328
	$fh->close();
328
	$fh->close();
329
	## Finalize.
329
	## Finalize.
330
	system("$tar chf $filepath $pkgname");
330
	R_system("$tar chf $filepath $pkgname");
331
	chdir($startdir);
331
	chdir($startdir);
332
	rmtree($tmpdir);
332
	rmtree($tmpdir);
333
	system("gzip -9f $filename");
333
	R_system("gzip -9f $filename");
334
    }
334
    }
335
    $log->close();
335
    $log->close();
336
    print("\n");
336
    print("\n");
337
}
337
}
338
 
338
 
339
 
339
 
340

340

341
sub prepare_pkg {
341
sub prepare_pkg {
342
 
342
 
343
    my ($pkg, $in_bundle, $description, $log) = @_;
343
    my ($pkg, $in_bundle, $description, $log) = @_;
344
 
344
 
345
    chdir($pkg);
345
    chdir($pkg);
346
    my $pkgdir = R_cwd();
346
    my $pkgdir = R_cwd();
347
    my $pkgname = basename($pkg);
347
    my $pkgname = basename($pkg);
348
 
348
 
349
    if(-d "src") {
349
    if(-d "src") {
350
	chdir("src");
350
	chdir("src");
351
	$log->message("cleaning src");
351
	$log->message("cleaning src");
352
	if($WINDOWS) {
352
	if($WINDOWS) {
353
	    ## a Windows Makefile.win might use $(RHOME)/src/gnuwin32/MkRules
353
	    ## a Windows Makefile.win might use $(RHOME)/src/gnuwin32/MkRules
354
	    $ENV{RHOME} = $ENV{R_HOME};
354
	    $ENV{RHOME} = $ENV{R_HOME};
355
	    if(-r "Makefile.win") {
355
	    if(-r "Makefile.win") {
356
		system("${R::Vars::MAKE} -f Makefile.win clean");
356
		R_system("${R::Vars::MAKE} -f Makefile.win clean");
357
	    } else {
357
	    } else {
358
		foreach my $file (<*.o $pkgname.a $pkgname.dll $pkgname.def>) {
358
		foreach my $file (<*.o $pkgname.a $pkgname.dll $pkgname.def>) {
359
		    unlink($file);
359
		    unlink($file);
360
		}
360
		}
361
		rmtree("_libs") if (-d "_libs");
361
		rmtree("_libs") if (-d "_libs");
362
	    }
362
	    }
363
	} else {
363
	} else {
364
	    if(-r "Makefile") {
364
	    if(-r "Makefile") {
365
		system("${R::Vars::MAKE} -f Makefile clean");
365
		system("${R::Vars::MAKE} -f Makefile clean");
366
	    } else {
366
	    } else {
367
		foreach my $file (<*.o *s[lo]>) {
367
		foreach my $file (<*.o *s[lo]>) {
368
		    unlink($file);
368
		    unlink($file);
369
		}
369
		}
370
		rmtree(".libs") if (-d ".libs");
370
		rmtree(".libs") if (-d ".libs");
371
	    }
371
	    }
372
	}
372
	}
373
    }
373
    }
374
    chdir($pkgdir);
374
    chdir($pkgdir);
375
    if(!$WINDOWS && -x "./cleanup") {
375
    if(!$WINDOWS && -x "./cleanup") {
376
	$log->message("running cleanup");
376
	$log->message("running cleanup");
377
	system("./cleanup");
377
	system("./cleanup");
378
    }
378
    }
379
 
379
 
380
    ## Only update existing INDEX files.
380
    ## Only update existing INDEX files.
381
    &updateRdIndex("INDEX", "man", $log) if(-f "INDEX");
381
    &updateRdIndex("INDEX", "man", $log) if(-f "INDEX");
382
 
382
 
383
    if((-d &file_path("inst", "doc"))
383
    if((-d &file_path("inst", "doc"))
384
       && &list_files_with_type(&file_path("inst", "doc"),
384
       && &list_files_with_type(&file_path("inst", "doc"),
385
				"vignette")) {
385
				"vignette")) {
386
	if(!$opt_no_vignettes) {
386
	if(!$opt_no_vignettes) {
387
	    $log->creating("vignettes");
387
	    $log->creating("vignettes");
388
	    my $Rcmd = "library(tools)\n";
388
	    my $Rcmd = "library(tools)\n";
389
	    $Rcmd .= "buildVignettes(dir = '.')\n";
389
	    $Rcmd .= "buildVignettes(dir = '.')\n";
390
	    my @out = R_runR($Rcmd, "--vanilla --no-save --quiet");
390
	    my @out = R_runR($Rcmd, "--vanilla --no-save --quiet");
391
	    my @err = grep(/^Error/, @out);
391
	    my @err = grep(/^Error/, @out);
392
	    if($#err >= 0) {
392
	    if($#err >= 0) {
393
		@out = grep(!/^\>/, @out);
393
		@out = grep(!/^\>/, @out);
394
		$log->error();
394
		$log->error();
395
		$log->print(join("\n", @out) . "\n");
395
		$log->print(join("\n", @out) . "\n");
396
		exit(1);
396
		exit(1);
397
	    }
397
	    }
398
	    else {
398
	    else {
399
		$log->result("OK");
399
		$log->result("OK");
400
	    }
400
	    }
401
	}
401
	}
402
    }
402
    }
403
 
403
 
404
    1;
404
    1;
405
}
405
}
406
 
406
 
407
sub deleteJunkFiles {
407
sub deleteJunkFiles {
408
    unlink($_) if /^(\.RData|\.Rhistory)$/;
408
    unlink($_) if /^(\.RData|\.Rhistory)$/;
409
    if(/^DESCRIPTION$/) {
409
    if(/^DESCRIPTION$/) {
410
	unlink($_) if (-f "DESCRIPTION.in");
410
	unlink($_) if (-f "DESCRIPTION.in");
411
    }
411
    }
412
}
412
}
413
 
413
 
414

414

415
sub updateIndex {
415
sub updateIndex {
416
    my ($oldindex, $newindex, $log) = @_;
416
    my ($oldindex, $newindex, $log) = @_;
417
 
417
 
418
    $log->checking("whether '$oldindex' is up-to-date");
418
    $log->checking("whether '$oldindex' is up-to-date");
419
    if(-r $oldindex) {
419
    if(-r $oldindex) {
420
	if(compare($oldindex, $newindex) != 0) {
420
	if(compare($oldindex, $newindex) != 0) {
421
	    $log->result("NO");
421
	    $log->result("NO");
422
	    if($opt_force) {
422
	    if($opt_force) {
423
		$log->message("overwriting '${oldindex}' as " .
423
		$log->message("overwriting '${oldindex}' as " .
424
			      "'--force' was given");
424
			      "'--force' was given");
425
		unlink $oldindex;
425
		unlink $oldindex;
426
		rename($newindex, $oldindex);
426
		rename($newindex, $oldindex);
427
	    }
427
	    }
428
	    else {
428
	    else {
429
		$log->message("use '--force' to overwrite " .
429
		$log->message("use '--force' to overwrite " .
430
			      "the existing '${oldindex}'");
430
			      "the existing '${oldindex}'");
431
		unlink $newindex;
431
		unlink $newindex;
432
	    }
432
	    }
433
	}
433
	}
434
	else {
434
	else {
435
	    $log->result("OK");
435
	    $log->result("OK");
436
	    unlink $newindex;
436
	    unlink $newindex;
437
	}
437
	}
438
    }
438
    }
439
    else {
439
    else {
440
	$log->result("NO");
440
	$log->result("NO");
441
	$log->message("creating new '$oldindex'");
441
	$log->message("creating new '$oldindex'");
442
	unlink $oldindex;
442
	unlink $oldindex;
443
	rename($newindex, $oldindex);
443
	rename($newindex, $oldindex);
444
    }
444
    }
445
 
445
 
446
    1;
446
    1;
447
}
447
}
448
 
448
 
449
sub updateRdIndex {
449
sub updateRdIndex {
450
    my ($oldindex, $Rdfiles, $log) = @_;
450
    my ($oldindex, $Rdfiles, $log) = @_;
451
 
451
 
452
    my $newindex = ".Rbuildindex.$$";
452
    my $newindex = ".Rbuildindex.$$";
453
 
453
 
454
    my $Rcmd = "Rdindex(\"${Rdfiles}\", \"${newindex}\")\n";
454
    my $Rcmd = "Rdindex(\"${Rdfiles}\", \"${newindex}\")\n";
455
 
455
 
456
    my %result = 
456
    my %result = 
457
	R_run_R($Rcmd, "--vanilla --quiet", "R_DEFAULT_PACKAGES=tools");
457
	R_run_R($Rcmd, "--vanilla --quiet", "R_DEFAULT_PACKAGES=tools");
458
    if($result{"status"}) {
458
    if($result{"status"}) {
459
	## This is a bit silly ... but just in case this fails, we want
459
	## This is a bit silly ... but just in case this fails, we want
460
	## a decent error message.
460
	## a decent error message.
461
	my @out = grep(!/^\>/, @{$result{"out"}});
461
	my @out = grep(!/^\>/, @{$result{"out"}});
462
	$log->message("computing Rd index");
462
	$log->message("computing Rd index");
463
	$log->error();
463
	$log->error();
464
	$log->print(join("\n", @out) . "\n");
464
	$log->print(join("\n", @out) . "\n");
465
	exit(1);
465
	exit(1);
466
    }
466
    }
467
 
467
 
468
    updateIndex($oldindex, $newindex, $log);
468
    updateIndex($oldindex, $newindex, $log);
469
    1;
469
    1;
470
}
470
}
471
 
471
 
472
 
472
 
473

473

474
sub usage {
474
sub usage {
475
    print STDERR <<END;
475
    print STDERR <<END;
476
Usage: R CMD $name [options] pkgdirs
476
Usage: R CMD $name [options] pkgdirs
477
 
477
 
478
Build R packages from package sources in the directories specified by
478
Build R packages from package sources in the directories specified by
479
pkgdirs. 
479
pkgdirs. 
480
 
480
 
481
Options:
481
Options:
482
  -h, --help		print short help message and exit
482
  -h, --help		print short help message and exit
483
  -v, --version		print version info and exit
483
  -v, --version		print version info and exit
484
 
484
 
485
  --force               force overwriting of INDEX file
485
  --force               force overwriting of INDEX file
486
  --no-vignettes        do not rebuild package vignettes
486
  --no-vignettes        do not rebuild package vignettes
487
 
487
 
488
  --binary              build pre-compiled binary packages, with options:
488
  --binary              build pre-compiled binary packages, with options:
489
END
489
END
490
    if($WINDOWS) {
490
    if($WINDOWS) {
491
	print STDERR "  --docs=TYPE           " .
491
	print STDERR "  --docs=TYPE           " .
492
	    "type(s) of documentation to build and install\n";
492
	    "type(s) of documentation to build and install\n";
493
	print STDERR "  --auto-zip            " .
493
	print STDERR "  --auto-zip            " .
494
	    "select zipping of data and help based on size\n";
494
	    "select zipping of data and help based on size\n";
495
    } else {
495
    } else {
496
	print STDERR "  --no-docs             " .
496
	print STDERR "  --no-docs             " .
497
	    "do not build and install documentation\n";
497
	    "do not build and install documentation\n";
498
    }
498
    }
499
    print STDERR <<END;
499
    print STDERR <<END;
500
  --use-zip-data        collect data files in zip archive
500
  --use-zip-data        collect data files in zip archive
501
  --use-zip-help        collect help and examples into zip archives
501
  --use-zip-help        collect help and examples into zip archives
502
  --use-zip             combine '--use-zip-data' and '--use-zip-help'
502
  --use-zip             combine '--use-zip-data' and '--use-zip-help'
503
 
503
 
504
Email bug reports to <r-bugs\@r-project.org>.
504
Email bug reports to <r-bugs\@r-project.org>.
505
END
505
END
506
    exit 0;
506
    exit 0;
507
}
507
}