Skip to content

Commit b5640a0

Browse files
committed
Add support for multiversion documentation
Signed-off-by: Parth Arora <partaror@qti.qualcomm.com>
1 parent f499b10 commit b5640a0

8 files changed

Lines changed: 789 additions & 0 deletions

File tree

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
name: Bulid multi-version docs
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths: ['docs/**', 'include/**']
7+
workflow_dispatch:
8+
pull_request: {}
9+
10+
jobs:
11+
build-docs-and-deploy:
12+
runs-on: ubuntu-latest
13+
concurrency:
14+
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
15+
cancel-in-progress: true
16+
17+
steps:
18+
- name: Checkout llvm-project
19+
uses: actions/checkout@v4
20+
with:
21+
repository: llvm/llvm-project
22+
path: llvm-project
23+
ref: main
24+
25+
- name: Checkout eld
26+
uses: actions/checkout@v4
27+
with:
28+
path: llvm-project/llvm/tools/eld
29+
fetch-depth: 0
30+
31+
- name: Configure workflow environment
32+
uses: ./llvm-project/llvm/tools/eld/.github/workflows/ConfigureBuildWorkflowENV
33+
34+
- name: Set up Python
35+
uses: actions/setup-python@v4
36+
with:
37+
python-version: '3.11'
38+
39+
- name: Install dependencies
40+
run: |
41+
sudo apt-get update
42+
sudo apt-get install -y graphviz doxygen ninja-build
43+
pip install -r llvm-project/llvm/tools/eld/docs/userguide/requirements.txt
44+
45+
- name: Configure CMake
46+
run: |
47+
cmake -G Ninja -B build \
48+
-DCMAKE_BUILD_TYPE=Release \
49+
-DCMAKE_C_COMPILER=$(which clang) \
50+
-DCMAKE_CXX_COMPILER=$(which clang++) \
51+
-DLLVM_TARGETS_TO_BUILD="ARM;AArch64;RISCV;Hexagon" \
52+
-DLLVM_ENABLE_SPHINX=ON \
53+
llvm-project/llvm
54+
55+
- name: Build multi-version docs
56+
run: cmake --build build --target eld-docs-multiversion
57+
58+
- name: Checkout documentation branch
59+
uses: actions/checkout@v4
60+
with:
61+
ref: documentation
62+
path: docs-branch
63+
continue-on-error: true
64+
65+
- name: Preserve existing content (dashboard)
66+
run: |
67+
if [ -d docs-branch/dash ]; then
68+
cp -r docs-branch/dash build/tools/eld/docs/multiversion/site/
69+
echo "Preserved dashboard from documentation branch"
70+
fi
71+
72+
- name: Deploy to documentation branch
73+
if: github.repository == 'qualcomm/eld'
74+
run: |
75+
SITE_DIR=$(pwd)/build/tools/eld/docs/multiversion/site
76+
cd llvm-project/llvm/tools/eld
77+
git config user.name 'github-actions'
78+
git config user.email 'github-actions@github.com'
79+
git checkout --orphan documentation-update
80+
git rm -rf .
81+
cp -r ${SITE_DIR}/. .
82+
git add .
83+
git commit -m "Update multi-version documentation"
84+
git push -f origin HEAD:refs/heads/multiversion-documentation
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# BuildMultiVersionDocs.cmake
2+
#
3+
# Provides CMake targets for building multi-version ELD documentation.
4+
#
5+
# This module creates git worktrees for each configured version, builds
6+
# the eld-docs target for each, and assembles a combined site with a
7+
# landing page and version selector.
8+
#
9+
# Variables (user-configurable):
10+
# ELD_DOC_VERSIONS - List of "branch:output_name:label" specs
11+
# Example: "main:latest:Latest (main);release/22.x:22.x:Release 22.x"
12+
# ELD_DOC_STABLE - Which version the "stable" symlink points to (default: 22.x)
13+
#
14+
# Targets created:
15+
# eld-docs-all-releases - Builds documentation for all configured versions
16+
# eld-docs-<name> - Builds documentation for a single version
17+
# eld-docs-assemble - Generates landing page and symlinks (depends on all versions)
18+
# eld-docs-multiversion - Convenience target: builds all + assembles
19+
#
20+
# Output:
21+
# ${CMAKE_BINARY_DIR}/tools/eld/docs/multiversion/
22+
# ├── index.html
23+
# ├── versions.json
24+
# ├── stable -> <ELD_DOC_STABLE>
25+
# ├── latest/
26+
# └── 22.x/
27+
28+
function(eld_add_multiversion_doc_targets)
29+
if(NOT DEFINED ELD_SOURCE_DIR)
30+
message(FATAL_ERROR "ELD_SOURCE_DIR must be defined before calling eld_add_multiversion_doc_targets")
31+
endif()
32+
33+
find_package(Python3 REQUIRED COMPONENTS Interpreter)
34+
35+
set(ELD_DOC_VERSIONS
36+
"main:latest:Latest (main)"
37+
"release/22.x:22.x:Release 22.x"
38+
CACHE STRING "Semicolon-separated list of 'branch:output_name:label' for multi-version docs")
39+
40+
set(ELD_DOC_STABLE "22.x"
41+
CACHE STRING "Version that 'stable' symlink points to")
42+
43+
set(ELD_MULTIVERSION_SCRIPTS_DIR "${ELD_SOURCE_DIR}/docs/multiversion")
44+
set(ELD_MULTIVERSION_WORK_DIR "${CMAKE_BINARY_DIR}/tools/eld/docs/multiversion")
45+
set(ELD_MULTIVERSION_SITE_DIR "${ELD_MULTIVERSION_WORK_DIR}/site")
46+
47+
# LLVM_SOURCE_DIR points to llvm/ subdirectory; parent is llvm-project root
48+
get_filename_component(ELD_LLVM_REPO_ROOT "${LLVM_SOURCE_DIR}/.." ABSOLUTE)
49+
if(NOT DEFINED LLVM_SOURCE_DIR OR NOT EXISTS "${ELD_LLVM_REPO_ROOT}/llvm/CMakeLists.txt")
50+
message(WARNING "LLVM_SOURCE_DIR not set or invalid")
51+
message(WARNING "Multi-version docs target will not be available")
52+
return()
53+
endif()
54+
55+
set(version_targets "")
56+
set(versions_json "[")
57+
set(first_version TRUE)
58+
59+
foreach(version_spec IN LISTS ELD_DOC_VERSIONS)
60+
# Parse "branch:output_name:label"
61+
string(REPLACE ":" ";" parts "${version_spec}")
62+
list(LENGTH parts num_parts)
63+
64+
if(num_parts LESS 2)
65+
message(WARNING "Invalid version spec (need at least branch:output_name): ${version_spec}")
66+
continue()
67+
endif()
68+
69+
list(GET parts 0 branch)
70+
list(GET parts 1 output_name)
71+
if(num_parts GREATER_EQUAL 3)
72+
list(GET parts 2 label)
73+
else()
74+
set(label "${output_name}")
75+
endif()
76+
77+
# Determine version attributes for JSON
78+
set(is_stable FALSE)
79+
set(is_dev FALSE)
80+
if(output_name STREQUAL "${ELD_DOC_STABLE}")
81+
set(is_stable TRUE)
82+
endif()
83+
if(output_name STREQUAL "latest")
84+
set(is_dev TRUE)
85+
endif()
86+
87+
# Build JSON entry for this version
88+
if(NOT first_version)
89+
string(APPEND versions_json ",")
90+
endif()
91+
set(first_version FALSE)
92+
93+
string(APPEND versions_json "{\"path\":\"${output_name}\",\"label\":\"${label}\"")
94+
if(is_stable)
95+
string(APPEND versions_json ",\"stable\":true")
96+
endif()
97+
if(is_dev)
98+
string(APPEND versions_json ",\"dev\":true")
99+
endif()
100+
string(APPEND versions_json "}")
101+
102+
# Create target for this version
103+
set(target_name "eld-docs-${output_name}")
104+
105+
add_custom_target(${target_name}
106+
COMMAND ${Python3_EXECUTABLE}
107+
"${ELD_MULTIVERSION_SCRIPTS_DIR}/build_docs.py"
108+
--branch "${branch}"
109+
--output-name "${output_name}"
110+
--eld-repo "${ELD_SOURCE_DIR}"
111+
--llvm-repo "${ELD_LLVM_REPO_ROOT}"
112+
--work-dir "${ELD_MULTIVERSION_WORK_DIR}"
113+
--output-dir "${ELD_MULTIVERSION_SITE_DIR}"
114+
--version-label "${label}"
115+
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
116+
COMMENT "Building ELD docs: ${branch} -> ${output_name}"
117+
USES_TERMINAL
118+
VERBATIM
119+
)
120+
121+
list(APPEND version_targets ${target_name})
122+
endforeach()
123+
124+
string(APPEND versions_json "]")
125+
126+
# Main target that builds all versions
127+
add_custom_target(eld-docs-all-releases
128+
DEPENDS ${version_targets}
129+
COMMENT "Building all ELD documentation versions"
130+
)
131+
132+
# Assembly target that generates landing page and symlinks
133+
add_custom_target(eld-docs-assemble
134+
DEPENDS eld-docs-all-releases
135+
COMMAND ${Python3_EXECUTABLE}
136+
"${ELD_MULTIVERSION_SCRIPTS_DIR}/assemble_site.py"
137+
--site-dir "${ELD_MULTIVERSION_SITE_DIR}"
138+
--versions "${versions_json}"
139+
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
140+
COMMENT "Assembling multi-version documentation site"
141+
USES_TERMINAL
142+
VERBATIM
143+
)
144+
145+
# Convenience target that does everything
146+
add_custom_target(eld-docs-multiversion
147+
DEPENDS eld-docs-assemble
148+
COMMENT "Multi-version documentation complete: ${ELD_MULTIVERSION_SITE_DIR}"
149+
)
150+
151+
message(STATUS "Multi-version docs target: eld-docs-all-releases")
152+
message(STATUS " Versions: ${ELD_DOC_VERSIONS}")
153+
message(STATUS " Stable: ${ELD_DOC_STABLE}")
154+
message(STATUS " Output: ${ELD_MULTIVERSION_SITE_DIR}")
155+
156+
endfunction()

