mesonbuild / meson

The Meson Build System
http://mesonbuild.com
Apache License 2.0
5.35k stars 1.53k forks source link

Add kernel and userland properties to machine objects #11692

Closed jpakkane closed 12 months ago

jpakkane commented 1 year ago

This is a WIP MR meant to illustrate and work on the UI of this feature. Will add the docs etc once design is final.

Add two new properties to machine files, namely "kernel" and "userland". This allows better configuration of builds to existing systems. For example you could make a difference between the different Apple platforms. MacOs would have userland set to macos but on other devices it could be ios, tvos, watchos or something else. Similarly you could tell the difference between a gnu userland Linux install as opposed to something like musl.

system would remain as a kind of a general description of the OS being used. It will also be the value used for kernel and userland if they are not defined or can't be detected.

nirbheek commented 1 year ago

It will also be the value used for kernel and userland if they are not defined or can't be detected.

I think we should be strict here and raise an exception if a build file tries to access these and they aren't defined in the machine file, or these can't be detected.

jpakkane commented 1 year ago

Fine by me.

dcbaker commented 1 year ago

Is musl vs glib really a userland? I would expect userland to be like "gnu" vs "android" or to detect links like debian-kfreebsd where you'd have kernel() == "freebsd" and userland() == "gnu"

eli-schwartz commented 1 year ago

GLib (the gnome library) has nothing to do with it. It's musl vs glibc.

But that's really another way of saying "musl" vs "GNU libc" so it seems quite privileged to say that "GNU" gets to call itself a libc but nothing else does. :P

GNU vs android doesn't group musl with GNU in opposition to android.

So you're correct, the options here are indeed:

dcbaker commented 1 year ago

that seem less like "userland" and more like "c_stdlib", because you could run into the same problem of "libc++ vs libstdc++"

but that list should be "gnu", "bionic", "musl" because android isn't a libc, it's an OS userland.

eli-schwartz commented 1 year ago

Except you can actually link to both libc++ and libstdc++ in the same program just fine. If you stick STL types in your public API and attempt to pass them from one implementation over to functions compiled with the other implementation, that's a link-time error, and if you don't attempt to do that then everything "just works".

In comparison, the C stdlib is pervasive (you get out of the C++ STL types problem by passing C types around the language barrier, for example) and scribbles all over each other, and is much more of an infrastructural matter than a C++ stdlib. You can't share anything with anything.

Musl distros are pretty adamant that it's a userland situation. It's also logically embedded in things like "python added a new platform tag called musllinux specifically because it was so different". No one has suggested creating libcplusplus platform tags...

And really, it does seem very much to me like bionic is the internal userland name, while android is the public userland name.

jpakkane commented 1 year ago

Updated.

jpakkane commented 1 year ago

FTR one of the main drivers for this change is being able to reliably differentiate between macOS, iOS, watchOS, tvOS and the like, which is currently not really possible. And that is very much a "userland" property rather than a libc one.

dcbaker commented 1 year ago

Right, and I think that's usefu (I have an issue about this opened from ages past somewhere). I think this is also the right place to handle the cases of weird things like "FreeBSD kernel + debian (or gentoo) user land" (Which, IIRC gstreamer needs to solve? @nirbheek) and what the heck android is "Linux + android userland"? But "Is this glibc or musl or bionic or freebsd libc or openbsd libc or netbsd libc or the microsoft visual c (which version) or ..." seems like an separate (but useful) attribute which is different from "is this TvOS or MacOS" or "is this linux + android or linux + gnu"

jpakkane commented 1 year ago

I consider "userland" to mean "some name for the basic libraries needed to run apps such as the standard C library". Which would match this. I tried to come up with a different separation that would keep the two issues separate but did not really succeed.

eli-schwartz commented 1 year ago

For clarity, I am arguing that for Linux, the libc vs musl split is both a libc choice and a "userland" definition.

I think this is also the right place to handle the cases of weird things like "FreeBSD kernel + debian (or gentoo) user land" (Which, IIRC gstreamer needs to solve? @nirbheek)

This is .kernel() == 'freebsd', .system() == 'freebsd', .userland() == 'gnu' presumably. That fits my argument.

and what the heck android is "Linux + android userland"?

