From dfbd4ce0f2ec91689566a8c965d7be5a6833af56 Mon Sep 17 00:00:00 2001 From: Alexander Neundorf Date: Tue, 14 Aug 2007 08:40:40 -0400 Subject: ENH: deb generator: don't use the system provided ar, but do it yourself using the code from OpenBSD ar COMP: don't build all package generators on all platforms Alex --- Modules/CPackDeb.cmake | 7 - Source/CMakeLists.txt | 28 +++- Source/CPack/cmCPackDebGenerator.cxx | 274 ++++++++++++++++++++++++++++++++--- Source/CPack/cmCPackDebGenerator.h | 3 +- 4 files changed, 274 insertions(+), 38 deletions(-) diff --git a/Modules/CPackDeb.cmake b/Modules/CPackDeb.cmake index a2b8261..64466fd 100644 --- a/Modules/CPackDeb.cmake +++ b/Modules/CPackDeb.cmake @@ -12,13 +12,6 @@ IF(NOT UNIX) MESSAGE(FATAL_ERROR "CPackDeb.cmake may only be used under UNIX.") ENDIF(NOT UNIX) -FIND_PROGRAM(AR_EXECUTABLE ar) - -IF(NOT AR_EXECUTABLE) - # Is there a *NIX out there without ar ? - MESSAGE(FATAL_ERROR "Debian package requires ar executable") -ENDIF(NOT AR_EXECUTABLE) - # Let's define the control file found in debian package: # Binary package: diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 1ca897c..8496bc3 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -295,18 +295,34 @@ SET(CPACK_SRCS CPack/cmCPackGenericGenerator.cxx CPack/cmCPackLog.cxx CPack/cmCPackNSISGenerator.cxx - CPack/cmCPackOSXX11Generator.cxx - CPack/cmCPackPackageMakerGenerator.cxx CPack/cmCPackSTGZGenerator.cxx CPack/cmCPackTGZGenerator.cxx CPack/cmCPackTarBZip2Generator.cxx CPack/cmCPackTarCompressGenerator.cxx CPack/cmCPackZIPGenerator.cxx - CPack/cmCPackCygwinBinaryGenerator.cxx - CPack/cmCPackCygwinSourceGenerator.cxx - CPack/cmCPackDebGenerator.cxx - CPack/cmCPackRPMGenerator.cxx ) + +IF(WIN32 AND UNIX) + SET(CPACK_SRCS ${CPACK_SRCS} + CPack/cmCPackCygwinBinaryGenerator.cxx + CPack/cmCPackCygwinSourceGenerator.cxx + ) +ENDIF(WIN32 AND UNIX) + +IF(UNIX) + SET(CPACK_SRCS ${CPACK_SRCS} + CPack/cmCPackDebGenerator.cxx + CPack/cmCPackRPMGenerator.cxx + ) +ENDIF(UNIX) + +IF(APPLE) + SET(CPACK_SRCS ${CPACK_SRCS} + CPack/cmCPackOSXX11Generator.cxx + CPack/cmCPackPackageMakerGenerator.cxx + ) +ENDIF(APPLE) + # Build CPackLib ADD_LIBRARY(CPackLib ${CPACK_SRCS}) TARGET_LINK_LIBRARIES(CPackLib CMakeLib) diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx index a2cdf51..fb799d0 100644 --- a/Source/CPack/cmCPackDebGenerator.cxx +++ b/Source/CPack/cmCPackDebGenerator.cxx @@ -24,6 +24,9 @@ #include #include + +static int ar_append(const char*archive,const std::vector& files); + //---------------------------------------------------------------------- cmCPackDebGenerator::cmCPackDebGenerator() { @@ -39,7 +42,7 @@ int cmCPackDebGenerator::CompressFiles(const char* outFileName, const char* toplevel, const std::vector& files) { - const char* arExecutable = this->GetOption("AR_EXECUTABLE"); + this->ReadListFile("CPackDeb.cmake"); const char* cmakeExecutable = this->GetOption("CMAKE_COMMAND"); // debian-binary file @@ -132,8 +135,9 @@ int cmCPackDebGenerator::CompressFiles(const char* outFileName, for ( fileIt = files.begin(); fileIt != files.end(); ++ fileIt ) { cmd = cmakeExecutable; - cmd += " -E md5sum "; + cmd += " -E md5sum \""; cmd += *fileIt; + cmd += "\""; //std::string output; //int retVal = -1; res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, @@ -171,39 +175,263 @@ int cmCPackDebGenerator::CompressFiles(const char* outFileName, } // ar -r your-package-name.deb debian-binary control.tar.gz data.tar.gz - cmd = arExecutable; - cmd += " -r \""; - cmd += outFileName; - cmd += "\" debian-binary control.tar.gz data.tar.gz"; - res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, - &retVal, toplevel, this->GeneratorVerbose, 0); - - if ( !res || retVal ) + // 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 arFiles; + std::string topLevelString = toplevel; + topLevelString += "/"; + arFiles.push_back(topLevelString + "debian-binary"); + arFiles.push_back(topLevelString + "control.tar.gz"); + arFiles.push_back(topLevelString + "data.tar.gz"); + res = ar_append(outFileName, arFiles); + if ( res!=0 ) { std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); tmpFile += "/Deb.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 ar command: " - << cmd.c_str() << std::endl - << "Please check " << tmpFile.c_str() << " for errors" << std::endl); + ofs << "# Problem creating archive using: " << res << std::endl; return 0; } return 1; } -//---------------------------------------------------------------------- -int cmCPackDebGenerator::InitializeInternal() +// 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 +#include + +#include +#include +#include + +#define ARMAG "!\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%-10qd%2s" +#define HDR2 "%-16.16s%-12ld%-6u%-6u%-8o%-10qd%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) { - this->ReadListFile("CPackDeb.cmake"); - if (!this->IsSet("AR_EXECUTABLE")) - { - cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find ar" << std::endl); + 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 int 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; + int 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 < sizeof(buf) ? sz : sizeof(buf), from )) > 0) { + sz -= nr; + for (int off = 0; off < nr; nr -= off, off += nw) + if ((nw = fwrite(buf + off, 1, nr, to)) < 0) + return -1; } - return this->Superclass::InitializeInternal(); + 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. */ + unsigned int 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)snprintf(ar_hb, sizeof(ar_hb), + HDR1, AR_EFMT1, lname, + (long int)sb->st_mtime, uid, gid, sb->st_mode, + sb->st_size + lname, ARFMAG); + else { + lname = 0; + (void)snprintf(ar_hb, sizeof(ar_hb), + HDR2, name, (long int)sb->st_mtime, + uid, gid, sb->st_mode, 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& files) +{ + int eval = 0; + FILE* aFile = fopen(archive, "wb+"); + if (aFile!=NULL) { + fwrite(ARMAG, SARMAG, 1, aFile); + if (fseek(aFile, (off_t)0, SEEK_END) != (off_t)-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::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 index edf3402..6683f2b 100644 --- a/Source/CPack/cmCPackDebGenerator.h +++ b/Source/CPack/cmCPackDebGenerator.h @@ -37,8 +37,7 @@ public: virtual ~cmCPackDebGenerator(); protected: - virtual int InitializeInternal(); - int CompressFiles(const char* outFileName, const char* toplevel, + virtual int CompressFiles(const char* outFileName, const char* toplevel, const std::vector& files); virtual const char* GetOutputExtension() { return ".deb"; } virtual const char* GetInstallPrefix() { return "/usr"; } -- cgit v0.12