Merge: New loader policy that features project.ini
authorJean Privat <jean@pryen.org>
Wed, 26 Aug 2015 18:27:03 +0000 (14:27 -0400)
committerJean Privat <jean@pryen.org>
Wed, 26 Aug 2015 18:27:03 +0000 (14:27 -0400)
This introduce the new loader policy by adapting the existing loader.
The API it still as bad as before but the behavior is expected to be better. Close #1250

The changes impact two decisions made by the loader:

* how to find the project a module belongs to? ie get the root project of an arbitrary file.
* how to find a named module in a project? ie get (and load) the module named `foo` in a mproject object.

The new loader use a recursive directory search for both answers, whereas the older loader only looked at some specific paths.
By the way, the new code use `String::files` and `String::file_stat` (they where unneeded by the previous loader, life was simpler back then). The light FFI used by the bootstrap make this possible.

Here is a detail of the new policy:

* A project is a directory identified with a `project.ini` file.
  Only the existence of the `.ini` file matter, the content is not used yet.
* A group is just a sub-directory of a project; there is no more naming or structural convention (ie hack).
* A module either belongs to a project, or is a standalone module (also called singleton project). This one is not new.

To know the project a module belongs to, its directory is recursively searched up to the `/` directory.
If a project is found, then the module belongs to it.
If no project is found, then the module is a standalone module.

Note: to simplify the migration from the previous loader, a special file `projects.ini` (with a plural) in a directory indicates that sub-directories are projects, even without a `project.ini` file.
These files simplify the PR as only some `projects.ini` files are needed (eg `lib/projects.ini`)

To search for a module in a project, the named module is searched in the directory and subdirectories of the project.

This PR also expose some vocabulary for the end user. Maybe some terms could be improved before the PR is merged. Some propositions:

* rename `project` as `package` (so `package.ini` instead of `project.ini`). Might be non-POLA from people coming from a Java ecosystem.
* rename `group` as `directory`. With the new policy, groups are more transparent than ever and are only here for the programmer to group modules as he prefers.

Pull-Request: #1654
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

26 files changed:
contrib/inkscape_tools/tests/projects.ini [new file with mode: 0644]
contrib/projects.ini [new file with mode: 0644]
examples/projects.ini [new file with mode: 0644]
lib/projects.ini [new file with mode: 0644]
src/loader.nit
src/platform/android.nit
src/project.ini [new file with mode: 0644]
tests/alt/projects.ini [new file with mode: 0644]
tests/base_import.nit
tests/project1/module1.nit
tests/project1/module2.nit
tests/project1/module3.nit [new file with mode: 0644]
tests/project1/project.ini [new file with mode: 0644]
tests/project1/project1.nit
tests/project1/project2/foo.nit [new file with mode: 0644]
tests/project1/project2/project.ini [new file with mode: 0644]
tests/project1/project2/project2.nit [new file with mode: 0644]
tests/project1/subdir/module4.nit [new file with mode: 0644]
tests/projects.ini [new file with mode: 0644]
tests/sav/base_import_alt4.res [new file with mode: 0644]
tests/sav/base_import_alt5.res [new file with mode: 0644]
tests/sav/nitls_args1.res
tests/sav/nitls_args2.res
tests/sav/nitls_args3.res
tests/sav/nitls_args4.res
tests/sav/xymus_net.res

diff --git a/contrib/inkscape_tools/tests/projects.ini b/contrib/inkscape_tools/tests/projects.ini
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/contrib/projects.ini b/contrib/projects.ini
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/projects.ini b/examples/projects.ini
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/lib/projects.ini b/lib/projects.ini
new file mode 100644 (file)
index 0000000..e69de29
index 7d1c564..0723cad 100644 (file)
@@ -18,6 +18,7 @@
 module loader
 
 import modelbuilder_base
+import ini
 
 redef class ToolContext
        # Option --path
