Merge remote-tracking branch 'origin/master'

This commit is contained in:
Vladimir.Orlov
2017-03-23 13:40:07 +03:00
18 changed files with 652 additions and 420 deletions
@@ -150,16 +150,21 @@ public class AppMainV2 {
public static class Agent {
public static void premain(String args, Instrumentation i) {
try {
int p = args.indexOf(':');
if (p < 0) throw new IllegalArgumentException("incorrect parameter: " + args);
boolean helperLibLoaded = loadHelper(args.substring(p + 1));
int portNumber = Integer.parseInt(args.substring(0, p));
startMonitor(portNumber, helperLibLoaded);
}
catch (Throwable t) {
System.err.println("Launcher failed - \"Dump Threads\" and \"Exit\" actions are unavailable (" + t.getMessage() + ')');
}
AppMainV2.premain(args);
}
}
// todo[r.sh] inline some time after 2017.1.1 release
public static void premain(String args) {
try {
int p = args.indexOf(':');
if (p < 0) throw new IllegalArgumentException("incorrect parameter: " + args);
boolean helperLibLoaded = loadHelper(args.substring(p + 1));
int portNumber = Integer.parseInt(args.substring(0, p));
startMonitor(portNumber, helperLibLoaded);
}
catch (Throwable t) {
System.err.println("Launcher failed - \"Dump Threads\" and \"Exit\" actions are unavailable (" + t.getMessage() + ')');
}
}
}
@@ -32,10 +32,8 @@ import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
import java.io.*;
import java.io.DataOutputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
public class CompilerBackwardReferenceIndex {
private final static Logger LOG = Logger.getInstance(CompilerBackwardReferenceIndex.class);
@@ -48,6 +46,7 @@ public class CompilerBackwardReferenceIndex {
private final NameEnumerator myNameEnumerator;
private final PersistentStringEnumerator myFilePathEnumerator;
private final File myIndicesDir;
private final boolean myReadOnly;
private final LowMemoryWatcher myLowMemoryWatcher = LowMemoryWatcher.register(new Runnable() {
@Override
public void run() {
@@ -67,6 +66,7 @@ public class CompilerBackwardReferenceIndex {
public CompilerBackwardReferenceIndex(File buildDir, boolean readOnly) {
myIndicesDir = getIndexDir(buildDir);
myReadOnly = readOnly;
if (!myIndicesDir.exists() && !myIndicesDir.mkdirs()) {
throw new RuntimeException("Can't create dir: " + buildDir.getAbsolutePath());
}
@@ -74,6 +74,8 @@ public class CompilerBackwardReferenceIndex {
if (versionDiffers(buildDir)) {
saveVersion(buildDir);
}
if (myReadOnly) logModification("open");
myFilePathEnumerator = new PersistentStringEnumerator(new File(myIndicesDir, FILE_ENUM_TAB)) {
@Override
public int enumerate(@Nullable String value) throws IOException {
@@ -123,6 +125,7 @@ public class CompilerBackwardReferenceIndex {
for (InvertedIndex<?, ?, CompiledFileData> index : myIndices.values()) {
close(index, exceptionProc);
}
if (myReadOnly) logModification("close");
final Exception exception = exceptionProc.getFoundValue();
if (exception != null) {
removeIndexFiles(myIndicesDir);
@@ -199,6 +202,20 @@ public class CompilerBackwardReferenceIndex {
myRebuildRequestCause = e;
}
private void logModification(String state) {
try {
File[] files = myIndicesDir.listFiles();
if (files != null) {
LOG.info("indices state on " +
state +
": " +
Arrays.stream(files).map(f -> f.getName() + ":" + f.lastModified()).collect(Collectors.joining(", ")));
}
}
catch (Exception ignored) {
}
}
private static void close(InvertedIndex<?, ?, CompiledFileData> index, CommonProcessors.FindFirstProcessor<Exception> exceptionProcessor) {
try {
index.dispose();
@@ -27,19 +27,13 @@ import org.jetbrains.jps.model.module.JpsModule
* @author nik
*/
@CompileStatic
abstract class BuildContext {
AntBuilder ant
BuildMessages messages
BuildPaths paths
JpsProject project
abstract class BuildContext implements CompilationContext {
ApplicationInfoProperties applicationInfo
JpsGantProjectBuilder projectBuilder
ProductProperties productProperties
WindowsDistributionCustomizer windowsDistributionCustomizer
LinuxDistributionCustomizer linuxDistributionCustomizer
MacDistributionCustomizer macDistributionCustomizer
ProprietaryBuildTools proprietaryBuildTools
BuildOptions options
BundledJreManager bundledJreManager
/**
@@ -77,14 +71,8 @@ abstract class BuildContext {
abstract void notifyArtifactBuilt(String artifactPath)
abstract File findApplicationInfoInSources()
abstract JpsModule findApplicationInfoModule()
abstract JpsModule findRequiredModule(String name)
abstract JpsModule findModule(String name)
abstract File findFileInModuleSources(String moduleName, String relativePath)
abstract void signExeFile(String path)
@@ -115,78 +103,3 @@ abstract class BuildContext {
abstract BuildContext createCopyForProduct(ProductProperties productProperties, String projectHomeForCustomizers)
}
/**
* All paths are absolute and use '/' as a separator
*/
abstract class BuildPaths {
/**
* Path to a directory where idea/community Git repository is checked out
*/
String communityHome
/**
* Path to a base directory of the project which will be compiled
*/
String projectHome
/**
* Path to a directory where build script will store temporary and resulting files
*/
String buildOutputRoot
/**
* Path to a directory where resulting artifacts will be placed
*/
String artifacts
/**
* Path to a directory containing distribution files ('bin', 'lib', 'plugins' directories) common for all operating systems
*/
String distAll
/**
* Path to a directory where temporary files required for a particular build step can be stored
*/
String temp
/**
* Path to a directory containing JDK (currently Java 8) which is used to compile the project
*/
String jdkHome
}
interface BuildMessages {
void info(String message)
void warning(String message)
/**
* Report an error and stop the build process
*/
void error(String message)
void error(String message, Throwable cause)
void progress(String message)
def <V> V block(String blockName, Closure<V> body)
void artifactBuild(String relativeArtifactPath)
BuildMessages forkForParallelTask(String taskName)
/**
* Must be invoked from the main thread when all forks have been finished
*/
void onAllForksFinished()
/**
* Must be invoked for the forked instance on the thread where it is executing before the task is started.
* It's required to correctly handle messages from Ant tasks.
*/
void onForkStarted()
/**
* Must be invoked for the forked instance on the thread where it is executing when the task is finished
*/
void onForkFinished()
}
@@ -0,0 +1,59 @@
/*
* Copyright 2000-2017 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.intellij.build
import groovy.transform.CompileStatic
/**
* @author nik
*/
@CompileStatic
interface BuildMessages {
void info(String message)
void warning(String message)
/**
* Report an error and stop the build process
*/
void error(String message)
void error(String message, Throwable cause)
void progress(String message)
def <V> V block(String blockName, Closure<V> body)
void artifactBuild(String relativeArtifactPath)
BuildMessages forkForParallelTask(String taskName)
/**
* Must be invoked from the main thread when all forks have been finished
*/
void onAllForksFinished()
/**
* Must be invoked for the forked instance on the thread where it is executing before the task is started.
* It's required to correctly handle messages from Ant tasks.
*/
void onForkStarted()
/**
* Must be invoked for the forked instance on the thread where it is executing when the task is finished
*/
void onForkFinished()
}
@@ -0,0 +1,61 @@
/*
* Copyright 2000-2017 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.intellij.build
import groovy.transform.CompileStatic
/**
* All paths are absolute and use '/' as a separator
*
* @author nik
*/
@CompileStatic
abstract class BuildPaths {
/**
* Path to a directory where idea/community Git repository is checked out
*/
String communityHome
/**
* Path to a base directory of the project which will be compiled
*/
String projectHome
/**
* Path to a directory where build script will store temporary and resulting files
*/
String buildOutputRoot
/**
* Path to a directory where resulting artifacts will be placed
*/
String artifacts
/**
* Path to a directory containing distribution files ('bin', 'lib', 'plugins' directories) common for all operating systems
*/
String distAll
/**
* Path to a directory where temporary files required for a particular build step can be stored
*/
String temp
/**
* Path to a directory containing JDK (currently Java 8) which is used to compile the project
*/
String jdkHome
}
@@ -0,0 +1,38 @@
/*
* Copyright 2000-2017 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.intellij.build
import groovy.transform.CompileStatic
import org.jetbrains.jps.gant.JpsGantProjectBuilder
import org.jetbrains.jps.model.JpsProject
import org.jetbrains.jps.model.module.JpsModule
/**
* @author nik
*/
@CompileStatic
interface CompilationContext {
AntBuilder getAnt()
BuildOptions getOptions()
BuildMessages getMessages()
BuildPaths getPaths()
JpsProject getProject()
JpsGantProjectBuilder getProjectBuilder()
JpsModule findRequiredModule(String name)
JpsModule findModule(String name)
}
@@ -0,0 +1,33 @@
/*
* Copyright 2000-2017 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.intellij.build
import groovy.transform.CompileStatic
import org.jetbrains.intellij.build.impl.CompilationTasksImpl
/**
* @author nik
*/
@CompileStatic
abstract class CompilationTasks {
abstract void compileAllModulesAndTests()
abstract void compileModules(List<String> moduleNames, List<String> includingTestsInModules = [])
static CompilationTasks create(CompilationContext context) {
return new CompilationTasksImpl(context)
}
}
@@ -26,11 +26,9 @@ import org.jetbrains.jps.model.JpsGlobal
import org.jetbrains.jps.model.JpsProject
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes
import org.jetbrains.jps.model.java.JavaSourceRootProperties
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsModule
import org.jetbrains.jps.model.serialization.JpsModelSerializationDataService
import org.jetbrains.jps.model.serialization.JpsProjectLoader
import org.jetbrains.jps.util.JpsPathUtil
import java.util.function.BiFunction
/**
* @author nik
@@ -38,61 +36,38 @@ import org.jetbrains.jps.util.JpsPathUtil
@CompileStatic
class BuildContextImpl extends BuildContext {
private final JpsGlobal global
private final CompilationContextImpl compilationContext
static BuildContextImpl create(AntBuilder ant, JpsGantProjectBuilder projectBuilder, JpsProject project, JpsGlobal global,
String communityHome, String projectHome, ProductProperties productProperties,
ProprietaryBuildTools proprietaryBuildTools, BuildOptions options) {
BuildMessages messages = BuildMessagesImpl.create(projectBuilder, ant.project)
communityHome = toCanonicalPath(communityHome)
if (["platform/build-scripts", "bin/log.xml", "build.txt"].any { !new File(communityHome, it).exists() }) {
messages.error("communityHome ($communityHome) doesn't point to a directory containing IntelliJ Community sources")
}
projectHome = toCanonicalPath(projectHome)
def jdk8Home = toCanonicalPath(JdkUtils.computeJdkHome(messages, "jdk8Home", "$projectHome/build/jdk/1.8", "JDK_18_x64"))
WindowsDistributionCustomizer windowsDistributionCustomizer = productProperties.createWindowsCustomizer(projectHome)
LinuxDistributionCustomizer linuxDistributionCustomizer = productProperties.createLinuxCustomizer(projectHome)
MacDistributionCustomizer macDistributionCustomizer = productProperties.createMacCustomizer(projectHome)
if (project.modules.isEmpty()) {
loadProject(communityHome, projectHome, jdk8Home, project, global, messages)
}
else {
//todo[nik] currently we need this to build IDEA CE from IDEA UI build scripts. It would be better to create a separate JpsProject instance instead
messages.info("Skipping loading project because it's already loaded")
}
def context = new BuildContextImpl(ant, messages, communityHome, projectHome, jdk8Home, project, global, projectBuilder, productProperties,
def compilationContext = CompilationContextImpl.create(ant, projectBuilder, project, global, communityHome, projectHome,
createBuildOutputRootEvaluator(projectHome, productProperties), options)
def context = new BuildContextImpl(compilationContext, productProperties,
windowsDistributionCustomizer, linuxDistributionCustomizer, macDistributionCustomizer,
proprietaryBuildTools, options)
context.prepareForBuild()
proprietaryBuildTools)
return context
}
private BuildContextImpl(AntBuilder ant, BuildMessages messages, String communityHome, String projectHome, String jdk8Home,
JpsProject project, JpsGlobal global, JpsGantProjectBuilder projectBuilder, ProductProperties productProperties,
private BuildContextImpl(CompilationContextImpl compilationContext, ProductProperties productProperties,
WindowsDistributionCustomizer windowsDistributionCustomizer,
LinuxDistributionCustomizer linuxDistributionCustomizer,
MacDistributionCustomizer macDistributionCustomizer,
ProprietaryBuildTools proprietaryBuildTools,
BuildOptions options) {
this.ant = ant
this.messages = messages
this.project = project
this.global = global
this.projectBuilder = projectBuilder
ProprietaryBuildTools proprietaryBuildTools) {
this.compilationContext = compilationContext
this.global = compilationContext.global
this.productProperties = productProperties
this.proprietaryBuildTools = proprietaryBuildTools
this.options = options
this.windowsDistributionCustomizer = windowsDistributionCustomizer
this.linuxDistributionCustomizer = linuxDistributionCustomizer
this.macDistributionCustomizer = macDistributionCustomizer
def appInfoFile = findApplicationInfoInSources()
def appInfoFile = findApplicationInfoInSources(project, productProperties, messages)
applicationInfo = new ApplicationInfoProperties(appInfoFile.absolutePath)
String buildOutputRoot = options.outputRootPath ?: "$projectHome/out/${productProperties.getOutputDirectoryName(applicationInfo)}"
paths = new BuildPathsImpl(communityHome, projectHome, buildOutputRoot, jdk8Home)
bundledJreManager = new BundledJreManager(this, paths.buildOutputRoot)
buildNumber = options.buildNumber ?: readSnapshotBuildNumber()
@@ -102,109 +77,23 @@ class BuildContextImpl extends BuildContext {
bootClassPathJarNames = ["bootstrap.jar", "extensions.jar", "util.jar", "jdom.jar", "log4j.jar", "trove4j.jar", "jna.jar"]
}
private static void loadProject(String communityHome, String projectHome, String jdkHome, JpsProject project, JpsGlobal global,
BuildMessages messages) {
def bundledKotlinPath = "$communityHome/build/kotlinc"
if (!new File(bundledKotlinPath, "lib/kotlin-runtime.jar").exists()) {
messages.error("Could not find Kotlin runtime at $bundledKotlinPath/lib/kotlin-runtime.jar: run download_kotlin.gant script to download Kotlin JARs")
}
JpsModelSerializationDataService.getOrCreatePathVariablesConfiguration(global).addPathVariable("KOTLIN_BUNDLED", bundledKotlinPath)
JdkUtils.defineJdk(global, "IDEA jdk", JdkUtils.computeJdkHome(messages, "jdkHome", "$projectHome/build/jdk/1.6", "JDK_16_x64"))
JdkUtils.defineJdk(global, "1.8", jdkHome)
def pathVariables = JpsModelSerializationDataService.computeAllPathVariables(global)
JpsProjectLoader.loadProject(project, pathVariables, projectHome)
messages.info("Loaded project $projectHome: ${project.modules.size()} modules, ${project.libraryCollection.libraries.size()} libraries")
}
private void prepareForBuild() {
checkCompilationOptions()
projectBuilder.buildIncrementally = options.incrementalCompilation
def dataDirName = options.incrementalCompilation ? ".jps-build-data-incremental" : ".jps-build-data"
projectBuilder.dataStorageRoot = new File(paths.buildOutputRoot, dataDirName)
def logDir = new File(paths.buildOutputRoot, "log")
FileUtil.delete(logDir)
projectBuilder.setupAdditionalLogging(new File("$logDir/compilation.log"), System.getProperty("intellij.build.debug.logging.categories", ""))
def classesDirName = "classes"
def classesOutput = "$paths.buildOutputRoot/$classesDirName"
List<String> outputDirectoriesToKeep = ["log"]
if (options.pathToCompiledClassesArchive != null) {
unpackCompiledClasses(messages, ant, classesOutput, options)
outputDirectoriesToKeep.add(classesDirName)
}
if (options.incrementalCompilation) {
outputDirectoriesToKeep.add(dataDirName)
outputDirectoriesToKeep.add(classesDirName)
}
if (!options.useCompiledClassesFromProjectOutput) {
projectBuilder.targetFolder = classesOutput
}
else {
def outputDir = JpsPathUtil.urlToFile(JpsJavaExtensionService.getInstance().getOrCreateProjectExtension(project).outputUrl)
if (!outputDir.exists()) {
messages.error("$BuildOptions.USE_COMPILED_CLASSES_PROPERTY is enabled, but the project output directory $outputDir.absolutePath doesn't exist")
}
}
suppressWarnings(project)
projectBuilder.exportModuleOutputProperties()
cleanOutput(outputDirectoriesToKeep)
}
void cleanOutput(List<String> outputDirectoriesToKeep) {
messages.block("Clean output") {
def outputPath = paths.buildOutputRoot
messages.progress("Cleaning output directory $outputPath")
new File(outputPath).listFiles()?.each { File file ->
if (outputDirectoriesToKeep.contains(file.name)) {
messages.info("Skipped cleaning for $file.absolutePath")
}
else {
FileUtil.delete(file)
}
}
}
}
@CompileDynamic
private static void unpackCompiledClasses(BuildMessages messages, AntBuilder ant, String classesOutput, BuildOptions options) {
messages.block("Unpack compiled classes archive") {
FileUtil.delete(new File(classesOutput))
ant.unzip(src: options.pathToCompiledClassesArchive, dest: classesOutput)
}
}
private void checkCompilationOptions() {
if (options.useCompiledClassesFromProjectOutput && options.incrementalCompilation) {
messages.warning("'${BuildOptions.USE_COMPILED_CLASSES_PROPERTY}' is specified, so 'incremental compilation' option will be ignored")
options.incrementalCompilation = false
}
if (options.pathToCompiledClassesArchive != null && options.incrementalCompilation) {
messages.warning("Paths to the compiled project output is specified, so 'incremental compilation' option will be ignored")
options.incrementalCompilation = false
}
if (options.pathToCompiledClassesArchive != null && options.useCompiledClassesFromProjectOutput) {
messages.warning("'${BuildOptions.USE_COMPILED_CLASSES_PROPERTY}' is specified, so the archive with compiled project output won't be used")
options.pathToCompiledClassesArchive = null
}
}
private static void suppressWarnings(JpsProject project) {
def compilerOptions = JpsJavaExtensionService.instance.getOrCreateCompilerConfiguration(project).currentCompilerOptions
compilerOptions.GENERATE_NO_WARNINGS = true
compilerOptions.DEPRECATION = false
compilerOptions.ADDITIONAL_OPTIONS_STRING = compilerOptions.ADDITIONAL_OPTIONS_STRING.replace("-Xlint:unchecked", "")
}
private String readSnapshotBuildNumber() {
new File(paths.communityHome, "build.txt").text.trim()
}
@Override
File findApplicationInfoInSources() {
JpsModule module = findApplicationInfoModule()
private static BiFunction<JpsProject, BuildMessages, String> createBuildOutputRootEvaluator(String projectHome, ProductProperties productProperties) {
return { JpsProject project, BuildMessages messages ->
def appInfoFile = findApplicationInfoInSources(project, productProperties, messages)
def applicationInfo = new ApplicationInfoProperties(appInfoFile.absolutePath)
return "$projectHome/out/${productProperties.getOutputDirectoryName(applicationInfo)}"
} as BiFunction<JpsProject, BuildMessages, String>
}
static File findApplicationInfoInSources(JpsProject project, ProductProperties productProperties, BuildMessages messages) {
JpsModule module = project.modules.find {it.name == productProperties.applicationInfoModule }
if (module == null) {
messages.error("Cannot find required '${productProperties.applicationInfoModule}' module")
}
def appInfoRelativePath = "idea/${productProperties.platformPrefix ?: ""}ApplicationInfo.xml"
def appInfoFile = module.sourceRoots.collect { new File(it.file, appInfoRelativePath) }.find { it.exists() }
if (appInfoFile == null) {
@@ -218,17 +107,43 @@ class BuildContextImpl extends BuildContext {
return findRequiredModule(productProperties.applicationInfoModule)
}
@Override
AntBuilder getAnt() {
compilationContext.ant
}
@Override
BuildOptions getOptions() {
compilationContext.options
}
@Override
BuildMessages getMessages() {
compilationContext.messages
}
@Override
BuildPaths getPaths() {
compilationContext.paths
}
@Override
JpsProject getProject() {
compilationContext.project
}
@Override
JpsGantProjectBuilder getProjectBuilder() {
compilationContext.projectBuilder
}
@Override
JpsModule findRequiredModule(String name) {
def module = findModule(name)
if (module == null) {
messages.error("Cannot find required module '$name' in the project")
}
return module
return compilationContext.findRequiredModule(name)
}
JpsModule findModule(String name) {
project.modules.find { it.name == name }
return compilationContext.findModule(name)
}
@Override
@@ -284,9 +199,10 @@ class BuildContextImpl extends BuildContext {
BuildContext forkForParallelTask(String taskName) {
def ant = new AntBuilder(ant.project)
def messages = messages.forkForParallelTask(taskName)
def child = new BuildContextImpl(ant, messages, paths.communityHome, paths.projectHome, paths.jdkHome, project, global, projectBuilder, productProperties,
def compilationContextCopy = compilationContext.createCopy(ant, messages, options, createBuildOutputRootEvaluator(compilationContext.paths.projectHome, productProperties))
def child = new BuildContextImpl(compilationContextCopy, productProperties,
windowsDistributionCustomizer, linuxDistributionCustomizer, macDistributionCustomizer,
proprietaryBuildTools, options)
proprietaryBuildTools)
child.paths.artifacts = paths.artifacts
child.bundledJreManager.baseDirectoryForJre = bundledJreManager.baseDirectoryForJre
return child
@@ -300,11 +216,12 @@ class BuildContextImpl extends BuildContext {
def options = new BuildOptions()
options.useCompiledClassesFromProjectOutput = true
def copy = new BuildContextImpl(ant, messages, paths.communityHome, paths.projectHome, paths.jdkHome, project, global, projectBuilder, productProperties,
windowsDistributionCustomizer, linuxDistributionCustomizer, macDistributionCustomizer, proprietaryBuildTools, options)
def compilationContextCopy = compilationContext.createCopy(ant, messages, options, createBuildOutputRootEvaluator(paths.projectHome, productProperties))
def copy = new BuildContextImpl(compilationContextCopy, productProperties,
windowsDistributionCustomizer, linuxDistributionCustomizer, macDistributionCustomizer, proprietaryBuildTools)
copy.paths.artifacts = paths.artifacts
copy.bundledJreManager.baseDirectoryForJre = bundledJreManager.baseDirectoryForJre
copy.prepareForBuild()
copy.compilationContext.prepareForBuild()
return copy
}
@@ -362,21 +279,4 @@ class BuildContextImpl extends BuildContext {
}
messages.artifactBuild(relativePath)
}
private static String toCanonicalPath(String communityHome) {
FileUtil.toSystemIndependentName(new File(communityHome).canonicalPath)
}
}
class BuildPathsImpl extends BuildPaths {
BuildPathsImpl(String communityHome, String projectHome, String buildOutputRoot, String jdkHome) {
this.communityHome = communityHome
this.projectHome = projectHome
this.buildOutputRoot = buildOutputRoot
this.jdkHome = jdkHome
artifacts = "$buildOutputRoot/artifacts"
distAll = "$buildOutputRoot/dist.all"
temp = "$buildOutputRoot/temp"
}
}
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.intellij.build.BuildContext
import org.jetbrains.intellij.build.BuildOptions
import org.jetbrains.intellij.build.BuildTasks
import org.jetbrains.intellij.build.CompilationTasks
import org.jetbrains.intellij.build.ProductModulesLayout
import org.jetbrains.jps.model.java.JavaResourceRootType
import org.jetbrains.jps.model.java.JavaSourceRootType
@@ -182,7 +183,7 @@ idea.fatal.error.notification=disabled
}
File patchApplicationInfo() {
def sourceFile = buildContext.findApplicationInfoInSources()
def sourceFile = BuildContextImpl.findApplicationInfoInSources(buildContext.project, buildContext.productProperties, buildContext.messages)
def targetFile = new File(buildContext.paths.temp, sourceFile.name)
def date = ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("uuuuMMddHHmm"))
BuildUtils.copyAndPatchFile(sourceFile.path, targetFile.path,
@@ -244,8 +245,14 @@ idea.fatal.error.notification=disabled
checkProductProperties()
def patchedApplicationInfo = patchApplicationInfo()
def distributionJARsBuilder = new DistributionJARsBuilder(buildContext, patchedApplicationInfo)
compileModules(buildContext.productProperties.productLayout.includedPluginModules + distributionJARsBuilder.platformModules +
buildContext.productProperties.additionalModulesToCompile, buildContext.productProperties.modulesToCompileTests)
compileModulesForDistribution(distributionJARsBuilder)
}
private compileModulesForDistribution(DistributionJARsBuilder distributionJARsBuilder) {
def moduleNames = buildContext.productProperties.productLayout.includedPluginModules + distributionJARsBuilder.platformModules +
buildContext.productProperties.additionalModulesToCompile +
(buildContext.proprietaryBuildTools.scrambleTool?.additionalModulesToCompile ?: [])
compileModules(moduleNames, buildContext.productProperties.modulesToCompileTests)
}
@Override
@@ -254,8 +261,7 @@ idea.fatal.error.notification=disabled
def patchedApplicationInfo = patchApplicationInfo()
def distributionJARsBuilder = new DistributionJARsBuilder(buildContext, patchedApplicationInfo)
compileModules(buildContext.productProperties.productLayout.includedPluginModules + distributionJARsBuilder.platformModules +
buildContext.productProperties.additionalModulesToCompile, buildContext.productProperties.modulesToCompileTests)
compileModulesForDistribution(distributionJARsBuilder)
buildContext.messages.block("Build platform and plugin JARs") {
if (buildContext.shouldBuildDistributions()) {
distributionJARsBuilder.buildJARs()
@@ -410,56 +416,7 @@ idea.fatal.error.notification=disabled
@Override
void compileModules(List<String> moduleNames, List<String> includingTestsInModules = []) {
if (buildContext.options.useCompiledClassesFromProjectOutput) {
buildContext.messages.info("Compilation skipped, the compiled classes from the project output will be used")
return
}
if (buildContext.options.pathToCompiledClassesArchive != null) {
buildContext.messages.info("Compilation skipped, the compiled classes from '${buildContext.options.pathToCompiledClassesArchive}' will be used")
return
}
ensureKotlinCompilerAddedToClassPath()
buildContext.projectBuilder.cleanOutput()
try {
if (moduleNames == null) {
buildContext.projectBuilder.buildProduction()
}
else {
List<String> modulesToBuild = ((moduleNames as Set<String>) +
buildContext.proprietaryBuildTools.scrambleTool?.additionalModulesToCompile ?: []) as List<String>
List<String> invalidModules = modulesToBuild.findAll {buildContext.findModule(it) == null}
if (!invalidModules.empty) {
buildContext.messages.warning("The following modules won't be compiled: $invalidModules")
}
buildContext.projectBuilder.buildModules(modulesToBuild.collect {buildContext.findModule(it)}.findAll {it != null})
}
for (String moduleName : includingTestsInModules) {
buildContext.projectBuilder.makeModuleTests(buildContext.findModule(moduleName))
}
}
catch (Throwable e) {
buildContext.messages.error("Compilation failed with exception: $e", e)
}
}
private void ensureKotlinCompilerAddedToClassPath() {
try {
Class.forName("org.jetbrains.kotlin.jps.build.KotlinBuilder")
return
}
catch (ClassNotFoundException ignored) {}
def kotlinPluginLibPath = "$buildContext.paths.communityHome/build/kotlinc/plugin/Kotlin/lib"
if (new File(kotlinPluginLibPath).exists()) {
["jps/kotlin-jps-plugin.jar", "kotlin-plugin.jar", "kotlin-runtime.jar", "kotlin-reflect.jar"].each {
BuildUtils.addToJpsClassPath("$kotlinPluginLibPath/$it", buildContext.ant)
}
}
else {
buildContext.messages.error("Could not find Kotlin JARs at $kotlinPluginLibPath: run download_kotlin.gant script to download them")
}
CompilationTasks.create(buildContext).compileModules(moduleNames, includingTestsInModules)
}
private <V> List<V> runInParallel(List<BuildTaskRunnable<V>> tasks) {
@@ -0,0 +1,218 @@
/*
* Copyright 2000-2017 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.intellij.build.impl
import com.intellij.openapi.util.io.FileUtil
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import org.jetbrains.intellij.build.BuildMessages
import org.jetbrains.intellij.build.BuildOptions
import org.jetbrains.intellij.build.BuildPaths
import org.jetbrains.intellij.build.CompilationContext
import org.jetbrains.jps.gant.JpsGantProjectBuilder
import org.jetbrains.jps.model.JpsGlobal
import org.jetbrains.jps.model.JpsProject
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsModule
import org.jetbrains.jps.model.serialization.JpsModelSerializationDataService
import org.jetbrains.jps.model.serialization.JpsProjectLoader
import org.jetbrains.jps.util.JpsPathUtil
import java.util.function.BiFunction
/**
* @author nik
*/
@CompileStatic
class CompilationContextImpl implements CompilationContext {
final AntBuilder ant
final BuildOptions options
final BuildMessages messages
final BuildPaths paths
final JpsProject project
final JpsGlobal global
final JpsGantProjectBuilder projectBuilder
static CompilationContextImpl create(AntBuilder ant, JpsGantProjectBuilder projectBuilder, JpsProject project, JpsGlobal global,
String communityHome, String projectHome,
BiFunction<JpsProject, BuildMessages, String> buildOutputRootEvaluator, BuildOptions options) {
def messages = BuildMessagesImpl.create(projectBuilder, ant.project)
communityHome = toCanonicalPath(communityHome)
if (["platform/build-scripts", "bin/log.xml", "build.txt"].any { !new File(communityHome, it).exists() }) {
messages.error("communityHome ($communityHome) doesn't point to a directory containing IntelliJ Community sources")
}
projectHome = toCanonicalPath(projectHome)
def jdk8Home = toCanonicalPath(JdkUtils.computeJdkHome(messages, "jdk8Home", "$projectHome/build/jdk/1.8", "JDK_18_x64"))
if (project.modules.isEmpty()) {
loadProject(communityHome, projectHome, jdk8Home, project, global, messages)
}
else {
//todo[nik] currently we need this to build IDEA CE from IDEA UI build scripts. It would be better to create a separate JpsProject instance instead
messages.info("Skipping loading project because it's already loaded")
}
def context = new CompilationContextImpl(ant, projectBuilder, project, global, communityHome, projectHome, jdk8Home, messages,
buildOutputRootEvaluator, options)
context.prepareForBuild()
return context
}
private CompilationContextImpl(AntBuilder ant, JpsGantProjectBuilder projectBuilder, JpsProject project, JpsGlobal global,
String communityHome, String projectHome, String jdk8Home, BuildMessages messages,
BiFunction<JpsProject, BuildMessages, String> buildOutputRootEvaluator, BuildOptions options) {
this.ant = ant
this.project = project
this.global = global
this.options = options
this.projectBuilder = projectBuilder
this.messages = messages
String buildOutputRoot = options.outputRootPath ?: buildOutputRootEvaluator.apply(project, messages)
this.paths = new BuildPathsImpl(communityHome, projectHome, buildOutputRoot, jdk8Home)
}
CompilationContextImpl createCopy(AntBuilder ant, BuildMessages messages, BuildOptions options,
BiFunction<JpsProject, BuildMessages, String> buildOutputRootEvaluator) {
return new CompilationContextImpl(ant, projectBuilder, project, global, paths.communityHome, paths.projectHome, paths.jdkHome,
messages, buildOutputRootEvaluator, options)
}
private static void loadProject(String communityHome, String projectHome, String jdkHome, JpsProject project, JpsGlobal global,
BuildMessages messages) {
def bundledKotlinPath = "$communityHome/build/kotlinc"
if (!new File(bundledKotlinPath, "lib/kotlin-runtime.jar").exists()) {
messages.error(
"Could not find Kotlin runtime at $bundledKotlinPath/lib/kotlin-runtime.jar: run download_kotlin.gant script to download Kotlin JARs")
}
JpsModelSerializationDataService.getOrCreatePathVariablesConfiguration(global).addPathVariable("KOTLIN_BUNDLED", bundledKotlinPath)
JdkUtils.defineJdk(global, "IDEA jdk", JdkUtils.computeJdkHome(messages, "jdkHome", "$projectHome/build/jdk/1.6", "JDK_16_x64"))
JdkUtils.defineJdk(global, "1.8", jdkHome)
def pathVariables = JpsModelSerializationDataService.computeAllPathVariables(global)
JpsProjectLoader.loadProject(project, pathVariables, projectHome)
messages.info("Loaded project $projectHome: ${project.modules.size()} modules, ${project.libraryCollection.libraries.size()} libraries")
}
void prepareForBuild() {
checkCompilationOptions()
projectBuilder.buildIncrementally = options.incrementalCompilation
def dataDirName = options.incrementalCompilation ? ".jps-build-data-incremental" : ".jps-build-data"
projectBuilder.dataStorageRoot = new File(paths.buildOutputRoot, dataDirName)
def logDir = new File(paths.buildOutputRoot, "log")
FileUtil.delete(logDir)
projectBuilder.setupAdditionalLogging(new File("$logDir/compilation.log"), System.getProperty("intellij.build.debug.logging.categories", ""))
def classesDirName = "classes"
def classesOutput = "$paths.buildOutputRoot/$classesDirName"
List<String> outputDirectoriesToKeep = ["log"]
if (options.pathToCompiledClassesArchive != null) {
unpackCompiledClasses(messages, ant, classesOutput, options)
outputDirectoriesToKeep.add(classesDirName)
}
if (options.incrementalCompilation) {
outputDirectoriesToKeep.add(dataDirName)
outputDirectoriesToKeep.add(classesDirName)
}
if (!options.useCompiledClassesFromProjectOutput) {
projectBuilder.targetFolder = classesOutput
}
else {
def outputDir = JpsPathUtil.urlToFile(JpsJavaExtensionService.getInstance().getOrCreateProjectExtension(project).outputUrl)
if (!outputDir.exists()) {
messages.error("$BuildOptions.USE_COMPILED_CLASSES_PROPERTY is enabled, but the project output directory $outputDir.absolutePath doesn't exist")
}
}
suppressWarnings(project)
projectBuilder.exportModuleOutputProperties()
cleanOutput(outputDirectoriesToKeep)
}
void cleanOutput(List<String> outputDirectoriesToKeep) {
messages.block("Clean output") {
def outputPath = paths.buildOutputRoot
messages.progress("Cleaning output directory $outputPath")
new File(outputPath).listFiles()?.each { File file ->
if (outputDirectoriesToKeep.contains(file.name)) {
messages.info("Skipped cleaning for $file.absolutePath")
}
else {
FileUtil.delete(file)
}
}
}
}
@CompileDynamic
private static void unpackCompiledClasses(BuildMessages messages, AntBuilder ant, String classesOutput, BuildOptions options) {
messages.block("Unpack compiled classes archive") {
FileUtil.delete(new File(classesOutput))
ant.unzip(src: options.pathToCompiledClassesArchive, dest: classesOutput)
}
}
private void checkCompilationOptions() {
if (options.useCompiledClassesFromProjectOutput && options.incrementalCompilation) {
messages.warning("'${BuildOptions.USE_COMPILED_CLASSES_PROPERTY}' is specified, so 'incremental compilation' option will be ignored")
options.incrementalCompilation = false
}
if (options.pathToCompiledClassesArchive != null && options.incrementalCompilation) {
messages.warning("Paths to the compiled project output is specified, so 'incremental compilation' option will be ignored")
options.incrementalCompilation = false
}
if (options.pathToCompiledClassesArchive != null && options.useCompiledClassesFromProjectOutput) {
messages.warning("'${BuildOptions.USE_COMPILED_CLASSES_PROPERTY}' is specified, so the archive with compiled project output won't be used")
options.pathToCompiledClassesArchive = null
}
}
private static void suppressWarnings(JpsProject project) {
def compilerOptions = JpsJavaExtensionService.instance.getOrCreateCompilerConfiguration(project).currentCompilerOptions
compilerOptions.GENERATE_NO_WARNINGS = true
compilerOptions.DEPRECATION = false
compilerOptions.ADDITIONAL_OPTIONS_STRING = compilerOptions.ADDITIONAL_OPTIONS_STRING.replace("-Xlint:unchecked", "")
}
@Override
JpsModule findRequiredModule(String name) {
def module = findModule(name)
if (module == null) {
messages.error("Cannot find required module '$name' in the project")
}
return module
}
JpsModule findModule(String name) {
project.modules.find { it.name == name }
}
private static String toCanonicalPath(String communityHome) {
FileUtil.toSystemIndependentName(new File(communityHome).canonicalPath)
}
}
class BuildPathsImpl extends BuildPaths {
BuildPathsImpl(String communityHome, String projectHome, String buildOutputRoot, String jdkHome) {
this.communityHome = communityHome
this.projectHome = projectHome
this.buildOutputRoot = buildOutputRoot
this.jdkHome = jdkHome
artifacts = "$buildOutputRoot/artifacts"
distAll = "$buildOutputRoot/dist.all"
temp = "$buildOutputRoot/temp"
}
}
@@ -0,0 +1,97 @@
/*
* Copyright 2000-2017 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.intellij.build.impl
import groovy.transform.CompileStatic
import org.jetbrains.intellij.build.CompilationContext
import org.jetbrains.intellij.build.CompilationTasks
/**
* @author nik
*/
@CompileStatic
class CompilationTasksImpl extends CompilationTasks {
private final CompilationContext context
CompilationTasksImpl(CompilationContext context) {
this.context = context
}
@Override
void compileModules(List<String> moduleNames, List<String> includingTestsInModules) {
if (context.options.useCompiledClassesFromProjectOutput) {
context.messages.info("Compilation skipped, the compiled classes from the project output will be used")
return
}
if (context.options.pathToCompiledClassesArchive != null) {
context.messages.info("Compilation skipped, the compiled classes from '${context.options.pathToCompiledClassesArchive}' will be used")
return
}
ensureKotlinCompilerAddedToClassPath()
context.projectBuilder.cleanOutput()
try {
if (moduleNames == null) {
if (includingTestsInModules == null) {
context.projectBuilder.buildAll()
}
else {
context.projectBuilder.buildProduction()
}
}
else {
List<String> invalidModules = moduleNames.findAll { context.findModule(it) == null }
if (!invalidModules.empty) {
context.messages.warning("The following modules won't be compiled: $invalidModules")
}
context.projectBuilder.buildModules(moduleNames.collect { context.findModule(it) }.findAll { it != null })
}
if (includingTestsInModules != null) {
for (String moduleName : includingTestsInModules) {
context.projectBuilder.makeModuleTests(context.findModule(moduleName))
}
}
}
catch (Throwable e) {
context.messages.error("Compilation failed with exception: $e", e)
}
}
@Override
void compileAllModulesAndTests() {
compileModules(null, null)
}
private void ensureKotlinCompilerAddedToClassPath() {
try {
Class.forName("org.jetbrains.kotlin.jps.build.KotlinBuilder")
return
}
catch (ClassNotFoundException ignored) {
}
def kotlinPluginLibPath = "$context.paths.communityHome/build/kotlinc/plugin/Kotlin/lib"
if (new File(kotlinPluginLibPath).exists()) {
["jps/kotlin-jps-plugin.jar", "kotlin-plugin.jar", "kotlin-runtime.jar", "kotlin-reflect.jar"].each {
BuildUtils.addToJpsClassPath("$kotlinPluginLibPath/$it", context.ant)
}
}
else {
context.messages.error("Could not find Kotlin JARs at $kotlinPluginLibPath: run download_kotlin.gant script to download them")
}
}
}
@@ -146,7 +146,8 @@ public class ClipboardSynchronizer implements ApplicationComponentAdapter, Dispo
return ClipboardUtil.handleClipboardSafely(myClipboardHandler::getContents, () -> null);
}
public Object getData(DataFlavor dataFlavor) {
@Nullable
public Object getData(@NotNull DataFlavor dataFlavor) {
return ClipboardUtil.handleClipboardSafely(() -> {
try {
return myClipboardHandler.getData(dataFlavor);
@@ -204,7 +205,8 @@ public class ClipboardSynchronizer implements ApplicationComponentAdapter, Dispo
return clipboard == null ? null: clipboard.getContents(this);
}
public Object getData(DataFlavor dataFlavor) throws IOException, UnsupportedFlavorException {
@Nullable
public Object getData(@NotNull DataFlavor dataFlavor) throws IOException, UnsupportedFlavorException {
Clipboard clipboard = getClipboard();
return clipboard == null ? null : clipboard.getData(dataFlavor);
}
@@ -256,7 +258,8 @@ public class ClipboardSynchronizer implements ApplicationComponentAdapter, Dispo
}
@Override
public Object getData(DataFlavor dataFlavor) throws IOException, UnsupportedFlavorException {
@Nullable
public Object getData(@NotNull DataFlavor dataFlavor) throws IOException, UnsupportedFlavorException {
if (myFullTransferable == null) return super.getData(dataFlavor);
Transferable contents = getContents();
return contents == null ? null : contents.getTransferData(dataFlavor);
@@ -371,22 +374,12 @@ public class ClipboardSynchronizer implements ApplicationComponentAdapter, Dispo
return ClipboardSynchronizer.areDataFlavorsAvailable(currentContent, flavors);
}
try {
Collection<DataFlavor> contents = checkContentsQuick();
if (contents != null) {
return ClipboardSynchronizer.areDataFlavorsAvailable(contents, flavors);
}
Collection<DataFlavor> contents = checkContentsQuick();
if (contents != null) {
return ClipboardSynchronizer.areDataFlavorsAvailable(contents, flavors);
}
return super.areDataFlavorsAvailable(flavors);
}
catch (NullPointerException e) {
LOG.warn("Java bug #6322854", e);
return false;
}
catch (IllegalArgumentException e) {
LOG.warn("Java bug #7173464", e);
return false;
}
return super.areDataFlavorsAvailable(flavors);
}
@Override
@@ -396,47 +389,28 @@ public class ClipboardSynchronizer implements ApplicationComponentAdapter, Dispo
return currentContent;
}
try {
final Collection<DataFlavor> contents = checkContentsQuick();
if (contents != null && contents.isEmpty()) {
return null;
}
Collection<DataFlavor> contents = checkContentsQuick();
if (contents != null && contents.isEmpty()) {
return null;
}
return super.getContents();
}
catch (NullPointerException e) {
LOG.warn("Java bug #6322854", e);
return null;
}
catch (IllegalArgumentException e) {
LOG.warn("Java bug #7173464", e);
return null;
}
return super.getContents();
}
@Override
public Object getData(DataFlavor dataFlavor) throws IOException, UnsupportedFlavorException {
@Nullable
public Object getData(@NotNull DataFlavor dataFlavor) throws IOException, UnsupportedFlavorException {
Transferable currentContent = myCurrentContent;
if (currentContent != null) {
return currentContent.getTransferData(dataFlavor);
}
try {
final Collection<DataFlavor> contents = checkContentsQuick();
if (contents != null && contents.isEmpty()) {
return null;
}
Collection<DataFlavor> contents = checkContentsQuick();
if (contents != null && !contents.contains(dataFlavor)) {
return null;
}
return super.getData(dataFlavor);
}
catch (NullPointerException e) {
LOG.warn("Java bug #6322854", e);
return null;
}
catch (IllegalArgumentException e) {
LOG.warn("Java bug #7173464", e);
return null;
}
return super.getData(dataFlavor);
}
@Override
@@ -508,7 +482,8 @@ public class ClipboardSynchronizer implements ApplicationComponentAdapter, Dispo
}
@Override
public Object getData(DataFlavor dataFlavor) throws IOException, UnsupportedFlavorException {
@Nullable
public Object getData(@NotNull DataFlavor dataFlavor) throws IOException, UnsupportedFlavorException {
return myContent.getTransferData(dataFlavor);
}
@@ -38,8 +38,14 @@ public class ClipboardUtil {
else {
LOG.warn(e);
}
return onFail.get();
}
catch (NullPointerException e) {
LOG.warn("Java bug #6322854", e);
}
catch (IllegalArgumentException e) {
LOG.warn("Java bug #7173464", e);
}
return onFail.get();
}
private static final String USE_LEGACY_MERGE_SORT_PROPERTY_NAME = "java.util.Arrays.useLegacyMergeSort";
@@ -38,7 +38,6 @@ public class EduNames {
public static final String TESTS_FILE = "tests.py";
public static final String TEST_HELPER = "test_helper.py";
public static final String SANDBOX_DIR = "Sandbox";
public static final String COURSE_META_FILE = "course.json";
public static final String PYCHARM_ADDITIONAL = "PyCharm additional materials";
public static final String PYCHARM = "PyCharm";
@@ -109,7 +109,6 @@ public class StudyGenerator {
lesson.setIndex(i);
createLesson(lesson, baseDir, resourceRoot, project);
}
baseDir.createChildDirectory(project, EduNames.SANDBOX_DIR);
File[] files = resourceRoot.listFiles(
(dir, name) -> !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS));
if (files != null) {
@@ -43,9 +43,6 @@ public class CourseDirectoryNode extends StudyDirectoryNode {
Object value = childNode.getValue();
if (value instanceof PsiDirectory) {
PsiDirectory directory = (PsiDirectory)value;
if (EduNames.SANDBOX_DIR.equals(directory.getName())) {
return new SandboxDirectoryNode(myProject, directory, myViewSettings);
}
Lesson lesson = myCourse.getLesson(directory.getName());
return lesson != null ? createChildDirectoryNode(lesson, directory) : null;
}
@@ -1,42 +0,0 @@
package com.jetbrains.edu.learning.projectView;
import com.intellij.ide.projectView.PresentationData;
import com.intellij.ide.projectView.ViewSettings;
import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDirectory;
import com.jetbrains.edu.learning.core.EduNames;
import com.jetbrains.edu.learning.courseFormat.StudyItem;
import icons.InteractiveLearningIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SandboxDirectoryNode extends StudyDirectoryNode {
public SandboxDirectoryNode(@NotNull Project project,
PsiDirectory value,
ViewSettings viewSettings) {
super(project, value, viewSettings);
}
@Nullable
@Override
public AbstractTreeNode modifyChildNode(AbstractTreeNode childNode) {
return childNode;
}
@Override
public PsiDirectoryNode createChildDirectoryNode(StudyItem item, PsiDirectory value) {
return null;
}
@Override
protected void updateImpl(PresentationData data) {
setPresentation(data, EduNames.SANDBOX_DIR, InteractiveLearningIcons.Sandbox);
}
@Override
public int getWeight() {
return Integer.MAX_VALUE;
}
}
@@ -58,7 +58,7 @@ public class HtmlCompletionContributor extends CompletionContributor {
});
}
private static boolean hasHtmlAttributesCompletion(PsiElement position) {
public static boolean hasHtmlAttributesCompletion(PsiElement position) {
if (PsiTreeUtil.getParentOfType(position, HtmlTag.class, false) != null) {
return true;
}