Skip to content

Allow for extensions on a per filesystem view basis#3227

Closed
obreitwi wants to merge 1 commit intospack:developfrom
electronicvisions:feature/fsview_extensions
Closed

Allow for extensions on a per filesystem view basis#3227
obreitwi wants to merge 1 commit intospack:developfrom
electronicvisions:feature/fsview_extensions

Conversation

@obreitwi
Copy link
Copy Markdown
Member

Resolves #1789

In our scenario we have a a globally installed (i.e., read-only) set of
packages from which each user should then be able to add a subset to
local filesystem views. However, the current extension mechanism only
allows for one set of extensions to be activated for any given extendee.

One way to circumvent this issue is to use a module system, but this way
environment variables tend to get very long after a short while.
Furthermore, since an error is raised when two files have the same
target in the view, the user can immediately spot when multiple specs
from one package are loaded (an error that sill creeps up from time to
time).

In order to support multiple (and maybe mutually exclusive)
configurations of extensions for a given extendee, the following changes
were made. The aim is to silently activate any specified extensions when
adding them to the filesystem view; no actions from the user required.

TL;DR: Adding python packages built with py-setuptools to fileystem
views without globally activating them should "just work".

  • DirectoryLayout/Package: All extension-related command accept a
    path_view argument restricting extension operations to the given
    filesystem view.

  • Package: {do_,}{de,}activate-functions also accept a path_view
    argument. If path_view is not None, the necessary extension files
    are linked into the view instead of the extendee's install directory.

  • LinkTree: merge-method now accepts a link argument to allow for
    both symlinks and hardlinks.

  • view: All linking operations have been refactored to use LinkTrees
    instead of custom code.

  • view: Check extensions prior to linking packages, activate
    extensions in filesystem view afterwards.

  • view statlink: The --verbose option now prints the short_spec of
    packages linked.

  • view statlink: status and remove now accept --all argument to
    display the status of/remove all packages in the filesystem view.

  • view statlink: Now uses display_specs-function for prettier
    output.

  • view remove: Only removes packages specified by user, packages
    depending on those packages and packages that only user-specified
    packages depend on.

I am currently working on some meaningful tests (I did not find any for the view command on its own to start from), but would appreciate some feedback in the meantime.

@obreitwi obreitwi changed the title Allow for extensions on a per filesystem basis Allow for extensions on a per filesystem view basis Feb 23, 2017
@paulhopkins
Copy link
Copy Markdown
Contributor

This looks broadly similar to my code mentioned in #3245. One thing I did add later was removing absolute references from merged easy-install.pth as dependencies were activated anyway and the file ends up with lots of duplicates. Maybe this does that as well, but I did not see it.

@citibeth
Copy link
Copy Markdown
Member

This is clearly the right direction. spack activate fundamentally breaks Spack, for the reasons given above; and this is a way to do a similar thing that does not break Spack. (I personally have been happy with loading modules).

@muffgaga
Copy link
Copy Markdown
Contributor

@citibeth wrote:

[...] (I personally have been happy with loading modules).

We started to get quite unhappy when we crossed the 50 entries per environment variable barrier (we basically dropped all system-installed software and rely 100% on spack for all our software dependencies).
→ You cannot open manpages anymore, loading python gets incredible slow, …

Due to @obreitwi's change we (@electronicvisions) are all happy again ;)

@citibeth
Copy link
Copy Markdown
Member

citibeth commented Feb 28, 2017 via email

@obreitwi obreitwi force-pushed the feature/fsview_extensions branch from fe0cc72 to e2d3ed6 Compare February 28, 2017 15:40
@obreitwi
Copy link
Copy Markdown
Member Author

obreitwi commented Feb 28, 2017

@citibeth yeah, we use NFS and it is unbearable...

Just added a missing feature that locally and globally activated extensions are handled correctly (forgot about this as we do not plan on using any globally activated extensions).

@paulhopkins how about using os.path.realpath to normalize all paths, make sure there are no duplicates and finally os.path.relpath to generate a pretty easy_install.pth file?

@obreitwi obreitwi force-pushed the feature/fsview_extensions branch 2 times, most recently from 8ce59bc to 216f074 Compare March 28, 2017 19:24
@obreitwi
Copy link
Copy Markdown
Member Author

