From 2b9b563efb851e5b619a0693aeac4c8659aeab77 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Thu, 3 Oct 2013 12:47:47 +0400 Subject: [PATCH] JPS plugin: add support external compilation for JS modules. --- .../org/jetbrains/jet/utils/LibraryUtils.java | 74 +++++++++++++++ .../compiler/runner/KotlinCompilerRunner.java | 89 ++++++++++++++---- .../jet/plugin/compiler/JetCompiler.java | 2 +- .../org/jetbrains/jet/jps/build/JpsUtil.java | 31 +++++++ .../jet/jps/build/KotlinBuilder.java | 92 ++++++++++++++++--- .../KotlinBuilderModuleScriptGenerator.java | 8 +- 6 files changed, 258 insertions(+), 38 deletions(-) create mode 100644 compiler/util/src/org/jetbrains/jet/utils/LibraryUtils.java create mode 100644 jps-plugin/src/org/jetbrains/jet/jps/build/JpsUtil.java diff --git a/compiler/util/src/org/jetbrains/jet/utils/LibraryUtils.java b/compiler/util/src/org/jetbrains/jet/utils/LibraryUtils.java new file mode 100644 index 00000000000..d07179155fe --- /dev/null +++ b/compiler/util/src/org/jetbrains/jet/utils/LibraryUtils.java @@ -0,0 +1,74 @@ +/* + * Copyright 2010-2013 JetBrains s.r.o. + * + * 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.jetbrains.jet.utils; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +public class LibraryUtils { + public static final String VENDOR_JETBRAINS = "JetBrains"; + + public static final String TITLE_KOTLIN_RUNTIME_AND_STDLIB = "Kotlin Compiler Runtime + StdLib"; + public static final String TITLE_KOTLIN_RUNTIME_AND_STDLIB_SOURCES = "Kotlin Compiler Runtime + StdLib Sources"; + public static final String TITLE_KOTLIN_JAVASCRIPT_STDLIB = "Kotlin JavaScript StdLib"; + + @Nullable + public static Manifest getManifestFromJar(@NotNull File library) { + if (!library.canRead()) return null; + + try { + JarFile jarFile = new JarFile(library); + try { + return jarFile.getManifest(); + } + finally { + jarFile.close(); + } + } + catch (IOException ignored) { } + + return null; + } + + @Nullable + public static Attributes getManifestMainAttributesFromJar(@NotNull File library) { + Manifest manifest = getManifestFromJar(library); + return manifest != null ? manifest.getMainAttributes() : null; + } + + private static boolean checkImplTitle(@NotNull File library, String expected) { + Attributes attributes = getManifestMainAttributesFromJar(library); + if (attributes == null) return false; + + String title = attributes.getValue(Attributes.Name.IMPLEMENTATION_TITLE); + return title != null && title.equals(expected); + } + + public static boolean isJsRuntimeLibrary(@NotNull File library) { + return checkImplTitle(library, TITLE_KOTLIN_JAVASCRIPT_STDLIB); + } + + public static boolean isJvmRuntimeLibrary(@NotNull File library) { + return checkImplTitle(library, TITLE_KOTLIN_RUNTIME_AND_STDLIB); + } +} diff --git a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinCompilerRunner.java b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinCompilerRunner.java index 4b8a78538d3..787572ae457 100644 --- a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinCompilerRunner.java +++ b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/KotlinCompilerRunner.java @@ -24,8 +24,12 @@ import com.intellij.openapi.projectRoots.JdkUtil; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SimpleJavaSdkType; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ArrayUtil; import com.intellij.util.Function; +import com.intellij.util.StringBuilderSpinAllocator; import com.intellij.util.SystemProperties; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; import org.jetbrains.jet.cli.common.messages.MessageCollector; @@ -35,40 +39,57 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; -import java.util.Arrays; +import java.util.*; public class KotlinCompilerRunner { - public static void runCompiler( + private static final String K2JVM_COMPILER = "org.jetbrains.jet.cli.jvm.K2JVMCompiler"; + private static final String K2JS_COMPILER = "org.jetbrains.jet.cli.js.K2JSCompiler"; + + public static void runK2JvmCompiler( MessageCollector messageCollector, CompilerEnvironment environment, File moduleFile, OutputItemsCollector collector, boolean runOutOfProcess ) { + String[] arguments = createArgumentsForJvmCompiler(environment.getOutput(), moduleFile); + if (runOutOfProcess) { - runOutOfProcess(messageCollector, collector, environment, moduleFile); + runOutOfProcess(K2JVM_COMPILER, arguments, messageCollector, collector, environment); } else { - runInProcess(messageCollector, collector, environment, moduleFile); + runInProcess(K2JVM_COMPILER, arguments, messageCollector, collector, environment); } } - private static void runInProcess(final MessageCollector messageCollector, + public static void runK2JsCompiler( + MessageCollector messageCollector, + CompilerEnvironment environment, OutputItemsCollector collector, - final CompilerEnvironment environment, - final File moduleFile) { + List sourceFiles, + Set libraryFiles, + File outputFile + ) { + String[] arguments = createArgumentsForJsCompiler(outputFile, sourceFiles, libraryFiles); + runInProcess(K2JS_COMPILER, arguments, messageCollector, collector, environment); + } + + private static void runInProcess( + final String compilerClassName, + final String[] arguments, + final MessageCollector messageCollector, + OutputItemsCollector collector, + final CompilerEnvironment environment) { CompilerRunnerUtil.outputCompilerMessagesAndHandleExitCode(messageCollector, collector, new Function() { @Override public Integer fun(PrintStream stream) { - return execInProcess(environment, moduleFile, stream, messageCollector); + return execInProcess(compilerClassName, arguments, environment, stream, messageCollector); } }); } - private static int execInProcess(CompilerEnvironment environment, File moduleFile, PrintStream out, MessageCollector messageCollector) { + private static int execInProcess(String compilerClassName, String[] arguments, CompilerEnvironment environment, PrintStream out, MessageCollector messageCollector) { try { - String compilerClassName = "org.jetbrains.jet.cli.jvm.K2JVMCompiler"; - String[] arguments = commandLineArguments(environment.getOutput(), moduleFile); messageCollector.report(CompilerMessageSeverity.INFO, "Using kotlinHome=" + environment.getKotlinPaths().getHomePath(), CompilerMessageLocation.NO_LOCATION); @@ -87,7 +108,7 @@ public class KotlinCompilerRunner { } } - private static String[] commandLineArguments(File outputDir, File moduleFile) { + private static String[] createArgumentsForJvmCompiler(File outputDir, File moduleFile) { return new String[]{ "-module", moduleFile.getAbsolutePath(), "-output", outputDir.getPath(), @@ -96,17 +117,53 @@ public class KotlinCompilerRunner { "-noStdlib", "-noJdkAnnotations", "-noJdk"}; } + private static String[] createArgumentsForJsCompiler( + File outputFile, + List sourceFiles, + Set libraryFiles + ) { + List args = new ArrayList(); + + Collections.addAll(args, "-tags", "-verbose", "-version", "-sourcemap"); + + args.add("-sourceFiles"); + args.add(convertSourceFilesListToString(sourceFiles)); + + args.add("-output"); + args.add(outputFile.getPath()); + + args.add("-libraryFiles"); + args.add(StringUtil.join(libraryFiles, ",")); + + return ArrayUtil.toStringArray(args); + } + + @NotNull + private static String convertSourceFilesListToString(@NotNull List sourceFiles) { + StringBuilder sb = StringBuilderSpinAllocator.alloc(); + + for (File file : sourceFiles) { + sb.append(file.getPath()).append(','); + } + String result = sb.substring(0, sb.length() - 1); + + StringBuilderSpinAllocator.dispose(sb); + + return result; + } + private static void runOutOfProcess( + String compilerClassName, + String[] arguments, final MessageCollector messageCollector, final OutputItemsCollector itemCollector, - CompilerEnvironment environment, - File moduleFile + CompilerEnvironment environment ) { SimpleJavaParameters params = new SimpleJavaParameters(); params.setJdk(new SimpleJavaSdkType().createJdk("tmp", SystemProperties.getJavaHome())); - params.setMainClass("org.jetbrains.jet.cli.jvm.K2JVMCompiler"); + params.setMainClass(compilerClassName); - for (String arg : commandLineArguments(environment.getOutput(), moduleFile)) { + for (String arg : arguments) { params.getProgramParametersList().add(arg); } diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java b/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java index e77859b031b..9bf2dbc44ee 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java +++ b/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java @@ -136,7 +136,7 @@ public class JetCompiler implements TranslatingCompiler { File scriptFile, OutputItemsCollector outputItemsCollector ) { - KotlinCompilerRunner.runCompiler(messageCollector, environment, scriptFile, outputItemsCollector, RUN_OUT_OF_PROCESS); + KotlinCompilerRunner.runK2JvmCompiler(messageCollector, environment, scriptFile, outputItemsCollector, RUN_OUT_OF_PROCESS); } public static File tryToWriteScriptFile( diff --git a/jps-plugin/src/org/jetbrains/jet/jps/build/JpsUtil.java b/jps-plugin/src/org/jetbrains/jet/jps/build/JpsUtil.java new file mode 100644 index 00000000000..3fa9a5d1947 --- /dev/null +++ b/jps-plugin/src/org/jetbrains/jet/jps/build/JpsUtil.java @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2013 JetBrains s.r.o. + * + * 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.jetbrains.jet.jps.build; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.incremental.ModuleBuildTarget; +import org.jetbrains.jps.model.java.JpsJavaClasspathKind; +import org.jetbrains.jps.model.java.JpsJavaDependenciesEnumerator; +import org.jetbrains.jps.model.java.JpsJavaExtensionService; + +public class JpsUtil { + @NotNull + public static JpsJavaDependenciesEnumerator getAllDependencies(@NotNull ModuleBuildTarget target) { + return JpsJavaExtensionService.dependencies(target.getModule()).recursively().exportedOnly() + .includedIn(JpsJavaClasspathKind.compile(target.isTests())); + } +} diff --git a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java index 39d6cef1493..4d4680f9ea8 100644 --- a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java +++ b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java @@ -17,6 +17,7 @@ package org.jetbrains.jet.jps.build; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.Consumer; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -25,6 +26,7 @@ import org.jetbrains.jet.cli.common.messages.CompilerMessageLocation; import org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity; import org.jetbrains.jet.cli.common.messages.MessageCollector; import org.jetbrains.jet.compiler.runner.*; +import org.jetbrains.jet.utils.LibraryUtils; import org.jetbrains.jet.utils.PathUtil; import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.builders.DirtyFilesHolder; @@ -33,19 +35,22 @@ import org.jetbrains.jps.incremental.*; import org.jetbrains.jps.incremental.java.JavaBuilder; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; +import org.jetbrains.jps.model.java.JavaSourceRootType; +import org.jetbrains.jps.model.java.JpsJavaModuleType; +import org.jetbrains.jps.model.library.JpsLibrary; +import org.jetbrains.jps.model.library.JpsLibraryRoot; +import org.jetbrains.jps.model.library.JpsOrderRootType; import org.jetbrains.jps.model.module.JpsModule; +import org.jetbrains.jps.model.module.JpsModuleSourceRoot; +import org.jetbrains.jps.util.JpsPathUtil; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; -import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.EXCEPTION; -import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.INFO; -import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.WARNING; +import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.*; public class KotlinBuilder extends ModuleLevelBuilder { @@ -109,8 +114,6 @@ public class KotlinBuilder extends ModuleLevelBuilder { return ExitCode.NOTHING_DONE; } - File moduleFile = KotlinBuilderModuleScriptGenerator.generateModuleDescription(context, representativeTarget, sourceFiles); - File outputDir = representativeTarget.getOutputDir(); CompilerEnvironment environment = CompilerEnvironment.getEnvironmentFor(PathUtil.getKotlinPathsForJpsPluginOrJpsTests(), outputDir); @@ -123,12 +126,27 @@ public class KotlinBuilder extends ModuleLevelBuilder { OutputItemsCollectorImpl outputItemCollector = new OutputItemsCollectorImpl(outputDir); - KotlinCompilerRunner.runCompiler( - messageCollector, - environment, - moduleFile, - outputItemCollector, - /*runOutOfProcess = */false); + if (isJsKotlinModule(representativeTarget)) { + File outputFile = new File(outputDir, representativeTarget.getModule().getName() + ".js"); + + KotlinCompilerRunner.runK2JsCompiler( + messageCollector, + environment, + outputItemCollector, + sourceFiles, + getLibraryFilesAndDependencies(representativeTarget), + outputFile); + } + else { + File moduleFile = KotlinBuilderModuleScriptGenerator.generateModuleDescription(context, representativeTarget, sourceFiles); + + KotlinCompilerRunner.runK2JvmCompiler( + messageCollector, + environment, + moduleFile, + outputItemCollector, + /*runOutOfProcess = */false); + } for (SimpleOutputItem outputItem : outputItemCollector.getOutputs()) { outputConsumer.registerOutputFile( @@ -211,4 +229,50 @@ public class KotlinBuilder extends ModuleLevelBuilder { public List getCompilableFileExtensions() { return COMPILABLE_FILE_EXTENSIONS; } + + private static boolean isJsKotlinModule(@NotNull ModuleBuildTarget target) { + Set libraries = JpsUtil.getAllDependencies(target).getLibraries(); + for (JpsLibrary library : libraries) { + for (JpsLibraryRoot root : library.getRoots(JpsOrderRootType.COMPILED)) { + if (LibraryUtils.isJsRuntimeLibrary(JpsPathUtil.urlToFile(root.getUrl()))) + return true; + } + } + return false; + } + + @NotNull + private static Set getLibraryFilesAndDependencies(@NotNull ModuleBuildTarget target) { + Set result = new HashSet(); + getLibraryPaths(target, result); + getDependencyModulesAndSources(target, result); + return result; + } + + private static void getLibraryPaths(@NotNull ModuleBuildTarget target, Set result) { + Set libraries = JpsUtil.getAllDependencies(target).getLibraries(); + for (JpsLibrary library : libraries) { + for (JpsLibraryRoot root : library.getRoots(JpsOrderRootType.COMPILED)) { + String path = JpsPathUtil.urlToOsPath(root.getUrl()); + if (path.endsWith(PathUtil.JS_LIB_JAR_NAME)) { + result.add(path); + } + } + } + } + + private static void getDependencyModulesAndSources(@NotNull final ModuleBuildTarget target, final Set result) { + JpsUtil.getAllDependencies(target).processModules(new Consumer() { + @Override + public void consume(JpsModule module) { + if (module == target.getModule() || module.getModuleType() != JpsJavaModuleType.INSTANCE) return; + + result.add("@" + module.getName()); + + for (JpsModuleSourceRoot root : module.getSourceRoots(JavaSourceRootType.SOURCE)) { + result.add(JpsPathUtil.urlToOsPath(root.getUrl())); + } + } + }); + } } diff --git a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilderModuleScriptGenerator.java b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilderModuleScriptGenerator.java index beecde4bfad..34a967fb3c5 100644 --- a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilderModuleScriptGenerator.java +++ b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilderModuleScriptGenerator.java @@ -27,7 +27,6 @@ import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; -import org.jetbrains.jps.model.JpsDummyElement; import org.jetbrains.jps.model.java.*; import org.jetbrains.jps.model.library.JpsLibrary; import org.jetbrains.jps.model.library.sdk.JpsSdk; @@ -44,6 +43,7 @@ import java.util.Collections; import java.util.List; import static org.jetbrains.jet.compiler.runner.KotlinModuleDescriptionGenerator.DependencyProvider; +import static org.jetbrains.jet.jps.build.JpsUtil.getAllDependencies; public class KotlinBuilderModuleScriptGenerator { @@ -144,12 +144,6 @@ public class KotlinBuilderModuleScriptGenerator { return JpsJavaSdkType.INSTANCE; } - @NotNull - private static JpsJavaDependenciesEnumerator getAllDependencies(@NotNull ModuleBuildTarget target) { - return JpsJavaExtensionService.dependencies(target.getModule()).recursively().exportedOnly() - .includedIn(JpsJavaClasspathKind.compile(target.isTests())); - } - @Nullable private static JpsLibrary getLibrary(@NotNull JpsDependencyElement dependencyElement) { if (dependencyElement instanceof JpsSdkDependency) {