Mercurial > repo
comparison share/lua/5.2/luarocks/build.lua @ 1132:d137f631bad5
<GreyKnight> (cd luabuild/luarocks-2.0.12; make install)
author | HackBot |
---|---|
date | Fri, 14 Dec 2012 22:24:27 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
1131:ff65dfb63263 | 1132:d137f631bad5 |
---|---|
1 | |
2 --- Module implementing the LuaRocks "build" command. | |
3 -- Builds a rock, compiling its C parts if any. | |
4 module("luarocks.build", package.seeall) | |
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 manif = require("luarocks.manif") | |
15 local cfg = require("luarocks.cfg") | |
16 | |
17 help_summary = "Build/compile a rock." | |
18 help_arguments = "[--pack-binary-rock] {<rockspec>|<rock>|<name> [<version>]}" | |
19 help = [[ | |
20 Build and install a rock, compiling its C parts if any. | |
21 Argument may be a rockspec file, a source rock file | |
22 or the name of a rock to be fetched from a repository. | |
23 | |
24 If --pack-binary-rock is passed, the rock is not installed; | |
25 instead, a .rock file with the contents of compilation is produced | |
26 in the current directory. | |
27 ]] | |
28 | |
29 --- Install files to a given location. | |
30 -- Takes a table where the array part is a list of filenames to be copied. | |
31 -- In the hash part, other keys, if is_module_path is set, are identifiers | |
32 -- in Lua module format, to indicate which subdirectory the file should be | |
33 -- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") | |
34 -- will copy src/bar.lua to boo/foo. | |
35 -- @param files table or nil: A table containing a list of files to copy in | |
36 -- the format described above. If nil is passed, this function is a no-op. | |
37 -- Directories should be delimited by forward slashes as in internet URLs. | |
38 -- @param location string: The base directory files should be copied to. | |
39 -- @param is_module_path boolean: True if string keys in files should be | |
40 -- interpreted as dotted module paths. | |
41 -- @return boolean or (nil, string): True if succeeded or | |
42 -- nil and an error message. | |
43 local function install_files(files, location, is_module_path) | |
44 assert(type(files) == "table" or not files) | |
45 assert(type(location) == "string") | |
46 if files then | |
47 for k, file in pairs(files) do | |
48 local dest = location | |
49 if type(k) == "string" then | |
50 if is_module_path then | |
51 dest = dir.path(location, path.module_to_path(k)) | |
52 fs.make_dir(dest) | |
53 else | |
54 dest = dir.path(location, dir.dir_name(k)) | |
55 fs.make_dir(dest) | |
56 dest = dir.path(dest, dir.base_name(k)) | |
57 end | |
58 else | |
59 fs.make_dir(dest) | |
60 end | |
61 local ok = fs.copy(dir.path(file), dest) | |
62 if not ok then | |
63 return nil, "Failed copying "..file | |
64 end | |
65 end | |
66 end | |
67 return true | |
68 end | |
69 | |
70 --- Write to the current directory the contents of a table, | |
71 -- where each key is a file name and its value is the file content. | |
72 -- @param files table: The table of files to be written. | |
73 local function extract_from_rockspec(files) | |
74 for name, content in pairs(files) do | |
75 local fd = io.open(dir.path(fs.current_dir(), name), "w+") | |
76 fd:write(content) | |
77 fd:close() | |
78 end | |
79 end | |
80 | |
81 --- Applies patches inlined in the build.patches section | |
82 -- and extracts files inlined in the build.extra_files section | |
83 -- of a rockspec. | |
84 -- @param rockspec table: A rockspec table. | |
85 -- @return boolean or (nil, string): True if succeeded or | |
86 -- nil and an error message. | |
87 function apply_patches(rockspec) | |
88 assert(type(rockspec) == "table") | |
89 | |
90 local build = rockspec.build | |
91 if build.extra_files then | |
92 extract_from_rockspec(build.extra_files) | |
93 end | |
94 if build.patches then | |
95 extract_from_rockspec(build.patches) | |
96 for patch, patchdata in util.sortedpairs(build.patches) do | |
97 util.printout("Applying patch "..patch.."...") | |
98 local ok, err = fs.apply_patch(tostring(patch), patchdata) | |
99 if not ok then | |
100 return nil, "Failed applying patch "..patch | |
101 end | |
102 end | |
103 end | |
104 return true | |
105 end | |
106 | |
107 --- Build and install a rock given a rockspec. | |
108 -- @param rockspec_file string: local or remote filename of a rockspec. | |
109 -- @param need_to_fetch boolean: true if sources need to be fetched, | |
110 -- false if the rockspec was obtained from inside a source rock. | |
111 -- @param minimal_mode boolean: true if there's no need to fetch, | |
112 -- unpack or change dir (this is used by "luarocks make"). Implies | |
113 -- need_to_fetch = false. | |
114 -- @param deps_mode: string: Which trees to check dependencies for: | |
115 -- "none", "one", "order" or "all". | |
116 -- @return boolean or (nil, string, [string]): True if succeeded or | |
117 -- nil and an error message followed by an error code. | |
118 function build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode) | |
119 assert(type(rockspec_file) == "string") | |
120 assert(type(need_to_fetch) == "boolean") | |
121 | |
122 local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) | |
123 if err then | |
124 return nil, err, errcode | |
125 elseif not rockspec.build then | |
126 return nil, "Rockspec error: build table not specified" | |
127 elseif not rockspec.build.type then | |
128 return nil, "Rockspec error: build type not specified" | |
129 end | |
130 | |
131 if deps_mode == "none" then | |
132 util.printerr("Warning: skipping dependency checks.") | |
133 else | |
134 local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) | |
135 if err then | |
136 return nil, err, errcode | |
137 end | |
138 end | |
139 | |
140 ok, err, errcode = deps.check_external_deps(rockspec, "build") | |
141 if err then | |
142 return nil, err, errcode | |
143 end | |
144 | |
145 local name, version = rockspec.name, rockspec.version | |
146 if repos.is_installed(name, version) then | |
147 repos.delete_version(name, version) | |
148 end | |
149 | |
150 if not minimal_mode then | |
151 local _, source_dir | |
152 if need_to_fetch then | |
153 ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) | |
154 if not ok then | |
155 return nil, source_dir, errcode | |
156 end | |
157 fs.change_dir(source_dir) | |
158 elseif rockspec.source.file then | |
159 local ok, err = fs.unpack_archive(rockspec.source.file) | |
160 if not ok then | |
161 return nil, err | |
162 end | |
163 end | |
164 fs.change_dir(rockspec.source.dir) | |
165 end | |
166 | |
167 local dirs = { | |
168 lua = { name = path.lua_dir(name, version), is_module_path = true }, | |
169 lib = { name = path.lib_dir(name, version), is_module_path = true }, | |
170 conf = { name = path.conf_dir(name, version), is_module_path = false }, | |
171 bin = { name = path.bin_dir(name, version), is_module_path = false }, | |
172 } | |
173 | |
174 for _, d in pairs(dirs) do | |
175 fs.make_dir(d.name) | |
176 end | |
177 local rollback = util.schedule_function(function() | |
178 fs.delete(path.install_dir(name, version)) | |
179 fs.remove_dir_if_empty(path.versions_dir(name)) | |
180 end) | |
181 | |
182 local build = rockspec.build | |
183 | |
184 if not minimal_mode then | |
185 ok, err = apply_patches(rockspec) | |
186 if err then | |
187 return nil, err | |
188 end | |
189 end | |
190 | |
191 if build.type ~= "none" then | |
192 | |
193 -- Temporary compatibility | |
194 if build.type == "module" then | |
195 util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") | |
196 build.type = "builtin" | |
197 end | |
198 | |
199 local build_type | |
200 ok, build_type = pcall(require, "luarocks.build." .. build.type) | |
201 if not ok or not type(build_type) == "table" then | |
202 return nil, "Failed initializing build back-end for build type '"..build.type.."': "..build_type | |
203 end | |
204 | |
205 ok, err = build_type.run(rockspec) | |
206 if not ok then | |
207 return nil, "Build error: " .. err | |
208 end | |
209 end | |
210 | |
211 if build.install then | |
212 for id, install_dir in pairs(dirs) do | |
213 ok, err = install_files(build.install[id], install_dir.name, install_dir.is_module_path) | |
214 if not ok then | |
215 return nil, err | |
216 end | |
217 end | |
218 end | |
219 | |
220 local copy_directories = build.copy_directories or {"doc"} | |
221 | |
222 for _, copy_dir in pairs(copy_directories) do | |
223 if fs.is_dir(copy_dir) then | |
224 local dest = dir.path(path.install_dir(name, version), copy_dir) | |
225 fs.make_dir(dest) | |
226 fs.copy_contents(copy_dir, dest) | |
227 else | |
228 util.warning("Directory '"..copy_dir.."' not found") | |
229 end | |
230 end | |
231 | |
232 for _, d in pairs(dirs) do | |
233 fs.remove_dir_if_empty(d.name) | |
234 end | |
235 | |
236 fs.pop_dir() | |
237 | |
238 fs.copy(rockspec.local_filename, path.rockspec_file(name, version)) | |
239 if need_to_fetch then | |
240 fs.pop_dir() | |
241 end | |
242 | |
243 ok, err = manif.make_rock_manifest(name, version) | |
244 if err then return nil, err end | |
245 | |
246 ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec)) | |
247 if err then return nil, err end | |
248 | |
249 util.remove_scheduled_function(rollback) | |
250 rollback = util.schedule_function(function() | |
251 repos.delete_version(name, version) | |
252 end) | |
253 | |
254 ok, err = repos.run_hook(rockspec, "post_install") | |
255 if err then return nil, err end | |
256 | |
257 ok, err = manif.update_manifest(name, version, nil, deps_mode) | |
258 if err then return nil, err end | |
259 | |
260 local license = "" | |
261 if rockspec.description and rockspec.description.license then | |
262 license = ("(license: "..rockspec.description.license..")") | |
263 end | |
264 | |
265 local root_dir = path.root_dir(cfg.rocks_dir) | |
266 util.printout() | |
267 util.printout(name.." "..version.." is now built and installed in "..root_dir.." "..license) | |
268 | |
269 util.remove_scheduled_function(rollback) | |
270 return true | |
271 end | |
272 | |
273 --- Build and install a rock. | |
274 -- @param rock_file string: local or remote filename of a rock. | |
275 -- @param need_to_fetch boolean: true if sources need to be fetched, | |
276 -- false if the rockspec was obtained from inside a source rock. | |
277 -- @param deps_mode: string: Which trees to check dependencies for: | |
278 -- "none", "one", "order" or "all". | |
279 -- @return boolean or (nil, string, [string]): True if build was successful, | |
280 -- or false and an error message and an optional error code. | |
281 function build_rock(rock_file, need_to_fetch, deps_mode) | |
282 assert(type(rock_file) == "string") | |
283 assert(type(need_to_fetch) == "boolean") | |
284 | |
285 local unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file) | |
286 if not unpack_dir then | |
287 return nil, err, errcode | |
288 end | |
289 local rockspec_file = path.rockspec_name_from_rock(rock_file) | |
290 fs.change_dir(unpack_dir) | |
291 local ok, err, errcode = build_rockspec(rockspec_file, need_to_fetch, false, deps_mode) | |
292 fs.pop_dir() | |
293 return ok, err, errcode | |
294 end | |
295 | |
296 local function do_build(name, version, deps_mode) | |
297 if name:match("%.rockspec$") then | |
298 return build_rockspec(name, true, false, deps_mode) | |
299 elseif name:match("%.src%.rock$") then | |
300 return build_rock(name, false, deps_mode) | |
301 elseif name:match("%.all%.rock$") then | |
302 local install = require("luarocks.install") | |
303 return install.install_binary_rock(name, deps_mode) | |
304 elseif name:match("%.rock$") then | |
305 return build_rock(name, true, deps_mode) | |
306 elseif not name:match(dir.separator) then | |
307 local search = require("luarocks.search") | |
308 return search.act_on_src_or_rockspec(run, name:lower(), version, deps.deps_mode_to_flag(deps_mode)) | |
309 end | |
310 return nil, "Don't know what to do with "..name | |
311 end | |
312 | |
313 --- Driver function for "build" command. | |
314 -- @param name string: A local or remote rockspec or rock file. | |
315 -- If a package name is given, forwards the request to "search" and, | |
316 -- if returned a result, installs the matching rock. | |
317 -- @param version string: When passing a package name, a version number may | |
318 -- also be given. | |
319 -- @return boolean or (nil, string): True if build was successful; nil and an | |
320 -- error message otherwise. | |
321 function run(...) | |
322 local flags, name, version = util.parse_flags(...) | |
323 if type(name) ~= "string" then | |
324 return nil, "Argument missing, see help." | |
325 end | |
326 assert(type(version) == "string" or not version) | |
327 | |
328 if flags["pack-binary-rock"] then | |
329 return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags)) | |
330 else | |
331 local ok, err = fs.check_command_permissions(flags) | |
332 if not ok then return nil, err end | |
333 return do_build(name, version, deps.get_deps_mode(flags)) | |
334 end | |
335 end |