/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2014 KiCad Developers, see CHANGELOG.TXT for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ /* This is a program launcher for a single KIFACE DSO. Initially it will only mimic a KIWAY, not actually implement one, since only a single DSO is supported by it. */ #include #include #include #include #include #include /** * Class PROCESS * provides its own OnInit() handler. */ class PROCESS : public wxApp { public: bool OnInit(); }; IMPLEMENT_APP( PROCESS ) #if !wxCHECK_VERSION( 3, 0, 0 ) // implement missing wx2.8 function until >= wx3.0 pervades. static wxString wxJoin(const wxArrayString& arr, const wxChar sep, const wxChar escape = '\\') { size_t count = arr.size(); if ( count == 0 ) return wxEmptyString; wxString str; // pre-allocate memory using the estimation of the average length of the // strings in the given array: this is very imprecise, of course, but // better than nothing str.reserve(count*(arr[0].length() + arr[count-1].length()) / 2); if ( escape == wxT('\0') ) { // escaping is disabled: for ( size_t i = 0; i < count; i++ ) { if ( i ) str += sep; str += arr[i]; } } else // use escape character { for ( size_t n = 0; n < count; n++ ) { if ( n ) str += sep; for ( wxString::const_iterator i = arr[n].begin(), end = arr[n].end(); i != end; ++i ) { const wxChar ch = *i; if ( ch == sep ) str += escape; // escape this separator str += ch; } } } str.Shrink(); // release extra memory if we allocated too much return str; } #endif // POLICY CHOICE: return the full path of the DSO to load from single_top. static const wxString dso_full_path( const wxString& aAbsoluteArgv0 ) { // Prefix basename with '_' and change extension to DSO_EXT. // POLICY CHOICE: Keep same path, and therefore installer must put the major DSO // in same dir as top process module. Obviously alternatives are possible // and that is why this is a separate function. One alternative would be to use // a portion of CMAKE_INSTALL_PREFIX and navigate to a "lib" dir, but that // would require a recompile any time you chose to install into a different place. // It is my decision to treat _eeschema.so and _pcbnew.so as "executables", // not "libraries" in this regard, since most all program functionality lives // in them. They are basically spin-offs from what was once a top process module. // That may not make linux package maintainers happy, but that is not my job. // Get over it. KiCad is not a trivial suite, and multiple platforms come // into play, not merely linux. If it freaks you out, we can use a different // file extension than ".so", but they are not purely libraries, else they // would begin with "lib" in basename. Like I said, get over it, we're serving // too many masters here: python, windows, linux, OSX, multiple versions of wx... wxFileName fn( aAbsoluteArgv0 ); wxString basename( KIFACE_PREFIX ); // start with special prefix basename += fn.GetName(); // add argv[0]'s basename fn.SetName( basename ); // here a suffix == an extension with a preceding '.', // so skip the preceding '.' to get an extension fn.SetExt( KIFACE_SUFFIX + 1 ); // special extension, + 1 => &KIFACE_SUFFIX[1] return fn.GetFullPath(); } /// Put aPriorityPath in front of all paths in the value of aEnvVar. const wxString PrePendPath( const wxString& aEnvVar, const wxString& aPriorityPath ) { wxPathList paths; paths.AddEnvList( aEnvVar ); paths.Insert( aPriorityPath, 0 ); return wxJoin( paths, wxPATH_SEP[0] ); } /// Extend LIB_ENV_VAR list with the directory from which I came, prepending it. void SetLibEnvVar( const wxString& aAbsoluteArgv0 ) { // POLICY CHOICE: Keep same path, so that installer MAY put the // "subsidiary shared libraries" in the same directory as the top process module. // A subsidiary shared library is one that is not a top level DSO, but rather // some shared library that a top level DSO needs to even be loaded. It is // a static link to a shared object from a top level DSO. // This directory POLICY CHOICE is not the only dir in play, since LIB_ENV_VAR // has numerous path options in it, as does DSO searching on linux, windows, and OSX. // See "man ldconfig" on linux. What's being done here is for quick installs // into a non-standard place, and especially for Windows users who may not // know what the PATH environment variable is or how to set it. wxFileName fn( aAbsoluteArgv0 ); wxString ld_path( LIB_ENV_VAR ); wxString my_path = fn.GetPath(); wxString new_paths = PrePendPath( ld_path, my_path ); wxSetEnv( ld_path, new_paths ); #if defined(DEBUG) { wxString test; wxGetEnv( ld_path, &test ); printf( "LIB_ENV_VAR:'%s'\n", TO_UTF8( test ) ); } #endif } // Only a single KIWAY is supported in this single_top top level component, // which is dedicated to loading only a single DSO. static KIWAY standalone; // Use of this is arbitrary, remember single_top only knows about a single DSO. // Could have used one from the KIWAY also. static wxDynamicLibrary dso; /** * Function get_kiface_getter * returns a KIFACE_GETTER_FUNC for the current process's main implemation link image. * * @param aDSOName is an absolute full path to the DSO to load and find KIFACE_GETTER_FUNC within. * * @return KIFACE_GETTER_FUNC* - a pointer to a function which can be called to get the KIFACE * or NULL if the getter func was not found. If not found, it is possibly not version compatible * since the lookup is done by name and the name contains the API version. */ static KIFACE_GETTER_FUNC* get_kiface_getter( const wxString& aDSOName ) { void* addr = NULL; if( !dso.Load( aDSOName, wxDL_VERBATIM | wxDL_NOW ) ) { // Failure: error reporting UI was done via wxLogSysError(). // No further reporting required here. } else if( ( addr = dso.GetSymbol( wxT( KIFACE_INSTANCE_NAME_AND_VERSION ) ) ) == NULL ) { // Failure: error reporting UI was done via wxLogSysError(). // No further reporting required here. } return (KIFACE_GETTER_FUNC*) addr; } static KIFACE* kiface; static int kiface_version; bool PROCESS::OnInit() { // Choose to use argv command line processing in base class's OnInit(). // That choice is not mandatory, see wx's appbase.cpp OnInit(). if( !wxApp::OnInit() ) return false; wxStandardPathsBase& paths = wxStandardPaths::Get(); #if defined(DEBUG) wxString dir = paths.GetLocalizedResourcesDir( wxT( "de" ), wxStandardPaths::ResourceCat_None ); printf( "LocalizeResourcesDir:'%s'\n", TO_UTF8( dir ) ); wxString dummy( _( "translate this" ) ); #endif wxString absoluteArgv0 = paths.GetExecutablePath(); #if 0 && defined(DEBUG) printf( "argv[0]:'%s' absoluteArgv0:'%s'\n", TO_UTF8( wxString( argv[0] ) ), TO_UTF8( absoluteArgv0 ) ); #endif if( !wxIsAbsolutePath( absoluteArgv0 ) ) { wxLogSysError( wxT( "No meaningful argv[0]" ) ); return false; } // Set LIB_ENV_VAR *before* loading the DSO, in case the top-level DSO holding the // KIFACE has hard dependencies on subsidiary DSOs below it. SetLibEnvVar( absoluteArgv0 ); wxString dname = dso_full_path( absoluteArgv0 ); // Get the getter. KIFACE_GETTER_FUNC* getter = get_kiface_getter( dname ); if( !getter ) { // get_kiface_getter() failed & already showed the UI message. // Return failure without any further UI. return false; } // Get the KIFACE, and give the DSO a single chance to do its // "process level" initialization. kiface = getter( &kiface_version, KIFACE_VERSION, &wxGetApp() ); // KIFACE_GETTER_FUNC function comment (API) says the non-NULL is uconditional. wxASSERT_MSG( kiface, wxT( "attempted DSO has a bug, failed to return a KIFACE*" ) ); // Use KIFACE to create a window that the KIFACE knows about, // pass classId=0 for now. KIFACE::CreateWindow() is a virtual // so we don't need to link to it. wxFrame* frame = (wxFrame*) kiface->CreateWindow( 0, &standalone ); SetTopWindow( frame ); frame->Centre(); frame->Show(); return true; }