1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
4 # Copyright 2008 Jean Privat <jean@pryen.org>
5 # Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
19 # This module is used to load a metamodel
26 redef class ToolContext
29 # Paths where to locate modules files
30 readable var _paths
: Array[String] = new Array[String]
32 # List of module loaders
33 var _loaders
: Array[ModuleLoader] = new Array[ModuleLoader]
36 readable var _opt_path
: OptionArray = new OptionArray("Set include path for loaders (may be used more than once)", "-I", "--path")
38 # Option --only-metamodel
39 readable var _opt_only_metamodel
: OptionBool = new OptionBool("Stop after meta-model processing", "--only-metamodel")
42 readable var _opt_only_parse
: OptionBool = new OptionBool("Only proceed to parse step of loaders", "--only-parse")
47 option_context
.add_option
(opt_path
, opt_only_parse
, opt_only_metamodel
)
50 # Parse and process the options given on the command line
51 redef fun process_options
55 # Setup the paths value
56 paths
.append
(opt_path
.value
)
58 var path_env
= "NIT_PATH".environ
59 if not path_env
.is_empty
then
60 paths
.append
(path_env
.split_with
(':'))
63 path_env
= "NIT_DIR".environ
64 if not path_env
.is_empty
then
65 var libname
= "{path_env}/lib"
66 if libname
.file_exists
then paths
.add
(libname
)
69 var libname
= "{sys.program_name.dirname}/../lib"
70 if libname
.file_exists
then paths
.add
(libname
.simplify_path
)
73 # Load and process a module in a directory (or a parent directory).
74 # If the module is already loaded, just return it without further processing.
75 # If no module is found, just return null without complaining.
76 private fun try_to_load
(module_name
: Symbol, dir
: MMDirectory): nullable MMModule
78 # Look in the module directory
79 for m
in dir
.modules
.values
do
80 if m
.name
== module_name
then return m
83 # print "try to load {module_name} in {dir.name} {_loaders.length}"
86 var dir2
= l
.try_to_load_dir
(module_name
, dir
)
88 var m
= try_to_load
(module_name
, dir2
)
96 if l
.can_handle
(module_name
, dir
) then
97 var full_name
= dir
.full_name_for
(module_name
)
98 if _processing_modules
.has
(full_name
) then
99 # FIXME: Generate better error
100 fatal_error
(null, "Error: Dependency loop for module {full_name}")
102 _processing_modules
.add
(full_name
)
103 var m
= l
.load_and_process_module
(self, module_name
, dir
)
104 _processing_modules
.remove
(full_name
)
105 #if m != null then print "loaded {m.name} in {m.directory.name} -> {m.full_name} ({m.full_name.object_id.to_hex})"
113 # List of module currently processed.
114 # Used to prevent dependence loops.
115 var _processing_modules
: HashSet[Symbol] = new HashSet[Symbol]
117 # Locate, load and analysis a module (and its supermodules) from its file name.
118 # If the module is already loaded, just return it without further processing.
119 # Beware, the files are automatically considered root of their directory.
120 fun get_module_from_filename
(filename
: String): MMModule
122 var path
= filename
.dirname
123 var module_name
= filename
.basename
(".nit").to_symbol
125 var dir
= directory_for
(path
)
127 if module_name
.to_s
== filename
then
128 # It's just a modulename
129 # look for it in the path directory "."
130 var m
= try_to_load
(module_name
, dir
)
131 if m
!= null then return m
133 # Else look for it in the path
134 return get_module
(module_name
, null)
137 if not filename
.file_exists
then
138 fatal_error
(null, "Error: File {filename} not found.")
142 # Try to load the module where mentionned
143 var m
= try_to_load
(module_name
, dir
)
144 if m
!= null then return m
146 fatal_error
(null, "Error: {filename} is not a NIT source module.")
150 # Locate, load and analysis a module (and its supermodules).
151 # If the module is already loaded, just return it without further processing.
152 fun get_module
(module_name
: Symbol, from
: nullable MMModule): MMModule
155 var dir
: nullable MMDirectory = from
.directory
157 var m
= try_to_load
(module_name
, dir
)
158 if m
!= null then return m
164 var m
= try_to_load
(module_name
, directory_for
(p
))
165 if m
!= null then return m
167 # FIXME: Generate better error
168 fatal_error
(null, "Error: No ressource found for module {module_name}.")
172 # Return the module directory associated with a given path
173 private fun directory_for
(path
: String): MMDirectory
175 if _path_dirs
.has_key
(path
) then return _path_dirs
[path
]
176 var dir
= new MMDirectory(path
.to_symbol
, path
, null)
177 _path_dirs
[path
] = dir
181 # Association bwtween plain path and module directories
182 var _path_dirs
: Map[String, MMDirectory] = new HashMap[String, MMDirectory]
184 # Register a new module loader
185 fun register_loader
(ml
: ModuleLoader) do _loaders
.add
(ml
)
188 # A load handler know how to load a specific module type
189 interface ModuleLoader
190 # Type of module loaded by the loader
191 type MODULE: MMModule
193 # Extension that the loadhandler accepts
194 fun file_type
: String is abstract
196 # Try to load a new module directory
197 fun try_to_load_dir
(dirname
: Symbol, parent_dir
: MMDirectory): nullable MMDirectory
199 var fname
= "{parent_dir.path}/{dirname}"
200 if not fname
.file_exists
then return null
202 var dir
= new MMDirectory(parent_dir
.full_name_for
(dirname
), fname
, parent_dir
)
206 # Can the loadhandler load a given module?
207 # Return the file found
208 fun can_handle
(module_name
: Symbol, dir
: MMDirectory): Bool
210 var fname
= "{dir.path}/{module_name}.{file_type}"
211 if fname
.file_exists
then return true
215 # Load the module and process it
216 # filename is the result of can_handle
217 fun load_and_process_module
(context
: ToolContext, module_name
: Symbol, dir
: MMDirectory): MODULE
219 var filename
= "{dir.path}/{module_name}.{file_type}".simplify_path
220 var m
= load_module
(context
, module_name
, dir
, filename
)
221 if not context
.opt_only_parse
.value
then process_metamodel
(context
, m
)
225 # Load an parse the module
226 private fun load_module
(context
: ToolContext, module_name
: Symbol, dir
: MMDirectory, filename
: String): MODULE
229 if filename
== "-" then
232 file
= new IFStream.open
(filename
.to_s
)
236 context
.fatal_error
(null, "Error: Problem in opening file {filename}")
238 var m
= parse_file
(context
, file
, filename
, module_name
, dir
)
239 if file
!= stdin
then file
.close
243 # Parse the file to load a module
244 protected fun parse_file
(context
: ToolContext, file
: IFStream, filename
: String, module_name
: Symbol, dir
: MMDirectory): MODULE is abstract
246 # Process a parsed module
247 protected fun process_metamodel
(context
: ToolContext, mod
: MODULE) is abstract