ENH: Add patch for feature request #6847 - CPack components for NSIS and PackageMaker installers. Thanks to Doug Gregor for all the hard work involved with implementing this patch! Also added new test CPackComponents that is conditionally executed only when NSIS or PackageMaker installer builders are available.

This commit is contained in:
David Cole 2008-06-17 11:39:26 -04:00
parent 64498a1287
commit 1105a86c52
18 changed files with 1723 additions and 235 deletions

View File

@ -32,6 +32,6 @@
<key>IFPkgFormatVersion</key> <key>IFPkgFormatVersion</key>
<real>0.10000000149011612</real> <real>0.10000000149011612</real>
<key>CFBundleIdentifier</key> <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> </dict>
</plist> </plist>

View File

@ -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>

View File

@ -37,50 +37,105 @@
;Set compression ;Set compression
SetCompressor @CPACK_NSIS_COMPRESSOR@ SetCompressor @CPACK_NSIS_COMPRESSOR@
;-------------------------------- !include Sections.nsh
; 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 ;--- Component support macros: ---
StrCpy $SV_ALLUSERS "JustMe" ; The code for the add/remove functionality is from:
StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@" ; 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
; 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 ClearErrors
UserInfo::GetName ;Reading component status from registry
IfErrors noLM ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_DIRECTORY@\Components\${SecName}" "Installed"
Pop $0 IfErrors "default_${SecName}"
UserInfo::GetAccountType ;Status will stay default if registry value not found
Pop $1 ;(component was never installed)
StrCmp $1 "Admin" 0 +3 IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits
SetShellVarContext all SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags
;MessageBox MB_OK 'User "$0" is in the Admin group' IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off
StrCpy $SV_ALLUSERS "AllUsers" IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit
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: ;Writing modified flags
StrCpy $SV_ALLUSERS "AllUsers" SectionSetFlags ${${SecName}} $AR_SecFlags
;Get installation folder from registry if available
done: "default_${SecName}:"
StrCmp $SV_ALLUSERS "AllUsers" 0 +2 !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected
StrCpy $INSTDIR "$PROGRAMFILES\@CPACK_PACKAGE_INSTALL_DIRECTORY@" !macroend
StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage !macro FinishSection SecName
!insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini" ; 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.
noOptionsPage: SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags
FunctionEnd ;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
; 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 ;Interface Settings
@ -289,7 +344,6 @@ Function un.RemoveFromPath
Pop $0 Pop $0
FunctionEnd FunctionEnd
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Uninstall sutff ; Uninstall sutff
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -406,10 +460,19 @@ Function ConditionalAddToRegisty
ConditionalAddToRegisty_EmptyString: ConditionalAddToRegisty_EmptyString:
FunctionEnd FunctionEnd
;--------------------------------
; Installation types
@CPACK_NSIS_INSTALLATION_TYPES@
;--------------------------------
; Component sections
@CPACK_NSIS_COMPONENT_SECTIONS@
;-------------------------------- ;--------------------------------
; Define some macro setting for the gui ; Define some macro setting for the gui
@CPACK_NSIS_INSTALLER_MUI_ICON_CODE@ @CPACK_NSIS_INSTALLER_MUI_ICON_CODE@
@CPACK_NSIS_INSTALLER_ICON_CODE@ @CPACK_NSIS_INSTALLER_ICON_CODE@
@CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC@
;-------------------------------- ;--------------------------------
;Pages ;Pages
@ -425,6 +488,8 @@ FunctionEnd
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
@CPACK_NSIS_PAGE_COMPONENTS@
!insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH !insertmacro MUI_PAGE_FINISH
@ -446,16 +511,14 @@ FunctionEnd
ReserveFile "NSIS.InstallOptions.ini" ReserveFile "NSIS.InstallOptions.ini"
!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
;-------------------------------- ;--------------------------------
;Installer Sections ;Installer Sections
Section "Installer Section" InstSection Section "-Core installation"
;Use the entire tree produced by the INSTALL target. Keep the ;Use the entire tree produced by the INSTALL target. Keep the
;list of directories here in sync with the RMDir commands below. ;list of directories here in sync with the RMDir commands below.
SetOutPath "$INSTDIR" SetOutPath "$INSTDIR"
File /r "${INST_DIR}\*.*" @CPACK_NSIS_FULL_INSTALL@
;Store installation folder ;Store installation folder
WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
@ -474,6 +537,7 @@ Section "Installer Section" InstSection
Push "UninstallString" Push "UninstallString"
Push "$INSTDIR\Uninstall.exe" Push "$INSTDIR\Uninstall.exe"
Call ConditionalAddToRegisty Call ConditionalAddToRegisty
Push "ModifyPath"
; Optional registration ; Optional registration
Push "DisplayIcon" Push "DisplayIcon"
@ -520,7 +584,7 @@ Section "Installer Section" InstSection
SectionEnd SectionEnd
Section "Add to path" Section "-Add to path"
Push $INSTDIR\bin Push $INSTDIR\bin
;Read a value from an InstallOptions INI file ;Read a value from an InstallOptions INI file
!insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State" !insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State"
@ -532,7 +596,6 @@ Section "Add to path"
doNotAddToPath: doNotAddToPath:
SectionEnd SectionEnd
;-------------------------------- ;--------------------------------
; Create custom pages ; Create custom pages
Function InstallOptionsPage Function InstallOptionsPage
@ -564,6 +627,26 @@ Function un.onInit
;Get installation folder from registry if available ;Get installation folder from registry if available
done: 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 FunctionEnd
;-------------------------------- ;--------------------------------
@ -601,6 +684,9 @@ Section "Uninstall"
; Remove the registry entries. ; Remove the registry entries.
DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" 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 !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
@ -647,4 +733,50 @@ Section "Uninstall"
doNotRemoveFromPath: doNotRemoveFromPath:
SectionEnd 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

View File

@ -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

View File

@ -23,6 +23,7 @@
#include "cmGlobalGenerator.h" #include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h" #include "cmLocalGenerator.h"
#include "cmGeneratedFileStream.h" #include "cmGeneratedFileStream.h"
#include "cmCPackComponentGroup.h"
#include <cmsys/SystemTools.hxx> #include <cmsys/SystemTools.hxx>
#include <cmsys/Glob.hxx> #include <cmsys/Glob.hxx>
@ -101,13 +102,6 @@ int cmCPackGenerator::PrepareNames()
std::string destFile = pdir; std::string destFile = pdir;
destFile += "/" + outName; destFile += "/" + outName;
std::string outFile = topDirectory + "/" + 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_TOPLEVEL_DIRECTORY", topDirectory.c_str());
this->SetOptionIfNotSet("CPACK_TEMPORARY_DIRECTORY", tempDirectory.c_str()); this->SetOptionIfNotSet("CPACK_TEMPORARY_DIRECTORY", tempDirectory.c_str());
this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_NAME", outName.c_str()); this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_NAME", outName.c_str());
@ -118,7 +112,7 @@ int cmCPackGenerator::PrepareNames()
this->SetOptionIfNotSet("CPACK_NATIVE_INSTALL_DIRECTORY", this->SetOptionIfNotSet("CPACK_NATIVE_INSTALL_DIRECTORY",
cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()).c_str()); cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()).c_str());
this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY", this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY",
installPrefix.c_str()); tempDirectory.c_str());
cmCPackLogger(cmCPackLog::LOG_DEBUG, cmCPackLogger(cmCPackLog::LOG_DEBUG,
"Look for: CPACK_PACKAGE_DESCRIPTION_FILE" << std::endl); "Look for: CPACK_PACKAGE_DESCRIPTION_FILE" << std::endl);
@ -172,11 +166,19 @@ int cmCPackGenerator::InstallProject()
{ {
cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Install projects" << std::endl); cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Install projects" << std::endl);
this->CleanTemporaryDirectory(); this->CleanTemporaryDirectory();
std::string tempInstallDirectoryWithPostfix
std::string bareTempInstallDirectory
= this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY"); = 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; int res = 1;
if ( !cmsys::SystemTools::MakeDirectory(tempInstallDirectory)) if ( !cmsys::SystemTools::MakeDirectory(bareTempInstallDirectory.c_str()))
{ {
cmCPackLogger(cmCPackLog::LOG_ERROR, cmCPackLogger(cmCPackLog::LOG_ERROR,
"Problem creating temporary directory: " "Problem creating temporary directory: "
@ -185,7 +187,6 @@ int cmCPackGenerator::InstallProject()
return 0; return 0;
} }
bool setDestDir = cmSystemTools::IsOn(this->GetOption("CPACK_SET_DESTDIR"));
if ( setDestDir ) if ( setDestDir )
{ {
std::string destDir = "DESTDIR="; std::string destDir = "DESTDIR=";
@ -227,7 +228,7 @@ int cmCPackGenerator::InstallProject()
// If the project is a CMAKE project then run pre-install // If the project is a CMAKE project then run pre-install
// and then read the cmake_install script to run it // and then read the cmake_install script to run it
if ( !this->InstallProjectViaInstallCMakeProjects( if ( !this->InstallProjectViaInstallCMakeProjects(
setDestDir, tempInstallDirectory) ) setDestDir, bareTempInstallDirectory.c_str()) )
{ {
return 0; return 0;
} }
@ -244,8 +245,6 @@ int cmCPackGenerator::InstallProject()
int cmCPackGenerator::InstallProjectViaInstallCommands( int cmCPackGenerator::InstallProjectViaInstallCommands(
bool setDestDir, const char* tempInstallDirectory) bool setDestDir, const char* tempInstallDirectory)
{ {
(void)setDestDir;
(void)tempInstallDirectory;
const char* installCommands = this->GetOption("CPACK_INSTALL_COMMANDS"); const char* installCommands = this->GetOption("CPACK_INSTALL_COMMANDS");
if ( installCommands && *installCommands ) if ( installCommands && *installCommands )
{ {
@ -454,7 +453,7 @@ int cmCPackGenerator::InstallProjectViaInstallScript(
//---------------------------------------------------------------------- //----------------------------------------------------------------------
int cmCPackGenerator::InstallProjectViaInstallCMakeProjects( int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
bool setDestDir, const char* tempInstallDirectory) bool setDestDir, const char* baseTempInstallDirectory)
{ {
const char* cmakeProjects const char* cmakeProjects
= this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS"); = this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS");
@ -502,6 +501,51 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
std::string installSubDirectory = it->c_str(); std::string installSubDirectory = it->c_str();
std::string installFile = installDirectory + "/cmake_install.cmake"; 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"); const char* buildConfig = this->GetOption("CPACK_BUILD_CONFIG");
cmGlobalGenerator* globalGenerator cmGlobalGenerator* globalGenerator
= this->MakefileMap->GetCMakeInstance()->CreateGlobalGenerator( = this->MakefileMap->GetCMakeInstance()->CreateGlobalGenerator(
@ -555,72 +599,100 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
cmCPackLogger(cmCPackLog::LOG_OUTPUT, cmCPackLogger(cmCPackLog::LOG_OUTPUT,
"- Install project: " << installProjectName << std::endl); "- 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 std::string tempInstallDirectory = baseTempInstallDirectory;
// underneath the tempInstallDirectory. The value of the project's installComponent = *componentIt;
// CMAKE_INSTALL_PREFIX is sent in here as the value of the if (componentInstall)
// CPACK_INSTALL_PREFIX variable.
std::string dir;
if (this->GetOption("CPACK_INSTALL_PREFIX"))
{ {
dir += this->GetOption("CPACK_INSTALL_PREFIX"); cmCPackLogger(cmCPackLog::LOG_OUTPUT,
"- Install component: " << installComponent
<< std::endl);
} }
mf->AddDefinition("CMAKE_INSTALL_PREFIX", dir.c_str());
cmCPackLogger(cmCPackLog::LOG_DEBUG, cmake cm;
"- Using DESTDIR + CPACK_INSTALL_PREFIX... (mf->AddDefinition)" cm.AddCMakePaths();
<< std::endl); cm.SetProgressCallback(cmCPackGeneratorProgress, this);
cmCPackLogger(cmCPackLog::LOG_DEBUG, cmGlobalGenerator gg;
"- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'" << std::endl); gg.SetCMakeInstance(&cm);
} std::auto_ptr<cmLocalGenerator> lg(gg.CreateLocalGenerator());
else lg->SetGlobalGenerator(&gg);
{ cmMakefile *mf = lg->GetMakefile();
mf->AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory); std::string realInstallDirectory = tempInstallDirectory;
if ( !installSubDirectory.empty() && installSubDirectory != "/" )
{
realInstallDirectory += installSubDirectory;
}
if (componentInstall)
{
tempInstallDirectory += "/";
tempInstallDirectory += installComponent;
}
cmCPackLogger(cmCPackLog::LOG_DEBUG, if (!setDestDir)
"- Using non-DESTDIR install... (mf->AddDefinition)" << std::endl); {
cmCPackLogger(cmCPackLog::LOG_DEBUG, tempInstallDirectory += this->GetPackagingInstallPrefix();
"- Setting CMAKE_INSTALL_PREFIX to '" << tempInstallDirectory }
<< "'" << std::endl);
}
if ( buildConfig && *buildConfig ) if ( setDestDir )
{ {
mf->AddDefinition("BUILD_TYPE", buildConfig); // For DESTDIR based packaging, use the *project* CMAKE_INSTALL_PREFIX
} // underneath the tempInstallDirectory. The value of the project's
std::string installComponentLowerCase // CMAKE_INSTALL_PREFIX is sent in here as the value of the
= cmSystemTools::LowerCase(installComponent); // CPACK_INSTALL_PREFIX variable.
if ( installComponentLowerCase != "all" ) std::string dir;
{ if (this->GetOption("CPACK_INSTALL_PREFIX"))
mf->AddDefinition("CMAKE_INSTALL_COMPONENT", {
installComponent.c_str()); dir += this->GetOption("CPACK_INSTALL_PREFIX");
} }
mf->AddDefinition("CMAKE_INSTALL_PREFIX", dir.c_str());
// strip on TRUE, ON, 1, one or several file names, but not on cmCPackLogger(cmCPackLog::LOG_DEBUG,
// FALSE, OFF, 0 and an empty string "- Using DESTDIR + CPACK_INSTALL_PREFIX... (mf->AddDefinition)"
if (!cmSystemTools::IsOff(this->GetOption("CPACK_STRIP_FILES"))) << std::endl);
{ cmCPackLogger(cmCPackLog::LOG_DEBUG,
mf->AddDefinition("CMAKE_INSTALL_DO_STRIP", "1"); "- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'"
} << std::endl);
int res = mf->ReadListFile(0, installFile.c_str()); }
if ( cmSystemTools::GetErrorOccuredFlag() || !res ) else
{ {
return 0; mf->AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory.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);
}
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* packageFileName = this->GetOption("CPACK_OUTPUT_FILE_PATH");
const char* tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); const char* tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl); cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl);
cmsys::Glob gl; cmsys::Glob gl;
std::string findExpr = tempDirectory; std::string findExpr = tempDirectory;
@ -736,8 +807,33 @@ int cmCPackGenerator::DoPackage()
{ {
tempDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); 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, if ( !this->CompressFiles(tempPackageFileName,
tempDirectory, gl.GetFiles()) || cmSystemTools::GetErrorOccuredFlag()) tempDirectory, files) || cmSystemTools::GetErrorOccuredFlag())
{ {
cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem compressing the directory" cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem compressing the directory"
<< std::endl); << std::endl);
@ -1092,3 +1188,158 @@ int cmCPackGenerator::CleanTemporaryDirectory()
} }
return 1; 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;
}

View File

@ -19,6 +19,8 @@
#define cmCPackGenerator_h #define cmCPackGenerator_h
#include "cmObject.h" #include "cmObject.h"
#include <map>
#include <vector>
#define cmCPackTypeMacro(class, superclass) \ #define cmCPackTypeMacro(class, superclass) \
cmTypeMacro(class, superclass); \ cmTypeMacro(class, superclass); \
@ -44,6 +46,9 @@
class cmMakefile; class cmMakefile;
class cmCPackLog; class cmCPackLog;
class cmCPackInstallationType;
class cmCPackComponent;
class cmCPackComponentGroup;
/** \class cmCPackGenerator /** \class cmCPackGenerator
* \brief A superclass of all CPack Generators * \brief A superclass of all CPack Generators
@ -120,6 +125,11 @@ protected:
virtual int InstallProjectViaInstallCMakeProjects( virtual int InstallProjectViaInstallCMakeProjects(
bool setDestDir, const char* tempInstallDirectory); 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; bool GeneratorVerbose;
std::string Name; std::string Name;
@ -129,6 +139,10 @@ protected:
std::string CMakeSelf; std::string CMakeSelf;
std::string CMakeRoot; std::string CMakeRoot;
std::map<std::string, cmCPackInstallationType> InstallationTypes;
std::map<std::string, cmCPackComponent> Components;
std::map<std::string, cmCPackComponentGroup> ComponentGroups;
cmCPackLog* Logger; cmCPackLog* Logger;
private: private:
cmMakefile* MakefileMap; cmMakefile* MakefileMap;

View File

@ -23,6 +23,7 @@
#include "cmMakefile.h" #include "cmMakefile.h"
#include "cmGeneratedFileStream.h" #include "cmGeneratedFileStream.h"
#include "cmCPackLog.h" #include "cmCPackLog.h"
#include "cmCPackComponentGroup.h"
#include <cmsys/SystemTools.hxx> #include <cmsys/SystemTools.hxx>
#include <cmsys/Glob.hxx> #include <cmsys/Glob.hxx>
@ -79,7 +80,12 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
for ( it = files.begin(); it != files.end(); ++ it ) for ( it = files.begin(); it != files.end(); ++ it )
{ {
std::string fileN = cmSystemTools::RelativePath(toplevel, 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, "/", "\\"); cmSystemTools::ReplaceString(fileN, "/", "\\");
str << " Delete \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl; str << " Delete \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
} }
@ -92,14 +98,32 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
cmOStringStream dstr; cmOStringStream dstr;
for ( sit = dirs.begin(); sit != dirs.end(); ++ sit ) for ( sit = dirs.begin(); sit != dirs.end(); ++ sit )
{ {
std::string fileN = cmSystemTools::RelativePath(toplevel, std::string componentName;
sit->c_str()); std::string fileN = cmSystemTools::RelativePath(toplevel, sit->c_str());
if ( fileN.empty() ) if ( fileN.empty() )
{ {
continue; 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, "/", "\\"); cmSystemTools::ReplaceString(fileN, "/", "\\");
dstr << " RMDir \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl; dstr << " RMDir \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
if (!componentName.empty())
{
this->Components[componentName].Directories.push_back(fileN);
}
} }
cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Dirs: " cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Dirs: "
<< dstr.str().c_str() << std::endl); << dstr.str().c_str() << std::endl);
@ -128,6 +152,116 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE", this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE",
installerIconCode.c_str()); 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(), this->ConfigureFile(nsisInInstallOptions.c_str(),
nsisInstallOptions.c_str()); nsisInstallOptions.c_str());
this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str()); this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str());
@ -252,9 +386,8 @@ int cmCPackNSISGenerator::InitializeInternal()
} }
else else
{ {
cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: " cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
<< "not set" << std::endl); << "not set" << std::endl);
} }
if ( cpackPackageExecutables ) if ( cpackPackageExecutables )
{ {
@ -274,8 +407,8 @@ int cmCPackNSISGenerator::InitializeInternal()
} }
std::vector<std::string>::iterator it; std::vector<std::string>::iterator it;
for ( it = cpackPackageExecutablesVector.begin(); for ( it = cpackPackageExecutablesVector.begin();
it != cpackPackageExecutablesVector.end(); it != cpackPackageExecutablesVector.end();
++it ) ++it )
{ {
std::string execName = *it; std::string execName = *it;
++ it; ++ it;
@ -415,3 +548,183 @@ bool cmCPackNSISGenerator::GetListOfSubdirectories(const char* topdir,
dirs.push_back(topdir); dirs.push_back(topdir);
return true; 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;
}

View File

@ -20,6 +20,7 @@
#include "cmCPackGenerator.h" #include "cmCPackGenerator.h"
#include <set>
/** \class cmCPackNSISGenerator /** \class cmCPackNSISGenerator
* \brief A generator for NSIS files * \brief A generator for NSIS files
@ -48,6 +49,32 @@ protected:
bool GetListOfSubdirectories(const char* dir, bool GetListOfSubdirectories(const char* dir,
std::vector<std::string>& dirs); 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 #endif

View File

@ -22,6 +22,7 @@
#include "cmSystemTools.h" #include "cmSystemTools.h"
#include "cmMakefile.h" #include "cmMakefile.h"
#include "cmGeneratedFileStream.h" #include "cmGeneratedFileStream.h"
#include "cmCPackComponentGroup.h"
#include "cmCPackLog.h" #include "cmCPackLog.h"
#include <cmsys/SystemTools.hxx> #include <cmsys/SystemTools.hxx>
@ -38,6 +39,13 @@ cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator()
{ {
} }
//----------------------------------------------------------------------
bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const
{
return true;
}
//----------------------------------------------------------------------
int cmCPackPackageMakerGenerator::CopyInstallScript(const char* resdir, int cmCPackPackageMakerGenerator::CopyInstallScript(const char* resdir,
const char* script, const char* script,
const char* name) const char* name)
@ -96,23 +104,59 @@ int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName,
// them executable // them executable
if(preflight) if(preflight)
{ {
this->CopyInstallScript(resDir.c_str(), this->CopyInstallScript(resDir.c_str(),
preflight, preflight,
"preflight"); "preflight");
} }
if(postflight) if(postflight)
{ {
this->CopyInstallScript(resDir.c_str(), this->CopyInstallScript(resDir.c_str(),
postflight, postflight,
"postflight"); "postflight");
} }
if(postupgrade) if(postupgrade)
{ {
this->CopyInstallScript(resDir.c_str(), this->CopyInstallScript(resDir.c_str(),
postupgrade, postupgrade,
"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") if ( !this->CopyCreateResourceFile("License")
|| !this->CopyCreateResourceFile("ReadMe") || !this->CopyCreateResourceFile("ReadMe")
|| !this->CopyCreateResourceFile("Welcome") || !this->CopyCreateResourceFile("Welcome")
@ -126,68 +170,58 @@ int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName,
std::string packageDirFileName std::string packageDirFileName
= this->GetOption("CPACK_TEMPORARY_DIRECTORY"); = 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; cmOStringStream pkgCmd;
pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM") pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
<< "\" -build -p \"" << packageDirFileName << "\" -f \"" << "\" -build -p \"" << packageDirFileName << "\"";
<< this->GetOption("CPACK_TEMPORARY_DIRECTORY") if (this->Components.empty())
<< "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") {
<< "/Resources\" -i \"" pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
<< this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Info.plist\" -d \"" }
<< this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Description.plist\""; else
{
pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
<< "/packages/";
}
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 ) if ( this->PackageMakerVersion > 2.0 )
{ {
pkgCmd << " -v"; pkgCmd << " -v";
} }
cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << pkgCmd.str().c_str() if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.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 )
{
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; return 0;
}
// sometimes the pkgCmd finishes but the directory is not yet if (!this->Components.empty())
// created, so try 10 times to see if it shows up
int tries = 10;
while(tries > 0 &&
!cmSystemTools::FileExists(packageDirFileName.c_str()))
{ {
cmSystemTools::Delay(500); WriteDistributionFile(packageDirFileName.c_str());
tries--;
} }
if(!cmSystemTools::FileExists(packageDirFileName.c_str()))
{ std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
cmCPackLogger(
cmCPackLog::LOG_ERROR,
"Problem running PackageMaker command: " << pkgCmd.str().c_str()
<< std::endl << "Package not created: " << packageDirFileName.c_str()
<< std::endl);
}
tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
tmpFile += "/hdiutilOutput.log"; tmpFile += "/hdiutilOutput.log";
cmOStringStream dmgCmd; cmOStringStream dmgCmd;
dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE") dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
<< "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName << "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName
<< "\" \"" << outFileName << "\""; << "\" \"" << 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); &retVal, 0, this->GeneratorVerbose, 0);
if ( !res || retVal ) if ( !res || retVal )
{ {
@ -339,8 +373,14 @@ bool cmCPackPackageMakerGenerator::CopyCreateResourceFile(const char* name)
return true; 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."; std::string inFName = "CPack.";
inFName += name; inFName += name;
inFName += ".in"; inFName += ".in";
@ -354,10 +394,374 @@ bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name)
std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
destFileName += "/"; destFileName += "/";
destFileName += name; destFileName += outName;
cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
<< inFileName.c_str() << " to " << destFileName.c_str() << std::endl); << inFileName.c_str() << " to " << destFileName.c_str() << std::endl);
this->ConfigureFile(inFileName.c_str(), destFileName.c_str()); this->ConfigureFile(inFileName.c_str(), destFileName.c_str());
return true; 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;
}

View File

@ -21,6 +21,8 @@
#include "cmCPackGenerator.h" #include "cmCPackGenerator.h"
class cmCPackComponent;
/** \class cmCPackPackageMakerGenerator /** \class cmCPackPackageMakerGenerator
* \brief A generator for PackageMaker files * \brief A generator for PackageMaker files
* *
@ -38,6 +40,8 @@ public:
cmCPackPackageMakerGenerator(); cmCPackPackageMakerGenerator();
virtual ~cmCPackPackageMakerGenerator(); virtual ~cmCPackPackageMakerGenerator();
virtual bool SupportsComponentInstallation() const;
protected: protected:
int CopyInstallScript(const char* resdir, int CopyInstallScript(const char* resdir,
const char* script, const char* script,
@ -49,7 +53,62 @@ protected:
virtual const char* GetOutputPostfix() { return "darwin"; } virtual const char* GetOutputPostfix() { return "darwin"; }
bool CopyCreateResourceFile(const char* name); 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; double PackageMakerVersion;
}; };

View File

@ -27,6 +27,34 @@ IF(BUILD_TESTING)
MARK_AS_ADVANCED(CTEST_TEST_CTEST) MARK_AS_ADVANCED(CTEST_TEST_CTEST)
ENDIF (CMAKE_RUN_LONG_TESTS) 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, # Use 1500 or CTEST_TEST_TIMEOUT for long test timeout value,
# whichever is greater. # whichever is greater.
SET(CMAKE_LONG_TEST_TIMEOUT 1500) 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" "-DSTAGE2:BOOL=1"
--test-command ${SimpleInstallInstallDir}/MyTest/bin/SimpleInstExeS2) --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} ADD_TEST(X11 ${CMAKE_CTEST_COMMAND}
--build-and-test --build-and-test
"${CMake_SOURCE_DIR}/Tests/X11" "${CMake_SOURCE_DIR}/Tests/X11"

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,7 @@
#include "mylib.h"
#include "stdio.h"
void mylib_function()
{
printf("This is mylib");
}

View File

@ -0,0 +1 @@
void mylib_function();

View File

@ -0,0 +1,6 @@
#include "mylib.h"
int main()
{
mylib_function();
}

View File

@ -355,27 +355,11 @@ SET(CMAKE_INSTALL_DEBUG_LIBRARIES 1)
INCLUDE(InstallRequiredSystemLibraries) INCLUDE(InstallRequiredSystemLibraries)
INCLUDE(CPack) INCLUDE(CPack)
# Disable packaging test on Apple 10.3 and below. PackageMaker starts IF(CTEST_TEST_CPACK)
# DiskManagementTool as root and disowns it SET(PACKAGE_TARGET --build-target package)
# (http://lists.apple.com/archives/installer-dev/2005/Jul/msg00005.html). ELSE(CTEST_TEST_CPACK)
# It is left holding open pipe handles and preventing ProcessUNIX from SET(PACKAGE_TARGET)
# detecting end-of-data even after its immediate child exits. Then ENDIF(CTEST_TEST_CPACK)
# 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)
ADD_CUSTOM_COMMAND( ADD_CUSTOM_COMMAND(
TARGET ${install_target} TARGET ${install_target}

View File

@ -355,27 +355,11 @@ SET(CMAKE_INSTALL_DEBUG_LIBRARIES 1)
INCLUDE(InstallRequiredSystemLibraries) INCLUDE(InstallRequiredSystemLibraries)
INCLUDE(CPack) INCLUDE(CPack)
# Disable packaging test on Apple 10.3 and below. PackageMaker starts IF(CTEST_TEST_CPACK)
# DiskManagementTool as root and disowns it SET(PACKAGE_TARGET --build-target package)
# (http://lists.apple.com/archives/installer-dev/2005/Jul/msg00005.html). ELSE(CTEST_TEST_CPACK)
# It is left holding open pipe handles and preventing ProcessUNIX from SET(PACKAGE_TARGET)
# detecting end-of-data even after its immediate child exits. Then ENDIF(CTEST_TEST_CPACK)
# 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)
ADD_CUSTOM_COMMAND( ADD_CUSTOM_COMMAND(
TARGET ${install_target} TARGET ${install_target}