# This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2008 Jean Privat # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Common things for NIT compilation and C generation package compiling_base import mmloader private import utils private import primitive_info import program import compiling_writer redef class ToolContext readable writable var _compdir: nullable String = null readable writable var _clibdir: nullable String = null readable writable var _bindir: nullable String = null readable writable var _output_file: nullable String = null readable writable var _boost: Bool = false readable writable var _no_cc: Bool = false readable writable var _cc_link: Bool = false readable writable var _cc_libs: Array[String] = new Array[String] readable writable var _cc_lib_paths: Array[String] = new Array[String] readable writable var _cc_include_paths: Array[String] = new Array[String] readable writable var _ext_prefix: String = "" end # A program that is compiled to C class CProgram init(p: Program) do _program = p _compdir = p.tc.compdir.as(not null) _build_file = "{compdir}/{program.main_module.cname}._build.sh" end # The Nit program compiled to C readable var _program: Program # C files (full path) required to compile readable var _files: Array[String] = new Array[String] # Includes paths (gcc -I) required to find the headers (.h) of native modules readable var _include_dirs: ArraySet[String] = new ArraySet[String] # The path of the building script readable var _build_file: String # The directory where all files are generated readable var _compdir: String # Return the basename of the public header file (.h) of a module fun module_header_name(m: MMModule): String do if _module_include.has_key(m) then return _module_include[m] end var filename = "{m.cname}.{get_file_ending}.h" _module_include[m] = filename return filename end # Cache for module_header_name var _module_include: Map[MMModule, String] = new HashMap[MMModule, String] # When we are using global compilation, we generate _glob files instead # of _sep files so that we do not corrupt separate compilation fun get_file_ending: String do return if program.tc.global then "_glob" else "_sep" # Generate the shell script that build the program by calling gccx fun generate_build_file do var f = new OFStream.open(_build_file) var verbose = "" var tc = program.tc if tc.verbose_level == 1 then verbose = "-v" else if tc.verbose_level >= 2 then # We catch tc.verbose_level >= 2, since 3+ is not valid with gccx verbose = "-vv" end # include compdir to find frontier files (._nitni.h) from native # implementations as .nit.h must have an import of <{name}._nitni.h> include_dirs.add( "-I {compdir}" ) f.write("#!/bin/sh\n") f.write("# This shell script is generated by NIT to compile the program {program.main_module.full_name}.\n") f.write("CLIBDIR=\"{tc.clibdir.as(not null)}\"\n") f.write("{tc.bindir.as(not null)}/gccx {verbose} -d {compdir} -I $CLIBDIR {include_dirs.join(" ")}") if tc.output_file != null then f.write(" -o {tc.output_file.as(not null)}") else if tc.ext_prefix.is_empty then f.write(" -o {program.main_module.name}") else f.write(" -o {program.main_module.name}_{tc.ext_prefix}") end if tc.boost then f.write(" -O") if not tc.cc_link then f.write(" -x \"-c\"") for l in tc.cc_libs do f.write(" -l {l}") for lp in tc.cc_lib_paths do f.write(" -x \"-L {lp}\"") for ip in tc.cc_include_paths do f.write(" -x \"-I {ip}\"") f.write(" \"$@\" \\\n {files.join("\\\n ")}\n") f.close end # Invoke the build_file fun run_c_compiler do program.tc.info("Building",1) sys.system("sh {_build_file}") end end # Class used to generate files. # Note that in fact it is not a visitor. # Note also that this class is unefficient and poorly designed thus requires love. class CompilerVisitor # Add a line in the current declaration block fun add_decl(s: String) do add_line_to(_decl_writer, s) end # Add a line in the current instr block fun add_instr(s: String) do add_line_to(_writer, s) end fun add_indent(w: Writer) do if _indent_level >= 8 then w.add("\t\t") else for i in [0.._indent_level[ do w.add(" ") end end end fun add_line_to(w: Writer, s: String) do add_indent(w) w.add(s) w.add("\n") end # Add a assignment between a variable and an expression fun add_assignment(v: String, s: String) do if v != s then var w = _writer add_indent(w) w.add(v) w.add(" = ") w.add(s) w.add(";\n") end end # Return a unique new number for the instance fun new_number: Int do var res = _number_cpt _number_cpt = res + 1 return res end # next number for new_number var _number_cpt: Int = 0 # Add an indent level. # New decl and instr will be indented. fun indent do _indent_level += 1 # Remove an indent level. fun unindent do _indent_level -= 1 if _indent_level < 0 then _indent_level = 0 end # The processed mmmodule readable var _mmmodule: MMModule # Where header decl are stored (public stuff) readable writable var _header_writer: Writer # Where current instr are stored (current function declaration) readable writable var _writer: Writer # Where current decl are stored (current function instructions) readable writable var _decl_writer: Writer # Where body instr are stored (C functions body) readable writable var _top_writer: Writer # Where body decl are stored (private C function proptypes and typedefs) readable writable var _top_decl_writer: Writer # The current indent lever readable writable var _indent_level: Int = 0 # The program we are compiling readable var _program: Program # The cprogram associed with program readable var _cprogram: CProgram # Create a new CompilerVisitor based on a module init(mod: MMModule, cp: CProgram) do _mmmodule = mod _cprogram = cp _program = cp.program var w = new Writer _header_writer = w _decl_writer = w w = new Writer _writer = w _top_writer = w _top_decl_writer = w.sub end end redef class MMGlobalProperty # C symbol refering a method inocation fun meth_call: String do return "CALL_{intro.cname}" end # C symbol refering an attribure access fun attr_access: String do return "ATTR_{intro.cname}" end # C symbol refering a virtual type class color fun vt_class_color: String do return "VTCOLOR_{intro.cname}" end # C symbol refering a virtual type class id fun vt_class_id: String do return "VTID_{intro.cname}" end end redef class MMGlobalClass # Cacher result of cname var _cname_cache: nullable String # The mangled name of the global class fun cname: String do var cname = _cname_cache if cname == null then cname = intro.mmmodule.cname + "___" + cmangle(intro.name) _cname_cache = cname end return cname end # C symbol refering the identifier of the class fun id_id: String do return "ID_{cname}" end # C symbol refering the color of the class (for subtype tests) fun color_id: String do return "COLOR_{cname}" end # C symbol refering the init table position of the class (for constructor linearization) fun init_table_pos_id: String do return "INIT_TABLE_POS_{cname}" end end redef class MMModule # Cacher result of cname var _cname_cache: nullable String # The mangled name of the module fun cname: String do var cname = _cname_cache if cname == null then var l = new List[String] var m: nullable MMModule = self while m != null do l.unshift(cmangle(m.name)) var d: nullable MMDirectory = m.directory while d != null and d.owner == m do d = d.parent if d == null then m = null else m = d.owner end cname = l.to_a.join("___") _cname_cache = cname end return cname end end redef class MMLocalClass # The mangled name of the global class fun cname: String do return global.cname end redef class MMLocalProperty # Cacher result of cname var _cname_cache: nullable String # The mangled name of the method fun cname: String do var cname = _cname_cache if cname == null then cname = mmmodule.cname + "___" + cmangle(local_class.name, name) _cname_cache = cname end return cname end # C macro used to get the function for the call of a super property fun super_meth_call: String do return "CALL_SUPER_{cname}" end end