Merge: Added contributing guidelines and link from readme
[nit.git] / src / nitls.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Simple tool to list Nit source files
18 module nitls
19
20 import modelbuilder
21 import ordered_tree
22 import console
23
24 class ProjTree
25 super OrderedTree[MConcern]
26
27 var opt_paths = false
28 var tc: ToolContext
29
30 redef fun display(o)
31 do
32 if o isa MGroup then
33 if opt_paths then
34 return o.filepath.as(not null)
35 else
36 var d = ""
37 if o.mdoc != null then
38 if tc.opt_no_color.value then
39 d = ": {o.mdoc.content.first}"
40 else
41 d = ": {o.mdoc.content.first.green}"
42 end
43 end
44 if tc.opt_no_color.value then
45 return "{o.name}{d} ({o.filepath.to_s})"
46 else
47 return "{o.name}{d} ({o.filepath.yellow})"
48 end
49 end
50 else if o isa MModule then
51 if opt_paths then
52 return o.filepath.as(not null)
53 else
54 var d = ""
55 var dd = ""
56 if o.mdoc != null then
57 if tc.opt_no_color.value then
58 d = ": {o.mdoc.content.first}"
59 else
60 d = ": {o.mdoc.content.first.green}"
61 end
62 end
63 if not o.in_importation.direct_greaters.is_empty then
64 var ms = new Array[String]
65 for m in o.in_importation.direct_greaters do
66 if m.mgroup.mpackage == o.mgroup.mpackage then
67 ms.add m.name
68 else
69 ms.add m.full_name
70 end
71 end
72 if tc.opt_no_color.value then
73 dd = " ({ms.join(" ")})"
74 else
75 dd = " ({ms.join(" ")})".light_gray
76 end
77 end
78 if tc.opt_no_color.value then
79 return "{o.name.bold}{d} ({o.filepath.to_s}){dd}"
80 else
81 return "{o.name.bold}{d} ({o.filepath.yellow}){dd}"
82 end
83 end
84 else
85 abort
86 end
87 end
88 end
89
90 var tc = new ToolContext
91
92 var opt_keep = new OptionBool("Ignore errors and files that are not a Nit source file", "-k", "--keep")
93 var opt_recursive = new OptionBool("Process directories recursively", "-r", "--recursive")
94 var opt_tree = new OptionBool("List source files in their groups and packages", "-t", "--tree")
95 var opt_source = new OptionBool("List source files in a flat list", "-s", "--source")
96 var opt_package = new OptionBool("List packages in a flat list (default)", "-P", "--package")
97 var opt_depends = new OptionBool("List dependencies of given modules", "-d", "--depends")
98 var opt_make = new OptionBool("List dependencies suitable for a rule in a Makefile (alias for -d, -p and -s)", "-M")
99 var opt_paths = new OptionBool("List only path (instead of name + path)", "-p", "--path-only")
100
101 tc.option_context.add_option(opt_keep, opt_recursive, opt_tree, opt_source, opt_package, opt_depends, opt_paths, opt_make)
102 tc.tooldescription = "Usage: nitls [OPTION]... <file.nit|directory>...\nLists the packages and/or paths of Nit sources files."
103 tc.accept_no_arguments = true
104 tc.process_options(args)
105
106 if opt_make.value then
107 opt_depends.value = true
108 opt_paths.value = true
109 opt_source.value = true
110 end
111
112 var sum = opt_tree.value.to_i + opt_source.value.to_i + opt_package.value.to_i
113 if sum > 1 then
114 print "Error: options --tree, --source, and --package are exclusive."
115 print tc.tooldescription
116 exit 1
117 end
118 if sum == 0 then opt_package.value = true
119 tc.keep_going = opt_keep.value
120
121 var model = new Model
122 var mb = new ModelBuilder(model, tc)
123
124 if tc.option_context.rest.is_empty then tc.option_context.rest.add "."
125 var files
126 if opt_recursive.value then
127 files = new Array[String]
128 for d in tc.option_context.rest do
129 var pipe = new ProcessReader("find", d, "-name", "*.nit")
130 while not pipe.eof do
131 var l = pipe.read_line
132 if l == "" then break # last line
133 files.add l
134 end
135 pipe.close
136 pipe.wait
137 if pipe.status != 0 and not opt_keep.value then exit 1
138 end
139 else
140 files = tc.option_context.rest
141 end
142
143 if sum == 0 then
144 # If one of the file is a group, default is `opt_tree` instead of `opt_package`
145 for a in files do
146 var g = mb.identify_group(a)
147 if g != null then
148 opt_tree.value = true
149 opt_package.value = false
150 break
151 end
152 end
153 end
154
155 var mmodules = mb.scan_full(files)
156
157 # Load modules to get more informations
158 for mmodule in mmodules do
159 if not opt_paths.value or opt_depends.value then
160 var ast = mmodule.parse(mb)
161 if ast != null and opt_depends.value then
162 mb.build_module_importation(ast)
163 end
164 end
165 end
166 #tc.check_errors
167
168 if opt_depends.value then
169 # Extends the list of module with the loaded ones
170 mmodules = mb.parsed_modules.to_a
171 end
172
173 var ot = new ProjTree(tc)
174 if opt_tree.value then
175 ot.opt_paths = opt_paths.value
176 var mgroups = new HashSet[MGroup]
177 for mp in mmodules do
178 var pa = mp.mgroup
179 while pa != null and not pa.is_interesting do pa = pa.parent
180 ot.add(pa, mp)
181 while pa != null do
182 mgroups.add pa
183 pa = pa.parent
184 end
185 end
186 for g in mgroups do
187 var pa = g.parent
188 if g.is_interesting then
189 ot.add(pa, g)
190 end
191 end
192 ot.sort_with(alpha_comparator)
193 ot.write_to(stdout)
194 end
195
196 if opt_source.value then
197 alpha_comparator.sort(mmodules)
198 for mp in mmodules do
199 if opt_paths.value then
200 print mp.filepath.as(not null)
201 else
202 print "{mp.mgroup.full_name}{ot.display(mp)}"
203 end
204 end
205 end
206
207 if opt_package.value then
208 var mpackages = new Array[MPackage]
209 for m in mmodules do
210 var p = m.mgroup.mpackage
211 if mpackages.has(p) then continue
212 mpackages.add p
213 end
214
215 alpha_comparator.sort(mpackages)
216 for p in mpackages do
217 var g = p.root.as(not null)
218 var path = g.filepath.as(not null)
219 if opt_paths.value then
220 print path
221 else
222 var d = ""
223 var md = g.mdoc_or_fallback
224 if md != null then
225 if tc.opt_no_color.value then
226 d = ": {md.content.first}"
227 else
228 d = ": {md.content.first.green}"
229 end
230 end
231 if tc.opt_no_color.value then
232 print "{g.name}{d} ({path})"
233 else
234 print "{g.name}{d} ({path.yellow})"
235 end
236 end
237 end
238 end