Skip to content

Licence hygiene: add LICENSE, header the unheadered files, quarantine the GPL modules - #35

Open
transfix wants to merge 13 commits into
masterfrom
chore/licence-hygiene
Open

transfix wants to merge 13 commits into
masterfrom
chore/licence-hygiene

Conversation

@transfix

@transfix transfix commented Sep 3, 2026 •

Copy link
Copy Markdown
Owner

Cleans up the licensing state of the repository, from a licence audit run on 2026-09-03. Full analysis in cvc-engagement-docs/modernization/f2dock-port-hamiltonian-docking-acceleration.md §4.2 (branch contract/f2dock-ph-docking-modernization).

Smallest-risk first, one commit per change. Two findings are deliberately not addressed here — see Left for a human decision.

What was wrong

The repository asserted LGPL-2.1 in prose only (README.md, OVERVIEW.md) and had no LICENSE file at any root, while CPACK_RESOURCE_FILE_LICENSE pointed at INSTALL — a build-instructions file — so every generated package shipped with no licence text. 44 sources had no header at all. And GPL-2.0-or-later code was compiled into the LGPL binary by default, including a GPL header #included directly into the LGPL core header.

Commits

1. docs(licence) — add the LGPL-2.1 text and ship it in packages
FSF LGPL-2.1 as LICENSE, CPack pointed at it, installed alongside README.md. Verified: cpack -G TGZ now contains LICENSE.

2. docs(licence) — add the project licence banner to files that had none
44 files get the same UT Austin LGPL-2.1 banner the rest of the tree uses, year from when the file first appeared, author line taken from a sibling in the same module where the tree records one. Six files are deliberately skipped because they are not this project's work — sturm.h, dsexceptions.h, and four libmol files. cuckoo.{h,cc} already carried a permissive BRICS grant.

3. docs(licence) — make the two SPDX tags agree with the rest of the tree
DockMode.h and test_dock_mode.cpp were the only files with an SPDX tag and it read LGPL-2.1-or-later; the other 68 LGPL files grant 2.1 with no "or later" clause. Aligned. Revert this commit alone if 2.1-or-later was in fact the intent.

4. build — gate the GPL modules behind F2DOCK_ENABLE_GPL_COMPONENTS (default OFF)

Module Licence Feature Was it built?
sparsefft3 GPL-2.0-or-later useSparseFFT Yes, and it was the default FFT path
libicp / f3dock-icp GPL-2.0-or-later icpRefine Yes, via F2DOCK_ENABLE_F3DOCK_EXPERIMENTAL=ON

Both now behind one option, OFF by default. inc/f2dock/Docking.h no longer #includes the GPL headers: inc/fft-utils/sparsefft3-api.h is the seam, forwarding to the real header when the module is built and otherwise declaring the same three entry points against an incomplete plan type — which is all the core needs, since the plan is only ever held and passed as an opaque pointer. sparsefft3-stub.cpp satisfies the link and fails loudly if reached.

pr.useSparseFFT was unconditionally true; it now follows the build. The dense FFTW fallback is the existing, already-exercised useSparseFFT false path — Docking.cpp pins both plan pointers to NULL and every call site tests for that — not new code. Asking for either feature in a parameter file is now an error with an explanation, following the F2DOCK_HAVE_NFFT precedent, rather than a silent downgrade.

5. docs — THIRD-PARTY.md
Nine vendored trees with upstream, author, licence, and whether the default build compiles them.

6. ci — build the GPL configuration once
After commit 4 nothing in the matrix compiled the GPL modules, so they would have rotted silently. One extra ubuntu-latest/Release job builds and tests with the option ON. Drop this commit if the extra CI minutes are not wanted.

Verification

Check Result
Default build (F2DOCK_ENABLE_GPL_COMPONENTS=OFF) configures, builds, 187/187 unit tests pass
GPL build (=ON) configures, builds, 197/197 pass (the 10 extra ICP tests)
GPL symbols in the default binary only the stubs, against the opaque type — no GPL header was reached; no libf3dock-icp.a
1A2K docking run, dense vs sparse pose output identical — every score, rank, RMSD, rotation and translation matches, differing only in signed zero on columns that are numerically zero
useSparseFFT true / icpRefine true in a default build rejected with the explanatory message, exit 1
cpack -G TGZ ships LICENSE
clang-format --dry-run --Werror, whole tree clean

