build scripts cleanup: obsolete unused methods from layouts.gant files are removed
[idea/community.git] / build / scripts / utils.gant
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17
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
27
28 includeTool << JpsGantTool
29
30 binding.setVariable("p", {String key, String defaultValue = null ->
31   try {
32     return getProperty(key) as String
33   }
34   catch (MissingPropertyException e) {
35     if (defaultValue != null) {
36       return defaultValue
37     }
38     throw e;
39   }
40 })
41
42 binding.setVariable("versionSelector", {
43   def fullVersion = p("component.version.major") + "." + p("component.version.minor")
44   def versionParts = fullVersion.split("\\.")
45
46   def minor = versionParts[1]
47   versionParts[0] + (minor == "0" ? "": "." + minor)
48 })
49
50 binding.setVariable("guessJdk", {
51   String javaHome = p("java.home")
52
53   if (new File(javaHome).getName() == "jre") {
54     javaHome = new File(javaHome).getParent()
55   }
56
57   return javaHome
58 })
59
60 binding.setVariable("includeFile", {String filePath ->
61   Script s = groovyShell.parse(new File(filePath))
62   s.setBinding(binding)
63   s
64 })
65
66 binding.setVariable("isMac", {
67   return System.getProperty("os.name").toLowerCase().startsWith("mac")
68 })
69
70 binding.setVariable("isWin", {
71   return System.getProperty("os.name").toLowerCase().startsWith("windows")
72 })
73
74 binding.setVariable("isEap", {
75   return "true" == p("component.version.eap")
76 })
77
78 /**
79  * @deprecated these properies aren't used in new build scripts anymore (they are still used in CLion and AppCode build scripts which aren't rewritten yet;
80  * modify {@link org.jetbrains.intellij.build.impl.VmOptionsGenerator} instead
81  */
82 binding.setVariable("mem32", "-server -Xms128m -Xmx512m -XX:ReservedCodeCacheSize=240m")
83 binding.setVariable("mem64", "-Xms128m -Xmx750m -XX:ReservedCodeCacheSize=240m")
84 binding.setVariable("common_vmoptions", "-XX:+UseConcMarkSweepGC -XX:SoftRefLRUPolicyMSPerMB=50 -ea " +
85                                         "-Dsun.io.useCanonCaches=false -Djava.net.preferIPv4Stack=true " +
86                                         "-XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow")
87
88 binding.setVariable("vmOptions", { "$common_vmoptions ${isEap() ? '-XX:MaxJavaStackTraceDepth=-1' : ''}".trim() })
89 binding.setVariable("vmOptions32", { "$mem32 ${vmOptions()}".trim() })
90 binding.setVariable("vmOptions64", { "$mem64 ${vmOptions()}".trim() })
91
92 binding.setVariable("yjpOptions", { String sessionName, String platformSuffix = "" ->
93   "-agentlib:yjpagent$platformSuffix=probe_disable=*,disablealloc,disabletracing,onlylocal,disableexceptiontelemetry,delay=10000,sessionname=$sessionName".trim()
94 })
95 binding.setVariable("vmOptions32yjp", { String sessionName ->
96   "${vmOptions32()} ${yjpOptions(sessionName)}".trim()
97 })
98 binding.setVariable("vmOptions64yjp", { String sessionName ->
99   "${vmOptions64()} ${yjpOptions(sessionName, "64")}".trim()
100 })
101
102 binding.setVariable("isDefined", {String key ->
103   try {
104     this[key]
105     return true
106   }
107   catch (MissingPropertyException ignored) {
108     return false
109   }
110 })
111
112 private String require(String key) {
113   try {
114     this[key]
115   }
116   catch (MissingPropertyException ignored) {
117     projectBuilder.error("Property '$key' is required")
118   }
119 }
120
121 private String require(String key, String defaultValue) {
122   try {
123     this[key]
124   }
125   catch (MissingPropertyException ignored) {
126     projectBuilder.info("'$key' is not defined. Defaulting to '$defaultValue'")
127     this[key] = defaultValue
128   }
129 }
130
131 binding.setVariable("requireProperty", {String key, String defaultValue = null ->
132   if (defaultValue == null) {
133     require(key)
134   }
135   else {
136     require(key, defaultValue)
137   }
138 })
139
140 binding.setVariable("guessHome", {
141   // current file is supposed to be at build/scripts/*.gant path
142   String uri = requireProperty("gant.file")
143   new File(new URI(uri).getSchemeSpecificPart()).getParentFile().getParentFile().getParent()
144 })
145
146 binding.setVariable("loadProject", {
147   defineJdk("IDEA jdk", setupJdkPath("jdkHome", "$home/build/jdk/1.6", "JDK_16_x64"))
148   defineJdk("1.8", setupJdkPath("jdk8Home", "$home/build/jdk/1.8", "JDK_18_x64"))
149   def bundledKotlinPath = "$home/build/kotlinc"
150   if (!new File(bundledKotlinPath, "lib/kotlin-runtime.jar").exists()) {
151     bundledKotlinPath = "$home/community/build/kotlinc"
152   }
153   if (!new File(bundledKotlinPath, "lib/kotlin-runtime.jar").exists()) {
154     projectBuilder.error("Could not find Kotlin runtime at $bundledKotlinPath/lib/kotlin-runtime.jar: run download_kotlin.gant script to download Kotlin JARs")
155     return
156   }
157   setPathVariable("KOTLIN_BUNDLED", bundledKotlinPath)
158   projectBuilder.buildIncrementally = Boolean.parseBoolean(p("jps.build.incrementally", "false"))
159   def dataDirName = projectBuilder.buildIncrementally ? ".jps-incremental-build" : ".jps-build-data"
160   projectBuilder.dataStorageRoot = new File("$home/$dataDirName")
161   projectBuilder.setupAdditionalLogging(new File("${p("teamcity.build.tempDir", p("java.io.tmpdir"))}/system/build-log/build.log"),
162                                         p("jps.build.debug.logging.categories", ""))
163   loadProjectFromPath(home)
164
165   def compilerOptions = JpsJavaExtensionService.instance.getOrCreateCompilerConfiguration(project).currentCompilerOptions
166   compilerOptions.GENERATE_NO_WARNINGS = true
167   compilerOptions.DEPRECATION = false
168   compilerOptions.ADDITIONAL_OPTIONS_STRING = compilerOptions.ADDITIONAL_OPTIONS_STRING.replace("-Xlint:unchecked", "")
169 })
170
171 binding.setVariable("removeJdkJarFiles", { Collection<String> classpath ->
172   def jdkHomePaths = project.model.global.libraryCollection.getLibraries(JpsJavaSdkType.INSTANCE).collect {
173     def homeDir = new File(it.properties.homePath)
174     return SystemInfo.isMac && homeDir.name == "Home" ? homeDir.parent : homeDir.absolutePath
175   }
176   return classpath.findAll { jarPath -> jdkHomePaths.every { !FileUtil.isAncestor(it, jarPath, false) } }
177 })
178
179 private String setupJdkPath(String propertyName, String defaultDir, String envVarName) {
180   try {
181     this[propertyName]
182   }
183   catch (MissingPropertyException ignored) {
184     def jdk = SystemInfo.isMac ? "$defaultDir/Home" : defaultDir
185     if (new File(jdk).exists()) {
186       projectBuilder.info("$propertyName set to $jdk")
187     }
188     else {
189       jdk = System.getenv(envVarName)
190       if (jdk != null) {
191         projectBuilder.info("'$defaultDir' doesn't exist, $propertyName set to '$envVarName' environment variable: $jdk")
192       }
193       else {
194         jdk = guessJdk()
195         def version = JdkVersionDetector.instance.detectJdkVersion(jdk)
196         if (propertyName.contains("8") && !version.contains("1.8.")) {
197           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")
198           return null
199         }
200         projectBuilder.info("'$envVarName' isn't defined and '$defaultDir' doesn't exist, $propertyName set to $jdk")
201       }
202     }
203     this[propertyName] = jdk
204     return jdk
205   }
206 }
207
208 private void defineJdk(String jdkName, jdkHomePath) {
209   jdk(jdkName, jdkHomePath) {
210     def toolsJar = "$jdkHomePath/lib/tools.jar"
211     if (new File(toolsJar).exists()) {
212       classpath toolsJar
213     }
214   }
215
216   if (SystemInfo.isMac) {
217     //temporary workaround for Mac: resolve symlinks manually. Previously ZipFileCache used FileUtil.toCanonicalPath method which doesn't resolve symlinks.
218     def jdk = global.libraryCollection.findLibrary(jdkName, JpsJavaSdkType.INSTANCE)
219     def jdkClasspath = jdk.getFiles(JpsOrderRootType.COMPILED)
220     def urls = jdk.getRootUrls(JpsOrderRootType.COMPILED)
221     urls.each { jdk.removeUrl(it, JpsOrderRootType.COMPILED) }
222     jdkClasspath.each {
223       try {
224         jdk.addRoot(it.getCanonicalFile(), JpsOrderRootType.COMPILED)
225       }
226       catch (IOException ignored) {
227       }
228     }
229     projectBuilder.info("JDK '$jdkName' classpath: ${jdk.getFiles(JpsOrderRootType.COMPILED)}")
230   }
231 }
232
233 private void setPathVariable(String name, String value) {
234   def pathVars = JpsModelSerializationDataService.getOrCreatePathVariablesConfiguration(global)
235   pathVars.addPathVariable(name, value)
236 }
237
238 binding.setVariable("prepareOutputFolder", {
239   def targetFolder = projectBuilder.buildIncrementally ? "$home/out/incremental-build" : out
240   projectBuilder.targetFolder = targetFolder
241   if (projectBuilder.buildIncrementally && Boolean.parseBoolean(p("jps.build.clear.incremental.caches", "false"))) {
242     FileUtil.delete(new File(targetFolder))
243     FileUtil.delete(projectBuilder.dataStorageRoot)
244   }
245 })
246
247 binding.setVariable("clearBuildCaches", {
248   //todo[nik] this is temporary solution until we update bootstrap jps-builders jars to the new version where cleaning is performed in JpsGantProjectBuilder#cleanOutput
249   def storageRoot = projectBuilder.dataStorageRoot
250   if (storageRoot != null) {
251     FileUtil.delete(storageRoot)
252   }
253 })
254
255 boolean hasSourceRoots(JpsModule module) {
256   return module.getSourceRoots(JavaSourceRootType.SOURCE).iterator().hasNext()
257 }
258
259 binding.setVariable("findModule", {String name ->
260   project.modules.find { it.name == name }
261 })
262
263 binding.setVariable("allModules", {
264   return project.modules
265 })
266
267 requireProperty("home", guessHome())
268
269 String readSnapshotBuild() {
270   def file = new File("$home/community/build.txt")
271   if (!file.exists()) {
272     file = new File("$home/build.txt")
273   }
274
275   return file.readLines().get(0)
276 }
277
278 binding.setVariable("snapshot", readSnapshotBuild())
279
280 projectBuilder.buildInfoPrinter = new TeamCityBuildInfoPrinter()
281 projectBuilder.compressJars = false
282
283 binding.setVariable("notifyArtifactBuilt", { String artifactPath ->
284   if (!FileUtil.startsWith(FileUtil.toSystemIndependentName(artifactPath), FileUtil.toSystemIndependentName(home))) {
285     projectBuilder.error("Artifact path $artifactPath should start with $home")
286   }
287   def relativePath = artifactPath.substring(home.length())
288   if (relativePath.startsWith("/")) {
289     relativePath = relativePath.substring(1)
290   }
291   def file = new File(artifactPath)
292   if (file.isDirectory()) {
293     relativePath += "=>" + file.name
294   }
295   projectBuilder.info("##teamcity[publishArtifacts '$relativePath']")
296 })
297
298 def debugPort = System.getProperty("debug.port")
299 def debugSuspend = System.getProperty("debug.suspend") ?: "n"
300 if (debugSuspend == 'y') {
301   println """
302
303 ------------->------------- The process suspended until remote debugger connects to debug port -------------<-------------
304 ---------------------------------------^------^------^------^------^------^------^----------------------------------------
305
306 """
307 }
308
309 binding.setVariable("patchFiles", { List files, Map args, String marker = "__" ->
310   files.each { file ->
311     args.each { arg ->
312       ant.replace(file: file, token: "${marker}${arg.key}${marker}", value:  arg.value)
313     }
314   }
315 })
316
317 binding.setVariable("copyAndPatchFile", { String file, String target, Map args, String marker = "__" ->
318   ant.copy(file: file, tofile: target, overwrite: "true") {
319     filterset(begintoken: marker, endtoken: marker) {
320       args.each {
321         filter(token: it.key, value: it.value)
322       }
323     }
324   }
325 })
326
327 binding.setVariable("copyAndPatchFiles", { Closure files, String target, Map args, String marker = "__" ->
328   ant.copy(todir: target, overwrite: "true") {
329     files()
330
331     filterset(begintoken: marker, endtoken: marker) {
332       args.each {
333         filter(token: it.key, value: it.value)
334       }
335     }
336   }
337 })
338
339 binding.setVariable("wireBuildDate", { String buildNumber, String appInfoFile ->
340   ant.tstamp()
341   patchFiles([appInfoFile], ["BUILD_NUMBER": buildNumber, "BUILD_DATE": DSTAMP])
342 })
343
344 binding.setVariable("commonJvmArgsForTests", {
345   def jdwp = "-Xrunjdwp:transport=dt_socket,server=y,suspend=$debugSuspend"
346   if (debugPort != null) jdwp += ",address=$debugPort"
347
348   return [
349     "-ea",
350     "-Dfile.encoding=UTF-8",
351     "-Dio.netty.leakDetectionLevel=PARANOID",
352 //    yjpOptions("junit", "64"), // uncomment to start tests with a profiling agent (e.g. in a personal build)
353     "-server",
354     "-Xbootclasspath/p:${projectBuilder.moduleOutput(findModule("boot"))}",
355     "-XX:+HeapDumpOnOutOfMemoryError",
356     "-Didea.home.path=$home",
357     "-Didea.config.path=${p("teamcity.build.tempDir")}/config",
358     "-Didea.system.path=${p("teamcity.build.tempDir")}/system",
359     "-Xdebug",
360     "-XX:ReservedCodeCacheSize=300m",
361     "-XX:SoftRefLRUPolicyMSPerMB=50",
362     "-XX:+UseConcMarkSweepGC",
363     "-XX:-OmitStackTraceInFastThrow",
364     jdwp
365   ]
366 })
367
368 binding.setVariable("classPathLibs", [
369         "bootstrap.jar",
370         "extensions.jar",
371         "util.jar",
372         "jdom.jar",
373         "log4j.jar",
374         "trove4j.jar",
375         "jna.jar"
376 ])
377
378 /**
379  * @deprecated these properies aren't used in new build scripts anymore (they are still used in CLion and AppCode build scripts which aren't rewritten yet;
380  * modify {@link org.jetbrains.intellij.build.CommunityRepositoryModules} instead
381  */
382 binding.setVariable("platformApiModules", [
383   "analysis-api",
384   "built-in-server-api",
385   "core-api",
386   "diff-api",
387   "dvcs-api",
388   "editor-ui-api",
389   "external-system-api",
390   "indexing-api",
391   "jps-model-api",
392   "lang-api",
393   "lvcs-api",
394   "platform-api",
395   "projectModel-api",
396   "remote-servers-agent-rt",
397   "remote-servers-api",
398   "usageView",
399   "vcs-api-core",
400   "vcs-api",
401   "vcs-log-api",
402   "vcs-log-graph-api",
403   "xdebugger-api",
404   "xml-analysis-api",
405   "xml-openapi",
406   "xml-psi-api",
407   "xml-structure-view-api"
408 ])
409
410 binding.setVariable("platformImplementationModules", [
411   "analysis-impl",
412   "built-in-server",
413   "core-impl",
414   "credential-store",
415   "diff-impl",
416   "dvcs-impl",
417   "editor-ui-ex",
418   "images",
419   "indexing-impl",
420   "jps-model-impl",
421   "jps-model-serialization",
422   "json",
423   "lang-impl",
424   "lvcs-impl",
425   "platform-impl",
426   "projectModel-impl",
427   "protocol-reader-runtime",
428   "RegExpSupport",
429   "relaxng",
430   "remote-servers-impl",
431   "script-debugger-backend",
432   "script-debugger-ui",
433   "smRunner",
434   "spellchecker",
435   "structure-view-impl",
436   "testRunner",
437   "vcs-impl",
438   "vcs-log-graph",
439   "vcs-log-impl",
440   "xdebugger-impl",
441   "xml-analysis-impl",
442   "xml-psi-impl",
443   "xml-structure-view-impl",
444   "xml",
445   "configuration-store-impl",
446 ])
447
448 binding.setVariable("layoutMacApp", { String path, String ch, Map args ->
449   ant.copy(todir: "$path/bin") {
450     fileset(dir: "$ch/bin/mac")
451   }
452
453   ant.copy(todir: path) {
454     fileset(dir: "$ch/build/conf/mac/Contents")
455   }
456
457   ant.tstamp() {
458     format(property: "todayYear", pattern: "yyyy")
459   }
460
461   String executable = args.executable != null ? args.executable : p("component.names.product").toLowerCase()
462   String helpId = args.help_id != null ? args.help_id : "IJ"
463   String icns = "idea.icns"
464   String helpIcns = "$path/Resources/${helpId}.help/Contents/Resources/Shared/product.icns"
465   if (args.icns != null) {
466     ant.delete(file: "$path/Resources/idea.icns")
467     ant.copy(file: args.icns, todir: "$path/Resources")
468     ant.copy(file: args.icns, tofile: helpIcns)
469     icns = new File((String)args.icns).getName();
470   } else {
471     ant.copy(file: "$path/Resources/idea.icns", tofile: helpIcns)
472   }
473
474   String fullName = args.fullName != null ? args.fullName : p("component.names.fullname")
475
476   String vmOptions = "-Dfile.encoding=UTF-8 ${vmOptions()} -Xverify:none"
477
478   String minor = p("component.version.minor")
479   String version = isEap() && !minor.contains("RC") && !minor.contains("Beta") ? "EAP $args.buildNumber" : "${p("component.version.major")}.${minor}"
480   String EAP = isEap() && !minor.contains("RC") && !minor.contains("Beta") ? "-EAP" : ""
481
482   Map properties = readIdeaProperties(args)
483
484   def coreKeys = ["idea.platform.prefix", "idea.paths.selector", "idea.executable"]
485
486   String coreProperties = submapToXml(properties, coreKeys);
487
488   StringBuilder effectiveProperties = new StringBuilder()
489   properties.each { k, v ->
490     if (!coreKeys.contains(k)) {
491       effectiveProperties.append("$k=$v\n");
492     }
493   }
494
495   new File("$path/bin/idea.properties").text = effectiveProperties.toString()
496   String ideaVmOptions = "$mem64 -XX:+UseCompressedOops"
497   if (isEap() && !args.mac_no_yjp) {
498     ideaVmOptions += " ${yjpOptions(args.system_selector)}"
499   }
500   new File("$path/bin/${executable}.vmoptions").text = ideaVmOptions.split(" ").join("\n")
501
502   String classPath = classPathLibs.collect {"\$APP_PACKAGE/Contents/lib/${it}" }.join(":")
503
504   String archs = """
505     <key>LSArchitecturePriority</key>
506     <array>"""
507   (args.archs != null ? args.archs : ["x86_64"]).each {
508     archs += "<string>${it}</string>"
509   }
510   archs +="</array>\n"
511
512   String urlSchemes = ""
513   if (args.urlSchemes != null) {
514     urlSchemes += """
515       <key>CFBundleURLTypes</key>
516       <array>
517         <dict>
518           <key>CFBundleTypeRole</key>
519           <string>Editor</string>
520           <key>CFBundleURLName</key>
521           <string>Stacktrace</string>
522           <key>CFBundleURLSchemes</key>
523           <array>
524 """
525     args.urlSchemes.each { scheme ->
526       urlSchemes += "            <string>${scheme}</string>"
527     }
528     urlSchemes += """
529           </array>
530         </dict>
531       </array>
532 """
533   }
534
535   ant.replace(file: "$path/Info.plist") {
536     replacefilter(token: "@@build@@", value: args.buildNumber)
537     replacefilter(token: "@@doc_types@@", value: ifNull(args.doc_types, ""))
538     replacefilter(token: "@@executable@@", value: executable)
539     replacefilter(token: "@@icns@@", value: icns)
540     replacefilter(token: "@@bundle_name@@", value: fullName)
541     replacefilter(token: "@@product_state@@", value: EAP)
542     replacefilter(token: "@@bundle_identifier@@", value: args.bundleIdentifier)
543     replacefilter(token: "@@year@@", value: "$todayYear")
544     replacefilter(token: "@@company_name@@", value: p("component.company.name"))
545     replacefilter(token: "@@min_year@@", value: "2000")
546     replacefilter(token: "@@max_year@@", value: "$todayYear")
547     replacefilter(token: "@@version@@", value: version)
548     replacefilter(token: "@@vmoptions@@", value: vmOptions)
549     replacefilter(token: "@@idea_properties@@", value: coreProperties)
550     replacefilter(token: "@@idea.paths.selector@@", value: args.system_selector)
551     replacefilter(token: "@@class_path@@", value: classPath)
552     replacefilter(token: "@@help_id@@", value: helpId)
553     replacefilter(token: "@@url_schemes@@", value: urlSchemes)
554     replacefilter(token: "@@archs@@", value: archs)
555     replacefilter(token: "@@min_osx@@", value: ifNull(args.min_osx, "10.8"))
556   }
557
558   if (executable != "idea") {
559     ant.move(file: "$path/MacOS/idea", tofile: "$path/MacOS/$executable")
560   }
561
562   ant.replace(file: "$path/bin/format.sh") {
563     replacefilter(token: "@@product_full@@", value: fullName)
564     replacefilter(token: "@@script_name@@", value: executable)
565   }
566   ant.replace(file: "$path/bin/inspect.sh") {
567     replacefilter(token: "@@product_full@@", value: fullName)
568     replacefilter(token: "@@script_name@@", value: executable)
569   }
570   if (args.inspect_script != null && args.inspect_script != "inspect") {
571     ant.move(file: "$path/bin/inspect.sh", tofile: "$path/bin/${args.inspect_script}.sh")
572   }
573
574   ant.fixcrlf(srcdir: "$path/bin", includes: "*.sh", eol: "unix")
575   ant.fixcrlf(srcdir: "$path/bin", includes: "*.py", eol: "unix")
576 })
577
578 binding.setVariable("winScripts", { String target, String home, String name, Map args ->
579   String fullName = args.fullName != null ? args.fullName : p("component.names.fullname")
580   String product_uc = args.product_uc != null ? args.product_uc : p("component.names.product").toUpperCase()
581   String vm_options = args.vm_options != null ? args.vm_options : "${p("component.names.product").toLowerCase()}.exe"
582   if (vm_options.endsWith(".exe")) {
583     vm_options = vm_options.replace(".exe", "%BITS%.exe")
584   }
585   else {
586     vm_options = vm_options + "%BITS%"
587   }
588
589   String classPath = "SET CLASS_PATH=%IDE_HOME%\\lib\\${classPathLibs[0]}\n"
590   classPath += classPathLibs[1..-1].collect {"SET CLASS_PATH=%CLASS_PATH%;%IDE_HOME%\\lib\\${it}"}.join("\n")
591   if (args.tools_jar) classPath += "\nSET CLASS_PATH=%CLASS_PATH%;%JDK%\\lib\\tools.jar"
592
593   ant.copy(todir: "$target/bin") {
594     fileset(dir: "$home/bin/scripts/win")
595
596     filterset(begintoken: "@@", endtoken: "@@") {
597       filter(token: "product_full", value: fullName)
598       filter(token: "product_uc", value: product_uc)
599       filter(token: "vm_options", value: vm_options)
600       filter(token: "isEap", value: isEap())
601       filter(token: "system_selector", value: args.system_selector)
602       filter(token: "ide_jvm_args", value: ifNull(args.ide_jvm_args, ""))
603       filter(token: "class_path", value: classPath)
604       filter(token: "script_name", value: name)
605     }
606   }
607
608   if (name != "idea.bat") {
609     ant.move(file: "$target/bin/idea.bat", tofile: "$target/bin/$name")
610   }
611   if (args.inspect_script != null && args.inspect_script != "inspect") {
612     ant.move(file: "$target/bin/inspect.bat", tofile: "$target/bin/${args.inspect_script}.bat")
613   }
614
615   ant.fixcrlf(srcdir: "$target/bin", includes: "*.bat", eol: "dos")
616 })
617
618 private ifNull(v, defVal) { v != null ? v : defVal }
619
620 binding.setVariable("unixScripts", { String target, String home, String name, Map args ->
621   String fullName = args.fullName != null ? args.fullName : p("component.names.fullname")
622   String product_uc = args.product_uc != null ? args.product_uc : p("component.names.product").toUpperCase()
623   String vm_options = args.vm_options != null ? args.vm_options : p("component.names.product").toLowerCase()
624
625   String classPath = "CLASSPATH=\"\$IDE_HOME/lib/${classPathLibs[0]}\"\n"
626   classPath += classPathLibs[1..-1].collect {"CLASSPATH=\"\$CLASSPATH:\$IDE_HOME/lib/${it}\""}.join("\n")
627   if (args.tools_jar) classPath += "\nCLASSPATH=\"\$CLASSPATH:\$JDK/lib/tools.jar\""
628
629   ant.copy(todir: "$target/bin") {
630     fileset(dir: "$home/bin/scripts/unix")
631
632     filterset(begintoken: "@@", endtoken: "@@") {
633       filter(token: "product_full", value: fullName)
634       filter(token: "product_uc", value: product_uc)
635       filter(token: "vm_options", value: vm_options)
636       filter(token: "isEap", value: isEap())
637       filter(token: "system_selector", value: args.system_selector)
638       filter(token: "ide_jvm_args", value: ifNull(args.ide_jvm_args, ""))
639       filter(token: "class_path", value: classPath)
640       filter(token: "script_name", value: name)
641     }
642   }
643
644   if (name != "idea.sh") {
645     ant.move(file: "$target/bin/idea.sh", tofile: "$target/bin/$name")
646   }
647   if (args.inspect_script != null && args.inspect_script != "inspect") {
648     ant.move(file: "$target/bin/inspect.sh", tofile: "$target/bin/${args.inspect_script}.sh")
649   }
650
651   ant.fixcrlf(srcdir: "$target/bin", includes: "*.sh", eol: "unix")
652 })
653
654 binding.setVariable("winVMOptions", { String target, String yjpSessionName, String name, String name64 = null ->
655   if (name != null) {
656     def options = isEap() && yjpSessionName != null ? vmOptions32yjp(yjpSessionName) : vmOptions32()
657     ant.echo(file: "$target/bin/${name}.vmoptions", message: options.replace(' ', '\n'))
658   }
659
660   if (name64 != null) {
661     def options = isEap() && yjpSessionName != null ? vmOptions64yjp(yjpSessionName) : vmOptions64()
662     ant.echo(file: "$target/bin/${name64}.vmoptions", message: options.replace(' ', '\n'))
663   }
664
665   ant.fixcrlf(srcdir: "$target/bin", includes: "*.vmoptions", eol: "dos")
666 })
667
668 binding.setVariable("unixVMOptions", { String target, String name, String name64 = (name + "64") ->
669   if (name != null) {
670     ant.echo(file: "$target/bin/${name}.vmoptions", message: "${vmOptions32()} -Dawt.useSystemAAFontSettings=lcd".trim().replace(' ', '\n'))
671   }
672   if (name64 != null) {
673     ant.echo(file: "$target/bin/${name64}.vmoptions", message: "${vmOptions64()} -Dawt.useSystemAAFontSettings=lcd".trim().replace(' ', '\n'))
674   }
675   ant.fixcrlf(srcdir: "$target/bin", includes: "*.vmoptions", eol: "unix")
676 })
677
678 binding.setVariable("unixReadme", { String target, String home, Map args ->
679   String fullName = args.fullName != null ? args.fullName : p("component.names.fullname")
680   String settings_dir = args.system_selector.replaceFirst("\\d+", "")
681   copyAndPatchFile("$home/build/Install-Linux-tar.txt", "$target/Install-Linux-tar.txt",
682                    ["product_full": fullName,
683                     "product": p("component.names.product").toLowerCase(),
684                     "system_selector": args.system_selector,
685                     "settings_dir": settings_dir], "@@")
686   ant.fixcrlf(file: "$target/bin/Install-Linux-tar.txt", eol: "unix")
687 })
688
689 binding.setVariable("forceDelete", { String dirPath ->
690   // if wasn't deleted - retry several times
691   attempt = 1
692   while (attempt < 21 && (new File(dirPath).exists())) {
693     if (attempt > 1) {
694       ant.echo "Deleting $dirPath ... (attempt=$attempt)"
695
696       // let's wait a bit and try again - may be help
697       // in some cases on our windows 7 agents
698       sleep(2000)
699     }
700
701     ant.delete(failonerror: false, dir: dirPath)
702
703     attempt++
704   }
705
706   if (new File(dirPath).exists()) {
707     ant.project.log ("Cannot delete directory: $dirPath" )
708     System.exit (1)
709   }
710 })
711
712 binding.setVariable("patchPropertiesFile", { String target, Map args = [:] ->
713   String file = "$target/bin/idea.properties"
714
715   if (args.appendices != null) {
716     ant.concat(destfile: file, append:  true) {
717       args.appendices.each {
718         fileset(file: it)
719       }
720     }
721   }
722
723   String product_uc = args.product_uc != null ? args.product_uc : p("component.names.product").toUpperCase()
724   String settings_dir = args.system_selector.replaceFirst("\\d+(\\.\\d+)?", "")
725   ant.replace(file: file) {
726     replacefilter(token: "@@product_uc@@", value: product_uc)
727     replacefilter(token: "@@settings_dir@@", value: settings_dir)
728   }
729
730   String message = (isEap() ? """
731 #-----------------------------------------------------------------------
732 # Change to 'disabled' if you don't want to receive instant visual notifications
733 # about fatal errors that happen to an IDE or plugins installed.
734 #-----------------------------------------------------------------------
735 idea.fatal.error.notification=enabled
736 """
737                  : """
738 #-----------------------------------------------------------------------
739 # Change to 'enabled' if you want to receive instant visual notifications
740 # about fatal errors that happen to an IDE or plugins installed.
741 #-----------------------------------------------------------------------
742 idea.fatal.error.notification=disabled
743 """)
744   ant.echo(file: file, append: true, message: message)
745 })
746
747 binding.setVariable("zipSources", { String home, String targetDir ->
748   String sources = "$targetDir/sources.zip"
749   projectBuilder.stage("zip sources to $sources")
750
751   ant.mkdir(dir: targetDir)
752   ant.delete(file: sources)
753   ant.zip(destfile: sources) {
754     fileset(dir: home) {
755       ["java", "groovy", "ipr", "iml", "form", "xml", "properties", "kt"].each {
756         include(name: "**/*.$it")
757       }
758       exclude(name: "**/testData/**")
759       exclude(name: "out/**")
760     }
761   }
762
763   notifyArtifactBuilt(sources)
764 })
765
766 binding.setVariable("zipSourcesOfModules", { String targetFilePath, Collection<String> modules ->
767   projectBuilder.stage("zip sources of ${modules.size()} modules to $targetFilePath")
768
769   ant.mkdir(dir: new File(targetFilePath).getParent())
770   ant.delete(file: targetFilePath)
771   ant.zip(destfile: targetFilePath) {
772     modules.each {
773       JpsModule module = findModule(it)
774       module.getSourceRoots(JavaSourceRootType.SOURCE).each { root ->
775         ant.zipfileset(dir: root.file.absolutePath, prefix: root.properties.packagePrefix.replace('.', '/'), erroronmissingdir: false)
776       }
777       module.getSourceRoots(JavaResourceRootType.RESOURCE).each { root ->
778         ant.zipfileset(dir: root.file.absolutePath, prefix: root.properties.relativeOutputPath, erroronmissingdir: false)
779       }
780     }
781   }
782
783   notifyArtifactBuilt(targetFilePath)
784 })
785
786 binding.setVariable("moduleSrc", {String moduleName ->
787   result = []
788   JpsModule module = findModule(moduleName)
789   if (module == null) projectBuilder.error("Module $moduleName not found")
790   module.getSourceRoots(JavaSourceRootType.SOURCE).each { root ->
791     result << ant.zipfileset(dir: root.file.absolutePath , prefix: root.properties.packagePrefix.replace('.', '/'), erroronmissingdir: false)
792   }
793   result
794 })
795
796 /**
797  * E.g.
798  *
799  * Load all properties from file:
800  *    readIdeaProperties("idea.properties.path" : "$home/ruby/build/idea.properties")
801  *
802  * Load all properties except "idea.cycle.buffer.size", change "idea.max.intellisense.filesize" to 3000
803  * and enable "idea.is.internal" mode:
804  *    readIdeaProperties("idea.properties.path" : "$home/ruby/build/idea.properties",
805  *                       "idea.properties" : ["idea.max.intellisense.filesize" : 3000,
806  *                                           "idea.cycle.buffer.size" : null,
807  *                                           "idea.is.internal" : true ])
808  * @param args
809  * @return text xml properties description in xml
810  */
811 private Map readIdeaProperties(Map args) {
812   String ideaPropertiesPath =  args == null ? null : args.get("idea.properties.path")
813   if (ideaPropertiesPath == null) {
814     return [:]
815   }
816
817   // read idea.properties file
818   Properties ideaProperties = new Properties();
819   FileInputStream ideaPropertiesFile = new FileInputStream(ideaPropertiesPath);
820   ideaProperties.load(ideaPropertiesFile);
821   ideaPropertiesFile.close();
822
823   def defaultProperties = ["CVS_PASSFILE": "~/.cvspass",
824                            "com.apple.mrj.application.live-resize": "false",
825                            "idea.paths.selector": args.system_selector,
826                            "idea.executable": args.executable,
827                            "java.endorsed.dirs": "",
828                            "idea.smooth.progress": "false",
829                            "apple.laf.useScreenMenuBar": "true",
830                            "apple.awt.fileDialogForDirectories": "true",
831                            "apple.awt.graphics.UseQuartz": "true",
832                            "apple.awt.fullscreencapturealldisplays": "false"]
833   if (args.platform_prefix != null) {
834     defaultProperties.put("idea.platform.prefix", args.platform_prefix)
835   }
836
837   Map properties = defaultProperties
838   def customProperties = args.get("idea.properties")
839   if (customProperties != null) {
840     properties += customProperties
841   }
842
843   properties.each {k, v ->
844     if (v == null) {
845       // if overridden with null - ignore property
846       ideaProperties.remove(k)
847     } else {
848       // if property is overridden in args map - use new value
849       ideaProperties.put(k, v)
850     }
851   }
852
853   return ideaProperties;
854 }
855
856 private String submapToXml(Map properties, List keys) {
857 // generate properties description for Info.plist
858   StringBuilder buff = new StringBuilder()
859
860   keys.each { key ->
861     String value = properties[key]
862     if (value != null) {
863       String string =
864         """
865         <key>$key</key>
866         <string>$value</string>
867 """
868       buff.append(string)
869     }
870   }
871   return buff.toString()
872 }
873
874 private List<File> getChildren(File file) {
875   if (!file.isDirectory()) return []
876   return file.listFiles().sort { File f -> f.name.toLowerCase() }
877 }
878
879 binding.setVariable("signExecutableFiles", { String binDir ->
880   projectBuilder.stage("Signing binaries")
881   def fileBinDir = new File(binDir)
882   def fileName = ""
883   getChildren(fileBinDir).each {
884     fileName = it.getName()
885     if (fileName.endsWith(".exe")) {
886       executeExternalAnt(["dirName": binDir, "fileName": fileName], "$home/build/signBuild.xml")
887     }
888   }
889   projectBuilder.stage("Signing done")
890 })
891
892 binding.setVariable("bundledJDKs"){
893   bundledJDK(false)
894 }
895
896 binding.setVariable("bundledJDK64s"){
897   bundledJDK(true)
898 }
899
900 private bundledJDK(boolean win64) {
901   requireProperty("artifact.linux.no.jdk", "true")
902   requireProperty("artifact.mac.no.jdk", "true")
903   requireProperty("jdk.oracle.win", "jdk8u")
904   requireProperty("jdk.win",   "jbre8")
905   requireProperty("jdk.mac",   "jbre8")
906   requireProperty("jdk.linux", "jbre8")
907
908   if (new File("${home}/build/jdk").exists()) {
909     def jdkDir = new File("${home}/build/jdk/win")
910     if (p("jdk.oracle.win") != "false" && (jdkDir.exists() && jdkDir.isDirectory())) {
911       setProperty("winJDK", getPathToBundledJDK(jdkDir, p("jdk.oracle.win"), (win64 ? "x64.tar.gz": "x86.tar.gz")))
912       extractRedistJre(winJDK, "${paths.sandbox}/jdk.oracle.win/jre")
913     }
914     if (p("jdk.win") != "false" && (jdkDir.exists() && jdkDir.isDirectory())) {
915       setProperty("winCustomJDKx32", getPathToBundledJDK(jdkDir, p("jdk.win"), (win64 ? "x64.tar.gz": "x86.tar.gz")))
916       extractRedistJre(winCustomJDKx32, "${paths.sandbox}/jdk.win/jre")
917     }
918     jdkDir = new File("${home}/build/jdk/mac")
919     if (p("jdk.mac") != "false" && (jdkDir.exists() && jdkDir.isDirectory())) {
920       setProperty("macCustomJDK", getPathToBundledJDK(jdkDir, p("jdk.mac"), ".tar.gz"))
921     }
922     jdkDir = new File("${home}/build/jdk/linux")
923     if (p("jdk.linux") != "false" && (jdkDir.exists() && jdkDir.isDirectory())) {
924       setProperty("linuxJDK", getPathToBundledJDK(jdkDir, p("jdk.linux"), ".tar.gz"))
925       extractRedistJre(linuxJDK, "${paths.sandbox}/jdk.linux/jre")
926     }
927   }
928 }
929
930 binding.setVariable("getPathToBundledJDK", { File jdkDir, String prefix, String ext ->
931   def JdkFileName = ""
932   getChildren(jdkDir).each {
933     if (it.getName().startsWith(prefix) && it.getName().endsWith(ext)) {
934       JdkFileName = it.getAbsolutePath()
935       if (ext.endsWith(".tar.gz")) {
936         def OriginalJdkFileName = JdkFileName.substring(0, JdkFileName.length() - 3)
937         JdkFileName = JdkFileName.substring(0, JdkFileName.length() - 7) + "_${buildNumber}.tar"
938         if (new File(JdkFileName).exists()) { ant.delete(file: JdkFileName) }
939         ant.gunzip(src: it.getAbsolutePath())
940         ant.copy(file: OriginalJdkFileName, tofile: JdkFileName)
941       }
942     }
943   }
944   return JdkFileName.replace('\\', '/')
945 })
946
947 binding.setVariable("buildWinZip", { String zipPath, List paths, String zipPrefix = "" ->
948   projectBuilder.stage(".win.zip")
949
950   fixIdeaPropertiesEol(paths, "dos")
951
952   ant.zip(zipfile: zipPath) {
953     paths.each {
954       zipfileset(dir: it, prefix: zipPrefix)
955     }
956   }
957
958   notifyArtifactBuilt(zipPath)
959 })
960
961 binding.setVariable("buildCrossPlatformZip", { String zipPath, String sandbox, List commonPaths, String distWin, String distUnix, String distMac ->
962   projectBuilder.stage("Building cross-platform zip")
963
964   def executableName = StringUtil.trimEnd(new File(distMac, "bin").list().find { it.endsWith(".vmoptions") }, ".vmoptions")
965   def zipDir = "$sandbox/cross-platform-zip"
966   def ideaPropertiesFile = commonPaths.collect { new File(it, "bin/idea.properties") }.find { it.exists() }
967   ["win", "linux"].each {
968     ant.mkdir(dir: "$zipDir/bin/$it")
969     ant.copy(file: ideaPropertiesFile.absolutePath, todir: "$zipDir/bin/$it")
970   }
971   ant.fixcrlf(file: "$zipDir/bin/win/idea.properties", eol: "dos")
972   ant.copy(todir: "$zipDir/bin/linux") {
973     fileset(dir: "$distUnix/bin") {
974       include(name: "*.vmoptions")
975     }
976   }
977   ant.copy(todir: "$zipDir/bin/mac") {
978     fileset(dir: "$distMac/bin") {
979       include(name: "${executableName}.vmoptions")
980       include(name: "idea.properties")
981     }
982   }
983   ant.copy(file: "$distMac/bin/${executableName}.vmoptions", tofile: "$zipDir/bin/mac/${executableName}64.vmoptions")
984   ant.copy(todir: "$zipDir/bin") {
985     fileset(dir: "$distMac/bin") {
986       include(name: "*.jnilib")
987     }
988     mapper(type: "glob", from: "*.jnilib", to: "*.dylib")
989   }
990
991   ant.zip(zipfile: zipPath, duplicate: "fail") {
992     commonPaths.each {
993       fileset(dir: it) {
994         exclude(name: "bin/idea.properties")
995       }
996     }
997     fileset(dir: zipDir)
998
999     fileset(dir: distWin) {
1000       exclude(name: "bin/fsnotifier*.exe")
1001       exclude(name: "bin/*.exe.vmoptions")
1002       exclude(name: "bin/${executableName}*.exe")
1003     }
1004     zipfileset(dir: "$distWin/bin", prefix: "bin/win") {
1005       include(name: "fsnotifier*.exe")
1006       include(name: "*.exe.vmoptions")
1007     }
1008
1009     fileset(dir: distUnix) {
1010       exclude(name: "bin/fsnotifier*")
1011       exclude(name: "bin/*.vmoptions")
1012       exclude(name: "bin/*.sh")
1013       exclude(name: "bin/*.py")
1014       exclude(name: "help/**")
1015     }
1016     zipfileset(dir: "$distUnix/bin", prefix: "bin", filemode: "775") {
1017       include(name: "*.sh")
1018       include(name: "*.py")
1019     }
1020     zipfileset(dir: "$distUnix/bin", prefix: "bin/linux", filemode: "775") {
1021       include(name: "fsnotifier*")
1022     }
1023
1024     fileset(dir: distMac) {
1025       exclude(name: "bin/fsnotifier*")
1026       exclude(name: "bin/restarter*")
1027       exclude(name: "bin/*.sh")
1028       exclude(name: "bin/*.py")
1029       exclude(name: "bin/*.jnilib")
1030       exclude(name: "bin/idea.properties")
1031       exclude(name: "bin/*.vmoptions")
1032     }
1033     zipfileset(dir: "$distMac/bin", prefix: "bin", filemode: "775") {
1034       include(name: "restarter*")
1035       include(name: "*.py")
1036     }
1037     zipfileset(dir: "$distMac/bin", prefix: "bin/mac", filemode: "775") {
1038       include(name: "fsnotifier*")
1039     }
1040   }
1041
1042   notifyArtifactBuilt(zipPath)
1043 })
1044
1045 binding.setVariable("buildMacZip", { String zipRoot, String zipPath, List paths, String macPath, List extraBins = [] ->
1046   projectBuilder.stage(".mac.zip")
1047
1048   allPaths = paths + [macPath]
1049   ant.zip(zipfile: zipPath) {
1050     allPaths.each {
1051       zipfileset(dir: it, prefix: zipRoot) {
1052         exclude(name: "bin/*.sh")
1053         exclude(name: "bin/*.py")
1054         exclude(name: "bin/fsnotifier")
1055         exclude(name: "bin/restarter")
1056         exclude(name: "MacOS/*")
1057         exclude(name: "build.txt")
1058         exclude(name: "NOTICE.txt")
1059         extraBins.each {
1060           exclude(name: it)
1061         }
1062         exclude(name: "bin/idea.properties")
1063       }
1064     }
1065
1066     allPaths.each {
1067       zipfileset(dir: it, prefix: zipRoot, filemode: "755") {
1068         include(name: "bin/*.sh")
1069         include(name: "bin/*.py")
1070         include(name: "bin/fsnotifier")
1071         include(name: "bin/restarter")
1072         include(name: "MacOS/*")
1073         extraBins.each {
1074           include(name: it)
1075         }
1076       }
1077     }
1078
1079     allPaths.each {
1080       zipfileset(dir: it, prefix: "$zipRoot/Resources") {
1081         include(name: "build.txt")
1082         include(name: "NOTICE.txt")
1083       }
1084     }
1085
1086     zipfileset(file: "$macPath/bin/idea.properties", prefix: "$zipRoot/bin")
1087   }
1088 })
1089
1090 binding.setVariable("buildTarGz", { String tarRoot, String tarPath, List paths, List extraBins = [] ->
1091   projectBuilder.stage(".tar.gz")
1092
1093   fixIdeaPropertiesEol(paths, "unix")
1094
1095   ant.tar(tarfile: tarPath, longfile: "gnu") {
1096     paths.each {
1097       tarfileset(dir: it, prefix: tarRoot) {
1098         exclude(name: "bin/*.sh")
1099         exclude(name: "bin/*.py")
1100         exclude(name: "bin/fsnotifier*")
1101         extraBins.each {
1102           exclude(name: it)
1103         }
1104         type(type: "file")
1105       }
1106     }
1107
1108     paths.each {
1109       tarfileset(dir: it, prefix: tarRoot, filemode: "755") {
1110         include(name: "bin/*.sh")
1111         include(name: "bin/*.py")
1112         include(name: "bin/fsnotifier*")
1113         extraBins.each {
1114           include(name: it)
1115         }
1116         type(type: "file")
1117       }
1118     }
1119   }
1120
1121   String gzPath = "${tarPath}.gz"
1122   ant.gzip(src: tarPath, zipfile: gzPath)
1123   ant.delete(file: tarPath)
1124   notifyArtifactBuilt(gzPath)
1125 })
1126
1127 binding.setVariable("extractRedistJre", { String jdk_file_name, String destination  ->
1128   def jre_redist = "${destination}"
1129   def jdkArchive = new File(jdk_file_name)
1130   if (jdkArchive.exists()) {
1131     ant.mkdir(dir: jre_redist)
1132     if (jdk_file_name.endsWith(".tar")) {
1133       ant.untar(dest: jre_redist, src: "${jdk_file_name}")
1134     }
1135     else {
1136       ant.unzip(dest: jre_redist, src: "${jdk_file_name}")
1137     }
1138   } else {
1139      projectBuilder.error("${jdk_file_name} doesn't exist.")
1140   }
1141 })
1142
1143 private void fixIdeaPropertiesEol(List paths, String eol) {
1144   paths.each {
1145     String file = "$it/bin/idea.properties"
1146     if (new File(file).exists()) {
1147       ant.fixcrlf(file: file, eol: eol)
1148     }
1149   }
1150 }
1151
1152 binding.setVariable("buildWinLauncher", { String ch, String inputPath, String outputPath, String appInfo,
1153                                           String launcherProperties, String pathsSelector, List resourcePaths ->
1154   projectBuilder.stage("winLauncher")
1155
1156   if (pathsSelector != null) {
1157     def paths = getProperty("paths")
1158     def launcherPropertiesTemp = "${paths.sandbox}/launcher.properties"
1159     copyAndPatchFile(launcherProperties, launcherPropertiesTemp, ["PRODUCT_PATHS_SELECTOR": pathsSelector,
1160                                                                   "product_uc" : p("component.names.product").toLowerCase(),
1161                                                                   "IDE-NAME": p("component.names.product").toUpperCase()])
1162     launcherProperties = launcherPropertiesTemp
1163   }
1164
1165   ant.java(classname: "com.pme.launcher.LauncherGeneratorMain", fork: "true", failonerror: "true") {
1166     sysproperty(key: "java.awt.headless", value: "true")
1167     arg(value: inputPath)
1168     arg(value: appInfo)
1169     arg(value: "$ch/native/WinLauncher/WinLauncher/resource.h")
1170     arg(value: launcherProperties)
1171     arg(value: outputPath)
1172     classpath {
1173       pathelement(location: "$ch/build/lib/launcher-generator.jar")
1174       fileset(dir: "$ch/lib") {
1175         include(name: "guava*.jar")
1176         include(name: "jdom.jar")
1177         include(name: "sanselan*.jar")
1178       }
1179       resourcePaths.each {
1180         pathelement(location: it)
1181       }
1182     }
1183   }
1184 })
1185
1186 binding.setVariable("layoutUpdater", { String target, String jarName = "updater.jar" ->
1187   layout(target) {
1188     jar(jarName) {
1189       module("updater")
1190       manifest {
1191         attribute(name: "Main-Class", value: "com.intellij.updater.Bootstrap")
1192       }
1193     }
1194   }
1195 })
1196
1197 binding.setVariable("collectUsedJars", { List modules, List approvedJars, List forbiddenJars, List modulesToBuild ->
1198   def usedJars = new HashSet();
1199
1200   modules.each {
1201     def module = findModule(it)
1202     if (module != null) {
1203       projectBuilder.moduleRuntimeClasspath(module, false).each {
1204         File file = new File(it)
1205         if (file.exists()) {
1206           String path = file.canonicalPath.replace('\\', '/')
1207           if (path.endsWith(".jar") && approvedJars.any { path.startsWith(it) } && !forbiddenJars.any { path.contains(it) }) {
1208             if (usedJars.add(path)) {
1209               projectBuilder.info("\tADDED: $path for ${module.getName()}")
1210             }
1211           }
1212         }
1213       }
1214       if (modulesToBuild != null) {
1215         modulesToBuild << module
1216       }
1217     }
1218     else {
1219       projectBuilder.warning("$it is not a module")
1220     }
1221   }
1222
1223   return usedJars
1224 })
1225
1226 final def javaRT = "java-runtime" // that is used by build system in "buildSearchableOptions"
1227
1228 binding.setVariable("buildModulesAndCollectUsedJars", { List modules, List approvedJars, List forbiddenJars ->
1229   def modulesToBuild = []
1230   def modulesNames = modules.contains(javaRT) ? modules : [*modules, javaRT]
1231   def usedJars = collectUsedJars(modulesNames, approvedJars, forbiddenJars, modulesToBuild)
1232   clearBuildCaches()
1233   projectBuilder.cleanOutput()
1234   projectBuilder.buildModules(modulesToBuild)
1235
1236   return usedJars
1237 })
1238
1239 binding.setVariable("buildSearchableOptions", { String target, List licenses, Closure cp, String jvmArgs = null,
1240                                                 def paths = getProperty("paths") ->
1241   def pathToJRT = "${projectBuilder.moduleOutput(findModule(javaRT))}"
1242   projectBuilder.stage("Building searchable options JRT:" + pathToJRT)
1243
1244   String targetFile = "${target}/searchableOptions.xml"
1245   ant.delete(file: targetFile)
1246
1247   licenses.each {
1248     ant.copy(file: it, todir: paths.ideaSystem)
1249   }
1250
1251   ant.path(id: "searchable.options.classpath") { cp() }
1252   String classpathFile = "${paths.sandbox}/classpath.txt"
1253   ant.echo(file: classpathFile, append: false, message: "\${toString:searchable.options.classpath}")
1254   ant.replace(file: classpathFile, token: File.pathSeparator, value: "\n")
1255
1256   ant.java(classname: "com.intellij.rt.execution.CommandLineWrapper", fork: true, failonerror: true) {
1257     jvmarg(line: "-ea -Xmx500m -XX:MaxPermSize=200m")
1258     jvmarg(value: "-Xbootclasspath/a:${projectBuilder.moduleOutput(findModule("boot"))}")
1259     jvmarg(value: "-Didea.home.path=${home}")
1260     jvmarg(value: "-Didea.system.path=${paths.ideaSystem}")
1261     jvmarg(value: "-Didea.config.path=${paths.ideaConfig}")
1262     if (jvmArgs != null) {
1263       jvmarg(line: jvmArgs)
1264     }
1265
1266     arg(value: "${classpathFile}")
1267     arg(line: "com.intellij.idea.Main traverseUI")
1268     arg(value: "${target}/searchableOptions.xml")
1269
1270     classpath() {
1271       pathelement(location: "${pathToJRT}")
1272     }
1273   }
1274
1275   ant.available(file: targetFile, property: "searchable.options.exists");
1276   ant.fail(unless: "searchable.options.exists", message: "Searchable options were not built.")
1277 })
1278
1279 binding.setVariable("reassignAltClickToMultipleCarets", {String communityHome ->
1280   String defaultKeymapContent = new File("$communityHome/platform/platform-resources/src/idea/Keymap_Default.xml").text
1281   defaultKeymapContent = defaultKeymapContent.replace("<mouse-shortcut keystroke=\"alt button1\"/>", "")
1282   defaultKeymapContent = defaultKeymapContent.replace("<mouse-shortcut keystroke=\"alt shift button1\"/>",
1283                                                       "<mouse-shortcut keystroke=\"alt button1\"/>")
1284   patchedKeymapFile = new File("${paths.sandbox}/classes/production/platform-resources/idea/Keymap_Default.xml")
1285   patchedKeymapFile.write(defaultKeymapContent)
1286 })
1287
1288 // modules used in Upsource and in Kotlin as an API to IDEA
1289 binding.setVariable("analysisApiModules", [
1290     "analysis-api",
1291     "boot",
1292     "core-api",
1293     "duplicates-analysis",
1294     "editor-ui-api",
1295     "editor-ui-ex",
1296     "extensions",
1297     "indexing-api",
1298     "java-analysis-api",
1299     "java-indexing-api",
1300     "java-psi-api",
1301     "java-structure-view",
1302     "jps-model-api",
1303     "jps-model-serialization",
1304     "projectModel-api",
1305     "util",
1306     "util-rt",
1307     "xml-analysis-api",
1308     "xml-psi-api",
1309     "xml-structure-view-api",
1310 ])
1311 binding.setVariable("analysisImplModules", [
1312     "analysis-impl",
1313     "core-impl",
1314     "indexing-impl",
1315     "java-analysis-impl",
1316     "java-indexing-impl",
1317     "java-psi-impl",
1318     "projectModel-impl",
1319     "structure-view-impl",
1320     "xml-analysis-impl",
1321     "xml-psi-impl",
1322     "xml-structure-view-impl",
1323 ])