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> url; 45 private final Property<Path> toDir; 46 private final MapProperty<String, String> launchers; 47 private final ListProperty<String> modules; 48 private final SetProperty<RegularFile> modulePath; 49 private final SetProperty<FileSystemLocation> runtimeModules; 50 51 @Inject 52 public LinkTask(ObjectFactory factory) { 53 os = factory.property(String.class); 54 url = factory.property(String.class); 55 toDir = factory.property(Path.class); 56 launchers = factory.mapProperty(String.class, String.class); 57 modules = factory.listProperty(String.class); 58 modulePath = factory.setProperty(RegularFile.class); 59 runtimeModules = factory.setProperty(FileSystemLocation.class); 60 } 61 62 @OutputDirectory 63 Property<Path> getToDir() { 64 return toDir; 65 } 66 67 @Input 68 Property<String> getOS() { 69 return os; 70 } 71 72 @Input 73 Property<String> getUrl() { 74 return url; 75 } 76 77 @Input 78 MapProperty<String, String> getLaunchers() { 79 return launchers; 80 } 81 82 @Input 83 ListProperty<String> getModules() { 84 return modules; 85 } 86 87 @InputFiles 88 SetProperty<RegularFile> getModulePath() { 89 return modulePath; 90 } 91 92 @InputFiles 93 SetProperty<FileSystemLocation> getRuntimeModules() { 94 return runtimeModules; 95 } 96 97 private static void clearDirectory(Path directory) { 98 try { 99 Files.walk(directory) 100 .map(Path::toFile) 101 .sorted(Comparator.reverseOrder()) 102 .forEach(File::delete); 103 } catch (IOException io) { 104 throw new RuntimeException(io); 105 } 106 } 107 108 @TaskAction 109 void link() throws IOException { 110 var project = getProject().getRootProject(); 111 112 var modularJars = new ArrayList<String>(); 113 for (var jar : modulePath.get()) { 114 modularJars.add(jar.getAsFile().toString()); 115 } 116 for (var jar : runtimeModules.get()) { 117 modularJars.add(jar.getAsFile().toString()); 118 } 119 120 var filename = Path.of(URI.create(url.get()).getPath()).getFileName().toString(); 121 var dirname = filename.replace(".zip", "").replace(".tar.gz", ""); 122 var jdk = project.getRootDir().toPath().toAbsolutePath().resolve(".jdk").resolve(dirname); 123 var dirs = Files.walk(jdk) 124 .filter(Files::isDirectory) 125 .filter(p -> p.getFileName().toString().equals("jmods")) 126 .collect(Collectors.toList()); 127 if (dirs.size() != 1) { 128 var plural = dirs.size() == 0 ? "no" : "multiple"; 129 throw new GradleException("JDK at " + jdk.toString() + " contains " + plural + " 'jmods' directories"); 130 } 131 var jmodsDir = dirs.get(0).toAbsolutePath(); 132 133 var modulePath = new ArrayList<String>(); 134 modulePath.add(jmodsDir.toString()); 135 modulePath.addAll(modularJars); 136 137 var uniqueModules = new HashSet<String>(); 138 for (var entry : launchers.get().values()) { 139 var firstSlash = entry.indexOf('/'); 140 uniqueModules.add(entry.substring(0, firstSlash)); 141 } 142 uniqueModules.addAll(modules.get()); 143 var allModules = new ArrayList<String>(uniqueModules); 144 145 Files.createDirectories(toDir.get()); 146 var dest = toDir.get().resolve(os.get()); 147 if (Files.exists(dest) && Files.isDirectory(dest)) { 148 clearDirectory(dest); 149 } 150 151 Collections.sort(modulePath); 152 Collections.sort(allModules); 153 154 var jlink = Path.of(System.getProperty("java.home"), "bin", "jlink").toAbsolutePath().toString(); 155 project.exec((spec) -> { 156 spec.setCommandLine(jlink, "--module-path", String.join(File.pathSeparator, modulePath), 157 "--add-modules", String.join(",", allModules), 158 "--no-man-pages", 159 "--no-header-files", 160 "--compress", "2", 161 "--vm", "server", 162 "--output", dest.toString()); 163 }); 164 165 var currentOS = System.getProperty("os.name").toLowerCase().substring(0, 3); 166 if (currentOS.equals(os.get().substring(0, 3))) { 167 var ext = currentOS.startsWith("win") ? ".exe" : ""; 168 var javaLaunchers = Files.walk(dest) 169 .filter(Files::isExecutable) 170 .filter(p -> p.getFileName().toString().equals("java" + ext)) 171 .collect(Collectors.toList()); 172 if (javaLaunchers.size() != 1) { 173 throw new GradleException("Multiple or no java launchers generated for " + os.get() + " image"); 174 } 175 var java = javaLaunchers.get(0); 176 project.exec((spec) -> { 177 spec.setCommandLine(java, "-Xshare:dump", "-version"); 178 }); 179 } 180 } 181 }