nitc/abscomp: add helper function maybenull to factorize code
[nit.git] / src / interpreter / dynamic_loading_ffi / on_demand_compiler.nit
index eba0678..e826f05 100644 (file)
@@ -20,6 +20,16 @@ import c_tools
 import nitni
 import ffi
 import naive_interpreter
+import pkgconfig
+import debugger_socket # To linearize `ToolContext::init`
+
+redef class ToolContext
+
+       # --compile-dir
+       var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
+
+       init do option_context.add_option opt_compile_dir
+end
 
 redef class AMethPropdef
        # Does this method definition use the FFI and is it supported by the interpreter?
@@ -34,21 +44,40 @@ redef class AMethPropdef
                        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
 
 redef class NaiveInterpreter
+       redef fun start(mainmodule)
+       do
+               super
+
+               # Delete temporary files
+               var compile_dir = compile_dir
+               if compile_dir.file_exists then compile_dir.rmdir
+       end
+
        # Where to store generated C and extracted code
-       #
-       # TODO make customizable and delete when execution completes
-       private var compile_dir = "nit_compile"
+       private var compile_dir: String is lazy do
+               # Prioritize the user supplied directory
+               var opt = modelbuilder.toolcontext.opt_compile_dir.value
+               if opt != null then return opt
+               return "/tmp/niti_ffi_{process_id}"
+       end
+
+       # Identifier for this process, unique between running interpreters
+       private fun process_id: Int `{ return getpid(); `}
 
        # Path of the compiled foreign code library
        #
@@ -75,7 +104,7 @@ redef class AModule
                var compile_dir = v.compile_dir
                var foreign_code_lib_path = v.foreign_code_lib_path(mmodule)
 
-               if not compile_dir.file_exists then compile_dir.mkdir
+               if not compile_dir.file_exists then compile_dir.mkdir(0o700)
 
                # Compile the common FFI part
                ensure_compile_ffi_wrapper
@@ -93,13 +122,29 @@ 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
-               end
-
+               # Compiler options specific to this module
                var ldflags = mmodule.ldflags[""].join(" ")
-               # TODO pkgconfig
+
+               # Protect pkg-config
+               var pkgconfigs = mmodule.pkgconfigs
+               var pkg_command = ""
+               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
+
+                       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_command = "`pkg-config --cflags --libs {pkgconfigs.join(" ")}`"
+               end
 
                # Compile each source file to an object file (or equivalent)
                var object_files = new Array[String]
@@ -108,9 +153,8 @@ redef class AModule
                end
 
                # Link everything in a shared library
-               # TODO customize the compiler
-               var cmd = "{v.c_compiler} -Wall -shared -Wl,-soname,{mmodule.name}.so -g -o {foreign_code_lib_path} {object_files.join(" ")} {ldflags}"
-               if sys.system(cmd) != 0 then
+               var cmd = "{v.c_compiler} -Wall -shared -o {foreign_code_lib_path} {object_files.join(" ")} {ldflags} {pkg_command}"
+               if system(cmd) != 0 then
                        v.fatal "FFI Error: Failed to link native code using `{cmd}`"
                        return false
                end
@@ -262,7 +306,7 @@ redef class MMethodDef
                for param in msignature.mparameters do params.add param.mtype.cname_blind
 
                # Declare the implementation function as extern
-               var impl_cname = mproperty.build_cname(mclassdef.mclass.mclass_type,
+               var impl_cname = mproperty.build_cname(mclassdef.bound_mtype,
                        mclassdef.mmodule, "___impl", long_signature)
                ecc.body_decl.add "extern {c_return_type} {impl_cname}({params.join(", ")});\n"