diff --git a/chinook/management/_flatten.py b/chinook/management/_flatten.py index f19d2d0..a83ff56 100644 --- a/chinook/management/_flatten.py +++ b/chinook/management/_flatten.py @@ -21,13 +21,14 @@ class _FlattenContext: _libs_list: list[Accessor] = field(init=False, default_factory=list) _srcs_list: list[Accessor] = field(init=False, default_factory=list) - _incs_set: set[Accessor] = field(init=False, default_factory=set) - _dirs_set: set[Accessor] = field(init=False, default_factory=set) - _opts_set: set[Accessor] = field(init=False, default_factory=set) - _defs_set: set[Accessor] = field(init=False, default_factory=set) - _deps_set: set[Accessor] = field(init=False, default_factory=set) - _libs_set: set[Accessor] = field(init=False, default_factory=set) - _srcs_set: set[Accessor] = field(init=False, default_factory=set) + # Keyed by value so the same entry at different access levels isn't repeated. + _incs_set: set[str] = field(init=False, default_factory=set) + _dirs_set: set[str] = field(init=False, default_factory=set) + _opts_set: set[str] = field(init=False, default_factory=set) + _defs_set: set[str] = field(init=False, default_factory=set) + _deps_set: set[str] = field(init=False, default_factory=set) + _libs_set: set[str] = field(init=False, default_factory=set) + _srcs_set: set[str] = field(init=False, default_factory=set) @property def incs(self) -> list[Accessor]: @@ -68,45 +69,45 @@ class _FlattenContext: for src in target.srcs: self.append_src(src) def append_inc(self, inc: Accessor) -> None: - if inc.level == Access.PRIVATE or inc in self._incs_set: + if inc.level == Access.PRIVATE or str(inc.value) in self._incs_set: return - self._incs_set.add(inc) + self._incs_set.add(str(inc.value)) self._incs_list.insert(0, inc) def append_dir(self, lib: Accessor) -> None: - if lib.level == Access.PRIVATE or lib in self._dirs_set: + if lib.level == Access.PRIVATE or str(lib.value) in self._dirs_set: return - self._dirs_set.add(lib) + self._dirs_set.add(str(lib.value)) self._dirs_list.insert(0, lib) def append_opt(self, opt: Accessor) -> None: - if opt.level == Access.PRIVATE or opt in self._opts_set: + if opt.level == Access.PRIVATE or str(opt.value) in self._opts_set: return - self._opts_set.add(opt) + self._opts_set.add(str(opt.value)) self._opts_list.insert(0, opt) def append_def(self, dfn: Accessor) -> None: - if dfn.level == Access.PRIVATE or dfn in self._defs_set: + if dfn.level == Access.PRIVATE or str(dfn.value) in self._defs_set: return - self._defs_set.add(dfn) + self._defs_set.add(str(dfn.value)) self._defs_list.insert(0, dfn) def append_dep(self, dep: Accessor) -> None: - if dep.level == Access.PRIVATE or dep in self._deps_set: + if dep.level == Access.PRIVATE or str(dep.value) in self._deps_set: return - self._deps_set.add(dep) + self._deps_set.add(str(dep.value)) self._deps_list.insert(0, dep) def append_lib(self, lib: Accessor) -> None: - if lib.level == Access.PRIVATE or lib in self._libs_set: + if lib.level == Access.PRIVATE or str(lib.value) in self._libs_set: return - self._libs_set.add(lib) + self._libs_set.add(str(lib.value)) self._libs_list.insert(0, lib) def append_src(self, src: Accessor) -> None: - if src.level == Access.PRIVATE or src in self._srcs_set: + if src.level == Access.PRIVATE or str(src.value) in self._srcs_set: return - self._srcs_set.add(src) + self._srcs_set.add(str(src.value)) self._srcs_list.insert(0, src) @@ -114,18 +115,7 @@ def flatten_targets(project: Project) -> None: for target in project.targets: if target._flattend: continue - context = _FlattenContext() - context.append_inc(Accessor( - level = Access.PUBLIC \ - if target.name in project.exports \ - else Access.PROTECTED, - value = project.origination.src("include") - )) - context.append_dir(Accessor( - level = Access.PUBLIC, - value = project.destination.libpath - )) - _flatten_target(context, target) + _flatten_target(_target_context(project, target), target) def flatten_tests(project: Project) -> None: for target in project.tests: @@ -142,6 +132,20 @@ def flatten_tests(project: Project) -> None: )) _flatten_test(context, target) +def _target_context(project: Project, target: Target) -> _FlattenContext: + context = _FlattenContext() + context.append_inc(Accessor( + level = Access.PUBLIC \ + if target.name in project.exports \ + else Access.PROTECTED, + value = project.origination.src("include") + )) + context.append_dir(Accessor( + level = Access.PUBLIC, + value = project.destination.libpath + )) + return context + def _flatten_target(context: _FlattenContext, target: Target) -> None: if target._flattend: return @@ -150,14 +154,7 @@ def _flatten_target(context: _FlattenContext, target: Target) -> None: if (parent:=targets_by_name_mapping.get(dependency.value)) == None: raise ValueError(f"No such dependable target: {dependency.value}") _target_inherit_target(context, parent) - target.incs.includes.extend(context.incs) - target.incs.libraries.extend(context.dirs) - target.opts.extend(context.opts) - target.defs.extend(context.defs) - target.deps.extend(context.deps) - target.libs.extend(context.libs) - target.srcs.extend(context.srcs) - target._flattend = True + _extend_target(context, target) def _flatten_test(context: _FlattenContext, target: Target) -> None: if target._flattend: @@ -167,15 +164,25 @@ def _flatten_test(context: _FlattenContext, target: Target) -> None: if (parent:=targets_by_name_mapping.get(dependency.value)) == None: raise ValueError(f"No such dependable target: {dependency.value}") _test_inherit_target(context, parent) - target.incs.includes.extend(context.incs) - target.incs.libraries.extend(context.dirs) - target.opts.extend(context.opts) - target.defs.extend(context.defs) - target.deps.extend(context.deps) - target.libs.extend(context.libs) - target.srcs.extend(context.srcs) + _extend_target(context, target) + +def _extend_target(context: _FlattenContext, target: Target) -> None: + _extend_unique(target.incs.includes, context.incs) + _extend_unique(target.incs.libraries, context.dirs) + _extend_unique(target.opts, context.opts) + _extend_unique(target.defs, context.defs) + _extend_unique(target.deps, context.deps) + _extend_unique(target.libs, context.libs) + _extend_unique(target.srcs, context.srcs) target._flattend = True +def _extend_unique(dst: list[Accessor], src: list[Accessor]) -> None: + values = {str(v.value) for v in dst} + for accessor in src: + if str(accessor.value) not in values: + values.add(str(accessor.value)) + dst.append(accessor) + def _target_inherit_target(context: _FlattenContext, parent: Target) -> None: if parent.name in tests_by_name_mapping: raise ValueError("Cannot inherit tests") @@ -188,7 +195,8 @@ def _inherit_raw(context: _FlattenContext, parent: Target) -> None: parent_project = project_by_targets_mapping.get(parent.name) assert parent_project is not None, "Parent project not mapped!" - _flatten_target(context, parent) + # Parents flatten in their own context so the child's entries don't leak into them. + _flatten_target(_target_context(parent_project, parent), parent) context.inherit_target(parent) context.append_inc(Accessor( level = Access.PROTECTED, @@ -202,4 +210,3 @@ def _inherit_raw(context: _FlattenContext, parent: Target) -> None: level = Access.PROTECTED, value = parent.name )) - diff --git a/chinook/pipeline/_prepare.py b/chinook/pipeline/_prepare.py index 1cb672e..c64e791 100644 --- a/chinook/pipeline/_prepare.py +++ b/chinook/pipeline/_prepare.py @@ -71,7 +71,8 @@ def prepare( libprj = find_project_by_target(libtgt.name) if libtgt.type == Output.LIB: libfile = libprj.destination.lib(library.value) - defopts.libdirs.append(libprj.destination.libpath) + if libprj.destination.libpath not in defopts.libdirs: + defopts.libdirs.append(libprj.destination.libpath) defopts.archives.append(library.value) elif libtgt.type == Output.DLL: libfile = libprj.destination.dll(library.value)