PageSourceSearch

https://www.buildbuddy.io/assets/js/24c09500.3828a4ba.js

js buildbuddy.io collected 2026-09-24 08:43:07 UTC 42,884 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkbuildbuddy_docs_website=self.webpackChunkbuildbuddy_docs_website||[]).push([[3726],{78290(e,n,t){t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>h,frontMatter:()=>r,metadata:()=>s,toc:()=>c});var s=t(67940),i=t(86070),o=t(35676);const r={slug:"buck2-review",title:"Buck2 Unboxing",description:"A review of Meta's new build tool Buck2.",authors:"son",date:"2023-05-30:12:00:00",image:"/img/blog/buck2-header.png",tags:["buck2","engineering"]},a="Buck2 unboxing",l={authorsImageUrls:[void 0]},c=[{value:"Overview",id:"overview",level:2},{value:"The pros",id:"the-pros",level:2},{value:"Incredible CLI UX",id:"incredible-cli-ux",level:4},{value:"First-class Remote Execution",id:"first-class-remote-execution",level:4},{value:"Easy to contribute",id:"easy-to-contribute",level:4},{value:"The cons",id:"the-cons",level:2},{value:"It&#39;s actually multiple phases!",id:"its-actually-multiple-phases",level:4},{value:"No Build Telemetry",id:"no-build-telemetry",level:4},{value:"No Repository Phase",id:"no-repository-phase",level:4},{value:"Complicated testing abstraction",id:"complicated-testing-abstraction",level:4},{value:"Other foot guns",id:"other-foot-guns",level:4},{value:"The verdict",id:"the-verdict",level:2},{value:"Update (2023-06-15)",id:"update-2023-06-15",level:2},{value:"Putting all new improvements together",id:"putting-all-new-improvements-together",level:4}];function d(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h4:"h4",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.p,{children:"Recently Meta announced the release of Buck2, their official build tool that was rewritten in Rust.\nI got a chance to try it and integrate it with BuildBuddy's Remote Cache and Remote Build Execution offerings."}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{src:t(22901).A+"",width:"800",height:"485"})}),"\n",(0,i.jsx)(n.p,{children:"Here are the initial impressions I have after a week of using Buck2."}),"\n",(0,i.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Edit"})," (2023-06-15): The Bazel team did reach out to me with some corrections to how the benchmark\nwas setup. Please find revised numbers at bottom of this blog."]}),"\n",(0,i.jsxs)(n.p,{children:["Similar to Buck(1) and Bazel, Buck2 is an ",(0,i.jsx)(n.a,{href:"https://bazel.build/basics/artifact-based-builds",children:"artifact-based"})," build tool that operates on a large graph of dependencies."]}),"\n",(0,i.jsxs)(n.p,{children:["Buck2 was rewritten from scratch in Rust while providing many backward compatibility features with Buck(1).\nOn the ",(0,i.jsx)(n.a,{href:"https://buck2.build/docs/why/",children:"official website of Buck2"}),", Meta touted it to be:"]}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsx)(n.li,{children:"Remote Execution first"}),"\n",(0,i.jsx)(n.li,{children:"Dynamic graph computation engine"}),"\n",(0,i.jsx)(n.li,{children:"A single build phase"}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"So how true are these claims?\nIs Buck2 going to replace Bazel?\nIs this the right tool for your needs?"}),"\n",(0,i.jsx)(n.p,{children:"Let's dive in!"}),"\n",(0,i.jsx)(n.h2,{id:"the-pros",children:"The pros"}),"\n",(0,i.jsx)(n.h4,{id:"incredible-cli-ux",children:"Incredible CLI UX"}),"\n",(0,i.jsxs)(n.p,{children:["Right out of the box, Buck2 feels incredibly responsive.\nThe command line is ridiculously fast compared to Bazel 6.2.0.\nHere, we are using ",(0,i.jsx)(n.a,{href:"https://github.com/sharkdp/hyperfine",children:"Hyperfine"})," to benchmark the two build tools."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"> hyperfine 'buck2 --version' --shell=none --warmup 3 --runs 20\nBenchmark 1: buck2 --version\n  Time (mean \xb1 \u03c3):       5.5 ms \xb1   0.6 ms    [User: 2.7 ms, System: 1.4 ms]\n  Range (min \u2026 max):     5.0 ms \u2026   7.1 ms    20 runs\n\n> hyperfine 'bazel version' --shell=none --warmup 3 --runs 20\nBenchmark 1: bazel version\n  Time (mean \xb1 \u03c3):      31.2 ms \xb1   0.7 ms    [User: 9.7 ms, System: 11.8 ms]\n  Range (min \u2026 max):    29.8 ms \u2026  32.4 ms    20 runs\n"})}),"\n",(0,i.jsx)(n.p,{children:"Moreover, the CLI was written in Rust's Clap library, which comes with a sane help menu\nand (soon to come) auto-completion."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"> buck2 -h\nbuck2 42652bf853883e0391382f6b8d038fe6 <local>
1\nA build system\n\nDocumentation: https://buck2.build/docs/\n\nUSAGE:\n    buck2 [OPTIONS] <SUBCOMMAND>\n\nOPTIONS:\n    -h, --help\n            Print help information\n\n        --isolation-dir <ISOLATION_DIR>\n            Instances of Buck2 share a daemon if and only if their isolation directory is identical.\n            The isolation directory also influences the output paths provided by Buck2, and as a\n            result using a non-default isolation dir will cause cache misses (and slower builds)\n            [env: BUCK_ISOLATION_DIR=] [default: v2]\n\n    -v, --verbose <NUMBER>\n            How verbose buck should be while logging. Values: 0 = Quiet, errors only; 1 = default; 2\n            = more info about errors; 3 = more info about everything [default: 1]\n\n    -V, --version\n            Print version information\n\nSUBCOMMANDS:\n    aquery       Perform queries on the action graph (experimental)\n    audit        Perform lower level queries\n    build        Build the specified targets\n    bxl          Run BXL scripts\n    clean        Delete generated files and caches\n    cquery       Perform queries on the configured target graph\n    ctargets     Resolve target patterns to configured targets\n    docs         Print documentation of specified symbols\n    help         Print this message or the help of the given subcommand(s)\n    init         Initialize a buck2 project\n    install      Build and install an application\n    kill         Kill the buck daemon\n    killall      Kill all buck2 processes on the machine\n    log          Commands for interacting with buck2 logs\n    lsp          Start an LSP server for starlark files\n    profile      Profiling mechanisms\n    query        Alias for `uquery`\n    rage         Record information about the previous failed buck2 command\n    root         Find buck cell, project or package root\n    run          Build and run the selected target\n    server       Start, query, and control the http server\n    starlark     Run Starlark operations\n    status       Buckd status\n    subscribe    Subscribe to updates from the Buck2 daemon\n    targets      Show details about the specified targets\n    test         Build and test the specified targets\n    uquery       Perform queries on the unconfigured target graph\n"})}),"\n",(0,i.jsx)(n.p,{children:"This means that even the subcommands flags are declared with the framework and\ncomes with amazing default documentation."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"> buck2 rage -h\nbuck2-rage\nRecord information about the previous failed buck2 command\n\nUSAGE:\n    buck2 rage [OPTIONS]\n\nOPTIONS:\n    -h, --help\n            Print help information\n\n        --invocation-id <INVOCATION_ID>\n            Select invocation directly using the invocation's UUID\n\n        --invocation-offset <INVOCATION_OFFSET>\n            Use value 0 to select last invocation, 1 to select second to last and so on\n\n        --no-invocation\n            Collect rage report about buck2 in general, not about specific invocation\n\n        --no-paste\n            We may want to omit paste if this is not a user or is called in a machine with no pastry\n            command\n\n        --origin <ORIGIN>\n            Where buck2 rage is being called from [default: unspecified] [possible values:\n            hang-detector, unspecified]\n\n        --timeout <TIMEOUT>\n            Stop collecting information after `<timeout>` seconds [default: 60]\n\n    -v, --verbose <NUMBER>\n            How verbose buck should be while logging. Values: 0 = Quiet, errors only; 1 = default; 2\n            = more info about errors; 3 = more info about everything [default: 1]\n"})}),"\n",(0,i.jsx)(n.p,{children:"The build progress library was custom-made for Buck2 and can sort and color-code slower actions.\nHere is a quick sped up demo:"}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{src:t(9480).A+"",width:"777",height:"250"})}),"\n",(0,i.jsx)(n.h4,{id:"first-class-remote-execution",children:"First-class Remote Execution"}),"\n",(0,i.jsx)(n.p,{children:'Buck2 was not kidding about it being a "Remote Execution first" build tool.'}),"\n",(0,i.jsx)(n.p,{children:"Thanks to this assumption, Buck2 was able to cut corners by not providing any local-sandboxing implementations by default.\nWhat seems to be an unwanted side-effect at scale, actually made the tool more friendly to new learners.\nThe lack of local sandboxing allows Buck2 to behave locally closer to other task-runner build tools such as Make or Just."}),"\n",(0,i.jsxs)(n.p,{children:["However, any mistakes that are made when authoring build definitions locally will very soon be revealed once moved to Remote Build Execution.\nBuck2 follows the same industry standard ",(0,i.jsx)(n.a,{href:"https://github.com/bazelbuild/remote-apis",children:"Remote Execution API"})," that was adopted by the like of Bazel, Goma, Pants, Please, Recc...\nThis means that Buck2 is compatible with BuildBuddy Remote Cache and Remote Build Execution offering! \ud83c\udf89"]}),"\n",(0,i.jsx)(n.p,{children:"So let's test it out!"}),"\n",(0,i.jsx)(n.p,{children:"First, let's establish a setup to benchmark our test"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'SIZE_RANGE = range(0, 1000)\n\nXYZ_SHA256 = "72036ae48c55d632cc333cac04c78be336b0c76f39b225d023e218efe077ab1b"\n\n# Add some execution time to each action to simulate a real build.\nSLEEP_CMD = "sleep $$((RANDOM % 5));"\n\n# Stage 1\n[\n    genrule(\n        name = "x_{}".format(n),\n        outs = ["x_{}.txt".format(n)],\n        cmd = SLEEP_CMD + "echo x_{} > $@".format(n),\n    )\n    for n in SIZE_RANGE\n]\n\n[\n    genrule(\n        name = "y_{}".format(n),\n        outs = ["y_{}.txt".format(n)],\n        cmd = SLEEP_CMD + "echo y_{} > $@".format(n),\n    )\n    for n in SIZE_RANGE\n]\n\n# Stage 2\n[\n    genrule(\n        name = "z_{}".format(n),\n        outs = ["z_{}.txt".format(n)],\n        cmd = SLEEP_CMD + "cat $(location :x_{n}) $(location :y_{n}) > $@".format(n = n),\n        tools = [\n            ":x_{}".format(n),\n            ":y_{}".format(n),\n        ],\n    )\n    for n in SIZE_RANGE\n]\n\n# Stage 3\ngenrule(\n    name = "xyz",\n    outs = ["xyz.txt"],\n    # cat $(location :z_0) $(location :z_1) ... $(location :z_999) > $@,\n    cmd = SLEEP_CMD + "cat " + " ".join([\n        "$(location :z_{})".format(n)\n        for n in SIZE_RANGE\n    ]) + " > $@",\n    tools = [\n        ":z_{}".format(n)\n        for n in SIZE_RANGE\n    ],\n)\n\n# Stage 4\nsh_test(\n    name = "test",\n    srcs = ["test.sh"],\n    args = [\n        "$(location :xyz)",\n        XYZ_SHA256,\n    ],\n    data = [\n        ":xyz",\n    ],\n)\n'})}),"\n",(0,i.jsx)(n.p,{children:"Our setup is a build graph composed of 4 stages:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:'Stage 1: Create 1000 "x" targets and 1000 "y" targets (2000 build targets)'}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:'Stage 2: Create 1000 "z" targets by concat "x" and "y" from Stage 1 (1000 build targets)'}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:'Stage 3: Concat all "z" targets (1 build target)'}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"Stage 4: Validate stage 3 output with a test (1 test target)"}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"Before each action, we sleep a random duration between 0 and 4 seconds to make the simulation closer to real-world build."}),"\n",(0,i.jsx)(n.p,{children:"We also inject this sleep value into our test to help validate testing features down the line:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:">
1 cat test.sh\n#!/bin/bash\n\nfile=$1\nexpected=$2\ngot=$(sha256sum $file | awk '{print $1}')\n\nsleep $((RANDOM % 5));\nif [[ $expected == $got ]]; then\n    exit 0\nelse\n    exit 1\nfi\n"})}),"\n",(0,i.jsx)(n.p,{children:"With a total of 2002 user-defined targets and 4 stages, assuming perfect concurrency,\nit should result in a worst-case 16 seconds, average 8 seconds, sleep overhead."}),"\n",(0,i.jsx)(n.p,{children:"Let's use Bazel's latest release, 6.2.0, as our baseline and see how Buck2 compares."}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"Local Execution"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"> cat .bazelrc\nbuild:local --strategy=local\nbuild:local --jobs=200\nbuild:local --local_cpu_resources=200\nbuild:local --local_ram_resources='HOST_RAM*2.0'\n"})}),"\n",(0,i.jsxs)(n.p,{children:["It's worth noting that Bazel executes actions in a sandbox by default, so ",(0,i.jsx)(n.code,{children:"--strategy=local"})," is used here to explicitly disable sandboxing.\nWe also set Bazel's concurrency limit to 200 by specifying ",(0,i.jsx)(n.code,{children:"--jobs=200"})," and override Bazel's system resource estimations by specifying\n",(0,i.jsx)(n.code,{children:"--local_cpu_resources"})," and ",(0,i.jsx)(n.code,{children:"--local_ram_resources"})," to larger values.\nBecause our actions are lightweight, with majority of the time being spent on ",(0,i.jsx)(n.code,{children:"sleep(1)"}),' call,\nthese Bazel flags enable us to hit the "200 concurrent build actions" mark.']}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"> hyperfine --prepare 'bazel clean' --warmup 1 'bazel test --config=local //...'\nBenchmark 1: bazel test --config=local //...\n  Time (mean \xb1 \u03c3):     65.631 s \xb1  0.460 s    [User: 0.051 s, System: 0.063 s]\n  Range (min \u2026 max):   65.179 s \u2026 66.310 s    10 runs\n"})}),"\n",(0,i.jsx)(n.p,{children:"To ensure fairness, we also set Buck2 execution concurrency to the same value as Bazel:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"> tail -2 .buckconfig\n[build]\nthreads = 200\n"})}),"\n",(0,i.jsx)(n.p,{children:"Here, Buck's Starlark API differs slightly from Bazel's, so some small modifications were needed.\nFor example:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-diff",children:'@@ -21,9 +21,9 @@\n [\n     genrule(\n         name = "z_{}".format(n),\n-        outs = ["z_{}.txt".format(n)],\n+        out = "z_{}.txt".format(n),\n-        cmd = "cat $(location :x_{n}) $(location :y_{n}) > $@".format(n = n),\n+        cmd = "cat $(location :x_{n}) $(location :y_{n}) >$OUT".format(n = n),\n-        tools = [\n+        srcs = [\n             ":x_{}".format(n),\n             ":y_{}".format(n),\n         ],\n'})}),"\n",(0,i.jsx)(n.p,{children:"With all that set, let's see how Buck2 performs:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"> hyperfine  --prepare 'buck2 clean' --warmup 1 'buck2 test //...'\nBenchmark 1: buck2 test //...\n  Time (mean \xb1 \u03c3):     41.175 s \xb1  2.183 s    [User: 0.371 s, System: 0.190 s]\n  Range (min \u2026 max):   37.584 s \u2026 43.803 s    10 runs\n"})}),"\n",(0,i.jsx)(n.p,{children:"And quite a performance indeed, Buck2 left Bazel in the dust!"}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"Remote Execution"}),"\n",(0,i.jsx)(n.p,{children:"Using BuildBuddy as the RBE backend should be as simple as copying the configurations from the website:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"build:remote --jobs=600\n\nbuild:remote --bes_results_url=https://app.buildbuddy.io/invocation/\nbuild:remote --bes_backend=grpcs://remote.buildbuddy.io\nbuild:remote --remote_cache=grpcs://remote.buildbuddy.io\nbuild:remote --remote_timeout=3600\nbuild:remote --remote_executor=grpcs://remote.buildbuddy.io\nbuild:remote --remote_header=x-buildbuddy-api-key=<redacted>\n"})}),"\n",(0,i.jsx)(n.p,{children:"With that set, let's check the result:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"# With remote cache\n> hyperfine --prepare 'bazel clean' \\\n            --warmup 1 \\\n            'bazel test --config=remote //...'\nBenchmark 1: bazel test --config=remote //...\n  Time (mean \xb1 \u03c3):      9.798 s \xb1  0.515 s    [User: 0.028 s, System: 0.039 s]\n  Range (min \u2026 max):    8.645 s \u2026 10.434 s    10 runs\n\n# Without remote cache\n# Set remote_instance_name to a random string to reset remote_cache\n> hyperfine  --prepare 'bazel clean' \\\n             --warmup 1 \\\n             'bazel test --config=remote --remote_instance_name=\"$RANDOM\" //...'\nBenchmark 1: bazel test --config=remote --remote_instance_name=\"$RANDOM\" //...\n  Time (mean \xb1 \u03c3):     106.062 s \xb1  3.106 s    [User: 0.090 s, System: 0.099 s]\n  Range (min \u2026 max):   102.111 s \u2026 111.645 s    10 runs\n"})}
1),"\n",(0,i.jsx)(n.p,{children:"10 seconds is amazing compared to the local execution time of 65 seconds.\nIt's 84% faster with Remote Build Execution and Remote Cache!"}),"\n",(0,i.jsxs)(n.p,{children:["But what about Buck2?\nWell setting it up is as simple as copying the provided configurations in ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/buck2/tree/main/examples/remote_execution/buildbuddy",children:"Buck2's BuildBuddy Example"})]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"[build]\nexecution_platforms = root//platforms:platforms\n\n# [buck2_re_client]\nengine_address = remote.buildbuddy.io\naction_cache_address = remote.buildbuddy.io\ncas_address = remote.buildbuddy.io\nhttp_headers = x-buildbuddy-api-key:<redacted>\n"})}),"\n",(0,i.jsx)(n.p,{children:"Let's run it:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"# With remote cache\n> hyperfine --prepare 'buck2 clean' \\\n            --warmup 1 \\\n            'buck2 test --unstable-allow-all-tests-on-re //...'\n  Time (mean \xb1 \u03c3):      7.937 s \xb1  0.487 s    [User: 0.296 s, System: 0.175 s]\n  Range (min \u2026 max):    7.357 s \u2026  8.945 s    10 runs\n\n# Without remote cache\n> hyperfine --prepare 'buck2 clean; echo \"INSTANCE_NAME = \\\"$RANDOM\\\"\" > instance.bzl' \\\n            'buck2 test --unstable-allow-all-tests-on-re //...'\nBenchmark 1: buck2 test --unstable-allow-all-tests-on-re //...\n  Time (mean \xb1 \u03c3):     203.431 s \xb1 29.882 s    [User: 4.187 s, System: 1.566 s]\n  Range (min \u2026 max):   163.044 s \u2026 258.968 s    10 runs\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Buck2 does not have a good way to invalidate the remote cache to give a fair comparison,\nso I did apply a workaround by invalidating all the ",(0,i.jsx)(n.code,{children:"x"})," and ",(0,i.jsx)(n.code,{children:"y"})," actions with a random value instead."]}),"\n",(0,i.jsx)(n.p,{children:"Regardless, the performance was very impressive.\nThe Remote Build Execution client of Buck2 is not as configurable as Bazel, so it was harder to tune.\nI expect it will make builds perform much better in the future."}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"easy-to-contribute",children:"Easy to contribute"}),"\n",(0,i.jsx)(n.p,{children:"Seriously, Buck2 code base is a blast to work with.\nUnlike Bazel, which was written in a mix of C++ and Java using Google's homegrown frameworks,\nBuck2 was built with a single language: Rust!"}),"\n",(0,i.jsx)(n.p,{children:'Not only can the code base be built using Buck2, but it could also be built using Cargo, and\nis completely compatible with Rust Analyzer (Rust\'s Language Server).\nIt has been a long time since I could open the code base of a build tool and have "Go to definitions"\nand "Find references" work out of the box.'}),"\n",(0,i.jsx)(n.p,{children:"The Buck2 team in Meta has been quite engaging thus far.\nIt often takes them less than a week to respond to my GitHub Issues / Pull Requests.\nAnd most of the time, I get a response within a day."}),"\n",(0,i.jsx)(n.p,{children:"Diving into Buck2 and its Rust code base is... addictive. \ud83d\ude05"}),"\n",(0,i.jsx)(n.h2,{id:"the-cons",children:"The cons"}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:"So is that it? Buck2 is faster, let's all move there?"}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"Not so fast young padawan!\nBuck2 does come with some downsides."}),"\n",(0,i.jsx)(n.h4,{id:"its-actually-multiple-phases",children:"It's actually multiple phases!"}),"\n",(0,i.jsxs)(n.p,{children:["In contrary to it's official introduction, ",(0,i.jsx)(n.a,{href:"https://buck2.build/docs/developers/architecture/buck2/",children:"Buck2's architecture diagram"}),"\ndescribes multiple phases and stages inside a typical build."]}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{src:t(95087).A+"",width:"2060",height:"1256"})}),"\n",(0,i.jsxs)(n.p,{children:["There seems to be 5 Phases acting as transitions between 5 states of Buck2 dynamic build graph, ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/buck2/tree/main/dice",children:"DICE"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:['The good thing about Buck2 is that none of these phases are "blocking", and thus multiple targets could\ntransition through different states in great parallelization. Bazel is also trying to work toward this\nsetup with the recently announced ',(0,i.jsx)(n.a,{href:"https://www.youtube.com/watch?v=IEJLHNNRP9U&list=PLxNYxgaZ8RsdH4GCIZ69dzxQCOPyuNlpF&index=10",children:"SkyMeld project"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"no-build-telemetry",children:"No Build Telemetry"}),"\n",(0,i.jsx)(n.p,{children:"First of all, because Buck2 does not implement Bazel's Build Event Stream protocol (not part of Remote Execution API),\nwe are not able to integrate with it to display your test results and provide you with sweet cache hit/miss analysis as we have with Bazel."}),"\n",(0,i.jsxs)(n.p,{children:["Buck2 does come with a proto-defined set of ",(0,i.jsx)(n.code,{children:"BuckEvent"})," data.\nCurrently, these log-like data are stored locally under ",(0,i.jsx)(n.code,{children:"buck-out/log"})," and could be interacted with using troubleshooting commands such as ",(0,i.jsx)(n.code,{children:"buck2 what-failed"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["Internally in Meta, these log data are sent to their ",(0,i.jsx)(n.a,{href:"https://engineering.fb.com/2019/10/07/data-infrastru
1cture/scribe/",children:"message queue system Scribe"})," using Thrift RPC, which is consumed by their Telemetry Data platform ",(0,i.jsx)(n.a,{href:"https://research.facebook.com/publications/scuba-diving-into-data-at-facebook/",children:"Scuba"}),".\nHowever, there is no open-source implementation equivalent and I created ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/buck2/issues/226",children:"buck2/issue/226"})," to discuss the creation of one."]}),"\n",(0,i.jsx)(n.h4,{id:"no-repository-phase",children:"No Repository Phase"}),"\n",(0,i.jsx)(n.p,{children:'In Bazel, external dependencies to the build are prepared before build execution happens, in the "Repository Phase".'}),"\n",(0,i.jsx)(n.p,{children:"Buck2 is touted to be a single phases build tool, so how does it manage 3rd party dependencies then?\nCurrently, there are only a limited set of options:"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["Git submodules\nBuck2 comes with ",(0,i.jsx)(n.strong,{children:"Prelude"}),", an open-source git repository that is meant to help define most Buck2 build rules.\nHowever, for each new Buck2 project being created with ",(0,i.jsx)(n.code,{children:"buck2 init"}),", you would need to set up Prelude as a git submodule in your git repo.\nWithout prelude, you would not get access to even basic rules such as ",(0,i.jsx)(n.code,{children:"genrule"})," or ",(0,i.jsx)(n.code,{children:"sh_binary"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["Vendor\nSince the majority of Buck2 is written in Rust, the companion tool ",(0,i.jsx)(n.strong,{children:"Reindeer"})," helps you vendor all\nCargo external packages into the repository and set up appropriate ",(0,i.jsx)(n.code,{children:"BUCK"})," files for them.\nThis is not a problem for Meta internally, with their custom monorepo and virtual file system ",(0,i.jsx)(n.a,{href:"https://sapling-scm.com/",children:"Sapling"}),",\nbut it should be a huge problem for Git repos and code review systems in the open-source world."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["Anon target and dynamic outputs\nAlthough there is no repository phase, you could still define ",(0,i.jsx)(n.code,{children:"http_archive"})," to have external downloads happen during Buck2 build execution.\nHowever, you will not be able to export build targets out of these newly downloaded archives easily.\nInstead of the Bazel approach which lets you run some commands to generate ",(0,i.jsx)(n.code,{children:"BUILD"})," files,\nBuck2 provides the complicated anon target, sub-targets, and dynamic outputs APIs in Starlark for you to accomplish the same thing.\nHowever, there is very limited documentation and examples of how these APIs work."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"complicated-testing-abstraction",children:"Complicated testing abstraction"}),"\n",(0,i.jsxs)(n.p,{children:["Unlike Bazel, which treats tests as another build action to be executed at the end of the graph,\nBuck2 took this to a whole new level by introducing a new RPC to integrate Buck2 with their internal testing platform ",(0,i.jsx)(n.a,{href:"https://buck2.build/docs/rule_authors/test_execution/",children:"TPX"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"Because TPX is in charge of coordinating test executions on various platforms and collecting related test outputs data,\nthe set of APIs that define integration between Buck2 and TPX is also quite featureful.\nHowever, there is not a lot of documentation at this moment regarding how these work."}),"\n",(0,i.jsxs)(n.p,{children:["Because of this reason, there is no test caching in Buck2 by default as the test target selector is supposed to be handled. But there are ongoing conversation about this in ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/buck2/issues/183",children:"buck2/issue/183"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"other-foot-guns",children:"Other foot guns"}),"\n",(0,i.jsx)(n.p,{children:"There are several pain points I experienced while testing out Buck2. They range from small annoyances to rendering the tool completely un-usable \ud83d\ude13:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"http_archive"})," does not work from within anon target. Fixed in ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/buck2-prelude/commit/370cd4d8b2cd002e0f794b91520febcab289b7d0",children:"buck2/commit/370cd4d"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["All Go rules are not usable without a standard library to compile/link against. Reported in ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/buck2/issues/240",children:"buck2/issue/240"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["Lack of documentation for ",(0,i.jsx)(n.code,{children:".buckconfig"})," keys and default values. Reported in ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/buck2/issues/152",children:"buck2/issue/152"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["No local cache for external downloads (leading to re-download happening on a Buck Daemon reset). Reported in ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/buck2/issues/246",children:"buck2/issue/246"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["Terminal UI reported the wrong value for actions counts. Reported in ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/buck2/issues/251",children:"buck2/issue/251"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["Remote Execution does not have symlinks supp
1ort ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/buck2/issues/222",children:"buck2/issue/222"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["Simply crash and not usable ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/buck2/issues/257",children:"buck2/issue/257"}),", but fixed with ",(0,i.jsx)(n.a,{href:"https://github.com/facebook/buck2/commit/79471567dd5e05f4035515c697e3fb9ef78622ac",children:"buck2/commit/7947156"}),"."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"the-verdict",children:"The verdict"}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:"So what does all this mean?"}),"\n",(0,i.jsx)(n.p,{children:"Should I start using Buck2 today?"}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"Buck2 is a fresh, new shiny build tool on the block.\nWith it comes a lot of good things:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"Great language, great performance"}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"Fresh UI"}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"Compatibility with existing RBE services"}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"It has all the joy and excitement of a typical green field project that attracts the Rust (and potentially C++) community toward it."}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:"But is it a Bazel replacement?"}),"\n",(0,i.jsx)(n.p,{children:"Should an enterprise consider adopting this for hundreds to thousands of users?"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["I would say ",(0,i.jsx)(n.em,{children:"not yet"}),"!"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"There are still too many foot guns that come with the new tool (as documented above)."}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"There is not enough configurations and/or documentation to support large-scale enterprise users."}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"Users are expected to be hands-on with the code base. If you don't know Rust... well you better learn it first."}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"Lack of build rules and toolings for typical use cases: building a container image, a typescript project, a go binary... are not possible just yet."}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"My conclusion is that Buck2 is exciting!\nHowever, today, it's still young and too early to replace typical enterprise use cases.\nI hope the current excitement could grow a community and ecosystem around Buck2 sustainably to further drive the tool to maturity."}),"\n",(0,i.jsxs)(n.p,{children:["In the meantime, I have created a repository to collect ",(0,i.jsx)(n.a,{href:"https://github.com/sluongng/awesome-buck2",children:"all Buck2-related resources here"}),".\nIf you are interested in Buck2, give it a look!"]}),"\n",(0,i.jsx)(n.h2,{id:"update-2023-06-15",children:"Update (2023-06-15)"}),"\n",(0,i.jsxs)(n.p,{children:["Shortly after this blog post was released, ",(0,i.jsx)(n.a,{href:"https://github.com/meisterT",children:"Tobias Werth"})," from the Bazel team pointed out to me\nthat the Bazel setup was not the most optimized."]}),"\n",(0,i.jsxs)(n.p,{children:["In particular, instead of using the ",(0,i.jsx)(n.code,{children:"tools"})," attribute in Bazel's ",(0,i.jsx)(n.code,{children:"genrule"}),", I should have used ",(0,i.jsx)(n.code,{children:"srcs"})," instead."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-diff",children:'--- a/BUILD.bazel\n+++ b/BUILD.bazel\n@@ -26,28 +26,28 @@ SLEEP_CMD = "sleep $$((RANDOM % 5));"\n [\n     genrule(\n         name = "z_{}".format(n),\n-        outs = ["z_{}.txt".format(n)],\n-        cmd = SLEEP_CMD + "cat $(location :x_{n}) $(location :y_{n}) > $@".format(n = n),\n-        tools = [\n+        srcs = [\n             ":x_{}".format(n),\n             ":y_{}".format(n),\n         ],\n+        outs = ["z_{}.txt".format(n)],\n+        cmd = SLEEP_CMD + "cat $(location :x_{n}) $(location :y_{n}) > $@".format(n = n),\n     )\n     for n in SIZE_RANGE\n ]\n\n genrule(\n     name = "xyz",\n+    srcs = [\n+        ":z_{}".format(n)\n+        for n in SIZE_RANGE\n+    ],\n     outs = ["xyz.txt"],\n     # cat $(location :z_0) $(location :z_1) ... $(location :z_999) > $@,\n     cmd = SLEEP_CMD + "cat " + " ".join([\n         "$(location :z_{})".format(n)\n         for n in SIZE_RANGE\n     ]) + " > $@",\n-    tools = [\n-        ":z_{}".format(n)\n-        for n in SIZE_RANGE\n-    ],\n )\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Using ",(0,i.jsx)(n.code,{children:"tools"})," would cause Bazel to configure twice the number of actions underneath due to Bazel's special treatment of tool dependencies.\nAnd fewer actions means a faster build."]}),"\n",(0,i.jsx)(n.p,{children:"So I reran the benchmark and here are the new results:"}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{style:{textAlign:"left"},children:"Tools"}),(0,i.jsx)(n.th,{style:{textAlign:"right"},children:"Local Build"}),(0,i.jsx)(n.th,{style:{textAlign:"right"},children:"RBE (with Remote Cache)"}),(0,i.jsx)(n.th,{style:{textAlign:"right"},children:"RBE (without Remote Cache)"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{style:{textAlign:"left"},children:"buck2"}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"41.175 s \xb1 2.183 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:" 7.937 s \xb1 0.487 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"203.431 s \xb1 29.882 s"})})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsxs)(n.td,{style:{textAlign:"left"},children:["bazel w. ",(0,i.jsx)(n.code,{children:"tools"})]}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"65.631 s \xb1 0.460 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:" 9.798 s \xb1 0.515 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"106.062 s \xb1 \u200e 3.106 s"})})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsxs)(n.td,{style:{textAlign:"left"},children:["bazel w. ",(0,i.jsx)(n.code,{children:"srcs"})]}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"42.580 s \xb1 1.881 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"10.047 s \xb1 0.517 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:" 73.009 s \xb1 \u200e 2.975 s"})})]})]})]}),"\n",(0,i.jsx)(n.p,{children:"This means that if setup correctly, Bazel performance is quite competitive with Buck2.\nDifferences of a few seconds could be negligible for most use cases."}),"\n",(0,i.jsx)(n.p,{children:"Another point of improvement is using the default BuildBuddy setting, we are uploading Bazel's Build Events to BuildBuddy on each build.\nWith Buck2, there is an equivalent Buck2 Event system.\nHowever, as noted in the blog post, Buck2 Event system is not yet compatible with systems outside of Meta so it's not possible to test with it just yet."}),"\n",(0,i.jsx)(n.p,{children:"So to achieve a more accurate test result, we could move Bazel's Build Event upload to a 
1separate config as follows:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-diff",children:"--- a/.bazelrc\n+++ b/.bazelrc\n@@ -14,12 +14,14 @@ build:remote --host_platform=//platforms:platform_linux\n build:remote --platforms=//platforms:platform_linux\n build:remote --crosstool_top=@buildbuddy_toolchain//:ubuntu_cc_toolchain_suite\n\n-build:remote --bes_results_url=https://app.buildbuddy.io/invocation/\n-build:remote --bes_backend=grpcs://remote.buildbuddy.io\n build:remote --remote_cache=grpcs://remote.buildbuddy.io\n build:remote --remote_timeout=3600\n build:remote --remote_executor=grpcs://remote.buildbuddy.io\n\n+build:remote-w-bes --config=remote\n+build:remote-w-bes --bes_results_url=https://app.buildbuddy.io/invocation/\n+build:remote-w-bes --bes_backend=grpcs://remote.buildbuddy.io\n"})}),"\n",(0,i.jsx)(n.p,{children:"How much of an improvement is it?"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"> hyperfine --prepare 'bazel clean' \\\n            --warmup 1 \\\n            'bazel test --config=remote //...' \\\n            'bazel test --config=remote-w-bes //...'\n\nBenchmark 1: bazel test --config=remote //...\n  Time (mean \xb1 \u03c3):      6.208 s \xb1  0.444 s    [User: 0.022 s, System: 0.031 s]\n  Range (min \u2026 max):    5.392 s \u2026  6.860 s    10 runs\n\nBenchmark 2: bazel test --config=remote-w-bes //...\n  Time (mean \xb1 \u03c3):     10.299 s \xb1  0.300 s    [User: 0.025 s, System: 0.036 s]\n  Range (min \u2026 max):    9.811 s \u2026 10.739 s    10 runs\n\nSummary\n  bazel test --config=remote //... ran\n    1.66 \xb1 0.13 times faster than bazel test --config=remote-w-bes //...\n"})}),"\n",(0,i.jsx)(n.h4,{id:"putting-all-new-improvements-together",children:"Putting all new improvements together"}),"\n",(0,i.jsx)(n.p,{children:"Here are the new benchmark results:"}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{style:{textAlign:"left"},children:"Tools"}),(0,i.jsx)(n.th,{style:{textAlign:"right"},children:"Local Build"}),(0,i.jsx)(n.th,{style:{textAlign:"right"},children:"RBE (with Remote Cache)"}),(0,i.jsx)(n.th,{style:{textAlign:"right"},children:"RBE (without Remote Cache)"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{style:{textAlign:"left"},children:"buck2"}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"41.175 s \xb1 2.183 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:" 7.937 s \xb1 0.487 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"203.431 s \xb1 29.882 s"})})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsxs)(n.td,{style:{textAlign:"left"},children:["bazel w. ",(0,i.jsx)(n.code,{children:"tools"})," (old)"]}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"65.631 s \xb1 0.460 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:" 9.798 s \xb1 0.515 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"106.062 s \xb1 \u200e 3.106 s"})})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsxs)(n.td,{style:{textAlign:"left"},children:["bazel w. ",(0,i.jsx)(n.code,{children:"srcs"})]}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"42.580 s \xb1 1.881 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"10.047 s \xb1 0.517 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:" 73.009 s \xb1 \u200e 2.975 s"})})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsxs)(n.td,{style:{textAlign:"left"},children:["bazel",(0,i.jsx)("br",{}),"w. ",(0,i.jsx)(n.code,{children:"srcs"}),", no ",(0,i.jsx)(n.code,{children:"BES"})]}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:"40.455 s \xb1 2.275 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:" 6.208 s \xb1 0.444 s"})}),(0,i.jsx)(n.td,{style:{textAlign:"right"},children:(0,i.jsx)(n.code,{children:" 70.788 s \xb1 \u200e 4.408 s"})})]})]})]}),"\n",(0,i.jsx)(n.p,{children:"This shows that when configured correctly, Bazel's performance is very competitive with Buck2's,\nand that Buck2 still has some room for growth in areas such as Remote Build Execution."}),"\n",(0,i.jsxs)(n.p,{children:["Interested readers could find my benchmarking setup in Github repo ",(0,i.jsx)(n.a,{href:"https://github.com/sluongng/buck2-rbe-bench",children:"sluongng/buck2-rbe-bench"}),"\nto experiment further."]})]})}function h(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},9480(e,n,t){t.d(n,{A:()=>s});const s=t.p+"assets/images/buck2-success-d388fa5c0141b3aa94952bbb94f8732c.gif"},22901(e,n,t){t.d(n,{A:()=>s});const s=t.p+"assets/images/buck2-780fab897c58d52a2c5e43171a00a001.jpg"},95087(e,n,t){t.d(n,{A:()=>s});const s=t.p+"assets/images/buck2_architecture-2404c487eb4c04045a09b9c7e27dd867.png"},35676(e,n,t){t.d(n,{R:()=>r,x:()=>a});var s=t(30758);const i={},o=s.createContext(i);function r(e){const n=s.useContext(o);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),s.createElement(o.Provider,{value:n},e.children)}},67940(e){e.exports=JSON.parse('{"permalink":"/blog/buck2-review","editUrl":"https://github.com/buildbuddy-io/buildbuddy/edit/master/website/blog/buck2.md","source":"@site/blog/buck2.md","title":"Buck2 Unboxing","description":"A review of Meta\'s new build tool Buck2.","date":"2023-05-30T12:00:00.000Z","tags":[{"inline":true,"label":"buck2","permalink":"/blog/tags/buck-2"},{"inline":true,"label":"engineering","permalink":"/blog/tags/engineering"}],"readingTime":19.16,"hasTruncateMarker":true,"authors":[{"name":"Son Luong Ngoc","title":"Solution Engineer @ BuildBuddy","url":"https://github.com/sluongng/","imageURL":"https://avatars.githubusercontent.com/u/26684313?v=4","key":"son","page":null}],"frontMatter":{"slug":"buck2-review","title":"Buck2 Unboxing","description":"A review of Meta\'s new build tool Buck2.","authors":"son","date":"2023-05-30:12:00:00","image":"/img/blog/buck2-header.png","tags":["buck2","engineering"]},"unlisted":false,"prevItem":{"title":"Providing Control Over Cache Encryption","permalink":"/blog/customer-managed-e
1ncryption-keys"},"nextItem":{"title":"Keyboard Shortcuts in BuildBuddy","permalink":"/blog/keyboard-shortcuts"}}')}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.