Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Install dependencies first #3667

Merged
merged 1 commit into from
Sep 13, 2022
Merged

Conversation

HebaruSan
Copy link
Member

@HebaruSan HebaruSan commented Sep 12, 2022

Motivation

@siimav reports that many RP-1 users have been having problems with TexturesUnlimited and ROLib when installing via CKAN, which have been attributed to some speculated property of the exFAT filesystem (generally used for thumb drives):

image

I suspect it's not sorting by creation date per se (since why would Microsoft have bothered to do that, just for exFAT?), but rather just returning the files in disk order, which would correspond to the order in which they were created on exFAT. If that's the case, then "normal" listing behavior for other filesystems may also be disk order (simpler/more consistent code in the kernel), but maybe the OS sorts the files alphanumerically at time of creation, since traditional hard drives don't have limits to the number of times they can be written the way flash drives do? In other words:

Filesystem Speculated behavior at file creation
NTFS Append file to directory listing, sort the inodes by name for neatness
exFAT Append file to directory listing, don't sort to reduce IO load on device

We're still looking for confirmation of this or any other similar explanation.

Regardless of how that search turns out, this is something I've thought about doing anyway, since it makes a big changeset more similar to a concatenation of the smaller changesets that it contains. In other words, if B depends on A, I could install A in one changeset and B in a later one, but I couldn't install B by itself first; so if I install both together in one changeset, it likewise makes sense for A to be installed before B.

Changes

  • RelationshipResolver now tracks multiple reasons per module
    • RelationshipResolver.ReasonFor is renamed ReasonsFor and returns a List instead of one value
    • Places using it are updated to search the list
    • Internal to RelationshipResolver, Reasons.Add is replaced by calling a new AddReason function that adds or appends to a List
    • The RelationshipResolver now adds a reason when we find that a dependency is satisfied by a mod already in the list, so all relationships will be represented
    • RelationshipResolver.ReasonStringFor was not in use and is removed
    • RelationshipResolver.ModList now returns its mods in an ordering based on dependencies instead of the random order of a HashSet's keys (see below)
  • ModChange now also tracks multiple reasons
    • ModChange.Description calls a new SelectionReason.DescribeWith function to merge all dependency reasons into one comma-delimited list
  • The GUI changeset tab now shows mods in the order they will be installed rather than a custom order
    • The English text of "To satisfy dependency from" is shortened to "Dependency of" to make room for more modules
    • All of the reasons are shown, and the English header for that column is now plural

The sorting algorithm

The goal is for a dependency to be installed before its depending mods, which implicitly includes indirect dependencies. This is not well suited to a traditional .Sort() call because given any two mods, you can't necessarily tell which one should go first without checking all of the intermediary dependency mods. So instead we use a procedural alrorithm based around an insertion sort.

First we sort the mods in ascending order of how many dependencies they have (so mods with no dependencies float to the top and are installed first), then alphanumerically by name (so the list doesn't look completely random on casual inspection).

Then we step through that list module by module and perform an insertion sort into what will be our final list. If the module we're inserting has an install reason indicating a dependency on any of the mods already in the final list, then we insert it before the first such dependency. Otherwise we add it to the end.

Circular dependencies complicate this; if A depends on B and B depends on A, then either ordering of those two mods would be acceptable. I don't know exactly what to do about that, so I'm just letting it pick whatever ordering it likes for those mods using the same logic as for everything else.

As a special case, user-requested mods are not sorted before non-user-requested mods, even if there is a dependency relationship, since we know they weren't pulled in as dependencies. This way you can see the ones you picked at the bottom, even if they have circular dependencies with other mods.

For confirmation that this puts TexturesUnlimited before ROLib:

image

@HebaruSan HebaruSan added Enhancement New features or functionality GUI Issues affecting the interactive GUI Core (ckan.dll) Issues affecting the core part of CKAN Pull request Windows Issues specific for Windows Relationships Issues affecting depends, recommends, etc. labels Sep 12, 2022
Copy link
Member

@techman83 techman83 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that makes sense and looks like a reasonable approach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Core (ckan.dll) Issues affecting the core part of CKAN Enhancement New features or functionality GUI Issues affecting the interactive GUI Relationships Issues affecting depends, recommends, etc. Windows Issues specific for Windows
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants