Preloader: support loading classpath dependencies from manifest
Move this logic from ide-compiler-runner. This fixes running 'kotlinc' CLI compiler locally when the runtime is changed binary incompatibly
This commit is contained in:
@@ -32,13 +32,10 @@ done;
|
|||||||
KOTLIN_HOME=`dirname "$SOURCE"`
|
KOTLIN_HOME=`dirname "$SOURCE"`
|
||||||
KOTLIN_HOME=`cd "$KOTLIN_HOME"; pwd -P`
|
KOTLIN_HOME=`cd "$KOTLIN_HOME"; pwd -P`
|
||||||
KOTLIN_HOME=`cd "$KOTLIN_HOME"/..; pwd`
|
KOTLIN_HOME=`cd "$KOTLIN_HOME"/..; pwd`
|
||||||
PATH_SEPARATOR=":"
|
|
||||||
|
|
||||||
if $cygwin; then
|
if $cygwin; then
|
||||||
# Remove spaces from KOTLIN_HOME on windows
|
# Remove spaces from KOTLIN_HOME on windows
|
||||||
KOTLIN_HOME=`cygpath --windows --short-name "$KOTLIN_HOME"`
|
KOTLIN_HOME=`cygpath --windows --short-name "$KOTLIN_HOME"`
|
||||||
|
|
||||||
PATH_SEPARATOR=";"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -n "$JAVA_OPTS" ] || JAVA_OPTS="-Xmx256M -Xms32M -noverify"
|
[ -n "$JAVA_OPTS" ] || JAVA_OPTS="-Xmx256M -Xms32M -noverify"
|
||||||
@@ -86,5 +83,5 @@ CPSELECT="-cp "
|
|||||||
"${java_args[@]}" \
|
"${java_args[@]}" \
|
||||||
${CPSELECT}"${KOTLIN_HOME}/lib/kotlin-preloader.jar" \
|
${CPSELECT}"${KOTLIN_HOME}/lib/kotlin-preloader.jar" \
|
||||||
org.jetbrains.jet.preloading.Preloader \
|
org.jetbrains.jet.preloading.Preloader \
|
||||||
"${KOTLIN_HOME}/lib/kotlin-compiler.jar${PATH_SEPARATOR}${KOTLIN_HOME}/lib/kotlin-runtime.jar" \
|
"${KOTLIN_HOME}/lib/kotlin-compiler.jar" \
|
||||||
org.jetbrains.jet.cli.js.K2JSCompiler 4096 notime "$@"
|
org.jetbrains.jet.cli.js.K2JSCompiler 4096 notime "$@"
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ rem We use the value of the JAVA_OPTS environment variable if defined
|
|||||||
set _JAVA_OPTS=-Xmx256M -Xms32M -noverify
|
set _JAVA_OPTS=-Xmx256M -Xms32M -noverify
|
||||||
|
|
||||||
"%_JAVACMD%" %_JAVA_OPTS% -cp "%_KOTLIN_HOME%\lib\kotlin-preloader.jar" ^
|
"%_JAVACMD%" %_JAVA_OPTS% -cp "%_KOTLIN_HOME%\lib\kotlin-preloader.jar" ^
|
||||||
org.jetbrains.jet.preloading.Preloader "%_KOTLIN_HOME%\lib\kotlin-compiler.jar;%_KOTLIN_HOME%\lib\kotlin-runtime.jar" ^
|
org.jetbrains.jet.preloading.Preloader "%_KOTLIN_HOME%\lib\kotlin-compiler.jar" ^
|
||||||
org.jetbrains.jet.cli.js.K2JSCompiler 4096 notime %*
|
org.jetbrains.jet.cli.js.K2JSCompiler 4096 notime %*
|
||||||
|
|
||||||
exit /b %ERRORLEVEL%
|
exit /b %ERRORLEVEL%
|
||||||
|
|||||||
@@ -32,13 +32,10 @@ done;
|
|||||||
KOTLIN_HOME=`dirname "$SOURCE"`
|
KOTLIN_HOME=`dirname "$SOURCE"`
|
||||||
KOTLIN_HOME=`cd "$KOTLIN_HOME"; pwd -P`
|
KOTLIN_HOME=`cd "$KOTLIN_HOME"; pwd -P`
|
||||||
KOTLIN_HOME=`cd "$KOTLIN_HOME"/..; pwd`
|
KOTLIN_HOME=`cd "$KOTLIN_HOME"/..; pwd`
|
||||||
PATH_SEPARATOR=":"
|
|
||||||
|
|
||||||
if $cygwin; then
|
if $cygwin; then
|
||||||
# Remove spaces from KOTLIN_HOME on windows
|
# Remove spaces from KOTLIN_HOME on windows
|
||||||
KOTLIN_HOME=`cygpath --windows --short-name "$KOTLIN_HOME"`
|
KOTLIN_HOME=`cygpath --windows --short-name "$KOTLIN_HOME"`
|
||||||
|
|
||||||
PATH_SEPARATOR=";"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -n "$JAVA_OPTS" ] || JAVA_OPTS="-Xmx256M -Xms32M -noverify"
|
[ -n "$JAVA_OPTS" ] || JAVA_OPTS="-Xmx256M -Xms32M -noverify"
|
||||||
@@ -86,5 +83,5 @@ CPSELECT="-cp "
|
|||||||
"${java_args[@]}" \
|
"${java_args[@]}" \
|
||||||
${CPSELECT}"${KOTLIN_HOME}/lib/kotlin-preloader.jar" \
|
${CPSELECT}"${KOTLIN_HOME}/lib/kotlin-preloader.jar" \
|
||||||
org.jetbrains.jet.preloading.Preloader \
|
org.jetbrains.jet.preloading.Preloader \
|
||||||
"${KOTLIN_HOME}/lib/kotlin-compiler.jar${PATH_SEPARATOR}${KOTLIN_HOME}/lib/kotlin-runtime.jar" \
|
"${KOTLIN_HOME}/lib/kotlin-compiler.jar" \
|
||||||
org.jetbrains.jet.cli.jvm.K2JVMCompiler 4096 notime "$@"
|
org.jetbrains.jet.cli.jvm.K2JVMCompiler 4096 notime "$@"
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ rem We use the value of the JAVA_OPTS environment variable if defined
|
|||||||
set _JAVA_OPTS=-Xmx256M -Xms32M -noverify
|
set _JAVA_OPTS=-Xmx256M -Xms32M -noverify
|
||||||
|
|
||||||
"%_JAVACMD%" %_JAVA_OPTS% -cp "%_KOTLIN_HOME%\lib\kotlin-preloader.jar" ^
|
"%_JAVACMD%" %_JAVA_OPTS% -cp "%_KOTLIN_HOME%\lib\kotlin-preloader.jar" ^
|
||||||
org.jetbrains.jet.preloading.Preloader "%_KOTLIN_HOME%\lib\kotlin-compiler.jar;%_KOTLIN_HOME%\lib\kotlin-runtime.jar" ^
|
org.jetbrains.jet.preloading.Preloader "%_KOTLIN_HOME%\lib\kotlin-compiler.jar" ^
|
||||||
org.jetbrains.jet.cli.jvm.K2JVMCompiler 4096 notime %*
|
org.jetbrains.jet.cli.jvm.K2JVMCompiler 4096 notime %*
|
||||||
|
|
||||||
exit /b %ERRORLEVEL%
|
exit /b %ERRORLEVEL%
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ import java.net.URL;
|
|||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.net.URLStreamHandler;
|
import java.net.URLStreamHandler;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.jar.Attributes;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.jar.Manifest;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
@@ -62,6 +65,11 @@ public class ClassPreloadingUtils {
|
|||||||
) throws IOException {
|
) throws IOException {
|
||||||
Map<String, Object> entries = loadAllClassesFromJars(jarFiles, classCountEstimation, handler);
|
Map<String, Object> entries = loadAllClassesFromJars(jarFiles, classCountEstimation, handler);
|
||||||
|
|
||||||
|
Collection<File> classpath = mergeClasspathFromManifests(entries);
|
||||||
|
if (!classpath.isEmpty()) {
|
||||||
|
parentClassLoader = preloadClasses(classpath, classCountEstimation, parentClassLoader, null, handler);
|
||||||
|
}
|
||||||
|
|
||||||
return createMemoryBasedClassLoader(parentClassLoader, entries, handler, classesToLoadByParent);
|
return createMemoryBasedClassLoader(parentClassLoader, entries, handler, classesToLoadByParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +79,43 @@ public class ClassPreloadingUtils {
|
|||||||
return preloadClasses(jarFiles, classCountEstimation, parentClassLoader, classesToLoadByParent, null);
|
return preloadClasses(jarFiles, classCountEstimation, parentClassLoader, classesToLoadByParent, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Collection<File> mergeClasspathFromManifests(Map<String, Object> preloadedResources) throws IOException {
|
||||||
|
Object manifest = preloadedResources.get(JarFile.MANIFEST_NAME);
|
||||||
|
if (manifest instanceof ResourceData) {
|
||||||
|
return extractManifestClasspath((ResourceData) manifest);
|
||||||
|
}
|
||||||
|
else if (manifest instanceof ArrayList) {
|
||||||
|
List<File> result = new ArrayList<File>();
|
||||||
|
for (ResourceData data : (ArrayList<ResourceData>) manifest) {
|
||||||
|
result.addAll(extractManifestClasspath(data));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert manifest == null : "Resource map should contain ResourceData or ArrayList<ResourceData>: " + manifest;
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection<File> extractManifestClasspath(ResourceData manifestData) throws IOException {
|
||||||
|
Manifest manifest = new Manifest(new ByteArrayInputStream(manifestData.bytes));
|
||||||
|
String classpathSpaceSeparated = (String) manifest.getMainAttributes().get(Attributes.Name.CLASS_PATH);
|
||||||
|
if (classpathSpaceSeparated == null) return Collections.emptyList();
|
||||||
|
|
||||||
|
Collection<File> classpath = new ArrayList<File>(1);
|
||||||
|
for (String jar : classpathSpaceSeparated.split(" ")) {
|
||||||
|
if (".".equals(jar)) continue;
|
||||||
|
|
||||||
|
if (!jar.endsWith(".jar")) {
|
||||||
|
throw new UnsupportedOperationException("Class-Path attribute should only contain paths to JAR files: " + jar);
|
||||||
|
}
|
||||||
|
|
||||||
|
classpath.add(new File(manifestData.jarFile.getParent(), jar));
|
||||||
|
}
|
||||||
|
|
||||||
|
return classpath;
|
||||||
|
}
|
||||||
|
|
||||||
private static ClassLoader createMemoryBasedClassLoader(
|
private static ClassLoader createMemoryBasedClassLoader(
|
||||||
final ClassLoader parent,
|
final ClassLoader parent,
|
||||||
final Map<String, Object> preloadedResources,
|
final Map<String, Object> preloadedResources,
|
||||||
|
|||||||
@@ -16,28 +16,18 @@
|
|||||||
|
|
||||||
package org.jetbrains.jet.compiler.runner;
|
package org.jetbrains.jet.compiler.runner;
|
||||||
|
|
||||||
import kotlin.Function1;
|
|
||||||
import kotlin.KotlinPackage;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.cli.common.messages.MessageCollector;
|
import org.jetbrains.jet.cli.common.messages.MessageCollector;
|
||||||
import org.jetbrains.jet.preloading.ClassCondition;
|
|
||||||
import org.jetbrains.jet.preloading.ClassPreloadingUtils;
|
import org.jetbrains.jet.preloading.ClassPreloadingUtils;
|
||||||
import org.jetbrains.jet.utils.KotlinPaths;
|
import org.jetbrains.jet.utils.KotlinPaths;
|
||||||
import org.jetbrains.jet.utils.UtilsPackage;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
|
||||||
import java.util.jar.Attributes;
|
|
||||||
import java.util.jar.JarFile;
|
|
||||||
|
|
||||||
import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION;
|
import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION;
|
||||||
import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.ERROR;
|
import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.ERROR;
|
||||||
@@ -53,7 +43,12 @@ public class CompilerRunnerUtil {
|
|||||||
) throws IOException {
|
) throws IOException {
|
||||||
ClassLoader classLoader = ourClassLoaderRef.get();
|
ClassLoader classLoader = ourClassLoaderRef.get();
|
||||||
if (classLoader == null) {
|
if (classLoader == null) {
|
||||||
classLoader = createClassLoader(libPath, CompilerRunnerUtil.class.getClassLoader(), environment.getClassesToLoadByParent());
|
classLoader = ClassPreloadingUtils.preloadClasses(
|
||||||
|
Collections.singletonList(new File(libPath, "kotlin-compiler.jar")),
|
||||||
|
/* estimatedClassNumber = */ 4096,
|
||||||
|
CompilerRunnerUtil.class.getClassLoader(),
|
||||||
|
environment.getClassesToLoadByParent()
|
||||||
|
);
|
||||||
ourClassLoaderRef = new SoftReference<ClassLoader>(classLoader);
|
ourClassLoaderRef = new SoftReference<ClassLoader>(classLoader);
|
||||||
}
|
}
|
||||||
return classLoader;
|
return classLoader;
|
||||||
@@ -73,48 +68,6 @@ public class CompilerRunnerUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static File compilerJar(@NotNull File libPath) {
|
|
||||||
return new File(libPath, "kotlin-compiler.jar");
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static String loadCompilerClasspathSpaceSeparated(@NotNull File libPath) throws IOException {
|
|
||||||
JarFile jar = new JarFile(compilerJar(libPath));
|
|
||||||
try {
|
|
||||||
return (String) jar.getManifest().getMainAttributes().get(Attributes.Name.CLASS_PATH);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
jar.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static ClassLoader createClassLoader(
|
|
||||||
@NotNull final File libPath,
|
|
||||||
@Nullable ClassLoader parentClassLoader,
|
|
||||||
@Nullable ClassCondition classToLoadByParent
|
|
||||||
) throws IOException {
|
|
||||||
List<URL> classpath = KotlinPackage.map(loadCompilerClasspathSpaceSeparated(libPath).split(" "), new Function1<String, URL>() {
|
|
||||||
@Override
|
|
||||||
public URL invoke(String dependency) {
|
|
||||||
try {
|
|
||||||
return new File(libPath, dependency).toURI().toURL();
|
|
||||||
}
|
|
||||||
catch (MalformedURLException e) {
|
|
||||||
throw UtilsPackage.rethrow(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return ClassPreloadingUtils.preloadClasses(
|
|
||||||
Collections.singletonList(compilerJar(libPath)),
|
|
||||||
/* estimatedClassNumber = */ 4096,
|
|
||||||
new URLClassLoader(classpath.toArray(new URL[classpath.size()]), parentClassLoader),
|
|
||||||
classToLoadByParent
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object invokeExecMethod(
|
public static Object invokeExecMethod(
|
||||||
@NotNull String compilerClassName,
|
@NotNull String compilerClassName,
|
||||||
|
|||||||
Reference in New Issue
Block a user