2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 import com.intellij.openapi.util.SystemInfo
19 import com.intellij.openapi.util.io.FileUtil
20 import com.intellij.openapi.util.text.StringUtil
21 import org.jetbrains.jps.gant.JpsGantTool
22 import org.jetbrains.jps.gant.TeamCityBuildInfoPrinter
23 import org.jetbrains.jps.model.java.*
24 import org.jetbrains.jps.model.library.JpsOrderRootType
25 import org.jetbrains.jps.model.module.JpsModule
26 import org.jetbrains.jps.model.serialization.JpsModelSerializationDataService
28 includeTool << JpsGantTool
30 binding.setVariable("p", {String key, String defaultValue = null ->
32 return getProperty(key) as String
34 catch (MissingPropertyException e) {
35 if (defaultValue != null) {
42 binding.setVariable("guessJdk", {
43 String javaHome = p("java.home")
45 if (new File(javaHome).getName() == "jre") {
46 javaHome = new File(javaHome).getParent()
52 binding.setVariable("includeFile", {String filePath ->
53 Script s = groovyShell.parse(new File(filePath))
58 binding.setVariable("isMac", {
59 return System.getProperty("os.name").toLowerCase().startsWith("mac")
62 binding.setVariable("isWin", {
63 return System.getProperty("os.name").toLowerCase().startsWith("windows")
66 binding.setVariable("isEap", {
67 return "true" == p("component.version.eap")
70 binding.setVariable("mem32", "-server -Xms128m -Xmx512m -XX:MaxPermSize=250m -XX:ReservedCodeCacheSize=240m")
71 binding.setVariable("mem64", "-Xms128m -Xmx750m -XX:MaxPermSize=350m -XX:ReservedCodeCacheSize=240m")
72 binding.setVariable("common_vmoptions", "-XX:+UseConcMarkSweepGC -XX:SoftRefLRUPolicyMSPerMB=50 -ea " +
73 "-Dsun.io.useCanonCaches=false -Djava.net.preferIPv4Stack=true " +
74 "-XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow")
76 binding.setVariable("vmOptions", { "$common_vmoptions".trim() })
77 binding.setVariable("vmOptions32", { "$mem32 ${vmOptions()}".trim() })
78 binding.setVariable("vmOptions64", { "$mem64 ${vmOptions()}".trim() })
80 binding.setVariable("yjpOptions", { String sessionName, String platformSuffix = "" ->
81 "-agentlib:yjpagent$platformSuffix=probe_disable=*,disablealloc,disabletracing,onlylocal,disableexceptiontelemetry,delay=10000,sessionname=$sessionName".trim()
83 binding.setVariable("vmOptions32yjp", { String sessionName ->
84 "${vmOptions32()} ${yjpOptions(sessionName)}".trim()
86 binding.setVariable("vmOptions64yjp", { String sessionName ->
87 "${vmOptions64()} ${yjpOptions(sessionName, "64")}".trim()
90 binding.setVariable("isDefined", {String key ->
95 catch (MissingPropertyException ignored) {
100 private String require(String key) {
104 catch (MissingPropertyException ignored) {
105 projectBuilder.error("Property '$key' is required")
109 private String require(String key, String defaultValue) {
113 catch (MissingPropertyException ignored) {
114 projectBuilder.info("'$key' is not defined. Defaulting to '$defaultValue'")
115 this[key] = defaultValue
119 binding.setVariable("requireProperty", {String key, String defaultValue = null ->
120 if (defaultValue == null) {
124 require(key, defaultValue)
128 binding.setVariable("guessHome", {
129 // current file is supposed to be at build/scripts/*.gant path
130 String uri = requireProperty("gant.file")
131 new File(new URI(uri).getSchemeSpecificPart()).getParentFile().getParentFile().getParent()
134 binding.setVariable("loadProject", {
135 defineJdk("IDEA jdk", setupJdkPath("jdkHome", "$home/build/jdk/1.6", "JDK_16_x64"))
136 defineJdk("1.8", setupJdkPath("jdk8Home", "$home/build/jdk/1.8", "JDK_18_x64"))
137 def bundledKotlinPath = "$home/build/kotlinc"
138 if (!new File(bundledKotlinPath, "lib/kotlin-runtime.jar").exists()) {
139 bundledKotlinPath = "$home/community/build/kotlinc"
141 if (!new File(bundledKotlinPath, "lib/kotlin-runtime.jar").exists()) {
142 projectBuilder.error("Could not find Kotlin runtime at $bundledKotlinPath/lib/kotlin-runtime.jar")
145 setPathVariable("KOTLIN_BUNDLED", bundledKotlinPath)
146 projectBuilder.buildIncrementally = Boolean.parseBoolean(p("jps.build.incrementally", "false"))
147 def dataDirName = projectBuilder.buildIncrementally ? ".jps-incremental-build" : ".jps-build-data"
148 projectBuilder.dataStorageRoot = new File("$home/$dataDirName")
149 projectBuilder.setupAdditionalLogging(new File("${p("teamcity.build.tempDir", p("java.io.tmpdir"))}/system/build-log/build.log"),
150 p("jps.build.debug.logging.categories", ""))
151 loadProjectFromPath(home)
153 def compilerOptions = JpsJavaExtensionService.instance.getOrCreateCompilerConfiguration(project).currentCompilerOptions
154 compilerOptions.GENERATE_NO_WARNINGS = true
155 compilerOptions.DEPRECATION = false
156 compilerOptions.ADDITIONAL_OPTIONS_STRING = compilerOptions.ADDITIONAL_OPTIONS_STRING.replace("-Xlint:unchecked", "")
159 binding.setVariable("removeJdkJarFiles", { Collection<String> classpath ->
160 def jdkHomePaths = project.model.global.libraryCollection.getLibraries(JpsJavaSdkType.INSTANCE).collect {
161 def homeDir = new File(it.properties.homePath)
162 return SystemInfo.isMac && homeDir.name == "Home" ? homeDir.parent : homeDir.absolutePath
164 return classpath.findAll { jarPath -> jdkHomePaths.every { !FileUtil.isAncestor(it, jarPath, false) } }
167 private String setupJdkPath(String propertyName, String defaultDir, String envVarName) {
171 catch (MissingPropertyException ignored) {
172 def jdk = SystemInfo.isMac ? "$defaultDir/Home" : defaultDir
173 if (new File(jdk).exists()) {
174 projectBuilder.info("$propertyName set to $jdk")
177 jdk = System.getenv(envVarName)
179 projectBuilder.info("'$defaultDir' doesn't exist, $propertyName set to '$envVarName' environment variable: $jdk")
183 def version = JdkVersionDetector.instance.detectJdkVersion(jdk)
184 if (propertyName.contains("8") && !version.contains("1.8.")) {
185 projectBuilder.error("JDK 1.8 is required to compile the project, but '$propertyName' property and '$envVarName' environment variable aren't defined and default JDK $jdk ($version) cannot be used as JDK 1.8")
188 projectBuilder.info("'$envVarName' isn't defined and '$defaultDir' doesn't exist, $propertyName set to $jdk")
191 this[propertyName] = jdk
196 private void defineJdk(String jdkName, jdkHomePath) {
197 jdk(jdkName, jdkHomePath) {
198 def toolsJar = "$jdkHomePath/lib/tools.jar"
199 if (new File(toolsJar).exists()) {
204 if (SystemInfo.isMac) {
205 //temporary workaround for Mac: resolve symlinks manually. Previously ZipFileCache used FileUtil.toCanonicalPath method which doesn't resolve symlinks.
206 def jdk = global.libraryCollection.findLibrary(jdkName, JpsJavaSdkType.INSTANCE)
207 def jdkClasspath = jdk.getFiles(JpsOrderRootType.COMPILED)
208 def urls = jdk.getRootUrls(JpsOrderRootType.COMPILED)
209 urls.each { jdk.removeUrl(it, JpsOrderRootType.COMPILED) }
212 jdk.addRoot(it.getCanonicalFile(), JpsOrderRootType.COMPILED)
214 catch (IOException ignored) {
217 projectBuilder.info("JDK '$jdkName' classpath: ${jdk.getFiles(JpsOrderRootType.COMPILED)}")
221 private void setPathVariable(String name, String value) {
222 def pathVars = JpsModelSerializationDataService.getOrCreatePathVariablesConfiguration(global)
223 pathVars.addPathVariable(name, value)
226 binding.setVariable("prepareOutputFolder", {
227 def targetFolder = projectBuilder.buildIncrementally ? "$home/out/incremental-build" : out
228 projectBuilder.targetFolder = targetFolder
229 if (projectBuilder.buildIncrementally && Boolean.parseBoolean(p("jps.build.clear.incremental.caches", "false"))) {
230 FileUtil.delete(new File(targetFolder))
231 FileUtil.delete(projectBuilder.dataStorageRoot)
235 binding.setVariable("clearBuildCaches", {
236 //todo[nik] this is temporary solution until we update bootstrap jps-builders jars to the new version where cleaning is performed in JpsGantProjectBuilder#cleanOutput
237 def storageRoot = projectBuilder.dataStorageRoot
238 if (storageRoot != null) {
239 FileUtil.delete(storageRoot)
243 boolean hasSourceRoots(JpsModule module) {
244 return module.getSourceRoots(JavaSourceRootType.SOURCE).iterator().hasNext()
247 binding.setVariable("findModule", {String name ->
248 project.modules.find { it.name == name }
251 binding.setVariable("allModules", {
252 return project.modules
255 requireProperty("home", guessHome())
257 String readSnapshotBuild() {
258 def file = new File("$home/community/build.txt")
259 if (!file.exists()) {
260 file = new File("$home/build.txt")
263 return file.readLines().get(0)
266 binding.setVariable("snapshot", readSnapshotBuild())
268 projectBuilder.buildInfoPrinter = new TeamCityBuildInfoPrinter()
269 projectBuilder.compressJars = false
271 binding.setVariable("notifyArtifactBuilt", { String artifactPath ->
272 if (!FileUtil.startsWith(artifactPath, home)) {
273 projectBuilder.error("Artifact path $artifactPath should start with $home")
275 def relativePath = artifactPath.substring(home.length())
276 if (relativePath.startsWith("/")) {
277 relativePath = relativePath.substring(1)
279 def file = new File(artifactPath)
280 if (file.isDirectory()) {
281 relativePath += "=>" + file.name
283 projectBuilder.info("##teamcity[publishArtifacts '$relativePath']")
286 def debugPort = System.getProperty("debug.port")
287 def debugSuspend = System.getProperty("debug.suspend") ?: "n"
288 if (debugSuspend == 'y') {
291 ------------->------------- The process suspended until remote debugger connects to debug port -------------<-------------
292 ---------------------------------------^------^------^------^------^------^------^----------------------------------------
297 binding.setVariable("patchFiles", { List files, Map args, String marker = "__" ->
300 ant.replace(file: file, token: "${marker}${arg.key}${marker}", value: arg.value)
305 binding.setVariable("copyAndPatchFile", { String file, String target, Map args, String marker = "__" ->
306 ant.copy(file: file, tofile: target, overwrite: "true") {
307 filterset(begintoken: marker, endtoken: marker) {
309 filter(token: it.key, value: it.value)
315 binding.setVariable("copyAndPatchFiles", { Closure files, String target, Map args, String marker = "__" ->
316 ant.copy(todir: target, overwrite: "true") {
319 filterset(begintoken: marker, endtoken: marker) {
321 filter(token: it.key, value: it.value)
327 binding.setVariable("wireBuildDate", { String buildNumber, String appInfoFile ->
329 patchFiles([appInfoFile], ["BUILD_NUMBER": buildNumber, "BUILD_DATE": DSTAMP,
330 "PACKAGE_CODE": buildNumber.substring(0, buildNumber.indexOf('-'))])
333 binding.setVariable("commonJvmArgsForTests", {
334 def jdwp = "-Xrunjdwp:transport=dt_socket,server=y,suspend=$debugSuspend"
335 if (debugPort != null) jdwp += ",address=$debugPort"
339 "-Dfile.encoding=UTF-8",
340 "-Dio.netty.leakDetectionLevel=PARANOID",
342 "-Xbootclasspath/p:${projectBuilder.moduleOutput(findModule("boot"))}",
343 "-XX:+HeapDumpOnOutOfMemoryError",
344 "-Didea.home.path=$home",
345 "-Didea.config.path=${p("teamcity.build.tempDir")}/config",
346 "-Didea.system.path=${p("teamcity.build.tempDir")}/system",
348 "-XX:ReservedCodeCacheSize=240m",
349 "-XX:-OmitStackTraceInFastThrow",
354 binding.setVariable("classPathLibs", [
364 binding.setVariable("platformApiModules", [
366 "built-in-server-api",
371 "external-system-api",
378 "remote-servers-agent-rt",
379 "remote-servers-api",
380 "structure-view-api",
390 "xml-structure-view-api"
393 binding.setVariable("platformImplementationModules", [
403 "jps-model-serialization",
409 "protocol-reader-runtime",
412 "remote-servers-impl",
413 "script-debugger-backend",
414 "script-debugger-ui",
417 "structure-view-impl",
425 "xml-structure-view-impl",
427 "configuration-store-impl",
430 binding.setVariable("layoutMacApp", { String path, String ch, Map args ->
431 ant.copy(todir: "$path/bin") {
432 fileset(dir: "$ch/bin/mac")
435 ant.copy(todir: path) {
436 fileset(dir: "$ch/build/conf/mac/Contents")
440 format(property: "todayYear", pattern: "yyyy")
443 String executable = args.executable != null ? args.executable : p("component.names.product").toLowerCase()
444 String helpId = args.help_id != null ? args.help_id : "IJ"
445 String icns = "idea.icns"
446 String helpIcns = "$path/Resources/${helpId}.help/Contents/Resources/Shared/product.icns"
447 if (args.icns != null) {
448 ant.delete(file: "$path/Resources/idea.icns")
449 ant.copy(file: args.icns, todir: "$path/Resources")
450 ant.copy(file: args.icns, tofile: helpIcns)
451 icns = new File((String)args.icns).getName();
453 ant.copy(file: "$path/Resources/idea.icns", tofile: helpIcns)
456 String fullName = args.fullName != null ? args.fullName : p("component.names.fullname")
458 String vmOptions = "-Dfile.encoding=UTF-8 ${vmOptions()} -Xverify:none"
460 String minor = p("component.version.minor")
461 String version = isEap() && !minor.contains("RC") && !minor.contains("Beta") ? "EAP $args.buildNumber" : "${p("component.version.major")}.${minor}"
462 String EAP = isEap() && !minor.contains("RC") && !minor.contains("Beta") ? "-EAP" : ""
464 Map properties = readIdeaProperties(args)
466 def coreKeys = ["idea.platform.prefix", "idea.paths.selector", "idea.executable"]
468 String coreProperties = submapToXml(properties, coreKeys);
470 StringBuilder effectiveProperties = new StringBuilder()
471 properties.each { k, v ->
472 if (!coreKeys.contains(k)) {
473 effectiveProperties.append("$k=$v\n");
477 new File("$path/bin/idea.properties").text = effectiveProperties.toString()
478 String ideaVmOptions = "$mem64 -XX:+UseCompressedOops"
479 if (isEap() && !args.mac_no_yjp) {
480 ideaVmOptions += " ${yjpOptions(args.system_selector)}"
482 new File("$path/bin/${executable}.vmoptions").text = ideaVmOptions.split(" ").join("\n")
484 String classPath = classPathLibs.collect {"\$APP_PACKAGE/Contents/lib/${it}" }.join(":")
487 <key>LSArchitecturePriority</key>
489 (args.archs != null ? args.archs : ["x86_64", "i386"]).each {
490 archs += "<string>${it}</string>"
494 String urlSchemes = ""
495 if (args.urlSchemes != null) {
497 <key>CFBundleURLTypes</key>
500 <key>CFBundleTypeRole</key>
501 <string>Editor</string>
502 <key>CFBundleURLName</key>
503 <string>Stacktrace</string>
504 <key>CFBundleURLSchemes</key>
507 args.urlSchemes.each { scheme ->
508 urlSchemes += " <string>${scheme}</string>"
517 ant.replace(file: "$path/Info.plist") {
518 replacefilter(token: "@@build@@", value: args.buildNumber)
519 replacefilter(token: "@@doc_types@@", value: ifNull(args.doc_types, ""))
520 replacefilter(token: "@@executable@@", value: executable)
521 replacefilter(token: "@@icns@@", value: icns)
522 replacefilter(token: "@@bundle_name@@", value: fullName)
523 replacefilter(token: "@@product_state@@", value: EAP)
524 replacefilter(token: "@@bundle_identifier@@", value: args.bundleIdentifier)
525 replacefilter(token: "@@year@@", value: "$todayYear")
526 replacefilter(token: "@@company_name@@", value: p("component.company.name"))
527 replacefilter(token: "@@min_year@@", value: "2000")
528 replacefilter(token: "@@max_year@@", value: "$todayYear")
529 replacefilter(token: "@@version@@", value: version)
530 replacefilter(token: "@@vmoptions@@", value: vmOptions)
531 replacefilter(token: "@@idea_properties@@", value: coreProperties)
532 replacefilter(token: "@@class_path@@", value: classPath)
533 replacefilter(token: "@@help_id@@", value: helpId)
534 replacefilter(token: "@@url_schemes@@", value: urlSchemes)
535 replacefilter(token: "@@archs@@", value: archs)
536 replacefilter(token: "@@min_osx@@", value: ifNull(args.min_osx, "10.6"))
539 if (executable != "idea") {
540 ant.move(file: "$path/MacOS/idea", tofile: "$path/MacOS/$executable")
543 ant.replace(file: "$path/bin/inspect.sh") {
544 replacefilter(token: "@@product_full@@", value: fullName)
545 replacefilter(token: "@@script_name@@", value: executable)
547 if (args.inspect_script != null && args.inspect_script != "inspect") {
548 ant.move(file: "$path/bin/inspect.sh", tofile: "$path/bin/${args.inspect_script}.sh")
551 ant.fixcrlf(srcdir: "$path/bin", includes: "*.sh", eol: "unix")
554 binding.setVariable("winScripts", { String target, String home, String name, Map args ->
555 String fullName = args.fullName != null ? args.fullName : p("component.names.fullname")
556 String product_uc = args.product_uc != null ? args.product_uc : p("component.names.product").toUpperCase()
557 String vm_options = args.vm_options != null ? args.vm_options : "${p("component.names.product").toLowerCase()}.exe"
558 if (vm_options.endsWith(".exe")) {
559 vm_options = vm_options.replace(".exe", "%BITS%.exe")
562 vm_options = vm_options + "%BITS%"
565 String classPath = "SET CLASS_PATH=%IDE_HOME%\\lib\\${classPathLibs[0]}\n"
566 classPath += classPathLibs[1..-1].collect {"SET CLASS_PATH=%CLASS_PATH%;%IDE_HOME%\\lib\\${it}"}.join("\n")
567 if (args.tools_jar) classPath += "\nSET CLASS_PATH=%CLASS_PATH%;%JDK%\\lib\\tools.jar"
569 ant.copy(todir: "$target/bin") {
570 fileset(dir: "$home/bin/scripts/win")
572 filterset(begintoken: "@@", endtoken: "@@") {
573 filter(token: "product_full", value: fullName)
574 filter(token: "product_uc", value: product_uc)
575 filter(token: "vm_options", value: vm_options)
576 filter(token: "isEap", value: isEap())
577 filter(token: "system_selector", value: args.system_selector)
578 filter(token: "ide_jvm_args", value: ifNull(args.ide_jvm_args, ""))
579 filter(token: "class_path", value: classPath)
580 filter(token: "script_name", value: name)
584 if (name != "idea.bat") {
585 ant.move(file: "$target/bin/idea.bat", tofile: "$target/bin/$name")
587 if (args.inspect_script != null && args.inspect_script != "inspect") {
588 ant.move(file: "$target/bin/inspect.bat", tofile: "$target/bin/${args.inspect_script}.bat")
591 ant.fixcrlf(srcdir: "$target/bin", includes: "*.bat", eol: "dos")
594 private ifNull(v, defVal) { v != null ? v : defVal }
596 binding.setVariable("unixScripts", { String target, String home, String name, Map args ->
597 String fullName = args.fullName != null ? args.fullName : p("component.names.fullname")
598 String product_uc = args.product_uc != null ? args.product_uc : p("component.names.product").toUpperCase()
599 String vm_options = args.vm_options != null ? args.vm_options : p("component.names.product").toLowerCase()
601 String classPath = "CLASSPATH=\"\$IDE_HOME/lib/${classPathLibs[0]}\"\n"
602 classPath += classPathLibs[1..-1].collect {"CLASSPATH=\"\$CLASSPATH:\$IDE_HOME/lib/${it}\""}.join("\n")
603 if (args.tools_jar) classPath += "\nCLASSPATH=\"\$CLASSPATH:\$JDK/lib/tools.jar\""
605 ant.copy(todir: "$target/bin") {
606 fileset(dir: "$home/bin/scripts/unix")
608 filterset(begintoken: "@@", endtoken: "@@") {
609 filter(token: "product_full", value: fullName)
610 filter(token: "product_uc", value: product_uc)
611 filter(token: "vm_options", value: vm_options)
612 filter(token: "isEap", value: isEap())
613 filter(token: "system_selector", value: args.system_selector)
614 filter(token: "ide_jvm_args", value: ifNull(args.ide_jvm_args, ""))
615 filter(token: "class_path", value: classPath)
616 filter(token: "script_name", value: name)
620 if (name != "idea.sh") {
621 ant.move(file: "$target/bin/idea.sh", tofile: "$target/bin/$name")
623 if (args.inspect_script != null && args.inspect_script != "inspect") {
624 ant.move(file: "$target/bin/inspect.sh", tofile: "$target/bin/${args.inspect_script}.sh")
627 ant.fixcrlf(srcdir: "$target/bin", includes: "*.sh", eol: "unix")
630 binding.setVariable("winVMOptions", { String target, String yjpSessionName, String name, String name64 = null ->
632 def options = isEap() && yjpSessionName != null ? vmOptions32yjp(yjpSessionName) : vmOptions32()
633 ant.echo(file: "$target/bin/${name}.vmoptions", message: options.replace(' ', '\n'))
636 if (name64 != null) {
637 def options = isEap() && yjpSessionName != null ? vmOptions64yjp(yjpSessionName) : vmOptions64()
638 ant.echo(file: "$target/bin/${name64}.vmoptions", message: options.replace(' ', '\n'))
641 ant.fixcrlf(srcdir: "$target/bin", includes: "*.vmoptions", eol: "dos")
644 binding.setVariable("unixVMOptions", { String target, String name, String name64 = (name + "64") ->
646 ant.echo(file: "$target/bin/${name}.vmoptions", message: "${vmOptions32()} -Dawt.useSystemAAFontSettings=lcd".trim().replace(' ', '\n'))
648 if (name64 != null) {
649 ant.echo(file: "$target/bin/${name64}.vmoptions", message: "${vmOptions64()} -Dawt.useSystemAAFontSettings=lcd".trim().replace(' ', '\n'))
651 ant.fixcrlf(srcdir: "$target/bin", includes: "*.vmoptions", eol: "unix")
654 binding.setVariable("unixReadme", { String target, String home, Map args ->
655 String fullName = args.fullName != null ? args.fullName : p("component.names.fullname")
656 String settings_dir = args.system_selector.replaceFirst("\\d+", "")
657 copyAndPatchFile("$home/build/Install-Linux-tar.txt", "$target/Install-Linux-tar.txt",
658 ["product_full": fullName,
659 "product": p("component.names.product").toLowerCase(),
660 "system_selector": args.system_selector,
661 "settings_dir": settings_dir], "@@")
662 ant.fixcrlf(file: "$target/bin/Install-Linux-tar.txt", eol: "unix")
665 binding.setVariable("forceDelete", { String dirPath ->
666 // if wasn't deleted - retry several times
668 while (attempt < 21 && (new File(dirPath).exists())) {
670 ant.echo "Deleting $dirPath ... (attempt=$attempt)"
672 // let's wait a bit and try again - may be help
673 // in some cases on our windows 7 agents
677 ant.delete(failonerror: false, dir: dirPath)
682 if (new File(dirPath).exists()) {
683 ant.project.log ("Cannot delete directory: $dirPath" )
688 binding.setVariable("patchPropertiesFile", { String target, Map args = [:] ->
689 String file = "$target/bin/idea.properties"
691 if (args.appendices != null) {
692 ant.concat(destfile: file, append: true) {
693 args.appendices.each {
699 String product_uc = args.product_uc != null ? args.product_uc : p("component.names.product").toUpperCase()
700 String settings_dir = args.system_selector.replaceFirst("\\d+", "")
701 ant.replace(file: file) {
702 replacefilter(token: "@@product_uc@@", value: product_uc)
703 replacefilter(token: "@@settings_dir@@", value: settings_dir)
706 String message = (isEap() ? """
707 #-----------------------------------------------------------------------
708 # Change to 'disabled' if you don't want to receive instant visual notifications
709 # about fatal errors that happen to an IDE or plugins installed.
710 #-----------------------------------------------------------------------
711 idea.fatal.error.notification=enabled
714 #-----------------------------------------------------------------------
715 # Change to 'enabled' if you want to receive instant visual notifications
716 # about fatal errors that happen to an IDE or plugins installed.
717 #-----------------------------------------------------------------------
718 idea.fatal.error.notification=disabled
720 ant.echo(file: file, append: true, message: message)
723 binding.setVariable("zipSources", { String home, String targetDir ->
724 String sources = "$targetDir/sources.zip"
725 projectBuilder.stage("zip sources to $sources")
727 ant.mkdir(dir: targetDir)
728 ant.delete(file: sources)
729 ant.zip(destfile: sources) {
731 ["java", "groovy", "ipr", "iml", "form", "xml", "properties", "kt"].each {
732 include(name: "**/*.$it")
734 exclude(name: "**/testData/**")
738 notifyArtifactBuilt(sources)
741 binding.setVariable("zipSourcesOfModules", { String targetFilePath, Collection<String> modules ->
742 projectBuilder.stage("zip sources of ${modules.size()} modules to $targetFilePath")
744 ant.mkdir(dir: new File(targetFilePath).getParent())
745 ant.delete(file: targetFilePath)
746 ant.zip(destfile: targetFilePath) {
748 JpsModule module = findModule(it)
749 module.getSourceRoots(JavaSourceRootType.SOURCE).each { root ->
750 ant.zipfileset(dir: root.file.absolutePath, prefix: root.properties.packagePrefix.replace('.', '/'), erroronmissingdir: false)
752 module.getSourceRoots(JavaResourceRootType.RESOURCE).each { root ->
753 ant.zipfileset(dir: root.file.absolutePath, prefix: root.properties.relativeOutputPath, erroronmissingdir: false)
758 notifyArtifactBuilt(targetFilePath)
761 binding.setVariable("moduleSrc", {String moduleName ->
763 JpsModule module = findModule(moduleName)
764 if (module == null) projectBuilder.error("Module $moduleName not found")
765 module.getSourceRoots(JavaSourceRootType.SOURCE).each { root ->
766 result << ant.zipfileset(dir: root.file.absolutePath , prefix: root.properties.packagePrefix.replace('.', '/'), erroronmissingdir: false)
774 * Load all properties from file:
775 * readIdeaProperties("idea.properties.path" : "$home/ruby/build/idea.properties")
777 * Load all properties except "idea.cycle.buffer.size", change "idea.max.intellisense.filesize" to 3000
778 * and enable "idea.is.internal" mode:
779 * readIdeaProperties("idea.properties.path" : "$home/ruby/build/idea.properties",
780 * "idea.properties" : ["idea.max.intellisense.filesize" : 3000,
781 * "idea.cycle.buffer.size" : null,
782 * "idea.is.internal" : true ])
784 * @return text xml properties description in xml
786 private Map readIdeaProperties(Map args) {
787 String ideaPropertiesPath = args == null ? null : args.get("idea.properties.path")
788 if (ideaPropertiesPath == null) {
792 // read idea.properties file
793 Properties ideaProperties = new Properties();
794 FileInputStream ideaPropertiesFile = new FileInputStream(ideaPropertiesPath);
795 ideaProperties.load(ideaPropertiesFile);
796 ideaPropertiesFile.close();
798 def defaultProperties = ["CVS_PASSFILE": "~/.cvspass",
799 "com.apple.mrj.application.live-resize": "false",
800 "idea.paths.selector": args.system_selector,
801 "idea.executable": args.executable,
802 "java.endorsed.dirs": "",
803 "idea.smooth.progress": "false",
804 "apple.laf.useScreenMenuBar": "true",
805 "apple.awt.graphics.UseQuartz": "true",
806 "apple.awt.fullscreencapturealldisplays": "false"]
807 if (args.platform_prefix != null) {
808 defaultProperties.put("idea.platform.prefix", args.platform_prefix)
811 Map properties = defaultProperties
812 def customProperties = args.get("idea.properties")
813 if (customProperties != null) {
814 properties += customProperties
817 properties.each {k, v ->
819 // if overridden with null - ignore property
820 ideaProperties.remove(k)
822 // if property is overridden in args map - use new value
823 ideaProperties.put(k, v)
827 return ideaProperties;
830 private String submapToXml(Map properties, List keys) {
831 // generate properties description for Info.plist
832 StringBuilder buff = new StringBuilder()
835 String value = properties[key]
840 <string>$value</string>
845 return buff.toString()
848 private List<File> getChildren(File file) {
849 if (!file.isDirectory()) return []
850 return file.listFiles().sort { File f -> f.name.toLowerCase() }
853 binding.setVariable("signExecutableFiles", { String binDir ->
854 projectBuilder.stage("Signing binaries")
855 def fileBinDir = new File(binDir)
857 getChildren(fileBinDir).each {
858 fileName = it.getName()
859 if (fileName.endsWith(".exe")) {
860 executeExternalAnt(["dirName": binDir, "fileName": fileName], "$home/build/signBuild.xml")
863 projectBuilder.stage("Signing done")
866 binding.setVariable("bundledJDKs"){
867 requireProperty("jdk16.mac", "true")
868 requireProperty("jdk.bundled.win", "1.8")
869 requireProperty("jdk.bundled.linux", "false")
870 requireProperty("jdk.bundled.mac", "1.8")
871 requireProperty("jdk.custom.mac", "true")
872 requireProperty("jdk.custom.linux", "false")
873 if (new File("${home}/build/jdk").exists()) {
874 if (p("jdk.bundled.win") != "false") {
875 setProperty("winJDK", getPathToBundledJDK(new File("${home}/build/jdk/win"), "jdk" + p("jdk.bundled.win"), "x32.zip"))
876 extractRedistJre(winJDK, "${paths.sandbox}/bundled.win.jdk/jre")
878 if (p("jdk.bundled.linux") != "false") {
879 setProperty("linuxJDK", getPathToBundledJDK(new File("${home}/build/jdk/linux"), "jdk" + p("jdk.bundled.linux"), ".tar"))
880 extractRedistJre(linuxJDK, "${paths.sandbox}/bundled.linux.jdk/jre")
882 def customLinuxJdkDir = new File("${home}/build/jdk/custom/linux")
883 if (p("jdk.custom.linux") == "true" && (customLinuxJdkDir.exists() && customLinuxJdkDir.isDirectory())) {
884 setProperty("linuxCustomJDK", getPathToBundledJDK(customLinuxJdkDir, "openjdk.1.8", ".tar.gz"))
885 extractRedistJre(linuxCustomJDK, "${paths.sandbox}/custom.linux.jdk")
887 if (p("jdk.bundled.mac") != "false") {
888 setProperty("macJDK", getPathToBundledJDK(new File("${home}/build/jdk/mac"), "jdk" + p("jdk.bundled.mac"), ".tar"))
890 def customJdkDir = new File("${home}/build/jdk/custom")
891 if (p("jdk.custom.mac") == "true" && (customJdkDir.exists() && customJdkDir.isDirectory())) {
892 setProperty("macCustomJDK", getPathToBundledJDK(customJdkDir, "openjdk.1.8", ".tar.gz"))
897 binding.setVariable("getPathToBundledJDK", { File jdkDir, String prefix, String ext ->
899 getChildren(jdkDir).each {
900 if (it.getName().startsWith(prefix) && it.getName().endsWith(ext)) {
901 JdkFileName = it.getAbsolutePath()
902 if (ext == ".tar.gz") {
903 def OriginalJdkFileName = JdkFileName.substring(0, JdkFileName.length() - 3)
904 JdkFileName = JdkFileName.substring(0, JdkFileName.length() - 7) + "_${buildNumber}.tar"
905 if (new File(JdkFileName).exists()) { ant.delete(file: JdkFileName) }
906 ant.gunzip(src: it.getAbsolutePath())
907 ant.copy(file: OriginalJdkFileName, tofile: JdkFileName)
911 return JdkFileName.replace('\\', '/')
914 binding.setVariable("buildWinZip", { String zipPath, List paths, String zipPrefix = "" ->
915 projectBuilder.stage(".win.zip")
917 fixIdeaPropertiesEol(paths, "dos")
919 ant.zip(zipfile: zipPath) {
921 zipfileset(dir: it, prefix: zipPrefix)
925 notifyArtifactBuilt(zipPath)
928 binding.setVariable("buildCrossPlatformZip", { String zipPath, String sandbox, List commonPaths, String distWin, String distUnix, String distMac ->
929 projectBuilder.stage("Building cross-platform zip")
931 def executableName = StringUtil.trimEnd(new File(distMac, "bin").list().find { it.endsWith(".vmoptions") }, ".vmoptions")
932 def zipDir = "$sandbox/cross-platform-zip"
933 def ideaPropertiesFile = commonPaths.collect { new File(it, "bin/idea.properties") }.find { it.exists() }
934 ["win", "linux"].each {
935 ant.mkdir(dir: "$zipDir/bin/$it")
936 ant.copy(file: ideaPropertiesFile.absolutePath, todir: "$zipDir/bin/$it")
938 ant.fixcrlf(file: "$zipDir/bin/win/idea.properties", eol: "dos")
939 ant.copy(todir: "$zipDir/bin/linux") {
940 fileset(dir: "$distUnix/bin") {
941 include(name: "*.vmoptions")
944 ant.copy(todir: "$zipDir/bin/mac") {
945 fileset(dir: "$distMac/bin") {
946 include(name: "${executableName}.vmoptions")
947 include(name: "idea.properties")
950 ant.copy(file: "$distMac/bin/${executableName}.vmoptions", tofile: "$zipDir/bin/mac/${executableName}64.vmoptions")
951 ant.copy(todir: "$zipDir/bin") {
952 fileset(dir: "$distMac/bin") {
953 include(name: "*.jnilib")
955 mapper(type: "glob", from: "*.jnilib", to: "*.dylib")
958 ant.zip(zipfile: zipPath, duplicate: "fail") {
961 exclude(name: "bin/idea.properties")
966 fileset(dir: distWin) {
967 exclude(name: "bin/fsnotifier*.exe")
968 exclude(name: "bin/*.exe.vmoptions")
969 exclude(name: "bin/${executableName}*.exe")
971 zipfileset(dir: "$distWin/bin", prefix: "bin/win") {
972 include(name: "fsnotifier*.exe")
973 include(name: "*.exe.vmoptions")
976 fileset(dir: distUnix) {
977 exclude(name: "bin/fsnotifier*")
978 exclude(name: "bin/*.vmoptions")
979 exclude(name: "bin/*.sh")
980 exclude(name: "help/**")
982 zipfileset(dir: "$distUnix/bin", filemode: "775", prefix: "bin") {
983 include(name: "*.sh")
985 zipfileset(dir: "$distUnix/bin", prefix: "bin/linux", filemode: "775") {
986 include(name: "fsnotifier*")
989 fileset(dir: distMac) {
990 exclude(name: "bin/fsnotifier*")
991 exclude(name: "bin/restarter*")
992 exclude(name: "bin/*.sh")
993 exclude(name: "bin/*.py")
994 exclude(name: "bin/*.jnilib")
995 exclude(name: "bin/idea.properties")
996 exclude(name: "bin/*.vmoptions")
998 zipfileset(dir: "$distMac/bin", filemode: "775", prefix: "bin") {
999 include(name: "restarter*")
1000 include(name: "*.py")
1002 zipfileset(dir: "$distMac/bin", prefix: "bin/mac", filemode: "775") {
1003 include(name: "fsnotifier*")
1007 notifyArtifactBuilt(zipPath)
1010 binding.setVariable("buildMacZip", { String zipRoot, String zipPath, List paths, String macPath, List extraBins = [] ->
1011 projectBuilder.stage(".mac.zip")
1013 allPaths = paths + [macPath]
1014 ant.zip(zipfile: zipPath) {
1016 zipfileset(dir: it, prefix: zipRoot) {
1017 exclude(name: "bin/*.sh")
1018 exclude(name: "bin/*.py")
1019 exclude(name: "bin/fsnotifier")
1020 exclude(name: "bin/restarter")
1021 exclude(name: "MacOS/*")
1022 exclude(name: "build.txt")
1023 exclude(name: "NOTICE.txt")
1027 exclude(name: "bin/idea.properties")
1032 zipfileset(dir: it, filemode: "755", prefix: zipRoot) {
1033 include(name: "bin/*.sh")
1034 include(name: "bin/*.py")
1035 include(name: "bin/fsnotifier")
1036 include(name: "bin/restarter")
1037 include(name: "MacOS/*")
1045 zipfileset(dir: it, prefix: "$zipRoot/Resources") {
1046 include(name: "build.txt")
1047 include(name: "NOTICE.txt")
1051 zipfileset(file: "$macPath/bin/idea.properties", prefix: "$zipRoot/bin")
1055 binding.setVariable("buildTarGz", { String tarRoot, String tarPath, List paths, List extraBins = [] ->
1056 projectBuilder.stage(".tar.gz")
1058 fixIdeaPropertiesEol(paths, "unix")
1060 ant.tar(tarfile: tarPath, longfile: "gnu") {
1062 tarfileset(dir: it, prefix: tarRoot) {
1063 exclude(name: "bin/*.sh")
1064 exclude(name: "bin/fsnotifier*")
1073 tarfileset(dir: it, filemode: "755", prefix: tarRoot) {
1074 include(name: "bin/*.sh")
1075 include(name: "bin/fsnotifier*")
1084 String gzPath = "${tarPath}.gz"
1085 ant.gzip(src: tarPath, zipfile: gzPath)
1086 ant.delete(file: tarPath)
1087 notifyArtifactBuilt(gzPath)
1090 binding.setVariable("extractRedistJre", { String jdk_file_name, String destination ->
1091 def jre_redist = "${destination}"
1092 ant.mkdir(dir: jre_redist)
1093 if (jdk_file_name.endsWith(".tar")) {
1094 ant.untar(dest: jre_redist, src: "${jdk_file_name}")
1097 ant.unzip(dest: jre_redist, src: "${jdk_file_name}")
1101 private void fixIdeaPropertiesEol(List paths, String eol) {
1103 String file = "$it/bin/idea.properties"
1104 if (new File(file).exists()) {
1105 ant.fixcrlf(file: file, eol: eol)
1110 binding.setVariable("buildWinLauncher", { String ch, String inputPath, String outputPath, String appInfo,
1111 String launcherProperties, String pathsSelector, List resourcePaths ->
1112 projectBuilder.stage("winLauncher")
1114 if (pathsSelector != null) {
1115 def paths = getProperty("paths")
1116 def launcherPropertiesTemp = "${paths.sandbox}/launcher.properties"
1117 copyAndPatchFile(launcherProperties, launcherPropertiesTemp, ["PRODUCT_PATHS_SELECTOR": pathsSelector,
1118 "IDE-NAME": p("component.names.product").toUpperCase()])
1119 launcherProperties = launcherPropertiesTemp
1122 ant.java(classname: "com.pme.launcher.LauncherGeneratorMain", fork: "true", failonerror: "true") {
1123 sysproperty(key: "java.awt.headless", value: "true")
1124 arg(value: inputPath)
1126 arg(value: "$ch/native/WinLauncher/WinLauncher/resource.h")
1127 arg(value: launcherProperties)
1128 arg(value: outputPath)
1130 pathelement(location: "$ch/build/lib/launcher-generator.jar")
1131 fileset(dir: "$ch/lib") {
1132 include(name: "guava*.jar")
1133 include(name: "jdom.jar")
1134 include(name: "sanselan*.jar")
1136 resourcePaths.each {
1137 pathelement(location: it)
1143 binding.setVariable("layoutUpdater", { String target, String jarName = "updater.jar" ->
1151 binding.setVariable("collectUsedJars", { List modules, List approvedJars, List forbiddenJars, List modulesToBuild ->
1152 def usedJars = new HashSet();
1155 def module = findModule(it)
1156 if (module != null) {
1157 projectBuilder.moduleRuntimeClasspath(module, false).each {
1158 File file = new File(it)
1159 if (file.exists()) {
1160 String path = file.canonicalPath.replace('\\', '/')
1161 if (path.endsWith(".jar") && approvedJars.any { path.startsWith(it) } && !forbiddenJars.any { path.contains(it) }) {
1162 if (usedJars.add(path)) {
1163 projectBuilder.info("\tADDED: $path for ${module.getName()}")
1168 if (modulesToBuild != null) {
1169 modulesToBuild << module
1173 projectBuilder.warning("$it is not a module")
1180 binding.setVariable("buildModulesAndCollectUsedJars", { List modules, List approvedJars, List forbiddenJars ->
1181 def modulesToBuild = []
1182 def usedJars = collectUsedJars(modules, approvedJars, forbiddenJars, modulesToBuild)
1184 projectBuilder.cleanOutput()
1185 projectBuilder.buildModules(modulesToBuild)
1190 binding.setVariable("buildSearchableOptions", { String target, List licenses, Closure cp, String jvmArgs = null,
1191 def paths = getProperty("paths") ->
1192 projectBuilder.stage("Building searchable options")
1194 String targetFile = "${target}/searchableOptions.xml"
1195 ant.delete(file: targetFile)
1198 ant.copy(file: it, todir: paths.ideaSystem)
1201 ant.path(id: "searchable.options.classpath") { cp() }
1202 String classpathFile = "${paths.sandbox}/classpath.txt"
1203 ant.echo(file: classpathFile, append: false, message: "\${toString:searchable.options.classpath}")
1204 ant.replace(file: classpathFile, token: File.pathSeparator, value: "\n")
1206 ant.java(classname: "com.intellij.rt.execution.CommandLineWrapper", fork: true, failonerror: true) {
1207 jvmarg(line: "-ea -Xmx500m -XX:MaxPermSize=200m")
1208 jvmarg(value: "-Xbootclasspath/a:${projectBuilder.moduleOutput(findModule("boot"))}")
1209 jvmarg(value: "-Didea.home.path=${home}")
1210 jvmarg(value: "-Didea.system.path=${paths.ideaSystem}")
1211 jvmarg(value: "-Didea.config.path=${paths.ideaConfig}")
1212 if (jvmArgs != null) {
1213 jvmarg(line: jvmArgs)
1216 arg(value: "${classpathFile}")
1217 arg(line: "com.intellij.idea.Main traverseUI")
1218 arg(value: "${target}/searchableOptions.xml")
1221 pathelement(location: "${projectBuilder.moduleOutput(findModule("java-runtime"))}")
1225 ant.available(file: targetFile, property: "searchable.options.exists");
1226 ant.fail(unless: "searchable.options.exists", message: "Searchable options were not built.")
1229 binding.setVariable("reassignAltClickToMultipleCarets", {String communityHome ->
1230 String defaultKeymapContent = new File("$communityHome/platform/platform-resources/src/idea/Keymap_Default.xml").text
1231 defaultKeymapContent = defaultKeymapContent.replace("<mouse-shortcut keystroke=\"alt button1\"/>", "")
1232 defaultKeymapContent = defaultKeymapContent.replace("<mouse-shortcut keystroke=\"alt shift button1\"/>",
1233 "<mouse-shortcut keystroke=\"alt button1\"/>")
1234 patchedKeymapFile = new File("${paths.sandbox}/classes/production/platform-resources/idea/Keymap_Default.xml")
1235 patchedKeymapFile.write(defaultKeymapContent)
1238 // modules used in Upsource and in Kotlin as an API to IDEA
1239 binding.setVariable("analysisApiModules", [
1243 "duplicates-analysis",
1248 "java-analysis-api",
1249 "java-indexing-api",
1251 "java-structure-view",
1253 "jps-model-serialization",
1255 "structure-view-api",
1260 "xml-structure-view-api",
1262 binding.setVariable("analysisImplModules", [
1266 "java-analysis-impl",
1267 "java-indexing-impl",
1269 "projectModel-impl",
1270 "structure-view-impl",
1271 "xml-analysis-impl",
1273 "xml-structure-view-impl",