diff options
Diffstat (limited to 'Source/CPack')
48 files changed, 9700 insertions, 0 deletions
diff --git a/Source/CPack/OSXLauncherScript.scpt b/Source/CPack/OSXLauncherScript.scpt Binary files differnew file mode 100644 index 000000000..342cf8c01 --- /dev/null +++ b/Source/CPack/OSXLauncherScript.scpt diff --git a/Source/CPack/OSXScriptLauncher.cxx b/Source/CPack/OSXScriptLauncher.cxx new file mode 100644 index 000000000..e0fbe9b75 --- /dev/null +++ b/Source/CPack/OSXScriptLauncher.cxx @@ -0,0 +1,147 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include <cmsys/SystemTools.hxx> +#include <cmsys/Process.h> +#include <cmsys/ios/fstream> +#include <cmsys/ios/iostream> + +#include <CoreFoundation/CoreFoundation.h> + +// For the PATH_MAX constant +#include <sys/syslimits.h> + +#define DebugError(x) \ + ofs << x << cmsys_ios::endl; \ + cmsys_ios::cout << x << cmsys_ios::endl + +int main(int argc, char* argv[]) +{ + //if ( cmsys::SystemTools::FileExists( + cmsys_stl::string cwd = cmsys::SystemTools::GetCurrentWorkingDirectory(); + cmsys_ios::ofstream ofs("/tmp/output.txt"); + + CFStringRef fileName; + CFBundleRef appBundle; + CFURLRef scriptFileURL; + UInt8 *path; + + //get CF URL for script + if (! (appBundle = CFBundleGetMainBundle())) + { + DebugError("Cannot get main bundle"); + return 1; + } + fileName = CFSTR("RuntimeScript"); + if (! (scriptFileURL = CFBundleCopyResourceURL(appBundle, fileName, NULL, + NULL))) + { + DebugError("CFBundleCopyResourceURL failed"); + return 1; + } + + //create path string + if (! (path = new UInt8[PATH_MAX])) + { + return 1; + } + + //get the file system path of the url as a cstring + //in an encoding suitable for posix apis + if ( CFURLGetFileSystemRepresentation(scriptFileURL, true, path, + PATH_MAX) == false) + { + DebugError("CFURLGetFileSystemRepresentation failed"); + return 1; + } + + //dispose of the CF variable + CFRelease(scriptFileURL); + + cmsys_stl::string fullScriptPath = reinterpret_cast<char*>(path); + delete [] path; + + + if (! cmsys::SystemTools::FileExists(fullScriptPath.c_str())) + { + return 1; + } + + cmsys_stl::string scriptDirectory = cmsys::SystemTools::GetFilenamePath( + fullScriptPath); + ofs << fullScriptPath.c_str() << cmsys_ios::endl; + cmsys_stl::vector<const char*> args; + args.push_back(fullScriptPath.c_str()); + int cc; + for ( cc = 1; cc < argc; ++ cc ) + { + args.push_back(argv[cc]); + } + args.push_back(0); + + cmsysProcess* cp = cmsysProcess_New(); + cmsysProcess_SetCommand(cp, &*args.begin()); + cmsysProcess_SetWorkingDirectory(cp, scriptDirectory.c_str()); + cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); + cmsysProcess_SetTimeout(cp, 0); + cmsysProcess_Execute(cp); + + std::vector<char> tempOutput; + char* data; + int length; + while(cmsysProcess_WaitForData(cp, &data, &length, 0)) + { + // Translate NULL characters in the output into valid text. + // Visual Studio 7 puts these characters in the output of its + // build process. + for(int i=0; i < length; ++i) + { + if(data[i] == '\0') + { + data[i] = ' '; + } + } + cmsys_ios::cout.write(data, length); + } + + cmsysProcess_WaitForExit(cp, 0); + + bool result = true; + if(cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) + { + if ( cmsysProcess_GetExitValue(cp) != 0 ) + { + result = false; + } + } + else if(cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) + { + const char* exception_str = cmsysProcess_GetExceptionString(cp); + std::cerr << exception_str << std::endl; + result = false; + } + else if(cmsysProcess_GetState(cp) == cmsysProcess_State_Error) + { + const char* error_str = cmsysProcess_GetErrorString(cp); + std::cerr << error_str << std::endl; + result = false; + } + else if(cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) + { + const char* error_str = "Process terminated due to timeout\n"; + std::cerr << error_str << std::endl; + result = false; + } + + cmsysProcess_Delete(cp); + + return 0; +} diff --git a/Source/CPack/bills-comments.txt b/Source/CPack/bills-comments.txt new file mode 100644 index 000000000..f73499d01 --- /dev/null +++ b/Source/CPack/bills-comments.txt @@ -0,0 +1,68 @@ +cpack.cxx + +cmCPackGenerators -- creates cmCPackGenericGenerator's via NewGenerator + - a cmCPackGenericGenerator factory + + +cmCPackGenericGenerator::Initialize + this->InitializeInternal + CPACK_INCLUDE_TOPLEVEL_DIRECTORY = 0 turns off + + +// binary package run +cmCPackGenericGenerator::ProcessGenerator // DoPackage + cmCPackGenericGenerator::PrepareNames -- sets a bunch of CPACK_vars + cmCPackGenericGenerator::InstallProject + run preinstall (make preinstall/fast) + call ReadListFile(cmake_install.cmake) + glob recurse in install directory to get list of files + this->CompressFiles with the list of files + + +// source package run +cmCPackGenericGenerator::ProcessGenerator // DoPackage + cmCPackGenericGenerator::PrepareNames -- sets a bunch of CPACK_vars + cmCPackGenericGenerator::InstallProject --> + if set CPACK_INSTALLED_DIRECTORIES + glob the files in that directory + copy those files to the tmp install directory _CPack something + glob recurse in install directory to get list of files + this->CompressFiles with the list of files + + +cmCPackGenericGenerator::InstallProject is used for both source and binary +packages. It is controled based on values set in CPACK_ variables. + + +InstallProject + 1. CPACK_INSTALL_COMMANDS - a list of commands used to install the package + + 2. CPACK_INSTALLED_DIRECTORIES - copy this directory to CPACK_TEMPORARY_DIRECTORY + + 3. CPACK_INSTALL_CMAKE_PROJECTS - a cmake install script + - run make preinstall + - run cmake_install.cmake + - set CMAKE_INSTALL_PREFIX to the temp directory + - CPACK_BUILD_CONFIG check this and set the BUILD_TYPE to it + - ReadListFile on the install script cmake_install.cmake + - run strip on the executables and libraries if CPACK_STRIP_FILES is TRUE + +Recommendations: + +rename cmCPackGenerators to cmCPackGeneratorFactory + +rename cmCPackGenericGenerator --> cmCPackGenerator + +rename cmCPackGenericGenerator::ProcessGenerator -> cmCPackGenerator::DoPackage + + +break up cmCPackGenerator::InstallProject so it calls the following: + +// run user provided install commands + cmCPackGenerator::RunInstallCommands(); +// copy entire directories that need no processing like source trees + cmCPackGenerator::CopyPreInstalledDirectories(); +// run the cmake install scripts if provided + cmCPackGenerator::RunCMakeInstallScripts() + +- diff --git a/Source/CPack/cmCPackArchiveGenerator.cxx b/Source/CPack/cmCPackArchiveGenerator.cxx new file mode 100644 index 000000000..6e7b8d7c8 --- /dev/null +++ b/Source/CPack/cmCPackArchiveGenerator.cxx @@ -0,0 +1,314 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackArchiveGenerator.h" + +#include "cmake.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmSystemTools.h" +#include "cmMakefile.h" +#include "cmGeneratedFileStream.h" +#include "cmCPackLog.h" +#include <errno.h> + +#include <cmsys/SystemTools.hxx> +#include <cmsys/Directory.hxx> +#include <cm_libarchive.h> + +//---------------------------------------------------------------------- +cmCPackArchiveGenerator::cmCPackArchiveGenerator(cmArchiveWrite::Compress t, + cmArchiveWrite::Type at) +{ + this->Compress = t; + this->Archive = at; +} + +//---------------------------------------------------------------------- +cmCPackArchiveGenerator::~cmCPackArchiveGenerator() +{ +} + +//---------------------------------------------------------------------- +int cmCPackArchiveGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "1"); + return this->Superclass::InitializeInternal(); +} +//---------------------------------------------------------------------- +int cmCPackArchiveGenerator::addOneComponentToArchive(cmArchiveWrite& archive, + cmCPackComponent* component) +{ + cmCPackLogger(cmCPackLog::LOG_VERBOSE, " - packaging component: " + << component->Name + << std::endl); + // Add the files of this component to the archive + std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + localToplevel += "/"+ component->Name; + std::string dir = cmSystemTools::GetCurrentWorkingDirectory(); + // Change to local toplevel + cmSystemTools::ChangeDirectory(localToplevel.c_str()); + std::string filePrefix; + if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) + { + filePrefix = this->GetOption("CPACK_PACKAGE_FILE_NAME"); + filePrefix += "/"; + } + std::vector<std::string>::const_iterator fileIt; + for (fileIt = component->Files.begin(); fileIt != component->Files.end(); + ++fileIt ) + { + std::string rp = filePrefix + *fileIt; + cmCPackLogger(cmCPackLog::LOG_DEBUG,"Adding file: " + << rp << std::endl); + archive.Add(rp); + if (!archive) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "ERROR while packaging files: " + << archive.GetError() + << std::endl); + return 0; + } + } + // Go back to previous dir + cmSystemTools::ChangeDirectory(dir.c_str()); + return 1; +} + +/* + * The macro will open/create a file 'filename' + * an declare and open the associated + * cmArchiveWrite 'archive' object. + */ +#define DECLARE_AND_OPEN_ARCHIVE(filename,archive) \ +cmGeneratedFileStream gf; \ +gf.Open(filename.c_str(), false, true); \ +if (!GenerateHeader(&gf)) \ + { \ + cmCPackLogger(cmCPackLog::LOG_ERROR, \ + "Problem to generate Header for archive < " \ + << filename \ + << ">." << std::endl); \ + return 0; \ + } \ +cmArchiveWrite archive(gf,this->Compress, this->Archive); \ +if (!archive) \ + { \ + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem to create archive < " \ + << filename \ + << ">. ERROR =" \ + << archive.GetError() \ + << std::endl); \ + return 0; \ + } + +//---------------------------------------------------------------------- +int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup) +{ + packageFileNames.clear(); + // The default behavior is to have one package by component group + // unless CPACK_COMPONENTS_IGNORE_GROUP is specified. + if (!ignoreGroup) + { + std::map<std::string, cmCPackComponentGroup>::iterator compGIt; + for (compGIt=this->ComponentGroups.begin(); + compGIt!=this->ComponentGroups.end(); ++compGIt) + { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Packaging component group: " + << compGIt->first + << std::endl); + // Begin the archive for this group + std::string packageFileName= std::string(toplevel); + packageFileName += "/"+ + GetComponentPackageFileName(this->GetOption("CPACK_PACKAGE_FILE_NAME"), + compGIt->first, + true) + + this->GetOutputExtension(); + // open a block in order to automatically close archive + // at the end of the block + { + DECLARE_AND_OPEN_ARCHIVE(packageFileName,archive); + // now iterate over the component of this group + std::vector<cmCPackComponent*>::iterator compIt; + for (compIt=(compGIt->second).Components.begin(); + compIt!=(compGIt->second).Components.end(); + ++compIt) + { + // Add the files of this component to the archive + addOneComponentToArchive(archive,*compIt); + } + } + // add the generated package to package file names list + packageFileNames.push_back(packageFileName); + } + // Handle Orphan components (components not belonging to any groups) + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt=this->Components.begin(); + compIt!=this->Components.end(); ++compIt ) + { + // Does the component belong to a group? + if (compIt->second.Group==NULL) + { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Component <" + << compIt->second.Name + << "> does not belong to any group, package it separately." + << std::endl); + std::string localToplevel( + this->GetOption("CPACK_TEMPORARY_DIRECTORY") + ); + std::string packageFileName = std::string(toplevel); + + localToplevel += "/"+ compIt->first; + packageFileName += "/"+ + GetComponentPackageFileName(this->GetOption("CPACK_PACKAGE_FILE_NAME"), + compIt->first, + false) + + this->GetOutputExtension(); + { + DECLARE_AND_OPEN_ARCHIVE(packageFileName,archive); + // Add the files of this component to the archive + addOneComponentToArchive(archive,&(compIt->second)); + } + // add the generated package to package file names list + packageFileNames.push_back(packageFileName); + } + } + } + // CPACK_COMPONENTS_IGNORE_GROUPS is set + // We build 1 package per component + else + { + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt=this->Components.begin(); + compIt!=this->Components.end(); ++compIt ) + { + std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + std::string packageFileName = std::string(toplevel); + + localToplevel += "/"+ compIt->first; + packageFileName += "/"+ + GetComponentPackageFileName(this->GetOption("CPACK_PACKAGE_FILE_NAME"), + compIt->first, + false) + + this->GetOutputExtension(); + { + DECLARE_AND_OPEN_ARCHIVE(packageFileName,archive); + // Add the files of this component to the archive + addOneComponentToArchive(archive,&(compIt->second)); + } + // add the generated package to package file names list + packageFileNames.push_back(packageFileName); + } + } + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackArchiveGenerator::PackageComponentsAllInOne() +{ + // reset the package file names + packageFileNames.clear(); + packageFileNames.push_back(std::string(toplevel)); + packageFileNames[0] += "/" + +std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) + + this->GetOutputExtension(); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Packaging all groups in one package..." + "(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE is set)" + << std::endl); + DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0],archive); + + // The ALL COMPONENTS in ONE package case + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt=this->Components.begin();compIt!=this->Components.end(); + ++compIt ) + { + // Add the files of this component to the archive + addOneComponentToArchive(archive,&(compIt->second)); + } + + // archive goes out of scope so it will finalized and closed. + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackArchiveGenerator::PackageFiles() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Toplevel: " + << toplevel << std::endl); + + if (WantsComponentInstallation()) { + // CASE 1 : COMPONENT ALL-IN-ONE package + // If ALL COMPONENTS in ONE package has been requested + // then the package file is unique and should be open here. + if (componentPackageMethod == ONE_PACKAGE) + { + return PackageComponentsAllInOne(); + } + // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one) + // There will be 1 package for each component group + // however one may require to ignore component group and + // in this case you'll get 1 package for each component. + else + { + return PackageComponents(componentPackageMethod == + ONE_PACKAGE_PER_COMPONENT); + } + } + + // CASE 3 : NON COMPONENT package. + DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0],archive); + std::vector<std::string>::const_iterator fileIt; + std::string dir = cmSystemTools::GetCurrentWorkingDirectory(); + cmSystemTools::ChangeDirectory(toplevel.c_str()); + for ( fileIt = files.begin(); fileIt != files.end(); ++ fileIt ) + { + // Get the relative path to the file + std::string rp = cmSystemTools::RelativePath(toplevel.c_str(), + fileIt->c_str()); + archive.Add(rp); + if(!archive) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem while adding file< " + << *fileIt + << "> to archive <" + << packageFileNames[0] << "> .ERROR =" + << archive.GetError() + << std::endl); + return 0; + } + } + cmSystemTools::ChangeDirectory(dir.c_str()); + // The destructor of cmArchiveWrite will close and finish the write + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackArchiveGenerator::GenerateHeader(std::ostream*) +{ + return 1; +} + +bool cmCPackArchiveGenerator::SupportsComponentInstallation() const { + // The Component installation support should only + // be activated if explicitly requested by the user + // (for backward compatibility reason) + if (IsOn("CPACK_ARCHIVE_COMPONENT_INSTALL")) + { + return true; + } + else + { + return false; + } +} diff --git a/Source/CPack/cmCPackArchiveGenerator.h b/Source/CPack/cmCPackArchiveGenerator.h new file mode 100644 index 000000000..b1bbb8363 --- /dev/null +++ b/Source/CPack/cmCPackArchiveGenerator.h @@ -0,0 +1,74 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackArchiveGenerator_h +#define cmCPackArchiveGenerator_h + +#include "cmArchiveWrite.h" +#include "cmCPackGenerator.h" + + +/** \class cmCPackArchiveGenerator + * \brief A generator base for libarchive generation. + * The generator itself uses the libarchive wrapper + * \ref cmArchiveWrite. + * + */ +class cmCPackArchiveGenerator : public cmCPackGenerator + { +public: + cmTypeMacro(cmCPackArchiveGenerator, cmCPackGenerator); + + /** + * Construct generator + */ + cmCPackArchiveGenerator(cmArchiveWrite::Compress, cmArchiveWrite::Type); + virtual ~cmCPackArchiveGenerator(); + // Used to add a header to the archive + virtual int GenerateHeader(std::ostream* os); + // component support + virtual bool SupportsComponentInstallation() const; +protected: + virtual int InitializeInternal(); + /** + * Add the files belonging to the specified component + * to the provided (already opened) archive. + * @param[in,out] archive the archive object + * @param[in] component the component whose file will be added to archive + */ + int addOneComponentToArchive(cmArchiveWrite& archive, + cmCPackComponent* component); + + /** + * The main package file method. + * If component install was required this + * method will call either PackageComponents or + * PackageComponentsAllInOne. + */ + int PackageFiles(); + /** + * The method used to package files when component + * install is used. This will create one + * archive for each component group. + */ + int PackageComponents(bool ignoreGroup); + /** + * Special case of component install where all + * components will be put in a single installer. + */ + int PackageComponentsAllInOne(); + virtual const char* GetOutputExtension() = 0; + cmArchiveWrite::Compress Compress; + cmArchiveWrite::Type Archive; + }; + +#endif diff --git a/Source/CPack/cmCPackBundleGenerator.cxx b/Source/CPack/cmCPackBundleGenerator.cxx new file mode 100644 index 000000000..af78e7822 --- /dev/null +++ b/Source/CPack/cmCPackBundleGenerator.cxx @@ -0,0 +1,174 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackBundleGenerator.h" +#include "cmCPackLog.h" +#include "cmSystemTools.h" + +#include <cmsys/RegularExpression.hxx> + +//---------------------------------------------------------------------- +cmCPackBundleGenerator::cmCPackBundleGenerator() +{ +} + +//---------------------------------------------------------------------- +cmCPackBundleGenerator::~cmCPackBundleGenerator() +{ +} + +//---------------------------------------------------------------------- +int cmCPackBundleGenerator::InitializeInternal() +{ + const char* name = this->GetOption("CPACK_BUNDLE_NAME"); + if(0 == name) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_BUNDLE_NAME must be set to use the Bundle generator." + << std::endl); + + return 0; + } + + return this->Superclass::InitializeInternal(); +} + +//---------------------------------------------------------------------- +const char* cmCPackBundleGenerator::GetPackagingInstallPrefix() +{ + this->InstallPrefix = "/"; + this->InstallPrefix += this->GetOption("CPACK_BUNDLE_NAME"); + this->InstallPrefix += ".app/Contents/Resources"; + + return this->InstallPrefix.c_str(); +} + +//---------------------------------------------------------------------- +int cmCPackBundleGenerator::PackageFiles() +{ + + // Get required arguments ... + const std::string cpack_bundle_name = this->GetOption("CPACK_BUNDLE_NAME") + ? this->GetOption("CPACK_BUNDLE_NAME") : ""; + if(cpack_bundle_name.empty()) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_BUNDLE_NAME must be set." + << std::endl); + + return 0; + } + + const std::string cpack_bundle_plist = this->GetOption("CPACK_BUNDLE_PLIST") + ? this->GetOption("CPACK_BUNDLE_PLIST") : ""; + if(cpack_bundle_plist.empty()) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_BUNDLE_PLIST must be set." + << std::endl); + + return 0; + } + + const std::string cpack_bundle_icon = this->GetOption("CPACK_BUNDLE_ICON") + ? this->GetOption("CPACK_BUNDLE_ICON") : ""; + if(cpack_bundle_icon.empty()) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_BUNDLE_ICON must be set." + << std::endl); + + return 0; + } + + // Get optional arguments ... + const std::string cpack_bundle_startup_command = + this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND") + ? this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND") : ""; + + // The staging directory contains everything that will end-up inside the + // final disk image ... + cmOStringStream staging; + staging << toplevel; + + cmOStringStream contents; + contents << staging.str() << "/" << cpack_bundle_name + << ".app/" << "Contents"; + + cmOStringStream application; + application << contents.str() << "/" << "MacOS"; + + cmOStringStream resources; + resources << contents.str() << "/" << "Resources"; + + // Install a required, user-provided bundle metadata file ... + cmOStringStream plist_source; + plist_source << cpack_bundle_plist; + + cmOStringStream plist_target; + plist_target << contents.str() << "/" << "Info.plist"; + + if(!this->CopyFile(plist_source, plist_target)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error copying plist. Check the value of CPACK_BUNDLE_PLIST." + << std::endl); + + return 0; + } + + // Install a user-provided bundle icon ... + cmOStringStream icon_source; + icon_source << cpack_bundle_icon; + + cmOStringStream icon_target; + icon_target << resources.str() << "/" << cpack_bundle_name << ".icns"; + + if(!this->CopyFile(icon_source, icon_target)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error copying bundle icon. Check the value of CPACK_BUNDLE_ICON." + << std::endl); + + return 0; + } + + // Optionally a user-provided startup command (could be an + // executable or a script) ... + if(!cpack_bundle_startup_command.empty()) + { + cmOStringStream command_source; + command_source << cpack_bundle_startup_command; + + cmOStringStream command_target; + command_target << application.str() << "/" << cpack_bundle_name; + + if(!this->CopyFile(command_source, command_target)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error copying startup command. " + " Check the value of CPACK_BUNDLE_STARTUP_COMMAND." + << std::endl); + + return 0; + } + + cmSystemTools::SetPermissions(command_target.str().c_str(), 0777); + } + + return this->CreateDMG(toplevel, packageFileNames[0]); +} + +bool cmCPackBundleGenerator::SupportsComponentInstallation() const +{ + return false; +} diff --git a/Source/CPack/cmCPackBundleGenerator.h b/Source/CPack/cmCPackBundleGenerator.h new file mode 100644 index 000000000..ed0187dd4 --- /dev/null +++ b/Source/CPack/cmCPackBundleGenerator.h @@ -0,0 +1,40 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackBundleGenerator_h +#define cmCPackBundleGenerator_h + +#include "cmCPackDragNDropGenerator.h" + +/** \class cmCPackBundleGenerator + * \brief A generator for OSX bundles + * + * Based on Gimp.app + */ +class cmCPackBundleGenerator : public cmCPackDragNDropGenerator +{ +public: + cmCPackTypeMacro(cmCPackBundleGenerator, cmCPackDragNDropGenerator); + + cmCPackBundleGenerator(); + virtual ~cmCPackBundleGenerator(); + +protected: + virtual int InitializeInternal(); + virtual const char* GetPackagingInstallPrefix(); + int PackageFiles(); + bool SupportsComponentInstallation() const; + + std::string InstallPrefix; +}; + +#endif diff --git a/Source/CPack/cmCPackComponentGroup.cxx b/Source/CPack/cmCPackComponentGroup.cxx new file mode 100644 index 000000000..1d21fb11c --- /dev/null +++ b/Source/CPack/cmCPackComponentGroup.cxx @@ -0,0 +1,44 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackComponentGroup.h" +#include "cmSystemTools.h" +#include <vector> +#include <string> + +//---------------------------------------------------------------------- +unsigned long cmCPackComponent::GetInstalledSize(const char* installDir) const +{ + if (this->TotalSize != 0) + { + return this->TotalSize; + } + + std::vector<std::string>::const_iterator fileIt; + for (fileIt = this->Files.begin(); fileIt != this->Files.end(); ++fileIt) + { + std::string path = installDir; + path += '/'; + path += *fileIt; + this->TotalSize += cmSystemTools::FileLength(path.c_str()); + } + + return this->TotalSize; +} + +//---------------------------------------------------------------------- +unsigned long +cmCPackComponent::GetInstalledSizeInKbytes(const char* installDir) const +{ + unsigned long result = (GetInstalledSize(installDir) + 512) / 1024; + return result? result : 1; +} diff --git a/Source/CPack/cmCPackComponentGroup.h b/Source/CPack/cmCPackComponentGroup.h new file mode 100644 index 000000000..cebdd6d20 --- /dev/null +++ b/Source/CPack/cmCPackComponentGroup.h @@ -0,0 +1,138 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackComponentGroup_h +#define cmCPackComponentGroup_h + +#include "cmStandardIncludes.h" + +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), TotalSize(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; + + /// Whether this component should be downloaded on-the-fly. If false, + /// the component will be a part of the installation package. + bool IsDownloaded : 1; + + /// A description of this component. + std::string Description; + + /// The installation types that this component is a part of. + std::vector<cmCPackInstallationType *> InstallationTypes; + + /// If IsDownloaded is true, the name of the archive file that + /// contains the files that are part of this component. + std::string ArchiveFile; + + /// The components that this component depends on. + std::vector<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; + + /// Get the total installed size of all of the files in this + /// component, in bytes. installDir is the directory into which the + /// component was installed. + unsigned long GetInstalledSize(const char* installDir) const; + + /// Identical to GetInstalledSize, but returns the result in + /// kilobytes. + unsigned long GetInstalledSizeInKbytes(const char* installDir) const; + + private: + mutable unsigned long TotalSize; +}; + +/** \class cmCPackComponentGroup + * \brief A component group to be installed by CPack. + */ +class cmCPackComponentGroup +{ +public: + cmCPackComponentGroup() : ParentGroup(0) { } + + /// 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; + + /// The parent group of this component group (if any). + cmCPackComponentGroup *ParentGroup; + + /// The subgroups of this group. + std::vector<cmCPackComponentGroup*> Subgroups; +}; + +#endif diff --git a/Source/CPack/cmCPackConfigure.h.in b/Source/CPack/cmCPackConfigure.h.in new file mode 100644 index 000000000..3d7702e1a --- /dev/null +++ b/Source/CPack/cmCPackConfigure.h.in @@ -0,0 +1,11 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ diff --git a/Source/CPack/cmCPackCygwinBinaryGenerator.cxx b/Source/CPack/cmCPackCygwinBinaryGenerator.cxx new file mode 100644 index 000000000..6c8fc54e0 --- /dev/null +++ b/Source/CPack/cmCPackCygwinBinaryGenerator.cxx @@ -0,0 +1,92 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackCygwinBinaryGenerator.h" + +#include "cmake.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmSystemTools.h" +#include "cmMakefile.h" +#include "cmGeneratedFileStream.h" +#include "cmCPackLog.h" + +#include <cmsys/SystemTools.hxx> + +//---------------------------------------------------------------------- +cmCPackCygwinBinaryGenerator::cmCPackCygwinBinaryGenerator() +{ +} + +//---------------------------------------------------------------------- +cmCPackCygwinBinaryGenerator::~cmCPackCygwinBinaryGenerator() +{ +} + +//---------------------------------------------------------------------- +int cmCPackCygwinBinaryGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr"); + this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "0"); + return this->Superclass::InitializeInternal(); +} + +//---------------------------------------------------------------------- +int cmCPackCygwinBinaryGenerator::PackageFiles() +{ + std::string packageName = this->GetOption("CPACK_PACKAGE_NAME"); + packageName += "-"; + packageName += this->GetOption("CPACK_PACKAGE_VERSION"); + packageName = cmsys::SystemTools::LowerCase(packageName); + std::string manifest = "/usr/share/doc/"; + manifest += packageName; + manifest += "/MANIFEST"; + std::string manifestFile + = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + // Create a MANIFEST file that contains all of the files in + // the tar file + std::string tempdir = manifestFile; + manifestFile += manifest; + // create an extra scope to force the stream + // to create the file before the super class is called + { + cmGeneratedFileStream ofs(manifestFile.c_str()); + for(std::vector<std::string>::const_iterator i = files.begin(); + i != files.end(); ++i) + { + // remove the temp dir and replace with /usr + ofs << (*i).substr(tempdir.size()) << "\n"; + } + ofs << manifest << "\n"; + } + // add the manifest file to the list of all files + files.push_back(manifestFile); + + // create the bzip2 tar file + return this->Superclass::PackageFiles(); +} + +const char* cmCPackCygwinBinaryGenerator::GetOutputExtension() +{ + this->OutputExtension = "-"; + const char* patchNumber =this->GetOption("CPACK_CYGWIN_PATCH_NUMBER"); + if(!patchNumber) + { + patchNumber = "1"; + cmCPackLogger(cmCPackLog::LOG_WARNING, + "CPACK_CYGWIN_PATCH_NUMBER not specified using 1" + << std::endl); + } + this->OutputExtension += patchNumber; + this->OutputExtension += ".tar.bz2"; + return this->OutputExtension.c_str(); +} diff --git a/Source/CPack/cmCPackCygwinBinaryGenerator.h b/Source/CPack/cmCPackCygwinBinaryGenerator.h new file mode 100644 index 000000000..38f6df17d --- /dev/null +++ b/Source/CPack/cmCPackCygwinBinaryGenerator.h @@ -0,0 +1,38 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackCygwinBinaryGenerator_h +#define cmCPackCygwinBinaryGenerator_h + +#include "cmCPackTarBZip2Generator.h" + +/** \class cmCPackCygwinBinaryGenerator + * \brief A generator for TarBZip2 files + */ +class cmCPackCygwinBinaryGenerator : public cmCPackTarBZip2Generator +{ +public: + cmCPackTypeMacro(cmCPackCygwinBinaryGenerator, cmCPackTarBZip2Generator); + + /** + * Construct generator + */ + cmCPackCygwinBinaryGenerator(); + virtual ~cmCPackCygwinBinaryGenerator(); +protected: + virtual int InitializeInternal(); + int PackageFiles(); + virtual const char* GetOutputExtension(); + std::string OutputExtension; +}; + +#endif diff --git a/Source/CPack/cmCPackCygwinSourceGenerator.cxx b/Source/CPack/cmCPackCygwinSourceGenerator.cxx new file mode 100644 index 000000000..597972992 --- /dev/null +++ b/Source/CPack/cmCPackCygwinSourceGenerator.cxx @@ -0,0 +1,182 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackCygwinSourceGenerator.h" + +#include "cmake.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmSystemTools.h" +#include "cmMakefile.h" +#include "cmGeneratedFileStream.h" +#include "cmCPackLog.h" + +#include <cmsys/SystemTools.hxx> + +// Includes needed for implementation of RenameFile. This is not in +// system tools because it is not implemented robustly enough to move +// files across directories. +#ifdef _WIN32 +# include <windows.h> +# include <sys/stat.h> +#endif + +//---------------------------------------------------------------------- +cmCPackCygwinSourceGenerator::cmCPackCygwinSourceGenerator() +{ +} + +//---------------------------------------------------------------------- +cmCPackCygwinSourceGenerator::~cmCPackCygwinSourceGenerator() +{ +} + +//---------------------------------------------------------------------- +int cmCPackCygwinSourceGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "0"); + return this->Superclass::InitializeInternal(); +} + +//---------------------------------------------------------------------- +int cmCPackCygwinSourceGenerator::PackageFiles() +{ + // Create a tar file of the sources + std::string packageDirFileName + = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + packageDirFileName += ".tar.bz2"; + packageFileNames[0] = packageDirFileName; + std::string output; + // skip one parent up to the cmCPackTarBZip2Generator + // to create tar.bz2 file with the list of source + // files + this->Compress = cmArchiveWrite::CompressBZip2; + if ( !this->cmCPackTarBZip2Generator::PackageFiles() ) + { + return 0; + } + // Now create a tar file that contains the above .tar.bz2 file + // and the CPACK_CYGWIN_PATCH_FILE and CPACK_TOPLEVEL_DIRECTORY + // files + std::string compressOutFile = packageDirFileName; + // at this point compressOutFile is the full path to + // _CPack_Package/.../package-2.5.0.tar.bz2 + // we want to create a tar _CPack_Package/.../package-2.5.0-1-src.tar.bz2 + // with these + // _CPack_Package/.../package-2.5.0-1.patch + // _CPack_Package/.../package-2.5.0-1.sh + // _CPack_Package/.../package-2.5.0.tar.bz2 + // the -1 is CPACK_CYGWIN_PATCH_NUMBER + + // first copy the patch file and the .sh file + // to the toplevel cpack temp dir + + // copy the patch file into place + if(!this->GetOption("CPACK_CYGWIN_PATCH_FILE")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "No patch file specified for cygwin sources."); + return 0; + } + if(!cmSystemTools::CopyFileAlways( + this->GetOption("CPACK_CYGWIN_PATCH_FILE"), + this->GetOption("CPACK_TOPLEVEL_DIRECTORY"))) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "problem copying: [" + << this->GetOption("CPACK_CYGWIN_PATCH_FILE") << "]\nto\n[" + << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "]\n"); + return 0; + } + if(!this->GetOption("CPACK_CYGWIN_BUILD_SCRIPT")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "No build script specified for cygwin sources."); + return 0; + } + // copy the build script into place + if(!cmSystemTools::CopyFileAlways( + this->GetOption("CPACK_CYGWIN_BUILD_SCRIPT"), + this->GetOption("CPACK_TOPLEVEL_DIRECTORY"))) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "problem copying: " + << this->GetOption("CPACK_CYGWIN_BUILD_SCRIPT") << "\nto\n" + << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "]\n"); + return 0; + } + std::string outerTarFile + = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + outerTarFile += "-"; + const char* patch = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER"); + if(!patch) + { + cmCPackLogger(cmCPackLog::LOG_WARNING, "CPACK_CYGWIN_PATCH_NUMBER" + << " not specified, defaulting to 1\n"); + patch = "1"; + } + outerTarFile += patch; + outerTarFile += "-src.tar.bz2"; + std::string tmpDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + std::string buildScript = tmpDir; + buildScript += "/"; + buildScript += cmSystemTools::GetFilenameName( + this->GetOption("CPACK_CYGWIN_BUILD_SCRIPT")); + std::string patchFile = tmpDir; + patchFile += "/"; + patchFile += cmSystemTools::GetFilenameName( + this->GetOption("CPACK_CYGWIN_PATCH_FILE")); + + std::string file = cmSystemTools::GetFilenameName(compressOutFile); + std::string sourceTar = cmSystemTools::GetFilenamePath(compressOutFile); + sourceTar += "/"; + sourceTar += file; + /* reset list of file to be packaged */ + files.clear(); + // a source release in cygwin should have the build script used + // to build the package, the patch file that is different from the + // regular upstream version of the sources, and a bziped tar file + // of the original sources + files.push_back(buildScript); + files.push_back(patchFile); + files.push_back(sourceTar); + /* update the name of the produced package */ + packageFileNames[0] = outerTarFile; + /* update the toplevel dir */ + toplevel = tmpDir; + if ( !this->cmCPackTarBZip2Generator::PackageFiles() ) + { + return 0; + } + return 1; +} + +const char* cmCPackCygwinSourceGenerator::GetPackagingInstallPrefix() +{ + this->InstallPrefix = "/"; + this->InstallPrefix += this->GetOption("CPACK_PACKAGE_FILE_NAME"); + return this->InstallPrefix.c_str(); +} + +const char* cmCPackCygwinSourceGenerator::GetOutputExtension() +{ + this->OutputExtension = "-"; + const char* patch = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER"); + if(!patch) + { + cmCPackLogger(cmCPackLog::LOG_WARNING, "CPACK_CYGWIN_PATCH_NUMBER" + << " not specified, defaulting to 1\n"); + patch = "1"; + } + this->OutputExtension += patch; + this->OutputExtension += "-src.tar.bz2"; + return this->OutputExtension.c_str(); +} + diff --git a/Source/CPack/cmCPackCygwinSourceGenerator.h b/Source/CPack/cmCPackCygwinSourceGenerator.h new file mode 100644 index 000000000..9d98a9b95 --- /dev/null +++ b/Source/CPack/cmCPackCygwinSourceGenerator.h @@ -0,0 +1,40 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackCygwinSourceGenerator_h +#define cmCPackCygwinSourceGenerator_h + +#include "cmCPackTarBZip2Generator.h" + +/** \class cmCPackCygwinSourceGenerator + * \brief A generator for cygwin source files + */ +class cmCPackCygwinSourceGenerator : public cmCPackTarBZip2Generator +{ +public: + cmCPackTypeMacro(cmCPackCygwinSourceGenerator, cmCPackTarBZip2Generator); + + /** + * Construct generator + */ + cmCPackCygwinSourceGenerator(); + virtual ~cmCPackCygwinSourceGenerator(); +protected: + const char* GetPackagingInstallPrefix(); + virtual int InitializeInternal(); + int PackageFiles(); + virtual const char* GetOutputExtension(); + std::string InstallPrefix; + std::string OutputExtension; +}; + +#endif diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx new file mode 100644 index 000000000..fa456deff --- /dev/null +++ b/Source/CPack/cmCPackDebGenerator.cxx @@ -0,0 +1,833 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmCPackDebGenerator.h" + +#include "cmSystemTools.h" +#include "cmMakefile.h" +#include "cmGeneratedFileStream.h" +#include "cmCPackLog.h" + +#include <cmsys/SystemTools.hxx> +#include <cmsys/Glob.hxx> + +#include <limits.h> // USHRT_MAX + +// NOTE: +// A debian package .deb is simply an 'ar' archive. The only subtle difference +// is that debian uses the BSD ar style archive whereas most Linux distro have +// a GNU ar. +// See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=161593 for more info +// Therefore we provide our own implementation of a BSD-ar: +static int ar_append(const char*archive,const std::vector<std::string>& files); + +//---------------------------------------------------------------------- +cmCPackDebGenerator::cmCPackDebGenerator() +{ +} + +//---------------------------------------------------------------------- +cmCPackDebGenerator::~cmCPackDebGenerator() +{ +} + +//---------------------------------------------------------------------- +int cmCPackDebGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr"); + if (cmSystemTools::IsOff(this->GetOption("CPACK_SET_DESTDIR"))) + { + this->SetOption("CPACK_SET_DESTDIR", "I_ON"); + } + return this->Superclass::InitializeInternal(); +} + +//---------------------------------------------------------------------- +int cmCPackDebGenerator::PackageOnePack(std::string initialTopLevel, + std::string packageName) + { + int retval = 1; + // Begin the archive for this pack + std::string localToplevel(initialTopLevel); + std::string packageFileName( + cmSystemTools::GetParentDirectory(toplevel.c_str()) + ); + std::string outputFileName( + std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) + +"-"+packageName + this->GetOutputExtension() + ); + + localToplevel += "/"+ packageName; + /* replace the TEMP DIRECTORY with the component one */ + this->SetOption("CPACK_TEMPORARY_DIRECTORY",localToplevel.c_str()); + packageFileName += "/"+ outputFileName; + /* replace proposed CPACK_OUTPUT_FILE_NAME */ + this->SetOption("CPACK_OUTPUT_FILE_NAME",outputFileName.c_str()); + /* replace the TEMPORARY package file name */ + this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", + packageFileName.c_str()); + // Tell CPackDeb.cmake the name of the component GROUP. + this->SetOption("CPACK_DEB_PACKAGE_COMPONENT",packageName.c_str()); + if (!this->ReadListFile("CPackDeb.cmake")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error while execution CPackDeb.cmake" << std::endl); + retval = 0; + return retval; + } + + cmsys::Glob gl; + std::string findExpr(this->GetOption("WDIR")); + findExpr += "/*"; + gl.RecurseOn(); + if ( !gl.FindFiles(findExpr) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find any files in the installed directory" << std::endl); + return 0; + } + packageFiles = gl.GetFiles(); + + int res = createDeb(); + if (res != 1) + { + retval = 0; + } + // add the generated package to package file names list + packageFileNames.push_back(packageFileName); + return retval; +} + +//---------------------------------------------------------------------- +int cmCPackDebGenerator::PackageComponents(bool ignoreGroup) +{ + int retval = 1; + /* Reset package file name list it will be populated during the + * component packaging run*/ + packageFileNames.clear(); + std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + + // The default behavior is to have one package by component group + // unless CPACK_COMPONENTS_IGNORE_GROUP is specified. + if (!ignoreGroup) + { + std::map<std::string, cmCPackComponentGroup>::iterator compGIt; + for (compGIt=this->ComponentGroups.begin(); + compGIt!=this->ComponentGroups.end(); ++compGIt) + { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Packaging component group: " + << compGIt->first + << std::endl); + // Begin the archive for this group + retval &= PackageOnePack(initialTopLevel,compGIt->first); + } + // Handle Orphan components (components not belonging to any groups) + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt=this->Components.begin(); + compIt!=this->Components.end(); ++compIt ) + { + // Does the component belong to a group? + if (compIt->second.Group==NULL) + { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Component <" + << compIt->second.Name + << "> does not belong to any group, package it separately." + << std::endl); + // Begin the archive for this orphan component + retval &= PackageOnePack(initialTopLevel,compIt->first); + } + } + } + // CPACK_COMPONENTS_IGNORE_GROUPS is set + // We build 1 package per component + else + { + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt=this->Components.begin(); + compIt!=this->Components.end(); ++compIt ) + { + retval &= PackageOnePack(initialTopLevel,compIt->first); + } + } + return retval; +} + +//---------------------------------------------------------------------- +int cmCPackDebGenerator::PackageComponentsAllInOne() +{ + int retval = 1; + std::string compInstDirName; + /* Reset package file name list it will be populated during the + * component packaging run*/ + packageFileNames.clear(); + std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + + compInstDirName = "ALL_COMPONENTS_IN_ONE"; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Packaging all groups in one package..." + "(CPACK_COMPONENTS_ALL_[GROUPS_]IN_ONE_PACKAGE is set)" + << std::endl); + + // The ALL GROUPS in ONE package case + std::string localToplevel(initialTopLevel); + std::string packageFileName( + cmSystemTools::GetParentDirectory(toplevel.c_str()) + ); + std::string outputFileName( + std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) + + this->GetOutputExtension() + ); + // all GROUP in one vs all COMPONENT in one + localToplevel += "/"+compInstDirName; + + /* replace the TEMP DIRECTORY with the component one */ + this->SetOption("CPACK_TEMPORARY_DIRECTORY",localToplevel.c_str()); + packageFileName += "/"+ outputFileName; + /* replace proposed CPACK_OUTPUT_FILE_NAME */ + this->SetOption("CPACK_OUTPUT_FILE_NAME",outputFileName.c_str()); + /* replace the TEMPORARY package file name */ + this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", + packageFileName.c_str()); + // Tell CPackDeb.cmake the name of the component GROUP. + this->SetOption("CPACK_DEB_PACKAGE_COMPONENT",compInstDirName.c_str()); + if (!this->ReadListFile("CPackDeb.cmake")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error while execution CPackDeb.cmake" << std::endl); + retval = 0; + return retval; + } + + cmsys::Glob gl; + std::string findExpr(this->GetOption("WDIR")); + findExpr += "/*"; + gl.RecurseOn(); + if ( !gl.FindFiles(findExpr) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find any files in the installed directory" << std::endl); + return 0; + } + packageFiles = gl.GetFiles(); + + int res = createDeb(); + if (res != 1) + { + retval = 0; + } + // add the generated package to package file names list + packageFileNames.push_back(packageFileName); + return retval; +} + +//---------------------------------------------------------------------- +int cmCPackDebGenerator::PackageFiles() +{ + int retval = -1; + + /* Are we in the component packaging case */ + if (WantsComponentInstallation()) { + // CASE 1 : COMPONENT ALL-IN-ONE package + // If ALL GROUPS or ALL COMPONENTS in ONE package has been requested + // then the package file is unique and should be open here. + if (componentPackageMethod == ONE_PACKAGE) + { + return PackageComponentsAllInOne(); + } + // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one) + // There will be 1 package for each component group + // however one may require to ignore component group and + // in this case you'll get 1 package for each component. + else + { + return PackageComponents(componentPackageMethod == + ONE_PACKAGE_PER_COMPONENT); + } + } + // CASE 3 : NON COMPONENT package. + else + { + if (!this->ReadListFile("CPackDeb.cmake")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error while execution CPackDeb.cmake" << std::endl); + retval = 0; + } + else + { + packageFiles = files; + return createDeb(); + } + } + return retval; +} + +int cmCPackDebGenerator::createDeb() +{ + const char* cmakeExecutable = this->GetOption("CMAKE_COMMAND"); + + // debian-binary file + std::string dbfilename; + dbfilename += this->GetOption("WDIR"); + dbfilename += "/debian-binary"; + { // the scope is needed for cmGeneratedFileStream + cmGeneratedFileStream out(dbfilename.c_str()); + out << "2.0"; + out << std::endl; // required for valid debian package + } + + // control file + std::string ctlfilename; + ctlfilename = this->GetOption("WDIR"); + ctlfilename += "/control"; + + // debian policy enforce lower case for package name + // mandatory entries: + std::string debian_pkg_name = cmsys::SystemTools::LowerCase( + this->GetOption("CPACK_DEBIAN_PACKAGE_NAME") ); + const char* debian_pkg_version = + this->GetOption("CPACK_DEBIAN_PACKAGE_VERSION"); + const char* debian_pkg_section = + this->GetOption("CPACK_DEBIAN_PACKAGE_SECTION"); + const char* debian_pkg_priority = + this->GetOption("CPACK_DEBIAN_PACKAGE_PRIORITY"); + const char* debian_pkg_arch = + this->GetOption("CPACK_DEBIAN_PACKAGE_ARCHITECTURE"); + const char* maintainer = this->GetOption("CPACK_DEBIAN_PACKAGE_MAINTAINER"); + const char* desc = this->GetOption("CPACK_DEBIAN_PACKAGE_DESCRIPTION"); + + // optional entries + const char* debian_pkg_dep = this->GetOption("CPACK_DEBIAN_PACKAGE_DEPENDS"); + const char* debian_pkg_rec = + this->GetOption("CPACK_DEBIAN_PACKAGE_RECOMMENDS"); + const char* debian_pkg_sug = + this->GetOption("CPACK_DEBIAN_PACKAGE_SUGGESTS"); + const char* debian_pkg_url = + this->GetOption("CPACK_DEBIAN_PACKAGE_HOMEPAGE"); + const char* debian_pkg_predep = + this->GetOption("CPACK_DEBIAN_PACKAGE_PREDEPENDS"); + const char* debian_pkg_enhances = + this->GetOption("CPACK_DEBIAN_PACKAGE_ENHANCES"); + const char* debian_pkg_breaks = + this->GetOption("CPACK_DEBIAN_PACKAGE_BREAKS"); + const char* debian_pkg_conflicts = + this->GetOption("CPACK_DEBIAN_PACKAGE_CONFLICTS"); + const char* debian_pkg_provides = + this->GetOption("CPACK_DEBIAN_PACKAGE_PROVIDES"); + const char* debian_pkg_replaces = + this->GetOption("CPACK_DEBIAN_PACKAGE_REPLACES"); + + { // the scope is needed for cmGeneratedFileStream + cmGeneratedFileStream out(ctlfilename.c_str()); + out << "Package: " << debian_pkg_name << "\n"; + out << "Version: " << debian_pkg_version << "\n"; + out << "Section: " << debian_pkg_section << "\n"; + out << "Priority: " << debian_pkg_priority << "\n"; + out << "Architecture: " << debian_pkg_arch << "\n"; + if(debian_pkg_dep && *debian_pkg_dep) + { + out << "Depends: " << debian_pkg_dep << "\n"; + } + if(debian_pkg_rec && *debian_pkg_rec) + { + out << "Recommends: " << debian_pkg_rec << "\n"; + } + if(debian_pkg_sug && *debian_pkg_sug) + { + out << "Suggests: " << debian_pkg_sug << "\n"; + } + if(debian_pkg_url && *debian_pkg_url) + { + out << "Homepage: " << debian_pkg_url << "\n"; + } + if (debian_pkg_predep && *debian_pkg_predep) + { + out << "Pre-Depends: " << debian_pkg_predep << "\n"; + } + if (debian_pkg_enhances && *debian_pkg_enhances) + { + out << "Enhances: " << debian_pkg_enhances << "\n"; + } + if (debian_pkg_breaks && *debian_pkg_breaks) + { + out << "Breaks: " << debian_pkg_breaks << "\n"; + } + if (debian_pkg_conflicts && *debian_pkg_conflicts) + { + out << "Conflicts: " << debian_pkg_conflicts << "\n"; + } + if (debian_pkg_provides && *debian_pkg_provides) + { + out << "Provides: " << debian_pkg_provides << "\n"; + } + if (debian_pkg_replaces && *debian_pkg_replaces) + { + out << "Replaces: " << debian_pkg_replaces << "\n"; + } + unsigned long totalSize = 0; + { + std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + dirName += '/'; + for (std::vector<std::string>::const_iterator fileIt = + packageFiles.begin(); + fileIt != packageFiles.end(); ++ fileIt ) + { + totalSize += cmSystemTools::FileLength(fileIt->c_str()); + } + } + out << "Installed-Size: " << (totalSize + 1023) / 1024 << "\n"; + out << "Maintainer: " << maintainer << "\n"; + out << "Description: " << desc << "\n"; + out << std::endl; + } + + std::string cmd; + if (NULL != this->GetOption("CPACK_DEBIAN_FAKEROOT_EXECUTABLE")) { + cmd += this->GetOption("CPACK_DEBIAN_FAKEROOT_EXECUTABLE"); + } + cmd += " \""; + cmd += cmakeExecutable; + cmd += "\" -E tar cfz data.tar.gz "; + + // now add all directories which have to be compressed + // collect all top level install dirs for that + // e.g. /opt/bin/foo, /usr/bin/bar and /usr/bin/baz would give /usr and /opt + size_t topLevelLength = std::string(this->GetOption("WDIR")).length(); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "WDIR: \"" << this->GetOption("WDIR") + << "\", length = " << topLevelLength + << std::endl); + std::set<std::string> installDirs; + for (std::vector<std::string>::const_iterator fileIt = + packageFiles.begin(); + fileIt != packageFiles.end(); ++ fileIt ) + { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "FILEIT: \"" << *fileIt << "\"" + << std::endl); + std::string::size_type slashPos = fileIt->find('/', topLevelLength+1); + std::string relativeDir = fileIt->substr(topLevelLength, + slashPos - topLevelLength); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "RELATIVEDIR: \"" << relativeDir + << "\"" << std::endl); + if (installDirs.find(relativeDir) == installDirs.end()) + { + installDirs.insert(relativeDir); + cmd += " ."; + cmd += relativeDir; + } + } + + std::string output; + int retval = -1; + int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, + &retval, this->GetOption("WDIR"), this->GeneratorVerbose, 0); + + if ( !res || retval ) + { + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/Deb.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << cmd.c_str() << std::endl + << "# Working directory: " << toplevel << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running tar command: " + << cmd.c_str() << std::endl + << "Please check " << tmpFile.c_str() << " for errors" << std::endl); + return 0; + } + + std::string md5filename; + md5filename = this->GetOption("WDIR"); + md5filename += "/md5sums"; + + { // the scope is needed for cmGeneratedFileStream + cmGeneratedFileStream out(md5filename.c_str()); + std::vector<std::string>::const_iterator fileIt; +// std::string topLevelWithTrailingSlash = toplevel; + std::string topLevelWithTrailingSlash = + this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + topLevelWithTrailingSlash += '/'; + for ( fileIt = packageFiles.begin(); + fileIt != packageFiles.end(); ++ fileIt ) + { + cmd = "\""; + cmd += cmakeExecutable; + cmd += "\" -E md5sum \""; + cmd += *fileIt; + cmd += "\""; + //std::string output; + //int retVal = -1; + res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, + &retval, toplevel.c_str(), this->GeneratorVerbose, 0); + // debian md5sums entries are like this: + // 014f3604694729f3bf19263bac599765 usr/bin/ccmake + // thus strip the full path (with the trailing slash) + cmSystemTools::ReplaceString(output, + topLevelWithTrailingSlash.c_str(), ""); + out << output; + } + // each line contains a eol. + // Do not end the md5sum file with yet another (invalid) + } + + cmd = ""; + if (NULL != this->GetOption("CPACK_DEBIAN_FAKEROOT_EXECUTABLE")) + { + cmd = this->GetOption("CPACK_DEBIAN_FAKEROOT_EXECUTABLE"); + } + cmd += " \""; + cmd += cmakeExecutable; + cmd += "\" -E tar cfz control.tar.gz ./control ./md5sums"; + const char* controlExtra = + this->GetOption("CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA"); + if( controlExtra ) + { + std::vector<std::string> controlExtraList; + cmSystemTools::ExpandListArgument(controlExtra, controlExtraList); + for(std::vector<std::string>::iterator i = + controlExtraList.begin(); i != controlExtraList.end(); ++i) + { + std::string filenamename = + cmsys::SystemTools::GetFilenameName(i->c_str()); + std::string localcopy = this->GetOption("WDIR"); + localcopy += "/"; + localcopy += filenamename; + // if we can copy the file, it means it does exist, let's add it: + if( cmsys::SystemTools::CopyFileIfDifferent( + i->c_str(), localcopy.c_str()) ) + { + // debian is picky and need relative to ./ path in the tar.gz + cmd += " ./"; + cmd += filenamename; + } + } + } + res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, + &retval, this->GetOption("WDIR"), this->GeneratorVerbose, 0); + + if ( !res || retval ) + { + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/Deb.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << cmd.c_str() << std::endl + << "# Working directory: " << toplevel << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running tar command: " + << cmd.c_str() << std::endl + << "Please check " << tmpFile.c_str() << " for errors" << std::endl); + return 0; + } + + // ar -r your-package-name.deb debian-binary control.tar.gz data.tar.gz + // since debian packages require BSD ar (most Linux distros and even + // FreeBSD and NetBSD ship GNU ar) we use a copy of OpenBSD ar here. + std::vector<std::string> arFiles; + std::string topLevelString = this->GetOption("WDIR"); + topLevelString += "/"; + arFiles.push_back(topLevelString + "debian-binary"); + arFiles.push_back(topLevelString + "control.tar.gz"); + arFiles.push_back(topLevelString + "data.tar.gz"); + std::string outputFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + outputFileName += "/"; + outputFileName += this->GetOption("CPACK_OUTPUT_FILE_NAME"); + res = ar_append(outputFileName.c_str(), arFiles); + if ( res!=0 ) + { + std::string tmpFile = this->GetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME"); + tmpFile += "/Deb.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Problem creating archive using: " << res << std::endl; + return 0; + } + return 1; +} + +bool cmCPackDebGenerator::SupportsComponentInstallation() const + { + if (IsOn("CPACK_DEB_COMPONENT_INSTALL")) + { + return true; + } + else + { + return false; + } + } + +std::string cmCPackDebGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) + { + if (componentPackageMethod == ONE_PACKAGE_PER_COMPONENT) { + return componentName; + } + + if (componentPackageMethod == ONE_PACKAGE) { + return std::string("ALL_COMPONENTS_IN_ONE"); + } + // We have to find the name of the COMPONENT GROUP + // the current COMPONENT belongs to. + std::string groupVar = "CPACK_COMPONENT_" + + cmSystemTools::UpperCase(componentName) + "_GROUP"; + if (NULL != GetOption(groupVar.c_str())) + { + return std::string(GetOption(groupVar.c_str())); + } + else + { + return componentName; + } + } + + +// The following code is taken from OpenBSD ar: +// http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ar/ +// It has been slightly modified: +// -return error codes instead exit() in functions +// -use the stdio file I/O functions instead the file descriptor based ones +// -merged into one cxx file +// -no additional options supported +// The coding style hasn't been modified. + +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Hugh Smith at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#define ARMAG "!<arch>\n" /* ar "magic number" */ +#define SARMAG 8 /* strlen(ARMAG); */ + +#define AR_EFMT1 "#1/" /* extended format #1 */ +#define ARFMAG "`\n" + +/* Header format strings. */ +#define HDR1 "%s%-13d%-12ld%-6u%-6u%-8o%-10lld%2s" +#define HDR2 "%-16.16s%-12ld%-6u%-6u%-8o%-10lld%2s" + +struct ar_hdr { + char ar_name[16]; /* name */ + char ar_date[12]; /* modification time */ + char ar_uid[6]; /* user id */ + char ar_gid[6]; /* group id */ + char ar_mode[8]; /* octal file permissions */ + char ar_size[10]; /* size in bytes */ + char ar_fmag[2]; /* consistency check */ +}; + +/* Set up file copy. */ +#define SETCF(from, fromname, to, toname, pad) { \ + cf.rFile = from; \ + cf.rname = fromname; \ + cf.wFile = to; \ + cf.wname = toname; \ + cf.flags = pad; \ +} + +/* File copy structure. */ +typedef struct { + FILE* rFile; /* read file descriptor */ + const char *rname; /* read name */ + FILE* wFile; /* write file descriptor */ + const char *wname; /* write name */ +#define NOPAD 0x00 /* don't pad */ +#define WPAD 0x02 /* pad on writes */ + unsigned int flags; /* pad flags */ +} CF; + +/* misc.c */ + +static const char * ar_rname(const char *path) +{ + const char *ind = strrchr(path, '/'); + return (ind ) ? ind + 1 : path; +} + +/* archive.c */ + +typedef struct ar_hdr HDR; +static char ar_hb[sizeof(HDR) + 1]; /* real header */ + +static size_t ar_already_written; + +/* copy_ar -- + * Copy size bytes from one file to another - taking care to handle the + * extra byte (for odd size files) when reading archives and writing an + * extra byte if necessary when adding files to archive. The length of + * the object is the long name plus the object itself; the variable + * already_written gets set if a long name was written. + * + * The padding is really unnecessary, and is almost certainly a remnant + * of early archive formats where the header included binary data which + * a PDP-11 required to start on an even byte boundary. (Or, perhaps, + * because 16-bit word addressed copies were faster?) Anyhow, it should + * have been ripped out long ago. + */ +static int copy_ar(CF *cfp, off_t size) +{ + static char pad = '\n'; + off_t sz = size; + size_t nr, nw; + char buf[8*1024]; + + if (sz == 0) + return 0; + + FILE* from = cfp->rFile; + FILE* to = cfp->wFile; + while (sz && + (nr = fread(buf, 1, sz < static_cast<off_t>(sizeof(buf)) + ? static_cast<size_t>(sz) : sizeof(buf), from )) + > 0) { + sz -= nr; + for (size_t off = 0; off < nr; nr -= off, off += nw) + if ((nw = fwrite(buf + off, 1, nr, to)) < nr) + return -1; + } + if (sz) + return -2; + + if (cfp->flags & WPAD && (size + ar_already_written) & 1 + && fwrite(&pad, 1, 1, to) != 1) + return -4; + + return 0; +} + +/* put_arobj -- Write an archive member to a file. */ +static int put_arobj(CF *cfp, struct stat *sb) +{ + int result = 0; + struct ar_hdr *hdr; + + /* If passed an sb structure, reading a file from disk. Get stat(2) + * information, build a name and construct a header. (Files are named + * by their last component in the archive.) */ + const char* name = ar_rname(cfp->rname); + (void)stat(cfp->rname, sb); + + /* If not truncating names and the name is too long or contains + * a space, use extended format 1. */ + size_t lname = strlen(name); + uid_t uid = sb->st_uid; + gid_t gid = sb->st_gid; + if (uid > USHRT_MAX) { + uid = USHRT_MAX; + } + if (gid > USHRT_MAX) { + gid = USHRT_MAX; + } + if (lname > sizeof(hdr->ar_name) || strchr(name, ' ')) + (void)sprintf(ar_hb, HDR1, AR_EFMT1, (int)lname, + (long int)sb->st_mtime, uid, gid, sb->st_mode, + (long long)sb->st_size + lname, ARFMAG); + else { + lname = 0; + (void)sprintf(ar_hb, HDR2, name, + (long int)sb->st_mtime, uid, gid, sb->st_mode, + (long long)sb->st_size, ARFMAG); + } + off_t size = sb->st_size; + + if (fwrite(ar_hb, 1, sizeof(HDR), cfp->wFile) != sizeof(HDR)) + return -1; + + if (lname) { + if (fwrite(name, 1, lname, cfp->wFile) != lname) + return -2; + ar_already_written = lname; + } + result = copy_ar(cfp, size); + ar_already_written = 0; + return result; +} + +/* append.c */ + +/* append -- + * Append files to the archive - modifies original archive or creates + * a new archive if named archive does not exist. + */ +static int ar_append(const char* archive,const std::vector<std::string>& files) +{ + int eval = 0; + FILE* aFile = fopen(archive, "wb+"); + if (aFile!=NULL) { + fwrite(ARMAG, SARMAG, 1, aFile); + if (fseek(aFile, 0, SEEK_END) != -1) { + CF cf; + struct stat sb; + /* Read from disk, write to an archive; pad on write. */ + SETCF(NULL, 0, aFile, archive, WPAD); + for(std::vector<std::string>::const_iterator fileIt = files.begin(); + fileIt!=files.end(); ++fileIt) { + const char* filename = fileIt->c_str(); + FILE* file = fopen(filename, "rb"); + if (file == NULL) { + eval = -1; + continue; + } + cf.rFile = file; + cf.rname = filename; + int result = put_arobj(&cf, &sb); + (void)fclose(file); + if (result!=0) { + eval = -2; + break; + } + } + } + else { + eval = -3; + } + fclose(aFile); + } + else { + eval = -4; + } + return eval; +} diff --git a/Source/CPack/cmCPackDebGenerator.h b/Source/CPack/cmCPackDebGenerator.h new file mode 100644 index 000000000..d678cfacf --- /dev/null +++ b/Source/CPack/cmCPackDebGenerator.h @@ -0,0 +1,77 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackDebGenerator_h +#define cmCPackDebGenerator_h + + +#include "cmCPackGenerator.h" + +/** \class cmCPackDebGenerator + * \brief A generator for Debian packages + * + */ +class cmCPackDebGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackDebGenerator, cmCPackGenerator); + + /** + * Construct generator + */ + cmCPackDebGenerator(); + virtual ~cmCPackDebGenerator(); + + static bool CanGenerate() + { +#ifdef __APPLE__ + // on MacOS enable CPackDeb iff dpkg is found + std::vector<std::string> locations; + locations.push_back("/sw/bin"); // Fink + locations.push_back("/opt/local/bin"); // MacPorts + return cmSystemTools::FindProgram("dpkg",locations) != "" ? true : false; +#else + // legacy behavior on other systems + return true; +#endif + } + +protected: + virtual int InitializeInternal(); + /** + * This method factors out the work done in component packaging case. + */ + int PackageOnePack(std::string initialToplevel, std::string packageName); + /** + * The method used to package files when component + * install is used. This will create one + * archive for each component group. + */ + int PackageComponents(bool ignoreGroup); + /** + * Special case of component install where all + * components will be put in a single installer. + */ + int PackageComponentsAllInOne(); + virtual int PackageFiles(); + virtual const char* GetOutputExtension() { return ".deb"; } + virtual bool SupportsComponentInstallation() const; + virtual std::string GetComponentInstallDirNameSuffix( + const std::string& componentName); + +private: + int createDeb(); + std::vector<std::string> packageFiles; + +}; + +#endif diff --git a/Source/CPack/cmCPackDocumentMacros.cxx b/Source/CPack/cmCPackDocumentMacros.cxx new file mode 100644 index 000000000..ddc75a4b4 --- /dev/null +++ b/Source/CPack/cmCPackDocumentMacros.cxx @@ -0,0 +1,16 @@ +#include "cmCPackDocumentMacros.h" + +void cmCPackDocumentMacros::GetMacrosDocumentation( + std::vector<cmDocumentationEntry>& ) +{ + // Commented-out example of use + // + // cmDocumentationEntry e("cpack_<macro>", + // "Brief Description" + // "which may be on several lines.", + // "Long description in pre-formatted format" + // " blah\n" + // " blah\n" + //); + //v.push_back(e); +} diff --git a/Source/CPack/cmCPackDocumentMacros.h b/Source/CPack/cmCPackDocumentMacros.h new file mode 100644 index 000000000..544f74f5f --- /dev/null +++ b/Source/CPack/cmCPackDocumentMacros.h @@ -0,0 +1,21 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmCPackDocumentMacros_h +#define cmCPackDocumentMacros_h +#include "cmStandardIncludes.h" +class cmCPackDocumentMacros +{ +public: + static void GetMacrosDocumentation(std::vector<cmDocumentationEntry>& v); +}; + +#endif diff --git a/Source/CPack/cmCPackDocumentVariables.cxx b/Source/CPack/cmCPackDocumentVariables.cxx new file mode 100644 index 000000000..edbef45ef --- /dev/null +++ b/Source/CPack/cmCPackDocumentVariables.cxx @@ -0,0 +1,111 @@ +#include "cmCPackDocumentVariables.h" +#include "cmake.h" + +void cmCPackDocumentVariables::DefineVariables(cmake* cm) +{ + // Subsection: variables defined/used by cpack, + // which are common to all CPack generators + + cm->DefineProperty + ("CPACK_PACKAGING_INSTALL_PREFIX", cmProperty::VARIABLE, + "The prefix used in the built package.", + "Each CPack generator has a default value (like /usr)." + " This default value may" + " be overwritten from the CMakeLists.txt or the cpack command line" + " by setting an alternative value.\n" + "e.g. " + " set(CPACK_PACKAGING_INSTALL_PREFIX \"/opt\")\n" + "This is not the same purpose as CMAKE_INSTALL_PREFIX which" + " is used when installing from the build tree without building" + " a package." + "", false, + "Variables common to all CPack generators"); + + cm->DefineProperty + ("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", cmProperty::VARIABLE, + "Boolean toggle to include/exclude top level directory.", + "When preparing a package CPack installs the item under" + " the so-called top level directory. The purpose of" + " is to include (set to 1 or ON or TRUE) the top level directory" + " in the package or not (set to 0 or OFF or FALSE).\n" + "Each CPack generator as a built-in default value for this" + " variable. E.g. Archive generators (ZIP, TGZ, ...) includes" + " the top level whereas RPM or DEB don't. The user may override" + " the default value byt setting this variable.\n" + "There is a similar variable " + "CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY" + "which may be used to override the behavior for the component" + "packaging case which may have different default value for" + "historical (now backward compatibility) reason.", false, + "Variables common to all CPack generators"); + + cm->DefineProperty + ("CPACK_SET_DESTDIR", cmProperty::VARIABLE, + "Boolean toggle to make CPack use DESTDIR mechanism when" + " packaging.", "DESTDIR means DESTination DIRectory." + " It is commonly used by makefile " + "users in order to install software at non-default location. It " + "is a basic relocation mechanism. " + "It is usually invoked like this:\n" + " make DESTDIR=/home/john install\n" + "which will install the concerned software using the" + " installation prefix, e.g. \"/usr/local\" prepended with " + "the DESTDIR value which finally gives \"/home/john/usr/local\"." + " When preparing a package, CPack first installs the items to be " + "packaged in a local (to the build tree) directory by using the " + "same DESTDIR mechanism. Nevertheless, if " + "CPACK_SET_DESTDIR is set then CPack will set DESTDIR before" + " doing the local install. The most noticeable difference is" + " that without CPACK_SET_DESTDIR, CPack uses " + "CPACK_PACKAGING_INSTALL_PREFIX as a prefix whereas with " + "CPACK_SET_DESTDIR set, CPack will use CMAKE_INSTALL_PREFIX as" + " a prefix.\n" + "Manually setting CPACK_SET_DESTDIR may help (or simply be" + " necessary) if some install rules uses absolute " + "DESTINATION (see CMake INSTALL command)." + " However, starting with" + " CPack/CMake 2.8.3 RPM and DEB installers tries to handle DESTDIR" + " automatically so that it is seldom necessary for the user to set" + " it.", false, + "Variables common to all CPack generators"); + + cm->DefineProperty + ("CPACK_INSTALL_SCRIPT", cmProperty::VARIABLE, + "Extra CMake script provided by the user.", + "If set this CMake script will be executed by CPack " + "during its local [CPack-private] installation " + "which is done right before packaging the files." + " The script is not called by e.g.: make install.", false, + "Variables common to all CPack generators"); + + cm->DefineProperty + ("CPACK_ABSOLUTE_DESTINATION_FILES", cmProperty::VARIABLE, + "List of files which have been installed using " + " an ABSOLUTE DESTINATION path.", + "This variable is a Read-Only variable which is set internally" + " by CPack during installation and before packaging using" + " CMAKE_ABSOLUTE_DESTINATION_FILES defined in cmake_install.cmake " + "scripts. The value can be used within CPack project configuration" + " file and/or CPack<GEN>.cmake file of <GEN> generator.", false, + "Variables common to all CPack generators"); + + cm->DefineProperty + ("CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION", cmProperty::VARIABLE, + "Ask CPack to warn each time a file with absolute INSTALL" + " DESTINATION is encountered.", + "This variable triggers the definition of " + "CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION when CPack runs" + " cmake_install.cmake scripts.", false, + "Variables common to all CPack generators"); + + cm->DefineProperty + ("CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION", cmProperty::VARIABLE, + "Ask CPack to error out as soon as a file with absolute INSTALL" + " DESTINATION is encountered.", + "The fatal error is emitted before the installation of " + "the offending file takes place. Some CPack generators, like NSIS," + "enforce this internally. " + "This variable triggers the definition of" + "CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION when CPack runs" + "Variables common to all CPack generators"); +} diff --git a/Source/CPack/cmCPackDocumentVariables.h b/Source/CPack/cmCPackDocumentVariables.h new file mode 100644 index 000000000..e7971be13 --- /dev/null +++ b/Source/CPack/cmCPackDocumentVariables.h @@ -0,0 +1,21 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef cmCPackDocumentVariables_h +#define cmCPackDocumentVariables_h +class cmake; +class cmCPackDocumentVariables +{ +public: + static void DefineVariables(cmake* cm); +}; + +#endif diff --git a/Source/CPack/cmCPackDragNDropGenerator.cxx b/Source/CPack/cmCPackDragNDropGenerator.cxx new file mode 100644 index 000000000..6aee401ff --- /dev/null +++ b/Source/CPack/cmCPackDragNDropGenerator.cxx @@ -0,0 +1,588 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackDragNDropGenerator.h" +#include "cmCPackLog.h" +#include "cmSystemTools.h" +#include "cmGeneratedFileStream.h" + +#include <cmsys/RegularExpression.hxx> + +static const char* SLAHeader = +"data 'LPic' (5000) {\n" +" $\"0002 0011 0003 0001 0000 0000 0002 0000\"\n" +" $\"0008 0003 0000 0001 0004 0000 0004 0005\"\n" +" $\"0000 000E 0006 0001 0005 0007 0000 0007\"\n" +" $\"0008 0000 0047 0009 0000 0034 000A 0001\"\n" +" $\"0035 000B 0001 0020 000C 0000 0011 000D\"\n" +" $\"0000 005B 0004 0000 0033 000F 0001 000C\"\n" +" $\"0010 0000 000B 000E 0000\"\n" +"};\n" +"\n"; + +static const char* SLASTREnglish = +"resource 'STR#' (5002, \"English\") {\n" +" {\n" +" \"English\",\n" +" \"Agree\",\n" +" \"Disagree\",\n" +" \"Print\",\n" +" \"Save...\",\n" +" \"You agree to the License Agreement terms when you click \"\n" +" \"the \\\"Agree\\\" button.\",\n" +" \"Software License Agreement\",\n" +" \"This text cannot be saved. This disk may be full or locked, " +"or the \"\n" +" \"file may be locked.\",\n" +" \"Unable to print. Make sure you have selected a printer.\"\n" +" }\n" +"};\n" +"\n"; + +//---------------------------------------------------------------------- +cmCPackDragNDropGenerator::cmCPackDragNDropGenerator() +{ + // default to one package file for components + this->componentPackageMethod = ONE_PACKAGE; +} + +//---------------------------------------------------------------------- +cmCPackDragNDropGenerator::~cmCPackDragNDropGenerator() +{ +} + +//---------------------------------------------------------------------- +int cmCPackDragNDropGenerator::InitializeInternal() +{ + // Starting with Xcode 4.3, look in "/Applications/Xcode.app" first: + // + std::vector<std::string> paths; + paths.push_back("/Applications/Xcode.app/Contents/Developer/Tools"); + paths.push_back("/Developer/Tools"); + + const std::string hdiutil_path = cmSystemTools::FindProgram("hdiutil", + std::vector<std::string>(), false); + if(hdiutil_path.empty()) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot locate hdiutil command" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_COMMAND_HDIUTIL", hdiutil_path.c_str()); + + const std::string setfile_path = cmSystemTools::FindProgram("SetFile", + paths, false); + if(setfile_path.empty()) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot locate SetFile command" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_COMMAND_SETFILE", setfile_path.c_str()); + + const std::string rez_path = cmSystemTools::FindProgram("Rez", + paths, false); + if(rez_path.empty()) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot locate Rez command" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_COMMAND_REZ", rez_path.c_str()); + + return this->Superclass::InitializeInternal(); +} + +//---------------------------------------------------------------------- +const char* cmCPackDragNDropGenerator::GetOutputExtension() +{ + return ".dmg"; +} + +//---------------------------------------------------------------------- +int cmCPackDragNDropGenerator::PackageFiles() +{ + // gather which directories to make dmg files for + // multiple directories occur if packaging components or groups separately + + // monolith + if(this->Components.empty()) + { + return this->CreateDMG(toplevel, packageFileNames[0]); + } + + // component install + std::vector<std::string> package_files; + + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt=this->Components.begin(); + compIt!=this->Components.end(); ++compIt ) + { + std::string name = GetComponentInstallDirNameSuffix(compIt->first); + package_files.push_back(name); + } + std::sort(package_files.begin(), package_files.end()); + package_files.erase(std::unique(package_files.begin(), + package_files.end()), + package_files.end()); + + + // loop to create dmg files + packageFileNames.clear(); + for(size_t i=0; i<package_files.size(); i++) + { + std::string full_package_name = std::string(toplevel) + std::string("/"); + if(package_files[i] == "ALL_IN_ONE") + { + full_package_name += this->GetOption("CPACK_PACKAGE_FILE_NAME"); + } + else + { + full_package_name += package_files[i]; + } + full_package_name += std::string(GetOutputExtension()); + packageFileNames.push_back(full_package_name); + + std::string src_dir = toplevel; + src_dir += "/"; + src_dir += package_files[i]; + + if(0 == this->CreateDMG(src_dir, full_package_name)) + { + return 0; + } + } + return 1; +} + +//---------------------------------------------------------------------- +bool cmCPackDragNDropGenerator::CopyFile(cmOStringStream& source, + cmOStringStream& target) +{ + if(!cmSystemTools::CopyFileIfDifferent( + source.str().c_str(), + target.str().c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error copying " + << source.str() + << " to " + << target.str() + << std::endl); + + return false; + } + + return true; +} + +//---------------------------------------------------------------------- +bool cmCPackDragNDropGenerator::RunCommand(cmOStringStream& command, + std::string* output) +{ + int exit_code = 1; + + bool result = cmSystemTools::RunSingleCommand( + command.str().c_str(), + output, + &exit_code, + 0, + this->GeneratorVerbose, + 0); + + if(!result || exit_code) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error executing: " + << command.str() + << std::endl); + + return false; + } + + return true; +} + +//---------------------------------------------------------------------- +int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, + const std::string& output_file) +{ + // Get optional arguments ... + const std::string cpack_package_icon = this->GetOption("CPACK_PACKAGE_ICON") + ? this->GetOption("CPACK_PACKAGE_ICON") : ""; + + const std::string cpack_dmg_volume_name = + this->GetOption("CPACK_DMG_VOLUME_NAME") + ? this->GetOption("CPACK_DMG_VOLUME_NAME") + : this->GetOption("CPACK_PACKAGE_FILE_NAME"); + + const std::string cpack_dmg_format = + this->GetOption("CPACK_DMG_FORMAT") + ? this->GetOption("CPACK_DMG_FORMAT") : "UDZO"; + + // Get optional arguments ... + std::string cpack_license_file = + this->GetOption("CPACK_RESOURCE_FILE_LICENSE") ? + this->GetOption("CPACK_RESOURCE_FILE_LICENSE") : ""; + + const std::string cpack_dmg_background_image = + this->GetOption("CPACK_DMG_BACKGROUND_IMAGE") + ? this->GetOption("CPACK_DMG_BACKGROUND_IMAGE") : ""; + + const std::string cpack_dmg_ds_store = + this->GetOption("CPACK_DMG_DS_STORE") + ? this->GetOption("CPACK_DMG_DS_STORE") : ""; + + // only put license on dmg if is user provided + if(!cpack_license_file.empty() && + cpack_license_file.find("CPack.GenericLicense.txt") != std::string::npos) + { + cpack_license_file = ""; + } + + // The staging directory contains everything that will end-up inside the + // final disk image ... + cmOStringStream staging; + staging << src_dir; + + // Add a symlink to /Applications so users can drag-and-drop the bundle + // into it + cmOStringStream application_link; + application_link << staging.str() << "/Applications"; + cmSystemTools::CreateSymlink("/Applications", + application_link.str().c_str()); + + // Optionally add a custom volume icon ... + if(!cpack_package_icon.empty()) + { + cmOStringStream package_icon_source; + package_icon_source << cpack_package_icon; + + cmOStringStream package_icon_destination; + package_icon_destination << staging.str() << "/.VolumeIcon.icns"; + + if(!this->CopyFile(package_icon_source, package_icon_destination)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error copying disk volume icon. " + "Check the value of CPACK_PACKAGE_ICON." + << std::endl); + + return 0; + } + } + + // Optionally add a custom .DS_Store file + // (e.g. for setting background/layout) ... + if(!cpack_dmg_ds_store.empty()) + { + cmOStringStream package_settings_source; + package_settings_source << cpack_dmg_ds_store; + + cmOStringStream package_settings_destination; + package_settings_destination << staging.str() << "/.DS_Store"; + + if(!this->CopyFile(package_settings_source, package_settings_destination)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error copying disk volume settings file. " + "Check the value of CPACK_DMG_DS_STORE." + << std::endl); + + return 0; + } + } + + // Optionally add a custom background image ... + if(!cpack_dmg_background_image.empty()) + { + cmOStringStream package_background_source; + package_background_source << cpack_dmg_background_image; + + cmOStringStream package_background_destination; + package_background_destination << staging.str() << "/background.png"; + + if(!this->CopyFile(package_background_source, + package_background_destination)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error copying disk volume background image. " + "Check the value of CPACK_DMG_BACKGROUND_IMAGE." + << std::endl); + + return 0; + } + + cmOStringStream temp_background_hiding_command; + temp_background_hiding_command << this->GetOption("CPACK_COMMAND_SETFILE"); + temp_background_hiding_command << " -a V \""; + temp_background_hiding_command << package_background_destination.str(); + temp_background_hiding_command << "\""; + + if(!this->RunCommand(temp_background_hiding_command)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error setting attributes on disk volume background image." + << std::endl); + + return 0; + } + } + + // Create a temporary read-write disk image ... + std::string temp_image = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + temp_image += "/temp.dmg"; + + cmOStringStream temp_image_command; + temp_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + temp_image_command << " create"; + temp_image_command << " -ov"; + temp_image_command << " -srcfolder \"" << staging.str() << "\""; + temp_image_command << " -volname \"" + << cpack_dmg_volume_name << "\""; + temp_image_command << " -format UDRW"; + temp_image_command << " \"" << temp_image << "\""; + + if(!this->RunCommand(temp_image_command)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error generating temporary disk image." + << std::endl); + + return 0; + } + + // Optionally set the custom icon flag for the image ... + if(!cpack_package_icon.empty()) + { + cmOStringStream temp_mount; + + cmOStringStream attach_command; + attach_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + attach_command << " attach"; + attach_command << " \"" << temp_image << "\""; + + std::string attach_output; + if(!this->RunCommand(attach_command, &attach_output)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error attaching temporary disk image." + << std::endl); + + return 0; + } + + cmsys::RegularExpression mountpoint_regex(".*(/Volumes/[^\n]+)\n.*"); + mountpoint_regex.find(attach_output.c_str()); + temp_mount << mountpoint_regex.match(1); + + cmOStringStream setfile_command; + setfile_command << this->GetOption("CPACK_COMMAND_SETFILE"); + setfile_command << " -a C"; + setfile_command << " \"" << temp_mount.str() << "\""; + + if(!this->RunCommand(setfile_command)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error assigning custom icon to temporary disk image." + << std::endl); + + return 0; + } + + cmOStringStream detach_command; + detach_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + detach_command << " detach"; + detach_command << " \"" << temp_mount.str() << "\""; + + if(!this->RunCommand(detach_command)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error detaching temporary disk image." + << std::endl); + + return 0; + } + } + + if(!cpack_license_file.empty()) + { + std::string sla_r = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + sla_r += "/sla.r"; + + std::ifstream ifs; + ifs.open(cpack_license_file.c_str()); + if(ifs.is_open()) + { + cmGeneratedFileStream osf(sla_r.c_str()); + osf << "#include <CoreServices/CoreServices.r>\n\n"; + osf << SLAHeader; + osf << "\n"; + osf << "data 'TEXT' (5002, \"English\") {\n"; + while(ifs.good()) + { + std::string line; + std::getline(ifs, line); + // escape quotes + std::string::size_type pos = line.find('\"'); + while(pos != std::string::npos) + { + line.replace(pos, 1, "\\\""); + pos = line.find('\"', pos+2); + } + osf << " \"" << line << "\\n\"\n"; + } + osf << "};\n"; + osf << "\n"; + osf << SLASTREnglish; + ifs.close(); + osf.close(); + } + + // convert to UDCO + std::string temp_udco = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + temp_udco += "/temp-udco.dmg"; + + cmOStringStream udco_image_command; + udco_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + udco_image_command << " convert \"" << temp_image << "\""; + udco_image_command << " -format UDCO"; + udco_image_command << " -o \"" << temp_udco << "\""; + + std::string error; + if(!this->RunCommand(udco_image_command, &error)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error converting to UDCO dmg for adding SLA." << std::endl + << error + << std::endl); + return 0; + } + + // unflatten dmg + cmOStringStream unflatten_command; + unflatten_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + unflatten_command << " unflatten "; + unflatten_command << "\"" << temp_udco << "\""; + + if(!this->RunCommand(unflatten_command, &error)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error unflattening dmg for adding SLA." << std::endl + << error + << std::endl); + return 0; + } + + // Rez the SLA + cmOStringStream embed_sla_command; + embed_sla_command << this->GetOption("CPACK_COMMAND_REZ"); + embed_sla_command << " \"" << sla_r << "\""; + embed_sla_command << " -a -o "; + embed_sla_command << "\"" << temp_udco << "\""; + + if(!this->RunCommand(embed_sla_command, &error)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error adding SLA." << std::endl + << error + << std::endl); + return 0; + } + + // flatten dmg + cmOStringStream flatten_command; + flatten_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + flatten_command << " flatten "; + flatten_command << "\"" << temp_udco << "\""; + + if(!this->RunCommand(flatten_command, &error)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error flattening dmg for adding SLA." << std::endl + << error + << std::endl); + return 0; + } + + temp_image = temp_udco; + } + + + // Create the final compressed read-only disk image ... + cmOStringStream final_image_command; + final_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + final_image_command << " convert \"" << temp_image << "\""; + final_image_command << " -format "; + final_image_command << cpack_dmg_format; + final_image_command << " -imagekey"; + final_image_command << " zlib-level=9"; + final_image_command << " -o \"" << output_file << "\""; + + if(!this->RunCommand(final_image_command)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error compressing disk image." + << std::endl); + + return 0; + } + + return 1; +} + +bool cmCPackDragNDropGenerator::SupportsComponentInstallation() const +{ + return true; +} + +std::string +cmCPackDragNDropGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) +{ + // we want to group components together that go in the same dmg package + std::string package_file_name = this->GetOption("CPACK_PACKAGE_FILE_NAME"); + + // we have 3 mutually exclusive modes to work in + // 1. all components in one package + // 2. each group goes in its own package with left over + // components in their own package + // 3. ignore groups - if grouping is defined, it is ignored + // and each component goes in its own package + + if(this->componentPackageMethod == ONE_PACKAGE) + { + return "ALL_IN_ONE"; + } + + if(this->componentPackageMethod == ONE_PACKAGE_PER_GROUP) + { + // We have to find the name of the COMPONENT GROUP + // the current COMPONENT belongs to. + std::string groupVar = "CPACK_COMPONENT_" + + cmSystemTools::UpperCase(componentName) + "_GROUP"; + const char* _groupName = GetOption(groupVar.c_str()); + if (_groupName) + { + std::string groupName = _groupName; + + groupName = GetComponentPackageFileName(package_file_name, + groupName, true); + return groupName; + } + } + + return GetComponentPackageFileName(package_file_name, componentName, false); +} diff --git a/Source/CPack/cmCPackDragNDropGenerator.h b/Source/CPack/cmCPackDragNDropGenerator.h new file mode 100644 index 000000000..808c61886 --- /dev/null +++ b/Source/CPack/cmCPackDragNDropGenerator.h @@ -0,0 +1,47 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackDragNDropGenerator_h +#define cmCPackDragNDropGenerator_h + +#include "cmCPackGenerator.h" + +/** \class cmCPackDragNDropGenerator + * \brief A generator for OSX drag-n-drop installs + */ +class cmCPackDragNDropGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackDragNDropGenerator, cmCPackGenerator); + + cmCPackDragNDropGenerator(); + virtual ~cmCPackDragNDropGenerator(); + +protected: + virtual int InitializeInternal(); + virtual const char* GetOutputExtension(); + int PackageFiles(); + bool SupportsComponentInstallation() const; + + + bool CopyFile(cmOStringStream& source, cmOStringStream& target); + bool RunCommand(cmOStringStream& command, std::string* output = 0); + + std::string + GetComponentInstallDirNameSuffix(const std::string& componentName); + + int CreateDMG(const std::string& src_dir, const std::string& output_file); + + std::string InstallPrefix; +}; + +#endif diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx new file mode 100644 index 000000000..0177653f5 --- /dev/null +++ b/Source/CPack/cmCPackGenerator.cxx @@ -0,0 +1,1685 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackGenerator.h" + +#include "cmMakefile.h" +#include "cmCPackLog.h" +#include "cmake.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmGeneratedFileStream.h" +#include "cmCPackComponentGroup.h" +#include "cmXMLSafe.h" + +#include <cmsys/SystemTools.hxx> +#include <cmsys/Glob.hxx> +#include <memory> // auto_ptr +#include <algorithm> + +#if defined(__HAIKU__) +#include <StorageKit.h> +#endif + +//---------------------------------------------------------------------- +cmCPackGenerator::cmCPackGenerator() +{ + this->GeneratorVerbose = cmSystemTools::OUTPUT_NONE; + this->MakefileMap = 0; + this->Logger = 0; + this->componentPackageMethod = ONE_PACKAGE_PER_GROUP; +} + +//---------------------------------------------------------------------- +cmCPackGenerator::~cmCPackGenerator() +{ + this->MakefileMap = 0; +} + +//---------------------------------------------------------------------- +void cmCPackGeneratorProgress(const char *msg, float prog, void* ptr) +{ + cmCPackGenerator* self = static_cast<cmCPackGenerator*>(ptr); + self->DisplayVerboseOutput(msg, prog); +} + +//---------------------------------------------------------------------- +void cmCPackGenerator::DisplayVerboseOutput(const char* msg, + float progress) +{ + (void)progress; + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "" << msg << std::endl); +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::PrepareNames() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Create temp directory." << std::endl); + + // checks CPACK_SET_DESTDIR support + if (IsOn("CPACK_SET_DESTDIR")) + { + if (SETDESTDIR_UNSUPPORTED==SupportsSetDestdir()) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_SET_DESTDIR is set to ON but the '" + << Name << "' generator does NOT support it." + << std::endl); + return 0; + } + else if (SETDESTDIR_SHOULD_NOT_BE_USED==SupportsSetDestdir()) + { + cmCPackLogger(cmCPackLog::LOG_WARNING, + "CPACK_SET_DESTDIR is set to ON but it is " + << "usually a bad idea to do that with '" + << Name << "' generator. Use at your own risk." + << std::endl); + } + } + + std::string tempDirectory = this->GetOption("CPACK_PACKAGE_DIRECTORY"); + tempDirectory += "/_CPack_Packages/"; + const char* toplevelTag = this->GetOption("CPACK_TOPLEVEL_TAG"); + if ( toplevelTag ) + { + tempDirectory += toplevelTag; + tempDirectory += "/"; + } + tempDirectory += this->GetOption("CPACK_GENERATOR"); + std::string topDirectory = tempDirectory; + this->GetOption("CPACK_PACKAGE_FILE_NAME"); + const char* pfname = this->GetOption("CPACK_PACKAGE_FILE_NAME"); + if(!pfname) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_PACKAGE_FILE_NAME not specified" << std::endl); + return 0; + } + std::string outName = pfname; + tempDirectory += "/" + outName; + if(!this->GetOutputExtension()) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "No output extension specified" << std::endl); + return 0; + } + outName += this->GetOutputExtension(); + const char* pdir = this->GetOption("CPACK_PACKAGE_DIRECTORY"); + if(!pdir) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_PACKAGE_DIRECTORY not specified" << std::endl); + return 0; + } + + std::string destFile = pdir; + this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PREFIX", destFile.c_str()); + destFile += "/" + outName; + std::string outFile = topDirectory + "/" + outName; + this->SetOptionIfNotSet("CPACK_TOPLEVEL_DIRECTORY", topDirectory.c_str()); + this->SetOptionIfNotSet("CPACK_TEMPORARY_DIRECTORY", tempDirectory.c_str()); + this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_NAME", outName.c_str()); + this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PATH", destFile.c_str()); + this->SetOptionIfNotSet("CPACK_TEMPORARY_PACKAGE_FILE_NAME", + outFile.c_str()); + this->SetOptionIfNotSet("CPACK_INSTALL_DIRECTORY", this->GetInstallPath()); + this->SetOptionIfNotSet("CPACK_NATIVE_INSTALL_DIRECTORY", + cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()).c_str()); + this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY", + tempDirectory.c_str()); + + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Look for: CPACK_PACKAGE_DESCRIPTION_FILE" << std::endl); + const char* descFileName + = this->GetOption("CPACK_PACKAGE_DESCRIPTION_FILE"); + if ( descFileName ) + { + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Look for: " << descFileName << std::endl); + if ( !cmSystemTools::FileExists(descFileName) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find description file name: [" + << descFileName << "]" << std::endl); + return 0; + } + std::ifstream ifs(descFileName); + if ( !ifs ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot open description file name: " << descFileName << std::endl); + return 0; + } + cmOStringStream ostr; + std::string line; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Read description file: " << descFileName << std::endl); + while ( ifs && cmSystemTools::GetLineFromStream(ifs, line) ) + { + ostr << cmXMLSafe(line) << std::endl; + } + this->SetOptionIfNotSet("CPACK_PACKAGE_DESCRIPTION", ostr.str().c_str()); + } + if ( !this->GetOption("CPACK_PACKAGE_DESCRIPTION") ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Project description not specified. Please specify " + "CPACK_PACKAGE_DESCRIPTION or CPACK_PACKAGE_DESCRIPTION_FILE." + << std::endl); + return 0; + } + + this->SetOptionIfNotSet("CPACK_REMOVE_TOPLEVEL_DIRECTORY", "1"); + + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::InstallProject() +{ + 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")) + | cmSystemTools::IsInternallyOn( + 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, + "Problem creating temporary directory: " + << (tempInstallDirectory ? tempInstallDirectory : "(NULL}") + << std::endl); + return 0; + } + + if ( setDestDir ) + { + std::string destDir = "DESTDIR="; + destDir += tempInstallDirectory; + cmSystemTools::PutEnv(destDir.c_str()); + } + else + { + // Make sure there is no destdir + cmSystemTools::PutEnv("DESTDIR="); + } + + // If the CPackConfig file sets CPACK_INSTALL_COMMANDS then run them + // as listed + if ( !this->InstallProjectViaInstallCommands( + setDestDir, tempInstallDirectory) ) + { + return 0; + } + + // If the CPackConfig file sets CPACK_INSTALL_SCRIPT then run them + // as listed + if ( !this->InstallProjectViaInstallScript( + setDestDir, tempInstallDirectory) ) + { + return 0; + } + + // If the CPackConfig file sets CPACK_INSTALLED_DIRECTORIES + // then glob it and copy it to CPACK_TEMPORARY_DIRECTORY + // This is used in Source packaging + if ( !this->InstallProjectViaInstalledDirectories( + setDestDir, tempInstallDirectory) ) + { + return 0; + } + + + // If the project is a CMAKE project then run pre-install + // and then read the cmake_install script to run it + if ( !this->InstallProjectViaInstallCMakeProjects( + setDestDir, bareTempInstallDirectory.c_str()) ) + { + return 0; + } + + if ( setDestDir ) + { + cmSystemTools::PutEnv("DESTDIR="); + } + + return res; +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::InstallProjectViaInstallCommands( + bool setDestDir, const char* tempInstallDirectory) +{ + (void) setDestDir; + const char* installCommands = this->GetOption("CPACK_INSTALL_COMMANDS"); + if ( installCommands && *installCommands ) + { + std::string tempInstallDirectoryEnv = "CMAKE_INSTALL_PREFIX="; + tempInstallDirectoryEnv += tempInstallDirectory; + cmSystemTools::PutEnv(tempInstallDirectoryEnv.c_str()); + std::vector<std::string> installCommandsVector; + cmSystemTools::ExpandListArgument(installCommands,installCommandsVector); + std::vector<std::string>::iterator it; + for ( it = installCommandsVector.begin(); + it != installCommandsVector.end(); + ++it ) + { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << it->c_str() + << std::endl); + std::string output; + int retVal = 1; + bool resB = cmSystemTools::RunSingleCommand(it->c_str(), &output, + &retVal, 0, this->GeneratorVerbose, 0); + if ( !resB || retVal ) + { + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/InstallOutput.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << it->c_str() << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem running install command: " << it->c_str() << std::endl + << "Please check " << tmpFile.c_str() << " for errors" + << std::endl); + return 0; + } + } + } + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::InstallProjectViaInstalledDirectories( + bool setDestDir, const char* tempInstallDirectory) +{ + (void)setDestDir; + (void)tempInstallDirectory; + std::vector<cmsys::RegularExpression> ignoreFilesRegex; + const char* cpackIgnoreFiles = this->GetOption("CPACK_IGNORE_FILES"); + if ( cpackIgnoreFiles ) + { + std::vector<std::string> ignoreFilesRegexString; + cmSystemTools::ExpandListArgument(cpackIgnoreFiles, + ignoreFilesRegexString); + std::vector<std::string>::iterator it; + for ( it = ignoreFilesRegexString.begin(); + it != ignoreFilesRegexString.end(); + ++it ) + { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Create ignore files regex for: " << it->c_str() << std::endl); + ignoreFilesRegex.push_back(it->c_str()); + } + } + const char* installDirectories + = this->GetOption("CPACK_INSTALLED_DIRECTORIES"); + if ( installDirectories && *installDirectories ) + { + std::vector<std::string> installDirectoriesVector; + cmSystemTools::ExpandListArgument(installDirectories, + installDirectoriesVector); + if ( installDirectoriesVector.size() % 2 != 0 ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_INSTALLED_DIRECTORIES should contain pairs of <directory> and " + "<subdirectory>. The <subdirectory> can be '.' to be installed in " + "the toplevel directory of installation." << std::endl); + return 0; + } + std::vector<std::string>::iterator it; + const char* tempDir = tempInstallDirectory; + for ( it = installDirectoriesVector.begin(); + it != installDirectoriesVector.end(); + ++it ) + { + std::list<std::pair<std::string,std::string> > symlinkedFiles; + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl); + cmsys::Glob gl; + std::string top = it->c_str(); + it ++; + std::string subdir = it->c_str(); + std::string findExpr = top; + findExpr += "/*"; + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Install directory: " << top << std::endl); + gl.RecurseOn(); + if ( !gl.FindFiles(findExpr) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find any files in the installed directory" << std::endl); + return 0; + } + files = gl.GetFiles(); + std::vector<std::string>::iterator gfit; + std::vector<cmsys::RegularExpression>::iterator regIt; + for ( gfit = files.begin(); gfit != files.end(); ++ gfit ) + { + bool skip = false; + std::string &inFile = *gfit; + for ( regIt= ignoreFilesRegex.begin(); + regIt!= ignoreFilesRegex.end(); + ++ regIt) + { + if ( regIt->find(inFile.c_str()) ) + { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Ignore file: " + << inFile.c_str() << std::endl); + skip = true; + } + } + if ( skip ) + { + continue; + } + std::string filePath = tempDir; + filePath += "/" + subdir + "/" + + cmSystemTools::RelativePath(top.c_str(), gfit->c_str()); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Copy file: " + << inFile.c_str() << " -> " << filePath.c_str() << std::endl); + /* If the file is a symlink we will have to re-create it */ + if ( cmSystemTools::FileIsSymlink(inFile.c_str())) + { + std::string targetFile; + std::string inFileRelative = + cmSystemTools::RelativePath(top.c_str(),inFile.c_str()); + cmSystemTools::ReadSymlink(inFile.c_str(),targetFile); + symlinkedFiles.push_back(std::pair<std::string, + std::string>(targetFile,inFileRelative)); + } + /* If it is not a symlink then do a plain copy */ + else if (!( + cmSystemTools::CopyFileIfDifferent(inFile.c_str(),filePath.c_str()) + && + cmSystemTools::CopyFileTime(inFile.c_str(),filePath.c_str()) + ) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying file: " + << inFile.c_str() << " -> " << filePath.c_str() << std::endl); + return 0; + } + } + /* rebuild symlinks in the installed tree */ + if (symlinkedFiles.size()>0) + { + std::list< std::pair<std::string,std::string> >::iterator symlinkedIt; + std::string curDir = cmSystemTools::GetCurrentWorkingDirectory(); + std::string goToDir = tempDir; + goToDir += "/"+subdir; + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Change dir to: " << goToDir <<std::endl); + cmSystemTools::ChangeDirectory(goToDir.c_str()); + for (symlinkedIt=symlinkedFiles.begin(); + symlinkedIt != symlinkedFiles.end(); + ++symlinkedIt) + { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Will create a symlink: " + << symlinkedIt->second << "--> " + << symlinkedIt->first << std::endl); + if (!cmSystemTools::CreateSymlink((symlinkedIt->first).c_str(), + (symlinkedIt->second).c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot create symlink: " + << symlinkedIt->second << "--> " + << symlinkedIt->first << std::endl); + return 0; + } + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Going back to: " + << curDir <<std::endl); + cmSystemTools::ChangeDirectory(curDir.c_str()); + } + } + } + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::InstallProjectViaInstallScript( + bool setDestDir, const char* tempInstallDirectory) +{ + const char* cmakeScripts + = this->GetOption("CPACK_INSTALL_SCRIPT"); + if ( cmakeScripts && *cmakeScripts ) + { + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Install scripts: " << cmakeScripts << std::endl); + std::vector<std::string> cmakeScriptsVector; + cmSystemTools::ExpandListArgument(cmakeScripts, + cmakeScriptsVector); + std::vector<std::string>::iterator it; + for ( it = cmakeScriptsVector.begin(); + it != cmakeScriptsVector.end(); + ++it ) + { + std::string installScript = it->c_str(); + + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Install script: " << installScript << std::endl); + + if ( setDestDir ) + { + // For DESTDIR based packaging, use the *project* CMAKE_INSTALL_PREFIX + // underneath the tempInstallDirectory. The value of the project's + // CMAKE_INSTALL_PREFIX is sent in here as the value of the + // CPACK_INSTALL_PREFIX variable. + + std::string dir; + if (this->GetOption("CPACK_INSTALL_PREFIX")) + { + dir += this->GetOption("CPACK_INSTALL_PREFIX"); + } + this->SetOption("CMAKE_INSTALL_PREFIX", dir.c_str()); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Using DESTDIR + CPACK_INSTALL_PREFIX... (this->SetOption)" + << std::endl); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'" << std::endl); + } + else + { + this->SetOption("CMAKE_INSTALL_PREFIX", tempInstallDirectory); + + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Using non-DESTDIR install... (this->SetOption)" << std::endl); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Setting CMAKE_INSTALL_PREFIX to '" << tempInstallDirectory + << "'" << std::endl); + } + + this->SetOptionIfNotSet("CMAKE_CURRENT_BINARY_DIR", + tempInstallDirectory); + this->SetOptionIfNotSet("CMAKE_CURRENT_SOURCE_DIR", + tempInstallDirectory); + int res = this->MakefileMap->ReadListFile(0, installScript.c_str()); + if ( cmSystemTools::GetErrorOccuredFlag() || !res ) + { + return 0; + } + } + } + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::InstallProjectViaInstallCMakeProjects( + bool setDestDir, const char* baseTempInstallDirectory) +{ + const char* cmakeProjects + = this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS"); + const char* cmakeGenerator + = this->GetOption("CPACK_CMAKE_GENERATOR"); + std::string absoluteDestFiles; + if ( cmakeProjects && *cmakeProjects ) + { + if ( !cmakeGenerator ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_INSTALL_CMAKE_PROJECTS is specified, but " + "CPACK_CMAKE_GENERATOR is not. CPACK_CMAKE_GENERATOR " + "is required to install the project." + << std::endl); + return 0; + } + std::vector<std::string> cmakeProjectsVector; + cmSystemTools::ExpandListArgument(cmakeProjects, + cmakeProjectsVector); + std::vector<std::string>::iterator it; + for ( it = cmakeProjectsVector.begin(); + it != cmakeProjectsVector.end(); + ++it ) + { + if ( it+1 == cmakeProjectsVector.end() || + it+2 == cmakeProjectsVector.end() || + it+3 == cmakeProjectsVector.end() ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Not enough items on list: CPACK_INSTALL_CMAKE_PROJECTS. " + "CPACK_INSTALL_CMAKE_PROJECTS should hold quadruplet of install " + "directory, install project name, install component, and install " + "subdirectory." + << std::endl); + return 0; + } + std::string installDirectory = it->c_str(); + ++it; + std::string installProjectName = it->c_str(); + ++it; + std::string installComponent = it->c_str(); + ++it; + std::string installSubDirectory = it->c_str(); + std::string installFile = installDirectory + "/cmake_install.cmake"; + + std::vector<std::string> componentsVector; + + bool componentInstall = false; + /* + * We do a component install iff + * - the CPack generator support component + * - the user did not request Monolithic install + * (this works at CPack time too) + */ + if (this->SupportsComponentInstallation() & + !(this->IsSet("CPACK_MONOLITHIC_INSTALL"))) + { + // Determine the installation types for this project (if provided). + std::string installTypesVar = "CPACK_" + + cmSystemTools::UpperCase(installComponent) + "_INSTALL_TYPES"; + const char *installTypes = this->GetOption(installTypesVar.c_str()); + if (installTypes && *installTypes) + { + std::vector<std::string> installTypesVector; + cmSystemTools::ExpandListArgument(installTypes, installTypesVector); + std::vector<std::string>::iterator installTypeIt; + for (installTypeIt = installTypesVector.begin(); + installTypeIt != installTypesVector.end(); + ++installTypeIt) + { + this->GetInstallationType(installProjectName.c_str(), + installTypeIt->c_str()); + } + } + + // Determine the set of components that will be used in this project + std::string componentsVar + = "CPACK_COMPONENTS_" + cmSystemTools::UpperCase(installComponent); + const char *components = this->GetOption(componentsVar.c_str()); + if (components && *components) + { + cmSystemTools::ExpandListArgument(components, componentsVector); + std::vector<std::string>::iterator compIt; + for (compIt = componentsVector.begin(); + compIt != componentsVector.end(); + ++compIt) + { + GetComponent(installProjectName.c_str(), compIt->c_str()); + } + componentInstall = true; + } + } + if (componentsVector.empty()) + { + componentsVector.push_back(installComponent); + } + + const char* buildConfig = this->GetOption("CPACK_BUILD_CONFIG"); + cmGlobalGenerator* globalGenerator + = this->MakefileMap->GetCMakeInstance()->CreateGlobalGenerator( + cmakeGenerator); + // set the global flag for unix style paths on cmSystemTools as + // soon as the generator is set. This allows gmake to be used + // on windows. + cmSystemTools::SetForceUnixPaths(globalGenerator->GetForceUnixPaths()); + + // Does this generator require pre-install? + if ( globalGenerator->GetPreinstallTargetName() ) + { + globalGenerator->FindMakeProgram(this->MakefileMap); + const char* cmakeMakeProgram + = this->MakefileMap->GetDefinition("CMAKE_MAKE_PROGRAM"); + std::string buildCommand + = globalGenerator->GenerateBuildCommand(cmakeMakeProgram, + installProjectName.c_str(), 0, + globalGenerator->GetPreinstallTargetName(), + buildConfig, false, false); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Install command: " << buildCommand << std::endl); + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Run preinstall target for: " << installProjectName << std::endl); + std::string output; + int retVal = 1; + bool resB = + cmSystemTools::RunSingleCommand(buildCommand.c_str(), + &output, + &retVal, + installDirectory.c_str(), + this->GeneratorVerbose, 0); + if ( !resB || retVal ) + { + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/PreinstallOutput.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << buildCommand.c_str() << std::endl + << "# Directory: " << installDirectory.c_str() << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem running install command: " << buildCommand.c_str() + << std::endl + << "Please check " << tmpFile.c_str() << " for errors" + << std::endl); + return 0; + } + } + delete globalGenerator; + + 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()); + cmMakefile *mf = lg->GetMakefile(); + std::string realInstallDirectory = tempInstallDirectory; + if ( !installSubDirectory.empty() && installSubDirectory != "/" ) + { + realInstallDirectory += installSubDirectory; + } + if (componentInstall) + { + tempInstallDirectory += "/"; + // Some CPack generators would rather chose + // the local installation directory suffix. + // Some (e.g. RPM) use + // one install directory for each component **GROUP** + // instead of the default + // one install directory for each component. + tempInstallDirectory += + GetComponentInstallDirNameSuffix(installComponent); + if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) + { + tempInstallDirectory += "/"; + tempInstallDirectory += this->GetOption("CPACK_PACKAGE_FILE_NAME"); + } + } + + 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. + // + // If DESTDIR has been 'internally set ON' this means that + // the underlying CPack specific generator did ask for that + // In this case we may override CPACK_INSTALL_PREFIX with + // CPACK_PACKAGING_INSTALL_PREFIX + // I know this is tricky and awkward but it's the price for + // CPACK_SET_DESTDIR backward compatibility. + if (cmSystemTools::IsInternallyOn( + this->GetOption("CPACK_SET_DESTDIR"))) + { + this->SetOption("CPACK_INSTALL_PREFIX", + this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX")); + } + 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); + + // Make sure that DESTDIR + CPACK_INSTALL_PREFIX directory + // exists: + // + if (cmSystemTools::StringStartsWith(dir.c_str(), "/")) + { + dir = tempInstallDirectory + dir; + } + else + { + dir = tempInstallDirectory + "/" + dir; + } + /* + * We must re-set DESTDIR for each component + * We must not add the CPACK_INSTALL_PREFIX part because + * it will be added using the override of CMAKE_INSTALL_PREFIX + * The main reason for this awkward trick is that + * are using DESTDIR for 2 different reasons: + * - Because it was asked by the CPack Generator or the user + * using CPACK_SET_DESTDIR + * - Because it was already used for component install + * in order to put things in subdirs... + */ + cmSystemTools::PutEnv( + (std::string("DESTDIR=")+tempInstallDirectory).c_str() + ); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Creating directory: '" << dir << "'" << std::endl); + + if ( !cmsys::SystemTools::MakeDirectory(dir.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating temporary directory: " + << dir << std::endl); + return 0; + } + } + else + { + mf->AddDefinition("CMAKE_INSTALL_PREFIX", + tempInstallDirectory.c_str()); + + if ( !cmsys::SystemTools::MakeDirectory( + tempInstallDirectory.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating temporary directory: " + << tempInstallDirectory << std::endl); + return 0; + } + + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Using non-DESTDIR install... (mf->AddDefinition)" + << std::endl); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- 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"); + } + // Remember the list of files before installation + // of the current component (if we are in component install) + const char* InstallPrefix = tempInstallDirectory.c_str(); + std::vector<std::string> filesBefore; + std::string findExpr(InstallPrefix); + if (componentInstall) + { + cmsys::Glob glB; + findExpr += "/*"; + glB.RecurseOn(); + glB.FindFiles(findExpr); + filesBefore = glB.GetFiles(); + std::sort(filesBefore.begin(),filesBefore.end()); + } + + // If CPack was asked to warn on ABSOLUTE INSTALL DESTINATION + // then forward request to cmake_install.cmake script + if (this->GetOption("CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION")) + { + mf->AddDefinition("CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION", + "1"); + } + // If current CPack generator does support + // ABSOLUTE INSTALL DESTINATION or CPack has been asked for + // then ask cmake_install.cmake script to error out + // as soon as it occurs (before installing file) + if (!SupportsAbsoluteDestination() || + this->GetOption("CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION")) + { + mf->AddDefinition("CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION", + "1"); + } + // do installation + int res = mf->ReadListFile(0, installFile.c_str()); + // forward definition of CMAKE_ABSOLUTE_DESTINATION_FILES + // to CPack (may be used by generators like CPack RPM or DEB) + // in order to transparently handle ABSOLUTE PATH + if (mf->GetDefinition("CMAKE_ABSOLUTE_DESTINATION_FILES")) + { + mf->AddDefinition("CPACK_ABSOLUTE_DESTINATION_FILES", + mf->GetDefinition("CMAKE_ABSOLUTE_DESTINATION_FILES")); + } + + // Now rebuild the list of files after installation + // of the current component (if we are in component install) + if (componentInstall) + { + cmsys::Glob glA; + glA.RecurseOn(); + glA.FindFiles(findExpr); + std::vector<std::string> filesAfter = glA.GetFiles(); + std::sort(filesAfter.begin(),filesAfter.end()); + std::vector<std::string>::iterator diff; + std::vector<std::string> result(filesAfter.size()); + diff = std::set_difference ( + filesAfter.begin(),filesAfter.end(), + filesBefore.begin(),filesBefore.end(), + result.begin()); + + std::vector<std::string>::iterator fit; + std::string localFileName; + // Populate the File field of each component + for (fit=result.begin();fit!=diff;++fit) + { + localFileName = + cmSystemTools::RelativePath(InstallPrefix, fit->c_str()); + localFileName = + localFileName.substr(localFileName.find('/')+1, + std::string::npos); + Components[installComponent].Files.push_back(localFileName); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file <" + <<localFileName<<"> to component <" + <<installComponent<<">"<<std::endl); + } + } + + if (NULL !=mf->GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES")) { + if (absoluteDestFiles.length()>0) { + absoluteDestFiles +=";"; + } + absoluteDestFiles += + mf->GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES"); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Got some ABSOLUTE DESTINATION FILES: " + << absoluteDestFiles << std::endl); + // define component specific var + if (componentInstall) + { + std::string absoluteDestFileComponent = + std::string("CPACK_ABSOLUTE_DESTINATION_FILES") + + "_" + GetComponentInstallDirNameSuffix(installComponent); + if (NULL != this->GetOption(absoluteDestFileComponent.c_str())) + { + std::string absoluteDestFilesListComponent = + this->GetOption(absoluteDestFileComponent.c_str()); + absoluteDestFilesListComponent +=";"; + absoluteDestFilesListComponent += + mf->GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES"); + this->SetOption(absoluteDestFileComponent.c_str(), + absoluteDestFilesListComponent.c_str()); + } + else + { + this->SetOption(absoluteDestFileComponent.c_str(), + mf->GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES")); + } + } + } + if ( cmSystemTools::GetErrorOccuredFlag() || !res ) + { + return 0; + } + } + } + } + this->SetOption("CPACK_ABSOLUTE_DESTINATION_FILES", + absoluteDestFiles.c_str()); + return 1; +} + +//---------------------------------------------------------------------- +bool cmCPackGenerator::ReadListFile(const char* moduleName) +{ + bool retval; + std::string fullPath = this->MakefileMap->GetModulesFile(moduleName); + retval = this->MakefileMap->ReadListFile(0, fullPath.c_str()); + // include FATAL_ERROR and ERROR in the return status + retval = retval && (! cmSystemTools::GetErrorOccuredFlag()); + return retval; +} + +//---------------------------------------------------------------------- +void cmCPackGenerator::SetOptionIfNotSet(const char* op, + const char* value) +{ + const char* def = this->MakefileMap->GetDefinition(op); + if ( def && *def ) + { + return; + } + this->SetOption(op, value); +} + +//---------------------------------------------------------------------- +void cmCPackGenerator::SetOption(const char* op, const char* value) +{ + if ( !op ) + { + return; + } + if ( !value ) + { + this->MakefileMap->RemoveDefinition(op); + return; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, this->GetNameOfClass() + << "::SetOption(" << op << ", " << value << ")" << std::endl); + this->MakefileMap->AddDefinition(op, value); +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::DoPackage() +{ + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "Create package using " << this->Name.c_str() << std::endl); + + // Prepare CPack internal name and check + // values for many CPACK_xxx vars + if ( !this->PrepareNames() ) + { + return 0; + } + + // Digest Component grouping specification + if ( !this->PrepareGroupingKind() ) + { + return 0; + } + + if ( cmSystemTools::IsOn( + this->GetOption("CPACK_REMOVE_TOPLEVEL_DIRECTORY")) ) + { + const char* toplevelDirectory + = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + if ( cmSystemTools::FileExists(toplevelDirectory) ) + { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Remove toplevel directory: " + << toplevelDirectory << std::endl); + if ( !cmSystemTools::RepeatedRemoveDirectory(toplevelDirectory) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem removing toplevel directory: " + << toplevelDirectory + << std::endl); + return 0; + } + } + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "About to install project " << std::endl); + + if ( !this->InstallProject() ) + { + return 0; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Done install project " << std::endl); + + + const char* tempPackageFileName = this->GetOption( + "CPACK_TEMPORARY_PACKAGE_FILE_NAME"); + const char* packageFileName = this->GetOption("CPACK_OUTPUT_FILE_PATH"); + const char* tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl); + cmsys::Glob gl; + std::string findExpr = tempDirectory; + findExpr += "/*"; + gl.RecurseOn(); + gl.SetRecurseThroughSymlinks(false); + if ( !gl.FindFiles(findExpr) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find any files in the packaging tree" << std::endl); + return 0; + } + + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Create package" << std::endl); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Package files to: " + << (tempPackageFileName ? tempPackageFileName : "(NULL)") << std::endl); + if ( cmSystemTools::FileExists(tempPackageFileName) ) + { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Remove old package file" + << std::endl); + cmSystemTools::RemoveFile(tempPackageFileName); + } + if ( cmSystemTools::IsOn(this->GetOption( + "CPACK_INCLUDE_TOPLEVEL_DIRECTORY")) ) + { + tempDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + } + + // The files to be installed + files = gl.GetFiles(); + + packageFileNames.clear(); + /* Put at least one file name into the list of + * wanted packageFileNames. The specific generator + * may update this during PackageFiles. + * (either putting several names or updating the provided one) + */ + packageFileNames.push_back(tempPackageFileName); + toplevel = tempDirectory; + if ( !this->PackageFiles() || cmSystemTools::GetErrorOccuredFlag()) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem compressing the directory" + << std::endl); + return 0; + } + + /* + * Copy the generated packages to final destination + * - there may be several of them + * - the initially provided name may have changed + * (because the specific generator did 'normalize' it) + */ + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Copying final package(s) [" + <<packageFileNames.size() + <<"]:"<<std::endl); + std::vector<std::string>::iterator it; + /* now copy package one by one */ + for (it=packageFileNames.begin();it!=packageFileNames.end();++it) + { + std::string tmpPF(this->GetOption("CPACK_OUTPUT_FILE_PREFIX")); + tempPackageFileName = it->c_str(); + tmpPF += "/"+cmSystemTools::GetFilenameName(*it); + packageFileName = tmpPF.c_str(); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Copy final package(s): " + << (tempPackageFileName ? tempPackageFileName : "(NULL)" ) + << " to " + << (packageFileName ? packageFileName : "(NULL)") + << std::endl); + if ( !cmSystemTools::CopyFileIfDifferent(tempPackageFileName, + packageFileName) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the package: " + << (tempPackageFileName ? tempPackageFileName : "(NULL)" ) + << " to " + << (packageFileName ? packageFileName : "(NULL)") + << std::endl); + return 0; + } + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- package: " + << packageFileName + << " generated." << std::endl); + } + + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::Initialize(const char* name, cmMakefile* mf) +{ + this->MakefileMap = mf; + this->Name = name; + if ( !this->SetCMakeRoot() ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot initialize the generator" << std::endl); + return 0; + } + // set the running generator name + this->SetOption("CPACK_GENERATOR", this->Name.c_str()); + // Load the project specific config file + const char* config = + this->GetOption("CPACK_PROJECT_CONFIG_FILE"); + if(config) + { + mf->ReadListFile(config); + } + int result = this->InitializeInternal(); + if (cmSystemTools::GetErrorOccuredFlag()) + { + return 0; + } + + // If a generator subclass did not already set this option in its + // InitializeInternal implementation, and the project did not already set + // it, the default value should be: + this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/"); + + return result; +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::InitializeInternal() +{ + return 1; +} + +//---------------------------------------------------------------------- +bool cmCPackGenerator::IsSet(const char* name) const +{ + return this->MakefileMap->IsSet(name); +} + +//---------------------------------------------------------------------- +bool cmCPackGenerator::IsOn(const char* name) const +{ + return cmSystemTools::IsOn(GetOption(name)); +} + +//---------------------------------------------------------------------- +const char* cmCPackGenerator::GetOption(const char* op) const +{ + const char* ret = this->MakefileMap->GetDefinition(op); + if(!ret) + { + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Warning, GetOption return NULL for: " + << op + << std::endl); + } + return ret; +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::SetCMakeRoot() +{ + // use the CMAKE_ROOT from cmake which should have been + // found by now + const char* root= + this->MakefileMap->GetDefinition("CMAKE_ROOT"); + + if(root) + { + this->CMakeRoot = root; + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Looking for CMAKE_ROOT: " + << this->CMakeRoot.c_str() << std::endl); + this->SetOption("CMAKE_ROOT", this->CMakeRoot.c_str()); + return 1; + } + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Could not find CMAKE_ROOT !!!" + << std::endl + << "CMake has most likely not been installed correctly." + << std::endl + <<"Modules directory not found in" + << std::endl); + return 0; +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::PackageFiles() +{ + return 0; +} + +//---------------------------------------------------------------------- +const char* cmCPackGenerator::GetInstallPath() +{ + if ( !this->InstallPath.empty() ) + { + return this->InstallPath.c_str(); + } +#if defined(_WIN32) && !defined(__CYGWIN__) + const char* prgfiles = cmsys::SystemTools::GetEnv("ProgramFiles"); + const char* sysDrive = cmsys::SystemTools::GetEnv("SystemDrive"); + if ( prgfiles ) + { + this->InstallPath = prgfiles; + } + else if ( sysDrive ) + { + this->InstallPath = sysDrive; + this->InstallPath += "/Program Files"; + } + else + { + this->InstallPath = "c:/Program Files"; + } + this->InstallPath += "/"; + this->InstallPath += this->GetOption("CPACK_PACKAGE_NAME"); + this->InstallPath += "-"; + this->InstallPath += this->GetOption("CPACK_PACKAGE_VERSION"); +#elif defined(__HAIKU__) + BPath dir; + if (find_directory(B_COMMON_DIRECTORY, &dir) == B_OK) + { + this->InstallPath = dir.Path(); + } + else + { + this->InstallPath = "/boot/common"; + } +#else + this->InstallPath = "/usr/local/"; +#endif + return this->InstallPath.c_str(); +} + +//---------------------------------------------------------------------- +const char* cmCPackGenerator::GetPackagingInstallPrefix() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "GetPackagingInstallPrefix: '" + << this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX") << "'" << std::endl); + + return this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX"); +} + +//---------------------------------------------------------------------- +std::string cmCPackGenerator::FindTemplate(const char* name) +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Look for template: " + << (name ? name : "(NULL)") << std::endl); + std::string ffile = this->MakefileMap->GetModulesFile(name); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Found template: " + << ffile.c_str() << std::endl); + return ffile; +} + +//---------------------------------------------------------------------- +bool cmCPackGenerator::ConfigureString(const std::string& inString, + std::string& outString) +{ + this->MakefileMap->ConfigureString(inString, + outString, true, false); + return true; +} + +//---------------------------------------------------------------------- +bool cmCPackGenerator::ConfigureFile(const char* inName, + const char* outName, bool copyOnly /* = false */) +{ + return this->MakefileMap->ConfigureFile(inName, outName, + copyOnly, true, false) == 1; +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::CleanTemporaryDirectory() +{ + std::string tempInstallDirectoryWithPostfix + = this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY"); + const char* tempInstallDirectory = tempInstallDirectoryWithPostfix.c_str(); + if(cmsys::SystemTools::FileExists(tempInstallDirectory)) + { + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Clean temporary : " + << tempInstallDirectory << std::endl); + if(!cmSystemTools::RepeatedRemoveDirectory(tempInstallDirectory)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem removing temporary directory: " << + tempInstallDirectory + << std::endl); + return 0; + } + } + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackGenerator::PrepareGroupingKind() +{ + // find a component package method specified by the user + ComponentPackageMethod method = UNKNOWN_COMPONENT_PACKAGE_METHOD; + + if(this->GetOption("CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE")) + { + method = ONE_PACKAGE; + } + + if(this->GetOption("CPACK_COMPONENTS_IGNORE_GROUPS")) + { + method = ONE_PACKAGE_PER_COMPONENT; + } + + if(this->GetOption("CPACK_COMPONENTS_ONE_PACKAGE_PER_GROUP")) + { + method = ONE_PACKAGE_PER_GROUP; + } + + std::string groupingType; + + // Second way to specify grouping + if (NULL != this->GetOption("CPACK_COMPONENTS_GROUPING")) { + groupingType = this->GetOption("CPACK_COMPONENTS_GROUPING"); + } + + if (groupingType.length()>0) + { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "[" + << this->Name << "]" + << " requested component grouping = "<< groupingType <<std::endl); + if (groupingType == "ALL_COMPONENTS_IN_ONE") + { + method = ONE_PACKAGE; + } + else if (groupingType == "IGNORE") + { + method = ONE_PACKAGE_PER_COMPONENT; + } + else if (groupingType == "ONE_PER_GROUP") + { + method = ONE_PACKAGE_PER_GROUP; + } + else + { + cmCPackLogger(cmCPackLog::LOG_WARNING, "[" + << this->Name << "]" + << " requested component grouping type <"<< groupingType + << "> UNKNOWN not in (ALL_COMPONENTS_IN_ONE,IGNORE,ONE_PER_GROUP)" + << std::endl); + } + } + + // Some components were defined but NO group + // fallback to default if not group based + if(method == ONE_PACKAGE_PER_GROUP && + this->ComponentGroups.empty() && !this->Components.empty()) + { + if(componentPackageMethod == ONE_PACKAGE) + { + method = ONE_PACKAGE; + } + else + { + method = ONE_PACKAGE_PER_COMPONENT; + } + cmCPackLogger(cmCPackLog::LOG_WARNING, "[" + << this->Name << "]" + << " One package per component group requested, " + << "but NO component groups exist: Ignoring component group." + << std::endl); + } + + // if user specified packaging method, override the default packaging method + if(method != UNKNOWN_COMPONENT_PACKAGE_METHOD) + { + componentPackageMethod = method; + } + + const char* method_names[] = + { + "ALL_COMPONENTS_IN_ONE", + "IGNORE_GROUPS", + "ONE_PER_GROUP" + }; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "[" + << this->Name << "]" + << " requested component grouping = " + << method_names[componentPackageMethod] + << std::endl); + + return 1; +} + +//---------------------------------------------------------------------- +std::string cmCPackGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) { + return componentName; +} +//---------------------------------------------------------------------- +std::string cmCPackGenerator::GetComponentPackageFileName( + const std::string& initialPackageFileName, + const std::string& groupOrComponentName, + bool isGroupName) { + + /* + * the default behavior is to use the + * component [group] name as a suffix + */ + std::string suffix="-"+groupOrComponentName; + /* check if we should use DISPLAY name */ + std::string dispNameVar = "CPACK_"+Name+"_USE_DISPLAY_NAME_IN_FILENAME"; + if (IsOn(dispNameVar.c_str())) + { + /* the component Group case */ + if (isGroupName) + { + std::string groupDispVar = "CPACK_COMPONENT_GROUP_" + + cmSystemTools::UpperCase(groupOrComponentName) + "_DISPLAY_NAME"; + const char* groupDispName = GetOption(groupDispVar.c_str()); + if (groupDispName) + { + suffix = "-"+std::string(groupDispName); + } + } + /* the [single] component case */ + else + { + std::string dispVar = "CPACK_COMPONENT_" + + cmSystemTools::UpperCase(groupOrComponentName) + "_DISPLAY_NAME"; + const char* dispName = GetOption(dispVar.c_str()); + if(dispName) + { + suffix = "-"+std::string(dispName); + } + } + } + return initialPackageFileName + suffix; +} + +//---------------------------------------------------------------------- +enum cmCPackGenerator::CPackSetDestdirSupport +cmCPackGenerator::SupportsSetDestdir() const +{ + return cmCPackGenerator::SETDESTDIR_SUPPORTED; +} + +//---------------------------------------------------------------------- +bool cmCPackGenerator::SupportsAbsoluteDestination() const +{ + return true; +} + +//---------------------------------------------------------------------- +bool cmCPackGenerator::SupportsComponentInstallation() const +{ + return false; +} + +//---------------------------------------------------------------------- +bool cmCPackGenerator::WantsComponentInstallation() const +{ + return (!IsOn("CPACK_MONOLITHIC_INSTALL") & SupportsComponentInstallation()); +} + +//---------------------------------------------------------------------- +cmCPackInstallationType* +cmCPackGenerator::GetInstallationType(const char *projectName, + const char *name) +{ + (void) projectName; + 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 = static_cast<unsigned>( + 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()); + component->IsDownloaded + = this->IsSet((macroPrefix + "_DOWNLOADED").c_str()) + || cmSystemTools::IsOn(this->GetOption("CPACK_DOWNLOAD_ALL")); + + const char* archiveFile = this->GetOption((macroPrefix + + "_ARCHIVE_FILE").c_str()); + if (archiveFile && *archiveFile) + { + component->ArchiveFile = archiveFile; + } + + const char* groupName = this->GetOption((macroPrefix + "_GROUP").c_str()); + if (groupName && *groupName) + { + 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) +{ + (void) projectName; + 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()); + const char* parentGroupName + = this->GetOption((macroPrefix + "_PARENT_GROUP").c_str()); + if (parentGroupName && *parentGroupName) + { + group->ParentGroup = GetComponentGroup(projectName, parentGroupName); + group->ParentGroup->Subgroups.push_back(group); + } + else + { + group->ParentGroup = 0; + } + } + return group; +} diff --git a/Source/CPack/cmCPackGenerator.h b/Source/CPack/cmCPackGenerator.h new file mode 100644 index 000000000..6748512d2 --- /dev/null +++ b/Source/CPack/cmCPackGenerator.h @@ -0,0 +1,329 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackGenerator_h +#define cmCPackGenerator_h + +#include "cmObject.h" +#include "cmSystemTools.h" +#include <map> +#include <vector> + +#include "cmCPackComponentGroup.h" // cmCPackComponent and friends + // Forward declarations are insufficient since we use them in + // std::map data members below... + +#define cmCPackTypeMacro(class, superclass) \ + cmTypeMacro(class, superclass); \ + static cmCPackGenerator* CreateGenerator() { return new class; } + +#define cmCPackLogger(logType, msg) \ + do { \ + cmOStringStream cmCPackLog_msg; \ + cmCPackLog_msg << msg; \ + this->Logger->Log(logType, __FILE__, __LINE__,\ + cmCPackLog_msg.str().c_str());\ + } while ( 0 ) + +#ifdef cerr +# undef cerr +#endif +#define cerr no_cerr_use_cmCPack_Log + +#ifdef cout +# undef cout +#endif +#define cout no_cout_use_cmCPack_Log + +class cmMakefile; +class cmCPackLog; + +/** \class cmCPackGenerator + * \brief A superclass of all CPack Generators + * + */ +class cmCPackGenerator : public cmObject +{ +public: + cmTypeMacro(cmCPackGenerator, cmObject); + /** + * If verbose then more information is printed out + */ + void SetVerbose(bool val) + { this->GeneratorVerbose = val ? + cmSystemTools::OUTPUT_MERGE : cmSystemTools::OUTPUT_NONE; } + + /** + * Returns true if the generator may work on this system. + * Rational: + * Some CPack generator may run on some host and may not on others + * (with the same system) because some tools are missing. If the tool + * is missing then CPack won't activate (in the CPackGeneratorFactory) + * this particular generator. + */ + static bool CanGenerate() { return true; } + + /** + * Do the actual whole package processing. + * Subclass may redefine it but its usually enough + * to redefine @ref PackageFiles, because in fact + * this method do call: + * - PrepareName + * - clean-up temp dirs + * - InstallProject (with the appropriate method) + * - prepare list of files and/or components to be package + * - PackageFiles + * - Copy produced packages at the expected place + * @return 0 if error. + */ + virtual int DoPackage(); + + /** + * Initialize generator + */ + int Initialize(const char* name, cmMakefile* mf); + + /** + * Construct generator + */ + cmCPackGenerator(); + virtual ~cmCPackGenerator(); + + //! Set and get the options + void SetOption(const char* op, const char* value); + void SetOptionIfNotSet(const char* op, const char* value); + const char* GetOption(const char* op) const; + bool IsSet(const char* name) const; + bool IsOn(const char* name) const; + + //! Set all the variables + int SetCMakeRoot(); + + //! Set the logger + void SetLogger(cmCPackLog* log) { this->Logger = log; } + + //! Display verbose information via logger + void DisplayVerboseOutput(const char* msg, float progress); + + bool ReadListFile(const char* moduleName); + +protected: + /** + * Prepare common used names by inspecting + * several CPACK_xxx var values. + */ + int PrepareNames(); + + /** + * Install the project using appropriate method. + */ + int InstallProject(); + + int CleanTemporaryDirectory(); + + virtual const char* GetOutputExtension() { return ".cpack"; } + virtual const char* GetOutputPostfix() { return 0; } + + /** + * Prepare requested grouping kind from CPACK_xxx vars + * CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE + * CPACK_COMPONENTS_IGNORE_GROUPS + * or + * CPACK_COMPONENTS_ONE_PACKAGE_PER_GROUP + * @return 1 on success 0 on failure. + */ + virtual int PrepareGroupingKind(); + + /** + * Some CPack generators may prefer to have + * CPack install all components belonging to the same + * [component] group to be install in the same directory. + * The default behavior is to install each component in + * a separate directory. + * @param[in] componentName the name of the component to be installed + * @return the name suffix the generator wants for the specified component + * default is "componentName" + */ + virtual std::string GetComponentInstallDirNameSuffix( + const std::string& componentName); + + /** + * CPack specific generator may mangle CPACK_PACKAGE_FILE_NAME + * with CPACK_COMPONENT_xxxx_<NAME>_DISPLAY_NAME if + * CPACK_<GEN>_USE_DISPLAY_NAME_IN_FILENAME is ON. + * @param[in] initialPackageFileName + * @param[in] groupOrComponentName + * @param[in] isGroupName + */ + virtual std::string GetComponentPackageFileName( + const std::string& initialPackageFileName, + const std::string& groupOrComponentName, + bool isGroupName); + + /** + * Package the list of files and/or components which + * has been prepared by the beginning of DoPackage. + * @pre @ref toplevel has been filled-in + * @pre the list of file @ref files has been populated + * @pre packageFileNames contains at least 1 entry + * @post packageFileNames may have been updated and contains + * the list of packages generated by the specific generator. + */ + virtual int PackageFiles(); + virtual const char* GetInstallPath(); + virtual const char* GetPackagingInstallPrefix(); + + virtual std::string FindTemplate(const char* name); + virtual bool ConfigureFile(const char* inName, const char* outName, + bool copyOnly = false); + virtual bool ConfigureString(const std::string& input, std::string& output); + virtual int InitializeInternal(); + + + //! Run install commands if specified + virtual int InstallProjectViaInstallCommands( + bool setDestDir, const char* tempInstallDirectory); + virtual int InstallProjectViaInstallScript( + bool setDestDir, const char* tempInstallDirectory); + virtual int InstallProjectViaInstalledDirectories( + bool setDestDir, const char* tempInstallDirectory); + virtual int InstallProjectViaInstallCMakeProjects( + bool setDestDir, const char* tempInstallDirectory); + + /** + * The various level of support of + * CPACK_SET_DESTDIR used by the generator. + */ + enum CPackSetDestdirSupport { + /* the generator works with or without it */ + SETDESTDIR_SUPPORTED, + /* the generator works best if automatically handled */ + SETDESTDIR_INTERNALLY_SUPPORTED, + /* no official support, use at your own risk */ + SETDESTDIR_SHOULD_NOT_BE_USED, + /* officially NOT supported */ + SETDESTDIR_UNSUPPORTED + }; + + /** + * Does the CPack generator support CPACK_SET_DESTDIR? + * The default legacy value is 'SETDESTDIR_SUPPORTED' generator + * have to override it in order change this. + * @return CPackSetDestdirSupport + */ + virtual enum CPackSetDestdirSupport SupportsSetDestdir() const; + + /** + * Does the CPack generator support absolute path + * in INSTALL DESTINATION? + * The default legacy value is 'true' generator + * have to override it in order change this. + * @return true if supported false otherwise + */ + virtual bool SupportsAbsoluteDestination() const; + + /** + * Does the CPack generator support component installation?. + * Some Generators requires the user to set + * CPACK_<GENNAME>_COMPONENT_INSTALL in order to make this + * method return true. + * @return true if supported, false otherwise + */ + virtual bool SupportsComponentInstallation() const; + /** + * Does the currently running generator want a component installation. + * The generator may support component installation but he may + * be requiring monolithic install using CPACK_MONOLITHIC_INSTALL. + * @return true if component installation is supported and wanted. + */ + virtual bool WantsComponentInstallation() 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); + + cmSystemTools::OutputOption GeneratorVerbose; + std::string Name; + + std::string InstallPath; + + /** + * The list of package file names. + * At beginning of DoPackage the (generic) generator will populate + * the list of desired package file names then it will + * call the redefined method PackageFiles which is may + * either use this set of names (usually on entry there should be + * only a single name) or update the vector with the list + * of created package file names. + */ + std::vector<std::string> packageFileNames; + + /** + * The directory where all the files to be packaged reside. + * If the installer support components there will be one + * sub-directory for each component. In those directories + * one will find the file belonging to the specified component. + */ + std::string toplevel; + + /** + * The complete list of files to be packaged. + * This list will be populated by DoPackage before + * PackageFiles is called. + */ + std::vector<std::string> files; + + std::string CPackSelf; + std::string CMakeSelf; + std::string CMakeRoot; + + std::map<std::string, cmCPackInstallationType> InstallationTypes; + /** + * The set of components. + * If component installation is supported then this map + * contains the component specified in CPACK_COMPONENTS_ALL + */ + std::map<std::string, cmCPackComponent> Components; + std::map<std::string, cmCPackComponentGroup> ComponentGroups; + + /** + * If components are enabled, this enum represents the different + * ways of mapping components to package files. + */ + enum ComponentPackageMethod + { + /* one package for all components */ + ONE_PACKAGE, + /* one package for each component */ + ONE_PACKAGE_PER_COMPONENT, + /* one package for each group, + * with left over components in their own package */ + ONE_PACKAGE_PER_GROUP, + UNKNOWN_COMPONENT_PACKAGE_METHOD + }; + + /** + * The component package method + * The default is ONE_PACKAGE_PER_GROUP, + * and generators may override the default + * before PrepareGroupingKind() is called. + */ + ComponentPackageMethod componentPackageMethod; + + cmCPackLog* Logger; +private: + cmMakefile* MakefileMap; +}; + +#endif diff --git a/Source/CPack/cmCPackGeneratorFactory.cxx b/Source/CPack/cmCPackGeneratorFactory.cxx new file mode 100644 index 000000000..37ff46041 --- /dev/null +++ b/Source/CPack/cmCPackGeneratorFactory.cxx @@ -0,0 +1,183 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackGeneratorFactory.h" + +#include "cmCPackGenerator.h" +#include "cmCPackTGZGenerator.h" +#include "cmCPackTarBZip2Generator.h" +#include "cmCPackTarCompressGenerator.h" +#include "cmCPackZIPGenerator.h" +#include "cmCPackSTGZGenerator.h" +#include "cmCPackNSISGenerator.h" +#ifdef __APPLE__ +# include "cmCPackDragNDropGenerator.h" +# include "cmCPackBundleGenerator.h" +# include "cmCPackPackageMakerGenerator.h" +# include "cmCPackOSXX11Generator.h" +#endif + +#ifdef __CYGWIN__ +# include "cmCPackCygwinBinaryGenerator.h" +# include "cmCPackCygwinSourceGenerator.h" +#endif + +#if !defined(_WIN32) \ + && !defined(__QNXNTO__) && !defined(__BEOS__) +# include "cmCPackDebGenerator.h" +# include "cmCPackRPMGenerator.h" +#endif + + +#include "cmCPackLog.h" + +#if defined(__BORLANDC__) +# pragma warn -8008 /* condition is always true */ +#endif + +//---------------------------------------------------------------------- +cmCPackGeneratorFactory::cmCPackGeneratorFactory() +{ + if (cmCPackTGZGenerator::CanGenerate()) + { + this->RegisterGenerator("TGZ", "Tar GZip compression", + cmCPackTGZGenerator::CreateGenerator); + } + if (cmCPackSTGZGenerator::CanGenerate()) + { + this->RegisterGenerator("STGZ", "Self extracting Tar GZip compression", + cmCPackSTGZGenerator::CreateGenerator); + } + if (cmCPackNSISGenerator::CanGenerate()) + { + this->RegisterGenerator("NSIS", "Null Soft Installer", + cmCPackNSISGenerator::CreateGenerator); + } +#ifdef __CYGWIN__ + if (cmCPackCygwinBinaryGenerator::CanGenerate()) + { + this->RegisterGenerator("CygwinBinary", "Cygwin Binary Installer", + cmCPackCygwinBinaryGenerator::CreateGenerator); + } + if (cmCPackCygwinSourceGenerator::CanGenerate()) + { + this->RegisterGenerator("CygwinSource", "Cygwin Source Installer", + cmCPackCygwinSourceGenerator::CreateGenerator); + } +#endif + + if (cmCPackZIPGenerator::CanGenerate()) + { + this->RegisterGenerator("ZIP", "ZIP file format", + cmCPackZIPGenerator::CreateGenerator); + } + if (cmCPackTarBZip2Generator::CanGenerate()) + { + this->RegisterGenerator("TBZ2", "Tar BZip2 compression", + cmCPackTarBZip2Generator::CreateGenerator); + } + if (cmCPackTarCompressGenerator::CanGenerate()) + { + this->RegisterGenerator("TZ", "Tar Compress compression", + cmCPackTarCompressGenerator::CreateGenerator); + } +#ifdef __APPLE__ + if (cmCPackDragNDropGenerator::CanGenerate()) + { + this->RegisterGenerator("DragNDrop", "Mac OSX Drag And Drop", + cmCPackDragNDropGenerator::CreateGenerator); + } + if (cmCPackBundleGenerator::CanGenerate()) + { + this->RegisterGenerator("Bundle", "Mac OSX bundle", + cmCPackBundleGenerator::CreateGenerator); + } + if (cmCPackPackageMakerGenerator::CanGenerate()) + { + this->RegisterGenerator("PackageMaker", "Mac OSX Package Maker installer", + cmCPackPackageMakerGenerator::CreateGenerator); + } + if (cmCPackOSXX11Generator::CanGenerate()) + { + this->RegisterGenerator("OSXX11", "Mac OSX X11 bundle", + cmCPackOSXX11Generator::CreateGenerator); + } +#endif +#if !defined(_WIN32) \ + && !defined(__QNXNTO__) && !defined(__BEOS__) + if (cmCPackDebGenerator::CanGenerate()) + { + this->RegisterGenerator("DEB", "Debian packages", + cmCPackDebGenerator::CreateGenerator); + } + if (cmCPackRPMGenerator::CanGenerate()) + { + this->RegisterGenerator("RPM", "RPM packages", + cmCPackRPMGenerator::CreateGenerator); + } +#endif +} + +//---------------------------------------------------------------------- +cmCPackGeneratorFactory::~cmCPackGeneratorFactory() +{ + std::vector<cmCPackGenerator*>::iterator it; + for ( it = this->Generators.begin(); it != this->Generators.end(); ++ it ) + { + delete *it; + } +} + +//---------------------------------------------------------------------- +cmCPackGenerator* cmCPackGeneratorFactory::NewGenerator(const char* name) +{ + cmCPackGenerator* gen = this->NewGeneratorInternal(name); + if ( !gen ) + { + return 0; + } + this->Generators.push_back(gen); + gen->SetLogger(this->Logger); + return gen; +} + +//---------------------------------------------------------------------- +cmCPackGenerator* cmCPackGeneratorFactory::NewGeneratorInternal( + const char* name) +{ + if ( !name ) + { + return 0; + } + cmCPackGeneratorFactory::t_GeneratorCreatorsMap::iterator it + = this->GeneratorCreators.find(name); + if ( it == this->GeneratorCreators.end() ) + { + return 0; + } + return (it->second)(); +} + +//---------------------------------------------------------------------- +void cmCPackGeneratorFactory::RegisterGenerator(const char* name, + const char* generatorDescription, + CreateGeneratorCall* createGenerator) +{ + if ( !name || !createGenerator ) + { + cmCPack_Log(this->Logger, cmCPackLog::LOG_ERROR, + "Cannot register generator" << std::endl); + return; + } + this->GeneratorCreators[name] = createGenerator; + this->GeneratorDescriptions[name] = generatorDescription; +} diff --git a/Source/CPack/cmCPackGeneratorFactory.h b/Source/CPack/cmCPackGeneratorFactory.h new file mode 100644 index 000000000..dff2e49eb --- /dev/null +++ b/Source/CPack/cmCPackGeneratorFactory.h @@ -0,0 +1,59 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackGeneratorFactory_h +#define cmCPackGeneratorFactory_h + +#include "cmObject.h" + +class cmCPackLog; +class cmCPackGenerator; + +/** \class cmCPackGeneratorFactory + * \brief A container for CPack generators + * + */ +class cmCPackGeneratorFactory : public cmObject +{ +public: + cmTypeMacro(cmCPackGeneratorFactory, cmObject); + + cmCPackGeneratorFactory(); + ~cmCPackGeneratorFactory(); + + //! Get the generator + cmCPackGenerator* NewGenerator(const char* name); + void DeleteGenerator(cmCPackGenerator* gen); + + typedef cmCPackGenerator* CreateGeneratorCall(); + + void RegisterGenerator(const char* name, + const char* generatorDescription, + CreateGeneratorCall* createGenerator); + + void SetLogger(cmCPackLog* logger) { this->Logger = logger; } + + typedef std::map<cmStdString, cmStdString> DescriptionsMap; + const DescriptionsMap& GetGeneratorsList() const + { return this->GeneratorDescriptions; } + +private: + cmCPackGenerator* NewGeneratorInternal(const char* name); + std::vector<cmCPackGenerator*> Generators; + + typedef std::map<cmStdString, CreateGeneratorCall*> t_GeneratorCreatorsMap; + t_GeneratorCreatorsMap GeneratorCreators; + DescriptionsMap GeneratorDescriptions; + cmCPackLog* Logger; +}; + +#endif diff --git a/Source/CPack/cmCPackLog.cxx b/Source/CPack/cmCPackLog.cxx new file mode 100644 index 000000000..4e8bf0f4e --- /dev/null +++ b/Source/CPack/cmCPackLog.cxx @@ -0,0 +1,225 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackLog.h" + +#include "cmGeneratedFileStream.h" +#include "cmSystemTools.h" + +//---------------------------------------------------------------------- +cmCPackLog::cmCPackLog() +{ + this->Verbose = false; + this->Debug = false; + this->Quiet = false; + this->NewLine = true; + + this->LastTag = cmCPackLog::NOTAG; +#undef cerr +#undef cout + this->DefaultOutput = &std::cout; + this->DefaultError = &std::cerr; + + this->LogOutput = 0; + this->LogOutputCleanup = false; +} + +//---------------------------------------------------------------------- +cmCPackLog::~cmCPackLog() +{ + this->SetLogOutputStream(0); +} + +//---------------------------------------------------------------------- +void cmCPackLog::SetLogOutputStream(std::ostream* os) +{ + if ( this->LogOutputCleanup && this->LogOutput ) + { + delete this->LogOutput; + } + this->LogOutputCleanup = false; + this->LogOutput = os; +} + +//---------------------------------------------------------------------- +bool cmCPackLog::SetLogOutputFile(const char* fname) +{ + cmGeneratedFileStream *cg = 0; + if ( fname ) + { + cg = new cmGeneratedFileStream(fname); + } + if ( cg && !*cg ) + { + delete cg; + cg = 0; + } + this->SetLogOutputStream(cg); + if ( !cg ) + { + return false; + } + this->LogOutputCleanup = true; + return true; +} + +//---------------------------------------------------------------------- +void cmCPackLog::Log(int tag, const char* file, int line, + const char* msg, size_t length) +{ + // By default no logging + bool display = false; + + // Display file and line number if debug + bool useFileAndLine = this->Debug; + + bool output = false; + bool debug = false; + bool warning = false; + bool error = false; + bool verbose = false; + + // When writing in file, add list of tags whenever tag changes. + std::string tagString; + bool needTagString = false; + if ( this->LogOutput && this->LastTag != tag ) + { + needTagString = true; + } + + if ( tag & LOG_OUTPUT ) + { + output = true; + display = true; + if ( needTagString ) + { + if ( tagString.size() > 0 ) { tagString += ","; } + tagString = "VERBOSE"; + } + } + if ( tag & LOG_WARNING ) + { + warning = true; + display = true; + if ( needTagString ) + { + if ( tagString.size() > 0 ) { tagString += ","; } + tagString = "WARNING"; + } + } + if ( tag & LOG_ERROR ) + { + error = true; + display = true; + if ( needTagString ) + { + if ( tagString.size() > 0 ) { tagString += ","; } + tagString = "ERROR"; + } + } + if ( tag & LOG_DEBUG && this->Debug ) + { + debug = true; + display = true; + if ( needTagString ) + { + if ( tagString.size() > 0 ) { tagString += ","; } + tagString = "DEBUG"; + } + useFileAndLine = true; + } + if ( tag & LOG_VERBOSE && this->Verbose ) + { + verbose = true; + display = true; + if ( needTagString ) + { + if ( tagString.size() > 0 ) { tagString += ","; } + tagString = "VERBOSE"; + } + } + if ( this->Quiet ) + { + display = false; + } + if ( this->LogOutput ) + { + if ( needTagString ) + { + *this->LogOutput << "[" << file << ":" << line << " " + << tagString << "] "; + } + this->LogOutput->write(msg, length); + } + this->LastTag = tag; + if ( !display ) + { + return; + } + if ( this->NewLine ) + { + if ( error && !this->ErrorPrefix.empty() ) + { + *this->DefaultError << this->ErrorPrefix.c_str(); + } + else if ( warning && !this->WarningPrefix.empty() ) + { + *this->DefaultError << this->WarningPrefix.c_str(); + } + else if ( output && !this->OutputPrefix.empty() ) + { + *this->DefaultOutput << this->OutputPrefix.c_str(); + } + else if ( verbose && !this->VerbosePrefix.empty() ) + { + *this->DefaultOutput << this->VerbosePrefix.c_str(); + } + else if ( debug && !this->DebugPrefix.empty() ) + { + *this->DefaultOutput << this->DebugPrefix.c_str(); + } + else if ( !this->Prefix.empty() ) + { + *this->DefaultOutput << this->Prefix.c_str(); + } + if ( useFileAndLine ) + { + if ( error || warning ) + { + *this->DefaultError << file << ":" << line << " "; + } + else + { + *this->DefaultOutput << file << ":" << line << " "; + } + } + } + if ( error || warning ) + { + this->DefaultError->write(msg, length); + this->DefaultError->flush(); + } + else + { + this->DefaultOutput->write(msg, length); + this->DefaultOutput->flush(); + } + if ( msg[length-1] == '\n' || length > 2 ) + { + this->NewLine = true; + } + + if ( error ) + { + cmSystemTools::SetErrorOccured(); + } +} diff --git a/Source/CPack/cmCPackLog.h b/Source/CPack/cmCPackLog.h new file mode 100644 index 000000000..812f1de27 --- /dev/null +++ b/Source/CPack/cmCPackLog.h @@ -0,0 +1,155 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackLog_h +#define cmCPackLog_h + +#include "cmObject.h" + +#define cmCPack_Log(ctSelf, logType, msg) \ + do { \ + cmOStringStream cmCPackLog_msg; \ + cmCPackLog_msg << msg; \ + (ctSelf)->Log(logType, __FILE__, __LINE__, cmCPackLog_msg.str().c_str());\ + } while ( 0 ) + +#ifdef cerr +# undef cerr +#endif +#define cerr no_cerr_use_cmCPack_Log + +#ifdef cout +# undef cout +#endif +#define cout no_cout_use_cmCPack_Log + + +/** \class cmCPackLog + * \brief A container for CPack generators + * + */ +class cmCPackLog : public cmObject +{ +public: + cmTypeMacro(cmCPackLog, cmObject); + + cmCPackLog(); + ~cmCPackLog(); + + enum __log_tags { + NOTAG = 0, + LOG_OUTPUT = 0x1, + LOG_VERBOSE = 0x2, + LOG_DEBUG = 0x4, + LOG_WARNING = 0x8, + LOG_ERROR = 0x10 + }; + + //! Various signatures for logging. + void Log(const char* file, int line, const char* msg) + { + this->Log(LOG_OUTPUT, file, line, msg); + } + void Log(const char* file, int line, const char* msg, size_t length) + { + this->Log(LOG_OUTPUT, file, line, msg, length); + } + void Log(int tag, const char* file, int line, const char* msg) + { + this->Log(tag, file, line, msg, strlen(msg)); + } + void Log(int tag, const char* file, int line, const char* msg, + size_t length); + + //! Set Verbose + void VerboseOn() { this->SetVerbose(true); } + void VerboseOff() { this->SetVerbose(true); } + void SetVerbose(bool verb) { this->Verbose = verb; } + bool GetVerbose() { return this->Verbose; } + + //! Set Debug + void DebugOn() { this->SetDebug(true); } + void DebugOff() { this->SetDebug(true); } + void SetDebug(bool verb) { this->Debug = verb; } + bool GetDebug() { return this->Debug; } + + //! Set Quiet + void QuietOn() { this->SetQuiet(true); } + void QuietOff() { this->SetQuiet(true); } + void SetQuiet(bool verb) { this->Quiet = verb; } + bool GetQuiet() { return this->Quiet; } + + //! Set the output stream + void SetOutputStream(std::ostream* os) { this->DefaultOutput = os; } + + //! Set the error stream + void SetErrorStream(std::ostream* os) { this->DefaultError = os; } + + //! Set the log output stream + void SetLogOutputStream(std::ostream* os); + + //! Set the log output file. The cmCPackLog will try to create file. If it + // cannot, it will report an error. + bool SetLogOutputFile(const char* fname); + + //! Set the various prefixes for the logging. SetPrefix sets the generic + // prefix that overwrittes missing ones. + void SetPrefix(std::string pfx) { this->Prefix = pfx; } + void SetOutputPrefix(std::string pfx) { this->OutputPrefix = pfx; } + void SetVerbosePrefix(std::string pfx) { this->VerbosePrefix = pfx; } + void SetDebugPrefix(std::string pfx) { this->DebugPrefix = pfx; } + void SetWarningPrefix(std::string pfx) { this->WarningPrefix = pfx; } + void SetErrorPrefix(std::string pfx) { this->ErrorPrefix = pfx; } + +private: + bool Verbose; + bool Debug; + bool Quiet; + + bool NewLine; + + int LastTag; + + std::string Prefix; + std::string OutputPrefix; + std::string VerbosePrefix; + std::string DebugPrefix; + std::string WarningPrefix; + std::string ErrorPrefix; + + std::ostream *DefaultOutput; + std::ostream *DefaultError; + + std::string LogOutputFileName; + std::ostream *LogOutput; + // Do we need to cleanup log output stream + bool LogOutputCleanup; +}; + +class cmCPackLogWrite +{ +public: + cmCPackLogWrite(const char* data, size_t length) + : Data(data), Length(length) {} + + const char* Data; + size_t Length; +}; + +inline std::ostream& operator<< (std::ostream& os, const cmCPackLogWrite& c) +{ + os.write(c.Data, c.Length); + os.flush(); + return os; +} + +#endif diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx new file mode 100644 index 000000000..7b5251148 --- /dev/null +++ b/Source/CPack/cmCPackNSISGenerator.cxx @@ -0,0 +1,1004 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackNSISGenerator.h" + +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmSystemTools.h" +#include "cmMakefile.h" +#include "cmGeneratedFileStream.h" +#include "cmCPackLog.h" +#include "cmCPackComponentGroup.h" + +#include <cmsys/SystemTools.hxx> +#include <cmsys/Glob.hxx> +#include <cmsys/Directory.hxx> +#include <cmsys/RegularExpression.hxx> + +/* NSIS uses different command line syntax on Windows and others */ +#ifdef _WIN32 +# define NSIS_OPT "/" +#else +# define NSIS_OPT "-" +#endif + +//---------------------------------------------------------------------- +cmCPackNSISGenerator::cmCPackNSISGenerator() +{ +} + +//---------------------------------------------------------------------- +cmCPackNSISGenerator::~cmCPackNSISGenerator() +{ +} + +//---------------------------------------------------------------------- +int cmCPackNSISGenerator::PackageFiles() +{ + // TODO: Fix nsis to force out file name + + std::string nsisInFileName = this->FindTemplate("NSIS.template.in"); + if ( nsisInFileName.size() == 0 ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPack error: Could not find NSIS installer template file." + << std::endl); + return false; + } + std::string nsisInInstallOptions + = this->FindTemplate("NSIS.InstallOptions.ini.in"); + if ( nsisInInstallOptions.size() == 0 ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPack error: Could not find NSIS installer options file." + << std::endl); + return false; + } + + std::string nsisFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + std::string tmpFile = nsisFileName; + tmpFile += "/NSISOutput.log"; + std::string nsisInstallOptions = nsisFileName + "/NSIS.InstallOptions.ini"; + nsisFileName += "/project.nsi"; + cmOStringStream str; + std::vector<std::string>::const_iterator it; + for ( it = files.begin(); it != files.end(); ++ it ) + { + std::string fileN = cmSystemTools::RelativePath(toplevel.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; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Files: " + << str.str().c_str() << std::endl); + this->SetOptionIfNotSet("CPACK_NSIS_DELETE_FILES", str.str().c_str()); + std::vector<std::string> dirs; + this->GetListOfSubdirectories(toplevel.c_str(), dirs); + std::vector<std::string>::const_iterator sit; + cmOStringStream dstr; + for ( sit = dirs.begin(); sit != dirs.end(); ++ sit ) + { + std::string componentName; + std::string fileN = cmSystemTools::RelativePath(toplevel.c_str(), + 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); + this->SetOptionIfNotSet("CPACK_NSIS_DELETE_DIRECTORIES", + dstr.str().c_str()); + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " << nsisInFileName + << " to " << nsisFileName << std::endl); + if(this->IsSet("CPACK_NSIS_MUI_ICON") + || this->IsSet("CPACK_NSIS_MUI_UNIICON")) + { + std::string installerIconCode; + if(this->IsSet("CPACK_NSIS_MUI_ICON")) + { + installerIconCode += "!define MUI_ICON \""; + installerIconCode += this->GetOption("CPACK_NSIS_MUI_ICON"); + installerIconCode += "\"\n"; + } + if(this->IsSet("CPACK_NSIS_MUI_UNIICON")) + { + installerIconCode += "!define MUI_UNICON \""; + installerIconCode += this->GetOption("CPACK_NSIS_MUI_UNIICON"); + installerIconCode += "\"\n"; + } + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_ICON_CODE", + installerIconCode.c_str()); + } + if(this->IsSet("CPACK_PACKAGE_ICON")) + { + std::string installerIconCode = "!define MUI_HEADERIMAGE_BITMAP \""; + installerIconCode += this->GetOption("CPACK_PACKAGE_ICON"); + installerIconCode += "\"\n"; + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE", + installerIconCode.c_str()); + } + + if(this->IsSet("CPACK_NSIS_MUI_FINISHPAGE_RUN")) + { + std::string installerRunCode = "!define MUI_FINISHPAGE_RUN \"$INSTDIR\\"; + installerRunCode += this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY"); + installerRunCode += "\\"; + installerRunCode += this->GetOption("CPACK_NSIS_MUI_FINISHPAGE_RUN"); + installerRunCode += "\"\n"; + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE", + installerRunCode.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; + std::string defines; + cmOStringStream macrosOut; + bool anyDownloadedComponents = false; + + // Create installation types. The order is significant, so we first fill + // in a vector based on the indices, and print them in that order. + 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) + { + if (groupIt->second.ParentGroup == 0) + { + componentCode += + this->CreateComponentGroupDescription(&groupIt->second, macrosOut); + } + + // 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.Files.empty()) + { + // NSIS cannot cope with components that have no files. + continue; + } + + anyDownloadedComponents = + anyDownloadedComponents || compIt->second.IsDownloaded; + + if (!compIt->second.Group) + { + componentCode + += this->CreateComponentDescription(&compIt->second, macrosOut); + } + + // Add this component to the various section lists. + sectionList += " !insertmacro \"${MacroName}\" \""; + sectionList += compIt->first; + sectionList += "\"\n"; + selectedVarsList += "Var " + compIt->first + "_selected\n"; + selectedVarsList += "Var " + compIt->first + "_was_installed\n"; + + // Add the component description, if any. + if (!compIt->second.Description.empty()) + { + componentDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${" + + compIt->first + "} \"" + + this->TranslateNewlines(compIt->second.Description) + "\"\n"; + } + } + + componentCode += macrosOut.str(); + + 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()); + } + + if (anyDownloadedComponents) + { + defines += "!define CPACK_USES_DOWNLOAD\n"; + if (cmSystemTools::IsOn(this->GetOption("CPACK_ADD_REMOVE"))) + { + defines += "!define CPACK_NSIS_ADD_REMOVE\n"; + } + } + + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", + installTypesCode.c_str()); + this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", + "!insertmacro MUI_PAGE_COMPONENTS"); + this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL", ""); + this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", + componentCode.c_str()); + this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", + sectionList.c_str()); + this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS", + selectedVarsList.c_str()); + this->SetOption("CPACK_NSIS_DEFINES", defines.c_str()); + } + + this->ConfigureFile(nsisInInstallOptions.c_str(), + nsisInstallOptions.c_str()); + this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str()); + std::string nsisCmd = "\""; + nsisCmd += this->GetOption("CPACK_INSTALLER_PROGRAM"); + nsisCmd += "\" \"" + nsisFileName + "\""; + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << nsisCmd.c_str() + << std::endl); + std::string output; + int retVal = 1; + bool res = cmSystemTools::RunSingleCommand(nsisCmd.c_str(), &output, + &retVal, 0, this->GeneratorVerbose, 0); + if ( !res || retVal ) + { + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << nsisCmd.c_str() << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running NSIS command: " + << nsisCmd.c_str() << std::endl + << "Please check " << tmpFile.c_str() << " for errors" << std::endl); + return 0; + } + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackNSISGenerator::InitializeInternal() +{ + if ( cmSystemTools::IsOn(this->GetOption( + "CPACK_INCLUDE_TOPLEVEL_DIRECTORY")) ) + { + cmCPackLogger(cmCPackLog::LOG_WARNING, + "NSIS Generator cannot work with CPACK_INCLUDE_TOPLEVEL_DIRECTORY set. " + "This option will be reset to 0 (for this generator only)." + << std::endl); + this->SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", 0); + } + + cmCPackLogger(cmCPackLog::LOG_DEBUG, "cmCPackNSISGenerator::Initialize()" + << std::endl); + std::vector<std::string> path; + std::string nsisPath; + bool gotRegValue = true; + +#ifdef _WIN32 + if ( !cmsys::SystemTools::ReadRegistryValue( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath, + cmsys::SystemTools::KeyWOW64_32) ) + { + if ( !cmsys::SystemTools::ReadRegistryValue( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath) ) + { + gotRegValue = false; + } + } + + if (gotRegValue) + { + path.push_back(nsisPath); + } +#endif + + nsisPath = cmSystemTools::FindProgram("makensis", path, false); + + if ( nsisPath.empty() ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find NSIS compiler makensis: likely it is not installed, " + "or not in your PATH" + << std::endl); + + if (!gotRegValue) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Could not read NSIS registry value. This is usually caused by " + "NSIS not being installed. Please install NSIS from " + "http://nsis.sourceforge.net" + << std::endl); + } + + return 0; + } + + std::string nsisCmd = "\"" + nsisPath + "\" " NSIS_OPT "VERSION"; + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Test NSIS version: " + << nsisCmd.c_str() << std::endl); + std::string output; + int retVal = 1; + bool resS = cmSystemTools::RunSingleCommand(nsisCmd.c_str(), + &output, &retVal, 0, this->GeneratorVerbose, 0); + + cmsys::RegularExpression versionRex("v([0-9]+.[0-9]+)"); + if ( !resS || retVal || !versionRex.find(output)) + { + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/NSISOutput.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << nsisCmd.c_str() << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem checking NSIS version with command: " + << nsisCmd.c_str() << std::endl + << "Please check " << tmpFile.c_str() << " for errors" << std::endl); + return 0; + } + double nsisVersion = atof(versionRex.match(1).c_str()); + double minNSISVersion = 2.09; + cmCPackLogger(cmCPackLog::LOG_DEBUG, "NSIS Version: " + << nsisVersion << std::endl); + if ( nsisVersion < minNSISVersion ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPack requires NSIS Version 2.09 or greater. NSIS found on the system " + "was: " + << nsisVersion << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", nsisPath.c_str()); + this->SetOptionIfNotSet("CPACK_NSIS_EXECUTABLES_DIRECTORY", "bin"); + const char* cpackPackageExecutables + = this->GetOption("CPACK_PACKAGE_EXECUTABLES"); + const char* cpackPackageDeskTopLinks + = this->GetOption("CPACK_CREATE_DESKTOP_LINKS"); + const char* cpackNsisExecutablesDirectory + = this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY"); + std::vector<std::string> cpackPackageDesktopLinksVector; + if(cpackPackageDeskTopLinks) + { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: " + << cpackPackageDeskTopLinks << std::endl); + + cmSystemTools:: + ExpandListArgument(cpackPackageDeskTopLinks, + cpackPackageDesktopLinksVector); + for(std::vector<std::string>::iterator i = + cpackPackageDesktopLinksVector.begin(); i != + cpackPackageDesktopLinksVector.end(); ++i) + { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: " + << *i << std::endl); + } + } + else + { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: " + << "not set" << std::endl); + } + + cmOStringStream str; + cmOStringStream deleteStr; + + if ( cpackPackageExecutables ) + { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackPackageExecutables: " + << cpackPackageExecutables << "." << std::endl); + std::vector<std::string> cpackPackageExecutablesVector; + cmSystemTools::ExpandListArgument(cpackPackageExecutables, + cpackPackageExecutablesVector); + if ( cpackPackageExecutablesVector.size() % 2 != 0 ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and " + "<icon name>." << std::endl); + return 0; + } + std::vector<std::string>::iterator it; + for ( it = cpackPackageExecutablesVector.begin(); + it != cpackPackageExecutablesVector.end(); + ++it ) + { + std::string execName = *it; + ++ it; + std::string linkName = *it; + str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\" + << linkName << ".lnk\" \"$INSTDIR\\" + << cpackNsisExecutablesDirectory << "\\" << execName << ".exe\"" + << std::endl; + deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName + << ".lnk\"" << std::endl; + // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on + // if so add a desktop link + if(cpackPackageDesktopLinksVector.size() && + std::find(cpackPackageDesktopLinksVector.begin(), + cpackPackageDesktopLinksVector.end(), + execName) + != cpackPackageDesktopLinksVector.end()) + { + str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n"; + str << " CreateShortCut \"$DESKTOP\\" + << linkName << ".lnk\" \"$INSTDIR\\" + << cpackNsisExecutablesDirectory << "\\" << execName << ".exe\"" + << std::endl; + deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n"; + deleteStr << " Delete \"$DESKTOP\\" << linkName + << ".lnk\"" << std::endl; + } + } + } + + this->CreateMenuLinks(str, deleteStr); + this->SetOptionIfNotSet("CPACK_NSIS_CREATE_ICONS", str.str().c_str()); + this->SetOptionIfNotSet("CPACK_NSIS_DELETE_ICONS", + deleteStr.str().c_str()); + + this->SetOptionIfNotSet("CPACK_NSIS_COMPRESSOR", "lzma"); + + return this->Superclass::InitializeInternal(); +} + +//---------------------------------------------------------------------- +void cmCPackNSISGenerator::CreateMenuLinks( cmOStringStream& str, + cmOStringStream& deleteStr) +{ + const char* cpackMenuLinks + = this->GetOption("CPACK_NSIS_MENU_LINKS"); + if(!cpackMenuLinks) + { + return; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackMenuLinks: " + << cpackMenuLinks << "." << std::endl); + std::vector<std::string> cpackMenuLinksVector; + cmSystemTools::ExpandListArgument(cpackMenuLinks, + cpackMenuLinksVector); + if ( cpackMenuLinksVector.size() % 2 != 0 ) + { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "CPACK_NSIS_MENU_LINKS should contain pairs of <shortcut target> and " + "<shortcut label>." << std::endl); + return; + } + + cmsys::RegularExpression urlRegex; + urlRegex.compile("^(mailto:|(ftps?|https?|news)://).*$"); + + std::vector<std::string>::iterator it; + for ( it = cpackMenuLinksVector.begin(); + it != cpackMenuLinksVector.end(); + ++it ) + { + std::string sourceName = *it; + const bool url = urlRegex.find(sourceName); + + // Convert / to \ in filenames, but not in urls: + // + if(!url) + { + cmSystemTools::ReplaceString(sourceName, "/", "\\"); + } + + ++ it; + std::string linkName = *it; + if(!url) + { + str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\" + << linkName << ".lnk\" \"$INSTDIR\\" << sourceName << "\"" + << std::endl; + deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName + << ".lnk\"" << std::endl; + } + else + { + str << " WriteINIStr \"$SMPROGRAMS\\$STARTMENU_FOLDER\\" + << linkName << ".url\" \"InternetShortcut\" \"URL\" \"" + << sourceName << "\"" + << std::endl; + deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName + << ".url\"" << std::endl; + } + // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on + // if so add a desktop link + std::string desktop = "CPACK_CREATE_DESKTOP_LINK_"; + desktop += linkName; + if(this->IsSet(desktop.c_str())) + { + str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n"; + str << " CreateShortCut \"$DESKTOP\\" + << linkName << ".lnk\" \"$INSTDIR\\" << sourceName << "\"" + << std::endl; + deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n"; + deleteStr << " Delete \"$DESKTOP\\" << linkName + << ".lnk\"" << std::endl; + } + } +} + +//---------------------------------------------------------------------- +bool cmCPackNSISGenerator::GetListOfSubdirectories(const char* topdir, + std::vector<std::string>& dirs) +{ + cmsys::Directory dir; + dir.Load(topdir); + size_t fileNum; + for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) + { + if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") && + strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),"..")) + { + cmsys_stl::string fullPath = topdir; + fullPath += "/"; + fullPath += dir.GetFile(static_cast<unsigned long>(fileNum)); + if(cmsys::SystemTools::FileIsDirectory(fullPath.c_str()) && + !cmsys::SystemTools::FileIsSymlink(fullPath.c_str())) + { + if (!this->GetListOfSubdirectories(fullPath.c_str(), dirs)) + { + return false; + } + } + } + } + dirs.push_back(topdir); + return true; +} + +//---------------------------------------------------------------------- +enum cmCPackGenerator::CPackSetDestdirSupport +cmCPackNSISGenerator::SupportsSetDestdir() const +{ + return cmCPackGenerator::SETDESTDIR_SHOULD_NOT_BE_USED; +} + +//---------------------------------------------------------------------- +bool cmCPackNSISGenerator::SupportsAbsoluteDestination() const +{ + return false; +} + +//---------------------------------------------------------------------- +bool cmCPackNSISGenerator::SupportsComponentInstallation() const +{ + return true; +} + +//---------------------------------------------------------------------- +std::string +cmCPackNSISGenerator:: +CreateComponentDescription(cmCPackComponent *component, + cmOStringStream& macrosOut) +{ + // 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()) + { + cmOStringStream 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"; + + // Create the actual installation commands + if (component->IsDownloaded) + { + if (component->ArchiveFile.empty()) + { + // Compute the name of the archive. + std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + packagesDir += ".dummy"; + cmOStringStream out; + out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir) + << "-" << component->Name << ".zip"; + component->ArchiveFile = out.str(); + } + + // Create the directory for the upload area + const char* userUploadDirectory = + this->GetOption("CPACK_UPLOAD_DIRECTORY"); + std::string uploadDirectory; + if (userUploadDirectory && *userUploadDirectory) + { + uploadDirectory = userUploadDirectory; + } + else + { + uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY"); + uploadDirectory += "/CPackUploads"; + } + if(!cmSystemTools::FileExists(uploadDirectory.c_str())) + { + if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Unable to create NSIS upload directory " << uploadDirectory + << std::endl); + return ""; + } + } + + // Remove the old archive, if one exists + std::string archiveFile = uploadDirectory + '/' + component->ArchiveFile; + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Building downloaded component archive: " + << archiveFile << std::endl); + if (cmSystemTools::FileExists(archiveFile.c_str(), true)) + { + if (!cmSystemTools::RemoveFile(archiveFile.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Unable to remove archive file " << archiveFile + << std::endl); + return ""; + } + } + + // Find a ZIP program + if (!this->IsSet("ZIP_EXECUTABLE")) + { + this->ReadListFile("CPackZIP.cmake"); + + if (!this->IsSet("ZIP_EXECUTABLE")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Unable to find ZIP program" + << std::endl); + return ""; + } + } + + // The directory where this component's files reside + std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + dirName += '/'; + dirName += component->Name; + dirName += '/'; + + // Build the list of files to go into this archive, and determine the + // size of the installed component. + std::string zipListFileName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + zipListFileName += "/winZip.filelist"; + bool needQuotesInFile + = cmSystemTools::IsOn(this->GetOption("CPACK_ZIP_NEED_QUOTES")); + unsigned long totalSize = 0; + { // the scope is needed for cmGeneratedFileStream + cmGeneratedFileStream out(zipListFileName.c_str()); + std::vector<std::string>::iterator fileIt; + for (fileIt = component->Files.begin(); + fileIt != component->Files.end(); + ++fileIt) + { + if ( needQuotesInFile ) + { + out << "\""; + } + out << *fileIt; + if ( needQuotesInFile ) + { + out << "\""; + } + out << std::endl; + + totalSize += cmSystemTools::FileLength((dirName + *fileIt).c_str()); + } + } + + // Build the archive in the upload area + std::string cmd = this->GetOption("CPACK_ZIP_COMMAND"); + cmsys::SystemTools::ReplaceString(cmd, "<ARCHIVE>", archiveFile.c_str()); + cmsys::SystemTools::ReplaceString(cmd, "<FILELIST>", + zipListFileName.c_str()); + std::string output; + int retVal = -1; + int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &retVal, + dirName.c_str(), + cmSystemTools::OUTPUT_NONE, 0); + if ( !res || retVal ) + { + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/CompressZip.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << cmd.c_str() << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running zip command: " + << cmd.c_str() << std::endl + << "Please check " << tmpFile.c_str() << " for errors" << std::endl); + return ""; + } + + // Create the NSIS code to download this file on-the-fly. + unsigned long totalSizeInKbytes = (totalSize + 512) / 1024; + if (totalSizeInKbytes == 0) + { + totalSizeInKbytes = 1; + } + cmOStringStream out; + out << " AddSize " << totalSizeInKbytes << "\n" + << " Push \"" << component->ArchiveFile << "\"\n" + << " Call DownloadFile\n" + << " ZipDLL::extractall \"$INSTDIR\\" + << component->ArchiveFile << "\" \"$INSTDIR\"\n" + << " Pop $2 ; error message\n" + " StrCmp $2 \"success\" +2 0\n" + " MessageBox MB_OK \"Failed to unzip $2\"\n" + " Delete $INSTDIR\\$0\n"; + componentCode += out.str(); + } + else + { + componentCode += " File /r \"${INST_DIR}\\" + + component->Name + "\\*.*\"\n"; + } + componentCode += "SectionEnd\n"; + + // Macro used to remove the component + macrosOut << "!macro Remove_${" << component->Name << "}\n"; + macrosOut << " IntCmp $" << component->Name << "_was_installed 0 noremove_" + << component->Name << "\n"; + std::vector<std::string>::iterator pathIt; + std::string path; + for (pathIt = component->Files.begin(); + pathIt != component->Files.end(); + ++pathIt) + { + path = *pathIt; + cmSystemTools::ReplaceString(path, "/", "\\"); + macrosOut << " Delete \"$INSTDIR\\" + << path.c_str() + << "\"\n"; + } + for (pathIt = component->Directories.begin(); + pathIt != component->Directories.end(); + ++pathIt) + { + path = *pathIt; + cmSystemTools::ReplaceString(path, "/", "\\"); + macrosOut << " RMDir \"$INSTDIR\\" + << path.c_str() + << "\"\n"; + } + macrosOut << " noremove_" << component->Name << ":\n"; + macrosOut << "!macroend\n"; + + // Macro used to select each of the components that this component + // depends on. + std::set<cmCPackComponent *> visited; + macrosOut << "!macro Select_" << component->Name << "_depends\n"; + macrosOut << CreateSelectionDependenciesDescription(component, visited); + macrosOut << "!macroend\n"; + + // Macro used to deselect each of the components that depend on this + // component. + visited.clear(); + macrosOut << "!macro Deselect_required_by_" << component->Name << "\n"; + macrosOut << CreateDeselectionDependenciesDescription(component, visited); + macrosOut << "!macroend\n"; + return componentCode; +} + +//---------------------------------------------------------------------- +std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription + (cmCPackComponent *component, + std::set<cmCPackComponent *>& visited) +{ + // Don't visit a component twice + if (visited.count(component)) + { + return std::string(); + } + visited.insert(component); + + cmOStringStream 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) +{ + // Don't visit a component twice + if (visited.count(component)) + { + return std::string(); + } + visited.insert(component); + + cmOStringStream 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, + cmOStringStream& macrosOut) +{ + if (group->Components.empty() && group->Subgroups.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<cmCPackComponentGroup*>::iterator groupIt; + for (groupIt = group->Subgroups.begin(); groupIt != group->Subgroups.end(); + ++groupIt) + { + code += this->CreateComponentGroupDescription(*groupIt, macrosOut); + } + + std::vector<cmCPackComponent*>::iterator comp; + for (comp = group->Components.begin(); + comp != group->Components.end(); + ++comp) + { + if ((*comp)->Files.empty()) + { + continue; + } + + code += this->CreateComponentDescription(*comp, macrosOut); + } + code += "SectionGroupEnd\n"; + return code; +} + +std::string cmCPackNSISGenerator::TranslateNewlines(std::string str) +{ + cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n"); + return str; +} diff --git a/Source/CPack/cmCPackNSISGenerator.h b/Source/CPack/cmCPackNSISGenerator.h new file mode 100644 index 000000000..82248546f --- /dev/null +++ b/Source/CPack/cmCPackNSISGenerator.h @@ -0,0 +1,82 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackNSISGenerator_h +#define cmCPackNSISGenerator_h + + +#include "cmCPackGenerator.h" +#include <set> + +/** \class cmCPackNSISGenerator + * \brief A generator for NSIS files + * + * http://people.freebsd.org/~kientzle/libarchive/ + */ +class cmCPackNSISGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackNSISGenerator, cmCPackGenerator); + + /** + * Construct generator + */ + cmCPackNSISGenerator(); + virtual ~cmCPackNSISGenerator(); + +protected: + virtual int InitializeInternal(); + void CreateMenuLinks( cmOStringStream& str, + cmOStringStream& deleteStr); + int PackageFiles(); + virtual const char* GetOutputExtension() { return ".exe"; } + virtual const char* GetOutputPostfix() { return "win32"; } + + bool GetListOfSubdirectories(const char* dir, + std::vector<std::string>& dirs); + + enum cmCPackGenerator::CPackSetDestdirSupport SupportsSetDestdir() const; + virtual bool SupportsAbsoluteDestination() const; + virtual bool SupportsComponentInstallation() const; + + /// Produce a string that contains the NSIS code to describe a + /// particular component. Any added macros will be emitted via + /// macrosOut. + std::string + CreateComponentDescription(cmCPackComponent *component, + cmOStringStream& macrosOut); + + /// Produce NSIS code that selects all of the components that this component + /// depends on, recursively. + std::string CreateSelectionDependenciesDescription + (cmCPackComponent *component, + std::set<cmCPackComponent *>& visited); + + /// Produce NSIS code that de-selects all of the components that are + /// dependent on this component, recursively. + std::string CreateDeselectionDependenciesDescription + (cmCPackComponent *component, + std::set<cmCPackComponent *>& visited); + + /// Produce a string that contains the NSIS code to describe a + /// particular component group, including its components. Any + /// added macros will be emitted via macrosOut. + std::string + CreateComponentGroupDescription(cmCPackComponentGroup *group, + cmOStringStream& macrosOut); + + /// Translations any newlines found in the string into \\r\\n, so that the + /// resulting string can be used within NSIS. + static std::string TranslateNewlines(std::string str); +}; + +#endif diff --git a/Source/CPack/cmCPackOSXX11Generator.cxx b/Source/CPack/cmCPackOSXX11Generator.cxx new file mode 100644 index 000000000..363ccea0c --- /dev/null +++ b/Source/CPack/cmCPackOSXX11Generator.cxx @@ -0,0 +1,311 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmCPackOSXX11Generator.h" + +#include "cmake.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmSystemTools.h" +#include "cmMakefile.h" +#include "cmGeneratedFileStream.h" +#include "cmCPackLog.h" + +#include <cmsys/SystemTools.hxx> +#include <cmsys/Glob.hxx> +#include <sys/stat.h> + +//---------------------------------------------------------------------- +cmCPackOSXX11Generator::cmCPackOSXX11Generator() +{ +} + +//---------------------------------------------------------------------- +cmCPackOSXX11Generator::~cmCPackOSXX11Generator() +{ +} + +//---------------------------------------------------------------------- +int cmCPackOSXX11Generator::PackageFiles() +{ + // TODO: Use toplevel ? + // It is used! Is this an obsolete comment? + + const char* cpackPackageExecutables + = this->GetOption("CPACK_PACKAGE_EXECUTABLES"); + if ( cpackPackageExecutables ) + { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackPackageExecutables: " + << cpackPackageExecutables << "." << std::endl); + cmOStringStream str; + cmOStringStream deleteStr; + std::vector<std::string> cpackPackageExecutablesVector; + cmSystemTools::ExpandListArgument(cpackPackageExecutables, + cpackPackageExecutablesVector); + if ( cpackPackageExecutablesVector.size() % 2 != 0 ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and " + "<icon name>." << std::endl); + return 0; + } + std::vector<std::string>::iterator it; + for ( it = cpackPackageExecutablesVector.begin(); + it != cpackPackageExecutablesVector.end(); + ++it ) + { + std::string cpackExecutableName = *it; + ++ it; + this->SetOptionIfNotSet("CPACK_EXECUTABLE_NAME", + cpackExecutableName.c_str()); + } + } + + // Disk image directories + std::string diskImageDirectory = toplevel; + std::string diskImageBackgroundImageDir + = diskImageDirectory + "/.background"; + + + // App bundle directories + std::string packageDirFileName = toplevel; + packageDirFileName += "/"; + packageDirFileName += this->GetOption("CPACK_PACKAGE_FILE_NAME"); + packageDirFileName += ".app"; + std::string contentsDirectory = packageDirFileName + "/Contents"; + std::string resourcesDirectory = contentsDirectory + "/Resources"; + std::string appDirectory = contentsDirectory + "/MacOS"; + std::string scriptDirectory = resourcesDirectory + "/Scripts"; + std::string resourceFileName = this->GetOption("CPACK_PACKAGE_FILE_NAME"); + resourceFileName += ".rsrc"; + + const char* dir = resourcesDirectory.c_str(); + const char* appdir = appDirectory.c_str(); + const char* scrDir = scriptDirectory.c_str(); + const char* contDir = contentsDirectory.c_str(); + const char* rsrcFile = resourceFileName.c_str(); + const char* iconFile = this->GetOption("CPACK_PACKAGE_ICON"); + if ( iconFile ) + { + std::string iconFileName = cmsys::SystemTools::GetFilenameName( + iconFile); + if ( !cmSystemTools::FileExists(iconFile) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find icon file: " + << iconFile << ". Please check CPACK_PACKAGE_ICON setting." + << std::endl); + return 0; + } + std::string destFileName = resourcesDirectory + "/" + iconFileName; + this->ConfigureFile(iconFile, destFileName.c_str(), true); + this->SetOptionIfNotSet("CPACK_APPLE_GUI_ICON", iconFileName.c_str()); + } + + std::string applicationsLinkName = diskImageDirectory + "/Applications"; + cmSystemTools::CreateSymlink("/Applications", applicationsLinkName.c_str()); + + if ( + !this->CopyResourcePlistFile("VolumeIcon.icns", + diskImageDirectory.c_str(), + ".VolumeIcon.icns", true ) || + !this->CopyResourcePlistFile("DS_Store", diskImageDirectory.c_str(), + ".DS_Store", true ) || + !this->CopyResourcePlistFile("background.png", + diskImageBackgroundImageDir.c_str(), "background.png", true ) || + !this->CopyResourcePlistFile("RuntimeScript", dir) || + !this->CopyResourcePlistFile("OSXX11.Info.plist", contDir, + "Info.plist" ) || + !this->CopyResourcePlistFile("OSXX11.main.scpt", scrDir, + "main.scpt", true ) || + !this->CopyResourcePlistFile("OSXScriptLauncher.rsrc", dir, + rsrcFile, true) || + !this->CopyResourcePlistFile("OSXScriptLauncher", appdir, + this->GetOption("CPACK_PACKAGE_FILE_NAME"), true) + ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files" + << std::endl); + return 0; + } + + // Two of the files need to have execute permission, so ensure they do: + std::string runTimeScript = dir; + runTimeScript += "/"; + runTimeScript += "RuntimeScript"; + + std::string appScriptName = appdir; + appScriptName += "/"; + appScriptName += this->GetOption("CPACK_PACKAGE_FILE_NAME"); + + mode_t mode; + if (cmsys::SystemTools::GetPermissions(runTimeScript.c_str(), mode)) + { + mode |= (S_IXUSR | S_IXGRP | S_IXOTH); + cmsys::SystemTools::SetPermissions(runTimeScript.c_str(), mode); + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Setting: " << runTimeScript + << " to permission: " << mode << std::endl); + } + + if (cmsys::SystemTools::GetPermissions(appScriptName.c_str(), mode)) + { + mode |= (S_IXUSR | S_IXGRP | S_IXOTH); + cmsys::SystemTools::SetPermissions(appScriptName.c_str(), mode); + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Setting: " << appScriptName + << " to permission: " << mode << std::endl); + } + + std::string output; + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/hdiutilOutput.log"; + cmOStringStream dmgCmd; + dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE") + << "\" create -ov -format UDZO -srcfolder \"" + << diskImageDirectory.c_str() + << "\" \"" << packageFileNames[0] << "\""; + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Compress disk image using command: " + << dmgCmd.str().c_str() << std::endl); + // since we get random dashboard failures with this one + // try running it more than once + int retVal = 1; + int numTries = 10; + bool res = false; + while(numTries > 0) + { + res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output, + &retVal, 0, + this->GeneratorVerbose, 0); + if ( res && !retVal ) + { + numTries = -1; + break; + } + cmSystemTools::Delay(500); + numTries--; + } + if ( !res || retVal ) + { + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << dmgCmd.str().c_str() << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running hdiutil command: " + << dmgCmd.str().c_str() << std::endl + << "Please check " << tmpFile.c_str() << " for errors" << std::endl); + return 0; + } + + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackOSXX11Generator::InitializeInternal() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "cmCPackOSXX11Generator::Initialize()" << std::endl); + std::vector<std::string> path; + std::string pkgPath = cmSystemTools::FindProgram("hdiutil", path, false); + if ( pkgPath.empty() ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find hdiutil compiler" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE", + pkgPath.c_str()); + + return this->Superclass::InitializeInternal(); +} + +//---------------------------------------------------------------------- +/* +bool cmCPackOSXX11Generator::CopyCreateResourceFile(const char* name) +{ + std::string uname = cmSystemTools::UpperCase(name); + std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname; + const char* inFileName = this->GetOption(cpackVar.c_str()); + if ( !inFileName ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "CPack option: " << cpackVar.c_str() + << " not specified. It should point to " + << (name ? name : "(NULL)") + << ".rtf, " << name + << ".html, or " << name << ".txt file" << std::endl); + return false; + } + if ( !cmSystemTools::FileExists(inFileName) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find " + << (name ? name : "(NULL)") + << " resource file: " << inFileName << std::endl); + return false; + } + std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName); + if ( ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt" ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Bad file extension specified: " + << ext << ". Currently only .rtfd, .rtf, .html, and .txt files allowed." + << std::endl); + return false; + } + + std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + destFileName += "/Resources/"; + destFileName += name + ext; + + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " + << (inFileName ? inFileName : "(NULL)") + << " to " << destFileName.c_str() << std::endl); + this->ConfigureFile(inFileName, destFileName.c_str()); + return true; +} +*/ + +//---------------------------------------------------------------------- +bool cmCPackOSXX11Generator::CopyResourcePlistFile(const char* name, + const char* dir, const char* outputFileName /* = 0 */, + bool copyOnly /* = false */) +{ + std::string inFName = "CPack."; + inFName += name; + inFName += ".in"; + std::string inFileName = this->FindTemplate(inFName.c_str()); + if ( inFileName.empty() ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: " + << inFName << std::endl); + return false; + } + + if ( !outputFileName ) + { + outputFileName = name; + } + + std::string destFileName = dir; + destFileName += "/"; + destFileName += outputFileName; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " + << inFileName.c_str() << " to " << destFileName.c_str() << std::endl); + this->ConfigureFile(inFileName.c_str(), destFileName.c_str(), copyOnly); + return true; +} + +//---------------------------------------------------------------------- +const char* cmCPackOSXX11Generator::GetPackagingInstallPrefix() +{ + this->InstallPrefix = "/"; + this->InstallPrefix += this->GetOption("CPACK_PACKAGE_FILE_NAME"); + this->InstallPrefix += ".app/Contents/Resources"; + return this->InstallPrefix.c_str(); +} diff --git a/Source/CPack/cmCPackOSXX11Generator.h b/Source/CPack/cmCPackOSXX11Generator.h new file mode 100644 index 000000000..b7bd24396 --- /dev/null +++ b/Source/CPack/cmCPackOSXX11Generator.h @@ -0,0 +1,46 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackOSXX11Generator_h +#define cmCPackOSXX11Generator_h + +#include "cmCPackGenerator.h" + +/** \class cmCPackOSXX11Generator + * \brief A generator for OSX X11 modules + * + * Based on Gimp.app + */ +class cmCPackOSXX11Generator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackOSXX11Generator, cmCPackGenerator); + + /** + * Construct generator + */ + cmCPackOSXX11Generator(); + virtual ~cmCPackOSXX11Generator(); + +protected: + virtual int InitializeInternal(); + int PackageFiles(); + virtual const char* GetPackagingInstallPrefix(); + virtual const char* GetOutputExtension() { return ".dmg"; } + + //bool CopyCreateResourceFile(const char* name, const char* dir); + bool CopyResourcePlistFile(const char* name, const char* dir, + const char* outputFileName = 0, bool copyOnly = false); + std::string InstallPrefix; +}; + +#endif diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx new file mode 100644 index 000000000..3a0e89bbd --- /dev/null +++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx @@ -0,0 +1,982 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmCPackPackageMakerGenerator.h" + +#include "cmake.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmSystemTools.h" +#include "cmMakefile.h" +#include "cmGeneratedFileStream.h" +#include "cmCPackComponentGroup.h" +#include "cmCPackLog.h" + +#include <cmsys/SystemTools.hxx> +#include <cmsys/Glob.hxx> + +//---------------------------------------------------------------------- +cmCPackPackageMakerGenerator::cmCPackPackageMakerGenerator() +{ + this->PackageMakerVersion = 0.0; + this->PackageCompatibilityVersion = 10.4; +} + +//---------------------------------------------------------------------- +cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator() +{ +} + +//---------------------------------------------------------------------- +bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const +{ + return this->PackageCompatibilityVersion >= 10.4; +} + +//---------------------------------------------------------------------- +int cmCPackPackageMakerGenerator::CopyInstallScript(const char* resdir, + const char* script, + const char* name) +{ + std::string dst = resdir; + dst += "/"; + dst += name; + cmSystemTools::CopyFileAlways(script, dst.c_str()); + cmSystemTools::SetPermissions(dst.c_str(),0777); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "copy script : " << script << "\ninto " << dst.c_str() << + std::endl); + + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackPackageMakerGenerator::PackageFiles() +{ + // TODO: Use toplevel + // It is used! Is this an obsolete comment? + + std::string resDir; // Where this package's resources will go. + std::string packageDirFileName + = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + if (this->Components.empty()) + { + packageDirFileName += ".pkg"; + resDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + resDir += "/Resources"; + } + else + { + packageDirFileName += ".mpkg"; + if ( !cmsys::SystemTools::MakeDirectory(packageDirFileName.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "unable to create package directory " + << packageDirFileName << std::endl); + return 0; + } + + resDir = packageDirFileName; + resDir += "/Contents"; + if ( !cmsys::SystemTools::MakeDirectory(resDir.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "unable to create package subdirectory " << resDir + << std::endl); + return 0; + } + + resDir += "/Resources"; + if ( !cmsys::SystemTools::MakeDirectory(resDir.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "unable to create package subdirectory " << resDir + << std::endl); + return 0; + } + + resDir += "/en.lproj"; + } + + + // Create directory structure + std::string preflightDirName = resDir + "/PreFlight"; + std::string postflightDirName = resDir + "/PostFlight"; + const char* preflight = this->GetOption("CPACK_PREFLIGHT_SCRIPT"); + const char* postflight = this->GetOption("CPACK_POSTFLIGHT_SCRIPT"); + const char* postupgrade = this->GetOption("CPACK_POSTUPGRADE_SCRIPT"); + // if preflight or postflight scripts not there create directories + // of the same name, I think this makes it work + if(!preflight) + { + if ( !cmsys::SystemTools::MakeDirectory(preflightDirName.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating installer directory: " + << preflightDirName.c_str() << std::endl); + return 0; + } + } + if(!postflight) + { + if ( !cmsys::SystemTools::MakeDirectory(postflightDirName.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating installer directory: " + << postflightDirName.c_str() << std::endl); + return 0; + } + } + // if preflight, postflight, or postupgrade are set + // then copy them into the resource directory and make + // them executable + 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 built. + std::string basePackageDir = packageDirFileName; + basePackageDir += "/Contents/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 the directory where downloaded component packages will + // be placed. + const char* userUploadDirectory = + this->GetOption("CPACK_UPLOAD_DIRECTORY"); + std::string uploadDirectory; + if (userUploadDirectory && *userUploadDirectory) + { + uploadDirectory = userUploadDirectory; + } + else + { + uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY"); + uploadDirectory += "/CPackUploads"; + } + + // Create packages for each component + bool warnedAboutDownloadCompatibility = false; + + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) + { + std::string packageFile; + if (compIt->second.IsDownloaded) + { + if (this->PackageCompatibilityVersion >= 10.5 && + this->PackageMakerVersion >= 3.0) + { + // Build this package within the upload directory. + packageFile = uploadDirectory; + + if(!cmSystemTools::FileExists(uploadDirectory.c_str())) + { + if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Unable to create package upload directory " + << uploadDirectory << std::endl); + return 0; + } + } + } + else if (!warnedAboutDownloadCompatibility) + { + if (this->PackageCompatibilityVersion < 10.5) + { + cmCPackLogger( + cmCPackLog::LOG_WARNING, + "CPack warning: please set CPACK_OSX_PACKAGE_VERSION to 10.5 " + "or greater enable downloaded packages. CPack will build a " + "non-downloaded package." + << std::endl); + } + + if (this->PackageMakerVersion < 3) + { + cmCPackLogger(cmCPackLog::LOG_WARNING, + "CPack warning: unable to build downloaded " + "packages with PackageMaker versions prior " + "to 3.0. CPack will build a non-downloaded package." + << std::endl); + } + + warnedAboutDownloadCompatibility = true; + } + } + + if (packageFile.empty()) + { + // Build this package within the overall distribution + // metapackage. + packageFile = basePackageDir; + + // We're not downloading this component, even if the user + // requested it. + compIt->second.IsDownloaded = false; + } + + 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", resDir.c_str()) + || !this->CopyCreateResourceFile("ReadMe", resDir.c_str()) + || !this->CopyCreateResourceFile("Welcome", resDir.c_str()) + || !this->CopyResourcePlistFile("Info.plist") + || !this->CopyResourcePlistFile("Description.plist") ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files" + << std::endl); + return 0; + } + + if (this->Components.empty()) + { + // Use PackageMaker to build the package. + 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; + } + else + { + // We have built the package in place. Generate the + // distribution.dist file to describe it for the installer. + WriteDistributionFile(packageDirFileName.c_str()); + } + + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/hdiutilOutput.log"; + cmOStringStream dmgCmd; + dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE") + << "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName + << "\" \"" << packageFileNames[0] << "\""; + std::string output; + int retVal = 1; + int numTries = 10; + bool res = false; + while(numTries > 0) + { + res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output, + &retVal, 0, this->GeneratorVerbose, + 0); + if ( res && !retVal ) + { + numTries = -1; + break; + } + cmSystemTools::Delay(500); + numTries--; + } + if ( !res || retVal ) + { + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << dmgCmd.str().c_str() << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running hdiutil command: " + << dmgCmd.str().c_str() << std::endl + << "Please check " << tmpFile.c_str() << " for errors" << std::endl); + return 0; + } + + return 1; +} + +//---------------------------------------------------------------------- +int cmCPackPackageMakerGenerator::InitializeInternal() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "cmCPackPackageMakerGenerator::Initialize()" << std::endl); + this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr"); + + // Starting with Xcode 4.3, PackageMaker is a separate app, and you + // can put it anywhere you want. So... use a variable for its location. + // People who put it in unexpected places can use the variable to tell + // us where it is. + // + // Use the following locations, in "most recent installation" order, + // to search for the PackageMaker app. Assume people who copy it into + // the new Xcode 4.3 app in "/Applications" will copy it into the nested + // Applications folder inside the Xcode bundle itself. Or directly in + // the "/Applications" directory. + // + // If found, save result in the CPACK_INSTALLER_PROGRAM variable. + + std::vector<std::string> paths; + paths.push_back( + "/Applications/Xcode.app/Contents/Applications" + "/PackageMaker.app/Contents/MacOS"); + paths.push_back( + "/Applications/Utilities" + "/PackageMaker.app/Contents/MacOS"); + paths.push_back( + "/Applications" + "/PackageMaker.app/Contents/MacOS"); + paths.push_back( + "/Developer/Applications/Utilities" + "/PackageMaker.app/Contents/MacOS"); + paths.push_back( + "/Developer/Applications" + "/PackageMaker.app/Contents/MacOS"); + + std::string pkgPath; + const char *inst_program = this->GetOption("CPACK_INSTALLER_PROGRAM"); + if (inst_program && *inst_program) + { + pkgPath = inst_program; + } + else + { + pkgPath = cmSystemTools::FindProgram("PackageMaker", paths, false); + if ( pkgPath.empty() ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find PackageMaker compiler" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", pkgPath.c_str()); + } + + // Get path to the real PackageMaker, not a symlink: + pkgPath = cmSystemTools::GetRealPath(pkgPath.c_str()); + // Up from there to find the version.plist file in the "Contents" dir: + std::string contents_dir; + contents_dir = cmSystemTools::GetFilenamePath(pkgPath); + contents_dir = cmSystemTools::GetFilenamePath(contents_dir); + + std::string versionFile = contents_dir + "/version.plist"; + + if ( !cmSystemTools::FileExists(versionFile.c_str()) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find PackageMaker compiler version file: " + << versionFile.c_str() + << std::endl); + return 0; + } + + std::ifstream ifs(versionFile.c_str()); + if ( !ifs ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot open PackageMaker compiler version file" << std::endl); + return 0; + } + + // Check the PackageMaker version + cmsys::RegularExpression rexKey("<key>CFBundleShortVersionString</key>"); + cmsys::RegularExpression rexVersion("<string>([0-9]+.[0-9.]+)</string>"); + std::string line; + bool foundKey = false; + while ( cmSystemTools::GetLineFromStream(ifs, line) ) + { + if ( rexKey.find(line) ) + { + foundKey = true; + break; + } + } + if ( !foundKey ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find CFBundleShortVersionString in the PackageMaker compiler " + "version file" << std::endl); + return 0; + } + if ( !cmSystemTools::GetLineFromStream(ifs, line) || + !rexVersion.find(line) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem reading the PackageMaker compiler version file: " + << versionFile.c_str() << std::endl); + return 0; + } + this->PackageMakerVersion = atof(rexVersion.match(1).c_str()); + if ( this->PackageMakerVersion < 1.0 ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Require PackageMaker 1.0 or higher" + << std::endl); + return 0; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "PackageMaker version is: " + << this->PackageMakerVersion << std::endl); + + // Determine the package compatibility version. If it wasn't + // specified by the user, we define it based on which features the + // user requested. + const char *packageCompat = this->GetOption("CPACK_OSX_PACKAGE_VERSION"); + if (packageCompat && *packageCompat) + { + this->PackageCompatibilityVersion = atof(packageCompat); + } + else if (this->GetOption("CPACK_DOWNLOAD_SITE")) + { + this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.5"); + this->PackageCompatibilityVersion = 10.5; + } + else if (this->GetOption("CPACK_COMPONENTS_ALL")) + { + this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.4"); + this->PackageCompatibilityVersion = 10.4; + } + else + { + this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.3"); + this->PackageCompatibilityVersion = 10.3; + } + + std::vector<std::string> no_paths; + pkgPath = cmSystemTools::FindProgram("hdiutil", no_paths, false); + if ( pkgPath.empty() ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find hdiutil compiler" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE", + pkgPath.c_str()); + + return this->Superclass::InitializeInternal(); +} + +//---------------------------------------------------------------------- +bool cmCPackPackageMakerGenerator::CopyCreateResourceFile(const char* name, + const char* dirName) +{ + std::string uname = cmSystemTools::UpperCase(name); + std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname; + const char* inFileName = this->GetOption(cpackVar.c_str()); + if ( !inFileName ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "CPack option: " << cpackVar.c_str() + << " not specified. It should point to " + << (name ? name : "(NULL)") + << ".rtf, " << name + << ".html, or " << name << ".txt file" << std::endl); + return false; + } + if ( !cmSystemTools::FileExists(inFileName) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find " + << (name ? name : "(NULL)") + << " resource file: " << inFileName << std::endl); + return false; + } + std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName); + if ( ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt" ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Bad file extension specified: " + << ext << ". Currently only .rtfd, .rtf, .html, and .txt files allowed." + << std::endl); + return false; + } + + std::string destFileName = dirName; + destFileName += '/'; + destFileName += name + ext; + + // Set this so that distribution.dist gets the right name (without + // the path). + this->SetOption(("CPACK_RESOURCE_FILE_" + uname + "_NOPATH").c_str(), + (name + ext).c_str()); + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " + << (inFileName ? inFileName : "(NULL)") + << " to " << destFileName.c_str() << std::endl); + this->ConfigureFile(inFileName, destFileName.c_str()); + return true; +} + +bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name, + const char* outName) +{ + if (!outName) + { + outName = name; + } + + std::string inFName = "CPack."; + inFName += name; + inFName += ".in"; + std::string inFileName = this->FindTemplate(inFName.c_str()); + if ( inFileName.empty() ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: " + << inFName << std::endl); + return false; + } + + std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + destFileName += "/"; + destFileName += outName; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " + << inFileName.c_str() << " to " << destFileName.c_str() << std::endl); + this->ConfigureFile(inFileName.c_str(), destFileName.c_str()); + return true; +} + +//---------------------------------------------------------------------- +bool cmCPackPackageMakerGenerator::RunPackageMaker(const char *command, + const char *packageFile) +{ + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/PackageMakerOutput.log"; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl); + std::string output; + int retVal = 1; + bool res = cmSystemTools::RunSingleCommand(command, &output, &retVal, 0, + this->GeneratorVerbose, 0); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running package maker" + << std::endl); + if ( !res || retVal ) + { + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << command << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem running PackageMaker command: " << command + << std::endl << "Please check " << tmpFile.c_str() << " for errors" + << std::endl); + return false; + } + // sometimes the command finishes but the directory is not yet + // created, so try 10 times to see if it shows up + int tries = 10; + while(tries > 0 && + !cmSystemTools::FileExists(packageFile)) + { + cmSystemTools::Delay(500); + tries--; + } + if(!cmSystemTools::FileExists(packageFile)) + { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Problem running PackageMaker command: " << command + << std::endl << "Package not created: " << packageFile + << std::endl); + return false; + } + + return true; +} + +//---------------------------------------------------------------------- +std::string +cmCPackPackageMakerGenerator::GetPackageName(const cmCPackComponent& component) +{ + if (component.ArchiveFile.empty()) + { + std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + packagesDir += ".dummy"; + cmOStringStream out; + out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir) + << "-" << component.Name << ".pkg"; + return out.str(); + } + else + { + return component.ArchiveFile + ".pkg"; + } +} + +//---------------------------------------------------------------------- +bool +cmCPackPackageMakerGenerator:: +GenerateComponentPackage(const char *packageFile, + const char *packageDir, + const cmCPackComponent& component) +{ + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Building component package: " << + packageFile << std::endl); + + // The command that will be used to run PackageMaker + cmOStringStream pkgCmd; + + if (this->PackageCompatibilityVersion < 10.5 || + this->PackageMakerVersion < 3.0) + { + // Create Description.plist and Info.plist files for normal Mac OS + // X packages, which work on Mac OS X 10.3 and newer. + 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; + } + + pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM") + << "\" -build -p \"" << packageFile << "\"" + << " -f \"" << packageDir << "\"" + << " -i \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") + << "/" << infoFileName << "\"" + << " -d \"" << descriptionFile << "\""; + } + else + { + // Create a "flat" package on Mac OS X 10.5 and newer. Flat + // packages are stored in a single file, rather than a directory + // like normal packages, and can be downloaded by the installer + // on-the-fly in Mac OS X 10.5 or newer. Thus, we need to create + // flat packages when the packages will be downloaded on the fly. + std::string pkgId = "com."; + pkgId += this->GetOption("CPACK_PACKAGE_VENDOR"); + pkgId += '.'; + pkgId += this->GetOption("CPACK_PACKAGE_NAME"); + pkgId += '.'; + pkgId += component.Name; + + pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM") + << "\" --root \"" << packageDir << "\"" + << " --id " << pkgId + << " --target " << this->GetOption("CPACK_OSX_PACKAGE_VERSION") + << " --out \"" << packageFile << "\""; + } + + // Run PackageMaker + 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) + { + if (groupIt->second.ParentGroup == 0) + { + 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<cmCPackComponentGroup*>::const_iterator groupIt; + for (groupIt = group.Subgroups.begin(); groupIt != group.Subgroups.end(); + ++groupIt) + { + CreateChoiceOutline(**groupIt, out); + } + + 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 += 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"; + std::set<const cmCPackComponent *> visited; + AddDependencyAttributes(component, visited, out); + visited.clear(); + AddReverseDependencyAttributes(component, visited, 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 += this->GetPackageName(component); + + // Determine the installed size of the package. + std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + dirName += '/'; + dirName += component.Name; + unsigned long installedSize + = component.GetInstalledSizeInKbytes(dirName.c_str()); + + out << "<pkg-ref id=\"" << packageId << "\" " + << "version=\"" << this->GetOption("CPACK_PACKAGE_VERSION") << "\" " + << "installKBytes=\"" << installedSize << "\" " + << "auth=\"Admin\" onConclusion=\"None\">"; + if (component.IsDownloaded) + { + out << this->GetOption("CPACK_DOWNLOAD_SITE") + << this->GetPackageName(component); + } + else + { + out << "file:./" << relativePackageLocation; + } + out << "</pkg-ref>" << std::endl; +} + +//---------------------------------------------------------------------- +void +cmCPackPackageMakerGenerator:: +AddDependencyAttributes(const cmCPackComponent& component, + std::set<const cmCPackComponent *>& visited, + cmOStringStream& out) +{ + if (visited.find(&component) != visited.end()) + { + return; + } + visited.insert(&component); + + std::vector<cmCPackComponent *>::const_iterator dependIt; + for (dependIt = component.Dependencies.begin(); + dependIt != component.Dependencies.end(); + ++dependIt) + { + out << " && choices['" << + (*dependIt)->Name << "Choice'].selected"; + AddDependencyAttributes(**dependIt, visited, out); + } +} + +//---------------------------------------------------------------------- +void +cmCPackPackageMakerGenerator:: +AddReverseDependencyAttributes(const cmCPackComponent& component, + std::set<const cmCPackComponent *>& visited, + cmOStringStream& out) +{ + if (visited.find(&component) != visited.end()) + { + return; + } + visited.insert(&component); + + std::vector<cmCPackComponent *>::const_iterator dependIt; + for (dependIt = component.ReverseDependencies.begin(); + dependIt != component.ReverseDependencies.end(); + ++dependIt) + { + out << " || choices['" << (*dependIt)->Name << "Choice'].selected"; + AddReverseDependencyAttributes(**dependIt, visited, out); + } +} + +//---------------------------------------------------------------------- +std::string cmCPackPackageMakerGenerator::EscapeForXML(std::string str) +{ + cmSystemTools::ReplaceString(str, "&", "&"); + cmSystemTools::ReplaceString(str, "<", "<"); + cmSystemTools::ReplaceString(str, ">", ">"); + cmSystemTools::ReplaceString(str, "\"", """); + return str; +} diff --git a/Source/CPack/cmCPackPackageMakerGenerator.h b/Source/CPack/cmCPackPackageMakerGenerator.h new file mode 100644 index 000000000..2bab94791 --- /dev/null +++ b/Source/CPack/cmCPackPackageMakerGenerator.h @@ -0,0 +1,119 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackPackageMakerGenerator_h +#define cmCPackPackageMakerGenerator_h + + +#include "cmCPackGenerator.h" + +class cmCPackComponent; + +/** \class cmCPackPackageMakerGenerator + * \brief A generator for PackageMaker files + * + * http://developer.apple.com/documentation/Darwin + * /Reference/ManPages/man1/packagemaker.1.html + */ +class cmCPackPackageMakerGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackPackageMakerGenerator, cmCPackGenerator); + + /** + * Construct generator + */ + cmCPackPackageMakerGenerator(); + virtual ~cmCPackPackageMakerGenerator(); + + virtual bool SupportsComponentInstallation() const; + +protected: + int CopyInstallScript(const char* resdir, + const char* script, + const char* name); + virtual int InitializeInternal(); + int PackageFiles(); + virtual const char* GetOutputExtension() { return ".dmg"; } + virtual const char* GetOutputPostfix() { return "darwin"; } + + // Copies or creates the resource file with the given name to the + // package or package staging directory dirName. The variable + // CPACK_RESOURCE_FILE_${NAME} (where ${NAME} is the uppercased + // version of name) specifies the input file to use for this file, + // which will be configured via ConfigureFile. + bool CopyCreateResourceFile(const char* name, const char *dirName); + 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, + std::set<const cmCPackComponent *>& visited, + cmOStringStream& out); + + // Subroutine of WriteDistributionFile that writes out the + // reverse dependency attributes for inter-component dependencies. + void + AddReverseDependencyAttributes(const cmCPackComponent& component, + std::set<const cmCPackComponent *>& visited, + 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 PackageCompatibilityVersion; +}; + +#endif diff --git a/Source/CPack/cmCPackRPMGenerator.cxx b/Source/CPack/cmCPackRPMGenerator.cxx new file mode 100644 index 000000000..413572ee7 --- /dev/null +++ b/Source/CPack/cmCPackRPMGenerator.cxx @@ -0,0 +1,275 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmCPackRPMGenerator.h" +#include "cmCPackLog.h" +#include "cmSystemTools.h" + +//---------------------------------------------------------------------- +cmCPackRPMGenerator::cmCPackRPMGenerator() +{ +} + +//---------------------------------------------------------------------- +cmCPackRPMGenerator::~cmCPackRPMGenerator() +{ +} + +//---------------------------------------------------------------------- +int cmCPackRPMGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr"); + if (cmSystemTools::IsOff(this->GetOption("CPACK_SET_DESTDIR"))) + { + this->SetOption("CPACK_SET_DESTDIR", "I_ON"); + } + /* Replace space in CPACK_PACKAGE_NAME in order to avoid + * rpmbuild scream on unwanted space in filename issue + * Moreover RPM file do not usually embed space in filename + */ + if (this->GetOption("CPACK_PACKAGE_NAME")) { + std::string packageName=this->GetOption("CPACK_PACKAGE_NAME"); + cmSystemTools::ReplaceString(packageName," ","-"); + this->SetOption("CPACK_PACKAGE_NAME",packageName.c_str()); + } + /* same for CPACK_PACKAGE_FILE_NAME */ + if (this->GetOption("CPACK_PACKAGE_FILE_NAME")) { + std::string packageName=this->GetOption("CPACK_PACKAGE_FILE_NAME"); + cmSystemTools::ReplaceString(packageName," ","-"); + this->SetOption("CPACK_PACKAGE_FILE_NAME",packageName.c_str()); + } + return this->Superclass::InitializeInternal(); +} + +//---------------------------------------------------------------------- +int cmCPackRPMGenerator::PackageOnePack(std::string initialToplevel, + std::string packageName) +{ + int retval = 1; + // Begin the archive for this pack + std::string localToplevel(initialToplevel); + std::string packageFileName( + cmSystemTools::GetParentDirectory(toplevel.c_str()) + ); + std::string outputFileName( + GetComponentPackageFileName(this->GetOption("CPACK_PACKAGE_FILE_NAME"), + packageName, + true) + + this->GetOutputExtension() + ); + + localToplevel += "/"+ packageName; + /* replace the TEMP DIRECTORY with the component one */ + this->SetOption("CPACK_TEMPORARY_DIRECTORY",localToplevel.c_str()); + packageFileName += "/"+ outputFileName; + /* replace proposed CPACK_OUTPUT_FILE_NAME */ + this->SetOption("CPACK_OUTPUT_FILE_NAME",outputFileName.c_str()); + /* replace the TEMPORARY package file name */ + this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", + packageFileName.c_str()); + // Tell CPackRPM.cmake the name of the component NAME. + this->SetOption("CPACK_RPM_PACKAGE_COMPONENT",packageName.c_str()); + if (!this->ReadListFile("CPackRPM.cmake")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error while execution CPackRPM.cmake" << std::endl); + retval = 0; + } + // add the generated package to package file names list + packageFileNames.push_back(packageFileName); + return retval; +} + +//---------------------------------------------------------------------- +int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup) +{ + int retval = 1; + /* Reset package file name list it will be populated during the + * component packaging run*/ + packageFileNames.clear(); + std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + + // The default behavior is to have one package by component group + // unless CPACK_COMPONENTS_IGNORE_GROUP is specified. + if (!ignoreGroup) + { + std::map<std::string, cmCPackComponentGroup>::iterator compGIt; + for (compGIt=this->ComponentGroups.begin(); + compGIt!=this->ComponentGroups.end(); ++compGIt) + { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Packaging component group: " + << compGIt->first + << std::endl); + retval &= PackageOnePack(initialTopLevel,compGIt->first); + } + // Handle Orphan components (components not belonging to any groups) + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt=this->Components.begin(); + compIt!=this->Components.end(); ++compIt ) + { + // Does the component belong to a group? + if (compIt->second.Group==NULL) + { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Component <" + << compIt->second.Name + << "> does not belong to any group, package it separately." + << std::endl); + retval &= PackageOnePack(initialTopLevel,compIt->first); + } + } + } + // CPACK_COMPONENTS_IGNORE_GROUPS is set + // We build 1 package per component + else + { + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt=this->Components.begin(); + compIt!=this->Components.end(); ++compIt ) + { + retval &= PackageOnePack(initialTopLevel,compIt->first); + } + } + return retval; +} + +//---------------------------------------------------------------------- +int cmCPackRPMGenerator::PackageComponentsAllInOne() +{ + int retval = 1; + std::string compInstDirName; + /* Reset package file name list it will be populated during the + * component packaging run*/ + packageFileNames.clear(); + std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + + compInstDirName = "ALL_COMPONENTS_IN_ONE"; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Packaging all groups in one package..." + "(CPACK_COMPONENTS_ALL_[GROUPS_]IN_ONE_PACKAGE is set)" + << std::endl); + + // The ALL GROUPS in ONE package case + std::string localToplevel(initialTopLevel); + std::string packageFileName( + cmSystemTools::GetParentDirectory(toplevel.c_str()) + ); + std::string outputFileName( + std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) + + this->GetOutputExtension() + ); + // all GROUP in one vs all COMPONENT in one + localToplevel += "/"+compInstDirName; + + /* replace the TEMP DIRECTORY with the component one */ + this->SetOption("CPACK_TEMPORARY_DIRECTORY",localToplevel.c_str()); + packageFileName += "/"+ outputFileName; + /* replace proposed CPACK_OUTPUT_FILE_NAME */ + this->SetOption("CPACK_OUTPUT_FILE_NAME",outputFileName.c_str()); + /* replace the TEMPORARY package file name */ + this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", + packageFileName.c_str()); + // Tell CPackRPM.cmake the name of the component GROUP. + this->SetOption("CPACK_RPM_PACKAGE_COMPONENT",compInstDirName.c_str()); + if (!this->ReadListFile("CPackRPM.cmake")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error while execution CPackRPM.cmake" << std::endl); + retval = 0; + } + // add the generated package to package file names list + packageFileNames.push_back(packageFileName); + + return retval; +} + +//---------------------------------------------------------------------- +int cmCPackRPMGenerator::PackageFiles() +{ + int retval = 1; + + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Toplevel: " + << toplevel << std::endl); + + /* Are we in the component packaging case */ + if (WantsComponentInstallation()) { + // CASE 1 : COMPONENT ALL-IN-ONE package + // If ALL COMPONENTS in ONE package has been requested + // then the package file is unique and should be open here. + if (componentPackageMethod == ONE_PACKAGE) + { + return PackageComponentsAllInOne(); + } + // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one) + // There will be 1 package for each component group + // however one may require to ignore component group and + // in this case you'll get 1 package for each component. + else + { + return PackageComponents(componentPackageMethod == + ONE_PACKAGE_PER_COMPONENT); + } + } + // CASE 3 : NON COMPONENT package. + else + { + if (!this->ReadListFile("CPackRPM.cmake")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error while execution CPackRPM.cmake" << std::endl); + retval = 0; + } + } + + if (!this->IsSet("RPMBUILD_EXECUTABLE")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find rpmbuild" << std::endl); + retval = 0; + } + return retval; +} + +bool cmCPackRPMGenerator::SupportsComponentInstallation() const + { + if (IsOn("CPACK_RPM_COMPONENT_INSTALL")) + { + return true; + } + else + { + return false; + } + } + +std::string cmCPackRPMGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) + { + if (componentPackageMethod == ONE_PACKAGE_PER_COMPONENT) { + return componentName; + } + + if (componentPackageMethod == ONE_PACKAGE) { + return std::string("ALL_COMPONENTS_IN_ONE"); + } + // We have to find the name of the COMPONENT GROUP + // the current COMPONENT belongs to. + std::string groupVar = "CPACK_COMPONENT_" + + cmSystemTools::UpperCase(componentName) + "_GROUP"; + if (NULL != GetOption(groupVar.c_str())) + { + return std::string(GetOption(groupVar.c_str())); + } + else + { + return componentName; + } + } diff --git a/Source/CPack/cmCPackRPMGenerator.h b/Source/CPack/cmCPackRPMGenerator.h new file mode 100644 index 000000000..a7722bc58 --- /dev/null +++ b/Source/CPack/cmCPackRPMGenerator.h @@ -0,0 +1,77 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackRPMGenerator_h +#define cmCPackRPMGenerator_h + + +#include "cmCPackGenerator.h" + +/** \class cmCPackRPMGenerator + * \brief A generator for RPM packages + * The idea of the CPack RPM generator is to use + * as minimal C++ code as possible. + * Ideally the C++ part of the CPack RPM generator + * will only 'execute' (aka ->ReadListFile) several + * CMake macros files. + */ +class cmCPackRPMGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackRPMGenerator, cmCPackGenerator); + + /** + * Construct generator + */ + cmCPackRPMGenerator(); + virtual ~cmCPackRPMGenerator(); + + static bool CanGenerate() + { +#ifdef __APPLE__ + // on MacOS enable CPackRPM iff rpmbuild is found + std::vector<std::string> locations; + locations.push_back("/sw/bin"); // Fink + locations.push_back("/opt/local/bin"); // MacPorts + return cmSystemTools::FindProgram("rpmbuild") != "" ? true : false; +#else + // legacy behavior on other systems + return true; +#endif + } + +protected: + virtual int InitializeInternal(); + virtual int PackageFiles(); + /** + * This method factors out the work done in component packaging case. + */ + int PackageOnePack(std::string initialToplevel, std::string packageName); + /** + * The method used to package files when component + * install is used. This will create one + * archive for each component group. + */ + int PackageComponents(bool ignoreGroup); + /** + * Special case of component install where all + * components will be put in a single installer. + */ + int PackageComponentsAllInOne(); + virtual const char* GetOutputExtension() { return ".rpm"; } + virtual bool SupportsComponentInstallation() const; + virtual std::string GetComponentInstallDirNameSuffix( + const std::string& componentName); + +}; + +#endif diff --git a/Source/CPack/cmCPackSTGZGenerator.cxx b/Source/CPack/cmCPackSTGZGenerator.cxx new file mode 100644 index 000000000..966a231b7 --- /dev/null +++ b/Source/CPack/cmCPackSTGZGenerator.cxx @@ -0,0 +1,138 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackSTGZGenerator.h" + +#include "cmake.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmSystemTools.h" +#include "cmMakefile.h" +#include "cmCPackLog.h" + +#include <cmsys/ios/sstream> +#include <sys/types.h> +#include <sys/stat.h> + +//---------------------------------------------------------------------- +cmCPackSTGZGenerator::cmCPackSTGZGenerator() +{ +} + +//---------------------------------------------------------------------- +cmCPackSTGZGenerator::~cmCPackSTGZGenerator() +{ +} + +//---------------------------------------------------------------------- +int cmCPackSTGZGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "0"); + + std::string inFile = this->FindTemplate("CPack.STGZ_Header.sh.in"); + if ( inFile.empty() ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find template file: " + << inFile.c_str() << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_STGZ_HEADER_FILE", inFile.c_str()); + this->SetOptionIfNotSet("CPACK_AT_SIGN", "@"); + + return this->Superclass::InitializeInternal(); +} + +//---------------------------------------------------------------------- +int cmCPackSTGZGenerator::PackageFiles() +{ + bool retval = true; + if ( !this->Superclass::PackageFiles() ) + { + return 0; + } + + /* TGZ generator (our Superclass) may + * have generated several packages (component packaging) + * so we must iterate over generated packages. + */ + for (std::vector<std::string>::iterator it=packageFileNames.begin(); + it != packageFileNames.end(); ++it) + { + retval &= cmSystemTools::SetPermissions((*it).c_str(), +#if defined( _MSC_VER ) || defined( __MINGW32__ ) + S_IREAD | S_IWRITE | S_IEXEC +#elif defined( __BORLANDC__ ) + S_IRUSR | S_IWUSR | S_IXUSR +#else + S_IRUSR | S_IWUSR | S_IXUSR | + S_IRGRP | S_IWGRP | S_IXGRP | + S_IROTH | S_IWOTH | S_IXOTH +#endif + ); + } + return retval; +} + +//---------------------------------------------------------------------- +int cmCPackSTGZGenerator::GenerateHeader(std::ostream* os) +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Writing header" << std::endl); + cmsys_ios::ostringstream str; + int counter = 0; + + std::string inLicFile = this->GetOption("CPACK_RESOURCE_FILE_LICENSE"); + std::string line; + std::ifstream ilfs(inLicFile.c_str()); + std::string licenseText; + while ( cmSystemTools::GetLineFromStream(ilfs, line) ) + { + licenseText += line + "\n"; + } + this->SetOptionIfNotSet("CPACK_RESOURCE_FILE_LICENSE_CONTENT", + licenseText.c_str()); + + const char headerLengthTag[] = "###CPACK_HEADER_LENGTH###"; + + // Create the header + std::string inFile = this->GetOption("CPACK_STGZ_HEADER_FILE"); + std::ifstream ifs(inFile.c_str()); + std::string packageHeaderText; + while ( cmSystemTools::GetLineFromStream(ifs, line) ) + { + packageHeaderText += line + "\n"; + } + + // Configure in the values + std::string res; + this->ConfigureString(packageHeaderText, res); + + // Count the lines + const char* ptr = res.c_str(); + while ( *ptr ) + { + if ( *ptr == '\n' ) + { + counter ++; + } + ++ptr; + } + counter ++; + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Number of lines: " << counter << std::endl); + char buffer[1024]; + sprintf(buffer, "%d", counter); + cmSystemTools::ReplaceString(res, headerLengthTag, buffer); + + // Write in file + *os << res.c_str(); + return this->Superclass::GenerateHeader(os); +} diff --git a/Source/CPack/cmCPackSTGZGenerator.h b/Source/CPack/cmCPackSTGZGenerator.h new file mode 100644 index 000000000..ccceec806 --- /dev/null +++ b/Source/CPack/cmCPackSTGZGenerator.h @@ -0,0 +1,41 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackSTGZGenerator_h +#define cmCPackSTGZGenerator_h + + +#include "cmCPackTGZGenerator.h" + +/** \class cmCPackSTGZGenerator + * \brief A generator for Self extractable TGZ files + * + */ +class cmCPackSTGZGenerator : public cmCPackTGZGenerator +{ +public: + cmCPackTypeMacro(cmCPackSTGZGenerator, cmCPackTGZGenerator); + + /** + * Construct generator + */ + cmCPackSTGZGenerator(); + virtual ~cmCPackSTGZGenerator(); + +protected: + int PackageFiles(); + virtual int InitializeInternal(); + int GenerateHeader(std::ostream* os); + virtual const char* GetOutputExtension() { return ".sh"; } +}; + +#endif diff --git a/Source/CPack/cmCPackTGZGenerator.cxx b/Source/CPack/cmCPackTGZGenerator.cxx new file mode 100644 index 000000000..509c7f80a --- /dev/null +++ b/Source/CPack/cmCPackTGZGenerator.cxx @@ -0,0 +1,26 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackTGZGenerator.h" + +//---------------------------------------------------------------------- +cmCPackTGZGenerator::cmCPackTGZGenerator() + :cmCPackArchiveGenerator(cmArchiveWrite::CompressGZip, + cmArchiveWrite::TypeTAR) +{ +} + +//---------------------------------------------------------------------- +cmCPackTGZGenerator::~cmCPackTGZGenerator() +{ +} + diff --git a/Source/CPack/cmCPackTGZGenerator.h b/Source/CPack/cmCPackTGZGenerator.h new file mode 100644 index 000000000..3a9fc6b41 --- /dev/null +++ b/Source/CPack/cmCPackTGZGenerator.h @@ -0,0 +1,35 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackTGZGenerator_h +#define cmCPackTGZGenerator_h + +#include "cmCPackArchiveGenerator.h" + +/** \class cmCPackTGZGenerator + * \brief A generator for TGZ files + * + */ +class cmCPackTGZGenerator : public cmCPackArchiveGenerator +{ +public: + cmCPackTypeMacro(cmCPackTGZGenerator, cmCPackArchiveGenerator); + /** + * Construct generator + */ + cmCPackTGZGenerator(); + virtual ~cmCPackTGZGenerator(); +protected: + virtual const char* GetOutputExtension() { return ".tar.gz"; } +}; + +#endif diff --git a/Source/CPack/cmCPackTarBZip2Generator.cxx b/Source/CPack/cmCPackTarBZip2Generator.cxx new file mode 100644 index 000000000..971d16667 --- /dev/null +++ b/Source/CPack/cmCPackTarBZip2Generator.cxx @@ -0,0 +1,25 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackTarBZip2Generator.h" +//---------------------------------------------------------------------- +cmCPackTarBZip2Generator::cmCPackTarBZip2Generator() + :cmCPackArchiveGenerator(cmArchiveWrite::CompressBZip2, + cmArchiveWrite::TypeTAR) +{ +} + +//---------------------------------------------------------------------- +cmCPackTarBZip2Generator::~cmCPackTarBZip2Generator() +{ +} + diff --git a/Source/CPack/cmCPackTarBZip2Generator.h b/Source/CPack/cmCPackTarBZip2Generator.h new file mode 100644 index 000000000..74c244e5a --- /dev/null +++ b/Source/CPack/cmCPackTarBZip2Generator.h @@ -0,0 +1,34 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackTarBZip2Generator_h +#define cmCPackTarBZip2Generator_h + +#include "cmCPackArchiveGenerator.h" + +/** \class cmCPackTarBZip2Generator + * \brief A generator for TarBZip2 files + */ +class cmCPackTarBZip2Generator : public cmCPackArchiveGenerator +{ +public: + cmCPackTypeMacro(cmCPackTarBZip2Generator, cmCPackArchiveGenerator); + /** + * Construct generator + */ + cmCPackTarBZip2Generator(); + virtual ~cmCPackTarBZip2Generator(); +protected: + virtual const char* GetOutputExtension() { return ".tar.bz2"; } +}; + +#endif diff --git a/Source/CPack/cmCPackTarCompressGenerator.cxx b/Source/CPack/cmCPackTarCompressGenerator.cxx new file mode 100644 index 000000000..7a8f697c0 --- /dev/null +++ b/Source/CPack/cmCPackTarCompressGenerator.cxx @@ -0,0 +1,26 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackTarCompressGenerator.h" + +//---------------------------------------------------------------------- +cmCPackTarCompressGenerator::cmCPackTarCompressGenerator() + :cmCPackArchiveGenerator(cmArchiveWrite::CompressCompress, + cmArchiveWrite::TypeTAR) +{ +} + +//---------------------------------------------------------------------- +cmCPackTarCompressGenerator::~cmCPackTarCompressGenerator() +{ +} + diff --git a/Source/CPack/cmCPackTarCompressGenerator.h b/Source/CPack/cmCPackTarCompressGenerator.h new file mode 100644 index 000000000..7ff9a0ade --- /dev/null +++ b/Source/CPack/cmCPackTarCompressGenerator.h @@ -0,0 +1,35 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackTarCompressGenerator_h +#define cmCPackTarCompressGenerator_h + +#include "cmCPackTGZGenerator.h" + +/** \class cmCPackTarCompressGenerator + * \brief A generator for TarCompress files + */ +class cmCPackTarCompressGenerator : public cmCPackArchiveGenerator +{ +public: + cmCPackTypeMacro(cmCPackTarCompressGenerator, cmCPackArchiveGenerator); + /** + * Construct generator + */ + cmCPackTarCompressGenerator(); + virtual ~cmCPackTarCompressGenerator(); + +protected: + virtual const char* GetOutputExtension() { return ".tar.Z"; } +}; + +#endif diff --git a/Source/CPack/cmCPackZIPGenerator.cxx b/Source/CPack/cmCPackZIPGenerator.cxx new file mode 100644 index 000000000..e6e4e77d0 --- /dev/null +++ b/Source/CPack/cmCPackZIPGenerator.cxx @@ -0,0 +1,26 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackZIPGenerator.h" + +//---------------------------------------------------------------------- +cmCPackZIPGenerator::cmCPackZIPGenerator() + :cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, + cmArchiveWrite::TypeZIP) +{ +} + +//---------------------------------------------------------------------- +cmCPackZIPGenerator::~cmCPackZIPGenerator() +{ +} + diff --git a/Source/CPack/cmCPackZIPGenerator.h b/Source/CPack/cmCPackZIPGenerator.h new file mode 100644 index 000000000..70e1a5fa8 --- /dev/null +++ b/Source/CPack/cmCPackZIPGenerator.h @@ -0,0 +1,36 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackZIPGenerator_h +#define cmCPackZIPGenerator_h + +#include "cmCPackArchiveGenerator.h" + +/** \class cmCPackZIPGenerator + * \brief A generator for ZIP files + */ +class cmCPackZIPGenerator : public cmCPackArchiveGenerator +{ +public: + cmCPackTypeMacro(cmCPackZIPGenerator, cmCPackArchiveGenerator); + + /** + * Construct generator + */ + cmCPackZIPGenerator(); + virtual ~cmCPackZIPGenerator(); + +protected: + virtual const char* GetOutputExtension() { return ".zip"; } +}; + +#endif diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx new file mode 100644 index 000000000..b6035854e --- /dev/null +++ b/Source/CPack/cpack.cxx @@ -0,0 +1,601 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmSystemTools.h" + +// Need these for documentation support. +#include "cmake.h" +#include "cmDocumentation.h" +#include "cmCPackDocumentVariables.h" +#include "cmCPackDocumentMacros.h" +#include "cmCPackGeneratorFactory.h" +#include "cmCPackGenerator.h" +#include "cmake.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" + +#include "cmCPackLog.h" + +#include <cmsys/CommandLineArguments.hxx> +#include <cmsys/SystemTools.hxx> +#include <memory> // auto_ptr + +//---------------------------------------------------------------------------- +static const char * cmDocumentationName[][3] = +{ + {0, + " cpack - Packaging driver provided by CMake.", 0}, + {0,0,0} +}; + +//---------------------------------------------------------------------------- +static const char * cmDocumentationUsage[][3] = +{ + {0, + " cpack -G <generator> [options]", + 0}, + {0,0,0} +}; + +//---------------------------------------------------------------------------- +static const char * cmDocumentationDescription[][3] = +{ + {0, + "The \"cpack\" executable is the CMake packaging program. " + "CMake-generated build trees created for projects that use " + "the INSTALL_* commands have packaging support. " + "This program will generate the package.", 0}, + CMAKE_STANDARD_INTRODUCTION, + {0,0,0} +}; + +//---------------------------------------------------------------------------- +static const char * cmDocumentationOptions[][3] = +{ + {"-G <generator>", "Use the specified generator to generate package.", + "CPack may support multiple native packaging systems on certain " + "platforms. A generator is responsible for generating input files for " + "particular system and invoking that systems. Possible generator names " + "are specified in the Generators section." }, + {"-C <Configuration>", "Specify the project configuration", + "This option specifies the configuration that the project was build " + "with, for example 'Debug', 'Release'." }, + {"-D <var>=<value>", "Set a CPack variable.", \ + "Set a variable that can be used by the generator."}, \ + {"--config <config file>", "Specify the config file.", + "Specify the config file to use to create the package. By default " + "CPackConfig.cmake in the current directory will be used." }, + {"--verbose,-V","enable verbose output","Run cpack with verbose output."}, + {"--debug","enable debug output (for CPack developers)", + "Run cpack with debug output (for CPack developers)."}, + {"-P <package name>","override/define CPACK_PACKAGE_NAME", + "If the package name is not specified on cpack commmand line then" + "CPack.cmake defines it as CMAKE_PROJECT_NAME"}, + {"-R <package version>","override/define CPACK_PACKAGE_VERSION", + "If version is not specified on cpack command line then" + "CPack.cmake defines it from CPACK_PACKAGE_VERSION_[MAJOR|MINOR|PATCH]" + "look into CPack.cmake for detail"}, + {"-B <package directory>","override/define CPACK_PACKAGE_DIRECTORY", + "The directory where CPack will be doing its packaging work." + "The resulting package will be found there. Inside this directory" + "CPack creates '_CPack_Packages' sub-directory which is the" + "CPack temporary directory."}, + {"--vendor <vendor name>","override/define CPACK_PACKAGE_VENDOR", + "If vendor is not specified on cpack command line " + "(or inside CMakeLists.txt) then" + "CPack.cmake defines it with a default value"}, + {"--help-command cmd [file]", "Print help for a single command and exit.", + "Full documentation specific to the given command is displayed. " + "If a file is specified, the documentation is written into and the output " + "format is determined depending on the filename suffix. Supported are man " + "page, HTML, DocBook and plain text."}, + {"--help-command-list [file]", "List available commands and exit.", + "The list contains all commands for which help may be obtained by using " + "the --help-command argument followed by a command name. " + "If a file is specified, the documentation is written into and the output " + "format is determined depending on the filename suffix. Supported are man " + "page, HTML, DocBook and plain text."}, + {"--help-commands [file]", "Print help for all commands and exit.", + "Full documentation specific for all current command is displayed." + "If a file is specified, the documentation is written into and the output " + "format is determined depending on the filename suffix. Supported are man " + "page, HTML, DocBook and plain text."}, + {"--help-variable var [file]", + "Print help for a single variable and exit.", + "Full documentation specific to the given variable is displayed." + "If a file is specified, the documentation is written into and the output " + "format is determined depending on the filename suffix. Supported are man " + "page, HTML, DocBook and plain text."}, + {"--help-variable-list [file]", "List documented variables and exit.", + "The list contains all variables for which help may be obtained by using " + "the --help-variable argument followed by a variable name. If a file is " + "specified, the help is written into it." + "If a file is specified, the documentation is written into and the output " + "format is determined depending on the filename suffix. Supported are man " + "page, HTML, DocBook and plain text."}, + {"--help-variables [file]", "Print help for all variables and exit.", + "Full documentation for all variables is displayed." + "If a file is specified, the documentation is written into and the output " + "format is determined depending on the filename suffix. Supported are man " + "page, HTML, DocBook and plain text."}, + {0,0,0} +}; + +//---------------------------------------------------------------------------- +static const char * cmDocumentationSeeAlso[][3] = +{ + {0, "cmake", 0}, + {0, "ccmake", 0}, + {0, 0, 0} +}; + +//---------------------------------------------------------------------------- +int cpackUnknownArgument(const char*, void*) +{ + return 1; +} + +//---------------------------------------------------------------------------- +struct cpackDefinitions +{ + typedef std::map<cmStdString, cmStdString> MapType; + MapType Map; + cmCPackLog *Log; +}; + +//---------------------------------------------------------------------------- +int cpackDefinitionArgument(const char* argument, const char* cValue, + void* call_data) +{ + (void)argument; + cpackDefinitions* def = static_cast<cpackDefinitions*>(call_data); + std::string value = cValue; + size_t pos = value.find_first_of("="); + if ( pos == std::string::npos ) + { + cmCPack_Log(def->Log, cmCPackLog::LOG_ERROR, + "Please specify CPack definitions as: KEY=VALUE" << std::endl); + return 0; + } + std::string key = value.substr(0, pos); + value = value.c_str() + pos + 1; + def->Map[key] = value; + cmCPack_Log(def->Log, cmCPackLog::LOG_DEBUG, "Set CPack variable: " + << key.c_str() << " to \"" << value.c_str() << "\"" << std::endl); + return 1; +} + + +//---------------------------------------------------------------------------- +// this is CPack. +int main (int argc, char *argv[]) +{ + cmSystemTools::FindExecutableDirectory(argv[0]); + cmCPackLog log; + int nocwd = 0; + + log.SetErrorPrefix("CPack Error: "); + log.SetWarningPrefix("CPack Warning: "); + log.SetOutputPrefix("CPack: "); + log.SetVerbosePrefix("CPack Verbose: "); + + cmSystemTools::EnableMSVCDebugHook(); + + if ( cmSystemTools::GetCurrentWorkingDirectory().size() == 0 ) + { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Current working directory cannot be established." << std::endl); + nocwd = 1; + } + + std::string generator; + bool help = false; + bool helpVersion = false; + bool verbose = false; + bool debug = false; + std::string helpFull; + std::string helpMAN; + std::string helpHTML; + + std::string cpackProjectName; + std::string cpackProjectDirectory; + std::string cpackBuildConfig; + std::string cpackProjectVersion; + std::string cpackProjectPatch; + std::string cpackProjectVendor; + std::string cpackConfigFile; + + cpackDefinitions definitions; + definitions.Log = &log; + + cpackConfigFile = ""; + + cmsys::CommandLineArguments arg; + arg.Initialize(argc, argv); + typedef cmsys::CommandLineArguments argT; + // Help arguments + arg.AddArgument("--help", argT::NO_ARGUMENT, &help, "CPack help"); + arg.AddArgument("--help-full", argT::SPACE_ARGUMENT, &helpFull, + "CPack help"); + arg.AddArgument("--help-html", argT::SPACE_ARGUMENT, &helpHTML, + "CPack help"); + arg.AddArgument("--help-man", argT::SPACE_ARGUMENT, &helpMAN, "CPack help"); + arg.AddArgument("--version", argT::NO_ARGUMENT, &helpVersion, "CPack help"); + + arg.AddArgument("-V", argT::NO_ARGUMENT, &verbose, "CPack verbose"); + arg.AddArgument("--verbose", argT::NO_ARGUMENT, &verbose, "-V"); + arg.AddArgument("--debug", argT::NO_ARGUMENT, &debug, "-V"); + arg.AddArgument("--config", argT::SPACE_ARGUMENT, &cpackConfigFile, + "CPack configuration file"); + arg.AddArgument("-C", argT::SPACE_ARGUMENT, &cpackBuildConfig, + "CPack build configuration"); + arg.AddArgument("-G", argT::SPACE_ARGUMENT, + &generator, "CPack generator"); + arg.AddArgument("-P", argT::SPACE_ARGUMENT, + &cpackProjectName, "CPack project name"); + arg.AddArgument("-R", argT::SPACE_ARGUMENT, + &cpackProjectVersion, "CPack project version"); + arg.AddArgument("-B", argT::SPACE_ARGUMENT, + &cpackProjectDirectory, "CPack project directory"); + arg.AddArgument("--patch", argT::SPACE_ARGUMENT, + &cpackProjectPatch, "CPack project patch"); + arg.AddArgument("--vendor", argT::SPACE_ARGUMENT, + &cpackProjectVendor, "CPack project vendor"); + arg.AddCallback("-D", argT::SPACE_ARGUMENT, + cpackDefinitionArgument, &definitions, "CPack Definitions"); + arg.SetUnknownArgumentCallback(cpackUnknownArgument); + + // Parse command line + int parsed = arg.Parse(); + + // Setup logging + if ( verbose ) + { + log.SetVerbose(verbose); + cmCPack_Log(&log, cmCPackLog::LOG_OUTPUT, "Enable Verbose" << std::endl); + } + if ( debug ) + { + log.SetDebug(debug); + cmCPack_Log(&log, cmCPackLog::LOG_OUTPUT, "Enable Debug" << std::endl); + } + + cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, + "Read CPack config file: " << cpackConfigFile.c_str() << std::endl); + + cmake cminst; + cminst.RemoveUnscriptableCommands(); + cmGlobalGenerator cmgg; + cmgg.SetCMakeInstance(&cminst); + std::auto_ptr<cmLocalGenerator> cmlg(cmgg.CreateLocalGenerator()); + cmMakefile* globalMF = cmlg->GetMakefile(); + + bool cpackConfigFileSpecified = true; + if ( cpackConfigFile.empty() ) + { + cpackConfigFile = cmSystemTools::GetCurrentWorkingDirectory(); + cpackConfigFile += "/CPackConfig.cmake"; + cpackConfigFileSpecified = false; + } + + cmCPackGeneratorFactory generators; + generators.SetLogger(&log); + cmCPackGenerator* cpackGenerator = 0; + + cmDocumentation doc; + doc.addCPackStandardDocSections(); + /* Were we invoked to display doc or to do some work ? + * Unlike cmake launching cpack with zero argument + * should launch cpack using "cpackConfigFile" if it exists + * in the current directory. + */ + if((doc.CheckOptions(argc, argv,"-G") || nocwd) && !(argc==1)) + { + help = true; + } + else + { + help = false; + } + + // This part is used for cpack documentation lookup as well. + cminst.AddCMakePaths(); + + if ( parsed && !help ) + { + // find out which system cpack is running on, so it can setup the search + // paths, so FIND_XXX() commands can be used in scripts + std::string systemFile = + globalMF->GetModulesFile("CMakeDetermineSystem.cmake"); + if (!globalMF->ReadListFile(0, systemFile.c_str())) + { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Error reading CMakeDetermineSystem.cmake" << std::endl); + return 1; + } + + systemFile = + globalMF->GetModulesFile("CMakeSystemSpecificInformation.cmake"); + if (!globalMF->ReadListFile(0, systemFile.c_str())) + { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Error reading CMakeSystemSpecificInformation.cmake" << std::endl); + return 1; + } + + if ( cmSystemTools::FileExists(cpackConfigFile.c_str()) ) + { + cpackConfigFile = + cmSystemTools::CollapseFullPath(cpackConfigFile.c_str()); + cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, + "Read CPack configuration file: " << cpackConfigFile.c_str() + << std::endl); + if ( !globalMF->ReadListFile(0, cpackConfigFile.c_str()) ) + { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Problem reading CPack config file: \"" + << cpackConfigFile.c_str() << "\"" << std::endl); + return 1; + } + } + else if ( cpackConfigFileSpecified ) + { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Cannot find CPack config file: \"" << cpackConfigFile.c_str() + << "\"" << std::endl); + return 1; + } + + if ( !generator.empty() ) + { + globalMF->AddDefinition("CPACK_GENERATOR", generator.c_str()); + } + if ( !cpackProjectName.empty() ) + { + globalMF->AddDefinition("CPACK_PACKAGE_NAME", cpackProjectName.c_str()); + } + if ( !cpackProjectVersion.empty() ) + { + globalMF->AddDefinition("CPACK_PACKAGE_VERSION", + cpackProjectVersion.c_str()); + } + if ( !cpackProjectVendor.empty() ) + { + globalMF->AddDefinition("CPACK_PACKAGE_VENDOR", + cpackProjectVendor.c_str()); + } + // if this is not empty it has been set on the command line + // go for it. Command line override values set in config file. + if ( !cpackProjectDirectory.empty() ) + { + globalMF->AddDefinition("CPACK_PACKAGE_DIRECTORY", + cpackProjectDirectory.c_str()); + } + // The value has not been set on the command line + else + { + // get a default value (current working directory) + cpackProjectDirectory = cmsys::SystemTools::GetCurrentWorkingDirectory(); + // use default value iff no value has been provided by the config file + if (!globalMF->IsSet("CPACK_PACKAGE_DIRECTORY")) + { + globalMF->AddDefinition("CPACK_PACKAGE_DIRECTORY", + cpackProjectDirectory.c_str()); + } + } + if ( !cpackBuildConfig.empty() ) + { + globalMF->AddDefinition("CPACK_BUILD_CONFIG", cpackBuildConfig.c_str()); + } + cpackDefinitions::MapType::iterator cdit; + for ( cdit = definitions.Map.begin(); + cdit != definitions.Map.end(); + ++cdit ) + { + globalMF->AddDefinition(cdit->first.c_str(), cdit->second.c_str()); + } + + const char* cpackModulesPath = + globalMF->GetDefinition("CPACK_MODULE_PATH"); + if ( cpackModulesPath ) + { + globalMF->AddDefinition("CMAKE_MODULE_PATH", cpackModulesPath); + } + const char* genList = globalMF->GetDefinition("CPACK_GENERATOR"); + if ( !genList ) + { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "CPack generator not specified" << std::endl); + parsed = 0; + } + else + { + std::vector<std::string> generatorsVector; + cmSystemTools::ExpandListArgument(genList, + generatorsVector); + std::vector<std::string>::iterator it; + for ( it = generatorsVector.begin(); + it != generatorsVector.end(); + ++it ) + { + const char* gen = it->c_str(); + cmMakefile newMF(*globalMF); + cmMakefile* mf = &newMF; + cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, + "Specified generator: " << gen << std::endl); + if ( parsed && !mf->GetDefinition("CPACK_PACKAGE_NAME") ) + { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "CPack project name not specified" << std::endl); + parsed = 0; + } + if (parsed && + !(mf->GetDefinition("CPACK_PACKAGE_VERSION") || + (mf->GetDefinition("CPACK_PACKAGE_VERSION_MAJOR") && + mf->GetDefinition("CPACK_PACKAGE_VERSION_MINOR") && + mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH")))) + { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "CPack project version not specified" << std::endl + << "Specify CPACK_PACKAGE_VERSION, or " + "CPACK_PACKAGE_VERSION_MAJOR, " + "CPACK_PACKAGE_VERSION_MINOR, and CPACK_PACKAGE_VERSION_PATCH." + << std::endl); + parsed = 0; + } + if ( parsed ) + { + cpackGenerator = generators.NewGenerator(gen); + if ( !cpackGenerator ) + { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Cannot initialize CPack generator: " + << gen << std::endl); + parsed = 0; + } + if ( parsed && !cpackGenerator->Initialize(gen, mf) ) + { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Cannot initialize the generator " << gen << std::endl); + parsed = 0; + } + + if ( !mf->GetDefinition("CPACK_INSTALL_COMMANDS") && + !mf->GetDefinition("CPACK_INSTALLED_DIRECTORIES") && + !mf->GetDefinition("CPACK_INSTALL_CMAKE_PROJECTS") ) + { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Please specify build tree of the project that uses CMake " + "using CPACK_INSTALL_CMAKE_PROJECTS, specify " + "CPACK_INSTALL_COMMANDS, or specify " + "CPACK_INSTALLED_DIRECTORIES." + << std::endl); + parsed = 0; + } + if ( parsed ) + { +#ifdef _WIN32 + std::string comspec = "cmw9xcom.exe"; + cmSystemTools::SetWindows9xComspecSubstitute(comspec.c_str()); +#endif + + const char* projName = mf->GetDefinition("CPACK_PACKAGE_NAME"); + cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, "Use generator: " + << cpackGenerator->GetNameOfClass() << std::endl); + cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, "For project: " + << projName << std::endl); + + const char* projVersion = + mf->GetDefinition("CPACK_PACKAGE_VERSION"); + if ( !projVersion ) + { + const char* projVersionMajor + = mf->GetDefinition("CPACK_PACKAGE_VERSION_MAJOR"); + const char* projVersionMinor + = mf->GetDefinition("CPACK_PACKAGE_VERSION_MINOR"); + const char* projVersionPatch + = mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH"); + cmOStringStream ostr; + ostr << projVersionMajor << "." << projVersionMinor << "." + << projVersionPatch; + mf->AddDefinition("CPACK_PACKAGE_VERSION", + ostr.str().c_str()); + } + + int res = cpackGenerator->DoPackage(); + if ( !res ) + { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Error when generating package: " << projName << std::endl); + return 1; + } + } + } + } + } + } + + /* In this case we are building the documentation object + * instance in order to create appropriate structure + * in order to satisfy the appropriate --help-xxx request + */ + if ( help ) + { + // Construct and print requested documentation. + + doc.SetName("cpack"); + doc.SetSection("Name",cmDocumentationName); + doc.SetSection("Usage",cmDocumentationUsage); + doc.SetSection("Description",cmDocumentationDescription); + doc.PrependSection("Options",cmDocumentationOptions); + + // statically (in C++ code) defined variables + cmCPackDocumentVariables::DefineVariables(&cminst); + + std::vector<cmDocumentationEntry> commands; + + std::string docedFile; + std::string docPath; + cmDocumentation::documentedModulesList_t docedModList; + + docedFile = globalMF->GetModulesFile("CPack.cmake"); + if (docedFile.length()!=0) + { + docPath = cmSystemTools::GetFilenamePath(docedFile.c_str()); + doc.getDocumentedModulesListInDir(docPath,"CPack*.cmake",docedModList); + } + + // parse the files for documentation. + cmDocumentation::documentedModulesList_t::iterator docedIt; + for (docedIt = docedModList.begin(); + docedIt!= docedModList.end(); ++docedIt) + { + doc.GetStructuredDocFromFile( + (docedIt->first).c_str(), + commands,&cminst); + } + + std::map<std::string,cmDocumentationSection *> propDocs; + cminst.GetPropertiesDocumentation(propDocs); + doc.SetSections(propDocs); + cminst.GetCommandDocumentation(commands,true,false); + // statically (in C++ code) defined macros/commands + cmCPackDocumentMacros::GetMacrosDocumentation(commands); + doc.SetSection("Commands",commands); + + std::vector<cmDocumentationEntry> v; + cmCPackGeneratorFactory::DescriptionsMap::const_iterator generatorIt; + for( generatorIt = generators.GetGeneratorsList().begin(); + generatorIt != generators.GetGeneratorsList().end(); + ++ generatorIt ) + { + cmDocumentationEntry e; + e.Name = generatorIt->first.c_str(); + e.Brief = generatorIt->second.c_str(); + e.Full = ""; + v.push_back(e); + } + doc.SetSection("Generators",v); + + doc.SetSeeAlsoList(cmDocumentationSeeAlso); +#undef cout + return doc.PrintRequestedDocumentation(std::cout)? 0:1; +#define cout no_cout_use_cmCPack_Log + } + + if (cmSystemTools::GetErrorOccuredFlag()) + { + return 1; + } + + return 0; +} diff --git a/Source/CPack/cygwin.readme b/Source/CPack/cygwin.readme new file mode 100644 index 000000000..88922d3e6 --- /dev/null +++ b/Source/CPack/cygwin.readme @@ -0,0 +1,69 @@ +http://cygwin.com/setup.html + + +Need to produce two tar files: + +Source- + +- create subdirs +- copy src +- duplicate src +- configure files into duplicate src + CPack.cygwin-readme.in + CPack.cygwin-install.sh.in + CPack.setup.hint.in +- diff duplicate src and orig src +- write diff into toplevel +- create tar file call super class + +cmake-2.2.3-1 + + +1. a source release +cmake-2.2.3-2-src.tar.bz2 + +cmake-2.2.3-2.patch has cmake-2.2.3/CYGWIN-PATCHES/cmake.README cmake-2.2.3/CYGWIN-PATCHES/setup.hint +cmake-2.2.3-2.sh -> script to create cygwin release +cmake-2.2.3.tar.bz2 -> unmodified cmake sources for 2.2.3 + + + + + +2 a binary release +cmake-2.2.3-2.tar.bz2 + +normal binary release with use as the root of the tree: + +Here is the bootstrap command used: + + ${SOURCE_DIR}/bootstrap --prefix=/usr --datadir=/share/cmake-${VER} \ + --docdir=/share/doc/cmake-${VER} --mandir=/share/man + +CMAKE_DOC_DIR /share/doc/${PKG}-${VER} +CMAKE_MAN_DIR /share/man +CMAKE_DATA_DIR /share/${PKG}-${VER} + +Here is the directory stucture: + +usr/bin/cmake.exe +usr/share/doc/cmake-2.2.3/MANIFEST *** +usr/share/doc/Cygwin/cmake-2.2.3-2.README **** +usr/share/cmake-2.2.3/Modules + + + +usr/bin +usr/share/cmake-2.2.3/include +usr/share/cmake-2.2.3/Modules/Platform +usr/share/cmake-2.2.3/Modules +usr/share/cmake-2.2.3/Templates +usr/share/cmake-2.2.3 +usr/share/doc/cmake-2.2.3 +usr/share/doc/Cygwin +usr/share/doc +usr/share/man/man1 +usr/share/man +usr/share +usr + |