Java FFI: Makefile adds the compilation dir to the Java CLASSPATH
[nit.git] / src / compiler / abstract_compiler.nit
index ea4adde..e786374 100644 (file)
@@ -39,6 +39,8 @@ redef class ToolContext
        var opt_no_cc = new OptionBool("Do not invoke the C compiler", "--no-cc")
        # --no-main
        var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
+       # --shared-lib
+       var opt_shared_lib = new OptionBool("Compile to a native shared library", "--shared-lib")
        # --make-flags
        var opt_make_flags = new OptionString("Additional options to the `make` command", "--make-flags")
        # --max-c-lines
@@ -75,11 +77,13 @@ redef class ToolContext
        var opt_release = new OptionBool("Compile in release mode and finalize application", "--release")
        # -g
        var opt_debug = new OptionBool("Compile in debug mode (no C-side optimization)", "-g", "--debug")
+       # --trace
+       var opt_trace = new OptionBool("Compile with lttng's instrumentation", "--trace")
 
        redef init
        do
                super
-               self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_run, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
+               self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_run, self.opt_no_cc, self.opt_no_main, self.opt_shared_lib, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
                self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
                self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
                self.option_context.add_option(self.opt_no_stacktrace)
@@ -87,8 +91,10 @@ redef class ToolContext
                self.option_context.add_option(self.opt_release)
                self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
                self.option_context.add_option(self.opt_debug)
+               self.option_context.add_option(self.opt_trace)
 
                opt_no_main.hidden = true
+               opt_shared_lib.hidden = true
        end
 
        redef fun process_options(args)
@@ -233,6 +239,16 @@ class MakefileToolchain
                compiler.files_to_copy.add "{clib}/gc_chooser.c"
                compiler.files_to_copy.add "{clib}/gc_chooser.h"
 
+               # Add lttng traces provider to external bodies
+               if toolcontext.opt_trace.value then
+                       #-I. is there in order to make the TRACEPOINT_INCLUDE directive in clib/traces.h refer to the directory in which gcc was invoked.
+                       var traces = new ExternCFile("traces.c", "-I.")
+                       traces.pkgconfigs.add "lttng-ust"
+                       compiler.extern_bodies.add(traces)
+                       compiler.files_to_copy.add "{clib}/traces.c"
+                       compiler.files_to_copy.add "{clib}/traces.h"
+               end
+
                # FFI
                for m in compiler.mainmodule.in_importation.greaters do
                        compiler.finalize_ffi_for_module(m)
@@ -376,6 +392,15 @@ LDFLAGS ?=
 LDLIBS  ?= -lm {{{linker_options.join(" ")}}}
 \n"""
 
+               if self.toolcontext.opt_trace.value then makefile.write "LDLIBS += -llttng-ust -ldl\n"
+
+               if toolcontext.opt_shared_lib.value then
+                       makefile.write """
+CFLAGS += -fPIC
+LDFLAGS += -shared -Wl,-soname,{{{outname}}}
+"""
+               end
+
                makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n"
                if platform.supports_libunwind then
                        if toolcontext.opt_no_stacktrace.value then
@@ -440,6 +465,13 @@ ifneq ($(findstring MINGW64,$(uname_S)),)
        CFLAGS += -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast
 endif
 
+# Add the compilation dir to the Java CLASSPATH
+ifeq ($(CLASSPATH),)
+       CLASSPATH := .
+else
+       CLASSPATH := $(CLASSPATH):.
+endif
+
 """
 
                makefile.write("all: {outpath}\n")
@@ -503,8 +535,7 @@ endif
                var java_files = new Array[ExternFile]
                for f in compiler.extern_bodies do
                        var o = f.makefile_rule_name
-                       var ff = f.filename.basename
-                       makefile.write("{o}: {ff}\n")
+                       makefile.write("{o}: {f.filename}\n")
                        makefile.write("\t{f.makefile_rule_content}\n\n")
                        dep_rules.add(f.makefile_rule_name)
 
@@ -531,9 +562,9 @@ endif
                end
                makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
                # Clean
-               makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n")
+               makefile.write("clean:\n\trm -f {ofiles.join(" ")} 2>/dev/null\n")
                if outpath != real_outpath then
-                       makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n")
+                       makefile.write("\trm -f -- {outpath.escape_to_sh} 2>/dev/null\n")
                end
                makefile.close
                self.toolcontext.info("Generated makefile: {makepath}", 2)
@@ -687,7 +718,7 @@ abstract class AbstractCompiler
                stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
                stream.close
 
-               extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
+               extern_bodies.add(new ExternCFile("c_functions_hash.c", ""))
        end
 
        # Compile C headers
@@ -706,6 +737,7 @@ abstract class AbstractCompiler
                self.header.add_decl("#endif")
                self.header.add_decl("#include <inttypes.h>\n")
                self.header.add_decl("#include \"gc_chooser.h\"")
+               if modelbuilder.toolcontext.opt_trace.value then self.header.add_decl("#include \"traces.h\"")
                self.header.add_decl("#ifdef __APPLE__")
                self.header.add_decl("  #include <TargetConditionals.h>")
                self.header.add_decl("  #include <syslog.h>")
@@ -766,7 +798,7 @@ struct catch_stack_t {
        int currentSize;
        jmp_buf *envs;
 };
