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
68 changes: 68 additions & 0 deletions .github/workflows/build-guidelines.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Build Safety-Critical Rust Coding Guidelines

on:
# Add on:
push:
tags:
- "*.*.*"
branches:
- "main"
pull_request:
branches:
- "main"
# workflow_call trigger to make this workflow reusable
workflow_call:
# You can add inputs here if needed in the future
# inputs:
# example_input:
# required: false
# type: string
# default: 'default value'

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="/root/.cargo/bin:$PATH"
uv --version

- name: Build documentation
run: |
mkdir -p build
./make.py 2>&1 | tee build/build.log

# Check for a wide range of error indicators in the log
if grep -q -E "Traceback" build/build.log; then
echo "::error::Build errors detected in log"

# Extract error contexts and annotate them in the GitHub Actions UI
echo "=== ERROR DETAILS ==="

# Check for the Sphinx temp error file reference and extract it if present
TEMP_ERROR_FILE=$(grep -o '/tmp/sphinx-err-[^ ]*\.log' build/build.log | head -1)
if [ ! -z "$TEMP_ERROR_FILE" ] && [ -f "$TEMP_ERROR_FILE" ]; then
echo "==== SPHINX DETAILED TRACEBACK START ===="
cat "$TEMP_ERROR_FILE"
echo "==== SPHINX DETAILED TRACEBACK END ===="

# Save this traceback for artifacts
cp "$TEMP_ERROR_FILE" build/sphinx_traceback.log
fi

exit 1
fi

- name: Archive build logs
uses: actions/upload-artifact@v4
if: always()
with:
name: build-logs
path: build
retention-days: 7
compression-level: 6 # Default compression level for a good balance of speed and size
29 changes: 29 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Nightly Safety-Critical Rust Coding Guidelines Build Schedule

on:
schedule:
# Run at 02:00 UTC every day
- cron: '0 2 * * *'
# Optional: Allow manual triggering for testing
workflow_dispatch:
# Temporary - remove before merging

jobs:
trigger-build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Log build start
run: echo "Starting nightly build at $(date)"

# This is a "setup" job that can add pre-build steps if needed in the future

run-build:
needs: trigger-build
# Call the main build workflow
uses: ./.github/workflows/build-guidelines.yml
# If your main workflow is in a different file, adjust the path accordingly


19 changes: 2 additions & 17 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,17 +1,2 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
__pycache__/
build/
File renamed without changes.
File renamed without changes.
File renamed without changes.
57 changes: 53 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,59 @@
# safety-critical-rust-coding-guidelines
# Safety-Critical Rust Coding Guidelines

Coding Guidelines for Safety Critical Rust developed by the [Safety Critical Rust Consortium][safety-critical-rust-consortium].

_Note_: Early, subject to changes.

## Building the coding guidelines

The Safety-Critical Rust Coding Guidelines use `Sphinx` and `Sphinx-Needs` to build a rendered version of the coding guidelines, and `uv` to install and manage Python dependencies (including Sphinx itself). To simplify building the rendered version, we created a script called `make.py` that takes care of invoking Sphinx with the right flags.

You can build the rendered version by running:

```shell
./make.py
```

By default, Sphinx uses incremental rebuilds to generate the content that
changed since the last invocation. If you notice a problem with incremental
rebuilds, you can pass the `-c` flag to clear the existing artifacts before
building:

```shell
./make.py -c
```

The rendered version will be available in `build/html/`.

A machine-parseable artifact will be available at `build/html/needs.json`. (ToDo: Pete LeVasseur) The `needs.json` file could use some cleaning up and some description here of the contents.

A record with checksums of the contents is available at `build/html/guidelines-ids.json`. Users of the coding guidelines can reference this file to determine if there have been changes to coding guidelines contents they should be aware of.

## Contributing to the coding guidelines