The dense path is slower — 8.9s vs 23.1s on the reduced 8-rotation smoke case, though that ratio is inverted from the real workload and is not a benchmark. Replacing sparsefft3 with a permissively-licensed sparse transform remains the right long-term fix.

Left for a human decision

Neither of these is touched by this PR. Both need a decision, and probably UT counsel, rather than a commit.

libmol (72 of 76 files). The Boston University notice at inc/libmol/mask.h:3–16 grants copy-and-modify rights only to "US Universities and US Government supported Research Institutions", only for educational and research purposes, and says the software "may not be distributed to any other institution without express permission". That is a class-of-licensee and field-of-use restriction incompatible with the LGPL, with the GPL, and with redistribution generally. HAVE_LIBMOL is OFF so it is not compiled — but it is in the published source tree, and source redistribution is exactly what the notice restricts. Neither CVC-Lab/F2Dock nor CVC-Lab/F3Dock ships it, while F3Dock's src/CMakeLists.txt still calls ADD_SUBDIRECTORY (libmol) for a directory that is not in its checkout — consistent with deliberate stripping before publication. Verified against the local F3Dock-upstream and F2Dock-cvclab checkouts.

inc/f2dock/ElementInformation.h provenance. GPL-2.0-or-later in both CVC-Lab repositories, LGPL-2.1-only here, copyright year moved 2003 → 2011. UT holds both, so UT can relicense its own work — but nothing in the tree or the history records the decision.

Two smaller items are recorded in THIRD-PARTY.md and also left alone:

  • src/f3dock-loop-closure/tripep_closure.{h,cpp} — bare copyright, no grant of any kind, and built by default. The algorithm is published and the original is widely redistributed, so this looks like an oversight, but the fix is an explicit grant from the authors.
  • inc/libicp/kdtree.h — AFL-1.1, referencing a LICENSE file with "additional provisions" that is not in the tree. Now unbuilt by default as part of the libicp gate, but the unknown provisions remain unknown.

These are observations about the source tree, not legal advice.

CI is red on macOS and Windows, and it is not this branch

All four Linux jobs pass, including the new GPL-components one. The six macOS and Windows jobs fail with one shared cause that predates this branch:

CGAL/Filtered_kernel/internal/Static_filters/Static_filter_error.h:110
  double error()  const { return _e; }
note: expanded from macro 'error'
  libcvc-src/src/cvc/SDF/SignDistanceFunction_v2/reg3data.h:12

Both runners now resolve CGAL 6.2 (Homebrew on macOS, vcpkg on Windows). In CGAL 6 the deprecated AABB_traits.h forwards to AABB_traits_3.h, which newly pulls in Static_filter_error.h. libcvc's reg3data.h:12 defines #define error(x) {fprintf(stderr, "%s\n", x); exit(1);} — an unguarded function-like macro escaping a header — and error() matches it with an empty argument. On MSVC the macro body shows up in the errors verbatim ('fprintf': is not a member of 'CGAL::internal::Static_filter_error').

Why it cannot be this branch:

  • The failing translation unit is _deps/libcvc-build/src/cvc/algorithm.cpp.o — target 11 of 229, inside libcvc, before any F2Dock source compiles. Every error in all six jobs is that one; there is not a single error from an F2Dock source file.
  • libcvc is pinned at LIBCVC_GIT_SHA 08c15b5e…, unchanged here.
  • On macOS and Windows F2DOCK_ENABLE_GPL_COMPONENTS is OFF, so the only lines this branch adds that run before libcvc is configured are an option() and a message(STATUS ...).
  • Two independent compilers and two independent package managers reproduce it identically.

master has not run CI since 2026-05-21, when Homebrew and vcpkg still shipped CGAL 5.x, which is why this surfaces now rather than then.

The fix belongs in libcvc — reg3data.h should not leak an error macro out of a header — or, as a stopgap, this workflow could pin the CGAL version it installs. Either is a separate change from this PR.

