From 83ec8c35933439305895b8569b2125a7e091fc57 Mon Sep 17 00:00:00 2001 From: David Cole Date: Tue, 8 Jul 2008 11:52:25 -0400 Subject: ENH: Further refinement of the CPack components functionality from Doug Gregor. Details: ========== - New cpack_add_component, cpack_add_component_group, and cpack_add_install_type "commands" defined as macros in the CPack module. - Documentation for all of the variables and commands in the CPack module. - Added get_cmake_property(... COMPONENTS) to CMake to ask for the names of all components. Used in the CPack module to automatically build component-based installers. (Set CPACK_MONOLITHIC_INSTALL to turn off component-based installation). - A group can declare its PARENT_GROUP, to build an arbitrary hierarchy of groups. - New CPack command cpack_configure_downloads, which creates an installer that downloads only the selected components on-the-fly. Those components marked DOWNLOADED will be separate packages downloaded on-the-fly (or, all packages can be marked as such with the ALL option to cpack_configure_downloads). Individual components are compressed with ZIP at installer-creation time and downloaded/uncompressed by the installer as needed. This feature is only available on Windows with NSIS at the moment. - NSIS installers can install themselves and enable the "Change" button in Add/Remove programs, allowing users to go back and install or remove components. This can be disabled through cpack_configure_downloads, because it's only really useful is most of the application's functionality is in downloaded components. - Bug fix: automatically install everything whose COMPONENT was not specified (it's a hidden, required group) - Bug fix: fixed removal of components when re-running the NSIS installer and unchecking components - Bug fix: NSIS installers now only install/remove the minimal number of files when re-run to update the installation (or by clicking "Change" in Add/Remove programs) --- Modules/CPack.cmake | 612 +++++++++++++++++++++++++- Modules/NSIS.template.in | 77 +++- Source/CPack/cmCPackComponentGroup.h | 16 + Source/CPack/cmCPackGenerator.cxx | 37 ++ Source/CPack/cmCPackNSISGenerator.cxx | 241 +++++++++- Source/CPack/cmCPackNSISGenerator.h | 18 +- Source/CPack/cmCPackPackageMakerGenerator.cxx | 12 +- Source/cmGetCMakePropertyCommand.cxx | 16 + Source/cmGetCMakePropertyCommand.h | 3 +- Source/cmGlobalGenerator.h | 3 + Source/cmInstallCommand.cxx | 77 +++- Source/cmInstallFilesCommand.cxx | 5 +- Source/cmInstallProgramsCommand.cxx | 5 +- Source/cmInstallTargetGenerator.h | 2 +- Source/cmInstallTargetsCommand.cxx | 3 + 15 files changed, 1076 insertions(+), 51 deletions(-) diff --git a/Modules/CPack.cmake b/Modules/CPack.cmake index 9782457..c8f7029 100644 --- a/Modules/CPack.cmake +++ b/Modules/CPack.cmake @@ -1,6 +1,370 @@ -# Default output files will be CPackConfig.cmake and CPackSourceConfig.cmake. -# This can be overwritten with CPACK_OUTPUT_CONFIG_FILE and -# CPACK_SOURCE_OUTPUT_CONFIG_FILE. +# - Build binary and source package installers +# +# The CPack module generates binary and source installers in a variety +# of formats using the cpack program. Inclusion of the CPack module +# adds two new targets to the resulting makefiles, package and +# package_source, which build the binary and source installers, +# respectively. The generated binary installers contain everything +# installed via CMake's INSTALL command (and the deprecated +# INSTALL_FILES, INSTALL_PROGRAMS, and INSTALL_TARGETS commands). +# +# For certain kinds of binary installers (including the graphical +# installers on Mac OS X and Windows), CPack generates installers that +# allow users to select individual application components to +# install. The contents of each of the components are identified by +# the COMPONENT argument of CMake's INSTALL command. These components +# can be annotated with user-friendly names and descriptions, +# inter-component dependencies, etc., and grouped in various ways to +# customize the resulting installer. See the cpack_add_* commands, +# described below, for more information about component-specific +# installations. +# +# Before including the CPack module, there are a variety of variables +# that can be set to customize the resulting installers. The most +# commonly-used variables are: +# +# CPACK_PACKAGE_NAME - The name of the package (or application). If +# not specified, defaults to the project name. +# +# CPACK_PACKAGE_VENDOR - The name of the package vendor (e.g., +# "Kitware"). +# +# CPACK_PACKAGE_VERSION_MAJOR - Package major Version +# +# CPACK_PACKAGE_VERSION_MINOR - Package minor Version +# +# CPACK_PACKAGE_VERSION_PATCH - Package patch Version +# +# CPACK_PACKAGE_DESCRIPTION_FILE - A text file used to describe the +# project. Used, for example, the introduction screen of a +# CPack-generated Windows installer to describe the project. +# +# CPACK_PACKAGE_DESCRIPTION_SUMMARY - Short description of the +# project (only a few words). +# +# CPACK_PACKAGE_FILE_NAME - The name of the package file to generate, +# not including the extension. For example, cmake-2.6.1-Linux-i686. +# +# CPACK_PACKAGE_INSTALL_DIRECTORY - Installation directory on the +# target system, e.g., "CMake 2.5". +# +# CPACK_RESOURCE_FILE_LICENSE - License file for the project, which +# will typically be displayed to the user (often with an explicit +# "Accept" button, for graphical installers) prior to installation. +# +# CPACK_RESOURCE_FILE_README - ReadMe file for the project, which +# typically describes in some detail +# +# CPACK_RESOURCE_FILE_WELCOME - Welcome file for the project, which +# welcomes users to this installer. Typically used in the graphical +# installers on Windows and Mac OS X. +# +# CPACK_MONOLITHIC_INSTALL - Disables the component-based +# installation mechanism, so that all components are always installed. +# +# CPACK_GENERATOR - List of CPack generators to use. If not +# specified, CPack will create a set of options (e.g., +# CPACK_BINARY_NSIS) allowing the user to enable/disable individual +# generators. +# +# CPACK_OUTPUT_CONFIG_FILE - The name of the CPack configuration file +# for binary installers that will be generated by the CPack +# module. Defaults to CPackConfig.cmake. +# +# CPACK_PACKAGE_EXECUTABLES - Lists each of the executables along +# with a text label, to be used to create Start Menu shortcuts on +# Windows. For example, setting this to the list ccmake;CMake will +# create a shortcut named "CMake" that will execute the installed +# executable ccmake. +# +# CPACK_STRIP_FILES - List of files to be stripped. Starting with +# CMake 2.6.0 CPACK_STRIP_FILES will be a boolean variable which +# enables stripping of all files (a list of files evaluates to TRUE +# in CMake, so this change is compatible). +# +# The following CPack variables are specific to source packages, and +# will not affect binary packages: +# +# CPACK_SOURCE_PACKAGE_FILE_NAME - The name of the source package, +# e.g., cmake-2.6.1 +# +# CPACK_SOURCE_STRIP_FILES - List of files in the source tree that +# will be stripped. Starting with CMake 2.6.0 +# CPACK_SOURCE_STRIP_FILES will be a boolean variable which enables +# stripping of all files (a list of files evaluates to TRUE in CMake, +# so this change is compatible). +# +# CPACK_SOURCE_GENERATOR - List of generators used for the source +# packages. As with CPACK_GENERATOR, if this is not specified then +# CPack will create a set of options (e.g., CPACK_SOURCE_ZIP) +# allowing users to select which packages will be generated. +# +# CPACK_SOURCE_OUTPUT_CONFIG_FILE - The name of the CPack +# configuration file for source installers that will be generated by +# the CPack module. Defaults to CPackSourceConfig.cmake. +# +# CPACK_SOURCE_IGNORE_FILES - Pattern of files in the source tree +# that won't be packaged when building a source package. This is a +# list of patterns, e.g., /CVS/;/\\.svn/;\\.swp$;\\.#;/#;.*~;cscope.* +# +# The following variables are specific to the graphical installers build +# on Windows using the Nullsoft Installation System. +# +# CPACK_PACKAGE_INSTALL_REGISTRY_KEY - Registry key used when +# installing this project. +# +# CPACK_NSIS_MUI_ICON - The icon file (.ico) for the generated +# install program. +# +# CPACK_NSIS_MUI_UNIICON - The icon file (.ico) for the generated +# uninstall program. +# +# CPACK_PACKAGE_ICON - A branding image that will be displayed inside +# the installer. +# +# CPACK_NSIS_EXTRA_INSTALL_COMMANDS - Extra NSIS commands that will +# be added to the install Section. +# +# CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS - Extra NSIS commands that will +# be added to the uninstall Section. +# +# CPACK_NSIS_COMPRESSOR - The arguments that will be passed to the +# NSIS SetCompressor command. +# +# CPACK_NSIS_MODIFY_PATH - If this is set to "ON", then an extra page +# will appear in the installer that will allow the user to choose +# whether the program directory should be added to the system PATH +# variable. +# +# CPACK_NSIS_DISPLAY_NAME - The title displayed at the top of the +# installer. +# +# CPACK_NSIS_INSTALLED_ICON_NAME - A path to the executable that +# contains the installer icon. +# +# CPACK_NSIS_HELP_LINK - URL to a web site providing assistance in +# installing your application. +# +# CPACK_NSIS_URL_INFO_ABOUT - URL to a web site providing more +# information about your application. +# +# CPACK_NSIS_CONTACT - Contact information for questions and comments +# about the installation process. +# +# CPACK_NSIS_CREATE_ICONS_EXTRA - Additional NSIS commands for +# creating start menu shortcuts. +# +# CPACK_NSIS_DELETE_ICONS_EXTRA -Additional NSIS commands to +# uninstall start menu shortcuts. +# +# The following variables are for advanced uses of CPack: +# +# CPACK_CMAKE_GENERATOR - What CMake generator should be used if the +# project is CMake project. Defaults to the value of CMAKE_GENERATOR; +# few users will want to change this setting. +# +# CPACK_INSTALL_CMAKE_PROJECTS - List of four values that specify +# what project to install. The four values are: Build directory, +# Project Name, Project Component, Directory. If omitted, CPack will +# build an installer that installers everything. +# +# CPACK_SYSTEM_NAME - System name, defaults to the value of +# ${CMAKE_SYSTEM_NAME}. +# +# CPACK_PACKAGE_VERSION - Package full version, used internally. By +# default, this is built from CPACK_PACKAGE_VERSION_MAJOR, +# CPACK_PACKAGE_VERSION_MINOR, and CPACK_PACKAGE_VERSION_PATCH. +# +# CPACK_TOPLEVEL_TAG - Directory for the installed files. +# +# CPACK_INSTALL_COMMANDS - Extra commands to install components. +# +# CPACK_INSTALL_DIRECTORIES - Extra directories to install. +# +# Component-specific installation allows users to select specific sets +# of components to install during the install process. Installation +# components are identified by the COMPONENT argument of CMake's +# INSTALL commands, and should be further described by the following +# CPack commands: +# +# cpack_add_component - Describes a CPack installation component +# named by the COMPONENT argument to a CMake INSTALL command. +# +# cpack_add_component(compname +# [DISPLAY_NAME name] +# [DESCRIPTION description] +# [HIDDEN | REQUIRED | DISABLED ] +# [GROUP group] +# [DEPENDS comp1 comp2 ... ] +# [INSTALL_TYPES type1 type2 ... ] +# [DOWNLOADED] +# [ARCHIVE_FILE filename]) +# +# The cmake_add_component command describes an installation +# component, which the user can opt to install or remove as part of +# the graphical installation process. compname is the name of the +# component, as provided to the COMPONENT argument of one or more +# CMake INSTALL commands. +# +# DISPLAY_NAME is the displayed name of the component, used in +# graphical installers to display the component name. This value can +# be any string. +# +# DESCRIPTION is an extended description of the component, used in +# graphical installers to give the user additional information about +# the component. Descriptions can span multiple lines using "\n" as +# the line separator. Typically, these descriptions should be no +# more than a few lines long. +# +# HIDDEN indicates that this component will be hidden in the +# graphical installer, so that the user cannot directly change +# whether it is installed or not. +# +# REQUIRED indicates that this component is required, and therefore +# will always be installed. It will be visible in the graphical +# installer, but it cannot be unselected. (Typically, required +# components are shown greyed out). +# +# DISABLED indicates that this component should be disabled +# (unselected) by default. The user is free to select this component +# for installation, unless it is also HIDDEN. +# +# DEPENDS lists the components on which this component depends. If +# this component is selected, then each of the components listed +# must also be selected. The dependency information is encoded +# within the installer itself, so that users cannot install +# inconsitent sets of components. +# +# GROUP names the component group of which this component is a +# part. If not provided, the component will be a standalone +# component, not part of any component group. Component groups are +# described with the cpack_add_component_group command, detailed +# below. +# +# INSTALL_TYPES lists the installation types of which this component +# is a part. When one of these installations types is selected, this +# component will automatically be selected. Installation types are +# described with the cpack_add_install_type command, detailed below. +# +# DOWNLOADED indicates that this component should be downloaded +# on-the-fly by the installer, rather than packaged in with the +# installer itself. For more information, see the cpack_configure_downloads +# command. +# +# ARCHIVE_FILE provides a name for the ZIP file created by CPack to +# be used for downloaded components. If not supplied, CPack will +# create ZIP file with some name based on CPACK_PACKAGE_FILE_NAME and +# the name of the component. See cpack_configure_downloads for more +# information. +# +# cpack_add_component_group - Describes a group of related CPack +# installation components. +# +# cpack_add_component_group(groupname +# [DISPLAY_NAME name] +# [DESCRIPTION description] +# [PARENT_GROUP parent] +# [EXPANDED] +# [BOLD_TITLE]) +# +# The cpack_add_component_group describes a group of installation +# components, which will be placed together within the listing of +# options. Typically, component groups allow the user to +# select/deselect all of the components within a single group via a +# single group-level option. Use component groups to reduce the +# complexity of installers with many options. groupname is an +# arbitrary name used to identify the group in the GROUP argument of +# the cpack_add_component command, which is used to place a +# component in a group. The name of the group must not conflict with +# the name of any component. +# +# DISPLAY_NAME is the displayed name of the component group, used in +# graphical installers to display the component group name. This +# value can be any string. +# +# DESCRIPTION is an extended description of the component group, +# used in graphical installers to give the user additional +# information about the components within that group. Descriptions +# can span multiple lines using "\n" as the line +# separator. Typically, these descriptions should be no more than a +# few lines long. +# +# PARENT_GROUP, if supplied, names the parent group of this group. +# Parent groups are used to establish a hierarchy of groups, +# providing an arbitrary hierarchy of groups. +# +# EXPANDED indicates that, by default, the group should show up as +# "expanded", so that the user immediately sees all of the +# components within the group. Otherwise, the group will initially +# show up as a single entry. +# +# BOLD_TITLE indicates that the group title should appear in bold, +# to call the user's attention to the group. +# +# cpack_add_install_type - Add a new installation type containing a +# set of predefined component selections to the graphical installer. +# +# cpack_add_install_type(typename +# [DISPLAY_NAME name]) +# +# The cpack_add_install_type command identifies a set of preselected +# components that represents a common use case for an +# application. For example, a "Developer" install type might include +# an application along with its header and library files, while an +# "End user" install type might just include the application's +# executable. Each component identifies itself with one or more +# install types via the INSTALL_TYPES argument to +# cpack_add_component. +# +# DISPLAY_NAME is the displayed name of the install type, which will +# typically show up in a drop-down box within a graphical +# installer. This value can be any string. +# +# cpack_configure_downloads - Configure CPack to download selected +# components on-the-fly as part of the installation process. +# +# cpack_configure_downloads(site +# [UPLOAD_DIRECTORY dirname] +# [ALL] +# [ADD_REMOVE|NO_ADD_REMOVE]) +# +# The cpack_configure_downloads command configures installation-time +# downloads of selected components. For each downloadable component, +# CPack will create a ZIP file containing the contents of that component, +# which should be uploaded to the given site. When the user selects that +# component for installation, the installer will download and extract +# the component in place. This feature is useful for creating small +# installers that only download the requested components, saving +# bandwidth. Additionally, the installers are small enough that they +# will be installed as part of the normal installation process, and the +# "Change" button in Windows Add/Remove Programs control panel will +# allow one to add or remove parts of the application after the original +# installation. The downloaded-components functionality is currently +# only available with the NSIS generator on Windows. It requires the +# ZipDLL plug-in for NSIS, available at: +# +# http://nsis.sourceforge.net/ZipDLL_plug-in +# +# The site argument is a URL where the ZIP files for downloadable +# components will reside, e.g., http://www.cmake.org/files/2.6.1/installer/ +# All of the ZIP files produced by CPack should be uploaded to that location. +# +# UPLOAD_DIRECTORY is the local directory where CPack will create the +# various .ZIP archives for each of the components. The contents of this +# directory should be uploaded to a location accessible by the URL given +# in the site argument. If omitted, CPack will use the directory CPackUploads +# inside the CMake binary directory to store the generated ZIP files. +# +# The ALL flag indicates that all components be downloaded. Otherwise, only +# those components explicitly marked as DOWNLOADED or that have a specified +# ARCHIVE_FILE will be downloaded. Additionally, the ALL option implies +# ADD_REMOVE (unless NO_ADD_REMOVE is specified). +# +# ADD_REMOVE indicates that CPack should install a copy of the installer +# that can be called from Windows' Add/Remove Programs dialog (via the +# "Modify" button) to change the set of installed components. NO_ADD_REMOVE +# turns off this behavior. + # Pick a configuration file SET(cpack_input_file "${CMAKE_ROOT}/Templates/CPackConfig.cmake.in") @@ -12,6 +376,215 @@ IF(EXISTS "${CMAKE_SOURCE_DIR}/CPackSourceConfig.cmake.in") SET(cpack_source_input_file "${CMAKE_SOURCE_DIR}/CPackSourceConfig.cmake.in") ENDIF(EXISTS "${CMAKE_SOURCE_DIR}/CPackSourceConfig.cmake.in") +# Argument-parsing macro from http://www.cmake.org/Wiki/CMakeMacroParseArguments +MACRO(cpack_parse_arguments prefix arg_names option_names) + SET(${prefix}_DEFAULT_ARGS) + FOREACH(arg_name ${arg_names}) + SET(${prefix}_${arg_name}) + ENDFOREACH(arg_name) + FOREACH(option ${option_names}) + SET(${prefix}_${option} FALSE) + ENDFOREACH(option) + + SET(current_arg_name DEFAULT_ARGS) + SET(current_arg_list) + FOREACH(arg ${ARGN}) + SET(larg_names ${arg_names}) + LIST(FIND larg_names "${arg}" is_arg_name) + IF (is_arg_name GREATER -1) + SET(${prefix}_${current_arg_name} ${current_arg_list}) + SET(current_arg_name ${arg}) + SET(current_arg_list) + ELSE (is_arg_name GREATER -1) + SET(loption_names ${option_names}) + LIST(FIND loption_names "${arg}" is_option) + IF (is_option GREATER -1) + SET(${prefix}_${arg} TRUE) + ELSE (is_option GREATER -1) + SET(current_arg_list ${current_arg_list} ${arg}) + ENDIF (is_option GREATER -1) + ENDIF (is_arg_name GREATER -1) + ENDFOREACH(arg) + SET(${prefix}_${current_arg_name} ${current_arg_list}) +ENDMACRO(cpack_parse_arguments) + +# Macro that appends a SET command for the given variable name (var) +# to the macro named strvar, but only if the variable named "var" +# has been defined. The string will eventually be appended to a CPack +# configuration file. +MACRO(cpack_append_variable_set_command var strvar) + IF (DEFINED ${var}) + SET(${strvar} "${${strvar}}SET(${var}") + FOREACH(APPENDVAL ${${var}}) + SET(${strvar} "${${strvar}} ${APPENDVAL}") + ENDFOREACH(APPENDVAL) + SET(${strvar} "${${strvar}})\n") + ENDIF (DEFINED ${var}) +ENDMACRO(cpack_append_variable_set_command) + +# Macro that appends a SET command for the given variable name (var) +# to the macro named strvar, but only if the variable named "var" +# has been defined and is a string. The string will eventually be +# appended to a CPack configuration file. +MACRO(cpack_append_string_variable_set_command var strvar) + IF (DEFINED ${var}) + LIST(LENGTH ${var} CPACK_APP_VALUE_LEN) + IF(${CPACK_APP_VALUE_LEN} EQUAL 1) + SET(${strvar} "${${strvar}}SET(${var} \"${${var}}\")\n") + ENDIF(${CPACK_APP_VALUE_LEN} EQUAL 1) + ENDIF (DEFINED ${var}) +ENDMACRO(cpack_append_string_variable_set_command) + +# Macro that appends a SET command for the given variable name (var) +# to the macro named strvar, but only if the variable named "var" +# has been set to true. The string will eventually be +# appended to a CPack configuration file. +MACRO(cpack_append_option_set_command var strvar) + IF (${var}) + LIST(LENGTH ${var} CPACK_APP_VALUE_LEN) + IF(${CPACK_APP_VALUE_LEN} EQUAL 1) + SET(${strvar} "${${strvar}}SET(${var} TRUE)\n") + ENDIF(${CPACK_APP_VALUE_LEN} EQUAL 1) + ENDIF (${var}) +ENDMACRO(cpack_append_option_set_command) + +# Macro that adds a component to the CPack installer +MACRO(cpack_add_component compname) + STRING(TOUPPER ${compname} CPACK_ADDCOMP_UNAME) + cpack_parse_arguments(CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME} + "DISPLAY_NAME;DESCRIPTION;GROUP;DEPENDS;INSTALL_TYPES;ARCHIVE_FILE" + "HIDDEN;REQUIRED;DISABLED;DOWNLOADED" + ${ARGN} + ) + + if (CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_DOWNLOADED) + SET(CPACK_ADDCOMP_STR "\n# Configuration for downloaded component \"${compname}\"\n") + else () + SET(CPACK_ADDCOMP_STR "\n# Configuration for component \"${compname}\"\n") + endif () + + IF(NOT CPACK_MONOLITHIC_INSTALL) + # If the user didn't set CPACK_COMPONENTS_ALL explicitly, update the + # value of CPACK_COMPONENTS_ALL in the configuration file. This will + # take care of any components that have been added after the CPack + # moduled was included. + IF(NOT CPACK_COMPONENTS_ALL_SET_BY_USER) + GET_CMAKE_PROPERTY(CPACK_ADDCOMP_COMPONENTS COMPONENTS) + SET(CPACK_ADDCOMP_STR "${CPACK_ADDCOMP_STR}\nSET(CPACK_COMPONENTS_ALL") + FOREACH(COMP ${CPACK_ADDCOMP_COMPONENTS}) + SET(CPACK_ADDCOMP_STR "${CPACK_ADDCOMP_STR} ${COMP}") + ENDFOREACH(COMP) + SET(CPACK_ADDCOMP_STR "${CPACK_ADDCOMP_STR})\n") + ENDIF(NOT CPACK_COMPONENTS_ALL_SET_BY_USER) + ENDIF(NOT CPACK_MONOLITHIC_INSTALL) + + cpack_append_string_variable_set_command( + CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_DISPLAY_NAME + CPACK_ADDCOMP_STR) + cpack_append_string_variable_set_command( + CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_DESCRIPTION + CPACK_ADDCOMP_STR) + cpack_append_variable_set_command( + CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_GROUP + CPACK_ADDCOMP_STR) + cpack_append_variable_set_command( + CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_DEPENDS + CPACK_ADDCOMP_STR) + cpack_append_variable_set_command( + CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_INSTALL_TYPES + CPACK_ADDCOMP_STR) + cpack_append_string_variable_set_command( + CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_ARCHIVE_FILE + CPACK_ADDCOMP_STR) + cpack_append_option_set_command( + CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_HIDDEN + CPACK_ADDCOMP_STR) + cpack_append_option_set_command( + CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_REQUIRED + CPACK_ADDCOMP_STR) + cpack_append_option_set_command( + CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_DISABLED + CPACK_ADDCOMP_STR) + cpack_append_option_set_command( + CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_DOWNLOADED + CPACK_ADDCOMP_STR) + FILE(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" "${CPACK_ADDCOMP_STR}") +ENDMACRO(cpack_add_component) + +# Macro that adds a component group to the CPack installer +MACRO(cpack_add_component_group grpname) + STRING(TOUPPER ${grpname} CPACK_ADDGRP_UNAME) + cpack_parse_arguments(CPACK_COMPONENT_GROUP_${CPACK_ADDGRP_UNAME} + "DISPLAY_NAME;DESCRIPTION" + "EXPANDED;BOLD_TITLE" + ${ARGN} + ) + + SET(CPACK_ADDGRP_STR "\n# Configuration for component group \"${grpname}\"\n") + cpack_append_string_variable_set_command( + CPACK_COMPONENT_GROUP_${CPACK_ADDGRP_UNAME}_DISPLAY_NAME + CPACK_ADDGRP_STR) + cpack_append_string_variable_set_command( + CPACK_COMPONENT_GROUP_${CPACK_ADDGRP_UNAME}_DESCRIPTION + CPACK_ADDGRP_STR) + cpack_append_option_set_command( + CPACK_COMPONENT_GROUP_${CPACK_ADDGRP_UNAME}_EXPANDED + CPACK_ADDGRP_STR) + cpack_append_option_set_command( + CPACK_COMPONENT_GROUP_${CPACK_ADDGRP_UNAME}_BOLD_TITLE + CPACK_ADDGRP_STR) + FILE(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" "${CPACK_ADDGRP_STR}") +ENDMACRO(cpack_add_component_group) + +# Macro that adds an installation type to the CPack installer +MACRO(cpack_add_install_type insttype) + STRING(TOUPPER ${insttype} CPACK_INSTTYPE_UNAME) + cpack_parse_arguments(CPACK_INSTALL_TYPE_${CPACK_INSTTYPE_UNAME} + "DISPLAY_NAME" + "" + ${ARGN} + ) + + SET(CPACK_INSTTYPE_STR + "\n# Configuration for installation type \"${insttype}\"\n") + SET(CPACK_INSTTYPE_STR + "${CPACK_INSTTYPE_STR}LIST(APPEND CPACK_ALL_INSTALL_TYPES ${insttype})\n") + cpack_append_string_variable_set_command( + CPACK_INSTALL_TYPE_${CPACK_INSTTYPE_UNAME}_DISPLAY_NAME + CPACK_INSTTYPE_STR) + FILE(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" "${CPACK_INSTTYPE_STR}") +ENDMACRO(cpack_add_install_type) + +MACRO(cpack_configure_downloads site) + cpack_parse_arguments(CPACK_DOWNLOAD + "UPLOAD_DIRECTORY" + "ALL;ADD_REMOVE;NO_ADD_REMOVE" + ${ARGN} + ) + + SET(CPACK_CONFIG_DL_STR + "\n# Downloaded components configuration\n") + SET(CPACK_UPLOAD_DIRECTORY ${CPACK_DOWNLOAD_UPLOAD_DIRECTORY}) + SET(CPACK_DOWNLOAD_SITE ${site}) + cpack_append_string_variable_set_command( + CPACK_DOWNLOAD_SITE + CPACK_CONFIG_DL_STR) + cpack_append_string_variable_set_command( + CPACK_UPLOAD_DIRECTORY + CPACK_CONFIG_DL_STR) + cpack_append_option_set_command( + CPACK_DOWNLOAD_ALL + CPACK_CONFIG_DL_STR) + IF (${CPACK_DOWNLOAD_ALL} AND NOT ${CPACK_DOWNLOAD_NO_ADD_REMOVE}) + SET(CPACK_DOWNLOAD_ADD_REMOVE ON) + ENDIF (${CPACK_DOWNLOAD_ALL} AND NOT ${CPACK_DOWNLOAD_NO_ADD_REMOVE}) + SET(CPACK_ADD_REMOVE ${CPACK_DOWNLOAD_ADD_REMOVE}) + cpack_append_option_set_command( + CPACK_ADD_REMOVE + CPACK_CONFIG_DL_STR) + FILE(APPEND "${CPACK_OUTPUT_CONFIG_FILE}" "${CPACK_CONFIG_DL_STR}") +ENDMACRO(cpack_configure_downloads) + # Macro for setting values if a user did not overwrite them MACRO(cpack_set_if_not_set name value) IF(NOT DEFINED "${name}") @@ -197,6 +770,39 @@ cpack_set_if_not_set(CPACK_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") cpack_set_if_not_set(CPACK_NSIS_INSTALLER_ICON_CODE "") cpack_set_if_not_set(CPACK_NSIS_INSTALLER_MUI_ICON_CODE "") +IF(DEFINED CPACK_COMPONENTS_ALL) + IF(CPACK_MONOLITHIC_INSTALL) + MESSAGE("CPack warning: both CPACK_COMPONENTS_ALL and CPACK_MONOLITHIC_INSTALL have been set.\nDefaulting to a monolithic installation.") + SET(CPACK_COMPONENTS_ALL) + ELSE(CPACK_MONOLITHIC_INSTALL) + # The user has provided the set of components to be installed as + # part of a component-based installation; trust her. + SET(CPACK_COMPONENTS_ALL_SET_BY_USER TRUE) + ENDIF(CPACK_MONOLITHIC_INSTALL) +ELSE(DEFINED CPACK_COMPONENTS_ALL) + # If the user has not specifically requested a monolithic installer + # but has specified components in various "install" commands, tell + # CPack about those components. + IF(NOT CPACK_MONOLITHIC_INSTALL) + GET_CMAKE_PROPERTY(CPACK_COMPONENTS_ALL COMPONENTS) + LIST(LENGTH CPACK_COMPONENTS_ALL CPACK_COMPONENTS_LEN) + IF(CPACK_COMPONENTS_LEN EQUAL 1) + # Only one component: this is not a component-based installation + # (at least, it isn't a component-based installation, but may + # become one later if the user uses the cpack_add_* commands). + SET(CPACK_COMPONENTS_ALL) + ENDIF(CPACK_COMPONENTS_LEN EQUAL 1) + SET(CPACK_COMPONENTS_LEN) + ENDIF(NOT CPACK_MONOLITHIC_INSTALL) +ENDIF(DEFINED CPACK_COMPONENTS_ALL) + +# CMake always generates a component named "Unspecified", which is +# used to install everything that doesn't have an explicitly-provided +# component. Since these files should always be installed, we'll make +# them hidden and required. +set(CPACK_COMPONENT_UNSPECIFIED_HIDDEN TRUE) +set(CPACK_COMPONENT_UNSPECIFIED_REQUIRED TRUE) + cpack_encode_variables() configure_file("${cpack_input_file}" "${CPACK_OUTPUT_CONFIG_FILE}" @ONLY IMMEDIATE) diff --git a/Modules/NSIS.template.in b/Modules/NSIS.template.in index 8879a7f..96598d9 100644 --- a/Modules/NSIS.template.in +++ b/Modules/NSIS.template.in @@ -37,6 +37,8 @@ ;Set compression SetCompressor @CPACK_NSIS_COMPRESSOR@ +@CPACK_NSIS_DEFINES@ + !include Sections.nsh ;--- Component support macros: --- @@ -60,6 +62,11 @@ Var AR_RegFlags IntOp $R0 0 + $${VarName} !macroend +; Sets the value of a variable +!macro StoreVar VarName IntValue + IntOp $${VarName} 0 + ${IntValue} +!macroend + !macro InitSection SecName ; This macro reads component installed flag from the registry and ;changes checked state of the section on the components page. @@ -76,9 +83,13 @@ Var AR_RegFlags IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit + ; Note whether this component was installed before + !insertmacro StoreVar ${SecName}_was_installed $AR_RegFlags + IntOp $R0 $AR_RegFlags & $AR_RegFlags + ;Writing modified flags SectionSetFlags ${${SecName}} $AR_SecFlags - + "default_${SecName}:" !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected !macroend @@ -461,6 +472,33 @@ Function ConditionalAddToRegisty FunctionEnd ;-------------------------------- + +!ifdef CPACK_USES_DOWNLOAD +Function DownloadFile + IfFileExists $INSTDIR\* +2 + CreateDirectory $INSTDIR + Pop $0 + + ; Skip if already downloaded + IfFileExists $INSTDIR\$0 0 +2 + Return + + StrCpy $1 "@CPACK_DOWNLOAD_SITE@" + + try_again: + NSISdl::download "$1/$0" "$INSTDIR\$0" + + Pop $1 + StrCmp $1 "success" success + StrCmp $1 "Cancelled" cancel + MessageBox MB_OK "Download failed: $1" + cancel: + Return + success: +FunctionEnd +!endif + +;-------------------------------- ; Installation types @CPACK_NSIS_INSTALLATION_TYPES@ @@ -519,7 +557,7 @@ Section "-Core installation" ;list of directories here in sync with the RMDir commands below. SetOutPath "$INSTDIR" @CPACK_NSIS_FULL_INSTALL@ - + ;Store installation folder WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR @@ -537,7 +575,20 @@ Section "-Core installation" Push "UninstallString" Push "$INSTDIR\Uninstall.exe" Call ConditionalAddToRegisty + Push "NoRepair" + Push "1" + Call ConditionalAddToRegisty + + !ifdef CPACK_NSIS_ADD_REMOVE + ;Create add/remove functionality Push "ModifyPath" + Push "$INSTDIR\AddRemove.exe" + Call ConditionalAddToRegisty + !else + Push "NoModify" + Push "1" + Call ConditionalAddToRegisty + !endif ; Optional registration Push "DisplayIcon" @@ -640,6 +691,23 @@ FunctionEnd Section -FinishComponents ;Removes unselected components and writes component status to registry !insertmacro SectionList "FinishSection" + +!ifdef CPACK_NSIS_ADD_REMOVE + ; Get the name of the installer executable + System::Call 'kernel32::GetModuleFileNameA(i 0, t .R0, i 1024) i r1' + StrCpy $R3 $R0 + + ; Strip off the last 13 characters, to see if we have AddRemove.exe + StrLen $R1 $R0 + IntOp $R1 $R0 - 13 + StrCpy $R2 $R0 13 $R1 + StrCmp $R2 "AddRemove.exe" addremove_installed + + ; We're not running AddRemove.exe, so install it + CopyFiles $R3 $INSTDIR\AddRemove.exe + + addremove_installed: +!endif SectionEnd ;--- End of Add/Remove callback functions --- @@ -674,6 +742,11 @@ Section "Uninstall" @CPACK_NSIS_DELETE_FILES@ @CPACK_NSIS_DELETE_DIRECTORIES@ +!ifdef CPACK_NSIS_ADD_REMOVE + ;Remove the add/remove program + Delete "$INSTDIR\AddRemove.exe" +!endif + ;Remove the uninstaller itself. Delete "$INSTDIR\Uninstall.exe" DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_DIRECTORY@" diff --git a/Source/CPack/cmCPackComponentGroup.h b/Source/CPack/cmCPackComponentGroup.h index 2e1703c..d080a87 100644 --- a/Source/CPack/cmCPackComponentGroup.h +++ b/Source/CPack/cmCPackComponentGroup.h @@ -70,12 +70,20 @@ public: /// Whether this component defaults to "disabled". bool IsDisabledByDefault : 1; + /// Whether this component should be downloaded on-the-fly. If false, + /// the component will be a part of the installation package. + bool IsDownloaded : 1; + /// A description of this component. std::string Description; /// The installation types that this component is a part of. std::vector InstallationTypes; + /// If IsDownloaded is true, the name of the archive file that + /// contains the files that are part of this component. + std::string ArchiveFile; + /// The components that this component depends on. std::vector Dependencies; @@ -95,6 +103,8 @@ public: class cmCPackComponentGroup { public: + cmCPackComponentGroup() : ParentGroup(0) { } + /// The name of the group (used to reference the group). std::string Name; @@ -112,6 +122,12 @@ public: /// The components within this group. std::vector Components; + + /// The parent group of this component group (if any). + cmCPackComponentGroup *ParentGroup; + + /// The subgroups of this group. + std::vector Subgroups; }; #endif diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx index f786f80..5090606 100644 --- a/Source/CPack/cmCPackGenerator.cxx +++ b/Source/CPack/cmCPackGenerator.cxx @@ -653,6 +653,14 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects( } mf->AddDefinition("CMAKE_INSTALL_PREFIX", dir.c_str()); + if ( !cmsys::SystemTools::MakeDirectory(dir.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating temporary directory: " + << dir << std::endl); + return 0; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "- Using DESTDIR + CPACK_INSTALL_PREFIX... (mf->AddDefinition)" << std::endl); @@ -664,6 +672,14 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects( { mf->AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory.c_str()); + if ( !cmsys::SystemTools::MakeDirectory(tempInstallDirectory.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating temporary directory: " + << tempInstallDirectory << std::endl); + return 0; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "- Using non-DESTDIR install... (mf->AddDefinition)" << std::endl); cmCPackLogger(cmCPackLog::LOG_DEBUG, @@ -1255,6 +1271,16 @@ cmCPackGenerator::GetComponent(const char *projectName, const char *name) = this->IsSet((macroPrefix + "_REQUIRED").c_str()); component->IsDisabledByDefault = this->IsSet((macroPrefix + "_DISABLED").c_str()); + component->IsDownloaded + = this->IsSet((macroPrefix + "_DOWNLOADED").c_str()) + || cmSystemTools::IsOn(this->GetOption("CPACK_DOWNLOAD_ALL")); + + const char* archiveFile = this->GetOption((macroPrefix + "_ARCHIVE_FILE").c_str()); + if (archiveFile && *archiveFile) + { + component->ArchiveFile = archiveFile; + } + const char* groupName = this->GetOption((macroPrefix + "_GROUP").c_str()); if (groupName && *groupName) { @@ -1344,6 +1370,17 @@ cmCPackGenerator::GetComponentGroup(const char *projectName, const char *name) = this->IsSet((macroPrefix + "_BOLD_TITLE").c_str()); group->IsExpandedByDefault = this->IsSet((macroPrefix + "_EXPANDED").c_str()); + const char* parentGroupName + = this->GetOption((macroPrefix + "_PARENT_GROUP").c_str()); + if (parentGroupName && *parentGroupName) + { + group->ParentGroup = GetComponentGroup(projectName, parentGroupName); + group->ParentGroup->Subgroups.push_back(group); + } + else + { + group->ParentGroup = 0; + } } return group; } diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx index cd4ffb8..7be5cb2 100644 --- a/Source/CPack/cmCPackNSISGenerator.cxx +++ b/Source/CPack/cmCPackNSISGenerator.cxx @@ -173,6 +173,9 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName, std::string componentDescriptions; std::string groupDescriptions; std::string installTypesCode; + std::string defines; + cmOStringStream macrosOut; + bool anyDownloadedComponents = false; // Create installation types. The order is significant, so we first fill // in a vector based on the indices, and print them in that order. @@ -201,7 +204,11 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName, groupIt != this->ComponentGroups.end(); ++groupIt) { - componentCode += this->CreateComponentGroupDescription(&groupIt->second); + if (groupIt->second.ParentGroup == 0) + { + componentCode += + this->CreateComponentGroupDescription(&groupIt->second, macrosOut); + } // Add the group description, if any. if (!groupIt->second.Description.empty()) @@ -218,9 +225,19 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName, compIt != this->Components.end(); ++compIt) { + if (compIt->second.Files.empty()) + { + // NSIS cannot cope with components that have no files. + continue; + } + + anyDownloadedComponents = + anyDownloadedComponents || compIt->second.IsDownloaded; + if (!compIt->second.Group) { - componentCode += this->CreateComponentDescription(&compIt->second); + componentCode + += this->CreateComponentDescription(&compIt->second, macrosOut); } // Add this component to the various section lists. @@ -228,6 +245,7 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName, sectionList += compIt->first; sectionList += "\"\n"; selectedVarsList += "Var " + compIt->first + "_selected\n"; + selectedVarsList += "Var " + compIt->first + "_was_installed\n"; // Add the component description, if any. if (!compIt->second.Description.empty()) @@ -238,6 +256,8 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName, } } + componentCode += macrosOut.str(); + if (componentDescriptions.empty() && groupDescriptions.empty()) { // Turn off the "Description" box @@ -254,12 +274,23 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName, this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", componentDescriptions.c_str()); } + + if (anyDownloadedComponents) + { + defines += "!define CPACK_USES_DOWNLOAD\n"; + if (cmSystemTools::IsOn(this->GetOption("CPACK_ADD_REMOVE"))) + { + defines += "!define CPACK_NSIS_ADD_REMOVE\n"; + } + } + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", installTypesCode.c_str()); this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", "!insertmacro MUI_PAGE_COMPONENTS"); this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL", ""); this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", componentCode.c_str()); this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", sectionList.c_str()); this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS", selectedVarsList.c_str()); + this->SetOption("CPACK_NSIS_DEFINES", defines.c_str()); } this->ConfigureFile(nsisInInstallOptions.c_str(), @@ -559,7 +590,7 @@ bool cmCPackNSISGenerator::SupportsComponentInstallation() const //---------------------------------------------------------------------- std::string cmCPackNSISGenerator:: -CreateComponentDescription(cmCPackComponent *component) const +CreateComponentDescription(cmCPackComponent *component, cmOStringStream& macrosOut) { // Basic description of the component std::string componentCode = "Section "; @@ -590,46 +621,204 @@ CreateComponentDescription(cmCPackComponent *component) const componentCode += " SectionIn" + out.str() + "\n"; } componentCode += " SetOutPath \"$INSTDIR\"\n"; - componentCode += " File /r \"${INST_DIR}\\" + component->Name + "\\*.*\"\n"; + + componentCode += " IntCmp $" + component->Name + + "_was_installed ${SF_SELECTED} noinstall_" + component->Name + "\n"; + + // Create the actual installation commands + if (component->IsDownloaded) + { + if (component->ArchiveFile.empty()) + { + // Compute the name of the archive. + std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + packagesDir += ".dummy"; + cmOStringStream out; + out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir) + << "-" << component->Name << ".zip"; + component->ArchiveFile = out.str(); + } + + // Create the directory for the upload area + const char* userUploadDirectory = this->GetOption("CPACK_UPLOAD_DIRECTORY"); + std::string uploadDirectory; + if (userUploadDirectory && *userUploadDirectory) + { + uploadDirectory = userUploadDirectory; + } + else + { + uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY"); + uploadDirectory += "/CPackUploads"; + } + if(!cmSystemTools::FileExists(uploadDirectory.c_str())) + { + if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Unable to create NSIS upload directory " << uploadDirectory + << std::endl); + return ""; + } + } + + // Remove the old archive, if one exists + std::string archiveFile = uploadDirectory + '/' + component->ArchiveFile; + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Building downloaded component archive: " + << archiveFile << std::endl); + if (cmSystemTools::FileExists(archiveFile.c_str(), true)) + { + if (!cmSystemTools::RemoveFile(archiveFile.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Unable to remove archive file " << archiveFile + << std::endl); + return ""; + } + } + + // Find a ZIP program + if (!this->IsSet("ZIP_EXECUTABLE")) + { + this->ReadListFile("CPackZIP.cmake"); + + if (!this->IsSet("ZIP_EXECUTABLE")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Unable to find ZIP program" + << std::endl); + return ""; + } + } + + // The directory where this component's files reside + std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + dirName += '/'; + dirName += component->Name; + dirName += '/'; + + // Build the list of files to go into this archive, and determine the + // size of the installed component. + std::string zipListFileName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + zipListFileName += "/winZip.filelist"; + bool needQuotesInFile + = cmSystemTools::IsOn(this->GetOption("CPACK_ZIP_NEED_QUOTES")); + unsigned long totalSize = 0; + { // the scope is needed for cmGeneratedFileStream + cmGeneratedFileStream out(zipListFileName.c_str()); + std::vector::iterator fileIt; + for (fileIt = component->Files.begin(); + fileIt != component->Files.end(); + ++fileIt) + { + if ( needQuotesInFile ) + { + out << "\""; + } + out << *fileIt; + if ( needQuotesInFile ) + { + out << "\""; + } + out << std::endl; + + totalSize += cmSystemTools::FileLength((dirName + *fileIt).c_str()); + } + } + + // Build the archive in the upload area + std::string cmd = this->GetOption("CPACK_ZIP_COMMAND"); + cmsys::SystemTools::ReplaceString(cmd, "", archiveFile.c_str()); + cmsys::SystemTools::ReplaceString(cmd, "", + zipListFileName.c_str()); + std::string output; + int retVal = -1; + int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &retVal, + dirName.c_str(), false, 0); + if ( !res || retVal ) + { + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/CompressZip.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << cmd.c_str() << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running zip command: " + << cmd.c_str() << std::endl + << "Please check " << tmpFile.c_str() << " for errors" << std::endl); + return ""; + } + + // Create the NSIS code to download this file on-the-fly. + unsigned totalSizeInKbytes = (totalSize + 512) / 1024; + if (totalSizeInKbytes == 0) + { + totalSizeInKbytes = 1; + } + cmOStringStream out; + out << " AddSize " << totalSizeInKbytes << "\n" + << " Push \"" << component->ArchiveFile << "\"\n" + << " Call DownloadFile\n" + << " ZipDLL::extractall \"$INSTDIR\\" + << component->ArchiveFile << "\" \"$INSTDIR\"\n" + << " Pop $2 ; error message\n" + " StrCmp $2 \"success\" +2 0\n" + " MessageBox MB_OK \"Failed to unzip $2\"\n" + " Delete $INSTDIR\\$0\n"; + componentCode += out.str(); + } + else + { + componentCode += " File /r \"${INST_DIR}\\" + component->Name + "\\*.*\"\n"; + } + componentCode += " noinstall_" + component->Name + ":\n"; componentCode += "SectionEnd\n"; // Macro used to remove the component - componentCode += "!macro Remove_${" + component->Name + "}\n"; + macrosOut << "!macro Remove_${" << component->Name << "}\n"; + macrosOut << " IntCmp $" << component->Name << "_was_installed 0 noremove_" + << component->Name << "\n"; std::vector::iterator pathIt; for (pathIt = component->Files.begin(); pathIt != component->Files.end(); ++pathIt) { - componentCode += " Delete \"$INSTDIR\\" + *pathIt + "\"\n"; + macrosOut << " Delete \"$INSTDIR\\" + << cmSystemTools::ConvertToWindowsOutputPath(pathIt->c_str()) + << "\"\n"; } for (pathIt = component->Directories.begin(); pathIt != component->Directories.end(); ++pathIt) { - componentCode += " RMDir \"$INSTDIR\\" + *pathIt + "\"\n"; + macrosOut << " RMDir \"$INSTDIR\\" + << cmSystemTools::ConvertToWindowsOutputPath(pathIt->c_str()) + << "\"\n"; } - componentCode += "!macroend\n"; + macrosOut << " noremove_" << component->Name << ":\n"; + macrosOut << "!macroend\n"; // Macro used to select each of the components that this component // depends on. std::set visited; - componentCode += "!macro Select_" + component->Name + "_depends\n"; - componentCode += CreateSelectionDependenciesDescription(component, visited); - componentCode += "!macroend\n"; + macrosOut << "!macro Select_" << component->Name << "_depends\n"; + macrosOut << CreateSelectionDependenciesDescription(component, visited); + macrosOut << "!macroend\n"; // Macro used to deselect each of the components that depend on this // component. visited.clear(); - componentCode += "!macro Deselect_required_by_" + component->Name + "\n"; - componentCode += CreateDeselectionDependenciesDescription(component, visited); - componentCode += "!macroend\n"; + macrosOut << "!macro Deselect_required_by_" << component->Name << "\n"; + macrosOut << CreateDeselectionDependenciesDescription(component, visited); + macrosOut << "!macroend\n"; return componentCode; } //---------------------------------------------------------------------- std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription (cmCPackComponent *component, - std::set& visited) const + std::set& visited) { // Don't visit a component twice if (visited.count(component)) @@ -660,7 +849,7 @@ std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription //---------------------------------------------------------------------- std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription (cmCPackComponent *component, - std::set& visited) const + std::set& visited) { // Don't visit a component twice if (visited.count(component)) @@ -692,9 +881,10 @@ std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription //---------------------------------------------------------------------- std::string cmCPackNSISGenerator:: -CreateComponentGroupDescription(cmCPackComponentGroup *group) const +CreateComponentGroupDescription(cmCPackComponentGroup *group, + cmOStringStream& macrosOut) { - if (group->Components.empty()) + if (group->Components.empty() && group->Subgroups.empty()) { // Silently skip empty groups. NSIS doesn't support them. return std::string(); @@ -713,12 +903,25 @@ CreateComponentGroupDescription(cmCPackComponentGroup *group) const { code += "\"" + group->DisplayName + "\" " + group->Name + "\n"; } + + std::vector::iterator groupIt; + for (groupIt = group->Subgroups.begin(); groupIt != group->Subgroups.end(); + ++groupIt) + { + code += this->CreateComponentGroupDescription(*groupIt, macrosOut); + } + std::vector::iterator comp; for (comp = group->Components.begin(); comp != group->Components.end(); ++comp) { - code += this->CreateComponentDescription(*comp); + if ((*comp)->Files.empty()) + { + continue; + } + + code += this->CreateComponentDescription(*comp, macrosOut); } code += "SectionGroupEnd\n"; return code; diff --git a/Source/CPack/cmCPackNSISGenerator.h b/Source/CPack/cmCPackNSISGenerator.h index 70068e41..5299a91 100644 --- a/Source/CPack/cmCPackNSISGenerator.h +++ b/Source/CPack/cmCPackNSISGenerator.h @@ -53,24 +53,30 @@ protected: virtual bool SupportsComponentInstallation() const; /// Produce a string that contains the NSIS code to describe a - /// particular component. - std::string CreateComponentDescription(cmCPackComponent *component) const; + /// particular component. Any added macros will be emitted via + /// macrosOut. + std::string + CreateComponentDescription(cmCPackComponent *component, + cmOStringStream& macrosOut); /// Produce NSIS code that selects all of the components that this component /// depends on, recursively. std::string CreateSelectionDependenciesDescription (cmCPackComponent *component, - std::set& visited) const; + std::set& visited); /// Produce NSIS code that de-selects all of the components that are dependent /// on this component, recursively. std::string CreateDeselectionDependenciesDescription (cmCPackComponent *component, - std::set& visited) const; + std::set& visited); /// Produce a string that contains the NSIS code to describe a - /// particular component group, including its components. - std::string CreateComponentGroupDescription(cmCPackComponentGroup *group) const; + /// particular component group, including its components. Any + /// added macros will be emitted via macrosOut. + std::string + CreateComponentGroupDescription(cmCPackComponentGroup *group, + cmOStringStream& macrosOut); /// Translations any newlines found in the string into \r\n, so that the /// resulting string can be used within NSIS. diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx index 04b7899..a156971 100644 --- a/Source/CPack/cmCPackPackageMakerGenerator.cxx +++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx @@ -580,7 +580,10 @@ WriteDistributionFile(const char* metapackageFile) groupIt != this->ComponentGroups.end(); ++groupIt) { - CreateChoiceOutline(groupIt->second, choiceOut); + if (groupIt->second.ParentGroup == 0) + { + CreateChoiceOutline(groupIt->second, choiceOut); + } } // Emit the outline for the non-grouped components @@ -622,6 +625,13 @@ cmCPackPackageMakerGenerator:: CreateChoiceOutline(const cmCPackComponentGroup& group, cmOStringStream& out) { out << "" << std::endl; + std::vector::const_iterator groupIt; + for (groupIt = group.Subgroups.begin(); groupIt != group.Subgroups.end(); + ++groupIt) + { + CreateChoiceOutline(**groupIt, out); + } + std::vector::const_iterator compIt; for (compIt = group.Components.begin(); compIt != group.Components.end(); ++compIt) diff --git a/Source/cmGetCMakePropertyCommand.cxx b/Source/cmGetCMakePropertyCommand.cxx index 09e7068..02e21bd 100644 --- a/Source/cmGetCMakePropertyCommand.cxx +++ b/Source/cmGetCMakePropertyCommand.cxx @@ -49,6 +49,22 @@ bool cmGetCMakePropertyCommand { this->Makefile->GetListOfMacros(output); } + else if ( args[1] == "COMPONENTS" ) + { + const std::set* components + = this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->GetInstallComponents(); + std::set::const_iterator compIt; + output = ""; + for (compIt = components->begin(); compIt != components->end(); ++compIt) + { + if (compIt != components->begin()) + { + output += ";"; + } + output += *compIt; + } + } else { const char *prop = diff --git a/Source/cmGetCMakePropertyCommand.h b/Source/cmGetCMakePropertyCommand.h index c1f66d9..b3cb442 100644 --- a/Source/cmGetCMakePropertyCommand.h +++ b/Source/cmGetCMakePropertyCommand.h @@ -62,7 +62,8 @@ public: "Get a property from the CMake instance. The value of the " "property is stored in the variable VAR. If the property is " "not found, CMake will report an error. Some supported properties " - "include: VARIABLES, CACHE_VARIABLES, COMMANDS, and MACROS."; + "include: VARIABLES, CACHE_VARIABLES, COMMANDS, MACROS, and " + "COMPONENTS."; } cmTypeMacro(cmGetCMakePropertyCommand, cmCommand); diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 2865a38..a76890b 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -139,6 +139,9 @@ public: void AddInstallComponent(const char* component); + const std::set* GetInstallComponents() const + { return &InstallComponents; } + ///! Add one installed target to the sets of the exports void AddTargetToExports(const char* exportSet, cmTarget* target, cmInstallTargetGenerator* archive, diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 6ce7ae5..eb162f5 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -384,6 +384,17 @@ bool cmInstallCommand::HandleTargetsMode(std::vector const& args) } } + // Keep track of whether we will be performing an installation of + // any files of the given type. + bool installsArchive = false; + bool installsLibrary = false; + bool installsRuntime = false; + bool installsFramework = false; + bool installsBundle = false; + bool installsPrivateHeader = false; + bool installsPublicHeader = false; + bool installsResource = false; + // Generate install script code to install the given targets. for(std::vector::iterator ti = targets.begin(); ti != targets.end(); ++ti) @@ -691,6 +702,16 @@ bool cmInstallCommand::HandleTargetsMode(std::vector const& args) } } + // Keep track of whether we're installing anything in each category + installsArchive = installsArchive || archiveGenerator != 0; + installsLibrary = installsLibrary || libraryGenerator != 0; + installsRuntime = installsRuntime || runtimeGenerator != 0; + installsFramework = installsFramework || frameworkGenerator != 0; + installsBundle = installsBundle || bundleGenerator != 0; + installsPrivateHeader = installsPrivateHeader || privateHeaderGenerator != 0; + installsPublicHeader = installsPublicHeader || publicHeaderGenerator != 0; + installsResource = installsResource || resourceGenerator; + this->Makefile->AddInstallGenerator(archiveGenerator); this->Makefile->AddInstallGenerator(libraryGenerator); this->Makefile->AddInstallGenerator(runtimeGenerator); @@ -713,22 +734,46 @@ bool cmInstallCommand::HandleTargetsMode(std::vector const& args) } // Tell the global generator about any installation component names specified - this->Makefile->GetLocalGenerator()->GetGlobalGenerator() - ->AddInstallComponent(archiveArgs.GetComponent().c_str()); - this->Makefile->GetLocalGenerator()->GetGlobalGenerator() - ->AddInstallComponent(libraryArgs.GetComponent().c_str()); - this->Makefile->GetLocalGenerator()->GetGlobalGenerator() - ->AddInstallComponent(runtimeArgs.GetComponent().c_str()); - this->Makefile->GetLocalGenerator()->GetGlobalGenerator() - ->AddInstallComponent(frameworkArgs.GetComponent().c_str()); - this->Makefile->GetLocalGenerator()->GetGlobalGenerator() - ->AddInstallComponent(bundleArgs.GetComponent().c_str()); - this->Makefile->GetLocalGenerator()->GetGlobalGenerator() - ->AddInstallComponent(privateHeaderArgs.GetComponent().c_str()); - this->Makefile->GetLocalGenerator()->GetGlobalGenerator() - ->AddInstallComponent(publicHeaderArgs.GetComponent().c_str()); - this->Makefile->GetLocalGenerator()->GetGlobalGenerator() - ->AddInstallComponent(resourceArgs.GetComponent().c_str()); + if (installsArchive) + { + this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->AddInstallComponent(archiveArgs.GetComponent().c_str()); + } + if (installsLibrary) + { + this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->AddInstallComponent(libraryArgs.GetComponent().c_str()); + } + if (installsRuntime) + { + this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->AddInstallComponent(runtimeArgs.GetComponent().c_str()); + } + if (installsFramework) + { + this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->AddInstallComponent(frameworkArgs.GetComponent().c_str()); + } + if (installsBundle) + { + this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->AddInstallComponent(bundleArgs.GetComponent().c_str()); + } + if (installsPrivateHeader) + { + this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->AddInstallComponent(privateHeaderArgs.GetComponent().c_str()); + } + if (installsPublicHeader) + { + this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->AddInstallComponent(publicHeaderArgs.GetComponent().c_str()); + } + if (installsResource) + { + this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->AddInstallComponent(resourceArgs.GetComponent().c_str()); + } return true; } diff --git a/Source/cmInstallFilesCommand.cxx b/Source/cmInstallFilesCommand.cxx index 36e7e25..a352244 100644 --- a/Source/cmInstallFilesCommand.cxx +++ b/Source/cmInstallFilesCommand.cxx @@ -59,6 +59,9 @@ bool cmInstallFilesCommand } } + this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->AddInstallComponent("Unspecified"); + return true; } @@ -126,7 +129,7 @@ void cmInstallFilesCommand::CreateInstallGenerator() const // Use a file install generator. const char* no_permissions = ""; const char* no_rename = ""; - const char* no_component = ""; + const char* no_component = "Unspecified"; std::vector no_configurations; this->Makefile->AddInstallGenerator( new cmInstallFilesGenerator(this->Files, diff --git a/Source/cmInstallProgramsCommand.cxx b/Source/cmInstallProgramsCommand.cxx index 6a55f17..d7557d0 100644 --- a/Source/cmInstallProgramsCommand.cxx +++ b/Source/cmInstallProgramsCommand.cxx @@ -38,6 +38,9 @@ bool cmInstallProgramsCommand this->FinalArgs.push_back(*s); } + this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->AddInstallComponent("Unspecified"); + return true; } @@ -87,7 +90,7 @@ void cmInstallProgramsCommand::FinalPass() // Use a file install generator. const char* no_permissions = ""; const char* no_rename = ""; - const char* no_component = ""; + const char* no_component = "Unspecified"; std::vector no_configurations; this->Makefile->AddInstallGenerator( new cmInstallFilesGenerator(this->Files, diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h index 61357b3..cd4d6e5 100644 --- a/Source/cmInstallTargetGenerator.h +++ b/Source/cmInstallTargetGenerator.h @@ -31,7 +31,7 @@ public: const char* file_permissions = "", std::vector const& configurations = std::vector(), - const char* component = "", + const char* component = "Unspecified", bool optional = false ); virtual ~cmInstallTargetGenerator(); diff --git a/Source/cmInstallTargetsCommand.cxx b/Source/cmInstallTargetsCommand.cxx index d3ecddf..cfcdc0c 100644 --- a/Source/cmInstallTargetsCommand.cxx +++ b/Source/cmInstallTargetsCommand.cxx @@ -62,6 +62,9 @@ bool cmInstallTargetsCommand } } + this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->AddInstallComponent("Unspecified"); + return true; } -- cgit v0.12