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

Generalize notion of arch-dependent modules #26287

Closed
wants to merge 12 commits into from

Conversation

abbradar
Copy link
Member

@abbradar abbradar commented May 31, 2017

Motivation for this change

Currently we have several places in NixOS which use global architecture-dependent modules:

  • OpenGL drivers;
  • Input methods;
  • sane drivers;
  • GTK engines;

All of those share common properties:

  • They are necessarily "impure";
  • They are architecture-dependent -- that is, 32-bit program must use 32-bit modules;
  • They are commonly found through hardcoded paths or environment variables.

This patch allows NixOS modules to specify functions that return needed packages given a package set as value for libraries.packages. Those functions are combined and used to build per-arch environments in /run/current-system/${system}-lib where system is stdenv.system for a given arch. Also we add a way to specify PATH-like variables that refer to those directories in libraries.environment.

For now those paths are added in LD_LIBRARY_PATH like OpenGL drivers currently. We want to move from this when libglvnd allows us but meanwhile I sanitize all packages in libraries.packages, removing all non-directories in /lib. This shields people from accidentially polluting their library path by e.g. adding a VDPAU driver. To avoid sanitization one needs to use libraries.unsafePackages, the only current user now is our OpenGL module. When LD_LIBRARY_PATH=/lib is dropped this won't be necessary.

The end goal of this patch is to both streamline arch-dependent modules and add multi-arch support to things that don't have it now, for example IM modules. If implemented correctly all applications, both 32-bit and 64-bit, should have same themes, input modules, drivers and all other arch-dependent modules available in the system as long as libraries.support32Bit is flipped. Also we get rid of /run/opengl-driver{,-32} which I dislike personally (why have it when we already have /run/current-system?) :D

This patchset also cleans up our environment variables situation, some things being prerequisites to the main idea and some (DEs envvar cleanup) because they came along and were straightforward.

Things done
  • Tested using sandboxing
    (nix.useSandbox on NixOS,
    or option build-use-sandbox in nix.conf
    on non-NixOS)
  • Built on platform(s)
    • NixOS
    • macOS
    • Linux
  • Tested compilation of all pkgs that depend on this change using nix-shell -p nox --run "nox-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • Fits CONTRIBUTING.md.

Initial implementation is finished and I'm now trying to build my system with this. Before continuing though I'd like to hear feedback from others.

@abbradar
Copy link
Member Author

abbradar commented May 31, 2017

cc @edolstra for general approval, @vcunat for video, @ericsagnes for input methods, @nbp for types.function (random people that I remembered to be involved in something close).

Checklist:

  • Test desktop environments (tested Xfce, others should be same)
  • Verify there are no places systemd.services.foo.environment.NAME = null is used
  • Test multiarch OpenGL, Vulkan, VDPAU, VA-API
  • Test multiarch input methods
  • Fix GTK2 engines search (it stops after the first module with given name is found and then fails if the architecture is invalid)
  • Fix GIO modules search (same as GTK2 engines)
  • Test multiarch sane
  • Figure out how to patch fglrx
  • Add changelog entry
  • Document types.function
  • Document how to add VDPAI/VA-API/whichever drivers

