diff --git a/build/conf/ideaCE/win/images/headerlogo.bmp b/build/conf/ideaCE/win/images/headerlogo.bmp new file mode 100644 index 000000000000..1a89f68c58c3 Binary files /dev/null and b/build/conf/ideaCE/win/images/headerlogo.bmp differ diff --git a/build/conf/ideaCE/win/images/install.ico b/build/conf/ideaCE/win/images/install.ico new file mode 100644 index 000000000000..b39143fc3e1e Binary files /dev/null and b/build/conf/ideaCE/win/images/install.ico differ diff --git a/build/conf/ideaCE/win/images/logo.bmp b/build/conf/ideaCE/win/images/logo.bmp new file mode 100644 index 000000000000..f7d12dfe40a5 Binary files /dev/null and b/build/conf/ideaCE/win/images/logo.bmp differ diff --git a/build/conf/ideaCE/win/images/uninstall.ico b/build/conf/ideaCE/win/images/uninstall.ico new file mode 100644 index 000000000000..9e469ec6fc7e Binary files /dev/null and b/build/conf/ideaCE/win/images/uninstall.ico differ diff --git a/build/conf/nsis/pathsCE.nsi b/build/conf/nsis/pathsCE.nsi deleted file mode 100644 index 9c84bbad0b2d..000000000000 --- a/build/conf/nsis/pathsCE.nsi +++ /dev/null @@ -1,5 +0,0 @@ -; Installer images -!define IMAGES_LOCATION "${BASE_DIR}\build\images\idea" -!define PRODUCT_PROPERTIES_FILE "${BASE_DIR}\out\release\dist.all.ce\bin\idea.properties" -!define PRODUCT_VM_OPTIONS_NAME idea*.exe.vmoptions -!define PRODUCT_VM_OPTIONS_FILE "${BASE_DIR}\out\release\dist.win.ce\bin\${PRODUCT_VM_OPTIONS_NAME}" \ No newline at end of file diff --git a/build/conf/nsis/stringsCE.nsi b/build/conf/nsis/stringsCE.nsi deleted file mode 100644 index 98fe712d90bb..000000000000 --- a/build/conf/nsis/stringsCE.nsi +++ /dev/null @@ -1,16 +0,0 @@ -!define MANUFACTURER "JetBrains" -!define MUI_PRODUCT "IntelliJ IDEA Community Edition" -!define PRODUCT_FULL_NAME "IntelliJ IDEA Community Edition" -!define PRODUCT_EXE_FILE "idea.exe" -!define PRODUCT_EXE_FILE_64 "idea64.exe" -!define PRODUCT_ICON_FILE "idea_CEinst.ico" -!define PRODUCT_UNINST_ICON_FILE "idea_CEuninst.ico" -!define PRODUCT_LOGO_FILE "logoCE.bmp" -!define PRODUCT_HEADER_FILE "headerlogoCE.bmp" -!define ASSOCIATION ".java,.groovy,.kt" -!define UNINSTALL_WEB_PAGE "https://www.jetbrains.com/idea/uninstall/?edition=IC-__VERSION_MAJOR__.__VERSION_MINOR__" - -; if SHOULD_SET_DEFAULT_INSTDIR != 0 then default installation directory will be directory where highest-numbered IDEA build has been installed -; set to 1 for release build -!define SHOULD_SET_DEFAULT_INSTDIR "0" - diff --git a/build/groovy/org/jetbrains/intellij/build/BuildOptions.groovy b/build/groovy/org/jetbrains/intellij/build/BuildOptions.groovy index 0a775b1558e0..474c599fa648 100644 --- a/build/groovy/org/jetbrains/intellij/build/BuildOptions.groovy +++ b/build/groovy/org/jetbrains/intellij/build/BuildOptions.groovy @@ -38,6 +38,7 @@ class BuildOptions { static final MAC_DMG_STEP = "mac_dmg" static final LINUX_DISTRIBUTION_STEP = "linux_dist" static final WINDOWS_DISTRIBUTION_STEP = "windows_dist" + static final WINDOWS_EXE_INSTALLER_STEP = "windows_exe_installer" static final CROSS_PLATFORM_DISTRIBUTION_STEP = "cross_platform_dist" /** diff --git a/build/groovy/org/jetbrains/intellij/build/ProductProperties.groovy b/build/groovy/org/jetbrains/intellij/build/ProductProperties.groovy index 6384a47b2645..f26af8425f96 100644 --- a/build/groovy/org/jetbrains/intellij/build/ProductProperties.groovy +++ b/build/groovy/org/jetbrains/intellij/build/ProductProperties.groovy @@ -31,6 +31,8 @@ public abstract class ProductProperties { */ boolean toolsJarRequired = false + String fullNameIncludingEdition = null + abstract def String systemSelector(ApplicationInfoProperties applicationInfo) String additionalIDEPropertiesFilePath @@ -46,6 +48,10 @@ public abstract class ProductProperties { abstract def String archiveName(String buildNumber) + String uninstallFeedbackPageUrl(ApplicationInfoProperties applicationInfo) { + return null + } + boolean setPluginAndIDEVersionInPluginXml = true String ideJvmArgs = "" @@ -77,6 +83,15 @@ public abstract class ProductProperties { class WindowsProductProperties { boolean includeBatchLauncher = true boolean bundleJre = true + boolean associateIpr = true + /** + * Path to a directory containing images for installer: logo.bpm, headerlogo.bpm, install.icon, uninstall.ico + */ + String installerImagesPath + /** + * List of file extensions (starting with dot) which need to be associated with the product + */ + List fileAssociations = [] } class MacProductProperties { diff --git a/build/groovy/org/jetbrains/intellij/build/WinInstallerBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/WinInstallerBuilder.groovy deleted file mode 100644 index 62e453892ad7..000000000000 --- a/build/groovy/org/jetbrains/intellij/build/WinInstallerBuilder.groovy +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.intellij.build - -import com.intellij.openapi.util.SystemInfoRt -import org.codehaus.gant.GantBuilder -import org.jetbrains.jps.gant.JpsGantProjectBuilder -/** - * @author nik - */ -class WinInstallerBuilder { - GantBuilder ant - JpsGantProjectBuilder projectBuilder - ApplicationInfoProperties applicationInfo - /** - * Path to a directory where project sources are located. It will be used to replace 'BASE_DIR' in *.nsi files. - */ - String baseDirectory - /** - * Path to a directory where IntelliJ IDEA community sources are located - */ - String communityHome - /** - * Path to a directory where artifacts are stored - */ - String artifactsPath - /** - * Path to a directory where temporary files can be stored - */ - String sandboxPath - /** - * Prefix for the output file name. The value of buildNumber will be appended to this prefix. - */ - String outNamePrefix - /** - * Short build number without product code (e.g. 142.239) - */ - String buildNumber - /** - * Name which will be used for default config/system paths, should include the product name and version. E.g. for IntelliJ Ultimate 16 it is - * 'IntelliJIdea16', so the settings and system files will be stored in $USER_HOME/.IntelliJIdea16 by default. - */ - String systemSelector - /** - * Determines whether tools.jar should be included to the bundled JDK - */ - boolean includeToolsJar = true - boolean associateIpr = true - /** - * Path to a JDK 8 zip file which should be bundled with the application - */ - String winJDKZipPath - - /** - * Builds .exe installer. If build/lib/jet-sign.jar exists in baseDirectory it will be used to sign the created .exe file. - * - * @param pathsToInclude list of paths to directories which contents should be included to the product distribution - * @param stringsFile path to *.nsi file where installers variables are defined (use build/conf/nsis/stringsCE.nsi as a reference) - * @param pathsFile path to *.nsi file where installers path variables are defined (use build/conf/nsis/pathsCE.nsi as a reference) - * @return path to the created installer file - */ - def buildInstaller(List pathsToInclude, String stringsFile, String pathsFile) { - if (!SystemInfoRt.isWindows && !SystemInfoRt.isLinux) { - projectBuilder.warning("Windows installer can be built only under Windows or Linux") - return null - } - - projectBuilder.stage("Building Windows installer") - String outFileName = "${outNamePrefix}${buildNumber}" - ant.taskdef(name: "nsis", classname: "com.intellij.internalUtilities.ant.NsiFiles", classpath: "$communityHome/build/lib/NsiFiles.jar") - - def box = sandboxPath - ant.mkdir(dir: "$box/bin") - ant.mkdir(dir: "$box/nsiconf") - - if (winJDKZipPath != null) { - ant.mkdir(dir: "$box/jre") - ant.unzip(dest: "$box/jre", src: winJDKZipPath) - - ant.copy(todir: "$box/bin") { - fileset(dir: "$box/jre/jre/bin") { - include(name: "msvcr71.dll") - } - } - } - - ant.copy(todir: "$box/nsiconf") { - fileset(dir: "$communityHome/build/conf/nsis") { - include(name: "*") - exclude(name: "version*") - exclude(name: "strings*") - exclude(name: "paths*") - } - } - - if (applicationInfo.isEAP) { - ant.copy(file: "$communityHome/build/conf/nsis/version.eap.nsi", - tofile: "$box/nsiconf/version.nsi", overwrite: true) - } - else { - ant.copy(file: "$communityHome/build/conf/nsis/version.nsi", - tofile: "$box/nsiconf/version.nsi", overwrite: true) - } - ant.copy(file: pathsFile, toFile: "$box/nsiconf/paths.nsi", overwrite: true) - - ant.nsis(instfile: "$box/nsiconf/idea_win.nsh", uninstfile: "$box/nsiconf/unidea_win.nsh") { - pathsToInclude.each { - ant.fileset(dir: it, includes: "**/*") { - exclude(name: "**/idea.properties") - exclude(name: "**/*.vmoptions") - } - } - ant.fileset(dir: box, includes: "bin/msvcr71.dll") - if (winJDKZipPath != null) { - ant.fileset(dir: box, includes: "jre/**/*") - if (includeToolsJar) { - ant.fileset(dir: box) { - include(name: "jre/lib/tools.jar") - } - } - } - } - - ant.copy(file: stringsFile, toFile: "$box/nsiconf/strings.nsi", overwrite: true) - ant.replace(file: "$box/nsiconf/strings.nsi") { - replacefilter(token: "__VERSION_MAJOR__", value: applicationInfo.majorVersion) - replacefilter(token: "__VERSION_MINOR__", value: applicationInfo.minorVersion) - } - - ant.replace(file: "$box/nsiconf/version.nsi") { - replacefilter(token: "__BUILD_NUMBER__", value: buildNumber) - replacefilter(token: "__VERSION_MAJOR__", value: applicationInfo.majorVersion) - replacefilter(token: "__VERSION_MINOR__", value: applicationInfo.minorVersion) - replacefilter(token: "__PRODUCT_PATHS_SELECTOR__", value: systemSelector) - } - - ant.unzip(src: "$communityHome/build/tools/NSIS.zip", dest: box) - if (SystemInfoRt.isWindows) { - ant.exec(command: "\"${box}/NSIS/makensis.exe\"" + - " /DBASE_DIR=\"$baseDirectory\"" + - " /DCOMMUNITY_DIR=\"$communityHome\"" + - " /DIPR=\"${associateIpr}\"" + - " /DOUT_FILE=\"${outFileName}\"" + - " /DOUT_DIR=\"$artifactsPath\"" + - " \"${box}/nsiconf/idea.nsi\"") - } - else if (SystemInfoRt.isLinux) { - ant.exec(command: "makensis" + - " '-X!AddPluginDir \"${box}/NSIS/Plugins\"'" + - " '-X!AddIncludeDir \"${box}/NSIS/Include\"'" + - " -DBASE_DIR=\"$baseDirectory\"" + - " -DCOMMUNITY_DIR=\"$communityHome\"" + - " -DIPR=\"${associateIpr}\"" + - " -DOUT_FILE=\"${outFileName}\"" + - " -DOUT_DIR=\"$artifactsPath\"" + - " \"${box}/nsiconf/idea.nsi\"") - } - - def installerPath = "$artifactsPath/${outFileName}.exe" - if (!new File(installerPath).exists()) { - projectBuilder.error("Installer wasn't created.") - } - - def signJarPath = "$baseDirectory/build/lib/jet-sign.jar" - if (new File(signJarPath).exists()) { - projectBuilder.stage("Signing $installerPath") - ant.taskdef(name: "jet-sign", classname: "jetbrains.sign.JetSignTask") { - classpath(path: signJarPath) - } - ant."jet-sign"() { - ant.fileset(dir: artifactsPath) { - include(name: "${outFileName}.exe") - } - } - projectBuilder.stage("Signing done") - } - else { - projectBuilder.warning("$signJarPath not found, installer won't be signed") - } - return installerPath - } -} \ No newline at end of file diff --git a/build/groovy/org/jetbrains/intellij/build/impl/WinExeInstallerBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/impl/WinExeInstallerBuilder.groovy new file mode 100644 index 000000000000..7ab3643e8c88 --- /dev/null +++ b/build/groovy/org/jetbrains/intellij/build/impl/WinExeInstallerBuilder.groovy @@ -0,0 +1,180 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.intellij.build.impl + +import com.intellij.openapi.util.SystemInfoRt +import org.codehaus.gant.GantBuilder +import org.jetbrains.intellij.build.BuildContext +/** + * @author nik + */ +class WinExeInstallerBuilder { + private final BuildContext buildContext + private final GantBuilder ant + + WinExeInstallerBuilder(BuildContext buildContext) { + this.buildContext = buildContext + ant = buildContext.ant + } + + void buildInstaller(String winDistPath) { + if (!SystemInfoRt.isWindows && !SystemInfoRt.isLinux) { + buildContext.messages.warning("Windows installer can be built only under Windows or Linux") + return + } + + String communityHome = buildContext.paths.communityHome + String outFileName = buildContext.productProperties.archiveName(buildContext.buildNumber) + buildContext.messages.progress("Building Windows installer $outFileName") + ant.taskdef(name: "nsis", classname: "com.intellij.internalUtilities.ant.NsiFiles", classpath: "$communityHome/build/lib/NsiFiles.jar") + + def box = "$buildContext.paths.temp/winInstaller" + ant.mkdir(dir: "$box/bin") + ant.mkdir(dir: "$box/nsiconf") + + def bundleJre = buildContext.productProperties.windows.bundleJre + if (bundleJre && !new File(buildContext.paths.winJre).exists()) { + buildContext.messages.info("JRE won't be bundled with Windows installer because JRE directory doesn't exist: ${buildContext.paths.winJre}") + bundleJre = false + } + + if (bundleJre) { + ant.copy(todir: "$box/bin") { + fileset(dir: "${buildContext.paths.winJre}/jre/bin") { + include(name: "msvcr71.dll") + } + } + } + + ant.copy(todir: "$box/nsiconf") { + fileset(dir: "$communityHome/build/conf/nsis") { + include(name: "*") + exclude(name: "strings*") + exclude(name: "paths*") + exclude(name: "version*") + } + } + + ant.nsis(instfile: "$box/nsiconf/idea_win.nsh", uninstfile: "$box/nsiconf/unidea_win.nsh") { + [buildContext.paths.distAll, winDistPath].each { + ant.fileset(dir: it, includes: "**/*") { + exclude(name: "**/idea.properties") + exclude(name: "**/*.vmoptions") + } + } + if (bundleJre) { + ant.fileset(dir: box, includes: "bin/msvcr71.dll") + ant.fileset(dir: buildContext.paths.winJre, includes: "jre/**/*") + //todo[nik] how tools.jar is excluded? + if (buildContext.productProperties.toolsJarRequired) { + ant.fileset(dir: buildContext.paths.winJre) { + include(name: "jre/lib/tools.jar") + } + } + } + } + + prepareConfigurationFiles(box, winDistPath) + + ant.unzip(src: "$communityHome/build/tools/NSIS.zip", dest: box) + if (SystemInfoRt.isWindows) { + ant.exec(command: "\"${box}/NSIS/makensis.exe\"" + + " /DCOMMUNITY_DIR=\"$communityHome\"" + + " /DIPR=\"${buildContext.productProperties.windows.associateIpr}\"" + + " /DOUT_FILE=\"${outFileName}\"" + + " /DOUT_DIR=\"${buildContext.paths.artifacts}\"" + + " \"${box}/nsiconf/idea.nsi\"") + } + else if (SystemInfoRt.isLinux) { + ant.exec(command: "makensis" + + " '-X!AddPluginDir \"${box}/NSIS/Plugins\"'" + + " '-X!AddIncludeDir \"${box}/NSIS/Include\"'" + + " -DCOMMUNITY_DIR=\"$communityHome\"" + + " -DIPR=\"${buildContext.productProperties.windows.associateIpr}\"" + + " -DOUT_FILE=\"${outFileName}\"" + + " -DOUT_DIR=\"${buildContext.paths.artifacts}\"" + + " \"${box}/nsiconf/idea.nsi\"") + } + + def installerPath = "${buildContext.paths.artifacts}/${outFileName}.exe" + if (!new File(installerPath).exists()) { + buildContext.messages.error("Windows installer wasn't created.") + } + + //todo[nik] improve, probably it would be better to automatically download jet-sign.jar or pass it via property + def signJarPath = "${buildContext.paths.projectHome}/build/lib/jet-sign.jar" + if (new File(signJarPath).exists()) { + buildContext.messages.progress("Signing $installerPath") + ant.taskdef(name: "jet-sign", classname: "jetbrains.sign.JetSignTask") { + classpath(path: signJarPath) + } + ant."jet-sign"() { + ant.fileset(dir: buildContext.paths.artifacts) { + include(name: "${outFileName}.exe") + } + } + buildContext.messages.info("Signing done") + } + else { + buildContext.messages.warning("$signJarPath not found, installer won't be signed") + } + buildContext.notifyArtifactBuilt(installerPath) + } + + private void prepareConfigurationFiles(String box, String winDistPath) { + new File(box, "nsiconf/paths.nsi").text = """ +!define IMAGES_LOCATION "${buildContext.productProperties.windows.installerImagesPath}" +!define PRODUCT_PROPERTIES_FILE "$winDistPath/bin/idea.properties" +!define PRODUCT_VM_OPTIONS_NAME ${buildContext.fileNamePrefix}*.exe.vmoptions +!define PRODUCT_VM_OPTIONS_FILE "$winDistPath/bin/\${PRODUCT_VM_OPTIONS_NAME}" +""" + + String fullProductName = buildContext.productProperties.fullNameIncludingEdition ?: buildContext.applicationInfo.productName + def extensionsList = buildContext.productProperties.windows.fileAssociations + def fileAssociations = extensionsList.isEmpty() ? "NoAssociation" : extensionsList.join(",") + new File(box, "nsiconf/strings.nsi").text = """ +!define MANUFACTURER "JetBrains" +!define MUI_PRODUCT "$fullProductName" +!define PRODUCT_FULL_NAME "$fullProductName" +!define PRODUCT_EXE_FILE "${buildContext.fileNamePrefix}.exe" +!define PRODUCT_EXE_FILE_64 "${buildContext.fileNamePrefix}64.exe" +!define PRODUCT_ICON_FILE "install.ico" +!define PRODUCT_UNINST_ICON_FILE "uninstall.ico" +!define PRODUCT_LOGO_FILE "logo.bmp" +!define PRODUCT_HEADER_FILE "headerlogo.bmp" +!define ASSOCIATION "$fileAssociations" +!define UNINSTALL_WEB_PAGE "${buildContext.productProperties.uninstallFeedbackPageUrl(buildContext.applicationInfo) ?: "feedback_web_page"}" + +; if SHOULD_SET_DEFAULT_INSTDIR != 0 then default installation directory will be directory where highest-numbered IDEA build has been installed +; set to 1 for release build +!define SHOULD_SET_DEFAULT_INSTDIR "0" + +""" + + def versionString = buildContext.applicationInfo.isEAP ? "\${VER_BUILD}" : "\${MUI_VERSION_MAJOR}.\${MUI_VERSION_MINOR}" + new File(box, "nsiconf/version.nsi").text = """ +!define MUI_VERSION_MAJOR "${buildContext.applicationInfo.majorVersion}" +!define MUI_VERSION_MINOR "${buildContext.applicationInfo.minorVersion}" + +!define VER_BUILD ${buildContext.buildNumber} + +!define PRODUCT_WITH_VER "\${MUI_PRODUCT} $versionString" +!define PRODUCT_FULL_NAME_WITH_VER "\${PRODUCT_FULL_NAME} $versionString" +!define PRODUCT_PATHS_SELECTOR "${buildContext.systemSelector}" +!define PRODUCT_SETTINGS_DIR ".\${PRODUCT_PATHS_SELECTOR}" +""" + } +} \ No newline at end of file diff --git a/build/groovy/org/jetbrains/intellij/build/impl/WindowsDistributionBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/impl/WindowsDistributionBuilder.groovy index 0c4d4bcf556a..3ab47c73f459 100644 --- a/build/groovy/org/jetbrains/intellij/build/impl/WindowsDistributionBuilder.groovy +++ b/build/groovy/org/jetbrains/intellij/build/impl/WindowsDistributionBuilder.groovy @@ -16,6 +16,7 @@ package org.jetbrains.intellij.build.impl import org.jetbrains.intellij.build.BuildContext +import org.jetbrains.intellij.build.BuildOptions import org.jetbrains.jps.model.module.JpsModuleSourceRoot /** * @author nik @@ -46,6 +47,10 @@ class WindowsDistributionBuilder { buildWinLauncher(JvmArchitecture.x64) buildContext.productProperties.customWinLayout(winDistPath) buildWinZip() + + buildContext.executeStep("Build Windows Exe Installer", BuildOptions.WINDOWS_EXE_INSTALLER_STEP) { + new WinExeInstallerBuilder(buildContext).buildInstaller(winDistPath) + } } //todo[nik] rename diff --git a/build/scripts/idea_properties.gant b/build/scripts/idea_properties.gant index cda10bfd9de1..94c009b9194e 100644 --- a/build/scripts/idea_properties.gant +++ b/build/scripts/idea_properties.gant @@ -24,6 +24,7 @@ def getProperties(String home) { platformPrefix = "Idea" code = "IC" appInfoModule = "community-resources" + fullNameIncludingEdition = "IntelliJ IDEA Community Edition" additionalIDEPropertiesFilePath = "$home/build/conf/ideaCE.properties" exe_launcher_properties = "$home/build/conf/ideaCE-launcher.properties" exe64_launcher_properties = "$home/build/conf/ideaCE64-launcher.properties" @@ -36,6 +37,8 @@ def getProperties(String home) { ico = "$home/platform/icons/src/idea_CE.ico" windows.bundleJre = true + windows.installerImagesPath = "$home/build/conf/ideaCE/win/images" + windows.fileAssociations = [".java", ".groovy", ".kt"] mac.helpId = "IJ" mac.urlSchemes = ["idea"] @@ -44,6 +47,11 @@ def getProperties(String home) { mac.dmgImagePath = "$home/build/conf/mac/communitydmg.png" } + @Override + String uninstallFeedbackPageUrl(ApplicationInfoProperties applicationInfo) { + return "https://www.jetbrains.com/idea/uninstall/?edition=IC-${applicationInfo.majorVersion}.${applicationInfo.minorVersion}" + } + def String appInfoFile() { "${projectBuilder.moduleOutput(findModule("community-resources"))}/idea/IdeaApplicationInfo.xml" }