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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 package org.openjdk.skara.gradle.images;
25
26 import org.gradle.api.DefaultTask;
27 import org.gradle.api.GradleException;
28 import org.gradle.api.artifacts.UnknownConfigurationException;
29 import org.gradle.api.model.ObjectFactory;
30 import org.gradle.api.provider.*;
31 import org.gradle.api.tasks.*;
32 import org.gradle.jvm.tasks.Jar;
33 import org.gradle.api.file.*;
34
35 import javax.inject.Inject;
36 import java.io.*;
37 import java.net.URI;
38 import java.nio.file.*;
39 import java.util.*;
40 import java.util.stream.Collectors;
41
42 public class LinkTask extends DefaultTask {
43 private final Property<String> os;
44 private final Property<String> cpu;
45 private final Property<String> url;
46 private final Property<Path> toDir;
47 private final MapProperty<String, String> launchers;
48 private final ListProperty<String> modules;
49 private final SetProperty<RegularFile> modulePath;
50 private final SetProperty<FileSystemLocation> runtimeModules;
51
52 @Inject
53 public LinkTask(ObjectFactory factory) {
54 os = factory.property(String.class);
55 cpu = factory.property(String.class);
56 url = factory.property(String.class);
57 toDir = factory.property(Path.class);
58 launchers = factory.mapProperty(String.class, String.class);
59 modules = factory.listProperty(String.class);
60 modulePath = factory.setProperty(RegularFile.class);
61 runtimeModules = factory.setProperty(FileSystemLocation.class);
62 }
63
64 @OutputDirectory
65 Property<Path> getToDir() {
66 return toDir;
67 }
68
69 @Input
70 Property<String> getOS() {
71 return os;
72 }
73
74 @Input
75 Property<String> getCPU() {
76 return cpu;
77 }
78
79 @Input
80 Property<String> getUrl() {
81 return url;
82 }
83
84 @Input
85 MapProperty<String, String> getLaunchers() {
86 return launchers;
87 }
88
89 @Input
90 ListProperty<String> getModules() {
91 return modules;
92 }
93
94 @InputFiles
95 SetProperty<RegularFile> getModulePath() {
96 return modulePath;
97 }
98
99 @InputFiles
100 SetProperty<FileSystemLocation> getRuntimeModules() {
101 return runtimeModules;
102 }
103
104 private static void clearDirectory(Path directory) {
105 try {
106 Files.walk(directory)
107 .map(Path::toFile)
108 .sorted(Comparator.reverseOrder())
109 .forEach(File::delete);
110 } catch (IOException io) {
111 throw new RuntimeException(io);
112 }
113 }
114
115 @TaskAction
116 void link() throws IOException {
117 var project = getProject().getRootProject();
118
119 var modularJars = new ArrayList<String>();
120 for (var jar : modulePath.get()) {
121 modularJars.add(jar.getAsFile().toString());
122 }
123 for (var jar : runtimeModules.get()) {
124 modularJars.add(jar.getAsFile().toString());
125 }
126
127 Path jdk = null;
128 if (!url.get().equals("local")) {
129 var filename = Path.of(URI.create(url.get()).getPath()).getFileName().toString();
130 var dirname = filename.replace(".zip", "").replace(".tar.gz", "");
131 jdk = project.getRootDir().toPath().toAbsolutePath().resolve(".jdk").resolve(dirname);
132 } else {
133 jdk = Path.of(System.getProperty("java.home"));
134 }
135 var dirs = Files.walk(jdk)
136 .filter(Files::isDirectory)
137 .filter(p -> p.getFileName().toString().equals("jmods"))
138 .collect(Collectors.toList());
139 if (dirs.size() != 1) {
140 var plural = dirs.size() == 0 ? "no" : "multiple";
141 throw new GradleException("JDK at " + jdk.toString() + " contains " + plural + " 'jmods' directories");
142 }
143 var jmodsDir = dirs.get(0).toAbsolutePath();
144
145 var modulePath = new ArrayList<String>();
146 modulePath.add(jmodsDir.toString());
147 modulePath.addAll(modularJars);
148
149 var uniqueModules = new HashSet<String>();
150 for (var entry : launchers.get().values()) {
151 var firstSlash = entry.indexOf('/');
152 uniqueModules.add(entry.substring(0, firstSlash));
153 }
154 uniqueModules.addAll(modules.get());
155 var allModules = new ArrayList<String>(uniqueModules);
156
157 Files.createDirectories(toDir.get());
158 var dest = toDir.get().resolve(os.get() + "-" + cpu.get());
159 if (Files.exists(dest) && Files.isDirectory(dest)) {
160 clearDirectory(dest);
161 }
162
163 Collections.sort(modulePath);
164 Collections.sort(allModules);
165
166 var jlink = Path.of(System.getProperty("java.home"), "bin", "jlink").toAbsolutePath().toString();
167 project.exec((spec) -> {
168 spec.setCommandLine(jlink, "--module-path", String.join(File.pathSeparator, modulePath),
169 "--add-modules", String.join(",", allModules),
170 "--no-man-pages",
171 "--no-header-files",
172 "--compress", "2",
173 "--vm", "server",
174 "--output", dest.toString());
175 });
176
177 var currentOS = System.getProperty("os.name").toLowerCase().substring(0, 3);
178 if (os.get().equals("local") || currentOS.equals(os.get().substring(0, 3))) {
179 var ext = currentOS.startsWith("win") ? ".exe" : "";
180 var javaLaunchers = Files.walk(dest)
181 .filter(Files::isExecutable)
182 .filter(p -> p.getFileName().toString().equals("java" + ext))
183 .collect(Collectors.toList());
184 if (javaLaunchers.size() != 1) {
185 throw new GradleException("Multiple or no java launchers generated for " + os.get() + "-" + cpu.get() + " image");
186 }
187 var java = javaLaunchers.get(0);
188 project.exec((spec) -> {
189 spec.setCommandLine(java, "-Xshare:dump", "-version");
190 });
191 }
192 }
193 }