Merge remote-tracking branch 'alexandre/libhtml'
authorJean Privat <jean@pryen.org>
Thu, 11 Jul 2013 08:21:45 +0000 (10:21 +0200)
committerJean Privat <jean@pryen.org>
Thu, 11 Jul 2013 08:21:45 +0000 (10:21 +0200)
1  2 
src/abstract_compiler.nit

@@@ -27,8 -27,6 +27,8 @@@ redef class ToolContex
        var opt_output: OptionString = new OptionString("Output file", "-o", "--output")
        # --no-cc
        var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc")
 +      # --cc-paths
 +      var opt_cc_path: OptionArray = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path")
        # --make-flags
        var opt_make_flags: OptionString = new OptionString("Additional options to make", "--make-flags")
        # --hardening
  end
  
  redef class ModelBuilder
 +      # The list of directories to search for included C headers (-I for C compilers)
 +      # The list is initially set with :
 +      #   * the toolcontext --cc-path option
 +      #   * the NIT_CC_PATH environment variable
 +      #   * some heuristics including the NIT_DIR environment variable and the progname of the process
 +      # Path can be added (or removed) by the client
 +      var cc_paths = new Array[String]
 +
 +      redef init(model, toolcontext)
 +      do
 +              super
 +
 +              # Look for the the Nit clib path
 +              var path_env = "NIT_DIR".environ
 +              if not path_env.is_empty then
 +                      var libname = "{path_env}/clib"
 +                      if libname.file_exists then cc_paths.add(libname)
 +              end
 +
 +              var libname = "{sys.program_name.dirname}/../clib"
 +              if libname.file_exists then cc_paths.add(libname.simplify_path)
 +
 +              if cc_paths.is_empty then
 +                      toolcontext.error(null, "Cannot determine the nit clib path. define envvar NIT_DIR.")
 +              end
 +
 +              # Add user defined cc_paths
 +              cc_paths.append(toolcontext.opt_cc_path.value)
 +
 +              path_env = "NIT_CC_PATH".environ
 +              if not path_env.is_empty then
 +                      cc_paths.append(path_env.split_with(':'))
 +              end
 +
 +      end
 +
        protected fun write_and_make(compiler: AbstractCompiler)
        do
                var mainmodule = compiler.mainmodule
                        outname = "{mainmodule.name}.bin"
                end
  
 -              var hfilename = ".nit_compile/{mainmodule.name}.1.h"
 -              var h = new OFStream.open(hfilename)
 +              var hfilename = compiler.header.file.name + ".h"
 +              var hfilepath = ".nit_compile/{hfilename}"
 +              var h = new OFStream.open(hfilepath)
                for l in compiler.header.decl_lines do
                        h.write l
                        h.write "\n"
                end
 +              for l in compiler.header.lines do
 +                      h.write l
 +                      h.write "\n"
 +              end
                h.close
  
                var cfiles = new Array[String]
  
 -              var file: nullable OFStream = null
 -              var count = 0
 -
 -              var i = 0
 -              for vis in compiler.visitors do
 -                      count += vis.lines.length
 -                      if file == null or count > 10000 or vis.file_break then
 -                              i += 1
 -                              if file != null then file.close
 -                              var cfilename = ".nit_compile/{mainmodule.name}.{i}.c"
 -                              cfiles.add(cfilename)
 -                              file = new OFStream.open(cfilename)
 -                              file.write "#include \"{mainmodule.name}.1.h\"\n"
 -                              count = vis.lines.length
 +              for f in compiler.files do
 +                      var i = 0
 +                      var hfile: nullable OFStream = null
 +                      var count = 0
 +                      var cfilename = ".nit_compile/{f.name}.0.h"
 +                      hfile = new OFStream.open(cfilename)
 +                      hfile.write "#include \"{hfilename}\"\n"
 +                      for key in f.required_declarations do
 +                              if not compiler.provided_declarations.has_key(key) then
 +                                      print "No provided declaration for {key}"
 +                                      abort
 +                              end
 +                              hfile.write compiler.provided_declarations[key]
 +                              hfile.write "\n"
                        end
 -                      if vis != compiler.header then
 +                      hfile.close
 +                      var file: nullable OFStream = null
 +                      for vis in f.writers do
 +                              if vis == compiler.header then continue
 +                              var total_lines = vis.lines.length + vis.decl_lines.length
 +                              if total_lines == 0 then continue
 +                              count += total_lines
 +                              if file == null or count > 10000  then
 +                                      i += 1
 +                                      if file != null then file.close
 +                                      cfilename = ".nit_compile/{f.name}.{i}.c"
 +                                      self.toolcontext.info("new C source files to compile: {cfilename}", 3)
 +                                      cfiles.add(cfilename)
 +                                      file = new OFStream.open(cfilename)
 +                                      file.write "#include \"{f.name}.0.h\"\n"
 +                                      count = total_lines
 +                              end
                                for l in vis.decl_lines do
                                        file.write l
                                        file.write "\n"
                                end
 +                              for l in vis.lines do
 +                                      file.write l
 +                                      file.write "\n"
 +                              end
                        end
 -                      for l in vis.lines do
 -                              file.write l
 -                              file.write "\n"
 -                      end
 +                      if file != null then file.close
                end
 -              if file != null then file.close
  
                self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
  
                var makename = ".nit_compile/{mainmodule.name}.mk"
                var makefile = new OFStream.open(makename)
  
 -              makefile.write("CC = ccache cc\nCFLAGS = -g -O2\nLDFLAGS ?= \nLDLIBS  ?= -lm -lgc\n\n")
 +              var cc_includes = ""
 +              for p in cc_paths do
 +                      #p = "..".join_path(p)
 +                      cc_includes += " -I \"" + p + "\""
 +              end
 +              makefile.write("CC = ccache cc\nCFLAGS = -g -O2{cc_includes}\nLDFLAGS ?= \nLDLIBS  ?= -lm -lgc\n\n")
                makefile.write("all: {outname}\n\n")
  
                var ofiles = new Array[String]
                        makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n")
                        ofiles.add(o)
                end
 +
 +              # Add gc_choser.h to aditionnal bodies
 +              var gc_chooser = new ExternCFile("{cc_paths.first}/gc_chooser.c", "-DWITH_LIBGC")
 +              compiler.extern_bodies.add(gc_chooser)
 +
                # Compile each required extern body into a specific .o
                for f in compiler.extern_bodies do
 -                      i += 1
 -                      var o = ".nit_compile/{mainmodule.name}.{i}.o"
 -                      makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n")
 +                      var basename = f.filename.basename(".c")
 +                      var o = ".nit_compile/{basename}.extern.o"
 +                      makefile.write("{o}: {f.filename}\n\t$(CC) $(CFLAGS) -D NONITCNI {f.cflags} -c -o {o} {f.filename}\n\n")
                        ofiles.add(o)
                end
 +
                # Link edition
                makefile.write("{outname}: {ofiles.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outname} {ofiles.join(" ")} $(LDLIBS)\n\n")
                # Clean
