1 /* |
|
2 * Copyright (c) 2019, 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 jdk.jpackage.internal; |
|
26 |
|
27 import java.io.IOException; |
|
28 import java.nio.file.Files; |
|
29 import java.nio.file.Path; |
|
30 import java.util.*; |
|
31 import java.util.function.Predicate; |
|
32 import java.util.regex.Matcher; |
|
33 import java.util.regex.Pattern; |
|
34 import java.util.stream.Collectors; |
|
35 import java.util.stream.Stream; |
|
36 |
|
37 /** |
|
38 * Builds list of packages providing dynamic libraries for the given set of files. |
|
39 */ |
|
40 final public class LibProvidersLookup { |
|
41 static boolean supported() { |
|
42 return (new ToolValidator(TOOL_LDD).validate() == null); |
|
43 } |
|
44 |
|
45 public LibProvidersLookup() { |
|
46 } |
|
47 |
|
48 LibProvidersLookup setPackageLookup(PackageLookup v) { |
|
49 packageLookup = v; |
|
50 return this; |
|
51 } |
|
52 |
|
53 List<String> execute(Path root) throws IOException { |
|
54 // Get the list of files in the root for which to look up for needed shared libraries |
|
55 List<Path> allPackageFiles; |
|
56 try (Stream<Path> stream = Files.walk(root)) { |
|
57 allPackageFiles = stream.filter(Files::isRegularFile).filter( |
|
58 LibProvidersLookup::canDependOnLibs).collect( |
|
59 Collectors.toList()); |
|
60 } |
|
61 |
|
62 Collection<Path> neededLibs = getNeededLibsForFiles(allPackageFiles); |
|
63 |
|
64 // Get the list of unique package names. |
|
65 List<String> neededPackages = neededLibs.stream().map(libPath -> { |
|
66 try { |
|
67 List<String> packageNames = packageLookup.apply(libPath).filter( |
|
68 Objects::nonNull).filter(Predicate.not(String::isBlank)).distinct().collect( |
|
69 Collectors.toList()); |
|
70 Log.verbose(String.format("%s is provided by %s", libPath, packageNames)); |
|
71 return packageNames; |
|
72 } catch (IOException ex) { |
|
73 // Ignore and keep going |
|
74 Log.verbose(ex); |
|
75 List<String> packageNames = Collections.emptyList(); |
|
76 return packageNames; |
|
77 } |
|
78 }).flatMap(List::stream).sorted().distinct().collect(Collectors.toList()); |
|
79 |
|
80 return neededPackages; |
|
81 } |
|
82 |
|
83 private static List<Path> getNeededLibsForFile(Path path) throws IOException { |
|
84 List<Path> result = new ArrayList<>(); |
|
85 int ret = Executor.of(TOOL_LDD, path.toString()).setOutputConsumer(lines -> { |
|
86 lines.map(line -> { |
|
87 Matcher matcher = LIB_IN_LDD_OUTPUT_REGEX.matcher(line); |
|
88 if (matcher.find()) { |
|
89 return matcher.group(1); |
|
90 } |
|
91 return null; |
|
92 }).filter(Objects::nonNull).map(Path::of).forEach(result::add); |
|
93 }).execute(); |
|
94 |
|
95 if (ret != 0) { |
|
96 // objdump failed. This is OK if the tool was applied to not a binary file |
|
97 return Collections.emptyList(); |
|
98 } |
|
99 |
|
100 return result; |
|
101 } |
|
102 |
|
103 private static Collection<Path> getNeededLibsForFiles(List<Path> paths) { |
|
104 // Depending on tool used, the set can contain full paths (ldd) or |
|
105 // only file names (objdump). |
|
106 Set<Path> allLibs = paths.stream().map(path -> { |
|
107 List<Path> libs; |
|
108 try { |
|
109 libs = getNeededLibsForFile(path); |
|
110 } catch (IOException ex) { |
|
111 Log.verbose(ex); |
|
112 libs = Collections.emptyList(); |
|
113 } |
|
114 return libs; |
|
115 }).flatMap(List::stream).collect(Collectors.toSet()); |
|
116 |
|
117 // `allLibs` contains names of all .so needed by files from `paths` list. |
|
118 // If there are mutual dependencies between binaries from `paths` list, |
|
119 // then names or full paths to these binaries are in `allLibs` set. |
|
120 // Remove these items from `allLibs`. |
|
121 Set<Path> excludedNames = paths.stream().map(Path::getFileName).collect( |
|
122 Collectors.toSet()); |
|
123 Iterator<Path> it = allLibs.iterator(); |
|
124 while (it.hasNext()) { |
|
125 Path libName = it.next().getFileName(); |
|
126 if (excludedNames.contains(libName)) { |
|
127 it.remove(); |
|
128 } |
|
129 } |
|
130 |
|
131 return allLibs; |
|
132 } |
|
133 |
|
134 private static boolean canDependOnLibs(Path path) { |
|
135 return path.toFile().canExecute() || path.toString().endsWith(".so"); |
|
136 } |
|
137 |
|
138 @FunctionalInterface |
|
139 public interface PackageLookup { |
|
140 Stream<String> apply(Path path) throws IOException; |
|
141 } |
|
142 |
|
143 private PackageLookup packageLookup; |
|
144 |
|
145 private static final String TOOL_LDD = "ldd"; |
|
146 |
|
147 // |
|
148 // Typical ldd output: |
|
149 // |
|
150 // ldd: warning: you do not have execution permission for `/tmp/jdk.jpackage17911687595930080396/images/opt/simplepackagetest/lib/runtime/lib/libawt_headless.so' |
|
151 // linux-vdso.so.1 => (0x00007ffce6bfd000) |
|
152 // libawt.so => /tmp/jdk.jpackage17911687595930080396/images/opt/simplepackagetest/lib/runtime/lib/libawt.so (0x00007f4e00c75000) |
|
153 // libjvm.so => not found |
|
154 // libjava.so => /tmp/jdk.jpackage17911687595930080396/images/opt/simplepackagetest/lib/runtime/lib/libjava.so (0x00007f4e00c41000) |
|
155 // libm.so.6 => /lib64/libm.so.6 (0x00007f4e00834000) |
|
156 // libdl.so.2 => /lib64/libdl.so.2 (0x00007f4e00630000) |
|
157 // libc.so.6 => /lib64/libc.so.6 (0x00007f4e00262000) |
|
158 // libjvm.so => not found |
|
159 // libjvm.so => not found |
|
160 // libverify.so => /tmp/jdk.jpackage17911687595930080396/images/opt/simplepackagetest/lib/runtime/lib/libverify.so (0x00007f4e00c2e000) |
|
161 // /lib64/ld-linux-x86-64.so.2 (0x00007f4e00b36000) |
|
162 // libjvm.so => not found |
|
163 // |
|
164 private static final Pattern LIB_IN_LDD_OUTPUT_REGEX = Pattern.compile( |
|
165 "^\\s*\\S+\\s*=>\\s*(\\S+)\\s+\\(0[xX]\\p{XDigit}+\\)"); |
|
166 } |
|