-
Notifications
You must be signed in to change notification settings - Fork 1
/
javacpp
executable file
·397 lines (368 loc) · 14.4 KB
/
javacpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
#!/usr/bin/perl -w
use strict;
#
# javacpp
#
# Runs the C preprocessor (cpp) on .prejava files to create .java files,
# and then runs javac (or whatever) on the .java files,
# filtering javac's error messages
# so that line numbers in the .java files
# are replaced with the corresponding line numbers
# in the .prejava (and #included) files.
# This adds negligible time to the compile process
# (at least compared to Sun's dog-slow javac).
#
# In addition to creating a .java file for each input .prejava file,
# this script also creates a corresponding .java.lines file
# containing just the line number remapping information,
# for use by other programs (e.g. javarenumber).
#
# Usage:
# javacpp [-v <verboseLevel> <cpp flags> javac <javac flags> class1.prejava [class2.prejava ...]
# Or to just preprocess the files without running javac:
# javacpp [-v <verboseLevel> <cpp flags> class1.prejava [class2.prejava ...]
#
# To remap the line number table in the resulting class files,
# use the companion javarenumber script:
# javarenumber class1.class class1\$whateverLocalClass.class ...
#
# Notes: For each source file name ending in .prejava,
# the intermediate java code will go in the corresponding .java file,
# which gets clobbered with each run
# (sorry, I couldn't name it something else,
# since javac has strict requirements about file names).
# Additional file names (and other arguments) that don't end in .prejava
# are passed directly to javac without preprocessing.
# Only initial flags beginning with '-' are passed to cpp
# (so, for example, you must say "-Dfoo=bar" instead of "-D foo=bar").
# Additionally, "-D__java" and -C and -ffreestanding are prepended to the arguments passed to cpp.
# (-C means don't strip comments, which is useful for javadoc).
# (-ffreestanding means don't #include "stdc-predef.h"; see https://reviews.llvm.org/D34158.
# I fear this may not be very portable).
#
# BUGS:
# - "import" directives cause javac to do strange magic
# for which the imported .java file apparently needs to exist.
# E.g. the following will not work (the first javac will fail):
# javacpp javac A.prejava # where A imports stuff from B
# javacpp javac B.prejava
# If the entire program is created with one execution of javacpp javac, or
# if the imported classes always get compiled before the importing classes,
# then it doesn't matter, but that is often not the case
# (and in fact is impossible in the case of mutual dependencies).
# I need to research this more
# to figure out if I can detect and handle it...
# I've worked around this problem in a large source heirarchy
# by doing the following first in the top-level directory
# (the idea is to make sure all the .java files exist beforehand):
# javacpp `find . -name \*.prejava -print`
# - This script should should really include the javarenumber functionality,
# but unfortunately it's hard to tell exactly which
# class files are being created
# (since there is a separate named file for each local class,
# and we don't know which class files are from this compile
# or left over from previous compiles).
#
# This script has been tested on RedHat Linux 6.1 and 7.1,
# with perl 5.6.0,
# with Sun's JDK 1.3.0_02 and Jikes 1.13.
# Also Win98/cygwin with JDK 1.3.0_02.
#
# Author: Don Hatch ([email protected])
# Revision history:
# Thu Aug 27 16:23:22 PDT 2020
# Pass -ffreestanding to cpp
# Fri Aug 21 05:05:09 PDT 2020
# Put lots of "// !!! THIS FILE WAS AUTOMATICALLY GENERATED by javacpp; DO NOT EDIT "
# at top of (read-only) output.
# Wed May 11 19:33:18 PDT 2016
# Hack to make it work on darwin (Mac Os X) on which cpp installation is all f'ed up.
# Wed Dec 3 16:45:57 PST 2014
# Allow more than one number at the end of a #line line in cpp output
# (I don't know what it means and it doesn't matter).
# Sun Jul 25 08:16:00 PDT 2004
# Print signal and core dump info if javac terminates abnormally.
# Mon Apr 7 15:55:06 PDT 2003
# Pass -C to cpp
# Thu Feb 13 20:47:00 PST 2003
# Fix argument passing in the common case of an argument that contains
# a semicolon (e.g. a classpath argument on Windows).
# Wed Nov 13 02:25:25 PST 2002
# Tweak for jikes lexical warnings
# Sun Nov 10 21:00:31 PST 2002
# Make it work with jikes on cygwin.
# Add command line option "-v <verboseLevel>".
# Wed Oct 9 10:53:32 PDT 2002
# Make it work on Windows (cygwin)
# Thu Sep 26 20:19:46 PDT 2002
# Recognize Jikes warnings as well as errors
# Fri Jun 15 12:54:42 PDT 2001
# Make .java and .lines output files read-only,
# to try to prevent common user error of editing the .java file
# Sat Jun 2 18:57:11 PDT 2001
# Pass -D__java to cpp,
# along with initial args beginning with '-' from the command line.
# Made to work with IBM's Jikes compiler as well as Sun's javac.
# Fri May 4 05:28:30 PDT 2001
# Initial revision
#
# This software may be used for any purpose
# as long as it is good and not evil.
#
# $Id: javacpp,v 1.24 2005/05/09 10:24:29 hatch Exp hatch $
#
# XXX TODO: allow command-line parameter to select alternate "cpp"?
# XXX TODO: don't process javac's stdout?
# XXX TODO: opening a newly-created 0444 file for writing might not be portable (e.g. NFS?), should find an alternate way
use Fcntl; # for O_WRONLY,O_CREAT
use File::Basename;
use POSIX; # for WEXITSTATUS and stuff
#
# Program to run as preprocessor...
#
my $cpp = "cpp"; # XXX allow setting on command line?
#
# Omfg, on Darwin, cpp is /usr/bin/cpp which apparently implicitly adds something like --traditional-cpp,
# which breaks everything.
# I have no idea how to turn that off, so the cpp command is useless on darwin.
# But "cc -E -xc" works instead.
#
if ($^O eq "darwin")
{
$cpp = "cc -E -xc";
}
my $debug = 0; # can be set on the command line using -v
#
# Use the greatest table entry with line number <= outLine,
# and return "$inFile:$inLine" from that entry.
# If the table is empty, return "$outFile:$outLine".
# If $outLine is before the first entry or after the last,
# use the first or last entry respectively.
#
sub lookup($$$)
{
my ($tableRef, $outLine, $outFile) = @_;
my $lo = 0; # first table entry
my $hi = @$tableRef-1; # last table entry
if ($lo > $hi)
{
$debug >= 1 && print STDERR "HOO: $outFile:$outLine -> table empty?";
return "$outFile:$outLine";
}
while ($lo < $hi)
{
my $mid = int(($lo+$hi+1)/2); # round up, so we never look at lo
if ($tableRef->[$mid][0] > $outLine)
{
$hi = $mid-1;
}
else # table entry <= $outLine
{
$lo = $mid;
}
}
$lo == $hi or die; # assertion
my ($entryOutLine,$entryInLine,$inFile) = @{$tableRef->[$lo]};
my $inLine = $entryInLine + ($outLine-$entryOutLine);
$debug >= 1 && print STDERR "HEY: $outFile:$outLine -> $inFile:$inLine\n";
return "$inFile:$inLine";
}
MAIN:
{
my @cppargs = ();
my @newargv = ();
my @prejavafiles = ();
my @javafiles = ();
my @linesfiles = ();
my $usageMessage = "Usage: $0 [-v <verboseLevel] <cpp flags> [javac <javac flags>] class1.prejava [class2.prejava ...]\n";
if (@ARGV >= 1 && $ARGV[0] eq "-v")
{
shift;
@ARGV >= 1 && $ARGV[0] =~ m/^-?[0-9]+$/ or die $usageMessage;
$debug = $ARGV[0];
shift;
}
#
# Initial args beginning with '-' are cpp args...
#
while (@cppargs < @ARGV
&& $ARGV[@cppargs] =~ /^-/)
{
push(@cppargs, $ARGV[@cppargs]);
}
#
# Must be something after the (optional) cpp args...
#
@cppargs < @ARGV or die $usageMessage;
#
# Every arg ending in ".prejava" is a file we must deal with...
#
foreach my $arg (@ARGV[@cppargs..@ARGV-1])
{
my $newarg = $arg;
if ($newarg =~ s/\.prejava$/.java/)
{
push(@prejavafiles, $arg);
push(@javafiles, $newarg);
push @linesfiles, "$newarg.lines";
}
push(@newargv, $newarg);
}
if ($debug >= 1)
{
print "\n";
print ("ARGV = @ARGV\n");
print ("cppargs = @cppargs\n");
print ("prejavafiles = @prejavafiles\n");
print ("javafiles = @javafiles\n");
print ("linesfiles = @linesfiles\n");
print ("newargv = @newargv\n");
print "\n";
}
#
# Preprocess prejavafiles to create java files,
# and construct tables mapping java line numbers to prejava line numbers.
#
my @tables = ();
for my $i (0..@prejavafiles-1)
{
my @table = ();
#
# Open and close input file to verify prejava file exists before
# we go and create any output files
# (cpp will tell us, but by then it will be too late).
# (A more efficient and robust strategy would be
# to create the output files lazily when we get
# the first line of output from cpp or when cpp
# exits successfully with no output, but that would be messy
# to code.)
#
open(DUMMY, "$prejavafiles[$i]") or die "Couldn't open $prejavafiles[$i]: $!\n";
close(DUMMY) or die;
print " Executing: $cpp -D__java -C -ffreestanding @cppargs $prejavafiles[$i]\n";
my $cppPid = open(CPP, "$cpp -D__java -C -ffreestanding @cppargs $prejavafiles[$i] |") or die "Couldn't fork cpp: $!\n";
unlink $javafiles[$i], $linesfiles[$i]; # ignore error, this is to help the sysopen succeed, if it fails we'll find out soon enough
#open(JAVAOUT, ">$javafiles[$i]")
sysopen(JAVAOUT, "$javafiles[$i]", O_WRONLY|O_CREAT, 0444)
or die "Couldn't open $javafiles[$i] for writing: $!\n";
#open(LINESOUT, ">$linesfiles[$i]")
sysopen(LINESOUT, "$linesfiles[$i]", O_WRONLY|O_CREAT, 0444)
or die "Couldn't open $linesfiles[$i] for writing: $!\n";
my $num_do_nots = 100;
print JAVAOUT "// !!! THIS FILE WAS AUTOMATICALLY GENERATED BY javacpp; DO NOT EDIT !!!\n" for 1 .. $num_do_nots;
while (<CPP>)
{
# comment out line directives,
# print them to the .java.lines file,
# and enter them in a table.
# XXX not sure what the optional final number means...
# XXX nothing = just orient, 1 = begin include, 2 = return from include?
my $followingLineNum = $.+1+$num_do_nots;
if (s/^(# ([0-9]+) "(.*)"( [0-9]+)*)$/\/\/ $followingLineNum $1/)
{
push(@table, [$followingLineNum, $2, $3]);
print LINESOUT;
}
print JAVAOUT;
}
close(JAVAOUT) or die "close $javafiles[$i]: $!\n";
close(LINESOUT) or die "close $linesfiles[$i]: $!\n";
close(CPP) or exit ($?>>8);
push(@tables, \@table);
}
if ($newargv[0] ne $ARGV[@cppargs])
{
# First argument is a .prejava file,
# so no program is being executed.
exit 0;
}
#
# In the common case that an argument contains a semicolon
# (e.g. a classpath argument on Windows), quote it.
# XXX is there a robust way to simply pass all arguments
# XXX without the quotes getting mangled?
@newargv = map {$_ =~ m/;/ ? "\"$_\"" : $_} @newargv;
#
# Run the java compiler (or whatever),
# filtering error messages to map java file:line to prejava file:line.
# XXX would be nice to only process javac's stderr and not its stdout, but this will do
#
print " Executing: @newargv\n";
my $javacPid = open(JAVAC, "@newargv 2>&1 |") or die "Couldn't fork $newargv[0]: $!\n";
my $currentFileNameFromJikes = undef;
my $adjustForJikes = undef; # how much indenting to add to next line
while (<JAVAC>)
{
if (defined $adjustForJikes)
{
$_ = (' ' x $adjustForJikes) . $_;
undef $adjustForJikes;
}
for my $i (0..@prejavafiles-1)
{
# XXX can mess up if $javafile has special re chars.
# XXX in fact '.' is a wildcard, but rarely matches anything
# XXX else so it usually works anyway.
s/\b($javafiles[$i]):([0-9]+)/lookup($tables[$i],$2,$1)/ge;
#
# Jikes is different; it says stuff like:
#
# Found 2 syntax errors in "HyperbolicApplet.java":
#
# 57. blah blah blah;
# <-->
#
# *** Syntax: Unexpected symbol ignored
#
#
# 58. bloo bloo bloo;
# <-->
#
# *** Syntax: Unexpected symbol ignored
#
if ((defined $currentFileNameFromJikes)
&& basename($currentFileNameFromJikes) eq $javafiles[$i])
{
my $oldLength = length($_);
if (s/^(\s*)([0-9]+)\.\s/lookup($tables[$i],$2,$1).'. '/e)
{
my $newLength = length($_);
$debug >= 1 && print STDERR "oldLength=$oldLength -> newLength=$newLength\n";
$adjustForJikes = $newLength - $oldLength;
}
}
}
# the \r is for jikes 1.17 on cygwin, which appears to put carriage returns at ends of lines
if (/^(Found|Issued) [0-9]+ (syntax|semantic|lexical) (error|warning)s? .*(in|compiling) "(.*)":\r?$/)
{
$currentFileNameFromJikes = $5;
$debug >= 1 && print STDERR "currentFileNameFromJikes = '$currentFileNameFromJikes'\n";
}
$debug >= 1 && print STDERR "|";
print STDERR "$_";
}
close(JAVAC);
if ($debug >= 1)
{
print " WIFEXITED = ".POSIX::WIFEXITED($?)."\n";
print " WEXITSTATUS = ".POSIX::WEXITSTATUS($?)."\n";
print " WIFSIGNALED = ".POSIX::WIFSIGNALED($?)."\n";
print " WTERMSIG = ".POSIX::WTERMSIG($?)."\n";
}
if (POSIX::WIFSIGNALED($?))
{
my $sig = WTERMSIG($?);
my $coredumped = ($? >> 7) & 1; # XXX not portable
print("Woops! Signal $sig");
if ($coredumped)
{
print(" (core dumped)");
}
print("\n");
}
else
{
# exit ($? >> 8); # whatever javac exited with (ignores any signal info)
exit WEXITSTATUS($?); # whatever javac exited with
}
} # main