diff options
-rw-r--r-- | src/luarocks/base/build.lua (renamed from src/luarocks/build.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/config_cmd.lua (renamed from src/luarocks/config_cmd.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/doc.lua (renamed from src/luarocks/doc.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/download.lua (renamed from src/luarocks/download.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/help.lua (renamed from src/luarocks/help.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/install.lua (renamed from src/luarocks/install.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/lint.lua (renamed from src/luarocks/lint.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/list.lua (renamed from src/luarocks/list.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/make.lua (renamed from src/luarocks/make.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/new_version.lua (renamed from src/luarocks/new_version.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/pack.lua (renamed from src/luarocks/pack.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/path_cmd.lua (renamed from src/luarocks/path_cmd.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/purge.lua (renamed from src/luarocks/purge.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/remove.lua (renamed from src/luarocks/remove.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/search.lua (renamed from src/luarocks/search.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/show.lua (renamed from src/luarocks/show.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/unpack.lua (renamed from src/luarocks/unpack.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/upload.lua (renamed from src/luarocks/upload.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/base/write_rockspec.lua (renamed from src/luarocks/write_rockspec.lua) | 0 | ||||
-rw-r--r-- | src/luarocks/cmd/build.lua | 443 | ||||
-rw-r--r-- | src/luarocks/cmd/config_cmd.lua | 71 | ||||
-rw-r--r-- | src/luarocks/cmd/doc.lua | 155 | ||||
-rw-r--r-- | src/luarocks/cmd/download.lua | 107 | ||||
-rw-r--r-- | src/luarocks/cmd/help.lua | 117 | ||||
-rw-r--r-- | src/luarocks/cmd/install.lua | 183 | ||||
-rw-r--r-- | src/luarocks/cmd/lint.lua | 53 | ||||
-rw-r--r-- | src/luarocks/cmd/list.lua | 95 | ||||
-rw-r--r-- | src/luarocks/cmd/make.lua | 86 | ||||
-rw-r--r-- | src/luarocks/cmd/new_version.lua | 199 | ||||
-rw-r--r-- | src/luarocks/cmd/pack.lua | 193 | ||||
-rw-r--r-- | src/luarocks/cmd/path_cmd.lua | 68 | ||||
-rw-r--r-- | src/luarocks/cmd/purge.lua | 77 | ||||
-rw-r--r-- | src/luarocks/cmd/remove.lua | 165 | ||||
-rw-r--r-- | src/luarocks/cmd/search.lua | 482 | ||||
-rw-r--r-- | src/luarocks/cmd/show.lua | 158 | ||||
-rw-r--r-- | src/luarocks/cmd/unpack.lua | 164 | ||||
-rw-r--r-- | src/luarocks/cmd/upload.lua | 94 | ||||
-rw-r--r-- | src/luarocks/cmd/write_rockspec.lua | 376 |
38 files changed, 3286 insertions, 0 deletions
diff --git a/src/luarocks/build.lua b/src/luarocks/base/build.lua index f3b054d2..f3b054d2 100644 --- a/src/luarocks/build.lua +++ b/src/luarocks/base/build.lua | |||
diff --git a/src/luarocks/config_cmd.lua b/src/luarocks/base/config_cmd.lua index b68f7898..b68f7898 100644 --- a/src/luarocks/config_cmd.lua +++ b/src/luarocks/base/config_cmd.lua | |||
diff --git a/src/luarocks/doc.lua b/src/luarocks/base/doc.lua index 5d521276..5d521276 100644 --- a/src/luarocks/doc.lua +++ b/src/luarocks/base/doc.lua | |||
diff --git a/src/luarocks/download.lua b/src/luarocks/base/download.lua index 557d1b65..557d1b65 100644 --- a/src/luarocks/download.lua +++ b/src/luarocks/base/download.lua | |||
diff --git a/src/luarocks/help.lua b/src/luarocks/base/help.lua index d27c3a50..d27c3a50 100644 --- a/src/luarocks/help.lua +++ b/src/luarocks/base/help.lua | |||
diff --git a/src/luarocks/install.lua b/src/luarocks/base/install.lua index c9b085f5..c9b085f5 100644 --- a/src/luarocks/install.lua +++ b/src/luarocks/base/install.lua | |||
diff --git a/src/luarocks/lint.lua b/src/luarocks/base/lint.lua index c9ea45ea..c9ea45ea 100644 --- a/src/luarocks/lint.lua +++ b/src/luarocks/base/lint.lua | |||
diff --git a/src/luarocks/list.lua b/src/luarocks/base/list.lua index 45f1a26f..45f1a26f 100644 --- a/src/luarocks/list.lua +++ b/src/luarocks/base/list.lua | |||
diff --git a/src/luarocks/make.lua b/src/luarocks/base/make.lua index eb38bff0..eb38bff0 100644 --- a/src/luarocks/make.lua +++ b/src/luarocks/base/make.lua | |||
diff --git a/src/luarocks/new_version.lua b/src/luarocks/base/new_version.lua index b13dbb97..b13dbb97 100644 --- a/src/luarocks/new_version.lua +++ b/src/luarocks/base/new_version.lua | |||
diff --git a/src/luarocks/pack.lua b/src/luarocks/base/pack.lua index 655cbf37..655cbf37 100644 --- a/src/luarocks/pack.lua +++ b/src/luarocks/base/pack.lua | |||
diff --git a/src/luarocks/path_cmd.lua b/src/luarocks/base/path_cmd.lua index 516a0c47..516a0c47 100644 --- a/src/luarocks/path_cmd.lua +++ b/src/luarocks/base/path_cmd.lua | |||
diff --git a/src/luarocks/purge.lua b/src/luarocks/base/purge.lua index 50f290c8..50f290c8 100644 --- a/src/luarocks/purge.lua +++ b/src/luarocks/base/purge.lua | |||
diff --git a/src/luarocks/remove.lua b/src/luarocks/base/remove.lua index e7f37604..e7f37604 100644 --- a/src/luarocks/remove.lua +++ b/src/luarocks/base/remove.lua | |||
diff --git a/src/luarocks/search.lua b/src/luarocks/base/search.lua index 44eff694..44eff694 100644 --- a/src/luarocks/search.lua +++ b/src/luarocks/base/search.lua | |||
diff --git a/src/luarocks/show.lua b/src/luarocks/base/show.lua index 1ff81e08..1ff81e08 100644 --- a/src/luarocks/show.lua +++ b/src/luarocks/base/show.lua | |||
diff --git a/src/luarocks/unpack.lua b/src/luarocks/base/unpack.lua index c50701b0..c50701b0 100644 --- a/src/luarocks/unpack.lua +++ b/src/luarocks/base/unpack.lua | |||
diff --git a/src/luarocks/upload.lua b/src/luarocks/base/upload.lua index baee47ab..baee47ab 100644 --- a/src/luarocks/upload.lua +++ b/src/luarocks/base/upload.lua | |||
diff --git a/src/luarocks/write_rockspec.lua b/src/luarocks/base/write_rockspec.lua index be563eaa..be563eaa 100644 --- a/src/luarocks/write_rockspec.lua +++ b/src/luarocks/base/write_rockspec.lua | |||
diff --git a/src/luarocks/cmd/build.lua b/src/luarocks/cmd/build.lua new file mode 100644 index 00000000..f3b054d2 --- /dev/null +++ b/src/luarocks/cmd/build.lua | |||
@@ -0,0 +1,443 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "build" command. | ||
3 | -- Builds a rock, compiling its C parts if any. | ||
4 | local build = {} | ||
5 | |||
6 | local pack = require("luarocks.pack") | ||
7 | local path = require("luarocks.path") | ||
8 | local util = require("luarocks.util") | ||
9 | local repos = require("luarocks.repos") | ||
10 | local fetch = require("luarocks.fetch") | ||
11 | local fs = require("luarocks.fs") | ||
12 | local dir = require("luarocks.dir") | ||
13 | local deps = require("luarocks.deps") | ||
14 | local writer = require("luarocks.manif.writer") | ||
15 | local remove = require("luarocks.remove") | ||
16 | local cfg = require("luarocks.core.cfg") | ||
17 | |||
18 | build.help_summary = "Build/compile a rock." | ||
19 | build.help_arguments = "[--pack-binary-rock] [--keep] {<rockspec>|<rock>|<name> [<version>]}" | ||
20 | build.help = [[ | ||
21 | Build and install a rock, compiling its C parts if any. | ||
22 | Argument may be a rockspec file, a source rock file | ||
23 | or the name of a rock to be fetched from a repository. | ||
24 | |||
25 | --pack-binary-rock Do not install rock. Instead, produce a .rock file | ||
26 | with the contents of compilation in the current | ||
27 | directory. | ||
28 | |||
29 | --keep Do not remove previously installed versions of the | ||
30 | rock after building a new one. This behavior can | ||
31 | be made permanent by setting keep_other_versions=true | ||
32 | in the configuration file. | ||
33 | |||
34 | --branch=<name> Override the `source.branch` field in the loaded | ||
35 | rockspec. Allows to specify a different branch to | ||
36 | fetch. Particularly for SCM rocks. | ||
37 | |||
38 | --only-deps Installs only the dependencies of the rock. | ||
39 | |||
40 | ]]..util.deps_mode_help() | ||
41 | |||
42 | --- Install files to a given location. | ||
43 | -- Takes a table where the array part is a list of filenames to be copied. | ||
44 | -- In the hash part, other keys, if is_module_path is set, are identifiers | ||
45 | -- in Lua module format, to indicate which subdirectory the file should be | ||
46 | -- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") | ||
47 | -- will copy src/bar.lua to boo/foo. | ||
48 | -- @param files table or nil: A table containing a list of files to copy in | ||
49 | -- the format described above. If nil is passed, this function is a no-op. | ||
50 | -- Directories should be delimited by forward slashes as in internet URLs. | ||
51 | -- @param location string: The base directory files should be copied to. | ||
52 | -- @param is_module_path boolean: True if string keys in files should be | ||
53 | -- interpreted as dotted module paths. | ||
54 | -- @param perms string: Permissions of the newly created files installed. | ||
55 | -- Directories are always created with the default permissions. | ||
56 | -- @return boolean or (nil, string): True if succeeded or | ||
57 | -- nil and an error message. | ||
58 | local function install_files(files, location, is_module_path, perms) | ||
59 | assert(type(files) == "table" or not files) | ||
60 | assert(type(location) == "string") | ||
61 | if files then | ||
62 | for k, file in pairs(files) do | ||
63 | local dest = location | ||
64 | local filename = dir.base_name(file) | ||
65 | if type(k) == "string" then | ||
66 | local modname = k | ||
67 | if is_module_path then | ||
68 | dest = dir.path(location, path.module_to_path(modname)) | ||
69 | local ok, err = fs.make_dir(dest) | ||
70 | if not ok then return nil, err end | ||
71 | if filename:match("%.lua$") then | ||
72 | local basename = modname:match("([^.]+)$") | ||
73 | filename = basename..".lua" | ||
74 | end | ||
75 | else | ||
76 | dest = dir.path(location, dir.dir_name(modname)) | ||
77 | local ok, err = fs.make_dir(dest) | ||
78 | if not ok then return nil, err end | ||
79 | filename = dir.base_name(modname) | ||
80 | end | ||
81 | else | ||
82 | local ok, err = fs.make_dir(dest) | ||
83 | if not ok then return nil, err end | ||
84 | end | ||
85 | local ok = fs.copy(dir.path(file), dir.path(dest, filename), perms) | ||
86 | if not ok then | ||
87 | return nil, "Failed copying "..file | ||
88 | end | ||
89 | end | ||
90 | end | ||
91 | return true | ||
92 | end | ||
93 | |||
94 | --- Write to the current directory the contents of a table, | ||
95 | -- where each key is a file name and its value is the file content. | ||
96 | -- @param files table: The table of files to be written. | ||
97 | local function extract_from_rockspec(files) | ||
98 | for name, content in pairs(files) do | ||
99 | local fd = io.open(dir.path(fs.current_dir(), name), "w+") | ||
100 | fd:write(content) | ||
101 | fd:close() | ||
102 | end | ||
103 | end | ||
104 | |||
105 | --- Applies patches inlined in the build.patches section | ||
106 | -- and extracts files inlined in the build.extra_files section | ||
107 | -- of a rockspec. | ||
108 | -- @param rockspec table: A rockspec table. | ||
109 | -- @return boolean or (nil, string): True if succeeded or | ||
110 | -- nil and an error message. | ||
111 | function build.apply_patches(rockspec) | ||
112 | assert(type(rockspec) == "table") | ||
113 | |||
114 | local build_spec = rockspec.build | ||
115 | if build_spec.extra_files then | ||
116 | extract_from_rockspec(build_spec.extra_files) | ||
117 | end | ||
118 | if build_spec.patches then | ||
119 | extract_from_rockspec(build_spec.patches) | ||
120 | for patch, patchdata in util.sortedpairs(build_spec.patches) do | ||
121 | util.printout("Applying patch "..patch.."...") | ||
122 | local ok, err = fs.apply_patch(tostring(patch), patchdata) | ||
123 | if not ok then | ||
124 | return nil, "Failed applying patch "..patch | ||
125 | end | ||
126 | end | ||
127 | end | ||
128 | return true | ||
129 | end | ||
130 | |||
131 | local function install_default_docs(name, version) | ||
132 | local patterns = { "readme", "license", "copying", ".*%.md" } | ||
133 | local dest = dir.path(path.install_dir(name, version), "doc") | ||
134 | local has_dir = false | ||
135 | for file in fs.dir() do | ||
136 | for _, pattern in ipairs(patterns) do | ||
137 | if file:lower():match("^"..pattern) then | ||
138 | if not has_dir then | ||
139 | fs.make_dir(dest) | ||
140 | has_dir = true | ||
141 | end | ||
142 | fs.copy(file, dest, cfg.perm_read) | ||
143 | break | ||
144 | end | ||
145 | end | ||
146 | end | ||
147 | end | ||
148 | |||
149 | local function check_macosx_deployment_target(rockspec) | ||
150 | local target = rockspec.build.macosx_deployment_target | ||
151 | local function minor(version) | ||
152 | return tonumber(version and version:match("^[^.]+%.([^.]+)")) | ||
153 | end | ||
154 | local function patch_variable(var, target) | ||
155 | if rockspec.variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then | ||
156 | rockspec.variables[var] = (rockspec.variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target) | ||
157 | else | ||
158 | rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var] | ||
159 | end | ||
160 | end | ||
161 | if cfg.platforms.macosx and rockspec:format_is_at_least("3.0") and target then | ||
162 | local version = util.popen_read("sw_vers -productVersion") | ||
163 | local versionminor = minor(version) | ||
164 | local targetminor = minor(target) | ||
165 | if targetminor > versionminor then | ||
166 | return nil, ("This rock requires Mac OSX 10.%d, and you are running 10.%d."):format(targetminor, versionminor) | ||
167 | end | ||
168 | patch_variable("CC", target) | ||
169 | patch_variable("LD", target) | ||
170 | end | ||
171 | return true | ||
172 | end | ||
173 | |||
174 | --- Build and install a rock given a rockspec. | ||
175 | -- @param rockspec_file string: local or remote filename of a rockspec. | ||
176 | -- @param need_to_fetch boolean: true if sources need to be fetched, | ||
177 | -- false if the rockspec was obtained from inside a source rock. | ||
178 | -- @param minimal_mode boolean: true if there's no need to fetch, | ||
179 | -- unpack or change dir (this is used by "luarocks make"). Implies | ||
180 | -- need_to_fetch = false. | ||
181 | -- @param deps_mode string: Dependency mode: "one" for the current default tree, | ||
182 | -- "all" for all trees, "order" for all trees with priority >= the current default, | ||
183 | -- "none" for no trees. | ||
184 | -- @param build_only_deps boolean: true to build the listed dependencies only. | ||
185 | -- @return (string, string) or (nil, string, [string]): Name and version of | ||
186 | -- installed rock if succeeded or nil and an error message followed by an error code. | ||
187 | function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode, build_only_deps) | ||
188 | assert(type(rockspec_file) == "string") | ||
189 | assert(type(need_to_fetch) == "boolean") | ||
190 | |||
191 | local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) | ||
192 | if err then | ||
193 | return nil, err, errcode | ||
194 | elseif not rockspec.build then | ||
195 | return nil, "Rockspec error: build table not specified" | ||
196 | elseif not rockspec.build.type then | ||
197 | return nil, "Rockspec error: build type not specified" | ||
198 | end | ||
199 | |||
200 | local ok | ||
201 | if not build_only_deps then | ||
202 | ok, err, errcode = deps.check_external_deps(rockspec, "build") | ||
203 | if err then | ||
204 | return nil, err, errcode | ||
205 | end | ||
206 | end | ||
207 | |||
208 | if deps_mode == "none" then | ||
209 | util.printerr("Warning: skipping dependency checks.") | ||
210 | else | ||
211 | local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) | ||
212 | if err then | ||
213 | return nil, err, errcode | ||
214 | end | ||
215 | end | ||
216 | |||
217 | local name, version = rockspec.name, rockspec.version | ||
218 | if build_only_deps then | ||
219 | util.printout("Stopping after installing dependencies for " ..name.." "..version) | ||
220 | util.printout() | ||
221 | return name, version | ||
222 | end | ||
223 | |||
224 | if repos.is_installed(name, version) then | ||
225 | repos.delete_version(name, version, deps_mode) | ||
226 | end | ||
227 | |||
228 | if not minimal_mode then | ||
229 | local source_dir | ||
230 | if need_to_fetch then | ||
231 | ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) | ||
232 | if not ok then | ||
233 | return nil, source_dir, errcode | ||
234 | end | ||
235 | local ok, err = fs.change_dir(source_dir) | ||
236 | if not ok then return nil, err end | ||
237 | elseif rockspec.source.file then | ||
238 | local ok, err = fs.unpack_archive(rockspec.source.file) | ||
239 | if not ok then | ||
240 | return nil, err | ||
241 | end | ||
242 | end | ||
243 | fs.change_dir(rockspec.source.dir) | ||
244 | end | ||
245 | |||
246 | local dirs = { | ||
247 | lua = { name = path.lua_dir(name, version), is_module_path = true, perms = cfg.perm_read }, | ||
248 | lib = { name = path.lib_dir(name, version), is_module_path = true, perms = cfg.perm_exec }, | ||
249 | conf = { name = path.conf_dir(name, version), is_module_path = false, perms = cfg.perm_read }, | ||
250 | bin = { name = path.bin_dir(name, version), is_module_path = false, perms = cfg.perm_exec }, | ||
251 | } | ||
252 | |||
253 | for _, d in pairs(dirs) do | ||
254 | local ok, err = fs.make_dir(d.name) | ||
255 | if not ok then return nil, err end | ||
256 | end | ||
257 | local rollback = util.schedule_function(function() | ||
258 | fs.delete(path.install_dir(name, version)) | ||
259 | fs.remove_dir_if_empty(path.versions_dir(name)) | ||
260 | end) | ||
261 | |||
262 | local build_spec = rockspec.build | ||
263 | |||
264 | if not minimal_mode then | ||
265 | ok, err = build.apply_patches(rockspec) | ||
266 | if err then | ||
267 | return nil, err | ||
268 | end | ||
269 | end | ||
270 | |||
271 | ok, err = check_macosx_deployment_target(rockspec) | ||
272 | if not ok then | ||
273 | return nil, err | ||
274 | end | ||
275 | |||
276 | if build_spec.type ~= "none" then | ||
277 | |||
278 | -- Temporary compatibility | ||
279 | if build_spec.type == "module" then | ||
280 | util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") | ||
281 | build_spec.type = "builtin" | ||
282 | end | ||
283 | |||
284 | if cfg.accepted_build_types and util.array_contains(cfg.accepted_build_types, build_spec.type) then | ||
285 | return nil, "This rockspec uses the '"..build_spec.type.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." | ||
286 | end | ||
287 | |||
288 | local build_type | ||
289 | ok, build_type = pcall(require, "luarocks.build." .. build_spec.type) | ||
290 | if not ok or not type(build_type) == "table" then | ||
291 | return nil, "Failed initializing build back-end for build type '"..build_spec.type.."': "..build_type | ||
292 | end | ||
293 | |||
294 | ok, err = build_type.run(rockspec) | ||
295 | if not ok then | ||
296 | return nil, "Build error: " .. err | ||
297 | end | ||
298 | end | ||
299 | |||
300 | if build_spec.install then | ||
301 | for id, install_dir in pairs(dirs) do | ||
302 | ok, err = install_files(build_spec.install[id], install_dir.name, install_dir.is_module_path, install_dir.perms) | ||
303 | if not ok then | ||
304 | return nil, err | ||
305 | end | ||
306 | end | ||
307 | end | ||
308 | |||
309 | local copy_directories = build_spec.copy_directories | ||
310 | local copying_default = false | ||
311 | if not copy_directories then | ||
312 | copy_directories = {"doc"} | ||
313 | copying_default = true | ||
314 | end | ||
315 | |||
316 | local any_docs = false | ||
317 | for _, copy_dir in pairs(copy_directories) do | ||
318 | if fs.is_dir(copy_dir) then | ||
319 | local dest = dir.path(path.install_dir(name, version), copy_dir) | ||
320 | fs.make_dir(dest) | ||
321 | fs.copy_contents(copy_dir, dest) | ||
322 | any_docs = true | ||
323 | else | ||
324 | if not copying_default then | ||
325 | return nil, "Directory '"..copy_dir.."' not found" | ||
326 | end | ||
327 | end | ||
328 | end | ||
329 | |||
330 | if not any_docs then | ||
331 | install_default_docs(name, version) | ||
332 | end | ||
333 | |||
334 | for _, d in pairs(dirs) do | ||
335 | fs.remove_dir_if_empty(d.name) | ||
336 | end | ||
337 | |||
338 | fs.pop_dir() | ||
339 | |||
340 | fs.copy(rockspec.local_filename, path.rockspec_file(name, version), cfg.perm_read) | ||
341 | if need_to_fetch then | ||
342 | fs.pop_dir() | ||
343 | end | ||
344 | |||
345 | ok, err = writer.make_rock_manifest(name, version) | ||
346 | if err then return nil, err end | ||
347 | |||
348 | ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) | ||
349 | if err then return nil, err end | ||
350 | |||
351 | util.remove_scheduled_function(rollback) | ||
352 | rollback = util.schedule_function(function() | ||
353 | repos.delete_version(name, version, deps_mode) | ||
354 | end) | ||
355 | |||
356 | ok, err = repos.run_hook(rockspec, "post_install") | ||
357 | if err then return nil, err end | ||
358 | |||
359 | util.announce_install(rockspec) | ||
360 | util.remove_scheduled_function(rollback) | ||
361 | return name, version | ||
362 | end | ||
363 | |||
364 | --- Build and install a rock. | ||
365 | -- @param rock_file string: local or remote filename of a rock. | ||
366 | -- @param need_to_fetch boolean: true if sources need to be fetched, | ||
367 | -- false if the rockspec was obtained from inside a source rock. | ||
368 | -- @param deps_mode: string: Which trees to check dependencies for: | ||
369 | -- "one" for the current default tree, "all" for all trees, | ||
370 | -- "order" for all trees with priority >= the current default, "none" for no trees. | ||
371 | -- @param build_only_deps boolean: true to build the listed dependencies only. | ||
372 | -- @return boolean or (nil, string, [string]): True if build was successful, | ||
373 | -- or false and an error message and an optional error code. | ||
374 | function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps) | ||
375 | assert(type(rock_file) == "string") | ||
376 | assert(type(need_to_fetch) == "boolean") | ||
377 | |||
378 | local ok, err, errcode | ||
379 | local unpack_dir | ||
380 | unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file) | ||
381 | if not unpack_dir then | ||
382 | return nil, err, errcode | ||
383 | end | ||
384 | local rockspec_file = path.rockspec_name_from_rock(rock_file) | ||
385 | ok, err = fs.change_dir(unpack_dir) | ||
386 | if not ok then return nil, err end | ||
387 | ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode, build_only_deps) | ||
388 | fs.pop_dir() | ||
389 | return ok, err, errcode | ||
390 | end | ||
391 | |||
392 | local function do_build(name, version, deps_mode, build_only_deps) | ||
393 | if name:match("%.rockspec$") then | ||
394 | return build.build_rockspec(name, true, false, deps_mode, build_only_deps) | ||
395 | elseif name:match("%.src%.rock$") then | ||
396 | return build.build_rock(name, false, deps_mode, build_only_deps) | ||
397 | elseif name:match("%.all%.rock$") then | ||
398 | local install = require("luarocks.install") | ||
399 | local install_fun = build_only_deps and install.install_binary_rock_deps or install.install_binary_rock | ||
400 | return install_fun(name, deps_mode) | ||
401 | elseif name:match("%.rock$") then | ||
402 | return build.build_rock(name, true, deps_mode, build_only_deps) | ||
403 | elseif not name:match(dir.separator) then | ||
404 | local search = require("luarocks.search") | ||
405 | return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps) | ||
406 | end | ||
407 | return nil, "Don't know what to do with "..name | ||
408 | end | ||
409 | |||
410 | --- Driver function for "build" command. | ||
411 | -- @param name string: A local or remote rockspec or rock file. | ||
412 | -- If a package name is given, forwards the request to "search" and, | ||
413 | -- if returned a result, installs the matching rock. | ||
414 | -- @param version string: When passing a package name, a version number may | ||
415 | -- also be given. | ||
416 | -- @return boolean or (nil, string, exitcode): True if build was successful; nil and an | ||
417 | -- error message otherwise. exitcode is optionally returned. | ||
418 | function build.command(flags, name, version) | ||
419 | if type(name) ~= "string" then | ||
420 | return nil, "Argument missing. "..util.see_help("build") | ||
421 | end | ||
422 | assert(type(version) == "string" or not version) | ||
423 | |||
424 | if flags["pack-binary-rock"] then | ||
425 | return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags)) | ||
426 | else | ||
427 | local ok, err = fs.check_command_permissions(flags) | ||
428 | if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end | ||
429 | ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"]) | ||
430 | if not ok then return nil, err end | ||
431 | name, version = ok, err | ||
432 | if flags["only-deps"] then | ||
433 | return name, version | ||
434 | end | ||
435 | if (not flags["keep"]) and not cfg.keep_other_versions then | ||
436 | local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) | ||
437 | if not ok then util.printerr(err) end | ||
438 | end | ||
439 | return name, version | ||
440 | end | ||
441 | end | ||
442 | |||
443 | return build | ||
diff --git a/src/luarocks/cmd/config_cmd.lua b/src/luarocks/cmd/config_cmd.lua new file mode 100644 index 00000000..b68f7898 --- /dev/null +++ b/src/luarocks/cmd/config_cmd.lua | |||
@@ -0,0 +1,71 @@ | |||
1 | --- Module implementing the LuaRocks "config" command. | ||
2 | -- Queries information about the LuaRocks configuration. | ||
3 | local config_cmd = {} | ||
4 | |||
5 | local cfg = require("luarocks.core.cfg") | ||
6 | local util = require("luarocks.util") | ||
7 | local dir = require("luarocks.dir") | ||
8 | |||
9 | config_cmd.help_summary = "Query information about the LuaRocks configuration." | ||
10 | config_cmd.help_arguments = "<flag>" | ||
11 | config_cmd.help = [[ | ||
12 | --lua-incdir Path to Lua header files. | ||
13 | |||
14 | --lua-libdir Path to Lua library files. | ||
15 | |||
16 | --lua-ver Lua version (in major.minor format). e.g. 5.1 | ||
17 | |||
18 | --system-config Location of the system config file. | ||
19 | |||
20 | --user-config Location of the user config file. | ||
21 | |||
22 | --rock-trees Rocks trees in use. First the user tree, then the system tree. | ||
23 | ]] | ||
24 | |||
25 | local function config_file(conf) | ||
26 | print(dir.normalize(conf.file)) | ||
27 | if conf.ok then | ||
28 | return true | ||
29 | else | ||
30 | return nil, "file not found" | ||
31 | end | ||
32 | end | ||
33 | |||
34 | --- Driver function for "config" command. | ||
35 | -- @return boolean: True if succeeded, nil on errors. | ||
36 | function config_cmd.command(flags) | ||
37 | if flags["lua-incdir"] then | ||
38 | print(cfg.variables.LUA_INCDIR) | ||
39 | return true | ||
40 | end | ||
41 | if flags["lua-libdir"] then | ||
42 | print(cfg.variables.LUA_LIBDIR) | ||
43 | return true | ||
44 | end | ||
45 | if flags["lua-ver"] then | ||
46 | print(cfg.lua_version) | ||
47 | return true | ||
48 | end | ||
49 | local conf = cfg.which_config() | ||
50 | if flags["system-config"] then | ||
51 | return config_file(conf.system) | ||
52 | end | ||
53 | if flags["user-config"] then | ||
54 | return config_file(conf.user) | ||
55 | end | ||
56 | if flags["rock-trees"] then | ||
57 | for _, tree in ipairs(cfg.rocks_trees) do | ||
58 | if type(tree) == "string" then | ||
59 | util.printout(dir.normalize(tree)) | ||
60 | else | ||
61 | local name = tree.name and "\t"..tree.name or "" | ||
62 | util.printout(dir.normalize(tree.root)..name) | ||
63 | end | ||
64 | end | ||
65 | return true | ||
66 | end | ||
67 | |||
68 | return nil, "Please provide a flag for querying configuration values. "..util.see_help("config") | ||
69 | end | ||
70 | |||
71 | return config_cmd | ||
diff --git a/src/luarocks/cmd/doc.lua b/src/luarocks/cmd/doc.lua new file mode 100644 index 00000000..5d521276 --- /dev/null +++ b/src/luarocks/cmd/doc.lua | |||
@@ -0,0 +1,155 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "doc" command. | ||
3 | -- Shows documentation for an installed rock. | ||
4 | local doc = {} | ||
5 | |||
6 | local util = require("luarocks.util") | ||
7 | local search = require("luarocks.search") | ||
8 | local path = require("luarocks.path") | ||
9 | local dir = require("luarocks.dir") | ||
10 | local fetch = require("luarocks.fetch") | ||
11 | local fs = require("luarocks.fs") | ||
12 | local download = require("luarocks.download") | ||
13 | |||
14 | doc.help_summary = "Show documentation for an installed rock." | ||
15 | |||
16 | doc.help = [[ | ||
17 | <argument> is an existing package name. | ||
18 | Without any flags, tries to load the documentation | ||
19 | using a series of heuristics. | ||
20 | With these flags, return only the desired information: | ||
21 | |||
22 | --home Open the home page of project. | ||
23 | --list List documentation files only. | ||
24 | |||
25 | For more information about a rock, see the 'show' command. | ||
26 | ]] | ||
27 | |||
28 | local function show_homepage(homepage, name, version) | ||
29 | if not homepage then | ||
30 | return nil, "No 'homepage' field in rockspec for "..name.." "..version | ||
31 | end | ||
32 | util.printout("Opening "..homepage.." ...") | ||
33 | fs.browser(homepage) | ||
34 | return true | ||
35 | end | ||
36 | |||
37 | local function try_to_open_homepage(name, version) | ||
38 | local temp_dir, err = fs.make_temp_dir("doc-"..name.."-"..(version or "")) | ||
39 | if not temp_dir then | ||
40 | return nil, "Failed creating temporary directory: "..err | ||
41 | end | ||
42 | util.schedule_function(fs.delete, temp_dir) | ||
43 | local ok, err = fs.change_dir(temp_dir) | ||
44 | if not ok then return nil, err end | ||
45 | local filename, err = download.download("rockspec", name, version) | ||
46 | if not filename then return nil, err end | ||
47 | local rockspec, err = fetch.load_local_rockspec(filename) | ||
48 | if not rockspec then return nil, err end | ||
49 | fs.pop_dir() | ||
50 | local descript = rockspec.description or {} | ||
51 | if not descript.homepage then return nil, "No homepage defined for "..name end | ||
52 | return show_homepage(descript.homepage, name, version) | ||
53 | end | ||
54 | |||
55 | --- Driver function for "doc" command. | ||
56 | -- @param name or nil: an existing package name. | ||
57 | -- @param version string or nil: a version may also be passed. | ||
58 | -- @return boolean: True if succeeded, nil on errors. | ||
59 | function doc.command(flags, name, version) | ||
60 | if not name then | ||
61 | return nil, "Argument missing. "..util.see_help("doc") | ||
62 | end | ||
63 | |||
64 | name = name:lower() | ||
65 | |||
66 | local iname, iversion, repo = search.pick_installed_rock(name, version, flags["tree"]) | ||
67 | if not iname then | ||
68 | util.printout(name..(version and " "..version or "").." is not installed. Looking for it in the rocks servers...") | ||
69 | return try_to_open_homepage(name, version) | ||
70 | end | ||
71 | name, version = iname, iversion | ||
72 | |||
73 | local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version, repo)) | ||
74 | if not rockspec then return nil,err end | ||
75 | local descript = rockspec.description or {} | ||
76 | |||
77 | if flags["home"] then | ||
78 | return show_homepage(descript.homepage, name, version) | ||
79 | end | ||
80 | |||
81 | local directory = path.install_dir(name,version,repo) | ||
82 | |||
83 | local docdir | ||
84 | local directories = { "doc", "docs" } | ||
85 | for _, d in ipairs(directories) do | ||
86 | local dirname = dir.path(directory, d) | ||
87 | if fs.is_dir(dirname) then | ||
88 | docdir = dirname | ||
89 | break | ||
90 | end | ||
91 | end | ||
92 | if not docdir then | ||
93 | if descript.homepage and not flags["list"] then | ||
94 | util.printout("Local documentation directory not found -- opening "..descript.homepage.." ...") | ||
95 | fs.browser(descript.homepage) | ||
96 | return true | ||
97 | end | ||
98 | return nil, "Documentation directory not found for "..name.." "..version | ||
99 | end | ||
100 | |||
101 | docdir = dir.normalize(docdir):gsub("/+", "/") | ||
102 | local files = fs.find(docdir) | ||
103 | local htmlpatt = "%.html?$" | ||
104 | local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" } | ||
105 | local basenames = { "index", "readme", "manual" } | ||
106 | |||
107 | local porcelain = flags["porcelain"] | ||
108 | if #files > 0 then | ||
109 | util.title("Documentation files for "..name.." "..version, porcelain) | ||
110 | if porcelain then | ||
111 | for _, file in ipairs(files) do | ||
112 | util.printout(docdir.."/"..file) | ||
113 | end | ||
114 | else | ||
115 | util.printout(docdir.."/") | ||
116 | for _, file in ipairs(files) do | ||
117 | util.printout("\t"..file) | ||
118 | end | ||
119 | end | ||
120 | end | ||
121 | |||
122 | if flags["list"] then | ||
123 | return true | ||
124 | end | ||
125 | |||
126 | for _, extension in ipairs(extensions) do | ||
127 | for _, basename in ipairs(basenames) do | ||
128 | local filename = basename..extension | ||
129 | local found | ||
130 | for _, file in ipairs(files) do | ||
131 | if file:lower():match(filename) and ((not found) or #file < #found) then | ||
132 | found = file | ||
133 | end | ||
134 | end | ||
135 | if found then | ||
136 | local pathname = dir.path(docdir, found) | ||
137 | util.printout() | ||
138 | util.printout("Opening "..pathname.." ...") | ||
139 | util.printout() | ||
140 | local ok = fs.browser(pathname) | ||
141 | if not ok and not pathname:match(htmlpatt) then | ||
142 | local fd = io.open(pathname, "r") | ||
143 | util.printout(fd:read("*a")) | ||
144 | fd:close() | ||
145 | end | ||
146 | return true | ||
147 | end | ||
148 | end | ||
149 | end | ||
150 | |||
151 | return true | ||
152 | end | ||
153 | |||
154 | |||
155 | return doc | ||
diff --git a/src/luarocks/cmd/download.lua b/src/luarocks/cmd/download.lua new file mode 100644 index 00000000..557d1b65 --- /dev/null +++ b/src/luarocks/cmd/download.lua | |||
@@ -0,0 +1,107 @@ | |||
1 | |||
2 | --- Module implementing the luarocks "download" command. | ||
3 | -- Download a rock from the repository. | ||
4 | local download = {} | ||
5 | |||
6 | local util = require("luarocks.util") | ||
7 | local path = require("luarocks.path") | ||
8 | local fetch = require("luarocks.fetch") | ||
9 | local search = require("luarocks.search") | ||
10 | local fs = require("luarocks.fs") | ||
11 | local dir = require("luarocks.dir") | ||
12 | local cfg = require("luarocks.core.cfg") | ||
13 | |||
14 | download.help_summary = "Download a specific rock file from a rocks server." | ||
15 | download.help_arguments = "[--all] [--arch=<arch> | --source | --rockspec] [<name> [<version>]]" | ||
16 | |||
17 | download.help = [[ | ||
18 | --all Download all files if there are multiple matches. | ||
19 | --source Download .src.rock if available. | ||
20 | --rockspec Download .rockspec if available. | ||
21 | --arch=<arch> Download rock for a specific architecture. | ||
22 | ]] | ||
23 | |||
24 | local function get_file(filename) | ||
25 | local protocol, pathname = dir.split_url(filename) | ||
26 | if protocol == "file" then | ||
27 | local ok, err = fs.copy(pathname, fs.current_dir(), cfg.perm_read) | ||
28 | if ok then | ||
29 | return pathname | ||
30 | else | ||
31 | return nil, err | ||
32 | end | ||
33 | else | ||
34 | return fetch.fetch_url(filename) | ||
35 | end | ||
36 | end | ||
37 | |||
38 | function download.download(arch, name, version, all) | ||
39 | local query = search.make_query(name, version) | ||
40 | if arch then query.arch = arch end | ||
41 | local search_err | ||
42 | |||
43 | if all then | ||
44 | if name == "" then query.exact_name = false end | ||
45 | local results = search.search_repos(query) | ||
46 | local has_result = false | ||
47 | local all_ok = true | ||
48 | local any_err = "" | ||
49 | for name, result in pairs(results) do | ||
50 | for version, items in pairs(result) do | ||
51 | for _, item in ipairs(items) do | ||
52 | -- Ignore provided rocks. | ||
53 | if item.arch ~= "installed" then | ||
54 | has_result = true | ||
55 | local filename = path.make_url(item.repo, name, version, item.arch) | ||
56 | local ok, err = get_file(filename) | ||
57 | if not ok then | ||
58 | all_ok = false | ||
59 | any_err = any_err .. "\n" .. err | ||
60 | end | ||
61 | end | ||
62 | end | ||
63 | end | ||
64 | end | ||
65 | |||
66 | if has_result then | ||
67 | return all_ok, any_err | ||
68 | end | ||
69 | else | ||
70 | local url | ||
71 | url, search_err = search.find_suitable_rock(query) | ||
72 | if url then | ||
73 | return get_file(url) | ||
74 | end | ||
75 | end | ||
76 | return nil, "Could not find a result named "..name..(version and " "..version or "").. | ||
77 | (search_err and ": "..search_err or ".") | ||
78 | end | ||
79 | |||
80 | --- Driver function for the "download" command. | ||
81 | -- @param name string: a rock name. | ||
82 | -- @param version string or nil: if the name of a package is given, a | ||
83 | -- version may also be passed. | ||
84 | -- @return boolean or (nil, string): true if successful or nil followed | ||
85 | -- by an error message. | ||
86 | function download.command(flags, name, version) | ||
87 | assert(type(version) == "string" or not version) | ||
88 | if type(name) ~= "string" and not flags["all"] then | ||
89 | return nil, "Argument missing. "..util.see_help("download") | ||
90 | end | ||
91 | if not name then name, version = "", "" end | ||
92 | |||
93 | local arch | ||
94 | |||
95 | if flags["source"] then | ||
96 | arch = "src" | ||
97 | elseif flags["rockspec"] then | ||
98 | arch = "rockspec" | ||
99 | elseif flags["arch"] then | ||
100 | arch = flags["arch"] | ||
101 | end | ||
102 | |||
103 | local dl, err = download.download(arch, name:lower(), version, flags["all"]) | ||
104 | return dl and true, err | ||
105 | end | ||
106 | |||
107 | return download | ||
diff --git a/src/luarocks/cmd/help.lua b/src/luarocks/cmd/help.lua new file mode 100644 index 00000000..d27c3a50 --- /dev/null +++ b/src/luarocks/cmd/help.lua | |||
@@ -0,0 +1,117 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "help" command. | ||
3 | -- This is a generic help display module, which | ||
4 | -- uses a global table called "commands" to find commands | ||
5 | -- to show help for; each command should be represented by a | ||
6 | -- table containing "help" and "help_summary" fields. | ||
7 | local help = {} | ||
8 | |||
9 | local util = require("luarocks.util") | ||
10 | local cfg = require("luarocks.core.cfg") | ||
11 | local dir = require("luarocks.dir") | ||
12 | |||
13 | local program = util.this_program("luarocks") | ||
14 | |||
15 | help.help_summary = "Help on commands. Type '"..program.." help <command>' for more." | ||
16 | |||
17 | help.help_arguments = "[<command>]" | ||
18 | help.help = [[ | ||
19 | <command> is the command to show help for. | ||
20 | ]] | ||
21 | |||
22 | local function print_banner() | ||
23 | util.printout("\nLuaRocks "..cfg.program_version..", a module deployment system for Lua") | ||
24 | end | ||
25 | |||
26 | local function print_section(section) | ||
27 | util.printout("\n"..section) | ||
28 | end | ||
29 | |||
30 | local function get_status(status) | ||
31 | if status then | ||
32 | return "ok" | ||
33 | else | ||
34 | return "not found" | ||
35 | end | ||
36 | end | ||
37 | |||
38 | --- Driver function for the "help" command. | ||
39 | -- @param command string or nil: command to show help for; if not | ||
40 | -- given, help summaries for all commands are shown. | ||
41 | -- @return boolean or (nil, string): true if there were no errors | ||
42 | -- or nil and an error message if an invalid command was requested. | ||
43 | function help.command(flags, command) | ||
44 | if not command then | ||
45 | local conf = cfg.which_config() | ||
46 | print_banner() | ||
47 | print_section("NAME") | ||
48 | util.printout("\t"..program..[[ - ]]..program_description) | ||
49 | print_section("SYNOPSIS") | ||
50 | util.printout("\t"..program..[[ [--from=<server> | --only-from=<server>] [--to=<tree>] [VAR=VALUE]... <command> [<argument>] ]]) | ||
51 | print_section("GENERAL OPTIONS") | ||
52 | util.printout([[ | ||
53 | These apply to all commands, as appropriate: | ||
54 | |||
55 | --server=<server> Fetch rocks/rockspecs from this server | ||
56 | (takes priority over config file) | ||
57 | --only-server=<server> Fetch rocks/rockspecs from this server only | ||
58 | (overrides any entries in the config file) | ||
59 | --only-sources=<url> Restrict downloads to paths matching the | ||
60 | given URL. | ||
61 | --tree=<tree> Which tree to operate on. | ||
62 | --local Use the tree in the user's home directory. | ||
63 | To enable it, see ']]..program..[[ help path'. | ||
64 | --verbose Display verbose output of commands executed. | ||
65 | --timeout=<seconds> Timeout on network operations, in seconds. | ||
66 | 0 means no timeout (wait forever). | ||
67 | Default is ]]..tostring(cfg.connection_timeout)..[[.]]) | ||
68 | print_section("VARIABLES") | ||
69 | util.printout([[ | ||
70 | Variables from the "variables" table of the configuration file | ||
71 | can be overriden with VAR=VALUE assignments.]]) | ||
72 | print_section("COMMANDS") | ||
73 | for name, command in util.sortedpairs(commands) do | ||
74 | local cmd = require(command) | ||
75 | util.printout("", name) | ||
76 | util.printout("\t", cmd.help_summary) | ||
77 | end | ||
78 | print_section("CONFIGURATION") | ||
79 | util.printout("\tLua version: " .. cfg.lua_version) | ||
80 | util.printout("\tConfiguration files:") | ||
81 | util.printout("\t\tSystem: ".. dir.normalize(conf.system.file) .. " (" .. get_status(conf.system.ok) ..")") | ||
82 | if conf.user.file then | ||
83 | util.printout("\t\tUser : ".. dir.normalize(conf.user.file) .. " (" .. get_status(conf.user.ok) ..")\n") | ||
84 | else | ||
85 | util.printout("\t\tUser : disabled in this LuaRocks installation.\n") | ||
86 | end | ||
87 | util.printout("\tRocks trees in use: ") | ||
88 | for _, tree in ipairs(cfg.rocks_trees) do | ||
89 | if type(tree) == "string" then | ||
90 | util.printout("\t\t"..dir.normalize(tree)) | ||
91 | else | ||
92 | local name = tree.name and " (\""..tree.name.."\")" or "" | ||
93 | util.printout("\t\t"..dir.normalize(tree.root)..name) | ||
94 | end | ||
95 | end | ||
96 | else | ||
97 | command = command:gsub("-", "_") | ||
98 | local cmd = commands[command] and require(commands[command]) | ||
99 | if cmd then | ||
100 | local arguments = cmd.help_arguments or "<argument>" | ||
101 | print_banner() | ||
102 | print_section("NAME") | ||
103 | util.printout("\t"..program.." "..command.." - "..cmd.help_summary) | ||
104 | print_section("SYNOPSIS") | ||
105 | util.printout("\t"..program.." "..command.." "..arguments) | ||
106 | print_section("DESCRIPTION") | ||
107 | util.printout("",(cmd.help:gsub("\n","\n\t"):gsub("\n\t$",""))) | ||
108 | print_section("SEE ALSO") | ||
109 | util.printout("","'"..program.." help' for general options and configuration.\n") | ||
110 | else | ||
111 | return nil, "Unknown command: "..command | ||
112 | end | ||
113 | end | ||
114 | return true | ||
115 | end | ||
116 | |||
117 | return help | ||
diff --git a/src/luarocks/cmd/install.lua b/src/luarocks/cmd/install.lua new file mode 100644 index 00000000..c9b085f5 --- /dev/null +++ b/src/luarocks/cmd/install.lua | |||
@@ -0,0 +1,183 @@ | |||
1 | --- Module implementing the LuaRocks "install" command. | ||
2 | -- Installs binary rocks. | ||
3 | local install = {} | ||
4 | |||
5 | local path = require("luarocks.path") | ||
6 | local repos = require("luarocks.repos") | ||
7 | local fetch = require("luarocks.fetch") | ||
8 | local util = require("luarocks.util") | ||
9 | local fs = require("luarocks.fs") | ||
10 | local deps = require("luarocks.deps") | ||
11 | local writer = require("luarocks.manif.writer") | ||
12 | local remove = require("luarocks.remove") | ||
13 | local cfg = require("luarocks.core.cfg") | ||
14 | |||
15 | install.help_summary = "Install a rock." | ||
16 | |||
17 | install.help_arguments = "{<rock>|<name> [<version>]}" | ||
18 | |||
19 | install.help = [[ | ||
20 | Argument may be the name of a rock to be fetched from a repository | ||
21 | or a filename of a locally available rock. | ||
22 | |||
23 | --keep Do not remove previously installed versions of the | ||
24 | rock after installing a new one. This behavior can | ||
25 | be made permanent by setting keep_other_versions=true | ||
26 | in the configuration file. | ||
27 | |||
28 | --only-deps Installs only the dependencies of the rock. | ||
29 | ]]..util.deps_mode_help() | ||
30 | |||
31 | |||
32 | --- Install a binary rock. | ||
33 | -- @param rock_file string: local or remote filename of a rock. | ||
34 | -- @param deps_mode: string: Which trees to check dependencies for: | ||
35 | -- "one" for the current default tree, "all" for all trees, | ||
36 | -- "order" for all trees with priority >= the current default, "none" for no trees. | ||
37 | -- @return (string, string) or (nil, string, [string]): Name and version of | ||
38 | -- installed rock if succeeded or nil and an error message followed by an error code. | ||
39 | function install.install_binary_rock(rock_file, deps_mode) | ||
40 | assert(type(rock_file) == "string") | ||
41 | |||
42 | local name, version, arch = path.parse_name(rock_file) | ||
43 | if not name then | ||
44 | return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." | ||
45 | end | ||
46 | |||
47 | if arch ~= "all" and arch ~= cfg.arch then | ||
48 | return nil, "Incompatible architecture "..arch, "arch" | ||
49 | end | ||
50 | if repos.is_installed(name, version) then | ||
51 | repos.delete_version(name, version, deps_mode) | ||
52 | end | ||
53 | |||
54 | local rollback = util.schedule_function(function() | ||
55 | fs.delete(path.install_dir(name, version)) | ||
56 | fs.remove_dir_if_empty(path.versions_dir(name)) | ||
57 | end) | ||
58 | |||
59 | local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) | ||
60 | if not ok then return nil, err, errcode end | ||
61 | |||
62 | local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) | ||
63 | if err then | ||
64 | return nil, "Failed loading rockspec for installed package: "..err, errcode | ||
65 | end | ||
66 | |||
67 | if deps_mode == "none" then | ||
68 | util.printerr("Warning: skipping dependency checks.") | ||
69 | else | ||
70 | ok, err, errcode = deps.check_external_deps(rockspec, "install") | ||
71 | if err then return nil, err, errcode end | ||
72 | end | ||
73 | |||
74 | -- For compatibility with .rock files built with LuaRocks 1 | ||
75 | if not fs.exists(path.rock_manifest_file(name, version)) then | ||
76 | ok, err = writer.make_rock_manifest(name, version) | ||
77 | if err then return nil, err end | ||
78 | end | ||
79 | |||
80 | if deps_mode ~= "none" then | ||
81 | ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) | ||
82 | if err then return nil, err, errcode end | ||
83 | end | ||
84 | |||
85 | ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) | ||
86 | if err then return nil, err end | ||
87 | |||
88 | util.remove_scheduled_function(rollback) | ||
89 | rollback = util.schedule_function(function() | ||
90 | repos.delete_version(name, version, deps_mode) | ||
91 | end) | ||
92 | |||
93 | ok, err = repos.run_hook(rockspec, "post_install") | ||
94 | if err then return nil, err end | ||
95 | |||
96 | util.announce_install(rockspec) | ||
97 | util.remove_scheduled_function(rollback) | ||
98 | return name, version | ||
99 | end | ||
100 | |||
101 | --- Installs the dependencies of a binary rock. | ||
102 | -- @param rock_file string: local or remote filename of a rock. | ||
103 | -- @param deps_mode: string: Which trees to check dependencies for: | ||
104 | -- "one" for the current default tree, "all" for all trees, | ||
105 | -- "order" for all trees with priority >= the current default, "none" for no trees. | ||
106 | -- @return (string, string) or (nil, string, [string]): Name and version of | ||
107 | -- the rock whose dependencies were installed if succeeded or nil and an error message | ||
108 | -- followed by an error code. | ||
109 | function install.install_binary_rock_deps(rock_file, deps_mode) | ||
110 | assert(type(rock_file) == "string") | ||
111 | |||
112 | local name, version, arch = path.parse_name(rock_file) | ||
113 | if not name then | ||
114 | return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." | ||
115 | end | ||
116 | |||
117 | if arch ~= "all" and arch ~= cfg.arch then | ||
118 | return nil, "Incompatible architecture "..arch, "arch" | ||
119 | end | ||
120 | |||
121 | local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) | ||
122 | if not ok then return nil, err, errcode end | ||
123 | |||
124 | local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) | ||
125 | if err then | ||
126 | return nil, "Failed loading rockspec for installed package: "..err, errcode | ||
127 | end | ||
128 | |||
129 | ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) | ||
130 | if err then return nil, err, errcode end | ||
131 | |||
132 | util.printout() | ||
133 | util.printout("Successfully installed dependencies for " ..name.." "..version) | ||
134 | |||
135 | return name, version | ||
136 | end | ||
137 | |||
138 | --- Driver function for the "install" command. | ||
139 | -- @param name string: name of a binary rock. If an URL or pathname | ||
140 | -- to a binary rock is given, fetches and installs it. If a rockspec or a | ||
141 | -- source rock is given, forwards the request to the "build" command. | ||
142 | -- If a package name is given, forwards the request to "search" and, | ||
143 | -- if returned a result, installs the matching rock. | ||
144 | -- @param version string: When passing a package name, a version number | ||
145 | -- may also be given. | ||
146 | -- @return boolean or (nil, string, exitcode): True if installation was | ||
147 | -- successful, nil and an error message otherwise. exitcode is optionally returned. | ||
148 | function install.command(flags, name, version) | ||
149 | if type(name) ~= "string" then | ||
150 | return nil, "Argument missing. "..util.see_help("install") | ||
151 | end | ||
152 | |||
153 | local ok, err = fs.check_command_permissions(flags) | ||
154 | if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end | ||
155 | |||
156 | if name:match("%.rockspec$") or name:match("%.src%.rock$") then | ||
157 | local build = require("luarocks.build") | ||
158 | return build.command(flags, name) | ||
159 | elseif name:match("%.rock$") then | ||
160 | if flags["only-deps"] then | ||
161 | ok, err = install.install_binary_rock_deps(name, deps.get_deps_mode(flags)) | ||
162 | else | ||
163 | ok, err = install.install_binary_rock(name, deps.get_deps_mode(flags)) | ||
164 | end | ||
165 | if not ok then return nil, err end | ||
166 | name, version = ok, err | ||
167 | if (not flags["only-deps"]) and (not flags["keep"]) and not cfg.keep_other_versions then | ||
168 | local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) | ||
169 | if not ok then util.printerr(err) end | ||
170 | end | ||
171 | return name, version | ||
172 | else | ||
173 | local search = require("luarocks.search") | ||
174 | local url, err = search.find_suitable_rock(search.make_query(name:lower(), version)) | ||
175 | if not url then | ||
176 | return nil, err | ||
177 | end | ||
178 | util.printout("Installing "..url) | ||
179 | return install.command(flags, url) | ||
180 | end | ||
181 | end | ||
182 | |||
183 | return install | ||
diff --git a/src/luarocks/cmd/lint.lua b/src/luarocks/cmd/lint.lua new file mode 100644 index 00000000..c9ea45ea --- /dev/null +++ b/src/luarocks/cmd/lint.lua | |||
@@ -0,0 +1,53 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "lint" command. | ||
3 | -- Utility function that checks syntax of the rockspec. | ||
4 | local lint = {} | ||
5 | |||
6 | local util = require("luarocks.util") | ||
7 | local download = require("luarocks.download") | ||
8 | local fetch = require("luarocks.fetch") | ||
9 | |||
10 | lint.help_summary = "Check syntax of a rockspec." | ||
11 | lint.help_arguments = "<rockspec>" | ||
12 | lint.help = [[ | ||
13 | This is a utility function that checks the syntax of a rockspec. | ||
14 | |||
15 | It returns success or failure if the text of a rockspec is | ||
16 | syntactically correct. | ||
17 | ]] | ||
18 | |||
19 | function lint.command(flags, input) | ||
20 | if not input then | ||
21 | return nil, "Argument missing. "..util.see_help("lint") | ||
22 | end | ||
23 | |||
24 | local filename = input | ||
25 | if not input:match(".rockspec$") then | ||
26 | local err | ||
27 | filename, err = download.download("rockspec", input:lower()) | ||
28 | if not filename then | ||
29 | return nil, err | ||
30 | end | ||
31 | end | ||
32 | |||
33 | local rs, err = fetch.load_local_rockspec(filename) | ||
34 | if not rs then | ||
35 | return nil, "Failed loading rockspec: "..err | ||
36 | end | ||
37 | |||
38 | local ok = true | ||
39 | |||
40 | -- This should have been done in the type checker, | ||
41 | -- but it would break compatibility of other commands. | ||
42 | -- Making 'lint' alone be stricter shouldn't be a problem, | ||
43 | -- because extra-strict checks is what lint-type commands | ||
44 | -- are all about. | ||
45 | if not rs.description.license then | ||
46 | util.printerr("Rockspec has no license field.") | ||
47 | ok = false | ||
48 | end | ||
49 | |||
50 | return ok, ok or filename.." failed consistency checks." | ||
51 | end | ||
52 | |||
53 | return lint | ||
diff --git a/src/luarocks/cmd/list.lua b/src/luarocks/cmd/list.lua new file mode 100644 index 00000000..45f1a26f --- /dev/null +++ b/src/luarocks/cmd/list.lua | |||
@@ -0,0 +1,95 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "list" command. | ||
3 | -- Lists currently installed rocks. | ||
4 | local list = {} | ||
5 | |||
6 | local search = require("luarocks.search") | ||
7 | local deps = require("luarocks.deps") | ||
8 | local cfg = require("luarocks.core.cfg") | ||
9 | local util = require("luarocks.util") | ||
10 | local path = require("luarocks.path") | ||
11 | |||
12 | list.help_summary = "List currently installed rocks." | ||
13 | list.help_arguments = "[--porcelain] <filter>" | ||
14 | list.help = [[ | ||
15 | <filter> is a substring of a rock name to filter by. | ||
16 | |||
17 | --outdated List only rocks for which there is a | ||
18 | higher version available in the rocks server. | ||
19 | |||
20 | --porcelain Produce machine-friendly output. | ||
21 | ]] | ||
22 | |||
23 | local function check_outdated(trees, query) | ||
24 | local results_installed = {} | ||
25 | for _, tree in ipairs(trees) do | ||
26 | search.manifest_search(results_installed, path.rocks_dir(tree), query) | ||
27 | end | ||
28 | local outdated = {} | ||
29 | for name, versions in util.sortedpairs(results_installed) do | ||
30 | versions = util.keys(versions) | ||
31 | table.sort(versions, deps.compare_versions) | ||
32 | local latest_installed = versions[1] | ||
33 | |||
34 | local query_available = search.make_query(name:lower()) | ||
35 | query.exact_name = true | ||
36 | local results_available, err = search.search_repos(query_available) | ||
37 | |||
38 | if results_available[name] then | ||
39 | local available_versions = util.keys(results_available[name]) | ||
40 | table.sort(available_versions, deps.compare_versions) | ||
41 | local latest_available = available_versions[1] | ||
42 | local latest_available_repo = results_available[name][latest_available][1].repo | ||
43 | |||
44 | if deps.compare_versions(latest_available, latest_installed) then | ||
45 | table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo }) | ||
46 | end | ||
47 | end | ||
48 | end | ||
49 | return outdated | ||
50 | end | ||
51 | |||
52 | local function list_outdated(trees, query, porcelain) | ||
53 | util.title("Outdated rocks:", porcelain) | ||
54 | local outdated = check_outdated(trees, query) | ||
55 | for _, item in ipairs(outdated) do | ||
56 | if porcelain then | ||
57 | util.printout(item.name, item.installed, item.available, item.repo) | ||
58 | else | ||
59 | util.printout(item.name) | ||
60 | util.printout(" "..item.installed.." < "..item.available.." at "..item.repo) | ||
61 | util.printout() | ||
62 | end | ||
63 | end | ||
64 | return true | ||
65 | end | ||
66 | |||
67 | --- Driver function for "list" command. | ||
68 | -- @param filter string or nil: A substring of a rock name to filter by. | ||
69 | -- @param version string or nil: a version may also be passed. | ||
70 | -- @return boolean: True if succeeded, nil on errors. | ||
71 | function list.command(flags, filter, version) | ||
72 | local query = search.make_query(filter and filter:lower() or "", version) | ||
73 | query.exact_name = false | ||
74 | local trees = cfg.rocks_trees | ||
75 | if flags["tree"] then | ||
76 | trees = { flags["tree"] } | ||
77 | end | ||
78 | |||
79 | if flags["outdated"] then | ||
80 | return list_outdated(trees, query, flags["porcelain"]) | ||
81 | end | ||
82 | |||
83 | local results = {} | ||
84 | for _, tree in ipairs(trees) do | ||
85 | local ok, err, errcode = search.manifest_search(results, path.rocks_dir(tree), query) | ||
86 | if not ok and errcode ~= "open" then | ||
87 | util.warning(err) | ||
88 | end | ||
89 | end | ||
90 | util.title("Installed rocks:", flags["porcelain"]) | ||
91 | search.print_results(results, flags["porcelain"]) | ||
92 | return true | ||
93 | end | ||
94 | |||
95 | return list | ||
diff --git a/src/luarocks/cmd/make.lua b/src/luarocks/cmd/make.lua new file mode 100644 index 00000000..eb38bff0 --- /dev/null +++ b/src/luarocks/cmd/make.lua | |||
@@ -0,0 +1,86 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "make" command. | ||
3 | -- Builds sources in the current directory, but unlike "build", | ||
4 | -- it does not fetch sources, etc., assuming everything is | ||
5 | -- available in the current directory. | ||
6 | local make = {} | ||
7 | |||
8 | local build = require("luarocks.build") | ||
9 | local fs = require("luarocks.fs") | ||
10 | local util = require("luarocks.util") | ||
11 | local cfg = require("luarocks.core.cfg") | ||
12 | local fetch = require("luarocks.fetch") | ||
13 | local pack = require("luarocks.pack") | ||
14 | local remove = require("luarocks.remove") | ||
15 | local deps = require("luarocks.deps") | ||
16 | |||
17 | make.help_summary = "Compile package in current directory using a rockspec." | ||
18 | make.help_arguments = "[--pack-binary-rock] [<rockspec>]" | ||
19 | make.help = [[ | ||
20 | Builds sources in the current directory, but unlike "build", | ||
21 | it does not fetch sources, etc., assuming everything is | ||
22 | available in the current directory. If no argument is given, | ||
23 | it looks for a rockspec in the current directory and in "rockspec/" | ||
24 | and "rockspecs/" subdirectories, picking the rockspec with newest version | ||
25 | or without version name. If rockspecs for different rocks are found | ||
26 | or there are several rockspecs without version, you must specify which to use, | ||
27 | through the command-line. | ||
28 | |||
29 | This command is useful as a tool for debugging rockspecs. | ||
30 | To install rocks, you'll normally want to use the "install" and | ||
31 | "build" commands. See the help on those for details. | ||
32 | |||
33 | --pack-binary-rock Do not install rock. Instead, produce a .rock file | ||
34 | with the contents of compilation in the current | ||
35 | directory. | ||
36 | |||
37 | --keep Do not remove previously installed versions of the | ||
38 | rock after installing a new one. This behavior can | ||
39 | be made permanent by setting keep_other_versions=true | ||
40 | in the configuration file. | ||
41 | |||
42 | --branch=<name> Override the `source.branch` field in the loaded | ||
43 | rockspec. Allows to specify a different branch to | ||
44 | fetch. Particularly for SCM rocks. | ||
45 | |||
46 | ]] | ||
47 | |||
48 | --- Driver function for "make" command. | ||
49 | -- @param name string: A local rockspec. | ||
50 | -- @return boolean or (nil, string, exitcode): True if build was successful; nil and an | ||
51 | -- error message otherwise. exitcode is optionally returned. | ||
52 | function make.command(flags, rockspec) | ||
53 | assert(type(rockspec) == "string" or not rockspec) | ||
54 | |||
55 | if not rockspec then | ||
56 | local err | ||
57 | rockspec, err = util.get_default_rockspec() | ||
58 | if not rockspec then | ||
59 | return nil, err | ||
60 | end | ||
61 | end | ||
62 | if not rockspec:match("rockspec$") then | ||
63 | return nil, "Invalid argument: 'make' takes a rockspec as a parameter. "..util.see_help("make") | ||
64 | end | ||
65 | |||
66 | if flags["pack-binary-rock"] then | ||
67 | local rspec, err, errcode = fetch.load_rockspec(rockspec) | ||
68 | if not rspec then | ||
69 | return nil, err | ||
70 | end | ||
71 | return pack.pack_binary_rock(rspec.name, rspec.version, build.build_rockspec, rockspec, false, true, deps.get_deps_mode(flags)) | ||
72 | else | ||
73 | local ok, err = fs.check_command_permissions(flags) | ||
74 | if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end | ||
75 | ok, err = build.build_rockspec(rockspec, false, true, deps.get_deps_mode(flags)) | ||
76 | if not ok then return nil, err end | ||
77 | local name, version = ok, err | ||
78 | if (not flags["keep"]) and not cfg.keep_other_versions then | ||
79 | local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) | ||
80 | if not ok then util.printerr(err) end | ||
81 | end | ||
82 | return name, version | ||
83 | end | ||
84 | end | ||
85 | |||
86 | return make | ||
diff --git a/src/luarocks/cmd/new_version.lua b/src/luarocks/cmd/new_version.lua new file mode 100644 index 00000000..b13dbb97 --- /dev/null +++ b/src/luarocks/cmd/new_version.lua | |||
@@ -0,0 +1,199 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "new_version" command. | ||
3 | -- Utility function that writes a new rockspec, updating data from a previous one. | ||
4 | local new_version = {} | ||
5 | |||
6 | local util = require("luarocks.util") | ||
7 | local download = require("luarocks.download") | ||
8 | local fetch = require("luarocks.fetch") | ||
9 | local persist = require("luarocks.persist") | ||
10 | local fs = require("luarocks.fs") | ||
11 | local type_check = require("luarocks.type_check") | ||
12 | |||
13 | new_version.help_summary = "Auto-write a rockspec for a new version of a rock." | ||
14 | new_version.help_arguments = "[--tag=<tag>] [<package>|<rockspec>] [<new_version>] [<new_url>]" | ||
15 | new_version.help = [[ | ||
16 | This is a utility function that writes a new rockspec, updating data | ||
17 | from a previous one. | ||
18 | |||
19 | If a package name is given, it downloads the latest rockspec from the | ||
20 | default server. If a rockspec is given, it uses it instead. If no argument | ||
21 | is given, it looks for a rockspec same way 'luarocks make' does. | ||
22 | |||
23 | If the version number is not given and tag is passed using --tag, | ||
24 | it is used as the version, with 'v' removed from beginning. | ||
25 | Otherwise, it only increments the revision number of the given | ||
26 | (or downloaded) rockspec. | ||
27 | |||
28 | If a URL is given, it replaces the one from the old rockspec with the | ||
29 | given URL. If a URL is not given and a new version is given, it tries | ||
30 | to guess the new URL by replacing occurrences of the version number | ||
31 | in the URL or tag. It also tries to download the new URL to determine | ||
32 | the new MD5 checksum. | ||
33 | |||
34 | If a tag is given, it replaces the one from the old rockspec. If there is | ||
35 | an old tag but no new one passed, it is guessed in the same way URL is. | ||
36 | |||
37 | WARNING: it writes the new rockspec to the current directory, | ||
38 | overwriting the file if it already exists. | ||
39 | ]] | ||
40 | |||
41 | local function try_replace(tbl, field, old, new) | ||
42 | if not tbl[field] then | ||
43 | return false | ||
44 | end | ||
45 | local old_field = tbl[field] | ||
46 | local new_field = tbl[field]:gsub(old, new) | ||
47 | if new_field ~= old_field then | ||
48 | util.printout("Guessing new '"..field.."' field as "..new_field) | ||
49 | tbl[field] = new_field | ||
50 | return true | ||
51 | end | ||
52 | return false | ||
53 | end | ||
54 | |||
55 | -- Try to download source file using URL from a rockspec. | ||
56 | -- If it specified MD5, update it. | ||
57 | -- @return (true, false) if MD5 was not specified or it stayed same, | ||
58 | -- (true, true) if MD5 changed, (nil, string) on error. | ||
59 | local function check_url_and_update_md5(out_rs) | ||
60 | local file, temp_dir = fetch.fetch_url_at_temp_dir(out_rs.source.url, "luarocks-new-version-"..out_rs.package) | ||
61 | if not file then | ||
62 | util.printerr("Warning: invalid URL - "..temp_dir) | ||
63 | return true, false | ||
64 | end | ||
65 | |||
66 | local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir) | ||
67 | if not inferred_dir then | ||
68 | return nil, found_dir | ||
69 | end | ||
70 | |||
71 | if found_dir and found_dir ~= inferred_dir then | ||
72 | out_rs.source.dir = found_dir | ||
73 | end | ||
74 | |||
75 | if file then | ||
76 | if out_rs.source.md5 then | ||
77 | util.printout("File successfully downloaded. Updating MD5 checksum...") | ||
78 | local new_md5, err = fs.get_md5(file) | ||
79 | if not new_md5 then | ||
80 | return nil, err | ||
81 | end | ||
82 | local old_md5 = out_rs.source.md5 | ||
83 | out_rs.source.md5 = new_md5 | ||
84 | return true, new_md5 ~= old_md5 | ||
85 | else | ||
86 | util.printout("File successfully downloaded.") | ||
87 | return true, false | ||
88 | end | ||
89 | end | ||
90 | end | ||
91 | |||
92 | local function update_source_section(out_rs, url, tag, old_ver, new_ver) | ||
93 | if tag then | ||
94 | out_rs.source.tag = tag | ||
95 | end | ||
96 | if url then | ||
97 | out_rs.source.url = url | ||
98 | return check_url_and_update_md5(out_rs) | ||
99 | end | ||
100 | if new_ver == old_ver then | ||
101 | return true | ||
102 | end | ||
103 | if out_rs.source.dir then | ||
104 | try_replace(out_rs.source, "dir", old_ver, new_ver) | ||
105 | end | ||
106 | if out_rs.source.file then | ||
107 | try_replace(out_rs.source, "file", old_ver, new_ver) | ||
108 | end | ||
109 | if try_replace(out_rs.source, "url", old_ver, new_ver) then | ||
110 | return check_url_and_update_md5(out_rs) | ||
111 | end | ||
112 | if tag or try_replace(out_rs.source, "tag", old_ver, new_ver) then | ||
113 | return true | ||
114 | end | ||
115 | -- Couldn't replace anything significant, use the old URL. | ||
116 | local ok, md5_changed = check_url_and_update_md5(out_rs) | ||
117 | if not ok then | ||
118 | return nil, md5_changed | ||
119 | end | ||
120 | if md5_changed then | ||
121 | util.printerr("Warning: URL is the same, but MD5 has changed. Old rockspec is broken.") | ||
122 | end | ||
123 | return true | ||
124 | end | ||
125 | |||
126 | function new_version.command(flags, input, version, url) | ||
127 | if not input then | ||
128 | local err | ||
129 | input, err = util.get_default_rockspec() | ||
130 | if not input then | ||
131 | return nil, err | ||
132 | end | ||
133 | end | ||
134 | assert(type(input) == "string") | ||
135 | |||
136 | local filename, err | ||
137 | if input:match("rockspec$") then | ||
138 | filename, err = fetch.fetch_url(input) | ||
139 | if not filename then | ||
140 | return nil, err | ||
141 | end | ||
142 | else | ||
143 | filename, err = download.download("rockspec", input:lower()) | ||
144 | if not filename then | ||
145 | return nil, err | ||
146 | end | ||
147 | end | ||
148 | |||
149 | local valid_rs, err = fetch.load_rockspec(filename) | ||
150 | if not valid_rs then | ||
151 | return nil, err | ||
152 | end | ||
153 | |||
154 | local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$") | ||
155 | local new_ver, new_rev | ||
156 | |||
157 | if flags.tag and not version then | ||
158 | version = flags.tag:gsub("^v", "") | ||
159 | end | ||
160 | |||
161 | if version then | ||
162 | new_ver, new_rev = version:match("(.*)%-(%d+)$") | ||
163 | new_rev = tonumber(new_rev) | ||
164 | if not new_rev then | ||
165 | new_ver = version | ||
166 | new_rev = 1 | ||
167 | end | ||
168 | else | ||
169 | new_ver = old_ver | ||
170 | new_rev = tonumber(old_rev) + 1 | ||
171 | end | ||
172 | local new_rockver = new_ver:gsub("-", "") | ||
173 | |||
174 | local out_rs, err = persist.load_into_table(filename) | ||
175 | local out_name = out_rs.package:lower() | ||
176 | out_rs.version = new_rockver.."-"..new_rev | ||
177 | |||
178 | local ok, err = update_source_section(out_rs, url, flags.tag, old_ver, new_ver) | ||
179 | if not ok then return nil, err end | ||
180 | |||
181 | if out_rs.build and out_rs.build.type == "module" then | ||
182 | out_rs.build.type = "builtin" | ||
183 | end | ||
184 | |||
185 | local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec" | ||
186 | |||
187 | persist.save_from_table(out_filename, out_rs, type_check.rockspec_order) | ||
188 | |||
189 | util.printout("Wrote "..out_filename) | ||
190 | |||
191 | local valid_out_rs, err = fetch.load_local_rockspec(out_filename) | ||
192 | if not valid_out_rs then | ||
193 | return nil, "Failed loading generated rockspec: "..err | ||
194 | end | ||
195 | |||
196 | return true | ||
197 | end | ||
198 | |||
199 | return new_version | ||
diff --git a/src/luarocks/cmd/pack.lua b/src/luarocks/cmd/pack.lua new file mode 100644 index 00000000..655cbf37 --- /dev/null +++ b/src/luarocks/cmd/pack.lua | |||
@@ -0,0 +1,193 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "pack" command. | ||
3 | -- Creates a rock, packing sources or binaries. | ||
4 | local pack = {} | ||
5 | |||
6 | local unpack = unpack or table.unpack | ||
7 | |||
8 | local path = require("luarocks.path") | ||
9 | local repos = require("luarocks.repos") | ||
10 | local fetch = require("luarocks.fetch") | ||
11 | local fs = require("luarocks.fs") | ||
12 | local cfg = require("luarocks.core.cfg") | ||
13 | local util = require("luarocks.util") | ||
14 | local dir = require("luarocks.dir") | ||
15 | local manif = require("luarocks.manif") | ||
16 | local search = require("luarocks.search") | ||
17 | |||
18 | pack.help_summary = "Create a rock, packing sources or binaries." | ||
19 | pack.help_arguments = "{<rockspec>|<name> [<version>]}" | ||
20 | pack.help = [[ | ||
21 | Argument may be a rockspec file, for creating a source rock, | ||
22 | or the name of an installed package, for creating a binary rock. | ||
23 | In the latter case, the app version may be given as a second | ||
24 | argument. | ||
25 | ]] | ||
26 | |||
27 | --- Create a source rock. | ||
28 | -- Packages a rockspec and its required source files in a rock | ||
29 | -- file with the .src.rock extension, which can later be built and | ||
30 | -- installed with the "build" command. | ||
31 | -- @param rockspec_file string: An URL or pathname for a rockspec file. | ||
32 | -- @return string or (nil, string): The filename of the resulting | ||
33 | -- .src.rock file; or nil and an error message. | ||
34 | function pack.pack_source_rock(rockspec_file) | ||
35 | assert(type(rockspec_file) == "string") | ||
36 | |||
37 | local rockspec, err = fetch.load_rockspec(rockspec_file) | ||
38 | if err then | ||
39 | return nil, "Error loading rockspec: "..err | ||
40 | end | ||
41 | rockspec_file = rockspec.local_filename | ||
42 | |||
43 | local name_version = rockspec.name .. "-" .. rockspec.version | ||
44 | local rock_file = fs.absolute_name(name_version .. ".src.rock") | ||
45 | |||
46 | local source_file, source_dir = fetch.fetch_sources(rockspec, false) | ||
47 | if not source_file then | ||
48 | return nil, source_dir | ||
49 | end | ||
50 | local ok, err = fs.change_dir(source_dir) | ||
51 | if not ok then return nil, err end | ||
52 | |||
53 | fs.delete(rock_file) | ||
54 | fs.copy(rockspec_file, source_dir, cfg.perm_read) | ||
55 | if not fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) then | ||
56 | return nil, "Failed packing "..rock_file | ||
57 | end | ||
58 | fs.pop_dir() | ||
59 | |||
60 | return rock_file | ||
61 | end | ||
62 | |||
63 | local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir, perms) | ||
64 | local ok, err = fs.make_dir(pack_dir) | ||
65 | if not ok then return nil, err end | ||
66 | for file, sub in pairs(file_tree) do | ||
67 | local source = dir.path(deploy_dir, file) | ||
68 | local target = dir.path(pack_dir, file) | ||
69 | if type(sub) == "table" then | ||
70 | local ok, err = copy_back_files(name, version, sub, source, target) | ||
71 | if not ok then return nil, err end | ||
72 | else | ||
73 | local versioned = path.versioned_name(source, deploy_dir, name, version) | ||
74 | if fs.exists(versioned) then | ||
75 | fs.copy(versioned, target, perms) | ||
76 | else | ||
77 | fs.copy(source, target, perms) | ||
78 | end | ||
79 | end | ||
80 | end | ||
81 | return true | ||
82 | end | ||
83 | |||
84 | -- @param name string: Name of package to pack. | ||
85 | -- @param version string or nil: A version number may also be passed. | ||
86 | -- @param tree string or nil: An optional tree to pick the package from. | ||
87 | -- @return string or (nil, string): The filename of the resulting | ||
88 | -- .src.rock file; or nil and an error message. | ||
89 | local function do_pack_binary_rock(name, version, tree) | ||
90 | assert(type(name) == "string") | ||
91 | assert(type(version) == "string" or not version) | ||
92 | |||
93 | local repo, repo_url | ||
94 | name, version, repo, repo_url = search.pick_installed_rock(name, version, tree) | ||
95 | if not name then | ||
96 | return nil, version | ||
97 | end | ||
98 | |||
99 | local root = path.root_dir(repo_url) | ||
100 | local prefix = path.install_dir(name, version, root) | ||
101 | if not fs.exists(prefix) then | ||
102 | return nil, "'"..name.." "..version.."' does not seem to be an installed rock." | ||
103 | end | ||
104 | |||
105 | local rock_manifest, err = manif.load_rock_manifest(name, version, root) | ||
106 | if not rock_manifest then return nil, err end | ||
107 | |||
108 | local name_version = name .. "-" .. version | ||
109 | local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") | ||
110 | |||
111 | local temp_dir = fs.make_temp_dir("pack") | ||
112 | fs.copy_contents(prefix, temp_dir) | ||
113 | |||
114 | local is_binary = false | ||
115 | if rock_manifest.lib then | ||
116 | local ok, err = copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib"), cfg.perm_exec) | ||
117 | if not ok then return nil, "Failed copying back files: " .. err end | ||
118 | is_binary = true | ||
119 | end | ||
120 | if rock_manifest.lua then | ||
121 | local ok, err = copy_back_files(name, version, rock_manifest.lua, path.deploy_lua_dir(root), dir.path(temp_dir, "lua"), cfg.perm_read) | ||
122 | if not ok then return nil, "Failed copying back files: " .. err end | ||
123 | end | ||
124 | |||
125 | local ok, err = fs.change_dir(temp_dir) | ||
126 | if not ok then return nil, err end | ||
127 | if not is_binary and not repos.has_binaries(name, version) then | ||
128 | rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") | ||
129 | end | ||
130 | fs.delete(rock_file) | ||
131 | if not fs.zip(rock_file, unpack(fs.list_dir())) then | ||
132 | return nil, "Failed packing "..rock_file | ||
133 | end | ||
134 | fs.pop_dir() | ||
135 | fs.delete(temp_dir) | ||
136 | return rock_file | ||
137 | end | ||
138 | |||
139 | function pack.pack_binary_rock(name, version, cmd, ...) | ||
140 | |||
141 | -- The --pack-binary-rock option for "luarocks build" basically performs | ||
142 | -- "luarocks build" on a temporary tree and then "luarocks pack". The | ||
143 | -- alternative would require refactoring parts of luarocks.build and | ||
144 | -- luarocks.pack, which would save a few file operations: the idea would be | ||
145 | -- to shave off the final deploy steps from the build phase and the initial | ||
146 | -- collect steps from the pack phase. | ||
147 | |||
148 | local temp_dir, err = fs.make_temp_dir("luarocks-build-pack-"..dir.base_name(name)) | ||
149 | if not temp_dir then | ||
150 | return nil, "Failed creating temporary directory: "..err | ||
151 | end | ||
152 | util.schedule_function(fs.delete, temp_dir) | ||
153 | |||
154 | path.use_tree(temp_dir) | ||
155 | local ok, err = cmd(...) | ||
156 | if not ok then | ||
157 | return nil, err | ||
158 | end | ||
159 | local rname, rversion = path.parse_name(name) | ||
160 | if not rname then | ||
161 | rname, rversion = name, version | ||
162 | end | ||
163 | return do_pack_binary_rock(rname, rversion, temp_dir) | ||
164 | end | ||
165 | |||
166 | --- Driver function for the "pack" command. | ||
167 | -- @param arg string: may be a rockspec file, for creating a source rock, | ||
168 | -- or the name of an installed package, for creating a binary rock. | ||
169 | -- @param version string or nil: if the name of a package is given, a | ||
170 | -- version may also be passed. | ||
171 | -- @return boolean or (nil, string): true if successful or nil followed | ||
172 | -- by an error message. | ||
173 | function pack.command(flags, arg, version) | ||
174 | assert(type(version) == "string" or not version) | ||
175 | if type(arg) ~= "string" then | ||
176 | return nil, "Argument missing. "..util.see_help("pack") | ||
177 | end | ||
178 | |||
179 | local file, err | ||
180 | if arg:match(".*%.rockspec") then | ||
181 | file, err = pack.pack_source_rock(arg) | ||
182 | else | ||
183 | file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"]) | ||
184 | end | ||
185 | if err then | ||
186 | return nil, err | ||
187 | else | ||
188 | util.printout("Packed: "..file) | ||
189 | return true | ||
190 | end | ||
191 | end | ||
192 | |||
193 | return pack | ||
diff --git a/src/luarocks/cmd/path_cmd.lua b/src/luarocks/cmd/path_cmd.lua new file mode 100644 index 00000000..516a0c47 --- /dev/null +++ b/src/luarocks/cmd/path_cmd.lua | |||
@@ -0,0 +1,68 @@ | |||
1 | |||
2 | --- @module luarocks.path_cmd | ||
3 | -- Driver for the `luarocks path` command. | ||
4 | local path_cmd = {} | ||
5 | |||
6 | local util = require("luarocks.util") | ||
7 | local cfg = require("luarocks.core.cfg") | ||
8 | |||
9 | path_cmd.help_summary = "Return the currently configured package path." | ||
10 | path_cmd.help_arguments = "" | ||
11 | path_cmd.help = [[ | ||
12 | Returns the package path currently configured for this installation | ||
13 | of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH. | ||
14 | |||
15 | --bin Adds the system path to the output | ||
16 | |||
17 | --append Appends the paths to the existing paths. Default is to prefix | ||
18 | the LR paths to the existing paths. | ||
19 | |||
20 | --lr-path Exports the Lua path (not formatted as shell command) | ||
21 | |||
22 | --lr-cpath Exports the Lua cpath (not formatted as shell command) | ||
23 | |||
24 | --lr-bin Exports the system path (not formatted as shell command) | ||
25 | |||
26 | |||
27 | On Unix systems, you may run: | ||
28 | eval `luarocks path` | ||
29 | And on Windows: | ||
30 | luarocks path > "%temp%\_lrp.bat" && call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat" | ||
31 | ]] | ||
32 | |||
33 | --- Driver function for "path" command. | ||
34 | -- @return boolean This function always succeeds. | ||
35 | function path_cmd.command(flags) | ||
36 | local lr_path, lr_cpath, lr_bin = cfg.package_paths(flags["tree"]) | ||
37 | local path_sep = cfg.export_path_separator | ||
38 | |||
39 | if flags["lr-path"] then | ||
40 | util.printout(util.remove_path_dupes(lr_path, ';')) | ||
41 | return true | ||
42 | elseif flags["lr-cpath"] then | ||
43 | util.printout(util.remove_path_dupes(lr_cpath, ';')) | ||
44 | return true | ||
45 | elseif flags["lr-bin"] then | ||
46 | util.printout(util.remove_path_dupes(lr_bin, path_sep)) | ||
47 | return true | ||
48 | end | ||
49 | |||
50 | if flags["append"] then | ||
51 | lr_path = package.path .. ";" .. lr_path | ||
52 | lr_cpath = package.cpath .. ";" .. lr_cpath | ||
53 | lr_bin = os.getenv("PATH") .. path_sep .. lr_bin | ||
54 | else | ||
55 | lr_path = lr_path.. ";" .. package.path | ||
56 | lr_cpath = lr_cpath .. ";" .. package.cpath | ||
57 | lr_bin = lr_bin .. path_sep .. os.getenv("PATH") | ||
58 | end | ||
59 | |||
60 | util.printout(cfg.export_lua_path:format(util.remove_path_dupes(lr_path, ';'))) | ||
61 | util.printout(cfg.export_lua_cpath:format(util.remove_path_dupes(lr_cpath, ';'))) | ||
62 | if flags["bin"] then | ||
63 | util.printout(cfg.export_path:format(util.remove_path_dupes(lr_bin, path_sep))) | ||
64 | end | ||
65 | return true | ||
66 | end | ||
67 | |||
68 | return path_cmd | ||
diff --git a/src/luarocks/cmd/purge.lua b/src/luarocks/cmd/purge.lua new file mode 100644 index 00000000..50f290c8 --- /dev/null +++ b/src/luarocks/cmd/purge.lua | |||
@@ -0,0 +1,77 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "purge" command. | ||
3 | -- Remove all rocks from a given tree. | ||
4 | local purge = {} | ||
5 | |||
6 | local util = require("luarocks.util") | ||
7 | local fs = require("luarocks.fs") | ||
8 | local path = require("luarocks.path") | ||
9 | local search = require("luarocks.search") | ||
10 | local deps = require("luarocks.deps") | ||
11 | local repos = require("luarocks.repos") | ||
12 | local writer = require("luarocks.manif.writer") | ||
13 | local cfg = require("luarocks.core.cfg") | ||
14 | local remove = require("luarocks.remove") | ||
15 | |||
16 | purge.help_summary = "Remove all installed rocks from a tree." | ||
17 | purge.help_arguments = "--tree=<tree> [--old-versions]" | ||
18 | purge.help = [[ | ||
19 | This command removes rocks en masse from a given tree. | ||
20 | By default, it removes all rocks from a tree. | ||
21 | |||
22 | The --tree argument is mandatory: luarocks purge does not | ||
23 | assume a default tree. | ||
24 | |||
25 | --old-versions Keep the highest-numbered version of each | ||
26 | rock and remove the other ones. By default | ||
27 | it only removes old versions if they are | ||
28 | not needed as dependencies. This can be | ||
29 | overridden with the flag --force. | ||
30 | ]] | ||
31 | |||
32 | function purge.command(flags) | ||
33 | local tree = flags["tree"] | ||
34 | |||
35 | if type(tree) ~= "string" then | ||
36 | return nil, "The --tree argument is mandatory. "..util.see_help("purge") | ||
37 | end | ||
38 | |||
39 | local results = {} | ||
40 | local query = search.make_query("") | ||
41 | query.exact_name = false | ||
42 | if not fs.is_dir(tree) then | ||
43 | return nil, "Directory not found: "..tree | ||
44 | end | ||
45 | |||
46 | local ok, err = fs.check_command_permissions(flags) | ||
47 | if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end | ||
48 | |||
49 | search.manifest_search(results, path.rocks_dir(tree), query) | ||
50 | |||
51 | local sort = function(a,b) return deps.compare_versions(b,a) end | ||
52 | if flags["old-versions"] then | ||
53 | sort = deps.compare_versions | ||
54 | end | ||
55 | |||
56 | for package, versions in util.sortedpairs(results) do | ||
57 | for version, _ in util.sortedpairs(versions, sort) do | ||
58 | if flags["old-versions"] then | ||
59 | util.printout("Keeping "..package.." "..version.."...") | ||
60 | local ok, err = remove.remove_other_versions(package, version, flags["force"], flags["force-fast"]) | ||
61 | if not ok then | ||
62 | util.printerr(err) | ||
63 | end | ||
64 | break | ||
65 | else | ||
66 | util.printout("Removing "..package.." "..version.."...") | ||
67 | local ok, err = repos.delete_version(package, version, "none", true) | ||
68 | if not ok then | ||
69 | util.printerr(err) | ||
70 | end | ||
71 | end | ||
72 | end | ||
73 | end | ||
74 | return writer.make_manifest(cfg.rocks_dir, "one") | ||
75 | end | ||
76 | |||
77 | return purge | ||
diff --git a/src/luarocks/cmd/remove.lua b/src/luarocks/cmd/remove.lua new file mode 100644 index 00000000..e7f37604 --- /dev/null +++ b/src/luarocks/cmd/remove.lua | |||
@@ -0,0 +1,165 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "remove" command. | ||
3 | -- Uninstalls rocks. | ||
4 | local remove = {} | ||
5 | |||
6 | local search = require("luarocks.search") | ||
7 | local deps = require("luarocks.deps") | ||
8 | local fetch = require("luarocks.fetch") | ||
9 | local repos = require("luarocks.repos") | ||
10 | local path = require("luarocks.path") | ||
11 | local util = require("luarocks.util") | ||
12 | local cfg = require("luarocks.core.cfg") | ||
13 | local fs = require("luarocks.fs") | ||
14 | |||
15 | remove.help_summary = "Uninstall a rock." | ||
16 | remove.help_arguments = "[--force|--force-fast] <name> [<version>]" | ||
17 | remove.help = [[ | ||
18 | Argument is the name of a rock to be uninstalled. | ||
19 | If a version is not given, try to remove all versions at once. | ||
20 | Will only perform the removal if it does not break dependencies. | ||
21 | To override this check and force the removal, use --force. | ||
22 | To perform a forced removal without reporting dependency issues, | ||
23 | use --force-fast. | ||
24 | |||
25 | ]]..util.deps_mode_help() | ||
26 | |||
27 | --- Obtain a list of packages that depend on the given set of packages | ||
28 | -- (where all packages of the set are versions of one program). | ||
29 | -- @param name string: the name of a program | ||
30 | -- @param versions array of string: the versions to be deleted. | ||
31 | -- @return array of string: an empty table if no packages depend on any | ||
32 | -- of the given list, or an array of strings in "name/version" format. | ||
33 | local function check_dependents(name, versions, deps_mode) | ||
34 | local dependents = {} | ||
35 | local blacklist = {} | ||
36 | blacklist[name] = {} | ||
37 | for version, _ in pairs(versions) do | ||
38 | blacklist[name][version] = true | ||
39 | end | ||
40 | local local_rocks = {} | ||
41 | local query_all = search.make_query("") | ||
42 | query_all.exact_name = false | ||
43 | search.manifest_search(local_rocks, cfg.rocks_dir, query_all) | ||
44 | local_rocks[name] = nil | ||
45 | for rock_name, rock_versions in pairs(local_rocks) do | ||
46 | for rock_version, _ in pairs(rock_versions) do | ||
47 | local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) | ||
48 | if rockspec then | ||
49 | local _, missing = deps.match_deps(rockspec, blacklist, deps_mode) | ||
50 | if missing[name] then | ||
51 | table.insert(dependents, { name = rock_name, version = rock_version }) | ||
52 | end | ||
53 | end | ||
54 | end | ||
55 | end | ||
56 | return dependents | ||
57 | end | ||
58 | |||
59 | --- Delete given versions of a program. | ||
60 | -- @param name string: the name of a program | ||
61 | -- @param versions array of string: the versions to be deleted. | ||
62 | -- @param deps_mode: string: Which trees to check dependencies for: | ||
63 | -- "one" for the current default tree, "all" for all trees, | ||
64 | -- "order" for all trees with priority >= the current default, "none" for no trees. | ||
65 | -- @return boolean or (nil, string): true on success or nil and an error message. | ||
66 | local function delete_versions(name, versions, deps_mode) | ||
67 | |||
68 | for version, _ in pairs(versions) do | ||
69 | util.printout("Removing "..name.." "..version.."...") | ||
70 | local ok, err = repos.delete_version(name, version, deps_mode) | ||
71 | if not ok then return nil, err end | ||
72 | end | ||
73 | |||
74 | return true | ||
75 | end | ||
76 | |||
77 | function remove.remove_search_results(results, name, deps_mode, force, fast) | ||
78 | local versions = results[name] | ||
79 | |||
80 | local version = next(versions) | ||
81 | local second = next(versions, version) | ||
82 | |||
83 | local dependents = {} | ||
84 | if not fast then | ||
85 | util.printout("Checking stability of dependencies in the absence of") | ||
86 | util.printout(name.." "..table.concat(util.keys(versions), ", ").."...") | ||
87 | util.printout() | ||
88 | dependents = check_dependents(name, versions, deps_mode) | ||
89 | end | ||
90 | |||
91 | if #dependents > 0 then | ||
92 | if force or fast then | ||
93 | util.printerr("The following packages may be broken by this forced removal:") | ||
94 | for _, dependent in ipairs(dependents) do | ||
95 | util.printerr(dependent.name.." "..dependent.version) | ||
96 | end | ||
97 | util.printerr() | ||
98 | else | ||
99 | if not second then | ||
100 | util.printerr("Will not remove "..name.." "..version..".") | ||
101 | util.printerr("Removing it would break dependencies for: ") | ||
102 | else | ||
103 | util.printerr("Will not remove installed versions of "..name..".") | ||
104 | util.printerr("Removing them would break dependencies for: ") | ||
105 | end | ||
106 | for _, dependent in ipairs(dependents) do | ||
107 | util.printerr(dependent.name.." "..dependent.version) | ||
108 | end | ||
109 | util.printerr() | ||
110 | util.printerr("Use --force to force removal (warning: this may break modules).") | ||
111 | return nil, "Failed removing." | ||
112 | end | ||
113 | end | ||
114 | |||
115 | local ok, err = delete_versions(name, versions, deps_mode) | ||
116 | if not ok then return nil, err end | ||
117 | |||
118 | util.printout("Removal successful.") | ||
119 | return true | ||
120 | end | ||
121 | |||
122 | function remove.remove_other_versions(name, version, force, fast) | ||
123 | local results = {} | ||
124 | search.manifest_search(results, cfg.rocks_dir, { name = name, exact_name = true, constraints = {{ op = "~=", version = version}} }) | ||
125 | if results[name] then | ||
126 | return remove.remove_search_results(results, name, cfg.deps_mode, force, fast) | ||
127 | end | ||
128 | return true | ||
129 | end | ||
130 | |||
131 | --- Driver function for the "remove" command. | ||
132 | -- @param name string: name of a rock. If a version is given, refer to | ||
133 | -- a specific version; otherwise, try to remove all versions. | ||
134 | -- @param version string: When passing a package name, a version number | ||
135 | -- may also be given. | ||
136 | -- @return boolean or (nil, string, exitcode): True if removal was | ||
137 | -- successful, nil and an error message otherwise. exitcode is optionally returned. | ||
138 | function remove.command(flags, name, version) | ||
139 | if type(name) ~= "string" then | ||
140 | return nil, "Argument missing. "..util.see_help("remove") | ||
141 | end | ||
142 | |||
143 | local deps_mode = flags["deps-mode"] or cfg.deps_mode | ||
144 | |||
145 | local ok, err = fs.check_command_permissions(flags) | ||
146 | if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end | ||
147 | |||
148 | local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") | ||
149 | local filename = name | ||
150 | if rock_type then | ||
151 | name, version = path.parse_name(filename) | ||
152 | if not name then return nil, "Invalid "..rock_type.." filename: "..filename end | ||
153 | end | ||
154 | |||
155 | local results = {} | ||
156 | name = name:lower() | ||
157 | search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version)) | ||
158 | if not results[name] then | ||
159 | return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir) | ||
160 | end | ||
161 | |||
162 | return remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"]) | ||
163 | end | ||
164 | |||
165 | return remove | ||
diff --git a/src/luarocks/cmd/search.lua b/src/luarocks/cmd/search.lua new file mode 100644 index 00000000..44eff694 --- /dev/null +++ b/src/luarocks/cmd/search.lua | |||
@@ -0,0 +1,482 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "search" command. | ||
3 | -- Queries LuaRocks servers. | ||
4 | local search = {} | ||
5 | |||
6 | |||
7 | local dir = require("luarocks.dir") | ||
8 | local path = require("luarocks.path") | ||
9 | local manif = require("luarocks.manif") | ||
10 | local deps = require("luarocks.deps") | ||
11 | local cfg = require("luarocks.core.cfg") | ||
12 | local util = require("luarocks.util") | ||
13 | |||
14 | search.help_summary = "Query the LuaRocks servers." | ||
15 | search.help_arguments = "[--source] [--binary] { <name> [<version>] | --all }" | ||
16 | search.help = [[ | ||
17 | --source Return only rockspecs and source rocks, | ||
18 | to be used with the "build" command. | ||
19 | --binary Return only pure Lua and binary rocks (rocks that can be used | ||
20 | with the "install" command without requiring a C toolchain). | ||
21 | --all List all contents of the server that are suitable to | ||
22 | this platform, do not filter by name. | ||
23 | ]] | ||
24 | |||
25 | --- Convert the arch field of a query table to table format. | ||
26 | -- @param query table: A query table. | ||
27 | local function query_arch_as_table(query) | ||
28 | local format = type(query.arch) | ||
29 | if format == "table" then | ||
30 | return | ||
31 | elseif format == "nil" then | ||
32 | local accept = {} | ||
33 | accept["src"] = true | ||
34 | accept["all"] = true | ||
35 | accept["rockspec"] = true | ||
36 | accept["installed"] = true | ||
37 | accept[cfg.arch] = true | ||
38 | query.arch = accept | ||
39 | elseif format == "string" then | ||
40 | local accept = {} | ||
41 | for a in query.arch:gmatch("[%w_-]+") do | ||
42 | accept[a] = true | ||
43 | end | ||
44 | query.arch = accept | ||
45 | end | ||
46 | end | ||
47 | |||
48 | --- Store a search result (a rock or rockspec) in the results table. | ||
49 | -- @param results table: The results table, where keys are package names and | ||
50 | -- values are tables matching version strings to arrays of | ||
51 | -- tables with fields "arch" and "repo". | ||
52 | -- @param name string: Package name. | ||
53 | -- @param version string: Package version. | ||
54 | -- @param arch string: Architecture of rock ("all", "src" or platform | ||
55 | -- identifier), "rockspec" or "installed" | ||
56 | -- @param repo string: Pathname of a local repository of URL of | ||
57 | -- rocks server. | ||
58 | local function store_result(results, name, version, arch, repo) | ||
59 | assert(type(results) == "table") | ||
60 | assert(type(name) == "string") | ||
61 | assert(type(version) == "string") | ||
62 | assert(type(arch) == "string") | ||
63 | assert(type(repo) == "string") | ||
64 | |||
65 | if not results[name] then results[name] = {} end | ||
66 | if not results[name][version] then results[name][version] = {} end | ||
67 | table.insert(results[name][version], { | ||
68 | arch = arch, | ||
69 | repo = repo | ||
70 | }) | ||
71 | end | ||
72 | |||
73 | --- Test the name field of a query. | ||
74 | -- If query has a boolean field exact_name set to false, | ||
75 | -- then substring match is performed; otherwise, exact string | ||
76 | -- comparison is done. | ||
77 | -- @param query table: A query in dependency table format. | ||
78 | -- @param name string: A package name. | ||
79 | -- @return boolean: True if names match, false otherwise. | ||
80 | local function match_name(query, name) | ||
81 | assert(type(query) == "table") | ||
82 | assert(type(name) == "string") | ||
83 | if query.exact_name == false then | ||
84 | return name:find(query.name, 0, true) and true or false | ||
85 | else | ||
86 | return name == query.name | ||
87 | end | ||
88 | end | ||
89 | |||
90 | --- Store a match in a results table if version matches query. | ||
91 | -- Name, version, arch and repository path are stored in a given | ||
92 | -- table, optionally checking if version and arch (if given) match | ||
93 | -- a query. | ||
94 | -- @param results table: The results table, where keys are package names and | ||
95 | -- values are tables matching version strings to arrays of | ||
96 | -- tables with fields "arch" and "repo". | ||
97 | -- @param repo string: URL or pathname of the repository. | ||
98 | -- @param name string: The name of the package being tested. | ||
99 | -- @param version string: The version of the package being tested. | ||
100 | -- @param arch string: The arch of the package being tested. | ||
101 | -- @param query table: A table describing the query in dependency | ||
102 | -- format (for example, {name = "filesystem", exact_name = false, | ||
103 | -- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). | ||
104 | -- If the arch field is omitted, the local architecture (cfg.arch) | ||
105 | -- is used. The special value "any" is also recognized, returning all | ||
106 | -- matches regardless of architecture. | ||
107 | local function store_if_match(results, repo, name, version, arch, query) | ||
108 | if match_name(query, name) then | ||
109 | if query.arch[arch] or query.arch["any"] then | ||
110 | if deps.match_constraints(deps.parse_version(version), query.constraints) then | ||
111 | store_result(results, name, version, arch, repo) | ||
112 | end | ||
113 | end | ||
114 | end | ||
115 | end | ||
116 | |||
117 | --- Perform search on a local repository. | ||
118 | -- @param repo string: The pathname of the local repository. | ||
119 | -- @param query table: A table describing the query in dependency | ||
120 | -- format (for example, {name = "filesystem", exact_name = false, | ||
121 | -- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). | ||
122 | -- If the arch field is omitted, the local architecture (cfg.arch) | ||
123 | -- is used. The special value "any" is also recognized, returning all | ||
124 | -- matches regardless of architecture. | ||
125 | -- @param results table or nil: If given, this table will store the | ||
126 | -- results; if not given, a new table will be created. | ||
127 | -- @return table: The results table, where keys are package names and | ||
128 | -- values are tables matching version strings to arrays of | ||
129 | -- tables with fields "arch" and "repo". | ||
130 | -- If a table was given in the "results" parameter, that is the result value. | ||
131 | function search.disk_search(repo, query, results) | ||
132 | assert(type(repo) == "string") | ||
133 | assert(type(query) == "table") | ||
134 | assert(type(results) == "table" or not results) | ||
135 | |||
136 | local fs = require("luarocks.fs") | ||
137 | |||
138 | if not results then | ||
139 | results = {} | ||
140 | end | ||
141 | query_arch_as_table(query) | ||
142 | |||
143 | for name in fs.dir(repo) do | ||
144 | local pathname = dir.path(repo, name) | ||
145 | local rname, rversion, rarch = path.parse_name(name) | ||
146 | |||
147 | if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then | ||
148 | store_if_match(results, repo, rname, rversion, rarch, query) | ||
149 | elseif fs.is_dir(pathname) then | ||
150 | for version in fs.dir(pathname) do | ||
151 | if version:match("-%d+$") then | ||
152 | store_if_match(results, repo, name, version, "installed", query) | ||
153 | end | ||
154 | end | ||
155 | end | ||
156 | end | ||
157 | return results | ||
158 | end | ||
159 | |||
160 | --- Perform search on a rocks server or tree. | ||
161 | -- @param results table: The results table, where keys are package names and | ||
162 | -- values are tables matching version strings to arrays of | ||
163 | -- tables with fields "arch" and "repo". | ||
164 | -- @param repo string: The URL of a rocks server or | ||
165 | -- the pathname of a rocks tree (as returned by path.rocks_dir()). | ||
166 | -- @param query table: A table describing the query in dependency | ||
167 | -- format (for example, {name = "filesystem", exact_name = false, | ||
168 | -- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). | ||
169 | -- If the arch field is omitted, the local architecture (cfg.arch) | ||
170 | -- is used. The special value "any" is also recognized, returning all | ||
171 | -- matches regardless of architecture. | ||
172 | -- @param lua_version string: Lua version in "5.x" format, defaults to installed version. | ||
173 | -- @return true or, in case of errors, nil, an error message and an optional error code. | ||
174 | function search.manifest_search(results, repo, query, lua_version) | ||
175 | assert(type(results) == "table") | ||
176 | assert(type(repo) == "string") | ||
177 | assert(type(query) == "table") | ||
178 | |||
179 | query_arch_as_table(query) | ||
180 | local manifest, err, errcode = manif.load_manifest(repo, lua_version) | ||
181 | if not manifest then | ||
182 | return nil, err, errcode | ||
183 | end | ||
184 | for name, versions in pairs(manifest.repository) do | ||
185 | for version, items in pairs(versions) do | ||
186 | for _, item in ipairs(items) do | ||
187 | store_if_match(results, repo, name, version, item.arch, query) | ||
188 | end | ||
189 | end | ||
190 | end | ||
191 | return true | ||
192 | end | ||
193 | |||
194 | --- Search on all configured rocks servers. | ||
195 | -- @param query table: A dependency query. | ||
196 | -- @param lua_version string: Lua version in "5.x" format, defaults to installed version. | ||
197 | -- @return table: A table where keys are package names | ||
198 | -- and values are tables matching version strings to arrays of | ||
199 | -- tables with fields "arch" and "repo". | ||
200 | function search.search_repos(query, lua_version) | ||
201 | assert(type(query) == "table") | ||
202 | |||
203 | local results = {} | ||
204 | for _, repo in ipairs(cfg.rocks_servers) do | ||
205 | if not cfg.disabled_servers[repo] then | ||
206 | if type(repo) == "string" then | ||
207 | repo = { repo } | ||
208 | end | ||
209 | for _, mirror in ipairs(repo) do | ||
210 | local protocol, pathname = dir.split_url(mirror) | ||
211 | if protocol == "file" then | ||
212 | mirror = pathname | ||
213 | end | ||
214 | local ok, err, errcode = search.manifest_search(results, mirror, query, lua_version) | ||
215 | if errcode == "network" then | ||
216 | cfg.disabled_servers[repo] = true | ||
217 | end | ||
218 | if ok then | ||
219 | break | ||
220 | else | ||
221 | util.warning("Failed searching manifest: "..err) | ||
222 | end | ||
223 | end | ||
224 | end | ||
225 | end | ||
226 | -- search through rocks in cfg.rocks_provided | ||
227 | local provided_repo = "provided by VM or rocks_provided" | ||
228 | for name, versions in pairs(cfg.rocks_provided) do | ||
229 | store_if_match(results, provided_repo, name, versions, "installed", query) | ||
230 | end | ||
231 | return results | ||
232 | end | ||
233 | |||
234 | --- Prepare a query in dependency table format. | ||
235 | -- @param name string: The query name. | ||
236 | -- @param version string or nil: | ||
237 | -- @return table: A query in table format | ||
238 | function search.make_query(name, version) | ||
239 | assert(type(name) == "string") | ||
240 | assert(type(version) == "string" or not version) | ||
241 | |||
242 | local query = { | ||
243 | name = name, | ||
244 | constraints = {} | ||
245 | } | ||
246 | if version then | ||
247 | table.insert(query.constraints, { op = "==", version = deps.parse_version(version)}) | ||
248 | end | ||
249 | return query | ||
250 | end | ||
251 | |||
252 | --- Get the URL for the latest in a set of versions. | ||
253 | -- @param name string: The package name to be used in the URL. | ||
254 | -- @param versions table: An array of version informations, as stored | ||
255 | -- in search results tables. | ||
256 | -- @return string or nil: the URL for the latest version if one could | ||
257 | -- be picked, or nil. | ||
258 | local function pick_latest_version(name, versions) | ||
259 | assert(type(name) == "string") | ||
260 | assert(type(versions) == "table") | ||
261 | |||
262 | local vtables = {} | ||
263 | for v, _ in pairs(versions) do | ||
264 | table.insert(vtables, deps.parse_version(v)) | ||
265 | end | ||
266 | table.sort(vtables) | ||
267 | local version = vtables[#vtables].string | ||
268 | local items = versions[version] | ||
269 | if items then | ||
270 | local pick = 1 | ||
271 | for i, item in ipairs(items) do | ||
272 | if (item.arch == 'src' and items[pick].arch == 'rockspec') | ||
273 | or (item.arch ~= 'src' and item.arch ~= 'rockspec') then | ||
274 | pick = i | ||
275 | end | ||
276 | end | ||
277 | return path.make_url(items[pick].repo, name, version, items[pick].arch) | ||
278 | end | ||
279 | return nil | ||
280 | end | ||
281 | |||
282 | -- Find out which other Lua versions provide rock versions matching a query, | ||
283 | -- @param query table: A dependency query matching a single rock. | ||
284 | -- @return table: array of Lua versions supported, in "5.x" format. | ||
285 | local function supported_lua_versions(query) | ||
286 | local results = {} | ||
287 | |||
288 | for lua_version in util.lua_versions() do | ||
289 | if lua_version ~= cfg.lua_version then | ||
290 | if search.search_repos(query, lua_version)[query.name] then | ||
291 | table.insert(results, lua_version) | ||
292 | end | ||
293 | end | ||
294 | end | ||
295 | |||
296 | return results | ||
297 | end | ||
298 | |||
299 | --- Attempt to get a single URL for a given search for a rock. | ||
300 | -- @param query table: A dependency query matching a single rock. | ||
301 | -- @return string or (nil, string): URL for latest matching version | ||
302 | -- of the rock if it was found, or nil followed by an error message. | ||
303 | function search.find_suitable_rock(query) | ||
304 | assert(type(query) == "table") | ||
305 | |||
306 | local results = search.search_repos(query) | ||
307 | local first_rock = next(results) | ||
308 | if not first_rock then | ||
309 | if cfg.rocks_provided[query.name] == nil then | ||
310 | -- Check if constraints are satisfiable with other Lua versions. | ||
311 | local lua_versions = supported_lua_versions(query) | ||
312 | |||
313 | if #lua_versions ~= 0 then | ||
314 | -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format | ||
315 | for i, lua_version in ipairs(lua_versions) do | ||
316 | lua_versions[i] = "Lua "..lua_version | ||
317 | end | ||
318 | |||
319 | local versions_message = "only "..table.concat(lua_versions, " and ").. | ||
320 | " but not Lua "..cfg.lua_version.."." | ||
321 | |||
322 | if #query.constraints == 0 then | ||
323 | return nil, query.name.." supports "..versions_message | ||
324 | elseif #query.constraints == 1 and query.constraints[1].op == "==" then | ||
325 | return nil, query.name.." "..query.constraints[1].version.string.." supports "..versions_message | ||
326 | else | ||
327 | return nil, "Matching "..query.name.." versions support "..versions_message | ||
328 | end | ||
329 | end | ||
330 | end | ||
331 | |||
332 | return nil, "No results matching query were found." | ||
333 | elseif next(results, first_rock) then | ||
334 | -- Shouldn't happen as query must match only one package. | ||
335 | return nil, "Several rocks matched query." | ||
336 | elseif cfg.rocks_provided[query.name] ~= nil then | ||
337 | -- Do not install versions listed in cfg.rocks_provided. | ||
338 | return nil, "Rock "..query.name.." "..cfg.rocks_provided[query.name].. | ||
339 | " was found but it is provided by VM or 'rocks_provided' in the config file." | ||
340 | else | ||
341 | return pick_latest_version(query.name, results[first_rock]) | ||
342 | end | ||
343 | end | ||
344 | |||
345 | --- Print a list of rocks/rockspecs on standard output. | ||
346 | -- @param results table: A table where keys are package names and versions | ||
347 | -- are tables matching version strings to an array of rocks servers. | ||
348 | -- @param porcelain boolean or nil: A flag to force machine-friendly output. | ||
349 | function search.print_results(results, porcelain) | ||
350 | assert(type(results) == "table") | ||
351 | assert(type(porcelain) == "boolean" or not porcelain) | ||
352 | |||
353 | for package, versions in util.sortedpairs(results) do | ||
354 | if not porcelain then | ||
355 | util.printout(package) | ||
356 | end | ||
357 | for version, repos in util.sortedpairs(versions, deps.compare_versions) do | ||
358 | for _, repo in ipairs(repos) do | ||
359 | repo.repo = dir.normalize(repo.repo) | ||
360 | if porcelain then | ||
361 | util.printout(package, version, repo.arch, repo.repo) | ||
362 | else | ||
363 | util.printout(" "..version.." ("..repo.arch..") - "..repo.repo) | ||
364 | end | ||
365 | end | ||
366 | end | ||
367 | if not porcelain then | ||
368 | util.printout() | ||
369 | end | ||
370 | end | ||
371 | end | ||
372 | |||
373 | --- Splits a list of search results into two lists, one for "source" results | ||
374 | -- to be used with the "build" command, and one for "binary" results to be | ||
375 | -- used with the "install" command. | ||
376 | -- @param results table: A search results table. | ||
377 | -- @return (table, table): Two tables, one for source and one for binary | ||
378 | -- results. | ||
379 | local function split_source_and_binary_results(results) | ||
380 | local sources, binaries = {}, {} | ||
381 | for name, versions in pairs(results) do | ||
382 | for version, repositories in pairs(versions) do | ||
383 | for _, repo in ipairs(repositories) do | ||
384 | local where = sources | ||
385 | if repo.arch == "all" or repo.arch == cfg.arch then | ||
386 | where = binaries | ||
387 | end | ||
388 | store_result(where, name, version, repo.arch, repo.repo) | ||
389 | end | ||
390 | end | ||
391 | end | ||
392 | return sources, binaries | ||
393 | end | ||
394 | |||
395 | --- Given a name and optionally a version, try to find in the rocks | ||
396 | -- servers a single .src.rock or .rockspec file that satisfies | ||
397 | -- the request, and run the given function on it; or display to the | ||
398 | -- user possibilities if it couldn't narrow down a single match. | ||
399 | -- @param action function: A function that takes a .src.rock or | ||
400 | -- .rockspec URL as a parameter. | ||
401 | -- @param name string: A rock name | ||
402 | -- @param version string or nil: A version number may also be given. | ||
403 | -- @return The result of the action function, or nil and an error message. | ||
404 | function search.act_on_src_or_rockspec(action, name, version, ...) | ||
405 | assert(type(action) == "function") | ||
406 | assert(type(name) == "string") | ||
407 | assert(type(version) == "string" or not version) | ||
408 | |||
409 | local query = search.make_query(name, version) | ||
410 | query.arch = "src|rockspec" | ||
411 | local url, err = search.find_suitable_rock(query) | ||
412 | if not url then | ||
413 | return nil, "Could not find a result named "..name..(version and " "..version or "")..": "..err | ||
414 | end | ||
415 | return action(url, ...) | ||
416 | end | ||
417 | |||
418 | function search.pick_installed_rock(name, version, given_tree) | ||
419 | local results = {} | ||
420 | local query = search.make_query(name, version) | ||
421 | query.exact_name = true | ||
422 | local tree_map = {} | ||
423 | local trees = cfg.rocks_trees | ||
424 | if given_tree then | ||
425 | trees = { given_tree } | ||
426 | end | ||
427 | for _, tree in ipairs(trees) do | ||
428 | local rocks_dir = path.rocks_dir(tree) | ||
429 | tree_map[rocks_dir] = tree | ||
430 | search.manifest_search(results, rocks_dir, query) | ||
431 | end | ||
432 | |||
433 | if not next(results) then -- | ||
434 | return nil,"cannot find package "..name.." "..(version or "").."\nUse 'list' to find installed rocks." | ||
435 | end | ||
436 | |||
437 | version = nil | ||
438 | local repo_url | ||
439 | local package, versions = util.sortedpairs(results)() | ||
440 | --question: what do we do about multiple versions? This should | ||
441 | --give us the latest version on the last repo (which is usually the global one) | ||
442 | for vs, repositories in util.sortedpairs(versions, deps.compare_versions) do | ||
443 | if not version then version = vs end | ||
444 | for _, rp in ipairs(repositories) do repo_url = rp.repo end | ||
445 | end | ||
446 | |||
447 | local repo = tree_map[repo_url] | ||
448 | return name, version, repo, repo_url | ||
449 | end | ||
450 | |||
451 | --- Driver function for "search" command. | ||
452 | -- @param name string: A substring of a rock name to search. | ||
453 | -- @param version string or nil: a version may also be passed. | ||
454 | -- @return boolean or (nil, string): True if build was successful; nil and an | ||
455 | -- error message otherwise. | ||
456 | function search.command(flags, name, version) | ||
457 | if flags["all"] then | ||
458 | name, version = "", nil | ||
459 | end | ||
460 | |||
461 | if type(name) ~= "string" and not flags["all"] then | ||
462 | return nil, "Enter name and version or use --all. "..util.see_help("search") | ||
463 | end | ||
464 | |||
465 | local query = search.make_query(name:lower(), version) | ||
466 | query.exact_name = false | ||
467 | local results, err = search.search_repos(query) | ||
468 | local porcelain = flags["porcelain"] | ||
469 | util.title("Search results:", porcelain, "=") | ||
470 | local sources, binaries = split_source_and_binary_results(results) | ||
471 | if next(sources) and not flags["binary"] then | ||
472 | util.title("Rockspecs and source rocks:", porcelain) | ||
473 | search.print_results(sources, porcelain) | ||
474 | end | ||
475 | if next(binaries) and not flags["source"] then | ||
476 | util.title("Binary and pure-Lua rocks:", porcelain) | ||
477 | search.print_results(binaries, porcelain) | ||
478 | end | ||
479 | return true | ||
480 | end | ||
481 | |||
482 | return search | ||
diff --git a/src/luarocks/cmd/show.lua b/src/luarocks/cmd/show.lua new file mode 100644 index 00000000..1ff81e08 --- /dev/null +++ b/src/luarocks/cmd/show.lua | |||
@@ -0,0 +1,158 @@ | |||
1 | --- Module implementing the LuaRocks "show" command. | ||
2 | -- Shows information about an installed rock. | ||
3 | local show = {} | ||
4 | |||
5 | local search = require("luarocks.search") | ||
6 | local cfg = require("luarocks.core.cfg") | ||
7 | local util = require("luarocks.util") | ||
8 | local path = require("luarocks.path") | ||
9 | local deps = require("luarocks.deps") | ||
10 | local fetch = require("luarocks.fetch") | ||
11 | local manif = require("luarocks.manif") | ||
12 | |||
13 | show.help_summary = "Show information about an installed rock." | ||
14 | |||
15 | show.help = [[ | ||
16 | <argument> is an existing package name. | ||
17 | Without any flags, show all module information. | ||
18 | With these flags, return only the desired information: | ||
19 | |||
20 | --home home page of project | ||
21 | --modules all modules provided by this package as used by require() | ||
22 | --deps packages this package depends on | ||
23 | --rockspec the full path of the rockspec file | ||
24 | --mversion the package version | ||
25 | --rock-tree local tree where rock is installed | ||
26 | --rock-dir data directory of the installed rock | ||
27 | ]] | ||
28 | |||
29 | local function keys_as_string(t, sep) | ||
30 | local keys = util.keys(t) | ||
31 | table.sort(keys) | ||
32 | return table.concat(keys, sep or " ") | ||
33 | end | ||
34 | |||
35 | local function word_wrap(line) | ||
36 | local width = tonumber(os.getenv("COLUMNS")) or 80 | ||
37 | if width > 80 then width = 80 end | ||
38 | if #line > width then | ||
39 | local brk = width | ||
40 | while brk > 0 and line:sub(brk, brk) ~= " " do | ||
41 | brk = brk - 1 | ||
42 | end | ||
43 | if brk > 0 then | ||
44 | return line:sub(1, brk-1) .. "\n" .. word_wrap(line:sub(brk+1)) | ||
45 | end | ||
46 | end | ||
47 | return line | ||
48 | end | ||
49 | |||
50 | local function format_text(text) | ||
51 | text = text:gsub("^%s*",""):gsub("%s$", ""):gsub("\n[ \t]+","\n"):gsub("([^\n])\n([^\n])","%1 %2") | ||
52 | local paragraphs = util.split_string(text, "\n\n") | ||
53 | for n, line in ipairs(paragraphs) do | ||
54 | paragraphs[n] = word_wrap(line) | ||
55 | end | ||
56 | return (table.concat(paragraphs, "\n\n"):gsub("%s$", "")) | ||
57 | end | ||
58 | |||
59 | local function installed_rock_label(name, tree) | ||
60 | local installed, version | ||
61 | if cfg.rocks_provided[name] then | ||
62 | installed, version = true, cfg.rocks_provided[name] | ||
63 | else | ||
64 | installed, version = search.pick_installed_rock(name, nil, tree) | ||
65 | end | ||
66 | return installed and "(using "..version..")" or "(missing)" | ||
67 | end | ||
68 | |||
69 | --- Driver function for "show" command. | ||
70 | -- @param name or nil: an existing package name. | ||
71 | -- @param version string or nil: a version may also be passed. | ||
72 | -- @return boolean: True if succeeded, nil on errors. | ||
73 | function show.command(flags, name, version) | ||
74 | if not name then | ||
75 | return nil, "Argument missing. "..util.see_help("show") | ||
76 | end | ||
77 | |||
78 | local repo, repo_url | ||
79 | name, version, repo, repo_url = search.pick_installed_rock(name:lower(), version, flags["tree"]) | ||
80 | if not name then | ||
81 | return nil, version | ||
82 | end | ||
83 | |||
84 | local directory = path.install_dir(name,version,repo) | ||
85 | local rockspec_file = path.rockspec_file(name, version, repo) | ||
86 | local rockspec, err = fetch.load_local_rockspec(rockspec_file) | ||
87 | if not rockspec then return nil,err end | ||
88 | |||
89 | local descript = rockspec.description or {} | ||
90 | local manifest, err = manif.load_manifest(repo_url) | ||
91 | if not manifest then return nil,err end | ||
92 | local minfo = manifest.repository[name][version][1] | ||
93 | |||
94 | if flags["rock-tree"] then util.printout(path.rocks_tree_to_string(repo)) | ||
95 | elseif flags["rock-dir"] then util.printout(directory) | ||
96 | elseif flags["home"] then util.printout(descript.homepage) | ||
97 | elseif flags["issues"] then util.printout(descript.issues_url) | ||
98 | elseif flags["labels"] then util.printout(descript.labels and table.concat(descript.labels, "\n")) | ||
99 | elseif flags["modules"] then util.printout(keys_as_string(minfo.modules, "\n")) | ||
100 | elseif flags["deps"] then util.printout(keys_as_string(minfo.dependencies)) | ||
101 | elseif flags["rockspec"] then util.printout(rockspec_file) | ||
102 | elseif flags["mversion"] then util.printout(version) | ||
103 | else | ||
104 | util.printout() | ||
105 | util.printout(rockspec.package.." "..rockspec.version.." - "..(descript.summary or "")) | ||
106 | util.printout() | ||
107 | if descript.detailed then | ||
108 | util.printout(format_text(descript.detailed)) | ||
109 | util.printout() | ||
110 | end | ||
111 | if descript.license then | ||
112 | util.printout("License: ", descript.license) | ||
113 | end | ||
114 | if descript.homepage then | ||
115 | util.printout("Homepage: ", descript.homepage) | ||
116 | end | ||
117 | if descript.issues_url then | ||
118 | util.printout("Issues: ", descript.issues_url) | ||
119 | end | ||
120 | if descript.labels then | ||
121 | util.printout("Labels: ", table.concat(descript.labels, ", ")) | ||
122 | end | ||
123 | util.printout("Installed in: ", path.rocks_tree_to_string(repo)) | ||
124 | if next(minfo.modules) then | ||
125 | util.printout() | ||
126 | util.printout("Modules:") | ||
127 | for mod, filename in util.sortedpairs(minfo.modules) do | ||
128 | util.printout("\t"..mod.." ("..path.which(mod, filename, name, version, repo, manifest)..")") | ||
129 | end | ||
130 | end | ||
131 | local direct_deps = {} | ||
132 | if #rockspec.dependencies > 0 then | ||
133 | util.printout() | ||
134 | util.printout("Depends on:") | ||
135 | for _, dep in ipairs(rockspec.dependencies) do | ||
136 | direct_deps[dep.name] = true | ||
137 | util.printout("\t"..deps.show_dep(dep).." "..installed_rock_label(dep.name, flags["tree"])) | ||
138 | end | ||
139 | end | ||
140 | local has_indirect_deps | ||
141 | for dep_name in util.sortedpairs(minfo.dependencies) do | ||
142 | if not direct_deps[dep_name] then | ||
143 | if not has_indirect_deps then | ||
144 | util.printout() | ||
145 | util.printout("Indirectly pulling:") | ||
146 | has_indirect_deps = true | ||
147 | end | ||
148 | |||
149 | util.printout("\t"..dep_name.." "..installed_rock_label(dep_name, flags["tree"])) | ||
150 | end | ||
151 | end | ||
152 | util.printout() | ||
153 | end | ||
154 | return true | ||
155 | end | ||
156 | |||
157 | |||
158 | return show | ||
diff --git a/src/luarocks/cmd/unpack.lua b/src/luarocks/cmd/unpack.lua new file mode 100644 index 00000000..c50701b0 --- /dev/null +++ b/src/luarocks/cmd/unpack.lua | |||
@@ -0,0 +1,164 @@ | |||
1 | |||
2 | --- Module implementing the LuaRocks "unpack" command. | ||
3 | -- Unpack the contents of a rock. | ||
4 | local unpack = {} | ||
5 | |||
6 | local fetch = require("luarocks.fetch") | ||
7 | local fs = require("luarocks.fs") | ||
8 | local util = require("luarocks.util") | ||
9 | local build = require("luarocks.build") | ||
10 | local dir = require("luarocks.dir") | ||
11 | local cfg = require("luarocks.core.cfg") | ||
12 | |||
13 | unpack.help_summary = "Unpack the contents of a rock." | ||
14 | unpack.help_arguments = "[--force] {<rock>|<name> [<version>]}" | ||
15 | unpack.help = [[ | ||
16 | Unpacks the contents of a rock in a newly created directory. | ||
17 | Argument may be a rock file, or the name of a rock in a rocks server. | ||
18 | In the latter case, the app version may be given as a second argument. | ||
19 | |||
20 | --force Unpack files even if the output directory already exists. | ||
21 | ]] | ||
22 | |||
23 | --- Load a rockspec file to the given directory, fetches the source | ||
24 | -- files specified in the rockspec, and unpack them inside the directory. | ||
25 | -- @param rockspec_file string: The URL for a rockspec file. | ||
26 | -- @param dir_name string: The directory where to store and unpack files. | ||
27 | -- @return table or (nil, string): the loaded rockspec table or | ||
28 | -- nil and an error message. | ||
29 | local function unpack_rockspec(rockspec_file, dir_name) | ||
30 | assert(type(rockspec_file) == "string") | ||
31 | assert(type(dir_name) == "string") | ||
32 | |||
33 | local rockspec, err = fetch.load_rockspec(rockspec_file) | ||
34 | if not rockspec then | ||
35 | return nil, "Failed loading rockspec "..rockspec_file..": "..err | ||
36 | end | ||
37 | local ok, err = fs.change_dir(dir_name) | ||
38 | if not ok then return nil, err end | ||
39 | local ok, sources_dir = fetch.fetch_sources(rockspec, true, ".") | ||
40 | if not ok then | ||
41 | return nil, sources_dir | ||
42 | end | ||
43 | ok, err = fs.change_dir(sources_dir) | ||
44 | if not ok then return nil, err end | ||
45 | ok, err = build.apply_patches(rockspec) | ||
46 | fs.pop_dir() | ||
47 | if not ok then return nil, err end | ||
48 | return rockspec | ||
49 | end | ||
50 | |||
51 | --- Load a .rock file to the given directory and unpack it inside it. | ||
52 | -- @param rock_file string: The URL for a .rock file. | ||
53 | -- @param dir_name string: The directory where to unpack. | ||
54 | -- @param kind string: the kind of rock file, as in the second-level | ||
55 | -- extension in the rock filename (eg. "src", "all", "linux-x86") | ||
56 | -- @return table or (nil, string): the loaded rockspec table or | ||
57 | -- nil and an error message. | ||
58 | local function unpack_rock(rock_file, dir_name, kind) | ||
59 | assert(type(rock_file) == "string") | ||
60 | assert(type(dir_name) == "string") | ||
61 | |||
62 | local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, dir_name) | ||
63 | if not ok then | ||
64 | return nil, "Failed unzipping rock "..rock_file, errcode | ||
65 | end | ||
66 | ok, err = fs.change_dir(dir_name) | ||
67 | if not ok then return nil, err end | ||
68 | local rockspec_file = dir_name..".rockspec" | ||
69 | local rockspec, err = fetch.load_rockspec(rockspec_file) | ||
70 | if not rockspec then | ||
71 | return nil, "Failed loading rockspec "..rockspec_file..": "..err | ||
72 | end | ||
73 | if kind == "src" then | ||
74 | if rockspec.source.file then | ||
75 | local ok, err = fs.unpack_archive(rockspec.source.file) | ||
76 | if not ok then | ||
77 | return nil, err | ||
78 | end | ||
79 | ok, err = fs.change_dir(rockspec.source.dir) | ||
80 | if not ok then return nil, err end | ||
81 | ok, err = build.apply_patches(rockspec) | ||
82 | fs.pop_dir() | ||
83 | if not ok then return nil, err end | ||
84 | end | ||
85 | end | ||
86 | return rockspec | ||
87 | end | ||
88 | |||
89 | --- Create a directory and perform the necessary actions so that | ||
90 | -- the sources for the rock and its rockspec are unpacked inside it, | ||
91 | -- laid out properly so that the 'make' command is able to build the module. | ||
92 | -- @param file string: A rockspec or .rock URL. | ||
93 | -- @return boolean or (nil, string): true if successful or nil followed | ||
94 | -- by an error message. | ||
95 | local function run_unpacker(file, force) | ||
96 | assert(type(file) == "string") | ||
97 | |||
98 | local base_name = dir.base_name(file) | ||
99 | local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$") | ||
100 | if not extension then | ||
101 | dir_name, extension = base_name:match("(.*)%.(rockspec)$") | ||
102 | kind = "rockspec" | ||
103 | end | ||
104 | if not extension then | ||
105 | return nil, file.." does not seem to be a valid filename." | ||
106 | end | ||
107 | |||
108 | local exists = fs.exists(dir_name) | ||
109 | if exists and not force then | ||
110 | return nil, "Directory "..dir_name.." already exists." | ||
111 | end | ||
112 | if not exists then | ||
113 | local ok, err = fs.make_dir(dir_name) | ||
114 | if not ok then return nil, err end | ||
115 | end | ||
116 | local rollback = util.schedule_function(fs.delete, fs.absolute_name(dir_name)) | ||
117 | |||
118 | local rockspec, err | ||
119 | if extension == "rock" then | ||
120 | rockspec, err = unpack_rock(file, dir_name, kind) | ||
121 | elseif extension == "rockspec" then | ||
122 | rockspec, err = unpack_rockspec(file, dir_name) | ||
123 | end | ||
124 | if not rockspec then | ||
125 | return nil, err | ||
126 | end | ||
127 | if kind == "src" or kind == "rockspec" then | ||
128 | if rockspec.source.dir ~= "." then | ||
129 | local ok = fs.copy(rockspec.local_filename, rockspec.source.dir, cfg.perm_read) | ||
130 | if not ok then | ||
131 | return nil, "Failed copying unpacked rockspec into unpacked source directory." | ||
132 | end | ||
133 | end | ||
134 | util.printout() | ||
135 | util.printout("Done. You may now enter directory ") | ||
136 | util.printout(dir.path(dir_name, rockspec.source.dir)) | ||
137 | util.printout("and type 'luarocks make' to build.") | ||
138 | end | ||
139 | util.remove_scheduled_function(rollback) | ||
140 | return true | ||
141 | end | ||
142 | |||
143 | --- Driver function for the "unpack" command. | ||
144 | -- @param name string: may be a rock filename, for unpacking a | ||
145 | -- rock file or the name of a rock to be fetched and unpacked. | ||
146 | -- @param version string or nil: if the name of a package is given, a | ||
147 | -- version may also be passed. | ||
148 | -- @return boolean or (nil, string): true if successful or nil followed | ||
149 | -- by an error message. | ||
150 | function unpack.command(flags, name, version) | ||
151 | assert(type(version) == "string" or not version) | ||
152 | if type(name) ~= "string" then | ||
153 | return nil, "Argument missing. "..util.see_help("unpack") | ||
154 | end | ||
155 | |||
156 | if name:match(".*%.rock") or name:match(".*%.rockspec") then | ||
157 | return run_unpacker(name, flags["force"]) | ||
158 | else | ||
159 | local search = require("luarocks.search") | ||
160 | return search.act_on_src_or_rockspec(run_unpacker, name:lower(), version) | ||
161 | end | ||
162 | end | ||
163 | |||
164 | return unpack | ||
diff --git a/src/luarocks/cmd/upload.lua b/src/luarocks/cmd/upload.lua new file mode 100644 index 00000000..baee47ab --- /dev/null +++ b/src/luarocks/cmd/upload.lua | |||
@@ -0,0 +1,94 @@ | |||
1 | |||
2 | local upload = {} | ||
3 | |||
4 | local util = require("luarocks.util") | ||
5 | local fetch = require("luarocks.fetch") | ||
6 | local pack = require("luarocks.pack") | ||
7 | local cfg = require("luarocks.core.cfg") | ||
8 | local Api = require("luarocks.upload.api") | ||
9 | |||
10 | upload.help_summary = "Upload a rockspec to the public rocks repository." | ||
11 | upload.help_arguments = "[--skip-pack] [--api-key=<key>] [--force] <rockspec>" | ||
12 | upload.help = [[ | ||
13 | <rockspec> Pack a source rock file (.src.rock extension), | ||
14 | upload rockspec and source rock to server. | ||
15 | --skip-pack Do not pack and send source rock. | ||
16 | --api-key=<key> Give it an API key. It will be stored for subsequent uses. | ||
17 | --force Replace existing rockspec if the same revision of | ||
18 | a module already exists. This should be used only | ||
19 | in case of upload mistakes: when updating a rockspec, | ||
20 | increment the revision number instead. | ||
21 | ]] | ||
22 | |||
23 | function upload.command(flags, fname) | ||
24 | if not fname then | ||
25 | return nil, "Missing rockspec. "..util.see_help("upload") | ||
26 | end | ||
27 | |||
28 | local api, err = Api.new(flags) | ||
29 | if not api then | ||
30 | return nil, err | ||
31 | end | ||
32 | if cfg.verbose then | ||
33 | api.debug = true | ||
34 | end | ||
35 | |||
36 | local rockspec, err, errcode = fetch.load_rockspec(fname) | ||
37 | if err then | ||
38 | return nil, err, errcode | ||
39 | end | ||
40 | |||
41 | util.printout("Sending " .. tostring(fname) .. " ...") | ||
42 | local res, err = api:method("check_rockspec", { | ||
43 | package = rockspec.package, | ||
44 | version = rockspec.version | ||
45 | }) | ||
46 | if not res then return nil, err end | ||
47 | |||
48 | if not res.module then | ||
49 | util.printout("Will create new module (" .. tostring(rockspec.package) .. ")") | ||
50 | end | ||
51 | if res.version and not flags["force"] then | ||
52 | return nil, "Revision "..rockspec.version.." already exists on the server. "..util.see_help("upload") | ||
53 | end | ||
54 | |||
55 | local rock_fname | ||
56 | if not flags["skip-pack"] and not rockspec.version:match("^scm") then | ||
57 | util.printout("Packing " .. tostring(rockspec.package)) | ||
58 | rock_fname, err = pack.pack_source_rock(fname) | ||
59 | if not rock_fname then | ||
60 | return nil, err | ||
61 | end | ||
62 | end | ||
63 | |||
64 | local multipart = require("luarocks.upload.multipart") | ||
65 | |||
66 | res, err = api:method("upload", nil, { | ||
67 | rockspec_file = multipart.new_file(fname) | ||
68 | }) | ||
69 | if not res then return nil, err end | ||
70 | |||
71 | if res.is_new and #res.manifests == 0 then | ||
72 | util.printerr("Warning: module not added to root manifest due to name taken.") | ||
73 | end | ||
74 | |||
75 | local module_url = res.module_url | ||
76 | |||
77 | if rock_fname then | ||
78 | if (not res.version) or (not res.version.id) then | ||
79 | return nil, "Invalid response from server." | ||
80 | end | ||
81 | util.printout(("Sending " .. tostring(rock_fname) .. " ...")) | ||
82 | res, err = api:method("upload_rock/" .. ("%d"):format(res.version.id), nil, { | ||
83 | rock_file = multipart.new_file(rock_fname) | ||
84 | }) | ||
85 | if not res then return nil, err end | ||
86 | end | ||
87 | |||
88 | util.printout() | ||
89 | util.printout("Done: " .. tostring(module_url)) | ||
90 | util.printout() | ||
91 | return true | ||
92 | end | ||
93 | |||
94 | return upload | ||
diff --git a/src/luarocks/cmd/write_rockspec.lua b/src/luarocks/cmd/write_rockspec.lua new file mode 100644 index 00000000..be563eaa --- /dev/null +++ b/src/luarocks/cmd/write_rockspec.lua | |||
@@ -0,0 +1,376 @@ | |||
1 | |||
2 | local write_rockspec = {} | ||
3 | |||
4 | local cfg = require("luarocks.core.cfg") | ||
5 | local dir = require("luarocks.dir") | ||
6 | local fetch = require("luarocks.fetch") | ||
7 | local fs = require("luarocks.fs") | ||
8 | local path = require("luarocks.path") | ||
9 | local persist = require("luarocks.persist") | ||
10 | local type_check = require("luarocks.type_check") | ||
11 | local util = require("luarocks.util") | ||
12 | local deps = require("luarocks.deps") | ||
13 | |||
14 | write_rockspec.help_summary = "Write a template for a rockspec file." | ||
15 | write_rockspec.help_arguments = "[--output=<file> ...] [<name>] [<version>] [<url>|<path>]" | ||
16 | write_rockspec.help = [[ | ||
17 | This command writes an initial version of a rockspec file, | ||
18 | based on a name, a version, and a location (an URL or a local path). | ||
19 | If only two arguments are given, the first one is considered the name and the | ||
20 | second one is the location. | ||
21 | If only one argument is given, it must be the location. | ||
22 | If no arguments are given, current directory is used as location. | ||
23 | LuaRocks will attempt to infer name and version if not given, | ||
24 | using 'scm' as default version. | ||
25 | |||
26 | Note that the generated file is a _starting point_ for writing a | ||
27 | rockspec, and is not guaranteed to be complete or correct. | ||
28 | |||
29 | --output=<file> Write the rockspec with the given filename. | ||
30 | If not given, a file is written in the current | ||
31 | directory with a filename based on given name and version. | ||
32 | --license="<string>" A license string, such as "MIT/X11" or "GNU GPL v3". | ||
33 | --summary="<txt>" A short one-line description summary. | ||
34 | --detailed="<txt>" A longer description string. | ||
35 | --homepage=<url> Project homepage. | ||
36 | --lua-version=<ver> Supported Lua versions. Accepted values are "5.1", "5.2", | ||
37 | "5.3", "5.1,5.2", "5.2,5.3", or "5.1,5.2,5.3". | ||
38 | --rockspec-format=<ver> Rockspec format version, such as "1.0" or "1.1". | ||
39 | --tag=<tag> Tag to use. Will attempt to extract version number from it. | ||
40 | --lib=<lib>[,<lib>] A comma-separated list of libraries that C files need to | ||
41 | link to. | ||
42 | ]] | ||
43 | |||
44 | local function open_file(name) | ||
45 | return io.open(dir.path(fs.current_dir(), name), "r") | ||
46 | end | ||
47 | |||
48 | local function get_url(rockspec) | ||
49 | local file, temp_dir, err_code, err_file, err_temp_dir = fetch.fetch_sources(rockspec, false) | ||
50 | if err_code == "source.dir" then | ||
51 | file, temp_dir = err_file, err_temp_dir | ||
52 | elseif not file then | ||
53 | util.warning("Could not fetch sources - "..temp_dir) | ||
54 | return false | ||
55 | end | ||
56 | util.printout("File successfully downloaded. Making checksum and checking base dir...") | ||
57 | if fetch.is_basic_protocol(rockspec.source.protocol) then | ||
58 | rockspec.source.md5 = fs.get_md5(file) | ||
59 | end | ||
60 | local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, rockspec.source.url) | ||
61 | return true, found_dir or inferred_dir, temp_dir | ||
62 | end | ||
63 | |||
64 | local function configure_lua_version(rockspec, luaver) | ||
65 | if luaver == "5.1" then | ||
66 | table.insert(rockspec.dependencies, "lua ~> 5.1") | ||
67 | elseif luaver == "5.2" then | ||
68 | table.insert(rockspec.dependencies, "lua ~> 5.2") | ||
69 | elseif luaver == "5.3" then | ||
70 | table.insert(rockspec.dependencies, "lua ~> 5.3") | ||
71 | elseif luaver == "5.1,5.2" then | ||
72 | table.insert(rockspec.dependencies, "lua >= 5.1, < 5.3") | ||
73 | elseif luaver == "5.2,5.3" then | ||
74 | table.insert(rockspec.dependencies, "lua >= 5.2, < 5.4") | ||
75 | elseif luaver == "5.1,5.2,5.3" then | ||
76 | table.insert(rockspec.dependencies, "lua >= 5.1, < 5.4") | ||
77 | else | ||
78 | util.warning("Please specify supported Lua version with --lua-version=<ver>. "..util.see_help("write_rockspec")) | ||
79 | end | ||
80 | end | ||
81 | |||
82 | local function detect_description() | ||
83 | local fd = open_file("README.md") or open_file("README") | ||
84 | if not fd then return end | ||
85 | local data = fd:read("*a") | ||
86 | fd:close() | ||
87 | local paragraph = data:match("\n\n([^%[].-)\n\n") | ||
88 | if not paragraph then paragraph = data:match("\n\n(.*)") end | ||
89 | local summary, detailed | ||
90 | if paragraph then | ||
91 | detailed = paragraph | ||
92 | |||
93 | if #paragraph < 80 then | ||
94 | summary = paragraph:gsub("\n", "") | ||
95 | else | ||
96 | summary = paragraph:gsub("\n", " "):match("([^.]*%.) ") | ||
97 | end | ||
98 | end | ||
99 | return summary, detailed | ||
100 | end | ||
101 | |||
102 | local function detect_mit_license(data) | ||
103 | local strip_copyright = (data:gsub("Copyright [^\n]*\n", "")) | ||
104 | local sum = 0 | ||
105 | for i = 1, #strip_copyright do | ||
106 | local num = string.byte(strip_copyright:sub(i,i)) | ||
107 | if num > 32 and num <= 128 then | ||
108 | sum = sum + num | ||
109 | end | ||
110 | end | ||
111 | return sum == 78656 | ||
112 | end | ||
113 | |||
114 | local simple_scm_protocols = { | ||
115 | git = true, ["git+http"] = true, ["git+https"] = true, | ||
116 | hg = true, ["hg+http"] = true, ["hg+https"] = true | ||
117 | } | ||
118 | |||
119 | local function detect_url_from_command(program, args, directory) | ||
120 | local command = fs.Q(cfg.variables[program:upper()]).. " "..args | ||
121 | local pipe = io.popen(fs.command_at(directory, fs.quiet_stderr(command))) | ||
122 | if not pipe then return nil end | ||
123 | local url = pipe:read("*a"):match("^([^\r\n]+)") | ||
124 | pipe:close() | ||
125 | if not url then return nil end | ||
126 | if not util.starts_with(url, program.."://") then | ||
127 | url = program.."+"..url | ||
128 | end | ||
129 | |||
130 | if simple_scm_protocols[dir.split_url(url)] then | ||
131 | return url | ||
132 | end | ||
133 | end | ||
134 | |||
135 | local function detect_scm_url(directory) | ||
136 | return detect_url_from_command("git", "config --get remote.origin.url", directory) or | ||
137 | detect_url_from_command("hg", "paths default", directory) | ||
138 | end | ||
139 | |||
140 | local function show_license(rockspec) | ||
141 | local fd = open_file("COPYING") or open_file("LICENSE") or open_file("MIT-LICENSE.txt") | ||
142 | if not fd then return nil end | ||
143 | local data = fd:read("*a") | ||
144 | fd:close() | ||
145 | local is_mit = detect_mit_license(data) | ||
146 | util.title("License for "..rockspec.package..":") | ||
147 | util.printout(data) | ||
148 | util.printout() | ||
149 | return is_mit | ||
150 | end | ||
151 | |||
152 | local function get_cmod_name(file) | ||
153 | local fd = open_file(file) | ||
154 | if not fd then return nil end | ||
155 | local data = fd:read("*a") | ||
156 | fd:close() | ||
157 | return (data:match("int%s+luaopen_([a-zA-Z0-9_]+)")) | ||
158 | end | ||
159 | |||
160 | local luamod_blacklist = { | ||
161 | test = true, | ||
162 | tests = true, | ||
163 | } | ||
164 | |||
165 | local function fill_as_builtin(rockspec, libs) | ||
166 | rockspec.build.type = "builtin" | ||
167 | rockspec.build.modules = {} | ||
168 | local prefix = "" | ||
169 | |||
170 | for _, parent in ipairs({"src", "lua"}) do | ||
171 | if fs.is_dir(parent) then | ||
172 | fs.change_dir(parent) | ||
173 | prefix = parent.."/" | ||
174 | break | ||
175 | end | ||
176 | end | ||
177 | |||
178 | local incdirs, libdirs | ||
179 | if libs then | ||
180 | incdirs, libdirs = {}, {} | ||
181 | for _, lib in ipairs(libs) do | ||
182 | local upper = lib:upper() | ||
183 | incdirs[#incdirs+1] = "$("..upper.."_INCDIR)" | ||
184 | libdirs[#libdirs+1] = "$("..upper.."_LIBDIR)" | ||
185 | end | ||
186 | end | ||
187 | |||
188 | for _, file in ipairs(fs.find()) do | ||
189 | local luamod = file:match("(.*)%.lua$") | ||
190 | if luamod and not luamod_blacklist[luamod] then | ||
191 | rockspec.build.modules[path.path_to_module(file)] = prefix..file | ||
192 | else | ||
193 | local cmod = file:match("(.*)%.c$") | ||
194 | if cmod then | ||
195 | local modname = get_cmod_name(file) or path.path_to_module(file:gsub("%.c$", ".lua")) | ||
196 | rockspec.build.modules[modname] = { | ||
197 | sources = prefix..file, | ||
198 | libraries = libs, | ||
199 | incdirs = incdirs, | ||
200 | libdirs = libdirs, | ||
201 | } | ||
202 | end | ||
203 | end | ||
204 | end | ||
205 | |||
206 | for _, directory in ipairs({ "doc", "docs", "samples", "tests" }) do | ||
207 | if fs.is_dir(directory) then | ||
208 | if not rockspec.build.copy_directories then | ||
209 | rockspec.build.copy_directories = {} | ||
210 | end | ||
211 | table.insert(rockspec.build.copy_directories, directory) | ||
212 | end | ||
213 | end | ||
214 | |||
215 | if prefix ~= "" then | ||
216 | fs.pop_dir() | ||
217 | end | ||
218 | end | ||
219 | |||
220 | local function rockspec_cleanup(rockspec) | ||
221 | rockspec.source.file = nil | ||
222 | rockspec.source.protocol = nil | ||
223 | rockspec.variables = nil | ||
224 | rockspec.name = nil | ||
225 | rockspec.format_is_at_least = nil | ||
226 | end | ||
227 | |||
228 | function write_rockspec.command(flags, name, version, url_or_dir) | ||
229 | if not name then | ||
230 | url_or_dir = "." | ||
231 | elseif not version then | ||
232 | url_or_dir = name | ||
233 | name = nil | ||
234 | elseif not url_or_dir then | ||
235 | url_or_dir = version | ||
236 | version = nil | ||
237 | end | ||
238 | |||
239 | if flags["tag"] then | ||
240 | if not version then | ||
241 | version = flags["tag"]:gsub("^v", "") | ||
242 | end | ||
243 | end | ||
244 | |||
245 | local protocol, pathname = dir.split_url(url_or_dir) | ||
246 | if protocol == "file" then | ||
247 | if pathname == "." then | ||
248 | name = name or dir.base_name(fs.current_dir()) | ||
249 | end | ||
250 | elseif fetch.is_basic_protocol(protocol) then | ||
251 | local filename = dir.base_name(url_or_dir) | ||
252 | local newname, newversion = filename:match("(.*)-([^-]+)") | ||
253 | if newname then | ||
254 | name = name or newname | ||
255 | version = version or newversion:gsub("%.[a-z]+$", ""):gsub("%.tar$", "") | ||
256 | end | ||
257 | else | ||
258 | name = name or dir.base_name(url_or_dir):gsub("%.[^.]+$", "") | ||
259 | end | ||
260 | |||
261 | if not name then | ||
262 | return nil, "Could not infer rock name. "..util.see_help("write_rockspec") | ||
263 | end | ||
264 | version = version or "scm" | ||
265 | |||
266 | local filename = flags["output"] or dir.path(fs.current_dir(), name:lower().."-"..version.."-1.rockspec") | ||
267 | |||
268 | local rockspec = { | ||
269 | rockspec_format = flags["rockspec-format"], | ||
270 | package = name, | ||
271 | name = name:lower(), | ||
272 | version = version.."-1", | ||
273 | source = { | ||
274 | url = "*** please add URL for source tarball, zip or repository here ***", | ||
275 | tag = flags["tag"], | ||
276 | }, | ||
277 | description = { | ||
278 | summary = flags["summary"] or "*** please specify description summary ***", | ||
279 | detailed = flags["detailed"] or "*** please enter a detailed description ***", | ||
280 | homepage = flags["homepage"] or "*** please enter a project homepage ***", | ||
281 | license = flags["license"] or "*** please specify a license ***", | ||
282 | }, | ||
283 | dependencies = {}, | ||
284 | build = {}, | ||
285 | } | ||
286 | path.configure_paths(rockspec) | ||
287 | rockspec.source.protocol = protocol | ||
288 | rockspec.format_is_at_least = deps.format_is_at_least | ||
289 | |||
290 | configure_lua_version(rockspec, flags["lua-version"]) | ||
291 | |||
292 | local local_dir = url_or_dir | ||
293 | |||
294 | if url_or_dir:match("://") then | ||
295 | rockspec.source.url = url_or_dir | ||
296 | rockspec.source.file = dir.base_name(url_or_dir) | ||
297 | rockspec.source.dir = "dummy" | ||
298 | if not fetch.is_basic_protocol(rockspec.source.protocol) then | ||
299 | if version ~= "scm" then | ||
300 | rockspec.source.tag = flags["tag"] or "v" .. version | ||
301 | end | ||
302 | end | ||
303 | rockspec.source.dir = nil | ||
304 | local ok, base_dir, temp_dir = get_url(rockspec) | ||
305 | if ok then | ||
306 | if base_dir ~= dir.base_name(url_or_dir) then | ||
307 | rockspec.source.dir = base_dir | ||
308 | end | ||
309 | end | ||
310 | if base_dir then | ||
311 | local_dir = dir.path(temp_dir, base_dir) | ||
312 | else | ||
313 | local_dir = nil | ||
314 | end | ||
315 | else | ||
316 | rockspec.source.url = detect_scm_url(local_dir) or rockspec.source.url | ||
317 | end | ||
318 | |||
319 | if not local_dir then | ||
320 | local_dir = "." | ||
321 | end | ||
322 | |||
323 | if not flags["homepage"] then | ||
324 | local url_protocol, url_path = dir.split_url(rockspec.source.url) | ||
325 | |||
326 | if simple_scm_protocols[url_protocol] then | ||
327 | for _, domain in ipairs({"github.com", "bitbucket.org", "gitlab.com"}) do | ||
328 | if util.starts_with(url_path, domain) then | ||
329 | rockspec.description.homepage = "https://"..url_path:gsub("%.git$", "") | ||
330 | break | ||
331 | end | ||
332 | end | ||
333 | end | ||
334 | end | ||
335 | |||
336 | local libs = nil | ||
337 | if flags["lib"] then | ||
338 | libs = {} | ||
339 | rockspec.external_dependencies = {} | ||
340 | for lib in flags["lib"]:gmatch("([^,]+)") do | ||
341 | table.insert(libs, lib) | ||
342 | rockspec.external_dependencies[lib:upper()] = { | ||
343 | library = lib | ||
344 | } | ||
345 | end | ||
346 | end | ||
347 | |||
348 | local ok, err = fs.change_dir(local_dir) | ||
349 | if not ok then return nil, "Failed reaching files from project - error entering directory "..local_dir end | ||
350 | |||
351 | if (not flags["summary"]) or (not flags["detailed"]) then | ||
352 | local summary, detailed = detect_description() | ||
353 | rockspec.description.summary = flags["summary"] or summary | ||
354 | rockspec.description.detailed = flags["detailed"] or detailed | ||
355 | end | ||
356 | |||
357 | local is_mit = show_license(rockspec) | ||
358 | |||
359 | if is_mit and not flags["license"] then | ||
360 | rockspec.description.license = "MIT" | ||
361 | end | ||
362 | |||
363 | fill_as_builtin(rockspec, libs) | ||
364 | |||
365 | rockspec_cleanup(rockspec) | ||
366 | |||
367 | persist.save_from_table(filename, rockspec, type_check.rockspec_order) | ||
368 | |||
369 | util.printout() | ||
370 | util.printout("Wrote template at "..filename.." -- you should now edit and finish it.") | ||
371 | util.printout() | ||
372 | |||
373 | return true | ||
374 | end | ||
375 | |||
376 | return write_rockspec | ||