The repository asserted LGPL-2.1 only in prose (README.md, OVERVIEW.md) and
had no LICENSE file at any root. CPACK_RESOURCE_FILE_LICENSE pointed at
INSTALL -- a build-instructions file with no licence text -- so every
generated DEB/TGZ shipped with no licence at all.

Add the FSF LGPL-2.1 text as LICENSE, point CPack at it, and install it
alongside README.md. The copy is the canonical FSF text whose "how to apply"
address matches the per-file banners already in the tree.
44 sources carried no licence header at all. Each now gets the same UT
Austin LGPL-2.1 banner the rest of the tree uses, with the copyright year
taken from when the file first appeared and the author line taken from a
sibling in the same module where the tree already records one.

Files deliberately left alone, because they are not this project's work and
stamping a UT banner on them would be a false claim:

  src/f3dock-loop-closure/sturm.h   Graphics Gems (Hook & McAree), via
                                    Chaok Seok; provenance comment, no grant
  inc/PG-range/dsexceptions.h       nine lines, provenance unknown
  inc/libmol/newgetline.h           libmol; Boston University notice applies
  src/libmol/newgetline.cpp         to the whole tree
  src/libmol/test.cpp
  src/libmol/testHbond.cpp

inc/PG-range/cuckoo.h and src/PG-range/cuckoo.cc already carry a permissive
BRICS grant and needed nothing. All six abstentions are recorded in
THIRD-PARTY.md.
DockMode.h and test_dock_mode.cpp were the only files carrying an SPDX tag,
and it read LGPL-2.1-or-later. Every other LGPL file in the tree grants
"version 2.1 as published by the Free Software Foundation" with no
"or (at your option) any later version" clause -- that is 2.1-only -- and
README.md and the new LICENSE both say 2.1. Align the tags.

Both files are UT Austin's own work, so this records the project's stated
licence rather than changing anyone's grant. Revert this commit alone if the
intent was in fact to relicense the project as 2.1-or-later.
@transfix
transfix force-pushed the chore/licence-hygiene branch from a1b6351 to 8d7c0f7 Compare September 3, 2026 23:20
…ult OFF)

F2Dock is LGPL-2.1-only, but two vendored modules are GPL-2.0-or-later and
one of them was built by default:

  sparsefft3  MIT 2000, modified at CVC Lab -- the `useSparseFFT` transform
  libicp      Andreas Geiger / KIT -- the `icpRefine` pose refinement

Linking either makes the F2Dock binary a combined work covered by the GPL.
Worse, inc/f2dock/Docking.h -- the LGPL core header -- #included
fft-utils/sparsefft3.h, which carries structs, enums and macros rather than
bare declarations, so the GPL text was pulled into LGPL translation units.

Both are now behind one explicitly-named option, OFF by default. With it OFF
neither source is compiled and neither object is linked.

The seam is inc/fft-utils/sparsefft3-api.h (LGPL): it forwards to the real
header when the module is built, and otherwise declares the same three entry
points against an incomplete plan type -- which is all the core needs, since
the plan is only ever held and passed as an opaque pointer.
src/fft-utils/sparsefft3-stub.cpp satisfies the link and fails loudly if a
future caller reaches it. Docking.h and fastfft.h now include the seam.
sparsefft3-timers.h was in Docking.h's include list but is used only by
sparsefft3-plan.cpp, so that include is simply gone.

The default FFT path changes as a result -- pr.useSparseFFT was
unconditionally true. It now follows the build. The dense FFTW path this
falls back to was already complete and already selected by
`useSparseFFT false`: Docking.cpp pins both plan pointers to NULL at the
`if (!useSparseFFT)` branch and every call site tests for that, so the
switch is the existing, exercised path rather than a new one. It is slower
and produces the same scores.

Asking for either feature in a parameter file is now an error with an
explanation, following the F2DOCK_HAVE_NFFT precedent, rather than a silent
downgrade. F2Dock prints its licence posture and FFT path at startup:

  GPL components: sparseFFT=no libicp=no (build licence: LGPL-2.1-only)
  FFT path: dense (FFTW)

f2dock::refine_pose_point_to_plane keeps its signature in both
configurations and becomes a no-op returning refined == false -- the same
result Docking.cpp already handles when ICP does not converge.

