Parcourir la source

Merge topic 'libarchive-wrapper'

aef6723 cmArchiveWrite: Fix signed/unsigned compare/convert
1b5b2ed Include entries for directories in tarballs (#11020)
c7c9009 Create class cmArchiveWrite to wrap libarchive (#11020)
ac26737 Merge branch 'system-libarchive-include' into libarchive-wrapper
3296e6a Include headers from chosen libarchive (#10923)
Brad King il y a 15 ans
Parent
commit
6d85679551

+ 1 - 0
Source/CMakeLists.txt

@@ -112,6 +112,7 @@ ENDIF(CMAKE_USE_ELF_PARSER)
 #
 SET(SRCS
   cmStandardIncludes.cxx
+  cmArchiveWrite.cxx
   cmBootstrapCommands.cxx
   cmCacheManager.cxx
   cmCacheManager.h

+ 1 - 3
Source/CPack/cmCPackArchiveGenerator.cxx

@@ -22,9 +22,7 @@
 #include <errno.h>
 
 #include <cmsys/SystemTools.hxx>
-#include <cmlibarchive/libarchive/archive.h>
-#include <cmlibarchive/libarchive/archive_entry.h>
-
+#include <cm_libarchive.h>
 
 //----------------------------------------------------------------------
 cmCPackArchiveGenerator::cmCPackArchiveGenerator(CompressType t,

+ 247 - 0
Source/cmArchiveWrite.cxx

@@ -0,0 +1,247 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2000-2010 Kitware, Inc., Insight Software Consortium
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  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.
+============================================================================*/
+#include "cmArchiveWrite.h"
+
+#include "cmSystemTools.h"
+#include <cmsys/ios/iostream>
+#include <cmsys/Directory.hxx>
+#include <cm_libarchive.h>
+
+//----------------------------------------------------------------------------
+class cmArchiveWrite::Entry
+{
+  struct archive_entry* Object;
+public:
+  Entry(): Object(archive_entry_new()) {}
+  ~Entry() { archive_entry_free(this->Object); }
+  operator struct archive_entry*() { return this->Object; }
+};
+
+//----------------------------------------------------------------------------
+struct cmArchiveWrite::Callback
+{
+  // archive_write_callback
+  static __LA_SSIZE_T Write(struct archive*, void *cd,
+                            const void *b, size_t n)
+    {
+    cmArchiveWrite* self = static_cast<cmArchiveWrite*>(cd);
+    if(self->Stream.write(static_cast<const char*>(b),
+                          static_cast<cmsys_ios::streamsize>(n)))
+      {
+      return static_cast<__LA_SSIZE_T>(n);
+      }
+    else
+      {
+      return static_cast<__LA_SSIZE_T>(-1);
+      }
+    }
+};
+
+//----------------------------------------------------------------------------
+cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c):
+  Stream(os),
+  Archive(archive_write_new()),
+  Disk(archive_read_disk_new()),
+  Verbose(false)
+{
+  switch (c)
+    {
+    case CompressNone:
+      if(archive_write_set_compression_none(this->Archive) != ARCHIVE_OK)
+        {
+        this->Error = "archive_write_set_compression_none: ";
+        this->Error += archive_error_string(this->Archive);
+        return;
+        }
+      break;
+    case CompressGZip:
+      if(archive_write_set_compression_gzip(this->Archive) != ARCHIVE_OK)
+        {
+        this->Error = "archive_write_set_compression_gzip: ";
+        this->Error += archive_error_string(this->Archive);
+        return;
+        }
+      break;
+    case CompressBZip2:
+      if(archive_write_set_compression_bzip2(this->Archive) != ARCHIVE_OK)
+        {
+        this->Error = "archive_write_set_compression_bzip2: ";
+        this->Error += archive_error_string(this->Archive);
+        return;
+        }
+      break;
+    };
+  archive_read_disk_set_standard_lookup(this->Disk);
+  if(archive_write_set_format_pax_restricted(this->Archive) != ARCHIVE_OK)
+    {
+    this->Error = "archive_write_set_format_pax_restricted: ";
+    this->Error += archive_error_string(this->Archive);
+    return;
+    }
+
+  if(archive_write_open(
+       this->Archive, this, 0,
+       reinterpret_cast<archive_write_callback*>(&Callback::Write),
+       0) != ARCHIVE_OK)
+    {
+    this->Error = "archive_write_open: ";
+    this->Error += archive_error_string(this->Archive);
+    return;
+    }
+}
+
+//----------------------------------------------------------------------------
+cmArchiveWrite::~cmArchiveWrite()
+{
+  archive_read_finish(this->Disk);
+  archive_write_finish(this->Archive);
+}
+
+//----------------------------------------------------------------------------
+bool cmArchiveWrite::Add(std::string path, size_t skip, const char* prefix)
+{
+  if(this->Okay())
+    {
+    if(!path.empty() && path[path.size()-1] == '/')
+      {
+      path.erase(path.size()-1);
+      }
+    this->AddPath(path.c_str(), skip, prefix);
+    }
+  return this->Okay();
+}
+
+//----------------------------------------------------------------------------
+bool cmArchiveWrite::AddPath(const char* path,
+                             size_t skip, const char* prefix)
+{
+  if(!this->AddFile(path, skip, prefix))
+    {
+    return false;
+    }
+  if(!cmSystemTools::FileIsDirectory(path))
+    {
+    return true;
+    }
+  cmsys::Directory d;
+  if(d.Load(path))
+    {
+    std::string next = path;
+    next += "/";
+    std::string::size_type end = next.size();
+    unsigned long n = d.GetNumberOfFiles();
+    for(unsigned long i = 0; i < n; ++i)
+      {
+      const char* file = d.GetFile(i);
+      if(strcmp(file, ".") != 0 && strcmp(file, "..") != 0)
+        {
+        next.erase(end);
+        next += file;
+        if(!this->AddPath(next.c_str(), skip, prefix))
+          {
+          return false;
+          }
+        }
+      }
+    }
+  return true;
+}
+
+//----------------------------------------------------------------------------
+bool cmArchiveWrite::AddFile(const char* file,
+                             size_t skip, const char* prefix)
+{
+  // Skip the file if we have no name for it.  This may happen on a
+  // top-level directory, which does not need to be included anyway.
+  if(skip >= strlen(file))
+    {
+    return true;
+    }
+  const char* out = file + skip;
+
+  // Meta-data.
+  std::string dest = prefix? prefix : "";
+  dest += out;
+  if(this->Verbose)
+    {
+    std::cout << dest << "\n";
+    }
+  Entry e;
+  archive_entry_copy_sourcepath(e, file);
+  archive_entry_set_pathname(e, dest.c_str());
+  if(archive_read_disk_entry_from_file(this->Disk, e, -1, 0) != ARCHIVE_OK)
+    {
+    this->Error = "archive_read_disk_entry_from_file: ";
+    this->Error += archive_error_string(this->Disk);
+    return false;
+    }
+  if(archive_write_header(this->Archive, e) != ARCHIVE_OK)
+    {
+    this->Error = "archive_write_header: ";
+    this->Error += archive_error_string(this->Archive);
+    return false;
+    }
+
+  // Content.
+  if(size_t size = static_cast<size_t>(archive_entry_size(e)))
+    {
+    return this->AddData(file, size);
+    }
+  return true;
+}
+
+//----------------------------------------------------------------------------
+bool cmArchiveWrite::AddData(const char* file, size_t size)
+{
+  std::ifstream fin(file, std::ios::in | cmsys_ios_binary);
+  if(!fin)
+    {
+    this->Error = "Error opening \"";
+    this->Error += file;
+    this->Error += "\": ";
+    this->Error += cmSystemTools::GetLastSystemError();
+    return false;
+    }
+
+  char buffer[16384];
+  size_t nleft = size;
+  while(nleft > 0)
+    {
+    cmsys_ios::streamsize nnext = static_cast<cmsys_ios::streamsize>(
+      nleft > sizeof(buffer)? sizeof(buffer) : nleft);
+    fin.read(buffer, nnext);
+    // Some stream libraries (older HPUX) return failure at end of
+    // file on the last read even if some data were read.  Check
+    // gcount instead of trusting the stream error status.
+    if(fin.gcount() != nnext)
+      {
+      break;
+      }
+    if(archive_write_data(this->Archive, buffer,
+                          static_cast<size_t>(nnext)) != nnext)
+      {
+      this->Error = "archive_write_data: ";
+      this->Error += archive_error_string(this->Archive);
+      return false;
+      }
+    nleft -= static_cast<size_t>(nnext);
+    }
+  if(nleft > 0)
+    {
+    this->Error = "Error reading \"";
+    this->Error += file;
+    this->Error += "\": ";
+    this->Error += cmSystemTools::GetLastSystemError();
+    return false;
+    }
+  return true;
+}

+ 84 - 0
Source/cmArchiveWrite.h

@@ -0,0 +1,84 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2000-2010 Kitware, Inc., Insight Software Consortium
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  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.
+============================================================================*/
+#ifndef cmArchiveWrite_h
+#define cmArchiveWrite_h
+
+#include "cmStandardIncludes.h"
+
+#if !defined(CMAKE_BUILD_WITH_CMAKE)
+# error "cmArchiveWrite not allowed during bootstrap build!"
+#endif
+
+/** \class cmArchiveWrite
+ * \brief Wrapper around libarchive for writing.
+ *
+ */
+class cmArchiveWrite
+{
+  typedef void (cmArchiveWrite::* safe_bool)();
+  void safe_bool_true() {}
+public:
+  /** Compression type.  */
+  enum Compress
+  {
+    CompressNone,
+    CompressGZip,
+    CompressBZip2
+  };
+
+  /** Construct with output stream to which to write archive.  */
+  cmArchiveWrite(std::ostream& os, Compress c = CompressNone);
+  ~cmArchiveWrite();
+
+  /**
+   * Add a path (file or directory) to the archive.  Directories are
+   * added recursively.  The "path" must be readable on disk, either
+   * full path or relative to current working directory.  The "skip"
+   * value indicates how many leading bytes from the input path to
+   * skip.  The remaining part of the input path is appended to the
+   * "prefix" value to construct the final name in the archive.
+   */
+  bool Add(std::string path, size_t skip = 0, const char* prefix = 0);
+
+  /** Returns true if there has been no error.  */
+  operator safe_bool() const
+    { return this->Okay()? &cmArchiveWrite::safe_bool_true : 0; }
+
+  /** Returns true if there has been an error.  */
+  bool operator!() const { return !this->Okay(); }
+
+  /** Return the error string; empty if none.  */
+  std::string GetError() const { return this->Error; }
+
+  // TODO: More general callback instead of hard-coding calls to
+  // std::cout.
+  void SetVerbose(bool v) { this->Verbose = v; }
+
+private:
+  bool Okay() const { return this->Error.empty(); }
+  bool AddPath(const char* path, size_t skip, const char* prefix);
+  bool AddFile(const char* file, size_t skip, const char* prefix);
+  bool AddData(const char* file, size_t size);
+
+  struct Callback;
+  friend struct Callback;
+
+  class Entry;
+
+  std::ostream& Stream;
+  struct archive* Archive;
+  struct archive* Disk;
+  bool Verbose;
+  std::string Error;
+};
+
+#endif

+ 23 - 125
Source/cmSystemTools.cxx

@@ -23,8 +23,8 @@
 #include <cmsys/Directory.hxx>
 #include <cmsys/System.h>
 #if defined(CMAKE_BUILD_WITH_CMAKE)
-#include <cmlibarchive/libarchive/archive.h>
-#include <cmlibarchive/libarchive/archive_entry.h>
+# include "cmArchiveWrite.h"
+# include <cm_libarchive.h>
 # include <cmsys/Terminal.h>
 #endif
 #include <cmsys/stl/algorithm>
@@ -1719,142 +1719,40 @@ bool cmSystemTools::CreateTar(const char* outFileName,
                               bool gzip, bool bzip2, bool verbose)
 {
 #if defined(CMAKE_BUILD_WITH_CMAKE)
-  
-  // Create a macro to handle return from libarchive 
-  // functions
-#define CHECK_ARCHIVE_ERROR(res, msg)\
-   if(res != ARCHIVE_OK)\
-    {\
-    cmSystemTools::Error(msg, \
-                         archive_error_string(a));\
-    return false;\
-    }
-  
   std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
-  // recursively expand all directories in files so that we have a list
-  // of files
-  std::vector<std::string> expandedFiles;
+  std::ofstream fout(outFileName, std::ios::out | cmsys_ios_binary);
+  if(!fout)
+    {
+    std::string e = "Cannot open output file \"";
+    e += outFileName;
+    e += "\": ";
+    e += cmSystemTools::GetLastSystemError();
+    cmSystemTools::Error(e.c_str());
+    return false;
+    }
+  cmArchiveWrite a(fout, (gzip? cmArchiveWrite::CompressGZip :
+                          (bzip2? cmArchiveWrite::CompressBZip2 :
+                           cmArchiveWrite::CompressNone)));
+  a.SetVerbose(verbose);
   for(std::vector<cmStdString>::const_iterator i = files.begin();
       i != files.end(); ++i)
     {
-    if(cmSystemTools::FileIsDirectory(i->c_str()))
+    std::string path = *i;
+    if(cmSystemTools::FileIsFullPath(path.c_str()))
       {
-      cmsys::Glob gl;
-      std::string findExpr = *i; 
-      if ( findExpr[findExpr.size()-1] != '/' )
-        {
-        findExpr +="/";
-        }
-      findExpr += "*";
-      gl.RecurseOn();
-      if ( gl.FindFiles(findExpr) )
-        {
-        std::vector<std::string> dirfiles = gl.GetFiles();
-        std::copy(dirfiles.begin(), dirfiles.end(),
-                  std::back_inserter(expandedFiles));
-        }
+      // Get the relative path to the file.
+      path = cmSystemTools::RelativePath(cwd.c_str(), path.c_str());
       }
-    else
+    if(!a.Add(path))
       {
-      if(!cmSystemTools::FileIsFullPath(i->c_str()))
-        {
-        std::string fullp = cwd + "/" + *i;
-        expandedFiles.push_back(fullp);
-        }
-      else
-        {
-        expandedFiles.push_back(*i);
-        }
+      break;
       }
     }
-  int res;
-  // create a new archive
-  struct archive* a = archive_write_new();
   if(!a)
     {
-    cmSystemTools::Error("Unable to use create archive");
+    cmSystemTools::Error(a.GetError().c_str());
     return false;
     }
-
-  if(gzip)
-    {
-    res = archive_write_set_compression_gzip(a);
-    CHECK_ARCHIVE_ERROR(res, "Can not set gzip:");
-    } 
-  if(bzip2)
-    {
-    res = archive_write_set_compression_bzip2(a); 
-    CHECK_ARCHIVE_ERROR(res, "Can not set bzip2:");
-    }
-  if(!bzip2 && !gzip)
-    { 
-    res = archive_write_set_compression_none(a); 
-    CHECK_ARCHIVE_ERROR(res, "Can not set none:");
-    }
-  res = archive_write_set_format_pax_restricted(a);
-  CHECK_ARCHIVE_ERROR(res, "Can not set tar format:");
-  res = archive_write_open_file(a, outFileName);
-  CHECK_ARCHIVE_ERROR(res, "write open:");
-    // create a new disk struct
-  struct archive* disk = archive_read_disk_new();
-  archive_read_disk_set_standard_lookup(disk);
-  std::vector<std::string>::const_iterator fileIt;
-  for ( fileIt = expandedFiles.begin(); 
-        fileIt != expandedFiles.end(); ++ fileIt )
-    {
-    // create a new entry for each file
-    struct archive_entry *entry = archive_entry_new(); 
-    // Get the relative path to the file
-    std::string rp = cmSystemTools::RelativePath(cwd.c_str(),
-                                                 fileIt->c_str());
-    if(verbose)
-      {
-      std::cout << rp << "\n";
-      }
-    // Set the name of the entry to the file name
-    archive_entry_set_pathname(entry, rp.c_str()); 
-    archive_read_disk_entry_from_file(disk, entry, -1, 0);
-    CHECK_ARCHIVE_ERROR(res, "read disk entry:");
-
-    // write  entry header
-    res = archive_write_header(a, entry);
-    CHECK_ARCHIVE_ERROR(res, "write header: ");
-    if(archive_entry_size(entry) > 0)
-      {
-      // now copy contents of file into archive a
-      FILE* file = fopen(fileIt->c_str(), "rb");
-      if(!file)
-        {
-          cmSystemTools::Error("Problem with fopen(): ",
-                               fileIt->c_str());
-          return false;
-        }
-      char buff[16384];
-      size_t len = fread(buff, 1, sizeof(buff), file);
-      while (len > 0)
-        {
-          size_t wlen = archive_write_data(a, buff, len);
-          if(wlen != len)
-            { 
-              cmOStringStream error;
-              error << "Problem with archive_write_data\n"
-                    << "Tried to write  [" << len << "] bytes.\n"
-                    << "archive_write_data wrote  [" << wlen << "] bytes.\n";
-              cmSystemTools::Error(error.str().c_str(),
-                                   archive_error_string(a)
-                                   );
-              return false;
-            }
-          len = fread(buff, 1, sizeof(buff), file);
-        }
-      // close the file and free the entry
-      fclose(file);
-      }
-    archive_entry_free(entry);
-    }
-  archive_write_close(a);
-  archive_write_finish(a);
-  archive_read_finish(disk);
   return true;
 #else
   (void)outFileName;

+ 1 - 0
Utilities/cmThirdParty.h.in

@@ -16,6 +16,7 @@
 #cmakedefine CMAKE_USE_SYSTEM_CURL
 #cmakedefine CMAKE_USE_SYSTEM_EXPAT
 #cmakedefine CMAKE_USE_SYSTEM_ZLIB
+#cmakedefine CMAKE_USE_SYSTEM_LIBARCHIVE
 #cmakedefine CTEST_USE_XMLRPC
 
 #endif

+ 25 - 0
Utilities/cm_libarchive.h

@@ -0,0 +1,25 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2000-2010 Kitware, Inc., Insight Software Consortium
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  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.
+============================================================================*/
+#ifndef __cm_libarchive_h
+#define __cm_libarchive_h
+
+/* Use the libarchive configured for CMake.  */
+#include "cmThirdParty.h"
+#ifdef CMAKE_USE_SYSTEM_LIBARCHIVE
+# include <archive.h>
+# include <archive_entry.h>
+#else
+# include <cmlibarchive/libarchive/archive.h>
+# include <cmlibarchive/libarchive/archive_entry.h>
+#endif
+
+#endif