Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bd1f6ba
Meson build for MMCoreJ
marktsuchida Jul 16, 2025
cddaf33
Don't compile Java in Meson build
marktsuchida Dec 10, 2025
62f217c
Fix indentation in pom.xml
marktsuchida Dec 10, 2025
affed8b
pom.xml typos
marktsuchida Dec 10, 2025
5d5b466
Reorder pom.xml sections
marktsuchida Dec 10, 2025
8c6e88f
Fix pom.xml GitHub URL
marktsuchida Dec 10, 2025
50d0721
Edit text in pom.xml
marktsuchida Dec 10, 2025
1b55815
Place SWIG-generated Java sources in subdir
marktsuchida Dec 10, 2025
174e711
Make pom.xml work to build Java-only package
marktsuchida Dec 10, 2025
69b526c
Use pom property to specify Meson builddir
marktsuchida Dec 10, 2025
80f639d
Disable javadoc warnings for now
marktsuchida Dec 10, 2025
117d3c7
Package native libraries in jar with classifiers
marktsuchida Dec 10, 2025
f09f924
Add README; unpin mmdevice/mmcore commit
marktsuchida Dec 11, 2025
6f85cf1
Check that SWIG-generated Java sources are present
marktsuchida Dec 11, 2025
dd2d2fa
Comments
marktsuchida Dec 11, 2025
2eaf059
Prevent leftover native libs from being packaged
marktsuchida Dec 11, 2025
23fa105
Add full-sources jar (complete source dist)
marktsuchida Dec 11, 2025
15635c7
Remove GPG signing from pom.xml
marktsuchida Dec 18, 2025
2c62041
Flatten the published POM
marktsuchida Dec 18, 2025
83e0505
Comment (arm32)
marktsuchida Dec 18, 2025
c95ec95
MMCore: Give name to docs target
marktsuchida Jan 16, 2026
21694b7
Add Javadoc from Doxygen HTML
marktsuchida Jan 16, 2026
f7faa82
Document existing JNI lib loading behavior
marktsuchida Jan 16, 2026
0af0e45
Rename JNI library MMCoreJ_wrap -> mmcorej
marktsuchida Jan 16, 2026
f39a401
Load native library by extracting from JAR
marktsuchida Jan 16, 2026
3b9e96b
Add test for (modern) native library loading
marktsuchida Jan 16, 2026
e12ab18
Add script to check SWIG-generated sources
marktsuchida Jan 16, 2026
3ba97ec
CI: Build MMCoreJ packages with Meson+Maven
marktsuchida Jan 16, 2026
24c017d
Use case-proof Doxygen HTML filenames
marktsuchida Jan 17, 2026
fa58fe8
CI: Hide progress messages
marktsuchida Jan 17, 2026
45e8fe2
CI: Disable mmdevice/mmcore tests for MMCoreJ
marktsuchida Jan 17, 2026
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
116 changes: 115 additions & 1 deletion .github/workflows/ci-mmdevice-mmcore.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
name: MMDevice/MMCore experimental Meson build & tests
name: MMDevice/MMCore/MMCoreJ Meson build & tests

on:
pull_request:
paths:
- MMDevice/**
- MMCore/**
- MMCoreJ_wrap/**
- .github/workflows/ci-mmdevice-mmcore.yml
push:
branches:
- main
paths:
- MMDevice/**
- MMCore/**
- MMCoreJ_wrap/**
- .github/workflows/ci-mmdevice-mmcore.yml

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

env:
CJDK_HIDE_PROGRESS_BARS: '1'
MAVEN_ARGS: '--batch-mode --no-transfer-progress'

jobs:
test:
strategy:
Expand Down Expand Up @@ -76,3 +82,111 @@ jobs:
cd MMCore
meson setup builddir --buildtype debug -Dcpp_std=${{ matrix.cppstd }}
meson test -C builddir --print-errorlogs

mmcorej-natives:
name: mmcorej-${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
name: linux-x86_64
- os: windows-2025
name: windows-x86_64
cxx: cl
- os: macos-15
name: macos-arm64
deployment_target: "11.0"
- os: macos-15
name: macos-x86_64
meson_cross_args: --cross-file=scripts/meson-cross-macos-x86_64.txt
jdk_arch: x86_64
deployment_target: "10.15"
env:
CXX: ${{ matrix.cxx }}
CJDK_ARCH: ${{ matrix.jdk_arch }}
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.deployment_target }}
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v7
with:
python-version: 3.14
- run: |
uv tool install cjdk
uv tool install meson
uv tool install ninja
uv tool install 'swig<4'
- name: Build in manylinux container
if: startsWith(matrix.os, 'ubuntu-')
run: |
docker run --rm -v "${{ github.workspace }}"/MMCoreJ_wrap:/work \
-w /work \
quay.io/pypa/manylinux_2_28_x86_64 \
bash scripts/build_jni_linux.sh
- name: Build (non-Linux)
if: "!startsWith(matrix.os, 'ubuntu-')"
shell: bash
run: |
cp -R MMDevice MMCoreJ_wrap/subprojects/mmdevice
cp -R MMCore MMCoreJ_wrap/subprojects/mmcore
cd MMCoreJ_wrap
export JAVA_HOME=$(cjdk -j zulu:8 java-home)
export PATH="$JAVA_HOME/bin:$PATH"
meson setup builddir --vsenv --buildtype=release \
${{ matrix.meson_cross_args }} \
-Dmmdevice:tests=disabled -Dmmcore:tests=disabled
meson compile -C builddir
- name: Check SWIG-generated sources are correct in meson.build
run: |
cd MMCoreJ_wrap
uv run --no-project scripts/verify_swig_gensrc.py builddir
- name: Package & test (non-Windows)
if: "!startsWith(matrix.os, 'windows-')"
run: |
cd MMCoreJ_wrap
cjdk -j zulu:8 exec -- mvn verify
- name: Package & test (Windows)
if: startsWith(matrix.os, 'windows-')
run: |
cd MMCoreJ_wrap
cjdk -j zulu:8 exec -- mvn.cmd verify
- uses: actions/upload-artifact@v6
with:
name: mmcorej-natives-${{ matrix.name }}
path: MMCoreJ_wrap/target/MMCoreJ-*-natives-*.jar

mmcorej-java:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v7
with:
python-version: 3.14
- run: |
uv tool install cjdk
uv tool install meson
uv tool install ninja
uv tool install 'swig<4'
- run: |
sudo apt-get update
sudo apt-get install -y doxygen
- run: |
cp -R MMDevice MMCoreJ_wrap/subprojects/mmdevice
cp -R MMCore MMCoreJ_wrap/subprojects/mmcore
cd MMCoreJ_wrap
export JAVA_HOME=$(cjdk -j zulu:8 java-home)
export PATH="$JAVA_HOME/bin:$PATH"
meson setup builddir -Djavadoc_from_doxygen=true \
-Dmmdevice:tests=disabled -Dmmcore:tests=disabled
meson compile -C builddir
cjdk -j zulu:8 exec -- mvn package \
-Dskip.natives=true -DskipTests=true
- name: Verify full sources JAR
run: |
cd MMCoreJ_wrap
./scripts/verify-full-sources-jar.sh target/MMCoreJ-*-full-sources.jar
- uses: actions/upload-artifact@v6
with:
name: mmcorej-java
path: MMCoreJ_wrap/target/MMCoreJ-*.jar
3 changes: 3 additions & 0 deletions MMCore/docs/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ EXCLUDE_SYMBOLS = internal std
EXTRACT_ALL = YES
RECURSIVE = YES

# Changing this changes the HTML file names; stick to what we had
CASE_SENSE_NAMES = NO

CLASS_GRAPH = TEXT
COLLABORATION_GRAPH = NO
GROUP_GRAPHS = NO
Expand Down
2 changes: 1 addition & 1 deletion MMCore/docs/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ doxyfile = configure_file(

doc_install_dir = get_option('datadir') / 'doc' / meson.project_name()

custom_target(
mmcore_docs_target = custom_target(
'docs',
command: [doxygen, '-q', doxyfile],
output: ['apidoc'],
Expand Down
3 changes: 3 additions & 0 deletions MMCoreJ_wrap/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.wraplock
/target/
.flattened-pom.xml
20 changes: 15 additions & 5 deletions MMCoreJ_wrap/MMCoreJ.i
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@
}

if (useStdErr) {
System.err.println("Load " + NATIVE_LIBRARY_NAME + ": " + message);
System.err.println("MMCoreJ native library loading: " + message);
}
}

Expand Down Expand Up @@ -197,10 +197,6 @@
}

if (loaded) {
// TODO If the library is already loaded, we still want to make sure
// that it was loaded from the right copy. The Core now has this
// information, so we should check it.
logLibraryLoading("Already loaded");
return true;
}
return false;
Expand Down Expand Up @@ -253,6 +249,20 @@
// Load the MMCoreJ_wrap native library.
static {
logLibraryLoading("Start loading...");

// New-style loading (extract from JAR, fall back to java.library.path,
// both with 'mmcorej').
if (!checkIfAlreadyLoaded()) {
try {
NativeLibraryLoader.load("mmcorej");
logLibraryLoading("Loaded 'mmcorej' library");
}
catch (UnsatisfiedLinkError e) {
logLibraryLoading("Falling back to 'MMCoreJ_wrap' loading mode...");
}
}

// Legacy loading ('MMCoreJ_wrap')
if (!checkIfAlreadyLoaded()) {
// The most reliable method for locating (the correct copy of)
// MMCoreJ_wrap is to look in the single path given as a Java system
Expand Down
90 changes: 90 additions & 0 deletions MMCoreJ_wrap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# MMCoreJ

MMCoreJ provides Java bindings for MMCore, Micro-Manager's hardware abstraction
layer, written in C++.

## Building MMCoreJ

The currently "supported" way to build MMCoreJ is to run the full Micro-Manager
build (using Ant on Windows and Autoconf/Automake elsewhere).

However, we are working to make MMCoreJ an independent project with its own
build system using Meson for the C++ parts and Maven for the Java parts.

You can test the new build system as follows. Note that SWIG version 2.x or 3.x
(not 4.x) must be available on the path.

```sh
# Copy over matching sources for MMDevice and MMCore (otherwise they will be
# fetched by Meson but may not be the correct versions).
rm -rf subprojects/mmdevice subprojects/mmcore
cp -R ../MMDeivce subprojects/mmdevice
cp -R ../MMCore subprojects/mmcore

meson setup --vsenv builddir # --vsenv recommended on Windows
meson compile -C builddir
mvn package
```

This should place the built JARs in target/. There is a main JAR containing the
Java classes and a separate per-OS/architecture "natives" JAR containing the
native library.

## Native Library Loading

MMCoreJ requires a native library containing the C++ MMCore. The library is
loaded automatically when the `CMMCore` class is first accessed.

There is a "modern" and "legacy" search order for the native library. The
modern way can be used when MMCoreJ is built by Meson + Maven; when it fails,
loading falls back to the legacy search order. (As of this writing, MMStudio
still uses the legacy method.)

### Modern Search Order (for Meson build)

**Library file names:**

- **Linux**: `libmmcorej.so`
- **macOS**: `libmmcorej.dylib`
- **Windows**: `mmcorej.dll`

The native library `mmcorej` is located in the following order:

1. **JAR resource extraction**: The library is extracted from JAR resources at
`/natives/<os>/<arch>/` where `<os>` is `linux`, `macos`, or `windows` and
`<arch>` is `arm64` or `x86_64`. These are provided as "natives" classifier
JARs (e.g., `MMCoreJ-<version>-natives-macos-arm64.jar`).

2. **System library path**: If not found as a JAR resource, `java.library.path`
is searched.

If the above fails, the legacy loading mechanism is used (see below).

### Legacy Search Order

**Library file names:**

- **Linux**: `libMMCoreJ_wrap.so`
- **macOS**: `libMMCoreJ_wrap.jnilib` or `.dylib`
- **Windows**: `MMCoreJ_wrap.dll`

The legacy loading mechanism searches for `MMCoreJ_wrap` as follows:

1. **System property path**: If `mmcorej.library.path` is set, the library is
loaded exclusively from that directory (no fallback to other locations).

2. **Relative to the JAR**: These paths are searched in order:
- The directory containing the MMCoreJ JAR
- The parent directory of the JAR
- _[deprecated]_ `../mm/<platform>/` relative to the JAR
- The grandparent directory of the JAR
- _[deprecated]_ `../../mm/<platform>/` relative to the JAR

Where `<platform>` is one of: `macosx`, `win32`, `win64`, `linux32`,
`linux64`. The `mm/<platform>/` locations are obsolete and were never used
by Micro-Manager.

3. **Compile-time path**: _[deprecated]_ A path optionally set at build time
(Autoconf/Automake build only).

4. **System default**: `java.library.path` is used as a last resort.
Loading
Loading