Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Source / CPack / cmCPackDebGenerator.cxx
blob0ab539a02533abd1069aff8f3b39c3245d8bda59
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCPackDebGenerator.cxx,v $
5 Language: C++
6 Date: $Date: 2008-09-11 14:48:49 $
7 Version: $Revision: 1.23 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmCPackDebGenerator.h"
19 #include "cmSystemTools.h"
20 #include "cmMakefile.h"
21 #include "cmGeneratedFileStream.h"
22 #include "cmCPackLog.h"
24 #include <cmsys/SystemTools.hxx>
25 #include <cmsys/Glob.hxx>
27 #include <limits.h> // USHRT_MAX
29 // NOTE:
30 // A debian package .deb is simply an 'ar' archive. The only subtle difference
31 // is that debian uses the BSD ar style archive whereas most Linux distro have
32 // a GNU ar.
33 // See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=161593 for more info
34 // Therefore we provide our own implementation of a BSD-ar:
35 static int ar_append(const char*archive,const std::vector<std::string>& files);
37 //----------------------------------------------------------------------
38 cmCPackDebGenerator::cmCPackDebGenerator()
42 //----------------------------------------------------------------------
43 cmCPackDebGenerator::~cmCPackDebGenerator()
47 //----------------------------------------------------------------------
48 int cmCPackDebGenerator::InitializeInternal()
50 this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");
52 return this->Superclass::InitializeInternal();
55 //----------------------------------------------------------------------
56 int cmCPackDebGenerator::CompressFiles(const char* outFileName,
57 const char* toplevel,
58 const std::vector<std::string>& files)
60 this->ReadListFile("CPackDeb.cmake");
61 const char* cmakeExecutable = this->GetOption("CMAKE_COMMAND");
63 // debian-binary file
64 std::string dbfilename;
65 dbfilename = toplevel;
66 dbfilename += "/debian-binary";
67 { // the scope is needed for cmGeneratedFileStream
68 cmGeneratedFileStream out(dbfilename.c_str());
69 out << "2.0";
70 out << std::endl; // required for valid debian package
73 // control file
74 std::string ctlfilename;
75 ctlfilename = toplevel;
76 ctlfilename += "/control";
78 // debian policy enforce lower case for package name
79 // mandatory entries:
80 std::string debian_pkg_name = cmsys::SystemTools::LowerCase(
81 this->GetOption("CPACK_DEBIAN_PACKAGE_NAME") );
82 const char* debian_pkg_version =
83 this->GetOption("CPACK_DEBIAN_PACKAGE_VERSION");
84 const char* debian_pkg_section =
85 this->GetOption("CPACK_DEBIAN_PACKAGE_SECTION");
86 const char* debian_pkg_priority =
87 this->GetOption("CPACK_DEBIAN_PACKAGE_PRIORITY");
88 const char* debian_pkg_arch =
89 this->GetOption("CPACK_DEBIAN_PACKAGE_ARCHITECTURE");
90 const char* maintainer = this->GetOption("CPACK_DEBIAN_PACKAGE_MAINTAINER");
91 const char* desc = this->GetOption("CPACK_DEBIAN_PACKAGE_DESCRIPTION");
93 // optional entries
94 const char* debian_pkg_dep = this->GetOption("CPACK_DEBIAN_PACKAGE_DEPENDS");
95 const char* debian_pkg_rec =
96 this->GetOption("CPACK_DEBIAN_PACKAGE_RECOMMENDS");
97 const char* debian_pkg_sug =
98 this->GetOption("CPACK_DEBIAN_PACKAGE_SUGGESTS");
100 { // the scope is needed for cmGeneratedFileStream
101 cmGeneratedFileStream out(ctlfilename.c_str());
102 out << "Package: " << debian_pkg_name << "\n";
103 out << "Version: " << debian_pkg_version << "\n";
104 out << "Section: " << debian_pkg_section << "\n";
105 out << "Priority: " << debian_pkg_priority << "\n";
106 out << "Architecture: " << debian_pkg_arch << "\n";
107 if(debian_pkg_dep)
109 out << "Depends: " << debian_pkg_dep << "\n";
111 if(debian_pkg_rec)
113 out << "Recommends: " << debian_pkg_rec << "\n";
115 if(debian_pkg_sug)
117 out << "Suggests: " << debian_pkg_sug << "\n";
119 unsigned long totalSize = 0;
121 std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
122 dirName += '/';
123 for (std::vector<std::string>::const_iterator fileIt = files.begin();
124 fileIt != files.end(); ++ fileIt )
126 totalSize += cmSystemTools::FileLength(fileIt->c_str());
129 out << "Installed-Size: " << totalSize << "\n";
130 out << "Maintainer: " << maintainer << "\n";
131 out << "Description: " << desc << "\n";
132 out << std::endl;
135 std::string cmd;
136 cmd = "\"";
137 cmd += cmakeExecutable;
138 cmd += "\" -E tar cfz data.tar.gz ";
140 // now add all directories which have to be compressed
141 // collect all top level install dirs for that
142 // e.g. /opt/bin/foo, /usr/bin/bar and /usr/bin/baz would give /usr and /opt
143 int topLevelLength = strlen(toplevel);
144 std::set<std::string> installDirs;
145 for (std::vector<std::string>::const_iterator fileIt = files.begin();
146 fileIt != files.end(); ++ fileIt )
148 std::string::size_type slashPos = fileIt->find('/', topLevelLength+1);
149 std::string relativeDir = fileIt->substr(topLevelLength,
150 slashPos - topLevelLength);
151 if (installDirs.find(relativeDir) == installDirs.end())
153 installDirs.insert(relativeDir);
154 cmd += " .";
155 cmd += relativeDir;
159 std::string output;
160 int retVal = -1;
161 int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output,
162 &retVal, toplevel, this->GeneratorVerbose, 0);
164 if ( !res || retVal )
166 std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
167 tmpFile += "/Deb.log";
168 cmGeneratedFileStream ofs(tmpFile.c_str());
169 ofs << "# Run command: " << cmd.c_str() << std::endl
170 << "# Working directory: " << toplevel << std::endl
171 << "# Output:" << std::endl
172 << output.c_str() << std::endl;
173 cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running tar command: "
174 << cmd.c_str() << std::endl
175 << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
176 return 0;
179 std::string md5filename;
180 md5filename = toplevel;
181 md5filename += "/md5sums";
183 { // the scope is needed for cmGeneratedFileStream
184 cmGeneratedFileStream out(md5filename.c_str());
185 std::vector<std::string>::const_iterator fileIt;
186 std::string topLevelWithTrailingSlash = toplevel;
187 topLevelWithTrailingSlash += '/';
188 for ( fileIt = files.begin(); fileIt != files.end(); ++ fileIt )
190 cmd = "\"";
191 cmd += cmakeExecutable;
192 cmd += "\" -E md5sum \"";
193 cmd += *fileIt;
194 cmd += "\"";
195 //std::string output;
196 //int retVal = -1;
197 res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output,
198 &retVal, toplevel, this->GeneratorVerbose, 0);
199 // debian md5sums entries are like this:
200 // 014f3604694729f3bf19263bac599765 usr/bin/ccmake
201 // thus strip the full path (with the trailing slash)
202 cmSystemTools::ReplaceString(output,
203 topLevelWithTrailingSlash.c_str(), "");
204 out << output;
206 // each line contains a eol.
207 // Do not end the md5sum file with yet another (invalid)
211 cmd = "\"";
212 cmd += cmakeExecutable;
213 cmd += "\" -E tar cfz control.tar.gz ./control ./md5sums";
214 const char* controlExtra =
215 this->GetOption("CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA");
216 if( controlExtra )
218 std::vector<std::string> controlExtraList;
219 cmSystemTools::ExpandListArgument(controlExtra, controlExtraList);
220 for(std::vector<std::string>::iterator i =
221 controlExtraList.begin(); i != controlExtraList.end(); ++i)
223 std::string filenamename =
224 cmsys::SystemTools::GetFilenameName(i->c_str());
225 std::string localcopy = toplevel;
226 localcopy += "/";
227 localcopy += filenamename;
228 // if we can copy the file, it means it does exist, let's add it:
229 if( cmsys::SystemTools::CopyFileIfDifferent(
230 i->c_str(), localcopy.c_str()) )
232 // debian is picky and need relative to ./ path in the tar.gz
233 cmd += " ./";
234 cmd += filenamename;
238 res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output,
239 &retVal, toplevel, this->GeneratorVerbose, 0);
241 if ( !res || retVal )
243 std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
244 tmpFile += "/Deb.log";
245 cmGeneratedFileStream ofs(tmpFile.c_str());
246 ofs << "# Run command: " << cmd.c_str() << std::endl
247 << "# Working directory: " << toplevel << std::endl
248 << "# Output:" << std::endl
249 << output.c_str() << std::endl;
250 cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running tar command: "
251 << cmd.c_str() << std::endl
252 << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
253 return 0;
256 // ar -r your-package-name.deb debian-binary control.tar.gz data.tar.gz
257 // since debian packages require BSD ar (most Linux distros and even
258 // FreeBSD and NetBSD ship GNU ar) we use a copy of OpenBSD ar here.
259 std::vector<std::string> arFiles;
260 std::string topLevelString = toplevel;
261 topLevelString += "/";
262 arFiles.push_back(topLevelString + "debian-binary");
263 arFiles.push_back(topLevelString + "control.tar.gz");
264 arFiles.push_back(topLevelString + "data.tar.gz");
265 res = ar_append(outFileName, arFiles);
266 if ( res!=0 )
268 std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
269 tmpFile += "/Deb.log";
270 cmGeneratedFileStream ofs(tmpFile.c_str());
271 ofs << "# Problem creating archive using: " << res << std::endl;
272 return 0;
275 return 1;
278 // The following code is taken from OpenBSD ar:
279 // http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ar/
280 // It has been slightly modified:
281 // -return error codes instead exit() in functions
282 // -use the stdio file I/O functions instead the file descriptor based ones
283 // -merged into one cxx file
284 // -no additional options supported
285 // The coding style hasn't been modified.
288 * Copyright (c) 1990, 1993, 1994
289 * The Regents of the University of California. All rights reserved.
291 * This code is derived from software contributed to Berkeley by
292 * Hugh Smith at The University of Guelph.
294 * Redistribution and use in source and binary forms, with or without
295 * modification, are permitted provided that the following conditions
296 * are met:
297 * 1. Redistributions of source code must retain the above copyright
298 * notice, this list of conditions and the following disclaimer.
299 * 2. Redistributions in binary form must reproduce the above copyright
300 * notice, this list of conditions and the following disclaimer in the
301 * documentation and/or other materials provided with the distribution.
302 * 3. Neither the name of the University nor the names of its contributors
303 * may be used to endorse or promote products derived from this software
304 * without specific prior written permission.
306 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
307 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
308 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
309 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
310 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
311 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
312 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
313 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
314 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
315 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
316 * SUCH DAMAGE.
319 #include <sys/types.h>
320 #include <sys/stat.h>
322 #include <stdio.h>
323 #include <string.h>
324 #include <stdlib.h>
326 #define ARMAG "!<arch>\n" /* ar "magic number" */
327 #define SARMAG 8 /* strlen(ARMAG); */
329 #define AR_EFMT1 "#1/" /* extended format #1 */
330 #define ARFMAG "`\n"
332 /* Header format strings. */
333 #define HDR1 "%s%-13d%-12ld%-6u%-6u%-8o%-10lld%2s"
334 #define HDR2 "%-16.16s%-12ld%-6u%-6u%-8o%-10lld%2s"
336 struct ar_hdr {
337 char ar_name[16]; /* name */
338 char ar_date[12]; /* modification time */
339 char ar_uid[6]; /* user id */
340 char ar_gid[6]; /* group id */
341 char ar_mode[8]; /* octal file permissions */
342 char ar_size[10]; /* size in bytes */
343 char ar_fmag[2]; /* consistency check */
346 /* Set up file copy. */
347 #define SETCF(from, fromname, to, toname, pad) { \
348 cf.rFile = from; \
349 cf.rname = fromname; \
350 cf.wFile = to; \
351 cf.wname = toname; \
352 cf.flags = pad; \
355 /* File copy structure. */
356 typedef struct {
357 FILE* rFile; /* read file descriptor */
358 const char *rname; /* read name */
359 FILE* wFile; /* write file descriptor */
360 const char *wname; /* write name */
361 #define NOPAD 0x00 /* don't pad */
362 #define WPAD 0x02 /* pad on writes */
363 unsigned int flags; /* pad flags */
364 } CF;
366 /* misc.c */
368 static const char * ar_rname(const char *path)
370 const char *ind = strrchr(path, '/');
371 return (ind ) ? ind + 1 : path;
374 /* archive.c */
376 typedef struct ar_hdr HDR;
377 static char ar_hb[sizeof(HDR) + 1]; /* real header */
379 static int ar_already_written;
381 /* copy_ar --
382 * Copy size bytes from one file to another - taking care to handle the
383 * extra byte (for odd size files) when reading archives and writing an
384 * extra byte if necessary when adding files to archive. The length of
385 * the object is the long name plus the object itself; the variable
386 * already_written gets set if a long name was written.
388 * The padding is really unnecessary, and is almost certainly a remnant
389 * of early archive formats where the header included binary data which
390 * a PDP-11 required to start on an even byte boundary. (Or, perhaps,
391 * because 16-bit word addressed copies were faster?) Anyhow, it should
392 * have been ripped out long ago.
394 static int copy_ar(CF *cfp, off_t size)
396 static char pad = '\n';
397 off_t sz = size;
398 size_t nr, nw;
399 char buf[8*1024];
401 if (sz == 0)
402 return 0;
404 FILE* from = cfp->rFile;
405 FILE* to = cfp->wFile;
406 while (sz &&
407 (nr = fread(buf, 1, sz < static_cast<off_t>(sizeof(buf))
408 ? static_cast<size_t>(sz) : sizeof(buf), from ))
409 > 0) {
410 sz -= nr;
411 for (size_t off = 0; off < nr; nr -= off, off += nw)
412 if ((nw = fwrite(buf + off, 1, nr, to)) < nr)
413 return -1;
415 if (sz)
416 return -2;
418 if (cfp->flags & WPAD && (size + ar_already_written) & 1
419 && fwrite(&pad, 1, 1, to) != 1)
420 return -4;
422 return 0;
425 /* put_arobj -- Write an archive member to a file. */
426 static int put_arobj(CF *cfp, struct stat *sb)
428 int result = 0;
429 struct ar_hdr *hdr;
431 /* If passed an sb structure, reading a file from disk. Get stat(2)
432 * information, build a name and construct a header. (Files are named
433 * by their last component in the archive.) */
434 const char* name = ar_rname(cfp->rname);
435 (void)stat(cfp->rname, sb);
437 /* If not truncating names and the name is too long or contains
438 * a space, use extended format 1. */
439 unsigned int lname = strlen(name);
440 uid_t uid = sb->st_uid;
441 gid_t gid = sb->st_gid;
442 if (uid > USHRT_MAX) {
443 uid = USHRT_MAX;
445 if (gid > USHRT_MAX) {
446 gid = USHRT_MAX;
448 if (lname > sizeof(hdr->ar_name) || strchr(name, ' '))
449 (void)sprintf(ar_hb, HDR1, AR_EFMT1, lname,
450 (long int)sb->st_mtime, uid, gid, sb->st_mode,
451 (long long)sb->st_size + lname, ARFMAG);
452 else {
453 lname = 0;
454 (void)sprintf(ar_hb, HDR2, name,
455 (long int)sb->st_mtime, uid, gid, sb->st_mode,
456 (long long)sb->st_size, ARFMAG);
458 off_t size = sb->st_size;
460 if (fwrite(ar_hb, 1, sizeof(HDR), cfp->wFile) != sizeof(HDR))
461 return -1;
463 if (lname) {
464 if (fwrite(name, 1, lname, cfp->wFile) != lname)
465 return -2;
466 ar_already_written = lname;
468 result = copy_ar(cfp, size);
469 ar_already_written = 0;
470 return result;
473 /* append.c */
475 /* append --
476 * Append files to the archive - modifies original archive or creates
477 * a new archive if named archive does not exist.
479 static int ar_append(const char* archive,const std::vector<std::string>& files)
481 int eval = 0;
482 FILE* aFile = fopen(archive, "wb+");
483 if (aFile!=NULL) {
484 fwrite(ARMAG, SARMAG, 1, aFile);
485 if (fseek(aFile, 0, SEEK_END) != -1) {
486 CF cf;
487 struct stat sb;
488 /* Read from disk, write to an archive; pad on write. */
489 SETCF(NULL, 0, aFile, archive, WPAD);
490 for(std::vector<std::string>::const_iterator fileIt = files.begin();
491 fileIt!=files.end(); ++fileIt) {
492 const char* filename = fileIt->c_str();
493 FILE* file = fopen(filename, "rb");
494 if (file == NULL) {
495 eval = -1;
496 continue;
498 cf.rFile = file;
499 cf.rname = filename;
500 int result = put_arobj(&cf, &sb);
501 (void)fclose(file);
502 if (result!=0) {
503 eval = -2;
504 break;
508 else {
509 eval = -3;
511 fclose(aFile);
513 else {
514 eval = -4;
516 return eval;