|
15 | 15 | from .utils import fatal, getlocstr, warning |
16 | 16 | from ivpm.package import Package, PackageType, SourceType |
17 | 17 | from ivpm.packages_info import PackagesInfo |
| 18 | +from ivpm.pkg_content_type import parse_type_field |
18 | 19 |
|
19 | 20 |
|
20 | 21 | class IvpmYamlReader(object): |
@@ -50,6 +51,9 @@ def read(self, fp, name) -> 'ProjInfo': |
50 | 51 | else: |
51 | 52 | ret.version = None |
52 | 53 |
|
| 54 | + if "type" in pkg.keys(): |
| 55 | + ret.self_types = parse_type_field(pkg["type"]) |
| 56 | + |
53 | 57 | # Specify where sub-packages are stored. Defaults to 'packages' |
54 | 58 | if "deps-dir" in pkg.keys(): |
55 | 59 | ret.deps_dir = pkg["deps-dir"] |
@@ -105,16 +109,64 @@ def read_dep_sets(self, info : 'ProjInfo', dep_sets): |
105 | 109 | ds = PackagesInfo(ds_name) |
106 | 110 | default_dep_set = None |
107 | 111 |
|
| 112 | + if "uses" in ds_ent.keys(): |
| 113 | + ds.uses = str(ds_ent["uses"]) |
| 114 | + |
108 | 115 | if "default-dep-set" in ds_ent.keys(): |
109 | 116 | default_dep_set = ds_ent["default-dep-set"] |
110 | 117 |
|
111 | | - |
112 | 118 | deps = ds_ent["deps"] |
113 | 119 |
|
114 | 120 | if not isinstance(deps, list): |
115 | 121 | raise Exception("deps is not a list") |
116 | 122 | self.read_deps(ds, ds_ent["deps"], default_dep_set) |
117 | 123 | info.set_dep_set(ds.name, ds) |
| 124 | + |
| 125 | + self._resolve_dep_set_inheritance(info) |
| 126 | + |
| 127 | + def _resolve_dep_set_inheritance(self, info: 'ProjInfo'): |
| 128 | + """ |
| 129 | + Merge inherited packages for every dep-set that declares a 'uses' base. |
| 130 | + The current dep-set's packages win on name collision. |
| 131 | + Detects cycles and unknown base names. |
| 132 | + """ |
| 133 | + dep_set_m = info.dep_set_m |
| 134 | + resolved = set() |
| 135 | + |
| 136 | + def resolve(name, visiting): |
| 137 | + if name in resolved: |
| 138 | + return |
| 139 | + ds = dep_set_m[name] |
| 140 | + if ds.uses is None: |
| 141 | + resolved.add(name) |
| 142 | + return |
| 143 | + if name in visiting: |
| 144 | + cycle = " -> ".join(list(visiting) + [name]) |
| 145 | + raise Exception( |
| 146 | + "Cyclic dep-set inheritance detected: %s" % cycle) |
| 147 | + base_name = ds.uses |
| 148 | + if base_name not in dep_set_m: |
| 149 | + raise Exception( |
| 150 | + "dep-set '%s' references unknown base dep-set '%s'" |
| 151 | + % (name, base_name)) |
| 152 | + visiting.add(name) |
| 153 | + resolve(base_name, visiting) |
| 154 | + visiting.discard(name) |
| 155 | + |
| 156 | + base_ds = dep_set_m[base_name] |
| 157 | + # Start with base packages, then let current overwrite |
| 158 | + merged_pkgs = base_ds.packages.copy() |
| 159 | + merged_pkgs.update(ds.packages) |
| 160 | + ds.packages = merged_pkgs |
| 161 | + |
| 162 | + merged_opts = base_ds.options.copy() |
| 163 | + merged_opts.update(ds.options) |
| 164 | + ds.options = merged_opts |
| 165 | + |
| 166 | + resolved.add(name) |
| 167 | + |
| 168 | + for ds_name in list(dep_set_m.keys()): |
| 169 | + resolve(ds_name, set()) |
118 | 170 |
|
119 | 171 |
|
120 | 172 | def read_deps(self, ret : PackagesInfo, deps, default_dep_set): |
@@ -168,19 +220,21 @@ def read_deps(self, ret : PackagesInfo, deps, default_dep_set): |
168 | 220 | raise Exception("Package %s has unknown type %s" % (d["name"], src)) |
169 | 221 | pkg = PkgTypeRgy.inst().mkPackage(src, str(d["name"]), d, si) |
170 | 222 |
|
171 | | - # Resolve content type and validate 'with:' parameters |
| 223 | + # Resolve content type from 'type:' field (string, dict, or list form). |
| 224 | + # 'with:' is no longer supported; options are now inline in the type dict. |
172 | 225 | ct_rgy = PkgContentTypeRgy.inst() |
| 226 | + if "with" in d.keys(): |
| 227 | + fatal("Package '%s': 'with:' is no longer supported; " |
| 228 | + "use inline options instead, e.g. type: { python: { editable: false } } @ %s" % ( |
| 229 | + pkg.name, getlocstr(d["with"]))) |
173 | 230 | if "type" in d.keys(): |
174 | | - type_name = str(d["type"]) |
175 | | - if not ct_rgy.has(type_name): |
176 | | - fatal("Package '%s': unknown type '%s' @ %s ; known types: %s" % ( |
177 | | - pkg.name, type_name, getlocstr(d["type"]), |
178 | | - ", ".join(ct_rgy.names()))) |
179 | | - with_opts = d["with"] if "with" in d.keys() else {} |
180 | | - pkg.type_data = ct_rgy.get(type_name).create_data(with_opts, si) |
181 | | - elif "with" in d.keys(): |
182 | | - fatal("Package '%s': 'with:' is specified but 'type:' is not @ %s" % ( |
183 | | - pkg.name, getlocstr(d["with"]))) |
| 231 | + raw = parse_type_field(d["type"]) |
| 232 | + for type_name, opts in raw: |
| 233 | + if not ct_rgy.has(type_name): |
| 234 | + fatal("Package '%s': unknown type '%s' @ %s ; known types: %s" % ( |
| 235 | + pkg.name, type_name, getlocstr(d["type"]), |
| 236 | + ", ".join(ct_rgy.names()))) |
| 237 | + pkg.type_data.append(ct_rgy.get(type_name).create_data(opts, si)) |
184 | 238 |
|
185 | 239 | # Unless specified, load the same dep-set from sub-packages |
186 | 240 | if pkg.dep_set is None: |
|
0 commit comments