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,37 @@
1
+ --- LuaDist simple message logger
2
+ -- Peter Drahoš, Peter Kapec, LuaDist Project, 2010
3
+
4
+ --- Very simple log system.
5
+ -- 2DO: change to LuaLogging on next version.
6
+ -- write - write a log line
7
+ -- message - write and optionally display a message
8
+
9
+ module ("dist.log", package.seeall)
10
+
11
+ local lfs = require "lfs"
12
+ local config = require "dist.config"
13
+
14
+ -- Profile to store info in
15
+ lfs.mkdir(config.temp)
16
+ local log = assert(io.open(config.log, "a"), "Could not create log file!")
17
+
18
+ --- Display and log a message
19
+ function message(...)
20
+ if config.message then config.message(...) end
21
+ return write(...)
22
+ end
23
+
24
+ --- Write a line to log
25
+ function write(...)
26
+
27
+ local args = ...
28
+ if type(...) == "string" then args = { ... } end
29
+ if type(args) ~= "table" then return nil end
30
+
31
+ log:write(os.date("%c", os.time()) .. ":: ")
32
+ for i = 1, #args do
33
+ log:write(tostring(args[i]) .. " ")
34
+ end
35
+ log:write("\n")
36
+ log:flush()
37
+ end
@@ -0,0 +1,204 @@
1
+ --- LuaDist manifest specific functions including checks
2
+ -- Peter Drahoš, Peter Kapec, LuaDist Project, 2010
3
+
4
+ --- The purpose of this module is to check and collect dist manifests.
5
+ -- There are three functions provided:
6
+ -- make - Generates manifest for local directory
7
+ -- get - Gets sorted manifest from URI, this uses make for local paths. For remote directories it requires dist.manifest file to be present.
8
+ -- info - Loads and/or corrects and checks dist.info contents
9
+ -- Dists and manifests are simply arrays of collected dist.info files.
10
+
11
+ module ("dist.manifest", package.seeall)
12
+
13
+ local log = require "dist.log"
14
+ local fetch = require "dist.fetch"
15
+ local persist = require "dist.persist"
16
+ local sys = require "dist.sys"
17
+ local config = require "dist.config"
18
+ local dep = require "dist.dep"
19
+
20
+ local function couldBeDistFile(path)
21
+ return (path:match"%.dist$" or path:match"%.zip$") and
22
+ sys.isFile(path)
23
+ end
24
+
25
+ --- Collect and sort dists in a directory.
26
+ -- @param dir string: Directory to get manifest from.
27
+ -- @return dists, log: Dists in the directory and log message.
28
+ function make(dir)
29
+ assert(type(dir) == "string", "manifest.make: argument 'dir' not a string.")
30
+
31
+ -- Collection
32
+ local dists
33
+
34
+ for id, file in pairs(sys.dir(dir) or {}) do
35
+ local path = sys.path(dir, file)
36
+ -- Try to load dist.info in case the file is expanded dist directory, then try loading it from the file using unzip
37
+ local dist =
38
+ persist.load(sys.path(path, "dist.info")) or
39
+ couldBeDistFile(path) and
40
+ persist.loadText(sys.getZipFile(path, "*/dist.info") or "fail")
41
+ -- note: couldBeDistFile test avoids calling unnecessarily calling
42
+ -- getZipFile, which requires io.popen and unzip, which may be
43
+ -- unavailable in the bootstrapping Lua.
44
+
45
+ -- We have identified a dist
46
+ if dist then
47
+ -- Test it
48
+ local dist, err = info(dist)
49
+ if dist then
50
+ -- Collect the dist
51
+ dists = dists or {}
52
+ dist.path = file
53
+ table.insert(dists, dist)
54
+ else
55
+ -- Log warnings
56
+ log.message("Warning, skipped malformed dist.info for file: ", file, "err:", err)
57
+ end
58
+ -- Recursively traverse subdirectory
59
+ elseif sys.isDir(path) then
60
+ local ret = make(path)
61
+ for _,v in pairs(ret or {}) do
62
+ dists = dists or {}
63
+ v.path = file .. "/" .. v.path
64
+ table.insert(dists, v)
65
+ end
66
+ end
67
+ end
68
+ if not dists then return nil, "No dists found in directory " .. dir end
69
+ return dists, "Generated manifest for directory" .. dir
70
+ end
71
+
72
+ --- Get or generate repository contents from URI.
73
+ -- @param url string: URL to load the dist.manifest from. In case file:// is used generate the manifest.
74
+ -- @return dists, log: Returns true on success and nil and error message on failure.
75
+ function get(src, variables)
76
+ assert(type(src) == "string", "manifest:get, argument 'url' is not a string." )
77
+
78
+ -- If the src is local then make the manifest
79
+ local dir = src:gsub("^file://","")
80
+ local dists
81
+
82
+ -- If src points to a unpacked dist
83
+ if sys.isFile(sys.path(src, "dist.info")) then
84
+ local dist = info(sys.path(src, "dist.info"))
85
+ if dist then dists = { dist } end
86
+ -- If src points to a dir
87
+ elseif sys.isDir(dir) then
88
+ dists = make(dir)
89
+ -- Try collecting from manifest, assuming non-local reporitory
90
+ else
91
+ local manifest = fetch.get(sys.path(src, "dist.manifest"))
92
+ if not manifest then return nil, "Could not obtain manifest for " .. src end
93
+ dists = persist.loadText(manifest)
94
+ end
95
+
96
+ -- Sort by name and version
97
+ if not dists then return nil, "No suitable dists found in " .. src end
98
+
99
+ -- Check every dist
100
+ local checked
101
+ for i = 1, #dists do
102
+ local dist, err = info(dists[i])
103
+ if dist then
104
+ -- Dist is ok
105
+ checked = checked or {}
106
+ if not string.match(dist.path, "^http://") then
107
+ dist.path = sys.path(src:gsub("file://",""), dist.path)
108
+ end
109
+ table.insert(checked, dist)
110
+ else
111
+ -- Malformed dist
112
+ local name = tostring(dist.name) .. " " .. tostring(dist.version)
113
+ log.message("Warning, skipped malformed dist.info (" .. name .. ") from:", src, "err:", err)
114
+ end
115
+ end
116
+ if not checked then return nil, "No suitable dists found in " .. src end
117
+
118
+ table.sort(checked, function(a, b)
119
+ -- If names match
120
+ if a.name == b.name then
121
+ -- When versions match
122
+ if a.version == b.version then
123
+ -- Prefer Universal arch
124
+ if a.arch == b.arch then
125
+ -- Prefer source arch
126
+ if a.type == "source" then return false end
127
+ end
128
+ if a.arch == "Universal" then return false end
129
+ end
130
+ return dep.compareVersions(a.version ,b.version)
131
+ end
132
+ return a.name < b.name
133
+ end)
134
+ return checked, "Succesfuly obtained dists from " .. src
135
+ end
136
+
137
+ --- Check names in table, used in info checks.
138
+ -- @param table: Table to check names in.
139
+ -- @return ok, err: Returns true if everything is ok. In case of error nil and the malformed entry is returned.
140
+ local function checkNames(table)
141
+ for k, v in pairs(table) do
142
+ if type(v) == "string" then
143
+ local name, const = dep.split(v)
144
+ if not name then return nil, v end
145
+ elseif type(v) == "table" then
146
+ return checkNames(v)
147
+ else
148
+ return nil, "unknown entry"
149
+ end
150
+ end
151
+ return true
152
+ end
153
+
154
+ --- Load and check info from file or check info table
155
+ -- @param dist string or table: Path to dist.info or info table to check.
156
+ -- @return info, log: Table containing loaded and checked info or nil and log message.
157
+ function info(dist)
158
+ assert(type(dist) == "string" or type(dist) == "table", "manifest:info, argument 'dist' is not a string or table." )
159
+
160
+ -- Load the info if needed from a file
161
+ if type(dist) == "string" then
162
+ dist = persist.load(dist)
163
+ if not dist then return nil, "manifest.info: Failed loading dist.info from", dist end
164
+ return info(dist), "manifest.info: Succesfully loaded dist.info from", dist
165
+ end
166
+
167
+ -- Add arch-type if not present
168
+ dist.arch = dist.arch or "Universal"
169
+ dist.type = dist.type or "source"
170
+
171
+
172
+ -- Check the info entries
173
+ if type(dist.name) ~= "string" then return nil, "Info does not contain valid name." end
174
+ if not dist.name:match("[%l%d%.%:%_%-]+") then return nil, "Info info entry 'name' contains invalid characters" end
175
+ if type(dist.version) ~= "string" then return nil, "Info does not contain valid version." end
176
+ if not dist.version:match("[%l%d%.%:%_%-]+") then return nil, "Info entry 'version' contains invalid characters" end
177
+ if type(dist.arch) ~= "string" then return nil, "Info does not contain valid architecture." end
178
+ if not dist.arch:match("[%a%d]") then return nil, "Info entry 'arch' contains invalid characters." end
179
+ if type(dist.type) ~= "string" then return nil, "Info does not contain valid type." end
180
+ if not dist.type:match("[%a%d]") then return nil, "Info entry 'type' contains invalid characters." end
181
+
182
+ -- Optional
183
+ if dist.desc and type(dist.desc) ~= "string" then return nil, "Info does not contain valid description." end
184
+ if dist.author and type(dist.author) ~= "string" then return nil, "Info does not contain valid author." end
185
+ if dist.maintainer and type(dist.maintainer) ~= "string" then return nil, "Info does not contain valid maintainer." end
186
+ if dist.url and type(dist.url) ~= "string" then return nil, "Info does not contain valid url." end
187
+ if dist.license and type(dist.license) ~= "string" then return nil, "Info does not contain valid license." end
188
+ if dist.depends and type(dist.depends) ~= "table" then return nil, "Info does not contain valid dependencies." end
189
+
190
+ -- Check dependency format, swap for arch type specific id needed
191
+ local ok, err = checkNames(dist.depends or {})
192
+ if not ok then return nil, "Dependencies contain malformed entry: " .. err end
193
+
194
+ -- Same for conflicts
195
+ local ok, err = checkNames(dist.conflicts or {})
196
+ if not ok then return nil, "Conflicts contain malformed entry: " .. err end
197
+
198
+ -- And provides
199
+ local ok, err = checkNames(dist.provides or {})
200
+ if not ok then return nil, "Provides contain malformed entry: " .. err end
201
+
202
+ -- Return, no log since it would spam the log file alot
203
+ return dist, "Contents of dist is valid."
204
+ end
@@ -0,0 +1,271 @@
1
+ --- LuaDist package specific functions
2
+ -- Peter Drahoš, Peter Kapec, LuaDist Project, 2010
3
+
4
+ --- Package handling functions.
5
+ -- This module deals with packages, these are unpacked dists in LuaDist terminology
6
+ -- The following functions are provided:
7
+ -- unpack - fetch and unpack a dist
8
+ -- build - compile a source dist
9
+ -- deploy - install a dist into deployment
10
+ -- pack - create dist from package
11
+ -- delete - delete package
12
+
13
+ module ("dist.package", package.seeall)
14
+
15
+ local config = require "dist.config"
16
+ local fetch = require "dist.fetch"
17
+ local persist = require "dist.persist"
18
+ local sys = require "dist.sys"
19
+ local manif = require "dist.manifest"
20
+ local log = require "dist.log"
21
+
22
+ --- Fetch and unpack zip/dist from URL into dest directory.
23
+ -- @param url string: Local packed dist or URL to fetch dist from.
24
+ -- @param dest string: Destination to unpack contents into, nil for temp directory.
25
+ -- @return path, log: Unpacked dist path or nil and log.
26
+ function unpack(url, dest)
27
+ -- Make sure dest is set up properly
28
+ dest = sys.path(dest) or config.temp
29
+
30
+ -- Check types
31
+ assert(type(url)=="string", "package.unpack: Argument 'url' is not a string.")
32
+ assert(type(dest)=="string", "package.unpack: Argument 'dest' is not a string.")
33
+
34
+ -- Setup destination
35
+ local name = url:match("([^/]+)%.[^%.]+$")
36
+ if not name then return nil, "Could not determine dist name from " .. url end
37
+ local pkg = sys.path(dest, name)
38
+ local dist = pkg .. ".dist"
39
+
40
+ -- If the files already exist
41
+ if sys.exists(pkg) then return pkg, "Skipped unpack, destination " .. dest .. " exists." end
42
+
43
+ -- Download if needed
44
+ if not sys.exists(dist) then
45
+ -- Download from URL or local path. Fetch handles this
46
+ dist = fetch.download(url)
47
+ if not dist then return nil, "Failed to download " .. url end
48
+ end
49
+
50
+ -- Unzip
51
+ local ok = sys.unzip(dist, pkg)
52
+ if not ok then return nil, "Failed to unzip " .. dist .. " to " .. pkg end
53
+
54
+ -- Cleanup
55
+ if not config.debug then sys.delete(dist) end
56
+
57
+ return pkg, "Unpacked " .. url .. " to " .. pkg
58
+ end
59
+
60
+ --- Build, deploy and test a source dist using CMake.
61
+ -- @param dist string: Directory of the source dist to build.
62
+ -- @param variables: Table containing optional CMake parameters.
63
+ -- @return path, log: Returns temporary directory the dist was build into and log message.
64
+ function build(dist, depl, variables)
65
+ -- Make sure deployment is always set up
66
+ depl = sys.path(depl) or config.root
67
+
68
+ assert(type(dist)=="string", "package.build: Arument 'dist' is not a string.")
69
+ assert(type(depl)=="string", "package.build: Argument 'depl' is not a string.")
70
+ assert(type(variables)=="table", "package.build: Argument 'variables' is not a table.")
71
+
72
+ -- Load dist info
73
+ local info = manif.info(sys.path(dist, "dist.info"))
74
+ if not info then return nil, "Directory " .. dist .. " does not contain valid dist.info." end
75
+
76
+ -- Prepare temporary directory and build directory
77
+ local install = sys.path(config.temp,info.name .. "-" .. info.version .. "-" .. config.arch .. "-" .. config.type)
78
+ local build = sys.path(config.temp,info.name .. "-" .. info.version .. "-CMake-build")
79
+ sys.makeDir(install)
80
+ sys.makeDir(build)
81
+
82
+ -- Prepare CMackeCache
83
+ variables["CMAKE_INSTALL_PREFIX"] = install
84
+
85
+ local cache = assert(io.open(build..'/cache.cmake', "w"), "Could not create cache file.")
86
+ for k,v in pairs(variables) do
87
+ cache:write('SET('..k..' "' .. tostring(v) ..'" CACHE STRING "" FORCE)\n')
88
+ end
89
+ cache:close()
90
+
91
+ -- Determine build commands
92
+ local make = config.make
93
+ local cmake = config.cmake
94
+ if config.debug then
95
+ make = config.makeDebug
96
+ cmake = config.cmakeDebug
97
+ end
98
+
99
+ -- Build
100
+ local ok = sys.execute("cd " .. sys.Q(build) .. " && " .. cmake .. " -C cache.cmake " .. sys.Q(dist))
101
+ if not ok then return nil, "CMake failed pre-cmake script in directory " .. build end
102
+ local ok = sys.execute("cd " .. sys.Q(build) .. " && " .. make)
103
+ if not ok then return nil, "CMake failed building in directory " .. build end
104
+
105
+ -- Save info
106
+ info.arch = config.arch
107
+ info.type = config.type
108
+ local ok = persist.save(sys.path(install, "dist.info"), info )
109
+ if not ok then return nil, "Cannot wite dist.info to" .. fullDist end
110
+
111
+ -- Deploy the dist
112
+ local ok, err = deploy(install, depl)
113
+ if not ok then return nil, err end
114
+
115
+ -- Clean up
116
+ if not config.debug then sys.delete(build) sys.delete(install) end
117
+
118
+ return install, "Successfully built dist in " .. install
119
+ end
120
+
121
+ --- Deploy dist into deployment directory.
122
+ -- @param dist string: Directory of the dist to deploy.
123
+ -- @param depl string: Deployment directory nil for default.
124
+ -- @return ok, log: Returns true on success and log message.
125
+ function deploy(dist, depl)
126
+ -- Make sure deployment is always set up
127
+ depl = sys.path(depl) or config.root
128
+
129
+ assert(type(dist)=="string", "package.deploy: Argument 'dist' is not a string.")
130
+ assert(type(depl)=="string", "package.deploy: Argument 'depl' is not a string.")
131
+
132
+ -- Load dist info
133
+ local info = manif.info(sys.path(dist, "dist.info"))
134
+ if not info then return nil, "Directory " .. dist .. " does not contain valid dist.info" end
135
+
136
+ -- Relative and full path to dist deployment
137
+ local distRel = config.dists .. "/" .. info.name .. "-" .. info.version
138
+ local distPath = sys.path(depl, distRel)
139
+
140
+ -- If we are deploying a dist into itself
141
+ if distPath == dist then return
142
+ true, "Skipping, already deployed"
143
+ end
144
+
145
+ -- Copy to install dir
146
+ sys.makeDir(depl)
147
+ sys.makeDir(distPath)
148
+
149
+ -- Collect files to process
150
+ local files = sys.list(dist)
151
+
152
+ -- Simple copy deployment
153
+ for i = 1, #files do
154
+ local file = files[i]
155
+ if file~="dist.info" then
156
+ local path = sys.path(dist, file)
157
+ -- Create directory in depl
158
+ if sys.isDir(path) then
159
+ local ok, err = sys.makeDir(sys.path(depl, file))
160
+ if not ok then return nil, "Failed to install " .. dist .. "/" .. file .. " to " .. depl end
161
+ -- Copy files to depl
162
+ else
163
+ local ok, err = sys.copy(sys.path(dist, file), sys.path(depl, file))
164
+ if not ok then return nil, "Failed to install " .. dist .. "/" .. file .. " to " .. depl end
165
+ end
166
+ end
167
+ end
168
+
169
+ -- Modify and save dist.info
170
+ info.files = files
171
+
172
+ local ok = persist.save(sys.path(distPath, "dist.info"), info)
173
+ if not ok then return nil, "Cannot wite dist.info to" .. distPath end
174
+
175
+ return true, "Successfully deployed dist to " .. depl
176
+ end
177
+
178
+ --- Pack a package to create a dist.
179
+ -- @param dist string: deployed dist to pack.
180
+ -- @param depl string: deployment dir to pack from.
181
+ -- @param dir string: Optional destination for the dist, current directory will be used by default.
182
+ -- @return ok, log: Returns success and log message.
183
+ function pack(dist, depl, dir)
184
+ depl = depl or dist
185
+ assert(type(dist)=="string", "package.pack: Argument 'dist' is not a string.")
186
+ assert(not dir or type(dir)=="string", "package.pack: Argument 'dir' is no a string.")
187
+ if not dir then dir = sys.curDir() end
188
+
189
+ -- Get the manifest of the dist
190
+ local info = manif.info(sys.path(dist, "dist.info"))
191
+ if not info then return nil, "Dist does not contain valid dist.info in " .. dist end
192
+
193
+ -- Create temporary folder
194
+ local pkg = info.name .. "-" .. info.version .. "-" .. info.arch .. "-" .. info.type
195
+ if info.arch == "Universal" and info.type == "source" then
196
+ pkg = info.name .. "-" .. info.version
197
+ end
198
+
199
+ local tmp = sys.path(config.temp, pkg)
200
+ sys.makeDir(tmp)
201
+
202
+ -- Copy dist files into temporary folder
203
+ local files = info.files or sys.list(dist)
204
+ if not files then return nil, "Failed to collect files for dist in " .. dist end
205
+
206
+ for i = 1, #files do
207
+ local file = files[i]
208
+ if sys.isDir(sys.path(depl, file)) then
209
+ sys.makeDir(sys.path(tmp, file))
210
+ elseif file ~= "dist.info" then
211
+ local ok = sys.copy(sys.path(depl, file), sys.path(tmp, file))
212
+ if not ok then return nil, "Pack failed to copy file " .. file end
213
+ end
214
+ end
215
+
216
+ -- Clean obsolete dist.info entries
217
+ info.path = nil
218
+ info.files = nil
219
+ local ok, err = persist.save(sys.path(tmp, "dist.info"), info)
220
+ if not ok then return nil, "Could not update dist.info." end
221
+
222
+ -- Zip dist files in the temporary folder. The zip will be placed into LuaDist/tmp folder
223
+ -- This cleans up .git .svn and Mac .DS_Store files.
224
+ local ok = sys.zip(config.temp, pkg .. ".dist", pkg, '-x "*.git*" -x "*.svn*" -x "*~" -x "*.DS_Store*"')
225
+ if not ok then return nil, "Failed to compress files in" .. pkg end
226
+ local ok = sys.move(tmp .. ".dist", dir .. "/") -- Adding the "/" gets around ambiguity issues on Windows.
227
+ if not ok then return nil, "Could not move dist to target directory " .. dir end
228
+
229
+ -- Remove the temporary folder
230
+ if not config.debug then sys.delete(tmp) end
231
+ return true, "Sucessfully packed dist " .. dist .. " to " .. dir
232
+ end
233
+
234
+ --- Delete a deployed dist.
235
+ -- @param dist string: dist to delete.
236
+ -- @param depl string: deployment dir to delete from.
237
+ -- @return ok, log: Returns success and nlog message.
238
+ function delete(dist, depl)
239
+ assert(type(dist)=="string", "package.delete: Argument 'dist' is not a string.")
240
+ assert(type(dist)=="string", "package.delete: Argument 'depl' is not a string.")
241
+
242
+ -- Get the manifest of the dist
243
+ local info = manif.info(sys.path(dist, "dist.info"))
244
+ if not info then return nil, "Dist does not contain valid dist.info in " .. dist end
245
+
246
+ -- Delete installed files
247
+ local files = info.files
248
+ if not files then return nil, "Failed to collect files for dist in " .. dist end
249
+
250
+ -- Remove list backwards to empty dirs 1st.
251
+ for i = #files, 1, -1 do
252
+ local file = sys.path(depl, files[i])
253
+ -- Delete deployed file
254
+ if sys.isFile(file) then
255
+ sys.delete(file)
256
+ end
257
+ -- Delete empty directories
258
+ if sys.isDir(file) then
259
+ local contents = sys.dir(file)
260
+ if #contents == 0 then
261
+ sys.delete(file)
262
+ end
263
+ end
264
+ end
265
+
266
+ -- Delete dist directory
267
+ local ok = sys.delete(dist)
268
+ if not ok then return nil, "Deleting dist files failed in " .. dist end
269
+
270
+ return true, "Successfully removed dist " .. dist
271
+ end