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
268 changes: 268 additions & 0 deletions .github/workflows/build-essentia.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
---
# This workflow is based on: https://github.com/MTG/essentia/blob/8dbdc0735c0dc54fc2c57e083883b4baba7bf272/.github/workflows/build-wheels-cibuildwheel.yml
name: Build essentia wheels (riscv64)

on:
workflow_dispatch:
inputs:
version:
description: 'Version glob to (re)build; empty builds every version of docs/packages/essentia.yaml not released yet'
required: false
default: ''
pull_request:
branches: [main]
paths:
- '.github/workflows/build-essentia.yml'
- 'docs/packages/essentia.yaml'
- 'patches/essentia/**'
push:
branches: [main]
paths:
- '.github/workflows/build-essentia.yml'
- 'docs/packages/essentia.yaml'
- 'patches/essentia/**'

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

permissions:
contents: read # to fetch code (actions/checkout)

env:
# Upstream publishes untagged dev builds whose version is `git describe`'s
# commit count; this is the commit 2.1b6.dev1438 was built from. Move both together.
ESSENTIA_REF: 8dbdc0735c0dc54fc2c57e083883b4baba7bf272
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64

jobs:
setup:
uses: $/.github/workflows/_setup.yml
with:
package: essentia
version: ${{ inputs.version }}

build_wheels:
needs: [setup]
if: needs.setup.outputs.versions != '[]'
name: Build essentia ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
timeout-minutes: 720
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.setup.outputs.versions) }}
python: ["cp312", "cp313", "cp314"]

env:
ESSENTIA_VERSION: ${{ matrix.version }}