lib/types.nix Outdated
function = from: to:
assert from.getSubModules == null;
mkOptionType rec {
name = "function";
Copy link
Member

Choose a reason for hiding this comment

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

No, No, No, NO, and NO!

Copy link
Member Author

Choose a reason for hiding this comment

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

Please elaborate :3

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

@abbradar abbradar May 31, 2017

Choose a reason for hiding this comment

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

Ah, thanks for the reference!

I'm unsure how to do it the other way (avoid using functions). Without them every added package would need to be effectively doubled, e.g. we'd need to have two lists of identical packages but for different architectures specified by users (we in fact have this now as hardware.opengl.extraPackages{,32} and it looks quite ugly). Also I think a function is just a very natural way to express this idea (abstract away from arch, and focus on "what packages are needed").

Any ideas much appreciated!

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we could have a pkgs-like module argument which corresponds to the recursiveUpdate of both pkgs and pkgsi686Linux packages, with a predicate to stop at derivations. Thus selecting a attribute name in the zipped set of packages would select the same attribute name in both sets.

Copy link
Member Author

Choose a reason for hiding this comment

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

@nbp Won't this be a heavy computation (zipping two package sets)? Still yes, sans that it sounds like a good idea. I'll try to implement it and see if it would indeed be slow (I think it won't be, after all).


packages = mkOption {
type = types.function types.attrs (types.listOf types.package);
default = pkgs: [];
Copy link
Member

Choose a reason for hiding this comment

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

pkgs is provided as argument of the module, why do you need a function here?

Copy link
Member Author

@abbradar abbradar May 31, 2017

Choose a reason for hiding this comment

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

The idea is that this function returns needed packages from passed package set. So when we need 32-bit support we evaluate this function twice, first with pkgs and then with pkgsi686Linux, getting what we want.

EDIT: the same idea is used in buildFHSEnv -- you pass it a function that gets pkgs and returns needed packages, and it evaluates it once or twice with different archs depending on what you need.


unsafePackages = mkOption {
type = types.function types.attrs (types.listOf types.package);
default = pkgs: [];
Copy link
Member

Choose a reason for hiding this comment

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

pkgs is provided as argument of the module, why do you need a function here?

};

libPath = mkLibPath pkgs;
libPath32 = mkLibPath pkgs.pkgsi686Linux;
Copy link
Member

Choose a reason for hiding this comment

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

Having both implies that installing lib32 support would cause the system to compile an x86 toolchain in addition to the existing x86_64 toolchain. This sounds like way too much overhead to me. Couldn't such package already provide a lib32 directory as part of the outputs of the package?

Copy link
Member Author

Choose a reason for hiding this comment

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

@nbp Not the whole system, only libraries which need to be multiarch. We already do this with video drivers, this just extends the idea to similar packages.

@vcunat
Copy link
Member

vcunat commented Jun 11, 2017

I only skimmed all this, but if we're blocked on the function type, why not use some attrmaps instead of function maps? Nix is lazy, so there's not too much difference. (EDIT: and the memoization we get might actually be a good thing.)

I also don't have any idea about problems caused by cross-compiling NixOS, but I guess we would better leave such questions for some later iteration.

@abbradar
Copy link
Member Author

@vcunat Yeah, I plan to do it this way after all (I had some doubts because while Nix is lazy we still need to compute at least the shallow top-level set one more time, and I don't know how long does it take).

I'll push a second revision in several days.

@vcunat
Copy link
Member

vcunat commented Jun 13, 2017

I expect each top-level set to be just a set of platforms, so that should be very cheap for all the unused platforms.

`recursiveZipWithUntil` is a generalization of `recursiveUpdateUntil` for a
list of attribute sets and using a custom merge function.

`recursiveZipWith` to `recursiveZipWithUntil` is like `recursiveUpdate` to
`recursiveUpdateUntil`.
Until now we had several options that accepted environment variables which had
different types and copied functionality. Two side-effects from this change:

* systemd.service.*.environment.NAME no longer accepts `null`s;
* systemd combines global and service-local variables: for example
  when LD_LIBRARY_PATH is defined both in globalEnvironment and in
  service's environment as a list they are merged together.
Most of needed variables are already defined system-wide or could be moved there.
This makes it possible to have working input methods in 32-bit programs on a
64-bit system.
Currently we have several places in NixOS which use global architecture-dependent modules:

* OpenGL drivers;
* Input methods;
* sane drivers;
* GTK engines;

All of those share common properties:

* They are necessarily "impure";
* They are architecture-dependent -- that is, 32-bit program must use 32-bit modules;
* They are commonly found through hardcoded paths or environment variables.

This patch allows NixOS modules to specify functions that return needed
packages given a package set as value for `libraries.packages`. Those functions are
combined and used to build per-arch environments in
/run/current-system/${system}-lib where `system` is `stdenv.system` for a given
arch. Also we add a way to specify PATH-like variables that refer to those
directories in `libraries.environment`.
FIXME: fglrx is not patched to use new paths yet!

References to OpenGL drivers path and other paths are replaced throughout
nixpkgs tree.

User-facing changes:

* `hardware.opengl.driSupport32Bit` is gone, replaced by generic `libraries.support32Bit`;
* `hardware.opengl.driSupport` is gone (not used anywhere);
* `hardware.opengl.extraPackages{,32}` are replaced by `libraries.packages`
  which is a function;
We expect now to have libraries handled by libraries.packages.
`i18n.inputMethod.{ibus,fcitx}.engines` are now a function.
`hardware.sane.extraBackends` is now a function.
Instead of placing GTK engines and GIO modules in system path, place then in
arch-dependent directories.
It's a zip of package sets for all supported architectures on the system.
@abbradar
Copy link
Member Author

I've updated this patchset to use zipped packages instead -- sorry for the delay, life gets into the way :D. To me it looks a step back: zipped packages are much less intuitive and composable. Compare for example abbradar@9bd3dd5 and 388a942. Still, this seems an improvement over what we have now, so if others approve this approach I'll continue the work (see TODO list above).

@vcunat
Copy link
Member

vcunat commented Jun 23, 2017

Oh, I originally thought it would be pkgs_multiarch.<system>.<pkgName> and not the other way around, but I see it would complicate things like unsafePackages...

@abbradar
Copy link
Member Author

abbradar commented Jun 23, 2017

@vcunat Won't that way also complicate users specifying their packages? For example, how'd I specify that I want libraries from package foo installed? Currently it's libraries.packages = [ pkgs_multiarch.foo ]. I still hope that we can figure out something better than all those map (x : x.${system})...

@vcunat
Copy link
Member

vcunat commented Jun 23, 2017

Yes, that seems similar. I can't immediately see into further consequences of either approach, it was mainly intuition to do it the other way. Zipping the top-level set hopefully won't make a noticeable slow-down (it's memoized).

@abbradar
Copy link
Member Author

abbradar commented Jun 23, 2017 via email

@vcunat
Copy link
Member

vcunat commented Jun 23, 2017

A string wouldn't be much flexible. It would be nice to support .override and similar stuff. Perhaps a utility function pkgs_multi.map (pkgs: pkgs.bash), returning a set indexable by system names, though I haven't given it any deep thought.

@Profpatsch
Copy link
Member

I think the changes @Ericson2314 is constantly working on should supersede this pull request? Or at least make it easier to implement.

@Ericson2314
Copy link
Member

Which changes haha?

@Ericson2314
Copy link
Member

If you mean cross in general, I haven't yet got rid of the old multi-lib stuff but yes that would be nice.

@mmahut
Copy link
Member

mmahut commented Aug 3, 2019

What is the status of this pull request?

@abbradar
Copy link
Member Author

abbradar commented Aug 9, 2019

I want to return to this idea some time as the problem of different arch-dependent modules is still there. We can close this as my next attempt will likely be a complete rewrite.

@abbradar abbradar closed this Aug 9, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants