-
Notifications
You must be signed in to change notification settings - Fork 214
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
How do target configuration constraints work? #184
Comments
It is a target. Try
Directory in
Simplified, But yes, documentation could be better. |
Thank you! Would you be able to also provide answers to the two questions at the end of the OP? |
Can you please give more concrete examples of what flags do you want to switch? |
Hmm, is that actually important? I chose a hypothetical non-existant flag |
Yes, it is. Depending on your use case I can suggest one solution or another, or describe our plans to address the issue. Basically we don't support "flags". Most of the current issue can be addressed by adding a custom configuration with |
There's a lot that goes into this. A lot of it is described here: https://buck2.build/docs/rule_authors/configurations/ Here's one way to define a platform and a select such that the select() will resolve as you ask when in the configuration defined by that platform:
Now, that example doesn't cause the |
It's hard to be sure whether you are asking this in order to understand better how platforms/configurations/selects work, vs. to accomplish these two specific pieces of functionality. Mostly likely it's the former, in which case cjhopman's answer has good guidance. But just in case it's the latter, select and constraint may not necessarily be the thing you're after.
|
This is quite a fundamental divergence from what I'm accustomed to, and it makes me question whether buck2 is even compatible with our builds at all. For some context, we currently use CMake. In CMake you can pass I probably can't list every possible way in which we rely on this, but here's a few:
Number 3 is especially problematic, because part of the semantics of that one involve passing a define into every compilation unit, including shared code. This means that a build of two different products passes different flags to the same source files so the compiled code can't be shared between products, even though much of the source code is the same. There are at least 20 more of these flags that we can pass to the build, and any combination of flags can be mixed and matched. The semantics of these flags are extremely diverse. They can mean anything from:
And more. And of course, the semantics can also be compound, so that a single build flag can do a combination of the above semantics, including various things that aren't listed. |
Hey Zach, good to see you around these parts; been a while! I'll try to give you a good answer to help get you moving along since I saw some other questions of yours...
Well, the statement itself is pretty simple, in theory, though I haven't tested this with the prelude: cxx_binary(
cflags = select({
"cell//path/to:target": [ "-O2" ],
"cell//path/to/other:target": [ "-O1" ],
}) + default_cflags,
) In the above example,
For example if the following cxx_binary(name = "test_cxx", ...)
rust_binary(name = "test_rs", ...) We'd say that Then there are two targets above: And there are lots of other target patterns you can use, try a bunch:
And some other various, more non-obvious ones, like Before you can talk about configurations, you need to talk about providers. This is a very, very, very important part of the design of Buck, and a lot more things made sense once I realized this. I mentioned
It is very, very important to understand this. It is key to Buck's modularity. You can never simply look at a target pattern Providers — data structures — are the only way a rule can provide information to downstream rules that depend on it. In Make, a rule can only create a single file, which can be consumed by downstream rules, in contrast. Providers are very important to understanding how the action graph is created after analysis, and are key to Buck's modularity — they're why you can have a So now we know that
My understanding of platforms and their ins-and-outs is poor still. This is also getting too long. But the upshot is that you can use As a side note: actually, I think
There is See also #142 and the "configuration-at syntax" RFC linked within. This would allow you to conveniently set a specific configuration platform using syntax on the command line, which is basically what you want. In this case you would simply set a configuration by building a target like This is way way way too long. But personally, in this case I'd probably structure my solution differently until some of those features get added — I'd just avoid configurations and build every version of the thing you want, all with different target names. This takes longer to build the whole package, but is more flexible right now. It also helps you ensure every version of a build is working; e.g. a change doesn't break ASAN builds or something. That's actually a big problem with the approach to using So instead, have def cxx_multi_binaries(name, **kwargs):
default_way = {
"cflags": [],
"ldflags": [],
}
other_ways = {
"O3": {
"cflags": [ "-O3" ],
"ldflags": [ "-O3" ],
},
"LTO": {
"cflags": [ "-O3", "-flto" ],
"ldflags": [ "-fuse-ld=lld", "-flto" ],
},
}
# generate a target named 'foobar-XYZ' for every 'way' above
for (way_name, way_opts) in other_ways .items():
cxx_binary(
name = "{}-{}".format(name, way_name)
cflags = way_opts.cflags,
ld_flags = way_opts.ldflags,
*kwargs,
)
# and also a default binary
cxx_binary(name = name, cflags = default_way["cflags"], ldflags = default_way["ldflags", **kwargs) Then, in the load(":defs.bzl", "cxx_multi_binaries")
cxx_multi_binaries(
name = "test",
srcs = glob(["*.cxx"])
) And then you'll have targets named This is a long post but hopefully it'll help. Restructuring a build for Buck is fundamentally a lot of work. But it gives you a lot more power than CMake once you lean into it. Hope you're doing well, too! |
Haha, noticed you too in other places. Good to see you :) Anyway, this is a very long answer, so thank you! Will take me some time to read / digest. That said, from a cursory skim, it sounds like this is exactly the kind of advice and guidance that should find a way into the documentation (or examples) in some form. Perhaps I'll take it as an exercise to the reader to produce such an example and make a PR updating the examples folder with new examples. |
I'll also note something about the efficiency of my proposed solution. It's true that by representing each build as an individual target, rather than modifying
I call the above solution the "cartesian product" approach because rather than modifying the set of build parameters in place, you instead effectively represent the set of parameters to the build as a tuple |
I like the cartesian product idea, we tried to do this with CMake at one point but gave up because CMake did not make this easy. My biggest concern is that now engineers who don't care about build systems and who just want to click a button and have things work suddenly have to start remembering this cartesian product. Do you think it makes sense to say that developer only options used for testing and experimentation are better off being done via |
Yeah, one issue is that people have to pick out what version of the build they want, in the cartesian approach. So if you want ASAN+UBSAN+LTO+Fuzzing, you might have to look for a I missed his comment originally but, the approach from @dtolnay is instead closer to what people would expect to do with dev_options = {
"cflags": [ ... ],
"ldflags": [ ... ],
"defines": [ ... ],
} load(":devopts.bzl", "dev_options")
cxx_binary(
cflags = dev_options.cflags
# and similar for ldflags, etc
) In this example Ultimately an exact 1-to-1 replica of the |
I found this code from grepping the repository:
How does parsing of this
"ovr_config//os:macos"
string work? That also looks a lot like one of those target patterns, but that clearly is not a target. And it's not the same thing as@prelude//toolchains:cxx.bzl
either, because it doesn't have an@
at the beginning, and doesn't refer to a file.I grepped for
ovr_config
and found this:I guess it's coming from here. But the exact syntax isn't documented. And what is
os
? What other selectors are there that I could use besidesos
? It looks like this pattern might be a "target configuration constraint", which I figured out by looking at this piece of code from somewhere else:but if you type "constraint" into the search box of the documentation page you get nothing.
And how do these map to values from
.buckconfig
? Concrete example: Let's say I have this in my.buckconfig
:How can I do the following two things:
select()
statement that passes--compiler-flag-1
iffoo.bar == baz
, pass--compiler-flag-2
iffoo.bar == buzz
, and generate a build failure iffoo.bar
is anything else?.buckconfig
for the purposes of obtaining a value offoo.bar
, and have the user writebuck2 build <command-line-argument-that-forces-foo.bar-to-buzz>
?The text was updated successfully, but these errors were encountered: