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

Platform normalization #24610

Merged
merged 7 commits into from
Apr 17, 2017
Merged

Conversation

Ericson2314
Copy link
Member

@Ericson2314 Ericson2314 commented Apr 4, 2017

Motivation for this change

Finally deal with the awkward friction between {build,host,target}Platform.system and {build,host,target}Platform.config. This is a followup to #22575 and fixes #24170 .

@nbp this uses your old system code and tests. I think I've used the module type system idiomatically, but I am unsure about the "CPU families" bit. In lieu of real sets, I used a map [why do we say attribute set"...], but perhaps matchAttrs should be mutually recursive with a matchLists that treats the list as a set (or looks for sublists].

@nbp Also, I broke the existing module system test infra with some mysterious call package exception. Not sure what's up with that, but want to get this up for review. Fixed.

This can be reviewed or even merged commit-by-commit, but watch out: github is putting the commits out or order again (argh!).

CC @wizeman

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.

@Ericson2314 Ericson2314 added the 6.topic: cross-compilation Building packages on a different platform than they will be used on label Apr 4, 2017
@mention-bot
Copy link

@Ericson2314, thanks for your PR! By analyzing the history of the files in this pull request, we identified @viric, @dezgeg and @nbp to be potential reviewers.

@Ericson2314 Ericson2314 added the 2.status: work-in-progress This PR isn't done label Apr 4, 2017
@Ericson2314 Ericson2314 changed the title Platform normalization [WIP] Platform normalization Apr 4, 2017
assertTrue = bool:
if bool
then pkgs.runCommand "evaluated-to-true" {} "true"
then pkgs.runCommand "evaluated-to-false" {} "false";
Copy link
Member

Choose a reason for hiding this comment

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

this looks like an syntax error.

Copy link
Member

Choose a reason for hiding this comment

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

ok this was fixed later.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, I want each commit to stand on its own so that should be correct from the start!

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed from the start!

{
all = assertTrue (mseteq all (linux ++ darwin ++ cygwin ++ freebsd ++ openbsd ++ netbsd ++ illumos));

arm = assertTrue (mseteq arm [ "armv5tel-linux" "armv6l-linux" "armv7l-linux" "aarch64-linux" ]);
Copy link
Member

Choose a reason for hiding this comment

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

I think there was a commit, which removed explicitly aarch64 from arm, because the cpu architecture is different.

Copy link
Member

Choose a reason for hiding this comment

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

This was a241abf

Copy link
Member Author

Choose a reason for hiding this comment

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

Just as I include x86_64 in the x86 family, so I still include aarch64 in the arm one. But the arm list there also constrains the word width to 32 bits---that should do the trick.

@shlevy
Copy link
Member

shlevy commented Apr 4, 2017

Just quickly glanced through this, why are we using the module system type system here? This seems like a really complex change and it's not clear to me why we're doing it this way.

@Ericson2314
Copy link
Member Author

@shlevy Well, on some level, I used it because the code that already existed by @nbp and wanting to learn more about it :).

But, assuming we indeed do want to have a white list of known components rather than plain strings, I don't actually think this could be simplified much.

@Ericson2314
Copy link
Member Author

Ericson2314 commented Apr 4, 2017

Ok fixed the module tests (mistake in 2nd commit). Removing the "WIP" as this thing is no longer obviously broken, but it needs more review for style / objectives before merging.

@Ericson2314 Ericson2314 changed the title [WIP] Platform normalization Platform normalization Apr 4, 2017
@shlevy shlevy removed their assignment Apr 7, 2017
@Ericson2314
Copy link
Member Author

@nbp You have time to review this? Besides use of the module system, it would be good to figure out some plan for all the crazy edge cases when converting between LLVM triples and Nix "doubles" (e.g. stuff like darwin10 for OS, windows in general).

assertTrue = bool:
if bool
then pkgs.runCommand "evaluated-to-true" {} "touch $out"
else pkgs.runCommand "evaluated-to-false" {} "false";
Copy link
Member

Choose a reason for hiding this comment

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

Why is this? Why not just use assert?

Copy link
Member Author

Choose a reason for hiding this comment

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

Basically a workaround around hydra not tracking evaluation errors per revisions. I'd like this to count towards the "test failed vs test pass" numbers like everything else.

Copy link
Member

Choose a reason for hiding this comment

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

IMHO such tests are better done at build time (i.e. by having a single job that does nix-instantiate --eval tests.nix, probably by adding it to lib/tests/release.nix). I don't want to clutter Nixpkgs/NixOS jobsets with lots of tiny Nixpkgs lib tests...

Copy link
Member

Choose a reason for hiding this comment

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

This is basically what the module tests are doing, they basically spawn multiple Nix expressions tests within one job which contains all module tests.

Copy link
Member Author

@Ericson2314 Ericson2314 Apr 11, 2017

Choose a reason for hiding this comment

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

I feel like recursive nix is a very heavy-handled solution here. If we don't want sub jobs so flattened, hydra should change the way it treats sub jobs.

Copy link
Member

Choose a reason for hiding this comment

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

Note that this is not what I would call recursive nix, as the module system does not evaluate any derivation, it only use the interpreter with nix-instanciate command, and check for the output of the command.

Copy link
Contributor

Choose a reason for hiding this comment

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

@Ericson2314 so what is easier, an extra job to test these, or changing Hydra so it tracks evaluation errors by revision?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm? In a later PR I rewrote the tests the semi-recursive-Nix way.

, abi ? assert false; null # ditto
} @ args: let
getCpu = name:
attrByPath [name] (throw "Unknow cpuType `${name}'.")
Copy link
Member

Choose a reason for hiding this comment

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

Unknow -> Unknown. Also don't use backticks as quote characters please.

Copy link
Member Author

@Ericson2314 Ericson2314 Apr 11, 2017

Choose a reason for hiding this comment

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

Thanks, fixed.

Copy link
Member

Choose a reason for hiding this comment

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

@edolstra the backtick-quote used to be the way names were reported by Nix tools.

lib/trivial.nix Outdated
@@ -108,6 +108,9 @@ rec {
# Flip the order of the arguments of a binary function.
flip = f: a: b: f b a;

# Apply function if argument is non-null
mapNullable = f: a: if isNull a then a else f a;
Copy link
Member

Choose a reason for hiding this comment

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

Functions used in only one place shouldn't be added to lib.

Copy link
Member Author

Choose a reason for hiding this comment

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

I can do that, but I've wanted this many times. I bet I could grep for == null|isNull and probably find many more uses. I'd be happy to do so after this PR to prove its utility :).

Copy link
Member Author

Choose a reason for hiding this comment

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

#24974 other uses :D.

@edolstra
Copy link
Member

To be honest I've never understood what lib/systems.nix etc. are for. Can anybody explain what the goal is?

If the goal is to provide a handful of properties of platforms, it would be simpler to just enumerate them, given that we only support something like 6 platforms. E.g.

platformProperties = {
  "i686-linux" = {
    is64Bit = false;
    execFormat = "elf";
    ...
  };
  "x86_64-darwin" = {
    is64Bit = true;
    execFormat = "macho";
    ...    
  };
}

Though some properties seem questionable. For example, why do we care whether a platform is 64 bit or big endian? (What do we even mean with 64 bit? LP64, LLP64, ...?)

The other thing I've never understood is why pkgs/top-level/platforms.nix (nixpkgs/lib/systems/platforms.nix in this PR) contains Linux kernel configuration. Kernel configuration should really be moved to pkgs/os-specific/linux/kernel (maybe just added to common-config.nix), and most can be eliminated (e.g. lines like IPV6 m are not platform-specific).

@Ericson2314
Copy link
Member Author

To be honest I've never understood what lib/systems.nix etc. are for. Can anybody explain what the goal is?

My end goal here is to eventually replace all the stdenv.is* predicates, which are woefully inadequate/misleading for cross compilation.

If the goal is to provide a handful of properties of platforms, it would be simpler to just enumerate them, given that we only support something like 6 platforms. E.g.

While we only support a few build platforms, people can and do specify all sorts of obscure stuff for the host platform. I want to be more general so the same predicates can be used for all of build, host, and target.

For example, why do we care whether a platform is 64 bit or big endian? (What do we even mean with 64 bit? LP64, LLP64, ...?)

What CPU attributes we track is less important than a mechanism to do so. If this specific needs to be given more detailed or removed, I'd be happy to do so.

The other thing I've never understood is why pkgs/top-level/platforms.nix (nixpkgs/lib/systems/platforms.nix in this PR) contains Linux kernel configuration.

Yeah I agree that file / the {build,host.target}Platform.platform (formerly crossPlatform.platform) attribute set is an undocumented, unorganized cesspool. I think this is a step in the right direction---at least get the file in a sane location before we triage its contents. The only kernel config I'd even consider keeping is that which effects the contents of the headers---that way downstream derivations can require that some kernel feature be enabled. Everything else kernel-wise deserves a better home and I like your suggestion.

