Files
GDevelop/Core/GDCore/IDE/ExtensionsLoader.cpp
T

297 lines
11 KiB
C++

/*
* GDevelop Core
* Copyright 2008-2014 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
* This project is released under the MIT License.
*/
#include <boost/version.hpp>
#include "GDCore/PlatformDefinition/PlatformExtension.h"
#include "GDCore/PlatformDefinition/Platform.h"
#include "GDCore/IDE/ExtensionsLoader.h"
#include "GDCore/Tools/Version.h"
#include "GDCore/Tools/DynamicLibrariesTools.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Tools/Log.h"
#if defined(GD_IDE_ONLY) && !defined(GD_NO_WX_GUI)
#include <wx/msgdlg.h>
#include <wx/filename.h>
#include "GDCore/Tools/Locale/LocaleManager.h"
#endif
//Compiler specific include, for listing files of directory ( see below )
#if defined(__GNUC__)
#include <dirent.h>
#elif defined(_MSC_VER)
#include <windows.h>
#endif
typedef gd::PlatformExtension* (*createExtension)();
using namespace std;
namespace gd
{
void ExtensionsLoader::LoadAllExtensions(const std::string & directory, gd::Platform & platform, bool forgiving)
{
std::cout << "Loading extensions for " << platform.GetName() << "... ";
string suffix = "";
#if defined(WINDOWS)
suffix += "w";
#elif defined(LINUX)
suffix += "l";
#elif defined(MAC)
suffix += "m";
#else
#warning No target system defined.
#endif
#if defined(GD_IDE_ONLY)
suffix += "e";
#endif
#if defined(__GNUC__) //For compilers with posix support
struct dirent *lecture;
DIR *rep;
rep = opendir( directory.c_str() );
int l = 0;
if ( rep == NULL )
{
cout << "Unable to open Extensions ("<< directory <<") directory." << endl;
return;
}
std::vector<std::string> librariesLoaded;
while ( (lecture = readdir( rep )) )
{
string lec = lecture->d_name;
if ( lec != "." && lec != ".." && lec.find(".xgd"+suffix, lec.length()-4-suffix.length()) != string::npos)
{
//Use a log file, in IDE only
#if defined(GD_IDE_ONLY) && !defined(GD_NO_WX_GUI)
{
wxFile errorDetectFile(wxFileName::GetTempDir()+"/ExtensionBeingLoaded.log", wxFile::write);
errorDetectFile.Write(directory+"/"+lec);
}
#endif
LoadExtension(directory+"/"+lec, platform, forgiving);
//Everything is ok : Delete the log file
#if defined(GD_IDE_ONLY) && !defined(GD_NO_WX_GUI)
wxRemoveFile(wxFileName::GetTempDir()+"/ExtensionBeingLoaded.log");
#endif
librariesLoaded.push_back(directory+"/"+lec);
l++;
}
}
closedir( rep );
#elif defined(_MSC_VER)
WIN32_FIND_DATA f;
string dirPart = "/*.xgd";
string dirComplete = directory + dirPart + suffix;
HANDLE h = FindFirstFile(dirComplete.c_str(), &f);
if(h != INVALID_HANDLE_VALUE)
{
do
{
//Use a log file, in IDE only
#if defined(GD_IDE_ONLY) && !defined(GD_NO_WX_GUI)
{
wxFile errorDetectFile(wxFileName::GetTempDir()+"/ExtensionBeingLoaded.log", wxFile::write);
errorDetectFile.Write(f.cFileName);
}
#endif
LoadExtension(f.cFileName, platform, forgiving);
//Everything is ok : Delete the log file
#if defined(GD_IDE_ONLY) && !defined(GD_NO_WX_GUI)
wxRemoveFile(wxFileName::GetTempDir()+"/ExtensionBeingLoaded.log");
#endif
} while(FindNextFile(h, &f));
}
#else
#warning Compiler not supported (but might support one style of directory listing, update defines if necessary) for dynamic libraries loading
#endif
std::cout << " done. " << std::endl;
}
void ExtensionsLoader::ExtensionsLoadingDone(const std::string & directory)
{
string suffix = "";
#if defined(WINDOWS)
suffix += "w";
#elif defined(LINUX)
suffix += "l";
#elif defined(MAC)
suffix += "m";
#else
#warning No target system defined.
#endif
#if defined(GD_IDE_ONLY)
suffix += "e";
#endif
#if defined(LINUX) || defined (MAC)
//List all extensions loaded
struct dirent *lecture;
DIR *rep;
rep = opendir( directory.c_str() );
int l = 0;
if ( rep == NULL )
{
cout << "Unable to open Extensions ("<< directory <<") directory." << endl;
return;
}
std::vector<std::string> librariesLoaded;
while ( (lecture = readdir( rep )) )
{
string lec = lecture->d_name;
if ( lec != "." && lec != ".." && lec.find(".xgd"+suffix, lec.length()-4-suffix.length()) != string::npos)
{
librariesLoaded.push_back(directory+"/"+lec);
l++;
}
}
closedir( rep );
//Libraries are loaded using dlopen(.., RTLD_LAZY|RTLD_LOCAL) meaning that their symbols are not available for other libraries
//nor for LLVM/Clang. We then reload set them as global to make their symbols available for LLVM/Clang. We couldn't mark them
//as global when loading them as every extension use the same "CreateGDExtension" symbol.
//SetLibraryGlobal is also setting RTLD_NOW to ensure that all symbols are resolved: Otherwise, we can get weird
//"symbol lookup error" even if the symbols exist in the extensions!
for (unsigned int i = 0;i<librariesLoaded.size();++i)
SetLibraryGlobal(librariesLoaded[i].c_str());
#else
//Nothing to do on Windows.
#endif
}
void ExtensionsLoader::LoadExtension(const std::string & fullpath, gd::Platform & platform, bool forgiving)
{
if ( platform.GetExtensionCreateFunctionName().empty() )
{
cout << "Unable to load extension " << fullpath << ":" << endl;
cout << "The plaftorm does not support extensions creation." << endl;
return;
}
Handle extensionHdl = OpenLibrary(fullpath.c_str());
if (extensionHdl == NULL)
{
std::string error = DynamicLibraryLastError();
cout << "Unable to load extension " << fullpath << "." << endl;
cout << "Error returned : \"" << error << "\"" << endl;
#if defined(GD_IDE_ONLY) && !defined(GD_NO_WX_GUI)
wxString userMsg = string(_("Extension "))+ fullpath + string(_(" could not be loaded.\nContact the developer for more informations.\n\nDetailed log:\n") + error);
wxMessageBox(userMsg, _("Extension not compatible"), wxOK | wxICON_EXCLAMATION);
#endif
return;
}
createExtension create_extension = (createExtension)GetSymbol(extensionHdl, platform.GetExtensionCreateFunctionName().c_str());
if (create_extension == NULL)
{
if (!forgiving)
{
cout << "Unable to load extension " << fullpath << " (Creation function symbol not found)." << endl;
#if defined(GD_IDE_ONLY) && !defined(GD_NO_WX_GUI)
wxString userMsg = string(_("Extension "))+ fullpath + string(_(" could not be loaded.\nContact the developer for more informations." ));
wxMessageBox(userMsg, _("Extension not compatible"), wxOK | wxICON_EXCLAMATION);
#endif
}
CloseLibrary(extensionHdl);
return;
}
#if defined(GD_IDE_ONLY) && !defined(GD_NO_WX_GUI)
gd::LocaleManager::Get()->AddCatalog(ToString(wxFileName(fullpath).GetName())); //In editor, load catalog associated with extension, if any.
#endif
gd::PlatformExtension * extensionPtr = create_extension();
string error;
//Perform safety check about the compilation
if ( !extensionPtr->compilationInfo.informationCompleted )
error += "Compilation information not filled.\n";
#if defined(GD_IDE_ONLY)
else if ( extensionPtr->compilationInfo.runtimeOnly )
error += "Extension compiled for runtime only.\n";
#if !defined(GD_NO_WX_GUI)
else if ( extensionPtr->compilationInfo.wxWidgetsMajorVersion != wxMAJOR_VERSION ||
extensionPtr->compilationInfo.wxWidgetsMinorVersion != wxMINOR_VERSION ||
extensionPtr->compilationInfo.wxWidgetsReleaseNumber != wxRELEASE_NUMBER ||
extensionPtr->compilationInfo.wxWidgetsSubReleaseNumber != wxSUBRELEASE_NUMBER )
error += "Not the same wxWidgets version.\n";
#endif
#endif
#if defined(__GNUC__)
else if ( extensionPtr->compilationInfo.gccMajorVersion != __GNUC__ ||
extensionPtr->compilationInfo.gccMinorVersion != __GNUC_MINOR__ )
error += "Not the same GNU Compiler version.\n";
#endif
else if ( extensionPtr->compilationInfo.sfmlMajorVersion != 2 ||
extensionPtr->compilationInfo.sfmlMinorVersion != 0 )
error += "Not the same SFML version.\n";
else if ( extensionPtr->compilationInfo.boostVersion != BOOST_VERSION )
error += "Not the same Boost version.\n(Extension is using "+ToString(extensionPtr->compilationInfo.boostVersion)+", GDevelop is using "+ToString(BOOST_VERSION)+")\n";
else if ( extensionPtr->compilationInfo.gdCoreVersion != GDCore_RC_FILEVERSION_STRING)
error += "Not the same GDevelop Core version.\n(Extension is using "+extensionPtr->compilationInfo.gdCoreVersion+", GDevelop is using "+GDCore_RC_FILEVERSION_STRING+")\n";
else if ( extensionPtr->compilationInfo.sizeOfpInt != sizeof(int*))
error += "Not the same architecture.\n(Extension sizeof(int*) is "+ToString(extensionPtr->compilationInfo.sizeOfpInt)+", GDevelop sizeof(int*) is "+ToString(sizeof(int*))+")\n";
if ( !error.empty() )
{
char beep = 7;
cout << "-- WARNING ! --" << beep << endl;
cout << "Bad extension " + fullpath + " loaded :\n" + error;
cout << "---------------" << endl;
#if defined(RELEASE)//Load extension despite errors in non release build
//Destroy the extension class THEN unload the library from memory
delete extensionPtr;
CloseLibrary(extensionHdl);
#endif
#if defined(GD_IDE_ONLY) && !defined(GD_NO_WX_GUI) && defined(RELEASE) //Show errors in IDE only
wxString userMsg = string(_("Extension "))+ fullpath + string(_(" has errors :\n")) + error + string(_("\nThe extension was not loaded. Contact the developer to get more information." ));
wxMessageBox(userMsg, _("Extension not compatible"), wxOK | wxICON_EXCLAMATION);
#endif
#if defined(RELEASE)//Load extension despite errors in non release build
return;
#endif
}
boost::shared_ptr<gd::PlatformExtension> extension(extensionPtr);
platform.AddExtension(extension);
return;
}
}