Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/c-cpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:

env:
SPECS_BRANCH: ${{ github.event.pull_request.base.ref || github.ref_name }}
SPECS_BUILD_SOURCE: github

jobs:
build-linux:
Expand Down Expand Up @@ -39,7 +40,7 @@ jobs:
run: make all

- name: Test specs executable
run: specs/exe/specs "@version" WRITE "@platform"
run: specs/exe/specs "Version:" 1 "@version" WRITE "Platform:" 1 "@platform" WRITE "Build info:" 1 "@build-info"

- name: make check
working-directory: specs/src
Expand Down Expand Up @@ -67,7 +68,7 @@ jobs:
run: make all

- name: Test specs executable
run: specs/exe/specs "@version" WRITE "@platform"
run: specs/exe/specs "Version:" 1 "@version" WRITE "Platform:" 1 "@platform" WRITE "Build info:" 1 "@build-info"

- name: make check
working-directory: specs/src
Expand All @@ -91,7 +92,7 @@ jobs:
run: msbuild specs/specs.sln /p:Configuration=Release /p:Platform=x64

- name: Test specs executable
run: specs/bin/Release/specs.exe "@version" WRITE "@platform"
run: specs/bin/Release/specs.exe "Version:" 1 "@version" WRITE "Platform:" 1 "@platform" WRITE "Build info:" 1 "@build-info"

build-windows-python:
runs-on: windows-latest
Expand All @@ -113,4 +114,4 @@ jobs:
run: msbuild specs/specs.sln /p:Configuration=Release /p:Platform=x64 /p:EnablePython=true

- name: Test specs executable
run: specs/bin/Release/specs.exe "@version" WRITE "@platform"
run: specs/bin/Release/specs.exe "Version:" 1 "@version" WRITE "Platform:" 1 "@platform" WRITE "Build info:" 1 "@build-info"
33 changes: 28 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,27 @@ on:
env:
SPECS_VERSION: ${{ github.event.release.tag_name }}
SPECS_BRANCH: ${{ github.event.release.target_commitish }}
SPECS_BUILD_SOURCE: github
SPECS_BUILD_NUMBER: ${{ github.run_number }}

jobs:
build-linux:
runs-on: ubuntu-latest
container:
image: ubuntu:22.04
steps:
- name: Install git (so checkout creates a real repository in the container)
run: |
DEBIAN_FRONTEND=noninteractive apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y git

- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Mark workspace as safe for git
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Dump event release payload
env:
EVENT: ${{ toJSON(github.event.release) }}
Expand All @@ -46,7 +56,7 @@ jobs:
run: make some

- name: Verify binary
run: specs/exe/specs "@version" WRITE "@platform"
run: specs/exe/specs "Version:" 1 "@version" WRITE "Platform:" 1 "@platform" WRITE "Build info:" 1 "@build-info"

- name: Prepare manpage
run: |
Expand Down Expand Up @@ -121,7 +131,7 @@ jobs:
run: make some

- name: Verify binary
run: specs/exe/specs "@version" WRITE "@platform"
run: specs/exe/specs "Version:" 1 "@version" WRITE "Platform:" 1 "@platform" WRITE "Build info:" 1 "@build-info"

- name: Prepare manpage
run: |
Expand Down Expand Up @@ -205,7 +215,7 @@ jobs:
run: msbuild specs/specs.sln /p:Configuration=Release /p:Platform=x64 /p:GitTag=${{ steps.version.outputs.display }}

- name: Verify binary
run: specs\bin\Release\specs.exe "@version" WRITE "@platform"
run: specs\bin\Release\specs.exe "Version:" 1 "@version" WRITE "Platform:" 1 "@platform" WRITE "Build info:" 1 "@build-info"

- name: Prepare standalone executable
shell: bash
Expand Down Expand Up @@ -291,7 +301,7 @@ jobs:
run: msbuild specs/specs.sln /p:Configuration=Release /p:Platform=x64 /p:GitTag=${{ steps.version.outputs.display }} /p:EnablePython=true