I'm not an Android expert, but I thought that Android was both a kernel and a userland? By which I mean to say that the Android kernel may be "based" on Linux, but that's like saying that darwin is a .kernel() == 'freebsd' since that's what it's based on.

But "Is this glibc or musl or bionic or freebsd libc or openbsd libc or netbsd libc or the microsoft visual c (which version) or ..." seems like an separate (but useful) attribute which is different from "is this TvOS or MacOS" or "is this linux + android or linux + gnu"

I don't think that it's practical to care about actual mainline linux kernels running bionic libc or any of the BSD libcs, does that actually happen? If it did happen, what would YOU call the userland for those machines? :)

I also don't think that we need to distinguish between versions of the msvc userland, but I do agree that cygwin and msys2 could be accurately captured as .kernel() == 'windows' and .userland() == 'cygwin' where, of course, cygwin is also the name of the libc

jpakkane commented 1 year ago

For clarity, I am arguing that for Linux, the libc vs musl split is both a libc choice and a "userland" definition.

In case it was unclear, I agree with this.

dcbaker commented 1 year ago

Okay, so real world problems to solve: FreeBSD has it's own libc which it uses in most cases. You can build glibc as a port/pkg and ports/pkgs that don't work with FreeBSD's libc will be built against that. How do we represent that.

I'm not an Android expert, but I thought that Android was both a kernel and a userland?

The relationship between the Android Linux kernel and Linus' Linux kernel is pretty minor, almost everything of substance is in Linus' tree, just not built by traditional linux distros. The XNU kernel used by Darwin is actually based on the mach micro kernel, with the FreeBSD POSIX interfaces glued on top of that, but modified to translate everything into mach tasks. More importantly there is the real potential for a Fuschia + Android future, with no Linux involved.

I'm just really concerned that we're going to end up in a case where "well, that all seemed to work fine until we ran into people trying to use Linux + musl + some new Rust userland that doesn't behave like a traditional Unix. Because we can't change the meaning of system, which is basically "OS name" unless we have a breaking 2.0

eli-schwartz commented 1 year ago

Okay, so real world problems to solve: FreeBSD has it's own libc which it uses in most cases. You can build glibc as a port/pkg and ports/pkgs that don't work with FreeBSD's libc will be built against that. How do we represent that.

Where is that glibc port, I don't see it in their ports tree?

More importantly there is the real potential for a Fuschia + Android future, with no Linux involved.

So that would be .kernel() == 'fuchsia' and .userland() == 'android' ?

I'm just really concerned that we're going to end up in a case where "well, that all seemed to work fine until we ran into people trying to use Linux + musl + some new Rust userland that doesn't behave like a traditional Unix. Because we can't change the meaning of system, which is basically "OS name" unless we have a breaking 2.0

I'm not entirely sure I can envision what "musl plus rust" means, is it compatible with musl? Is .userland() really 'rustmusl' and derived from musl but not actually compatible with it? What do non-rust programs building for this userland think they have in terms of a userland API/ABI?

dcbaker commented 1 year ago

My libc is musl, but the actual set of userland applications I have are not gnu, or even gnu like. Imagine if android used glibc instead of bionic

eli-schwartz commented 1 year ago

I don't understand what you're trying to say.

nirbheek commented 1 year ago

"FreeBSD kernel + debian (or gentoo) user land" (Which, IIRC gstreamer needs to solve? @nirbheek)

It's a general problem for Debian GNU userland + FreeBSD kernel. Gentoo also has something similar.

I think we're getting a bit lost in the weeds here. This doesn't have to be perfect. We can always change it later or add more properties, and we likely will have to as the world evolves.

I actually don't see the use for being able to know whether you are targeting glibc or musl. Everyone's going to do individual compiler checks to know what is available anyway.

The target kernel cannot be reliably auto-detected, especially when cross-compiling. So that info has to be provided in the cross file somehow, so kernel() is definitely needed IMO.

Maybe instead of making userland, we can borrow the cpu vs cpu_family system for system?

cpu_family == system and cpu == system_subtype (bikeshed welcome). Then we can specify only watchos ios ipados tvos macos as subtypes of darwin and stop there for now.

This prevents us from getting tied to a weird definition of "userland", and also is fuzzy enough to be extensible in the future.