Verified: both configurations configure, build and pass. Default build,
187/187 unit tests; the only sparse3DFFT symbols in the binary are the
stubs, against the opaque type, so no GPL header was reached.
Nine vendored trees, with upstream, author, licence, and whether the default
build compiles them. Four are permissive and need nothing (SSEApproxMath,
the vendored fftw3.h, cuckoo). Two are the GPL modules now behind
F2DOCK_ENABLE_GPL_COMPONENTS. Three are open questions, marked as such:

  libmol                  Boston University notice restricting both the class
                          of licensee and the field of use. Not free, not
                          redistributable. Not compiled, but it is in the
                          published source tree, and source redistribution is
                          what the notice restricts.
  tripep_closure.{h,cpp}  bare copyright, no grant of any kind -- and built
                          by default.
  dsexceptions.h          provenance unknown; nine lines, probably not
                          copyrightable, but nobody knows where it came from.

Also records, without resolving, that ElementInformation.h is
GPL-2.0-or-later in both CVC-Lab repositories and LGPL-2.1-only here with the
copyright year moved 2003 -> 2011.

README points at the file from both the layout and licence sections.
F2DOCK_ENABLE_GPL_COMPONENTS is OFF by default, so after the previous commit
nothing in the matrix compiled sparsefft3 or libicp -- the code would break
silently and only surface for whoever next needed the sparse FFT. One extra
ubuntu-latest/Release job builds and tests with the option ON.

The job name, concurrency group and artifact name all pick up a suffix so the
new entry does not collide with the existing ubuntu-latest/Release one.

Also notes the licence gate in doc/F3DOCK_MIGRATION_MATRIX.md, whose Phase 2
and Phase 4 sections describe an ICP pass that a default build no longer
contains.
Every macOS and Windows job fails while compiling libcvc, not F2Dock:

  CGAL/.../Static_filters/Static_filter_error.h:110:10: error: expected
    member name or ';' after declaration specifiers
    110 |   double error()  const { return _e; }
  note: expanded from macro 'error'
    libcvc-src/src/cvc/SDF/SignDistanceFunction_v2/reg3data.h:12

libcvc's reg3data.h defines a function-like `error(x)` macro at file scope.
CGAL 6 deprecated AABB_traits.h into AABB_traits_3.h, which now pulls in
Static_filter_error and its `double error() const`; `error()` matches the
one-parameter macro with an empty argument. Homebrew and vcpkg both ship
CGAL 6.2 now, so libcvc stopped compiling on both platforms. Fixed upstream
in transfix/libcvc#324.

Picking the fix up means moving off the v3.2.1-era pin. Two consequences for
F2Dock, both confined to src/vol/RAWIV.cpp, the only file here that touches
libcvc:

  - 3.3.0 moved the public headers into subdirectories: cvc/app.h ->
    cvc/core/app.h, cvc/types.h -> cvc/core/types.h, and bounding_box /
    dimension / volume / volume_file_info under cvc/volume/.
  - CVC_NAMESPACE is gone; cvc/core/namespace.h now just declares
    `namespace cvc {}`. The 17 CVC_NAMESPACE:: qualifiers become cvc::.

LIBCVC_VERSION moves to 3.3.0 as well, so it acts as a compatibility floor:
an older installed or prebuilt libcvc now fails the find_package() version
check and falls through to the source build, instead of being accepted and
then failing to compile against the new include paths. There is no v3.3.0
GitHub release, so the prebuilt strategy misses and the source path is what
runs -- same as it already did for v3.2.1.

The SHA is pinned one commit past v3.3.0 because the tag itself still has
the macro.

