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
142 changes: 142 additions & 0 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: Package

on:
push:
tags:
- 'v*'
workflow_dispatch:

permissions:
contents: write

concurrency:
group: package-${{ github.ref }}
cancel-in-progress: true

# plot is header-only and dependency-free, so the package payload is just the
# amalgamated single header (plot.hpp) + LICENSE. Each job configures with
# -DPLOT_INSTALL_FULL=OFF and builds only the `plot-amalgamate` target.
env:
CMAKE_ARGS: >-
-DCMAKE_BUILD_TYPE=Release
-DPLOT_INSTALL_FULL=OFF
-DPLOTS_BUILD_TESTS=OFF
-DPLOTS_BUILD_EXAMPLES=OFF

jobs:
# ── Linux: DEB (Debian) + RPM (RedHat) + tar.gz + zip ───────────────────────
package-linux:
name: linux / DEB + RPM + tar.gz + zip
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install rpmbuild
run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends rpm
- name: Configure
run: cmake -B build ${{ env.CMAKE_ARGS }}
- name: Build amalgamation
run: cmake --build build --target plot-amalgamate
- name: Package
run: |
cd build
cpack -G DEB
cpack -G RPM
cpack -G TGZ
cpack -G ZIP
- uses: actions/upload-artifact@v4
with:
name: packages-linux
path: |
build/*.deb
build/*.rpm
build/*.tar.gz
build/*.zip
if-no-files-found: error

# ── macOS: productbuild (.pkg) ──────────────────────────────────────────────
package-macos:
name: macos / pkg
runs-on: macos-15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Configure
run: cmake -B build ${{ env.CMAKE_ARGS }}
- name: Build amalgamation
run: cmake --build build --target plot-amalgamate
- name: Package
run: cd build && cpack -G productbuild
- uses: actions/upload-artifact@v4
with:
name: packages-macos
path: build/*.pkg
if-no-files-found: error

# ── Windows: NSIS (.exe) ────────────────────────────────────────────────────
package-windows:
name: windows / NSIS
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install NSIS
shell: powershell
run: |
# CPack -G NSIS needs makensis on PATH; chocolatey installs it.
choco install nsis -y --no-progress
$nsisDir = "C:\Program Files (x86)\NSIS"
if (-not (Test-Path "$nsisDir\makensis.exe")) { $nsisDir = "C:\Program Files\NSIS" }
if (-not (Test-Path "$nsisDir\makensis.exe")) { throw "makensis.exe not found after NSIS install" }
Add-Content -Path $env:GITHUB_PATH -Value $nsisDir
- name: Configure
run: cmake -B build ${{ env.CMAKE_ARGS }}
- name: Build amalgamation
run: cmake --build build --target plot-amalgamate --config Release
- name: Package
run: cd build && cpack -G NSIS
- uses: actions/upload-artifact@v4
with:
name: packages-windows
path: build/*.exe
if-no-files-found: error

# ── Publish to the GitHub release ───────────────────────────────────────────
# Runs only for a tag ref (push of v* or a workflow_dispatch with the tag
# selected as the ref). Creates the release if absent, else uploads/replaces
# assets so a hand-edited release body is preserved.
publish:
name: Publish release
needs: [package-linux, package-macos, package-windows]
runs-on: ubuntu-22.04
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
pattern: packages-*
merge-multiple: true
path: dist/
- name: List artifacts
run: ls -lh dist/
- name: Create or update GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euxo pipefail
tag="${{ github.ref_name }}"
repo="${{ github.repository }}"
if gh release view "$tag" --repo "$repo" >/dev/null 2>&1; then
gh release upload "$tag" dist/* --clobber --repo "$repo"
else
gh release create "$tag" dist/* \
--title "plot $tag" \
--generate-notes \
--repo "$repo"
fi
153 changes: 125 additions & 28 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# _________________________________________________________

cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(plot VERSION 0.1.0 LANGUAGES CXX)
project(plot VERSION 1.0.0 LANGUAGES CXX)

add_library(plot INTERFACE)
add_library(plot::plot ALIAS plot)
Expand Down Expand Up @@ -39,31 +39,128 @@ if(DOXYGEN_EXECUTABLE)
endif()

include(GNUInstallDirs)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(TARGETS plot EXPORT plot-targets
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

include(CMakePackageConfigHelpers)
set(PLOT_INSTALL_CMAKEDIR ${CMAKE_INSTALL_DATADIR}/cmake/plot)
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/plot-config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/plot-config.cmake
INSTALL_DESTINATION ${PLOT_INSTALL_CMAKEDIR}
)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/plot-config-version.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
ARCH_INDEPENDENT
)
install(EXPORT plot-targets
FILE plot-targets.cmake
NAMESPACE plot::
DESTINATION ${PLOT_INSTALL_CMAKEDIR}
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/plot-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/plot-config-version.cmake
DESTINATION ${PLOT_INSTALL_CMAKEDIR}
)
# ─── Single-header amalgamation (plot.hpp) ───────────────────────────────────
# Generate a self-contained plot.hpp from include/plot/ (order taken from the
# `all` umbrella). This is the payload shipped in the release packages. The
# target is built as part of ALL when a Python 3 interpreter is available;
# without one the rest of the build (tests/examples/find_package) is unaffected.
find_package(Python3 COMPONENTS Interpreter QUIET)
if(Python3_Interpreter_FOUND)
file(GLOB PLOT_HEADERS CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/include/plot/*.hpp)
set(PLOT_AMALGAMATED_HEADER ${CMAKE_CURRENT_BINARY_DIR}/plot.hpp)
add_custom_command(
OUTPUT ${PLOT_AMALGAMATED_HEADER}
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/amalgamate.py
${CMAKE_CURRENT_SOURCE_DIR}/include/plot ${PLOT_AMALGAMATED_HEADER}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/scripts/amalgamate.py
${CMAKE_CURRENT_SOURCE_DIR}/include/plot/all ${PLOT_HEADERS}
COMMENT "Amalgamating plot headers into plot.hpp"
VERBATIM
)
add_custom_target(plot-amalgamate ALL DEPENDS ${PLOT_AMALGAMATED_HEADER})
# Ship plot.hpp alongside the tree so #include <plot.hpp> works ambiently.
install(FILES ${PLOT_AMALGAMATED_HEADER} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
else()
message(WARNING "Python3 not found — plot.hpp amalgamation disabled.")
endif()

# LICENSE travels in every package (and find_package install).
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})

# ─── Full header tree + CMake package config (find_package(plot)) ────────────
# ON for source installs; the release packages set -DPLOT_INSTALL_FULL=OFF so
# the package payload is just the single header + LICENSE.
option(PLOT_INSTALL_FULL "Install the full include/plot tree and CMake package config" ON)
if(PLOT_INSTALL_FULL)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(TARGETS plot EXPORT plot-targets
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

include(CMakePackageConfigHelpers)
set(PLOT_INSTALL_CMAKEDIR ${CMAKE_INSTALL_DATADIR}/cmake/plot)
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/plot-config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/plot-config.cmake
INSTALL_DESTINATION ${PLOT_INSTALL_CMAKEDIR}
)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/plot-config-version.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
ARCH_INDEPENDENT
)
install(EXPORT plot-targets
FILE plot-targets.cmake
NAMESPACE plot::
DESTINATION ${PLOT_INSTALL_CMAKEDIR}
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/plot-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/plot-config-version.cmake
DESTINATION ${PLOT_INSTALL_CMAKEDIR}
)
endif()

# ─── CPack: H5CPP-style release packages ─────────────────────────────────────
# Header-only ⇒ arch-independent (DEB all / RPM noarch). The package payload is
# the single header + LICENSE (configure the release build with
# -DPLOT_INSTALL_FULL=OFF). Generators: DEB, RPM, NSIS (Windows), productbuild
# (macOS), plus TGZ + ZIP archives.
set(CPACK_PACKAGE_NAME "plot-dev")
set(CPACK_PACKAGE_VENDOR "Varga Labs")
set(CPACK_PACKAGE_CONTACT "steven.varga@gmail.com")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Header-only, dependency-free C++ SVG plotting library")
set(CPACK_PACKAGE_HOMEPAGE_URL "https://vargalabs.github.io/plots")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")

# productbuild (macOS) rejects license files without a .rtf/.html/.txt
# extension; the canonical LICENSE is extensionless, so stage a .txt copy.
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/LICENSE"
"${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt"
COPYONLY)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_BINARY_DIR}/LICENSE.txt")
set(CPACK_STRIP_FILES OFF)

# Headers land at <prefix>/include/plot.hpp — ambient, no -I needed. Linux uses
# /usr (apt/yum convention); macOS uses /usr/local (Apple/Homebrew). Windows
# NSIS ignores this and lets the user pick a target dir.
if(APPLE)
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr/local")
else()
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr")
endif()

# DEB (Debian/Ubuntu)
set(CPACK_DEBIAN_PACKAGE_NAME "plot-dev")
set(CPACK_DEBIAN_PACKAGE_SECTION "libdevel")
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "all") # header-only
set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT")

# RPM (RedHat/Fedora)
set(CPACK_RPM_PACKAGE_NAME "plot-devel")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
set(CPACK_RPM_PACKAGE_ARCHITECTURE "noarch") # header-only
set(CPACK_RPM_FILE_NAME "RPM-DEFAULT")

# NSIS (Windows installer)
set(CPACK_NSIS_PACKAGE_NAME "plot ${PROJECT_VERSION}")
set(CPACK_NSIS_DISPLAY_NAME "plot ${PROJECT_VERSION}")
set(CPACK_NSIS_URL_INFO_ABOUT "${CPACK_PACKAGE_HOMEPAGE_URL}")
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)

# productbuild (macOS .pkg)
set(CPACK_PRODUCTBUILD_IDENTIFIER "org.vargalabs.plot")

# Distinct, lowercase per-OS filenames for NSIS/productbuild/TGZ/ZIP. DEB/RPM
# keep their own conventional *-DEFAULT names set above.
set(CPACK_PACKAGE_FILE_NAME "plot-v${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}")
string(TOLOWER "${CPACK_PACKAGE_FILE_NAME}" CPACK_PACKAGE_FILE_NAME)

include(CPack)
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,28 @@ find_package(plot CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE plot::plot)
```

### Releases & the single-header drop

Each tagged release ships prebuilt packages (built by `.github/workflows/package.yml`):

| Platform | Artifact |
|----------|----------|
| Debian / Ubuntu | `plot-dev_<ver>_all.deb` |
| RedHat / Fedora | `plot-devel-<ver>.noarch.rpm` |
| Windows | `plot-v<ver>-windows.exe` (NSIS) |
| macOS | `plot-v<ver>-darwin.pkg` |
| any | `plot-v<ver>-<os>.tar.gz` / `.zip` |

The payload is a single amalgamated header `plot.hpp` (generated from
`include/plot/` by `scripts/amalgamate.py`). Once installed — or just dropped in
your tree — it is all you need:

```cpp
#include <plot.hpp> // no -I plot/, no other files
```

Regenerate it manually with `python3 scripts/amalgamate.py include/plot plot.hpp`.

## Examples

`-DPLOTS_BUILD_EXAMPLES=ON` builds the demos under `examples/`; each writes an
Expand Down
Loading
Loading