Another option is to just change what system() returns, add system_family() and let people opt-into it with something like:

project('name', future_features: ['01-more-systems'])

Honestly, I've been wanting a better way for us to do deprecations / breaking changes, and this might be a good start.

nirbheek commented 1 year ago

... or we could add new kwargs to system()

system(family: false) would return ios instead of darwin when targeting iOS. That would be fully backwards-compatible.

jpakkane commented 1 year ago

system(family: false)

That's not readable. And even then, we'd need to define a new mapping system to map a cross file entry to this. IMHO it's better to keep a 1:1 mapping between method call names and the corresponding cross property.

jpakkane commented 1 year ago

Maybe instead of making userland, we can borrow the cpu vs cpu_family system for system?

FTR if we would be designing this from scratch, this is roughly what I'd do. system_family would return what system currently returns and system would return what this MR calls userland. Sadly backwards compatibility says no.

jpakkane commented 1 year ago

To move things forward, kernel remains as is. How should userland be changed? Should it report gnu on glibc systems or not?

nirbheek commented 1 year ago

How should userland be changed? Should it report gnu on glibc systems or not?

I think we should not auto-detect the libc, I think autodetection should only return 'linux', but cross files are encouraged to make it more specific, and we can specify some common values for it.

jpakkane commented 1 year ago

I think autodetection should only return 'linux',

Also for linux/kfreebsd?

How about we return 'linux' for now and state that this might get more specific in the future?

nirbheek commented 1 year ago

Also for linux/kfreebsd?

Yes.

How about we return 'linux' for now and state that this might get more specific in the future?

Maybe? We probably specify some values that machine files can specify, like gnu musl etc, otherwise people will just rely on it always returning linux.

jpakkane commented 1 year ago

But in that case the values returned by userland would be different when cross compiling as opposed to native building on, say, Linux/kfreebsd. For iOS, watchOS etc this is not an issue as you can't compile natively on those, but for Linux it is different.

eli-schwartz commented 1 year ago

If we make literally no guarantees what these methods return and just shrug and say "subject to change, roll your own values" then we might as well pack up and go home right now. Close the PR and forget about the topic.

nirbheek commented 1 year ago

But in that case the values returned by userland would be different when cross compiling as opposed to native building on, say, Linux/kfreebsd

I don't think there is a way to autodetect with complete accuracy what the user actually wants to use.

I think we should just require userland to be filled in the machine file, whether native or cross (if the build files call .userland())

jpakkane commented 1 year ago

I don't think there is a way to autodetect with complete accuracy what the user actually wants to use.

No, but the actual power of this feature only comes about if all projects use (mostly) the same values for these. Otherwise you can't combine two different projects together and expect them to work. If detecting "Debian kfreebsd"ness is something people actually need then we should autodetect and report it somehow.

This follows Meson's usual philosophy of "do the common thing automatically if possible".

jpakkane commented 1 year ago

At the very least the value of "userland" under Linux can not be "linux" as there is no such thing as a Linux userland.

nirbheek commented 1 year ago

No, but the actual power of this feature only comes about if all projects use (mostly) the same values for these. Otherwise you can't combine two different projects together and expect them to work. If detecting "Debian kfreebsd"ness is something people actually need then we should autodetect and report it somehow.

I agree, but I think we can also achieve that by specifying it in our reference tables.

I guess it doesn't hurt too much to auto-detect it incorrectly. People can fix it by specifying a native file.

eli-schwartz commented 1 year ago

Native files cannot be a baseline requirement in order to get a project that builds correctly. If builds make use of a userland property and this property is wrong by default, and we document "if you want to build that project you MUST use a native file", then...

... again, we might as well not add the property at all.

We can just as well tell people to use the [properties] section with a user-defined userland = 'foobar' option. That also doesn't have a correct or meaningful value unless users specify it in a native file.

The advantage of using properties here is that we don't document and freeze an incorrect API that cannot be fixed.

NACK, NACK, NACK, NACK to merging this PR if people are expected to specify a native file to fix the value. Either get it right out of the box for the native case, or do not add the feature.

nirbheek commented 1 year ago

NACK, NACK, NACK, NACK to merging this PR

Let's not be so dramatic. It's always an option to present your opinion calmly.