-extern __thread struct catch_stack_t catchStack;
+extern struct catch_stack_t *getCatchStack();
 """
        end
 
@@ -867,7 +899,44 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                v.add_decl("int glob_argc;")
                v.add_decl("char **glob_argv;")
                v.add_decl("val *glob_sys;")
-               v.add_decl("__thread struct catch_stack_t catchStack = \{-1, 0, NULL\};")
+
+               # Store catch stack in thread local storage
+               v.add_decl """
+#if defined(TARGET_OS_IPHONE)
+       // Use pthread_key_create and others for iOS
+       #include <pthread.h>
+
+       static pthread_key_t catch_stack_key;
+       static pthread_once_t catch_stack_key_created = PTHREAD_ONCE_INIT;
+
+       static void create_catch_stack()
+       {
+               pthread_key_create(&catch_stack_key, NULL);
+       }
+
+       struct catch_stack_t *getCatchStack()
+       {
+               pthread_once(&catch_stack_key_created, &create_catch_stack);
+               struct catch_stack_t *data = pthread_getspecific(catch_stack_key);
+               if (data == NULL) {
+                       data = malloc(sizeof(struct catch_stack_t));
+                       data->cursor = -1;
+                       data->currentSize = 0;
+                       data->envs = NULL;
+                       pthread_setspecific(catch_stack_key, data);
+               }
+               return data;
+       }
+#else
+       // Use __thread when available
+       __thread struct catch_stack_t catchStack = {-1, 0, NULL};
+
+       struct catch_stack_t *getCatchStack()
+       {
+               return &catchStack;
+       }
+#endif
+"""
 
                if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
                        for tag in count_type_test_tags do
@@ -1736,7 +1805,7 @@ abstract class AbstractCompilerVisitor
                var nat = new_var(mtype)
                var byte_esc = new Buffer.with_cap(len * 4)
                for i in [0 .. len[ do
-                       byte_esc.append("\\x{ns[i].to_s.substring_from(2)}")
+                       byte_esc.append("\\x{ns[i].to_hex}")
                end
                self.add("{nat} = \"{byte_esc}\";")
                return nat
@@ -1860,8 +1929,11 @@ abstract class AbstractCompilerVisitor
        # This method should be called before the error messages and before a `add_raw_abort`.
        fun add_raw_throw
        do
-               self.add("if(catchStack.cursor >= 0)\{")
-               self.add("longjmp(catchStack.envs[catchStack.cursor], 1);")
+               self.add("\{")
+               self.add("struct catch_stack_t *catchStack = getCatchStack();")
+               self.add("if(catchStack->cursor >= 0)\{")
+               self.add("      longjmp(catchStack->envs[catchStack->cursor], 1);")
+               self.add("\}")
                self.add("\}")
        end
 
@@ -3505,22 +3577,25 @@ redef class ADoExpr
        redef fun stmt(v)
        do
                if self.n_catch != null then
-                       v.add("if(catchStack.currentSize == 0) \{")
-                       v.add("         catchStack.cursor = -1;")
-                       v.add("         catchStack.currentSize = 100;")
-                       v.add("         catchStack.envs = malloc(sizeof(jmp_buf)*100);")
-                       v.add("\} else if(catchStack.cursor == catchStack.currentSize - 1) \{")
-                       v.add("         catchStack.currentSize *= 2;")
-                       v.add("         catchStack.envs = realloc(catchStack.envs, sizeof(jmp_buf)*catchStack.currentSize);")
+                       v.add("\{")
+                       v.add("struct catch_stack_t *catchStack = getCatchStack();")
+                       v.add("if(catchStack->currentSize == 0) \{")
+                       v.add("         catchStack->cursor = -1;")
+                       v.add("         catchStack->currentSize = 100;")
+                       v.add("         catchStack->envs = malloc(sizeof(jmp_buf)*100);")
+                       v.add("\} else if(catchStack->cursor == catchStack->currentSize - 1) \{")
+                       v.add("         catchStack->currentSize *= 2;")
+                       v.add("         catchStack->envs = realloc(catchStack->envs, sizeof(jmp_buf)*catchStack->currentSize);")
                        v.add("\}")
-                       v.add("catchStack.cursor += 1;")
-                       v.add("if(!setjmp(catchStack.envs[catchStack.cursor]))\{")
+                       v.add("catchStack->cursor += 1;")
+                       v.add("if(!setjmp(catchStack->envs[catchStack->cursor]))\{")
                        v.stmt(self.n_block)
-                       v.add("catchStack.cursor -= 1;")
+                       v.add("catchStack->cursor -= 1;")
                        v.add("\}else \{")
-                       v.add("catchStack.cursor -= 1;")
+                       v.add("catchStack->cursor -= 1;")
                        v.stmt(self.n_catch)
                        v.add("\}")
+                       v.add("\}")
                else
                        v.stmt(self.n_block)
                end
@@ -3750,8 +3825,9 @@ end
 
 redef class ACharExpr
        redef fun expr(v) do
-               if is_ascii then return v.byte_instance(value.as(not null).ascii)
-               if is_code_point then return v.int_instance(value.as(not null).code_point)
+               if is_code_point then
+                       return v.int_instance(value.as(not null).code_point)
+               end
                return v.char_instance(self.value.as(not null))
        end
 end