We have the same chapter layout as the [Ferrocene Language Specification](https://spec.ferrocene.dev/) (FLS). If you would like to contribute you may find a section from the FLS of interest and then write a guideline in the corresponding chapter of these coding guidelines.

### Guideline template

We have a script `./generate-guideline-templates.py` which which assumes you're using `uv` that can be run to generate the template for a guideline with properly randomized IDs.

You can the copy and paste this guideline from the command line into the correct chapter.

### Filling out the guideline

Reference `src/conf.py` to see valid selections for unfilled options in the guideline template.

Note that the `:fls:` option should be filled according to the FLS paragraph ID for which the guideline is covering. One way to go about finding this is to inspect the page using your web browser. You'll be looking for something like:

```html
<p><span class="spec-paragraph-id" id="fls_4rhjpdu4zfqj">4.1:1</span>
```

You would then pull `fls_4rhjpdu4zfqj` to place in the `:fls:` option.

Existing guidelines can also serve as examples on how guidelines are filled.


## [Code of Conduct][code-of-conduct]

The [Rust Foundation][rust-foundation] has adopted a Code of Conduct that we
Expand Down Expand Up @@ -37,6 +89,3 @@ You can read about other Rust Foundation policies in the footer of the Foundatio
[media-guide and trademark]: https://foundation.rust-lang.org/policies/logo-policy-and-media-guide/
[rust-foundation]: https://foundation.rust-lang.org/
[safety-critical-rust-consortium]: https://github.com/rustfoundation/safety-critical-rust-consortium



3 changes: 3 additions & 0 deletions builder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Builder

A minimal project which creates `build_cli.py` to allow us to build the Safety-Critical Coding Guidelines.
93 changes: 93 additions & 0 deletions builder/build_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors

# Convenience script to build Sphinx books, including setting up a Python
# virtual environment to install Sphinx into (removing the need to manage
# dependencies globally). Each book should have a `make.py` script that updates
# the submodules, import this shared module, and calls the main function here.

from pathlib import Path
import argparse
import subprocess
import sys


# Automatically watch the following extra directories when --serve is used.
EXTRA_WATCH_DIRS = ["exts", "themes"]


def build_docs(root, builder, clear, serve, debug):
dest = root / "build"

args = ["-b", builder, "-d", dest / "doctrees"]
if debug:
# Disable parallel builds and show exceptions in debug mode.
#
# We can't show exceptions in parallel mode because in parallel mode
# all exceptions will be swallowed up by Python's multiprocessing.
# That's also why we don't show exceptions outside of debug mode.
args += ["-j", "1", "-T"]
else:
# Enable parallel builds:
args += ["-j", "auto"]
if clear:
args.append("-E")
if serve:
for extra_watch_dir in EXTRA_WATCH_DIRS:
extra_watch_dir = root / extra_watch_dir
if extra_watch_dir.exists():
args += ["--watch", extra_watch_dir]
else:
# Error out at the *end* of the build if there are warnings:
args += ["-W", "--keep-going"]

try:
subprocess.run(
[
"sphinx-autobuild" if serve else "sphinx-build",
*args,
root / "src",
dest / builder,
],
check=True,
)
except KeyboardInterrupt:
exit(1)
except subprocess.CalledProcessError:
print("\nhint: if you see an exception, pass --debug to see the full traceback")
exit(1)

return dest / builder

def main(root):
root = Path(root)

parser = argparse.ArgumentParser()
parser.add_argument(
"-c", "--clear", help="disable incremental builds", action="store_true"
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
"-s",
"--serve",
help="start a local server with live reload",
action="store_true",
)
group.add_argument(
"--check-links", help="Check whether all links are valid", action="store_true"
)
group.add_argument(
"--xml", help="Generate Sphinx XML rather than HTML", action="store_true"
)
group.add_argument(
"--debug",
help="Debug mode for the extensions, showing exceptions",
action="store_true",
)
args = parser.parse_args()

rendered = build_docs(
root, "xml" if args.xml else "html", args.clear, args.serve, args.debug
)

7 changes: 7 additions & 0 deletions builder/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
name = "builder"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
39 changes: 39 additions & 0 deletions exts/coding_guidelines/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.. SPDX-License-Identifier: MIT OR Apache-2.0
SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors

==================================
Coding Guidelines Sphinx Extension
==================================

To enhance the Safety-Critical Rust Coding Guidelines, and to facilitate its
authoring, updating, and testing it easier, we developed and use a custom
Sphinx extension that adds new roles and directives. The source code of the
extension is in the ``exts/coding_guidelines`` directory.

.. contents:: In this document:

Ferrocene Language Specification Conformance
============================================

Various checks are performed against the ``:fls:`` option present in ``guideline`` directives to
ensure they are valid.

Coverage of the coding guidlines over the FLS is calculated.

Each coding guideline has its ``:fls:`` option turned into a hyperlink to the corresponding element
within the FLS to be able to navigate there directly.

Further an ``fls.lock`` file located at ``root/src/fls.lock`` is validated against the currently
deployed version of the Ferrocene Language Spec and the build is failed if there is discrepency.

Links to the Rust standard library
==================================

You can link to the documentation of items defined in the Rust standard library
(``core``, ``alloc``, ``std``, ``test`` and ``proc_macro``) by using the
``:std:`type``` role (even for types defined in other standard library crates)
with the fully qualified item path:

.. code-block:: rst

The type needs to implement :std:`core::marker::Copy`.
55 changes: 55 additions & 0 deletions exts/coding_guidelines/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors

from . import fls_checks
from . import write_guidelines_ids
from . import std_role
from . import fls_linking

from sphinx_needs.api import add_dynamic_function
from sphinx.errors import SphinxError
from sphinx.domains import Domain

import logging

# Get the Sphinx logger
logger = logging.getLogger('sphinx')


class CodingGuidelinesDomain(Domain):
name = "coding-guidelines"
label = "Rust Standard Library"
roles = {
"std": std_role.StdRefRole(),
}
directives = {}
object_types = {}
indices = {}

def get_objects(self):
return []

def merge_domaindata(self, docnames, other):
pass # No domain data to merge

def setup(app):

app.add_domain(CodingGuidelinesDomain)
app.add_config_value(
name="spec_std_docs_url",
default="https://doc.rust-lang.org/stable/std",
rebuild="env", # Rebuild the environment when this changes
types=[str],
)
app.add_config_value(name='fls_paragraph_ids_url',
default='https://spec.ferrocene.dev/paragraph-ids.json',
rebuild='env')

app.connect('env-check-consistency', fls_checks.check_fls)
app.connect('build-finished', write_guidelines_ids.build_finished)
app.connect('build-finished', fls_linking.build_finished)

return {
'version': '0.1',
'parallel_read_safe': True,
}
Loading