Merge remote-tracking branch 'origin/master'
[idea/community.git] / build / groovy / org / jetbrains / intellij / build / impl / WindowsDistributionBuilder.groovy
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 package org.jetbrains.intellij.build.impl
17
18 import com.intellij.openapi.util.io.FileFilters
19 import org.jetbrains.intellij.build.BuildContext
20 import org.jetbrains.intellij.build.BuildOptions
21 import org.jetbrains.intellij.build.JvmArchitecture
22 import org.jetbrains.intellij.build.WindowsDistributionCustomizer
23 import org.jetbrains.jps.model.module.JpsModuleSourceRoot
24
25 /**
26  * @author nik
27  */
28 class WindowsDistributionBuilder extends OsSpecificDistributionBuilder {
29   private final WindowsDistributionCustomizer customizer
30   private final File ideaProperties
31   private final File patchedApplicationInfo
32
33   WindowsDistributionBuilder(BuildContext buildContext, WindowsDistributionCustomizer customizer, File ideaProperties, File patchedApplicationInfo) {
34     super(BuildOptions.OS_WINDOWS, "Windows", buildContext)
35     this.patchedApplicationInfo = patchedApplicationInfo
36     this.customizer = customizer
37     this.ideaProperties = ideaProperties
38   }
39
40   @Override
41   String copyFilesForOsDistribution() {
42     buildContext.messages.progress("Building distributions for Windows")
43     String winDistPath = "$buildContext.paths.buildOutputRoot/dist.win"
44     buildContext.ant.copy(todir: "$winDistPath/bin") {
45       fileset(dir: "$buildContext.paths.communityHome/bin/win") {
46         if (!buildContext.includeBreakGenLibraries()) {
47           exclude(name: "breakgen*")
48         }
49       }
50       if (buildContext.productProperties.yourkitAgentBinariesDirectoryPath != null) {
51         fileset(dir: buildContext.productProperties.yourkitAgentBinariesDirectoryPath) {
52           include(name: "yjpagent*.dll")
53         }
54       }
55     }
56     buildContext.ant.copy(todir: "$winDistPath/lib/libpty/win") {
57       fileset(dir: "$buildContext.paths.communityHome/lib/libpty/win")
58     }
59
60     buildContext.ant.copy(file: ideaProperties.path, todir: "$winDistPath/bin")
61     buildContext.ant.fixcrlf(file: "$winDistPath/bin/idea.properties", eol: "dos")
62
63     if (customizer.icoPath != null) {
64       buildContext.ant.copy(file: customizer.icoPath, tofile: "$winDistPath/bin/${buildContext.productProperties.baseFileName}.ico")
65     }
66     if (customizer.includeBatchLaunchers) {
67       generateScripts(winDistPath)
68     }
69     List<JvmArchitecture> architectures = customizer.include32BitLauncher ? JvmArchitecture.values() : [JvmArchitecture.x64]
70     generateVMOptions(winDistPath, architectures)
71     architectures.each {
72       buildWinLauncher(it, winDistPath)
73     }
74     customizer.copyAdditionalFiles(buildContext, winDistPath)
75     new File(winDistPath, "bin").listFiles(FileFilters.filesWithExtension("exe"))?.each {
76       buildContext.signExeFile(it.absolutePath)
77     }
78     return winDistPath
79   }
80
81   @Override
82   void buildArtifacts(String winDistPath) {
83     def arch = customizer.bundledJreArchitecture
84     def jreDirectoryPath = arch != null ? buildContext.bundledJreManager.extractWinJre(arch) : null
85     if (customizer.buildZipArchive) {
86       buildWinZip(jreDirectoryPath, buildContext.productProperties.buildCrossPlatformDistribution ? ".win" : "", winDistPath)
87     }
88
89     if (arch != null && customizer.buildZipWithBundledOracleJre) {
90       String oracleJrePath = buildContext.bundledJreManager.extractOracleWinJre(arch)
91       if (oracleJrePath != null) {
92         buildWinZip(oracleJrePath, "-oracle-win", winDistPath)
93       }
94       else {
95         buildContext.messages.warning("Skipping building Windows zip archive with bundled Oracle JRE because JRE archive is missing")
96       }
97     }
98
99     buildContext.executeStep("Build Windows Exe Installer", BuildOptions.WINDOWS_EXE_INSTALLER_STEP) {
100       new WinExeInstallerBuilder(buildContext, customizer, jreDirectoryPath).buildInstaller(winDistPath)
101     }
102   }
103
104   private void generateScripts(String winDistPath) {
105     String fullName = buildContext.applicationInfo.productName
106     String vmOptionsFileName = "${buildContext.productProperties.baseFileName}%BITS%.exe"
107
108     String classPath = "SET CLASS_PATH=%IDE_HOME%\\lib\\${buildContext.bootClassPathJarNames[0]}\n"
109     classPath += buildContext.bootClassPathJarNames[1..-1].collect { "SET CLASS_PATH=%CLASS_PATH%;%IDE_HOME%\\lib\\$it" }.join("\n")
110     if (buildContext.productProperties.toolsJarRequired) {
111       classPath += "\nSET CLASS_PATH=%CLASS_PATH%;%JDK%\\lib\\tools.jar"
112     }
113
114     def batName = "${buildContext.productProperties.baseFileName}.bat"
115     buildContext.ant.copy(todir: "$winDistPath/bin") {
116       fileset(dir: "$buildContext.paths.communityHome/bin/scripts/win")
117
118       filterset(begintoken: "@@", endtoken: "@@") {
119         filter(token: "product_full", value: fullName)
120         filter(token: "product_uc", value: buildContext.productProperties.environmentVariableBaseName(buildContext.applicationInfo))
121         filter(token: "vm_options", value: vmOptionsFileName)
122         filter(token: "isEap", value: buildContext.applicationInfo.isEAP)
123         filter(token: "system_selector", value: buildContext.systemSelector)
124         filter(token: "ide_jvm_args", value: buildContext.additionalJvmArguments)
125         filter(token: "class_path", value: classPath)
126         filter(token: "script_name", value: batName)
127       }
128     }
129
130     if (batName != "idea.bat") {
131       //todo[nik] rename idea.bat in sources to something more generic
132       buildContext.ant.move(file: "$winDistPath/bin/idea.bat", tofile: "$winDistPath/bin/$batName")
133     }
134     String inspectScript = buildContext.productProperties.inspectCommandName
135     if (inspectScript != "inspect") {
136       String targetPath = "$winDistPath/bin/${inspectScript}.bat"
137       buildContext.ant.move(file: "$winDistPath/bin/inspect.bat", tofile: targetPath)
138       buildContext.patchInspectScript(targetPath)
139     }
140
141
142     buildContext.ant.fixcrlf(srcdir: "$winDistPath/bin", includes: "*.bat", eol: "dos")
143   }
144
145   private void generateVMOptions(String winDistPath, Collection<JvmArchitecture> architectures) {
146     architectures.each {
147       def yourkitSessionName = buildContext.applicationInfo.isEAP && buildContext.productProperties.enableYourkitAgentInEAP ? buildContext.systemSelector : null
148       def fileName = "${buildContext.productProperties.baseFileName}${it.fileSuffix}.exe.vmoptions"
149       def vmOptions = VmOptionsGenerator.computeVmOptions(it, buildContext.applicationInfo.isEAP, buildContext.productProperties, yourkitSessionName)
150       new File(winDistPath, "bin/$fileName").text = vmOptions.replace(' ', '\n') + "\n"
151     }
152
153     buildContext.ant.fixcrlf(srcdir: "$winDistPath/bin", includes: "*.vmoptions", eol: "dos")
154   }
155
156   private void buildWinLauncher(JvmArchitecture arch, String winDistPath) {
157     buildContext.messages.block("Build Windows executable ${arch.name()}") {
158       String exeFileName = "${buildContext.productProperties.baseFileName}${arch.fileSuffix}.exe"
159       def launcherPropertiesPath = "${buildContext.paths.temp}/launcher${arch.fileSuffix}.properties"
160       def upperCaseProductName = buildContext.applicationInfo.upperCaseProductName
161       def lowerCaseProductName = buildContext.applicationInfo.shortProductName.toLowerCase()
162       String vmOptions = "$buildContext.additionalJvmArguments -Didea.paths.selector=${buildContext.systemSelector}".trim()
163       def productName = buildContext.applicationInfo.shortProductName
164
165       String jdkEnvVarSuffix = arch == JvmArchitecture.x64 && customizer.include32BitLauncher ? "_64" : "";
166       String vmOptionsEnvVarSuffix = arch == JvmArchitecture.x64 && customizer.include32BitLauncher ? "64" : ""
167       def envVarBaseName = buildContext.productProperties.environmentVariableBaseName(buildContext.applicationInfo)
168       new File(launcherPropertiesPath).text = """
169 IDS_JDK_ONLY=$buildContext.productProperties.toolsJarRequired
170 IDS_JDK_ENV_VAR=${envVarBaseName}_JDK$jdkEnvVarSuffix
171 IDS_APP_TITLE=$productName Launcher
172 IDS_VM_OPTIONS_PATH=%USERPROFILE%\\\\.$buildContext.systemSelector
173 IDS_VM_OPTION_ERRORFILE=-XX:ErrorFile=%USERPROFILE%\\\\java_error_in_${lowerCaseProductName}_%p.log
174 IDS_VM_OPTION_HEAPDUMPPATH=-XX:HeapDumpPath=%USERPROFILE%\\\\java_error_in_${lowerCaseProductName}.hprof
175 IDC_WINLAUNCHER=${upperCaseProductName}_LAUNCHER
176 IDS_PROPS_ENV_VAR=${envVarBaseName}_PROPERTIES
177 IDS_VM_OPTIONS_ENV_VAR=$envVarBaseName${vmOptionsEnvVarSuffix}_VM_OPTIONS
178 IDS_ERROR_LAUNCHING_APP=Error launching $productName
179 IDS_VM_OPTIONS=$vmOptions
180 """.trim()
181
182       def communityHome = "$buildContext.paths.communityHome"
183       String inputPath = "$communityHome/bin/WinLauncher/WinLauncher${arch.fileSuffix}.exe"
184       def outputPath = "$winDistPath/bin/$exeFileName"
185       def resourceModules = [buildContext.findApplicationInfoModule(), buildContext.findModule("icons")]
186       buildContext.ant.java(classname: "com.pme.launcher.LauncherGeneratorMain", fork: "true", failonerror: "true") {
187         sysproperty(key: "java.awt.headless", value: "true")
188         arg(value: inputPath)
189         arg(value: patchedApplicationInfo.absolutePath)
190         arg(value: "$communityHome/native/WinLauncher/WinLauncher/resource.h")
191         arg(value: launcherPropertiesPath)
192         arg(value: outputPath)
193         classpath {
194           pathelement(location: "$communityHome/build/lib/launcher-generator.jar")
195           fileset(dir: "$communityHome/lib") {
196             include(name: "guava*.jar")
197             include(name: "jdom.jar")
198             include(name: "sanselan*.jar")
199           }
200           resourceModules.collectMany { it.sourceRoots }.each { JpsModuleSourceRoot root ->
201             pathelement(location: root.file.absolutePath)
202           }
203           buildContext.productProperties.brandingResourcePaths.each {
204             pathelement(location: it)
205           }
206         }
207       }
208     }
209   }
210
211   private void buildWinZip(String jreDirectoryPath, String zipNameSuffix, String winDistPath) {
212     buildContext.messages.block("Build Windows ${zipNameSuffix}.zip distribution") {
213       def targetPath = "$buildContext.paths.artifacts/${buildContext.productProperties.baseArtifactName(buildContext.applicationInfo, buildContext.buildNumber)}${zipNameSuffix}.zip"
214       def zipPrefix = customizer.rootDirectoryName(buildContext.applicationInfo, buildContext.buildNumber)
215       def dirs = [buildContext.paths.distAll, winDistPath]
216       if (jreDirectoryPath != null) {
217         dirs += jreDirectoryPath
218       }
219       buildContext.messages.progress("Building Windows ${zipNameSuffix}.zip archive")
220       buildContext.ant.zip(zipfile: targetPath) {
221         dirs.each {
222           zipfileset(dir: it, prefix: zipPrefix)
223         }
224       }
225       buildContext.notifyArtifactBuilt(targetPath)
226     }
227   }
228 }