Some further functionality (plus a rebase on current develop branch):

  • Added -i/--ignore-conflicts command line option to print warnings
    instead of raising an exception whenever conflicting files are found.
    Please note that this only works for linking non-extensions as the
    activation-procedure should take care of conflicting files in
    extensions.

  • To that end, LinkTree now has the option to report_conflicts instead
    of raising assertions when merge-ing.

  • When activating a python extension, the easy_install.pth file is
    pruned from duplicated lines (while preserving the original order).

@obreitwi
Copy link
Copy Markdown
Member Author

Actually, I just noticed #3587, which solves the problem of filesystem views being unusable with python extensions in general, still, we need this PR to have different sets of activated extensions in each view within one spack installation.

@obreitwi obreitwi force-pushed the feature/fsview_extensions branch from 216f074 to 87f610f Compare March 28, 2017 19:39
@scheibelp
Copy link
Copy Markdown
Member

I've spoken with @tgamblin and I will be reviewing this over the next couple days.

Two initial notes:

  • I notice you generally check for things like if path_view is None vs. if not path_view - are there times when the empty string would be a valid path_view?
  • When I build "git" in your PR and then try spack view soft sandbox/ git I get the error Specs passed to a DirectoryLayout must be concrete!. I don't think this is on account of the merge conflict (that appears to just be imports). The original view logic appears to be able to do this. If you're not sure I can look into it myself.

@obreitwi obreitwi force-pushed the feature/fsview_extensions branch from 87f610f to 03724ff Compare April 13, 2017 09:37
@obreitwi
Copy link
Copy Markdown
Member Author

Thank you for taking the time.

  • You are correct, there is no point in differentiating between None and "", an empty path is never a valid location. I have refactored accordingly.

  • We did, however, encounter something similar a while back, but I did not find the time to properly investigate:
    In our case, the error occurred because the .extendee_spec of a given package was still present, even though the package was not configured to be an extension. If I understood the code correctly, this is not supposed to happen.
    Upon (quick) second investigation it might also be the case that the flatten(...)-method should simply use concretized() instead…

I will test this out as soon as I have time!

@scheibelp
Copy link
Copy Markdown
Member

scheibelp commented Apr 13, 2017

When I build "git" in your PR and then try spack view soft sandbox/ git I get the error Specs passed to a DirectoryLayout must be concrete!.

If you modify check_is_extension like so then this issue should be resolved:

    if not ext_spec.package.is_extension:
        return False

    # TODO: Update this once more than one extendee is allowed
    #  for spec in ext_spec.package.extendees:
    spec = ext_spec.package.extendee_spec
    if spec not in ext_spec:
        print "not actually an extension", ext_spec.name
        return False

Main issue is that package.is_extension checks whether the package could be an extension. In the case where I was trying to link in git, the libxml2 package conditionally extends python but in my case actually doesn't, so when you grab extendee_spec you're not actually getting an installed package.

EDIT: my mistake - Package should deal with this (and you shouldn't have to)

@scheibelp
Copy link
Copy Markdown
Member

In our case, the error occurred because the .extendee_spec of a given package was still present, even though the package was not configured to be an extension. If I understood the code correctly, this is not supposed to happen.

Oh I see what you're saying there. Yeah in this case that is more likely something that should be handled in package.py

@scheibelp
Copy link
Copy Markdown
Member

When I build "git" in your PR and then try spack view soft sandbox/ git I get the error Specs passed to a DirectoryLayout must be concrete!.

I edited my follow-up comment but I also wanted to make explicit that this is not an issue with this PR.

@scheibelp
Copy link
Copy Markdown
Member

Upon (quick) second investigation it might also be the case that the flatten(...)-method should simply use concretized() instead…

It shouldn't need to call normalized or concretized (i.e. just use spec.traverse()) since at that point the function is working with specs resolved with the disambiguate_specs function (which fetches installed specs from the database).

I can avoid the error I mentioned by updating Package. I'll try to get a PR out for it by the end of tomorrow.

continue

paths.append(line)
line = os.path.realpath(line)
Copy link
Copy Markdown
Member

@scheibelp scheibelp Apr 13, 2017

Choose a reason for hiding this comment

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

Some of the easy-install.pth files contain lines like:

./Babel-2.3.4-py2.7.egg

In which case realpath may expand relative to the current directory (vs. the directory where the easy-install.pth file is)

As you mention, it appears that #3587 will do away with easy-install.pth files. I'll add a comment there that perhaps this entire function should disappear. If it can't for some reason then IMO this issue should be fixed.

@scheibelp
Copy link
Copy Markdown
Member

I just noticed #3587, which solves the problem of filesystem views being unusable with python extensions in general,

I don't think #3587 fully resolves this, since you take the effort to use activate logic for extensions you benefit from whatever files they choose not to merge. In the case of python modules that includes "site.py"

I'm noticing an extra file that is added is .spack-empty - do you know where that comes from?

@obreitwi
Copy link
Copy Markdown
Member Author

obreitwi commented Apr 14, 2017 via email

@scheibelp
Copy link
Copy Markdown
Member

When I merge #3853 into your PR I can add git to a view

apparently there are still some cases left where easy_install.pths need to be merged.

In that case, does my note about realpath make sense?

@obreitwi
Copy link
Copy Markdown
Member Author

obreitwi commented Apr 15, 2017 via email

@scheibelp
Copy link
Copy Markdown
Member

scheibelp commented Apr 18, 2017

Yes, we should just remove duplicate lines from the resulting easy_install.pth, imho.

To be clear I think there is an issue beyond other than duplicate lines. For example if I install py-babel and then run

./bin/spack view soft sandbox/ py-babel

With your PR, then sandbox/lib/python2.7/site-packages/easy-install.pth looks like:

/g/g17/scheibel/repos/spack/Babel-2.3.4-py2.7.egg
/g/g17/scheibel/repos/spack/opt/spack/linux-rhel6-x86_64/gcc-4.4.7/py-pytz-2016.10-f54ik3bezdsnnyfvixrtvreb5t5vytbc/lib/python2.7/site-packages/pytz-2016.10-py2.7.egg
/g/g17/scheibel/repos/spack/pytz-2016.10-py2.7.egg
import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)

