cmCPackBundleGenerator.cxx 9.02 KB
Newer Older
1 2 3
/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4

5 6
  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.
7

8 9 10 11
  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.
============================================================================*/
12 13 14 15 16

#include "cmCPackBundleGenerator.h"
#include "cmCPackLog.h"
#include "cmSystemTools.h"

17 18
#include <cmsys/RegularExpression.hxx>

19 20 21 22 23 24 25 26 27 28
//----------------------------------------------------------------------
cmCPackBundleGenerator::cmCPackBundleGenerator()
{
}

//----------------------------------------------------------------------
cmCPackBundleGenerator::~cmCPackBundleGenerator()
{
}

29 30 31 32 33 34 35 36 37 38 39 40 41
//----------------------------------------------------------------------
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;
    }

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
  if(this->GetOption("CPACK_BUNDLE_APPLE_CERT_APP"))
    {
    const std::string codesign_path = cmSystemTools::FindProgram("codesign",
       std::vector<std::string>(), false);

    if(codesign_path.empty())
      {
      cmCPackLogger(cmCPackLog::LOG_ERROR,
        "Cannot locate codesign command"
        << std::endl);
      return 0;
      }
    this->SetOptionIfNotSet("CPACK_COMMAND_CODESIGN", codesign_path.c_str());
    }

57 58 59
  return this->Superclass::InitializeInternal();
}

60 61 62 63 64 65 66 67 68 69 70
//----------------------------------------------------------------------
const char* cmCPackBundleGenerator::GetPackagingInstallPrefix()
{
  this->InstallPrefix = "/";
  this->InstallPrefix += this->GetOption("CPACK_BUNDLE_NAME");
  this->InstallPrefix += ".app/Contents/Resources";

  return this->InstallPrefix.c_str();
}

//----------------------------------------------------------------------
71
int cmCPackBundleGenerator::ConstructBundle()
72
{
73

74
  // Get required arguments ...
75 76
  const std::string cpack_bundle_name = this->GetOption("CPACK_BUNDLE_NAME")
    ? this->GetOption("CPACK_BUNDLE_NAME") : "";
77 78 79 80 81 82 83 84 85
  if(cpack_bundle_name.empty())
    {
    cmCPackLogger(cmCPackLog::LOG_ERROR,
      "CPACK_BUNDLE_NAME must be set."
      << std::endl);

    return 0;
    }

86 87
  const std::string cpack_bundle_plist = this->GetOption("CPACK_BUNDLE_PLIST")
    ? this->GetOption("CPACK_BUNDLE_PLIST") : "";
88 89 90 91 92 93 94 95 96
  if(cpack_bundle_plist.empty())
    {
    cmCPackLogger(cmCPackLog::LOG_ERROR,
      "CPACK_BUNDLE_PLIST must be set."
      << std::endl);

    return 0;
    }

97 98
  const std::string cpack_bundle_icon = this->GetOption("CPACK_BUNDLE_ICON")
    ? this->GetOption("CPACK_BUNDLE_ICON") : "";
99 100 101 102 103 104 105 106 107 108
  if(cpack_bundle_icon.empty())
    {
    cmCPackLogger(cmCPackLog::LOG_ERROR,
      "CPACK_BUNDLE_ICON must be set."
      << std::endl);

    return 0;
    }

  // Get optional arguments ...
109 110
  const std::string cpack_bundle_startup_command =
    this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND")
111 112
    ? this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND") : "";

113 114
  // The staging directory contains everything that will end-up inside the
  // final disk image ...
115
  std::ostringstream staging;
116 117
  staging << toplevel;

118
  std::ostringstream contents;
119
  contents << staging.str() << "/" << cpack_bundle_name
120 121
    << ".app/" << "Contents";

122
  std::ostringstream application;
123 124
  application << contents.str() << "/" << "MacOS";

125
  std::ostringstream resources;
126 127
  resources << contents.str() << "/" << "Resources";

128
  // Install a required, user-provided bundle metadata file ...
129
  std::ostringstream plist_source;
130
  plist_source << cpack_bundle_plist;
131

132
  std::ostringstream plist_target;
133
  plist_target << contents.str() << "/" << "Info.plist";
134

135 136 137 138 139 140 141
  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;
142 143 144
    }

  // Install a user-provided bundle icon ...
145
  std::ostringstream icon_source;
146
  icon_source << cpack_bundle_icon;
147

148
  std::ostringstream icon_target;
149
  icon_target << resources.str() << "/" << cpack_bundle_name << ".icns";
150

151 152 153 154 155 156 157
  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;
158 159
    }

160 161 162 163
  // Optionally a user-provided startup command (could be an
  // executable or a script) ...
  if(!cpack_bundle_startup_command.empty())
    {
164
    std::ostringstream command_source;
165
    command_source << cpack_bundle_startup_command;
166

167
    std::ostringstream command_target;
168
    command_target << application.str() << "/" << cpack_bundle_name;
169

170 171 172 173 174 175
    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);
