~charles/headalyzer

Header file generator.
faff with test_install some more
add workaround installation instructions
use GO111MODULE again

refs

master
browse  log 

clone

read-only
https://git.sr.ht/~charles/headalyzer
read/write
git@git.sr.ht:~charles/headalyzer

You can also use your local clone with git send-email.

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 */

How to Install

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.

Once Upstream is Fixed

  • go get git.sr.ht/~charles/headalyzer/...

  • go install git.sr.ht/~charles/headalyzer/...

Motivation

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.

How It Works

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).

Starlark Extensions

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().

Supported Languages

  • C
  • Shell
  • SystemVerilog

To find out what languages your copy of hdr supports, run hdr list.

Supported Types

  • int
  • string

Contributing

If you have a question, comment, or patch, you should send it in via my public inbox.

Adding A New Language

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.

Adding A New Type

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.

Status

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.

FAQ

Isn't This overkill?

Probably. But I'm tired of echo-ing things into header files from Makefiles.

Why Starlark?

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).

Can I Use Headalyzer As a Library?

Yes, that is specifically supported. However, as noted in the Status, Headalyzer probably should not be considered Stable.

License

BSD 3-Clause. See ./LICENSE.