The problematic line IMO is /g/g17/scheibel/repos/spack/Babel-2.3.4-py2.7.egg - that path doesn't exist. I'm pretty sure it looks like that because your PR calls realpath on a path which starts with ./ while Spack is being run from /g/g17/scheibel/repos/spack/

@obreitwi obreitwi force-pushed the feature/fsview_extensions branch from 03724ff to 6f6b167 Compare April 19, 2017 12:41
@obreitwi
Copy link
Copy Markdown
Member Author

Yes, you are completely correct.

I removed the call to realpath and just make sure any duplicate lines are removed (plus a rebase on current upstream/develop).

@obreitwi obreitwi force-pushed the feature/fsview_extensions branch from c1ef696 to 083a466 Compare May 12, 2017 14:09
@obreitwi
Copy link
Copy Markdown
Member Author

Okay, thanks, in the meantime I'll see to it that Travis has nothing to complain about.

@obreitwi obreitwi force-pushed the feature/fsview_extensions branch from 083a466 to 6a47234 Compare May 12, 2017 17:58
@scheibelp
Copy link
Copy Markdown
Member

Generally I like the reorganization but I'm still seeing the issue I mentioned earlier:

One thing I did notice when trying spack view soft sandbox/ py-pytz was that the pytz module files were showing up in sandbox/.spack/python/lib/python2.7/site-packages/pytz (where I would instead expect them to show up in sandbox/lib/python2.7/site-packages/pytz)

@obreitwi obreitwi force-pushed the feature/fsview_extensions branch from 6a47234 to 24bea31 Compare May 13, 2017 12:23
@obreitwi
Copy link
Copy Markdown
Member Author

Oh, I guess I couldn't see the forest for the trees. All extensions were linked into the meta-folder of the extendee instead of the root of the view. Should be fixed now.

Copy link
Copy Markdown
Member

@scheibelp scheibelp left a comment

Choose a reason for hiding this comment

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

Like I mentioned before: I really like the general organization and refactoring. IMO all requests here are minor except where I press on my original request not to manipulate a global variable to control whether global or view extensions are being used; as I mention in the relevant comment, if you are OK with me changing this in a follow-up PR I consider this less of an issue. However if you think it is a bad idea to do this, then I'd like to talk about it a bit more.

I'll also start thinking of tests to suggest and I'll add an update on that by the end of tomorrow.

to_keep = all_specs - to_deactivate

# remove all packages depending on the ones to remove
to_deactivate.update(find_dependents(to_keep, to_deactivate))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I thought this was originally only automatically done if the user enabled an option (and that otherwise Spack would complain if there were "leftover" dependents).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Up until now, there was no check on packages depending on the ones to remove. There was a simple --dependencies switch (enabled by default), that would remove dependencies of packages to remove.

