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.io.FileUtil
19 import org.jetbrains.jps.gant.JpsGantTool
20 import org.jetbrains.jps.gant.TeamCityBuildInfoPrinter
21 import org.jetbrains.jps.model.java.JavaSourceRootType
22 import org.jetbrains.jps.model.java.JpsJavaExtensionService
23 import org.jetbrains.jps.model.module.JpsModule
25 includeTool << JpsGantTool
27 binding.setVariable("p", {String key, String defaultValue = null ->
29 return getProperty(key) as String
31 catch (MissingPropertyException e) {
32 if (defaultValue != null) {
39 binding.setVariable("guessJdk", {
40 String javaHome = p("java.home")
42 if (new File(javaHome).getName() == "jre") {
43 javaHome = new File(javaHome).getParent()
49 binding.setVariable("includeFile", {String filePath ->
50 Script s = groovyShell.parse(new File(filePath))
55 binding.setVariable("isMac", {
56 return System.getProperty("os.name").toLowerCase().startsWith("mac")
59 binding.setVariable("isWin", {
60 return System.getProperty("os.name").toLowerCase().startsWith("windows")
63 binding.setVariable("isEap", {
64 return "true" == p("component.version.eap")
67 binding.setVariable("mem32", "-server -Xms128m -Xmx512m -XX:MaxPermSize=250m -XX:ReservedCodeCacheSize=240m")
68 binding.setVariable("mem64", "-Xms128m -Xmx750m -XX:MaxPermSize=350m -XX:ReservedCodeCacheSize=240m")
69 binding.setVariable("common_vmoptions", "-XX:+UseConcMarkSweepGC -XX:SoftRefLRUPolicyMSPerMB=50 -ea " +
70 "-Dsun.io.useCanonCaches=false -Djava.net.preferIPv4Stack=true " +
71 "-XX:+HeapDumpOnOutOfMemoryError")
73 binding.setVariable("vmOptions", { "$common_vmoptions".trim() })
74 binding.setVariable("vmOptions32", { "$mem32 ${vmOptions()}".trim() })
75 binding.setVariable("vmOptions64", { "$mem64 ${vmOptions()}".trim() })
77 binding.setVariable("yjpOptions", { String systemSelector, String platformSuffix = "" ->
78 "-agentlib:yjpagent$platformSuffix=probe_disable=*,disablealloc,disabletracing,onlylocal,disableexceptiontelemetry,delay=10000,sessionname=$systemSelector".trim()
80 binding.setVariable("vmOptions32yjp", { String systemSelector ->
81 "${vmOptions32()} ${yjpOptions(systemSelector)}".trim()
83 binding.setVariable("vmOptions64yjp", { String systemSelector ->
84 "${vmOptions64()} ${yjpOptions(systemSelector, "64")}".trim()
87 binding.setVariable("isDefined", {String key ->
92 catch (MissingPropertyException ignored) {
97 private String require(String key) {
101 catch (MissingPropertyException ignored) {
102 projectBuilder.error("Property '$key' is required")
106 private String require(String key, String defaultValue) {
110 catch (MissingPropertyException ignored) {
111 projectBuilder.info("'$key' is not defined. Defaulting to '$defaultValue'")
112 this[key] = defaultValue
116 binding.setVariable("requireProperty", {String key, String defaultValue = null ->
117 if (defaultValue == null) {
121 require(key, defaultValue)
125 binding.setVariable("guessHome", {
126 // current file is supposed to be at build/scripts/*.gant path
127 String uri = requireProperty("gant.file")
128 new File(new URI(uri).getSchemeSpecificPart()).getParentFile().getParentFile().getParent()
131 binding.setVariable("loadProject", {
132 requireProperty("jdkHome", guessJdk())
134 jdk("IDEA jdk", jdkHome) {
136 classpath "$jdkHome/lib/tools.jar"
139 projectBuilder.buildIncrementally = Boolean.parseBoolean(p("jps.build.incrementally", "false"))
140 def dataDirName = projectBuilder.buildIncrementally ? ".jps-incremental-build" : ".jps-build-data"
141 projectBuilder.dataStorageRoot = new File("$home/$dataDirName")
142 projectBuilder.setupAdditionalLogging(new File("${p("teamcity.build.tempDir", p("java.io.tmpdir"))}/system/build-log/build.log"),
143 p("jps.build.debug.logging.categories", ""))
144 loadProjectFromPath(home)
146 def compilerOptions = JpsJavaExtensionService.instance.getOrCreateCompilerConfiguration(project).currentCompilerOptions
147 compilerOptions.GENERATE_NO_WARNINGS = true
148 compilerOptions.DEPRECATION = false
149 compilerOptions.ADDITIONAL_OPTIONS_STRING = compilerOptions.ADDITIONAL_OPTIONS_STRING.replace("-Xlint:unchecked", "")
152 binding.setVariable("prepareOutputFolder", {
153 def targetFolder = projectBuilder.buildIncrementally ? "$home/out/incremental-build" : out
154 projectBuilder.targetFolder = targetFolder
155 if (projectBuilder.buildIncrementally && Boolean.parseBoolean(p("jps.build.clear.incremental.caches", "false"))) {
156 FileUtil.delete(new File(targetFolder))
157 FileUtil.delete(projectBuilder.dataStorageRoot)
161 boolean hasSourceRoots(JpsModule module) {
162 return module.getSourceRoots(JavaSourceRootType.SOURCE).iterator().hasNext()
165 binding.setVariable("findModule", {String name ->
166 project.modules.find { it.name == name }
169 binding.setVariable("allModules", {
170 return project.modules
173 binding.setVariable("printUnusedModules", {Set<String> usedModules ->
174 allModules().each {JpsModule m ->
175 if (!usedModules.contains(m.name) && hasSourceRoots(m)) {
176 projectBuilder.warning("Module $m.name is not used in project layout")
181 requireProperty("home", guessHome())
183 String readSnapshotBuild() {
184 def file = new File("$home/community/build.txt")
185 if (!file.exists()) {
186 file = new File("$home/build.txt")
189 return file.readLines().get(0)
192 binding.setVariable("snapshot", readSnapshotBuild())
194 projectBuilder.buildInfoPrinter = new TeamCityBuildInfoPrinter()
195 projectBuilder.compressJars = false
197 binding.setVariable("notifyArtifactBuilt", { String artifactPath ->
198 if (!artifactPath.startsWith(home)) {
199 projectBuilder.error("Artifact path $artifactPath should start with $home")
201 def relativePath = artifactPath.substring(home.length())
202 if (relativePath.startsWith("/")) {
203 relativePath = relativePath.substring(1)
205 def file = new File(artifactPath)
206 if (file.isDirectory()) {
207 relativePath += "=>" + file.name
209 projectBuilder.info("##teamcity[publishArtifacts '$relativePath']")
212 def debugPort = System.getProperty("debug.port")
213 def debugSuspend = System.getProperty("debug.suspend") ?: "n"
214 if (debugSuspend == 'y') {
217 ------------->------------- The process suspended until remote debugger connects to debug port -------------<-------------
218 ---------------------------------------^------^------^------^------^------^------^----------------------------------------
223 binding.setVariable("patchFiles", { List files, Map args, String marker = "__" ->
226 ant.replace(file: file, token: "${marker}${arg.key}${marker}", value: arg.value)
231 binding.setVariable("copyAndPatchFile", { String file, String target, Map args, String marker = "__" ->
232 ant.copy(file: file, tofile: target, overwrite: "true") {
233 filterset(begintoken: marker, endtoken: marker) {
235 filter(token: it.key, value: it.value)
241 binding.setVariable("copyAndPatchFiles", { Closure files, String target, Map args, String marker = "__" ->
242 ant.copy(todir: target, overwrite: "true") {
245 filterset(begintoken: marker, endtoken: marker) {
247 filter(token: it.key, value: it.value)
253 binding.setVariable("wireBuildDate", { String buildNumber, String appInfoFile ->
255 patchFiles([appInfoFile], ["BUILD_NUMBER": buildNumber, "BUILD_DATE": DSTAMP])
258 binding.setVariable("commonJvmArgsForTests", {
259 def jdwp = "-Xrunjdwp:transport=dt_socket,server=y,suspend=$debugSuspend"
260 if (debugPort != null) jdwp += ",address=$debugPort"
263 "-Dio.netty.leakDetectionLevel=PARANOID",
265 "-Xbootclasspath/p:${projectBuilder.moduleOutput(findModule("boot"))}",
266 "-XX:+HeapDumpOnOutOfMemoryError",
267 "-Didea.home.path=$home",
268 "-Didea.config.path=${p("teamcity.build.tempDir")}/config",
269 "-Didea.system.path=${p("teamcity.build.tempDir")}/system",
275 binding.setVariable("classPathLibs", [
285 binding.setVariable("platformApiModules", [
287 "built-in-server-api",
292 "external-system-api",
299 "remote-servers-agent-rt",
300 "remote-servers-api",
301 "structure-view-api",
311 "xml-structure-view-api"
314 binding.setVariable("platformImplementationModules", [
324 "jps-model-serialization",
330 "protocol-reader-runtime",
333 "remote-servers-impl",
334 "script-debugger-backend",
335 "script-debugger-ui",
338 "structure-view-impl",
346 "xml-structure-view-impl",
350 binding.setVariable("layoutMacApp", { String path, String ch, Map args ->
351 ant.copy(todir: "$path/bin") {
352 fileset(dir: "$ch/bin/mac")
355 ant.copy(todir: path) {
356 fileset(dir: "$ch/build/conf/mac/Contents")
360 format(property: "todayYear", pattern: "yyyy")
363 String executable = args.executable != null ? args.executable : p("component.names.product").toLowerCase()
364 String helpId = args.help_id != null ? args.help_id : "IJ"
365 String icns = "idea.icns"
366 String helpIcns = "$path/Resources/${helpId}.help/Contents/Resources/Shared/product.icns"
367 if (args.icns != null) {
368 ant.delete(file: "$path/Resources/idea.icns")
369 ant.copy(file: args.icns, todir: "$path/Resources")
370 ant.copy(file: args.icns, tofile: helpIcns)
371 icns = new File((String)args.icns).getName();
373 ant.copy(file: "$path/Resources/idea.icns", tofile: helpIcns)
376 String fullName = args.fullName != null ? args.fullName : p("component.names.fullname")
378 String vmOptions = "-Dfile.encoding=UTF-8 ${vmOptions()} -Xverify:none"
380 String minor = p("component.version.minor")
381 String version = isEap() && !minor.contains("RC") && !minor.contains("Beta") ? "EAP $args.buildNumber" : "${p("component.version.major")}.${minor}"
382 String EAP = isEap() && !minor.contains("RC") && !minor.contains("Beta") ? "-EAP" : ""
384 Map properties = readIdeaProperties(args)
386 def coreKeys = ["idea.platform.prefix", "idea.paths.selector", "idea.executable"]
388 String coreProperties = submapToXml(properties, coreKeys);
390 StringBuilder effectiveProperties = new StringBuilder()
391 properties.each { k, v ->
392 if (!coreKeys.contains(k)) {
393 effectiveProperties.append("$k=$v\n");
397 new File("$path/bin/idea.properties").text = effectiveProperties.toString()
398 String ideaVmOptions = "$mem64 -XX:+UseCompressedOops"
399 if (isEap() && !args.mac_no_yjp) {
400 ideaVmOptions += " ${yjpOptions(args.system_selector)}"
402 new File("$path/bin/${executable}.vmoptions").text = ideaVmOptions.split(" ").join("\n")
404 String classPath = classPathLibs.collect {"\$APP_PACKAGE/Contents/lib/${it}" }.join(":")
407 <key>LSArchitecturePriority</key>
409 (args.archs != null ? args.archs : ["x86_64", "i386"]).each {
410 archs += "<string>${it}</string>"
414 String urlSchemes = ""
415 if (args.urlSchemes != null) {
417 <key>CFBundleURLTypes</key>
420 <key>CFBundleTypeRole</key>
421 <string>Editor</string>
422 <key>CFBundleURLName</key>
423 <string>Stacktrace</string>
424 <key>CFBundleURLSchemes</key>
427 args.urlSchemes.each { scheme ->
428 urlSchemes += " <string>${scheme}</string>"
437 ant.replace(file: "$path/Info.plist") {
438 replacefilter(token: "@@build@@", value: args.buildNumber)
439 replacefilter(token: "@@doc_types@@", value: ifNull(args.doc_types, ""))
440 replacefilter(token: "@@executable@@", value: executable)
441 replacefilter(token: "@@icns@@", value: icns)
442 replacefilter(token: "@@bundle_name@@", value: fullName)
443 replacefilter(token: "@@product_state@@", value: EAP)
444 replacefilter(token: "@@bundle_identifier@@", value: args.bundleIdentifier)
445 replacefilter(token: "@@year@@", value: "$todayYear")
446 replacefilter(token: "@@version@@", value: version)
447 replacefilter(token: "@@vmoptions@@", value: vmOptions)
448 replacefilter(token: "@@idea_properties@@", value: coreProperties)
449 replacefilter(token: "@@class_path@@", value: classPath)
450 replacefilter(token: "@@help_id@@", value: helpId)
451 replacefilter(token: "@@url_schemes@@", value: urlSchemes)
452 replacefilter(token: "@@archs@@", value: archs)
453 replacefilter(token: "@@min_osx@@", value: ifNull(args.min_osx, "10.6"))
456 if (executable != "idea") {
457 ant.move(file: "$path/MacOS/idea", tofile: "$path/MacOS/$executable")
460 ant.replace(file: "$path/bin/inspect.sh") {
461 replacefilter(token: "@@product_full@@", value: fullName)
462 replacefilter(token: "@@script_name@@", value: executable)
464 if (args.inspect_script != null && args.inspect_script != "inspect") {
465 ant.move(file: "$path/bin/inspect.sh", tofile: "$path/bin/${args.inspect_script}.sh")
468 ant.fixcrlf(srcdir: "$path/bin", includes: "*.sh", eol: "unix")
471 binding.setVariable("winScripts", { String target, String home, String name, Map args ->
472 String fullName = args.fullName != null ? args.fullName : p("component.names.fullname")
473 String product_uc = args.product_uc != null ? args.product_uc : p("component.names.product").toUpperCase()
474 String vm_options = args.vm_options != null ? args.vm_options : "${p("component.names.product").toLowerCase()}.exe"
475 if (vm_options.endsWith(".exe")) {
476 vm_options = vm_options.replace(".exe", "%BITS%.exe")
479 vm_options = vm_options + "%BITS%"
482 String classPath = "SET CLASS_PATH=%IDE_HOME%\\lib\\${classPathLibs[0]}\n"
483 classPath += classPathLibs[1..-1].collect {"SET CLASS_PATH=%CLASS_PATH%;%IDE_HOME%\\lib\\${it}"}.join("\n")
484 if (args.tools_jar) classPath += "\nSET CLASS_PATH=%CLASS_PATH%;%JDK%\\lib\\tools.jar"
486 ant.copy(todir: "$target/bin") {
487 fileset(dir: "$home/bin/scripts/win")
489 filterset(begintoken: "@@", endtoken: "@@") {
490 filter(token: "product_full", value: fullName)
491 filter(token: "product_uc", value: product_uc)
492 filter(token: "vm_options", value: vm_options)
493 filter(token: "isEap", value: isEap())
494 filter(token: "system_selector", value: args.system_selector)
495 filter(token: "ide_jvm_args", value: ifNull(args.ide_jvm_args, ""))
496 filter(token: "class_path", value: classPath)
497 filter(token: "script_name", value: name)
501 if (name != "idea.bat") {
502 ant.move(file: "$target/bin/idea.bat", tofile: "$target/bin/$name")
504 if (args.inspect_script != null && args.inspect_script != "inspect") {
505 ant.move(file: "$target/bin/inspect.bat", tofile: "$target/bin/${args.inspect_script}.bat")
508 ant.fixcrlf(srcdir: "$target/bin", includes: "*.bat", eol: "dos")
511 private ifNull(v, defVal) { v != null ? v : defVal }
513 binding.setVariable("unixScripts", { String target, String home, String name, Map args ->
514 String fullName = args.fullName != null ? args.fullName : p("component.names.fullname")
515 String product_uc = args.product_uc != null ? args.product_uc : p("component.names.product").toUpperCase()
516 String vm_options = args.vm_options != null ? args.vm_options : p("component.names.product").toLowerCase()
518 String classPath = "CLASSPATH=\"\$IDE_HOME/lib/${classPathLibs[0]}\"\n"
519 classPath += classPathLibs[1..-1].collect {"CLASSPATH=\"\$CLASSPATH:\$IDE_HOME/lib/${it}\""}.join("\n")
520 if (args.tools_jar) classPath += "\nCLASSPATH=\"\$CLASSPATH:\$JDK/lib/tools.jar\""
522 ant.copy(todir: "$target/bin") {
523 fileset(dir: "$home/bin/scripts/unix")
525 filterset(begintoken: "@@", endtoken: "@@") {
526 filter(token: "product_full", value: fullName)
527 filter(token: "product_uc", value: product_uc)
528 filter(token: "vm_options", value: vm_options)
529 filter(token: "isEap", value: isEap())
530 filter(token: "system_selector", value: args.system_selector)
531 filter(token: "ide_jvm_args", value: ifNull(args.ide_jvm_args, ""))
532 filter(token: "class_path", value: classPath)
533 filter(token: "script_name", value: name)
537 if (name != "idea.sh") {
538 ant.move(file: "$target/bin/idea.sh", tofile: "$target/bin/$name")
540 if (args.inspect_script != null && args.inspect_script != "inspect") {
541 ant.move(file: "$target/bin/inspect.sh", tofile: "$target/bin/${args.inspect_script}.sh")
544 ant.fixcrlf(srcdir: "$target/bin", includes: "*.sh", eol: "unix")
547 binding.setVariable("winVMOptions", { String target, String system_selector, String name, String name64 = null ->
549 def options = isEap() && system_selector != null ? vmOptions32yjp(system_selector) : vmOptions32()
550 ant.echo(file: "$target/bin/${name}.vmoptions", message: options.replace(' ', '\n'))
553 if (name64 != null) {
554 def options = isEap() && system_selector != null ? vmOptions64yjp(system_selector) : vmOptions64()
555 ant.echo(file: "$target/bin/${name64}.vmoptions", message: options.replace(' ', '\n'))
558 ant.fixcrlf(srcdir: "$target/bin", includes: "*.vmoptions", eol: "dos")
561 binding.setVariable("unixVMOptions", { String target, String name, String name64 = (name + "64") ->
563 ant.echo(file: "$target/bin/${name}.vmoptions", message: "${vmOptions32()} -Dawt.useSystemAAFontSettings=lcd".trim().replace(' ', '\n'))
565 if (name64 != null) {
566 ant.echo(file: "$target/bin/${name64}.vmoptions", message: "${vmOptions64()} -Dawt.useSystemAAFontSettings=lcd".trim().replace(' ', '\n'))
568 ant.fixcrlf(srcdir: "$target/bin", includes: "*.vmoptions", eol: "unix")
571 binding.setVariable("unixReadme", { String target, String home, Map args ->
572 String fullName = args.fullName != null ? args.fullName : p("component.names.fullname")
573 String settings_dir = args.system_selector.replaceFirst("\\d+", "")
574 copyAndPatchFile("$home/build/Install-Linux-tar.txt", "$target/Install-Linux-tar.txt",
575 ["product_full": fullName,
576 "product": p("component.names.product").toLowerCase(),
577 "system_selector": args.system_selector,
578 "settings_dir": settings_dir], "@@")
579 ant.fixcrlf(file: "$target/bin/Install-Linux-tar.txt", eol: "unix")
582 binding.setVariable("forceDelete", { String dirPath ->
583 // if wasn't deleted - retry several times
585 while (attempt < 21 && (new File(dirPath).exists())) {
587 ant.echo "Deleting $dirPath ... (attempt=$attempt)"
589 // let's wait a bit and try again - may be help
590 // in some cases on our windows 7 agents
594 ant.delete(failonerror: false, dir: dirPath)
599 if (new File(dirPath).exists()) {
600 ant.project.log ("Cannot delete directory: $dirPath" )
605 binding.setVariable("patchPropertiesFile", { String target, Map args = [:] ->
606 String file = "$target/bin/idea.properties"
608 if (args.appendices != null) {
609 ant.concat(destfile: file, append: true) {
610 args.appendices.each {
616 String product_uc = args.product_uc != null ? args.product_uc : p("component.names.product").toUpperCase()
617 String settings_dir = args.system_selector.replaceFirst("\\d+", "")
618 ant.replace(file: file) {
619 replacefilter(token: "@@product_uc@@", value: product_uc)
620 replacefilter(token: "@@settings_dir@@", value: settings_dir)
623 String message = (isEap() ? """
624 #-----------------------------------------------------------------------
625 # Change to 'disabled' if you don't want to receive instant visual notifications
626 # about fatal errors that happen to an IDE or plugins installed.
627 #-----------------------------------------------------------------------
628 idea.fatal.error.notification=enabled
631 #-----------------------------------------------------------------------
632 # Change to 'enabled' if you want to receive instant visual notifications
633 # about fatal errors that happen to an IDE or plugins installed.
634 #-----------------------------------------------------------------------
635 idea.fatal.error.notification=disabled
637 ant.echo(file: file, append: true, message: message)
640 binding.setVariable("zipSources", { String home, String targetDir ->
641 String sources = "$targetDir/sources.zip"
642 projectBuilder.stage("zip sources to $sources")
644 ant.mkdir(dir: targetDir)
645 ant.delete(file: sources)
646 ant.zip(destfile: sources) {
648 ["java", "groovy", "ipr", "iml", "form", "xml", "properties"].each {
649 include(name: "**/*.$it")
651 exclude(name: "**/testData/**")
655 notifyArtifactBuilt(sources)
658 binding.setVariable("zipSourcesOfModules", { String home, String targetFilePath, Collection<String> modules ->
659 projectBuilder.stage("zip sources of ${modules.size()} modules to $targetFilePath")
661 ant.mkdir(dir: new File(targetFilePath).getParent())
662 ant.delete(file: targetFilePath)
663 ant.zip(destfile: targetFilePath) {
665 JpsModule module = findModule(it)
666 module.getSourceRoots(JavaSourceRootType.SOURCE).flatten().collect {it.file}.each { File srcRoot ->
667 zipfileset(prefix: module.name, dir: srcRoot.absolutePath)
672 notifyArtifactBuilt(targetFilePath)
678 * Load all properties from file:
679 * readIdeaProperties("idea.properties.path" : "$home/ruby/build/idea.properties")
681 * Load all properties except "idea.cycle.buffer.size", change "idea.max.intellisense.filesize" to 3000
682 * and enable "idea.is.internal" mode:
683 * readIdeaProperties("idea.properties.path" : "$home/ruby/build/idea.properties",
684 * "idea.properties" : ["idea.max.intellisense.filesize" : 3000,
685 * "idea.cycle.buffer.size" : null,
686 * "idea.is.internal" : true ])
688 * @return text xml properties description in xml
690 private Map readIdeaProperties(Map args) {
691 String ideaPropertiesPath = args == null ? null : args.get("idea.properties.path")
692 if (ideaPropertiesPath == null) {
696 // read idea.properties file
697 Properties ideaProperties = new Properties();
698 FileInputStream ideaPropertiesFile = new FileInputStream(ideaPropertiesPath);
699 ideaProperties.load(ideaPropertiesFile);
700 ideaPropertiesFile.close();
702 def defaultProperties = ["CVS_PASSFILE": "~/.cvspass",
703 "JVMVersion": "1.6+",
704 "com.apple.mrj.application.live-resize": "false",
705 "idea.paths.selector": args.system_selector,
706 "idea.executable": args.executable,
707 "java.endorsed.dirs": "",
708 "idea.smooth.progress": "false",
709 "apple.laf.useScreenMenuBar": "true",
710 "apple.awt.graphics.UseQuartz": "true",
711 "apple.awt.fullscreencapturealldisplays": "false"]
712 if (args.platform_prefix != null) {
713 defaultProperties.put("idea.platform.prefix", args.platform_prefix)
716 Map properties = defaultProperties
717 def customProperties = args.get("idea.properties")
718 if (customProperties != null) {
719 properties += customProperties
722 properties.each {k, v ->
724 // if overridden with null - ignore property
725 ideaProperties.remove(k)
727 // if property is overridden in args map - use new value
728 ideaProperties.put(k, v)
732 return ideaProperties;
735 private String submapToXml(Map properties, List keys) {
736 // generate properties description for Info.plist
737 StringBuilder buff = new StringBuilder()
740 String value = properties[key]
745 <string>$value</string>
750 return buff.toString()
753 private List<File> getChildren(File file) {
754 if (!file.isDirectory()) return []
755 return file.listFiles().sort { File f -> f.name.toLowerCase() }
758 binding.setVariable("getBundledJDK", { File jdkDir, String prefix, String ext, String bundledJDKFileName ->
759 getChildren(jdkDir).each {
760 if (it.getName().startsWith(prefix) && it.getName().endsWith(ext)) {
761 if (new File(bundledJDKFileName).exists()) { ant.delete(file: bundledJDKFileName) }
762 def JdkFileName = it.getAbsolutePath()
763 if (ext == ".tar.gz") {
764 JdkFileName = JdkFileName.substring(0, JdkFileName.length() - 3)
765 if (new File(JdkFileName).exists()) { ant.delete(file: JdkFileName) }
766 ant.gunzip(src: it.getAbsolutePath())
768 projectBuilder.info("JdkFileName: " + JdkFileName)
769 ant.copy(file: JdkFileName, tofile: bundledJDKFileName)
774 binding.setVariable("buildWinZip", { String zipPath, List paths ->
775 projectBuilder.stage(".win.zip")
777 fixIdeaPropertiesEol(paths, "dos")
779 ant.zip(zipfile: zipPath) {
785 notifyArtifactBuilt(zipPath)
788 binding.setVariable("buildCrossPlatformZip", { String zipPath, String sandbox, List commonPaths, String distWin, String distUnix, String distMac ->
789 projectBuilder.stage("Building cross-platform zip")
791 def zipDir = "$sandbox/cross-platform-zip"
792 def ideaPropertiesFile = commonPaths.collect { new File(it, "bin/idea.properties") }.find { it.exists() }
793 ["win", "linux"].each {
794 ant.mkdir(dir: "$zipDir/bin/$it")
795 ant.copy(file: ideaPropertiesFile.absolutePath, todir: "$zipDir/bin/$it")
797 ant.fixcrlf(file: "$zipDir/bin/win/idea.properties", eol: "dos")
798 ant.copy(todir: "$zipDir/bin/linux") {
799 fileset(dir: "$distUnix/bin") {
800 include(name: "idea*.vmoptions")
803 ant.copy(todir: "$zipDir/bin/mac") {
804 fileset(dir: "$distMac/bin") {
805 include(name: "idea*.vmoptions")
806 include(name: "idea.properties")
810 ant.zip(zipfile: zipPath, duplicate: "fail") {
813 exclude(name: "bin/idea.properties")
818 fileset(dir: distWin) {
819 exclude(name: "bin/fsnotifier*.exe")
820 exclude(name: "bin/idea*.exe.vmoptions")
821 exclude(name: "bin/idea*.exe")
823 zipfileset(dir: "$distWin/bin", prefix: "bin/win") {
824 include(name: "fsnotifier*.exe")
825 include(name: "idea*.exe.vmoptions")
828 fileset(dir: distUnix) {
829 exclude(name: "bin/fsnotifier*")
830 exclude(name: "bin/idea*.vmoptions")
831 exclude(name: "bin/*.sh")
832 exclude(name: "help/**")
834 zipfileset(dir: "$distUnix/bin", filemode: "775", prefix: "bin") {
835 include(name: "*.sh")
837 zipfileset(dir: "$distUnix/bin", prefix: "bin/linux", filemode: "775") {
838 include(name: "fsnotifier*")
841 fileset(dir: distMac) {
842 exclude(name: "bin/fsnotifier*")
843 exclude(name: "bin/restarter*")
844 exclude(name: "bin/*.sh")
845 exclude(name: "bin/*.py")
846 exclude(name: "bin/idea.properties")
847 exclude(name: "bin/idea*.vmoptions")
849 zipfileset(dir: "$distMac/bin", filemode: "775", prefix: "bin") {
850 include(name: "restarter*")
851 include(name: "*.py")
853 zipfileset(dir: "$distMac/bin", prefix: "bin/mac", filemode: "775") {
854 include(name: "fsnotifier*")
858 notifyArtifactBuilt(zipPath)
861 binding.setVariable("buildMacZip", { String zipRoot, String zipPath, List paths, String macPath, List extraBins = [] ->
862 projectBuilder.stage(".mac.zip")
864 allPaths = paths + [macPath]
865 ant.zip(zipfile: zipPath) {
867 zipfileset(dir: it, prefix: zipRoot) {
868 exclude(name: "bin/*.sh")
869 exclude(name: "bin/*.py")
870 exclude(name: "bin/fsnotifier")
871 exclude(name: "bin/restarter")
872 exclude(name: "MacOS/*")
873 exclude(name: "build.txt")
874 exclude(name: "NOTICE.txt")
878 exclude(name: "bin/idea.properties")
883 zipfileset(dir: it, filemode: "755", prefix: zipRoot) {
884 include(name: "bin/*.sh")
885 include(name: "bin/*.py")
886 include(name: "bin/fsnotifier")
887 include(name: "bin/restarter")
888 include(name: "MacOS/*")
896 zipfileset(dir: it, prefix: "$zipRoot/Resources") {
897 include(name: "build.txt")
898 include(name: "NOTICE.txt")
902 zipfileset(file: "$macPath/bin/idea.properties", prefix: "$zipRoot/bin")
906 binding.setVariable("buildTarGz", { String tarRoot, String tarPath, List paths, List extraBins = [] ->
907 projectBuilder.stage(".tar.gz")
909 fixIdeaPropertiesEol(paths, "unix")
911 ant.tar(tarfile: tarPath, longfile: "gnu") {
913 tarfileset(dir: it, prefix: tarRoot) {
914 exclude(name: "bin/*.sh")
915 exclude(name: "bin/fsnotifier*")
924 tarfileset(dir: it, filemode: "755", prefix: tarRoot) {
925 include(name: "bin/*.sh")
926 include(name: "bin/fsnotifier*")
935 String gzPath = "${tarPath}.gz"
936 ant.gzip(src: tarPath, zipfile: gzPath)
937 ant.delete(file: tarPath)
938 notifyArtifactBuilt(gzPath)
941 private void fixIdeaPropertiesEol(List paths, String eol) {
943 String file = "$it/bin/idea.properties"
944 if (new File(file).exists()) {
945 ant.fixcrlf(file: file, eol: eol)
950 binding.setVariable("buildWinLauncher", { String ch, String inputPath, String outputPath, String appInfo,
951 String launcherProperties, String pathsSelector, List resourcePaths ->
952 projectBuilder.stage("winLauncher")
954 if (pathsSelector != null) {
955 def paths = getProperty("paths")
956 def launcherPropertiesTemp = "${paths.sandbox}/launcher.properties"
957 copyAndPatchFile(launcherProperties, launcherPropertiesTemp, ["PRODUCT_PATHS_SELECTOR": pathsSelector,
958 "IDE-NAME": p("component.names.product").toUpperCase()])
959 launcherProperties = launcherPropertiesTemp
962 ant.java(classname: "com.pme.launcher.LauncherGeneratorMain", fork: "true", failonerror: "true") {
963 sysproperty(key: "java.awt.headless", value: "true")
964 arg(value: inputPath)
966 arg(value: "$ch/native/WinLauncher/WinLauncher/resource.h")
967 arg(value: launcherProperties)
968 arg(value: outputPath)
970 pathelement(location: "$ch/build/lib/launcher-generator.jar")
971 fileset(dir: "$ch/lib") {
972 include(name: "guava*.jar")
973 include(name: "jdom.jar")
974 include(name: "sanselan*.jar")
977 pathelement(location: it)
983 binding.setVariable("collectUsedJars", { List modules, List approvedJars, List forbiddenJars, List modulesToBuild ->
984 def usedJars = new HashSet();
987 def module = findModule(it)
988 if (module != null) {
989 projectBuilder.moduleRuntimeClasspath(module, false).each {
990 File file = new File(it)
992 String path = file.canonicalPath.replace('\\', '/')
993 if (path.endsWith(".jar") && approvedJars.any { path.startsWith(it) } && !forbiddenJars.any { path.contains(it) }) {
994 if (usedJars.add(path)) {
995 projectBuilder.info("\tADDED: $path for ${module.getName()}")
1000 if (modulesToBuild != null) {
1001 modulesToBuild << module
1005 projectBuilder.warning("$it is not a module")
1012 binding.setVariable("buildModulesAndCollectUsedJars", { List modules, List approvedJars, List forbiddenJars ->
1013 def modulesToBuild = []
1014 def usedJars = collectUsedJars(modules, approvedJars, forbiddenJars, modulesToBuild)
1015 projectBuilder.cleanOutput()
1016 projectBuilder.buildModules(modulesToBuild)
1021 binding.setVariable("buildSearchableOptions", { String target, List licenses, Closure cp, String jvmArgs = null,
1022 def paths = getProperty("paths") ->
1023 projectBuilder.stage("Building searchable options")
1025 String targetFile = "${target}/searchableOptions.xml"
1026 ant.delete(file: targetFile)
1029 ant.copy(file: it, todir: paths.ideaSystem)
1032 ant.path(id: "searchable.options.classpath") { cp() }
1033 String classpathFile = "${paths.sandbox}/classpath.txt"
1034 ant.echo(file: classpathFile, append: false, message: "\${toString:searchable.options.classpath}")
1035 ant.replace(file: classpathFile, token: File.pathSeparator, value: "\n")
1037 ant.java(classname: "com.intellij.rt.execution.CommandLineWrapper", fork: true, failonerror: true) {
1038 jvmarg(line: "-ea -Xmx500m -XX:MaxPermSize=200m")
1039 jvmarg(value: "-Xbootclasspath/a:${projectBuilder.moduleOutput(findModule("boot"))}")
1040 jvmarg(value: "-Didea.home.path=${home}")
1041 jvmarg(value: "-Didea.system.path=${paths.ideaSystem}")
1042 jvmarg(value: "-Didea.config.path=${paths.ideaConfig}")
1043 if (jvmArgs != null) {
1044 jvmarg(line: jvmArgs)
1047 arg(value: "${classpathFile}")
1048 arg(line: "com.intellij.idea.Main traverseUI")
1049 arg(value: "${target}/searchableOptions.xml")
1052 pathelement(location: "${projectBuilder.moduleOutput(findModule("java-runtime"))}")
1056 ant.available(file: targetFile, property: "searchable.options.exists");
1057 ant.fail(unless: "searchable.options.exists", message: "Searchable options were not built.")
1060 binding.setVariable("reassignAltClickToMultipleCarets", {String communityHome ->
1061 String defaultKeymapContent = new File("$communityHome/platform/platform-resources/src/idea/Keymap_Default.xml").text
1062 defaultKeymapContent = defaultKeymapContent.replace("<mouse-shortcut keystroke=\"alt button1\"/>", "")
1063 defaultKeymapContent = defaultKeymapContent.replace("<mouse-shortcut keystroke=\"alt shift button1\"/>",
1064 "<mouse-shortcut keystroke=\"alt button1\"/>")
1065 patchedKeymapFile = new File("${paths.sandbox}/classes/production/platform-resources/idea/Keymap_Default.xml")
1066 patchedKeymapFile.write(defaultKeymapContent)
1069 // modules used in Upsource and in Kotlin as an API to IDEA
1070 binding.setVariable("analysisApiModules", [
1074 "duplicates-analysis",
1079 "java-analysis-api",
1080 "java-indexing-api",
1082 "java-structure-view",
1084 "jps-model-serialization",
1086 "structure-view-api",
1091 "xml-structure-view-api",
1093 binding.setVariable("analysisImplModules", [
1097 "java-analysis-impl",
1098 "java-indexing-impl",
1100 "projectModel-impl",
1101 "structure-view-impl",
1102 "xml-analysis-impl",
1104 "xml-structure-view-impl",