niti: filter the -lrt flag on OS X
[nit.git] / src / interpreter / dynamic_loading_ffi / on_demand_compiler.nit
index ab9e845..4e4a5b3 100644 (file)
@@ -20,6 +20,7 @@ import c_tools
 import nitni
 import ffi
 import naive_interpreter
+import pkgconfig
 import debugger_socket # To linearize `ToolContext::init`
 
 redef class ToolContext
@@ -36,19 +37,28 @@ redef class AMethPropdef
        # * Must use the nested foreign code block of the FFI.
        # * Must not have callbacks.
        # * Must be implemented in C.
+       # * Must not have a parameter or return typed with a Nit standard class.
        fun supported_by_dynamic_ffi: Bool
        do
+               # If the user specfied `is light_ffi`, it must be supported
+               var nats = get_annotations("light_ffi")
+               if nats.not_empty then return true
+
                var n_extern_code_block = n_extern_code_block
                if not (n_extern_calls == null and n_extern_code_block != null and
                        n_extern_code_block.is_c) then return false
 
                for mparam in mpropdef.msignature.mparameters do
-                       var mtype = mparam.mtype
-                       if not mtype.is_cprimitive then
+                       if not mparam.mtype.is_cprimitive then
                                return false
                        end
                end
 
+               var return_mtype = mpropdef.msignature.return_mtype
+               if return_mtype != null and not return_mtype.is_cprimitive then
+                       return false
+               end
+
                return true
        end
 end
@@ -83,7 +93,7 @@ redef class NaiveInterpreter
        end
 
        # External compiler used to generate the foreign code library
-       private var c_compiler = "gcc"
+       private var c_compiler = "cc"
 end
 
 redef class AModule
@@ -117,24 +127,45 @@ redef class AModule
                var srcs = [for file in ccu.files do new ExternCFile(file, ""): ExternFile]
                srcs.add_all mmodule.ffi_files
 
-               if mmodule.pkgconfigs.not_empty then
-                       fatal(v, "NOT YET IMPLEMENTED annotation `pkgconfig`")
-                       return false
+               # Compiler options specific to this module
+               var ldflags_array = mmodule.ldflags[""]
+               if ldflags_array.has("-lrt") and system("sh -c 'uname -s 2>/dev/null || echo not' | grep Darwin >/dev/null") == 0 then
+                       # Remove -lrt on OS X
+                       ldflags_array.remove "-lrt"
                end
+               var ldflags = ldflags_array.join(" ")
+
+               # Protect pkg-config
+               var pkgconfigs = mmodule.pkgconfigs
+               var pkg_cflags = ""
+               if not pkgconfigs.is_empty then
+                       var cmd = "which pkg-config >/dev/null"
+                       if system(cmd) != 0 then
+                               v.fatal "FFI Error: Command `pkg-config` not found. Please install it"
+                               return false
+                       end
 
-               var ldflags = mmodule.ldflags[""].join(" ")
-               # TODO pkgconfig
+                       for p in pkgconfigs do
+                               cmd = "pkg-config --exists '{p}'"
+                               if system(cmd) != 0 then
+                                       v.fatal "FFI Error: package {p} is not found by `pkg-config`. Please install it."
+                                       return false
+                               end
+                       end
+
+                       pkg_cflags = "`pkg-config --cflags {pkgconfigs.join(" ")}`"
+                       ldflags += " `pkg-config --libs {pkgconfigs.join(" ")}`"
+               end
 
                # Compile each source file to an object file (or equivalent)
                var object_files = new Array[String]
                for f in srcs do
-                       f.compile(v, mmodule, object_files)
+                       f.compile(v, mmodule, object_files, pkg_cflags)
                end
 
                # Link everything in a shared library
-               # TODO customize the compiler
                var cmd = "{v.c_compiler} -Wall -shared -o {foreign_code_lib_path} {object_files.join(" ")} {ldflags}"
-               if sys.system(cmd) != 0 then
+               if system(cmd) != 0 then
                        v.fatal "FFI Error: Failed to link native code using `{cmd}`"
                        return false
                end
@@ -373,14 +404,14 @@ end
 redef class ExternFile
        # Compile this source file
        private fun compile(v: NaiveInterpreter, mmodule: MModule,
-               object_files: Array[String]): Bool is abstract
+               object_files: Array[String], pkg_cflags: String): Bool is abstract
 end
 
 redef class ExternCFile
-       redef fun compile(v, mmodule, object_files)
+       redef fun compile(v, mmodule, object_files, pkg_cflags)
        do
                var compile_dir = v.compile_dir
-               var cflags = mmodule.cflags[""].join(" ")
+               var cflags = mmodule.cflags[""].join(" ") + " " + pkg_cflags
                var obj = compile_dir / filename.basename(".c") + ".o"
 
                var cmd = "{v.c_compiler} -Wall -c -fPIC -I {compile_dir} -g -o {obj} {filename} {cflags}"