docs/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
add_subdirectory(design)
22
add_subdirectory(userguide)
3+
add_subdirectory(multiversion)

docs/multiversion/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
if(LLVM_ENABLE_SPHINX)
2+
include(BuildMultiVersionDocs)
3+
eld_add_multiversion_doc_targets()
4+
endif()

docs/multiversion/assemble_site.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Assemble multi-version ELD documentation site.
4+
5+
Creates the landing page (index.html), version manifest (versions.json),
6+
and stable symlink for the multi-version documentation site.
7+
8+
Usage:
9+
assemble_site.py --site-dir <path> --versions '<json>'
10+
11+
Example:
12+
assemble_site.py --site-dir ./site \
13+
--versions '[{"path":"latest","label":"Latest (main)","dev":true},
14+
{"path":"22.x","label":"Release 22.x","stable":true}]'
15+
"""
16+
17+
import argparse
18+
import json
19+
import os
20+
import sys
21+
from pathlib import Path
22+
from typing import Any
23+
24+
SCRIPT_DIR = Path(__file__).parent
25+
TEMPLATES_DIR = SCRIPT_DIR / "templates"
26+
27+
28+
def load_template(name: str) -> str:
29+
"""Load a template file from the templates directory."""
30+
template_path = TEMPLATES_DIR / name
31+
return template_path.read_text()
32+
33+
34+
def find_stable_version(versions: list[dict[str, Any]]) -> str | None:
35+
"""Find the version marked as stable in the versions list."""
36+
for v in versions:
37+
if v.get("stable"):
38+
return v["path"]
39+
return None
40+
41+
42+
def generate_index_html(versions: list[dict[str, Any]], output_path: Path) -> None:
43+
"""Generate the landing page HTML with version links."""
44+
index_template = load_template("index.html")
45+
version_link_template = load_template("version_link.html")
46+
47+
version_links = []
48+
for v in versions:
49+
badge = ""
50+
if v.get("stable"):
51+
badge = '\n <span class="badge badge-stable">stable</span>'
52+
elif v.get("dev"):
53+
badge = '\n <span class="badge badge-dev">dev</span>'
54+
55+
version_links.append(version_link_template.format(
56+
path=v["path"],
57+
label=v["label"],
58+
badge=badge,
59+
))
60+
61+
html = index_template.format(version_links="".join(version_links))
62+
output_path.write_text(html)
63+
print(f"Generated: {output_path}")
64+
65+
66+
def generate_versions_json(versions: list[dict[str, Any]], stable: str | None, output_path: Path) -> None:
67+
"""Generate the versions manifest JSON file."""
68+
manifest = {
69+
"versions": versions,
70+
"stable": stable,
71+
"default": "stable" if stable else versions[0]["path"] if versions else None,
72+
}
73+
output_path.write_text(json.dumps(manifest, indent=2) + "\n")
74+
print(f"Generated: {output_path}")
75+
76+
77+
def create_stable_symlink(site_dir: Path, stable_target: str) -> None:
78+
"""Create the 'stable' symlink pointing to the stable version directory."""
79+
stable_link = site_dir / "stable"
80+
81+
if stable_link.is_symlink():
82+
stable_link.unlink()
83+
elif stable_link.exists():
84+
raise RuntimeError(f"'stable' exists but is not a symlink: {stable_link}")
85+
86+
target_dir = site_dir / stable_target
87+
if not target_dir.exists():
88+
print(f"WARNING: Target directory does not exist yet: {target_dir}")
89+
90+
os.symlink(stable_target, stable_link)
91+
print(f"Created symlink: stable -> {stable_target}")
92+
93+
94+
def main() -> int:
95+
parser = argparse.ArgumentParser(
96+
description="Assemble multi-version ELD documentation site",
97+
formatter_class=argparse.RawDescriptionHelpFormatter,
98+
epilog=__doc__,
99+
)
100+
parser.add_argument(
101+
"--site-dir", required=True, type=Path,
102+
help="Site output directory containing version subdirectories"
103+
)
104+
parser.add_argument(
105+
"--versions", required=True,
106+
help="JSON array of version specs: [{\"path\":\"...\",\"label\":\"...\",\"stable\":true}]"
107+
)
108+
109+
args = parser.parse_args()
110+
111+
try:
112+
site_dir = args.site_dir.resolve()
113+
versions = json.loads(args.versions)
114+
115+
if not isinstance(versions, list):
116+
raise ValueError("--versions must be a JSON array")
117+
118+
stable = find_stable_version(versions)
119+
120+
site_dir.mkdir(parents=True, exist_ok=True)
121+
122+
generate_index_html(versions, site_dir / "index.html")
123+
generate_versions_json(versions, stable, site_dir / "versions.json")
124+
125+
if stable:
126+
create_stable_symlink(site_dir, stable)
127+
else:
128+
print("WARNING: No version marked as stable, skipping stable symlink")
129+
130+
nojekyll = site_dir / ".nojekyll"
131+
nojekyll.touch()
132+
print(f"Created: {nojekyll}")
133+
134+
print(f"\nSite assembled successfully: {site_dir}")
135+
return 0
136+
137+
except json.JSONDecodeError as e:
138+
print(f"ERROR: Invalid JSON in --versions: {e}", file=sys.stderr)
139+
return 1
140+
except Exception as e:
141+
print(f"ERROR: {e}", file=sys.stderr)
142+
return 1
143+
144+
145+
if __name__ == "__main__":
146+
sys.exit(main())

0 commit comments

Comments
 (0)