1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCPackDebGenerator.cxx,v $
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
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
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
,
58 const std::vector
<std::string
>& files
)
60 this->ReadListFile("CPackDeb.cmake");
61 const char* cmakeExecutable
= this->GetOption("CMAKE_COMMAND");
64 std::string dbfilename
;
65 dbfilename
= toplevel
;
66 dbfilename
+= "/debian-binary";
67 { // the scope is needed for cmGeneratedFileStream
68 cmGeneratedFileStream
out(dbfilename
.c_str());
70 out
<< std::endl
; // required for valid debian package
74 std::string ctlfilename
;
75 ctlfilename
= toplevel
;
76 ctlfilename
+= "/control";
78 // debian policy enforce lower case for package name
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");
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";
109 out
<< "Depends: " << debian_pkg_dep
<< "\n";
113 out
<< "Recommends: " << debian_pkg_rec
<< "\n";
117 out
<< "Suggests: " << debian_pkg_sug
<< "\n";
119 unsigned long totalSize
= 0;
121 std::string dirName
= this->GetOption("CPACK_TEMPORARY_DIRECTORY");
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";
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
);
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
);
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
)
191 cmd
+= cmakeExecutable
;
192 cmd
+= "\" -E md5sum \"";
195 //std::string output;
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(), "");
206 // each line contains a eol.
207 // Do not end the md5sum file with yet another (invalid)
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");
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
;
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
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
);
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
);
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
;
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
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
319 #include <sys/types.h>
320 #include <sys/stat.h>
326 #define ARMAG "!<arch>\n" /* ar "magic number" */
327 #define SARMAG 8 /* strlen(ARMAG); */
329 #define AR_EFMT1 "#1/" /* extended format #1 */
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"
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) { \
349 cf.rname = fromname; \
355 /* File copy structure. */
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 */
368 static const char * ar_rname(const char *path
)
370 const char *ind
= strrchr(path
, '/');
371 return (ind
) ? ind
+ 1 : path
;
376 typedef struct ar_hdr HDR
;
377 static char ar_hb
[sizeof(HDR
) + 1]; /* real header */
379 static int ar_already_written
;
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';
404 FILE* from
= cfp
->rFile
;
405 FILE* to
= cfp
->wFile
;
407 (nr
= fread(buf
, 1, sz
< static_cast<off_t
>(sizeof(buf
))
408 ? static_cast<size_t>(sz
) : sizeof(buf
), from
))
411 for (size_t off
= 0; off
< nr
; nr
-= off
, off
+= nw
)
412 if ((nw
= fwrite(buf
+ off
, 1, nr
, to
)) < nr
)
418 if (cfp
->flags
& WPAD
&& (size
+ ar_already_written
) & 1
419 && fwrite(&pad
, 1, 1, to
) != 1)
425 /* put_arobj -- Write an archive member to a file. */
426 static int put_arobj(CF
*cfp
, struct stat
*sb
)
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
) {
445 if (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
);
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
))
464 if (fwrite(name
, 1, lname
, cfp
->wFile
) != lname
)
466 ar_already_written
= lname
;
468 result
= copy_ar(cfp
, size
);
469 ar_already_written
= 0;
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
)
482 FILE* aFile
= fopen(archive
, "wb+");
484 fwrite(ARMAG
, SARMAG
, 1, aFile
);
485 if (fseek(aFile
, 0, SEEK_END
) != -1) {
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");
500 int result
= put_arobj(&cf
, &sb
);