1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Helpful features about packages
20 redef class ToolContext
22 var opt_expand
= new OptionBool("Move singleton packages to their own directory", "--expand")
25 var opt_check_ini
= new OptionBool("Check package.ini files", "--check-ini")
28 var opt_gen_ini
= new OptionBool("Generate package.ini files", "--gen-ini")
31 var opt_force
= new OptionBool("Force update of existing files", "-f", "--force")
33 # README handling phase
34 var readme_phase
: Phase = new ReadmePhase(self, null)
38 option_context
.add_option
(opt_expand
, opt_force
)
39 option_context
.add_option
(opt_check_ini
, opt_gen_ini
)
43 private class ReadmePhase
46 redef fun process_mainmodule
(mainmodule
, mmodules
) do
47 var mpackages
= extract_mpackages
(mmodules
)
48 for mpackage
in mpackages
do
50 # Fictive and buggy packages are ignored
51 if not mpackage
.has_source
then
52 toolcontext
.warning
(mpackage
.location
, "no-source",
53 "Warning: `{mpackage}` has no source file")
57 # Check package INI files
58 if toolcontext
.opt_check_ini
.value
then
59 mpackage
.check_ini
(toolcontext
)
64 if toolcontext
.opt_expand
.value
and not mpackage
.is_expanded
then
65 var path
= mpackage
.expand
66 toolcontext
.info
("{mpackage} moved to {path}", 0)
68 if not mpackage
.is_expanded
then
69 toolcontext
.warning
(mpackage
.location
, "no-dir",
70 "Warning: `{mpackage}` has no package directory")
75 if toolcontext
.opt_gen_ini
.value
then
76 if not mpackage
.has_ini
or toolcontext
.opt_force
.value
then
77 var path
= mpackage
.gen_ini
78 toolcontext
.info
("generated INI file `{path}`", 0)
84 # Extract the list of packages from the mmodules passed as arguments
85 fun extract_mpackages
(mmodules
: Collection[MModule]): Collection[MPackage] do
86 var mpackages
= new ArraySet[MPackage]
87 for mmodule
in mmodules
do
88 var mpackage
= mmodule
.mpackage
89 if mpackage
== null then continue
90 mpackages
.add mpackage
98 # Expand `self` in its own directory
99 private fun expand
: String do
100 assert not is_expanded
102 var ori_path
= package_path
.as(not null)
103 var new_path
= ori_path
.dirname
/ name
106 sys
.system
"mv {ori_path} {new_path / name}.nit"
108 var ini_file
= "{new_path}.ini"
109 if ini_file
.file_exists
then
110 sys
.system
"mv {new_path}.ini {new_path}/package.ini"
116 private var maintainer
: nullable String is lazy
do
117 return git_exec
("git shortlog -esn . | head -n 1 | sed 's/\\s*[0-9]*\\s*//'")
120 private var contributors
: Array[String] is lazy
do
121 var contribs
= git_exec
("git shortlog -esn . | head -n -1 | " +
122 "sed 's/\\s*[0-9]*\\s*//'")
123 if contribs
== null then return new Array[String]
124 return contribs
.split
("\n")
127 private var git_url
: nullable String is lazy
do
128 var git
= git_exec
("git remote get-url origin")
129 if git
== null then return null
130 git
= git
.replace
("git@github.com:", "https://github.com/")
131 git
= git
.replace
("git@gitlab.com:", "https://gitlab.com/")
135 private var git_dir
: nullable String is lazy
do
136 return git_exec
("git rev-parse --show-prefix")
139 private var browse_url
: nullable String is lazy
do
141 if git
== null then return null
142 var browse
= git
.replace
(".git", "")
144 if dir
== null or dir
.is_empty
then return browse
145 return "{browse}/tree/master/{dir}"
148 private var homepage_url
: nullable String is lazy
do
150 if git
== null then return null
151 # Special case for nit files
152 if git
.has_suffix
("/nit.git") then
153 return "http://nitlanguage.org"
155 return git
.replace
(".git", "")
158 private var issues_url
: nullable String is lazy
do
160 if git
== null then return null
161 return "{git.replace(".git", "")}/issues"
164 private var license
: nullable String is lazy
do
166 if git
== null then return null
167 # Special case for nit files
168 if git
.has_suffix
("/nit.git") then
174 private fun git_exec
(cmd
: String): nullable String do
175 var path
= package_path
176 if path
== null then return null
177 if not is_expanded
then path
= path
.dirname
178 with pr
= new ProcessReader("sh", "-c", "cd {path} && {cmd}") do
179 return pr
.read_all
.trim
183 private var allowed_ini_keys
= [
184 "package.name", "package.desc", "package.tags", "package.license",
185 "package.maintainer", "package.more_contributors",
186 "upstream.browse", "upstream.git", "upstream.git.directory",
187 "upstream.homepage", "upstream.issues", "upstream.apk", "upstream.tryit",
191 private fun check_ini
(toolcontext
: ToolContext) do
193 toolcontext
.error
(location
, "No `package.ini` file for `{name}`")
197 var pkg_path
= package_path
198 if pkg_path
== null then return
200 var ini_path
= ini_path
201 if ini_path
== null then return
203 var ini
= new ConfigTree(ini_path
)
205 ini
.check_key
(toolcontext
, self, "package.name", name
)
206 ini
.check_key
(toolcontext
, self, "package.desc")
207 ini
.check_key
(toolcontext
, self, "package.tags")
209 # FIXME since `git reflog --follow` seems bugged
210 ini
.check_key
(toolcontext
, self, "package.maintainer")
211 # var maint = mpackage.maintainer
212 # if maint != null then
213 # ini.check_key(toolcontext, self, "package.maintainer", maint)
216 # FIXME since `git reflog --follow` seems bugged
217 # var contribs = mpackage.contributors
218 # if contribs.not_empty then
219 # ini.check_key(toolcontext, self, "package.more_contributors", contribs.join(", "))
222 ini
.check_key
(toolcontext
, self, "package.license", license
)
223 ini
.check_key
(toolcontext
, self, "upstream.browse", browse_url
)
224 ini
.check_key
(toolcontext
, self, "upstream.git", git_url
)
225 ini
.check_key
(toolcontext
, self, "upstream.git.directory", git_dir
)
226 ini
.check_key
(toolcontext
, self, "upstream.homepage", homepage_url
)
227 ini
.check_key
(toolcontext
, self, "upstream.issues", issues_url
)
229 for key
in ini
.to_map
.keys
do
230 if not allowed_ini_keys
.has
(key
) then
231 toolcontext
.warning
(location
, "unknown-ini-key",
232 "Warning: ignoring unknown `{key}` key in `{ini.ini_file}`")
237 private fun gen_ini
: String do
238 var ini_path
= self.ini_path
.as(not null)
239 var ini
= new ConfigTree(ini_path
)
241 ini
.update_value
("package.name", name
)
242 ini
.update_value
("package.desc", "")
243 ini
.update_value
("package.tags", "")
244 ini
.update_value
("package.maintainer", maintainer
)
245 ini
.update_value
("package.more_contributors", contributors
.join
(","))
246 ini
.update_value
("package.license", license
or else "")
248 ini
.update_value
("upstream.browse", browse_url
)
249 ini
.update_value
("upstream.git", git_url
)
250 ini
.update_value
("upstream.git.directory", git_dir
)
251 ini
.update_value
("upstream.homepage", homepage_url
)
252 ini
.update_value
("upstream.issues", issues_url
)
259 redef class ConfigTree
260 private fun check_key
(toolcontext
: ToolContext, mpackage
: MPackage, key
: String, value
: nullable String) do
261 if not has_key
(key
) then
262 toolcontext
.warning
(mpackage
.location
, "missing-ini-key",
263 "Warning: missing `{key}` key in `{ini_file}`")
266 if self[key
].as(not null).is_empty
then
267 toolcontext
.warning
(mpackage
.location
, "missing-ini-value",
268 "Warning: empty `{key}` key in `{ini_file}`")
271 if value
!= null and self[key
] != value
then
272 toolcontext
.warning
(mpackage
.location
, "wrong-ini-value",
273 "Warning: wrong value for `{key}` in `{ini_file}`. " +
274 "Expected `{value}`, got `{self[key] or else ""}`")
278 private fun update_value
(key
: String, value
: nullable String) do
279 if value
== null then return
280 if not has_key
(key
) then
283 var old_value
= self[key
]
284 if not value
.is_empty
and old_value
!= value
then
292 var toolcontext
= new ToolContext
293 var tpl
= new Template
294 tpl
.add
"Usage: nitpackage [OPTION]... <file.nit>...\n"
295 tpl
.add
"Helpful features about packages."
296 toolcontext
.tooldescription
= tpl
.write_to_string
299 toolcontext
.process_options
(args
)
300 var arguments
= toolcontext
.option_context
.rest
303 var model
= new Model
304 var mbuilder
= new ModelBuilder(model
, toolcontext
)
305 var mmodules
= mbuilder
.parse_full
(arguments
)
308 if mmodules
.is_empty
then return
310 toolcontext
.run_global_phases
(mmodules
)