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/ModuleInfoBuilder.java
41161 views
1
/*
2
* Copyright (c) 2015, 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
package com.sun.tools.jdeps;
26
27
import static com.sun.tools.jdeps.JdepsTask.*;
28
import static com.sun.tools.jdeps.Analyzer.*;
29
import static com.sun.tools.jdeps.JdepsFilter.DEFAULT_FILTER;
30
31
import java.io.IOException;
32
import java.io.PrintWriter;
33
import java.io.UncheckedIOException;
34
import java.lang.module.ModuleDescriptor;
35
import java.lang.module.ModuleDescriptor.Exports;
36
import java.lang.module.ModuleDescriptor.Provides;
37
import java.lang.module.ModuleDescriptor.Requires;
38
import java.lang.module.ModuleFinder;
39
import java.nio.file.Files;
40
import java.nio.file.Path;
41
import java.nio.file.Paths;
42
import java.util.Collections;
43
import java.util.Comparator;
44
import java.util.HashMap;
45
import java.util.List;
46
import java.util.Locale;
47
import java.util.Map;
48
import java.util.Optional;
49
import java.util.Set;
50
import java.util.function.Function;
51
import java.util.stream.Collectors;
52
import java.util.stream.Stream;
53
import static java.util.stream.Collectors.*;
54
55
56
public class ModuleInfoBuilder {
57
final JdepsConfiguration configuration;
58
final Path outputdir;
59
final boolean open;
60
61
final DependencyFinder dependencyFinder;
62
final Analyzer analyzer;
63
64
// an input JAR file (loaded as an automatic module for analysis)
65
// maps to a normal module to generate module-info.java
66
final Map<Module, Module> automaticToNormalModule;
67
public ModuleInfoBuilder(JdepsConfiguration configuration,
68
List<String> args,
69
Path outputdir,
70
boolean open) {
71
this.configuration = configuration;
72
this.outputdir = outputdir;
73
this.open = open;
74
75
this.dependencyFinder = new DependencyFinder(configuration, DEFAULT_FILTER);
76
this.analyzer = new Analyzer(configuration, Type.CLASS, DEFAULT_FILTER);
77
78
// add targets to modulepath if it has module-info.class
79
List<Path> paths = args.stream()
80
.map(fn -> Paths.get(fn))
81
.toList();
82
83
// automatic module to convert to normal module
84
this.automaticToNormalModule = ModuleFinder.of(paths.toArray(new Path[0]))
85
.findAll().stream()
86
.map(configuration::toModule)
87
.collect(toMap(Function.identity(), Function.identity()));
88
89
Optional<Module> om = automaticToNormalModule.keySet().stream()
90
.filter(m -> !m.descriptor().isAutomatic())
91
.findAny();
92
if (om.isPresent()) {
93
throw new UncheckedBadArgs(new BadArgs("err.genmoduleinfo.not.jarfile",
94
om.get().getPathName()));
95
}
96
if (automaticToNormalModule.isEmpty()) {
97
throw new UncheckedBadArgs(new BadArgs("err.invalid.path", args));
98
}
99
}
100
101
public boolean run(boolean ignoreMissingDeps, PrintWriter log, boolean quiet) throws IOException {
102
try {
103
// pass 1: find API dependencies
104
Map<Archive, Set<Archive>> requiresTransitive = computeRequiresTransitive();
105
106
// pass 2: analyze all class dependences
107
dependencyFinder.parse(automaticModules().stream());
108
109
analyzer.run(automaticModules(), dependencyFinder.locationToArchive());
110
111
for (Module m : automaticModules()) {
112
Set<Archive> apiDeps = requiresTransitive.containsKey(m)
113
? requiresTransitive.get(m)
114
: Collections.emptySet();
115
116
// if this is a multi-release JAR, write to versions/$VERSION/module-info.java
117
Runtime.Version version = configuration.getVersion();
118
Path dir = version != null
119
? outputdir.resolve(m.name())
120
.resolve("versions")
121
.resolve(String.valueOf(version.feature()))
122
: outputdir.resolve(m.name());
123
Path file = dir.resolve("module-info.java");
124
125
// computes requires and requires transitive
126
Module normalModule = toNormalModule(m, apiDeps, ignoreMissingDeps);
127
if (normalModule != null) {
128
automaticToNormalModule.put(m, normalModule);
129
130
// generate module-info.java
131
if (!quiet) {
132
if (ignoreMissingDeps && analyzer.requires(m).anyMatch(Analyzer::notFound)) {
133
log.format("Warning: --ignore-missing-deps specified. Missing dependencies from %s are ignored%n",
134
m.name());
135
}
136
log.format("writing to %s%n", file);
137
}
138
writeModuleInfo(file, normalModule.descriptor());
139
} else {
140
// find missing dependences
141
return false;
142
}
143
}
144
145
} finally {
146
dependencyFinder.shutdown();
147
}
148
return true;
149
}
150
151
private Module toNormalModule(Module module, Set<Archive> requiresTransitive, boolean ignoreMissingDeps)
152
throws IOException
153
{
154
// done analysis
155
module.close();
156
157
if (!ignoreMissingDeps && analyzer.requires(module).anyMatch(Analyzer::notFound)) {
158
// missing dependencies
159
return null;
160
}
161
162
Map<String, Boolean> requires = new HashMap<>();
163
requiresTransitive.stream()
164
.filter(a -> !(ignoreMissingDeps && Analyzer.notFound(a)))
165
.map(Archive::getModule)
166
.forEach(m -> requires.put(m.name(), Boolean.TRUE));
167
168
analyzer.requires(module)
169
.filter(a -> !(ignoreMissingDeps && Analyzer.notFound(a)))
170
.map(Archive::getModule)
171
.forEach(d -> requires.putIfAbsent(d.name(), Boolean.FALSE));
172
173
return module.toNormalModule(requires);
174
}
175
176
/**
177
* Returns the stream of resulting modules
178
*/
179
Stream<Module> modules() {
180
return automaticToNormalModule.values().stream();
181
}
182
183
/**
184
* Returns the stream of resulting ModuleDescriptors
185
*/
186
public Stream<ModuleDescriptor> descriptors() {
187
return automaticToNormalModule.entrySet().stream()
188
.map(Map.Entry::getValue)
189
.map(Module::descriptor);
190
}
191
192
void visitMissingDeps(Analyzer.Visitor visitor) {
193
automaticModules().stream()
194
.filter(m -> analyzer.requires(m).anyMatch(Analyzer::notFound))
195
.forEach(m -> {
196
analyzer.visitDependences(m, visitor, Analyzer.Type.VERBOSE, Analyzer::notFound);
197
});
198
}
199
200
void writeModuleInfo(Path file, ModuleDescriptor md) {
201
try {
202
Files.createDirectories(file.getParent());
203
try (PrintWriter pw = new PrintWriter(Files.newOutputStream(file))) {
204
printModuleInfo(pw, md);
205
}
206
} catch (IOException e) {
207
throw new UncheckedIOException(e);
208
}
209
}
210
211
private void printModuleInfo(PrintWriter writer, ModuleDescriptor md) {
212
writer.format("%smodule %s {%n", open ? "open " : "", md.name());
213
214
Map<String, Module> modules = configuration.getModules();
215
216
// first print requires
217
Set<Requires> reqs = md.requires().stream()
218
.filter(req -> !req.name().equals("java.base") && req.modifiers().isEmpty())
219
.collect(Collectors.toSet());
220
reqs.stream()
221
.sorted(Comparator.comparing(Requires::name))
222
.forEach(req -> writer.format(" requires %s;%n",
223
toString(req.modifiers(), req.name())));
224
if (!reqs.isEmpty()) {
225
writer.println();
226
}
227
228
// requires transitive
229
reqs = md.requires().stream()
230
.filter(req -> !req.name().equals("java.base") && !req.modifiers().isEmpty())
231
.collect(Collectors.toSet());
232
reqs.stream()
233
.sorted(Comparator.comparing(Requires::name))
234
.forEach(req -> writer.format(" requires %s;%n",
235
toString(req.modifiers(), req.name())));
236
if (!reqs.isEmpty()) {
237
writer.println();
238
}
239
240
if (!open) {
241
md.exports().stream()
242
.peek(exp -> {
243
if (exp.isQualified())
244
throw new InternalError(md.name() + " qualified exports: " + exp);
245
})
246
.sorted(Comparator.comparing(Exports::source))
247
.forEach(exp -> writer.format(" exports %s;%n", exp.source()));
248
249
if (!md.exports().isEmpty()) {
250
writer.println();
251
}
252
}
253
254
md.provides().stream()
255
.sorted(Comparator.comparing(Provides::service))
256
.map(p -> p.providers().stream()
257
.map(impl -> " " + impl.replace('$', '.'))
258
.collect(joining(",\n",
259
String.format(" provides %s with%n",
260
p.service().replace('$', '.')),
261
";")))
262
.forEach(writer::println);
263
264
if (!md.provides().isEmpty()) {
265
writer.println();
266
}
267
writer.println("}");
268
}
269
270
private Set<Module> automaticModules() {
271
return automaticToNormalModule.keySet();
272
}
273
274
/**
275
* Returns a string containing the given set of modifiers and label.
276
*/
277
private static <M> String toString(Set<M> mods, String what) {
278
return (Stream.concat(mods.stream().map(e -> e.toString().toLowerCase(Locale.US)),
279
Stream.of(what)))
280
.collect(Collectors.joining(" "));
281
}
282
283
/**
284
* Compute 'requires transitive' dependences by analyzing API dependencies
285
*/
286
private Map<Archive, Set<Archive>> computeRequiresTransitive()
287
throws IOException
288
{
289
// parse the input modules
290
dependencyFinder.parseExportedAPIs(automaticModules().stream());
291
292
return dependencyFinder.dependences();
293
}
294
}
295
296