- name: Verify binary
run: specs\bin\Release\specs.exe "@version" WRITE "@platform"
run: specs\bin\Release\specs.exe "Version:" 1 "@version" WRITE "Platform:" 1 "@platform" WRITE "Build info:" 1 "@build-info"

- name: Prepare standalone executable
shell: bash
Expand Down Expand Up @@ -337,10 +347,23 @@ jobs:
runs-on: ${{ matrix.runner }}
container: ${{ matrix.container || '' }}
steps:
- name: Install git (so checkout creates a real repository in the container)
run: |
if [ "$(id -u)" -eq 0 ]; then
APT="apt-get"
else
APT="sudo apt-get"
fi
DEBIAN_FRONTEND=noninteractive $APT update
DEBIAN_FRONTEND=noninteractive $APT install -y git

- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Mark workspace as safe for git
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Install build tools and Python 3.12
run: |
if [ "$(id -u)" -eq 0 ]; then
Expand All @@ -366,7 +389,7 @@ jobs:
run: make some

- name: Verify binary
run: specs/exe/specs "@version" WRITE "@platform"
run: specs/exe/specs "Version:" 1 "@version" WRITE "Platform:" 1 "@platform" WRITE "Build info:" 1 "@build-info"

- name: Prepare manpage
run: |
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ specs/src/Release/
# GDB-related
specs/src/.gdbinit
specs/src/gdb/__pycache__

# Generated build info
specs/src/utils/build_info.h
35 changes: 33 additions & 2 deletions manpage
Original file line number Diff line number Diff line change
Expand Up @@ -2272,11 +2272,11 @@ The system also defines some labels:
.IP "version" 3
This returns the version of
.B specs,
for example "v0.6"
for example "1.0.0"
.P
To find out the version of specs that you are using, use the following command:
.P
specs @version 1
specs @version
.IP "cols" 3
contains the number of columns in the display. You can override this in the configuration file. For example, the following prints a right-justified string.
.P
Expand All @@ -2293,6 +2293,37 @@ depending on whether support for python function is available.
contains a string describing this build. For example:
.p
POSIX (darwin) system using the g++ compiler and Python 3.9.6 - release variation
.IP "build-commit" 3
contains the git commit hash (short form) of the build. For example:
.p
b74fcb1
.IP "build-branch" 3
contains the git branch name of the build. May be empty if not available. For example:
.p
dev-1.0.0
.IP "build-time" 3
contains the UTC timestamp of when the build was created. Format is
.B yyyy-MM-ddTHH:mm:ss.
For example:
.p
2026-06-08T08:22:52
.IP "build-source" 3
contains either
.B local
or
.B github
depending on where the build was created.
.IP "build-number" 3
contains the build number from GitHub Actions (github.run_number). Empty for local builds.
.IP "build-info" 3
contains a composite string with all build information. For example:
.p
Built locally from commit 8bd11da on branch dev-1.0.0 at 2026-06-08T13:01:18
.p
or
.p
Built on github (build 217) from commit 8bd11da of version 1.0.0 at 2026-06-08T12:40:01
.p

.SH EXAMPLES
`ls -l` yields this:
Expand Down
12 changes: 12 additions & 0 deletions specs/docs/alu.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ POSIX (darwin) system using the g++ compiler and Python 3.9.6 - release variatio
```
Others are `@cols`, which contains the number of columns in the terminal screen, and `@rows`, which contains the number of rows on that same screen.

Build information is also available via the following labels:
- `@build-commit` — the git commit hash (short form) of the build
- `@build-branch` — the git branch name (may be empty)
- `@build-time` — the UTC timestamp when the build was created (format: `yyyy-MM-ddTHH:mm:ss`)
- `@build-source` — either `local` or `github`
- `@build-number` — the GitHub Actions build number (empty for local builds)
- `@build-info` — a composite string with all build information, e.g.:
```
Built locally from commit 8bd11da on branch dev-1.0.0 at 2026-06-08T13:01:18
Built on github (build 217) from commit 8bd11da of version 1.0.0 at 2026-06-08T12:40:01
```

Additionally, the `@@` string stands for the entire input record. When rolling context is in effect (see [Streams and Records](streams.md#rolling-context)), `@@` always refers to the original input record. The `@!` string refers to the current record as affected by `CONTEXT`, which is the same as `@@` when no `CONTEXT` is active. The `@-n` and `@+n` syntax is an alternative to using that is effective within expressions. Note that reading beyond the input with `@+n` or `@-n` does not cause processing to stop, even if a `READSTOP` token is present in the specification. The following three specifications are equivalent:

```
Expand Down
6 changes: 6 additions & 0 deletions specs/docs/onepage.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ There are some pre-configured labels that do not need to be explicitly defined:
* platform - contains a string with the OS type, the compiler and the variation used to build *specs*
* cols - contains the number of screen columns - useful for composed output placement.
* rows - contains the number of screen rows.
* build-commit - contains the git commit hash (short form) of the build
* build-branch - contains the git branch name (may be empty)
* build-time - contains the UTC timestamp when the build was created (format: `yyyy-MM-ddTHH:mm:ss`)
* build-source - contains either `local` or `github`
* build-number - contains the GitHub Actions build number (empty for local builds)
* build-info - contains a composite string with all build information

