KiCad PCB EDA Suite
bom_plugins.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) 2018 CERN
5  * @author Maciej Suminski <[email protected]>
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 3
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  * https://www.gnu.org/licenses/gpl-3.0.html
20  * or you may search the http://www.gnu.org website for the version 3 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 #include "bom_plugins.h"
26 #include <config.h>
27 #include <paths.h>
28 #include <wx/ffile.h>
29 #include <wx/log.h>
30 
31 
32 const wxChar BOM_TRACE[] = wxT( "BOM_GENERATORS" );
33 
34 
36  : m_storedPath( aFile )
37 {
38  m_isOk = false;
39  m_file = wxFileName( aFile );
40 
41  if( !wxFile::Exists( m_file.GetFullPath() ) )
42  m_file = FindFilePath();
43 
44  if( !wxFile::Exists( m_file.GetFullPath() ) )
45  {
46  m_info.Printf( _("Script file:\n%s\nnot found. Script not available."), aFile );
47  return;
48  }
49 
50  m_isOk = true;
51 
52  m_name = m_file.GetName();
53  wxString extension = m_file.GetExt().Lower();
54 
55  // Important note:
56  // On Windows the right command command to run a python script is:
57  // python <script_path>/script.py
58  // and *not* python <script_path>\script.py
59  // Otherwise the script does not find some auxiliary pythons scripts needed by this script
60  if( extension == wxT( "xsl" ) )
61  {
62  m_info = readHeader( wxT( "-->" ) );
63  m_cmd = wxString::Format( "xsltproc -o \"%%O%s\" \"%s\" \"%%I\"",
65  m_file.GetFullPath() );
66  }
67  else if( extension == wxT( "py" ) )
68  {
69  m_info = readHeader( "\"\"\"" );
70 #ifdef __WINDOWS__
71  m_cmd = wxString::Format( "python \"%s/%s\" \"%%I\" \"%%O%s\"",
72  m_file.GetPath(),
73  m_file.GetFullName(),
75 #else
76 // For macOS, we want to use the Python we bundle along, rather than just PYTHON_EXECUTABLE.
77 // For non-Windows, non-macOS, we can call out to PYTHON_EXECUTABLE.
78 #ifdef __APPLE__
79  // python is at Contents/Frameworks/Python.framework/Versions/Current/bin/python3
80 
81  // Of course, for macOS, it's not quite that simple, since the relative path
82  // will depend on if we are in standalone mode or not.
83 
84  // (If we're going to call out to python like this in other places, we probably want to
85  // think about pulling this into PATHS.)
86 
87  wxFileName python( PATHS::GetOSXKicadDataDir(), wxEmptyString );
88  python.RemoveLastDir();
89  python.AppendDir( wxT( "Frameworks" ) );
90  python.AppendDir( wxT( "Python.framework" ) );
91  python.AppendDir( wxT( "Versions" ) );
92  python.AppendDir( wxT( "Current" ) );
93  python.AppendDir( wxT( "bin" ) );
94  python.SetFullName(wxT( "python3" ) );
95 
96  wxString interpreter = python.GetFullPath();
97 #else
98  wxString interpreter = wxString::FromUTF8Unchecked( PYTHON_EXECUTABLE );
99 #endif
100  if( interpreter.IsEmpty() )
101  interpreter = wxT( "python" ); // For macOS, should we log here? Error here?
102 
103  m_cmd = wxString::Format( wxT( "\"%s\" \"%s\" \"%%I\" \"%%O%s\"" ),
104  interpreter,
105  m_file.GetFullPath(),
107 #endif
108  }
109 #ifdef __WINDOWS__
110  else if( extension == wxT( "pyw" ) )
111  {
112  m_info = readHeader( "\"\"\"" );
113  m_cmd = wxString::Format( "pythonw \"%s/%s\" \"%%I\" \"%%O%s\"",
114  m_file.GetPath(),
115  m_file.GetFullName(),
117  }
118 #endif /* __WINDOWS__ */
119  else // fallback
120  {
121  m_cmd = m_file.GetFullPath();
122  }
123 
124  wxLogTrace( BOM_TRACE, wxT( "%s: extracted command line %s" ), m_name, m_cmd );
125 }
126 
127 
128 bool BOM_GENERATOR_HANDLER::IsValidGenerator( const wxString& aFile )
129 {
130  wxFileName fn( aFile );
131  wxString ext = fn.GetExt().Lower();
132 
133  for( const auto& pluginExt : { wxT( "xsl" ), wxT( "py" ), wxT( "pyw" ) } )
134  {
135  if( pluginExt == ext )
136  return true;
137  }
138 
139  return false;
140 }
141 
142 
143 wxString BOM_GENERATOR_HANDLER::readHeader( const wxString& aEndSection )
144 {
145  if( aEndSection.IsEmpty() )
146  return wxEmptyString;
147 
148  wxFFile fdata( m_file.GetFullPath(), wxT( "rb" ) ); // dtor will close the file
149  wxString data;
150 
151  if( !fdata.ReadAll( &data ) )
152  return wxEmptyString;
153 
154  const wxString header( wxT( "@package" ) );
155 
156  // Extract substring between @package and endsection
157  int strstart = data.Find( header );
158 
159  if( strstart == wxNOT_FOUND )
160  return wxEmptyString;
161 
162  strstart += header.Length();
163  int strend = data.find( aEndSection, strstart );
164 
165  if( strend == wxNOT_FOUND )
166  return wxEmptyString;
167 
168  // Remove empty line if any
169  while( data[strstart] < ' ' )
170  strstart++;
171 
172  return data.SubString( strstart, strend - 1 );
173 }
174 
175 
176 wxString BOM_GENERATOR_HANDLER::getOutputExtension( const wxString& aHeader )
177 {
178  // search header for extension after %O (extension includes '.')
179  // looks for output argument of the form `"%O.extension"`
180  const wxString outputarg( "\"%O" );
181 
182  int strstart = aHeader.Find( outputarg );
183 
184  if( strstart == wxNOT_FOUND )
185  return wxEmptyString;
186 
187  strstart += outputarg.Length();
188  int strend = aHeader.find( "\"", strstart );
189 
190  if( strend == wxNOT_FOUND )
191  return wxEmptyString;
192 
193  return aHeader.SubString( strstart, strend - 1 );
194 }
195 
196 
198 {
199  if( m_file.IsAbsolute() && m_file.Exists( wxFILE_EXISTS_REGULAR ) )
200  {
201  wxLogTrace( BOM_TRACE, wxT( "%s found directly" ), m_file.GetFullPath() );
202  return m_file;
203  }
204 
205  wxFileName test( PATHS::GetUserPluginsPath(), m_file.GetName(), m_file.GetExt() );
206 
207  if( test.Exists( wxFILE_EXISTS_REGULAR ) )
208  {
209  wxLogTrace( BOM_TRACE, wxT( "%s found in user plugins path %s" ), m_file.GetFullName(),
211  return test;
212  }
213 
214  test = wxFileName( PATHS::GetStockPluginsPath(), m_file.GetName(), m_file.GetExt() );
215 
216  if( test.Exists( wxFILE_EXISTS_REGULAR ) )
217  {
218  wxLogTrace( BOM_TRACE, wxT( "%s found in stock plugins path %s" ), m_file.GetFullName(),
220  return test;
221  }
222 
223  wxLogTrace( BOM_TRACE, wxT( "Could not find %s (checked %s, %s)" ), m_file.GetFullName(),
225 
226  return m_file;
227 }
wxString readHeader(const wxString &aEndSection)
Read the plugin file header.
wxFileName m_file
Path to the plugin stored in config (can be absolute or just a filename)
Definition: bom_plugins.h:155
wxString m_cmd
Description of the plugin (normally from the plugin header)
Definition: bom_plugins.h:164
const wxChar BOM_TRACE[]
Definition: bom_plugins.cpp:32
static wxString GetUserPluginsPath()
Gets the user path for plugins.
Definition: paths.cpp:53
wxFileName FindFilePath() const
Returns the calculated path to the plugin: if the path is already absolute and exists,...
static bool IsValidGenerator(const wxString &aFile)
Return true if a file name matches a recognized plugin format.
#define _(s)
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
BOM_GENERATOR_HANDLER(const wxString &aFile)
Definition: bom_plugins.cpp:35
static wxString getOutputExtension(const wxString &aHeader)
Extracts the output BOM file's extension, including the '.
wxString m_name
Command to execute the plugin.
Definition: bom_plugins.h:161
wxString m_info
Plugin specific options.
Definition: bom_plugins.h:167
bool m_isOk
Path to the plugin.
Definition: bom_plugins.h:152
static wxString GetStockPluginsPath()
Gets the stock (install) plugins path.
Definition: paths.cpp:262