steps:
- name: Checkout essentia ${{ env.ESSENTIA_VERSION }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: MTG/essentia
ref: ${{ env.ESSENTIA_REF }}
fetch-depth: 0
persist-credentials: false

- name: Fetch release tags from GitHub
run: git fetch --tags --force

- name: Checkout python-wheels
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: python-wheels
persist-credentials: false

- name: Patch essentia source
run: git apply python-wheels/patches/essentia/${{ env.ESSENTIA_VERSION }}/*.patch

- name: Stage the statically linked libraries' licence texts
run: |
. packaging/build_config.sh
mkdir -p ../licences && cd ../licences
stage() {
name=$1 url=$2 pattern=${3:-}
curl -fsSL --retry 5 "$url" -o "$name.tar.gz"
mkdir "$name" && tar -xf "$name.tar.gz" -C "$name" --strip-components=1
if [ -n "$pattern" ]; then
cat "$name/$pattern" > "$GITHUB_WORKSPACE/LICENSE.$name"
else
find "$name" -maxdepth 1 -type f \( -name 'COPYING*' -o -name 'LICEN[CS]E*' -o -name 'COPYRIGHT*' \) \
| sort | xargs cat > "$GITHUB_WORKSPACE/LICENSE.$name"
fi
test -s "$GITHUB_WORKSPACE/LICENSE.$name"
}
stage eigen "https://gitlab.com/libeigen/eigen/-/archive/$EIGEN_VERSION/eigen-$EIGEN_VERSION.tar.gz"
stage fftw "http://www.fftw.org/$FFTW_VERSION.tar.gz"
stage lame "https://sourceforge.net/projects/lame/files/lame/$LAME_VERSION/lame-$LAME_VERSION.tar.gz/download"
stage ffmpeg "https://ffmpeg.org/releases/$FFMPEG_VERSION.tar.gz"
stage libsamplerate "https://github.com/libsndfile/libsamplerate/releases/download/0.1.9/$LIBSAMPLERATE_VERSION.tar.gz"
# zlib 1.2.12 has no LICENSE file; its licence text is in the README.
stage zlib "https://zlib.net/fossils/$ZLIB_VERSION.tar.gz" README
stage taglib "http://taglib.github.io/releases/$TAGLIB_VERSION.tar.gz"
stage libyaml "http://pyyaml.org/download/libyaml/$LIBYAML_VERSION.tar.gz"
stage chromaprint "https://github.com/acoustid/chromaprint/releases/download/v$CHROMAPRINT_VERSION/chromaprint-$CHROMAPRINT_VERSION.tar.gz"
cd "$GITHUB_WORKSPACE"
cp src/3rdparty/kiss_fft130/COPYING LICENSE.kissfft
cp "Essentia Licensing.txt" LICENSE.essentia-third-party
rm -rf ../licences

- name: Write the smoke test
run: |
cat > riscv64_smoke.py <<'EOF'
import os
import tempfile

import numpy as np

import essentia
import essentia.standard as es
import essentia.streaming
from essentia.standard import Chromaprinter, MetadataReader, MonoLoader, YamlInput

sr = 44100
t = np.arange(sr * 5) / sr
x = (0.5 * np.sin(2 * np.pi * 440 * t)).astype(np.float32)
spec = es.Spectrum()(es.Windowing(type="hann")(x[:4096]))
peak = np.argmax(spec) * sr / 4096
assert abs(peak - 440) < sr / 4096, peak
f = os.path.join(tempfile.mkdtemp(), "sine.wav")
es.MonoWriter(filename=f, sampleRate=sr, format="wav")(x)
y = MonoLoader(filename=f, sampleRate=22050)()
assert abs(len(y) - 5 * 22050) < 2048, len(y)
meta = MetadataReader(filename=f)()
assert meta[-2] == sr, meta[-4:]
fp = Chromaprinter(sampleRate=sr)(x)
assert isinstance(fp, str) and fp, fp
print("essentia", essentia.__version__, "ok", fp[:24], meta[-4:])
EOF

- name: Build wheels
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
with:
config-file: cibuildwheel.toml
output-dir: wheelhouse/
only: ${{ matrix.python }}-manylinux_riscv64
env:
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
CIBW_ENVIRONMENT_LINUX: >-
PROJECT_NAME=essentia
ESSENTIA_PROJECT_NAME=essentia
ESSENTIA_WHEEL_SKIP_3RDPARTY=1
ESSENTIA_WHEEL_ONLY_PYTHON=1
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
CMAKE_POLICY_VERSION_MINIMUM=3.5
PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/
PIP_ONLY_BINARY=numpy
# Upstream's image ships these static dependencies prebuilt; it has no
# riscv64 variant, so build them with upstream's own script. No Gaia: its
# Linux build compiles Qt 5 from source, and the macOS wheels skip it too.
CIBW_BEFORE_ALL_LINUX: >-
git config --global --add safe.directory '*' &&
PREFIX=/usr/local packaging/build_3rdparty_static_debian.sh &&
python3 waf configure --build-static --static-dependencies --pkg-config-path="${PKG_CONFIG_PATH}" &&
python3 waf &&
python3 waf install
CIBW_TEST_SOURCES: riscv64_smoke.py
CIBW_TEST_COMMAND: python riscv64_smoke.py

- name: Check the wheel version, extension and licences
run: |
python3 - wheelhouse/*.whl <<'EOF'
import os, sys, zipfile
version = os.environ["ESSENTIA_VERSION"]
expected = {"AUTHORS", "COPYING.txt", "LICENSE.kissfft", "LICENSE.essentia-third-party"} | {
f"LICENSE.{d}" for d in ("eigen", "fftw", "lame", "ffmpeg", "libsamplerate", "zlib", "taglib", "libyaml", "chromaprint")
}
for whl in sys.argv[1:]:
assert os.path.basename(whl).startswith(f"essentia-{version}-"), whl
names = zipfile.ZipFile(whl).namelist()
assert any(n.startswith("essentia/_essentia.") and n.endswith(".so") for n in names), whl
licences = {n.rsplit("/", 1)[1] for n in names if ".dist-info/licenses/" in n} - {""}
assert licences == expected, (whl, sorted(licences ^ expected))
print(whl, "ok")
EOF

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: essentia-${{ env.ESSENTIA_VERSION }}-${{ matrix.python }}-manylinux_riscv64
path: ./wheelhouse/*.whl
if-no-files-found: error

gpl_sources:
needs: [setup]
if: needs.setup.outputs.versions != '[]'
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.setup.outputs.versions) }}
name: Collect GPL sources for essentia ${{ matrix.version }}
runs-on: ubuntu-latest

env:
ESSENTIA_VERSION: ${{ matrix.version }}

steps:
- name: Checkout essentia ${{ env.ESSENTIA_VERSION }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: MTG/essentia
ref: ${{ env.ESSENTIA_REF }}
path: essentia
persist-credentials: false

- name: Checkout python-wheels
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: python-wheels
persist-credentials: false

- name: Download the essentia and third-party library sources
run: |
mkdir sources
git -C essentia archive --prefix="essentia-${ESSENTIA_VERSION}/" -o "$PWD/sources/essentia-${ESSENTIA_VERSION}.tar.gz" HEAD
cp -r "python-wheels/patches/essentia/${ESSENTIA_VERSION}" sources/essentia-patches
. essentia/packaging/build_config.sh
cd sources
fetch() { curl -fsSL --retry 5 "$2" -o "$1"; }
fetch "eigen-$EIGEN_VERSION.tar.gz" "https://gitlab.com/libeigen/eigen/-/archive/$EIGEN_VERSION/eigen-$EIGEN_VERSION.tar.gz"
fetch "$FFTW_VERSION.tar.gz" "http://www.fftw.org/$FFTW_VERSION.tar.gz"
fetch "lame-$LAME_VERSION.tar.gz" "https://sourceforge.net/projects/lame/files/lame/$LAME_VERSION/lame-$LAME_VERSION.tar.gz/download"
fetch "$FFMPEG_VERSION.tar.gz" "https://ffmpeg.org/releases/$FFMPEG_VERSION.tar.gz"
fetch "$LIBSAMPLERATE_VERSION.tar.gz" "https://github.com/libsndfile/libsamplerate/releases/download/0.1.9/$LIBSAMPLERATE_VERSION.tar.gz"
fetch "$ZLIB_VERSION.tar.gz" "https://zlib.net/fossils/$ZLIB_VERSION.tar.gz"
fetch "$TAGLIB_VERSION.tar.gz" "http://taglib.github.io/releases/$TAGLIB_VERSION.tar.gz"
fetch "$LIBYAML_VERSION.tar.gz" "http://pyyaml.org/download/libyaml/$LIBYAML_VERSION.tar.gz"
fetch "chromaprint-$CHROMAPRINT_VERSION.tar.gz" "https://github.com/acoustid/chromaprint/releases/download/v$CHROMAPRINT_VERSION/chromaprint-$CHROMAPRINT_VERSION.tar.gz"
cd ..
tar cf gpl-sources.tar -C sources .
tar tvf gpl-sources.tar

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: essentia-${{ matrix.version }}-gpl-sources
path: gpl-sources.tar
if-no-files-found: error

publish:
name: Publish essentia ${{ matrix.version }}
needs: [setup, build_wheels, gpl_sources]
if: needs.setup.outputs.versions != '[]'
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.setup.outputs.versions) }}
permissions:
contents: write
pull-requests: write
uses: $/.github/workflows/_publish-wheel.yml
secrets:
app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }}
with:
artifact-pattern: essentia-${{ matrix.version }}-*-manylinux_riscv64
gpl-sources-artifact: essentia-${{ matrix.version }}-gpl-sources
gpl-sources-description: Essentia (AGPL-3.0) with the statically linked FFTW (GPL-2.0), FFmpeg, LAME, TagLib, Chromaprint, libsamplerate, libyaml, zlib and Eigen sources
13 changes: 13 additions & 0 deletions docs/packages/essentia.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package-name: essentia
source-code: https://github.com/MTG/essentia
license: AGPL-3.0-only
versions:
- version: 2.1b6.dev1438
patched: true
comment: >-
Built without Gaia (no GaiaTransform), as upstream's macOS wheels are: upstream's Linux
Gaia build compiles Qt 5 from source. The static third-party libraries (FFTW, FFmpeg,
LAME, TagLib, Chromaprint, libsamplerate, libyaml, zlib, Eigen) are built with upstream's
own packaging scripts; the patches drop FFTW's x86-only SSE2 configure flags, refresh
the stale config.guess/config.sub of the older autotools tarballs, and drop TagLib 1.11.1's
cmake_policy(SET CMP0022 OLD) call, which CMake 4 rejects (backported from TagLib 1.12).
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
From 6045cee15d84ffbe220450d8e8f9462a71dd8cf6 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Tue, 13 Oct 2026 18:44:25 +0000
Subject: [PATCH 1/2] Only pass FFTW's x86 SIMD configure flags on x86 hosts

packaging/build_config.sh passes --enable-sse2 and
--with-incoming-stack-boundary=2 to FFTW's configure unconditionally.
Both are x86-only: on any other architecture FFTW's configure probes the
compiler for -msse2, fails, and aborts with "Need a version of gcc with
-msse2", so build_3rdparty_static_debian.sh cannot build FFTW on
aarch64, riscv64 or any non-x86 Linux host. Keep the two flags for
i?86/x86_64 build machines (which also covers the mingw cross build) and
drop them elsewhere, leaving FFTW's portable scalar codelets.

Upstream-Status: To upstream [not yet submitted; upstream publishes no non-x86 Linux wheel, so the gap only shows when the static 3rdparty build runs on another architecture]

Signed-off-by: Ludovic Henry <git@ludovic.dev>
---
packaging/build_config.sh | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/packaging/build_config.sh b/packaging/build_config.sh
index d193130a..25604638 100755
--- a/packaging/build_config.sh
+++ b/packaging/build_config.sh
@@ -205,10 +205,16 @@ FFMPEG_AUDIO_FLAGS_MUXERS="
# see http://www.fftw.org/install/windows.html
FFTW_FLAGS="
--enable-float
+ --with-our-malloc16
+"
+case "$(uname -m)" in
+ i?86|x86_64)
+ FFTW_FLAGS="$FFTW_FLAGS
--enable-sse2
--with-incoming-stack-boundary=2
- --with-our-malloc16
"
+ ;;
+esac

LIBSAMPLERATE_FLAGS="
--disable-fftw
--
2.43.0

Loading
Loading