Verified locally: configures, builds, 187/187 unit tests, and a 1A2K docking
run produces output identical to the same run against the old pin.
F2Dock now gets every third-party library from the cvcpkg catalog and builds
libcvc never. Removed outright: the FetchContent source build of libcvc, the
release-tarball download, the legacy fetch-libcvc-deps action, the apt/brew
dependency installs, and the entire vcpkg apparatus on Windows (the D: drive
re-routing for disk space, the cache save/restore, and the 5-attempt retry
loop that existed because gmp's GNU mirror timed out on ~1 cold run in 3).

Why the source build had to go rather than stay as a fallback: compiling
libcvc is what pulled CGAL and the host toolchain into F2Dock's build, and it
broke on both non-Linux platforms at once. Homebrew and vcpkg have both moved
to CGAL 6.2, which collides with a `#define error(x)` in libcvc's reg3data.h;
windows-latest's VS18 v180 crashes CL.exe outright on cvc.vcxproj. A fallback
that only runs when the fast path fails is a fallback that only runs on the
broken platforms.

  cvcpkg/recipes/f2dock/   the recipe: libcvc + fftw3 (+ pthreads4w on
                           Windows), everything else transitive through
                           libcvc. Builds F2Dock/GB-Rerank/FilterOutput/
                           F2DockServer with the GPL components OFF, so the
                           published bundle is unambiguously LGPL-2.1.
  cvcpkg/recipes/_common/  env-windows.ps1, so the Windows recipe build pins
                           cl.exe and the CRT instead of letting CMake pick
                           MinGW off the runner PATH.
  .github/actions/cvcpkg-deps/  one composite action, used by all six jobs
                           across ci.yml and release.yml.

CMakeLists.txt drops the three-strategy resolution for a plain
find_package(cvc 3.3.0 CONFIG) plus an explicit cvc::xmlrpc check, each with
an error that names the fix. --include-host-tools brings cmake and ninja from
the catalog too, so the runner image's toolchain is not in play either.

The only apt left anywhere is clang-format (lint job), lcov (coverage
report) and dpkg-dev (.deb packaging) -- none participate in compiling or
linking F2Dock.

Verified end to end against a locally-built stand-in for the libcvc 3.3.0
+cvc.4 bundle (CVC_USING_XMLRPC=ON, exported version fixed):

  configure   8.1s, "Using libcvc 3.3.0 from <prefix>/lib/cmake/cvc"
  build       24s, and zero libcvc objects compiled (was ~35 min)
  ctest       187/187
  1A2K run    pose output identical to the pre-change build

This needs libcvc 3.3.0+cvc.4. Every bundle published so far exports no
cvc::xmlrpc and reports PACKAGE_VERSION "3.0", so both checks reject them --
which is exactly why F2Dock always fell back to the source build.
tests/unit/CMakeLists.txt FetchContent'd googletest v1.14.0 from github by
commit SHA at configure time. That was the only thing left in the build that
reached the network, and the only dependency not resolved through the
recipe. It now comes from the catalog like everything else:

    find_package(GTest CONFIG REQUIRED)

with `googletest` added to cvcpkg/recipes/f2dock's BUILD deps only -- the
tests need it, the published f2dock bundle does not, so it stays out of the
runtime closure.

Needs the googletest recipe in transfix/libcvc-deps#563 (1.17.0, gtest +
gmock, gtest_force_shared_crt=ON on Windows so the CRT matches the link
mode).

Verified against a prefix carrying the catalog build of googletest 1.17.0
alongside libcvc:

  configure   no FetchContent at all
  build       22.6s, zero _deps/ objects compiled
  ctest       187/187

The suite was written against 1.14 and needed no changes for 1.17.
…question

THIRD-PARTY.md noted in passing that "the FFTW library itself is
GPL-2.0-or-later and is linked dynamically". That understates it. SetupFFTW()
is unconditional at CMakeLists.txt:32 and FFTW_LIB is linked unconditionally,
and the cvcpkg recipe agrees (license: GPL-2.0-or-later) -- so every F2Dock
binary is a combined work with GPL code regardless of the sparsefft3/libicp
gating. Quarantining those cleaned up the vendored tree; it did not make the
shipped binary LGPL.

So an FFT-provider swap is load-bearing for the licence, not an optimisation.
Documents the measured trade rather than asserting one, since that is the
decision:

  * PocketFFT (BSD-3) agrees with FFTW to float64 round-off (<= 7.3e-12 across
    N=64..256), so it is a valid drop-in on correctness.
  * At F2Dock's real numFreq=120 a single transform is 1.31x slower, but the
    number that matters is throughput at equal core count -- F2Dock
    parallelises over rotations with single-threaded transforms -- and there
    FFTW is 1.41-1.73x faster. That is the honest cost.
  * A latency-only comparison flatters PocketFFT (nthreads=4 makes one
    transform 2.6x faster than single-threaded FFTW), but that spends four
    cores on one transform while F2Dock already spends them on four rotations.