@@ -424,11 +424,11 @@ rec {
matchAttrs { cpu = { bits = 64; }; } sys
=> true
*/
matchAttrs = pattern: attrs:
fold or false (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
matchAttrs = pattern: attrs: assert isAttrs pattern;
Copy link
Member

Choose a reason for hiding this comment

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

Can we remove the FIXME from the comment now?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

};
isKernel = x: isType "kernel" x
&& isExecFormat x.execFormat
&& all isKernelFamily (attrValues x.families);
Copy link
Member

Choose a reason for hiding this comment

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

These conditions should be assertions at the creation of anything with _type = "kernel", otherwise this assertion would be checked every time these sets are accessed instead of the only time these sets are created. Thus the type "kernel" would imply that these assertions are valid as well, without recursively going down in the set.

Copy link
Member Author

Choose a reason for hiding this comment

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

Inductive enforcement makes sense. Likewise for isCpuType which you wrote I'd think :D?

Copy link
Member

Choose a reason for hiding this comment

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

yes, please.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

inherit name cpu arch kernel;
&& isVendor x.vendor
&& isKernel x.kernel
&& isAbi x.abi;
Copy link
Member

Choose a reason for hiding this comment

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

Same here for isSystem.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

l = lib.splitString "-" s;

mkSystemFromSkeleton = { cpu
, vendor ? assert false; null # optional, but fallback too complex for here
Copy link
Member

Choose a reason for hiding this comment

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

nit: These are actually inferred below, update the comment to say so.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

elaborate = args: let
final = {
# Prefer to parse `config` as it is strictly more informative.
parsed = parse.mkSystemFromString (if args ? config then args.config else args.system);
Copy link
Member

Choose a reason for hiding this comment

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

What is this config attribute and from where does it come from?

Copy link
Member Author

@Ericson2314 Ericson2314 Apr 11, 2017

Choose a reason for hiding this comment

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

It's an LLVM target triple. The name is unfortunate but not my choice. See the code block in https://nixos.org/wiki/CrossCompiling#Cross-compiling_in_practice for example.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sadly that wiki page is gone now - Can we not rename this attribute to llvmTargetConfig?

Copy link
Member Author

Choose a reason for hiding this comment

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

https://web.archive.org/web/20160322211219/https://nixos.org/wiki/CrossCompiling

I'm all for improving the name but we must be careful as this is a public interface to how Nixpkgs is instantiated.

Also, while I follow LLVM most closely, other tools use these too.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah right. In that case, maybe rename args to compileConfig?

, abi ? assert false; null # ditto
} @ args: let
getCpu = name:
attrByPath [name] (throw "Unknow cpuType `${name}'.")
Copy link
Member

Choose a reason for hiding this comment

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

@edolstra the backtick-quote used to be the way names were reported by Nix tools.

This is a semantic change, but probably a safe one. In any event, this is
very old hardware that probably no one uses anymore anyways.
Previously, platforms was a random thing in top-level
The old hard-coded lists are now used to test system parsing.

In the process, make an `assertTrue` in release lib for eval tests; also
use it in release-cross
Warning, this changes the compatibility claims of existing packages!
@Ericson2314 Ericson2314 merged commit f0b634c into NixOS:master Apr 17, 2017
@Ericson2314
Copy link
Member Author

I've addressed all concerns but the nature of the tests. There is some future worked blocked on this I'd like to get to, so I am merging now. @edolstra if you'd like me to redo the tests to match what @nbp did, just ping and I'll make another PR doing that.

@Ericson2314 Ericson2314 deleted the platform-normalization branch April 17, 2017 21:28
} @ args: let
getCpu = name:
attrByPath [name] (throw "Unknown CPU type: ${name}")
cpuTypes;
Copy link
Contributor

Choose a reason for hiding this comment

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

@Ericson2314 This might have been pointed out, but... I believe you can also do this to the same effect:

cpuTypes.${name} or (throw "Unknown CPU type: ${name}")

At least, if I'm not mistaken. Which is better is a matter of opinion, I suppose (though I guess it's also possible that one performs better than the other).

Copy link
Member Author

@Ericson2314 Ericson2314 Apr 17, 2017

Choose a reason for hiding this comment

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

That's true and nicer. And those functions can be inlined too. Blame @nbp who originally wrote the code a decade ago 56ed820 :D:D:D

Copy link
Member Author

@Ericson2314 Ericson2314 Apr 18, 2017

Choose a reason for hiding this comment

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

Feel free to make another PR---in general I expect this file to get a lot attention and grow in complexity as this stuff gets used---I've got more to stuff to upstream so not sure whether I have time!

@edolstra
Copy link
Member

@Ericson2314 Yes please, it would be nice to have one Hydra job for Nixpkgs lib tests.

Ericson2314 added a commit to Ericson2314/nixpkgs that referenced this pull request Apr 18, 2017
Worthwhile to do now that NixOS#24610 makes it less abysmal.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
6.topic: cross-compilation Building packages on a different platform than they will be used on
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Infinite recursion after updating to 17.03
8 participants