Select
The Select type represents a conditional attribute value in Buck2.
Select objects are created using the select() function and enable build rules to have
different attribute values based on the target's configuration.
This is essential for cross-platform builds and conditional compilation.
Resolution Timing
Select objects are resolved during Buck2's configuration phase, which happens after
BUCK file evaluation but before rule implementation. This means:
- Starlark code in BUCK files and macro rules cannot see resolved values
- Use
select_map()orselect_test()for macro-level operations
Operations
Select supports the addition operator (+) for combining conditional values:
# Combine multiple selects
deps = select({
"//config:linux": ["//lib:linux"],
"DEFAULT": [],
}) + select({
"//config:debug": ["//debug:tools"],
"DEFAULT": [],
})
# Add to regular values
flags = ["-Wall"] + select({
"//config:optimize": ["-O3"],
"DEFAULT": ["-O0"],
})
Resolution Algorithm
When Buck2 resolves a Select:
- Evaluates each key of select options
- Collects all keys that match the current configuration
- Applies refinement to choose the most specific match:
- If zero matches: Use DEFAULT or error if no DEFAULT
- If one match: Use that value
- If multiple matches: Select the one that is most specific
- If ties exist (equally specific):
- Same values: OK, use that value
- Different values: Error
Select Refinement Example
config_setting(
name = "linux",
constraint_values = ["//constraints:linux"], # 1 constraint
)
config_setting(
name = "linux-arm64",
constraint_values = [
"//constraints:linux",
"//constraints:arm64", # 2 constraints - more specific
],
)
my_rule(
srcs = select({
":linux": ["generic_linux.cpp"],
":linux-arm64": ["optimized_arm64.cpp"], # This wins when both match
}),
)
Key point: Order in the dictionary doesn't matter; specificity always wins.
Configuration System
Constraints
-
constraint_setting: Defines a configuration dimension
constraint_setting(name = "os")
constraint_setting(name = "cpu") -
constraint_value: Specific value for a dimension
constraint_value(name = "linux", constraint_setting = ":os")
constraint_value(name = "arm64", constraint_setting = ":cpu") -
platform: Collection of constraint values
platform(
name = "linux-arm64",
constraint_values = [":linux", ":arm64"],
)
config_setting
Matches specific configuration conditions:
# Match constraints
config_setting(
name = "linux-arm",
constraint_values = [
"//constraints:linux",
"//constraints:arm",
],
)
# Match buckconfig values
config_setting(
name = "fastmode",
values = {
"build.fastmode": "true",
},
)
# Combine both
config_setting(
name = "linux-fastmode",
constraint_values = ["//constraints:linux"],
values = {"build.fastmode": "true"},
)
Platform Selection
Buck2 determines the target platform through:
--target-platformscommand-line flag (highest priority)default_target_platformattribute on the target- Cell's default platform from buckconfig
Example:
buck2 build //app:main --target-platforms //platforms:linux-x86_64
Target Compatibility
Buck2 provides two attributes for platform compatibility:
target_compatible_with
ALL semantics: Target is compatible only if all listed constraints match:
cxx_library(
name = "windows_dev_only",
target_compatible_with = [
"//constraints:windows", # Must be Windows AND
"//constraints:dev", # Must be dev mode
],
)
compatible_with
ANY semantics: Target is compatible if any listed constraint matches:
cxx_library(
name = "unix_compatible",
compatible_with = [
"//constraints:linux", # Linux OR
"//constraints:macos", # macOS (either works)
],
)
Common Patterns
Platform-Specific Dependencies
cxx_library(
name = "mylib",
srcs = ["common.cpp"],
deps = [
"//common:base",
] + select({
"//config:linux": ["//platform:linux_support"],
"//config:macos": ["//platform:macos_support"],
"//config:windows": ["//platform:windows_support"],
}),
)
Build Mode Flags
cxx_binary(
name = "app",
compiler_flags = select({
"//config:debug": ["-g", "-O0"],
"//config:release": ["-O3", "-DNDEBUG"],
"DEFAULT": ["-O2"],
}),
)
Architecture-Specific Optimizations
cxx_library(
name = "simd",
compiler_flags = select({
"//config:x86_64": ["-msse4.2"],
"//config:arm64": ["-march=armv8-a"],
"DEFAULT": [],
}),
)
Working with Selects in Macros
Since select() values aren't resolved during BUCK evaluation, use these functions:
-
select_map(value, func): Transform all possible values
# Add suffix to all possible values
new_deps = select_map(
deps_select,
lambda items: [item + "_wrapper" for item in items]
) -
select_test(value, func): Test if any value matches predicate
# Check if any branch has more than 2 items
has_many = select_test(deps_select, lambda items: len(items) > 2)
Error Messages
Common errors:
- No match without DEFAULT: "None of N conditions matched configuration ... and no default was set"
- Ambiguous match: "Both select keys
XandYmatch the configuration, but neither is more specific and they have different values" - Invalid usage: Cannot use
select()inside config_setting or other configuration rules
See Also
select()- Creates a Select objectselect_map()- Applies a function to all possible valuesselect_test()- Tests if any value passes a predicatetarget_compatible_with- Platform compatibility filtering- Buck2 docs: "Configurations By Example"