summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Modules/CPack.Info.plist.in2
-rw-r--r--Modules/CPack.distribution.dist.in6
-rw-r--r--Modules/NSIS.template.in232
-rw-r--r--Source/CPack/cmCPackComponentGroup.h117
-rw-r--r--Source/CPack/cmCPackGenerator.cxx403
-rw-r--r--Source/CPack/cmCPackGenerator.h14
-rw-r--r--Source/CPack/cmCPackNSISGenerator.cxx329
-rw-r--r--Source/CPack/cmCPackNSISGenerator.h27
-rw-r--r--Source/CPack/cmCPackPackageMakerGenerator.cxx518
-rw-r--r--Source/CPack/cmCPackPackageMakerGenerator.h61
-rw-r--r--Tests/CMakeLists.txt45
-rw-r--r--Tests/CPackComponents/CMakeLists.txt92
-rw-r--r--Tests/CPackComponents/VerifyResult.cmake48
-rw-r--r--Tests/CPackComponents/mylib.cpp7
-rw-r--r--Tests/CPackComponents/mylib.h1
-rw-r--r--Tests/CPackComponents/mylibapp.cpp6
-rw-r--r--Tests/SimpleInstall/CMakeLists.txt26
-rw-r--r--Tests/SimpleInstallS2/CMakeLists.txt26
18 files changed, 1724 insertions, 236 deletions
diff --git a/Modules/CPack.Info.plist.in b/Modules/CPack.Info.plist.in
index da4872b..8971d6e 100644
--- a/Modules/CPack.Info.plist.in
+++ b/Modules/CPack.Info.plist.in
@@ -32,6 +32,6 @@
<key>IFPkgFormatVersion</key>
<real>0.10000000149011612</real>
<key>CFBundleIdentifier</key>
-<string>com.@CPACK_PACKAGE_VENDOR@.@CPACK_PACKAGE_NAME@.@CPACK_PACKAGE_VERSION@</string>
+<string>com.@CPACK_PACKAGE_VENDOR@.@CPACK_PACKAGE_NAME@.@CPACK_PACKAGE_VERSION@@CPACK_MODULE_VERSION_SUFFIX@</string>
</dict>
</plist>
diff --git a/Modules/CPack.distribution.dist.in b/Modules/CPack.distribution.dist.in
new file mode 100644
index 0000000..b5cae03
--- /dev/null
+++ b/Modules/CPack.distribution.dist.in
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<installer-gui-script minSpecVersion="1.0">
+ <title>@CPACK_PACKAGE_NAME@</title>
+ <options allow-external-scripts="no" customize="allow" rootVolumeOnly="false"></options>
+ @CPACK_PACKAGEMAKER_CHOICES@
+</installer-gui-script>
diff --git a/Modules/NSIS.template.in b/Modules/NSIS.template.in
index 7aaf137..8879a7f 100644
--- a/Modules/NSIS.template.in
+++ b/Modules/NSIS.template.in
@@ -36,58 +36,113 @@
;Set compression
SetCompressor @CPACK_NSIS_COMPRESSOR@
+
+ !include Sections.nsh
+
+;--- Component support macros: ---
+; The code for the add/remove functionality is from:
+; http://nsis.sourceforge.net/Add/Remove_Functionality
+; It has been modified slightly and extended to provide
+; inter-component dependencies.
+Var AR_SecFlags
+Var AR_RegFlags
+@CPACK_NSIS_SECTION_SELECTED_VARS@
+
+; Loads the "selected" flag for the section named SecName into the
+; variable VarName.
+!macro LoadSectionSelectedIntoVar SecName VarName
+ SectionGetFlags ${${SecName}} $${VarName}
+ IntOp $${VarName} $${VarName} & ${SF_SELECTED} ;Turn off all other bits
+!macroend
-;--------------------------------
-; determine admin versus local install
-; Is install for "AllUsers" or "JustMe"?
-; Default to "JustMe" - set to "AllUsers" if admin or on Win9x
-; This function is used for the very first "custom page" of the installer.
-; This custom page does not show up visibly, but it executes prior to the
-; first visible page and sets up $INSTDIR properly...
-; Choose different default installation folder based on SV_ALLUSERS...
-; "Program Files" for AllUsers, "My Documents" for JustMe...
-
-Function .onInit
- StrCpy $SV_ALLUSERS "JustMe"
- StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
+; Loads the value of a variable... can we get around this?
+!macro LoadVar VarName
+ IntOp $R0 0 + $${VarName}
+!macroend
+!macro InitSection SecName
+ ; This macro reads component installed flag from the registry and
+ ;changes checked state of the section on the components page.
+ ;Input: section index constant name specified in Section command.
+
ClearErrors
- UserInfo::GetName
- IfErrors noLM
- Pop $0
- UserInfo::GetAccountType
- Pop $1
- StrCmp $1 "Admin" 0 +3
- SetShellVarContext all
- ;MessageBox MB_OK 'User "$0" is in the Admin group'
- StrCpy $SV_ALLUSERS "AllUsers"
- Goto done
- StrCmp $1 "Power" 0 +3
- SetShellVarContext all
- ;MessageBox MB_OK 'User "$0" is in the Power Users group'
- StrCpy $SV_ALLUSERS "AllUsers"
- Goto done
-
- noLM:
- StrCpy $SV_ALLUSERS "AllUsers"
- ;Get installation folder from registry if available
-
- done:
- StrCmp $SV_ALLUSERS "AllUsers" 0 +2
- StrCpy $INSTDIR "$PROGRAMFILES\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
-
- StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage
- !insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini"
+ ;Reading component status from registry
+ ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_DIRECTORY@\Components\${SecName}" "Installed"
+ IfErrors "default_${SecName}"
+ ;Status will stay default if registry value not found
+ ;(component was never installed)
+ IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits
+ SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags
+ IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off
+ IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit
+
+ ;Writing modified flags
+ SectionSetFlags ${${SecName}} $AR_SecFlags
+
+ "default_${SecName}:"
+ !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected
+!macroend
+
+!macro FinishSection SecName
+ ; This macro reads section flag set by user and removes the section
+ ;if it is not selected.
+ ;Then it writes component installed flag to registry
+ ;Input: section index constant name specified in Section command.
+
+ SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags
+ ;Checking lowest bit:
+ IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED}
+ IntCmp $AR_SecFlags 1 "leave_${SecName}"
+ ;Section is not selected:
+ ;Calling Section uninstall macro and writing zero installed flag
+ !insertmacro "Remove_${${SecName}}"
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_DIRECTORY@\Components\${SecName}" \
+ "Installed" 0
+ Goto "exit_${SecName}"
+
+ "leave_${SecName}:"
+ ;Section is selected:
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_DIRECTORY@\Components\${SecName}" \
+ "Installed" 1
+
+ "exit_${SecName}:"
+!macroend
+
+!macro RemoveSection SecName
+ ; This macro is used to call section's Remove_... macro
+ ;from the uninstaller.
+ ;Input: section index constant name specified in Section command.
+
+ !insertmacro "Remove_${${SecName}}"
+!macroend
- noOptionsPage:
-FunctionEnd
+; Determine whether the selection of SecName changed
+!macro MaybeSelectionChanged SecName
+ !insertmacro LoadVar ${SecName}_selected
+ SectionGetFlags ${${SecName}} $R1
+ IntOp $R1 $R1 & ${SF_SELECTED} ;Turn off all other bits
+
+ ; See if the status has changed:
+ IntCmp $R0 $R1 "${SecName}_unchanged"
+ !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected
+
+ IntCmp $R1 ${SF_SELECTED} "${SecName}_was_selected"
+ !insertmacro "Deselect_required_by_${SecName}"
+ goto "${SecName}_unchanged"
+
+ "${SecName}_was_selected:"
+ !insertmacro "Select_${SecName}_depends"
+
+ "${SecName}_unchanged:"
+!macroend
+;--- End of Add/Remove macros ---
;--------------------------------
;Interface Settings
!define MUI_HEADERIMAGE
!define MUI_ABORTWARNING
-
+
;--------------------------------
; path functions
@@ -289,7 +344,6 @@ Function un.RemoveFromPath
Pop $0
FunctionEnd
-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Uninstall sutff
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -407,9 +461,18 @@ Function ConditionalAddToRegisty
FunctionEnd
;--------------------------------
+; Installation types
+@CPACK_NSIS_INSTALLATION_TYPES@
+
+;--------------------------------
+; Component sections
+@CPACK_NSIS_COMPONENT_SECTIONS@
+
+;--------------------------------
; Define some macro setting for the gui
@CPACK_NSIS_INSTALLER_MUI_ICON_CODE@
@CPACK_NSIS_INSTALLER_ICON_CODE@
+@CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC@
;--------------------------------
;Pages
@@ -425,6 +488,8 @@ FunctionEnd
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
+ @CPACK_NSIS_PAGE_COMPONENTS@
+
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
@@ -446,16 +511,14 @@ FunctionEnd
ReserveFile "NSIS.InstallOptions.ini"
!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
-
;--------------------------------
;Installer Sections
-Section "Installer Section" InstSection
-
+Section "-Core installation"
;Use the entire tree produced by the INSTALL target. Keep the
;list of directories here in sync with the RMDir commands below.
SetOutPath "$INSTDIR"
- File /r "${INST_DIR}\*.*"
+ @CPACK_NSIS_FULL_INSTALL@
;Store installation folder
WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
@@ -474,7 +537,8 @@ Section "Installer Section" InstSection
Push "UninstallString"
Push "$INSTDIR\Uninstall.exe"
Call ConditionalAddToRegisty
-
+ Push "ModifyPath"
+
; Optional registration
Push "DisplayIcon"
Push "$INSTDIR\@CPACK_NSIS_INSTALLED_ICON_NAME@"
@@ -520,7 +584,7 @@ Section "Installer Section" InstSection
SectionEnd
-Section "Add to path"
+Section "-Add to path"
Push $INSTDIR\bin
;Read a value from an InstallOptions INI file
!insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State"
@@ -532,7 +596,6 @@ Section "Add to path"
doNotAddToPath:
SectionEnd
-
;--------------------------------
; Create custom pages
Function InstallOptionsPage
@@ -564,6 +627,26 @@ Function un.onInit
;Get installation folder from registry if available
done:
+
+FunctionEnd
+
+;--- Add/Remove callback functions: ---
+!macro SectionList MacroName
+ ;This macro used to perform operation on multiple sections.
+ ;List all of your components in following manner here.
+@CPACK_NSIS_COMPONENT_SECTION_LIST@
+!macroend
+
+Section -FinishComponents
+ ;Removes unselected components and writes component status to registry
+ !insertmacro SectionList "FinishSection"
+SectionEnd
+;--- End of Add/Remove callback functions ---
+
+;--------------------------------
+; Component dependencies
+Function .onSelChange
+ !insertmacro SectionList MaybeSelectionChanged
FunctionEnd
;--------------------------------
@@ -601,6 +684,9 @@ Section "Uninstall"
; Remove the registry entries.
DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
+ ; Removes all optional components
+ !insertmacro SectionList "RemoveSection"
+
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
@@ -647,4 +733,50 @@ Section "Uninstall"
doNotRemoveFromPath:
SectionEnd
+;--------------------------------
+; determine admin versus local install
+; Is install for "AllUsers" or "JustMe"?
+; Default to "JustMe" - set to "AllUsers" if admin or on Win9x
+; This function is used for the very first "custom page" of the installer.
+; This custom page does not show up visibly, but it executes prior to the
+; first visible page and sets up $INSTDIR properly...
+; Choose different default installation folder based on SV_ALLUSERS...
+; "Program Files" for AllUsers, "My Documents" for JustMe...
+
+Function .onInit
+ ; Reads components status for registry
+ !insertmacro SectionList "InitSection"
+
+ StrCpy $SV_ALLUSERS "JustMe"
+ StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
+
+ ClearErrors
+ UserInfo::GetName
+ IfErrors noLM
+ Pop $0
+ UserInfo::GetAccountType
+ Pop $1
+ StrCmp $1 "Admin" 0 +3
+ SetShellVarContext all
+ ;MessageBox MB_OK 'User "$0" is in the Admin group'
+ StrCpy $SV_ALLUSERS "AllUsers"
+ Goto done
+ StrCmp $1 "Power" 0 +3
+ SetShellVarContext all
+ ;MessageBox MB_OK 'User "$0" is in the Power Users group'
+ StrCpy $SV_ALLUSERS "AllUsers"
+ Goto done
+
+ noLM:
+ StrCpy $SV_ALLUSERS "AllUsers"
+ ;Get installation folder from registry if available
+ done:
+ StrCmp $SV_ALLUSERS "AllUsers" 0 +2
+ StrCpy $INSTDIR "$PROGRAMFILES\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
+
+ StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage
+ !insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini"
+
+ noOptionsPage:
+FunctionEnd
diff --git a/Source/CPack/cmCPackComponentGroup.h b/Source/CPack/cmCPackComponentGroup.h
new file mode 100644
index 0000000..2e1703c
--- /dev/null
+++ b/Source/CPack/cmCPackComponentGroup.h
@@ -0,0 +1,117 @@
+/*=========================================================================
+
+ Program: CMake - Cross-Platform Makefile Generator
+ Module: $RCSfile$
+ Language: C++
+ Date: $Date$
+ Version: $Revision$
+
+ Copyright (c) 2002 Kitware, Inc. All rights reserved.
+ See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+
+#ifndef cmCPackComponentGroup_h
+#define cmCPackComponentGroup_h
+
+#include <map>
+#include <string>
+#include <vector>
+
+class cmCPackComponentGroup;
+
+/** \class cmCPackInstallationType
+ * \brief A certain type of installation, which encompasses a
+ * set of components.
+ */
+class cmCPackInstallationType
+{
+public:
+ /// The name of the installation type (used to reference this
+ /// installation type).
+ std::string Name;
+
+ /// The name of the installation type as displayed to the user.
+ std::string DisplayName;
+
+ /// The index number of the installation type. This is an arbitrary
+ /// numbering from 1 to the number of installation types.
+ unsigned Index;
+};
+
+/** \class cmCPackComponent
+ * \brief A single component to be installed by CPack.
+ */
+class cmCPackComponent
+{
+public:
+ cmCPackComponent() : Group(0) { }
+
+ /// The name of the component (used to reference the component).
+ std::string Name;
+
+ /// The name of the component as displayed to the user.
+ std::string DisplayName;
+
+ /// The component group that contains this component (if any).
+ cmCPackComponentGroup *Group;
+
+ /// Whether this component group must always be installed.
+ bool IsRequired : 1;
+
+ /// Whether this component group is hidden. A hidden component group
+ /// is always installed. However, it may still be shown to the user.
+ bool IsHidden : 1;
+
+ /// Whether this component defaults to "disabled".
+ bool IsDisabledByDefault : 1;
+
+ /// A description of this component.
+ std::string Description;
+
+ /// The installation types that this component is a part of.
+ std::vector<cmCPackInstallationType *> InstallationTypes;
+
+ /// The components that this component depends on.
+ std::vector<cmCPackComponent *> Dependencies;
+
+ /// The components that depend on this component.
+ std::vector<cmCPackComponent *> ReverseDependencies;
+
+ /// The list of installed files that are part of this component.
+ std::vector<std::string> Files;
+
+ /// The list of installed directories that are part of this component.
+ std::vector<std::string> Directories;
+};
+
+/** \class cmCPackComponentGroup
+ * \brief A component group to be installed by CPack.
+ */
+class cmCPackComponentGroup
+{
+public:
+ /// The name of the group (used to reference the group).
+ std::string Name;
+
+ /// The name of the component as displayed to the user.
+ std::string DisplayName;
+
+ /// The description of this component group.
+ std::string Description;
+
+ /// Whether the name of the component will be shown in bold.
+ bool IsBold : 1;
+
+ /// Whether the section should be expanded by default
+ bool IsExpandedByDefault : 1;
+
+ /// The components within this group.
+ std::vector<cmCPackComponent*> Components;
+};
+
+#endif
diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx
index 8502df0..396bd9e 100644
--- a/Source/CPack/cmCPackGenerator.cxx
+++ b/Source/CPack/cmCPackGenerator.cxx
@@ -23,6 +23,7 @@
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmGeneratedFileStream.h"
+#include "cmCPackComponentGroup.h"
#include <cmsys/SystemTools.hxx>
#include <cmsys/Glob.hxx>
@@ -101,13 +102,6 @@ int cmCPackGenerator::PrepareNames()
std::string destFile = pdir;
destFile += "/" + outName;
std::string outFile = topDirectory + "/" + outName;
- bool setDestDir = cmSystemTools::IsOn(this->GetOption("CPACK_SET_DESTDIR"));
- std::string installPrefix = tempDirectory;
- if (!setDestDir)
- {
- installPrefix += this->GetPackagingInstallPrefix();
- }
-
this->SetOptionIfNotSet("CPACK_TOPLEVEL_DIRECTORY", topDirectory.c_str());
this->SetOptionIfNotSet("CPACK_TEMPORARY_DIRECTORY", tempDirectory.c_str());
this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_NAME", outName.c_str());
@@ -118,7 +112,7 @@ int cmCPackGenerator::PrepareNames()
this->SetOptionIfNotSet("CPACK_NATIVE_INSTALL_DIRECTORY",
cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()).c_str());
this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY",
- installPrefix.c_str());
+ tempDirectory.c_str());
cmCPackLogger(cmCPackLog::LOG_DEBUG,
"Look for: CPACK_PACKAGE_DESCRIPTION_FILE" << std::endl);
@@ -172,11 +166,19 @@ int cmCPackGenerator::InstallProject()
{
cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Install projects" << std::endl);
this->CleanTemporaryDirectory();
- std::string tempInstallDirectoryWithPostfix
+
+ std::string bareTempInstallDirectory
= this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY");
- const char* tempInstallDirectory = tempInstallDirectoryWithPostfix.c_str();
+ std::string tempInstallDirectoryStr = bareTempInstallDirectory;
+ bool setDestDir = cmSystemTools::IsOn(this->GetOption("CPACK_SET_DESTDIR"));
+ if (!setDestDir)
+ {
+ tempInstallDirectoryStr += this->GetPackagingInstallPrefix();
+ }
+
+ const char* tempInstallDirectory = tempInstallDirectoryStr.c_str();
int res = 1;
- if ( !cmsys::SystemTools::MakeDirectory(tempInstallDirectory))
+ if ( !cmsys::SystemTools::MakeDirectory(bareTempInstallDirectory.c_str()))
{
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Problem creating temporary directory: "
@@ -185,7 +187,6 @@ int cmCPackGenerator::InstallProject()
return 0;
}
- bool setDestDir = cmSystemTools::IsOn(this->GetOption("CPACK_SET_DESTDIR"));
if ( setDestDir )
{
std::string destDir = "DESTDIR=";
@@ -227,7 +228,7 @@ int cmCPackGenerator::InstallProject()
// If the project is a CMAKE project then run pre-install
// and then read the cmake_install script to run it
if ( !this->InstallProjectViaInstallCMakeProjects(
- setDestDir, tempInstallDirectory) )
+ setDestDir, bareTempInstallDirectory.c_str()) )
{
return 0;
}
@@ -244,8 +245,6 @@ int cmCPackGenerator::InstallProject()
int cmCPackGenerator::InstallProjectViaInstallCommands(
bool setDestDir, const char* tempInstallDirectory)
{
- (void)setDestDir;
- (void)tempInstallDirectory;
const char* installCommands = this->GetOption("CPACK_INSTALL_COMMANDS");
if ( installCommands && *installCommands )
{
@@ -454,7 +453,7 @@ int cmCPackGenerator::InstallProjectViaInstallScript(
//----------------------------------------------------------------------
int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
- bool setDestDir, const char* tempInstallDirectory)
+ bool setDestDir, const char* baseTempInstallDirectory)
{
const char* cmakeProjects
= this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS");
@@ -502,6 +501,51 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
std::string installSubDirectory = it->c_str();
std::string installFile = installDirectory + "/cmake_install.cmake";
+ std::vector<std::string> componentsVector;
+
+ bool componentInstall = false;
+ if (this->SupportsComponentInstallation())
+ {
+ // Determine the installation types for this project (if provided).
+ std::string installTypesVar = "CPACK_"
+ + cmSystemTools::UpperCase(installComponent) + "_INSTALL_TYPES";
+ const char *installTypes = this->GetOption(installTypesVar.c_str());
+ if (installTypes && *installTypes)
+ {
+ std::vector<std::string> installTypesVector;
+ cmSystemTools::ExpandListArgument(installTypes, installTypesVector);
+ std::vector<std::string>::iterator installTypeIt;
+ for (installTypeIt = installTypesVector.begin();
+ installTypeIt != installTypesVector.end();
+ ++installTypeIt)
+ {
+ this->GetInstallationType(installProjectName.c_str(),
+ installTypeIt->c_str());
+ }
+ }
+
+ // Determine the set of components that will be used in this project
+ std::string componentsVar
+ = "CPACK_COMPONENTS_" + cmSystemTools::UpperCase(installComponent);
+ const char *components = this->GetOption(componentsVar.c_str());
+ if (components && *components)
+ {
+ cmSystemTools::ExpandListArgument(components, componentsVector);
+ std::vector<std::string>::iterator compIt;
+ for (compIt = componentsVector.begin();
+ compIt != componentsVector.end();
+ ++compIt)
+ {
+ GetComponent(installProjectName.c_str(), compIt->c_str());
+ }
+ componentInstall = true;
+ }
+ }
+ if (componentsVector.empty())
+ {
+ componentsVector.push_back(installComponent);
+ }
+
const char* buildConfig = this->GetOption("CPACK_BUILD_CONFIG");
cmGlobalGenerator* globalGenerator
= this->MakefileMap->GetCMakeInstance()->CreateGlobalGenerator(
@@ -555,72 +599,100 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
cmCPackLogger(cmCPackLog::LOG_OUTPUT,
"- Install project: " << installProjectName << std::endl);
- cmake cm;
- cm.AddCMakePaths();
- cm.SetProgressCallback(cmCPackGeneratorProgress, this);
- cmGlobalGenerator gg;
- gg.SetCMakeInstance(&cm);
- std::auto_ptr<cmLocalGenerator> lg(gg.CreateLocalGenerator());
- lg->SetGlobalGenerator(&gg);
- cmMakefile *mf = lg->GetMakefile();
- std::string realInstallDirectory = tempInstallDirectory;
- if ( !installSubDirectory.empty() && installSubDirectory != "/" )
- {
- realInstallDirectory += installSubDirectory;
- }
- if ( setDestDir )
+ // Run the installation for each component
+ std::vector<std::string>::iterator componentIt;
+ for (componentIt = componentsVector.begin();
+ componentIt != componentsVector.end();
+ ++componentIt)
{
- // For DESTDIR based packaging, use the *project* CMAKE_INSTALL_PREFIX
- // underneath the tempInstallDirectory. The value of the project's
- // CMAKE_INSTALL_PREFIX is sent in here as the value of the
- // CPACK_INSTALL_PREFIX variable.
- std::string dir;
- if (this->GetOption("CPACK_INSTALL_PREFIX"))
+ std::string tempInstallDirectory = baseTempInstallDirectory;
+ installComponent = *componentIt;
+ if (componentInstall)
{
- dir += this->GetOption("CPACK_INSTALL_PREFIX");
+ cmCPackLogger(cmCPackLog::LOG_OUTPUT,
+ "- Install component: " << installComponent
+ << std::endl);
+ }
+
+ cmake cm;
+ cm.AddCMakePaths();
+ cm.SetProgressCallback(cmCPackGeneratorProgress, this);
+ cmGlobalGenerator gg;
+ gg.SetCMakeInstance(&cm);
+ std::auto_ptr<cmLocalGenerator> lg(gg.CreateLocalGenerator());
+ lg->SetGlobalGenerator(&gg);
+ cmMakefile *mf = lg->GetMakefile();
+ std::string realInstallDirectory = tempInstallDirectory;
+ if ( !installSubDirectory.empty() && installSubDirectory != "/" )
+ {
+ realInstallDirectory += installSubDirectory;
+ }
+ if (componentInstall)
+ {
+ tempInstallDirectory += "/";
+ tempInstallDirectory += installComponent;
}
- mf->AddDefinition("CMAKE_INSTALL_PREFIX", dir.c_str());
- cmCPackLogger(cmCPackLog::LOG_DEBUG,
- "- Using DESTDIR + CPACK_INSTALL_PREFIX... (mf->AddDefinition)"
- << std::endl);
- cmCPackLogger(cmCPackLog::LOG_DEBUG,
- "- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'" << std::endl);
- }
- else
- {
- mf->AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory);
+ if (!setDestDir)
+ {
+ tempInstallDirectory += this->GetPackagingInstallPrefix();
+ }
- cmCPackLogger(cmCPackLog::LOG_DEBUG,
- "- Using non-DESTDIR install... (mf->AddDefinition)" << std::endl);
- cmCPackLogger(cmCPackLog::LOG_DEBUG,
- "- Setting CMAKE_INSTALL_PREFIX to '" << tempInstallDirectory
- << "'" << std::endl);
- }
+ if ( setDestDir )
+ {
+ // For DESTDIR based packaging, use the *project* CMAKE_INSTALL_PREFIX
+ // underneath the tempInstallDirectory. The value of the project's
+ // CMAKE_INSTALL_PREFIX is sent in here as the value of the
+ // CPACK_INSTALL_PREFIX variable.
+ std::string dir;
+ if (this->GetOption("CPACK_INSTALL_PREFIX"))
+ {
+ dir += this->GetOption("CPACK_INSTALL_PREFIX");
+ }
+ mf->AddDefinition("CMAKE_INSTALL_PREFIX", dir.c_str());
+
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "- Using DESTDIR + CPACK_INSTALL_PREFIX... (mf->AddDefinition)"
+ << std::endl);
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'"
+ << std::endl);
+ }
+ else
+ {
+ mf->AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory.c_str());
- if ( buildConfig && *buildConfig )
- {
- mf->AddDefinition("BUILD_TYPE", buildConfig);
- }
- std::string installComponentLowerCase
- = cmSystemTools::LowerCase(installComponent);
- if ( installComponentLowerCase != "all" )
- {
- mf->AddDefinition("CMAKE_INSTALL_COMPONENT",
- installComponent.c_str());
- }
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "- Using non-DESTDIR install... (mf->AddDefinition)" << std::endl);
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "- Setting CMAKE_INSTALL_PREFIX to '" << tempInstallDirectory
+ << "'" << std::endl);
+ }
- // strip on TRUE, ON, 1, one or several file names, but not on
- // FALSE, OFF, 0 and an empty string
- if (!cmSystemTools::IsOff(this->GetOption("CPACK_STRIP_FILES")))
- {
- mf->AddDefinition("CMAKE_INSTALL_DO_STRIP", "1");
- }
- int res = mf->ReadListFile(0, installFile.c_str());
- if ( cmSystemTools::GetErrorOccuredFlag() || !res )
- {
- return 0;
+ if ( buildConfig && *buildConfig )
+ {
+ mf->AddDefinition("BUILD_TYPE", buildConfig);
+ }
+ std::string installComponentLowerCase
+ = cmSystemTools::LowerCase(installComponent);
+ if ( installComponentLowerCase != "all" )
+ {
+ mf->AddDefinition("CMAKE_INSTALL_COMPONENT",
+ installComponent.c_str());
+ }
+
+ // strip on TRUE, ON, 1, one or several file names, but not on
+ // FALSE, OFF, 0 and an empty string
+ if (!cmSystemTools::IsOff(this->GetOption("CPACK_STRIP_FILES")))
+ {
+ mf->AddDefinition("CMAKE_INSTALL_DO_STRIP", "1");
+ }
+ int res = mf->ReadListFile(0, installFile.c_str());
+ if ( cmSystemTools::GetErrorOccuredFlag() || !res )
+ {
+ return 0;
+ }
}
}
}
@@ -709,7 +781,6 @@ int cmCPackGenerator::DoPackage()
const char* packageFileName = this->GetOption("CPACK_OUTPUT_FILE_PATH");
const char* tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
-
cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl);
cmsys::Glob gl;
std::string findExpr = tempDirectory;
@@ -736,8 +807,33 @@ int cmCPackGenerator::DoPackage()
{
tempDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
}
+
+ // The files to be installed
+ std::vector<std::string> files = gl.GetFiles();
+
+ // For component installations, determine which files go into which
+ // components.
+ if (!this->Components.empty())
+ {
+ std::vector<std::string>::const_iterator it;
+ for ( it = files.begin(); it != files.end(); ++ it )
+ {
+ std::string fileN = cmSystemTools::RelativePath(tempDirectory,
+ it->c_str());
+
+ // Determine which component we are in.
+ std::string componentName = fileN.substr(0, fileN.find('/'));
+
+ // Strip off the component part of the path.
+ fileN = fileN.substr(fileN.find('/')+1, std::string::npos);
+
+ // Add this file to the list of files for the component.
+ this->Components[componentName].Files.push_back(fileN);
+ }
+ }
+
if ( !this->CompressFiles(tempPackageFileName,
- tempDirectory, gl.GetFiles()) || cmSystemTools::GetErrorOccuredFlag())
+ tempDirectory, files) || cmSystemTools::GetErrorOccuredFlag())
{
cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem compressing the directory"
<< std::endl);
@@ -1092,3 +1188,158 @@ int cmCPackGenerator::CleanTemporaryDirectory()
}
return 1;
}
+
+//----------------------------------------------------------------------
+bool cmCPackGenerator::SupportsComponentInstallation() const
+{
+ return false;
+}
+
+//----------------------------------------------------------------------
+cmCPackInstallationType*
+cmCPackGenerator::GetInstallationType(const char *projectName, const char *name)
+{
+ bool hasInstallationType = this->InstallationTypes.count(name) != 0;
+ cmCPackInstallationType *installType = &this->InstallationTypes[name];
+ if (!hasInstallationType)
+ {
+ // Define the installation type
+ std::string macroPrefix = "CPACK_INSTALL_TYPE_"
+ + cmsys::SystemTools::UpperCase(name);
+ installType->Name = name;
+
+ const char* displayName
+ = this->GetOption((macroPrefix + "_DISPLAY_NAME").c_str());
+ if (displayName && *displayName)
+ {
+ installType->DisplayName = displayName;
+ }
+ else
+ {
+ installType->DisplayName = installType->Name;
+ }
+
+ installType->Index = this->InstallationTypes.size();
+ }
+ return installType;
+}
+
+//----------------------------------------------------------------------
+cmCPackComponent*
+cmCPackGenerator::GetComponent(const char *projectName, const char *name)
+{
+ bool hasComponent = this->Components.count(name) != 0;
+ cmCPackComponent *component = &this->Components[name];
+ if (!hasComponent)
+ {
+ // Define the component
+ std::string macroPrefix = "CPACK_COMPONENT_"
+ + cmsys::SystemTools::UpperCase(name);
+ component->Name = name;
+ const char* displayName
+ = this->GetOption((macroPrefix + "_DISPLAY_NAME").c_str());
+ if (displayName && *displayName)
+ {
+ component->DisplayName = displayName;
+ }
+ else
+ {
+ component->DisplayName = component->Name;
+ }
+ component->IsHidden
+ = this->IsSet((macroPrefix + "_HIDDEN").c_str());
+ component->IsRequired
+ = this->IsSet((macroPrefix + "_REQUIRED").c_str());
+ component->IsDisabledByDefault
+ = this->IsSet((macroPrefix + "_DISABLED").c_str());
+ const char* groupName = this->GetOption((macroPrefix + "_GROUP").c_str());
+ if (groupName && *groupName)
+ {
+ component->Group = GetComponentGroup(projectName, groupName);
+ component->Group->Components.push_back(component);
+ }
+ else
+ {
+ component->Group = 0;
+ }
+
+ const char* description
+ = this->GetOption((macroPrefix + "_DESCRIPTION").c_str());
+ if (description && *description)
+ {
+ component->Description = description;
+ }
+
+ // Determine the installation types.
+ const char *installTypes
+ = this->GetOption((macroPrefix + "_INSTALL_TYPES").c_str());
+ if (installTypes && *installTypes)
+ {
+ std::vector<std::string> installTypesVector;
+ cmSystemTools::ExpandListArgument(installTypes, installTypesVector);
+ std::vector<std::string>::iterator installTypesIt;
+ for (installTypesIt = installTypesVector.begin();
+ installTypesIt != installTypesVector.end();
+ ++installTypesIt)
+ {
+ component->InstallationTypes.push_back(
+ this->GetInstallationType(projectName, installTypesIt->c_str()));
+ }
+ }
+
+ // Determine the component dependencies.
+ const char *depends = this->GetOption((macroPrefix + "_DEPENDS").c_str());
+ if (depends && *depends)
+ {
+ std::vector<std::string> dependsVector;
+ cmSystemTools::ExpandListArgument(depends, dependsVector);
+ std::vector<std::string>::iterator dependIt;
+ for (dependIt = dependsVector.begin();
+ dependIt != dependsVector.end();
+ ++dependIt)
+ {
+ cmCPackComponent *child = GetComponent(projectName, dependIt->c_str());
+ component->Dependencies.push_back(child);
+ child->ReverseDependencies.push_back(component);
+ }
+ }
+ }
+ return component;
+}
+
+//----------------------------------------------------------------------
+cmCPackComponentGroup*
+cmCPackGenerator::GetComponentGroup(const char *projectName, const char *name)
+{
+ std::string macroPrefix = "CPACK_COMPONENT_GROUP_"
+ + cmsys::SystemTools::UpperCase(name);
+ bool hasGroup = this->ComponentGroups.count(name) != 0;
+ cmCPackComponentGroup *group = &this->ComponentGroups[name];
+ if (!hasGroup)
+ {
+ // Define the group
+ group->Name = name;
+ const char* displayName
+ = this->GetOption((macroPrefix + "_DISPLAY_NAME").c_str());
+ if (displayName && *displayName)
+ {
+ group->DisplayName = displayName;
+ }
+ else
+ {
+ group->DisplayName = group->Name;
+ }
+
+ const char* description
+ = this->GetOption((macroPrefix + "_DESCRIPTION").c_str());
+ if (description && *description)
+ {
+ group->Description = description;
+ }
+ group->IsBold
+ = this->IsSet((macroPrefix + "_BOLD_TITLE").c_str());
+ group->IsExpandedByDefault
+ = this->IsSet((macroPrefix + "_EXPANDED").c_str());
+ }
+ return group;
+}
diff --git a/Source/CPack/cmCPackGenerator.h b/Source/CPack/cmCPackGenerator.h
index da819d6..ad61d65 100644
--- a/Source/CPack/cmCPackGenerator.h
+++ b/Source/CPack/cmCPackGenerator.h
@@ -19,6 +19,8 @@
#define cmCPackGenerator_h
#include "cmObject.h"
+#include <map>
+#include <vector>
#define cmCPackTypeMacro(class, superclass) \
cmTypeMacro(class, superclass); \
@@ -44,6 +46,9 @@
class cmMakefile;
class cmCPackLog;
+class cmCPackInstallationType;
+class cmCPackComponent;
+class cmCPackComponentGroup;
/** \class cmCPackGenerator
* \brief A superclass of all CPack Generators
@@ -120,6 +125,11 @@ protected:
virtual int InstallProjectViaInstallCMakeProjects(
bool setDestDir, const char* tempInstallDirectory);
+ virtual bool SupportsComponentInstallation() const;
+ virtual cmCPackInstallationType* GetInstallationType(const char *projectName, const char* name);
+ virtual cmCPackComponent* GetComponent(const char *projectName, const char* name);
+ virtual cmCPackComponentGroup* GetComponentGroup(const char *projectName, const char* name);
+
bool GeneratorVerbose;
std::string Name;
@@ -129,6 +139,10 @@ protected:
std::string CMakeSelf;
std::string CMakeRoot;
+ std::map<std::string, cmCPackInstallationType> InstallationTypes;
+ std::map<std::string, cmCPackComponent> Components;
+ std::map<std::string, cmCPackComponentGroup> ComponentGroups;
+
cmCPackLog* Logger;
private:
cmMakefile* MakefileMap;
diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx
index eb1cb1f..6b43e1e 100644
--- a/Source/CPack/cmCPackNSISGenerator.cxx
+++ b/Source/CPack/cmCPackNSISGenerator.cxx
@@ -23,6 +23,7 @@
#include "cmMakefile.h"
#include "cmGeneratedFileStream.h"
#include "cmCPackLog.h"
+#include "cmCPackComponentGroup.h"
#include <cmsys/SystemTools.hxx>
#include <cmsys/Glob.hxx>
@@ -79,7 +80,12 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
for ( it = files.begin(); it != files.end(); ++ it )
{
std::string fileN = cmSystemTools::RelativePath(toplevel,
- it->c_str());
+ it->c_str());
+ if (!this->Components.empty())
+ {
+ // Strip off the component part of the path.
+ fileN = fileN.substr(fileN.find('/')+1, std::string::npos);
+ }
cmSystemTools::ReplaceString(fileN, "/", "\\");
str << " Delete \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
}
@@ -92,14 +98,32 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
cmOStringStream dstr;
for ( sit = dirs.begin(); sit != dirs.end(); ++ sit )
{
- std::string fileN = cmSystemTools::RelativePath(toplevel,
- sit->c_str());
+ std::string componentName;
+ std::string fileN = cmSystemTools::RelativePath(toplevel, sit->c_str());
if ( fileN.empty() )
{
continue;
}
+ if (!Components.empty())
+ {
+ // If this is a component installation, strip off the component
+ // part of the path.
+ std::string::size_type slash = fileN.find('/');
+ if (slash != std::string::npos)
+ {
+ // If this is a component installation, determine which component it is.
+ componentName = fileN.substr(0, slash);
+
+ // Strip off the component part of the path.
+ fileN = fileN.substr(slash+1, std::string::npos);
+ }
+ }
cmSystemTools::ReplaceString(fileN, "/", "\\");
dstr << " RMDir \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
+ if (!componentName.empty())
+ {
+ this->Components[componentName].Directories.push_back(fileN);
+ }
}
cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Dirs: "
<< dstr.str().c_str() << std::endl);
@@ -128,6 +152,116 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE",
installerIconCode.c_str());
}
+
+ // Setup all of the component sections
+ if (this->Components.empty())
+ {
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", "");
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", "");
+ this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", "");
+ this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL",
+ "File /r \"${INST_DIR}\\*.*\"");
+ this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", "");
+ this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", "");
+ this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS", "");
+ }
+ else
+ {
+ std::string componentCode;
+ std::string sectionList;
+ std::string selectedVarsList;
+ std::string componentDescriptions;
+ std::string groupDescriptions;
+ std::string installTypesCode;
+
+ // Create installation types. The order is significant, so we first fill
+ // in a vector based on the indices, and print them in that order.
+ std::vector<cmCPackInstallationType *>
+ installTypes(this->InstallationTypes.size());
+ std::map<std::string, cmCPackInstallationType>::iterator installTypeIt;
+ for (installTypeIt = this->InstallationTypes.begin();
+ installTypeIt != this->InstallationTypes.end();
+ ++installTypeIt)
+ {
+ installTypes[installTypeIt->second.Index-1] = &installTypeIt->second;
+ }
+ std::vector<cmCPackInstallationType *>::iterator installTypeIt2;
+ for (installTypeIt2 = installTypes.begin();
+ installTypeIt2 != installTypes.end();
+ ++installTypeIt2)
+ {
+ installTypesCode += "InstType \"";
+ installTypesCode += (*installTypeIt2)->DisplayName;
+ installTypesCode += + "\"\n";
+ }
+
+ // Create installation groups first
+ std::map<std::string, cmCPackComponentGroup>::iterator groupIt;
+ for (groupIt = this->ComponentGroups.begin();
+ groupIt != this->ComponentGroups.end();
+ ++groupIt)
+ {
+ componentCode += this->CreateComponentGroupDescription(&groupIt->second);
+
+ // Add the group description, if any.
+ if (!groupIt->second.Description.empty())
+ {
+ groupDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${"
+ + groupIt->first + "} \""
+ + this->TranslateNewlines(groupIt->second.Description) + "\"\n";
+ }
+ }
+
+ // Create the remaining components, which aren't associated with groups.
+ std::map<std::string, cmCPackComponent>::iterator compIt;
+ for (compIt = this->Components.begin();
+ compIt != this->Components.end();
+ ++compIt)
+ {
+ if (!compIt->second.Group)
+ {
+ componentCode += this->CreateComponentDescription(&compIt->second);
+ }
+
+ // Add this component to the various section lists.
+ sectionList += " !insertmacro \"${MacroName}\" \"";
+ sectionList += compIt->first;
+ sectionList += "\"\n";
+ selectedVarsList += "Var " + compIt->first + "_selected\n";
+
+ // Add the component description, if any.
+ if (!compIt->second.Description.empty())
+ {
+ componentDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${"
+ + compIt->first + "} \""
+ + this->TranslateNewlines(compIt->second.Description) + "\"\n";
+ }
+ }
+
+ if (componentDescriptions.empty() && groupDescriptions.empty())
+ {
+ // Turn off the "Description" box
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
+ "!define MUI_COMPONENTSPAGE_NODESC");
+ }
+ else
+ {
+ componentDescriptions =
+ "!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN\n"
+ + componentDescriptions
+ + groupDescriptions
+ + "!insertmacro MUI_FUNCTION_DESCRIPTION_END\n";
+ this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
+ componentDescriptions.c_str());
+ }
+ 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->ConfigureFile(nsisInInstallOptions.c_str(),
nsisInstallOptions.c_str());
this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str());
@@ -252,9 +386,8 @@ int cmCPackNSISGenerator::InitializeInternal()
}
else
{
- cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
- << "not set" << std::endl);
-
+ cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
+ << "not set" << std::endl);
}
if ( cpackPackageExecutables )
{
@@ -274,8 +407,8 @@ int cmCPackNSISGenerator::InitializeInternal()
}
std::vector<std::string>::iterator it;
for ( it = cpackPackageExecutablesVector.begin();
- it != cpackPackageExecutablesVector.end();
- ++it )
+ it != cpackPackageExecutablesVector.end();
+ ++it )
{
std::string execName = *it;
++ it;
@@ -415,3 +548,183 @@ bool cmCPackNSISGenerator::GetListOfSubdirectories(const char* topdir,
dirs.push_back(topdir);
return true;
}
+
+//----------------------------------------------------------------------
+bool cmCPackNSISGenerator::SupportsComponentInstallation() const
+{
+ return true;
+}
+
+//----------------------------------------------------------------------
+std::string
+cmCPackNSISGenerator::
+CreateComponentDescription(cmCPackComponent *component) const
+{
+ // Basic description of the component
+ std::string componentCode = "Section ";
+ if (component->IsDisabledByDefault)
+ {
+ componentCode += "/o ";
+ }
+ componentCode += "\"";
+ if (component->IsHidden)
+ {
+ componentCode += "-";
+ }
+ componentCode += component->DisplayName + "\" " + component->Name + "\n";
+ if (component->IsRequired)
+ {
+ componentCode += " SectionIn RO\n";
+ }
+ else if (!component->InstallationTypes.empty())
+ {
+ std::ostringstream out;
+ std::vector<cmCPackInstallationType *>::iterator installTypeIter;
+ for (installTypeIter = component->InstallationTypes.begin();
+ installTypeIter != component->InstallationTypes.end();
+ ++installTypeIter)
+ {
+ out << " " << (*installTypeIter)->Index;
+ }
+ componentCode += " SectionIn" + out.str() + "\n";
+ }
+ componentCode += " SetOutPath \"$INSTDIR\"\n";
+ componentCode += " File /r \"${INST_DIR}\\" + component->Name + "\\*.*\"\n";
+ componentCode += "SectionEnd\n";
+
+ // Macro used to remove the component
+ componentCode += "!macro Remove_${" + component->Name + "}\n";
+ std::vector<std::string>::iterator pathIt;
+ for (pathIt = component->Files.begin();
+ pathIt != component->Files.end();
+ ++pathIt)
+ {
+ componentCode += " Delete \"$INSTDIR\\" + *pathIt + "\"\n";
+ }
+ for (pathIt = component->Directories.begin();
+ pathIt != component->Directories.end();
+ ++pathIt)
+ {
+ componentCode += " RMDir \"$INSTDIR\\" + *pathIt + "\"\n";
+ }
+ componentCode += "!macroend\n";
+
+ // Macro used to select each of the components that this component
+ // depends on.
+ std::set<cmCPackComponent *> visited;
+ componentCode += "!macro Select_" + component->Name + "_depends\n";
+ componentCode += CreateSelectionDependenciesDescription(component, visited);
+ componentCode += "!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";
+ return componentCode;
+}
+
+//----------------------------------------------------------------------
+std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription
+ (cmCPackComponent *component,
+ std::set<cmCPackComponent *>& visited) const
+{
+ // Don't visit a component twice
+ if (visited.count(component))
+ {
+ return std::string();
+ }
+ visited.insert(component);
+
+ std::ostringstream out;
+ std::vector<cmCPackComponent *>::iterator dependIt;
+ for (dependIt = component->Dependencies.begin();
+ dependIt != component->Dependencies.end();
+ ++dependIt)
+ {
+ // Write NSIS code to select this dependency
+ out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n";
+ out << " IntOp $0 $0 | ${SF_SELECTED}\n";
+ out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n";
+ out << " IntOp $" << (*dependIt)->Name << "_selected 0 + ${SF_SELECTED}\n";
+ // Recurse
+ out << CreateSelectionDependenciesDescription(*dependIt, visited).c_str();
+ }
+
+ return out.str();
+}
+
+
+//----------------------------------------------------------------------
+std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription
+ (cmCPackComponent *component,
+ std::set<cmCPackComponent *>& visited) const
+{
+ // Don't visit a component twice
+ if (visited.count(component))
+ {
+ return std::string();
+ }
+ visited.insert(component);
+
+ std::ostringstream out;
+ std::vector<cmCPackComponent *>::iterator dependIt;
+ for (dependIt = component->ReverseDependencies.begin();
+ dependIt != component->ReverseDependencies.end();
+ ++dependIt)
+ {
+ // Write NSIS code to deselect this dependency
+ out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n";
+ out << " IntOp $1 ${SF_SELECTED} ~\n";
+ out << " IntOp $0 $0 & $1\n";
+ out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n";
+ out << " IntOp $" << (*dependIt)->Name << "_selected 0 + 0\n";
+
+ // Recurse
+ out << CreateDeselectionDependenciesDescription(*dependIt, visited).c_str();
+ }
+
+ return out.str();
+}
+
+//----------------------------------------------------------------------
+std::string
+cmCPackNSISGenerator::
+CreateComponentGroupDescription(cmCPackComponentGroup *group) const
+{
+ if (group->Components.empty())
+ {
+ // Silently skip empty groups. NSIS doesn't support them.
+ return std::string();
+ }
+
+ std::string code = "SectionGroup ";
+ if (group->IsExpandedByDefault)
+ {
+ code += "/e ";
+ }
+ if (group->IsBold)
+ {
+ code += "\"!" + group->DisplayName + "\" " + group->Name + "\n";
+ }
+ else
+ {
+ code += "\"" + group->DisplayName + "\" " + group->Name + "\n";
+ }
+ std::vector<cmCPackComponent*>::iterator comp;
+ for (comp = group->Components.begin();
+ comp != group->Components.end();
+ ++comp)
+ {
+ code += this->CreateComponentDescription(*comp);
+ }
+ code += "SectionGroupEnd\n";
+ return code;
+}
+
+std::string cmCPackNSISGenerator::TranslateNewlines(std::string str)
+{
+ cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n");
+ return str;
+}
diff --git a/Source/CPack/cmCPackNSISGenerator.h b/Source/CPack/cmCPackNSISGenerator.h
index 0ae4ef7..70068e41 100644
--- a/Source/CPack/cmCPackNSISGenerator.h
+++ b/Source/CPack/cmCPackNSISGenerator.h
@@ -20,6 +20,7 @@
#include "cmCPackGenerator.h"
+#include <set>
/** \class cmCPackNSISGenerator
* \brief A generator for NSIS files
@@ -48,6 +49,32 @@ protected:
bool GetListOfSubdirectories(const char* dir,
std::vector<std::string>& dirs);
+
+ virtual bool SupportsComponentInstallation() const;
+
+ /// Produce a string that contains the NSIS code to describe a
+ /// particular component.
+ std::string CreateComponentDescription(cmCPackComponent *component) const;
+
+ /// Produce NSIS code that selects all of the components that this component
+ /// depends on, recursively.
+ std::string CreateSelectionDependenciesDescription
+ (cmCPackComponent *component,
+ std::set<cmCPackComponent *>& visited) const;
+
+ /// Produce NSIS code that de-selects all of the components that are dependent
+ /// on this component, recursively.
+ std::string CreateDeselectionDependenciesDescription
+ (cmCPackComponent *component,
+ std::set<cmCPackComponent *>& visited) const;
+
+ /// Produce a string that contains the NSIS code to describe a
+ /// particular component group, including its components.
+ std::string CreateComponentGroupDescription(cmCPackComponentGroup *group) const;
+
+ /// Translations any newlines found in the string into \r\n, so that the
+ /// resulting string can be used within NSIS.
+ static std::string TranslateNewlines(std::string str);
};
#endif
diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx
index a5884a9..e4a003d 100644
--- a/Source/CPack/cmCPackPackageMakerGenerator.cxx
+++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx
@@ -22,6 +22,7 @@
#include "cmSystemTools.h"
#include "cmMakefile.h"
#include "cmGeneratedFileStream.h"
+#include "cmCPackComponentGroup.h"
#include "cmCPackLog.h"
#include <cmsys/SystemTools.hxx>
@@ -38,6 +39,13 @@ cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator()
{
}
+//----------------------------------------------------------------------
+bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const
+{
+ return true;
+}
+
+//----------------------------------------------------------------------
int cmCPackPackageMakerGenerator::CopyInstallScript(const char* resdir,
const char* script,
const char* name)
@@ -96,23 +104,59 @@ int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName,
// them executable
if(preflight)
{
- this->CopyInstallScript(resDir.c_str(),
- preflight,
- "preflight");
+ this->CopyInstallScript(resDir.c_str(),
+ preflight,
+ "preflight");
}
if(postflight)
{
- this->CopyInstallScript(resDir.c_str(),
- postflight,
- "postflight");
+ this->CopyInstallScript(resDir.c_str(),
+ postflight,
+ "postflight");
}
if(postupgrade)
{
- this->CopyInstallScript(resDir.c_str(),
- postupgrade,
- "postupgrade");
+ this->CopyInstallScript(resDir.c_str(),
+ postupgrade,
+ "postupgrade");
}
+ if (!this->Components.empty())
+ {
+ // Create the directory where component packages will be installed.
+ std::string basePackageDir = toplevel;
+ basePackageDir += "/packages";
+ if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str()))
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Problem creating component packages directory: "
+ << basePackageDir.c_str() << std::endl);
+ return 0;
+ }
+
+ // Create packages for each component
+ std::map<std::string, cmCPackComponent>::iterator compIt;
+ for (compIt = this->Components.begin(); compIt != this->Components.end();
+ ++compIt)
+ {
+ std::string packageFile = basePackageDir;
+ packageFile += '/';
+ packageFile += GetPackageName(compIt->second);
+
+ std::string packageDir = toplevel;
+ packageDir += '/';
+ packageDir += compIt->first;
+ if (!this->GenerateComponentPackage(packageFile.c_str(),
+ packageDir.c_str(),
+ compIt->second))
+ {
+ return 0;
+ }
+ }
+ }
+ this->SetOption("CPACK_MODULE_VERSION_SUFFIX", "");
+
+ // Copy or create all of the resource files we need.
if ( !this->CopyCreateResourceFile("License")
|| !this->CopyCreateResourceFile("ReadMe")
|| !this->CopyCreateResourceFile("Welcome")
@@ -126,68 +170,58 @@ int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName,
std::string packageDirFileName
= this->GetOption("CPACK_TEMPORARY_DIRECTORY");
- packageDirFileName += ".pkg";
+ if (this->Components.empty())
+ {
+ packageDirFileName += ".pkg";
+ }
+ else
+ {
+ packageDirFileName += ".mpkg";
+ if (this->PackageMakerVersion == 3.0)
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "PackageMaker 3.0 cannot build component-based installations."
+ << std::endl << "Please use PackageMaker 2.5 instead." << std::endl);
+ }
+ }
- std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
- tmpFile += "/PackageMakerOutput.log";
cmOStringStream pkgCmd;
pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
- << "\" -build -p \"" << packageDirFileName << "\" -f \""
- << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
- << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
- << "/Resources\" -i \""
- << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Info.plist\" -d \""
- << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Description.plist\"";
- if ( this->PackageMakerVersion > 2.0 )
+ << "\" -build -p \"" << packageDirFileName << "\"";
+ if (this->Components.empty())
{
- pkgCmd << " -v";
+ pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
}
- cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << pkgCmd.str().c_str()
- << std::endl);
- std::string output;
- int retVal = 1;
- //bool res = cmSystemTools::RunSingleCommand(pkgCmd.str().c_str(), &output,
- //&retVal, 0, this->GeneratorVerbose, 0);
- bool res = true;
- retVal = system(pkgCmd.str().c_str());
- cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running package maker"
- << std::endl);
- if ( !res || retVal )
+ else
{
- cmGeneratedFileStream ofs(tmpFile.c_str());
- ofs << "# Run command: " << pkgCmd.str().c_str() << std::endl
- << "# Output:" << std::endl
- << output.c_str() << std::endl;
- cmCPackLogger(cmCPackLog::LOG_ERROR,
- "Problem running PackageMaker command: " << pkgCmd.str().c_str()
- << std::endl << "Please check " << tmpFile.c_str() << " for errors"
- << std::endl);
- return 0;
+ pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
+ << "/packages/";
}
- // sometimes the pkgCmd finishes but the directory is not yet
- // created, so try 10 times to see if it shows up
- int tries = 10;
- while(tries > 0 &&
- !cmSystemTools::FileExists(packageDirFileName.c_str()))
+ pkgCmd << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
+ << "/Resources\" -i \""
+ << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Info.plist\" -d \""
+ << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Description.plist\"";
+ if ( this->PackageMakerVersion > 2.0 )
{
- cmSystemTools::Delay(500);
- tries--;
+ pkgCmd << " -v";
}
- if(!cmSystemTools::FileExists(packageDirFileName.c_str()))
+ if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str()))
+ return 0;
+
+ if (!this->Components.empty())
{
- cmCPackLogger(
- cmCPackLog::LOG_ERROR,
- "Problem running PackageMaker command: " << pkgCmd.str().c_str()
- << std::endl << "Package not created: " << packageDirFileName.c_str()
- << std::endl);
+ WriteDistributionFile(packageDirFileName.c_str());
}
- tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+
+ std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
tmpFile += "/hdiutilOutput.log";
cmOStringStream dmgCmd;
dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
<< "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName
<< "\" \"" << outFileName << "\"";
- res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
+ std::string output;
+ int retVal = 1;
+ bool res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
&retVal, 0, this->GeneratorVerbose, 0);
if ( !res || retVal )
{
@@ -339,8 +373,14 @@ bool cmCPackPackageMakerGenerator::CopyCreateResourceFile(const char* name)
return true;
}
-bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name)
+bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name,
+ const char* outName)
{
+ if (!outName)
+ {
+ outName = name;
+ }
+
std::string inFName = "CPack.";
inFName += name;
inFName += ".in";
@@ -354,10 +394,374 @@ bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name)
std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
destFileName += "/";
- destFileName += name;
+ destFileName += outName;
cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
<< inFileName.c_str() << " to " << destFileName.c_str() << std::endl);
this->ConfigureFile(inFileName.c_str(), destFileName.c_str());
return true;
}
+
+//----------------------------------------------------------------------
+bool cmCPackPackageMakerGenerator::RunPackageMaker(const char *command,
+ const char *packageFile)
+{
+ std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+ tmpFile += "/PackageMakerOutput.log";
+
+ cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl);
+ std::string output;
+ int retVal = 1;
+ bool res = cmSystemTools::RunSingleCommand(command, &output, &retVal, 0,
+ this->GeneratorVerbose, 0);
+ cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running package maker"
+ << std::endl);
+ if ( !res || retVal )
+ {
+ cmGeneratedFileStream ofs(tmpFile.c_str());
+ ofs << "# Run command: " << command << std::endl
+ << "# Output:" << std::endl
+ << output.c_str() << std::endl;
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Problem running PackageMaker command: " << command
+ << std::endl << "Please check " << tmpFile.c_str() << " for errors"
+ << std::endl);
+ return false;
+ }
+ // sometimes the command finishes but the directory is not yet
+ // created, so try 10 times to see if it shows up
+ int tries = 10;
+ while(tries > 0 &&
+ !cmSystemTools::FileExists(packageFile))
+ {
+ cmSystemTools::Delay(500);
+ tries--;
+ }
+ if(!cmSystemTools::FileExists(packageFile))
+ {
+ cmCPackLogger(
+ cmCPackLog::LOG_ERROR,
+ "Problem running PackageMaker command: " << command
+ << std::endl << "Package not created: " << packageFile
+ << std::endl);
+ return false;
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------
+std::string
+cmCPackPackageMakerGenerator::GetPackageName(const cmCPackComponent& component)
+{
+ std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ packagesDir += ".dummy";
+ cmOStringStream out;
+ out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
+ << "-" << component.Name << ".pkg";
+ return out.str();
+}
+
+//----------------------------------------------------------------------
+bool
+cmCPackPackageMakerGenerator::
+GenerateComponentPackage(const char *packageFile,
+ const char *packageDir,
+ const cmCPackComponent& component)
+{
+ cmCPackLogger(cmCPackLog::LOG_OUTPUT,
+ "- Building component package: " << packageFile << std::endl);
+
+ // Create the description file for this component.
+ std::string descriptionFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+ descriptionFile += '/' + component.Name + "-Description.plist";
+ std::ofstream out(descriptionFile.c_str());
+ out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl
+ << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
+ << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" << std::endl
+ << "<plist version=\"1.4\">" << std::endl
+ << "<dict>" << std::endl
+ << " <key>IFPkgDescriptionTitle</key>" << std::endl
+ << " <string>" << component.DisplayName << "</string>" << std::endl
+ << " <key>IFPkgDescriptionVersion</key>" << std::endl
+ << " <string>" << this->GetOption("CPACK_PACKAGE_VERSION")
+ << "</string>" << std::endl
+ << " <key>IFPkgDescriptionDescription</key>" << std::endl
+ << " <string>" + this->EscapeForXML(component.Description)
+ << "</string>" << std::endl
+ << "</dict>" << std::endl
+ << "</plist>" << std::endl;
+ out.close();
+
+ // Create the Info.plist file for this component
+ std::string moduleVersionSuffix = ".";
+ moduleVersionSuffix += component.Name;
+ this->SetOption("CPACK_MODULE_VERSION_SUFFIX", moduleVersionSuffix.c_str());
+ std::string infoFileName = component.Name;
+ infoFileName += "-Info.plist";
+ if (!this->CopyResourcePlistFile("Info.plist", infoFileName.c_str()))
+ {
+ return false;
+ }
+
+ // Run PackageMaker
+ cmOStringStream pkgCmd;
+ pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
+ << "\" -build -p \"" << packageFile << "\""
+ << " -f \"" << packageDir << "\""
+ << "-i \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
+ << "/" << infoFileName << "\""
+ << "-d \"" << descriptionFile << "\"";
+ return RunPackageMaker(pkgCmd.str().c_str(), packageFile);
+}
+
+//----------------------------------------------------------------------
+void
+cmCPackPackageMakerGenerator::
+WriteDistributionFile(const char* metapackageFile)
+{
+ std::string distributionTemplate
+ = this->FindTemplate("CPack.distribution.dist.in");
+ if ( distributionTemplate.empty() )
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: "
+ << distributionTemplate << std::endl);
+ return;
+ }
+
+ std::string distributionFile = metapackageFile;
+ distributionFile += "/Contents/distribution.dist";
+
+ // Create the choice outline, which provides a tree-based view of
+ // the components in their groups.
+ cmOStringStream choiceOut;
+ choiceOut << "<choices-outline>" << std::endl;
+
+ // Emit the outline for the groups
+ std::map<std::string, cmCPackComponentGroup>::iterator groupIt;
+ for (groupIt = this->ComponentGroups.begin();
+ groupIt != this->ComponentGroups.end();
+ ++groupIt)
+ {
+ CreateChoiceOutline(groupIt->second, choiceOut);
+ }
+
+ // Emit the outline for the non-grouped components
+ std::map<std::string, cmCPackComponent>::iterator compIt;
+ for (compIt = this->Components.begin(); compIt != this->Components.end();
+ ++compIt)
+ {
+ if (!compIt->second.Group)
+ {
+ choiceOut << "<line choice=\"" << compIt->first << "Choice\"></line>"
+ << std::endl;
+ }
+ }
+ choiceOut << "</choices-outline>" << std::endl;
+
+ // Create the actual choices
+ for (groupIt = this->ComponentGroups.begin();
+ groupIt != this->ComponentGroups.end();
+ ++groupIt)
+ {
+ CreateChoice(groupIt->second, choiceOut);
+ }
+ for (compIt = this->Components.begin(); compIt != this->Components.end();
+ ++compIt)
+ {
+ CreateChoice(compIt->second, choiceOut);
+ }
+ this->SetOption("CPACK_PACKAGEMAKER_CHOICES", choiceOut.str().c_str());
+
+ // Create the distribution.dist file in the metapackage to turn it
+ // into a distribution package.
+ this->ConfigureFile(distributionTemplate.c_str(),
+ distributionFile.c_str());
+}
+
+//----------------------------------------------------------------------
+void
+cmCPackPackageMakerGenerator::
+CreateChoiceOutline(const cmCPackComponentGroup& group, cmOStringStream& out)
+{
+ out << "<line choice=\"" << group.Name << "Choice\">" << std::endl;
+ std::vector<cmCPackComponent*>::const_iterator compIt;
+ for (compIt = group.Components.begin(); compIt != group.Components.end();
+ ++compIt)
+ {
+ out << " <line choice=\"" << (*compIt)->Name << "Choice\"></line>"
+ << std::endl;
+ }
+ out << "</line>" << std::endl;
+}
+
+//----------------------------------------------------------------------
+void
+cmCPackPackageMakerGenerator::CreateChoice(const cmCPackComponentGroup& group,
+ cmOStringStream& out)
+{
+ out << "<choice id=\"" << group.Name << "Choice\" "
+ << "title=\"" << group.DisplayName << "\" "
+ << "start_selected=\"true\" "
+ << "start_enabled=\"true\" "
+ << "start_visible=\"true\" ";
+ if (!group.Description.empty())
+ {
+ out << "description=\"" << EscapeForXML(group.Description)
+ << "\"";
+ }
+ out << "></choice>" << std::endl;
+}
+
+//----------------------------------------------------------------------
+void
+cmCPackPackageMakerGenerator::CreateChoice(const cmCPackComponent& component,
+ cmOStringStream& out)
+{
+ std::string packageId = "com.";
+ packageId += this->GetOption("CPACK_PACKAGE_VENDOR");
+ packageId += '.';
+ packageId += this->GetOption("CPACK_PACKAGE_NAME");
+ packageId += '.';
+ packageId += this->GetOption("CPACK_PACKAGE_VERSION");
+ packageId += '.';
+ packageId += component.Name;
+
+ out << "<choice id=\"" << component.Name << "Choice\" "
+ << "title=\"" << component.DisplayName << "\" "
+ << "start_selected=\""
+ << (component.IsDisabledByDefault && !component.IsRequired? "false" : "true")
+ << "\" "
+ << "start_enabled=\""
+ << (component.IsRequired? "false" : "true")
+ << "\" "
+ << "start_visible=\"" << (component.IsHidden? "false" : "true") << "\" ";
+ if (!component.Description.empty())
+ {
+ out << "description=\"" << EscapeForXML(component.Description)
+ << "\" ";
+ }
+ if (!component.Dependencies.empty() || !component.ReverseDependencies.empty())
+ {
+ // The "selected" expression is evaluated each time any choice is
+ // selected, for all choices *except* the one that the user
+ // selected. A component is marked selected if it has been
+ // selected (my.choice.selected in Javascript) and all of the
+ // components it depends on have been selected (transitively) or
+ // if any of the components that depend on it have been selected
+ // (transitively). Assume that we have components A, B, C, D, and
+ // E, where each component depends on the previous component (B
+ // depends on A, C depends on B, D depends on C, and E depends on
+ // D). The expression we build for the component C will be
+ // my.choice.selected && B && A || D || E
+ // This way, selecting C will automatically select everything it depends
+ // on (B and A), while selecting something that depends on C--either D
+ // or E--will automatically cause C to get selected.
+ out << "selected=\"my.choice.selected";
+ AddDependencyAttributes(component, out);
+ AddReverseDependencyAttributes(component, out);
+ out << "\"";
+ }
+ out << ">" << std::endl;
+ out << " <pkg-ref id=\"" << packageId << "\"></pkg-ref>" << std::endl;
+ out << "</choice>" << std::endl;
+
+ // Create a description of the package associated with this
+ // component.
+ std::string relativePackageLocation = "Contents/Packages/";
+ relativePackageLocation += GetPackageName(component);
+
+ // Determine the installed size of the package. To do so, we dig
+ // into the Info.plist file from the generated package to retrieve
+ // this size.
+ int installedSize = 0;
+ std::string infoPlistFile = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ infoPlistFile += ".mpkg/";
+ infoPlistFile += relativePackageLocation;
+ infoPlistFile += "/Contents/Info.plist";
+ bool foundFlagInstalledSize = false;
+ std::string line;
+ std::ifstream ifs(infoPlistFile.c_str());
+ while ( cmSystemTools::GetLineFromStream(ifs, line) )
+ {
+ if (foundFlagInstalledSize)
+ {
+ std::string::size_type pos = line.find("<integer>");
+ if (pos == std::string::npos)
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot parse package size in "
+ << infoPlistFile << std::endl
+ << "String is \"" << line << "\"" << std::endl);
+ }
+ else
+ {
+ line.erase(0, pos + 9);
+ pos = line.find("</integer>");
+ if (pos == std::string::npos)
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot parse package size in "
+ << infoPlistFile << std::endl);
+ }
+ else
+ {
+ line.erase(pos, std::string::npos);
+ installedSize = atoi(line.c_str());
+ }
+ }
+ foundFlagInstalledSize = false;
+ }
+ else
+ {
+ foundFlagInstalledSize
+ = line.find("IFPkgFlagInstalledSize") != std::string::npos;
+ }
+ }
+
+
+ out << "<pkg-ref id=\"" << packageId << "\" "
+ << "version=\"" << this->GetOption("CPACK_PACKAGE_VERSION") << "\" "
+ << "installKBytes=\"" << installedSize << "\" "
+ << "auth=\"Admin\" onConclusion=\"None\">"
+ << "file:./" << relativePackageLocation << "</pkg-ref>" << std::endl;
+}
+
+//----------------------------------------------------------------------
+void
+cmCPackPackageMakerGenerator::
+AddDependencyAttributes(const cmCPackComponent& component, cmOStringStream& out)
+{
+ std::vector<cmCPackComponent *>::const_iterator dependIt;
+ for (dependIt = component.Dependencies.begin();
+ dependIt != component.Dependencies.end();
+ ++dependIt)
+ {
+ out << " &amp;&amp; choices['" << (*dependIt)->Name << "Choice'].selected";
+ AddDependencyAttributes(**dependIt, out);
+ }
+}
+
+//----------------------------------------------------------------------
+void
+cmCPackPackageMakerGenerator::
+AddReverseDependencyAttributes(const cmCPackComponent& component,
+ cmOStringStream& out)
+{
+ std::vector<cmCPackComponent *>::const_iterator dependIt;
+ for (dependIt = component.ReverseDependencies.begin();
+ dependIt != component.ReverseDependencies.end();
+ ++dependIt)
+ {
+ out << " || choices['" << (*dependIt)->Name << "Choice'].selected";
+ AddReverseDependencyAttributes(**dependIt, out);
+ }
+}
+
+//----------------------------------------------------------------------
+std::string cmCPackPackageMakerGenerator::EscapeForXML(std::string str)
+{
+ cmSystemTools::ReplaceString(str, "&", "&amp;");
+ cmSystemTools::ReplaceString(str, "<", "&lt;");
+ cmSystemTools::ReplaceString(str, ">", "&gt;");
+ cmSystemTools::ReplaceString(str, "\"", "&quot;");
+ return str;
+}
diff --git a/Source/CPack/cmCPackPackageMakerGenerator.h b/Source/CPack/cmCPackPackageMakerGenerator.h
index 6c16c75..2be8a2c 100644
--- a/Source/CPack/cmCPackPackageMakerGenerator.h
+++ b/Source/CPack/cmCPackPackageMakerGenerator.h
@@ -21,6 +21,8 @@
#include "cmCPackGenerator.h"
+class cmCPackComponent;
+
/** \class cmCPackPackageMakerGenerator
* \brief A generator for PackageMaker files
*
@@ -38,6 +40,8 @@ public:
cmCPackPackageMakerGenerator();
virtual ~cmCPackPackageMakerGenerator();
+ virtual bool SupportsComponentInstallation() const;
+
protected:
int CopyInstallScript(const char* resdir,
const char* script,
@@ -49,8 +53,63 @@ protected:
virtual const char* GetOutputPostfix() { return "darwin"; }
bool CopyCreateResourceFile(const char* name);
- bool CopyResourcePlistFile(const char* name);
+ bool CopyResourcePlistFile(const char* name, const char* outName = 0);
+
+ // Run PackageMaker with the given command line, which will (if
+ // successful) produce the given package file. Returns true if
+ // PackageMaker succeeds, false otherwise.
+ bool RunPackageMaker(const char *command, const char *packageFile);
+
+ // Retrieve the name of package file that will be generated for this
+ // component. The name is just the file name with extension, and
+ // does not include the subdirectory.
+ std::string GetPackageName(const cmCPackComponent& component);
+
+ // Generate a package in the file packageFile for the given
+ // component. All of the files within this component are stored in
+ // the directory packageDir. Returns true if successful, false
+ // otherwise.
+ bool GenerateComponentPackage(const char *packageFile,
+ const char *packageDir,
+ const cmCPackComponent& component);
+
+ // Writes a distribution.dist file, which turns a metapackage into a
+ // full-fledged distribution. This file is used to describe
+ // inter-component dependencies. metapackageFile is the name of the
+ // metapackage for the distribution. Only valid for a
+ // component-based install.
+ void WriteDistributionFile(const char* metapackageFile);
+
+ // Subroutine of WriteDistributionFile that writes out the
+ // dependency attributes for inter-component dependencies.
+ void AddDependencyAttributes(const cmCPackComponent& component,
+ cmOStringStream& out);
+
+ // Subroutine of WriteDistributionFile that writes out the
+ // reverse dependency attributes for inter-component dependencies.
+ void AddReverseDependencyAttributes(const cmCPackComponent& component,
+ cmOStringStream& out);
+
+ // Generates XML that encodes the hierarchy of component groups and
+ // their components in a form that can be used by distribution
+ // metapackages.
+ void CreateChoiceOutline(const cmCPackComponentGroup& group,
+ cmOStringStream& out);
+
+ /// Create the "choice" XML element to describe a component group
+ /// for the installer GUI.
+ void CreateChoice(const cmCPackComponentGroup& group,
+ cmOStringStream& out);
+
+ /// Create the "choice" XML element to describe a component for the
+ /// installer GUI.
+ void CreateChoice(const cmCPackComponent& component,
+ cmOStringStream& out);
+ // Escape the given string to make it usable as an XML attribute
+ // value.
+ std::string EscapeForXML(std::string str);
+
double PackageMakerVersion;
};
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 03d50fb..74bbc07 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -19,7 +19,7 @@ IF(BUILD_TESTING)
OPTION(CMAKE_RUN_LONG_TESTS
"Should the long tests be run (such as Bootstrap)." ON)
MARK_AS_ADVANCED(CMAKE_RUN_LONG_TESTS)
-
+
IF (CMAKE_RUN_LONG_TESTS)
OPTION(CTEST_TEST_CTEST
"Should the tests that run a full sub ctest process be run?"
@@ -27,6 +27,34 @@ IF(BUILD_TESTING)
MARK_AS_ADVANCED(CTEST_TEST_CTEST)
ENDIF (CMAKE_RUN_LONG_TESTS)
+ # Should CPack tests be run? By default, yes, but...
+ #
+ # Disable packaging test on Apple 10.3 and below. PackageMaker starts
+ # DiskManagementTool as root and disowns it
+ # (http://lists.apple.com/archives/installer-dev/2005/Jul/msg00005.html).
+ # It is left holding open pipe handles and preventing ProcessUNIX from
+ # detecting end-of-data even after its immediate child exits. Then
+ # the test hangs until it times out and is killed. This is a
+ # well-known bug in kwsys process execution that I would love to get
+ # time to fix.
+ #
+ OPTION(CTEST_TEST_CPACK
+ "Should the tests that use '--build-target package' be run?"
+ ON)
+ MARK_AS_ADVANCED(CTEST_TEST_CPACK)
+ IF(APPLE AND CTEST_TEST_CPACK)
+ EXECUTE_PROCESS(
+ COMMAND sw_vers -productVersion
+ OUTPUT_VARIABLE OSX_VERSION
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ IF(OSX_VERSION MATCHES "^10\\.[0123]" OR OSX_VERSION MATCHES "ProductVersion:\t10\\.[0123]")
+ MESSAGE(STATUS "Forcing CTEST_TEST_CPACK=OFF on OSX < 10.4")
+ MESSAGE(STATUS "OSX_VERSION='${OSX_VERSION}'")
+ SET(CTEST_TEST_CPACK OFF)
+ ENDIF(OSX_VERSION MATCHES "^10\\.[0123]" OR OSX_VERSION MATCHES "ProductVersion:\t10\\.[0123]")
+ ENDIF(APPLE AND CTEST_TEST_CPACK)
+
# Use 1500 or CTEST_TEST_TIMEOUT for long test timeout value,
# whichever is greater.
SET(CMAKE_LONG_TEST_TIMEOUT 1500)
@@ -259,6 +287,21 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=CVS -P ${CMake_SOURCE_DIR}/Utilities/Rel
"-DSTAGE2:BOOL=1"
--test-command ${SimpleInstallInstallDir}/MyTest/bin/SimpleInstExeS2)
+ IF(CTEST_TEST_CPACK)
+ ADD_TEST(CPackComponents ${CMAKE_CTEST_COMMAND}
+ --build-and-test
+ "${CMake_SOURCE_DIR}/Tests/CPackComponents"
+ "${CMake_BINARY_DIR}/Tests/CPackComponents"
+ --build-generator ${CMAKE_TEST_GENERATOR}
+ --build-project CPackComponents
+ --build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
+ --build-two-config
+ --build-target package
+ --test-command ${CMAKE_CMAKE_COMMAND}
+ "-DCPackComponents_BINARY_DIR:PATH=${CMake_BINARY_DIR}/Tests/CPackComponents"
+ -P "${CMake_SOURCE_DIR}/Tests/CPackComponents/VerifyResult.cmake")
+ ENDIF(CTEST_TEST_CPACK)
+
ADD_TEST(X11 ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMake_SOURCE_DIR}/Tests/X11"
diff --git a/Tests/CPackComponents/CMakeLists.txt b/Tests/CPackComponents/CMakeLists.txt
new file mode 100644
index 0000000..e52eaa8
--- /dev/null
+++ b/Tests/CPackComponents/CMakeLists.txt
@@ -0,0 +1,92 @@
+# CPack Example: User-selectable Installation Components
+#
+# In this example, we have a simple library (mylib) with an example
+# application (mylibapp). We create a binary installer that allows
+# users to select which pieces will be installed: the example
+# application, the library binaries, and/or the header file.
+cmake_minimum_required(VERSION 2.6)
+project(CPackComponents)
+
+# Create the mylib library
+add_library(mylib mylib.cpp)
+
+# Create the mylibapp application
+add_executable(mylibapp mylibapp.cpp)
+target_link_libraries(mylibapp mylib)
+
+# Create installation targets. Note that we put each kind of file
+# into a different component via COMPONENT. These components will
+# be used to create the installation components.
+install(TARGETS mylib
+ ARCHIVE
+ DESTINATION lib
+ COMPONENT libraries)
+install(TARGETS mylibapp
+ RUNTIME
+ DESTINATION bin
+ COMPONENT applications)
+install(FILES mylib.h
+ DESTINATION include
+ COMPONENT headers)
+
+# CPack boilerplate for this project
+set(CPACK_PACKAGE_NAME "MyLib")
+set(CPACK_PACKAGE_VENDOR "CMake.org")
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MyLib - CPack Component Installation Example")
+set(CPACK_PACKAGE_VERSION "1.0.0")
+set(CPACK_PACKAGE_VERSION_MAJOR "1")
+set(CPACK_PACKAGE_VERSION_MINOR "0")
+set(CPACK_PACKAGE_VERSION_PATCH "0")
+set(CPACK_PACKAGE_INSTALL_DIRECTORY "CPack Component Example")
+
+# Tell CPack all of the components to install. The "ALL"
+# refers to the fact that this is the set of components that
+# will be included when CPack is instructed to put everything
+# into the binary installer (the default behavior).
+set(CPACK_COMPONENTS_ALL applications libraries headers)
+
+# Set the displayed names for each of the components to install.
+# These will be displayed in the list of components inside the installer.
+set(CPACK_COMPONENT_APPLICATIONS_DISPLAY_NAME "MyLib Application")
+set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries")
+set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "C++ Headers")
+
+# Provide descriptions for each of the components to install.
+# When the user hovers the mouse over the name of a component,
+# the description will be shown in the "Description" box in the
+# installer. If no descriptions are provided, the "Description"
+# box will be removed.
+set(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION
+ "An extremely useful application that makes use of MyLib")
+set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION
+ "Static libraries used to build programs with MyLib")
+set(CPACK_COMPONENT_HEADERS_DESCRIPTION
+ "C/C++ header files for use with MyLib")
+
+# Put the components into two different groups: "Runtime" and "Development"
+set(CPACK_COMPONENT_APPLICATIONS_GROUP "Runtime")
+set(CPACK_COMPONENT_LIBRARIES_GROUP "Development")
+set(CPACK_COMPONENT_HEADERS_GROUP "Development")
+
+# Expand the "Development" group by default, since we have so few components.
+# Also, provide this group with a description.
+set(CPACK_COMPONENT_GROUP_DEVELOPMENT_EXPANDED ON)
+set(CPACK_COMPONENT_GROUP_DEVELOPMENT_DESCRIPTION
+ "All of the tools you'll ever need to develop software")
+
+# It doesn't make sense to install the headers without the libraries
+# (because you could never use the headers!), so make the headers component
+# depend on the libraries component.
+set(CPACK_COMPONENT_HEADERS_DEPENDS libraries)
+
+# Create two installation types with pre-selected components.
+# The "Developer" installation has just the library and headers,
+# while the "Full" installation has everything.
+set(CPACK_ALL_INSTALL_TYPES Full Developer)
+set(CPACK_INSTALL_TYPE_FULL_DISPLAY_NAME "Everything")
+set(CPACK_COMPONENT_LIBRARIES_INSTALL_TYPES Developer Full)
+set(CPACK_COMPONENT_HEADERS_INSTALL_TYPES Developer Full)
+set(CPACK_COMPONENT_APPLICATIONS_INSTALL_TYPES Full)
+
+# Include CPack to introduce the appropriate targets
+include(CPack)
diff --git a/Tests/CPackComponents/VerifyResult.cmake b/Tests/CPackComponents/VerifyResult.cmake
new file mode 100644
index 0000000..3e64a46
--- /dev/null
+++ b/Tests/CPackComponents/VerifyResult.cmake
@@ -0,0 +1,48 @@
+message(STATUS "=============================================================================")
+message(STATUS "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)")
+message(STATUS "")
+
+if(NOT CPackComponents_BINARY_DIR)
+ message(FATAL_ERROR "CPackComponents_BINARY_DIR not set")
+endif(NOT CPackComponents_BINARY_DIR)
+
+set(expected_file_mask "")
+
+if(WIN32)
+ # Only expect the *.exe installer if it looks like NSIS is
+ # installed on this machine:
+ #
+ find_program(NSIS_MAKENSIS_EXECUTABLE NAMES makensis
+ PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS]
+ DOC "makensis.exe location"
+ )
+ if(NSIS_MAKENSIS_EXECUTABLE)
+ set(expected_file_mask "${CPackComponents_BINARY_DIR}/*.exe")
+ endif(NSIS_MAKENSIS_EXECUTABLE)
+endif(WIN32)
+
+if(APPLE)
+ # Always expect the *.dmg installer - PackageMaker should always
+ # be installed on a development Mac:
+ #
+ set(expected_file_mask "${CPackComponents_BINARY_DIR}/*.dmg")
+endif(APPLE)
+
+if(expected_file_mask)
+ set(expected_count 1)
+ file(GLOB expected_file "${expected_file_mask}")
+
+ message(STATUS "expected_count='${expected_count}'")
+ message(STATUS "expected_file='${expected_file}'")
+ message(STATUS "expected_file_mask='${expected_file_mask}'")
+
+ if(NOT expected_file)
+ message(FATAL_ERROR "error: expected_file does not exist: CPackComponents test fails.")
+ endif(NOT expected_file)
+
+ list(LENGTH expected_file actual_count)
+ message(STATUS "actual_count='${actual_count}'")
+ if(NOT actual_count EQUAL expected_count)
+ message(FATAL_ERROR "error: expected_count does not match actual_count: CPackComponents test fails.")
+ endif(NOT actual_count EQUAL expected_count)
+endif(expected_file_mask)
diff --git a/Tests/CPackComponents/mylib.cpp b/Tests/CPackComponents/mylib.cpp
new file mode 100644
index 0000000..8ddac19
--- /dev/null
+++ b/Tests/CPackComponents/mylib.cpp
@@ -0,0 +1,7 @@
+#include "mylib.h"
+#include "stdio.h"
+
+void mylib_function()
+{
+ printf("This is mylib");
+}
diff --git a/Tests/CPackComponents/mylib.h b/Tests/CPackComponents/mylib.h
new file mode 100644
index 0000000..5d0a822
--- /dev/null
+++ b/Tests/CPackComponents/mylib.h
@@ -0,0 +1 @@
+void mylib_function();
diff --git a/Tests/CPackComponents/mylibapp.cpp b/Tests/CPackComponents/mylibapp.cpp
new file mode 100644
index 0000000..a438ac7
--- /dev/null
+++ b/Tests/CPackComponents/mylibapp.cpp
@@ -0,0 +1,6 @@
+#include "mylib.h"
+
+int main()
+{
+ mylib_function();
+}
diff --git a/Tests/SimpleInstall/CMakeLists.txt b/Tests/SimpleInstall/CMakeLists.txt
index 23d3d27..34914b6 100644
--- a/Tests/SimpleInstall/CMakeLists.txt
+++ b/Tests/SimpleInstall/CMakeLists.txt
@@ -355,27 +355,11 @@ SET(CMAKE_INSTALL_DEBUG_LIBRARIES 1)
INCLUDE(InstallRequiredSystemLibraries)
INCLUDE(CPack)
-# Disable packaging test on Apple 10.3 and below. PackageMaker starts
-# DiskManagementTool as root and disowns it
-# (http://lists.apple.com/archives/installer-dev/2005/Jul/msg00005.html).
-# It is left holding open pipe handles and preventing ProcessUNIX from
-# detecting end-of-data even after its immediate child exits. Then
-# the test hangs until it times out and is killed. This is a
-# well-known bug in kwsys process execution that I would love to get
-# time to fix.
-SET(PACKAGE_TARGET --build-target package)
-IF(APPLE AND NOT CTEST_TEST_CPACK)
- EXECUTE_PROCESS(
- COMMAND sw_vers -productVersion
- OUTPUT_VARIABLE OSX_VERSION
- OUTPUT_STRIP_TRAILING_WHITESPACE
- )
- IF("${OSX_VERSION}" MATCHES "^10\\.[0123]" OR "${OSX_VERSION}" MATCHES "ProductVersion:\t10\\.[0123]")
- MESSAGE(STATUS "Disabling package test on OSX < 10.4")
- MESSAGE(STATUS "OSX_VERSION='${OSX_VERSION}'")
- SET(PACKAGE_TARGET)
- ENDIF("${OSX_VERSION}" MATCHES "^10\\.[0123]" OR "${OSX_VERSION}" MATCHES "ProductVersion:\t10\\.[0123]")
-ENDIF(APPLE AND NOT CTEST_TEST_CPACK)
+IF(CTEST_TEST_CPACK)
+ SET(PACKAGE_TARGET --build-target package)
+ELSE(CTEST_TEST_CPACK)
+ SET(PACKAGE_TARGET)
+ENDIF(CTEST_TEST_CPACK)
ADD_CUSTOM_COMMAND(
TARGET ${install_target}
diff --git a/Tests/SimpleInstallS2/CMakeLists.txt b/Tests/SimpleInstallS2/CMakeLists.txt
index 23d3d27..34914b6 100644
--- a/Tests/SimpleInstallS2/CMakeLists.txt
+++ b/Tests/SimpleInstallS2/CMakeLists.txt
@@ -355,27 +355,11 @@ SET(CMAKE_INSTALL_DEBUG_LIBRARIES 1)
INCLUDE(InstallRequiredSystemLibraries)
INCLUDE(CPack)
-# Disable packaging test on Apple 10.3 and below. PackageMaker starts
-# DiskManagementTool as root and disowns it
-# (http://lists.apple.com/archives/installer-dev/2005/Jul/msg00005.html).
-# It is left holding open pipe handles and preventing ProcessUNIX from
-# detecting end-of-data even after its immediate child exits. Then
-# the test hangs until it times out and is killed. This is a
-# well-known bug in kwsys process execution that I would love to get
-# time to fix.
-SET(PACKAGE_TARGET --build-target package)
-IF(APPLE AND NOT CTEST_TEST_CPACK)
- EXECUTE_PROCESS(
- COMMAND sw_vers -productVersion
- OUTPUT_VARIABLE OSX_VERSION
- OUTPUT_STRIP_TRAILING_WHITESPACE
- )
- IF("${OSX_VERSION}" MATCHES "^10\\.[0123]" OR "${OSX_VERSION}" MATCHES "ProductVersion:\t10\\.[0123]")
- MESSAGE(STATUS "Disabling package test on OSX < 10.4")
- MESSAGE(STATUS "OSX_VERSION='${OSX_VERSION}'")
- SET(PACKAGE_TARGET)
- ENDIF("${OSX_VERSION}" MATCHES "^10\\.[0123]" OR "${OSX_VERSION}" MATCHES "ProductVersion:\t10\\.[0123]")
-ENDIF(APPLE AND NOT CTEST_TEST_CPACK)
+IF(CTEST_TEST_CPACK)
+ SET(PACKAGE_TARGET --build-target package)
+ELSE(CTEST_TEST_CPACK)
+ SET(PACKAGE_TARGET)
+ENDIF(CTEST_TEST_CPACK)
ADD_CUSTOM_COMMAND(
TARGET ${install_target}