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.
@@ -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