comparison 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
comparison
equal deleted inserted replaced
0:4e2ce2894c21 1:c11ca1d8ed91
1 --[[
2 Name: AceLibrary
3 Revision: $Rev: 19062 $
4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
5 Inspired By: Iriel (iriel@vigilance-committee.org)
6 Tekkub (tekkub@gmail.com)
7 Revision: $Rev: 19062 $
8 Website: http://www.wowace.com/
9 Documentation: http://www.wowace.com/index.php/AceLibrary
10 SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary
11 Description: Versioning library to handle other library instances, upgrading,
12 and proper access.
13 It also provides a base for libraries to work off of, providing
14 proper error tools. It is handy because all the errors occur in the
15 file that called it, not in the library file itself.
16 Dependencies: None
17 ]]
18
19 local ACELIBRARY_MAJOR = "AceLibrary"
20 local ACELIBRARY_MINOR = "$Revision: 19062 $"
21
22 local _G = getfenv(0)
23 local previous = _G[ACELIBRARY_MAJOR]
24 if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end
25
26 local function safecall(func,...)
27 local success, err = pcall(func,...)
28 if not success then geterrorhandler()(err) end
29 end
30
31 -- @table AceLibrary
32 -- @brief System to handle all versioning of libraries.
33 local AceLibrary = {}
34 local AceLibrary_mt = {}
35 setmetatable(AceLibrary, AceLibrary_mt)
36
37 local function error(self, message, ...)
38 if type(self) ~= "table" then
39 return _G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2)
40 end
41
42 local stack = debugstack()
43 if not message then
44 local _,_,second = string.find(stack, "\n(.-)\n")
45 message = "error raised! " .. second
46 else
47 local arg = { ... }
48
49 for i = 1, #arg do
50 arg[i] = tostring(arg[i])
51 end
52 for i = 1, 10 do
53 table.insert(arg, "nil")
54 end
55 message = string.format(message, unpack(arg))
56 end
57
58 if getmetatable(self) and getmetatable(self).__tostring then
59 message = string.format("%s: %s", tostring(self), message)
60 elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
61 message = string.format("%s: %s", self:GetLibraryVersion(), message)
62 elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
63 message = string.format("%s: %s", self.class:GetLibraryVersion(), message)
64 end
65
66 local first = string.gsub(stack, "\n.*", "")
67 local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1")
68 file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
69
70
71 local i = 0
72 for s in string.gmatch(stack, "\n([^\n]*)") do
73 i = i + 1
74 if not string.find(s, file .. "%.lua:%d+:") and not string.find(s, "%(tail call%)") then
75 file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1")
76 file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
77 break
78 end
79 end
80 local j = 0
81 for s in string.gmatch(stack, "\n([^\n]*)") do
82 j = j + 1
83 if j > i and not string.find(s, file .. "%.lua:%d+:") and not string.find(s, "%(tail call%)") then
84 return _G.error(message, j+1)
85 end
86 end
87 return _G.error(message, 2)
88 end
89
90 local function assert(self, condition, message, ...)
91 if not condition then
92 if not message then
93 local stack = debugstack()
94 local _,_,second = string.find(stack, "\n(.-)\n")
95 message = "assertion failed! " .. second
96 end
97 return error(self, message, ...)
98 end
99 return condition
100 end
101
102 local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
103 if type(num) ~= "number" then
104 return error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
105 elseif type(kind) ~= "string" then
106 return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind))
107 end
108 local errored = false
109 arg = type(arg)
110 if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then
111 local _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])")
112 if not func then
113 _,_,func = string.find(debugstack(), "([`<].-['>])")
114 end
115 if kind5 then
116 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)
117 elseif kind4 then
118 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)
119 elseif kind3 then
120 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)
121 elseif kind2 then
122 return error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
123 else
124 return error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
125 end
126 end
127 end
128
129 local pcall
130 do
131 local function check(self, ret, ...)
132 if not ret then
133 return error(self, (string.gsub(..., ".-%.lua:%d-: ", "")))
134 else
135 return ...
136 end
137 end
138
139 function pcall(self, func, ...)
140 return check(self, _G.pcall(func, ...))
141 end
142 end
143
144 local recurse = {}
145 local function addToPositions(t, major)
146 if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then
147 rawset(t, recurse, true)
148 AceLibrary.positions[t] = major
149 for k,v in pairs(t) do
150 if type(v) == "table" and not rawget(v, recurse) then
151 addToPositions(v, major)
152 end
153 if type(k) == "table" and not rawget(k, recurse) then
154 addToPositions(k, major)
155 end
156 end
157 local mt = getmetatable(t)
158 if mt and not rawget(mt, recurse) then
159 addToPositions(mt, major)
160 end
161 rawset(t, recurse, nil)
162 end
163 end
164
165 local function svnRevisionToNumber(text)
166 if type(text) == "string" then
167 if string.find(text, "^%$Revision: (%d+) %$$") then
168 return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1")))
169 elseif string.find(text, "^%$Rev: (%d+) %$$") then
170 return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1")))
171 elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then
172 return tonumber((string.gsub(text, "^%$LastChangedRevision: (%d+) %$$", "%1")))
173 end
174 elseif type(text) == "number" then
175 return text
176 end
177 return nil
178 end
179
180 local crawlReplace
181 do
182 local recurse = {}
183 local function func(t, to, from)
184 if recurse[t] then
185 return
186 end
187 recurse[t] = true
188 local mt = getmetatable(t)
189 setmetatable(t, nil)
190 rawset(t, to, rawget(t, from))
191 rawset(t, from, nil)
192 for k,v in pairs(t) do
193 if v == from then
194 t[k] = to
195 elseif type(v) == "table" then
196 if not recurse[v] then
197 func(v, to, from)
198 end
199 end
200
201 if type(k) == "table" then
202 if not recurse[k] then
203 func(k, to, from)
204 end
205 end
206 end
207 setmetatable(t, mt)
208 if mt then
209 if mt == from then
210 setmetatable(t, to)
211 elseif not recurse[mt] then
212 func(mt, to, from)
213 end
214 end
215 end
216 function crawlReplace(t, to, from)
217 func(t, to, from)
218 for k in pairs(recurse) do
219 recurse[k] = nil
220 end
221 end
222 end
223
224 -- @function destroyTable
225 -- @brief remove all the contents of a table
226 -- @param t table to destroy
227 local function destroyTable(t)
228 setmetatable(t, nil)
229 for k,v in pairs(t) do
230 t[k] = nil
231 end
232 end
233
234 local function isFrame(frame)
235 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"
236 end
237
238 -- @function copyTable
239 -- @brief Create a shallow copy of a table and return it.
240 -- @param from The table to copy from
241 -- @return A shallow copy of the table
242 local function copyTable(from)
243 local to = {}
244 for k,v in pairs(from) do
245 to[k] = v
246 end
247 setmetatable(to, getmetatable(from))
248 return to
249 end
250
251 -- @function deepTransfer
252 -- @brief Fully transfer all data, keeping proper previous table
253 -- backreferences stable.
254 -- @param to The table with which data is to be injected into
255 -- @param from The table whose data will be injected into the first
256 -- @param saveFields If available, a shallow copy of the basic data is saved
257 -- in here.
258 -- @param list The account of table references
259 -- @param list2 The current status on which tables have been traversed.
260 local deepTransfer
261 do
262 -- @function examine
263 -- @brief Take account of all the table references to be shared
264 -- between the to and from tables.
265 -- @param to The table with which data is to be injected into
266 -- @param from The table whose data will be injected into the first
267 -- @param list An account of the table references
268 local function examine(to, from, list, major)
269 list[from] = to
270 for k,v in pairs(from) do
271 if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then
272 if from[k] == to[k] then
273 list[from[k]] = to[k]
274 elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then
275 list[from[k]] = from[k]
276 elseif not list[from[k]] then
277 examine(to[k], from[k], list, major)
278 end
279 end
280 end
281 return list
282 end
283
284 function deepTransfer(to, from, saveFields, major, list, list2)
285 setmetatable(to, nil)
286 if not list then
287 list = {}
288 list2 = {}
289 examine(to, from, list, major)
290 end
291 list2[to] = to
292 for k,v in pairs(to) do
293 if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then
294 if saveFields then
295 saveFields[k] = v
296 end
297 to[k] = nil
298 elseif v ~= _G then
299 if saveFields then
300 saveFields[k] = copyTable(v)
301 end
302 end
303 end
304 for k in pairs(from) do
305 if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then
306 if not list2[to[k]] then
307 deepTransfer(to[k], from[k], nil, major, list, list2)
308 end
309 to[k] = list[to[k]] or list2[to[k]]
310 else
311 rawset(to, k, from[k])
312 end
313 end
314 setmetatable(to, getmetatable(from))
315 local mt = getmetatable(to)
316 if mt then
317 if list[mt] then
318 setmetatable(to, list[mt])
319 elseif mt.__index and list[mt.__index] then
320 mt.__index = list[mt.__index]
321 end
322 end
323 destroyTable(from)
324 end
325 end
326
327 -- @method TryToLoadStandalone
328 -- @brief Attempt to find and load a standalone version of the requested library
329 -- @param major A string representing the major version
330 -- @return If library is found, return values from the call to LoadAddOn are returned
331 -- If the library has been requested previously, nil is returned.
332 local function TryToLoadStandalone(major)
333 if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end
334 if AceLibrary.scannedlibs[major] then return end
335
336 AceLibrary.scannedlibs[major] = true
337
338 local name, _, _, enabled, loadable = GetAddOnInfo(major)
339 if loadable then
340 return LoadAddOn(name)
341 end
342
343 for i=1,GetNumAddOns() do
344 if GetAddOnMetadata(i, "X-AceLibrary-"..major) then
345 local name, _, _, enabled, loadable = GetAddOnInfo(i)
346 if loadable then
347 return LoadAddOn(name)
348 end
349 end
350 end
351 end
352
353 -- @method IsNewVersion
354 -- @brief Obtain whether the supplied version would be an upgrade to the
355 -- current version. This allows for bypass code in library
356 -- declaration.
357 -- @param major A string representing the major version
358 -- @param minor An integer or an svn revision string representing the minor version
359 -- @return whether the supplied version would be newer than what is
360 -- currently available.
361 function AceLibrary:IsNewVersion(major, minor)
362 argCheck(self, major, 2, "string")
363 TryToLoadStandalone(major)
364
365 if type(minor) == "string" then
366 local m = svnRevisionToNumber(minor)
367 if m then
368 minor = m
369 else
370 _G.error(string.format("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
371 end
372 end
373 argCheck(self, minor, 3, "number")
374 local data = self.libs[major]
375 if not data then
376 return true
377 end
378 return data.minor < minor
379 end
380
381 -- @method HasInstance
382 -- @brief Returns whether an instance exists. This allows for optional support of a library.
383 -- @param major A string representing the major version.
384 -- @param minor (optional) An integer or an svn revision string representing the minor version.
385 -- @return Whether an instance exists.
386 function AceLibrary:HasInstance(major, minor)
387 argCheck(self, major, 2, "string")
388 TryToLoadStandalone(major)
389
390 if minor then
391 if type(minor) == "string" then
392 local m = svnRevisionToNumber(minor)
393 if m then
394 minor = m
395 else
396 _G.error(string.format("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
397 end
398 end
399 argCheck(self, minor, 3, "number")
400 if not self.libs[major] then
401 return
402 end
403 return self.libs[major].minor == minor
404 end
405 return self.libs[major] and true
406 end
407
408 -- @method GetInstance
409 -- @brief Returns the library with the given major/minor version.
410 -- @param major A string representing the major version.
411 -- @param minor (optional) An integer or an svn revision string representing the minor version.
412 -- @return The library with the given major/minor version.
413 function AceLibrary:GetInstance(major, minor)
414 argCheck(self, major, 2, "string")
415 TryToLoadStandalone(major)
416
417 local data = self.libs[major]
418 if not data then
419 _G.error(string.format("Cannot find a library instance of %s.", major), 2)
420 return
421 end
422 if minor then
423 if type(minor) == "string" then
424 local m = svnRevisionToNumber(minor)
425 if m then
426 minor = m
427 else
428 _G.error(string.format("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
429 end
430 end
431 argCheck(self, minor, 2, "number")
432 if data.minor ~= minor then
433 _G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2)
434 end
435 end
436 return data.instance
437 end
438
439 -- Syntax sugar. AceLibrary("FooBar-1.0")
440 AceLibrary_mt.__call = AceLibrary.GetInstance
441
442 local donothing = function() end
443
444 local AceEvent
445
446 -- @method Register
447 -- @brief Registers a new version of a given library.
448 -- @param newInstance the library to register
449 -- @param major the major version of the library
450 -- @param minor the minor version of the library
451 -- @param activateFunc (optional) A function to be called when the library is
452 -- fully activated. Takes the arguments
453 -- (newInstance [, oldInstance, oldDeactivateFunc]). If
454 -- oldInstance is given, you should probably call
455 -- oldDeactivateFunc(oldInstance).
456 -- @param deactivateFunc (optional) A function to be called by a newer library's
457 -- activateFunc.
458 -- @param externalFunc (optional) A function to be called whenever a new
459 -- library is registered.
460 function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc)
461 argCheck(self, newInstance, 2, "table")
462 argCheck(self, major, 3, "string")
463 if major ~= ACELIBRARY_MAJOR then
464 for k,v in pairs(_G) do
465 if v == newInstance then
466 geterrorhandler()(string.format("Cannot register library %q. It is part of the global table in _G[%q].", major, k))
467 end
468 end
469 end
470 if major ~= ACELIBRARY_MAJOR and not string.find(major, "^[%a%-][%a%d%-]+[%a%-]%-%d+%.%d+$") and not string.find(major, "^[%a%-]+%-%d+%.%d+$") then
471 _G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2)
472 end
473 if type(minor) == "string" then
474 local m = svnRevisionToNumber(minor)
475 if m then
476 minor = m
477 else
478 _G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
479 end
480 end
481 argCheck(self, minor, 4, "number")
482 if math.floor(minor) ~= minor or minor < 0 then
483 error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor)
484 end
485 argCheck(self, activateFunc, 5, "function", "nil")
486 argCheck(self, deactivateFunc, 6, "function", "nil")
487 argCheck(self, externalFunc, 7, "function", "nil")
488 if not deactivateFunc then
489 deactivateFunc = donothing
490 end
491 local data = self.libs[major]
492 if not data then
493 -- This is new
494 local instance = copyTable(newInstance)
495 crawlReplace(instance, instance, newInstance)
496 destroyTable(newInstance)
497 if AceLibrary == newInstance then
498 self = instance
499 AceLibrary = instance
500 end
501 self.libs[major] = {
502 instance = instance,
503 minor = minor,
504 deactivateFunc = deactivateFunc,
505 externalFunc = externalFunc,
506 }
507 rawset(instance, 'GetLibraryVersion', function(self)
508 return major, minor
509 end)
510 if not rawget(instance, 'error') then
511 rawset(instance, 'error', error)
512 end
513 if not rawget(instance, 'assert') then
514 rawset(instance, 'assert', assert)
515 end
516 if not rawget(instance, 'argCheck') then
517 rawset(instance, 'argCheck', argCheck)
518 end
519 if not rawget(instance, 'pcall') then
520 rawset(instance, 'pcall', pcall)
521 end
522 addToPositions(instance, major)
523 if activateFunc then
524 safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil
525
526 --[[ if major ~= ACELIBRARY_MAJOR then
527 for k,v in pairs(_G) do
528 if v == instance then
529 geterrorhandler()(string.format("Cannot register library %q. It is part of the global table in _G[%q].", major, k))
530 end
531 end
532 end]]
533 end
534
535 if externalFunc then
536 for k,data in pairs(self.libs) do
537 if k ~= major then
538 safecall(externalFunc, instance, k, data.instance)
539 end
540 end
541 end
542
543 for k,data in pairs(self.libs) do
544 if k ~= major and data.externalFunc then
545 safecall(data.externalFunc, data.instance, major, instance)
546 end
547 end
548 if major == "AceEvent-2.0" then
549 AceEvent = instance
550 end
551 if AceEvent then
552 AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance)
553 end
554
555 return instance
556 end
557 local instance = data.instance
558 if minor <= data.minor then
559 -- This one is already obsolete, raise an error.
560 _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)
561 return
562 end
563 -- This is an update
564 local oldInstance = {}
565
566 addToPositions(newInstance, major)
567 local isAceLibrary = (AceLibrary == newInstance)
568 local old_error, old_assert, old_argCheck, old_pcall
569 if isAceLibrary then
570 self = instance
571 AceLibrary = instance
572
573 old_error = instance.error
574 old_assert = instance.assert
575 old_argCheck = instance.argCheck
576 old_pcall = instance.pcall
577
578 self.error = error
579 self.assert = assert
580 self.argCheck = argCheck
581 self.pcall = pcall
582 end
583 deepTransfer(instance, newInstance, oldInstance, major)
584 crawlReplace(instance, instance, newInstance)
585 local oldDeactivateFunc = data.deactivateFunc
586 data.minor = minor
587 data.deactivateFunc = deactivateFunc
588 data.externalFunc = externalFunc
589 rawset(instance, 'GetLibraryVersion', function(self)
590 return major, minor
591 end)
592 if not rawget(instance, 'error') then
593 rawset(instance, 'error', error)
594 end
595 if not rawget(instance, 'assert') then
596 rawset(instance, 'assert', assert)
597 end
598 if not rawget(instance, 'argCheck') then
599 rawset(instance, 'argCheck', argCheck)
600 end
601 if not rawget(instance, 'pcall') then
602 rawset(instance, 'pcall', pcall)
603 end
604 if isAceLibrary then
605 for _,v in pairs(self.libs) do
606 local i = type(v) == "table" and v.instance
607 if type(i) == "table" then
608 if not rawget(i, 'error') or i.error == old_error then
609 rawset(i, 'error', error)
610 end
611 if not rawget(i, 'assert') or i.assert == old_assert then
612 rawset(i, 'assert', assert)
613 end
614 if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then
615 rawset(i, 'argCheck', argCheck)
616 end
617 if not rawget(i, 'pcall') or i.pcall == old_pcall then
618 rawset(i, 'pcall', pcall)
619 end
620 end
621 end
622 end
623 if activateFunc then
624 safecall(activateFunc, instance, oldInstance, oldDeactivateFunc)
625
626 if major ~= ACELIBRARY_MAJOR then
627 for k,v in pairs(_G) do
628 if v == instance then
629 geterrorhandler()(string.format("Cannot register library %q. It is part of the global table in _G[%q].", major, k))
630 end
631 end
632 end
633 else
634 safecall(oldDeactivateFunc, oldInstance)
635 end
636 oldInstance = nil
637
638 if externalFunc then
639 for k,data in pairs(self.libs) do
640 if k ~= major then
641 safecall(externalFunc, instance, k, data.instance)
642 end
643 end
644 end
645
646 return instance
647 end
648
649 local iter
650 function AceLibrary:IterateLibraries()
651 if not iter then
652 local function iter(t, k)
653 k = next(t, k)
654 if not k then
655 return nil
656 else
657 return k, t[k].instance
658 end
659 end
660 end
661 return iter, self.libs, nil
662 end
663
664 -- @function Activate
665 -- @brief The activateFunc for AceLibrary itself. Called when
666 -- AceLibrary properly registers.
667 -- @param self Reference to AceLibrary
668 -- @param oldLib (optional) Reference to an old version of AceLibrary
669 -- @param oldDeactivate (optional) Function to deactivate the old lib
670 local function activate(self, oldLib, oldDeactivate)
671 AceLibrary = self
672 if not self.libs then
673 self.libs = oldLib and oldLib.libs or {}
674 self.scannedlibs = oldLib and oldLib.scannedlibs or {}
675 end
676 if not self.positions then
677 self.positions = oldLib and oldLib.positions or setmetatable({}, { __mode = "k" })
678 end
679
680 -- Expose the library in the global environment
681 _G[ACELIBRARY_MAJOR] = self
682
683 if oldDeactivate then
684 oldDeactivate(oldLib)
685 end
686 end
687
688 if not previous then
689 previous = AceLibrary
690 end
691 if not previous.libs then
692 previous.libs = {}
693 end
694 AceLibrary.libs = previous.libs
695 if not previous.positions then
696 previous.positions = setmetatable({}, { __mode = "k" })
697 end
698 AceLibrary.positions = previous.positions
699 AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate)