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