1 module llvm.functions.load;
2 
3 version (LLVM_Load):
4 
5 import std.traits;
6 import std.meta;
7 
8 import link = llvm.functions.link;
9 
10 private:
11 
12 version(Posix)
13 {
14     import core.sys.posix.dlfcn;
15 
16     alias SharedLibHandle = void*;
17 
18     version(OSX)
19     {
20         enum libPrefix = "lib";
21         enum libSuffix = "dylib";
22     }
23     else
24     {
25         enum libPrefix = "lib";
26         enum libSuffix = "so";
27     }
28 
29     pragma(lib, "dl");
30 }
31 else version(Windows)
32 {
33     import core.sys.windows.windows;
34     import std.path : dirName;
35 
36     alias SharedLibHandle = HMODULE;
37 
38     enum libPrefix = "";
39     enum libSuffix = "dll";
40 } else {
41     static assert(0, "Unsupported operating system");
42 }
43 
44 struct SharedLib
45 {
46     import std.string : fromStringz, toStringz;
47 private:
48     SharedLibHandle handle;
49 public:
50     bool loaded() @property { return handle !is null; }
51 
52     void load(string filename)
53     {
54         version(Posix)
55         {
56             if((handle = dlopen(filename.toStringz(), RTLD_NOW)) is null)
57             {
58                 throw new SharedLibException("Failed to load library " ~ filename ~ ": " ~ dlerror().fromStringz().idup);
59             }
60         }
61         else version(Windows)
62         {
63             if((handle = LoadLibraryA(filename.toStringz())) is null)
64             {
65                 LPCSTR error;
66                 DWORD tchar_length = FormatMessageA(
67                     FORMAT_MESSAGE_ALLOCATE_BUFFER |
68                     FORMAT_MESSAGE_FROM_SYSTEM |
69                     FORMAT_MESSAGE_IGNORE_INSERTS,
70                     null,
71                     GetLastError(),
72                     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
73                     cast(char*) &error,
74                     0,
75                     null);
76                 scope(exit) LocalFree(cast(HLOCAL) error);
77 
78                 throw new SharedLibException("Failed to load library " ~ filename ~ ": " ~ error[0..tchar_length].idup);
79             }
80         }
81         else static assert(0, "Unsupported operating system");
82     }
83 
84     void unload()
85     {
86              version (Posix)   alias close = dlclose;
87         else version (Windows) alias close = FreeLibrary;
88         else static assert(0, "Unsupported operating system");
89 
90         if (handle) {
91             close(handle);
92             handle = null;
93         }
94     }
95 
96     T getSymbol(T)(string symbol)
97     {
98              version (Posix)   alias get = dlsym;
99         else version (Windows) alias get = GetProcAddress;
100         else static assert(0, "Unsupported operating system");
101 
102         return cast(T) get(handle, symbol.toStringz());
103     }
104 }
105 
106 public:
107 
108 final class SharedLibException : Exception
109 {
110     this(string msg) @safe pure nothrow
111     {
112         super(msg);
113     }
114 }
115 
116 private
117 {
118     bool isSym(string m) { return m != "object" && m != "llvm" && m != "core" && m != "orEmpty"; }
119 
120     string declareSymPtr(string m) { return "typeof(link." ~ m ~ ")* " ~ m ~ ";"; }
121     string getSymPtr(string m) { return m ~ " = library.getSymbol!(typeof(" ~ m ~ "))(\"" ~ m ~ "\");"; }
122 }
123 
124 __gshared
125 {
126     mixin ({
127         string code;
128         foreach (m; __traits(allMembers, link)) if (m.isSym) {
129             code ~= m.declareSymPtr;
130         }
131         return code;
132     }());
133 }
134 
135 /// Container for holding the LLVM library and the load/unload functions.
136 public struct LLVM
137 {
138     import llvm.config : LLVM_VersionString;
139 private:
140     __gshared static SharedLib library;
141 
142     static void getSymbols()
143     {
144         foreach (m; __traits(allMembers, link)) static if (m.isSym) {
145             mixin (m.getSymPtr);
146             debug {
147                 import std.stdio : stderr;
148                 if (!mixin(m)) stderr.writeln("Missing LLVM symbol: " ~ m);
149             }
150         }
151     }
152 public:
153     /// true iff the LLVM library is loaded
154     static bool loaded() @property { return library.loaded; }
155 
156     /// Loads the LLVM library using the default filename.
157     static void load()
158     {
159         load(null);
160     }
161 
162     /// Loads the LLVM library using the specified filename
163     static void load(string filename)
164     {
165         if (filename is null) {
166             filename = libPrefix ~ "LLVM-" ~ LLVM_VersionString ~ "." ~ libSuffix;
167         }
168 
169         if (library.loaded) library.unload();
170 
171         library.load(filename);
172         getSymbols();
173     }
174 
175     /// Unloads the LLVM library
176     static void unload()
177     {
178         library.unload();
179     }
180 }
181 
182 version (LLVM_Autoload)
183 {
184     shared static this()
185     {
186         LLVM.load();
187     }
188 
189     shared static ~this()
190     {
191         LLVM.unload();
192     }
193 }