p4c, the P4 reference compiler, and the seven backends it ships with
P4_16 reference compiler
At a glance
- What is it?
- p4c is the reference compiler for the P4 language, built so that a standard front end and mid-end can be paired with any target back end. It gives network engineers seven working sample backends and a testing front end, at the cost of a C++ toolchain and a fast moving release cadence.
- Who is it for?
- p4c fits programmers who write P4 for a software switch, want the compiler's own backends to read alongside their own code, or need to test a program before it reaches hardware. It is the wrong choice for someone who only needs to parse a P4 file or wants a target that ships no open source back end.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A front end and mid-end that any back end can plug into
p4c is the reference compiler for the P4 programming language, and it supports both P4-14 and P4-16. The design decision that shapes everything else is modularity. The compiler supplies a standard front end and mid-end, and these combine with a target-specific back end to make a complete compiler. The stated goal is to make adding new back ends easy.
That arrangement tells you where the value sits for most adopters. You are not locked into one vendor's pipeline, and a target you care about that has no open source back end can still be worked on because the parsing, type checking and middle-end passes are shared. The repository layout matches that description directly, with separate top-level directories for frontends/, midend/, ir/, lib/, backends/, control-plane/ and tools/.
There is also a Python side to the project. The pyproject.toml declares a project named p4c requiring Python 3.10 or newer, with a license field of Apache and dependencies that include ply, scapy, thrift, protobuf, grpcio, p4runtime and ptf. So the tree is not purely a C++ compiler with a helper script; the tooling around the compiler, test utilities and control plane interfaces, is Python and versioned alongside the compiler itself.
Seven sample back ends, each aimed at a different target
The sample backends bundled in the tree are the quickest way to understand what the compiler can reach.
p4c-bm2-ss targets the P4 simple_switch written using the BMv2 behavioral model, and is the usual starting point for learning. p4c-dpdk targets the DPDK software switch (SWX) pipeline. p4c-ebpf generates C code that can be compiled to eBPF and loaded in the Linux kernel, and it implements three architecture models: ebpf_model.p4 for packet filtering, xdp_model.p4 for XDP, and a fully featured PSA (Portable Switch Architecture) model. p4c-ubpf generates eBPF code that runs in user space instead.
Two of the seven are not traffic compilers at all. p4test is a source-to-source P4 translator for testing, learning compiler internals and debugging. p4c-graphs generates visual representations of a P4 program, and for now only supports generating graphs of top-level control flows. The seventh, p4tools, is a platform for P4 test utilities including a test-case generator for P4 programs.
A developer who only needs one of these can install a package and ignore the rest. A developer building a new target reads the front end and mid end, then writes a back end of their own.
What the bmv2 target actually writes to disk
Running the compiler against the bmv2 target produces more than one artefact, and knowing the names saves a lot of confusion later. On success, the command creates files with the same base name as the P4 program you supplied, but with different suffixes in place of the .p4 extension.
The first is a file with the suffix .p4i, which is the output from running the preprocessor on your P4 program. The second is a file with the suffix .json, and that is the JSON file format expected by the BMv2 behavioral model simple_switch.
Two invocations cover both language versions:
p4c --target bmv2 --arch v1model my-p4-16-prog.p4
p4c --target bmv2 --arch v1model --std p4-14 my-p4-14-prog.p4The second line is worth reading twice, because the flag --std p4-14 changes what the front end accepts. A program written for P4-14 does not compile as P4-16 by accident, and the compiler is explicit about which dialect it is parsing rather than guessing.
The archive and architecture pair is discoverable from the command line itself, which saves time guessing at valid combinations:
p4c --help
p4c --target-helpThe first prints the documentation of supported command line options, and the second lists the supported target and arch pairs.
P4Info files describe the control plane your program exposes
A P4 program is only half of a network pipeline. The other half is the control plane, and p4c generates a description of it so that a controller can be written against your tables rather than against your source code.
The mechanism is the --p4runtime-files option. When it is supplied, P4C also creates a text file with the .txt suffix, described in the README as a P4Info file. It contains a description of the tables and other objects in the P4 program that have an auto-generated control plane API. The P4Runtime format is the mechanism by which a P4 target and a controller agree on the same set of tables, counters and action handles, and generating that description at compile time keeps the two in step.
This is the feature that separates p4c from a plain translator, and it is also the point of friction when versions drift. The pyproject.toml pins p4runtime to a specific commit hash and notes a FIXME about synchronising P4Runtime versions across CMake and Python, with a ptf pin beside it. In other words, the project itself flags a place where two version references can fall out of step, which is worth checking when a controller and a compiled target disagree about the control plane API.
The control-plane/ directory at the top of the repository is where this part of the toolchain lives, separate from the backends that emit packet processing code.
p4test and p4c-graphs let you read a program before you run it
Two tools in the tree answer questions that come up long before a program meets a target.
p4test checks the syntax of P4_16 or P4_14 source code, without limitations that might be imposed by any particular compiler back end, and it produces no output beyond error and warning messages. That makes it the right tool for a syntax question about a program you cannot yet compile for a specific target:
p4test my-p4-16-prog.p4
p4test --std p4-14 my-p4-14-prog.p4The same binary can move code between the two language versions. Auto-translating P4_14 source to P4_16 source is done with the --pp option:
p4test --std p4-14 my-p4-14-prog.p4 --pp auto-translated-p4-16-prog.p4For understanding structure, p4c-graphs generates GraphViz .dot files for parsers and controls of a P4_16 or P4_14 source program:
p4c-graphs my-p4-16-prog.p4
p4c-graphs --std p4-14 my-p4-14-prog.p4The .dot file is an intermediate, and the README shows how it becomes a document with graphviz in the picture:
dot -Tpdf ParserImpl.dot > ParserImpl.pdfThe catch is stated in the project itself: for now p4c-graphs only supports generating graphs of top-level control flows, so a deeply nested flow inside a control block will not show up in the picture.
Packaged installs exist for Ubuntu and Debian, and the build has two systems
P4C has package support for several Ubuntu and Debian distributions, and the documented Ubuntu path is the fastest way to get a working compiler.
For Ubuntu 20.04 and newer, a package is available from the p4lang repositories. The following adds that repository, imports its signing key, refreshes the package lists and installs the package:
source /etc/lsb-release
echo "deb http://download.opensuse.org/repositories/home:/p4lang/xUbuntu_${DISTRIB_RELEASE}/ /" | sudo tee /etc/apt/sources.list.d/home:p4lang.list
curl -fsSL https://download.opensuse.org/repositories/home:p4lang/xUbuntu_${DISTRIB_RELEASE}/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/home_p4lang.gpg > /dev/null
sudo apt-get update
sudo apt install p4lang-p4cThe repository is hosted on an openSUSE download server and the repository name is built from the distribution release you just sourced, which is why the first line matters. Debian 11 (Bullseye) is covered by a parallel procedure in the README.
Behind that package sits a source tree with two build systems side by side. CMakeLists.txt, cmake/, bootstrap.sh, the debian/ directory and pyproject.toml describe one path, while BUILD.bazel, WORKSPACE.bazel, MODULE.bazel, .bazelrc, .bazelversion and the bazel/ directory describe another. The CI configuration points the same way, with separate workflow badges for Debian, Fedora, macOS, a Bazel build, a nightly validation run and a container image build. A Dockerfile sits at the top of the tree for the container route, with build-time arguments for the base image, the number of make jobs, whether to build the P4Tools back end, and whether to treat warnings as errors.
Apache-2.0 terms, a moving version number and a fast release line
The README carries an SPDX header naming The P4 Language Consortium as the copyright holder under Apache-2.0, the pyproject.toml declares license = "Apache", and a LICENSES/ directory sits in the tree, so the code is published under a permissive licence with an explicit patent grant. The licence covers the compiler and its sample back ends. It says nothing about the P4 specifications themselves, which are published separately on p4.org, and the specifications are what a program you compile must conform to.
The cost of staying current shows up in the version numbers. The recent releases are v1.2.5.18 on 2026-10-02, v1.2.5.17 on 2026-09-02 and v1.2.5.16 on 2026-08-02, and the last push to the repository was on 2026-10-20. A monthly tag cadence on a compiler that is the reference implementation for a language several vendors are shipping means your build is on a schedule whether you asked for one or not.
The Python metadata drifts in a related way. The pyproject.toml records version = "1.2.5.1" while the repository tags run ahead of it, and the dependency list pins exact versions for most packages, including two protobuf versions chosen by interpreter version and two black versions split the same way. The project also publishes AGENTS.md, CONTRIBUTING.md, CHANGELOG.md, SECURITY.md, a CPPLINT.cfg and .clang-format, .clang-tidy and .clangd files, so contribution and formatting rules are written down rather than left to the reviewer.
Where p4c is the wrong tool
The clearest boundary is the back end list. p4c-ebpf, p4c-ubpf, p4c-bm2-ss and p4c-dpdk cover software targets, and p4c-graphs and p4test cover analysis. If your traffic is going onto proprietary silicon, the open source side of the story is that the front end and mid end are reusable and the back end is yours to write. That is a real option, but it is a project, and the compiler will not hand you a target for hardware that has no open source back end.
The second limit is operational. Building from source means a C++ toolchain plus CMake or Bazel plus the Python side with its pinned protobuf, thrift and grpcio versions, and the pyproject.toml comments already flag one dependency that behaves differently under Ubuntu 22 and 24. The packaged p4lang-p4c path avoids that entirely, at the price of taking whatever the packaged build has.
The third limit is a mismatch in purpose rather than capability. A full compiler with a back end per target is the wrong instrument when the task is to inspect one file for syntax errors, and p4test exists precisely for that, without the target and architecture pair. Conversely, if you need unattended compilation on a build server, the Docker route with the Dockerfile at the top of the tree is the documented shape to reach for, rather than assuming the interactive build works unattended.
A fair comparison point: a vendor toolchain built for one device is faster to start with, since its back end is already written. p4c trades that convenience for a shared front end you can read, extend and reuse, and for a back end interface that the project set out to keep open.
Editorial conclusion
p4c fits programmers who write P4 for a software switch, want the compiler's own backends to read alongside their own code, or need to test a program before it reaches hardware. It is the wrong choice for someone who only needs to parse a P4 file or wants a target that ships no open source back end. Check first that the p4lang package repository still carries a build for your distribution, that the P4Runtime version in pyproject.toml matches the control plane you run, and that your target is one of the pairs listed by p4c --target-help.
Frequently asked questions
How do I install p4c on Ubuntu?
A package is available for Ubuntu 20.04 and newer from the p4lang repositories, and the README gives the exact sequence for adding the repository, importing its Release.key with gpg --dearmor and running sudo apt install p4lang-p4c. Debian 11 (Bullseye) has a parallel procedure.
What files does p4c produce when compiling for bmv2?
On success it creates files with the same base name as the source, replacing the .p4 suffix: a .p4i file holding the preprocessor output, and a .json file in the format expected by the BMv2 simple_switch. Adding --p4runtime-files also produces a .txt P4Info file describing tables and other objects with an auto-generated control plane API.
Can p4c translate P4-14 source into P4-16?
Yes. p4test is a source-to-source translator, and the README shows the call p4test --std p4-14 my-p4-14-prog.p4 --pp auto-translated-p4-16-prog.p4, which writes the translated program to the file named by --pp.
Which targets and architectures does p4c support?
The bundled sample back ends target the BMv2 simple_switch, the DPDK SWX pipeline, eBPF in the Linux kernel across the ebpf, XDP and PSA models, and user-space eBPF, plus the p4test translator, p4c-graphs and p4tools. The command p4c --target-help lists the supported target and arch pairs.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/p4lang-p4c)Was this useful?