1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCPackDebGenerator.cxx,v $
6 Date: $Date: 2008/01/24 12:31:59 $
7 Version: $Revision: 1.20 $
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 out
<< "Maintainer: " << maintainer
<< "\n";
120 out
<< "Description: " << desc
<< "\n";
126 cmd
+= cmakeExecutable
;
127 cmd
+= "\" -E tar cfz data.tar.gz ./usr";
130 int res
= cmSystemTools::RunSingleCommand(cmd
.c_str(), &output
,
131 &retVal
, toplevel
, this->GeneratorVerbose
, 0);
133 if ( !res
|| retVal
)
135 std::string tmpFile
= this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
136 tmpFile
+= "/Deb.log";
137 cmGeneratedFileStream
ofs(tmpFile
.c_str());
138 ofs
<< "# Run command: " << cmd
.c_str() << std::endl
139 << "# Working directory: " << toplevel
<< std::endl
140 << "# Output:" << std::endl
141 << output
.c_str() << std::endl
;
142 cmCPackLogger(cmCPackLog::LOG_ERROR
, "Problem running tar command: "
143 << cmd
.c_str() << std::endl
144 << "Please check " << tmpFile
.c_str() << " for errors" << std::endl
);
148 std::string md5filename
;
149 md5filename
= toplevel
;
150 md5filename
+= "/md5sums";
152 { // the scope is needed for cmGeneratedFileStream
153 cmGeneratedFileStream
out(md5filename
.c_str());
154 std::vector
<std::string
>::const_iterator fileIt
;
155 std::string topLevelWithTrailingSlash
= toplevel
;
156 topLevelWithTrailingSlash
+= '/';
157 for ( fileIt
= files
.begin(); fileIt
!= files
.end(); ++ fileIt
)
160 cmd
+= cmakeExecutable
;
161 cmd
+= "\" -E md5sum \"";
164 //std::string output;
166 res
= cmSystemTools::RunSingleCommand(cmd
.c_str(), &output
,
167 &retVal
, toplevel
, this->GeneratorVerbose
, 0);
168 // debian md5sums entries are like this:
169 // 014f3604694729f3bf19263bac599765 usr/bin/ccmake
170 // thus strip the full path (with the trailing slash)
171 cmSystemTools::ReplaceString(output
,
172 topLevelWithTrailingSlash
.c_str(), "");
175 // each line contains a eol.
176 // Do not end the md5sum file with yet another (invalid)
181 cmd
+= cmakeExecutable
;
182 cmd
+= "\" -E tar cfz control.tar.gz ./control ./md5sums";
183 res
= cmSystemTools::RunSingleCommand(cmd
.c_str(), &output
,
184 &retVal
, toplevel
, this->GeneratorVerbose
, 0);
186 if ( !res
|| retVal
)
188 std::string tmpFile
= this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
189 tmpFile
+= "/Deb.log";
190 cmGeneratedFileStream
ofs(tmpFile
.c_str());
191 ofs
<< "# Run command: " << cmd
.c_str() << std::endl
192 << "# Working directory: " << toplevel
<< std::endl
193 << "# Output:" << std::endl
194 << output
.c_str() << std::endl
;
195 cmCPackLogger(cmCPackLog::LOG_ERROR
, "Problem running tar command: "
196 << cmd
.c_str() << std::endl
197 << "Please check " << tmpFile
.c_str() << " for errors" << std::endl
);
201 // ar -r your-package-name.deb debian-binary control.tar.gz data.tar.gz
202 // since debian packages require BSD ar (most Linux distros and even
203 // FreeBSD and NetBSD ship GNU ar) we use a copy of OpenBSD ar here.
204 std::vector
<std::string
> arFiles
;
205 std::string topLevelString
= toplevel
;
206 topLevelString
+= "/";
207 arFiles
.push_back(topLevelString
+ "debian-binary");
208 arFiles
.push_back(topLevelString
+ "control.tar.gz");
209 arFiles
.push_back(topLevelString
+ "data.tar.gz");
210 res
= ar_append(outFileName
, arFiles
);
213 std::string tmpFile
= this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
214 tmpFile
+= "/Deb.log";
215 cmGeneratedFileStream
ofs(tmpFile
.c_str());
216 ofs
<< "# Problem creating archive using: " << res
<< std::endl
;
223 // The following code is taken from OpenBSD ar:
224 // http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ar/
225 // It has been slightly modified:
226 // -return error codes instead exit() in functions
227 // -use the stdio file I/O functions instead the file descriptor based ones
228 // -merged into one cxx file
229 // -no additional options supported
230 // The coding style hasn't been modified.
233 * Copyright (c) 1990, 1993, 1994
234 * The Regents of the University of California. All rights reserved.
236 * This code is derived from software contributed to Berkeley by
237 * Hugh Smith at The University of Guelph.
239 * Redistribution and use in source and binary forms, with or without
240 * modification, are permitted provided that the following conditions
242 * 1. Redistributions of source code must retain the above copyright
243 * notice, this list of conditions and the following disclaimer.
244 * 2. Redistributions in binary form must reproduce the above copyright
245 * notice, this list of conditions and the following disclaimer in the
246 * documentation and/or other materials provided with the distribution.
247 * 3. Neither the name of the University nor the names of its contributors
248 * may be used to endorse or promote products derived from this software
249 * without specific prior written permission.
251 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
252 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
253 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
254 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
255 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
256 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
257 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
258 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
259 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
260 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
264 #include <sys/types.h>
265 #include <sys/stat.h>
271 #define ARMAG "!<arch>\n" /* ar "magic number" */
272 #define SARMAG 8 /* strlen(ARMAG); */
274 #define AR_EFMT1 "#1/" /* extended format #1 */
277 /* Header format strings. */
278 #define HDR1 "%s%-13d%-12ld%-6u%-6u%-8o%-10lld%2s"
279 #define HDR2 "%-16.16s%-12ld%-6u%-6u%-8o%-10lld%2s"
282 char ar_name
[16]; /* name */
283 char ar_date
[12]; /* modification time */
284 char ar_uid
[6]; /* user id */
285 char ar_gid
[6]; /* group id */
286 char ar_mode
[8]; /* octal file permissions */
287 char ar_size
[10]; /* size in bytes */
288 char ar_fmag
[2]; /* consistency check */
291 /* Set up file copy. */
292 #define SETCF(from, fromname, to, toname, pad) { \
294 cf.rname = fromname; \
300 /* File copy structure. */
302 FILE* rFile
; /* read file descriptor */
303 const char *rname
; /* read name */
304 FILE* wFile
; /* write file descriptor */
305 const char *wname
; /* write name */
306 #define NOPAD 0x00 /* don't pad */
307 #define WPAD 0x02 /* pad on writes */
308 unsigned int flags
; /* pad flags */
313 static const char * ar_rname(const char *path
)
315 const char *ind
= strrchr(path
, '/');
316 return (ind
) ? ind
+ 1 : path
;
321 typedef struct ar_hdr HDR
;
322 static char ar_hb
[sizeof(HDR
) + 1]; /* real header */
324 static int ar_already_written
;
327 * Copy size bytes from one file to another - taking care to handle the
328 * extra byte (for odd size files) when reading archives and writing an
329 * extra byte if necessary when adding files to archive. The length of
330 * the object is the long name plus the object itself; the variable
331 * already_written gets set if a long name was written.
333 * The padding is really unnecessary, and is almost certainly a remnant
334 * of early archive formats where the header included binary data which
335 * a PDP-11 required to start on an even byte boundary. (Or, perhaps,
336 * because 16-bit word addressed copies were faster?) Anyhow, it should
337 * have been ripped out long ago.
339 static int copy_ar(CF
*cfp
, off_t size
)
341 static char pad
= '\n';
349 FILE* from
= cfp
->rFile
;
350 FILE* to
= cfp
->wFile
;
352 (nr
= fread(buf
, 1, sz
< static_cast<off_t
>(sizeof(buf
))
353 ? static_cast<size_t>(sz
) : sizeof(buf
), from
))
356 for (size_t off
= 0; off
< nr
; nr
-= off
, off
+= nw
)
357 if ((nw
= fwrite(buf
+ off
, 1, nr
, to
)) < nr
)
363 if (cfp
->flags
& WPAD
&& (size
+ ar_already_written
) & 1
364 && fwrite(&pad
, 1, 1, to
) != 1)
370 /* put_arobj -- Write an archive member to a file. */
371 static int put_arobj(CF
*cfp
, struct stat
*sb
)
376 /* If passed an sb structure, reading a file from disk. Get stat(2)
377 * information, build a name and construct a header. (Files are named
378 * by their last component in the archive.) */
379 const char* name
= ar_rname(cfp
->rname
);
380 (void)stat(cfp
->rname
, sb
);
382 /* If not truncating names and the name is too long or contains
383 * a space, use extended format 1. */
384 unsigned int lname
= strlen(name
);
385 uid_t uid
= sb
->st_uid
;
386 gid_t gid
= sb
->st_gid
;
387 if (uid
> USHRT_MAX
) {
390 if (gid
> USHRT_MAX
) {
393 if (lname
> sizeof(hdr
->ar_name
) || strchr(name
, ' '))
394 (void)sprintf(ar_hb
, HDR1
, AR_EFMT1
, lname
,
395 (long int)sb
->st_mtime
, uid
, gid
, sb
->st_mode
,
396 (long long)sb
->st_size
+ lname
, ARFMAG
);
399 (void)sprintf(ar_hb
, HDR2
, name
,
400 (long int)sb
->st_mtime
, uid
, gid
, sb
->st_mode
,
401 (long long)sb
->st_size
, ARFMAG
);
403 off_t size
= sb
->st_size
;
405 if (fwrite(ar_hb
, 1, sizeof(HDR
), cfp
->wFile
) != sizeof(HDR
))
409 if (fwrite(name
, 1, lname
, cfp
->wFile
) != lname
)
411 ar_already_written
= lname
;
413 result
= copy_ar(cfp
, size
);
414 ar_already_written
= 0;
421 * Append files to the archive - modifies original archive or creates
422 * a new archive if named archive does not exist.
424 static int ar_append(const char* archive
,const std::vector
<std::string
>& files
)
427 FILE* aFile
= fopen(archive
, "wb+");
429 fwrite(ARMAG
, SARMAG
, 1, aFile
);
430 if (fseek(aFile
, 0, SEEK_END
) != -1) {
433 /* Read from disk, write to an archive; pad on write. */
434 SETCF(NULL
, 0, aFile
, archive
, WPAD
);
435 for(std::vector
<std::string
>::const_iterator fileIt
= files
.begin();
436 fileIt
!=files
.end(); ++fileIt
) {
437 const char* filename
= fileIt
->c_str();
438 FILE* file
= fopen(filename
, "rb");
445 int result
= put_arobj(&cf
, &sb
);