@@ -203,36 +204,12 @@ redef class ModelBuilder
        do
                # First, look in groups
                var c = mgroup
-               while c != null do
-                       var dirname = c.filepath
-                       if dirname == null then break # virtual group
-                       if dirname.has_suffix(".nit") then break # singleton project
-
-                       # Second, try the directory to find a file
-                       var try_file = dirname + "/" + name + ".nit"
-                       if try_file.file_exists then
-                               var res = self.identify_file(try_file.simplify_path)
-                               assert res != null
-                               return res
-                       end
-
-                       # Third, try if the requested module is itself a group
-                       try_file = dirname + "/" + name + "/" + name + ".nit"
-                       if try_file.file_exists then
-                               var res = self.identify_file(try_file.simplify_path)
-                               assert res != null
-                               return res
-                       end
-
-                       # Fourth, try if the requested module is itself a group with a src
-                       try_file = dirname + "/" + name + "/src/" + name + ".nit"
-                       if try_file.file_exists then
-                               var res = self.identify_file(try_file.simplify_path)
-                               assert res != null
-                               return res
-                       end
-
-                       c = c.parent
+               if c != null then
+                       var r = c.mproject.root
+                       assert r != null
+                       scan_group(r)
+                       var res = r.mmodule_paths_by_name(name)
+                       if res.not_empty then return res.first
                end
 
                # Look at some known directories
@@ -291,50 +268,23 @@ redef class ModelBuilder
        # If found, the path of the file is returned
        private fun search_module_in_paths(location: nullable Location, name: String, lookpaths: Collection[String]): nullable ModulePath
        do
-               var candidate: nullable String = null
+               var res = new ArraySet[ModulePath]
                for dirname in lookpaths do
-                       var try_file = (dirname + "/" + name + ".nit").simplify_path
-                       if try_file.file_exists then
-                               if candidate == null then
-                                       candidate = try_file
-                               else if candidate != try_file then
-                                       # try to disambiguate conflicting modules
-                                       var abs_candidate = module_absolute_path(candidate)
-                                       var abs_try_file = module_absolute_path(try_file)
-                                       if abs_candidate != abs_try_file then
-                                               toolcontext.error(location, "Error: conflicting module file for `{name}`: `{candidate}` `{try_file}`")
-                                       end
-                               end
-                       end
-                       try_file = (dirname + "/" + name + "/" + name + ".nit").simplify_path
-                       if try_file.file_exists then
-                               if candidate == null then
-                                       candidate = try_file
-                               else if candidate != try_file then
-                                       # try to disambiguate conflicting modules
-                                       var abs_candidate = module_absolute_path(candidate)
-                                       var abs_try_file = module_absolute_path(try_file)
-                                       if abs_candidate != abs_try_file then
-                                               toolcontext.error(location, "Error: conflicting module file for `{name}`: `{candidate}` `{try_file}`")
-                                       end
-                               end
-                       end
-                       try_file = (dirname + "/" + name + "/src/" + name + ".nit").simplify_path
-                       if try_file.file_exists then
-                               if candidate == null then
-                                       candidate = try_file
-                               else if candidate != try_file then
-                                       # try to disambiguate conflicting modules
-                                       var abs_candidate = module_absolute_path(candidate)
-                                       var abs_try_file = module_absolute_path(try_file)
-                                       if abs_candidate != abs_try_file then
-                                               toolcontext.error(location, "Error: conflicting module file for `{name}`: `{candidate}` `{try_file}`")
-                                       end
-                               end
+                       # Try a single module file
+                       var mp = identify_file((dirname/"{name}.nit").simplify_path)
+                       if mp != null then res.add mp
+                       # Try the default module of a group
+                       var g = get_mgroup((dirname/name).simplify_path)
+                       if g != null then
+                               scan_group(g)
+                               res.add_all g.mmodule_paths_by_name(name)
                        end
                end