However, if you removed the dependency of another package, rendering it unusable, there was no check performed and hence no warning.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Got it. I would prefer there would be an option for this like --remove-dependents. If it is true by default, warn the user when you are automatically removing dependents; if it is false by default, warn the user when you are leaving "detached" dependents around.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Okay, I added the option (default True), so that the user does not have to worry about removing the dangling packages and the view remains usable. People who claim to know what they are doing should put in the extra work :)


return True

def activate_standalone(self, spec):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

My understanding is that this is the function used to add non-extension packages to views. I'd prefer using a term other than "activate", like add_package_to_view and add_extension_to_view, because as of now Package.activate is associated with extensions. This is a minor preference though, since eventually Package.activate could account for the details of adding non-extension packages to views.

Likewise, I'd prefer YamlFilesystemView.add_packages_to_view vs. YamlFilesystemView.activate

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

When I decided on the name I wanted to keep in the spirit of extensions in that activating a package is linking it somewhere else, but I have no strong opinion on the issue.

Your concern is very valid that it might confuse people → I'll change it.

raise NotImplementedError


def with_extensions(f):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While I think this is nearly the clearest possible way to handle this by modifying global state, I still feel that the general approach of modifying a global variable which implicitly alters the behavior of the Package.do_activate function is less clear than just passing in the ExtensionsLayout as a parameter to Package.do_activate. IMO the user wouldn't get the impression that this could happen based on looking at Package.do_activate by itself so it would be easy to get confused. I've voiced that opinion before but hopefully this at least makes my reasoning clear, so you can point out if you think it is flawed, or compare it to the disadvantages of my suggestion to make ExtensionsLayout a parameter.

Overall I imagine this would only require changing about 20 lines or so (not that this implies that it's "OK", but if you get a different sense of the work involved I'd be interested to know). I should say that if your are fine with me making this change in a follow-up PR, then I'm less-inclined to make an issue out of it here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Hm… I wanted to mirror the "global" behavior of the DirectoryLayout/databases etc. Why should extensions be passed explicitly but everything else remains in global objects residing under spack.store - seems a bit asymmetric.

My actual primary motivation was to keep Package.activated a property, but you are right, explicit is better than implicit.

return parent


def assuredir(path):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can this be removed? As far as I can tell, this function is no longer called based on the updates in this PR.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh, yes, I moved it from cmd/view.py to utils and then forgot to delete it.

raise ValueError("%s is not a link tree!" % dest)
os.remove(dest)
# only remove if src actually points to dest
if filecmp.cmp(src, dest, shallow=True):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would it be clearer to use if os.path.realpath(dest) == src? IMO it is sufficient if dest is in fact a symlink to src; if you agree with me on that IMO this is more straightforward since it takes more effort to understand the implications of filecmp.cmp.

Copy link
Copy Markdown
Member Author

@obreitwi obreitwi May 16, 2017

Choose a reason for hiding this comment

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

Using realpath would be sufficient if we only ever used symlinks, but for hardlinks the realpaths differ. I updated the comment accordingly.

Although I do admit that this should only ever be an issue when adding (and subsequently removing) packages containing conflicting files (that are important), possibly with hardlinks. In all other cases the original os.remove would have been sufficient.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I updated the comment accordingly.

Sounds good - thanks!

