← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 3271: plugins: OS differentiation in dcext; name the install dir from the UUID; rework errors

 

------------------------------------------------------------
revno: 3271
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Tue 2013-04-23 17:34:45 +0200
message:
  plugins: OS differentiation in dcext; name the install dir from the UUID; rework errors
modified:
  Plugin format (dcext).txt
  dcpp/PluginManager.cpp
  dcpp/PluginManager.h
  win32/PluginInfoDlg.cpp
  win32/PluginInfoDlg.h
  win32/PluginPage.cpp


--
lp:dcplusplus
https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk

Your team Dcplusplus-team is subscribed to branch lp:dcplusplus.
To unsubscribe from this branch go to https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk/+edit-subscription
=== modified file 'Plugin format (dcext).txt'
--- Plugin format (dcext).txt	2013-04-22 22:31:37 +0000
+++ Plugin format (dcext).txt	2013-04-23 15:34:45 +0000
@@ -34,13 +34,18 @@
 - "Description" (optional): Short description of the plugin.
 - "Website" (optional): Plugin website.
 
-- "Plugin" (compulsory): Location of the loadable shared extension within the archive. The optional
-	"Arch" attribute of this tag designates the architecture this plugin has been compiled for. It
-	may be one of "x86", "x64"; "x86" is assumed by default in the absence of this attribute.
-	Multiple "Plugin" tags may be provided for different architectures.
+- "Plugin" (compulsory): Location of the loadable shared extension within the archive. The required
+	"Platform" attribute of this tag must be a value from the "Platform codes" list below. Multiple
+	"Plugin" tags may be provided for different platforms.
 - "Files" (optional): Additional files required by the plugin, each within a "File" tag. "File"
-	tags may contain an "Arch" attribute to specify the architecture the file works on; files are
-	assumed to target every architecture by default.
+	tags may contain an optional "Platform" attribute which must be a value from the "Platform
+	codes" list below; in its absence, files are assumed to be platform-independant by default.
+
+Platform codes:
+- elf-x64: ELF format, x64 architecture.
+- elf-x86: ELF format, x86 architecture.
+- pe-x64: PE format, x64 architecture.
+- pe-x86: PE format, x86 architecture.
 
 Example info.xml:
 
@@ -53,11 +58,13 @@
 	<Author>Test team</Author>
 	<Description>Plugin to do X</Description>
 	<Website>http://example.com</Website>
-	<Plugin Arch="x86">x86/TestPlugin.so</Plugin>
-	<Plugin Arch="x64">x64/TestPlugin.so</Plugin>
+	<Plugin Platform="elf-x64">x64/TestPlugin.so</Plugin>
+	<Plugin Platform="elf-x86">x86/TestPlugin.so</Plugin>
+	<Plugin Platform="pe-x64">x64/TestPlugin.dll</Plugin>
+	<Plugin Platform="pe-x86">x86/TestPlugin.dll</Plugin>
 	<Files>
 		<File>icons/TestPlugin.ico</File>
 		<File>fonts/cool.font</File>
-		<File Arch="x64">FasterHash.so</File>
+		<File Platform="elf-x64">FasterHash.so</File>
 	</Files>
 </dcext>

=== modified file 'dcpp/PluginManager.cpp'
--- dcpp/PluginManager.cpp	2013-04-22 22:31:37 +0000
+++ dcpp/PluginManager.cpp	2013-04-23 15:34:45 +0000
@@ -26,9 +26,11 @@
 #include "File.h"
 #include "LogManager.h"
 #include "QueueManager.h"
+#include "ScopedFunctor.h"
 #include "SimpleXML.h"
 #include "StringTokenizer.h"
 #include "UserConnection.h"
+#include "version.h"
 
 #include <utility>
 
@@ -91,14 +93,21 @@
 			}
 		};
 
