-
Notifications
You must be signed in to change notification settings - Fork 599
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Java binding using the existing SWIG infrastructure #578
base: master
Are you sure you want to change the base?
Conversation
Does the binding make sense to you as is? Or do you think I should use the SWIG |
It would be nice to make the naming somewhat more idiomatic for Java. On the other hand, it adds maintenance overhead if we expand the API in the future. |
In the new revision (commit 32a4a64), I have fixed all the actual bugs you have found. What is still unresolved is:
|
this was not a strong requirement, just a remark to make the code simpler, if you feel java < 1.6 will be useful you can leave the code as is |
It turns out that SWIG supports global renaming directives that can do most of the work, as in: // use Java naming conventions
%rename("%(camelcase)s", match="enum") "";
%rename("%(camelcase)s", match="class") "";
%rename("%(lowercamelcase)s", %$isfunction) ""; so I have implemented just that in the latest revision, commit bb97515. I needed only a handful other changes (with 2 added |
4ad8577
to
7f09d01
Compare
The latest revision (commit 7f09d01) should hopefully also fix the CI failure on Windows. (The one on macOS was due to the Guile issue that I fixed in the previous revision.) The problem was that the classpath was using the wrong separator between paths on Windows. (It was hardcoded to |
Hmmmph, Windows is still unhappy, probably because CMake is munging the Also, I noticed strict-aliasing warnings in the GNU/Linux CI run, |
This and …
… this should both be fixed in the latest revision (commit 4cab95d). |
The latest revision (commit 8c481a9) now simplifies the syntax of the DoubleVector x0 = new DoubleVector(1.234, 5.678); instead of: DoubleVector x0 = new DoubleVector(new double[]{1.234, 5.678}); This is done by using the varargs syntax This raises the minimum Java version for the Java binding to 1.5. (The test program still requires 1.8.) For some perspective, Java 1.5 was released in 2004 and reached EOL in 2009, Java 1.4 (which is no longer supported) was released in 2002 and reached EOL in 2008. SWIG is unfortunately still unconditionally using the legacy Java 1.4 syntax in 2024 and requiring a manual replacement in the output file to fix this. (The EDIT: And actually, the binding already required 1.5 anyway because it uses Java |
The Java tests are still failing on Windows, and I do not know why it (still) does not find the class in the classpath there. I do not have a Windows computer to test with locally. |
There are actually several issues with the Windows CI build, e.g., it explicitly installs NumPy with |
I fixed the numpy detection on windows, but dlls are still manually copied to the swig binary dir, so if the target dir changes you will have to modify the destination |
The latest revision (commit fe30d98) adds the missing documentation that was requested in the review. It also fixes the exception handling to work as in Python (noticed while adapting the documentation): Runtime exceptions thrown by the Java callbacks now get rethrown as is (with the original class, message, and stack trace) rather than as a |
seems there is a conflict with master (I fixed the numpy detection on the CI and improved it on the cmake side) |
3b56a1e
to
427ca21
Compare
What I still do not fully understand is the reason for the Windows CI failure. As far as I can tell, the first error is this:
which means that the The rule has a |
57858e8
to
f4a1e4e
Compare
The latest revision (commit f4a1e4e) attempts to fix this, but I cannot test it on Windows, so I can only hope for the best. What this does is add a dependency on To be clear, this is all I changed, ignoring the 2 slightly botched intermediate revisions: |
Hmmmph, the added |
So I have now made that dependency a regular Using it as a |
The latest revision (commit 819ee46) fixes the build with CMake < 3.24, e.g, I tried it in a CentOS 7 chroot with the |
8cd6dad
to
b1401bb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great!
just a rebase to master and it should pass CI
CMakeLists.txt: Add NLOPT_JAVA option. If set, check for C++, JNI, and Java (>= 1.5, because of varargs and enums). src/swig/nlopt-java.i: New SWIG input file (not standalone, but to be included into nlopt.i). Contains the Java-specific declarations. src/swig/nlopt.i: Add syntax highlighting declaration for KatePart-based editors. Call the module NLopt instead of nlopt for SWIGJAVA, because in Java, this is the name of the globals class (not the package, which is set separately on the SWIG command line), so it should be in CamelCase. Instantiate vector<double> as DoubleVector instead of nlopt_doublevector for SWIGJAVA, because everything is in an nlopt package in Java, so the nlopt_ part is redundant, and because it should be in CamelCase. Ignore nlopt_get_initial_step because at least for Java, SWIG wants to generate a binding for it, using SWIGTYPE_p_* types that cannot be practically used. (It is in-place just like nlopt::opt::get_initial_step, which is already ignored.) Rename nlopt::opt::get_initial_step_ to the lowerCamelCase getInitialStep instead of get_initial_step. For SWIGJAVA, %include nlopt-java.i. src/swig/CMakeLists.txt: Set UseSWIG_MODULE_VERSION to 2 so that UseSWIG cleans up old generated source files before running SWIG, useful for Java. (In particular, it prevents obsolete .java files from old .i file versions, which might not even compile anymore, from being included in the Java compilation and the JAR.) Update the path handling for the Python and Guile bindings accordingly. If JNI, Java, and SWIG were found, generate the Java binding with SWIG, and compile the JNI library with the C++ compiler and the Java JAR with the Java compiler. For the Guile binding, set and unset CMAKE_SWIG_FLAGS instead of using set_source_files_properties on nlopt.i, because nlopt.i is shared for all bindings, so setting the property on the source file does not help preventing breaking other target language bindings (such as Java). Also replace the deprecated swig_link_libraries with target_link_libraries. src/swig/glob_java.cmake: New helper script whose main purpose is to invoke the CMake file(GLOB ...) command at the correct stage of the build process, after the SWIG run, not when CMake is run on the main CMakeLists.txt, because the latter happens before anything at all is built. The script is invoked through cmake -P by an add_custom_command in CMakeLists.txt, whose dependencies order it to the correct spot of the build. This is the only portable way to automatically determine which *.java files SWIG has generated from the *.i files. The result is written to java_sources.txt, which is dynamically read by the add_jar command thanks to the @ indirection. In addition, it also does a replacement in DoubleVector.java, changing double[] initialElements to double... initialElements (introduced in Java 1.5), which needs to happen at the same stage of the build, so that initial elements can be passed directly to new DoubleVector without extra new double[]{ boilerplate. test/t_java.java: New test. Java port of t_tutorial.cxx/t_python.py. test/CMakeLists.txt: If JNI, Java >= 1.8, and SWIG were found, run the t_java test program with the algorithms 23 (MMA), 24 (COBYLA), 30 (augmented Lagrangian), and 39 (SLSQP). All 4 tests pass. (Java < 1.8 should be supported by the binding itself, but not by the test.) Update the PYINSTALLCHECK_ENVIRONMENT settings for the src/swig/CMakeLists.txt changes, so that the Python tests keep passing. Update the GUILE_LOAD_PATH settings for the src/swig/CMakeLists.txt changes, so that the Guile tests keep passing. This code is mostly unrelated to the old unmaintained nlopt4j binding (https://github.com/dibyendumajumdar/nlopt4j), which used handwritten JNI code. Instead, it reuses the existing SWIG binding infrastructure, adding Java as a supported language to that. Only the code in the func_java and mfunc_java wrappers is loosely inspired by the nlopt4j code. The rest of the code in this binding relies mostly on SWIG features and uses very little handwritten JNI code, so nlopt4j was not useful as a reference for it. This binding is also backwards-incompatible with nlopt4j due to differing naming conventions. Note that this binding maps the C++ class and method names identically to Java. As a result, it does not use idiomatic Java case conventions, but uses snake_case for both class and method names instead of the usual UpperCamelCase for class names and lowerCamelCase for method names in Java. The C++ namespace nlopt is mapped to the Java package nlopt. doc/docs/NLopt_Java_Reference.md: New file, based on NLopt_Python_Reference.md and NLopt_Guile_Reference.md, adapted to the Java syntax. doc/docs/NLopt_Tutorial.md: Add example in Java. doc/docs/index.md: Mention Java, linking to NLopt_Java_Reference.md. README.md: Add Java. (The link will become valid after the commit makes it into master and the documentation on readthedocs is regenerated.)
Rebased now. |
CMakeLists.txt: Add NLOPT_JAVA option. If set, check for C++, JNI, and Java.
src/swig/nlopt-java.i: New SWIG input file (not standalone, but to be included into nlopt.i). Contains the Java-specific declarations.
src/swig/nlopt.i: Add syntax highlighting declaration for KatePart-based editors. Instantiate vector as doublevector instead of nlopt_doublevector for SWIGJAVA, because everything is in an nlopt package in Java, so the nlopt_ part is redundant. Ignore nlopt_get_initial_step because at least for Java, SWIG wants to generate a binding for it, using SWIGTYPE_p_* types that cannot be practically used. (It is in-place just like nlopt::opt::get_initial_step, which is already ignored.) For SWIGJAVA, %include nlopt-java.i.
src/swig/CMakeLists.txt: Set UseSWIG_MODULE_VERSION to 2 so that UseSWIG cleans up old generated source files before running SWIG, useful for Java. (In particular, it prevents obsolete .java files from old .i file versions, which might not even compile anymore, from being included in the Java compilation and the JAR.) If JNI, Java, and SWIG were found, generate the Java binding with SWIG, and compile the JNI library with the C++ compiler and the Java JAR with the Java compiler.
src/swig/glob_java.cmake: New helper script whose only purpose is to invoke the CMake file(GLOB ...) command at the correct stage of the build process, after the SWIG run, not when CMake is run on the main CMakeLists.txt, because the latter happens before anything at all is built. The script is invoked through cmake -P by an add_custom_command in CMakeLists.txt, whose dependencies order it to the correct spot of the build. This is the only portable way to automatically determine which *.java files SWIG has generated from the *.i files. The result is written to java_sources.txt, which is dynamically read by the add_jar command thanks to the @ indirection.
test/t_java.java: New test. Java port of t_tutorial.cxx/t_python.py.
test/CMakeLists.txt: If JNI, Java >= 1.8, and SWIG were found, run the t_java test program with the algorithms 23 (MMA), 24 (COBYLA), 30 (augmented Lagrangian), and 39 (SLSQP). All 4 tests pass. (Java < 1.8 should be supported by the binding itself, but not by the test.)
This code is mostly unrelated to the old unmaintained nlopt4j binding (https://github.com/dibyendumajumdar/nlopt4j), which used handwritten JNI code. Instead, it reuses the existing SWIG binding infrastructure, adding Java as a supported language to that. Only the code in the func_java and mfunc_java wrappers is loosely inspired by the nlopt4j code. The rest of the code in this binding relies mostly on SWIG features and uses very little handwritten JNI code, so nlopt4j was not useful as a reference for it. This binding is also backwards-incompatible with nlopt4j due to differing naming conventions.
Note that this binding maps the C++ class and method names identically to Java. As a result, it does not use idiomatic Java case conventions, but uses snake_case for both class and method names instead of the usual UpperCamelCase for class names and lowerCamelCase for method names in Java. The C++ namespace nlopt is mapped to the Java package nlopt.