Tamar 0.7.2 → 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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