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