build scripts redesign: use groovy instead of gant, common code extracted, pass data between tasks explicitly, improved logging, allow to skip some build steps via system property

This commit is contained in:
nik
2016-07-12 10:53:01 +03:00
parent 3d4ddffb66
commit 82c2b625c0
20 changed files with 1768 additions and 283 deletions
-1
View File
@@ -1,4 +1,3 @@
#---------------------------------------------------------------------
# IDEA can copy library .jar files to prevent their locking.
# By default this behavior is enabled on Windows and disabled on other platforms.
@@ -14,21 +14,25 @@
* limitations under the License.
*/
package org.jetbrains.intellij.build
import groovy.transform.Immutable
/**
* @author nik
*/
class ApplicationInfoProperties {
final String majorVersion
final String minorVersion
final String shortProductName
final String productName
final String companyName
final boolean isEAP
@SuppressWarnings("GrUnresolvedAccess")
ApplicationInfoProperties(String appInfoXmlPath) {
def root = new XmlParser().parse(new File(appInfoXmlPath))
majorVersion = root.version.first().@major
minorVersion = root.version.first().@minor
shortProductName = root.names.first().@product
productName = root.names.first().@fullname
companyName = root.company.first().@name
isEAP = Boolean.parseBoolean(root.version.first().@eap)
}
}
@@ -0,0 +1,134 @@
/*
* Copyright 2000-2016 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 org.codehaus.gant.GantBuilder
import org.jetbrains.intellij.build.impl.BuildContextImpl
import org.jetbrains.jps.gant.JpsGantProjectBuilder
import org.jetbrains.jps.model.JpsGlobal
import org.jetbrains.jps.model.JpsProject
import org.jetbrains.jps.model.module.JpsModule
/**
* @author nik
*/
abstract class BuildContext {
GantBuilder ant
BuildMessages messages
BuildPaths paths
JpsProject project
ApplicationInfoProperties applicationInfo
JpsGantProjectBuilder projectBuilder
ProductProperties productProperties
BuildOptions options
/**
* Build number without product code (e.g. '162.500.10')
*/
String buildNumber
/**
* Build number with product code (e.g. 'IC-162.500.10')
*/
String fullBuildNumber
/**
* An identifier which will be used to form names for directories where configuration and caches will be stored, usually a product name
* without spaces with added major version ('IntelliJIdea2016.1' for IntelliJ IDEA 2016.1)
*/
String systemSelector
/**
* Base name for script files (*.bat, *.sh, *.exe), usually a shortened product name in lower case (e.g. 'idea' for IntelliJ IDEA, 'datagrip' for DataGrip)
*/
String fileNamePrefix
/**
* Names of JARs inside IDE_HOME/lib directory which need to be added to bootclasspath to start the IDE
*/
List<String> bootClassPathJarNames
abstract void notifyArtifactBuilt(String artifactPath)
abstract File findApplicationInfoInSources()
abstract JpsModule findApplicationInfoModule()
abstract JpsModule findModule(String name)
abstract void executeStep(String stepMessage, String stepId, Closure step)
public static BuildContext createContext(GantBuilder ant, JpsGantProjectBuilder projectBuilder, JpsProject project, JpsGlobal global,
String communityHome, String projectHome, String buildOutputRoot, ProductProperties productProperties,
BuildOptions options = new BuildOptions()) {
return new BuildContextImpl(ant, projectBuilder, project, global, communityHome, projectHome, buildOutputRoot, productProperties, options)
}
}
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 steps can be stored
*/
String temp
/**
* Path to a directory containing distribution of JRE for Windows which will be bundled with the product
*/
String winJre
/**
* Path to a directory containing distribution of JRE for Linux which will be bundled with the product
*/
String linuxJre
}
interface BuildMessages {
void info(String message)
void warning(String message)
/**
* Report an error and stop the build process
*/
void error(String message)
void progress(String message)
void block(String blockName, Closure body)
}
@@ -0,0 +1,41 @@
/*
* Copyright 2000-2016 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 com.intellij.util.SystemProperties
/**
* @author nik
*/
class BuildOptions {
/**
* By default build scripts compile project classes to a special output directory (to not interfere with the default project output if
* invoked on a developer machine). Pass 'true' to this system property to skip compilation step and use compiled classes from the project output instead.
*/
public static final String USE_COMPILED_CLASSES_PROPERTY = "intellij.build.useCompiledClasses"
boolean useCompiledClassesFromProjectOutput = SystemProperties.getBooleanProperty(USE_COMPILED_CLASSES_PROPERTY, false)
/**
* Pass comma-separated names of build steps (see below) to 'intellij.build.skipBuildSteps' system property to skip them when building locally.
*/
Set<String> buildStepsToSkip = System.getProperty("intellij.build.skipBuildSteps", "").split(",") as Set<String>
static final SEARCHABLE_OPTIONS_INDEX_STEP = "search_index"
static final SOURCES_ARCHIVE_STEP = "sources_archive"
static final MAC_DISTRIBUTION_STEP = "mac_dist"
static final LINUX_DISTRIBUTION_STEP = "linux_dist"
static final WINDOWS_DISTRIBUTION_STEP = "windows_dist"
static final CROSS_PLATFORM_DISTRIBUTION_STEP = "cross_platform_dist"
}
@@ -0,0 +1,52 @@
/*
* Copyright 2000-2016 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 org.jetbrains.intellij.build.impl.BuildTasksImpl
/**
* @author nik
*/
abstract class BuildTasks {
/**
* Build archive of the project source files keeping the original layout
*/
abstract void zipSources()
/**
* Update search/searchableOptions.xml file in {@code targetModuleName} module output directory
*/
abstract void buildSearchableOptions(String targetModuleName, List<String> modulesToIndex, List<String> pathsToLicenses)
/**
* Create a copy of *ApplicationInfo.xml file with substituted __BUILD_NUMBER__ and __BUILD_DATE__ placeholders
* @return path to the copied file
*/
abstract File patchApplicationInfo()
/**
* Create distribution for all operating system from JAR files located at {@link BuildPaths#distAll}
*/
abstract void buildDistributions()
abstract void cleanOutput()
abstract void compileProjectAndTests(List<String> includingTestsInModules = [])
public static BuildTasks create(BuildContext context) {
return new BuildTasksImpl(context)
}
}
@@ -22,43 +22,40 @@ public abstract class ProductProperties {
String prefix
String code
String appInfoModule
String appInfoModulePath
String customInspectScriptName
abstract def String appInfoFile()
/**
* @return build number with product code (e.g. IC-142.239 for IDEA Community)
* Return {@code true} if tools.jar from JDK must be added to IDE's classpath
*/
abstract def String fullBuildNumber()
boolean toolsJarRequired = false
abstract def String systemSelector()
abstract def String systemSelector(ApplicationInfoProperties applicationInfo)
String additionalIDEPropertiesFilePath
String exe_launcher_properties
String exe64_launcher_properties
String platformPrefix = null
String bundleIdentifier
abstract def String macAppRoot()
abstract def String macAppRoot(ApplicationInfoProperties applicationInfo, String buildNumber)
abstract def String winAppRoot()
abstract def String winAppRoot(String buildNumber)
abstract def String linuxAppRoot()
abstract def String linuxAppRoot(String buildNumber)
abstract def String archiveName()
abstract def String archiveName(String buildNumber)
boolean setPluginAndIDEVersionInPluginXml = true
String ideJvmArgs = null
String ideJvmArgs = ""
boolean maySkipAndroidPlugin
String relativeAndroidHome
String relativeAndroidToolsBaseHome
boolean includeYourkitAgentInEAP = false
boolean includeBatchLauncher = true
boolean buildUpdater = false
List<String> excludedPlugins = []
List<String> extraMacBins = []
List<String> extraLinuxBins = []
def customLayout(targetDirectory) {}
@@ -71,4 +68,28 @@ public abstract class ProductProperties {
String icon128
String ico
String icns
WindowsProductProperties windows = new WindowsProductProperties()
MacProductProperties mac = new MacProductProperties()
LinuxProductProperties linux = new LinuxProductProperties()
}
class WindowsProductProperties {
boolean includeBatchLauncher = true
boolean bundleJre = true
}
class MacProductProperties {
String minOSXVersion = "10.8"
String helpId = ""
String docTypes = null
List<String> urlSchemes = []
List<String> architectures = ["x86_64"]
boolean includeYourkitAgentInEAP = true
List<String> extraMacBins = []
String bundleIdentifier
}
class LinuxProductProperties {
List<String> extraLinuxBins = []
}
@@ -0,0 +1,175 @@
/*
* Copyright 2000-2016 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 com.intellij.openapi.util.text.StringUtil
import com.intellij.util.SystemProperties
import org.codehaus.gant.GantBuilder
import org.jetbrains.intellij.build.ApplicationInfoProperties
import org.jetbrains.intellij.build.BuildContext
import org.jetbrains.intellij.build.BuildOptions
import org.jetbrains.intellij.build.BuildPaths
import org.jetbrains.intellij.build.ProductProperties
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
/**
* @author nik
*/
class BuildContextImpl extends BuildContext {
private final JpsGlobal global
private final underTeamCity
BuildContextImpl(GantBuilder ant, JpsGantProjectBuilder projectBuilder, JpsProject project, JpsGlobal global,
String communityHome, String projectHome, String buildOutputRoot, ProductProperties productProperties,
BuildOptions options = new BuildOptions()) {
this.projectBuilder = projectBuilder
this.ant = ant
this.project = project
this.global = global
this.productProperties = productProperties
this.options = options
underTeamCity = System.getProperty("teamcity.buildType.id") != null
messages = new BuildMessagesImpl(projectBuilder, ant.project, underTeamCity)
paths = new BuildPathsImpl(communityHome, projectHome, buildOutputRoot)
loadProject()
def appInfoFile = findApplicationInfoInSources()
applicationInfo = new ApplicationInfoProperties(appInfoFile.absolutePath)
buildNumber = System.getProperty("build.number") ?: readSnapshotBuildNumber()
fullBuildNumber = "$productProperties.code-$buildNumber"
systemSelector = productProperties.systemSelector(applicationInfo)
fileNamePrefix = productProperties.prefix
bootClassPathJarNames = ["bootstrap.jar", "extensions.jar", "util.jar", "jdom.jar", "log4j.jar", "trove4j.jar", "jna.jar"]
}
private void loadProject() {
def projectHome = paths.projectHome
JdkUtils.defineJdk(global, "IDEA jdk", JdkUtils.computeJdkHome(messages, "jdkHome", "$projectHome/build/jdk/1.6", "JDK_16_x64"))
JdkUtils.defineJdk(global, "1.8", JdkUtils.computeJdkHome(messages, "jdk8Home", "$projectHome/build/jdk/1.8", "JDK_18_x64"))
def bundledKotlinPath = "$paths.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)
projectBuilder.buildIncrementally = SystemProperties.getBooleanProperty("jps.build.incrementally", false)
def dataDirName = projectBuilder.buildIncrementally ? ".jps-incremental-build" : ".jps-build-data"
projectBuilder.dataStorageRoot = new File("$projectHome/$dataDirName")
def tempDir = System.getProperty("teamcity.build.tempDir") ?: System.getProperty("java.io.tmpdir")
projectBuilder.setupAdditionalLogging(new File("$tempDir/system/build-log/build.log"), System.getProperty("jps.build.debug.logging.categories", ""))
def pathVariables = JpsModelSerializationDataService.computeAllPathVariables(global)
JpsProjectLoader.loadProject(project, pathVariables, projectHome)
projectBuilder.exportModuleOutputProperties()
messages.info("Loaded project $projectHome: ${project.modules.size()} modules, ${project.libraryCollection.libraries.size()} libraries")
if (!options.useCompiledClassesFromProjectOutput) {
projectBuilder.targetFolder = "$paths.buildOutputRoot/classes"
}
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()
}
private void suppressWarnings() {
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()
def appInfoRelativePath = "idea/${productProperties.platformPrefix}ApplicationInfo.xml"
def appInfoFile = module.sourceRoots.collect { new File(it.file, appInfoRelativePath) }.find { it.exists() }
if (appInfoFile == null) {
messages.error("Cannot find $appInfoRelativePath in '$module.name' module")
}
return appInfoFile
}
@Override
JpsModule findApplicationInfoModule() {
def module = findModule(productProperties.appInfoModule)
if (module == null) {
messages.error("Cannot find module '$productProperties.appInfoModule' containing ApplicationInfo.xml file")
}
return module
}
JpsModule findModule(String name) {
project.modules.find { it.name == name }
}
@Override
void executeStep(String stepMessage, String stepId, Closure step) {
if (options.buildStepsToSkip.contains(stepId)) {
messages.info("Skipping '$stepMessage'")
}
else {
messages.block(stepMessage, step)
}
}
@Override
void notifyArtifactBuilt(String artifactPath) {
if (!underTeamCity) return
if (!FileUtil.startsWith(artifactPath, paths.projectHome)) {
messages.warning("Artifact '$artifactPath' is not under '$paths.projectHome', it won't be reported")
return
}
def relativePath = StringUtil.trimStart(artifactPath.substring(paths.projectHome.length()), "/")
def file = new File(artifactPath)
if (file.isDirectory()) {
relativePath += "=>" + file.name
}
messages.info("##teamcity[publishArtifacts '$relativePath']")
}
}
class BuildPathsImpl extends BuildPaths {
BuildPathsImpl(String communityHome, String projectHome, String buildOutputRoot) {
this.communityHome = new File(communityHome).canonicalPath
this.projectHome = new File(projectHome).canonicalPath
this.buildOutputRoot = new File(buildOutputRoot).canonicalPath
artifacts = "${this.buildOutputRoot}/artifacts"
distAll = "$buildOutputRoot/dist.all"
temp = "$buildOutputRoot/temp"
winJre = "$buildOutputRoot/jdk.win"
linuxJre = "$buildOutputRoot/jdk.linux"
}
}
@@ -0,0 +1,95 @@
/*
* Copyright 2000-2016 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.text.StringUtil
import org.apache.tools.ant.BuildException
import org.apache.tools.ant.Project
import org.jetbrains.intellij.build.BuildMessages
import org.jetbrains.jps.gant.BuildInfoPrinter
import org.jetbrains.jps.gant.DefaultBuildInfoPrinter
import org.jetbrains.jps.gant.JpsGantProjectBuilder
import org.jetbrains.jps.gant.TeamCityBuildInfoPrinter
/**
* @author nik
*/
class BuildMessagesImpl implements BuildMessages {
private final JpsGantProjectBuilder builder
private final BuildInfoPrinter buildInfoPrinter
private final Project antProject
private final boolean underTeamCity
private int indent = 0
BuildMessagesImpl(JpsGantProjectBuilder builder, Project antProject, boolean underTeamCity) {
this.underTeamCity = underTeamCity
this.antProject = antProject
this.builder = builder
buildInfoPrinter = underTeamCity ? new TeamCityBuildInfoPrinter() : new DefaultBuildInfoPrinter()
}
@Override
void info(String message) {
antProject.log(withIndent(message), Project.MSG_INFO)
}
private String withIndent(String message) {
StringUtil.repeat(" ", 2 * indent) + message
}
@Override
void warning(String message) {
antProject.log(withIndent(message), Project.MSG_WARN)
}
@Override
void error(String message) {
throw new BuildException(message)
}
@Override
void progress(String message) {
if (underTeamCity) {
buildInfoPrinter.printProgressMessage(builder, message)
}
else {
info(message)
}
}
@Override
void block(String blockName, Closure body) {
try {
//todo[nik] move this logic into DefaultBuildInfoPrinter?
if (underTeamCity) {
buildInfoPrinter.printBlockOpenedMessage(builder, blockName)
}
else {
info(blockName)
indent++
}
body()
}
finally {
if (underTeamCity) {
buildInfoPrinter.printBlockClosedMessage(builder, blockName)
}
else {
indent--
}
}
}
}
@@ -0,0 +1,225 @@
/*
* Copyright 2000-2016 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 org.jetbrains.intellij.build.BuildContext
import org.jetbrains.intellij.build.BuildOptions
import org.jetbrains.intellij.build.BuildTasks
import java.time.LocalDate
import java.time.format.DateTimeFormatter
/**
* @author nik
*/
class BuildTasksImpl extends BuildTasks {
final BuildContext buildContext
BuildTasksImpl(BuildContext buildContext) {
this.buildContext = buildContext
}
@Override
void zipSources() {
buildContext.executeStep("Build sources zip archive", BuildOptions.SOURCES_ARCHIVE_STEP) {
String targetFile = "$buildContext.paths.artifacts/sources.zip"
buildContext.messages.progress("Building sources archive $targetFile")
buildContext.ant.mkdir(dir: buildContext.paths.artifacts)
buildContext.ant.delete(file: targetFile)
buildContext.ant.zip(destfile: targetFile) {
fileset(dir: buildContext.paths.projectHome) {
["java", "groovy", "ipr", "iml", "form", "xml", "properties", "kt"].each {
include(name: "**/*.$it")
}
exclude(name: "**/testData/**")
exclude(name: "out/**")
}
}
buildContext.notifyArtifactBuilt(targetFile)
}
}
//todo[nik] do we need 'cp' and 'jvmArgs' parameters?
@Override
void buildSearchableOptions(String targetModuleName, List<String> modulesToIndex, List<String> pathsToLicenses) {
//todo[nik] create searchableOptions.xml in a separate directory instead of modifying it in the module output
buildContext.executeStep("Build searchable options index", BuildOptions.SEARCHABLE_OPTIONS_INDEX_STEP, {
def javaRuntimeClasses = "${buildContext.projectBuilder.moduleOutput(buildContext.findModule("java-runtime"))}"
if (!new File(javaRuntimeClasses).exists()) {
buildContext.messages.
error("Cannot build searchable options, 'java-runtime' module isn't compiled ($javaRuntimeClasses doesn't exist)")
}
buildContext.messages.progress("Building searchable options for modules $modulesToIndex")
def targetModuleOutput = buildContext.projectBuilder.moduleOutput(buildContext.findModule(targetModuleName))
String targetFile = "$targetModuleOutput/search/searchableOptions.xml"
FileUtil.delete(new File(targetFile))
def tempDir = "$buildContext.paths.temp/searchableOptions"
String systemPath = "$tempDir/system"
String configPath = "$tempDir/config"
buildContext.ant.mkdir(dir: tempDir)
pathsToLicenses.each {
//todo[nik] previously licenses were copied to systemPath
buildContext.ant.copy(file: it, todir: configPath)
}
def ideClasspath = new LinkedHashSet<String>()
modulesToIndex.collectMany(ideClasspath) { buildContext.projectBuilder.moduleRuntimeClasspath(buildContext.findModule(it), false) }
String classpathFile = "$tempDir/classpath.txt"
new File(classpathFile).text = ideClasspath.join("\n")
buildContext.ant.java(classname: "com.intellij.rt.execution.CommandLineWrapper", fork: true, failonerror: true) {
jvmarg(line: "-ea -Xmx500m -XX:MaxPermSize=200m")
jvmarg(value: "-Xbootclasspath/a:${buildContext.projectBuilder.moduleOutput(buildContext.findModule("boot"))}")
sysproperty(key: "idea.home.path", value: buildContext.paths.projectHome)
sysproperty(key: "idea.system.path", value: systemPath)
sysproperty(key: "idea.config.path", value: configPath)
arg(value: "$classpathFile")
arg(line: "com.intellij.idea.Main traverseUI")
arg(value: targetFile)
classpath() {
pathelement(location: "$javaRuntimeClasses")
}
}
if (!new File(targetFile).exists()) {
buildContext.messages.error("Failed to build searchable options index: $targetFile doesn't exist")
}
})
}
File patchIdeaPropertiesFile() {
File originalFile = new File("$buildContext.paths.communityHome/bin/idea.properties")
String text = originalFile.text
if (buildContext.productProperties.additionalIDEPropertiesFilePath != null) {
text += "\n" + new File(buildContext.productProperties.additionalIDEPropertiesFilePath).text
}
//todo[nik] introduce special systemSelectorWithoutVersion instead?
String settingsDir = buildContext.systemSelector.replaceFirst("\\d+(\\.\\d+)?", "")
text = BuildUtils.replaceAll(text, ["settings_dir": settingsDir], "@@")
text += (buildContext.applicationInfo.isEAP ? """
#-----------------------------------------------------------------------
# Change to 'disabled' if you don't want to receive instant visual notifications
# about fatal errors that happen to an IDE or plugins installed.
#-----------------------------------------------------------------------
idea.fatal.error.notification=enabled
"""
: """
#-----------------------------------------------------------------------
# Change to 'enabled' if you want to receive instant visual notifications
# about fatal errors that happen to an IDE or plugins installed.
#-----------------------------------------------------------------------
idea.fatal.error.notification=disabled
""")
File propertiesFile = new File(buildContext.paths.temp, "idea.properties")
propertiesFile.text = text
return propertiesFile
}
@Override
File patchApplicationInfo() {
def sourceFile = buildContext.findApplicationInfoInSources()
def targetFile = new File(buildContext.paths.temp, sourceFile.name)
def date = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE)
BuildUtils.copyAndPatchFile(sourceFile.path, targetFile.path,
["BUILD_NUMBER": buildContext.fullBuildNumber, "BUILD_DATE": date])
return targetFile
}
void layoutShared() {
new File(buildContext.paths.distAll, "build.txt").text = buildContext.fullBuildNumber
buildContext.ant.copy(todir: "$buildContext.paths.distAll/bin") {
fileset(dir: "$buildContext.paths.communityHome/bin") {
include(name: "*.*")
exclude(name: "idea.properties")
}
}
buildContext.ant.copy(todir: "$buildContext.paths.distAll/license") {
fileset(dir: "$buildContext.paths.communityHome/license")
}
//todo[nik] these seems to be required for IDEA CE only
buildContext.ant.copy(todir: "$buildContext.paths.distAll") {
fileset(file: "$buildContext.paths.communityHome/LICENSE.txt")
fileset(file: "$buildContext.paths.communityHome/NOTICE.txt")
}
}
@Override
void buildDistributions() {
layoutShared()
def propertiesFile = patchIdeaPropertiesFile()
WindowsDistributionBuilder windowsBuilder = null
buildContext.executeStep("Build Windows distribution", BuildOptions.WINDOWS_DISTRIBUTION_STEP, {
windowsBuilder = new WindowsDistributionBuilder(buildContext)
windowsBuilder.layoutWin(propertiesFile)
})
LinuxDistributionBuilder linuxBuilder = null
buildContext.executeStep("Build Linux distribution", BuildOptions.LINUX_DISTRIBUTION_STEP) {
linuxBuilder = new LinuxDistributionBuilder(buildContext)
linuxBuilder.layoutUnix(propertiesFile)
}
MacDistributionBuilderImpl macBuilder = null
buildContext.executeStep("Build Mac OS X distribution", BuildOptions.MAC_DISTRIBUTION_STEP) {
macBuilder = new MacDistributionBuilderImpl(buildContext)
macBuilder.layoutMac(propertiesFile)
}
if (windowsBuilder != null && linuxBuilder != null && macBuilder != null) {
buildContext.executeStep("Build cross-platform distribution", BuildOptions.CROSS_PLATFORM_DISTRIBUTION_STEP) {
def crossPlatformBuilder = new CrossPlatformDistributionBuilder(buildContext)
crossPlatformBuilder.buildCrossPlatformZip(windowsBuilder.winDistPath, linuxBuilder.unixDistPath, macBuilder.macDistPath)
}
}
else {
buildContext.messages.info("Skipping building cross-platform distribution because some OS-specific distributions was skipeed")
}
}
@Override
void cleanOutput() {
buildContext.messages.block("Clean output") {
def outputPath = buildContext.paths.buildOutputRoot
buildContext.messages.progress("Cleaning output directory $outputPath")
FileUtil.delete(new File(outputPath))
}
}
@Override
void compileProjectAndTests(List<String> includingTestsInModules = []) {
if (buildContext.options.useCompiledClassesFromProjectOutput) {
buildContext.messages.info("Compilation skipped, the compiled classes from the project output will be used")
return
}
buildContext.projectBuilder.cleanOutput()
buildContext.projectBuilder.buildProduction()
for (String moduleName : includingTestsInModules) {
buildContext.projectBuilder.makeModuleTests(buildContext.findModule(moduleName))
}
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2000-2016 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 com.intellij.openapi.util.text.StringUtil
/**
* @author nik
*/
class BuildUtils {
static String replaceAll(String text, Map<String, String> replacements, String marker = "__") {
replacements.each {
text = StringUtil.replace(text, "$marker$it.key$marker", it.value)
}
return text
}
static void copyAndPatchFile(String sourcePath, String targetPath, Map<String, String> replacements, String marker = "__") {
FileUtil.createParentDirs(new File(targetPath))
new File(targetPath).text = replaceAll(new File(sourcePath).text, replacements, marker)
}
}
@@ -0,0 +1,110 @@
/*
* Copyright 2000-2016 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 org.jetbrains.intellij.build.BuildContext
/**
* @author nik
*/
class CrossPlatformDistributionBuilder {
private final BuildContext buildContext
CrossPlatformDistributionBuilder(BuildContext buildContext) {
this.buildContext = buildContext
}
void buildCrossPlatformZip(String winDistPath, String linuxDistPath, String macDistPath) {
buildContext.messages.block("Building cross-platform zip") {
def executableName = buildContext.fileNamePrefix
def zipDir = "$buildContext.paths.temp/cross-platform-zip"
buildContext.ant.copy(todir: "$zipDir/bin/win") {
fileset(dir: "$linuxDistPath/bin") {
include(name: "idea.properties")
}
}
buildContext.ant.copy(todir: "$zipDir/bin/linux") {
fileset(dir: "$linuxDistPath/bin") {
include(name: "*.vmoptions")
include(name: "idea.properties")
}
}
buildContext.ant.copy(todir: "$zipDir/bin/mac") {
fileset(dir: "$macDistPath/bin") {
include(name: "${executableName}.vmoptions")
include(name: "idea.properties")
}
}
buildContext.ant.copy(file: "$macDistPath/bin/${executableName}.vmoptions", tofile: "$zipDir/bin/mac/${executableName}64.vmoptions")
buildContext.ant.copy(todir: "$zipDir/bin") {
fileset(dir: "$macDistPath/bin") {
include(name: "*.jnilib")
}
mapper(type: "glob", from: "*.jnilib", to: "*.dylib")
}
String targetPath = "$buildContext.paths.artifacts/$buildContext.fileNamePrefix${buildContext.fullBuildNumber}.zip"
buildContext.ant.zip(zipfile: targetPath, duplicate: "fail") {
fileset(dir: buildContext.paths.distAll) {
exclude(name: "bin/idea.properties")
}
fileset(dir: zipDir)
fileset(dir: winDistPath) {
exclude(name: "bin/fsnotifier*.exe")
exclude(name: "bin/*.exe.vmoptions")
exclude(name: "bin/${executableName}*.exe")
exclude(name: "bin/idea.properties")
}
zipfileset(dir: "$winDistPath/bin", prefix: "bin/win") {
include(name: "fsnotifier*.exe")
include(name: "*.exe.vmoptions")
}
fileset(dir: linuxDistPath) {
exclude(name: "bin/fsnotifier*")
exclude(name: "bin/*.vmoptions")
exclude(name: "bin/*.sh")
exclude(name: "bin/idea.properties")
exclude(name: "help/**")
}
zipfileset(dir: "$linuxDistPath/bin", filemode: "775", prefix: "bin") {
include(name: "*.sh")
}
zipfileset(dir: "$linuxDistPath/bin", prefix: "bin/linux", filemode: "775") {
include(name: "fsnotifier*")
}
fileset(dir: macDistPath) {
exclude(name: "bin/fsnotifier*")
exclude(name: "bin/restarter*")
exclude(name: "bin/*.sh")
exclude(name: "bin/*.py")
exclude(name: "bin/*.jnilib")
exclude(name: "bin/idea.properties")
exclude(name: "bin/*.vmoptions")
}
zipfileset(dir: "$macDistPath/bin", filemode: "775", prefix: "bin") {
include(name: "restarter*")
include(name: "*.py")
}
zipfileset(dir: "$macDistPath/bin", prefix: "bin/mac", filemode: "775") {
include(name: "fsnotifier*")
}
}
buildContext.notifyArtifactBuilt(targetPath)
}
}
}
@@ -0,0 +1,73 @@
/*
* Copyright 2000-2016 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.SystemInfo
import com.intellij.util.SystemProperties
import org.jetbrains.intellij.build.BuildMessages
import org.jetbrains.jps.model.JpsGlobal
import org.jetbrains.jps.model.java.JdkVersionDetector
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.library.JpsOrderRootType
/**
* @author nik
*/
class JdkUtils {
public static void defineJdk(JpsGlobal global, String jdkName, String jdkHomePath) {
def sdk = JpsJavaExtensionService.instance.addJavaSdk(global, jdkName, jdkHomePath)
def toolsJar = new File(jdkHomePath, "lib/tools.jar")
if (toolsJar.exists()) {
sdk.addRoot(toolsJar, JpsOrderRootType.COMPILED)
}
}
public static String computeJdkHome(BuildMessages messages, String propertyName, String defaultDir, String envVarName) {
def jdkDir = System.getProperty(propertyName)
if (jdkDir != null) {
return jdkDir
}
jdkDir = SystemInfo.isMac ? "$defaultDir/Home" : defaultDir
if (new File(jdkDir).exists()) {
messages.info("$propertyName set to $jdkDir")
}
else {
jdkDir = System.getenv(envVarName)
if (jdkDir != null) {
messages.info("'$defaultDir' doesn't exist, $propertyName set to '$envVarName' environment variable: $jdkDir")
}
else {
jdkDir = getCurrentJdk()
def version = JdkVersionDetector.instance.detectJdkVersion(jdkDir)
if (propertyName.contains("8") && !version.contains("1.8.")) {
messages.error("JDK 1.8 is required to compile the project, but '$propertyName' property and '$envVarName' environment variable aren't defined and default JDK $jdkDir ($version) cannot be used as JDK 1.8")
return null
}
messages.info("'$envVarName' isn't defined and '$defaultDir' doesn't exist, $propertyName set to $jdkDir")
}
}
return jdkDir
}
private static String getCurrentJdk() {
def javaHome = SystemProperties.javaHome
if (new File(javaHome).name == "jre") {
return new File(javaHome).getParent()
}
return javaHome
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2000-2016 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
/**
* @author nik
*/
enum JvmArchitecture {
x32(""), x64("64")
final String fileSuffix
JvmArchitecture(String fileSuffix) {
this.fileSuffix = fileSuffix
}
}
@@ -0,0 +1,153 @@
/*
* Copyright 2000-2016 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 org.jetbrains.intellij.build.BuildContext
/**
* @author nik
*/
class LinuxDistributionBuilder {
private final BuildContext buildContext
final String unixDistPath
LinuxDistributionBuilder(BuildContext buildContext) {
this.buildContext = buildContext
unixDistPath = "$buildContext.paths.buildOutputRoot/dist.unix"
}
//todo[nik] rename
void layoutUnix(File ideaProperties) {
buildContext.ant.copy(todir: "$unixDistPath/bin") {
fileset(dir: "$buildContext.paths.communityHome/bin/linux")
}
buildContext.ant.copy(file: ideaProperties.path, todir: "$unixDistPath/bin")
//todo[nik] converting line separators to unix-style make sense only when building Linux distributions under Windows on a local machine;
// for real installers we need to checkout all text files with 'lf' separators anyway
buildContext.ant.fixcrlf(file: "$unixDistPath/bin/idea.properties", eol: "unix")
buildContext.ant.copy(file: buildContext.productProperties.icon128, tofile: "$unixDistPath/bin/${buildContext.fileNamePrefix}.png")
unixScripts()
unixVMOptions()
unixReadme()
buildContext.productProperties.customLinLayout(unixDistPath)
buildTarGz(false)
if (new File(buildContext.paths.linuxJre).exists()) {
buildTarGz(true)
}
}
private void unixScripts() {
String name = "${buildContext.fileNamePrefix}.sh"
String fullName = buildContext.applicationInfo.productName
String productUpperCase = buildContext.applicationInfo.shortProductName.toUpperCase()
String vmOptionsFileName = buildContext.fileNamePrefix
String classPath = "CLASSPATH=\"\$IDE_HOME/lib/${buildContext.bootClassPathJarNames[0]}\"\n"
classPath += buildContext.bootClassPathJarNames[1..-1].collect { "CLASSPATH=\"\$CLASSPATH:\$IDE_HOME/lib/${it}\"" }.join("\n")
def jvmArgs = buildContext.productProperties.ideJvmArgs
if (buildContext.productProperties.toolsJarRequired) {
classPath += "\nCLASSPATH=\"\$CLASSPATH:\$JDK/lib/tools.jar\""
jvmArgs = "$jvmArgs -Didea.jre.check=true".trim()
}
buildContext.ant.copy(todir: "${unixDistPath}/bin") {
fileset(dir: "$buildContext.paths.communityHome/bin/scripts/unix")
filterset(begintoken: "@@", endtoken: "@@") {
filter(token: "product_full", value: fullName)
filter(token: "product_uc", value: productUpperCase)
filter(token: "vm_options", value: vmOptionsFileName)
filter(token: "isEap", value: buildContext.applicationInfo.isEAP)
filter(token: "system_selector", value: buildContext.systemSelector)
filter(token: "ide_jvm_args", value: jvmArgs)
filter(token: "class_path", value: classPath)
filter(token: "script_name", value: name)
}
}
if (name != "idea.sh") {
//todo[nik] rename idea.sh in sources to something more generic
buildContext.ant.move(file: "${unixDistPath}/bin/idea.sh", tofile: "${unixDistPath}/bin/$name")
}
String inspectScript = buildContext.productProperties.customInspectScriptName
if (inspectScript != null && inspectScript != "inspect") {
buildContext.ant.move(file: "${unixDistPath}/bin/inspect.sh", tofile: "${unixDistPath}/bin/${inspectScript}.sh")
}
buildContext.ant.fixcrlf(srcdir: "${unixDistPath}/bin", includes: "*.sh", eol: "unix")
}
private void unixVMOptions() {
JvmArchitecture.values().each {
def fileName = "${buildContext.fileNamePrefix}${it.fileSuffix}.vmoptions"
//todo[nik] why we don't add yourkit agent on unix?
def options = VmOptionsGenerator.computeVmOptions(it, buildContext.applicationInfo.isEAP, null) + " -Dawt.useSystemAAFontSettings=lcd"
new File(unixDistPath, "bin/$fileName").text = options.replace(' ', '\n')
}
}
private void unixReadme() {
String fullName = buildContext.applicationInfo.productName
BuildUtils.copyAndPatchFile("$buildContext.paths.communityHome/build/Install-Linux-tar.txt", "$unixDistPath/Install-Linux-tar.txt",
["product_full" : fullName,
"product" : buildContext.fileNamePrefix,
"system_selector": buildContext.systemSelector], "@@")
buildContext.ant.fixcrlf(file: "$unixDistPath/bin/Install-Linux-tar.txt", eol: "unix")
}
private void buildTarGz(boolean bundleJre) {
def tarRoot = buildContext.productProperties.linuxAppRoot(buildContext.buildNumber)
def suffix = bundleJre ? "" : "-no-jdk"
def tarPath = "$buildContext.paths.artifacts/${buildContext.productProperties.archiveName(buildContext.buildNumber)}${suffix}.tar"
def extraBins = buildContext.productProperties.linux.extraLinuxBins
def paths = [buildContext.paths.distAll, unixDistPath]
if (bundleJre) {
paths += buildContext.paths.linuxJre
extraBins += "jre/jre/bin/*"
}
buildContext.messages.block("Build Linux tar.gz archive${bundleJre ? "" : " (without JRE)"}") {
buildContext.ant.tar(tarfile: tarPath, longfile: "gnu") {
paths.each {
tarfileset(dir: it, prefix: tarRoot) {
exclude(name: "bin/*.sh")
exclude(name: "bin/fsnotifier*")
extraBins.each {
exclude(name: it)
}
type(type: "file")
}
}
paths.each {
tarfileset(dir: it, filemode: "755", prefix: tarRoot) {
include(name: "bin/*.sh")
include(name: "bin/fsnotifier*")
extraBins.each {
include(name: it)
}
type(type: "file")
}
}
}
String gzPath = "${tarPath}.gz"
buildContext.ant.gzip(src: tarPath, zipfile: gzPath)
buildContext.ant.delete(file: tarPath)
buildContext.notifyArtifactBuilt(gzPath)
}
}
}
@@ -0,0 +1,305 @@
/*
* Copyright 2000-2016 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 org.jetbrains.intellij.build.BuildContext
import java.time.LocalDate
/**
* @author nik
*/
class MacDistributionBuilderImpl {
private final BuildContext buildContext
final String macDistPath
MacDistributionBuilderImpl(BuildContext buildContext) {
this.buildContext = buildContext
macDistPath = "$buildContext.paths.buildOutputRoot/dist.mac"
}
public layoutMac(File ideaPropertiesFile) {
def docTypes = buildContext.productProperties.mac.docTypes ?: """
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>ipr</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>${buildContext.fileNamePrefix}.icns</string>
<key>CFBundleTypeName</key>
<string>${buildContext.applicationInfo.productName} Project File</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
</dict>
"""
Map<String, String> customIdeaProperties = ["idea.jre.check": "$buildContext.productProperties.toolsJarRequired"];
layoutMacApp(ideaPropertiesFile, customIdeaProperties, docTypes)
buildContext.productProperties.customMacLayout(macDistPath)
buildMacZip()
}
private void layoutMacApp(File ideaPropertiesFile, Map<String, String> customIdeaProperties, String docTypes) {
String target = macDistPath
def macProductProperties = buildContext.productProperties.mac
buildContext.ant.copy(todir: "$target/bin") {
fileset(dir: "$buildContext.paths.communityHome/bin/mac")
}
buildContext.ant.copy(todir: target) {
fileset(dir: "$buildContext.paths.communityHome/build/conf/mac/Contents")
}
String executable = buildContext.fileNamePrefix
String icns = "idea.icns" //todo[nik] rename to more generic name?
String helpId = macProductProperties.helpId
String helpIcns = "$target/Resources/${helpId}.help/Contents/Resources/Shared/product.icns"
String customIcns = buildContext.productProperties.icns
if (customIcns != null) {
buildContext.ant.delete(file: "$target/Resources/idea.icns")
buildContext.ant.copy(file: customIcns, todir: "$target/Resources")
buildContext.ant.copy(file: customIcns, tofile: helpIcns)
icns = new File(customIcns).name
}
else {
buildContext.ant.copy(file: "$target/Resources/idea.icns", tofile: helpIcns)
}
String fullName = buildContext.applicationInfo.productName
//todo[nik] why do we put vm options to separate places (some into Info.plist, some into vmoptions file)?
String vmOptions = "-Dfile.encoding=UTF-8 ${VmOptionsGenerator.computeCommonVmOptions(buildContext.applicationInfo.isEAP)} -Xverify:none"
//todo[nik] improve
String minor = buildContext.applicationInfo.minorVersion
boolean isNotRelease = buildContext.applicationInfo.isEAP && !minor.contains("RC") && !minor.contains("Beta")
String version = isNotRelease ? "EAP $buildContext.fullBuildNumber" : "${buildContext.applicationInfo.majorVersion}.${minor}"
String EAP = isNotRelease ? "-EAP" : ""
//todo[nik] don't mix properties for idea.properties file with properties for Info.plist
Map<String, String> properties = readIdeaProperties(ideaPropertiesFile, customIdeaProperties)
def coreKeys = ["idea.platform.prefix", "idea.paths.selector", "idea.executable"]
String coreProperties = submapToXml(properties, coreKeys);
StringBuilder effectiveProperties = new StringBuilder()
properties.each { k, v ->
if (!coreKeys.contains(k)) {
effectiveProperties.append("$k=$v\n");
}
}
new File("$target/bin/idea.properties").text = effectiveProperties.toString()
String ideaVmOptions = "${VmOptionsGenerator.vmOptionsForArch(JvmArchitecture.x64)} -XX:+UseCompressedOops"
if (buildContext.applicationInfo.isEAP && buildContext.productProperties.includeYourkitAgentInEAP && macProductProperties.includeYourkitAgentInEAP) {
ideaVmOptions += VmOptionsGenerator.yourkitOptions(buildContext.systemSelector, "")
}
new File("$target/bin/${executable}.vmoptions").text = ideaVmOptions.split(" ").join("\n")
String classPath = buildContext.bootClassPathJarNames.collect { "\$APP_PACKAGE/Contents/lib/${it}" }.join(":")
String archsString = """
<key>LSArchitecturePriority</key>
<array>"""
macProductProperties.architectures.each {
archsString += "<string>$it</string>"
}
archsString += "</array>\n"
List<String> urlSchemes = macProductProperties.urlSchemes
String urlSchemesString = ""
if (urlSchemes.size() > 0) {
urlSchemesString += """
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>Stacktrace</string>
<key>CFBundleURLSchemes</key>
<array>
"""
urlSchemes.each { scheme ->
urlSchemesString += " <string>${scheme}</string>"
}
urlSchemesString += """
</array>
</dict>
</array>
"""
}
String todayYear = LocalDate.now().year
buildContext.ant.replace(file: "$target/Info.plist") {
replacefilter(token: "@@build@@", value: buildContext.fullBuildNumber)
replacefilter(token: "@@doc_types@@", value: docTypes ?: "")
replacefilter(token: "@@executable@@", value: executable)
replacefilter(token: "@@icns@@", value: icns)
replacefilter(token: "@@bundle_name@@", value: fullName)
replacefilter(token: "@@product_state@@", value: EAP)
replacefilter(token: "@@bundle_identifier@@", value: macProductProperties.bundleIdentifier)
replacefilter(token: "@@year@@", value: "$todayYear")
replacefilter(token: "@@company_name@@", value: buildContext.applicationInfo.companyName)
replacefilter(token: "@@min_year@@", value: "2000")
replacefilter(token: "@@max_year@@", value: "$todayYear")
replacefilter(token: "@@version@@", value: version)
replacefilter(token: "@@vmoptions@@", value: vmOptions)
replacefilter(token: "@@idea_properties@@", value: coreProperties)
replacefilter(token: "@@class_path@@", value: classPath)
replacefilter(token: "@@help_id@@", value: helpId)
replacefilter(token: "@@url_schemes@@", value: urlSchemesString)
replacefilter(token: "@@archs@@", value: archsString)
replacefilter(token: "@@min_osx@@", value: macProductProperties.minOSXVersion)
}
if (executable != "idea") {
buildContext.ant.move(file: "$target/MacOS/idea", tofile: "$target/MacOS/$executable")
}
buildContext.ant.replace(file: "$target/bin/inspect.sh") {
replacefilter(token: "@@product_full@@", value: fullName)
replacefilter(token: "@@script_name@@", value: executable)
}
String inspectScript = buildContext.productProperties.customInspectScriptName
if (inspectScript != null && inspectScript != "inspect") {
buildContext.ant.move(file: "$target/bin/inspect.sh", tofile: "$target/bin/${inspectScript}.sh")
}
buildContext.ant.fixcrlf(srcdir: "$target/bin", includes: "*.sh", eol: "unix")
buildContext.ant.fixcrlf(srcdir: "$target/bin", includes: "*.py", eol: "unix")
}
void buildMacZip() {
buildContext.messages.block("Build zip archive for Mac OS") {
def extraBins = buildContext.productProperties.mac.extraMacBins
def allPaths = [buildContext.paths.distAll, macDistPath]
def zipRoot = buildContext.productProperties.macAppRoot(buildContext.applicationInfo, buildContext.buildNumber)
def targetPath = "$buildContext.paths.artifacts/${buildContext.productProperties.archiveName(buildContext.buildNumber)}.mac.zip"
buildContext.ant.zip(zipfile: targetPath) {
allPaths.each {
zipfileset(dir: it, prefix: zipRoot) {
exclude(name: "bin/*.sh")
exclude(name: "bin/*.py")
exclude(name: "bin/fsnotifier")
exclude(name: "bin/restarter")
exclude(name: "MacOS/*")
exclude(name: "build.txt")
exclude(name: "NOTICE.txt")
extraBins.each {
exclude(name: it)
}
exclude(name: "bin/idea.properties")
}
}
allPaths.each {
zipfileset(dir: it, filemode: "755", prefix: zipRoot) {
include(name: "bin/*.sh")
include(name: "bin/*.py")
include(name: "bin/fsnotifier")
include(name: "bin/restarter")
include(name: "MacOS/*")
extraBins.each {
include(name: it)
}
}
}
allPaths.each {
zipfileset(dir: it, prefix: "$zipRoot/Resources") {
include(name: "build.txt")
include(name: "NOTICE.txt")
}
}
zipfileset(file: "$macDistPath/bin/idea.properties", prefix: "$zipRoot/bin")
}
buildContext.notifyArtifactBuilt(targetPath)
}
}
private static String submapToXml(Map<String, String> properties, List<String> keys) {
// generate properties description for Info.plist
StringBuilder buff = new StringBuilder()
keys.each { key ->
String value = properties[key]
if (value != null) {
String string =
"""
<key>$key</key>
<string>$value</string>
"""
buff.append(string)
}
}
return buff.toString()
}
/**
* E.g.
*
* Load all properties from file:
* readIdeaProperties(buildContext, "$home/ruby/build/idea.properties")
*
* Load all properties except "idea.cycle.buffer.size", change "idea.max.intellisense.filesize" to 3000
* and enable "idea.is.internal" mode:
* readIdeaProperties(buildContext, "$home/ruby/build/idea.properties",
* "idea.properties" : ["idea.max.intellisense.filesize" : 3000,
* "idea.cycle.buffer.size" : null,
* "idea.is.internal" : true ])
* @param args
* @return text xml properties description in xml
*/
private Map<String, String> readIdeaProperties(File propertiesFile, Map<String, String> customProperties = [:]) {
Map<String, String> ideaProperties = [:]
propertiesFile.withReader {
Properties loadedProperties = new Properties();
loadedProperties.load(it)
ideaProperties.putAll(loadedProperties as Map<String, String>)
}
Map<String, String> properties =
["CVS_PASSFILE" : "~/.cvspass",
"com.apple.mrj.application.live-resize" : "false",
"idea.paths.selector" : buildContext.systemSelector,
"idea.executable" : buildContext.fileNamePrefix,
"java.endorsed.dirs" : "",
"idea.smooth.progress" : "false",
"apple.laf.useScreenMenuBar" : "true",
"apple.awt.graphics.UseQuartz" : "true",
"apple.awt.fullscreencapturealldisplays": "false"]
if (buildContext.productProperties.platformPrefix != null) {
properties["idea.platform.prefix"] = buildContext.productProperties.platformPrefix
}
properties += customProperties
properties.each { k, v ->
if (v == null) {
// if overridden with null - ignore property
ideaProperties.remove(k)
}
else {
// if property is overridden in args map - use new value
ideaProperties[k] = v
}
}
return ideaProperties
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2000-2016 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
/**
* @author nik
*/
class VmOptionsGenerator {
private static final String COMMON_VM_OPTIONS = "-XX:+UseConcMarkSweepGC -XX:SoftRefLRUPolicyMSPerMB=50 -ea " +
"-Dsun.io.useCanonCaches=false -Djava.net.preferIPv4Stack=true " +
"-XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow"
static String computeVmOptions(JvmArchitecture arch, boolean isEAP, String yourkitSessionName = null) {
String options = vmOptionsForArch(arch) + " " + computeCommonVmOptions(isEAP)
if (yourkitSessionName != null) {
options += " " + yourkitOptions(yourkitSessionName, arch.fileSuffix)
}
return options
}
static String computeCommonVmOptions(boolean isEAP) {
String options = COMMON_VM_OPTIONS
if (isEAP) {
options += " -XX:MaxJavaStackTraceDepth=-1"
}
return options
}
static String vmOptionsForArch(JvmArchitecture arch) {
switch (arch) {
case JvmArchitecture.x32: return "-server -Xms128m -Xmx512m -XX:ReservedCodeCacheSize=240m"
case JvmArchitecture.x64: return "-Xms128m -Xmx750m -XX:ReservedCodeCacheSize=240m"
}
throw new AssertionError(arch)
}
static String yourkitOptions(String sessionName, String fileSuffix) {
"-agentlib:yjpagent$fileSuffix=probe_disable=*,disablealloc,disabletracing,onlylocal,disableexceptiontelemetry,delay=10000,sessionname=$sessionName".trim()
}
}
@@ -0,0 +1,158 @@
/*
* Copyright 2000-2016 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 org.jetbrains.intellij.build.BuildContext
import org.jetbrains.jps.model.module.JpsModuleSourceRoot
/**
* @author nik
*/
class WindowsDistributionBuilder {
private final BuildContext buildContext
final String winDistPath
WindowsDistributionBuilder(BuildContext buildContext) {
this.buildContext = buildContext
winDistPath = "$buildContext.paths.buildOutputRoot/dist.win"
}
//todo[nik] rename
void layoutWin(File ideaProperties) {
buildContext.ant.copy(todir: "$winDistPath/bin") {
fileset(dir: "$buildContext.paths.communityHome/bin/win")
}
buildContext.ant.copy(file: ideaProperties.path, todir: "$winDistPath/bin")
buildContext.ant.fixcrlf(file: "$winDistPath/bin/idea.properties", eol: "dos")
buildContext.ant.copy(file: buildContext.productProperties.ico, tofile: "$winDistPath/bin/${buildContext.fileNamePrefix}.ico")
if (buildContext.productProperties.windows.includeBatchLauncher) {
winScripts()
}
winVMOptions()
buildWinLauncher(JvmArchitecture.x32)
buildWinLauncher(JvmArchitecture.x64)
buildContext.productProperties.customWinLayout(winDistPath)
buildWinZip()
}
//todo[nik] rename
private void winScripts() {
String fullName = buildContext.applicationInfo.productName
String productUpperCase = buildContext.applicationInfo.shortProductName.toUpperCase()
//todo[nik] looks like names without .exe were also supported, do we need this?
String vmOptionsFileName = "${buildContext.fileNamePrefix}%BITS%.exe"
String classPath = "SET CLASS_PATH=%IDE_HOME%\\lib\\${buildContext.bootClassPathJarNames[0]}\n"
classPath += buildContext.bootClassPathJarNames[1..-1].collect { "SET CLASS_PATH=%CLASS_PATH%;%IDE_HOME%\\lib\\$it" }.join("\n")
def jvmArgs = buildContext.productProperties.ideJvmArgs
if (buildContext.productProperties.toolsJarRequired) {
classPath += "\nSET CLASS_PATH=%CLASS_PATH%;%JDK%\\lib\\tools.jar"
jvmArgs = "$jvmArgs -Didea.jre.check=true".trim()
}
def batName = "${buildContext.fileNamePrefix}.bat"
buildContext.ant.copy(todir: "$winDistPath/bin") {
fileset(dir: "$buildContext.paths.communityHome/bin/scripts/win")
filterset(begintoken: "@@", endtoken: "@@") {
filter(token: "product_full", value: fullName)
filter(token: "product_uc", value: productUpperCase)
filter(token: "vm_options", value: vmOptionsFileName)
filter(token: "isEap", value: buildContext.applicationInfo.isEAP)
filter(token: "system_selector", value: buildContext.systemSelector)
filter(token: "ide_jvm_args", value: jvmArgs)
filter(token: "class_path", value: classPath)
filter(token: "script_name", value: batName)
}
}
if (batName != "idea.bat") {
//todo[nik] rename idea.bat in sources to something more generic
buildContext.ant.move(file: "$winDistPath/bin/idea.bat", tofile: "$winDistPath/bin/$batName")
}
String inspectScript = buildContext.productProperties.customInspectScriptName
if (inspectScript != null && inspectScript != "inspect") {
buildContext.ant.move(file: "$winDistPath/bin/inspect.bat", tofile: "$winDistPath/bin/${inspectScript}.bat")
}
buildContext.ant.fixcrlf(srcdir: "$winDistPath/bin", includes: "*.bat", eol: "dos")
}
//todo[nik] rename
private void winVMOptions() {
JvmArchitecture.values().each {
def yourkitSessionName = buildContext.applicationInfo.isEAP && buildContext.productProperties.includeYourkitAgentInEAP ? buildContext.systemSelector : null
def fileName = "${buildContext.fileNamePrefix}${it.fileSuffix}.exe.vmoptions"
new File(winDistPath, "bin/$fileName").text = VmOptionsGenerator.computeVmOptions(it, buildContext.applicationInfo.isEAP, yourkitSessionName).replace(' ', '\n')
}
buildContext.ant.fixcrlf(srcdir: "$winDistPath/bin", includes: "*.vmoptions", eol: "dos")
}
private void buildWinLauncher(JvmArchitecture arch) {
buildContext.messages.block("Build Windows executable ${arch.name()}") {
String exeFileName = "$buildContext.fileNamePrefix${arch.fileSuffix}.exe"
def launcherPropertiesPath = "${buildContext.paths.temp}/launcher.properties"
//todo[nik] generate launcher.properties file automatically
def launcherPropertiesTemplatePath = arch == JvmArchitecture.x32 ? buildContext.productProperties.exe_launcher_properties
: buildContext.productProperties.exe64_launcher_properties
BuildUtils.copyAndPatchFile(launcherPropertiesTemplatePath, launcherPropertiesPath,
["PRODUCT_PATHS_SELECTOR": buildContext.systemSelector,
"IDE-NAME": buildContext.applicationInfo.shortProductName.toUpperCase()])
def communityHome = "$buildContext.paths.communityHome"
String inputPath = "$communityHome/bin/WinLauncher/WinLauncher${arch.fileSuffix}.exe"
buildContext.ant.java(classname: "com.pme.launcher.LauncherGeneratorMain", fork: "true", failonerror: "true") {
sysproperty(key: "java.awt.headless", value: "true")
arg(value: inputPath)
arg(value: buildContext.findApplicationInfoInSources().absolutePath)
arg(value: "$communityHome/native/WinLauncher/WinLauncher/resource.h")
arg(value: launcherPropertiesPath)
arg(value: "$winDistPath/bin/$exeFileName")
classpath {
pathelement(location: "$communityHome/build/lib/launcher-generator.jar")
fileset(dir: "$communityHome/lib") {
include(name: "guava*.jar")
include(name: "jdom.jar")
include(name: "sanselan*.jar")
}
[buildContext.findApplicationInfoModule(), buildContext.findModule("icons")].collectMany { it.sourceRoots }.each { JpsModuleSourceRoot root ->
pathelement(location: root.file.absolutePath)
}
}
}
}
}
private void buildWinZip() {
buildContext.messages.block("Build Windows .zip distribution") {
def targetPath = "$buildContext.paths.artifacts/${buildContext.productProperties.archiveName(buildContext.buildNumber)}.win.zip"
def zipPrefix = buildContext.productProperties.winAppRoot(buildContext.buildNumber)
def dirs = [buildContext.paths.distAll, winDistPath]
if (buildContext.productProperties.windows.bundleJre && new File(buildContext.paths.winJre).exists()) {
dirs += buildContext.paths.winJre
}
buildContext.ant.zip(zipfile: targetPath) {
dirs.each {
zipfileset(dir: it, prefix: zipPrefix)
}
}
buildContext.notifyArtifactBuilt(targetPath)
}
}
}
+55 -239
View File
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.intellij.openapi.util.text.StringUtil
import org.apache.tools.ant.BuildException
import org.jetbrains.intellij.build.BuildContext
import org.jetbrains.intellij.build.BuildOptions
import org.jetbrains.intellij.build.BuildTasks
import org.jetbrains.jps.gant.LayoutInfo
import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome
@@ -27,40 +27,12 @@ requireProperty("out", "$home/out")
// "out" has to be canonical, otherwise the ant build fails
// with mysterious errors
out = new File(out).getCanonicalPath()
setProperty("out", out)
String out = new File(out).getCanonicalPath()
class Paths {
final sandbox
final distWin
final distAll
final distUnix
final distMac
final artifacts
final artifacts_core
final artifacts_jps
final ideaSystem
final ideaConfig
loadProductProperties(home)
def Paths(String out) {
sandbox = out
distWin = "$sandbox/dist.win.ce"
distAll = "$sandbox/dist.all.ce"
distUnix = "$sandbox/dist.unix.ce"
distMac = "$sandbox/dist.mac.ce"
artifacts = "$sandbox/artifacts"
artifacts_core = "$artifacts/core"
artifacts_jps = "$artifacts/jps"
ideaSystem = "$sandbox/system"
ideaConfig = "$sandbox/config"
}
}
def paths = new Paths(out)
setProperty("paths", paths)
loadProductProperties(home, snapshot)
def loadProductProperties(String home, String buildNumber) {
def loadProductProperties(String home) {
//todo[nik] improve (load implementation class from groovy resources root directly?)
requireProperty("product", "idea")
requireProperty("productPropertiesPath", "")
if (productPropertiesPath.isEmpty()) {
@@ -69,231 +41,75 @@ def loadProductProperties(String home, String buildNumber) {
if (!new File(productPropertiesPath).exists()) {
throw new BuildException("No product specific properties file found at: " + productPropertiesPath)
}
setProperty("productProperties", includeFile(productPropertiesPath).getProperties(home, buildNumber))
productProperties.ideJvmArgs = productProperties.ideJvmArgs != null ?
productProperties.ideJvmArgs + " -Didea.jre.check=true" :
"-Didea.jre.check=true"
setProperty("productProperties", includeFile(productPropertiesPath).getProperties(home))
}
private BuildContext createBuildContext(String out, BuildOptions options = new BuildOptions()) {
//todo[nik] construct buildOutputRoot automatically based on product name
BuildContext.createContext(ant, projectBuilder, project, global, home, home, "$out/release", productProperties, options)
}
private static void compileModules(BuildContext buildContext) {
BuildTasks.create(buildContext).compileProjectAndTests(["jps-builders"])
}
target(compile: "Compile project") {
projectBuilder.stage("Cleaning up sandbox folder")
forceDelete(paths.sandbox)
loadProject()
[paths.sandbox, paths.distWin, paths.distAll, paths.distUnix, paths.distMac, paths.artifacts, paths.artifacts_core, paths.artifacts_jps].each {
ant.mkdir(dir: it)
}
projectBuilder.targetFolder = "$out/classes"
clearBuildCaches()
projectBuilder.cleanOutput()
projectBuilder.buildProduction()
projectBuilder.makeModuleTests(findModule("jps-builders"))
}
private String appInfoFile() {
return productProperties.appInfoFile()
compileModules(createBuildContext(out))
}
target('default': 'The default target') {
depends([compile])
// load ApplicationInfo.xml properties
ant.xmlproperty(file: appInfoFile(), collapseAttributes: "true")
zipSources(home, paths.artifacts)
indexSearchableOptions()
layoutAll([buildNumber: productProperties.fullBuildNumber(),
system_selector: productProperties.systemSelector(),
platform_prefix: productProperties.platformPrefix,
ide_jvm_args: productProperties.ideJvmArgs,
tools_jar: true],
home, null, paths, true)
String archiveName = productProperties.archiveName()
String macZip = "$paths.artifacts/${archiveName}.mac.zip"
notifyArtifactBuilt(macZip)
def buildContext = createBuildContext(out)
compileModules(buildContext)
def tasks = BuildTasks.create(buildContext)
tasks.cleanOutput()
tasks.buildSearchableOptions("resources-en", ["community-main"], [])
layoutAll(buildContext, true)
if (productProperties.buildUpdater) {
// Generate updater.jar from the updater module (patch updater)
layoutUpdater(out)
}
tasks.zipSources()
}
//todo[nik] do we really need this target? updates.xml calls layout.gant directly
target('build-dist-jars' : 'Target to build jars from locally compiled classes') {
loadProject()
// load ApplicationInfo.xml properties
ant.xmlproperty(file: appInfoFile(), collapseAttributes: "true")
indexSearchableOptions()
layoutAll([buildNumber: productProperties.fullBuildNumber(),
system_selector: productProperties.systemSelector(),
platform_prefix: productProperties.platformPrefix,
ide_jvm_args: productProperties.ideJvmArgs,
tools_jar: true],
home, null, paths)
String macZipPrefix = productProperties.prefix + productProperties.fullBuildNumber()
String macZip = "$paths.artifacts/${macZipPrefix}.mac.zip"
notifyArtifactBuilt(macZip)
def options = new BuildOptions()
options.useCompiledClassesFromProjectOutput = true
def buildContext = createBuildContext(out, options)
compileModules(buildContext)
def tasks = BuildTasks.create(buildContext)
tasks.cleanOutput()
tasks.buildSearchableOptions("resources-en", ["community-main"], [])
layoutAll(buildContext)
}
private void indexSearchableOptions() {
buildSearchableOptions("${projectBuilder.moduleOutput(findModule("resources-en"))}/search", [], {
ant.pathelement(location: "$jdkHome/lib/tools.jar")
ant.pathelement(location: "$home/lib/junit.jar")
projectBuilder.moduleRuntimeClasspath(findModule("community-main"), false).each {
ant.pathelement(location: it)
def layoutAll(BuildContext buildContext, buildJps = false) {
def layouts = includeFile("$buildContext.paths.communityHome/build/scripts/layouts.gant")
def tasks = BuildTasks.create(buildContext)
def applicationInfo = tasks.patchApplicationInfo()
LayoutInfo info = layouts.layoutFull(buildContext.paths.projectHome, buildContext.paths.distAll, applicationInfo)
buildContext.messages.block("Build intellij-core") {
String coreArtifactDir = "$buildContext.paths.artifacts/core"
buildContext.ant.mkdir(dir: coreArtifactDir)
layouts.layout_core(buildContext.paths.projectHome, coreArtifactDir)
buildContext.notifyArtifactBuilt(coreArtifactDir)
def intellijCoreZip = "${buildContext.paths.artifacts}/intellij-core-${buildContext.buildNumber}.zip"
ant.zip(destfile: intellijCoreZip) {
fileset(dir: coreArtifactDir)
}
})
}
def layoutAll(Map args, String home, String out, Paths _paths = null, buildJps = false) {
Paths paths = _paths != null ? _paths : new Paths(out)
wireBuildDate(args.buildNumber, appInfoFile())
ant.echo(message: args.buildNumber, file: "$paths.distAll/build.txt")
def layouts = includeFile("$home/build/scripts/layouts.gant")
LayoutInfo info = layouts.layoutFull(home, paths.distAll, null)
layouts.layout_core(home, paths.artifacts_core)
ant.zip(destfile: "${paths.artifacts}/intellij-core-${StringUtil.trimStart(args.buildNumber, "IC-")}.zip") {
fileset(dir: paths.artifacts_core)
buildContext.notifyArtifactBuilt(intellijCoreZip)
}
notifyArtifactBuilt(paths.artifacts_core)
if (buildJps) {
layouts.layoutJps(home, paths.artifacts_jps, args.buildNumber, {})
notifyArtifactBuilt(paths.artifacts_jps)
}
layout(paths.distAll) {
dir("bin") {
fileset(dir: "${home}/bin") {
include(name: "*.*")
}
buildContext.messages.block("Build standalone JPS") {
String jpsArtifactDir = "$buildContext.paths.artifacts/jps"
layouts.layoutJps(buildContext.paths.communityHome, jpsArtifactDir, buildContext.fullBuildNumber, {})
buildContext.notifyArtifactBuilt(jpsArtifactDir)
}
dir("license") {
fileset(dir: "${home}/license") {
exclude(name: "placeholder.txt")
}
}
fileset(file: "${home}/LICENSE.txt")
fileset(file: "${home}/NOTICE.txt")
}
patchPropertiesFile(paths.distAll, args + [appendices: ["$home/build/conf/ideaCE.properties"]])
layoutWin(args, home, paths)
layoutMac(args, home, paths)
layoutUnix(args, home, paths)
def macAppRoot = productProperties.macAppRoot()
def winAppRoot = productProperties.winAppRoot()
def linuxAppRoot = productProperties.linuxAppRoot()
def archiveName = productProperties.archiveName()
List pathsDist = new File("${paths.sandbox}/jdk.win/jre").exists() ? [paths.distAll, paths.distWin, "${paths.sandbox}/jdk.win"] : [paths.distAll, paths.distWin]
buildWinZip("$paths.artifacts/${archiveName}.win.zip", pathsDist, winAppRoot)
//win oracle jdk
if (new File("${paths.sandbox}/jdk.oracle.win/jre").exists()) {
buildWinZip("$paths.artifacts/${archiveName}-oracle-win.zip",
[paths.distAll, paths.distWin, "${paths.sandbox}/jdk.oracle.win"])
}
buildCrossPlatformZip("$paths.artifacts/idea${args.buildNumber}.zip", "${paths.sandbox}/sandbox-ce", [paths.distAll],
paths.distWin, paths.distUnix, paths.distMac)
String macZip = "$paths.artifacts/${archiveName}.mac.zip"
buildMacZip(macAppRoot, macZip, [paths.distAll], paths.distMac, productProperties.extraMacBins)
buildTarGz(linuxAppRoot, "$paths.artifacts/${archiveName}-no-jdk.tar", [paths.distAll, paths.distUnix], productProperties.extraLinuxBins)
requireProperty("jdk.linux", "false")
if (p("jdk.linux") != "false") {
buildTarGz(linuxAppRoot, "$paths.artifacts/${archiveName}.tar", [paths.distAll, paths.distUnix, "${paths.sandbox}/jdk.linux"], ["jre/jre/bin/*"] + productProperties.extraLinuxBins)
}
tasks.buildDistributions()
return info
}
private layoutWin(Map args, String home, Paths paths) {
String target = paths.distWin
layout(target) {
dir("bin") {
fileset(dir: "$home/bin/win")
}
}
def name = productProperties.prefix
def ico = productProperties.ico
ant.copy(file: ico, tofile: "$target/bin/${name}.ico")
if (productProperties.includeBatchLauncher) {
winScripts(target, home, "${name}.bat", args)
}
winVMOptions(target, productProperties.includeYourkitAgentInEAP ? args.system_selector : null, "${name}.exe", "${name}64.exe")
def appInfoModulePath = productProperties.appInfoModulePath
List resourcePaths = ["$home/${appInfoModulePath}/src", "$home/platform/icons/src"]
buildWinLauncher(home, "$home/bin/WinLauncher/WinLauncher.exe", "$target/bin/${name}.exe", appInfoFile(),
productProperties.exe_launcher_properties, args.system_selector, resourcePaths)
buildWinLauncher(home, "$home/bin/WinLauncher/WinLauncher64.exe", "$target/bin/${name}64.exe", appInfoFile(),
productProperties.exe64_launcher_properties, args.system_selector, resourcePaths)
productProperties.customWinLayout(target)
}
private layoutMac(Map _args, String home, Paths paths) {
String target = paths.distMac
def prefix = productProperties.prefix
Map args = new HashMap(_args)
args.bundleIdentifier = productProperties.bundleIdentifier
args.doc_types = """
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>ipr</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>${prefix}.icns</string>
<key>CFBundleTypeName</key>
<string>IntelliJ IDEA Project File</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
</dict>
"""
args."idea.properties.path" = "${paths.distAll}/bin/idea.properties"
args."idea.properties" = ["idea.jre.check": true];
args.urlSchemes = ["idea"]
args.mac_no_yjp = true
args.executable = prefix
args.icns = productProperties.icns
layoutMacApp(target, home, args)
productProperties.customMacLayout(target)
}
private layoutUnix(Map args, String home, Paths paths) {
String target = paths.distUnix
layout(target) {
dir("bin") {
fileset(dir: "$home/bin/linux")
}
}
def name = productProperties.prefix
def icon128 = productProperties.icon128
ant.copy(file: icon128, tofile: "$target/bin/${name}.png")
unixScripts(target, home, "${name}.sh", args)
unixVMOptions(target, "${name}")
unixReadme(target, home, args)
productProperties.customLinLayout(target)
}
+18 -12
View File
@@ -1,5 +1,5 @@
import org.jetbrains.intellij.build.ApplicationInfoProperties
import org.jetbrains.intellij.build.ProductProperties
/*
* Copyright (C) 2015 The Android Open Source Project
*
@@ -17,41 +17,47 @@ import org.jetbrains.intellij.build.ProductProperties
*/
def getProperties(String home, String buildNumber) {
def getProperties(String home) {
return new ProductProperties() {
{
prefix = "idea"
platformPrefix = "Idea"
code = "IC"
appInfoModule = "community-resources"
appInfoModulePath = "community-resources"
additionalIDEPropertiesFilePath = "$home/build/conf/ideaCE.properties"
exe_launcher_properties = "$home/build/conf/ideaCE-launcher.properties"
exe64_launcher_properties = "$home/build/conf/ideaCE64-launcher.properties"
bundleIdentifier = "com.jetbrains.intellij.ce"
maySkipAndroidPlugin = true
relativeAndroidHome = "android"
relativeAndroidToolsBaseHome = "android/tools-base"
toolsJarRequired = true
icon128 = "$home/platform/icons/src/icon_CE_128.png"
ico = "$home/platform/icons/src/idea_CE.ico"
windows.bundleJre = true
mac.helpId = "IJ"
mac.urlSchemes = ["idea"]
mac.includeYourkitAgentInEAP = false
mac.bundleIdentifier = "com.jetbrains.intellij.ce"
}
def String appInfoFile() {
"${projectBuilder.moduleOutput(findModule("community-resources"))}/idea/IdeaApplicationInfo.xml"
}
def String fullBuildNumber() { "IC-$buildNumber" }
def String systemSelector(ApplicationInfoProperties applicationInfo) { "IdeaIC$applicationInfo.majorVersion" }
def String systemSelector() { "IdeaIC${p("component.version.major")}" }
def String macAppRoot() {
isEap() ? "IntelliJ IDEA ${p("component.version.major")}.${p("component.version.minor")} CE EAP.app/Contents"
def String macAppRoot(ApplicationInfoProperties applicationInfo, String buildNumber) {
applicationInfo.isEAP ? "IntelliJ IDEA ${applicationInfo.majorVersion}.${applicationInfo.minorVersion} CE EAP.app/Contents"
: "IntelliJ IDEA CE.app/Contents"
}
def String winAppRoot() { "" }
def String winAppRoot(String buildNumber) { "" }
def String linuxAppRoot() { "idea-IC-$buildNumber" }
def String linuxAppRoot(String buildNumber) { "idea-IC-$buildNumber" }
def String archiveName() { "ideaIC-$buildNumber" }
def String archiveName(String buildNumber) { "ideaIC-$buildNumber" }
}
}
+15 -15
View File
@@ -21,9 +21,9 @@ target('default': "Developers update") {
//when IDEA CE is updated from IDEA UE sources project should be loaded from IDEA UE directory
String projectHome = isDefined("devIdeaHome") ? devIdeaHome : home
loadProjectFromPath(projectHome)
def patchedDescriptorDir = patchAppDescriptor(deploy)
layoutFull(home, deploy, patchedDescriptorDir)
ant.delete(dir: patchedDescriptorDir)
def patchedAppInfo = patchApplicationInfo(deploy)
layoutFull(home, deploy, patchedAppInfo)
ant.delete(dir: patchedAppInfo)
}
String appInfoFileName() {
@@ -64,7 +64,7 @@ List<String> getExcludedPlugins() {
return isDefined("productProperties") ? productProperties.excludedPlugins : []
}
String patchAppDescriptor(String targetDirectory) {
File patchApplicationInfo(String targetDirectory) {
def patchedDirectory = "${targetDirectory}/../patched"
ant.delete(dir: patchedDirectory)
@@ -79,10 +79,10 @@ String patchAppDescriptor(String targetDirectory) {
ant.replace(file: "$patchedDirectory/${appInfoFileName()}", token: "__BUILD_NUMBER__", value: "${code}-$snapshot")
ant.replace(file: "$patchedDirectory/${appInfoFileName()}", token: "__BUILD_DATE__", value: new Date().format("yyyyMMddHHmm"))
return patchedDirectory
return new File(patchedDirectory, "idea/${appInfoFileName()}")
}
def layoutFull(String home, String targetDirectory, String patchedDescriptorDir = null) {
def layoutFull(String home, String targetDirectory, File patchedApplicationInfo = null) {
projectBuilder.stage("layout to $targetDirectory")
List<String> jpsCommonModules = ["jps-model-impl", "jps-model-serialization"]
@@ -197,24 +197,24 @@ def layoutFull(String home, String targetDirectory, String patchedDescriptorDir
module("platform-resources")
def appInfoModule = appInfoModule()
def appInfoInCommunity = "community-resources".equals(appInfoModule)
if (appInfoInCommunity) {
if ("community-resources" == appInfoModule) {
module("community-resources") {
if (patchedDescriptorDir != null) {
exclude(name: appInfoFileName())
if (patchedApplicationInfo != null) {
exclude(name: "idea/$patchedApplicationInfo.name")
}
}
} else {
module("community-resources")
module(appInfoModule) {
if (patchedDescriptorDir != null) {
exclude(name: appInfoFileName())
if (patchedApplicationInfo != null) {
exclude(name: "idea/$patchedApplicationInfo.name")
}
}
}
if (patchedDescriptorDir != null) {
fileset(dir: patchedDescriptorDir)
if (patchedApplicationInfo != null) {
dir("idea") {
fileset(file: patchedApplicationInfo.path)
}
}
}