single_top.cpp 9.69 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
 * 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
 */


/*

Dick Hollenbeck's avatar
Dick Hollenbeck committed
28 29 30
    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.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109

*/

#include <macros.h>
#include <fctsys.h>
#include <wx/dynlib.h>
#include <wx/filename.h>
#include <kiway.h>
#include <wx/stdpaths.h>


/**
 * 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


Dick Hollenbeck's avatar
Dick Hollenbeck committed
110 111
// POLICY CHOICE: return the full path of the DSO to load from single_top.
static const wxString dso_full_path( const wxString& aAbsoluteArgv0 )
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
{
    // 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 );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
132
    wxString    basename( KIFACE_PREFIX );  // start with special prefix
133

Dick Hollenbeck's avatar
Dick Hollenbeck committed
134
    basename += fn.GetName();               // add argv[0]'s basename
135
    fn.SetName( basename );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
136 137 138 139

    // 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]
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162

    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
Dick Hollenbeck's avatar
Dick Hollenbeck committed
163 164
    // 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.
165 166

    // This directory POLICY CHOICE is not the only dir in play, since LIB_ENV_VAR
Dick Hollenbeck's avatar
Dick Hollenbeck committed
167
    // has numerous path options in it, as does DSO searching on linux, windows, and OSX.
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
    // 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
}


Dick Hollenbeck's avatar
Dick Hollenbeck committed
190
// Only a single KIWAY is supported in this single_top top level component,
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
// 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
Dick Hollenbeck's avatar
Dick Hollenbeck committed
206 207
 *  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.
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
 */
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();

Dick Hollenbeck's avatar
Dick Hollenbeck committed
242
#if defined(DEBUG)
243 244 245 246 247 248
    wxString dir = paths.GetLocalizedResourcesDir( wxT( "de" ),
                        wxStandardPaths::ResourceCat_None );

    printf( "LocalizeResourcesDir:'%s'\n",  TO_UTF8( dir ) );

    wxString dummy( _( "translate this" ) );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
249
#endif
250 251 252

    wxString absoluteArgv0 = paths.GetExecutablePath();

Dick Hollenbeck's avatar
Dick Hollenbeck committed
253
#if 0 && defined(DEBUG)
254 255 256 257 258 259 260 261 262 263 264 265
    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;
    }

Dick Hollenbeck's avatar
Dick Hollenbeck committed
266 267
    // 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.
268 269
    SetLibEnvVar( absoluteArgv0 );

Dick Hollenbeck's avatar
Dick Hollenbeck committed
270
    wxString dname = dso_full_path( absoluteArgv0 );
271 272 273 274 275

    // Get the getter.
    KIFACE_GETTER_FUNC* getter = get_kiface_getter( dname );

    if( !getter )
Dick Hollenbeck's avatar
Dick Hollenbeck committed
276 277 278
    {
        // get_kiface_getter() failed & already showed the UI message.
        // Return failure without any further UI.
279
        return false;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
280
    }
281 282 283 284 285

    // Get the KIFACE, and give the DSO a single chance to do its
    // "process level" initialization.
    kiface = getter( &kiface_version, KIFACE_VERSION, &wxGetApp() );

Dick Hollenbeck's avatar
Dick Hollenbeck committed
286 287
    // 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*" ) );
288

Dick Hollenbeck's avatar
Dick Hollenbeck committed
289 290
    // Use KIFACE to create a window that the KIFACE knows about,
    // pass classId=0 for now.  KIFACE::CreateWindow() is a virtual
291 292 293 294 295 296 297 298 299 300 301 302
    // so we don't need to link to it.
    wxFrame* frame = (wxFrame*) kiface->CreateWindow( 0, &standalone );

    SetTopWindow( frame );

    frame->Centre();

    frame->Show();

    return true;
}