You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
7.4 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2018 CERN
  5. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Maciej Suminski <maciej.suminski@cern.ch>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 3
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * https://www.gnu.org/licenses/gpl-3.0.html
  21. * or you may search the http://www.gnu.org website for the version 3 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  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. const wxChar BOM_TRACE[] = wxT( "BOM_GENERATORS" );
  31. BOM_GENERATOR_HANDLER::BOM_GENERATOR_HANDLER( const wxString& aFile )
  32. : m_storedPath( aFile )
  33. {
  34. m_isOk = false;
  35. m_file = wxFileName( aFile );
  36. if( !wxFile::Exists( m_file.GetFullPath() ) )
  37. m_file = FindFilePath();
  38. if( !wxFile::Exists( m_file.GetFullPath() ) )
  39. {
  40. m_info.Printf( _("Script file:\n%s\nnot found. Script not available."), aFile );
  41. return;
  42. }
  43. m_isOk = true;
  44. m_name = m_file.GetName();
  45. wxString extension = m_file.GetExt().Lower();
  46. // Important note:
  47. // On Windows the right command command to run a python script is:
  48. // python <script_path>/script.py
  49. // and *not* python <script_path>\script.py
  50. // Otherwise the script does not find some auxiliary pythons scripts needed by this script
  51. if( extension == wxS( "xsl" ) )
  52. {
  53. m_info = readHeader( wxS( "-->" ) );
  54. m_cmd = wxString::Format( wxS( "xsltproc -o \"%%O%s\" \"%s\" \"%%I\"" ),
  55. getOutputExtension( m_info ),
  56. m_file.GetFullPath() );
  57. }
  58. else if( extension == wxS( "py" ) )
  59. {
  60. m_info = readHeader( wxS( "\"\"\"" ) );
  61. #ifdef __WINDOWS__
  62. m_cmd = wxString::Format( "python \"%s/%s\" \"%%I\" \"%%O%s\"",
  63. m_file.GetPath(),
  64. m_file.GetFullName(),
  65. getOutputExtension( m_info ) );
  66. #else
  67. // For macOS, we want to use the Python we bundle along, rather than just PYTHON_EXECUTABLE.
  68. // For non-Windows, non-macOS, we can call out to PYTHON_EXECUTABLE.
  69. #ifdef __APPLE__
  70. // python is at Contents/Frameworks/Python.framework/Versions/Current/bin/python3
  71. // Of course, for macOS, it's not quite that simple, since the relative path
  72. // will depend on if we are in standalone mode or not.
  73. // (If we're going to call out to python like this in other places, we probably want to
  74. // think about pulling this into PATHS.)
  75. wxFileName python( PATHS::GetOSXKicadDataDir(), wxEmptyString );
  76. python.RemoveLastDir();
  77. python.AppendDir( wxT( "Frameworks" ) );
  78. python.AppendDir( wxT( "Python.framework" ) );
  79. python.AppendDir( wxT( "Versions" ) );
  80. python.AppendDir( wxT( "Current" ) );
  81. python.AppendDir( wxT( "bin" ) );
  82. python.SetFullName(wxT( "python3" ) );
  83. wxString interpreter = python.GetFullPath();
  84. #else
  85. wxString interpreter = wxString::FromUTF8Unchecked( PYTHON_EXECUTABLE );
  86. #endif
  87. if( interpreter.IsEmpty() )
  88. interpreter = wxT( "python" ); // For macOS, should we log here? Error here?
  89. m_cmd = wxString::Format( "%s \"%s\" \"%%I\" \"%%O%s\"",
  90. interpreter,
  91. m_file.GetFullPath(),
  92. getOutputExtension( m_info ) );
  93. #endif
  94. }
  95. #ifdef __WINDOWS__
  96. else if( extension == wxS( "pyw" ) )
  97. {
  98. m_info = readHeader( wxS( "\"\"\"" ) );
  99. m_cmd = wxString::Format( wxS( "pythonw \"%s/%s\" \"%%I\" \"%%O%s\"" ),
  100. m_file.GetPath(),
  101. m_file.GetFullName(),
  102. getOutputExtension( m_info ) );
  103. }
  104. #endif /* __WINDOWS__ */
  105. else // fallback
  106. {
  107. m_cmd = m_file.GetFullPath();
  108. }
  109. wxLogTrace( BOM_TRACE, wxS( "%s: extracted command line %s" ), m_name, m_cmd );
  110. }
  111. bool BOM_GENERATOR_HANDLER::IsValidGenerator( const wxString& aFile )
  112. {
  113. wxFileName fn( aFile );
  114. wxString ext = fn.GetExt().Lower();
  115. for( const auto& pluginExt : { wxS( "xsl" ), wxS( "py" ), wxS( "pyw" ) } )
  116. {
  117. if( pluginExt == ext )
  118. return true;
  119. }
  120. return false;
  121. }
  122. wxString BOM_GENERATOR_HANDLER::readHeader( const wxString& aEndSection )
  123. {
  124. if( aEndSection.IsEmpty() )
  125. return wxEmptyString;
  126. wxFFile fdata( m_file.GetFullPath(), wxS( "rb" ) ); // dtor will close the file
  127. wxString data;
  128. if( !fdata.ReadAll( &data ) )
  129. return wxEmptyString;
  130. const wxString header( wxS( "@package" ) );
  131. // Extract substring between @package and endsection
  132. size_t strstart = data.find( header );
  133. if( strstart == wxString::npos )
  134. return wxEmptyString;
  135. strstart += header.Length();
  136. size_t strend = data.find( aEndSection, strstart );
  137. if( strend == wxString::npos )
  138. return wxEmptyString;
  139. // Remove empty line if any
  140. while( data[strstart] < ' ' )
  141. strstart++;
  142. return data.SubString( strstart, strend - 1 );
  143. }
  144. wxString BOM_GENERATOR_HANDLER::getOutputExtension( const wxString& aHeader )
  145. {
  146. // search header for extension after %O (extension includes '.')
  147. // looks for output argument of the form `"%O.extension"`
  148. const wxString outputarg( wxS( "\"%O" ) );
  149. size_t strstart = aHeader.find( outputarg );
  150. if( strstart == wxString::npos )
  151. return wxEmptyString;
  152. strstart += outputarg.Length();
  153. size_t strend = aHeader.find( wxS( "\"" ), strstart );
  154. if( strend == wxString::npos )
  155. return wxEmptyString;
  156. return aHeader.SubString( strstart, strend - 1 );
  157. }
  158. wxFileName BOM_GENERATOR_HANDLER::FindFilePath() const
  159. {
  160. if( m_file.IsAbsolute() && m_file.Exists( wxFILE_EXISTS_REGULAR ) )
  161. {
  162. wxLogTrace( BOM_TRACE, wxS( "%s found directly" ), m_file.GetFullPath() );
  163. return m_file;
  164. }
  165. wxFileName test( PATHS::GetUserPluginsPath(), m_file.GetName(), m_file.GetExt() );
  166. if( test.Exists( wxFILE_EXISTS_REGULAR ) )
  167. {
  168. wxLogTrace( BOM_TRACE, wxS( "%s found in user plugins path %s" ), m_file.GetFullName(),
  169. PATHS::GetUserPluginsPath() );
  170. return test;
  171. }
  172. test = wxFileName( PATHS::GetStockPluginsPath(), m_file.GetName(), m_file.GetExt() );
  173. if( test.Exists( wxFILE_EXISTS_REGULAR ) )
  174. {
  175. wxLogTrace( BOM_TRACE, wxS( "%s found in stock plugins path %s" ), m_file.GetFullName(),
  176. PATHS::GetStockPluginsPath() );
  177. return test;
  178. }
  179. wxLogTrace( BOM_TRACE, wxS( "Could not find %s (checked %s, %s)" ), m_file.GetFullName(),
  180. PATHS::GetUserPluginsPath(), PATHS::GetStockPluginsPath() );
  181. return m_file;
  182. }