nitc: rename `c_compiler_option` and cie to `cflags` and `ldflags`
[nit.git] / src / ffi / objc.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # FFI support for Objective-C
18 #
19 # Compiles all Objective-C code with clang. The user module must define
20 # the framework used.
21 #
22 # This module is heavily based on the C++ FFI.
23 module objc
24
25 import extern_classes
26 import c
27
28 redef class FFILanguageAssignationPhase
29 # The Objective-C language visitor
30 var objc_language: FFILanguage = new ObjCLanguage(self)
31 end
32
33 redef class MModule
34 private var objc_file: nullable ObjCCompilationUnit = null
35
36 private var has_public_objc_header = false
37
38 # Imported modules with public Objective-C code blocks
39 var objc_imported_headers: HashSet[MModule] is lazy do
40 var dep = new HashSet[MModule]
41
42 # gather from importation
43 for m in in_importation.direct_greaters do
44 # does the super module has inherited dependencies?
45 var import_dep = m.objc_imported_headers
46 if not import_dep.is_empty then
47 dep.add_all import_dep
48 end
49
50 # does the super module itself has a public header?
51 if m.has_public_objc_header then dep.add(m)
52 end
53
54 return dep
55 end
56 end
57
58 # The Objective-C langugage visitor
59 class ObjCLanguage
60 super FFILanguage
61
62 redef fun identify_language(n) do return n.is_objc
63
64 redef fun compile_module_block(block, ecc, mmodule)
65 do
66 if mmodule.objc_file == null then mmodule.objc_file = new ObjCCompilationUnit
67
68 if block.is_objc_header then
69 mmodule.objc_file.header_custom.add block.location.as_line_pragma
70 mmodule.objc_file.header_custom.add block.code
71
72 mmodule.has_public_objc_header = true
73 else if block.is_objc_body then
74 mmodule.objc_file.body_custom.add block.location.as_line_pragma
75 mmodule.objc_file.body_custom.add block.code
76 end
77 end
78
79 redef fun compile_extern_method(block, m, ecc, mmodule)
80 do
81 if mmodule.objc_file == null then mmodule.objc_file = new ObjCCompilationUnit
82
83 var mpropdef = m.mpropdef
84 var recv_mtype = mpropdef.mclassdef.bound_mtype
85 var csignature = mpropdef.mproperty.build_csignature(
86 recv_mtype, mmodule, "___impl", long_signature, from_objc_call_context)
87
88 var fc = new CFunction(csignature)
89 fc.decls.add block.location.as_line_pragma
90 fc.exprs.add block.code
91 mmodule.objc_file.add_exported_function fc
92 end
93
94 redef fun compile_extern_class(block, m, ecc, mmodule) do end
95
96 redef fun get_ftype(block, m) do return new ForeignObjCType(block.code)
97
98 redef fun compile_to_files(mmodule, compdir)
99 do
100 var objc_file = mmodule.objc_file
101 assert objc_file != null
102
103 # Import public Objective-C header of imported modules
104 var dep = mmodule.objc_imported_headers
105 for mod in dep do
106 objc_file.header_custom.add "#include \"{mod.c_name}._ffi_m.h\"\n"
107 end
108
109 # write .m and _m.h file
110 mmodule.objc_file.header_c_types.add """
111 #include "{{{mmodule.c_name}}}._ffi.h"
112 """
113
114 var file = objc_file.write_to_files(mmodule, compdir)
115
116 # add compilation to makefile
117 mmodule.ffi_files.add file
118 end
119
120 redef fun compile_callback(callback, mmodule, mainmodule, ecc)
121 do
122 callback.compile_callback_to_objc(mmodule, mainmodule)
123 end
124 end
125
126 redef class AExternCodeBlock
127 # Is this Objective-C code?
128 fun is_objc : Bool do return language_name != null and
129 (language_name_lowered == "objc" or language_name_lowered.has_prefix("objc "))
130
131 # Is this Objective-C code for the body file?
132 fun is_objc_body : Bool do return language_name != null and
133 (language_name_lowered == "objc" or language_name_lowered == "objc body")
134
135 # Is this Objective-C code for the header file?
136 fun is_objc_header : Bool do return language_name != null and
137 (language_name_lowered == "objc header")
138 end
139
140 private class ObjCCompilationUnit
141 super CCompilationUnit
142
143 # Write this compilation unit to Objective-C source files
144 fun write_to_files(mmodule: MModule, compdir: String): ExternObjCFile
145 do
146 var base_name = "{mmodule.c_name}._ffi"
147
148 var h_file = "{base_name}_m.h"
149 var guard = "{mmodule.c_name.to_upper}_NIT_OBJC_H"
150 write_header_to_file(mmodule, compdir/h_file, new Array[String], guard)
151
152 var c_file = "{base_name}.m"
153 write_body_to_file(mmodule, compdir/c_file, ["\"{h_file}\""])
154
155 files.add compdir/c_file
156
157 mmodule.ldflags = "{mmodule.ldflags} -lobjc"
158
159 return new ExternObjCFile(compdir/c_file, mmodule)
160 end
161 end
162
163 # A Objective-C file
164 class ExternObjCFile
165 super ExternFile
166
167 # Associated `MModule`
168 var mmodule: MModule
169
170 redef fun makefile_rule_name do return "{filename.basename(".m")}_m.o"
171 redef fun makefile_rule_content do
172 return "clang $(CFLAGS) -c {filename.basename("")} -o {makefile_rule_name}"
173 end
174 redef fun compiles_to_o_file do return true
175 end
176
177 # An Objective-C type
178 class ForeignObjCType
179 super ForeignType
180
181 # Type name
182 var objc_type: String
183 end
184
185 redef class NitniCallback
186 # Compile this callback to be callable from Objective-C
187 fun compile_callback_to_objc(mmodule: MModule, mainmodule: MModule) do end
188 end
189
190 redef class MExplicitCall
191 redef fun compile_callback_to_objc(mmodule, mainmodule)
192 do
193 var mproperty = mproperty
194 assert mproperty isa MMethod
195
196 var objc_signature = mproperty.build_csignature(recv_mtype, mainmodule, null, short_signature, from_objc_call_context)
197 var ccall = mproperty.build_ccall(recv_mtype, mainmodule, null, long_signature, from_objc_call_context, null)
198 var fc = new CFunction(objc_signature)
199 fc.exprs.add ccall
200 mmodule.objc_file.add_local_function fc
201 end
202 end
203
204 # Calls withing Objective-C code
205 private fun objc_call_context: ObjCCallContext do return once new ObjCCallContext
206
207 # Calls from C to Objective-C
208 private fun to_objc_call_context: ToObjCCallContext do return once new ToObjCCallContext
209
210 # Calls from Objective-C to C
211 private fun from_objc_call_context: FromObjCCallContext do return once new FromObjCCallContext
212
213 private class ObjCCallContext
214 super CallContext
215
216 redef fun name_mtype(mtype)
217 do
218 if mtype isa MClassType then
219 var ftype = mtype.mclass.ftype
220 if ftype isa ForeignObjCType then
221 return ftype.objc_type
222 end
223 end
224
225 return mtype.cname
226 end
227 end
228
229 private class ToObjCCallContext
230 super ObjCCallContext
231
232 redef fun cast_to(mtype, name)
233 do
234 if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then
235 return "(void*)({name})"
236 else return name
237 end
238 end
239
240 private class FromObjCCallContext
241 super ObjCCallContext
242
243 redef fun cast_to(mtype, name)
244 do
245 if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then
246 return "(__bridge void*)({name})"
247 else return name
248 end
249
250 redef fun cast_from(mtype, name)
251 do
252 if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then
253 return "({name_mtype(mtype)})({name})"
254 else return name
255 end
256 end