> ## Documentation Index
> Fetch the complete documentation index at: https://site.aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Never Compile protoc Again

> Learn how to optimize Bazel builds by avoiding protoc compilation. Discover toolchain alternatives and solutions for faster, more efficient development.

export const BlogPost = ({title, date, authors, tags, image, children}) => {
  const tagList = tags ? tags.split(", ").filter(Boolean) : [];
  const tagSlug = t => t.toLowerCase().replace(/&/g, "").replace(/\+/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
  const formattedDate = date ? new Date(date + "T00:00:00").toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric"
  }) : "";
  return <section className="w-full flex justify-center px-4 py-12 md:py-16">
      <div style={{
    maxWidth: "800px",
    width: "100%"
  }}>
        {image && (typeof image === "string" ? <img noZoom src={image} alt={title} className="w-full rounded-xl mb-8" style={{
    maxHeight: "400px",
    objectFit: "cover"
  }} /> : <div className="blog-post-hero-image">{image}</div>)}
        <h1 className="text-3xl md:text-4xl font-bold text-zinc-900 dark:text-white">
          {title}
        </h1>
        <div className="flex flex-wrap items-center gap-3 mt-4 text-sm text-zinc-500 dark:text-zinc-400">
          {authors && <span>{authors}</span>}
          {authors && formattedDate && <span>·</span>}
          {formattedDate && <span>{formattedDate}</span>}
        </div>
        {tagList.length > 0 && <div className="flex flex-wrap gap-2 mt-3">
            {tagList.map(tag => <a key={tag} href={"/blog/tags/" + tagSlug(tag)} className="px-2 py-0.5 rounded-full text-xs bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 hover:bg-blue-100 dark:hover:bg-blue-900/40 hover:text-blue-700 dark:hover:text-blue-300 transition">
                {tag}
              </a>)}
          </div>}
        <hr className="my-8 border-zinc-200 dark:border-zinc-700" />
        <div className="prose dark:prose-invert max-w-none">{children}</div>
      </div>
    </section>;
};

export const MarketingPage = () => <div className="marketing-page-marker" style={{
  display: "none"
}} />;

export const Section = ({children, className = "", gray = false, dark = false, id}) => <section id={id} className={`w-full flex justify-center px-4 py-16 md:py-24 ${gray ? "bg-gray-50 dark:bg-zinc-900" : dark ? "bg-zinc-900 dark:bg-zinc-950" : ""} ${className}`}>
    <div className="w-full" style={{
  maxWidth: "1140px"
}}>
      {children}
    </div>
  </section>;

<MarketingPage />

<BlogPost title="Never Compile protoc Again" date="2025-04-28" authors="Alex Eagle" tags="Bazel, Releases">
  <img noZoom src="https://mintcdn.com/aspectbuild/x1L7Iep716jCyJVo/images/blog/hashnode/hn-64dadc5430.jpeg?fit=max&auto=format&n=x1L7Iep716jCyJVo&q=85&s=2a8fa0e5334e6fcad424c812a2772b13" alt="" className="blog-post-cover" width="1600" height="1067" data-path="images/blog/hashnode/hn-64dadc5430.jpeg" />

  ## Background

  Protocol Buffers needs a code generation step, turning your `.proto` files into “stub” code in your language of choice which provides marshaling/unmarshaling of proto messages (binary or JSON typically) into language-idiomatic data structures. If you use `services`, then you also want “stub” code to implement clients and/or servers that use gRPC.

  This code generation needs a “compiler” for the protobuf language and the idiomatic compiler provided by the Protobuf team at Google is `protoc`. They provide binary releases of this tool on [https://github.com/protocolbuffers/protobuf/releases](https://github.com/protocolbuffers/protobuf/releases) and that’s where most protobuf users get it (maybe via a plugin for their Build system, such as [https://github.com/google/protobuf-gradle-plugin](https://github.com/google/protobuf-gradle-plugin))

  However under Bazel, the common means of getting the tool is to compile it yourself from [this `cc_binary` target](https://github.com/protocolbuffers/protobuf/blob/5014f131760523702281f3e05c1e539f08f450c9/BUILD.bazel#L348-L354). This is what Googlers expect, having lived in an extreme monorepo where everything is vendored, and security is paramount. See an [interesting comment](https://github.com/bazelbuild/bazel-central-registry/pull/3843#issuecomment-2675948147) I got in a discussion:

  > Interesting. I wasn't aware there was a contingent of Bazel rules maintainers who are opposed to google3-style source-only builds.

  Yes! I’m one of those maintainers. That’s because most of us don’t have perfect caching. Ideally you don’t notice `protoc` compilation other than “the first time”. In practice, I wait for `protoc` to build all the time, and it spams my CI log with `gcc` warnings that are irrelevant to me. And on some machines, the compilation fails. If you haven’t listened to the Aspect Insights podcast, this topic was all the way back at [episode 1](https://www.youtube.com/watch?v=s0i_Ra_mG9U\&list=PLLU28e_DRwdtpojOqWM5UeFyxad7m9gCF\&index=12).

  ## Avoiding it

  The exciting news is that many Bazel projects can stop compiling `protoc` TODAY.

  Aspect engineer Sahin worked with the Bazel team long ago to help [introduce a new flag](https://github.com/bazelbuild/rules_proto/discussions/213), allowing the `protoc` binary to be registered as a [toolchain](https://bazel.build/extending/toolchains). This gives us the flexibility of registering something other than the `cc_binary` target. In fact, you’re free to register `/bin/false` as your protocol buffer compiler, if you want something very fast and very broken.

  I then wrote [https://github.com/aspect-build/toolchains\_protoc](https://github.com/aspect-build/toolchains_protoc) which gives the convenience of downloading the official binary release, and registering that. See the docs on that repo, and especially the `examples` folder.

  Unfortunately we haven’t made as much progress as we’d like, because there’s an ecosystem-wide change required. Everywhere that references the protobuf compiler needs to use the toolchain to resolve it. As of this writing, [https://github.com/protocolbuffers/protobuf/pull/19679](https://github.com/protocolbuffers/protobuf/pull/19679) is pending as a fix for the worst culprit.

  Then there is a long, long tail of issues. For example, I was waiting 10 minutes to cut releases of our Bazel OSS rulesets, just because the stardoc documentation generator is a `java_binary` target with a hard-coded transitive dep on the `cc_binary(name=”protoc”)` target and the CD step wasn’t getting a cache hit for it. So I worked around that with another pre-build: [https://github.com/alexeagle/stardoc-prebuilt](https://github.com/alexeagle/stardoc-prebuilt) and then applied a small patch to our rulesets to override the `renderer` attribute, for example: [https://github.com/aspect-build/rules\_js/pull/2156](https://github.com/aspect-build/rules_js/pull/2156). It’s unfortunate to have to work around the problem, but in this case it’s worth it to fix the slowest step in our releases.

  ## Making it official

  We’re hoping to upstream our `toolchains_protoc` to the protobuf repository, so that the default Bazel experience will be fast. Of course, you can still choose to register the `cc_binary` target as your proto compiler toolchain, if you want to build it from source.
</BlogPost>
