diff --git a/MMCoreJ_wrap/.gitignore b/MMCoreJ_wrap/.gitignore
new file mode 100644
index 000000000..bc654be1e
--- /dev/null
+++ b/MMCoreJ_wrap/.gitignore
@@ -0,0 +1,3 @@
+.wraplock
+/target/
+.flattened-pom.xml
diff --git a/MMCoreJ_wrap/README.md b/MMCoreJ_wrap/README.md
new file mode 100644
index 000000000..f11218e8e
--- /dev/null
+++ b/MMCoreJ_wrap/README.md
@@ -0,0 +1,31 @@
+# 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.
diff --git a/MMCoreJ_wrap/meson.build b/MMCoreJ_wrap/meson.build
new file mode 100644
index 000000000..5be44414f
--- /dev/null
+++ b/MMCoreJ_wrap/meson.build
@@ -0,0 +1,138 @@
+# This Meson script is experimental and potentially incomplete. It is not part
+# of the supported build system for Micro-Manager or mmCoreAndDevices.
+
+project(
+ 'mmcorej',
+ 'cpp', 'java',
+ # TODO version (set here and generate pom.xml)
+ meson_version: '>=1.3.0',
+ default_options: [
+ 'cpp_std=c++14',
+ 'warning_level=3',
+ ],
+)
+
+cxx = meson.get_compiler('cpp')
+
+if cxx.get_id() in ['gcc', 'clang']
+ # SWIG Java typemaps call for -fno-strict-aliasing when compiling the
+ # SWIG-generated code (see SWIG docs).
+ add_project_arguments('-fno-strict-aliasing', language: 'cpp')
+endif
+
+if cxx.get_id() in ['msvc', 'clang-cl']
+ add_project_arguments('-DNOMINMAX', language: 'cpp')
+endif
+
+fs = import('fs')
+
+swig = find_program('swig', native: true)
+
+mmcore_proj = subproject(
+ 'mmcore',
+ default_options: {
+ 'default_library': 'static',
+ 'tests': 'disabled',
+ },
+)
+mmcore_dep = mmcore_proj.get_variable('mmcore_dep')
+
+jni_dep = dependency('jni', version: '>= 1.8.0')
+
+threads_dep = dependency('threads')
+
+swig_include_dirs = mmcore_proj.get_variable('swig_include_dirs')
+swig_incdir_args = []
+foreach abspath : swig_include_dirs
+ swig_incdir_args += '-I' + fs.relative_to(
+ abspath,
+ meson.project_build_root(),
+ )
+endforeach
+
+# TODO Add check to ensure we don't miss any
+swig_gen_java_source_names = [
+ 'ActionType.java',
+ 'BooleanVector.java',
+ 'CharVector.java',
+ 'CMMCore.java',
+ 'Configuration.java',
+ 'DeviceDetectionStatus.java',
+ 'DeviceInitializationState.java',
+ 'DeviceNotification.java',
+ 'DeviceType.java',
+ 'DoubleVector.java',
+ 'FocusDirection.java',
+ 'LongVector.java',
+ 'Metadata.java',
+ 'MetadataArrayTag.java',
+ 'MetadataError.java',
+ 'MetadataSingleTag.java',
+ 'MetadataTag.java',
+ 'MMCoreJ.java',
+ 'MMCoreJConstants.java',
+ 'MMCoreJJNI.java',
+ 'MMEventCallback.java',
+ 'pair_ss.java',
+ 'PortType.java',
+ 'PropertySetting.java',
+ 'PropertyType.java',
+ 'StrMap.java',
+ 'StrVector.java',
+ 'SWIGTYPE_p_std__istringstream.java',
+ 'UnsignedVector.java',
+]
+
+swig_gen_java_src_dir = 'generated-java/mmcorej'
+swig_gen_java_sources = []
+foreach src : swig_gen_java_source_names
+ swig_gen_java_sources += swig_gen_java_src_dir / src
+endforeach
+
+swig_gen = custom_target(
+ 'swig-mmcorej',
+ input: 'MMCoreJ.i',
+ output: [
+ 'MMCoreJ_swig_wrap.cpp', # @OUTPUT0@, swig_gen[0]
+ 'MMCoreJ_swig_wrap.h', # @OUTPUT1@, swig_gen[1]
+ swig_gen_java_source_names,
+ ],
+ depfile: 'MMCoreJ_swig_wrap.d',
+ # Run via wrapper script because outdir needs to pre-exist.
+ command: [
+ import('python').find_installation(),
+ files('scripts/run_swig.py'),
+ '--outdir', '@OUTDIR@' / swig_gen_java_src_dir,
+ '--',
+ swig,
+ '-c++',
+ '-java',
+ '-package', 'mmcorej',
+ '-module', 'MMCoreJ',
+ swig_incdir_args,
+ '-MD', '-MF', '@DEPFILE@',
+ '-o', '@OUTPUT0@',
+ '-oh', '@OUTPUT1@',
+ '-outdir', '@OUTDIR@' / swig_gen_java_src_dir,
+ '@INPUT@',
+ ],
+)
+
+swig_gen_cpp_sources = [swig_gen[0], swig_gen[1]]
+
+# Note that JNI libraries are supposed to be dylibs, not Mach-O bundles, on
+# macOS, even though they are loaded by dlopen() (other platforms don't have
+# this distinction). So we use shared_library(), not shared_module().
+shared_library(
+ 'MMCoreJ_wrap',
+ swig_gen_cpp_sources,
+ dependencies: [
+ jni_dep,
+ mmcore_dep,
+ threads_dep,
+ ],
+ gnu_symbol_visibility: 'hidden',
+ install: true,
+)
+
+# Note: We do not compile Java sources; leave that to Maven.
diff --git a/MMCoreJ_wrap/pom.xml b/MMCoreJ_wrap/pom.xml
index a7e77cfe0..28ae9af9f 100644
--- a/MMCoreJ_wrap/pom.xml
+++ b/MMCoreJ_wrap/pom.xml
@@ -1,141 +1,448 @@
-
- 4.0.0
- org.micro-manager.mmcorej
- MMCoreJ
- jar
- 11.11.0
- Micro-Manager Java Interface to MMCore
- Micro-Manager is open source software for control of automated/motorized microscopes. This specific packages provides the Java interface to the device abstractino layer (MMCore) that is written in C++ with a C-interface
- http://micro-manager.org
-
-
- 1.8
- 1.8
- UTF-8
-
-
-
-
- nenada
- Nenad Amodaj
- Luminous Point
-
-
- marktsuchida
- Mark Tsuchida
- University of Wisconsin, Madison
-
-
- nicost
- Nico Stuurman
- UCSF/HHMI
-
-
-
-
- https://github.com/micro-manager/mmCoreAndDevices
- scm:git:git://github.com/micro-manager/mmCoreAndDevices.git
- scm:git:git@github.com:micro-manager/mmCoreAndDevices.git
-
-
-
-
- LGPL-2.1
- https://github.com/micro-manager/mmCoreAndDevices/blob/main/MMCoreJ_wrap/license.txt
- repo
-
-
-
-
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
- 1.7
-
-
- add-source
- generate-sources
-
- add-source
-
-
-
- ../build/intermediates/Swig
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
- 3.2.1
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 3.2.0
-
-
- attach-javadocs
-
- jar
-
-
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.5
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.7
- true
-
- ossrh
- https://s01.oss.sonatype.org/
- true
-
-
-
-
-
-
-
- ossrh
- https://s01.oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
+
+ 4.0.0
-
+ org.micro-manager.mmcorej
+ MMCoreJ
+ 11.11.0
+
+ MMCore Java API
+ Java bindings for MMCore, the device abstraction layer of Micro-Manager, the microscope control and acquisition platform.
+ https://micro-manager.org
+
+
+
+ LGPL-2.1
+ https://github.com/micro-manager/mmCoreAndDevices/blob/main/MMCoreJ_wrap/license.txt
+ repo
+
+
+
+
+
+ nenada
+ Nenad Amodaj
+ Luminous Point
+
+
+ marktsuchida
+ Mark Tsuchida
+ University of Wisconsin–Madison
+
+
+ nicost
+ Nico Stuurman
+ University of California, San Francisco
+
+
+
+
+ https://github.com/micro-manager/mmCoreAndDevices
+ scm:git:https://github.com/micro-manager/mmCoreAndDevices.git
+ scm:git:git@github.com:micro-manager/mmCoreAndDevices.git
+
+
+
+ 1.8
+ 1.8
+ UTF-8
+
+
+ builddir
+
+
+ natives-${natives.os}-${natives.arch}
+ ${native.library.prefix}MMCoreJ_wrap${native.library.suffix}
+ ${meson.build.dir}/${native.library.name}
+
+
+ false
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 3.6.2
+
+
+
+ enforce-maven
+
+ enforce
+
+
+
+
+ 3.6.3
+
+
+
+
+
+ enforce-platform-supported
+ validate
+
+ enforce
+
+
+ ${skip.natives}
+
+
+ natives.os
+
+Property natives.os is not set, probably because the OS could not be detected.
+Set natives.os to a supported value: linux, macos, windows.
+Also set native.library.prefix and native.library.suffix appropriately.
+Set -Dskip.natives=true to build only the main JAR.
+
+
+
+ natives.arch
+
+Property natives.arch is not set, probably because it could not be detected.
+Set natives.arch to a supported value: x86_64, arm64, or arm32.
+Set -Dskip.natives=true to build only the main JAR.
+
+
+
+
+
+
+ enforce-native-library-exists
+ prepare-package
+
+ enforce
+
+
+ ${skip.natives}
+
+
+
+ ${native.library.path}
+
+
+Native library not found: ${native.library.path}
+Run the Meson build first:
+ meson setup ${meson.build.dir}
+ meson compile -C ${meson.build.dir}
+Also check that native.library.prefix and native.library.suffix are correct.
+Or set -Dskip.natives=true to build only the main JAR.
+
+
+
+
+
+
+ enforce-generated-sources-exist
+ process-sources
+
+ enforce
+
+
+
+
+
+ ${meson.build.dir}/generated-java/mmcorej/CMMCore.java
+
+
+Generated Java sources not found: ${meson.build.dir}/generated-java/mmcorej/*.java
+Run the Meson build first:
+ meson setup ${meson.build.dir}
+ meson compile -C ${meson.build.dir}
+
+
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.6.1
+
+
+ add-source
+ generate-sources
+
+ add-source
+
+
+
+ ${meson.build.dir}/generated-java
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.4.0
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.7.1
+
+
+ full-sources-jar
+ package
+
+ single
+
+
+
+ src/assembly/full-sources.xml
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.12.0
+
+
+ none
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-clean-plugin
+ 3.5.0
+
+
+ clean-natives-staging
+ prepare-package
+
+ clean
+
+
+ true
+
+
+ ${project.build.directory}/natives-staging
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 3.4.0
+
+
+ stage-native-library
+ prepare-package
+
+ copy-resources
+
+
+ ${skip.natives}
+ ${project.build.directory}/natives-staging/natives/${natives.os}/${natives.arch}
+
+
+ ${meson.build.dir}
+
+ ${native.library.name}
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.5.0
+
+
+ natives-jar
+ package
+
+ jar
+
+
+ true
+ ${natives.classifier}
+ ${project.build.directory}/natives-staging
+
+ natives/**
+
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ flatten-maven-plugin
+ 1.7.3
+
+ oss
+
+
+
+ flatten
+ process-resources
+
+ flatten
+
+
+
+ flatten-clean
+ clean
+
+ clean
+
+
+
+
+
+
+
+
+
+
+ natives-os-linux
+
+ Linux
+
+
+ linux
+ lib
+ .so
+
+
+
+ natives-os-macos
+
+ mac
+
+
+ macos
+ lib
+ .dylib
+
+
+
+ natives-os-windows
+
+ windows
+
+
+ windows
+
+ .dll
+
+
+
+
+
+
+ natives-arch-amd64
+
+ amd64
+
+
+ x86_64
+
+
+
+
+ natives-arch-x86_64
+
+ x86_64
+
+
+ x86_64
+
+
+
+
+ natives-arch-aarch64
+
+ aarch64
+
+
+ arm64
+
+
+
+
+ natives-arch-arm
+
+ arm
+
+
+ arm32
+
+
+
+
+ natives-arch-armv7l
+
+ armv7l
+
+
+ arm32
+
+
+
+
diff --git a/MMCoreJ_wrap/scripts/run_swig.py b/MMCoreJ_wrap/scripts/run_swig.py
new file mode 100644
index 000000000..cb87ae6fd
--- /dev/null
+++ b/MMCoreJ_wrap/scripts/run_swig.py
@@ -0,0 +1,25 @@
+import argparse
+import subprocess
+import sys
+from pathlib import Path
+
+parser = argparse.ArgumentParser(description="Create directory and run SWIG")
+parser.add_argument("--outdir", type=str, help="Directory to create")
+parser.add_argument("swig_cmd", nargs="*", help="SWIG command and arguments")
+
+args = parser.parse_args()
+
+swig_command = args.swig_cmd
+if swig_command and swig_command[0] == "--":
+ swig_command = swig_command[1:]
+
+if not swig_command:
+ print("Error: No SWIG command provided", file=sys.stderr)
+ sys.exit(1)
+
+if args.outdir:
+ outdir = Path(args.outdir)
+ outdir.mkdir(parents=True, exist_ok=True)
+
+result = subprocess.run(swig_command)
+sys.exit(result.returncode)
diff --git a/MMCoreJ_wrap/scripts/verify-full-sources-jar.sh b/MMCoreJ_wrap/scripts/verify-full-sources-jar.sh
new file mode 100755
index 000000000..1241b4ae1
--- /dev/null
+++ b/MMCoreJ_wrap/scripts/verify-full-sources-jar.sh
@@ -0,0 +1,124 @@
+#!/bin/bash
+# Verify that the full-sources JAR contains all expected source files.
+# Usage: ./scripts/verify-full-sources-jar.sh [path-to-jar]
+#
+# Works whether MMCoreJ_wrap is its own repo (for future) or a subdirectory of
+# mmCoreAndDevices.
+
+set -e
+
+JAR_FILE="${1:-target/MMCoreJ-*-full-sources.jar}"
+
+# Files that may be omitted from the JAR despite being in git.
+ALLOWED_MISSING=(
+ 'Makefile.am'
+ 'build.xml'
+ '*.vcxproj'
+ '*.vcxproj.filters'
+)
+
+# Subprojects to check for source files.
+SUBPROJECTS=(
+ 'subprojects/mmcore'
+ 'subprojects/mmdevice'
+)
+
+# Expand glob if needed
+JAR_FILE=$(echo $JAR_FILE)
+
+if [[ ! -f "$JAR_FILE" ]]; then
+ echo "ERROR: JAR file not found: $JAR_FILE"
+ echo "Run 'mvn package' first."
+ exit 1
+fi
+
+echo "Verifying: $JAR_FILE"
+
+# Verify subprojects exist on filesystem (required as reference for JAR verification)
+for subproj in "${SUBPROJECTS[@]}"; do
+ if [[ ! -d "$subproj" ]]; then
+ echo "ERROR: Required subproject directory not found: $subproj"
+ echo "Run 'meson setup' before 'mvn package'."
+ exit 1
+ fi
+ if [[ ! -f "$subproj/meson.build" ]]; then
+ echo "ERROR: Subproject missing meson.build: $subproj"
+ echo "Subproject may be incomplete or corrupted."
+ exit 1
+ fi
+done
+
+# Create temp files for comparison
+JAR_CONTENTS=$(mktemp)
+EXPECTED_FILES=$(mktemp)
+trap "rm -f $JAR_CONTENTS $EXPECTED_FILES" EXIT
+
+# Detect if we're in a subdirectory of a larger git repo.
+# If so, we need to strip the prefix from git ls-files output.
+# Empty if at root, "subdir/" if in subdir:
+GIT_PREFIX=$(git rev-parse --show-prefix)
+
+# List JAR contents (excluding META-INF/)
+jar tf "$JAR_FILE" | grep -v 'META-INF' | sort > "$JAR_CONTENTS"
+
+# Helper to strip the git prefix from paths
+strip_prefix() {
+ if [[ -n "$GIT_PREFIX" ]]; then
+ sed "s|^${GIT_PREFIX}||"
+ else
+ cat
+ fi
+}
+
+# Helper to filter out allowed missing files
+filter_allowed_missing() {
+ local result
+ result=$(cat)
+ for pattern in "${ALLOWED_MISSING[@]}"; do
+ # Convert glob pattern to grep -v pattern
+ # e.g., *.vcxproj -> \.vcxproj$
+ local regex=$(echo "$pattern" | sed 's/\./\\./g' | sed 's/\*/.*/g')
+ result=$(echo "$result" | grep -v "$regex" || true)
+ done
+ echo "$result"
+}
+
+# Build expected file list from git
+{
+ # All files tracked by git in this directory
+ git ls-files --full-name | strip_prefix
+
+ # subprojects (from submodules - have their own .git)
+ for subproj in "${SUBPROJECTS[@]}"; do
+ if [[ -d "$subproj" ]]; then
+ if [[ -e "$subproj/.git" ]]; then
+ # Submodule: query its own git
+ git -C "$subproj" ls-files --full-name | sed "s|^|$subproj/|"
+ else
+ # Not a git repo: enumerate all files on disk
+ # (meson.build existence already verified above)
+ find "$subproj" -type f
+ fi
+ fi
+ done
+} | sort -u > "$EXPECTED_FILES"
+
+# Compare (filtering out allowed missing files)
+MISSING=$(comm -23 "$EXPECTED_FILES" "$JAR_CONTENTS" | filter_allowed_missing)
+
+if [[ -n "$MISSING" ]]; then
+ echo "ERROR: The following expected source files are missing from the JAR:"
+ echo "$MISSING"
+ exit 1
+fi
+
+echo "OK: All expected source files are present in the JAR."
+# Optionally show extra files in JAR (not an error, just informational)
+# Filter out directory entries (end with /) and blank lines
+EXTRA=$(comm -13 "$EXPECTED_FILES" "$JAR_CONTENTS" | grep -v '/$' | grep -v '^$' || true)
+if [[ -n "$EXTRA" ]]; then
+ echo "Note: JAR contains additional files not in git (this is OK):"
+ echo "$EXTRA"
+fi
+
+exit 0
diff --git a/MMCoreJ_wrap/src/assembly/full-sources.xml b/MMCoreJ_wrap/src/assembly/full-sources.xml
new file mode 100644
index 000000000..29225af9f
--- /dev/null
+++ b/MMCoreJ_wrap/src/assembly/full-sources.xml
@@ -0,0 +1,67 @@
+
+ full-sources
+
+
+
+
+ jar
+
+ false
+
+
+
+
+ ${project.basedir}
+ true
+
+
+
+ builddir*/**
+ build/**
+ target/**
+ gensrc/**
+
+ MMCoreJ_wrap.cxx
+ MMCoreJ_wrap.h
+
+ **/Makefile.am
+ **/build.xml
+ **/*.vcxproj
+ **/*.vcxproj.filters
+
+ **/*.o
+ **/*.lo
+ **/*.a
+ **/*.la
+ **/*.lai
+ **/*.so
+ **/*.dylib
+ **/*.jnilib
+ **/*.jar
+ **/*.class
+ **/*.stamp
+ **/*.Plo
+
+ .deps/**
+ .libs/**
+ Makefile
+ Makefile.in
+
+ .vscode/**
+ .idea/**
+
+ subprojects/.wraplock
+ **/.meson-subproject-wrap-hash.txt
+
+
+
+
diff --git a/MMCoreJ_wrap/subprojects/.gitignore b/MMCoreJ_wrap/subprojects/.gitignore
new file mode 100644
index 000000000..586e6acaf
--- /dev/null
+++ b/MMCoreJ_wrap/subprojects/.gitignore
@@ -0,0 +1,14 @@
+/packagecache/
+
+/mmcore/
+/mmdevice/
+
+# Subprojects installed by meson wrap
+/*-*/
+
+# Ignore *.wrap by default (may be auto-installed transitive dependencies)
+/*.wrap
+
+# Do not ignore wraps we provide
+!/mmcore.wrap
+!/mmdevice.wrap
diff --git a/MMCoreJ_wrap/subprojects/mmcore.wrap b/MMCoreJ_wrap/subprojects/mmcore.wrap
new file mode 100644
index 000000000..345cb664f
--- /dev/null
+++ b/MMCoreJ_wrap/subprojects/mmcore.wrap
@@ -0,0 +1,7 @@
+[wrap-git]
+url = https://github.com/micro-manager/mmcore.git
+revision = HEAD
+depth = 1
+
+[provide]
+dependency_names = mmcore
diff --git a/MMCoreJ_wrap/subprojects/mmdevice.wrap b/MMCoreJ_wrap/subprojects/mmdevice.wrap
new file mode 100644
index 000000000..d3d5465d3
--- /dev/null
+++ b/MMCoreJ_wrap/subprojects/mmdevice.wrap
@@ -0,0 +1,7 @@
+[wrap-git]
+url = https://github.com/micro-manager/mmdevice.git
+revision = HEAD
+depth = 1
+
+[provide]
+dependency_names = mmdevice