Also records how small the swap is -- the whole FFTW surface is already behind
the FFTW_* macros in fftwPrecision.h and is ~25 call shapes with no threads,
guru interface, wisdom or advanced plans -- and lists MKL/cuFFT (proprietary
but redistributable, which LGPL permits linking) and FFTW's commercial licence
as the alternatives to eating the 1.4-1.7x.

Benchmarks were run on this workstation against fftw3 from the catalog; they
are a decision aid, not a portable claim.
`pip install cvcpkg` fails -- cvcpkg is not published to PyPI:

    ERROR: Could not find a version that satisfies the requirement cvcpkg
      (from versions: none)

which failed every lane on this branch. The supported entry point is
libcvc-deps' own cvcpkg-install composite action, which provisions cvcpkg and
runs `cvcpkg install-deps` against a recipe; libcvc consumes it the same way.

Passes include_host_tools so cmake, ninja and googletest come from the
catalog. Without it a hermetic build has nothing to fall back on, since the
apt/brew/vcpkg steps are gone.

NOTE: include_host_tools is added by transfix/libcvc-deps#564 and is not on
that repo's master yet, so this pins @master and will fail with "Unexpected
input" until #564 lands. Sequencing, not a defect.
F2Dock is LGPL-2.1-only and FFTW is GPL-2.0-or-later, so every binary built
so far has been a combined work under the GPL. Gating the vendored GPL modules
(sparsefft3, libicp) cleaned the source tree but left that untouched -- the
linked library was always the larger question.

  -DF2DOCK_FFT_PROVIDER=pocketfft   (default) BSD-3
                        mkl         Intel oneMKL
                        cufft       NVIDIA cuFFT
                        fftw        GPL, opt-in, warns

Choosing fftw prints a boxed configure-time warning that the resulting binary
is GPL and must not be shipped under F2Dock's licence, and SetupFFTW() now runs
ONLY on that path -- an LGPL build no longer even searches for a GPL library.

The seam is small because the engine never called FFTW directly: every
transform already went through the FFTW_* macros in fftwPrecision.h. Those
macros now resolve either to FFTW (unchanged) or to cvc::fft, a provider-neutral
API that deliberately keeps FFTW's semantics -- unnormalised in both
directions, half-complex r2c layout, plans that capture their buffers -- so
swapping providers is not a change in behaviour. Docking.cpp is untouched.

Two smaller things fell out:

  * fftwPrecision.h no longer defines plan_many_dft, execute_dft or the wisdom
    pair on the neutral path. Those were used only by the GPL sparsefft3 module
    and by rank-fftw, so leaving them undefined turns any future non-FFTW use
    into a compile error naming the symbol instead of a silent fallback.
  * rank-fftw builds for every provider now, which is more correct: the
    efficient grid sizes it measures should reflect the transform actually in
    use. Its only FFTW-specific calls were the wisdom pair, already dead behind
    #ifdef SAVE_WISDOM_TO_FILE, which is defined nowhere in the tree.

Verified on PocketFFT, against the FFTW build as baseline:
  build           clean, no fftw3 on F2Dock's own link line
  ctest           187/187
  1A2K docking    pose output IDENTICAL -- every score, rank, rmsd, rotation
                  and translation

KNOWN GAP: the binary still resolves libfftw3 transitively, because libcvc
itself is built with CVC_ENABLE_FFTW and exports it to consumers. F2Dock's
own dependency is gone; finishing the job needs libcvc built without FFTW
(F2Dock uses it for volume I/O and xmlrpc, not transforms) or moved to a
permissive provider too. That is a libcvc packaging decision, not one to make
here.
googletest is published for linux-release and both windows configs, but this
matrix also builds linux-debug and macOS, and cvcpkg fails hard on a missing
tuple:

    Error: requested component(s) not found in the catalog for this platform
    tuple: googletest

Tying F2Dock's CI to the completeness of libcvc-deps' publish matrix makes it
red for reasons no change here can cause or fix. fallback_to_source builds the
gap from the recipe's pinned source instead -- slower for that one component,
still hermetic, and it closes on its own as the tuples get published.

Needs transfix/libcvc-deps#564, which adds the input.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant