fix spurios test failure
add support for Make output
faff with test_install some more
Headalyzer is a tool for generating header files (or equivalent) in several languages. This is useful when you have shared constants that you wish to use across several languages. This is especially handy when you need to share information in projects that use FFI.
In a nutshell:
$ cat sample.star
# "symbol components" like "version", "major" will be "cased" appropriately
# according to the target language's conventions
declare(["version", "major"], "int", 2)
declare(["version", "minor"], "int", 7)
declare(["version", "patch"], "int", 1)
# If a symbol only has one component, the brackets can be omitted
declare("build", "string", "git")
# Target-language-specific "hints" can be defined, say width in bits for
# SystemVerilog
declare("bufsize", "int", 128)
hint("bufsize", "systemverilog", "width", 10)
# Generators can be configured too, the C generator uses CAPITAL_SNAKE_CASE
# by default, but we can ask it to use UpperCamelCase instead...
option("c", "case", "UpperCamelCase")
$ hdr describe sample.star
Description of file 'sample.star'
Options:
{c case UpperCamelCase}
Constants:
<Constant type=int val='2'>
<Constant type=int val='7'>
<Constant type=int val='1'>
<Constant type=string val='git'>
<Constant type=int val='128'>
Hints:
{{[bufsize]} {systemverilog width 10}}
$ hdr generate -l c sample.star
#ifndef HEADALYZER_GENERATED_H
#define HEADALYZER_GENERATED_H
#define VersionMajor 2
#define VersionMinor 7
#define VersionPatch 1
#define Build "git"
#define Bufsize 128
#endif /* HEADALYZER_GENERATED_H */
$ hdr generate -l shell sample.star
VERSION_MAJOR='2'
VERSION_MINOR='7'
VERSION_PATCH='1'
BUILD='git'
BUFSIZE='128'
$ hdr generate -l systemverilog sample.star
`ifndef HEADALYZER_GENERATED_H
`define HEADALYZER_GENERATED_H
`define VERSION_MAJOR 2
`define VERSION_MINOR 7
`define VERSION_PATCH 1
`define BUILD "git"
`define BUFSIZE 10'd128
`endif /* HEADALYZER_GENERATED_H */
Due to this issue with Starlib, it's not currently possible to install this program in the usual way. Instead:
git clone https://git.sr.ht/~charles/headalyzer
cd headalyzer
make install
This will no longer be needed once the upstream issue is resolved.
go get git.sr.ht/~charles/headalyzer/...
go install git.sr.ht/~charles/headalyzer/...
My motivation behind writing Headalyzer was needing to share certain constants between some C++ and SystemVerilog code in a Verilator project. I've run into these types of troubles in the past, so I decided to finally solve this problem in the general case.
Headalyzer is built on top of
google/starlark-go. It implements three special functions,
declare(symbol, type, value)
, option(section, key, value)
, and
hint(symbol, section, key, value)
which is uses to build an in-memory list of
constants, options, and hints.
A constant is what Headalzer is outputting when you run hdr generate
, it
associates a symbol with a type and a value. The type is used to influence
how the output is generated.
A symbol is a collection of string components. A symbol is how a
particular constant is referred to. This approach was chosen because it enabled
Headalyzer to automatically convert symbols into the appropriate casing style
for the target language. For example, when targeting Go you might want to case
like VersionMajor
, but for C you might prefer VERSION_MAJOR
.
A generator is a library under Headalyzer that contains methods to generate output targeting a specific language.
An option associates a section, key, and value. By convention, the section will be the name of a generator (e.g. "c" or "shell"). A key is a configuration value associated with that generator. The value is stored as a string, but the generator may internally case it to a different type, and may throw an appropriate error if this fails. Generators will almost always have default values and not require you to explicitly specify any options.
A hint associates an option with a symbol, it can be used to express metadata about a single specific constant, rather than configuring a generator at large. An example of where this is useful is that in SystemVerilog, integer constants can optionally have an explicit bit width associated with them, which wouldn't be meaningful in say, C.
When you run hdr generate
, the input file is run to build an in-memory list
of these constants, options, and hints, then the appropriate Generate
function is called (based on the -l
flag).
To make it easier to compute constants, starlib is included, and starlib modules can be loaded, which may be useful for calculating floating point constants with starlib's math library.
Further considering the intended use of Headalyzer and the lack of logarithm
functions in Starlib's math library, wrappers around Golang's math.Log()
,
math.Log2()
, and math.Log10()
as the Starlark functions log()
, log2()
,
and log10()
.
To find out what languages your copy of hdr
supports, run hdr list
.
int
string
If you have a question, comment, or patch, you should send it in via my public inbox.
To support a new language, you should create a new package in
./pkg/headalyzer
named after your desired output
language. The systemverilog
generator is a good example, as
it uses options and hints.
Once you have added the appropraite package, update
./cmd/hdr/main.go
to list the newly added language, and
to support using it with -l
.
Presently, types are defined in
./pkg/headalyzer/headalyzer.go
. There is a
big list of constant type names where you may wish to add your type, if it
isn't there already. You need to implement the functions of the Constant
interface for your new constant, then add it to the CreateConstant()
function. You should also add it to all of the generators as well.
Keep in mind that Constant.String()
is intended to return a human-readable
string, while Constant.Text()
is intended to return the way it should be
represented by the generator during output. The generator will apply any
requisite quoting, semicolons, etc. so do not insert these in your Text()
function.
Headalyzer is a very new project. The API and command syntax should not be considered stable at this time, and breaking changes are possible. Once I have used Headalyzer in a real project or two and am convinced it works the way it aught to, I will cut a 1.0.0 release and switch to semantic versioning.
Probably. But I'm tired of echo-ing things into header files from Makefiles.
I wanted to use an existing file format, not write a new one from scratch. Starlark provides a lot of power, and is easy to bind functions into. I also like the Python-like syntax more than Lua or JS (which are the other languages I'm aware of that have Go-embeddable variants).
Yes, that is specifically supported. However, as noted in the Status, Headalyzer probably should not be considered Stable.
BSD 3-Clause. See ./LICENSE
.