-               if candidate == null then return null
-               return identify_file(candidate)
+               if res.is_empty then return null
+               if res.length > 1 then
+                       toolcontext.error(location, "Error: conflicting module files for `{name}`: `{res.join(",")}`")
+               end
+               return res.first
        end
 
        # Cache for `identify_file` by realpath
@@ -402,7 +352,7 @@ redef class ModelBuilder
                        mgroup = new MGroup(pn, mproject, null) # same name for the root group
                        mgroup.filepath = path
                        mproject.root = mgroup
-                       toolcontext.info("found project `{pn}` at {path}", 2)
+                       toolcontext.info("found singleton project `{pn}` at {path}", 2)
                end
 
                var res = new ModulePath(pn, path, mgroup)
@@ -438,42 +388,63 @@ redef class ModelBuilder
                        return mgroups[rdp]
                end
 
-               # Hack, a group is determined by one of the following:
-               # * the presence of a honomymous nit file
-               # * the fact that the directory is named `src`
-               # * the fact that there is a sub-directory named `src`
+               # Filter out non-directories
+               var stat = dirpath.file_stat
+               if stat == null or not stat.is_dir then
+                       mgroups[rdp] = null
+                       return null
+               end
+
+               # By default, the name of the project or group is the base_name of the directory
                var pn = rdp.basename(".nit")
-               var mp = dirpath.join_path(pn + ".nit").simplify_path
-
-               # dirpath2 is the root directory
-               # dirpath is the src subdirectory directory, if any, else it is the same that dirpath2
-               var dirpath2 = dirpath
-               if not mp.file_exists then
-                       if pn == "src" then
-                               # With a src directory, the group name is the name of the parent directory
-                               dirpath2 = rdp.dirname
-                               pn = dirpath2.basename
-                       else
-                               # Check a `src` subdirectory
-                               dirpath = dirpath2 / "src"
-                               if not dirpath.file_exists then
-                                       # All rules failed, so return null
+
+               # Check `project.ini` that indicate a project
+               var ini = null
+               var parent = null
+               var inipath = dirpath / "project.ini"
+               if inipath.file_exists then
+                       ini = new ConfigTree(inipath)
+               end
+
+               if ini == null then
+                       # No ini, multiple course of action
+
+                       # The root of the directory hierarchy in the file system.
+                       if rdp == "/" then
+                               mgroups[rdp] = null
+                               return null
+                       end
+
+                       # Special stopper `projects.ini`
+                       if (dirpath/"projects.ini").file_exists then
+                               # dirpath cannot be a project since it is a project directory
+                               mgroups[rdp] = null
+                               return null
+                       end
+
+                       # check the parent directory (if it does not contain the stopper file)
+                       var parentpath = dirpath.join_path("..").simplify_path
+                       var stopper = parentpath / "projects.ini"
+                       if not stopper.file_exists then
+                               # Recursively get the parent group
+                               parent = get_mgroup(parentpath)
+                               if parent == null then
+                                       # Parent is not a group, thus we are not a group either
+                                       mgroups[rdp] = null
                                        return null
                                end
                        end
                end
 
-               # check parent directory
-               var parentpath = dirpath2.join_path("..").simplify_path
-               var parent = get_mgroup(parentpath)
-
                var mgroup
                if parent == null then
                        # no parent, thus new project
+                       if ini != null and ini.has_key("name") then pn = ini["name"]
                        var mproject = new MProject(pn, model)
                        mgroup = new MGroup(pn, mproject, null) # same name for the root group
                        mproject.root = mgroup
                        toolcontext.info("found project `{mproject}` at {dirpath}", 2)
+                       mproject.ini = ini
                else
                        mgroup = new MGroup(pn, parent.mproject, parent)
                        toolcontext.info("found sub group `{mgroup.full_name}` at {dirpath}", 2)
@@ -483,8 +454,6 @@ redef class ModelBuilder
                # in src first so the documentation of the project code can be distinct for the documentation of the project usage
                var readme = dirpath.join_path("README.md")
                if not readme.file_exists then readme = dirpath.join_path("README")