I think you would like to know that there are contributors to this project that actively avoid interacting with you because of your tendency for hyperbole and outbursts.

jpakkane commented 1 year ago

Either get it right out of the box for the native case, or do not add the feature

Yes, and that is what the current code does The autodetection could probably be better and we need to determine if and how things like musl are reported.

jpakkane commented 1 year ago

To get this merged how about I remove musl from the list of userlands and add a comment in the documentation saying that current userland values are preliminary and we expect to add more and better values and autodetection in future releases?

eli-schwartz commented 1 year ago

Yes -- if you remove both gnu and musl from the docs and the source code, and make the exception for an unknown userland get raised on the Linux operating system unconditionally.

This is to prevent effectively sealing in an API that says linux systems always return "gnu". Users can't rely on that function returning anything meaningful on Linux anyway.

Separately, should we return "unknown" instead of aborting the build?

jpakkane commented 1 year ago

if you remove both gnu and musl from the docs and the source code, and make the exception for an unknown userland get raised on the Linux operating system unconditionally

How about checking if libc is actually glibc and returning 'gnu' if it is and erroring out if it is not?

dcbaker commented 1 year ago

How about checking if libc is actually glibc and returning 'gnu' if it is and erroring out if it is not?

How do we do that reliably? Musl goes to great pains to ensure that you can't tell the difference between glibc and musl easily, I know because we've run into issues like this with Mesa that are very hard to do a cc.run() test for because they're complex implementation details.

I'm 100% on board with having the kernel bits, FWIW. I think we can do that reliably and accurately, and it's generally useful. I'm still not 100% convinced by the userland, because it's just not clear to me what we're trying to represent. It seems like there's three distinct things that are being talked about:

  1. "sub-OSes" like TVoS vs MacOS
  2. libcs like gnu vs musl
  3. "operating environments" like android vs gnu, msys vs windows native, etc

All of these things seem pretty darn useful to be able to distinguish, but I'm also 100% convinced that they are not interchangeable. You could, in theory have kfreebsd with musl libc running a not unix like operating environment, and you really need three values to distinguish that from say kfreebsd with glibc running a freebsd environment.

So my take is: if the kernel part came in it's own PR, I would review that and be happy to see it land. As-is I'm still not convinced that we agree on what problem the userland() field is solving.

I tend to agree with @nirbheek though, for things like TVoS having a subsystem() (or whatever color you paint the bikeshed) makes a lot of sense, specifically solves the problem that it seems @jpakkane wants to solve, and wont get confused with the issues of which libc are we using, and is this android/linux, gnu/linux, or alpine/linux questions, which we can continue to discuss and solve later.

jpakkane commented 1 year ago

How do we do that reliably?

My plan was to look up the current system's libc and scrape its contents for some magic string (if on a deb platform you could use dpkg to see if glibc is installed etc). We don't need to limit ourselves to compilation tests in this one.

having a subsystem() (or whatever color you paint the bikeshed) makes a lot of sense

So how about renaming userland to subsystem, with the default behaviour in autodetection that it always returns system and removing mentions of musl etc in the documentation?

nirbheek commented 1 year ago

So how about renaming userland to subsystem, with the default behaviour in autodetection that it always returns system and removing mentions of musl etc in the documentation?

:+1: but only for build machine (native compilation).

We can autodetect watchos vs tvos etc, using a compiler check. It's fairly straightforward.

We should raise an exception if while cross-compiling the build file calls subsystem() and (1) we can't autodetect it and (2) the machine file doesn't specify it

jpakkane commented 1 year ago

We should raise an exception if while cross-compiling the build file calls subsystem() and (1) we can't autodetect it and (2) the machine file doesn't specify it

No autodetection in cross files. Either it is specified or we error out if the build definition tries to access it.

nirbheek commented 1 year ago

No autodetection in cross files. Either it is specified or we error out if the build definition tries to access it.

Sure, we can always add this later if we change our mind, so I don't mind going along with this.

jpakkane commented 1 year ago

Updated.

jpakkane commented 1 year ago

All reported issues should be fixed.

jpakkane commented 1 year ago

Everything except dragonfly naming should now be fixed.

jpakkane commented 1 year ago

Unless someone speaks up soon about dragonfly naming, I'm going to merge this.