diff libs/AceLibrary/AceLibrary.lua @ 1:c11ca1d8ed91

Version 0.1
author Flick <flickerstreak@gmail.com>
date Tue, 20 Mar 2007 21:03:57 +0000
parents
children f920db5fc6b1
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/AceLibrary/AceLibrary.lua	Tue Mar 20 21:03:57 2007 +0000
@@ -0,0 +1,699 @@
+--[[
+Name: AceLibrary
+Revision: $Rev: 19062 $
+Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
+Inspired By: Iriel (iriel@vigilance-committee.org)
+             Tekkub (tekkub@gmail.com)
+             Revision: $Rev: 19062 $
+Website: http://www.wowace.com/
+Documentation: http://www.wowace.com/index.php/AceLibrary
+SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary
+Description: Versioning library to handle other library instances, upgrading,
+             and proper access.
+             It also provides a base for libraries to work off of, providing
+             proper error tools. It is handy because all the errors occur in the
+             file that called it, not in the library file itself.
+Dependencies: None
+]]
+
+local ACELIBRARY_MAJOR = "AceLibrary"
+local ACELIBRARY_MINOR = "$Revision: 19062 $"
+
+local _G = getfenv(0)
+local previous = _G[ACELIBRARY_MAJOR]
+if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end
+
+local function safecall(func,...)
+    local success, err = pcall(func,...)
+    if not success then geterrorhandler()(err) end
+end
+
+-- @table AceLibrary
+-- @brief System to handle all versioning of libraries.
+local AceLibrary = {}
+local AceLibrary_mt = {}
+setmetatable(AceLibrary, AceLibrary_mt)
+
+local function error(self, message, ...)
+	if type(self) ~= "table" then
+		return _G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2)
+	end
+	
+	local stack = debugstack()
+	if not message then
+		local _,_,second = string.find(stack, "\n(.-)\n")
+		message = "error raised! " .. second
+	else
+		local arg = { ... }
+		
+		for i = 1, #arg do
+			arg[i] = tostring(arg[i])
+		end
+		for i = 1, 10 do
+			table.insert(arg, "nil")
+		end
+		message = string.format(message, unpack(arg))
+	end
+	
+	if getmetatable(self) and getmetatable(self).__tostring then
+		message = string.format("%s: %s", tostring(self), message)
+	elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
+		message = string.format("%s: %s", self:GetLibraryVersion(), message)
+	elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
+		message = string.format("%s: %s", self.class:GetLibraryVersion(), message)
+	end
+	
+	local first = string.gsub(stack, "\n.*", "")
+	local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1")
+	file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
+	
+	
+	local i = 0
+	for s in string.gmatch(stack, "\n([^\n]*)") do
+		i = i + 1
+		if not string.find(s, file .. "%.lua:%d+:") and not string.find(s, "%(tail call%)") then
+			file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1")
+			file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
+			break
+		end
+	end
+	local j = 0
+	for s in string.gmatch(stack, "\n([^\n]*)") do
+		j = j + 1
+		if j > i and not string.find(s, file .. "%.lua:%d+:") and not string.find(s, "%(tail call%)") then
+			return _G.error(message, j+1)
+		end
+	end
+	return _G.error(message, 2)
+end
+
+local function assert(self, condition, message, ...)
+	if not condition then
+		if not message then
+			local stack = debugstack()
+			local _,_,second = string.find(stack, "\n(.-)\n")
+			message = "assertion failed! " .. second
+		end
+		return error(self, message, ...)
+	end
+	return condition
+end
+
+local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
+	if type(num) ~= "number" then
+		return error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
+	elseif type(kind) ~= "string" then
+		return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind))
+	end
+	local errored = false
+	arg = type(arg)
+	if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then
+		local _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])")
+		if not func then
+			_,_,func = string.find(debugstack(), "([`<].-['>])")
+		end
+		if kind5 then
+			return error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg)
+		elseif kind4 then
+			return error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg)
+		elseif kind3 then
+			return error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg)
+		elseif kind2 then
+			return error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
+		else
+			return error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
+		end
+	end
+end
+
+local pcall
+do
+	local function check(self, ret, ...)
+		if not ret then
+			return error(self, (string.gsub(..., ".-%.lua:%d-: ", "")))
+		else
+			return ...
+		end
+	end
+
+	function pcall(self, func, ...)
+		return check(self, _G.pcall(func, ...))
+	end
+end
+
+local recurse = {}
+local function addToPositions(t, major)
+	if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then
+		rawset(t, recurse, true)
+		AceLibrary.positions[t] = major
+		for k,v in pairs(t) do
+			if type(v) == "table" and not rawget(v, recurse) then
+				addToPositions(v, major)
+			end
+			if type(k) == "table" and not rawget(k, recurse) then
+				addToPositions(k, major)
+			end
+		end
+		local mt = getmetatable(t)
+		if mt and not rawget(mt, recurse) then
+			addToPositions(mt, major)
+		end
+		rawset(t, recurse, nil)
+	end
+end
+
+local function svnRevisionToNumber(text)
+	if type(text) == "string" then
+		if string.find(text, "^%$Revision: (%d+) %$$") then
+			return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1")))
+		elseif string.find(text, "^%$Rev: (%d+) %$$") then
+			return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1")))
+		elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then
+			return tonumber((string.gsub(text, "^%$LastChangedRevision: (%d+) %$$", "%1")))
+		end
+	elseif type(text) == "number" then
+		return text
+	end
+	return nil
+end
+
+local crawlReplace
+do
+	local recurse = {}
+	local function func(t, to, from)
+		if recurse[t] then
+			return
+		end
+		recurse[t] = true
+		local mt = getmetatable(t)
+		setmetatable(t, nil)
+		rawset(t, to, rawget(t, from))
+		rawset(t, from, nil)
+		for k,v in pairs(t) do
+			if v == from then
+				t[k] = to
+			elseif type(v) == "table" then
+				if not recurse[v] then
+					func(v, to, from)
+				end
+			end
+			
+			if type(k) == "table" then
+				if not recurse[k] then
+					func(k, to, from)
+				end
+			end
+		end
+		setmetatable(t, mt)
+		if mt then
+			if mt == from then
+				setmetatable(t, to)
+			elseif not recurse[mt] then
+				func(mt, to, from)
+			end
+		end
+	end
+	function crawlReplace(t, to, from)
+		func(t, to, from)
+		for k in pairs(recurse) do
+			recurse[k] = nil
+		end
+	end
+end
+
+-- @function destroyTable
+-- @brief    remove all the contents of a table
+-- @param t  table to destroy
+local function destroyTable(t)
+	setmetatable(t, nil)
+	for k,v in pairs(t) do
+		t[k] = nil 
+	end
+end
+
+local function isFrame(frame)
+	return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function"
+end
+
+-- @function   copyTable
+-- @brief      Create a shallow copy of a table and return it.
+-- @param from The table to copy from
+-- @return     A shallow copy of the table
+local function copyTable(from)
+	local to = {}
+	for k,v in pairs(from) do
+		to[k] = v
+	end
+	setmetatable(to, getmetatable(from))
+	return to
+end
+
+-- @function         deepTransfer
+-- @brief            Fully transfer all data, keeping proper previous table
+--                   backreferences stable.
+-- @param to         The table with which data is to be injected into
+-- @param from       The table whose data will be injected into the first
+-- @param saveFields If available, a shallow copy of the basic data is saved
+--                   in here.
+-- @param list       The account of table references
+-- @param list2      The current status on which tables have been traversed.
+local deepTransfer
+do
+	-- @function   examine
+	-- @brief      Take account of all the table references to be shared
+	--             between the to and from tables.
+	-- @param to   The table with which data is to be injected into
+	-- @param from The table whose data will be injected into the first
+	-- @param list An account of the table references
+	local function examine(to, from, list, major)
+		list[from] = to
+		for k,v in pairs(from) do
+			if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then
+				if from[k] == to[k] then
+					list[from[k]] = to[k]
+				elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then
+					list[from[k]] = from[k]
+				elseif not list[from[k]] then
+					examine(to[k], from[k], list, major)
+				end
+			end
+		end
+		return list
+	end
+	
+	function deepTransfer(to, from, saveFields, major, list, list2)
+		setmetatable(to, nil)
+		if not list then
+			list = {}
+			list2 = {}
+			examine(to, from, list, major)
+		end
+		list2[to] = to
+		for k,v in pairs(to) do
+			if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then
+				if saveFields then
+					saveFields[k] = v
+				end
+				to[k] = nil
+			elseif v ~= _G then
+				if saveFields then
+					saveFields[k] = copyTable(v)
+				end
+			end
+		end
+		for k in pairs(from) do
+			if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then
+				if not list2[to[k]] then
+					deepTransfer(to[k], from[k], nil, major, list, list2)
+				end
+				to[k] = list[to[k]] or list2[to[k]]
+			else
+				rawset(to, k, from[k])
+			end
+		end
+		setmetatable(to, getmetatable(from))
+		local mt = getmetatable(to)
+		if mt then
+			if list[mt] then
+				setmetatable(to, list[mt])
+			elseif mt.__index and list[mt.__index] then
+				mt.__index = list[mt.__index]
+			end
+		end
+		destroyTable(from)
+	end
+end
+
+-- @method      TryToLoadStandalone
+-- @brief       Attempt to find and load a standalone version of the requested library
+-- @param major A string representing the major version
+-- @return      If library is found, return values from the call to LoadAddOn are returned
+--              If the library has been requested previously, nil is returned.
+local function TryToLoadStandalone(major)
+	if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end
+	if AceLibrary.scannedlibs[major] then return end
+
+	AceLibrary.scannedlibs[major] = true
+
+	local name, _, _, enabled, loadable = GetAddOnInfo(major)
+	if loadable then
+		return LoadAddOn(name)
+	end
+
+	for i=1,GetNumAddOns() do
+		if GetAddOnMetadata(i, "X-AceLibrary-"..major) then
+			local name, _, _, enabled, loadable = GetAddOnInfo(i)
+			if loadable then
+				return LoadAddOn(name)
+			end
+		end
+	end
+end
+
+-- @method      IsNewVersion
+-- @brief       Obtain whether the supplied version would be an upgrade to the
+--              current version. This allows for bypass code in library
+--              declaration.
+-- @param major A string representing the major version
+-- @param minor An integer or an svn revision string representing the minor version
+-- @return      whether the supplied version would be newer than what is
+--              currently available.
+function AceLibrary:IsNewVersion(major, minor)
+	argCheck(self, major, 2, "string")
+	TryToLoadStandalone(major)
+
+	if type(minor) == "string" then
+		local m = svnRevisionToNumber(minor)
+		if m then
+			minor = m
+		else
+			_G.error(string.format("Bad argument #3 to  `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
+		end
+	end
+	argCheck(self, minor, 3, "number")
+	local data = self.libs[major]
+	if not data then
+		return true
+	end
+	return data.minor < minor
+end
+
+-- @method      HasInstance
+-- @brief       Returns whether an instance exists. This allows for optional support of a library.
+-- @param major A string representing the major version.
+-- @param minor (optional) An integer or an svn revision string representing the minor version.
+-- @return      Whether an instance exists.
+function AceLibrary:HasInstance(major, minor)
+	argCheck(self, major, 2, "string")
+	TryToLoadStandalone(major)
+
+	if minor then
+		if type(minor) == "string" then
+			local m = svnRevisionToNumber(minor)
+			if m then
+				minor = m
+			else
+				_G.error(string.format("Bad argument #3 to  `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
+			end
+		end
+		argCheck(self, minor, 3, "number")
+		if not self.libs[major] then
+			return
+		end
+		return self.libs[major].minor == minor
+	end
+	return self.libs[major] and true
+end
+
+-- @method      GetInstance
+-- @brief       Returns the library with the given major/minor version.
+-- @param major A string representing the major version.
+-- @param minor (optional) An integer or an svn revision string representing the minor version.
+-- @return      The library with the given major/minor version.
+function AceLibrary:GetInstance(major, minor)
+	argCheck(self, major, 2, "string")
+	TryToLoadStandalone(major)
+
+	local data = self.libs[major]
+	if not data then
+		_G.error(string.format("Cannot find a library instance of %s.", major), 2)
+		return
+	end
+	if minor then
+		if type(minor) == "string" then
+			local m = svnRevisionToNumber(minor)
+			if m then
+				minor = m
+			else
+				_G.error(string.format("Bad argument #3 to  `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
+			end
+		end
+		argCheck(self, minor, 2, "number")
+		if data.minor ~= minor then
+			_G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2)
+		end
+	end
+	return data.instance
+end
+
+-- Syntax sugar.  AceLibrary("FooBar-1.0")
+AceLibrary_mt.__call = AceLibrary.GetInstance
+
+local donothing = function() end
+
+local AceEvent
+
+-- @method               Register
+-- @brief                Registers a new version of a given library.
+-- @param newInstance    the library to register
+-- @param major          the major version of the library
+-- @param minor          the minor version of the library
+-- @param activateFunc   (optional) A function to be called when the library is
+--                       fully activated. Takes the arguments
+--                       (newInstance [, oldInstance, oldDeactivateFunc]). If
+--                       oldInstance is given, you should probably call
+--                       oldDeactivateFunc(oldInstance).
+-- @param deactivateFunc (optional) A function to be called by a newer library's
+--                       activateFunc.
+-- @param externalFunc   (optional) A function to be called whenever a new
+--                       library is registered.
+function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc)
+	argCheck(self, newInstance, 2, "table")
+	argCheck(self, major, 3, "string")
+	if major ~= ACELIBRARY_MAJOR then
+		for k,v in pairs(_G) do
+			if v == newInstance then
+				geterrorhandler()(string.format("Cannot register library %q. It is part of the global table in _G[%q].", major, k))
+			end
+		end
+	end
+	if major ~= ACELIBRARY_MAJOR and not string.find(major, "^[%a%-][%a%d%-]+[%a%-]%-%d+%.%d+$") and not string.find(major, "^[%a%-]+%-%d+%.%d+$") then
+		_G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2)
+	end
+	if type(minor) == "string" then
+		local m = svnRevisionToNumber(minor)
+		if m then
+			minor = m
+		else
+			_G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
+		end
+	end
+	argCheck(self, minor, 4, "number")
+	if math.floor(minor) ~= minor or minor < 0 then
+		error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor)
+	end
+	argCheck(self, activateFunc, 5, "function", "nil")
+	argCheck(self, deactivateFunc, 6, "function", "nil")
+	argCheck(self, externalFunc, 7, "function", "nil")
+	if not deactivateFunc then
+		deactivateFunc = donothing
+	end
+	local data = self.libs[major]
+	if not data then
+		-- This is new
+		local instance = copyTable(newInstance)
+		crawlReplace(instance, instance, newInstance)
+		destroyTable(newInstance)
+		if AceLibrary == newInstance then
+			self = instance
+			AceLibrary = instance
+		end
+		self.libs[major] = {
+			instance = instance,
+			minor = minor,
+			deactivateFunc = deactivateFunc,
+			externalFunc = externalFunc,
+		}
+		rawset(instance, 'GetLibraryVersion', function(self)
+			return major, minor
+		end)
+		if not rawget(instance, 'error') then
+			rawset(instance, 'error', error)
+		end
+		if not rawget(instance, 'assert') then
+			rawset(instance, 'assert', assert)
+		end
+		if not rawget(instance, 'argCheck') then
+			rawset(instance, 'argCheck', argCheck)
+		end
+		if not rawget(instance, 'pcall') then
+			rawset(instance, 'pcall', pcall)
+		end
+		addToPositions(instance, major)
+		if activateFunc then
+			safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil
+			
+--[[			if major ~= ACELIBRARY_MAJOR then
+				for k,v in pairs(_G) do
+					if v == instance then
+						geterrorhandler()(string.format("Cannot register library %q. It is part of the global table in _G[%q].", major, k))
+					end
+				end
+			end]]
+		end
+		
+		if externalFunc then
+			for k,data in pairs(self.libs) do
+				if k ~= major then
+					safecall(externalFunc, instance, k, data.instance)
+				end
+			end
+		end
+		
+		for k,data in pairs(self.libs) do
+			if k ~= major and data.externalFunc then
+				safecall(data.externalFunc, data.instance, major, instance)
+			end
+		end
+		if major == "AceEvent-2.0" then
+			AceEvent = instance
+		end
+		if AceEvent then
+			AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance)
+		end
+		
+		return instance
+	end
+	local instance = data.instance
+	if minor <= data.minor then
+		-- This one is already obsolete, raise an error.
+		_G.error(string.format("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end", major, data.minor, minor, major, minor), 2)
+		return
+	end
+	-- This is an update
+	local oldInstance = {}
+	
+	addToPositions(newInstance, major)
+	local isAceLibrary = (AceLibrary == newInstance)
+	local old_error, old_assert, old_argCheck, old_pcall
+	if isAceLibrary then
+		self = instance
+		AceLibrary = instance
+		
+		old_error = instance.error
+		old_assert = instance.assert
+		old_argCheck = instance.argCheck
+		old_pcall = instance.pcall
+		
+		self.error = error
+		self.assert = assert
+		self.argCheck = argCheck
+		self.pcall = pcall
+	end
+	deepTransfer(instance, newInstance, oldInstance, major)
+	crawlReplace(instance, instance, newInstance)
+	local oldDeactivateFunc = data.deactivateFunc
+	data.minor = minor
+	data.deactivateFunc = deactivateFunc
+	data.externalFunc = externalFunc
+	rawset(instance, 'GetLibraryVersion', function(self)
+		return major, minor
+	end)
+	if not rawget(instance, 'error') then
+		rawset(instance, 'error', error)
+	end
+	if not rawget(instance, 'assert') then
+		rawset(instance, 'assert', assert)
+	end
+	if not rawget(instance, 'argCheck') then
+		rawset(instance, 'argCheck', argCheck)
+	end
+	if not rawget(instance, 'pcall') then
+		rawset(instance, 'pcall', pcall)
+	end
+	if isAceLibrary then
+		for _,v in pairs(self.libs) do
+			local i = type(v) == "table" and v.instance
+			if type(i) == "table" then
+				if not rawget(i, 'error') or i.error == old_error then
+					rawset(i, 'error', error)
+				end
+				if not rawget(i, 'assert') or i.assert == old_assert then
+					rawset(i, 'assert', assert)
+				end
+				if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then
+					rawset(i, 'argCheck', argCheck)
+				end
+				if not rawget(i, 'pcall') or i.pcall == old_pcall then
+					rawset(i, 'pcall', pcall)
+				end
+			end
+		end
+	end
+	if activateFunc then
+		safecall(activateFunc, instance, oldInstance, oldDeactivateFunc)	
+				
+		if major ~= ACELIBRARY_MAJOR then
+			for k,v in pairs(_G) do
+				if v == instance then
+					geterrorhandler()(string.format("Cannot register library %q. It is part of the global table in _G[%q].", major, k))
+				end
+			end
+		end
+	else
+		safecall(oldDeactivateFunc, oldInstance)
+	end
+	oldInstance = nil
+	
+	if externalFunc then
+		for k,data in pairs(self.libs) do
+			if k ~= major then
+				safecall(externalFunc, instance, k, data.instance)
+			end
+		end
+	end
+	
+	return instance
+end
+
+local iter
+function AceLibrary:IterateLibraries()
+	if not iter then
+		local function iter(t, k)
+			k = next(t, k)
+			if not k then
+				return nil
+			else
+				return k, t[k].instance
+			end
+		end
+	end
+	return iter, self.libs, nil
+end
+
+-- @function            Activate
+-- @brief               The activateFunc for AceLibrary itself. Called when
+--                      AceLibrary properly registers.
+-- @param self          Reference to AceLibrary
+-- @param oldLib        (optional) Reference to an old version of AceLibrary
+-- @param oldDeactivate (optional) Function to deactivate the old lib
+local function activate(self, oldLib, oldDeactivate)
+	AceLibrary = self
+	if not self.libs then
+		self.libs = oldLib and oldLib.libs or {}
+		self.scannedlibs = oldLib and oldLib.scannedlibs or {}
+	end
+	if not self.positions then
+		self.positions = oldLib and oldLib.positions or setmetatable({}, { __mode = "k" })
+	end
+	
+	-- Expose the library in the global environment
+	_G[ACELIBRARY_MAJOR] = self
+	
+	if oldDeactivate then
+		oldDeactivate(oldLib)
+	end
+end
+
+if not previous then
+	previous = AceLibrary
+end
+if not previous.libs then
+	previous.libs = {}
+end
+AceLibrary.libs = previous.libs
+if not previous.positions then
+	previous.positions = setmetatable({}, { __mode = "k" })
+end
+AceLibrary.positions = previous.positions
+AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate)