Skip to main content

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() or select_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:

  1. Evaluates each key of select options
  2. Collects all keys that match the current configuration
  3. 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
  4. 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:

  1. --target-platforms command-line flag (highest priority)
  2. default_target_platform attribute on the target
  3. 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 X and Y match 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 object
  • select_map() - Applies a function to all possible values
  • select_test() - Tests if any value passes a predicate
  • target_compatible_with - Platform compatibility filtering
  • Buck2 docs: "Configurations By Example"