diff options
author | David Cole <david.cole@kitware.com> | 2008-06-17 15:39:26 (GMT) |
---|---|---|
committer | David Cole <david.cole@kitware.com> | 2008-06-17 15:39:26 (GMT) |
commit | 1105a86c520d34a59b482b86b6f288c11ff45b81 (patch) | |
tree | 6814d23047d44fe8c2c3d18e40b98143aca186c4 /Source/CPack/cmCPackNSISGenerator.cxx | |
parent | 64498a1287e13e9f7ad3488c1050318030b18f8d (diff) | |
download | CMake-1105a86c520d34a59b482b86b6f288c11ff45b81.zip CMake-1105a86c520d34a59b482b86b6f288c11ff45b81.tar.gz CMake-1105a86c520d34a59b482b86b6f288c11ff45b81.tar.bz2 |
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.
Diffstat (limited to 'Source/CPack/cmCPackNSISGenerator.cxx')
-rw-r--r-- | Source/CPack/cmCPackNSISGenerator.cxx | 329 |
1 files changed, 321 insertions, 8 deletions
diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx index eb1cb1f..6b43e1e 100644 --- a/Source/CPack/cmCPackNSISGenerator.cxx +++ b/Source/CPack/cmCPackNSISGenerator.cxx @@ -23,6 +23,7 @@ #include "cmMakefile.h" #include "cmGeneratedFileStream.h" #include "cmCPackLog.h" +#include "cmCPackComponentGroup.h" #include <cmsys/SystemTools.hxx> #include <cmsys/Glob.hxx> @@ -79,7 +80,12 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName, for ( it = files.begin(); it != files.end(); ++ it ) { std::string fileN = cmSystemTools::RelativePath(toplevel, - it->c_str()); + it->c_str()); + if (!this->Components.empty()) + { + // Strip off the component part of the path. + fileN = fileN.substr(fileN.find('/')+1, std::string::npos); + } cmSystemTools::ReplaceString(fileN, "/", "\\"); str << " Delete \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl; } @@ -92,14 +98,32 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName, cmOStringStream dstr; for ( sit = dirs.begin(); sit != dirs.end(); ++ sit ) { - std::string fileN = cmSystemTools::RelativePath(toplevel, - sit->c_str()); + std::string componentName; + std::string fileN = cmSystemTools::RelativePath(toplevel, sit->c_str()); if ( fileN.empty() ) { continue; } + if (!Components.empty()) + { + // If this is a component installation, strip off the component + // part of the path. + std::string::size_type slash = fileN.find('/'); + if (slash != std::string::npos) + { + // If this is a component installation, determine which component it is. + componentName = fileN.substr(0, slash); + + // Strip off the component part of the path. + fileN = fileN.substr(slash+1, std::string::npos); + } + } cmSystemTools::ReplaceString(fileN, "/", "\\"); dstr << " RMDir \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl; + if (!componentName.empty()) + { + this->Components[componentName].Directories.push_back(fileN); + } } cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Dirs: " << dstr.str().c_str() << std::endl); @@ -128,6 +152,116 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName, this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE", installerIconCode.c_str()); } + + // Setup all of the component sections + if (this->Components.empty()) + { + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", ""); + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", ""); + this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", ""); + this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL", + "File /r \"${INST_DIR}\\*.*\""); + this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", ""); + this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", ""); + this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS", ""); + } + else + { + std::string componentCode; + std::string sectionList; + std::string selectedVarsList; + std::string componentDescriptions; + std::string groupDescriptions; + std::string installTypesCode; + + // Create installation types. The order is significant, so we first fill + // in a vector based on the indices, and print them in that order. + std::vector<cmCPackInstallationType *> + installTypes(this->InstallationTypes.size()); + std::map<std::string, cmCPackInstallationType>::iterator installTypeIt; + for (installTypeIt = this->InstallationTypes.begin(); + installTypeIt != this->InstallationTypes.end(); + ++installTypeIt) + { + installTypes[installTypeIt->second.Index-1] = &installTypeIt->second; + } + std::vector<cmCPackInstallationType *>::iterator installTypeIt2; + for (installTypeIt2 = installTypes.begin(); + installTypeIt2 != installTypes.end(); + ++installTypeIt2) + { + installTypesCode += "InstType \""; + installTypesCode += (*installTypeIt2)->DisplayName; + installTypesCode += + "\"\n"; + } + + // Create installation groups first + std::map<std::string, cmCPackComponentGroup>::iterator groupIt; + for (groupIt = this->ComponentGroups.begin(); + groupIt != this->ComponentGroups.end(); + ++groupIt) + { + componentCode += this->CreateComponentGroupDescription(&groupIt->second); + + // Add the group description, if any. + if (!groupIt->second.Description.empty()) + { + groupDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${" + + groupIt->first + "} \"" + + this->TranslateNewlines(groupIt->second.Description) + "\"\n"; + } + } + + // Create the remaining components, which aren't associated with groups. + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); + compIt != this->Components.end(); + ++compIt) + { + if (!compIt->second.Group) + { + componentCode += this->CreateComponentDescription(&compIt->second); + } + + // Add this component to the various section lists. + sectionList += " !insertmacro \"${MacroName}\" \""; + sectionList += compIt->first; + sectionList += "\"\n"; + selectedVarsList += "Var " + compIt->first + "_selected\n"; + + // Add the component description, if any. + if (!compIt->second.Description.empty()) + { + componentDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${" + + compIt->first + "} \"" + + this->TranslateNewlines(compIt->second.Description) + "\"\n"; + } + } + + if (componentDescriptions.empty() && groupDescriptions.empty()) + { + // Turn off the "Description" box + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", + "!define MUI_COMPONENTSPAGE_NODESC"); + } + else + { + componentDescriptions = + "!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN\n" + + componentDescriptions + + groupDescriptions + + "!insertmacro MUI_FUNCTION_DESCRIPTION_END\n"; + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", + componentDescriptions.c_str()); + } + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", installTypesCode.c_str()); + this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", "!insertmacro MUI_PAGE_COMPONENTS"); + this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL", ""); + this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", componentCode.c_str()); + this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", sectionList.c_str()); + this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS", selectedVarsList.c_str()); + } + this->ConfigureFile(nsisInInstallOptions.c_str(), nsisInstallOptions.c_str()); this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str()); @@ -252,9 +386,8 @@ int cmCPackNSISGenerator::InitializeInternal() } else { - cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: " - << "not set" << std::endl); - + cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: " + << "not set" << std::endl); } if ( cpackPackageExecutables ) { @@ -274,8 +407,8 @@ int cmCPackNSISGenerator::InitializeInternal() } std::vector<std::string>::iterator it; for ( it = cpackPackageExecutablesVector.begin(); - it != cpackPackageExecutablesVector.end(); - ++it ) + it != cpackPackageExecutablesVector.end(); + ++it ) { std::string execName = *it; ++ it; @@ -415,3 +548,183 @@ bool cmCPackNSISGenerator::GetListOfSubdirectories(const char* topdir, dirs.push_back(topdir); return true; } + +//---------------------------------------------------------------------- +bool cmCPackNSISGenerator::SupportsComponentInstallation() const +{ + return true; +} + +//---------------------------------------------------------------------- +std::string +cmCPackNSISGenerator:: +CreateComponentDescription(cmCPackComponent *component) const +{ + // Basic description of the component + std::string componentCode = "Section "; + if (component->IsDisabledByDefault) + { + componentCode += "/o "; + } + componentCode += "\""; + if (component->IsHidden) + { + componentCode += "-"; + } + componentCode += component->DisplayName + "\" " + component->Name + "\n"; + if (component->IsRequired) + { + componentCode += " SectionIn RO\n"; + } + else if (!component->InstallationTypes.empty()) + { + std::ostringstream out; + std::vector<cmCPackInstallationType *>::iterator installTypeIter; + for (installTypeIter = component->InstallationTypes.begin(); + installTypeIter != component->InstallationTypes.end(); + ++installTypeIter) + { + out << " " << (*installTypeIter)->Index; + } + componentCode += " SectionIn" + out.str() + "\n"; + } + componentCode += " SetOutPath \"$INSTDIR\"\n"; + componentCode += " File /r \"${INST_DIR}\\" + component->Name + "\\*.*\"\n"; + componentCode += "SectionEnd\n"; + + // Macro used to remove the component + componentCode += "!macro Remove_${" + component->Name + "}\n"; + std::vector<std::string>::iterator pathIt; + for (pathIt = component->Files.begin(); + pathIt != component->Files.end(); + ++pathIt) + { + componentCode += " Delete \"$INSTDIR\\" + *pathIt + "\"\n"; + } + for (pathIt = component->Directories.begin(); + pathIt != component->Directories.end(); + ++pathIt) + { + componentCode += " RMDir \"$INSTDIR\\" + *pathIt + "\"\n"; + } + componentCode += "!macroend\n"; + + // Macro used to select each of the components that this component + // depends on. + std::set<cmCPackComponent *> visited; + componentCode += "!macro Select_" + component->Name + "_depends\n"; + componentCode += CreateSelectionDependenciesDescription(component, visited); + componentCode += "!macroend\n"; + + // Macro used to deselect each of the components that depend on this + // component. + visited.clear(); + componentCode += "!macro Deselect_required_by_" + component->Name + "\n"; + componentCode += CreateDeselectionDependenciesDescription(component, visited); + componentCode += "!macroend\n"; + return componentCode; +} + +//---------------------------------------------------------------------- +std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription + (cmCPackComponent *component, + std::set<cmCPackComponent *>& visited) const +{ + // Don't visit a component twice + if (visited.count(component)) + { + return std::string(); + } + visited.insert(component); + + std::ostringstream out; + std::vector<cmCPackComponent *>::iterator dependIt; + for (dependIt = component->Dependencies.begin(); + dependIt != component->Dependencies.end(); + ++dependIt) + { + // Write NSIS code to select this dependency + out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n"; + out << " IntOp $0 $0 | ${SF_SELECTED}\n"; + out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n"; + out << " IntOp $" << (*dependIt)->Name << "_selected 0 + ${SF_SELECTED}\n"; + // Recurse + out << CreateSelectionDependenciesDescription(*dependIt, visited).c_str(); + } + + return out.str(); +} + + +//---------------------------------------------------------------------- +std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription + (cmCPackComponent *component, + std::set<cmCPackComponent *>& visited) const +{ + // Don't visit a component twice + if (visited.count(component)) + { + return std::string(); + } + visited.insert(component); + + std::ostringstream out; + std::vector<cmCPackComponent *>::iterator dependIt; + for (dependIt = component->ReverseDependencies.begin(); + dependIt != component->ReverseDependencies.end(); + ++dependIt) + { + // Write NSIS code to deselect this dependency + out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n"; + out << " IntOp $1 ${SF_SELECTED} ~\n"; + out << " IntOp $0 $0 & $1\n"; + out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n"; + out << " IntOp $" << (*dependIt)->Name << "_selected 0 + 0\n"; + + // Recurse + out << CreateDeselectionDependenciesDescription(*dependIt, visited).c_str(); + } + + return out.str(); +} + +//---------------------------------------------------------------------- +std::string +cmCPackNSISGenerator:: +CreateComponentGroupDescription(cmCPackComponentGroup *group) const +{ + if (group->Components.empty()) + { + // Silently skip empty groups. NSIS doesn't support them. + return std::string(); + } + + std::string code = "SectionGroup "; + if (group->IsExpandedByDefault) + { + code += "/e "; + } + if (group->IsBold) + { + code += "\"!" + group->DisplayName + "\" " + group->Name + "\n"; + } + else + { + code += "\"" + group->DisplayName + "\" " + group->Name + "\n"; + } + std::vector<cmCPackComponent*>::iterator comp; + for (comp = group->Components.begin(); + comp != group->Components.end(); + ++comp) + { + code += this->CreateComponentDescription(*comp); + } + code += "SectionGroupEnd\n"; + return code; +} + +std::string cmCPackNSISGenerator::TranslateNewlines(std::string str) +{ + cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n"); + return str; +} |