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.

227 lines
7.2 KiB

  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 <maciej.suminski@cern.ch>
  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. #include "bom_plugins.h"
  25. #include <config.h>
  26. #include <paths.h>
  27. #include <wx/ffile.h>
  28. #include <wx/log.h>
  29. const wxChar BOM_TRACE[] = wxT( "BOM_GENERATORS" );
  30. BOM_GENERATOR_HANDLER::BOM_GENERATOR_HANDLER( const wxString& aFile )
  31. : m_storedPath( aFile )
  32. {
  33. m_isOk = false;
  34. m_file = wxFileName( aFile );
  35. if( !wxFile::Exists( m_file.GetFullPath() ) )
  36. m_file = FindFilePath();
  37. if( !wxFile::Exists( m_file.GetFullPath() ) )
  38. {
  39. m_info.Printf( _("Script file:\n%s\nnot found. Script not available."), aFile );
  40. return;
  41. }
  42. m_isOk = true;
  43. m_name = m_file.GetName();
  44. wxString extension = m_file.GetExt().Lower();
  45. // Important note:
  46. // On Windows the right command command to run a python script is:
  47. // python <script_path>/script.py
  48. // and *not* python <script_path>\script.py
  49. // Otherwise the script does not find some auxiliary pythons scripts needed by this script
  50. if( extension == "xsl" )
  51. {
  52. m_info = readHeader( "-->" );
  53. m_cmd = wxString::Format( "xsltproc -o \"%%O%s\" \"%s\" \"%%I\"",
  54. getOutputExtension( m_info ),
  55. m_file.GetFullPath() );
  56. }
  57. else if( extension == "py" )
  58. {
  59. m_info = readHeader( "\"\"\"" );
  60. #ifdef __WINDOWS__
  61. m_cmd = wxString::Format( "python \"%s/%s\" \"%%I\" \"%%O%s\"",
  62. m_file.GetPath(),
  63. m_file.GetFullName(),
  64. getOutputExtension( m_info ) );
  65. #else
  66. // For macOS, we want to use the Python we bundle along, rather than just PYTHON_EXECUTABLE.
  67. // For non-Windows, non-macOS, we can call out to PYTHON_EXECUTABLE.
  68. #ifdef __APPLE__
  69. // python is at Contents/Frameworks/Python.framework/Versions/Current/bin/python3
  70. // Of course, for macOS, it's not quite that simple, since the relative path
  71. // will depend on if we are in standalone mode or not.
  72. // (If we're going to call out to python like this in other places, we probably want to
  73. // think about pulling this into PATHS.)
  74. wxFileName python( PATHS::GetOSXKicadDataDir(), wxEmptyString );
  75. python.RemoveLastDir();
  76. python.AppendDir( wxT( "Frameworks" ) );
  77. python.AppendDir( wxT( "Python.framework" ) );
  78. python.AppendDir( wxT( "Versions" ) );
  79. python.AppendDir( wxT( "Current" ) );
  80. python.AppendDir( wxT( "bin" ) );
  81. python.SetFullName(wxT( "python3" ) );
  82. wxString interpreter = python.GetFullPath();
  83. #else
  84. wxString interpreter = wxString::FromUTF8Unchecked( PYTHON_EXECUTABLE );
  85. #endif
  86. if( interpreter.IsEmpty() )
  87. interpreter = wxT( "python" ); // For macOS, should we log here? Error here?
  88. m_cmd = wxString::Format( "%s \"%s\" \"%%I\" \"%%O%s\"",
  89. interpreter,
  90. m_file.GetFullPath(),
  91. getOutputExtension( m_info ) );
  92. #endif
  93. }
  94. #ifdef __WINDOWS__
  95. else if( extension == "pyw" )
  96. {
  97. m_info = readHeader( "\"\"\"" );
  98. m_cmd = wxString::Format( "pythonw \"%s/%s\" \"%%I\" \"%%O%s\"",
  99. m_file.GetPath(),
  100. m_file.GetFullName(),
  101. getOutputExtension( m_info ) );
  102. }
  103. #endif /* __WINDOWS__ */
  104. else // fallback
  105. {
  106. m_cmd = m_file.GetFullPath();
  107. }
  108. wxLogTrace( BOM_TRACE, "%s: extracted command line %s", m_name, m_cmd );
  109. }
  110. bool BOM_GENERATOR_HANDLER::IsValidGenerator( const wxString& aFile )
  111. {
  112. wxFileName fn( aFile );
  113. wxString ext = fn.GetExt().Lower();
  114. for( const auto& pluginExt : { "xsl", "py", "pyw" } )
  115. {
  116. if( pluginExt == ext )
  117. return true;
  118. }
  119. return false;
  120. }
  121. wxString BOM_GENERATOR_HANDLER::readHeader( const wxString& aEndSection )
  122. {
  123. if( aEndSection.IsEmpty() )
  124. return wxEmptyString;
  125. wxFFile fdata( m_file.GetFullPath(), "rb" ); // dtor will close the file
  126. wxString data;
  127. if( !fdata.ReadAll( &data ) )
  128. return wxEmptyString;
  129. const wxString header( "@package" );
  130. // Extract substring between @package and endsection
  131. int strstart = data.Find( header );
  132. if( strstart == wxNOT_FOUND )
  133. return wxEmptyString;
  134. strstart += header.Length();
  135. int strend = data.find( aEndSection, strstart );
  136. if( strend == wxNOT_FOUND )
  137. return wxEmptyString;
  138. // Remove empty line if any
  139. while( data[strstart] < ' ' )
  140. strstart++;
  141. return data.SubString( strstart, strend - 1 );
  142. }
  143. wxString BOM_GENERATOR_HANDLER::getOutputExtension( const wxString& aHeader )
  144. {
  145. // search header for extension after %O (extension includes '.')
  146. // looks for output argument of the form `"%O.extension"`
  147. const wxString outputarg( "\"%O" );
  148. int strstart = aHeader.Find( outputarg );
  149. if( strstart == wxNOT_FOUND )
  150. return wxEmptyString;
  151. strstart += outputarg.Length();
  152. int strend = aHeader.find( "\"", strstart );
  153. if( strend == wxNOT_FOUND )
  154. return wxEmptyString;
  155. return aHeader.SubString( strstart, strend - 1 );
  156. }
  157. wxFileName BOM_GENERATOR_HANDLER::FindFilePath() const
  158. {
  159. if( m_file.IsAbsolute() && m_file.Exists( wxFILE_EXISTS_REGULAR ) )
  160. {
  161. wxLogTrace( BOM_TRACE, "%s found directly", m_file.GetFullPath() );
  162. return m_file;
  163. }
  164. wxFileName test( PATHS::GetUserPluginsPath(), m_file.GetName(), m_file.GetExt() );
  165. if( test.Exists( wxFILE_EXISTS_REGULAR ) )
  166. {
  167. wxLogTrace( BOM_TRACE, "%s found in user plugins path %s", m_file.GetFullName(),
  168. PATHS::GetUserPluginsPath() );
  169. return test;
  170. }
  171. test = wxFileName( PATHS::GetStockPluginsPath(), m_file.GetName(), m_file.GetExt() );
  172. if( test.Exists( wxFILE_EXISTS_REGULAR ) )
  173. {
  174. wxLogTrace( BOM_TRACE, "%s found in stock plugins path %s", m_file.GetFullName(),
  175. PATHS::GetStockPluginsPath() );
  176. return test;
  177. }
  178. wxLogTrace( BOM_TRACE, "Could not find %s (checked %s, %s)", m_file.GetFullName(),
  179. PATHS::GetUserPluginsPath(), PATHS::GetStockPluginsPath() );
  180. return m_file;
  181. }