Examples
========
Expand Down
1 change: 1 addition & 0 deletions specs/src/ALUUnitTest.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="build_info.targets" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
Expand Down
1 change: 1 addition & 0 deletions specs/src/ProcessingTest.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="build_info.targets" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
Expand Down
1 change: 1 addition & 0 deletions specs/src/TokenTest.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="build_info.targets" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
Expand Down
47 changes: 47 additions & 0 deletions specs/src/build_info.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Generate build_info.h before compilation -->
<Target Name="GenerateBuildInfo" BeforeTargets="ClCompile">
<!-- Set build time to current UTC time -->
<PropertyGroup>
<BuildTime>$([System.DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ss"))</BuildTime>
<BuildSource Condition="'$(SPECS_BUILD_SOURCE)' == ''">local</BuildSource>
<BuildSource Condition="'$(SPECS_BUILD_SOURCE)' != ''">$(SPECS_BUILD_SOURCE)</BuildSource>
<BuildNumber>$(SPECS_BUILD_NUMBER)</BuildNumber>
</PropertyGroup>

<!-- Get git commit hash -->
<Exec Command="git rev-parse --short HEAD" ConsoleToMSBuild="true" ContinueOnError="true">
<Output TaskParameter="ConsoleOutput" PropertyName="BuildCommit" />
</Exec>

<!-- Get git branch name -->
<Exec Command="git branch --show-current" ConsoleToMSBuild="true" ContinueOnError="true">
<Output TaskParameter="ConsoleOutput" PropertyName="BuildBranch" />
</Exec>

<!-- Trim whitespace from outputs -->
<PropertyGroup>
<BuildCommit>$(BuildCommit.Trim())</BuildCommit>
<BuildBranch>$(BuildBranch.Trim())</BuildBranch>
</PropertyGroup>

<!-- fall back to getting BuildBranch from env variable -->
<PropertyGroup>
<BuildBranch Condition="'$(BuildBranch)' == ''">$(SPECS_BRANCH)</BuildBranch>
</PropertyGroup>

<!-- Generate build_info.h -->
<ItemGroup>
<BuildInfoLines Include="#define SPECS_BUILD_COMMIT &quot;$(BuildCommit)&quot;" />
<BuildInfoLines Include="#define SPECS_BUILD_BRANCH &quot;$(BuildBranch)&quot;" />
<BuildInfoLines Include="#define SPECS_BUILD_TIME &quot;$(BuildTime)&quot;" />
<BuildInfoLines Include="#define SPECS_BUILD_SOURCE &quot;$(BuildSource)&quot;" />
<BuildInfoLines Include="#define SPECS_BUILD_NUMBER &quot;$(BuildNumber)&quot;" />
</ItemGroup>

<WriteLinesToFile File="utils\build_info.h" Lines="@(BuildInfoLines)" Overwrite="true" />

<Message Text="Generated utils\build_info.h" Importance="high" />
</Target>
</Project>
1 change: 1 addition & 0 deletions specs/src/cacheTest.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="build_info.targets" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
Expand Down
2 changes: 1 addition & 1 deletion specs/src/cli/tokens.cc
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ void parseSingleToken(std::vector<Token> *pVec, std::string arg, int argidx)

/* Check for a configuration literal */
std::string key = arg.substr(1);
if ((arg[0]=='@') && (arg.length() > 1) && (configSpecLiteralExists(key))) {
if ((arg[0]=='@') && (arg.length() > 1) && (configSpecLiteralDefined(key))) {
std::string literal = configSpecLiteralGet(key);
pVec->insert(pVec->end(),
Token(TokenListType__LITERAL, nullptr /* range */,
Expand Down
76 changes: 76 additions & 0 deletions specs/src/generate_build_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""Generate build_info.h with current build information."""

import datetime
import os
import subprocess
import sys


def report_success(name, value):
print('Setting {} to "{}"'.format(name, value))


def report_failure(name, exc):
sys.stderr.write("Failed to determine {}: {}\n".format(name, repr(exc)))
# If the failure came from a subprocess, surface the command's stderr too,
# since that usually explains *why* (e.g. git's "dubious ownership" error).
output = getattr(exc, "output", None)
if output:
if isinstance(output, bytes):
output = output.decode(errors="replace")
sys.stderr.write(" stdout: {}\n".format(output.strip()))
stderr = getattr(exc, "stderr", None)
if stderr:
if isinstance(stderr, bytes):
stderr = stderr.decode(errors="replace")
sys.stderr.write(" stderr: {}\n".format(stderr.strip()))


def run_git(name, args):
"""Run a git command, capturing stderr so failures can be reported."""
try:
value = subprocess.check_output(
['git'] + args,
stderr=subprocess.PIPE
).decode().strip()
if value:
report_success(name, value)
return value
except Exception as exc:
report_failure(name, exc)
return ""


# Get commit hash
build_commit = run_git("SPECS_BUILD_COMMIT", ['rev-parse', '--short', 'HEAD'])

# Get branch name
build_branch = run_git("SPECS_BUILD_BRANCH", ['branch', '--show-current'])

# Fall back to the SPECS_BRANCH environment variable (set by release.yml)
# since `git branch --show-current` is empty on a detached HEAD / tag checkout.
if build_branch == "":
build_branch = os.environ.get("SPECS_BRANCH", "")
if build_branch:
report_success("SPECS_BUILD_BRANCH (from SPECS_BRANCH env)", build_branch)

# Get UTC build time
build_time = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%S")
report_success("SPECS_BUILD_TIME", build_time)

# Get build source and number from environment
build_source = os.environ.get("SPECS_BUILD_SOURCE", "local")
report_success("SPECS_BUILD_SOURCE", build_source)
build_number = os.environ.get("SPECS_BUILD_NUMBER", "")
report_success("SPECS_BUILD_NUMBER", build_number)

# Write the header file
with open("utils/build_info.h", "w") as f:
f.write('#define SPECS_BUILD_COMMIT "{}"\n'.format(build_commit))
f.write('#define SPECS_BUILD_BRANCH "{}"\n'.format(build_branch))
f.write('#define SPECS_BUILD_TIME "{}"\n'.format(build_time))
f.write('#define SPECS_BUILD_SOURCE "{}"\n'.format(build_source))
f.write('#define SPECS_BUILD_NUMBER "{}"\n'.format(build_number))

print("Generated utils/build_info.h")
Loading
Loading