Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java
41161 views
1
/*
2
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package com.sun.tools.jdeps;
27
28
import com.sun.tools.jdeps.Analyzer.Type;
29
import static com.sun.tools.jdeps.Analyzer.Type.*;
30
import static com.sun.tools.jdeps.JdepsWriter.*;
31
import static java.util.stream.Collectors.*;
32
33
import java.io.IOException;
34
import java.io.PrintWriter;
35
import java.lang.module.ResolutionException;
36
import java.nio.file.Files;
37
import java.nio.file.Path;
38
import java.nio.file.Paths;
39
import java.text.MessageFormat;
40
import java.util.*;
41
import java.util.jar.JarFile;
42
import java.util.regex.Pattern;
43
44
/**
45
* Implementation for the jdeps tool for static class dependency analysis.
46
*/
47
class JdepsTask {
48
static interface BadArguments {
49
String getKey();
50
Object[] getArgs();
51
boolean showUsage();
52
}
53
static class BadArgs extends Exception implements BadArguments {
54
static final long serialVersionUID = 8765093759964640721L;
55
BadArgs(String key, Object... args) {
56
super(JdepsTask.getMessage(key, args));
57
this.key = key;
58
this.args = args;
59
}
60
61
BadArgs showUsage(boolean b) {
62
showUsage = b;
63
return this;
64
}
65
final String key;
66
final Object[] args;
67
boolean showUsage;
68
69
@Override
70
public String getKey() {
71
return key;
72
}
73
74
@Override
75
public Object[] getArgs() {
76
return args;
77
}
78
79
@Override
80
public boolean showUsage() {
81
return showUsage;
82
}
83
}
84
85
static class UncheckedBadArgs extends RuntimeException implements BadArguments {
86
static final long serialVersionUID = -1L;
87
final BadArgs cause;
88
UncheckedBadArgs(BadArgs cause) {
89
super(cause);
90
this.cause = cause;
91
}
92
@Override
93
public String getKey() {
94
return cause.key;
95
}
96
97
@Override
98
public Object[] getArgs() {
99
return cause.args;
100
}
101
102
@Override
103
public boolean showUsage() {
104
return cause.showUsage;
105
}
106
}
107
108
static abstract class Option {
109
Option(boolean hasArg, String... aliases) {
110
this.hasArg = hasArg;
111
this.aliases = aliases;
112
}
113
114
Option(boolean hasArg, CommandOption cmd) {
115
this(hasArg, cmd.names());
116
}
117
118
boolean isHidden() {
119
return false;
120
}
121
122
boolean matches(String opt) {
123
for (String a : aliases) {
124
if (a.equals(opt))
125
return true;
126
if (hasArg && opt.startsWith(a + "="))
127
return true;
128
}
129
return false;
130
}
131
132
boolean ignoreRest() {
133
return false;
134
}
135
136
abstract void process(JdepsTask task, String opt, String arg) throws BadArgs;
137
final boolean hasArg;
138
final String[] aliases;
139
}
140
141
static abstract class HiddenOption extends Option {
142
HiddenOption(boolean hasArg, String... aliases) {
143
super(hasArg, aliases);
144
}
145
146
boolean isHidden() {
147
return true;
148
}
149
}
150
151
enum CommandOption {
152
ANALYZE_DEPS(""),
153
GENERATE_DOT_FILE("-dotoutput", "--dot-output"),
154
GENERATE_MODULE_INFO("--generate-module-info"),
155
GENERATE_OPEN_MODULE("--generate-open-module"),
156
LIST_DEPS("--list-deps"),
157
LIST_REDUCED_DEPS("--list-reduced-deps"),
158
PRINT_MODULE_DEPS("--print-module-deps"),
159
CHECK_MODULES("--check");
160
161
private final String[] names;
162
CommandOption(String... names) {
163
this.names = names;
164
}
165
166
String[] names() {
167
return names;
168
}
169
170
@Override
171
public String toString() {
172
return names[0];
173
}
174
}
175
176
static Option[] recognizedOptions = {
177
new Option(false, "-h", "-?", "-help", "--help") {
178
void process(JdepsTask task, String opt, String arg) {
179
task.options.help = true;
180
}
181
},
182
new Option(true, CommandOption.GENERATE_DOT_FILE) {
183
void process(JdepsTask task, String opt, String arg) throws BadArgs {
184
if (task.command != null) {
185
throw new BadArgs("err.command.set", task.command, opt);
186
}
187
task.command = task.genDotFile(Paths.get(arg));
188
}
189
},
190
new Option(false, "-s", "-summary") {
191
void process(JdepsTask task, String opt, String arg) {
192
task.options.showSummary = true;
193
}
194
},
195
new Option(false, "-v", "-verbose",
196
"-verbose:module",
197
"-verbose:package",
198
"-verbose:class") {
199
void process(JdepsTask task, String opt, String arg) throws BadArgs {
200
switch (opt) {
201
case "-v":
202
case "-verbose":
203
task.options.verbose = VERBOSE;
204
task.options.filterSameArchive = false;
205
task.options.filterSamePackage = false;
206
break;
207
case "-verbose:module":
208
task.options.verbose = MODULE;
209
break;
210
case "-verbose:package":
211
task.options.verbose = PACKAGE;
212
break;
213
case "-verbose:class":
214
task.options.verbose = CLASS;
215
break;
216
default:
217
throw new BadArgs("err.invalid.arg.for.option", opt);
218
}
219
}
220
},
221
new Option(false, "-apionly", "--api-only") {
222
void process(JdepsTask task, String opt, String arg) {
223
task.options.apiOnly = true;
224
}
225
},
226
227
new Option(false, "-jdkinternals", "--jdk-internals") {
228
void process(JdepsTask task, String opt, String arg) {
229
task.options.findJDKInternals = true;
230
if (task.options.includePattern == null) {
231
task.options.includePattern = Pattern.compile(".*");
232
}
233
}
234
},
235
236
// ---- paths option ----
237
new Option(true, "-cp", "-classpath", "--class-path") {
238
void process(JdepsTask task, String opt, String arg) {
239
task.options.classpath = arg;
240
}
241
},
242
new Option(true, "--module-path") {
243
void process(JdepsTask task, String opt, String arg) throws BadArgs {
244
task.options.modulePath = arg;
245
}
246
},
247
new Option(true, "--upgrade-module-path") {
248
void process(JdepsTask task, String opt, String arg) throws BadArgs {
249
task.options.upgradeModulePath = arg;
250
}
251
},
252
new Option(true, "--system") {
253
void process(JdepsTask task, String opt, String arg) throws BadArgs {
254
if (arg.equals("none")) {
255
task.options.systemModulePath = null;
256
} else {
257
Path path = Paths.get(arg);
258
if (Files.isRegularFile(path.resolve("lib").resolve("modules")))
259
task.options.systemModulePath = arg;
260
else
261
throw new BadArgs("err.invalid.path", arg);
262
}
263
}
264
},
265
new Option(true, "--add-modules") {
266
void process(JdepsTask task, String opt, String arg) throws BadArgs {
267
Set<String> mods = Set.of(arg.split(","));
268
task.options.addmods.addAll(mods);
269
}
270
},
271
new Option(true, "--multi-release") {
272
void process(JdepsTask task, String opt, String arg) throws BadArgs {
273
if (arg.equalsIgnoreCase("base")) {
274
task.options.multiRelease = JarFile.baseVersion();
275
} else {
276
try {
277
int v = Integer.parseInt(arg);
278
if (v < 9) {
279
throw new BadArgs("err.invalid.arg.for.option", arg);
280
}
281
} catch (NumberFormatException x) {
282
throw new BadArgs("err.invalid.arg.for.option", arg);
283
}
284
task.options.multiRelease = Runtime.Version.parse(arg);
285
}
286
}
287
},
288
new Option(false, "-q", "-quiet") {
289
void process(JdepsTask task, String opt, String arg) {
290
task.options.nowarning = true;
291
}
292
},
293
new Option(false, "-version", "--version") {
294
void process(JdepsTask task, String opt, String arg) {
295
task.options.version = true;
296
}
297
},
298
299
// ---- module-specific options ----
300
301
new Option(true, "-m", "--module") {
302
void process(JdepsTask task, String opt, String arg) throws BadArgs {
303
if (!task.options.rootModules.isEmpty()) {
304
throw new BadArgs("err.option.already.specified", opt);
305
}
306
task.options.rootModules.add(arg);
307
task.options.addmods.add(arg);
308
}
309
},
310
new Option(true, CommandOption.GENERATE_MODULE_INFO) {
311
void process(JdepsTask task, String opt, String arg) throws BadArgs {
312
if (task.command != null) {
313
throw new BadArgs("err.command.set", task.command, opt);
314
}
315
task.command = task.genModuleInfo(Paths.get(arg), false);
316
}
317
},
318
new Option(true, CommandOption.GENERATE_OPEN_MODULE) {
319
void process(JdepsTask task, String opt, String arg) throws BadArgs {
320
if (task.command != null) {
321
throw new BadArgs("err.command.set", task.command, opt);
322
}
323
task.command = task.genModuleInfo(Paths.get(arg), true);
324
}
325
},
326
new Option(true, CommandOption.CHECK_MODULES) {
327
void process(JdepsTask task, String opt, String arg) throws BadArgs {
328
if (task.command != null) {
329
throw new BadArgs("err.command.set", task.command, opt);
330
}
331
Set<String> mods = Set.of(arg.split(","));
332
task.options.addmods.addAll(mods);
333
task.command = task.checkModuleDeps(mods);
334
}
335
},
336
new Option(false, CommandOption.LIST_DEPS) {
337
void process(JdepsTask task, String opt, String arg) throws BadArgs {
338
if (task.command != null) {
339
throw new BadArgs("err.command.set", task.command, opt);
340
}
341
task.command = task.listModuleDeps(CommandOption.LIST_DEPS);
342
}
343
},
344
new Option(false, CommandOption.LIST_REDUCED_DEPS) {
345
void process(JdepsTask task, String opt, String arg) throws BadArgs {
346
if (task.command != null) {
347
throw new BadArgs("err.command.set", task.command, opt);
348
}
349
task.command = task.listModuleDeps(CommandOption.LIST_REDUCED_DEPS);
350
}
351
},
352
new Option(false, CommandOption.PRINT_MODULE_DEPS) {
353
void process(JdepsTask task, String opt, String arg) throws BadArgs {
354
if (task.command != null) {
355
throw new BadArgs("err.command.set", task.command, opt);
356
}
357
task.command = task.listModuleDeps(CommandOption.PRINT_MODULE_DEPS);
358
}
359
},
360
new Option(false, "--ignore-missing-deps") {
361
void process(JdepsTask task, String opt, String arg) {
362
task.options.ignoreMissingDeps = true;
363
}
364
},
365
366
// ---- Target filtering options ----
367
new Option(true, "-p", "-package", "--package") {
368
void process(JdepsTask task, String opt, String arg) {
369
task.options.packageNames.add(arg);
370
}
371
},
372
new Option(true, "-e", "-regex", "--regex") {
373
void process(JdepsTask task, String opt, String arg) {
374
task.options.regex = Pattern.compile(arg);
375
}
376
},
377
new Option(true, "--require") {
378
void process(JdepsTask task, String opt, String arg) {
379
task.options.requires.add(arg);
380
task.options.addmods.add(arg);
381
}
382
},
383
new Option(true, "-f", "-filter") {
384
void process(JdepsTask task, String opt, String arg) {
385
task.options.filterRegex = Pattern.compile(arg);
386
}
387
},
388
new Option(false, "-filter:package",
389
"-filter:archive", "-filter:module",
390
"-filter:none") {
391
void process(JdepsTask task, String opt, String arg) {
392
switch (opt) {
393
case "-filter:package":
394
task.options.filterSamePackage = true;
395
task.options.filterSameArchive = false;
396
break;
397
case "-filter:archive":
398
case "-filter:module":
399
task.options.filterSameArchive = true;
400
task.options.filterSamePackage = false;
401
break;
402
case "-filter:none":
403
task.options.filterSameArchive = false;
404
task.options.filterSamePackage = false;
405
break;
406
}
407
}
408
},
409
new Option(false, "--missing-deps") {
410
void process(JdepsTask task, String opt, String arg) {
411
task.options.findMissingDeps = true;
412
}
413
},
414
415
// ---- Source filtering options ----
416
new Option(true, "-include") {
417
void process(JdepsTask task, String opt, String arg) throws BadArgs {
418
task.options.includePattern = Pattern.compile(arg);
419
}
420
},
421
422
new Option(false, "-P", "-profile") {
423
void process(JdepsTask task, String opt, String arg) throws BadArgs {
424
task.options.showProfile = true;
425
}
426
},
427
428
new Option(false, "-R", "-recursive", "--recursive") {
429
void process(JdepsTask task, String opt, String arg) throws BadArgs {
430
task.options.recursive = Options.RECURSIVE;
431
// turn off filtering
432
task.options.filterSameArchive = false;
433
task.options.filterSamePackage = false;
434
}
435
},
436
new Option(false, "--no-recursive") {
437
void process(JdepsTask task, String opt, String arg) throws BadArgs {
438
task.options.recursive = Options.NO_RECURSIVE;
439
}
440
},
441
new Option(false, "-I", "--inverse") {
442
void process(JdepsTask task, String opt, String arg) {
443
task.options.inverse = true;
444
// equivalent to the inverse of compile-time view analysis
445
task.options.compileTimeView = true;
446
task.options.filterSamePackage = true;
447
task.options.filterSameArchive = true;
448
}
449
},
450
451
new Option(false, "--compile-time") {
452
void process(JdepsTask task, String opt, String arg) {
453
task.options.compileTimeView = true;
454
task.options.recursive = Options.RECURSIVE;
455
task.options.filterSamePackage = true;
456
task.options.filterSameArchive = true;
457
}
458
},
459
460
new HiddenOption(false, "-fullversion") {
461
void process(JdepsTask task, String opt, String arg) {
462
task.options.fullVersion = true;
463
}
464
},
465
new HiddenOption(false, "-showlabel") {
466
void process(JdepsTask task, String opt, String arg) {
467
task.options.showLabel = true;
468
}
469
},
470
new HiddenOption(false, "--hide-show-module") {
471
void process(JdepsTask task, String opt, String arg) {
472
task.options.showModule = false;
473
}
474
},
475
new HiddenOption(true, "-depth") {
476
void process(JdepsTask task, String opt, String arg) throws BadArgs {
477
try {
478
task.options.depth = Integer.parseInt(arg);
479
} catch (NumberFormatException e) {
480
throw new BadArgs("err.invalid.arg.for.option", opt);
481
}
482
}
483
},
484
};
485
486
private static final String PROGNAME = "jdeps";
487
private final Options options = new Options();
488
private final List<String> inputArgs = new ArrayList<>();
489
490
private Command command;
491
private PrintWriter log;
492
void setLog(PrintWriter out) {
493
log = out;
494
}
495
496
/**
497
* Result codes.
498
*/
499
static final int EXIT_OK = 0, // Completed with no errors.
500
EXIT_ERROR = 1, // Completed but reported errors.
501
EXIT_CMDERR = 2, // Bad command-line arguments
502
EXIT_SYSERR = 3, // System error or resource exhaustion.
503
EXIT_ABNORMAL = 4; // terminated abnormally
504
505
int run(String... args) {
506
if (log == null) {
507
log = new PrintWriter(System.out);
508
}
509
try {
510
handleOptions(args);
511
if (options.help) {
512
showHelp();
513
}
514
if (options.version || options.fullVersion) {
515
showVersion(options.fullVersion);
516
}
517
if (options.help || options.version || options.fullVersion) {
518
return EXIT_OK;
519
}
520
if (options.numFilters() > 1) {
521
reportError("err.invalid.filters");
522
return EXIT_CMDERR;
523
}
524
525
// default command to analyze dependences
526
if (command == null) {
527
command = analyzeDeps();
528
}
529
if (!command.checkOptions()) {
530
return EXIT_CMDERR;
531
}
532
533
boolean ok = run();
534
return ok ? EXIT_OK : EXIT_ERROR;
535
536
} catch (BadArgs|UncheckedBadArgs e) {
537
reportError(e.getKey(), e.getArgs());
538
if (e.showUsage()) {
539
log.println(getMessage("main.usage.summary", PROGNAME));
540
}
541
return EXIT_CMDERR;
542
} catch (ResolutionException e) {
543
reportError("err.exception.message", e.getMessage());
544
return EXIT_CMDERR;
545
} catch (IOException e) {
546
e.printStackTrace();
547
return EXIT_CMDERR;
548
} catch (MultiReleaseException e) {
549
reportError(e.getKey(), e.getParams());
550
return EXIT_CMDERR; // could be EXIT_ABNORMAL sometimes
551
} finally {
552
log.flush();
553
}
554
}
555
556
boolean run() throws IOException {
557
try (JdepsConfiguration config = buildConfig()) {
558
if (!options.nowarning) {
559
// detect split packages
560
config.splitPackages().entrySet()
561
.stream()
562
.sorted(Map.Entry.comparingByKey())
563
.forEach(e -> warning("warn.split.package",
564
e.getKey(),
565
e.getValue().stream().collect(joining(" "))));
566
}
567
568
// check if any module specified in --add-modules, --require, and -m is missing
569
options.addmods.stream()
570
.filter(mn -> !JdepsConfiguration.isToken(mn))
571
.forEach(mn -> config.findModule(mn).orElseThrow(() ->
572
new UncheckedBadArgs(new BadArgs("err.module.not.found", mn))));
573
574
return command.run(config);
575
}
576
}
577
578
private JdepsConfiguration buildConfig() throws IOException {
579
JdepsConfiguration.Builder builder =
580
new JdepsConfiguration.Builder(options.systemModulePath);
581
582
builder.upgradeModulePath(options.upgradeModulePath)
583
.appModulePath(options.modulePath)
584
.addmods(options.addmods)
585
.addmods(command.addModules());
586
587
if (options.classpath != null)
588
builder.addClassPath(options.classpath);
589
590
if (options.multiRelease != null)
591
builder.multiRelease(options.multiRelease);
592
593
// build the root set of archives to be analyzed
594
for (String s : inputArgs) {
595
Path p = Paths.get(s);
596
if (Files.exists(p)) {
597
builder.addRoot(p);
598
} else {
599
warning("warn.invalid.arg", s);
600
}
601
}
602
603
return builder.build();
604
}
605
606
// ---- factory methods to create a Command
607
608
private AnalyzeDeps analyzeDeps() throws BadArgs {
609
return options.inverse ? new InverseAnalyzeDeps()
610
: new AnalyzeDeps();
611
}
612
613
private GenDotFile genDotFile(Path dir) throws BadArgs {
614
if (Files.exists(dir) && (!Files.isDirectory(dir) || !Files.isWritable(dir))) {
615
throw new BadArgs("err.invalid.path", dir.toString());
616
}
617
return new GenDotFile(dir);
618
}
619
620
private GenModuleInfo genModuleInfo(Path dir, boolean openModule) throws BadArgs {
621
if (Files.exists(dir) && (!Files.isDirectory(dir) || !Files.isWritable(dir))) {
622
throw new BadArgs("err.invalid.path", dir.toString());
623
}
624
return new GenModuleInfo(dir, openModule);
625
}
626
627
private ListModuleDeps listModuleDeps(CommandOption option) throws BadArgs {
628
// do transitive dependence analysis unless --no-recursive is set
629
if (options.recursive != Options.NO_RECURSIVE) {
630
options.recursive = Options.RECURSIVE;
631
}
632
// no need to record the dependences on the same archive or same package
633
options.filterSameArchive = true;
634
options.filterSamePackage = true;
635
switch (option) {
636
case LIST_DEPS:
637
return new ListModuleDeps(option, true, false);
638
case LIST_REDUCED_DEPS:
639
return new ListModuleDeps(option, true, true);
640
case PRINT_MODULE_DEPS:
641
return new ListModuleDeps(option, false, true, ",");
642
default:
643
throw new IllegalArgumentException(option.toString());
644
}
645
}
646
647
private CheckModuleDeps checkModuleDeps(Set<String> mods) throws BadArgs {
648
return new CheckModuleDeps(mods);
649
}
650
651
abstract class Command {
652
final CommandOption option;
653
protected Command(CommandOption option) {
654
this.option = option;
655
}
656
657
/**
658
* Returns true if the command-line options are all valid;
659
* otherwise, returns false.
660
*/
661
abstract boolean checkOptions();
662
663
/**
664
* Do analysis
665
*/
666
abstract boolean run(JdepsConfiguration config) throws IOException;
667
668
/**
669
* Includes all modules on system module path and application module path
670
*
671
* When a named module is analyzed, it will analyze the dependences
672
* only. The method should be overridden when this command should
673
* analyze all modules instead.
674
*/
675
Set<String> addModules() {
676
return Set.of();
677
}
678
679
@Override
680
public String toString() {
681
return option.toString();
682
}
683
}
684
685
686
/**
687
* Analyze dependences
688
*/
689
class AnalyzeDeps extends Command {
690
JdepsWriter writer;
691
AnalyzeDeps() {
692
this(CommandOption.ANALYZE_DEPS);
693
}
694
695
AnalyzeDeps(CommandOption option) {
696
super(option);
697
}
698
699
@Override
700
boolean checkOptions() {
701
if (options.findJDKInternals || options.findMissingDeps) {
702
// cannot set any filter, -verbose and -summary option
703
if (options.showSummary || options.verbose != null) {
704
reportError("err.invalid.options", "-summary or -verbose",
705
options.findJDKInternals ? "-jdkinternals" : "--missing-deps");
706
return false;
707
}
708
if (options.hasFilter()) {
709
reportError("err.invalid.options", "--package, --regex, --require",
710
options.findJDKInternals ? "-jdkinternals" : "--missing-deps");
711
return false;
712
}
713
}
714
if (options.showSummary) {
715
// -summary cannot use with -verbose option
716
if (options.verbose != null) {
717
reportError("err.invalid.options", "-v, -verbose", "-s, -summary");
718
return false;
719
}
720
}
721
722
if (!inputArgs.isEmpty() && !options.rootModules.isEmpty()) {
723
reportError("err.invalid.arg.for.option", "-m");
724
}
725
if (inputArgs.isEmpty() && !options.hasSourcePath()) {
726
showHelp();
727
return false;
728
}
729
return true;
730
}
731
732
/*
733
* Default is to show package-level dependencies
734
*/
735
Type getAnalyzerType() {
736
if (options.showSummary)
737
return Type.SUMMARY;
738
739
if (options.findJDKInternals || options.findMissingDeps)
740
return Type.CLASS;
741
742
// default to package-level verbose
743
return options.verbose != null ? options.verbose : PACKAGE;
744
}
745
746
@Override
747
boolean run(JdepsConfiguration config) throws IOException {
748
Type type = getAnalyzerType();
749
// default to package-level verbose
750
JdepsWriter writer = new SimpleWriter(log,
751
type,
752
options.showProfile,
753
options.showModule);
754
755
return run(config, writer, type);
756
}
757
758
boolean run(JdepsConfiguration config, JdepsWriter writer, Type type)
759
throws IOException
760
{
761
// analyze the dependencies
762
DepsAnalyzer analyzer = new DepsAnalyzer(config,
763
dependencyFilter(config),
764
writer,
765
type,
766
options.apiOnly);
767
768
boolean ok = analyzer.run(options.compileTimeView, options.depth());
769
770
// print skipped entries, if any
771
if (!options.nowarning) {
772
analyzer.archives()
773
.forEach(archive -> archive.reader()
774
.skippedEntries().stream()
775
.forEach(name -> warning("warn.skipped.entry", name)));
776
}
777
778
if (options.findJDKInternals && !options.nowarning) {
779
Map<String, String> jdkInternals = new TreeMap<>();
780
Set<String> deps = analyzer.dependences();
781
// find the ones with replacement
782
deps.forEach(cn -> replacementFor(cn).ifPresent(
783
repl -> jdkInternals.put(cn, repl))
784
);
785
786
if (!deps.isEmpty()) {
787
log.println();
788
warning("warn.replace.useJDKInternals", getMessage("jdeps.wiki.url"));
789
}
790
791
if (!jdkInternals.isEmpty()) {
792
log.println();
793
String internalApiTitle = getMessage("internal.api.column.header");
794
String replacementApiTitle = getMessage("public.api.replacement.column.header");
795
log.format("%-40s %s%n", internalApiTitle, replacementApiTitle);
796
log.format("%-40s %s%n",
797
internalApiTitle.replaceAll(".", "-"),
798
replacementApiTitle.replaceAll(".", "-"));
799
jdkInternals.entrySet().stream()
800
.forEach(e -> {
801
String key = e.getKey();
802
String[] lines = e.getValue().split("\\n");
803
for (String s : lines) {
804
log.format("%-40s %s%n", key, s);
805
key = "";
806
}
807
});
808
}
809
}
810
return ok;
811
}
812
}
813
814
815
class InverseAnalyzeDeps extends AnalyzeDeps {
816
InverseAnalyzeDeps() {
817
}
818
819
@Override
820
boolean checkOptions() {
821
if (options.recursive != -1 || options.depth != -1) {
822
reportError("err.invalid.options", "--recursive and --no-recursive", "--inverse");
823
return false;
824
}
825
826
if (options.numFilters() == 0) {
827
reportError("err.filter.not.specified");
828
return false;
829
}
830
831
if (!super.checkOptions()) {
832
return false;
833
}
834
835
return true;
836
}
837
838
@Override
839
boolean run(JdepsConfiguration config) throws IOException {
840
Type type = getAnalyzerType();
841
842
InverseDepsAnalyzer analyzer =
843
new InverseDepsAnalyzer(config,
844
dependencyFilter(config),
845
writer,
846
type,
847
options.apiOnly);
848
boolean ok = analyzer.run();
849
850
log.println();
851
if (!options.requires.isEmpty())
852
log.println(getMessage("inverse.transitive.dependencies.on",
853
options.requires));
854
else
855
log.println(getMessage("inverse.transitive.dependencies.matching",
856
options.regex != null
857
? options.regex.toString()
858
: "packages " + options.packageNames));
859
860
analyzer.inverseDependences()
861
.stream()
862
.sorted(comparator())
863
.map(this::toInversePath)
864
.forEach(log::println);
865
return ok;
866
}
867
868
private String toInversePath(Deque<Archive> path) {
869
return path.stream()
870
.map(Archive::getName)
871
.collect(joining(" <- "));
872
}
873
874
/*
875
* Returns a comparator for sorting the inversed path, grouped by
876
* the first module name, then the shortest path and then sort by
877
* the module names of each path
878
*/
879
private Comparator<Deque<Archive>> comparator() {
880
return Comparator.<Deque<Archive>, String>
881
comparing(deque -> deque.peekFirst().getName())
882
.thenComparingInt(Deque::size)
883
.thenComparing(this::toInversePath);
884
}
885
886
/*
887
* Returns true if --require is specified so that all modules are
888
* analyzed to find all modules that depend on the modules specified in the
889
* --require option directly and indirectly
890
*/
891
Set<String> addModules() {
892
return options.requires.size() > 0 ? Set.of("ALL-SYSTEM") : Set.of();
893
}
894
}
895
896
897
class GenModuleInfo extends Command {
898
final Path dir;
899
final boolean openModule;
900
GenModuleInfo(Path dir, boolean openModule) {
901
super(CommandOption.GENERATE_MODULE_INFO);
902
this.dir = dir;
903
this.openModule = openModule;
904
}
905
906
@Override
907
boolean checkOptions() {
908
if (options.classpath != null) {
909
reportError("err.invalid.options", "-classpath",
910
option);
911
return false;
912
}
913
if (options.hasFilter()) {
914
reportError("err.invalid.options", "--package, --regex, --require",
915
option);
916
return false;
917
}
918
if (!options.rootModules.isEmpty()) {
919
reportError("err.invalid.options", "-m or --module",
920
option);
921
return false;
922
}
923
return true;
924
}
925
926
@Override
927
boolean run(JdepsConfiguration config) throws IOException {
928
// check if any JAR file contains unnamed package
929
for (String arg : inputArgs) {
930
try (ClassFileReader reader = ClassFileReader.newInstance(Paths.get(arg), config.getVersion())) {
931
Optional<String> classInUnnamedPackage =
932
reader.entries().stream()
933
.filter(n -> n.endsWith(".class"))
934
.filter(cn -> toPackageName(cn).isEmpty())
935
.findFirst();
936
937
if (classInUnnamedPackage.isPresent()) {
938
if (classInUnnamedPackage.get().equals("module-info.class")) {
939
reportError("err.genmoduleinfo.not.jarfile", arg);
940
} else {
941
reportError("err.genmoduleinfo.unnamed.package", arg);
942
}
943
return false;
944
}
945
}
946
}
947
948
ModuleInfoBuilder builder
949
= new ModuleInfoBuilder(config, inputArgs, dir, openModule);
950
boolean ok = builder.run(options.ignoreMissingDeps, log, options.nowarning);
951
if (!ok) {
952
reportError("err.missing.dependences");
953
log.println();
954
builder.visitMissingDeps(new SimpleDepVisitor());
955
}
956
return ok;
957
}
958
959
private String toPackageName(String name) {
960
int i = name.lastIndexOf('/');
961
return i > 0 ? name.replace('/', '.').substring(0, i) : "";
962
}
963
}
964
965
class CheckModuleDeps extends Command {
966
final Set<String> modules;
967
CheckModuleDeps(Set<String> mods) {
968
super(CommandOption.CHECK_MODULES);
969
this.modules = mods;
970
}
971
972
@Override
973
boolean checkOptions() {
974
if (!inputArgs.isEmpty()) {
975
reportError("err.invalid.options", inputArgs, "--check");
976
return false;
977
}
978
return true;
979
}
980
981
@Override
982
boolean run(JdepsConfiguration config) throws IOException {
983
if (!config.initialArchives().isEmpty()) {
984
String list = config.initialArchives().stream()
985
.map(Archive::getPathName).collect(joining(" "));
986
throw new UncheckedBadArgs(new BadArgs("err.invalid.options",
987
list, "--check"));
988
}
989
return new ModuleAnalyzer(config, log, modules).run(options.ignoreMissingDeps);
990
}
991
992
/*
993
* Returns true to analyze all modules
994
*/
995
Set<String> addModules() {
996
return Set.of("ALL-SYSTEM", "ALL-MODULE-PATH");
997
}
998
}
999
1000
class ListModuleDeps extends Command {
1001
final boolean jdkinternals;
1002
final boolean reduced;
1003
final String separator;
1004
ListModuleDeps(CommandOption option, boolean jdkinternals, boolean reduced) {
1005
this(option, jdkinternals, reduced, System.getProperty("line.separator"));
1006
}
1007
ListModuleDeps(CommandOption option, boolean jdkinternals, boolean reduced, String sep) {
1008
super(option);
1009
this.jdkinternals = jdkinternals;
1010
this.reduced = reduced;
1011
this.separator = sep;
1012
}
1013
1014
@Override
1015
boolean checkOptions() {
1016
if (options.showSummary || options.verbose != null) {
1017
reportError("err.invalid.options", "-summary or -verbose", option);
1018
return false;
1019
}
1020
if (options.findJDKInternals) {
1021
reportError("err.invalid.options", "-jdkinternals", option);
1022
return false;
1023
}
1024
if (options.findMissingDeps) {
1025
reportError("err.invalid.options", "--missing-deps", option);
1026
return false;
1027
}
1028
1029
if (!inputArgs.isEmpty() && !options.rootModules.isEmpty()) {
1030
reportError("err.invalid.arg.for.option", "-m");
1031
}
1032
if (inputArgs.isEmpty() && !options.hasSourcePath()) {
1033
showHelp();
1034
return false;
1035
}
1036
return true;
1037
}
1038
1039
@Override
1040
boolean run(JdepsConfiguration config) throws IOException {
1041
ModuleExportsAnalyzer analyzer = new ModuleExportsAnalyzer(config,
1042
dependencyFilter(config),
1043
jdkinternals,
1044
reduced,
1045
log,
1046
separator);
1047
boolean ok = analyzer.run(options.depth(), options.ignoreMissingDeps);
1048
if (!ok) {
1049
reportError("err.missing.dependences");
1050
log.println();
1051
analyzer.visitMissingDeps(new SimpleDepVisitor());
1052
}
1053
return ok;
1054
}
1055
}
1056
1057
class GenDotFile extends AnalyzeDeps {
1058
final Path dotOutputDir;
1059
GenDotFile(Path dotOutputDir) {
1060
super(CommandOption.GENERATE_DOT_FILE);
1061
1062
this.dotOutputDir = dotOutputDir;
1063
}
1064
1065
@Override
1066
boolean run(JdepsConfiguration config) throws IOException {
1067
if ((options.showSummary || options.verbose == MODULE) &&
1068
!options.addmods.isEmpty() && inputArgs.isEmpty()) {
1069
// generate dot graph from the resolved graph from module
1070
// resolution. No class dependency analysis is performed.
1071
return new ModuleDotGraph(config, options.apiOnly)
1072
.genDotFiles(dotOutputDir);
1073
}
1074
1075
Type type = getAnalyzerType();
1076
JdepsWriter writer = new DotFileWriter(dotOutputDir,
1077
type,
1078
options.showProfile,
1079
options.showModule,
1080
options.showLabel);
1081
return run(config, writer, type);
1082
}
1083
}
1084
1085
class SimpleDepVisitor implements Analyzer.Visitor {
1086
private Archive source;
1087
@Override
1088
public void visitDependence(String origin, Archive originArchive, String target, Archive targetArchive) {
1089
if (source != originArchive) {
1090
source = originArchive;
1091
log.format("%s%n", originArchive);
1092
}
1093
log.format(" %-50s -> %-50s %s%n", origin, target, targetArchive.getName());
1094
}
1095
}
1096
1097
/**
1098
* Returns a filter used during dependency analysis
1099
*/
1100
private JdepsFilter dependencyFilter(JdepsConfiguration config) {
1101
// Filter specified by -filter, -package, -regex, and --require options
1102
JdepsFilter.Builder builder = new JdepsFilter.Builder();
1103
1104
// source filters
1105
builder.includePattern(options.includePattern);
1106
1107
// target filters
1108
builder.filter(options.filterSamePackage, options.filterSameArchive);
1109
builder.findJDKInternals(options.findJDKInternals);
1110
builder.findMissingDeps(options.findMissingDeps);
1111
1112
// --require
1113
if (!options.requires.isEmpty()) {
1114
options.requires.stream()
1115
.forEach(mn -> {
1116
Module m = config.findModule(mn).get();
1117
builder.requires(mn, m.packages());
1118
});
1119
}
1120
// -regex
1121
if (options.regex != null)
1122
builder.regex(options.regex);
1123
// -package
1124
if (!options.packageNames.isEmpty())
1125
builder.packages(options.packageNames);
1126
// -filter
1127
if (options.filterRegex != null)
1128
builder.filter(options.filterRegex);
1129
1130
return builder.build();
1131
}
1132
1133
public void handleOptions(String[] args) throws BadArgs {
1134
// process options
1135
for (int i=0; i < args.length; i++) {
1136
if (args[i].charAt(0) == '-') {
1137
String name = args[i];
1138
Option option = getOption(name);
1139
String param = null;
1140
if (option.hasArg) {
1141
if (name.startsWith("-") && name.indexOf('=') > 0) {
1142
param = name.substring(name.indexOf('=') + 1, name.length());
1143
} else if (i + 1 < args.length) {
1144
param = args[++i];
1145
}
1146
if (param == null || param.isEmpty() || param.charAt(0) == '-') {
1147
throw new BadArgs("err.missing.arg", name).showUsage(true);
1148
}
1149
}
1150
option.process(this, name, param);
1151
if (option.ignoreRest()) {
1152
i = args.length;
1153
}
1154
} else {
1155
// process rest of the input arguments
1156
for (; i < args.length; i++) {
1157
String name = args[i];
1158
if (name.charAt(0) == '-') {
1159
throw new BadArgs("err.option.after.class", name).showUsage(true);
1160
}
1161
inputArgs.add(name);
1162
}
1163
}
1164
}
1165
}
1166
1167
private Option getOption(String name) throws BadArgs {
1168
for (Option o : recognizedOptions) {
1169
if (o.matches(name)) {
1170
return o;
1171
}
1172
}
1173
throw new BadArgs("err.unknown.option", name).showUsage(true);
1174
}
1175
1176
private void reportError(String key, Object... args) {
1177
log.println(getMessage("error.prefix") + " " + getMessage(key, args));
1178
}
1179
1180
void warning(String key, Object... args) {
1181
log.println(getMessage("warn.prefix") + " " + getMessage(key, args));
1182
}
1183
1184
private void showHelp() {
1185
log.println(getMessage("main.usage", PROGNAME));
1186
for (Option o : recognizedOptions) {
1187
String name = o.aliases[0].substring(1); // there must always be at least one name
1188
name = name.charAt(0) == '-' ? name.substring(1) : name;
1189
if (o.isHidden() || name.startsWith("filter:")) {
1190
continue;
1191
}
1192
log.println(getMessage("main.opt." + name));
1193
}
1194
}
1195
1196
private void showVersion(boolean full) {
1197
log.println(version(full ? "full" : "release"));
1198
}
1199
1200
private String version(String key) {
1201
// key=version: mm.nn.oo[-milestone]
1202
// key=full: mm.mm.oo[-milestone]-build
1203
try {
1204
return ResourceBundleHelper.getVersion(key);
1205
} catch (MissingResourceException e) {
1206
return getMessage("version.unknown", System.getProperty("java.version"));
1207
}
1208
}
1209
1210
static String getMessage(String key, Object... args) {
1211
try {
1212
return MessageFormat.format(ResourceBundleHelper.getMessage(key), args);
1213
} catch (MissingResourceException e) {
1214
throw new InternalError("Missing message: " + key);
1215
}
1216
}
1217
1218
private static class Options {
1219
static final int NO_RECURSIVE = 0;
1220
static final int RECURSIVE = 1;
1221
boolean help;
1222
boolean version;
1223
boolean fullVersion;
1224
boolean showProfile;
1225
boolean showModule = true;
1226
boolean showSummary;
1227
boolean apiOnly;
1228
boolean showLabel;
1229
boolean findJDKInternals;
1230
boolean findMissingDeps;
1231
boolean ignoreMissingDeps;
1232
boolean nowarning = false;
1233
Analyzer.Type verbose;
1234
// default filter references from same package
1235
boolean filterSamePackage = true;
1236
boolean filterSameArchive = false;
1237
Pattern filterRegex;
1238
String classpath;
1239
int recursive = -1; // 0: --no-recursive, 1: --recursive
1240
int depth = -1;
1241
Set<String> requires = new HashSet<>();
1242
Set<String> packageNames = new HashSet<>();
1243
Pattern regex; // apply to the dependences
1244
Pattern includePattern;
1245
boolean inverse = false;
1246
boolean compileTimeView = false;
1247
String systemModulePath = System.getProperty("java.home");
1248
String upgradeModulePath;
1249
String modulePath;
1250
Set<String> rootModules = new HashSet<>();
1251
Set<String> addmods = new HashSet<>();
1252
Runtime.Version multiRelease;
1253
1254
boolean hasSourcePath() {
1255
return !addmods.isEmpty() || includePattern != null;
1256
}
1257
1258
boolean hasFilter() {
1259
return numFilters() > 0;
1260
}
1261
1262
int numFilters() {
1263
int count = 0;
1264
if (requires.size() > 0) count++;
1265
if (regex != null) count++;
1266
if (packageNames.size() > 0) count++;
1267
return count;
1268
}
1269
1270
int depth() {
1271
// ignore -depth if --no-recursive is set
1272
if (recursive == NO_RECURSIVE)
1273
return 1;
1274
1275
// depth == 0 if recursive
1276
if (recursive == RECURSIVE && depth == -1)
1277
return 0;
1278
1279
// default depth is 1 unless specified via -depth option
1280
return depth == -1 ? 1 : depth;
1281
}
1282
}
1283
1284
private static class ResourceBundleHelper {
1285
static final String LS = System.lineSeparator();
1286
static final ResourceBundle versionRB;
1287
static final ResourceBundle bundle;
1288
static final ResourceBundle jdkinternals;
1289
1290
static {
1291
Locale locale = Locale.getDefault();
1292
try {
1293
bundle = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdeps", locale);
1294
} catch (MissingResourceException e) {
1295
throw new InternalError("Cannot find jdeps resource bundle for locale " + locale);
1296
}
1297
try {
1298
versionRB = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.version");
1299
} catch (MissingResourceException e) {
1300
throw new InternalError("version.resource.missing");
1301
}
1302
try {
1303
jdkinternals = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdkinternals");
1304
} catch (MissingResourceException e) {
1305
throw new InternalError("Cannot find jdkinternals resource bundle");
1306
}
1307
}
1308
1309
static String getMessage(String key) {
1310
return bundle.getString(key).replace("\n", LS);
1311
}
1312
1313
static String getVersion(String key) {
1314
if (ResourceBundleHelper.versionRB == null) {
1315
return System.getProperty("java.version");
1316
}
1317
return versionRB.getString(key).replace("\n", LS);
1318
}
1319
1320
static String getSuggestedReplacement(String key) {
1321
return ResourceBundleHelper.jdkinternals.getString(key).replace("\n", LS);
1322
}
1323
}
1324
1325
/**
1326
* Returns the recommended replacement API for the given classname;
1327
* or return null if replacement API is not known.
1328
*/
1329
private Optional<String> replacementFor(String cn) {
1330
String name = cn;
1331
String value = null;
1332
while (value == null && name != null) {
1333
try {
1334
value = ResourceBundleHelper.getSuggestedReplacement(name);
1335
} catch (MissingResourceException e) {
1336
// go up one subpackage level
1337
int i = name.lastIndexOf('.');
1338
name = i > 0 ? name.substring(0, i) : null;
1339
}
1340
}
1341
return Optional.ofNullable(value);
1342
}
1343
}
1344
1345