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
32const 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() ) )
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 == "xsl" )
61 {
62 m_info = readHeader( "-->" );
63 m_cmd = wxString::Format( "xsltproc -o \"%%O%s\" \"%s\" \"%%I\"",
65 m_file.GetFullPath() );
66 }
67 else if( extension == "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( "%s \"%s\" \"%%I\" \"%%O%s\"",
104 interpreter,
105 m_file.GetFullPath(),
107#endif
108 }
109#ifdef __WINDOWS__
110 else if( extension == "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, "%s: extracted command line %s", m_name, m_cmd );
125}
126
127
128bool BOM_GENERATOR_HANDLER::IsValidGenerator( const wxString& aFile )
129{
130 wxFileName fn( aFile );
131 wxString ext = fn.GetExt().Lower();
132
133 for( const auto& pluginExt : { "xsl", "py", "pyw" } )
134 {
135 if( pluginExt == ext )
136 return true;
137 }
138
139 return false;
140}
141
142
143wxString BOM_GENERATOR_HANDLER::readHeader( const wxString& aEndSection )
144{
145 if( aEndSection.IsEmpty() )
146 return wxEmptyString;
147
148 wxFFile fdata( m_file.GetFullPath(), "rb" ); // dtor will close the file
149 wxString data;
150
151 if( !fdata.ReadAll( &data ) )
152 return wxEmptyString;
153
154 const wxString header( "@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
176wxString 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, "%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, "%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, "%s found in stock plugins path %s", m_file.GetFullName(),
220 return test;
221 }
222
223 wxLogTrace( BOM_TRACE, "Could not find %s (checked %s, %s)", m_file.GetFullName(),
225
226 return m_file;
227}
const wxChar BOM_TRACE[]
Definition: bom_plugins.cpp:32
wxString m_name
Command to execute the plugin.
Definition: bom_plugins.h:161
static bool IsValidGenerator(const wxString &aFile)
Return true if a file name matches a recognized plugin format.
wxString readHeader(const wxString &aEndSection)
Read the plugin file header.
bool m_isOk
Path to the plugin.
Definition: bom_plugins.h:152
BOM_GENERATOR_HANDLER(const wxString &aFile)
Definition: bom_plugins.cpp:35
wxString m_info
Plugin specific options.
Definition: bom_plugins.h:167
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
wxFileName FindFilePath() const
Returns the calculated path to the plugin: if the path is already absolute and exists,...
static wxString getOutputExtension(const wxString &aHeader)
Extracts the output BOM file's extension, including the '.
static wxString GetUserPluginsPath()
Gets the user path for plugins.
Definition: paths.cpp:53
static wxString GetStockPluginsPath()
Gets the stock (install) plugins path.
Definition: paths.cpp:262
#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