-		auto checkArch = [&xml] {
-			auto arch = xml.getChildAttrib("Arch", "x86");
-#if defined(__x86_64__) || defined(_WIN64)
-			return arch == "x64";
-#elif defined(__i386__) || defined(_M_IX86)
-			return arch == "x86";
+		auto checkPlatform = [&xml](bool emptyAllowed) {
+			auto platform = xml.getChildAttrib("Platform");
+			if(emptyAllowed && platform.empty()) {
+				return true;
+			}
+#if defined(_WIN32) && defined(_WIN64)
+			return platform == "pe-x64";
+#elif defined(_WIN32)
+			return platform == "pe-x86";
+#elif defined(__x86_64__)
+			return platform == "elf-x64";
+#elif defined(__i386__)
+			return platform == "elf-x86";
 #else
-#error Unknown architecture
+#error Unknown platform
 #endif
 		};
 
@@ -117,7 +126,7 @@
 
 		xml.resetCurrentChild();
 		while(xml.findChild("Plugin")) {
-			if(checkArch()) {
+			if(checkPlatform(false)) {
 				info.plugin = xml.getChildData();
 			}
 		}
@@ -127,7 +136,7 @@
 			xml.stepIn();
 
 			while(xml.findChild("File")) {
-				if(checkArch()) {
+				if(checkPlatform(true)) {
 					info.files.push_back(xml.getChildData());
 				}
 			}
@@ -138,28 +147,27 @@
 		xml.stepOut();
 	}
 
-	if(info.uuid.empty() || info.name.empty() || info.version == 0 || info.plugin.empty()) {
-		throw str(F_("%1% is not a valid DC extension") % Util::getFileName(path));
-	}
-
-	{
-		Lock l(cs);
-
-		if(isLoaded(info.uuid)) {
-			throw str(F_("%1% is already installed") % Util::getFileName(path));
-		}
+	if(info.uuid.empty() || info.name.empty() || info.version == 0) {
+		throw Exception(str(F_("%1% is not a valid plugin") % Util::getFileName(path)));
+	}
+	if(info.plugin.empty()) {
+		throw Exception(str(F_("%1% is not compatible with %2%") % Util::getFileName(path) % APPNAME));
+	}
+
+	if(isLoaded(info.uuid)) {
+		throw Exception(str(F_("%1% is already installed") % Util::getFileName(path)));
 	}
 
 	return info;
 }
 
-void PluginManager::install(const string& name, const string& plugin, const StringList& files) {
-	if(name.empty() || plugin.empty()) {
+void PluginManager::install(const string& uuid, const string& plugin, const StringList& files) {
+	if(uuid.empty() || plugin.empty()) {
 		throw Exception();
 	}
 
 	const auto source = Util::getTempPath() + "dcext" PATH_SEPARATOR_STR;
-	const auto target = Util::getPath(Util::PATH_USER_LOCAL) + "Plugins" PATH_SEPARATOR_STR + name + PATH_SEPARATOR_STR;
+	const auto target = Util::getPath(Util::PATH_USER_LOCAL) + "Plugins" PATH_SEPARATOR_STR + uuid + PATH_SEPARATOR_STR;
 	const auto lib = target + Util::getFileName(plugin);
 
 	File::ensureDirectory(lib);
@@ -170,7 +178,7 @@
 		File::renameFile(source + file, target + file);
 	}
 
-	loadPlugin(lib, [](const string& err) { throw Exception(err); }, true);
+	loadPlugin(lib, true);
 }
 
 void PluginManager::loadPlugins(function<void (const string&)> f) {
@@ -182,73 +190,67 @@
 	loadSettings();
 
 	StringTokenizer<string> st(getPluginSetting("CoreSetup", "Plugins"), ";");
-	auto err = [](const string& str) { LogManager::getInstance()->message(str); };
 	for(auto& i: st.getTokens()) {
-		if(!loadPlugin(i, err) || !f) continue;
-		f(Util::getFileName(i));
+		if(f) { f(Util::getFileName(i)); }
+		try { loadPlugin(i); }
+		catch(const Exception& e) { LogManager::getInstance()->message(e.getError()); }
 	}
 }
 
-bool PluginManager::loadPlugin(const string& fileName, function<void (const string&)> err, bool install) {
+void PluginManager::loadPlugin(const string& fileName, bool install) {
 	Lock l(cs);
 
 	dcassert(dcCore.apiVersion != 0);
 
 	PluginHandle hr = LOAD_LIBRARY(fileName);
 	if(!hr) {
-		err(str(F_("Error loading %1%: %2%") % Util::getFileName(fileName) % GET_ERROR()));
-		return false;
+		throw Exception(str(F_("Error loading %1%: %2%") % Util::getFileName(fileName) % GET_ERROR()));
 	}
+	ScopedFunctor([&hr] { if(hr) FREE_LIBRARY(hr); });
 
 	PluginInfo::PLUGIN_INIT pluginInfo = reinterpret_cast<PluginInfo::PLUGIN_INIT>(GET_ADDRESS(hr, "pluginInit"));
-
-	if(pluginInfo != NULL) {
-		MetaData info = { 0 };
-		DCMAIN dcMain;
-		if((dcMain = pluginInfo(&info))) {
-			if(checkPlugin(info, err)) {
-				if(dcMain((install ? ON_INSTALL : ON_LOAD), &dcCore, NULL) != False) {
-					plugins.emplace_back(new PluginInfo(fileName, hr, info, dcMain));
-					return true;
-				}
-			}
-		}
-	} else err(str(F_("%1% is not a valid plugin") % Util::getFileName(fileName)));
-
-	FREE_LIBRARY(hr);
-	return false;
+	MetaData info { };
+	DCMAIN dcMain;
+	if(!pluginInfo || !(dcMain = pluginInfo(&info))) {
+		throw Exception(str(F_("%1% is not a valid plugin") % Util::getFileName(fileName)));
+	}
+
+	checkPlugin(info);
+
+	if(dcMain((install ? ON_INSTALL : ON_LOAD), &dcCore, nullptr) == False) {
+		throw Exception(str(F_("Error loading %1%") % Util::getFileName(fileName)));
+	}
+
+	plugins.emplace_back(new PluginInfo(fileName, hr, info, dcMain));
+	hr = nullptr; // bypass the scoped deleter.
 }
 
 bool PluginManager::isLoaded(const string& guid) {
+	Lock l(cs);
 	auto pluginComp = [&guid](const unique_ptr<PluginInfo>& p) -> bool { return strcmp(p->getInfo().guid, guid.c_str()) == 0; };
 	auto i = std::find_if(plugins.begin(), plugins.end(), pluginComp);
 	return (i != plugins.end());
 }
 
-bool PluginManager::checkPlugin(const MetaData& info, function<void (const string&)> err) {
+void PluginManager::checkPlugin(const MetaData& info) {
 	// Check if user is trying to load a duplicate
 	if(isLoaded(info.guid)) {
-		err(str(F_("%1% is already installed") % info.name));
-		return false;
+		throw Exception(str(F_("%1% is already installed") % info.name));
 	}
 
 	// Check API compatibility (this should only block on absolutely wrecking api changes, which generally should not happen)
 	if(info.apiVersion < DCAPI_CORE_VER) {
-		err(str(F_("%1% is too old, contact the plugin author for an update") % info.name));
-		return false;
+		throw Exception(str(F_("%1% is too old, contact the plugin author for an update") % info.name));
 	}
 
 	// Check that all dependencies are loaded
 	if(info.numDependencies != 0) {
 		for(size_t i = 0; i < info.numDependencies; ++i) {
 			if(!isLoaded(info.dependencies[i])) {
-				err(str(F_("Missing dependencies for %1%") % info.name));
-				return false;
+				throw Exception(str(F_("Missing dependencies for %1%") % info.name));
 			}
 		}
 	}
-
-	return true;
 }
 
 void PluginManager::unloadPlugins() {

=== modified file 'dcpp/PluginManager.h'
--- dcpp/PluginManager.h	2013-04-22 22:31:37 +0000
+++ dcpp/PluginManager.h	2013-04-23 15:34:45 +0000
@@ -108,10 +108,10 @@
 
 	/** Extract a dcext-packaged plugin. Throws on errors. */
 	DcextInfo extract(const string& path);
-	void install(const string& name, const string& plugin, const StringList& files);
+	void install(const string& uuid, const string& plugin, const StringList& files);
 
 	void loadPlugins(function<void (const string&)> f);
-	bool loadPlugin(const string& fileName, function<void (const string&)> err, bool install = false);
+	void loadPlugin(const string& fileName, bool install = false);
 	bool isLoaded(const string& guid);
 
 	void unloadPlugins();
@@ -179,8 +179,8 @@
 	void loadSettings() noexcept;
 	void saveSettings() noexcept;
 
-	// Check if plugin can be loaded
-	bool checkPlugin(const MetaData& info, function<void (const string&)> err);
+	// Check if the plugin can be loaded; throws if it can't.
+	void checkPlugin(const MetaData& info);
 
 	// Listeners
 	void on(TimerManagerListener::Second, uint64_t ticks) noexcept { runHook(HOOK_TIMER_SECOND, NULL, &ticks); }

=== modified file 'win32/PluginInfoDlg.cpp'
--- win32/PluginInfoDlg.cpp	2013-04-22 22:31:37 +0000
+++ win32/PluginInfoDlg.cpp	2013-04-23 15:34:45 +0000
@@ -100,7 +100,7 @@
 		cur->column(0).align = GridInfo::BOTTOM_RIGHT;
 		cur->setSpacing(grid->getSpacing());
 		WinUtil::addDlgButtons(cur,
-			[this, info] { handleOK(info.name, info.plugin, info.files); },
+			[this, info] { handleOK(info.name, info.uuid, info.plugin, info.files); },
 			[this] { endDialog(IDCANCEL); }).first->setText(T_("Install the plugin"));
 	}
 
@@ -112,9 +112,9 @@
 	return false;
 }
 
-void PluginInfoDlg::handleOK(const string& name, const string& plugin, const StringList& files) {
+void PluginInfoDlg::handleOK(const string& name, const string& uuid, const string& plugin, const StringList& files) {
 	try {
-		PluginManager::getInstance()->install(name, plugin, files);
+		PluginManager::getInstance()->install(uuid, plugin, files);
 		endDialog(IDOK);
 
 	} catch(const Exception& e) {

=== modified file 'win32/PluginInfoDlg.h'
--- win32/PluginInfoDlg.h	2013-04-22 22:31:37 +0000
+++ win32/PluginInfoDlg.h	2013-04-23 15:34:45 +0000
@@ -35,7 +35,7 @@
 
 private:
 	bool handleInitDialog(const string& path);
-	void handleOK(const string& name, const string& plugin, const StringList& files);
+	void handleOK(const string& name, const string& uuid, const string& plugin, const StringList& files);
 
 	void layout();
 

=== modified file 'win32/PluginPage.cpp'
--- win32/PluginPage.cpp	2013-04-22 22:31:37 +0000
+++ win32/PluginPage.cpp	2013-04-23 15:34:45 +0000
@@ -248,12 +248,30 @@
 }
 
 void PluginPage::handleAddPlugin() {
-	tstring path;
-	if(LoadDialog(this).addFilter(T_("dcext files"), _T("*.dcext")).open(path) &&
-		PluginInfoDlg(this, Text::fromT(path)).run() == IDOK)
+	tstring path_t;
+	if(LoadDialog(this)
+		.addFilter(str(TF_("%1% files") % _T("dcext")), _T("*.dcext"))
+		.addFilter(str(TF_("%1% files") % _T("dll")), _T("*.dll"))
+		.open(path_t))
 	{
-		auto pos = plugins->size();
-		addEntry(pos, PluginManager::getInstance()->getPlugin(pos)->getInfo());
+		auto path = Text::fromT(path_t);
+		auto added = false;
+		if(Util::getFileExt(path) == ".dcext") {
+			added = PluginInfoDlg(this, path).run() == IDOK;
+		} else {
+			try {
+				PluginManager::getInstance()->loadPlugin(path, true);
+				added = true;
+			} catch(const Exception& e) {
+				dwt::MessageBox(this).show(tstring(T_("Cannot install the plugin:")) + _T("\r\n\r\n") + Text::toT(e.getError()),
+					Text::toT(Util::getFileName(path)), dwt::MessageBox::BOX_OK, dwt::MessageBox::BOX_ICONSTOP);
+			}
+		}
+
+		if(added) {
+			auto pos = plugins->size();
+			addEntry(pos, PluginManager::getInstance()->getPlugin(pos)->getInfo());
+		}
 	}
 }