-               if not readme.file_exists then readme = dirpath2.join_path("README.md")
-               if not readme.file_exists then readme = dirpath2.join_path("README")
                if readme.file_exists then
                        var mdoc = load_markdown(readme)
                        mgroup.mdoc = mdoc
@@ -492,8 +461,7 @@ redef class ModelBuilder
                end
 
                mgroup.filepath = dirpath
-               mgroups[module_absolute_path(dirpath)] = mgroup
-               mgroups[module_absolute_path(dirpath2)] = mgroup
+               mgroups[rdp] = mgroup
                return mgroup
        end
 
@@ -535,7 +503,10 @@ redef class ModelBuilder
                for f in p.files do
                        var fp = p/f
                        var g = get_mgroup(fp)
-                       if g != null then scan_group(g)
+                       # Recursively scan for groups of the same project
+                       if g != null and g.mproject == mgroup.mproject then
+                               scan_group(g)
+                       end
                        identify_file(fp)
                end
        end
@@ -966,6 +937,15 @@ class ModulePath
        redef fun to_s do return filepath
 end
 
+redef class MProject
+       # The associated `.ini` file, if any
+       #
+       # The `ini` file is given as is and might contain invalid or missing information.
+       #
+       # Some projects, like stand-alone projects or virtual projects have no `ini` file associated.
+       var ini: nullable ConfigTree = null
+end
+
 redef class MGroup
        # Modules paths associated with the group
        var module_paths = new Array[ModulePath]
@@ -993,6 +973,23 @@ redef class MGroup
        #
        # See `ModelBuilder::scan_group`.
        var scanned = false
+
+       # Return the modules in self and subgroups named `name`.
+       #
+       # If `self` is not scanned (see `ModelBuilder::scan_group`) the
+       # results might be partial.
+       fun mmodule_paths_by_name(name: String): Array[ModulePath]
+       do
+               var res = new Array[ModulePath]
+               for g in in_nesting.smallers do
+                       for mp in g.module_paths do
+                               if mp.name == name then
+                                       res.add mp
+                               end
+                       end
+               end
+               return res
+       end
 end
 
 redef class SourceFile
index f001cd3..a925c88 100644 (file)
@@ -266,7 +266,7 @@ $(call import-module,android/native_app_glue)
                        if root != null then
                                var filepath = root.filepath
                                if filepath != null then
-                                       project_root = filepath / ".."
+                                       project_root = filepath
                                end
                        end
                end
diff --git a/src/project.ini b/src/project.ini
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/alt/projects.ini b/tests/alt/projects.ini
new file mode 100644 (file)
index 0000000..e69de29
index 7a057ab..8fb3e6c 100644 (file)
@@ -1,3 +1,3 @@
-import project1 #alt1# import project1::module1 #alt2# import project1::module2 #alt3# import project1::fail
+import project1 #alt1# import project1::module1 #alt2# import project1::module2 #alt3# import project1::fail #alt4# import project1::module4 #alt5# import project1::project2
 
 foo
index 576b35c..7f75de5 100644 (file)
@@ -1,3 +1,17 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
 import kernel
 
 fun foo do 1.output
index 4020f72..a7d5274 100644 (file)
@@ -1,3 +1,17 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
 import project1
 
 redef fun foo do 3.output
diff --git a/tests/project1/module3.nit b/tests/project1/module3.nit
new file mode 100644 (file)
index 0000000..e1509c9
--- /dev/null
@@ -0,0 +1,15 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+import module4
diff --git a/tests/project1/project.ini b/tests/project1/project.ini
new file mode 100644 (file)
index 0000000..e69de29
index b70c23a..62a9576 100644 (file)
@@ -1,3 +1,17 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
 import module1
 
 redef fun foo do 2.output