Note that files blocking directories will still cause an error.
"""
kwargs['order'] = 'pre'
report_existing = kwargs.get("report_existing", False)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

IMO this should also be called ignore_conflicts. The variable definitely results in reporting existing files, but IMO the most important effect is that this option allows for conflicts to occur.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't know why I did not choose that name, but I renamed it now…

@scheibelp
Copy link
Copy Markdown
Member

I'll also start thinking of tests to suggest and I'll add an update on that by the end of tomorrow.

  • Firstly I suggested testing write_easy_install_pth at Allow for extensions on a per filesystem view basis #3227 (comment). This might be easier to test if that function were refactored to take the names of the .pth files to merge, and returned the contents of the merged file (or if that were to be placed in a helper method).
  • Some existing mock packages already create installation files. You could test installing and then adding any of them to a view.
  • There are a couple of mock extension packages, but neither of them define multiple versions. IMO you could modify either one to have 2 versions, install both, and then confirm that both cannot be added to a view.
  • Install a dependent, add it (plus the dependency) to a view, remove the dependency from the view. The functionality to decide on what gets removed could be refactored into its own function to make this trivial (so you don't necessarily have to install things, just make sure the specs are calculated properly).

I'll keep thinking on it. Let me know if you think these aren't useful or aren't in scope for your PR. Also if you think the tests are useful but want to chat about how to implement them let me know.

@obreitwi obreitwi force-pushed the feature/fsview_extensions branch 2 times, most recently from 5db2ea9 to 9ee2c9f Compare May 17, 2017 20:29
@obreitwi
Copy link
Copy Markdown
Member Author

Okay, just updated the branch. Now global state is not modified, the status and remove command are a bit more lenient when it comes to specifying specs (basically names are enough) and the color-output is disabled when there is no TTY attached to stdout.

After making my head hurt at first (I wanted to not remove dependencies that have non-user-specified dependents) I decided to make it nice and simple:

  • If dependencies are to be removed they are added to the list of specs to remove.
  • If dependents are to be removed all dependents are identified and removed as well (this includes dependents of dependencies of user-specified packages). A warning is printed.
  • If dependents are not to be removed, we identify them anyway and print a warning to the user (even without verbose output) that the packages will become unusable.

Basically I don't see users removing packages from views that much anyway. It is just too easy to delete everything and add exactly what you want. Now it is the most conservative approach (which happens to delete more rather than having unusable packages linger).

If the PR is fine otherwise I'll have a closer look at the tests in the coming days.

Return the specs of all packages that extend
the given spec
"""
# TODO: Shouldn't this function be called "activated_extensions_for"?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes - if you could change the name I'd appreciate it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done

if not os.path.islink(dest):
raise ValueError("%s is not a link tree!" % dest)
os.remove(dest)
# only remove if src actually points to dest
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

IIRC dest is actually the file/symlink in the view, so IMO it would be clearer if this read "if dest points to src" or perhaps even better is "only remove if dest is a hardlink/symlink to src" and it occurs to me that it would also be useful to explain how this can occur since it is not expected to be a common situation. So in all:

"remove if dest is a hardlink/symlink to src; this will only be false if two packages are merged into a prefix and have a conflicting file"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done

tty.warn(self._croot +
"The following dependents will be removed: %s"
% ", ".join((s.name for s in dependents)))
to_deactivate.update(dependents)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since you go through the work of aggregating dependents here, my understanding is that if you sorted them based on a post-order traversal of the dependency DAG that calling Package.do_deactivate with with_dependents=True would not be required. I don't think there's any harm in doing it this way although the redundancy may be confusing. If I'm right I think it would make sense to make a comment on it (I don't necessarily think it requires a change).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, you are correct: Prior to this PR there was an exception thrown if an activated dependent was detected, so I added the option to remove dependents instead (back when this PR was a quick hack).

I am unsure as to whether the method you describe will be reliable enough (I have yet to fully understand all code related to the DAG), but I'll add a comment that this should be done in the future if performance is too detrimental.

raise NotImplementedError

@property
def hidden_file_paths(self):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think either implementation of the interface implements this method. Is there some reason it is here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh, right, that was erroneously copied from DirectoryLayout, removed.

@scheibelp
Copy link
Copy Markdown
Member

Basically I don't see users removing packages from views that much anyway. It is just too easy to delete everything and add exactly what you want.

Makes sense to me

If the PR is fine otherwise I'll have a closer look at the tests in the coming days.

Added a few more comments. IMO they are all minor issues.

Resolves spack#1789

In our scenario we have a a globally installed (i.e., read-only) set of
packages from which each user should then be able to add a subset to
local filesystem views. However, the current extension mechanism only
allows for one set of extensions to be activated for any given extendee.

One way to circumvent this issue is to use a module system, but this way
environment variables tend to get very long after a short while.
Furthermore, since an error is raised when two files have the same
target in the view, the user can immediately spot when multiple specs
from one package are loaded (an error that sill creeps up from time to
time).

In order to support multiple (and maybe mutually exclusive)
configurations of extensions for a given extendee, the following changes
were made. The aim is to silently activate any specified extensions when
adding them to the filesystem view; no actions from the user required.

If an extension is globally activated, it will not be linked again by the
view. Note that the view might break, however, if the extension gets
(globally) deactivated later on.

TL;DR: Adding python packages built with py-setuptools to fileystem
views without globally activating them should "just work".

* Split extension-functionality into seperate `ExtensionLayout` and
  `YamlExtensionLayout` classes that reside under
  `spack.store.extensions`.

* `ExtensionLayout` has a `extendee_target_directory(extendee)` method
  to specify to what target directory files should be linked upon
  package activation.

