Tamar 0.7.2 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY +19 -2
- data/Rakefile +5 -0
- data/Tamar.gemspec +27 -2
- data/VERSION +1 -1
- data/src/luadist/CMakeLists.txt +42 -0
- data/src/luadist/COPYRIGHT +34 -0
- data/src/luadist/README +95 -0
- data/src/luadist/dist.cmake +436 -0
- data/src/luadist/dist.info +20 -0
- data/src/luadist/dist/config.lua.in +108 -0
- data/src/luadist/dist/dep.lua +283 -0
- data/src/luadist/dist/fetch.lua +234 -0
- data/src/luadist/dist/init.lua +616 -0
- data/src/luadist/dist/log.lua +37 -0
- data/src/luadist/dist/manifest.lua +204 -0
- data/src/luadist/dist/package.lua +271 -0
- data/src/luadist/dist/persist.lua +132 -0
- data/src/luadist/dist/sys.lua +436 -0
- data/src/luadist/doc/index.html +147 -0
- data/src/luadist/doc/luadoc.css +286 -0
- data/src/luadist/doc/modules/dist.dep.html +489 -0
- data/src/luadist/doc/modules/dist.fetch.html +197 -0
- data/src/luadist/doc/modules/dist.html +547 -0
- data/src/luadist/doc/modules/dist.log.html +187 -0
- data/src/luadist/doc/modules/dist.manifest.html +235 -0
- data/src/luadist/doc/modules/dist.package.html +323 -0
- data/src/luadist/doc/modules/dist.persist.html +345 -0
- data/src/luadist/doc/modules/dist.sys.html +1058 -0
- data/src/luadist/luadist +443 -0
- metadata +27 -2
@@ -0,0 +1,616 @@
|
|
1
|
+
--- LuaDist Primary API functions
|
2
|
+
-- Peter Drahoš, Peter Kapec, LuaDist Project, 2010
|
3
|
+
|
4
|
+
--- This file contains the basic functions of LuaDist.
|
5
|
+
-- Feel free to use this exposed API in your projects if you need to deploy something automatically.
|
6
|
+
-- Please note that everything may change in future releases.
|
7
|
+
--- Terminology used:
|
8
|
+
-- _dist_ - General reference to "package" in LuaDist, often synonymous with _info_.
|
9
|
+
-- _info_ - Meta-data describing a _dist_. Collected from dist.info files inside dist archives.
|
10
|
+
-- _manifest_ - A collection of sorted _info_ entries in a table. Sorted alphabetically by name and by version descending.
|
11
|
+
-- _name_ - Refers to a string containing dist name and version constraints. Eg. "lua-5.1.4" or "luajit>=2"
|
12
|
+
-- _deployment_ - A directory containing dists installed using LuaDist.
|
13
|
+
|
14
|
+
module ("dist", package.seeall)
|
15
|
+
|
16
|
+
-- Include Submodules
|
17
|
+
local man = require "dist.manifest"
|
18
|
+
local pkg = require "dist.package"
|
19
|
+
local per = require "dist.persist"
|
20
|
+
local cfg = require "dist.config"
|
21
|
+
local sys = require "dist.sys"
|
22
|
+
local dep = require "dist.dep"
|
23
|
+
local log = require "dist.log"
|
24
|
+
|
25
|
+
--- Get contents of repository or a list of repositories.
|
26
|
+
-- _getManifest_ is used to fetch or generate collections of dist.info entries from
|
27
|
+
-- URIs, system paths and on-line repositories. Returned dist manifests are not
|
28
|
+
-- filtered and may not be appropriate for installation, see _filterManifest_ for this
|
29
|
+
-- purpose.
|
30
|
+
--
|
31
|
+
-- Dist information is gathered by collecting dist.info entries from dist.manifest
|
32
|
+
-- file in case an URL to on-line repository is given. System paths are searched
|
33
|
+
-- for dist.info files on demand and do extracted dists can be used for local
|
34
|
+
-- repositories, this is handy for bootstrap and development of multiple
|
35
|
+
-- interconnected dists. Zip archives and dists are peeked into on demand and in
|
36
|
+
-- case it contains dist.info entry its added to the manifest.
|
37
|
+
--
|
38
|
+
-- In case of multiple repositories, first repository always has priority. Dists are
|
39
|
+
-- ordered by repository, name and version. Every item is checked, malformed entries
|
40
|
+
-- are removed.
|
41
|
+
--
|
42
|
+
-- @param repositories string or table: Path or URL of a repository or repositories. By default cfg.repo is used if argument is not defined.
|
43
|
+
-- @return manifest: Table containing collections of available dists.
|
44
|
+
function getManifest(repositories)
|
45
|
+
-- If no repository is provided use default ones from configuration.
|
46
|
+
repositories = repositories or cfg.repo
|
47
|
+
-- Make sure the argument is always a table.
|
48
|
+
if type(repositories) == "string" then repositories = { repositories } end
|
49
|
+
-- Check if we can continue.
|
50
|
+
assert(type(repositories) == "table", "dist.getManifest: Argument 'repositories' is not a table or string.")
|
51
|
+
|
52
|
+
local manifest = {}
|
53
|
+
-- Collect all dists.info entries from each repo.
|
54
|
+
for i = 1, #repositories do
|
55
|
+
-- Append all dist.infos found.
|
56
|
+
local repo = man.get(repositories[i]) or {}
|
57
|
+
for j = 1, #repo do
|
58
|
+
table.insert(manifest, repo[j])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
return manifest
|
62
|
+
end
|
63
|
+
|
64
|
+
--- Get all installed dists from deployment directory.
|
65
|
+
-- _getInstalled_ will collect all installed dists inside a deployment directory.
|
66
|
+
-- These represent installed dists; provided dists are not included, see _getDeployed_
|
67
|
+
-- if you need a provided dists too.
|
68
|
+
--
|
69
|
+
-- @param deployment string: Path to deployment directory. If not specified the deployment LuaDist is running in will be used.
|
70
|
+
-- @return manifest: Table containing collections of installed dists.
|
71
|
+
function getInstalled(deployment)
|
72
|
+
-- Set up deployment directory if not set
|
73
|
+
deployment = sys.path(deployment or getDeployment())
|
74
|
+
assert(type(deployment) == "string", "dist.getInstalled: Argument 'deployment' is not a string.")
|
75
|
+
if not sys.isDir(sys.path(deployment, cfg.dists)) then
|
76
|
+
return {}
|
77
|
+
end
|
78
|
+
-- Collect dists in deployment using getManifest.
|
79
|
+
return getManifest(sys.path(deployment, cfg.dists)) or {}
|
80
|
+
end
|
81
|
+
|
82
|
+
--- Get all deployed dists in the target dir, this will also list dists provided by other dists.
|
83
|
+
-- _getDeployed_ will collect all deployed dists inside a deployment directory.
|
84
|
+
-- This inludes entries that simulate provided dists which act as dependency satisfaction for dists
|
85
|
+
-- during installation.
|
86
|
+
--
|
87
|
+
-- Integration with host package managers can be achieved by providing a list of modules already
|
88
|
+
-- installed directly to the host system. The list can be edited directly by the user in configuration.
|
89
|
+
--
|
90
|
+
-- @param deployment string: Path to deployment directory. If not specified the deployment LuaDist is running in will be used.
|
91
|
+
-- @return manifest: Table containing collections of deployed dists.
|
92
|
+
function getDeployed(deployment)
|
93
|
+
-- Set up deployment directory if not set
|
94
|
+
deployment = sys.path(deployment or getDeployment())
|
95
|
+
assert(type(deployment) == "string", "dist.getDeployed: Argument 'deployment' is not a string.")
|
96
|
+
-- Get installed dists
|
97
|
+
local list, err = getInstalled(deployment)
|
98
|
+
if not list then return nil, err end
|
99
|
+
|
100
|
+
-- Helpful dist.info generator from dists and cfg.provided
|
101
|
+
local function getProvidedDists(info)
|
102
|
+
if not info then
|
103
|
+
-- Fake host provides
|
104
|
+
info = { name = "Host", version = "config", provides = cfg.provides, arch = cfg.arch, type = cfg.type }
|
105
|
+
end
|
106
|
+
|
107
|
+
-- Generate dist.info for the provided dists
|
108
|
+
local manifest = {}
|
109
|
+
for _, provide in pairs(info.provides or {}) do
|
110
|
+
local dist = {}
|
111
|
+
dist.name, dist.version = dep.split(provide)
|
112
|
+
dist.arch = info.arch
|
113
|
+
dist.type = info.type
|
114
|
+
dist.provided = info.name .. "-" .. info.version
|
115
|
+
table.insert(manifest, dist)
|
116
|
+
end
|
117
|
+
return manifest
|
118
|
+
end
|
119
|
+
|
120
|
+
-- Insert provided entries collected from installed dists
|
121
|
+
local manifest = {}
|
122
|
+
for i = 1, #list do
|
123
|
+
local dist = list[i]
|
124
|
+
table.insert(manifest, dist)
|
125
|
+
local provided = getProvidedDists(dist)
|
126
|
+
for j = 1, #provided do
|
127
|
+
table.insert(manifest, provided[j])
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
-- Include list of provided dists from host (cfg.provided)
|
132
|
+
local provided = getProvidedDists()
|
133
|
+
for j = 1, #provided do
|
134
|
+
table.insert(manifest, provided[j])
|
135
|
+
end
|
136
|
+
|
137
|
+
return manifest
|
138
|
+
end
|
139
|
+
|
140
|
+
--- Filter dist manifests using constraint functions.
|
141
|
+
-- _filterManifest_ is used to remove dists from dist manifests that don't satisfy conditions.
|
142
|
+
-- Conditions are specified using a table of functions. Functions are named after the attribute
|
143
|
+
-- they check and return true if an argument satisfies its condition.
|
144
|
+
--
|
145
|
+
-- When constraint are not defined a default set of constraints is used. These constraints filter
|
146
|
+
-- out dists not suitable for the architecture LuaDist is running on. More specifically, only dists
|
147
|
+
-- of arch "Universal" or of arch equal to cfg.arch and of type "all" or type equal to cfg.type
|
148
|
+
-- are preserved. Additionally when source dists are enabled in configuration this will include dists
|
149
|
+
-- of type equal to "source".
|
150
|
+
--
|
151
|
+
-- In case no manifest is specified _filterManifest_ will automatically collect dist manifests from default
|
152
|
+
-- repositories using _getManifest_.
|
153
|
+
--
|
154
|
+
-- @param list table: Dist list or manifest to filter. By default all dists collected from cfg.repo will be filtered.
|
155
|
+
-- @param constraints table: Table of constraint function. Key determines what dist attribute is checked while the value is the actual constraint test written as a function. By default dists are filtered for correct arch-type.
|
156
|
+
-- @return manifest: Table containing collections of filtered dists.
|
157
|
+
function filterManifest(list, constraints)
|
158
|
+
-- By default fetch dists if not provided
|
159
|
+
list = list or getManifest() or {}
|
160
|
+
|
161
|
+
-- Default constraints filter arch and type of dists
|
162
|
+
constraints = constraints or {
|
163
|
+
-- Architecture constraints
|
164
|
+
arch = function(arch)
|
165
|
+
if not arch or arch == "Universal" then return true end
|
166
|
+
if cfg.binary and arch == cfg.arch then return true end
|
167
|
+
return false
|
168
|
+
end,
|
169
|
+
-- Type constraints
|
170
|
+
type = function(type)
|
171
|
+
if not type or type == "all" then return true end
|
172
|
+
if cfg.binary and type == cfg.type then return true end
|
173
|
+
if cfg.source and type == "source" then return true end
|
174
|
+
return false
|
175
|
+
end
|
176
|
+
}
|
177
|
+
|
178
|
+
-- Check if we can continue.
|
179
|
+
assert(type(list) == "table", "dist.filterManifest: Argument 'list' is not a table.")
|
180
|
+
assert(type(constraints) == "table", "dist.filterManifest: Argument 'constraints' is not a table.")
|
181
|
+
|
182
|
+
-- Matching dists
|
183
|
+
local manifest = {}
|
184
|
+
|
185
|
+
-- For each dist match constraints
|
186
|
+
for i = 1, #list do
|
187
|
+
local dist = list[i]
|
188
|
+
local match = true
|
189
|
+
-- Accumulate constraint checks
|
190
|
+
for key, constraint in pairs(constraints) do
|
191
|
+
match = match and constraint(dist[key])
|
192
|
+
end
|
193
|
+
-- If all went ok
|
194
|
+
if match then
|
195
|
+
table.insert(manifest, dist)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
return manifest
|
200
|
+
end
|
201
|
+
|
202
|
+
--- Find dists in manifest by name.
|
203
|
+
-- _findDists_ will find all dists in manifest that satisfy conditions in the name string.
|
204
|
+
-- Name contains dist name to search for and a set of optional version constraints. For example
|
205
|
+
-- searching for "lua-5.1.4" will find all dists of name "lua" and version "5.1.4". Searching for version
|
206
|
+
-- ranges is done using sets of constraints eg. "luajit >= 2.0 < 3.0". For more information, limitations
|
207
|
+
-- and use of the constraint system please see LuaDist documentation.
|
208
|
+
--
|
209
|
+
-- @param name string: String specifying the dist to find, version constraints can be used. Case sensitive.
|
210
|
+
-- @param manifest string: Dist manifest to search through.
|
211
|
+
-- @return dist, log: List of matching dists. Returns nil on error and log message.
|
212
|
+
function findDists(names, manifest)
|
213
|
+
-- We can handle name lists and mane sring directly
|
214
|
+
if type(names) == "string" then names = { names } end
|
215
|
+
|
216
|
+
-- If manifest is missing we get it from configured repository
|
217
|
+
manifest = manifest or filterManifest() or {}
|
218
|
+
|
219
|
+
-- Check if we can continue
|
220
|
+
assert(type(names) == "table", "dist.findDists: Argument 'names' is not a table or string.")
|
221
|
+
assert(type(manifest) == "table", "dist.findDists: Argument 'manifest' is not a table.")
|
222
|
+
|
223
|
+
if #names == 0 then return manifest end
|
224
|
+
|
225
|
+
local dists = {}
|
226
|
+
for i = 1, #names do
|
227
|
+
local name = names[i]
|
228
|
+
|
229
|
+
-- Split name to dist-name and constraints
|
230
|
+
local name, constraints = dep.split(name)
|
231
|
+
-- Escape special chars in name
|
232
|
+
local match = "^" .. name:gsub("%.", "%%."):gsub("%-", "%%-"):gsub("%_", "%%_"):gsub("%*", ".*") .. "$"
|
233
|
+
-- Filter manifest using custom constraints
|
234
|
+
local list = filterManifest(manifest, {
|
235
|
+
name = function(distName)
|
236
|
+
return string.match(distName, match)
|
237
|
+
end,
|
238
|
+
version = function(distVer)
|
239
|
+
return dep.constrain(distVer, constraints or "")
|
240
|
+
end
|
241
|
+
})
|
242
|
+
for j = 1, #list do
|
243
|
+
table.insert(dists, list[j])
|
244
|
+
end
|
245
|
+
end
|
246
|
+
return dists
|
247
|
+
end
|
248
|
+
|
249
|
+
--- Small help function to get relevant names from info
|
250
|
+
local function getInfoNames(tbl)
|
251
|
+
local ret = {}
|
252
|
+
tbl = tbl or {}
|
253
|
+
for k,v in pairs(tbl) do if (type(v)=="string") then table.insert(ret, v) end end
|
254
|
+
for k,v in pairs(tbl[cfg.arch] or {}) do if (type(v)=="string") then table.insert(ret, v) end end
|
255
|
+
for k,v in pairs(tbl[cfg.type] or {}) do if (type(v)=="string") then table.insert(ret, v) end end
|
256
|
+
return ret
|
257
|
+
end
|
258
|
+
|
259
|
+
--- Collect dependencies.
|
260
|
+
-- _getDeps_ is the magic function where dependency resolving happens.
|
261
|
+
-- This function handles multiple names for which it computes the best possible set of dists that satisfy
|
262
|
+
-- all the names. It consideres provided dists, conflicts and dependencies of each candidate dist and avoids
|
263
|
+
-- dependency loops. It may not be very elegant or efficient, contributions welcome.
|
264
|
+
--
|
265
|
+
-- The algorithm relies on sequential satisfaction of names. For each name it tries to determine
|
266
|
+
-- best possible candidate and tries to install its dependencies and rest of the names list.
|
267
|
+
-- If dependencies fail another candidate is checked untill all names check out or candidates run out.
|
268
|
+
-- Provides are faked by injecting dists into manifest and replaced before return.
|
269
|
+
--
|
270
|
+
-- @param names string or table: Names to compute dependencies for.
|
271
|
+
-- @param manifest table: Manifest of dists to select dependencies from, needs to be sorted.
|
272
|
+
function getDeps(names, manifest)
|
273
|
+
manifest = manifest or filterDists()
|
274
|
+
if type(names) == "string" then names = { names } end
|
275
|
+
|
276
|
+
-- Type Checks
|
277
|
+
assert(type(names) == "table", "dist.getDeps: Argument 'names' is not a table or string.")
|
278
|
+
assert(type(manifest)=="table", "dist.getDeps: Argument 'manifest' is not a table.")
|
279
|
+
|
280
|
+
-- Check out first from the list and find its candidates
|
281
|
+
local name = names[1]
|
282
|
+
local candidates = findDists(name, manifest)
|
283
|
+
|
284
|
+
-- For each candidate we check its suitability
|
285
|
+
for i = 1, #candidates do
|
286
|
+
local info = candidates[i]
|
287
|
+
local infoName = info.name .. "-" .. info.version
|
288
|
+
|
289
|
+
-- Add candidate dependencies to names
|
290
|
+
-- Notice deps will be used according to arch and type if they are available
|
291
|
+
local newNames = {}
|
292
|
+
for _, entry in pairs(getInfoNames(info.depends)) do
|
293
|
+
table.insert(newNames, entry)
|
294
|
+
end
|
295
|
+
for j = 2, #names do
|
296
|
+
table.insert(newNames, names[j])
|
297
|
+
end
|
298
|
+
|
299
|
+
-- Make new manifest with provided dists
|
300
|
+
local newManifest = {}
|
301
|
+
for _, entry in pairs(getInfoNames(info.provides)) do
|
302
|
+
-- Construct a fake provided dist pointing to the provider
|
303
|
+
local dist = {}
|
304
|
+
dist.name, dist.version = dep.split(entry)
|
305
|
+
dist.arch = info.arch
|
306
|
+
dist.type = info.type
|
307
|
+
dist.provided = info
|
308
|
+
table.insert(newManifest, dist)
|
309
|
+
end
|
310
|
+
for j = 1, #manifest do
|
311
|
+
table.insert(newManifest, manifest[j])
|
312
|
+
end
|
313
|
+
|
314
|
+
-- Check dependencies first
|
315
|
+
local dependencies = {}
|
316
|
+
if #newNames > 0 then
|
317
|
+
dependencies, err = getDeps(newNames, newManifest)
|
318
|
+
if not dependencies then
|
319
|
+
return nil, err
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
-- If provided we skip tests as the providing dist will make sure of it
|
324
|
+
if info.provided then
|
325
|
+
if type(info.provided) == "table" then
|
326
|
+
table.insert(dependencies, info.provided)
|
327
|
+
end
|
328
|
+
return dependencies
|
329
|
+
end
|
330
|
+
|
331
|
+
-- Skip rest if candidate is in deps
|
332
|
+
for _, prov in pairs(dependencies) do
|
333
|
+
local provName = prov.name .. "-" .. prov.version
|
334
|
+
|
335
|
+
-- Check if already provided
|
336
|
+
if info.name == prov.name then
|
337
|
+
if info.version == prov.version then
|
338
|
+
-- If provided by a dist checking its deps.
|
339
|
+
return dependencies
|
340
|
+
else
|
341
|
+
-- Different version
|
342
|
+
return nil, infoName .. " is blocked by " .. provName
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
-- Check dependency constraints
|
347
|
+
for _, entry in pairs(getInfoNames(prov.depends)) do
|
348
|
+
local name, const = dep.split(entry)
|
349
|
+
if info.name == name and (not const or not dep.constrain(info.version, const)) then
|
350
|
+
return nil, infoName .. " is blocked by " .. provName .. " dependency " .. entry
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
-- Indirect conflicts
|
355
|
+
for _, entry in pairs(getInfoNames(prov.conflicts)) do
|
356
|
+
local name, const = dep.split(entry)
|
357
|
+
if info.name == name and (not const or not dep.constrain(info.version, const)) then
|
358
|
+
return nil, infoName .. " is blocked by " .. provName .. " conflict " .. entry
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
-- Direct conflicts
|
363
|
+
for _, entry in pairs(getInfoNames(info.conflicts)) do
|
364
|
+
local name, const = dep.split(entry)
|
365
|
+
if prov.name == name and (not const or not dep.constrain(prov.version, const)) then
|
366
|
+
return nil, infoName .. " is in conflict with " .. provName .. " conflict " .. entry
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
--- Candidate matches, add it to result
|
372
|
+
table.insert(dependencies, info)
|
373
|
+
return dependencies
|
374
|
+
end
|
375
|
+
return nil, "No suitable dists in repository for " .. name
|
376
|
+
end
|
377
|
+
|
378
|
+
--- Install dist by name.
|
379
|
+
-- _install_ is capable to install dists from various sources and of both source and binary type.
|
380
|
+
-- Sources can be accumulated into install lists in which case LuaDist will install the sources in sequence.
|
381
|
+
-- Sources supported by the command currently include: name, path and URL to .dist and .zip, path to unpacked dist
|
382
|
+
-- and dist.info table.
|
383
|
+
--
|
384
|
+
-- @param name string: Package to install identified by name which can include version constraints. Case sensitive.
|
385
|
+
-- @param deployment string: Deployment directory to install into. Leave undefined for current deployment directory.
|
386
|
+
-- @param manifest table: Manifest of dists used to search for sources and resolve dependencies. Leave undefined for default repositories.
|
387
|
+
-- @param variables table: Table of key value pairs that can be used to set additional CMake variables for source dists.
|
388
|
+
-- @return ok, log: Returns true on success. nil on error and log message.
|
389
|
+
function install(names, deployment, manifest, variables)
|
390
|
+
-- Make sure deployment path is absolute and set
|
391
|
+
deployment = sys.path(deployment or getDeployment())
|
392
|
+
-- Make sure we have a manifest to install dists from
|
393
|
+
manifest = manifest or filterManifest()
|
394
|
+
-- Default variables can be omitted so make sure we have a table
|
395
|
+
variables = variables or {}
|
396
|
+
-- Make sure names are always in a list
|
397
|
+
if type(names) == "string" then names = { names } end
|
398
|
+
|
399
|
+
-- Check types
|
400
|
+
assert(type(names) == "table", "dist.install: Argument 'name' is not a string or table.")
|
401
|
+
assert(type(deployment)=="string", "dist.install: Argument 'deployment' is not a string.")
|
402
|
+
assert(type(manifest)=="table", "dist.install: Argument 'manifest' is not a table.")
|
403
|
+
assert(type(variables)=="table", "dist.install: Argument 'variables' is not a table.")
|
404
|
+
|
405
|
+
log.message("Computing dependencies.")
|
406
|
+
|
407
|
+
-- Create custom manifest starting with deployed dists
|
408
|
+
local manif = {}
|
409
|
+
for _, depl in pairs(getDeployed(deployment) or {}) do
|
410
|
+
table.insert(manif, depl)
|
411
|
+
end
|
412
|
+
for j = 1, #manifest do
|
413
|
+
local dist = manifest[j]
|
414
|
+
table.insert(manif, dist)
|
415
|
+
end
|
416
|
+
|
417
|
+
-- Compute dependencies
|
418
|
+
local deps, err = getDeps(names, manif)
|
419
|
+
if not deps then return nil, err end
|
420
|
+
|
421
|
+
-- Fetch and deploy
|
422
|
+
for i = 1, #deps do
|
423
|
+
local ok, err = deploy(deps[i].path, deployment, variables)
|
424
|
+
if not ok then return nil, err end
|
425
|
+
end
|
426
|
+
return true, "Install Successful."
|
427
|
+
end
|
428
|
+
|
429
|
+
--- Deploy a dist directly from source.
|
430
|
+
-- _deploy_ will install; and in case of source dists, build a dist from source path.
|
431
|
+
-- Deployment will automatically fetch from remote URLs and unpack dist or zip files into deploymemt.
|
432
|
+
-- Variables can contain additional CMake variables to be used when building source dists.
|
433
|
+
--
|
434
|
+
-- @param src string: Path to the dist directory to make/deploy or nil to use current directory.
|
435
|
+
-- @param deployment string: Deployment directory to install the dist into. If nil LuaDist directory is used.
|
436
|
+
-- @param variables table: Table of key value pairs that can be used to set additional CMake variables for source dists.
|
437
|
+
-- @return ok, log: Returns true on success. nil on error and log message.
|
438
|
+
function deploy(src, deployment, variables)
|
439
|
+
-- Make sure deployment path is absolute and set
|
440
|
+
deployment = sys.path(deployment or getDeployment())
|
441
|
+
|
442
|
+
-- Make sure deployment exists and create startup script
|
443
|
+
sys.makeDir(deployment)
|
444
|
+
|
445
|
+
-- Default variables can be omited so make sure we have a table
|
446
|
+
variables = variables or {}
|
447
|
+
|
448
|
+
-- Handle each source individually if a list is passed
|
449
|
+
if type(src) == "table" then
|
450
|
+
for i = 1, #src do
|
451
|
+
local ok, err = deploy(src[i], deployment, variables)
|
452
|
+
if not ok then return nil, err end
|
453
|
+
end
|
454
|
+
return true, "Install Successful"
|
455
|
+
end
|
456
|
+
|
457
|
+
-- By default build in current directory
|
458
|
+
src = sys.path(src or sys.curDir())
|
459
|
+
|
460
|
+
assert(type(src)=="string", "dist.deploy: Argument 'src' is not a string.")
|
461
|
+
assert(type(deployment)=="string", "dist.deploy: Argument 'deployment' is not a string.")
|
462
|
+
assert(type(variables)=="table", "dist.deploy: Argument 'variables' is not a table.")
|
463
|
+
|
464
|
+
-- Fetch if needed
|
465
|
+
local tmp, err
|
466
|
+
if not sys.isDir(src) then
|
467
|
+
tmp, err = package.unpack(src)
|
468
|
+
if not tmp then
|
469
|
+
log.message("Could not fetch following dist: " .. src .. " (" .. err .. ")")
|
470
|
+
return false, "Could not fetch following dist: " .. src .. " (" .. err .. ")"
|
471
|
+
end
|
472
|
+
|
473
|
+
-- Use the 1st subdirectory of the tmp
|
474
|
+
src = sys.path(tmp, sys.dir(tmp)[1])
|
475
|
+
end
|
476
|
+
|
477
|
+
-- Check dist info
|
478
|
+
local info, err = persist.load(sys.path(src, "dist.info"), variables)
|
479
|
+
if not info then return nil, "Dist does not contain valid dist.info in " .. src end
|
480
|
+
|
481
|
+
-- We have a dist
|
482
|
+
local infoName = info.name .. "-" .. info.version
|
483
|
+
log.message("Processing " .. infoName)
|
484
|
+
|
485
|
+
-- Check the package contents for CMakeLists.txt, that would mean the package is of type source.
|
486
|
+
if sys.exists(sys.path(src, "CMakeLists.txt")) then
|
487
|
+
info.arch = info.arch or "Universal"
|
488
|
+
info.type = info.type or "source"
|
489
|
+
end
|
490
|
+
|
491
|
+
-- Check the architecture if its suitable for deployment.
|
492
|
+
if info.arch ~= cfg.arch and info.arch ~= "Universal" then
|
493
|
+
return nil, "Dist is of incompatible architecture " .. (info.arch or "NOT-SET") .. "-" .. (info.type or "NOT-SET")
|
494
|
+
end
|
495
|
+
|
496
|
+
-- Check the type if its suitable for deployment
|
497
|
+
if info.type ~= cfg.type and info.type ~= "all" and info.type ~= "source" then
|
498
|
+
return nil, "Dist is of incompatible architecture type " .. info.arch .. "-" .. info.type
|
499
|
+
end
|
500
|
+
|
501
|
+
-- If the dist is not source we simply deploy it.
|
502
|
+
if info.type ~= "source" then
|
503
|
+
local ok, err = package.deploy(src, deployment)
|
504
|
+
|
505
|
+
-- Cleanup
|
506
|
+
if not config.debug and tmp then sys.delete(tmp) end
|
507
|
+
|
508
|
+
if not ok then return nil, "Failed to deploy package " .. src .. " to " .. deployment .. "." .. err end
|
509
|
+
|
510
|
+
-- Display message
|
511
|
+
if info.message then
|
512
|
+
log.message(info.message)
|
513
|
+
end
|
514
|
+
|
515
|
+
return true, "Deployment Successful."
|
516
|
+
end
|
517
|
+
|
518
|
+
--- We have a source dist.
|
519
|
+
log.message("Building ...")
|
520
|
+
-- Setup variables by mergeing cfg.variables and variables
|
521
|
+
local vars = {}
|
522
|
+
for k,v in pairs(cfg.variables or {}) do
|
523
|
+
vars[k] = v
|
524
|
+
end
|
525
|
+
for k, v in pairs(variables or {}) do
|
526
|
+
vars[k] = v
|
527
|
+
end
|
528
|
+
|
529
|
+
-- Set CMake search paths for libs and headers to the deployment directory
|
530
|
+
vars.CMAKE_INCLUDE_PATH = table.concat({ sys.path(vars.CMAKE_INCLUDE_PATH) or "", sys.path(deployment, "include")}, ";")
|
531
|
+
vars.CMAKE_LIBRARY_PATH = table.concat({ sys.path(vars.CMAKE_LIBRARY_PATH) or "", sys.path(deployment, "lib"), sys.path(deployment, "bin")}, ";")
|
532
|
+
vars.CMAKE_PROGRAM_PATH = table.concat({ sys.path(vars.CMAKE_PROGRAM_PATH) or "", sys.path(deployment, "bin")}, ";")
|
533
|
+
|
534
|
+
-- Build and deploy the package
|
535
|
+
local bin, err = package.build(src, deployment, vars)
|
536
|
+
|
537
|
+
-- Cleanup
|
538
|
+
if not config.debug and tmp then sys.delete(tmp) end
|
539
|
+
|
540
|
+
if not bin then return nil, "Failed to build dist " .. infoName .. " error: " .. err end
|
541
|
+
|
542
|
+
-- Display message
|
543
|
+
if info.message then
|
544
|
+
log.message(info.message)
|
545
|
+
end
|
546
|
+
|
547
|
+
return true, "Deployment Successful."
|
548
|
+
end
|
549
|
+
|
550
|
+
--- Remove a deployed dist.
|
551
|
+
-- _remove_ will delete specified dist from deployment.
|
552
|
+
-- When no name is specified, the whole deployment directory will be removed.
|
553
|
+
-- WARNING: Calling "luadist remove" will instruct LuaDist to commit suicide.
|
554
|
+
--
|
555
|
+
-- @param dist string or table: Dist name or info to uninstall.
|
556
|
+
-- @param deployment string: Deployment directory to uninstall from, nil for default LuaDist directory.
|
557
|
+
-- @return ok, log: Returns true on success. nil on error and log message.
|
558
|
+
function remove(names, deployment)
|
559
|
+
-- Make sure deployment path is absolute and set
|
560
|
+
deployment = sys.path(deployment or getDeployment())
|
561
|
+
|
562
|
+
-- Make sure names are always in a table
|
563
|
+
if type(names) == "string" then names = {names} end
|
564
|
+
|
565
|
+
-- Check types
|
566
|
+
assert(type(names) == "table" , "dist.remove: Argument 'names' is not a string or table.")
|
567
|
+
assert(type(deployment) == "string", "dist.remove: Argument 'deployment' is not a string.")
|
568
|
+
|
569
|
+
-- Find dists
|
570
|
+
local dists = findDists(names, getInstalled(deployment))
|
571
|
+
if not dists then return nil, "Nothing to delete." end
|
572
|
+
|
573
|
+
-- Delete the dists
|
574
|
+
for _, dist in pairs(dists) do
|
575
|
+
local ok = package.delete(dist.path, deployment)
|
576
|
+
if not ok then return nil, "Could not remove dist " .. dist.name .. "-" .. dist.version end
|
577
|
+
end
|
578
|
+
return true, "Remove Successful."
|
579
|
+
end
|
580
|
+
|
581
|
+
--- Pack a deployed dist.
|
582
|
+
-- _pack_ will pack specified dist from deployment deployment and generate .dist archives in dest.
|
583
|
+
-- When no name is specified all dists will be packed.
|
584
|
+
--
|
585
|
+
-- @param dist string or table: Dist name or info to uninstall.
|
586
|
+
-- @param deployment string: Deployment directory to uninstall from, nil for default LuaDist directory.
|
587
|
+
-- @param dest string: Optional destination for the result
|
588
|
+
-- @return ok, log: Returns true on success. nil on error and log message.
|
589
|
+
function pack(names, deployment, dest)
|
590
|
+
-- Make sure deployment path is absolute and set
|
591
|
+
deployment = sys.path(deployment or getDeployment())
|
592
|
+
|
593
|
+
-- Make sure names are always in a table
|
594
|
+
if type(names) == "string" then names = {names} end
|
595
|
+
|
596
|
+
-- Check types
|
597
|
+
assert(type(names) == "table" , "dist.pack: Argument 'names' is not a string or table.")
|
598
|
+
assert(type(deployment) == "string", "dist.pack: Argument 'deployment' is not a string.")
|
599
|
+
|
600
|
+
-- Find dists
|
601
|
+
local dists = findDists(names, getInstalled(deployment))
|
602
|
+
if not dists then return nil, "Nothing to pack." end
|
603
|
+
|
604
|
+
-- Pack the dists
|
605
|
+
for _, dist in pairs(dists) do
|
606
|
+
local ok, err = package.pack(dist.path, deployment, dest)
|
607
|
+
if not ok then return nil, "Failed to pack dist " .. dist.name .. "-" .. dist.version end
|
608
|
+
end
|
609
|
+
return true, "Pack Successful."
|
610
|
+
end
|
611
|
+
|
612
|
+
--- Get deployment directory.
|
613
|
+
-- @return path: Full path to the LuaDist install directory
|
614
|
+
function getDeployment()
|
615
|
+
return cfg.root
|
616
|
+
end
|