diff options
author | Hisham Muhammad <hisham@gobolinux.org> | 2018-06-06 14:27:07 -0300 |
---|---|---|
committer | Hisham Muhammad <hisham@gobolinux.org> | 2018-06-14 14:06:51 -0300 |
commit | f8c877d486af39fc563b8a4107092682d49e4ae9 (patch) | |
tree | 808ae48d9d00aac713696093a251b6321fb68606 | |
parent | 2d739d1ab3c0027fb677ee6007bc9269800eb18d (diff) | |
download | luarocks-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.lua | 100 | ||||
-rw-r--r-- | src/luarocks/type/rockspec.lua | 222 | ||||
-rw-r--r-- | src/luarocks/type_check.lua | 82 | ||||
-rw-r--r-- | src/luarocks/util.lua | 13 |
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 | ||
3 | local type_check = require("luarocks.type_check") | 3 | local type_check = require("luarocks.type_check") |
4 | 4 | ||
5 | local number_1 = type_check.number_1 | 5 | local manifest_formats = type_check.declare_schemas({ |
6 | local string_1 = type_check.string_1 | 6 | ["1.0"] = { |
7 | local mandatory_string_1 = type_check.mandatory_string_1 | 7 | repository = { |
8 | 8 | _mandatory = true, | |
9 | local 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. |
74 | function type_manifest.check(manifest, globals) | 71 | function 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, "") |
79 | end | 77 | end |
80 | 78 | ||
81 | return type_manifest | 79 | return 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 | ||
5 | type_rockspec.rockspec_format = "3.0" | 5 | type_rockspec.rockspec_format = "3.0" |
6 | 6 | ||
7 | local string_1 = type_check.string_1 | ||
8 | local mandatory_string_1 = type_check.mandatory_string_1 | ||
9 | |||
10 | local string_3 = { _type = "string", _version = "3.0" } | ||
11 | local 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 | ||
27 | local rockspec_types = { | 20 | local 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 | ||
132 | type_rockspec.order = {"rockspec_format", "package", "version", | 138 | type_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 | ||
140 | rockspec_types.build.platforms._any = rockspec_types.build | ||
141 | rockspec_types.dependencies.platforms._any = rockspec_types.dependencies | ||
142 | rockspec_types.build_dependencies.platforms._any = rockspec_types.build_dependencies | ||
143 | rockspec_types.external_dependencies.platforms._any = rockspec_types.external_dependencies | ||
144 | rockspec_types.source.platforms._any = rockspec_types.source | ||
145 | rockspec_types.hooks.platforms._any = rockspec_types.hooks | ||
146 | rockspec_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. |
154 | function type_rockspec.check(rockspec, globals) | 152 | function 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 .. ")" |
167 | end | 167 | end |
168 | 168 | ||
169 | return type_rockspec | 169 | return 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 | ||
4 | local cfg = require("luarocks.core.cfg") | 4 | local cfg = require("luarocks.core.cfg") |
5 | local vers = require("luarocks.core.vers") | 5 | local vers = require("luarocks.core.vers") |
6 | local util = require("luarocks.util") | ||
6 | local require = nil | 7 | local require = nil |
7 | -------------------------------------------------------------------------------- | 8 | -------------------------------------------------------------------------------- |
8 | 9 | ||
9 | type_check.string_1 = { _type = "string" } | 10 | type_check.MAGIC_PLATFORMS = {} |
10 | type_check.number_1 = { _type = "number" } | 11 | |
11 | type_check.mandatory_string_1 = { _type = "string", _mandatory = true } | 12 | do |
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 | ||
83 | end | ||
84 | |||
85 | -------------------------------------------------------------------------------- | ||
12 | 86 | ||
13 | local function check_version(version, typetbl, context) | 87 | local 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 | |||
15 | util.printerr = core.printerr | 15 | util.printerr = core.printerr |
16 | util.sortedpairs = core.sortedpairs | 16 | util.sortedpairs = core.sortedpairs |
17 | util.warning = core.warning | 17 | util.warning = core.warning |
18 | util.deep_merge = core.deep_merge | ||
18 | 19 | ||
19 | local unpack = unpack or table.unpack | 20 | local 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 |
480 | end | 481 | end |
481 | 482 | ||
483 | function 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 | ||
493 | end | ||
494 | |||
482 | return util | 495 | return util |