@@@ -260,33 -190,25 +260,33 @@@ abstract class AbstractCompile
  
        # Force the creation of a new file
        # The point is to avoid contamination between must-be-compiled-separately files
 -      fun new_file
 +      fun new_file(name: String): CodeFile
        do
 -              var v = self.new_visitor
 -              v.file_break = true
 +              var f = new CodeFile(name)
 +              self.files.add(f)
 +              return f
        end
  
 -      # The list of all associated visitors
 +      # The list of all associated files
        # Used to generate .c files
 -      # FIXME: should not be vistors but just somewhere to store lines
 -      var visitors: List[VISITOR] = new List[VISITOR]
 +      var files: List[CodeFile] = new List[CodeFile]
  
        # Initialize a visitor specific for a compiler engine
        fun new_visitor: VISITOR is abstract
  
        # Where global declaration are stored (the main .h)
 -      #
 -      # FIXME: should not be a visitor but just somewhere to store lines
 -      # FIXME: should not have a global .h since it does not help recompilations
 -      var header: VISITOR writable
 +      var header: CodeWriter writable
 +
 +      # Provide a declaration that can be requested (before or latter) by a visitor
 +      fun provide_declaration(key: String, s: String)
 +      do
 +              if self.provided_declarations.has_key(key) then
 +                      assert self.provided_declarations[key] == s
 +              end
 +              self.provided_declarations[key] = s
 +      end
 +
 +      private var provided_declarations = new HashMap[String, String]
  
        # Compile C headers
        # This method call compile_header_strucs method that has to be refined
                self.header.add_decl("#include <stdlib.h>")
                self.header.add_decl("#include <stdio.h>")
                self.header.add_decl("#include <string.h>")
 -              self.header.add_decl("#ifndef NOBOEHM")
 -              self.header.add_decl("#include <gc/gc.h>")
 -              self.header.add_decl("#ifdef NOBOEHM_ATOMIC")
 -              self.header.add_decl("#undef GC_MALLOC_ATOMIC")
 -              self.header.add_decl("#define GC_MALLOC_ATOMIC(x) GC_MALLOC(x)")
 -              self.header.add_decl("#endif /*NOBOEHM_ATOMIC*/")
 -              self.header.add_decl("#else /*NOBOEHM*/")
 -              self.header.add_decl("#define GC_MALLOC(x) calloc(1, (x))")
 -              self.header.add_decl("#define GC_MALLOC_ATOMIC(x) calloc(1, (x))")
 -              self.header.add_decl("#endif /*NOBOEHM*/")
 +              self.header.add_decl("#include <gc_chooser.h>")
  
                compile_header_structs
  
                end
                v.add_decl("int main(int argc, char** argv) \{")
                v.add("glob_argc = argc; glob_argv = argv;")
 +              v.add("initialize_gc_option();")
                var main_type = mainmodule.sys_type
                if main_type != null then
                        var mainmodule = v.compiler.mainmodule
                v.add("\}")
        end
  
 -      # look for a needed .h and .c file for a given .nit source-file
 -      # FIXME: bad API, parameter should be a MModule, not its source-file
 -      fun add_extern(file: String)
 -      do
 -              file = file.strip_extension(".nit")
 -              var tryfile = file + ".nit.h"
 -              if tryfile.file_exists then
 -                      self.header.add_decl("#include \"{"..".join_path(tryfile)}\"")
 -              end
 -              tryfile = file + "_nit.h"
 -              if tryfile.file_exists then
 -                      self.header.add_decl("#include \"{"..".join_path(tryfile)}\"")
 -              end
 -              tryfile = file + ".nit.c"
 -              if tryfile.file_exists then
 -                      self.extern_bodies.add(tryfile)
 -              end
 -              tryfile = file + "_nit.c"
 -              if tryfile.file_exists then
 -                      self.extern_bodies.add(tryfile)
 -              end
 -      end
 -
        # List of additional .c files required to compile (native interface)
 -      var extern_bodies = new ArraySet[String]
 +      var extern_bodies = new Array[ExternCFile]
 +
 +      # This is used to avoid adding an extern file more than once
 +      private var seen_extern = new ArraySet[String]
  
        # Generate code that check if an instance is correctly initialized
        fun generate_check_init_instance(mtype: MClassType) is abstract
        end
  end
  
 +# A file unit (may be more than one file if
 +# A file unit aim to be autonomous and is made or one or more `CodeWriter`s
 +class CodeFile
 +      var name: String
 +      var writers = new Array[CodeWriter]
 +      var required_declarations = new HashSet[String]
 +end
 +
 +# Where to store generated lines
 +class CodeWriter
 +      var file: CodeFile
 +      var lines: List[String] = new List[String]
 +      var decl_lines: List[String] = new List[String]
 +
 +      # Add a line in the main part of the generated C
 +      fun add(s: String) do self.lines.add(s)
 +
 +      # Add a line in the
 +      # (used for local or global declaration)
 +      fun add_decl(s: String) do self.decl_lines.add(s)
 +
 +      init(file: CodeFile)
 +      do
 +              self.file = file
 +              file.writers.add(self)
 +      end
 +end
 +
  # A visitor on the AST of property definition that generate the C code.
  abstract class AbstractCompilerVisitor
  
        # Alias for self.compiler.mainmodule.bool_type
        fun bool_type: MClassType do return self.compiler.mainmodule.bool_type
  
 -      var file_break: Bool = false
 +      var writer: CodeWriter
  
        init(compiler: COMPILER)
        do
                self.compiler = compiler
 -              compiler.visitors.add(self)
 +              self.writer = new CodeWriter(compiler.files.last)
        end
  
        # Force to get the primitive class named `name' or abort
  
        # Code generation
  
 -      private var lines: List[String] = new List[String]
 -      private var decl_lines: List[String] = new List[String]
 -
        # Add a line in the main part of the generated C
 -      fun add(s: String) do self.lines.add(s)
 +      fun add(s: String) do self.writer.lines.add(s)
  
        # Add a line in the
        # (used for local or global declaration)
 -      fun add_decl(s: String) do self.decl_lines.add(s)
 +      fun add_decl(s: String) do self.writer.decl_lines.add(s)
 +
 +      # Request the presence of a global declaration
 +      fun require_declaration(key: String)
 +      do
 +              self.writer.file.required_declarations.add(key)
 +      end
 +
 +      # Add a declaration in the local-header
 +      # The declaration is ensured to be present once
 +      fun declare_once(s: String)
 +      do
 +              self.compiler.provide_declaration(s, s)
 +              self.require_declaration(s)
 +      end
 +
 +      # look for a needed .h and .c file for a given .nit source-file
 +      # FIXME: bad API, parameter should be a MModule, not its source-file
 +      fun add_extern(file: String)
 +      do
 +              file = file.strip_extension(".nit")
 +              var tryfile = file + ".nit.h"
 +              if tryfile.file_exists then
 +                      self.declare_once("#include \"{"..".join_path(tryfile)}\"")
 +              end
 +              tryfile = file + "_nit.h"
 +              if tryfile.file_exists then
 +                      self.declare_once("#include \"{"..".join_path(tryfile)}\"")
 +              end
 +
 +              if self.compiler.seen_extern.has(file) then return
 +              self.compiler.seen_extern.add(file)
 +              tryfile = file + ".nit.c"
 +              if not tryfile.file_exists then
 +                      tryfile = file + "_nit.c"
 +                      if not tryfile.file_exists then return
 +              end
 +              var f = new ExternCFile(tryfile, "")
 +              self.compiler.extern_bodies.add(f)
 +      end
  
        # Return a new local runtime_variable initialized with the C expression `cexpr'.
        fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
