Skip to content

Commit

Permalink
issue #176: use SourceCodeRunner for reading ModuleDescriptors
Browse files Browse the repository at this point in the history
  • Loading branch information
siordache committed Mar 23, 2021
1 parent d983428 commit dc292e9
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 108 deletions.
74 changes: 55 additions & 19 deletions src/main/groovy/org/beryx/jlink/impl/JlinkTaskImpl.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,24 @@ package org.beryx.jlink.impl
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import org.beryx.jlink.data.JlinkTaskData
import org.beryx.jlink.util.LaunchScriptGenerator
import org.beryx.jlink.util.SuggestedModulesBuilder
import org.beryx.jlink.util.Util
import org.beryx.jlink.util.*
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging

import java.lang.module.ModuleDescriptor

import static org.beryx.jlink.util.Util.EXEC_EXTENSION

@CompileStatic
class JlinkTaskImpl extends BaseTaskImpl<JlinkTaskData> {
private static final Logger LOGGER = Logging.getLogger(JlinkZipTaskImpl.class);

static class ModuleData {
String name
File file
Set<String> requires
}

JlinkTaskImpl(Project project, JlinkTaskData taskData) {
super(project, taskData)
LOGGER.info("taskData: $taskData")
Expand Down Expand Up @@ -105,25 +107,13 @@ class JlinkTaskImpl extends BaseTaskImpl<JlinkTaskData> {
result.rethrowFailure()
}

static class ModuleData {
String name
File file
Set<String> requires

ModuleData(File file, ModuleDescriptor md) {
this.file = file
this.name = md.name()
this.requires = new HashSet(md.requires().collect {it.name()})
}
}

@CompileDynamic
private void copyNonImageModules(File imageDir, List<String> modulePaths) {
if (td.customImageData.enabled) {
Map<String, ModuleData> moduleData = [:]
Util.getJarsAndMods(modulePaths.toArray()).each { file ->
ModuleDescriptor md = Util.getModuleDescriptor(file)
if(md) moduleData[md.name()] = new ModuleData(file, md)
ModuleData md = getModuleData(file)
if(md) moduleData[md.name] = md
}
Set<String> transitiveModules = moduleData.keySet()
LOGGER.info "transitiveModules: $transitiveModules"
Expand Down Expand Up @@ -195,4 +185,50 @@ class JlinkTaskImpl extends BaseTaskImpl<JlinkTaskData> {
secondaryGenerator.generate("$imageDir/bin")
}
}

private ModuleData getModuleData(File f) {
def runner = new SourceCodeRunner(td.javaHome, 'ModuleData', sourceCode)
def output = runner.getOutput(f.absolutePath)
if(!output) return null
def tokens = output.split(':')
if(tokens.length != 2) {
LOGGER.warn("getModuleData($f): output contains $tokens.length + tokens: " + output)
return null
}
ModuleData moduleData = new ModuleData()
moduleData.file = f
moduleData.name = tokens[0]
moduleData.requires = new HashSet<>(tokens[1].split(',').collect())
return moduleData
}

static final String sourceCode = """
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.module.ModuleDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class ModuleData {
public static void main(String[] args) {
if(args.length != 1) throw new IllegalArgumentException("ModuleData expects one argument.");
File f = new File(args[0]);
ModuleDescriptor md = getModuleDescriptor(f);
if(md != null && !md.exports().isEmpty()) {
String requires = md.requires().stream()
.map(ModuleDescriptor.Requires::name)
.collect(Collectors.joining(","));
System.out.println(md.name() + ":" + requires);
}
}
$SourceCodeConstants.GET_MODULE_DESCRIPTOR
}
"""
}
59 changes: 12 additions & 47 deletions src/main/groovy/org/beryx/jlink/util/JavaVersion.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,20 @@ import org.gradle.api.GradleException
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging

import java.nio.file.Files
import java.util.concurrent.TimeUnit

@CompileStatic
class JavaVersion {
private static final Logger LOGGER = Logging.getLogger(JavaVersion.class);
private static final Logger LOGGER = Logging.getLogger(JavaVersion.class)

static int get(String javaHome) {
def runner = new SourceCodeRunner(javaHome, 'JavaVersion', sourceCode)
String javaOutput = runner.getOutput()
LOGGER.info "javaVersion($javaHome): $javaOutput"
try {
return javaOutput as int
} catch (Exception e) {
throw new GradleException("Cannot parse java version: $javaOutput")
}
}

static final String sourceCode = '''
import java.lang.reflect.Method;
Expand Down Expand Up @@ -58,47 +66,4 @@ public class JavaVersion {
}
}
'''

static int get(String javaHome) {
def path = Files.createTempDirectory("badass-")
def javaFile = path.resolve('JavaVersion.java').toFile()
javaFile << sourceCode

def javacCmd = "$javaHome/bin/javac -cp . -d . JavaVersion.java"
LOGGER.info("Executing: $javacCmd")
def javacProc = javacCmd.execute(null as String[], path.toFile())
def javacErrOutput = new StringBuilder()
javacProc.consumeProcessErrorStream(javacErrOutput)
if(!javacProc.waitFor(30, TimeUnit.SECONDS)) {
throw new GradleException("javac JavaVersion.java hasn't exited after 30 seconds.")
}
String javacOutput = javacProc.text
LOGGER.info(javacOutput)
if(javacProc.exitValue()) {
throw new GradleException("javac JavaVersion.java failed: $javacErrOutput")
}
if(javacErrOutput.size() > 0) LOGGER.error("javac failed: $javacErrOutput")

def javaCmd = "$javaHome/bin/java -cp . JavaVersion"
LOGGER.info("Executing: $javaCmd")
def javaProc = javaCmd.execute(null as String[], path.toFile())
def javaErrOutput = new StringBuilder()
javaProc.consumeProcessErrorStream(javaErrOutput)
if(!javaProc.waitFor(30, TimeUnit.SECONDS)) {
throw new GradleException("java JavaVersion hasn't exited after 30 seconds.")
}

String javaOutput = javaProc.text
LOGGER.info(javaOutput)
if(javaProc.exitValue()) {
throw new GradleException("java JavaVersion failed: $javaErrOutput")
}
if(javaErrOutput.size() > 0) LOGGER.error("java failed: $javaErrOutput")

try {
return javaOutput as int
} catch (Exception e) {
throw new GradleException("Cannot parse java version: $javaOutput")
}
}
}
26 changes: 3 additions & 23 deletions src/main/groovy/org/beryx/jlink/util/ModuleManager.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class ModuleManager {
exportMap
}

static final String sourceCode = '''
static final String sourceCode = """
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
Expand Down Expand Up @@ -98,27 +98,7 @@ public class ModuleManager {
return exports;
}
private static ModuleDescriptor getModuleDescriptor(File f) {
try {
if(!f.isFile()) throw new IllegalArgumentException(f + " is not a file");
if(f.getName().equals("module-info.class")) {
return ModuleDescriptor.read(new FileInputStream(f));
}
if(!f.getName().endsWith(".jar") && !f.getName().endsWith(".jmod")) throw new IllegalArgumentException("Unsupported file type: " + f);
String prefix = f.getName().endsWith(".jmod") ? "classes/" : "";
ZipFile zipFile = new ZipFile(f);
for (Enumeration<? extends ZipEntry> entries = zipFile.entries(); entries.hasMoreElements();) {
ZipEntry entry = entries.nextElement();
if(entry.getName().equals(prefix + "module-info.class")) {
InputStream entryStream = zipFile.getInputStream(entry);
return ModuleDescriptor.read(entryStream);
}
}
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
$SourceCodeConstants.GET_MODULE_DESCRIPTOR
}
'''
"""
}
42 changes: 42 additions & 0 deletions src/main/groovy/org/beryx/jlink/util/SourceCodeConstants.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.beryx.jlink.util

class SourceCodeConstants {
static final String GET_MODULE_DESCRIPTOR = '''
private static ModuleDescriptor getModuleDescriptor(File f) {
try {
if(!f.isFile()) throw new IllegalArgumentException(f + " is not a file");
if(f.getName().equals("module-info.class")) {
return ModuleDescriptor.read(new FileInputStream(f));
}
if(!f.getName().endsWith(".jar") && !f.getName().endsWith(".jmod")) throw new IllegalArgumentException("Unsupported file type: " + f);
String prefix = f.getName().endsWith(".jmod") ? "classes/" : "";
ZipFile zipFile = new ZipFile(f);
for (Enumeration<? extends ZipEntry> entries = zipFile.entries(); entries.hasMoreElements();) {
ZipEntry entry = entries.nextElement();
if(entry.getName().equals(prefix + "module-info.class")) {
InputStream entryStream = zipFile.getInputStream(entry);
return ModuleDescriptor.read(entryStream);
}
}
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
'''
}
4 changes: 2 additions & 2 deletions src/main/groovy/org/beryx/jlink/util/SourceCodeRunner.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ class SourceCodeRunner {
def javacProc = javacCmd.execute(null as String[], path.toFile())
def javacErrOutput = new StringBuilder()
javacProc.consumeProcessErrorStream(javacErrOutput)
if (!javacProc.waitFor(30, TimeUnit.SECONDS)) {
throw new GradleException("javac ${className}.java hasn't exited after 30 seconds.")
if (!javacProc.waitFor(timeoutSeconds, TimeUnit.SECONDS)) {
throw new GradleException("javac ${className}.java hasn't exited after $timeoutSeconds seconds.")
}
String javacOutput = javacProc.text
LOGGER.info(javacOutput)
Expand Down
17 changes: 0 additions & 17 deletions src/main/groovy/org/beryx/jlink/util/Util.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import org.gradle.api.tasks.bundling.Jar
import org.gradle.jvm.toolchain.JavaToolchainService
import org.gradle.util.GradleVersion

import java.lang.module.ModuleDescriptor
import java.lang.module.ModuleFinder
import java.util.jar.JarEntry
import java.util.jar.JarFile
Expand Down Expand Up @@ -358,22 +357,6 @@ class Util {
allFiles.findAll {it.name.endsWith(".jar") || it.name.endsWith(".jmod")}
}

static ModuleDescriptor getModuleDescriptor(File f) {
if(!f.file) throw new IllegalArgumentException("$f is not a file")
if(f.name == 'module-info.class') return ModuleDescriptor.read(f.newInputStream())
if(!f.name.endsWith('.jar') && !f.name.endsWith('.jmod')) throw new IllegalArgumentException("Unsupported file type: $f")
def prefix = f.name.endsWith('.jmod') ? 'classes/' : ''
def zipFile = new ZipFile(f)
for(entry in zipFile.entries()) {
ZipEntry zipEntry = (ZipEntry)entry
if(zipEntry.name == "${prefix}module-info.class" as String) {
def entryStream = zipFile.getInputStream(zipEntry)
return ModuleDescriptor.read(entryStream)
}
}
null
}

static List<Project> getAllDependentProjects(Project project) {
getAllDependentProjectsExt(project, new HashSet<Project>())
}
Expand Down

0 comments on commit dc292e9

Please sign in to comment.