176

177 178
      return 0;
      }
179

180 181
    cmSystemTools::SetPermissions(command_target.str().c_str(), 0777);
    }
182

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
  return 1;
}

//----------------------------------------------------------------------
int cmCPackBundleGenerator::PackageFiles()
{
  if(!this->ConstructBundle())
    {
    return 0;
    }

  if(!this->SignBundle(toplevel))
    {
    return 0;
    }

199 200 201 202 203 204
  return this->CreateDMG(toplevel, packageFileNames[0]);
}

bool cmCPackBundleGenerator::SupportsComponentInstallation() const
{
  return false;
205
}
206 207 208 209 210 211 212 213 214 215 216


int cmCPackBundleGenerator::SignBundle(const std::string& src_dir)
{
  const std::string cpack_apple_cert_app =
    this->GetOption("CPACK_BUNDLE_APPLE_CERT_APP")
    ? this->GetOption("CPACK_BUNDLE_APPLE_CERT_APP") : "";

  // codesign the application.
  if(!cpack_apple_cert_app.empty())
    {
217
    std::string output;
218 219 220 221 222 223
    std::string bundle_path;
    bundle_path = src_dir + "/";
    bundle_path += this->GetOption("CPACK_BUNDLE_NAME");
    bundle_path += ".app";

    // A list of additional files to sign, ie. frameworks and plugins.
224 225 226 227 228
    const std::string sign_parameter =
      this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_PARAMETER")
      ? this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_PARAMETER")
      : "--deep -f";

229 230 231 232 233 234 235 236 237 238 239
    const std::string sign_files =
      this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_FILES")
      ? this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_FILES") : "";

    std::vector<std::string> relFiles;
    cmSystemTools::ExpandListArgument(sign_files, relFiles);

    // sign the files supplied by the user, ie. frameworks.
    for(std::vector<std::string>::iterator it = relFiles.begin();
      it != relFiles.end(); ++it)
      {
240
      std::ostringstream temp_sign_file_cmd;
241
      temp_sign_file_cmd << this->GetOption("CPACK_COMMAND_CODESIGN");
242 243
      temp_sign_file_cmd << " " << sign_parameter << " -s \""
                         << cpack_apple_cert_app;
244 245 246 247 248 249
      temp_sign_file_cmd << "\" -i ";
      temp_sign_file_cmd << this->GetOption("CPACK_APPLE_BUNDLE_ID");
      temp_sign_file_cmd << " \"";
      temp_sign_file_cmd << bundle_path;
      temp_sign_file_cmd << it->c_str() << "\"";

250
      if(!this->RunCommand(temp_sign_file_cmd, &output))
251 252 253
        {
        cmCPackLogger(cmCPackLog::LOG_ERROR,
          "Error signing file:"
254
          << bundle_path << it->c_str() << std::endl << output << std::endl);
255 256 257 258 259 260

        return 0;
        }
      }

    // sign main binary
261
    std::ostringstream temp_sign_binary_cmd;
262
    temp_sign_binary_cmd << this->GetOption("CPACK_COMMAND_CODESIGN");
263 264
    temp_sign_binary_cmd << " " << sign_parameter << " -s \""
                         << cpack_apple_cert_app;
265 266
    temp_sign_binary_cmd << "\" \"" << bundle_path << "\"";

267
    if(!this->RunCommand(temp_sign_binary_cmd, &output))
268 269 270
      {
      cmCPackLogger(cmCPackLog::LOG_ERROR,
        "Error signing the application binary."
271
        << std::endl << output << std::endl);
272 273 274 275 276

      return 0;
      }

    // sign app bundle
277
    std::ostringstream temp_codesign_cmd;
278
    temp_codesign_cmd << this->GetOption("CPACK_COMMAND_CODESIGN");
279 280
    temp_codesign_cmd << " " << sign_parameter << " -s \""
                      << cpack_apple_cert_app << "\"";
281 282 283 284 285 286 287
    if(this->GetOption("CPACK_BUNDLE_APPLE_ENTITLEMENTS"))
      {
      temp_codesign_cmd << " --entitlements ";
      temp_codesign_cmd << this->GetOption("CPACK_BUNDLE_APPLE_ENTITLEMENTS");
      }
    temp_codesign_cmd << " \"" << bundle_path << "\"";

288
    if(!this->RunCommand(temp_codesign_cmd, &output))
289 290 291
      {
      cmCPackLogger(cmCPackLog::LOG_ERROR,
        "Error signing the application package."
292
        << std::endl << output << std::endl);
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307

      return 0;
      }

    cmCPackLogger(cmCPackLog::LOG_OUTPUT,
      "- Application has been codesigned"
      << std::endl);
    cmCPackLogger(cmCPackLog::LOG_VERBOSE,
       (this->GetOption("CPACK_BUNDLE_APPLE_ENTITLEMENTS")
         ? "with entitlement sandboxing" : "without entitlement sandboxing")
       << std::endl);
  }

  return 1;
}