aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHisham Muhammad <hisham@gobolinux.org>2018-06-06 14:27:07 -0300
committerHisham Muhammad <hisham@gobolinux.org>2018-06-14 14:06:51 -0300
commitf8c877d486af39fc563b8a4107092682d49e4ae9 (patch)
tree808ae48d9d00aac713696093a251b6321fb68606
parent2d739d1ab3c0027fb677ee6007bc9269800eb18d (diff)
downloadluarocks-f8c877d486af39fc563b8a4107092682d49e4ae9.tar.gz
luarocks-f8c877d486af39fc563b8a4107092682d49e4ae9.tar.bz2
luarocks-f8c877d486af39fc563b8a4107092682d49e4ae9.zip
Refactor type checking to allow mandatory attribute to be dropped
-rw-r--r--src/luarocks/type/manifest.lua100
-rw-r--r--src/luarocks/type/rockspec.lua222
-rw-r--r--src/luarocks/type_check.lua82
-rw-r--r--src/luarocks/util.lua13
4 files changed, 251 insertions, 166 deletions
diff --git a/src/luarocks/type/manifest.lua b/src/luarocks/type/manifest.lua
index f58ff984..407428b2 100644
--- a/src/luarocks/type/manifest.lua
+++ b/src/luarocks/type/manifest.lua
@@ -2,59 +2,57 @@ local type_manifest = {}
2 2
3local type_check = require("luarocks.type_check") 3local type_check = require("luarocks.type_check")
4 4
5local number_1 = type_check.number_1 5local manifest_formats = type_check.declare_schemas({
6local string_1 = type_check.string_1 6 ["1.0"] = {
7local mandatory_string_1 = type_check.mandatory_string_1 7 repository = {
8 8 _mandatory = true,
9local manifest_types = { 9 -- packages
10 repository = {
11 _mandatory = true,
12 -- packages
13 _any = {
14 -- versions
15 _any = { 10 _any = {
16 -- items 11 -- versions
17 _any = { 12 _any = {
18 arch = mandatory_string_1, 13 -- items
19 modules = { _any = string_1 }, 14 _any = {
20 commands = { _any = string_1 }, 15 arch = { _type = "string", _mandatory = true },
21 dependencies = { _any = string_1 }, 16 modules = { _any = { _type = "string" } },
22 -- TODO: to be extended with more metadata. 17 commands = { _any = { _type = "string" } },
18 dependencies = { _any = { _type = "string" } },
19 -- TODO: to be extended with more metadata.
20 }
23 } 21 }
24 } 22 }
25 } 23 },
26 }, 24 modules = {
27 modules = { 25 _mandatory = true,
28 _mandatory = true, 26 -- modules
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 = { 27 _any = {
48 -- each dependency 28 -- providers
29 _any = { _type = "string" }
30 }
31 },
32 commands = {
33 _mandatory = true,
34 -- modules
35 _any = {
36 -- commands
37 _any = { _type = "string" }
38 }
39 },
40 dependencies = {
41 -- each module
42 _any = {
43 -- each version
49 _any = { 44 _any = {
50 name = string_1, 45 -- each dependency
51 constraints = { 46 _any = {
52 _any = { 47 name = { _type = "string" },
53 no_upgrade = { _type = "boolean" }, 48 constraints = {
54 op = string_1, 49 _any = {
55 version = { 50 no_upgrade = { _type = "boolean" },
56 string = string_1, 51 op = { _type = "string" },
57 _any = number_1, 52 version = {
53 string = { _type = "string" },
54 _any = { _type = "number" },
55 }
58 } 56 }
59 } 57 }
60 } 58 }
@@ -62,8 +60,7 @@ local manifest_types = {
62 } 60 }
63 } 61 }
64 } 62 }
65} 63})
66
67 64
68--- Type check a manifest table. 65--- Type check a manifest table.
69-- Verify the correctness of elements from a 66-- Verify the correctness of elements from a
@@ -73,9 +70,10 @@ local manifest_types = {
73-- succeeded, or nil and an error message if it failed. 70-- succeeded, or nil and an error message if it failed.
74function type_manifest.check(manifest, globals) 71function type_manifest.check(manifest, globals)
75 assert(type(manifest) == "table") 72 assert(type(manifest) == "table")
76 local ok, err = type_check.check_undeclared_globals(globals, manifest_types) 73 local format = manifest_formats["1.0"]
74 local ok, err = type_check.check_undeclared_globals(globals, format)
77 if not ok then return nil, err end 75 if not ok then return nil, err end
78 return type_check.type_check_table("1.0", manifest, manifest_types, "") 76 return type_check.type_check_table("1.0", manifest, format, "")
79end 77end
80 78
81return type_manifest 79return type_manifest
diff --git a/src/luarocks/type/rockspec.lua b/src/luarocks/type/rockspec.lua
index 16ab911c..8b2bdffa 100644
--- a/src/luarocks/type/rockspec.lua
+++ b/src/luarocks/type/rockspec.lua
@@ -4,18 +4,11 @@ local type_check = require("luarocks.type_check")
4 4
5type_rockspec.rockspec_format = "3.0" 5type_rockspec.rockspec_format = "3.0"
6 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: 7-- Syntax for type-checking tables:
14-- 8--
15-- A type-checking table describes typing data for a value. 9-- A type-checking table describes typing data for a value.
16-- Any key starting with an underscore has a special meaning: 10-- Any key starting with an underscore has a special meaning:
17-- _type (string) is the Lua type of the value. Default is "table". 11-- _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. 12-- _mandatory (boolean) indicates if the value is a mandatory key in its container table. Default is false.
20-- For "string" types only: 13-- For "string" types only:
21-- _pattern (string) is the string-matching pattern, valid for string types only. Default is ".*". 14-- _pattern (string) is the string-matching pattern, valid for string types only. Default is ".*".
@@ -24,110 +17,123 @@ local list_of_strings_3 = { _any = string_3, _version = "3.0" }
24-- _more (boolean) indicates that the table accepts unspecified keys and does not type-check them. 17-- _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. 18-- Any other string keys that don't start with an underscore represent known keys and are type-checking tables, recursively checked.
26 19
27local rockspec_types = { 20local rockspec_formats = type_check.declare_schemas({
28 rockspec_format = string_1, 21 ["1.0"] = {
29 package = mandatory_string_1, 22 rockspec_format = { _type = "string" },
30 version = { _type = "string", _pattern = "[%w.]+-[%d]+", _mandatory = true }, 23 package = { _type = "string", _mandatory = true },
31 description = { 24 version = { _type = "string", _pattern = "[%w.]+-[%d]+", _mandatory = true },
32 summary = string_1, 25 description = {
33 detailed = string_1, 26 summary = { _type = "string" },
34 homepage = string_1, 27 detailed = { _type = "string" },
35 license = string_1, 28 homepage = { _type = "string" },
36 maintainer = string_1, 29 license = { _type = "string" },
37 labels = list_of_strings_3, 30 maintainer = { _type = "string" },
38 issues_url = string_3, 31 },
39 }, 32 dependencies = {
40 dependencies = { 33 platforms = type_check.MAGIC_PLATFORMS,
41 platforms = {}, -- recursively defined below 34 _any = {
42 _any = { 35 _type = "string",
43 _type = "string", 36 _name = "a valid dependency string",
44 _name = "a valid dependency string", 37 _pattern = "%s*([a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)",
45 _patterns = {
46 ["1.0"] = "%s*([a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)",
47 ["3.0"] = "%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)",
48 }, 38 },
49 }, 39 },
50 }, 40 supported_platforms = {
51 build_dependencies = { 41 _any = { _type = "string" },
52 _version = "3.0",
53 platforms = {}, -- recursively defined below
54 _any = {
55 _type = "string",
56 _name = "a valid dependency string",
57 _pattern = "%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)",
58 }, 42 },
59 }, 43 external_dependencies = {
60 test_dependencies = { 44 platforms = type_check.MAGIC_PLATFORMS,
61 _version = "3.0", 45 _any = {
62 platforms = {}, -- recursively defined below 46 program = { _type = "string" },
63 _any = { 47 header = { _type = "string" },
64 _type = "string", 48 library = { _type = "string" },
65 _name = "a valid dependency string", 49 }
66 _pattern = "%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)", 50 },
51 source = {
52 _mandatory = true,
53 platforms = type_check.MAGIC_PLATFORMS,
54 url = { _type = "string", _mandatory = true },
55 md5 = { _type = "string" },
56 file = { _type = "string" },
57 dir = { _type = "string" },
58 tag = { _type = "string" },
59 branch = { _type = "string" },
60 module = { _type = "string" },
61 cvs_tag = { _type = "string" },
62 cvs_module = { _type = "string" },
63 },
64 build = {
65 platforms = type_check.MAGIC_PLATFORMS,
66 type = { _type = "string" },
67 install = {
68 lua = {
69 _more = true
70 },
71 lib = {
72 _more = true
73 },
74 conf = {
75 _more = true
76 },
77 bin = {
78 _more = true
79 }
80 },
81 copy_directories = {
82 _any = { _type = "string" },
83 },
84 _more = true,
85 _mandatory = true
86 },
87 hooks = {
88 platforms = type_check.MAGIC_PLATFORMS,
89 post_install = { _type = "string" },
67 }, 90 },
68 }, 91 },
69 supported_platforms = { 92
70 _any = string_1, 93 ["1.1"] = {
71 }, 94 deploy = {
72 external_dependencies = { 95 wrap_bin_scripts = { _type = "boolean" },
73 platforms = {}, -- recursively defined below
74 _any = {
75 program = string_1,
76 header = string_1,
77 library = string_1,
78 } 96 }
79 }, 97 },
80 source = { 98
81 _mandatory = true, 99 ["3.0"] = {
82 platforms = {}, -- recursively defined below 100 description = {
83 url = mandatory_string_1, 101 labels = {
84 md5 = string_1, 102 _any = { _type = "string" }
85 file = string_1,
86 dir = string_1,
87 tag = string_1,
88 branch = string_1,
89 module = string_1,
90 cvs_tag = string_1,
91 cvs_module = string_1,
92 },
93 build = {
94 platforms = {}, -- recursively defined below
95 type = string_1,
96 install = {
97 lua = {
98 _more = true
99 }, 103 },
100 lib = { 104 issues_url = { _type = "string" },
101 _more = true 105 },
106 dependencies = {
107 _any = {
108 _pattern = "%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)",
102 }, 109 },
103 conf = { 110 },
104 _more = true 111 build_dependencies = {
112 platforms = type_check.MAGIC_PLATFORMS,
113 _any = {
114 _type = "string",
115 _name = "a valid dependency string",
116 _pattern = "%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)",
117 },
118 },
119 test_dependencies = {
120 platforms = type_check.MAGIC_PLATFORMS,
121 _any = {
122 _type = "string",
123 _name = "a valid dependency string",
124 _pattern = "%s*([a-zA-Z0-9%.%-%_]*/?[a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*([^/]*)",
105 }, 125 },
106 bin = {
107 _more = true
108 }
109 }, 126 },
110 copy_directories = { 127 build = {
111 _any = string_1, 128 _mandatory = false,
129 },
130 test = {
131 platforms = type_check.MAGIC_PLATFORMS,
132 type = { _type = "string" },
133 _more = true,
112 }, 134 },
113 _more = true,
114 _mandatory = true
115 },
116 test = {
117 _version = "3.0",
118 platforms = {}, -- recursively defined below
119 type = string_1,
120 _more = true,
121 },
122 hooks = {
123 platforms = {}, -- recursively defined below
124 post_install = string_1,
125 },
126 deploy = {
127 _version = "1.1",
128 wrap_bin_scripts = { _type = "boolean", _version = "1.1" },
129 } 135 }
130} 136})
131 137
132type_rockspec.order = {"rockspec_format", "package", "version", 138type_rockspec.order = {"rockspec_format", "package", "version",
133 { "source", { "url", "tag", "branch", "md5" } }, 139 { "source", { "url", "tag", "branch", "md5" } },
@@ -137,14 +143,6 @@ type_rockspec.order = {"rockspec_format", "package", "version",
137 "test_dependencies", { "test", {"type"} }, 143 "test_dependencies", { "test", {"type"} },
138 "hooks"} 144 "hooks"}
139 145
140rockspec_types.build.platforms._any = rockspec_types.build
141rockspec_types.dependencies.platforms._any = rockspec_types.dependencies
142rockspec_types.build_dependencies.platforms._any = rockspec_types.build_dependencies
143rockspec_types.external_dependencies.platforms._any = rockspec_types.external_dependencies
144rockspec_types.source.platforms._any = rockspec_types.source
145rockspec_types.hooks.platforms._any = rockspec_types.hooks
146rockspec_types.test.platforms._any = rockspec_types.test
147
148--- Type check a rockspec table. 146--- Type check a rockspec table.
149-- Verify the correctness of elements from a 147-- Verify the correctness of elements from a
150-- rockspec table, reporting on unknown fields and type 148-- rockspec table, reporting on unknown fields and type
@@ -153,17 +151,19 @@ rockspec_types.test.platforms._any = rockspec_types.test
153-- succeeded, or nil and an error message if it failed. 151-- succeeded, or nil and an error message if it failed.
154function type_rockspec.check(rockspec, globals) 152function type_rockspec.check(rockspec, globals)
155 assert(type(rockspec) == "table") 153 assert(type(rockspec) == "table")
156 if not rockspec.rockspec_format then 154 local version = rockspec.rockspec_format or "1.0"
157 rockspec.rockspec_format = "1.0" 155 local schema = rockspec_formats[version]
156 if not schema then
157 return nil, "unknown rockspec format " .. version
158 end 158 end
159 local ok, err = type_check.check_undeclared_globals(globals, rockspec_types) 159 local ok, err = type_check.check_undeclared_globals(globals, schema)
160 if ok then 160 if ok then
161 ok, err = type_check.type_check_table(rockspec.rockspec_format, rockspec, rockspec_types, "") 161 ok, err = type_check.type_check_table(version, rockspec, schema, "")
162 end 162 end
163 if ok then 163 if ok then
164 return true 164 return true
165 end 165 end
166 return nil, err .. " (rockspec format " .. rockspec.rockspec_format .. ")" 166 return nil, err .. " (rockspec format " .. version .. ")"
167end 167end
168 168
169return type_rockspec 169return type_rockspec
diff --git a/src/luarocks/type_check.lua b/src/luarocks/type_check.lua
index ebd59f85..827c64b6 100644
--- a/src/luarocks/type_check.lua
+++ b/src/luarocks/type_check.lua
@@ -3,12 +3,86 @@ local type_check = {}
3 3
4local cfg = require("luarocks.core.cfg") 4local cfg = require("luarocks.core.cfg")
5local vers = require("luarocks.core.vers") 5local vers = require("luarocks.core.vers")
6local util = require("luarocks.util")
6local require = nil 7local require = nil
7-------------------------------------------------------------------------------- 8--------------------------------------------------------------------------------
8 9
9type_check.string_1 = { _type = "string" } 10type_check.MAGIC_PLATFORMS = {}
10type_check.number_1 = { _type = "number" } 11
11type_check.mandatory_string_1 = { _type = "string", _mandatory = true } 12do
13 local function fill_in_version(tbl, version)
14 for _, v in pairs(tbl) do
15 if type(v) == "table" then
16 if v._version == nil then
17 v._version = version
18 end
19 fill_in_version(v)
20 end
21 end
22 end
23
24 local function expand_magic_platforms(tbl)
25 for k,v in pairs(tbl) do
26 if v == type_check.MAGIC_PLATFORMS then
27 tbl[k] = {
28 _any = util.deep_copy(tbl)
29 }
30 tbl[k]._any[v] = nil
31 elseif type(v) == "table" then
32 expand_magic_platforms(v)
33 end
34 end
35 end
36
37 local function merge_under(dst, src)
38 for k, v in pairs(src) do
39 if dst[k] == nil then
40 if type(dst[k]) == "table" then
41 util.deep_merge(dst[k], v)
42 else
43 dst[k] = v
44 end
45 end
46 end
47 end
48
49 -- Build a table of schemas.
50 -- @param versions a table where each key is a version number as a string,
51 -- and the value is a schema specification. Schema versions are considered
52 -- incremental: version "2.0" only needs to specify what's new/changed from
53 -- version "1.0".
54 function type_check.declare_schemas(versions)
55 local schemas = {}
56 local parent_version
57
58 local version_list = {}
59 -- FIXME sorting lexicographically! "1.9" > "1.10"
60 for version, schema in util.sortedpairs(versions) do
61 table.insert(version_list, version)
62 if parent_version ~= nil then
63 local copy = util.deep_copy(schemas[parent_version])
64 util.deep_merge(copy, schema)
65 schemas[version] = copy
66 else
67 schemas[version] = schema
68 end
69 fill_in_version(schemas[version], version)
70 expand_magic_platforms(schemas[version])
71 parent_version = version
72 end
73
74 -- Merge future versions as fallbacks under the old versions,
75 -- so that error messages can inform users when they try
76 -- to use new features without bumping rockspec_format in their rockspecs.
77 for i = #version_list, 2, -1 do
78 merge_under(schemas[version_list[i - 1]], schemas[version_list[i]])
79 end
80
81 return schemas
82 end
83end
84
85--------------------------------------------------------------------------------
12 86
13local function check_version(version, typetbl, context) 87local function check_version(version, typetbl, context)
14 local typetbl_version = typetbl._version or "1.0" 88 local typetbl_version = typetbl._version or "1.0"
@@ -55,7 +129,7 @@ local function type_check_item(version, item, typetbl, context)
55 if item_type ~= "string" then 129 if item_type ~= "string" then
56 return nil, "Type mismatch on field "..context..": expected a string, got "..item_type 130 return nil, "Type mismatch on field "..context..": expected a string, got "..item_type
57 end 131 end
58 local pattern = (typetbl._patterns and typetbl._patterns[version]) or typetbl._pattern 132 local pattern = typetbl._pattern
59 if pattern then 133 if pattern then
60 if not item:match("^"..pattern.."$") then 134 if not item:match("^"..pattern.."$") then
61 local what = typetbl._name or ("'"..pattern.."'") 135 local what = typetbl._name or ("'"..pattern.."'")
diff --git a/src/luarocks/util.lua b/src/luarocks/util.lua
index cd946451..f5506f53 100644
--- a/src/luarocks/util.lua
+++ b/src/luarocks/util.lua
@@ -15,6 +15,7 @@ util.keys = core.keys
15util.printerr = core.printerr 15util.printerr = core.printerr
16util.sortedpairs = core.sortedpairs 16util.sortedpairs = core.sortedpairs
17util.warning = core.warning 17util.warning = core.warning
18util.deep_merge = core.deep_merge
18 19
19local unpack = unpack or table.unpack 20local unpack = unpack or table.unpack
20 21
@@ -479,4 +480,16 @@ function util.split_namespace(ns_name)
479 return ns_name 480 return ns_name
480end 481end
481 482
483function util.deep_copy(tbl)
484 local copy = {}
485 for k, v in pairs(tbl) do
486 if type(v) == "table" then
487 copy[k] = util.deep_copy(v)
488 else
489 copy[k] = v
490 end
491 end
492 return copy
493end
494
482return util 495return util