KiCad PCB EDA Suite
project_archiver.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <wx/dir.h>
21 #include <wx/filedlg.h>
22 #include <wx/fs_zip.h>
23 #include <wx/uri.h>
24 #include <wx/wfstream.h>
25 #include <wx/zipstrm.h>
26 
27 #include <core/arraydim.h>
28 #include <macros.h>
30 #include <reporter.h>
32 #include <wxstream_helper.h>
33 #include <wx/log.h>
34 
35 
36 #define ZipFileExtension wxT( "zip" )
37 
38 
40 {
41 }
42 
43 
44 // Unarchive Files code comes from wxWidgets sample/archive/archive.cpp
45 bool PROJECT_ARCHIVER::Unarchive( const wxString& aSrcFile, const wxString& aDestDir,
46  REPORTER& aReporter )
47 {
48  wxFFileInputStream stream( aSrcFile );
49 
50  if( !stream.IsOk() )
51  {
52  aReporter.Report( _( "Could not open archive file." ), RPT_SEVERITY_ERROR );
53  return false;
54  }
55 
56  const wxArchiveClassFactory* archiveClassFactory =
57  wxArchiveClassFactory::Find( aSrcFile, wxSTREAM_FILEEXT );
58 
59  if( !archiveClassFactory )
60  {
61  aReporter.Report( _( "Invalid archive file format." ), RPT_SEVERITY_ERROR );
62  return false;
63  }
64 
65  wxScopedPtr<wxArchiveInputStream> archiveStream( archiveClassFactory->NewStream( stream ) );
66 
67  wxString fileStatus;
68 
69  for( wxArchiveEntry* entry = archiveStream->GetNextEntry(); entry;
70  entry = archiveStream->GetNextEntry() )
71  {
72  fileStatus.Printf( _( "Extracting file '%s'." ), entry->GetName() );
73  aReporter.Report( fileStatus, RPT_SEVERITY_INFO );
74 
75  wxString fullname = aDestDir + entry->GetName();
76 
77  // Ensure the target directory exists and create it if not
78  wxString t_path = wxPathOnly( fullname );
79 
80  if( !wxDirExists( t_path ) )
81  {
82  wxFileName::Mkdir( t_path, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
83  }
84 
85  // Directory entries need only be created, not extracted (0 size)
86  if( entry->IsDir() )
87  continue;
88 
89 
90  wxTempFileOutputStream outputFileStream( fullname );
91 
92  if( CopyStreamData( *archiveStream, outputFileStream, entry->GetSize() ) )
93  outputFileStream.Commit();
94  else
95  aReporter.Report( _( "Error extracting file!" ), RPT_SEVERITY_ERROR );
96 
97  // Now let's set the filetimes based on what's in the zip
98  wxFileName outputFileName( fullname );
99  wxDateTime fileTime = entry->GetDateTime();
100  // For now we set access, mod, create to the same datetime
101  // create (third arg) is only used on Windows
102  outputFileName.SetTimes( &fileTime, &fileTime, &fileTime );
103  }
104 
105  aReporter.Report( wxT( "Extracted project." ), RPT_SEVERITY_INFO );
106  return true;
107 }
108 
109 
110 bool PROJECT_ARCHIVER::Archive( const wxString& aSrcDir, const wxString& aDestFile,
111  REPORTER& aReporter, bool aVerbose, bool aIncludeExtraFiles )
112 {
113  // List of file extensions that are always archived
114  static const wxChar* extensionList[] = {
115  wxT( "*.kicad_pro" ),
116  wxT( "*.kicad_prl" ),
117  wxT( "*.kicad_sch" ),
118  wxT( "*.kicad_sym" ),
119  wxT( "*.kicad_pcb" ),
120  wxT( "*.kicad_mod" ),
121  wxT( "*.kicad_dru" ),
122  wxT( "*.kicad_wks" ),
123  wxT( "fp-lib-table" ),
124  wxT( "sym-lib-table" )
125  };
126 
127  // List of additional file extensions that are only archived when aIncludeExtraFiles is true
128  static const wxChar* extraExtensionList[] = {
129  wxT( "*.pro" ),
130  wxT( "*.sch" ), // Legacy schematic files
131  wxT( "*.lib" ), wxT( "*.dcm" ), // Legacy schematic library files
132  wxT( "*.cmp" ),
133  wxT( "*.brd" ),
134  wxT( "*.mod" ),
135  wxT( "*.stp" ), wxT( "*.step" ), // 3d files
136  wxT( "*.wrl" ),
137  wxT( "*.gb?" ), wxT( "*.gbrjob" ), // Gerber files
138  wxT( "*.gko" ), wxT( "*.gm1" ),
139  wxT( "*.gm2" ), wxT( "*.g?" ),
140  wxT( "*.gp1" ), wxT( "*.gp2" ),
141  wxT( "*.gpb" ), wxT( "*.gpt" ),
142  wxT( "*.gt?" ),
143  wxT( "*.pos" ), wxT( "*.drl" ), wxT( "*.nc" ), wxT( "*.xnc" ), // Fab files
144  wxT( "*.d356" ), wxT( "*.rpt" ),
145  wxT( "*.net" ), wxT( "*.py" ),
146  wxT( "*.pdf" ), wxT( "*.txt" )
147  };
148 
149  bool success = true;
150  wxString msg;
151  wxString oldCwd = wxGetCwd();
152 
153  wxSetWorkingDirectory( aSrcDir );
154 
155  wxFFileOutputStream ostream( aDestFile );
156 
157  if( !ostream.IsOk() ) // issue to create the file. Perhaps not writable dir
158  {
159  msg.Printf( _( "Failed to create file '%s'." ), aDestFile );
160  aReporter.Report( msg, RPT_SEVERITY_ERROR );
161  return false;
162  }
163 
164  wxZipOutputStream zipstream( ostream, -1, wxConvUTF8 );
165 
166  // Build list of filenames to put in zip archive
167  wxString currFilename;
168 
169  wxArrayString files;
170 
171  for( unsigned ii = 0; ii < arrayDim( extensionList ); ii++ )
172  wxDir::GetAllFiles( aSrcDir, &files, extensionList[ii] );
173 
174  if( aIncludeExtraFiles )
175  {
176  for( unsigned ii = 0; ii < arrayDim( extraExtensionList ); ii++ )
177  wxDir::GetAllFiles( aSrcDir, &files, extraExtensionList[ii] );
178  }
179 
180  files.Sort();
181 
182  unsigned long uncompressedBytes = 0;
183 
184  for( unsigned ii = 0; ii < files.GetCount(); ii++ )
185  {
186  wxFileSystem fsfile;
187 
188  wxFileName curr_fn( files[ii] );
189  curr_fn.MakeRelativeTo( aSrcDir );
190  currFilename = curr_fn.GetFullPath();
191 
192  // Read input file and add it to the zip file:
193  wxFSFile* infile = fsfile.OpenFile( wxFileSystem::FileNameToURL( curr_fn ) );
194 
195  if( infile )
196  {
197  zipstream.PutNextEntry( currFilename, infile->GetModificationTime() );
198  infile->GetStream()->Read( zipstream );
199  zipstream.CloseEntry();
200 
201  uncompressedBytes += infile->GetStream()->GetSize();
202 
203  if( aVerbose )
204  {
205  msg.Printf( _( "Archived file '%s'." ), currFilename );
206  aReporter.Report( msg, RPT_SEVERITY_INFO );
207  }
208 
209  delete infile;
210  }
211  else
212  {
213  if( aVerbose )
214  {
215  msg.Printf( _( "Failed to archive file '%s'." ), currFilename );
216  aReporter.Report( msg, RPT_SEVERITY_ERROR );
217  }
218 
219  success = false;
220  }
221  }
222 
223  auto reportSize =
224  []( unsigned long aSize ) -> wxString
225  {
226  constexpr float KB = 1024.0;
227  constexpr float MB = KB * 1024.0;
228 
229  if( aSize >= MB )
230  return wxString::Format( wxT( "%0.2f MB" ), aSize / MB );
231  else if( aSize >= KB )
232  return wxString::Format( wxT( "%0.2f KB" ), aSize / KB );
233  else
234  return wxString::Format( wxT( "%lu bytes" ), aSize );
235  };
236 
237  size_t zipBytesCnt = ostream.GetSize();
238 
239  if( zipstream.Close() )
240  {
241  msg.Printf( _( "Zip archive '%s' created (%s uncompressed, %s compressed)." ),
242  aDestFile,
243  reportSize( uncompressedBytes ),
244  reportSize( zipBytesCnt ) );
245  aReporter.Report( msg, RPT_SEVERITY_INFO );
246  }
247  else
248  {
249  msg.Printf( wxT( "Failed to create file '%s'." ), aDestFile );
250  aReporter.Report( msg, RPT_SEVERITY_ERROR );
251  success = false;
252  }
253 
254  wxSetWorkingDirectory( oldCwd );
255  return success;
256 }
bool Archive(const wxString &aSrcDir, const wxString &aDestFile, REPORTER &aReporter, bool aVerbose=true, bool aIncludeExtraFiles=false)
Creates an archive of the project.
static const std::vector< std::string > extensionList
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:70
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
This file contains miscellaneous commonly used macros and functions.
static bool CopyStreamData(wxInputStream &inputStream, wxOutputStream &outputStream, wxFileOffset size)
bool Unarchive(const wxString &aSrcFile, const wxString &aDestDir, REPORTER &aReporter)
Extracts an archive of the current project over existing files Warning: this will overwrite files in ...
Definition of file extensions used in Kicad.
#define _(s)
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200