* All filesystem view functionality has been reimplemented in
  `spack/filesystem_view.py`: `FilesystemView`/`YamlFilesystemView`
  govern all operations preformed on views.

* `LinkTree`: `merge`-method now accepts a link argument to allow for
  both symlinks and hardlinks.

* `view`: All linking operations have been refactored to use `LinkTree`s
  instead of custom code.

* `view`: Check extensions prior to linking packages, activate
  extensions in filesystem view afterwards.

* `view statlink`: The `--verbose` option now prints the `(c)short_spec`
  of packages linked.

* `view statlink`: `remove` now accepts `--all` argument to display the
  status of/remove all packages in the filesystem view. `statlink`
  displays all specs when none are supplied.

* `view statlink`: Now uses `display_specs`-function for prettier
  output.

* `view remove`: Only removes packages specified by user, packages
  depending on those packages and packages that only user-specified
  packages depend on. The latter can be adjusted via `-d` command line
  option.

* Added `-i/--ignore-conflicts` command line option to print warnings
  instead of raising an exception whenever conflicting files are found.
  Please note that this only works for linking non-extensions as the
  activation-procedure should take care of conflicting files in
  extensions.

* To that end, LinkTree now has the option to `ignore_conflicts` and
  report the conflicts instead of raising assertions when `merge`-ing.
@obreitwi obreitwi force-pushed the feature/fsview_extensions branch from 9ee2c9f to 8c0c520 Compare May 18, 2017 14:48
@obreitwi
Copy link
Copy Markdown
Member Author

After having had a quick look at the testing routines, some questions/thoughts came up:

  • I think write_easy_install_pth should be tested and refactored out; especially given the recent rewrites (fix easy-install.pth #3569, Restore newlines to easy-install.pth files. #3583). Although, partly thanks to Don't build eggs. #3587, it is not that much of an issue and really outside the scope of this PR (which is being able to activate different sets of extensions from the same spack-repository into different filesystem views). It was its primary motivation, though.

  • The builtin.mock-python package is missing its do_activate function. How should proper functionality of the actual package be verified in the current testing framework?

  • On what level should be tested? Only user based input with builtin.mock as repository or directly on the level of FilesystemView? In the latter case, I am torn between adding some packages to builtin.mock and having mocked Specs and Package generated on the fly (as with the install command tests) that just install an adjustable set of random-dummy files based on their version-hash.

  • Even more general: Do we have to test for proper linking of files given the fact that LinkTree is already covered by tests? Would testing conflict-detection, dependency-resolutoin and proper removal of dependencies/dependents be enough when performed on "specs without install files"-level?

  • If linking should be tested, are their testing routines that allow builtin.mock to contain installed specs (so that these could actually be linked to a temporary filesystem view)?

@scheibelp
Copy link
Copy Markdown
Member

The builtin.mock-python package is missing its do_activate function. How should proper functionality of the actual package be verified in the current testing framework?

IMO you don't need to test Python.activate. Even testing write_easy_install_pth is a bit beyond the norm. That being said I think you can access the "real" python easily if you don't use the install_mockery fixture.

Do we have to test for proper linking of files given the fact that LinkTree is already covered by tests?

IMO no

Would testing conflict-detection, dependency-resolution and proper removal of dependencies/dependents be enough when performed on "specs without install files"-level?

I could see how the last two could be tested on specs without install files. The first one would be harder assuming we're talking about file conflict detection (vs. detecting whether some other version of a package has been added to a view), although IMO you don't have to work with specs (could just be one of the link_tree tests)

On what level should be tested? Only user based input with builtin.mock as repository or directly on the level of FilesystemView?

If by "Only user based input with builtin.mock" you mean invocations of cmd.view I don't think it has to be at that level. Direct testing of filesystem_view seems good to me.

In the latter case, I am torn between adding some packages to builtin.mock and having mocked Specs and Package generated on the fly (as with the install command tests) that just install an adjustable set of random-dummy files based on their version-hash.

While test/cmd/install uses mock Specs and packages test/install does not. IMO builtin.mock would be easier here given that the view logic calls more functions but either way would be fine.

@mathstuf
Copy link
Copy Markdown
Contributor

I've rebased this and split it into multiple commits in #5415.

@alalazo
Copy link
Copy Markdown
Member

alalazo commented Nov 3, 2017

#5415 went in. Can we close this?

@tgamblin
Copy link
Copy Markdown
Member

tgamblin commented Nov 3, 2017

Superseded by #5415

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants