[Cmake-commits] [cmake-commits] david.cole committed cmCPackGenerator.cxx 1.6 1.7 cmCPackGenerator.h 1.2 1.3 cmCPackNSISGenerator.cxx 1.31 1.32 cmCPackNSISGenerator.h 1.11 1.12 cmCPackPackageMakerGenerator.cxx 1.23 1.24 cmCPackPackageMakerGenerator.h 1.12 1.13 cmCPackComponentGroup.h NONE 1.1

cmake-commits at cmake.org cmake-commits at cmake.org
Tue Jun 17 11:39:28 EDT 2008


Update of /cvsroot/CMake/CMake/Source/CPack
In directory public:/mounts/ram/cvs-serv23549/Source/CPack

Modified Files:
	cmCPackGenerator.cxx cmCPackGenerator.h 
	cmCPackNSISGenerator.cxx cmCPackNSISGenerator.h 
	cmCPackPackageMakerGenerator.cxx 
	cmCPackPackageMakerGenerator.h 
Added Files:
	cmCPackComponentGroup.h 
Log Message:
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.


Index: cmCPackGenerator.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CPack/cmCPackGenerator.cxx,v
retrieving revision 1.6
retrieving revision 1.7
diff -C 2 -d -r1.6 -r1.7
*** cmCPackGenerator.cxx	13 Mar 2008 01:54:27 -0000	1.6
--- cmCPackGenerator.cxx	17 Jun 2008 15:39:25 -0000	1.7
***************
*** 24,27 ****
--- 24,28 ----
  #include "cmLocalGenerator.h"
  #include "cmGeneratedFileStream.h"
+ #include "cmCPackComponentGroup.h"
  
  #include <cmsys/SystemTools.hxx>
***************
*** 102,112 ****
    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());
--- 103,106 ----
***************
*** 119,123 ****
      cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()).c_str());
    this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY",
!                           installPrefix.c_str());
  
    cmCPackLogger(cmCPackLog::LOG_DEBUG,
--- 113,117 ----
      cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()).c_str());
    this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY",
!                           tempDirectory.c_str());
  
    cmCPackLogger(cmCPackLog::LOG_DEBUG,
***************
*** 173,181 ****
    cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Install projects" << std::endl);
    this->CleanTemporaryDirectory();
!   std::string tempInstallDirectoryWithPostfix
      = this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY");
!   const char* tempInstallDirectory = tempInstallDirectoryWithPostfix.c_str();
    int res = 1;
!   if ( !cmsys::SystemTools::MakeDirectory(tempInstallDirectory))
      {
      cmCPackLogger(cmCPackLog::LOG_ERROR,
--- 167,183 ----
    cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Install projects" << std::endl);
    this->CleanTemporaryDirectory();
! 
!   std::string bareTempInstallDirectory
      = this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY");
!   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(bareTempInstallDirectory.c_str()))
      {
      cmCPackLogger(cmCPackLog::LOG_ERROR,
***************
*** 186,190 ****
      }
  
-   bool setDestDir = cmSystemTools::IsOn(this->GetOption("CPACK_SET_DESTDIR"));
    if ( setDestDir )
      {
--- 188,191 ----
***************
*** 228,232 ****
    // and then read the cmake_install script to run it
    if ( !this->InstallProjectViaInstallCMakeProjects(
!       setDestDir, tempInstallDirectory) )
      {
      return 0;
--- 229,233 ----
    // and then read the cmake_install script to run it
    if ( !this->InstallProjectViaInstallCMakeProjects(
!          setDestDir, bareTempInstallDirectory.c_str()) )
      {
      return 0;
***************
*** 245,250 ****
    bool setDestDir, const char* tempInstallDirectory)
  {
-   (void)setDestDir;
-   (void)tempInstallDirectory;
    const char* installCommands = this->GetOption("CPACK_INSTALL_COMMANDS");
    if ( installCommands && *installCommands )
--- 246,249 ----
***************
*** 455,459 ****
  //----------------------------------------------------------------------
  int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
!   bool setDestDir, const char* tempInstallDirectory)
  {
    const char* cmakeProjects
--- 454,458 ----
  //----------------------------------------------------------------------
  int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
!   bool setDestDir, const char* baseTempInstallDirectory)
  {
    const char* cmakeProjects
***************
*** 503,506 ****
--- 502,550 ----
        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
***************
*** 556,625 ****
        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 )
          {
!         // 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);
  
!         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;
          }
        }
--- 600,697 ----
        cmCPackLogger(cmCPackLog::LOG_OUTPUT,
          "- Install project: " << installProjectName << std::endl);
  
!       // Run the installation for each component
!       std::vector<std::string>::iterator componentIt;
!       for (componentIt = componentsVector.begin();
!            componentIt != componentsVector.end();
!            ++componentIt)
          {
!         std::string tempInstallDirectory = baseTempInstallDirectory;
!         installComponent = *componentIt;
!         if (componentInstall)
            {
!           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;
            }
  
!         if (!setDestDir)
!           {
!           tempInstallDirectory += this->GetPackagingInstallPrefix();
!           }
  
!         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());
  
!           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;
!           }
          }
        }
***************
*** 710,714 ****
    const char* tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  
- 
    cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl);
    cmsys::Glob gl;
--- 782,785 ----
***************
*** 737,742 ****
      tempDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
      }
    if ( !this->CompressFiles(tempPackageFileName,
!       tempDirectory, gl.GetFiles()) || cmSystemTools::GetErrorOccuredFlag())
      {
      cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem compressing the directory"
--- 808,838 ----
      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, files) || cmSystemTools::GetErrorOccuredFlag())
      {
      cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem compressing the directory"
***************
*** 1093,1094 ****
--- 1189,1345 ----
    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;
+ }

Index: cmCPackGenerator.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CPack/cmCPackGenerator.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -C 2 -d -r1.2 -r1.3
*** cmCPackGenerator.h	6 Nov 2007 13:28:26 -0000	1.2
--- cmCPackGenerator.h	17 Jun 2008 15:39:25 -0000	1.3
***************
*** 20,23 ****
--- 20,25 ----
  
  #include "cmObject.h"
+ #include <map>
+ #include <vector>
  
  #define cmCPackTypeMacro(class, superclass) \
***************
*** 45,48 ****
--- 47,53 ----
  class cmMakefile;
  class cmCPackLog;
+ class cmCPackInstallationType;
+ class cmCPackComponent;
+ class cmCPackComponentGroup;
  
  /** \class cmCPackGenerator
***************
*** 121,124 ****
--- 126,134 ----
      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;
***************
*** 130,133 ****
--- 140,147 ----
    std::string CMakeRoot;
  
+   std::map<std::string, cmCPackInstallationType> InstallationTypes;
+   std::map<std::string, cmCPackComponent> Components;
+   std::map<std::string, cmCPackComponentGroup> ComponentGroups;
+ 
    cmCPackLog* Logger;
  private:

Index: cmCPackPackageMakerGenerator.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CPack/cmCPackPackageMakerGenerator.h,v
retrieving revision 1.12
retrieving revision 1.13
diff -C 2 -d -r1.12 -r1.13
*** cmCPackPackageMakerGenerator.h	19 Feb 2008 19:26:19 -0000	1.12
--- cmCPackPackageMakerGenerator.h	17 Jun 2008 15:39:26 -0000	1.13
***************
*** 22,25 ****
--- 22,27 ----
  #include "cmCPackGenerator.h"
  
+ class cmCPackComponent;
+ 
  /** \class cmCPackPackageMakerGenerator
   * \brief A generator for PackageMaker files
***************
*** 39,42 ****
--- 41,46 ----
    virtual ~cmCPackPackageMakerGenerator();
  
+   virtual bool SupportsComponentInstallation() const;
+ 
  protected:
    int CopyInstallScript(const char* resdir,
***************
*** 50,55 ****
  
    bool CopyCreateResourceFile(const char* name);
!   bool CopyResourcePlistFile(const char* name);
  
    double PackageMakerVersion;
  };
--- 54,114 ----
  
    bool CopyCreateResourceFile(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;
  };

Index: cmCPackNSISGenerator.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CPack/cmCPackNSISGenerator.h,v
retrieving revision 1.11
retrieving revision 1.12
diff -C 2 -d -r1.11 -r1.12
*** cmCPackNSISGenerator.h	5 Nov 2007 21:55:45 -0000	1.11
--- cmCPackNSISGenerator.h	17 Jun 2008 15:39:26 -0000	1.12
***************
*** 21,24 ****
--- 21,25 ----
  
  #include "cmCPackGenerator.h"
+ #include <set>
  
  /** \class cmCPackNSISGenerator
***************
*** 49,52 ****
--- 50,79 ----
    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);
  };
  

Index: cmCPackPackageMakerGenerator.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CPack/cmCPackPackageMakerGenerator.cxx,v
retrieving revision 1.23
retrieving revision 1.24
diff -C 2 -d -r1.23 -r1.24
*** cmCPackPackageMakerGenerator.cxx	19 Feb 2008 19:26:19 -0000	1.23
--- cmCPackPackageMakerGenerator.cxx	17 Jun 2008 15:39:26 -0000	1.24
***************
*** 23,26 ****
--- 23,27 ----
  #include "cmMakefile.h"
  #include "cmGeneratedFileStream.h"
+ #include "cmCPackComponentGroup.h"
  #include "cmCPackLog.h"
  
***************
*** 39,42 ****
--- 40,50 ----
  }
  
+ //----------------------------------------------------------------------
+ bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const
+ {
+   return true;
+ }
+ 
+ //----------------------------------------------------------------------
  int cmCPackPackageMakerGenerator::CopyInstallScript(const char* resdir,
                                                      const char* script,
***************
*** 97,117 ****
    if(preflight)
      {
!       this->CopyInstallScript(resDir.c_str(),
!                               preflight,
!                               "preflight");
      }
    if(postflight)
      {
!       this->CopyInstallScript(resDir.c_str(),
!                               postflight,
!                               "postflight");
      }
    if(postupgrade)
      {
!       this->CopyInstallScript(resDir.c_str(),
!                               postupgrade,
!                               "postupgrade");
      }
  
    if ( !this->CopyCreateResourceFile("License")
      || !this->CopyCreateResourceFile("ReadMe")
--- 105,161 ----
    if(preflight)
      {
!     this->CopyInstallScript(resDir.c_str(),
!                             preflight,
!                             "preflight");
      }
    if(postflight)
      {
!     this->CopyInstallScript(resDir.c_str(),
!                             postflight,
!                             "postflight");
      }
    if(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")
***************
*** 127,186 ****
    std::string packageDirFileName
      = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
!   packageDirFileName += ".pkg";
  
-   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 )
      {
!     pkgCmd << " -v";
      }
!   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 )
      {
!     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;
      }
!   // 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()))
      {
!     cmSystemTools::Delay(500);
!     tries--;
      }
!   if(!cmSystemTools::FileExists(packageDirFileName.c_str()))
      {
!     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";
    cmOStringStream dmgCmd;
--- 171,218 ----
    std::string packageDirFileName
      = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
!   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);
!       }
!     }
  
    cmOStringStream pkgCmd;
    pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
!          << "\" -build -p \"" << packageDirFileName << "\"";
!   if (this->Components.empty())
      {
!     pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
      }
!   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 )
      {
!     pkgCmd << " -v";
      }
!   if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str()))
!     return 0;
! 
!   if (!this->Components.empty())
      {
!     WriteDistributionFile(packageDirFileName.c_str());
      }
! 
!   std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
    tmpFile += "/hdiutilOutput.log";
    cmOStringStream dmgCmd;
***************
*** 188,192 ****
      << "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName
      << "\" \"" << outFileName << "\"";
!   res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
      &retVal, 0, this->GeneratorVerbose, 0);
    if ( !res || retVal )
--- 220,226 ----
      << "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName
      << "\" \"" << outFileName << "\"";
!   std::string output;
!   int retVal = 1;
!   bool res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
      &retVal, 0, this->GeneratorVerbose, 0);
    if ( !res || retVal )
***************
*** 340,345 ****
  }
  
! bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name)
  {
    std::string inFName = "CPack.";
    inFName += name;
--- 374,385 ----
  }
  
! bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name,
!                                                          const char* outName)
  {
+   if (!outName)
+     {
+     outName = name;
+     }
+ 
    std::string inFName = "CPack.";
    inFName += name;
***************
*** 355,359 ****
    std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
    destFileName += "/";
!   destFileName += name;
  
    cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
--- 395,399 ----
    std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
    destFileName += "/";
!   destFileName += outName;
  
    cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
***************
*** 362,363 ****
--- 402,767 ----
    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;
+ }

--- NEW FILE: cmCPackComponentGroup.h ---
/*=========================================================================

  Program:   CMake - Cross-Platform Makefile Generator
  Module:    $RCSfile: cmCPackComponentGroup.h,v $
  Language:  C++
  Date:      $Date: 2008-06-17 15:39:26 $
  Version:   $Revision: 1.1 $

  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

Index: cmCPackNSISGenerator.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CPack/cmCPackNSISGenerator.cxx,v
retrieving revision 1.31
retrieving revision 1.32
diff -C 2 -d -r1.31 -r1.32
*** cmCPackNSISGenerator.cxx	15 Feb 2008 15:40:55 -0000	1.31
--- cmCPackNSISGenerator.cxx	17 Jun 2008 15:39:26 -0000	1.32
***************
*** 24,27 ****
--- 24,28 ----
  #include "cmGeneratedFileStream.h"
  #include "cmCPackLog.h"
+ #include "cmCPackComponentGroup.h"
  
  #include <cmsys/SystemTools.hxx>
***************
*** 80,84 ****
      {
      std::string fileN = cmSystemTools::RelativePath(toplevel,
!       it->c_str());
      cmSystemTools::ReplaceString(fileN, "/", "\\");
      str << "  Delete \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
--- 81,90 ----
      {
      std::string fileN = cmSystemTools::RelativePath(toplevel,
!                                                     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;
***************
*** 93,104 ****
    for ( sit = dirs.begin(); sit != dirs.end(); ++ sit )
      {
!     std::string fileN = cmSystemTools::RelativePath(toplevel,
!       sit->c_str());
      if ( fileN.empty() )
        {
        continue;
        }
      cmSystemTools::ReplaceString(fileN, "/", "\\");
      dstr << "  RMDir \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
      }
    cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Dirs: "
--- 99,128 ----
    for ( sit = dirs.begin(); sit != dirs.end(); ++ sit )
      {
!     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: "
***************
*** 129,132 ****
--- 153,266 ----
                              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());
***************
*** 253,259 ****
    else
      {
!         cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
!                 << "not set" << std::endl);
! 
      }
    if ( cpackPackageExecutables )
--- 387,392 ----
    else
      {
!     cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
!                   << "not set" << std::endl);
      }
    if ( cpackPackageExecutables )
***************
*** 275,280 ****
      std::vector<std::string>::iterator it;
      for ( it = cpackPackageExecutablesVector.begin();
!       it != cpackPackageExecutablesVector.end();
!       ++it )
        {
        std::string execName = *it;
--- 408,413 ----
      std::vector<std::string>::iterator it;
      for ( it = cpackPackageExecutablesVector.begin();
!           it != cpackPackageExecutablesVector.end();
!           ++it )
        {
        std::string execName = *it;
***************
*** 416,417 ****
--- 549,730 ----
    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;
+ }



More information about the Cmake-commits mailing list