Universal Ctags: a maintained ctags fork and what its 6.2.x line changes for tag files
A maintained ctags implementation
At a glance
- What is it?
- Universal Ctags continues Exuberant Ctags development under a new project, with a rewritten C/C++ parser, a stricter optlib, and a config layout that breaks old .ctags files. This article covers the mechanism, the build, the incompatibilities, and when you should stay on Exuberant Ctags.
- Who is it for?
- Adopt Universal Ctags if you need a ctags binary that still receives parser work, particularly for C and C++, and you can afford to move your configuration into .ctags.d directories. Stay on Exuberant Ctags if your tooling depends on reading ~/.ctags directly and you cannot change that file path.
- Can I use it commercially?
- Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What problem Universal Ctags solves, and who it is for
ctags generates an index, or tag file, of language objects found in source files. Editors and other tools read that index to jump to a definition. The original ctags and its better-known descendant, Exuberant Ctags, did this for years, but the README states that activity on Exuberant Ctags stalled. Universal Ctags exists to continue that development in a shared space rather than as a single maintainer's fork. The intended audience is anyone who relies on tag jumping: editor users who want go-to-definition without a language server, and tool authors who parse tag files as a stable, plain-text interface. The README lists the project's stated major features as more and improved language support, a new extended C/C++ parser, a fully extended optlib, and an experimental interactive mode. The relevant question for a new adopter is not whether tag files are useful, since that format is long established, but whether this fork's parser coverage and configuration model fit the languages and editors already in use.
How the index is produced and what optlib adds
The mechanism is a scan of source files that emits records for language objects, written to a tag file that editors query by name. The README describes optlib as the ability for a user to define new languages searched by regular expressions, and calls Universal Ctags' version fully extended. That matters when a language has no built-in parser: instead of waiting for a contribution, you describe the language with regex-based rules and the same indexing pipeline handles it. The README also notes that Exuberant Ctags introduced multi-language support and emacs-style TAGS generation, and that Universal Ctags continues both. One architectural consequence is worth stating plainly: because optlib definitions are part of the configuration surface, they are also the part most affected by the stricter kind rules described below. The README points to ctags-incompatibilities(7) and the news page for the full list of differences, so the authoritative account of parser-level changes lives in the man pages, not in the README.
Building from source with Autotools
The README gives an Autotools sequence for GNU/Linux, OSX, and Windows 10 WSL. Clone the repository, run ./autogen.sh, then ./configure, make, and make install. The configure step accepts --prefix to override the installation directory; without it the default is /usr/local, and the README notes that make install may require extra privileges depending on where you install. GNU make is assumed as the make command. Platform-specific notes are not in the README itself: docs/windows.rst and docs/osx.rst cover Windows and OSX respectively, and docs/autotools.rst has more on the Autotools path. If you would rather not build, the README lists prebuilt options. Windows daily builds are published as zip packages in the ctags-win32 project. Unix-like nightly builds are published as tarball archives in the ctags-nightly-build project. On Mac, recent builds are available through the universal-ctags Homebrew formula. For Snap, the README directs you to clone the ctags-snap repository and follow its instructions; Snapcraft fetches the source from GitHub automatically. Note the distinction between stable releases and these channels: the listed releases are v6.2.1 from October 2025, v6.2.0 from June 2025, and v6.1.0 from December 2023, while the nightly and daily channels track master and will not match a tagged release.
The .ctags.d change and the stricter kind letter rules
The README calls out two incompatible changes, and both are configuration-level, which means they surface the moment you migrate. First, Universal Ctags does not load ~/.ctags or ./.ctags at startup. It loads ~/.ctags.d/*.ctags and ./.ctags.d/*.ctags instead. A user with a working Exuberant Ctags setup who installs u-ctags and changes nothing will find that configuration silently unused, because the old path is no longer read. Second, kind letters and kind names are validated more strictly. The letter must be an alphabetical character in the range [a-zA-EG-Z]; F is reserved for the file kind. The first character of a kind name must be alphabetic and the remainder alphanumeric, matching [a-zA-Z][a-zA-Z0-9]*. The README explains that these rules affect definitions written with --regex-<LANG>= options, and suggests using --kinddef-<LANG>= when updating them. The README also links to a pull request, #1737, for the background. The practical reading: any regex-based language definition carried over from Exuberant Ctags needs review before it will load. This is a deliberate trade-off. Stricter rules make kind letters predictable across languages, but they turn previously tolerated definitions into startup failures rather than warnings.
Where Universal Ctags is the wrong tool
A tag file is an index of names and locations. It is not a type-aware resolver, and the README makes no claim that it is. If you need cross-file type inference, overload resolution, or refactoring support, a language server is the appropriate layer; ctags answers where a name is defined, not which definition a particular call binds to. The second limitation follows from the configuration change above. If your editor or script reads ~/.ctags directly, or if your build embeds a .ctags file at that path, u-ctags will not pick it up, and the failure is quiet from the tool's perspective because the file simply is not loaded at startup. Third, the interactive mode is described in the README as experimental, so it should not be treated as a stable interface. Finally, the README's note about CVE-2022-4515 is worth reading carefully: it states that Universal Ctags is not affected and that the issue was fixed in a 2016 commit. That is a statement about this codebase, not a general security guarantee for tag generation, and tag files themselves remain plain text that any tool you point at them will parse.
Exuberant Ctags versus Universal Ctags: the actual difference
The two projects share an ancestor, so the difference is not in the output format. Exuberant Ctags, maintained by Darren Hiebert, is where multi-language support, user-defined regex languages, and emacs-style TAGS generation were introduced, according to the README. Universal Ctags continues that line after activity on the original stalled. In practice the split shows up in three places. Parser work continues here, with the README citing a new extended C/C++ language parser. The optlib feature is described as fully extended, which is the mechanism you use to add languages without touching C. And configuration behavior diverges: Exuberant Ctags reads ~/.ctags and ./.ctags, while Universal Ctags reads the .ctags.d directories and validates kind letters and names strictly. So the migration cost is not in the tag file your editor consumes; it is in the definitions and config files you maintain around the binary. If your setup is a single invocation with no custom regex languages and no ~/.ctags file, the move is close to transparent. If it is the opposite, budget time for the kind letter review.
Maintenance, releases and licence
The repository is not archived, and the last push in the supplied metadata is dated 2026-09-08. Releases are infrequent rather than rapid: v6.1.0 in December 2023, v6.2.0 in June 2025, v6.2.1 in October 2025. That cadence is a reasonable fit for a tool whose output format is stable and whose users pin a binary rather than track master, but it does mean that a parser fix you need may sit on master for a while before it appears in a tagged release. The nightly and daily build channels exist precisely for that gap. The licence is GPL-2.0. If you distribute a modified u-ctags binary, or link its code into a larger program, the GPL-2.0 terms apply to that distribution; running the unmodified binary to generate tag files does not raise the same question. This is a description of the licence identifier, not legal advice, and anyone embedding ctags code in a product should read the licence text and the project's own statements rather than rely on a summary. The README also states that pull requests are welcome, which is the project's stated route for parser contributions.
Editorial conclusion
Adopt Universal Ctags if you need a ctags binary that still receives parser work, particularly for C and C++, and you can afford to move your configuration into .ctags.d directories. Stay on Exuberant Ctags if your tooling depends on reading ~/.ctags directly and you cannot change that file path. Before switching, run your existing --regex-<LANG> definitions through the new kind letter rules and confirm that every kind letter is in [a-zA-EG-Z] with an alphabetic first character in the name, because u-ctags rejects the rest at startup rather than warning.
Community notes