diff --git a/tests/project1/project2/foo.nit b/tests/project1/project2/foo.nit
new file mode 100644 (file)
index 0000000..7058f2e
--- /dev/null
@@ -0,0 +1,13 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
diff --git a/tests/project1/project2/project.ini b/tests/project1/project2/project.ini
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/project1/project2/project2.nit b/tests/project1/project2/project2.nit
new file mode 100644 (file)
index 0000000..7058f2e
--- /dev/null
@@ -0,0 +1,13 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
diff --git a/tests/project1/subdir/module4.nit b/tests/project1/subdir/module4.nit
new file mode 100644 (file)
index 0000000..ac6563a
--- /dev/null
@@ -0,0 +1,15 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+import module1
diff --git a/tests/projects.ini b/tests/projects.ini
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/sav/base_import_alt4.res b/tests/sav/base_import_alt4.res
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/sav/base_import_alt5.res b/tests/sav/base_import_alt5.res
new file mode 100644 (file)
index 0000000..7be428f
--- /dev/null
@@ -0,0 +1 @@
+alt/base_import_alt5.nit:1,8--25: Error: cannot find module `project2` from `project1`. Tried: ., ../lib/standard, ../lib/standard/collection, alt, ../lib, ../contrib.
index accb0d6..b497cd5 100644 (file)
@@ -2,4 +2,8 @@
 project1 (\e[33mproject1\e[m)
 |--\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
 |--\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
-`--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
+|--\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)
+|--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
+`--subdir (\e[33mproject1/subdir\e[m)
+   `--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
+\e[1mproject2\e[m (\e[33mproject1/project2/project2.nit\e[m)
index accb0d6..b497cd5 100644 (file)
@@ -2,4 +2,8 @@
 project1 (\e[33mproject1\e[m)
 |--\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
 |--\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
-`--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
+|--\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)
+|--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
+`--subdir (\e[33mproject1/subdir\e[m)
+   `--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
+\e[1mproject2\e[m (\e[33mproject1/project2/project2.nit\e[m)
index accb0d6..52b93a0 100644 (file)
@@ -2,4 +2,10 @@
 project1 (\e[33mproject1\e[m)
 |--\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
 |--\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
-`--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
+|--\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)
+|--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
+`--subdir (\e[33mproject1/subdir\e[m)
+   `--\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
+project2 (\e[33mproject1/project2\e[m)
+|--\e[1mfoo\e[m (\e[33mproject1/project2/foo.nit\e[m)
+`--\e[1mproject2\e[m (\e[33mproject1/project2/project2.nit\e[m)
index f54c342..eeed2c7 100644 (file)
@@ -1,4 +1,7 @@
 base_simple3/\e[1mbase_simple3\e[m (\e[33mbase_simple3.nit\e[m)
 project1/\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
 project1/\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
+project1/\e[1mmodule3\e[m (\e[33mproject1/module3.nit\e[m)
+project1/subdir/\e[1mmodule4\e[m (\e[33mproject1/subdir/module4.nit\e[m)
 project1/\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
+project2/\e[1mproject2\e[m (\e[33mproject1/project2/project2.nit\e[m)
index 64d374b..cb5a73d 100644 (file)
@@ -1,2 +1,2 @@
-../examples/nitcorn/src/xymus_net.nit:25,8--26: Error: cannot find module `benitlux_controller` from `nitcorn`. Tried: alt, ../lib, ../contrib, ../examples/nitcorn.
-../examples/nitcorn/src/xymus_net.nit:26,8--29: Error: cannot find module `opportunity_controller` from `nitcorn`. Tried: alt, ../lib, ../contrib, ../examples/nitcorn.
+../examples/nitcorn/src/xymus_net.nit:25,8--26: Error: cannot find module `benitlux_controller` from `src`. Tried: alt, ../lib, ../contrib, ../examples.
+../examples/nitcorn/src/xymus_net.nit:26,8--29: Error: cannot find module `opportunity_controller` from `src`. Tried: alt, ../lib, ../contrib, ../examples.