KiCad PCB EDA Suite
Loading...
Searching...
No Matches
project_template.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) 2012 Brian Sidebotham <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25
26#include <wx/bitmap.h>
27#include <wx/dir.h>
28#include <wx/txtstrm.h>
29#include <wx/wfstream.h>
30#include <wx/log.h>
31#include <wx/textfile.h>
32#include <unordered_map>
33
35#include "project_template.h"
36
37
38#define SEP wxFileName::GetPathSeparator()
39
40
41PROJECT_TEMPLATE::PROJECT_TEMPLATE( const wxString& aPath )
42{
43 m_basePath = wxFileName::DirName( aPath );
44 m_metaPath = wxFileName::DirName( aPath + SEP + METADIR );
45 m_metaHtmlFile = wxFileName::FileName( aPath + SEP + METADIR + SEP + METAFILE_INFO_HTML );
46 m_metaIconFile = wxFileName::FileName( aPath + SEP + METADIR + SEP + METAFILE_ICON );
47
48 m_title = wxEmptyString;
49
50 // Test the project template requirements to make sure aPath is a valid template structure.
51 if( !wxFileName::DirExists( m_basePath.GetPath() ) )
52 {
53 // Error, the path doesn't exist!
54 m_title.Printf( _( "Could not open the template path '%s'" ), aPath );
55 }
56 else if( !wxFileName::DirExists( m_metaPath.GetPath() ) )
57 {
58 // Error, the meta information directory doesn't exist!
59 m_title.Printf( _( "Could not find the expected 'meta' directory at '%s'" ), m_metaPath.GetPath() );
60 }
61 else if( !wxFileName::FileExists( m_metaHtmlFile.GetFullPath() ) )
62 {
63 // Error, the meta information directory doesn't contain the informational html file!
64 m_title.Printf( _( "Could not find the expected meta HTML file at '%s'" ), m_metaHtmlFile.GetFullPath() );
65 }
66
67 // Try to load an icon
68 if( !wxFileName::FileExists( m_metaIconFile.GetFullPath() ) )
69 m_metaIcon = &wxNullBitmap;
70 else
71 m_metaIcon = new wxBitmap( m_metaIconFile.GetFullPath(), wxBITMAP_TYPE_PNG );
72}
73
74
75class FILE_TRAVERSER : public wxDirTraverser
76{
77public:
78 FILE_TRAVERSER( std::vector<wxFileName>& files, const wxString& exclude ) :
79 m_files( files ),
80 m_exclude( exclude )
81 {
82 }
83
84 virtual wxDirTraverseResult OnFile( const wxString& filename ) override
85 {
86 wxFileName fn( filename );
87 wxString path( fn.GetPathWithSep() );
88
90
91 if( IsIgnored( path, fn.GetFullName(), false ) )
92 return wxDIR_CONTINUE;
93
94 bool exclude = fn.GetName().Contains( "fp-info-cache" )
95 || fn.GetName().StartsWith( FILEEXT::AutoSaveFilePrefix )
96 || fn.GetName().StartsWith( FILEEXT::LockFilePrefix );
97
98 if( !exclude )
99 m_files.emplace_back( wxFileName( filename ) );
100
101 return wxDIR_CONTINUE;
102 }
103
104 virtual wxDirTraverseResult OnDir( const wxString& dirname ) override
105 {
106 wxFileName dir( dirname );
107 wxString parent = dir.GetPathWithSep();
108
109 EnsureGitFiles( parent );
110
111 if( dir.GetFullName() == wxT( ".git" ) || IsIgnored( parent, dir.GetFullName(), true )
112 || dirname.StartsWith( m_exclude ) || dirname.EndsWith( "-backups" ) )
113 {
114 return wxDIR_IGNORE;
115 }
116
117 m_files.emplace_back( wxFileName::DirName( dirname ) );
118 EnsureGitFiles( dirname + wxFileName::GetPathSeparator() );
119 return wxDIR_CONTINUE;
120 }
121
122private:
123 void EnsureGitFiles( const wxString& path )
124 {
125 if( m_gitIgnores.find( path ) != m_gitIgnores.end() )
126 return;
127
128 wxString gitignore = path + wxT( ".gitignore" );
129
130 if( wxFileExists( gitignore ) )
131 {
132 wxFileInputStream input( gitignore );
133 wxTextInputStream text( input, wxT( "\x9" ), wxConvUTF8 );
134
135 while( input.IsOk() && !input.Eof() )
136 {
137 wxString line = text.ReadLine();
138
139 line.Trim().Trim( false );
140
141 if( line.IsEmpty() || line.StartsWith( wxT( "#" ) ) )
142 continue;
143
144 m_gitIgnores[path].push_back( line );
145 }
146
147 m_files.emplace_back( wxFileName( gitignore ) );
148 }
149 else
150 {
151 m_gitIgnores[path] = {};
152 }
153
154 wxString gitattributes = path + wxT( ".gitattributes" );
155
156 if( wxFileExists( gitattributes ) )
157 m_files.emplace_back( wxFileName( gitattributes ) );
158 }
159
160 bool IsIgnored( const wxString& path, const wxString& name, bool isDir )
161 {
162 auto it = m_gitIgnores.find( path );
163
164 if( it == m_gitIgnores.end() )
165 return false;
166
167 for( const wxString& pattern : it->second )
168 {
169 bool dirOnly = pattern.EndsWith( wxT( "/" ) );
170 wxString pat = dirOnly ? pattern.substr( 0, pattern.length() - 1 ) : pattern;
171
172 if( dirOnly && !isDir )
173 continue;
174
175 if( wxMatchWild( pat, name ) )
176 return true;
177 }
178
179 return false;
180 }
181
182 std::vector<wxFileName>& m_files;
183 wxString m_exclude;
184 std::unordered_map<wxString, std::vector<wxString>> m_gitIgnores;
185};
186
187
188std::vector<wxFileName> PROJECT_TEMPLATE::GetFileList()
189{
190 std::vector<wxFileName> files;
191 FILE_TRAVERSER sink( files, m_metaPath.GetPath() );
192 wxDir dir( m_basePath.GetPath() );
193
194 dir.Traverse( sink, wxEmptyString, ( wxDIR_FILES | wxDIR_DIRS ) );
195 return files;
196}
197
198
200{
201 return m_basePath.GetDirs()[m_basePath.GetDirCount() - 1];
202}
203
204
206{
207}
208
209
211{
212 return m_metaHtmlFile;
213}
214
215
217{
218 return m_metaIcon;
219}
220
221
222size_t PROJECT_TEMPLATE::GetDestinationFiles( const wxFileName& aNewProjectPath, std::vector<wxFileName>& aDestFiles )
223{
224 std::vector<wxFileName> srcFiles = GetFileList();
225
226 // Find the template file name base. this is the name of the .pro template file
227 wxString basename;
228 bool multipleProjectFilesFound = false;
229
230 for( wxFileName& file : srcFiles )
231 {
232 if( file.GetExt() == FILEEXT::ProjectFileExtension || file.GetExt() == FILEEXT::LegacyProjectFileExtension )
233 {
234 if( !basename.IsEmpty() && basename != file.GetName() )
235 multipleProjectFilesFound = true;
236
237 basename = file.GetName();
238 }
239 }
240
241 if( multipleProjectFilesFound )
242 basename = GetPrjDirName();
243
244 for( wxFileName& srcFile : srcFiles )
245 {
246 // Replace the template path
247 wxFileName destFile = srcFile;
248
249 // Replace the template filename with the project filename for the new project creation
250 wxString name = destFile.GetName();
251 name.Replace( basename, aNewProjectPath.GetName() );
252 destFile.SetName( name );
253
254 // Replace the template path with the project path.
255 wxString path = destFile.GetPathWithSep();
256 path.Replace( m_basePath.GetPathWithSep(), aNewProjectPath.GetPathWithSep() );
257 destFile.SetPath( path );
258
259 aDestFiles.push_back( destFile );
260 }
261
262 return aDestFiles.size();
263}
264
265
266bool PROJECT_TEMPLATE::CreateProject( wxFileName& aNewProjectPath, wxString* aErrorMsg )
267{
268 // CreateProject copy the files from template to the new project folder and renames files
269 // which have the same name as the template .kicad_pro file
270 bool result = true;
271
272 std::vector<wxFileName> srcFiles = GetFileList();
273
274 // Find the template file name base. this is the name of the .kicad_pro (or .pro) template
275 // file
276 wxString basename;
277 bool multipleProjectFilesFound = false;
278
279 for( wxFileName& file : srcFiles )
280 {
281 if( file.GetExt() == FILEEXT::ProjectFileExtension || file.GetExt() == FILEEXT::LegacyProjectFileExtension )
282 {
283 if( !basename.IsEmpty() && basename != file.GetName() )
284 multipleProjectFilesFound = true;
285
286 basename = file.GetName();
287 }
288 }
289
290 if( multipleProjectFilesFound )
291 basename = GetPrjDirName();
292
293 for( wxFileName& srcFile : srcFiles )
294 {
295 // Replace the template path
296 wxFileName destFile = srcFile;
297
298 // Replace the template filename with the project filename for the new project creation
299 wxString currname = destFile.GetName();
300
301 if( destFile.GetExt() == FILEEXT::DrawingSheetFileExtension )
302 {
303 // Don't rename drawing sheet definitions; they're often shared
304 }
305 else if( destFile.GetName().EndsWith( "-cache" ) || destFile.GetName().EndsWith( "-rescue" ) )
306 {
307 currname.Replace( basename, aNewProjectPath.GetName() );
308 }
309 else if( destFile.GetExt() == FILEEXT::LegacySymbolDocumentFileExtension
310 || destFile.GetExt() == FILEEXT::LegacySymbolLibFileExtension
311 // Footprint libraries are directories not files, so GetExt() won't work
312 || destFile.GetPath().EndsWith( '.' + FILEEXT::KiCadFootprintLibPathExtension ) )
313 {
314 // Don't rename project-specific libraries. This will break the library tables and
315 // cause broken links in the schematic/pcb.
316 }
317 else
318 {
319 currname.Replace( basename, aNewProjectPath.GetName() );
320 }
321
322 destFile.SetName( currname );
323
324 // Replace the template path with the project path for the new project creation
325 // but keep the sub directory name, if exists
326 wxString destpath = destFile.GetPathWithSep();
327 destpath.Replace( m_basePath.GetPathWithSep(), aNewProjectPath.GetPathWithSep() );
328
329 // Check to see if the path already exists, if not attempt to create it here.
330 if( !wxFileName::DirExists( destpath ) )
331 {
332 if( !wxFileName::Mkdir( destpath, 0777, wxPATH_MKDIR_FULL ) )
333 {
334 if( aErrorMsg )
335 {
336 if( !aErrorMsg->empty() )
337 *aErrorMsg += "\n";
338
339 wxString msg;
340
341 msg.Printf( _( "Cannot create folder '%s'." ), destpath );
342 *aErrorMsg += msg;
343 }
344
345 continue;
346 }
347 }
348
349 destFile.SetPath( destpath );
350
351 if( srcFile.FileExists() && !wxCopyFile( srcFile.GetFullPath(), destFile.GetFullPath() ) )
352 {
353 if( aErrorMsg )
354 {
355 if( !aErrorMsg->empty() )
356 *aErrorMsg += "\n";
357
358 wxString msg;
359
360 msg.Printf( _( "Cannot copy file '%s'." ), destFile.GetFullPath() );
361 *aErrorMsg += msg;
362 }
363
364 result = false;
365 }
366 }
367
368 return result;
369}
370
371
373{
374 wxFFileInputStream input( GetHtmlFile().GetFullPath() );
375 wxString separator( wxT( "\x9" ) );
376 wxTextInputStream text( input, separator, wxConvUTF8 );
377
378 /* Open HTML file and get the text between the title tags */
379 if( m_title == wxEmptyString )
380 {
381 int start = 0;
382 int finish = 0;
383 bool done = false;
384 bool hasStart = false;
385
386 while( input.IsOk() && !input.Eof() && !done )
387 {
388 wxString line = text.ReadLine();
389 wxString upperline = line.Clone().Upper();
390
391 start = upperline.Find( wxT( "<TITLE>" ) );
392 finish = upperline.Find( wxT( "</TITLE>" ) );
393 int length = finish - start - 7;
394
395 // find the opening tag
396 if( start != wxNOT_FOUND )
397 {
398 if( finish != wxNOT_FOUND )
399 {
400 m_title = line( start + 7, length );
401 done = true;
402 }
403 else
404 {
405 m_title = line.Mid( start + 7 );
406 hasStart = true;
407 }
408 }
409 else
410 {
411 if( finish != wxNOT_FOUND )
412 {
413 m_title += line.SubString( 0, finish - 1 );
414 done = true;
415 }
416 else if( hasStart )
417 m_title += line;
418 }
419 }
420
421 // Remove line endings
422 m_title.Replace( wxT( "\r" ), wxT( "" ) );
423 m_title.Replace( wxT( "\n" ), wxT( "" ) );
424
425 m_title.Trim( false ); // Trim from left
426 m_title.Trim(); // Trim from right
427 }
428
429 return &m_title;
430}
const char * name
Definition: DXF_plotter.cpp:62
virtual wxDirTraverseResult OnDir(const wxString &dirname) override
FILE_TRAVERSER(std::vector< wxFileName > &files, const wxString &exclude)
void EnsureGitFiles(const wxString &path)
virtual wxDirTraverseResult OnFile(const wxString &filename) override
std::unordered_map< wxString, std::vector< wxString > > m_gitIgnores
std::vector< wxFileName > & m_files
bool IsIgnored(const wxString &path, const wxString &name, bool isDir)
wxBitmap * GetIcon()
Get the 64px^2 icon for the project template.
size_t GetDestinationFiles(const wxFileName &aNewProjectPath, std::vector< wxFileName > &aDestFiles)
Fetch the list of destination files to be copied when the new project is created.
wxFileName m_metaHtmlFile
PROJECT_TEMPLATE(const wxString &aPath)
Create a new project instance from aPath.
std::vector< wxFileName > GetFileList()
Get a vector list of filenames for the template.
~PROJECT_TEMPLATE()
Non-virtual destructor (so no derived classes)
wxFileName GetHtmlFile()
Get the full Html filename for the project template.
wxString * GetTitle()
Get the title of the project (extracted from the html title tag)
bool CreateProject(wxFileName &aNewProjectPath, wxString *aErrorMsg=nullptr)
Copies and renames all template files to create a new project.
wxFileName m_metaIconFile
wxString GetPrjDirName()
Get the dir name of the project template (i.e.
#define _(s)
static const std::string ProjectFileExtension
static const std::string LegacyProjectFileExtension
static const std::string LegacySymbolLibFileExtension
static const std::string LockFilePrefix
static const std::string DrawingSheetFileExtension
static const std::string AutoSaveFilePrefix
static const std::string LegacySymbolDocumentFileExtension
static const std::string KiCadFootprintLibPathExtension
#define SEP()
#define METAFILE_ICON
An optional png icon, exactly 64px x 64px which is used in the template selector if present.
#define METADIR
A directory which contains information about the project template and does not get copied.
#define METAFILE_INFO_HTML
A required html formatted file which contains information about the project template.
Definition of file extensions used in Kicad.