aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHisham Muhammad <hisham@gobolinux.org>2017-10-08 02:15:14 -0300
committerHisham Muhammad <hisham@gobolinux.org>2017-10-08 02:15:14 -0300
commit49fdc6e080637fd9e5d8a9ae355fa06510cb68ee (patch)
treeca5b4a63f037f7af70cfb7b4f508f9a220f68bd5
parentc5bcb900a8245845dd5cd4fd8bf796591769bbee (diff)
downloadluarocks-49fdc6e080637fd9e5d8a9ae355fa06510cb68ee.tar.gz
luarocks-49fdc6e080637fd9e5d8a9ae355fa06510cb68ee.tar.bz2
luarocks-49fdc6e080637fd9e5d8a9ae355fa06510cb68ee.zip
Reorganize luarocks.core requires and type checking tables
* For each `luarocks.core.x` module, make `luarocks.x` module load the core module explicitly as `core`, and expose its relevant methods explicitly as well (instead of using `setmetatable`). * Move all type checking out of the core, adjusting the manifest modules accordingly. * Create separate modules for the rockspec and manifest schemas in the `luarocks.type` namespace.
-rw-r--r--src/luarocks/cmd/new_version.lua4
-rw-r--r--src/luarocks/cmd/write_rockspec.lua4
-rw-r--r--src/luarocks/core/manif.lua16
-rw-r--r--src/luarocks/core/type_check.lua231
-rw-r--r--src/luarocks/dir.lua6
-rw-r--r--src/luarocks/fetch.lua6
-rw-r--r--src/luarocks/manif.lua37
-rw-r--r--src/luarocks/path.lua8
-rw-r--r--src/luarocks/persist.lua4
-rw-r--r--src/luarocks/type/manifest.lua81
-rw-r--r--src/luarocks/type/rockspec.lua130
-rw-r--r--src/luarocks/type_check.lua259
-rw-r--r--src/luarocks/util.lua14
-rw-r--r--src/luarocks/vers.lua7
14 files changed, 426 insertions, 381 deletions
diff --git a/src/luarocks/cmd/new_version.lua b/src/luarocks/cmd/new_version.lua
index b13dbb97..5149f924 100644
--- a/src/luarocks/cmd/new_version.lua
+++ b/src/luarocks/cmd/new_version.lua
@@ -8,7 +8,7 @@ local download = require("luarocks.download")
8local fetch = require("luarocks.fetch") 8local fetch = require("luarocks.fetch")
9local persist = require("luarocks.persist") 9local persist = require("luarocks.persist")
10local fs = require("luarocks.fs") 10local fs = require("luarocks.fs")
11local type_check = require("luarocks.type_check") 11local type_rockspec = require("luarocks.type.rockspec")
12 12
13new_version.help_summary = "Auto-write a rockspec for a new version of a rock." 13new_version.help_summary = "Auto-write a rockspec for a new version of a rock."
14new_version.help_arguments = "[--tag=<tag>] [<package>|<rockspec>] [<new_version>] [<new_url>]" 14new_version.help_arguments = "[--tag=<tag>] [<package>|<rockspec>] [<new_version>] [<new_url>]"
@@ -184,7 +184,7 @@ function new_version.command(flags, input, version, url)
184 184
185 local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec" 185 local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec"
186 186
187 persist.save_from_table(out_filename, out_rs, type_check.rockspec_order) 187 persist.save_from_table(out_filename, out_rs, type_rockspec.order)
188 188
189 util.printout("Wrote "..out_filename) 189 util.printout("Wrote "..out_filename)
190 190
diff --git a/src/luarocks/cmd/write_rockspec.lua b/src/luarocks/cmd/write_rockspec.lua
index 73010f08..06d50c69 100644
--- a/src/luarocks/cmd/write_rockspec.lua
+++ b/src/luarocks/cmd/write_rockspec.lua
@@ -7,7 +7,7 @@ local fetch = require("luarocks.fetch")
7local fs = require("luarocks.fs") 7local fs = require("luarocks.fs")
8local path = require("luarocks.path") 8local path = require("luarocks.path")
9local persist = require("luarocks.persist") 9local persist = require("luarocks.persist")
10local type_check = require("luarocks.type_check") 10local type_rockspec = require("luarocks.type.rockspec")
11local util = require("luarocks.util") 11local util = require("luarocks.util")
12local vers = require("luarocks.vers") 12local vers = require("luarocks.vers")
13 13
@@ -364,7 +364,7 @@ function write_rockspec.command(flags, name, version, url_or_dir)
364 364
365 rockspec_cleanup(rockspec) 365 rockspec_cleanup(rockspec)
366 366
367 persist.save_from_table(filename, rockspec, type_check.rockspec_order) 367 persist.save_from_table(filename, rockspec, type_rockspec.order)
368 368
369 util.printout() 369 util.printout()
370 util.printout("Wrote template at "..filename.." -- you should now edit and finish it.") 370 util.printout("Wrote template at "..filename.." -- you should now edit and finish it.")
diff --git a/src/luarocks/core/manif.lua b/src/luarocks/core/manif.lua
index 549cfb4c..cd8d7a06 100644
--- a/src/luarocks/core/manif.lua
+++ b/src/luarocks/core/manif.lua
@@ -3,7 +3,6 @@
3local manif = {} 3local manif = {}
4 4
5local persist = require("luarocks.core.persist") 5local persist = require("luarocks.core.persist")
6local type_check = require("luarocks.core.type_check")
7local cfg = require("luarocks.core.cfg") 6local cfg = require("luarocks.core.cfg")
8local dir = require("luarocks.core.dir") 7local dir = require("luarocks.core.dir")
9local require = nil 8local require = nil
@@ -37,24 +36,15 @@ end
37-- @param file string: The local filename of the manifest file. 36-- @param file string: The local filename of the manifest file.
38-- @param repo_url string: The repository identifier. 37-- @param repo_url string: The repository identifier.
39-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. 38-- @param lua_version string: Lua version in "5.x" format, defaults to installed version.
40-- @param quick boolean: If given, skips type checking.
41-- @return table or (nil, string, string): the manifest or nil, 39-- @return table or (nil, string, string): the manifest or nil,
42-- error message and error code ("open", "load", "run" or "type"). 40-- error message and error code ("open", "load", "run").
43function manif.manifest_loader(file, repo_url, lua_version, quick) 41function manif.manifest_loader(file, repo_url, lua_version)
44 local manifest, err, errcode = persist.load_into_table(file) 42 local manifest, err, errcode = persist.load_into_table(file)
45 if not manifest then 43 if not manifest then
46 return nil, "Failed loading manifest for "..repo_url..": "..err, errcode 44 return nil, "Failed loading manifest for "..repo_url..": "..err, errcode
47 end 45 end
48 local globals = err
49 if not quick then
50 local ok, err = type_check.type_check_manifest(manifest, globals)
51 if not ok then
52 return nil, "Error checking manifest: "..err, "type"
53 end
54 end
55
56 manif.cache_manifest(repo_url, lua_version, manifest) 46 manif.cache_manifest(repo_url, lua_version, manifest)
57 return manifest 47 return manifest, err, errcode
58end 48end
59 49
60--- Load a local manifest describing a repository. 50--- Load a local manifest describing a repository.
diff --git a/src/luarocks/core/type_check.lua b/src/luarocks/core/type_check.lua
deleted file mode 100644
index 8b103a58..00000000
--- a/src/luarocks/core/type_check.lua
+++ /dev/null
@@ -1,231 +0,0 @@
1
2local type_check = {}
3
4local cfg = require("luarocks.core.cfg")
5local vers = require("luarocks.core.vers")
6local require = nil
7--------------------------------------------------------------------------------
8
9type_check.string_1 = { _type = "string" }
10type_check.number_1 = { _type = "number" }
11type_check.mandatory_string_1 = { _type = "string", _mandatory = true }
12
13local number_1 = type_check.number_1
14local string_1 = type_check.string_1
15local mandatory_string_1 = type_check.mandatory_string_1
16
17local manifest_types = {
18 repository = {
19 _mandatory = true,
20 -- packages
21 _any = {
22 -- versions
23 _any = {
24 -- items
25 _any = {
26 arch = mandatory_string_1,
27 modules = { _any = string_1 },
28 commands = { _any = string_1 },
29 dependencies = { _any = string_1 },
30 -- TODO: to be extended with more metadata.
31 }
32 }
33 }
34 },
35 modules = {
36 _mandatory = true,
37 -- modules
38 _any = {
39 -- providers
40 _any = string_1
41 }
42 },
43 commands = {
44 _mandatory = true,
45 -- modules
46 _any = {
47 -- commands
48 _any = string_1
49 }
50 },
51 dependencies = {
52 -- each module
53 _any = {
54 -- each version
55 _any = {
56 -- each dependency
57 _any = {
58 name = string_1,
59 constraints = {
60 _any = {
61 no_upgrade = { _type = "boolean" },
62 op = string_1,
63 version = {
64 string = string_1,
65 _any = number_1,
66 }
67 }
68 }
69 }
70 }
71 }
72 }
73}
74
75local function check_version(version, typetbl, context)
76 local typetbl_version = typetbl._version or "1.0"
77 if vers.compare_versions(typetbl_version, version) then
78 if context == "" then
79 return nil, "Invalid rockspec_format version number in rockspec? Please fix rockspec accordingly."
80 else
81 return nil, context.." is not supported in rockspec format "..version.." (requires version "..typetbl_version.."), please fix the rockspec_format field accordingly."
82 end
83 end
84 return true
85end
86
87--- Type check an object.
88-- The object is compared against an archetypical value
89-- matching the expected type -- the actual values don't matter,
90-- only their types. Tables are type checked recursively.
91-- @param version string: The version of the item.
92-- @param item any: The object being checked.
93-- @param typetbl any: The type-checking table for the object.
94-- @param context string: A string indicating the "context" where the
95-- error occurred (the full table path), for error messages.
96-- @return boolean or (nil, string): true if type checking
97-- succeeded, or nil and an error message if it failed.
98-- @see type_check_table
99local function type_check_item(version, item, typetbl, context)
100 assert(type(version) == "string")
101
102 if typetbl._version and typetbl._version ~= "1.0" then
103 local ok, err = check_version(version, typetbl, context)
104 if not ok then
105 return nil, err
106 end
107 end
108
109 local item_type = type(item) or "nil"
110 local expected_type = typetbl._type or "table"
111
112 if expected_type == "number" then
113 if not tonumber(item) then
114 return nil, "Type mismatch on field "..context..": expected a number"
115 end
116 elseif expected_type == "string" then
117 if item_type ~= "string" then
118 return nil, "Type mismatch on field "..context..": expected a string, got "..item_type
119 end
120 if typetbl._pattern then
121 if not item:match("^"..typetbl._pattern.."$") then
122 return nil, "Type mismatch on field "..context..": invalid value "..item.." does not match '"..typetbl._pattern.."'"
123 end
124 end
125 elseif expected_type == "table" then
126 if item_type ~= expected_type then
127 return nil, "Type mismatch on field "..context..": expected a table"
128 else
129 return type_check.type_check_table(version, item, typetbl, context)
130 end
131 elseif item_type ~= expected_type then
132 return nil, "Type mismatch on field "..context..": expected "..expected_type
133 end
134 return true
135end
136
137local function mkfield(context, field)
138 if context == "" then
139 return tostring(field)
140 elseif type(field) == "string" then
141 return context.."."..field
142 else
143 return context.."["..tostring(field).."]"
144 end
145end
146
147--- Type check the contents of a table.
148-- The table's contents are compared against a reference table,
149-- which contains the recognized fields, with archetypical values
150-- matching the expected types -- the actual values of items in the
151-- reference table don't matter, only their types (ie, for field x
152-- in tbl that is correctly typed, type(tbl.x) == type(types.x)).
153-- If the reference table contains a field called MORE, then
154-- unknown fields in the checked table are accepted.
155-- If it contains a field called ANY, then its type will be
156-- used to check any unknown fields. If a field is prefixed
157-- with MUST_, it is mandatory; its absence from the table is
158-- a type error.
159-- Tables are type checked recursively.
160-- @param version string: The version of tbl.
161-- @param tbl table: The table to be type checked.
162-- @param typetbl table: The type-checking table, containing
163-- values for recognized fields in the checked table.
164-- @param context string: A string indicating the "context" where the
165-- error occurred (such as the name of the table the item is a part of),
166-- to be used by error messages.
167-- @return boolean or (nil, string): true if type checking
168-- succeeded, or nil and an error message if it failed.
169function type_check.type_check_table(version, tbl, typetbl, context)
170 assert(type(version) == "string")
171 assert(type(tbl) == "table")
172 assert(type(typetbl) == "table")
173
174 local ok, err = check_version(version, typetbl, context)
175 if not ok then
176 return nil, err
177 end
178
179 for k, v in pairs(tbl) do
180 local t = typetbl[k] or typetbl._any
181 if t then
182 local ok, err = type_check_item(version, v, t, mkfield(context, k))
183 if not ok then return nil, err end
184 elseif typetbl._more then
185 -- Accept unknown field
186 else
187 if not cfg.accept_unknown_fields then
188 return nil, "Unknown field "..k
189 end
190 end
191 end
192 for k, v in pairs(typetbl) do
193 if k:sub(1,1) ~= "_" and v._mandatory then
194 if not tbl[k] then
195 return nil, "Mandatory field "..mkfield(context, k).." is missing."
196 end
197 end
198 end
199 return true
200end
201
202function type_check.check_undeclared_globals(globals, typetbl)
203 local undeclared = {}
204 for glob, _ in pairs(globals) do
205 if not (typetbl[glob] or typetbl["MUST_"..glob]) then
206 table.insert(undeclared, glob)
207 end
208 end
209 if #undeclared == 1 then
210 return nil, "Unknown variable: "..undeclared[1]
211 elseif #undeclared > 1 then
212 return nil, "Unknown variables: "..table.concat(undeclared, ", ")
213 end
214 return true
215end
216
217--- Type check a manifest table.
218-- Verify the correctness of elements from a
219-- manifest table, reporting on unknown fields and type
220-- mismatches.
221-- @return boolean or (nil, string): true if type checking
222-- succeeded, or nil and an error message if it failed.
223function type_check.type_check_manifest(manifest, globals)
224 assert(type(manifest) == "table")
225 local ok, err = type_check.check_undeclared_globals(globals, manifest_types)
226 if not ok then return nil, err end
227 return type_check.type_check_table("1.0", manifest, manifest_types, "")
228end
229
230return type_check
231
diff --git a/src/luarocks/dir.lua b/src/luarocks/dir.lua
index 71477804..72395e47 100644
--- a/src/luarocks/dir.lua
+++ b/src/luarocks/dir.lua
@@ -1,7 +1,11 @@
1 1
2--- Generic utilities for handling pathnames. 2--- Generic utilities for handling pathnames.
3local dir = {} 3local dir = {}
4setmetatable(dir, { __index = require("luarocks.core.dir") }) 4
5local core = require("luarocks.core.dir")
6
7dir.path = core.path
8dir.split_url = core.split_url
5 9
6--- Strip the path off a path+filename. 10--- Strip the path off a path+filename.
7-- @param pathname string: A path+name, such as "/a/b/c" 11-- @param pathname string: A path+name, such as "/a/b/c"
diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua
index dc467d19..de4a3dee 100644
--- a/src/luarocks/fetch.lua
+++ b/src/luarocks/fetch.lua
@@ -4,7 +4,7 @@ local fetch = {}
4 4
5local fs = require("luarocks.fs") 5local fs = require("luarocks.fs")
6local dir = require("luarocks.dir") 6local dir = require("luarocks.dir")
7local type_check = require("luarocks.type_check") 7local type_rockspec = require("luarocks.type.rockspec")
8local path = require("luarocks.path") 8local path = require("luarocks.path")
9local vers = require("luarocks.vers") 9local vers = require("luarocks.vers")
10local persist = require("luarocks.persist") 10local persist = require("luarocks.persist")
@@ -199,13 +199,13 @@ function fetch.load_local_rockspec(filename, quick)
199 local globals = err 199 local globals = err
200 200
201 if rockspec.rockspec_format then 201 if rockspec.rockspec_format then
202 if vers.compare_versions(rockspec.rockspec_format, type_check.rockspec_format) then 202 if vers.compare_versions(rockspec.rockspec_format, type_rockspec.rockspec_format) then
203 return nil, "Rockspec format "..rockspec.rockspec_format.." is not supported, please upgrade LuaRocks." 203 return nil, "Rockspec format "..rockspec.rockspec_format.." is not supported, please upgrade LuaRocks."
204 end 204 end
205 end 205 end
206 206
207 if not quick then 207 if not quick then
208 local ok, err = type_check.type_check_rockspec(rockspec, globals) 208 local ok, err = type_rockspec.check(rockspec, globals)
209 if not ok then 209 if not ok then
210 return nil, filename..": "..err 210 return nil, filename..": "..err
211 end 211 end
diff --git a/src/luarocks/manif.lua b/src/luarocks/manif.lua
index 7f3085db..df9f22ae 100644
--- a/src/luarocks/manif.lua
+++ b/src/luarocks/manif.lua
@@ -3,8 +3,8 @@
3-- They are loaded into manifest tables, which are then used for 3-- They are loaded into manifest tables, which are then used for
4-- performing searches, matching dependencies, etc. 4-- performing searches, matching dependencies, etc.
5local manif = {} 5local manif = {}
6setmetatable(manif, { __index = require("luarocks.core.manif") })
7 6
7local core = require("luarocks.core.manif")
8local persist = require("luarocks.persist") 8local persist = require("luarocks.persist")
9local fetch = require("luarocks.fetch") 9local fetch = require("luarocks.fetch")
10local dir = require("luarocks.dir") 10local dir = require("luarocks.dir")
@@ -12,9 +12,32 @@ local fs = require("luarocks.fs")
12local cfg = require("luarocks.core.cfg") 12local cfg = require("luarocks.core.cfg")
13local path = require("luarocks.path") 13local path = require("luarocks.path")
14local util = require("luarocks.util") 14local util = require("luarocks.util")
15local type_manifest = require("luarocks.type.manifest")
16
17manif.cache_manifest = core.cache_manifest
15 18
16manif.rock_manifest_cache = {} 19manif.rock_manifest_cache = {}
17 20
21local function check_manifest(repo_url, manifest, globals)
22 local ok, err = type_manifest.check(manifest, globals)
23 if not ok then
24 core.cache_manifest(repo_url, cfg.lua_version, nil)
25 return nil, "Error checking manifest: "..err, "type"
26 end
27 return manifest
28end
29
30function manif.load_local_manifest(repo_url)
31 local manifest, err, errcode = core.load_local_manifest(repo_url)
32 if not manifest then
33 return nil, err, errcode
34 end
35 if err then
36 return check_manifest(repo_url, manifest, err)
37 end
38 return manifest
39end
40
18function manif.load_rock_manifest(name, version, root) 41function manif.load_rock_manifest(name, version, root)
19 assert(type(name) == "string") 42 assert(type(name) == "string")
20 assert(type(version) == "string") 43 assert(type(version) == "string")
@@ -60,7 +83,7 @@ function manif.load_manifest(repo_url, lua_version)
60 assert(type(lua_version) == "string" or not lua_version) 83 assert(type(lua_version) == "string" or not lua_version)
61 lua_version = lua_version or cfg.lua_version 84 lua_version = lua_version or cfg.lua_version
62 85
63 local cached_manifest = manif.get_cached_manifest(repo_url, lua_version) 86 local cached_manifest = core.get_cached_manifest(repo_url, lua_version)
64 if cached_manifest then 87 if cached_manifest then
65 return cached_manifest 88 return cached_manifest
66 end 89 end
@@ -94,8 +117,8 @@ function manif.load_manifest(repo_url, lua_version)
94 end 117 end
95 if pathname:match(".*%.zip$") then 118 if pathname:match(".*%.zip$") then
96 pathname = fs.absolute_name(pathname) 119 pathname = fs.absolute_name(pathname)
97 local dir = dir.dir_name(pathname) 120 local dirname = dir.dir_name(pathname)
98 fs.change_dir(dir) 121 fs.change_dir(dirname)
99 local nozip = pathname:match("(.*)%.zip$") 122 local nozip = pathname:match("(.*)%.zip$")
100 fs.delete(nozip) 123 fs.delete(nozip)
101 local ok = fs.unzip(pathname) 124 local ok = fs.unzip(pathname)
@@ -107,7 +130,11 @@ function manif.load_manifest(repo_url, lua_version)
107 end 130 end
108 pathname = nozip 131 pathname = nozip
109 end 132 end
110 return manif.manifest_loader(pathname, repo_url, lua_version) 133 local manifest, err, errcode = core.manifest_loader(pathname, repo_url, lua_version)
134 if not manifest then
135 return nil, err, errcode
136 end
137 return check_manifest(repo_url, manifest, err)
111end 138end
112 139
113--- Get type and name of an item (a module or a command) provided by a file. 140--- Get type and name of an item (a module or a command) provided by a file.
diff --git a/src/luarocks/path.lua b/src/luarocks/path.lua
index d740331b..8a56233f 100644
--- a/src/luarocks/path.lua
+++ b/src/luarocks/path.lua
@@ -3,12 +3,18 @@
3-- All paths are configured in this module, making it a single 3-- All paths are configured in this module, making it a single
4-- point where the layout of the local installation is defined in LuaRocks. 4-- point where the layout of the local installation is defined in LuaRocks.
5local path = {} 5local path = {}
6setmetatable(path, { __index = require("luarocks.core.path") })
7 6
7local core = require("luarocks.core.path")
8local dir = require("luarocks.dir") 8local dir = require("luarocks.dir")
9local cfg = require("luarocks.core.cfg") 9local cfg = require("luarocks.core.cfg")
10local util = require("luarocks.util") 10local util = require("luarocks.util")
11 11
12path.rocks_dir = core.rocks_dir
13path.versioned_name = core.versioned_name
14path.path_to_module = core.path_to_module
15path.deploy_lua_dir = core.deploy_lua_dir
16path.deploy_lib_dir = core.deploy_lib_dir
17
12--- Infer rockspec filename from a rock filename. 18--- Infer rockspec filename from a rock filename.
13-- @param rock_name string: Pathname of a rock file. 19-- @param rock_name string: Pathname of a rock file.
14-- @return string: Filename of the rockspec, without path. 20-- @return string: Filename of the rockspec, without path.
diff --git a/src/luarocks/persist.lua b/src/luarocks/persist.lua
index 6d5e917b..1460bd15 100644
--- a/src/luarocks/persist.lua
+++ b/src/luarocks/persist.lua
@@ -4,10 +4,12 @@
4-- Implemented separately to avoid interdependencies, 4-- Implemented separately to avoid interdependencies,
5-- as it is used in the bootstrapping stage of the cfg module. 5-- as it is used in the bootstrapping stage of the cfg module.
6local persist = {} 6local persist = {}
7setmetatable(persist, { __index = require("luarocks.core.persist") })
8 7
8local core = require("luarocks.core.persist")
9local util = require("luarocks.util") 9local util = require("luarocks.util")
10 10
11persist.load_into_table = core.load_into_table
12
11local write_table 13local write_table
12 14
13--- Write a value as Lua code. 15--- Write a value as Lua code.
diff --git a/src/luarocks/type/manifest.lua b/src/luarocks/type/manifest.lua
new file mode 100644
index 00000000..f58ff984
--- /dev/null
+++ b/src/luarocks/type/manifest.lua
@@ -0,0 +1,81 @@
1local type_manifest = {}
2
3local type_check = require("luarocks.type_check")
4
5local number_1 = type_check.number_1
6local string_1 = type_check.string_1
7local mandatory_string_1 = type_check.mandatory_string_1
8
9local manifest_types = {
10 repository = {
11 _mandatory = true,
12 -- packages
13 _any = {
14 -- versions
15 _any = {
16 -- items
17 _any = {
18 arch = mandatory_string_1,
19 modules = { _any = string_1 },
20 commands = { _any = string_1 },
21 dependencies = { _any = string_1 },
22 -- TODO: to be extended with more metadata.
23 }
24 }
25 }
26 },
27 modules = {
28 _mandatory = true,
29 -- modules
30 _any = {
31 -- providers
32 _any = string_1
33 }
34 },
35 commands = {
36 _mandatory = true,
37 -- modules
38 _any = {
39 -- commands
40 _any = string_1
41 }
42 },
43 dependencies = {
44 -- each module
45 _any = {
46 -- each version
47 _any = {
48 -- each dependency
49 _any = {
50 name = string_1,
51 constraints = {
52 _any = {
53 no_upgrade = { _type = "boolean" },
54 op = string_1,
55 version = {
56 string = string_1,
57 _any = number_1,
58 }
59 }
60 }
61 }
62 }
63 }
64 }
65}
66
67
68--- Type check a manifest table.
69-- Verify the correctness of elements from a
70-- manifest table, reporting on unknown fields and type
71-- mismatches.
72-- @return boolean or (nil, string): true if type checking
73-- succeeded, or nil and an error message if it failed.
74function type_manifest.check(manifest, globals)
75 assert(type(manifest) == "table")
76 local ok, err = type_check.check_undeclared_globals(globals, manifest_types)
77 if not ok then return nil, err end
78 return type_check.type_check_table("1.0", manifest, manifest_types, "")
79end
80
81return type_manifest
diff --git a/src/luarocks/type/rockspec.lua b/src/luarocks/type/rockspec.lua
new file mode 100644
index 00000000..5ff48177
--- /dev/null
+++ b/src/luarocks/type/rockspec.lua
@@ -0,0 +1,130 @@
1local type_rockspec = {}
2
3local type_check = require("luarocks.type_check")
4
5type_rockspec.rockspec_format = "3.0"
6
7local string_1 = type_check.string_1
8local mandatory_string_1 = type_check.mandatory_string_1
9
10local string_3 = { _type = "string", _version = "3.0" }
11local list_of_strings_3 = { _any = string_3, _version = "3.0" }
12
13-- Syntax for type-checking tables:
14--
15-- A type-checking table describes typing data for a value.
16-- Any key starting with an underscore has a special meaning:
17-- _type (string) is the Lua type of the value. Default is "table".
18-- _version (string) is the minimum rockspec_version that supports this value. Default is "1.0".
19-- _mandatory (boolean) indicates if the value is a mandatory key in its container table. Default is false.
20-- For "string" types only:
21-- _pattern (string) is the string-matching pattern, valid for string types only. Default is ".*".
22-- For "table" types only:
23-- _any (table) is the type-checking table for unspecified keys, recursively checked.
24-- _more (boolean) indicates that the table accepts unspecified keys and does not type-check them.
25-- Any other string keys that don't start with an underscore represent known keys and are type-checking tables, recursively checked.
26
27local rockspec_types = {
28 rockspec_format = string_1,
29 package = mandatory_string_1,
30 version = { _type = "string", _pattern = "[%w.]+-[%d]+", _mandatory = true },
31 description = {
32 summary = string_1,
33 detailed = string_1,
34 homepage = string_1,
35 license = string_1,
36 maintainer = string_1,
37 labels = list_of_strings_3,
38 issues_url = string_3,
39 },
40 dependencies = {
41 platforms = {}, -- recursively defined below
42 _any = string_1,
43 },
44 supported_platforms = {
45 _any = string_1,
46 },
47 external_dependencies = {
48 platforms = {}, -- recursively defined below
49 _any = {
50 program = string_1,
51 header = string_1,
52 library = string_1,
53 }
54 },
55 source = {
56 _mandatory = true,
57 platforms = {}, -- recursively defined below
58 url = mandatory_string_1,
59 md5 = string_1,
60 file = string_1,
61 dir = string_1,
62 tag = string_1,
63 branch = string_1,
64 module = string_1,
65 cvs_tag = string_1,
66 cvs_module = string_1,
67 },
68 build = {
69 platforms = {}, -- recursively defined below
70 type = string_1,
71 install = {
72 lua = {
73 _more = true
74 },
75 lib = {
76 _more = true
77 },
78 conf = {
79 _more = true
80 },
81 bin = {
82 _more = true
83 }
84 },
85 copy_directories = {
86 _any = string_1,
87 },
88 _more = true,
89 _mandatory = true
90 },
91 hooks = {
92 platforms = {}, -- recursively defined below
93 post_install = string_1,
94 },
95 deploy = {
96 _version = "1.1",
97 wrap_bin_scripts = { _type = "boolean", _version = "1.1" },
98 }
99}
100
101type_rockspec.order = {"rockspec_format", "package", "version",
102 { "source", { "url", "tag", "branch", "md5" } },
103 { "description", {"summary", "detailed", "homepage", "license" } },
104 "supported_platforms", "dependencies", "external_dependencies",
105 { "build", {"type", "modules", "copy_directories", "platforms"} },
106 "hooks"}
107
108rockspec_types.build.platforms._any = rockspec_types.build
109rockspec_types.dependencies.platforms._any = rockspec_types.dependencies
110rockspec_types.external_dependencies.platforms._any = rockspec_types.external_dependencies
111rockspec_types.source.platforms._any = rockspec_types.source
112rockspec_types.hooks.platforms._any = rockspec_types.hooks
113
114--- Type check a rockspec table.
115-- Verify the correctness of elements from a
116-- rockspec table, reporting on unknown fields and type
117-- mismatches.
118-- @return boolean or (nil, string): true if type checking
119-- succeeded, or nil and an error message if it failed.
120function type_rockspec.check(rockspec, globals)
121 assert(type(rockspec) == "table")
122 if not rockspec.rockspec_format then
123 rockspec.rockspec_format = "1.0"
124 end
125 local ok, err = type_check.check_undeclared_globals(globals, rockspec_types)
126 if not ok then return nil, err end
127 return type_check.type_check_table(rockspec.rockspec_format, rockspec, rockspec_types, "")
128end
129
130return type_rockspec
diff --git a/src/luarocks/type_check.lua b/src/luarocks/type_check.lua
index 4b7e68d7..250c6258 100644
--- a/src/luarocks/type_check.lua
+++ b/src/luarocks/type_check.lua
@@ -1,132 +1,155 @@
1--- Type-checking functions.
2-- Functions and definitions for doing a basic lint check on files
3-- loaded by LuaRocks.
4local type_check = {}
5setmetatable(type_check, { __index = require("luarocks.core.type_check") })
6 1
7type_check.rockspec_format = "3.0" 2local type_check = {}
8 3
9local string_1 = type_check.string_1 4local cfg = require("luarocks.core.cfg")
10local mandatory_string_1 = type_check.mandatory_string_1 5local vers = require("luarocks.core.vers")
6local require = nil
7--------------------------------------------------------------------------------
11 8
12local string_3 = { _type = "string", _version = "3.0" } 9type_check.string_1 = { _type = "string" }
13local list_of_strings_3 = { _any = string_3, _version = "3.0" } 10type_check.number_1 = { _type = "number" }
11type_check.mandatory_string_1 = { _type = "string", _mandatory = true }
14 12
15-- Syntax for type-checking tables: 13local function check_version(version, typetbl, context)
16-- 14 local typetbl_version = typetbl._version or "1.0"
17-- A type-checking table describes typing data for a value. 15 if vers.compare_versions(typetbl_version, version) then
18-- Any key starting with an underscore has a special meaning: 16 if context == "" then
19-- _type (string) is the Lua type of the value. Default is "table". 17 return nil, "Invalid rockspec_format version number in rockspec? Please fix rockspec accordingly."
20-- _version (string) is the minimum rockspec_version that supports this value. Default is "1.0". 18 else
21-- _mandatory (boolean) indicates if the value is a mandatory key in its container table. Default is false. 19 return nil, context.." is not supported in rockspec format "..version.." (requires version "..typetbl_version.."), please fix the rockspec_format field accordingly."
22-- For "string" types only: 20 end
23-- _pattern (string) is the string-matching pattern, valid for string types only. Default is ".*". 21 end
24-- For "table" types only: 22 return true
25-- _any (table) is the type-checking table for unspecified keys, recursively checked. 23end
26-- _more (boolean) indicates that the table accepts unspecified keys and does not type-check them.
27-- Any other string keys that don't start with an underscore represent known keys and are type-checking tables, recursively checked.
28 24
29local rockspec_types = { 25--- Type check an object.
30 rockspec_format = string_1, 26-- The object is compared against an archetypical value
31 package = mandatory_string_1, 27-- matching the expected type -- the actual values don't matter,
32 version = { _type = "string", _pattern = "[%w.]+-[%d]+", _mandatory = true }, 28-- only their types. Tables are type checked recursively.
33 description = { 29-- @param version string: The version of the item.
34 summary = string_1, 30-- @param item any: The object being checked.
35 detailed = string_1, 31-- @param typetbl any: The type-checking table for the object.
36 homepage = string_1, 32-- @param context string: A string indicating the "context" where the
37 license = string_1, 33-- error occurred (the full table path), for error messages.
38 maintainer = string_1, 34-- @return boolean or (nil, string): true if type checking
39 labels = list_of_strings_3, 35-- succeeded, or nil and an error message if it failed.
40 issues_url = string_3, 36-- @see type_check_table
41 }, 37local function type_check_item(version, item, typetbl, context)
42 dependencies = { 38 assert(type(version) == "string")
43 platforms = {}, -- recursively defined below
44 _any = string_1,
45 },
46 supported_platforms = {
47 _any = string_1,
48 },
49 external_dependencies = {
50 platforms = {}, -- recursively defined below
51 _any = {
52 program = string_1,
53 header = string_1,
54 library = string_1,
55 }
56 },
57 source = {
58 _mandatory = true,
59 platforms = {}, -- recursively defined below
60 url = mandatory_string_1,
61 md5 = string_1,
62 file = string_1,
63 dir = string_1,
64 tag = string_1,
65 branch = string_1,
66 module = string_1,
67 cvs_tag = string_1,
68 cvs_module = string_1,
69 },
70 build = {
71 platforms = {}, -- recursively defined below
72 type = string_1,
73 install = {
74 lua = {
75 _more = true
76 },
77 lib = {
78 _more = true
79 },
80 conf = {
81 _more = true
82 },
83 bin = {
84 _more = true
85 }
86 },
87 copy_directories = {
88 _any = string_1,
89 },
90 _more = true,
91 _mandatory = true
92 },
93 hooks = {
94 platforms = {}, -- recursively defined below
95 post_install = string_1,
96 },
97 deploy = {
98 _version = "1.1",
99 wrap_bin_scripts = { _type = "boolean", _version = "1.1" },
100 }
101}
102 39
103type_check.rockspec_order = {"rockspec_format", "package", "version", 40 if typetbl._version and typetbl._version ~= "1.0" then
104 { "source", { "url", "tag", "branch", "md5" } }, 41 local ok, err = check_version(version, typetbl, context)
105 { "description", {"summary", "detailed", "homepage", "license" } }, 42 if not ok then
106 "supported_platforms", "dependencies", "external_dependencies", 43 return nil, err
107 { "build", {"type", "modules", "copy_directories", "platforms"} }, 44 end
108 "hooks"} 45 end
46
47 local item_type = type(item) or "nil"
48 local expected_type = typetbl._type or "table"
49
50 if expected_type == "number" then
51 if not tonumber(item) then
52 return nil, "Type mismatch on field "..context..": expected a number"
53 end
54 elseif expected_type == "string" then
55 if item_type ~= "string" then
56 return nil, "Type mismatch on field "..context..": expected a string, got "..item_type
57 end
58 if typetbl._pattern then
59 if not item:match("^"..typetbl._pattern.."$") then
60 return nil, "Type mismatch on field "..context..": invalid value "..item.." does not match '"..typetbl._pattern.."'"
61 end
62 end
63 elseif expected_type == "table" then
64 if item_type ~= expected_type then
65 return nil, "Type mismatch on field "..context..": expected a table"
66 else
67 return type_check.type_check_table(version, item, typetbl, context)
68 end
69 elseif item_type ~= expected_type then
70 return nil, "Type mismatch on field "..context..": expected "..expected_type
71 end
72 return true
73end
109 74
110rockspec_types.build.platforms._any = rockspec_types.build 75local function mkfield(context, field)
111rockspec_types.dependencies.platforms._any = rockspec_types.dependencies 76 if context == "" then
112rockspec_types.external_dependencies.platforms._any = rockspec_types.external_dependencies 77 return tostring(field)
113rockspec_types.source.platforms._any = rockspec_types.source 78 elseif type(field) == "string" then
114rockspec_types.hooks.platforms._any = rockspec_types.hooks 79 return context.."."..field
80 else
81 return context.."["..tostring(field).."]"
82 end
83end
115 84
116--- Type check a rockspec table. 85--- Type check the contents of a table.
117-- Verify the correctness of elements from a 86-- The table's contents are compared against a reference table,
118-- rockspec table, reporting on unknown fields and type 87-- which contains the recognized fields, with archetypical values
119-- mismatches. 88-- matching the expected types -- the actual values of items in the
89-- reference table don't matter, only their types (ie, for field x
90-- in tbl that is correctly typed, type(tbl.x) == type(types.x)).
91-- If the reference table contains a field called MORE, then
92-- unknown fields in the checked table are accepted.
93-- If it contains a field called ANY, then its type will be
94-- used to check any unknown fields. If a field is prefixed
95-- with MUST_, it is mandatory; its absence from the table is
96-- a type error.
97-- Tables are type checked recursively.
98-- @param version string: The version of tbl.
99-- @param tbl table: The table to be type checked.
100-- @param typetbl table: The type-checking table, containing
101-- values for recognized fields in the checked table.
102-- @param context string: A string indicating the "context" where the
103-- error occurred (such as the name of the table the item is a part of),
104-- to be used by error messages.
120-- @return boolean or (nil, string): true if type checking 105-- @return boolean or (nil, string): true if type checking
121-- succeeded, or nil and an error message if it failed. 106-- succeeded, or nil and an error message if it failed.
122function type_check.type_check_rockspec(rockspec, globals) 107function type_check.type_check_table(version, tbl, typetbl, context)
123 assert(type(rockspec) == "table") 108 assert(type(version) == "string")
124 if not rockspec.rockspec_format then 109 assert(type(tbl) == "table")
125 rockspec.rockspec_format = "1.0" 110 assert(type(typetbl) == "table")
111
112 local ok, err = check_version(version, typetbl, context)
113 if not ok then
114 return nil, err
115 end
116
117 for k, v in pairs(tbl) do
118 local t = typetbl[k] or typetbl._any
119 if t then
120 local ok, err = type_check_item(version, v, t, mkfield(context, k))
121 if not ok then return nil, err end
122 elseif typetbl._more then
123 -- Accept unknown field
124 else
125 if not cfg.accept_unknown_fields then
126 return nil, "Unknown field "..k
127 end
128 end
129 end
130 for k, v in pairs(typetbl) do
131 if k:sub(1,1) ~= "_" and v._mandatory then
132 if not tbl[k] then
133 return nil, "Mandatory field "..mkfield(context, k).." is missing."
134 end
135 end
136 end
137 return true
138end
139
140function type_check.check_undeclared_globals(globals, typetbl)
141 local undeclared = {}
142 for glob, _ in pairs(globals) do
143 if not (typetbl[glob] or typetbl["MUST_"..glob]) then
144 table.insert(undeclared, glob)
145 end
146 end
147 if #undeclared == 1 then
148 return nil, "Unknown variable: "..undeclared[1]
149 elseif #undeclared > 1 then
150 return nil, "Unknown variables: "..table.concat(undeclared, ", ")
126 end 151 end
127 local ok, err = type_check.check_undeclared_globals(globals, rockspec_types) 152 return true
128 if not ok then return nil, err end
129 return type_check.type_check_table(rockspec.rockspec_format, rockspec, rockspec_types, "")
130end 153end
131 154
132return type_check 155return type_check
diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua
index 59173449..0af23df6 100644
--- a/src/luarocks/util.lua
+++ b/src/luarocks/util.lua
@@ -5,7 +5,15 @@
5-- as this is used in the bootstrapping stage of luarocks.core.cfg. 5-- as this is used in the bootstrapping stage of luarocks.core.cfg.
6 6
7local util = {} 7local util = {}
8setmetatable(util, { __index = require("luarocks.core.util") }) 8
9local core = require("luarocks.core.util")
10
11util.popen_read = core.popen_read
12util.cleanup_path = core.cleanup_path
13util.split_string = core.split_string
14util.keys = core.keys
15util.printerr = core.printerr
16util.sortedpairs = core.sortedpairs
9 17
10local unpack = unpack or table.unpack 18local unpack = unpack or table.unpack
11 19
@@ -223,7 +231,7 @@ function util.platform_overrides(tbl)
223 for _, platform in ipairs(cfg.platforms) do 231 for _, platform in ipairs(cfg.platforms) do
224 local platform_tbl = tbl.platforms[platform] 232 local platform_tbl = tbl.platforms[platform]
225 if platform_tbl then 233 if platform_tbl then
226 util.deep_merge(tbl, platform_tbl) 234 core.deep_merge(tbl, platform_tbl)
227 end 235 end
228 end 236 end
229 end 237 end
@@ -243,7 +251,7 @@ local var_format_pattern = "%$%((%a[%a%d_]+)%)"
243-- needed variables. 251-- needed variables.
244-- @param msg string: the warning message to display. 252-- @param msg string: the warning message to display.
245function util.warn_if_not_used(var_defs, needed_set, msg) 253function util.warn_if_not_used(var_defs, needed_set, msg)
246 needed_set = util.make_shallow_copy(needed_set) 254 needed_set = core.make_shallow_copy(needed_set)
247 for _, val in pairs(var_defs) do 255 for _, val in pairs(var_defs) do
248 for used in val:gmatch(var_format_pattern) do 256 for used in val:gmatch(var_format_pattern) do
249 needed_set[used] = nil 257 needed_set[used] = nil
diff --git a/src/luarocks/vers.lua b/src/luarocks/vers.lua
index d37690a8..c1dfbd39 100644
--- a/src/luarocks/vers.lua
+++ b/src/luarocks/vers.lua
@@ -10,7 +10,12 @@
10-- "common sense" heuristics. The precise specification of the 10-- "common sense" heuristics. The precise specification of the
11-- comparison criteria is the source code of this module. 11-- comparison criteria is the source code of this module.
12local vers = {} 12local vers = {}
13setmetatable(vers, { __index = require("luarocks.core.vers") }) 13
14local core = require("luarocks.core.vers")
15
16vers.parse_version = core.parse_version
17vers.compare_versions = core.compare_versions
18vers.match_constraints = core.match_constraints
14 19
15--- Check if rockspec format version satisfies version requirement. 20--- Check if rockspec format version satisfies version requirement.
16-- @param rockspec table: The rockspec table. 21-- @param rockspec table: The rockspec table.