@@@ -1049,14 -935,6 +1049,14 @@@ class Fram
        var returnlabel: nullable String writable = null
  end
  
 +# An extern C file to compile
 +class ExternCFile
 +      # The filename of the file
 +      var filename: String
 +      # Additionnal specific CC compiler -c flags
 +      var cflags: String
 +end
 +
  redef class String
        # Mangle a string to be a unique valid C identifier
        fun to_cmangle: String
@@@ -1606,7 -1484,7 +1606,7 @@@ redef class AInternMethPropde
                        v.ret(v.new_expr("glob_sys", ret.as(not null)))
                        return
                else if pname == "calloc_string" then
 -                      v.ret(v.new_expr("(char*)GC_MALLOC_ATOMIC({arguments[1]})", ret.as(not null)))
 +                      v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
                        return
                else if pname == "calloc_array" then
                        v.calloc_array(ret.as(not null), arguments)
                        v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
                        return
                else if pname == "force_garbage_collection" then
 -                      v.add("GC_gcollect();")
 +                      v.add("nit_gcollect();")
                        return
                end
                v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
@@@ -1647,7 -1525,7 +1647,7 @@@ redef class AExternMethPropde
                externname = nextern.text.substring(1, nextern.text.length-2)
                if location.file != null then
                        var file = location.file.filename
 -                      v.compiler.add_extern(file)
 +                      v.add_extern(file)
                end
                var res: nullable RuntimeVariable = null
                var ret = mpropdef.msignature.return_mtype
@@@ -1679,7 -1557,7 +1679,7 @@@ redef class AExternInitPropde
                externname = nextern.text.substring(1, nextern.text.length-2)
                if location.file != null then
                        var file = location.file.filename
 -                      v.compiler.add_extern(file)
 +                      v.add_extern(file)
                end
                v.adapt_signature(mpropdef, arguments)
                var ret = arguments.first.mtype
  
  # Utils
  
- redef class HashSet[E]
-       init from(elements: Collection[E]) do
-               init
-               self.add_all(elements)
-       end
- end
  redef class Array[E]
-       init from(elements: Collection[E]) do
-               init
-               self.add_all(elements)
-       end
        # Return a new Array with the elements only contened in 'self' and not in 'o'
        fun -(o: Array[E]): Array[E] do
                var res = new Array[E]