From 2746b48c5cbe2b8d2fe3684e1dadee8fad31b723 Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Thu, 29 Dec 2022 19:56:02 +0100 Subject: [PATCH] add bazel java POC; works with algorithms and graph --- WORKSPACE | 21 ++ bazel/BUILD.bazel | 6 + bazel/mkdir_wrapper.sh | 27 +++ bazel/swig_java.bzl | 184 ++++++++++++++++++ ortools/algorithms/java/BUILD.bazel | 18 ++ ortools/algorithms/samples/BUILD.bazel | 4 +- ortools/algorithms/samples/code_samples.bzl | 16 ++ ortools/base/BUILD.bazel | 10 + ortools/graph/java/BUILD.bazel | 19 ++ ortools/graph/samples/BUILD.bazel | 12 +- .../graph/samples/SimpleMaxFlowProgram.java | 1 + ortools/graph/samples/code_samples.bzl | 16 ++ ortools/init/BUILD.bazel | 1 + ortools/init/java/BUILD.bazel | 17 ++ ortools/java/com/google/ortools/BUILD.bazel | 42 ++++ ortools/java/com/google/ortools/Loader.java | 6 + ortools/sat/BUILD.bazel | 13 ++ ortools/sat/java/BUILD.bazel | 24 +++ ortools/util/java/BUILD.bazel | 7 + 19 files changed, 442 insertions(+), 2 deletions(-) create mode 100755 bazel/mkdir_wrapper.sh create mode 100644 bazel/swig_java.bzl create mode 100644 ortools/algorithms/java/BUILD.bazel create mode 100644 ortools/graph/java/BUILD.bazel create mode 100644 ortools/init/java/BUILD.bazel create mode 100644 ortools/java/com/google/ortools/BUILD.bazel create mode 100644 ortools/sat/java/BUILD.bazel create mode 100644 ortools/util/java/BUILD.bazel diff --git a/WORKSPACE b/WORKSPACE index 5cce890efa..bcf954ef69 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -151,3 +151,24 @@ new_git_repository( load("@pybind11_bazel//:python_configure.bzl", "python_configure") python_configure(name = "local_config_python", python_version = "3") + +new_git_repository( + name = "swig", + build_file = "//bazel:swig.BUILD", + tag = "v4.0.2", + remote = "https://github.com/swig/swig.git", +) + +# Java support +load("@rules_jvm_external//:defs.bzl", "maven_install") + +maven_install( + artifacts = [ + "net.java.dev.jna:jna:aar:5.8.0" + ], + repositories = [ + "https://repo1.maven.org/maven2", + ], +) + + diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel index 6c566aa2a7..65e13b761a 100644 --- a/bazel/BUILD.bazel +++ b/bazel/BUILD.bazel @@ -24,3 +24,9 @@ exports_files([ "archive_helper.bzl", "python_deps.txt", ]) + +sh_binary( + name = "mkdir_wrapper", + srcs = ["mkdir_wrapper.sh"], + visibility = ["//visibility:public"], +) diff --git a/bazel/mkdir_wrapper.sh b/bazel/mkdir_wrapper.sh new file mode 100755 index 0000000000..66584729b3 --- /dev/null +++ b/bazel/mkdir_wrapper.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Copyright 2021 The Cross-Media Measurement Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Wrapper which creates the directory specified as $1, then executing the +# command in $2 with the remaining arguments. +# +# This is to work around https://github.com/bazelbuild/bazel/issues/6393 + +readonly directory="$1" +readonly command="$2" +shift 2 + +mkdir -p "${directory}" +exec "${command}" "$@" \ No newline at end of file diff --git a/bazel/swig_java.bzl b/bazel/swig_java.bzl new file mode 100644 index 0000000000..09c6714e3f --- /dev/null +++ b/bazel/swig_java.bzl @@ -0,0 +1,184 @@ +# Copyright 2020 The Cross-Media Measurement Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Build definitions for SWIG Java.""" + +def _create_src_jar(ctx, java_runtime_info, input_dir, output_jar): + jar_args = ctx.actions.args() + jar_args.add("cf", output_jar) + jar_args.add_all([input_dir]) + + ctx.actions.run( + outputs = [output_jar], + inputs = [input_dir], + executable = "%s/bin/jar" % java_runtime_info.java_home, + tools = java_runtime_info.files, + arguments = [jar_args], + mnemonic = "SwigJar", + ) + +def _java_wrap_cc_impl(ctx): + name = ctx.attr.name + src = ctx.file.src + outfile = ctx.outputs.outfile + + header_sets = [] # depsets of Files + include_path_sets = [] # depsets of strings + + # Include headers from deps. + for target in ctx.attr.deps: + cc_context = target[CcInfo].compilation_context + header_sets.append(cc_context.headers) + include_path_sets.append(cc_context.includes) + + # Include workspace root in include path for when target is defined in an + # external workspace. + if target.label.workspace_root: + include_path_sets.append(depset([target.label.workspace_root])) + + java_files_dir = ctx.actions.declare_directory("java_files") + + swig_args = ctx.actions.args() + swig_args.add_all([java_files_dir], expand_directories = False) + swig_args.add("swig") + swig_args.add("-c++") + swig_args.add("-java") + swig_args.add("-package", ctx.attr.package) + swig_args.add_all("-outdir", [java_files_dir], expand_directories = False) + swig_args.add("-o", outfile) + if ctx.attr.module: + swig_args.add("-module", ctx.attr.module) + for include_path in depset(transitive = include_path_sets).to_list(): + swig_args.add("-I" + include_path) + swig_args.add(src.path) + + ctx.actions.run( + outputs = [outfile, java_files_dir], + inputs = depset([src] + ctx.files.swig_includes, transitive = header_sets), + executable = ctx.executable._mkdir_wrapper, + arguments = [swig_args], + mnemonic = "SwigCompile", + ) + + java_runtime = ctx.attr._jdk[java_common.JavaRuntimeInfo] + _create_src_jar(ctx, java_runtime, java_files_dir, ctx.outputs.srcjar) + +_java_wrap_cc = rule( + doc = """ +Wraps C++ in Java using Swig. + +It's expected that the `swig` binary exists in the host's path. +""", + implementation = _java_wrap_cc_impl, + attrs = { + "src": attr.label( + doc = "Single swig source file.", + allow_single_file = True, + mandatory = True, + ), + "deps": attr.label_list( + doc = "C++ dependencies.", + providers = [CcInfo], + ), + "package": attr.string( + doc = "Package for generated Java.", + mandatory = True, + ), + "module": attr.string(doc = "Optional Swig module name."), + "outfile": attr.output( + doc = "Generated C++ output file.", + mandatory = True, + ), + "srcjar": attr.output( + doc = "Generated Java source jar.", + mandatory = True, + ), + "_jdk": attr.label( + default = Label("@bazel_tools//tools/jdk:current_java_runtime"), + providers = [java_common.JavaRuntimeInfo], + ), + "_mkdir_wrapper": attr.label( + default = Label("//bazel:mkdir_wrapper"), + executable = True, + cfg = "exec", + ), + "swig_includes": attr.label_list( + allow_files = True, + ), + }, +) + +def ortools_java_wrap_cc( + name, + src, + package, + deps = [], + swig_includes = [], + module = None, + visibility = None, + **kwargs): + """Wraps C++ in Java using Swig. + + It's expected that the `swig` binary exists in the host's path. + + Args: + name: target name. + src: single .swig source file. + package: package of generated Java files. + deps: C++ deps. + module: optional name of Swig module. + + Generated targets: + {name}: java_library + lib{name}_cc: cc_library + """ + + wrapper_name = "_" + name + "_wrapper" + cc_name = name + "_cc" + outfile = name + ".cc" + srcjar = name + ".srcjar" + + _java_wrap_cc( + name = wrapper_name, + src = src, + package = package, + outfile = outfile, + srcjar = srcjar, + deps = deps, + module = module, + swig_includes = swig_includes, + visibility = ["//visibility:private"], + **kwargs + ) + + native.cc_library( + name = cc_name, + srcs = [outfile], + deps = deps + ["@bazel_tools//tools/jdk:jni"], + alwayslink = True, + visibility = visibility, + **kwargs + ) + + native.java_library( + name = name, + srcs = [srcjar], + runtime_deps = [ + "//ortools/java/com/google/ortools:libjniortools.so", + "//ortools/java/com/google/ortools:libjniortools.dylib", + "//ortools/java/com/google/ortools:jniortools.dll", + ], + visibility = visibility, + **kwargs + ) diff --git a/ortools/algorithms/java/BUILD.bazel b/ortools/algorithms/java/BUILD.bazel new file mode 100644 index 0000000000..4c7a714aff --- /dev/null +++ b/ortools/algorithms/java/BUILD.bazel @@ -0,0 +1,18 @@ +# Description: java wrapping of the C++ code at ../ + +load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") + +ortools_java_wrap_cc( + name = "knapsacksolver", + src = "knapsack_solver.i", + package = "com.google.ortools.algorithms", + module = "operations_research_algorithms", + swig_includes = [ + "//ortools/base:base_swig", + "//ortools/util/java:vector_swig", + ], + visibility = ["//visibility:public"], + deps = [ + "//ortools/algorithms:knapsack_solver_lib", + ], +) \ No newline at end of file diff --git a/ortools/algorithms/samples/BUILD.bazel b/ortools/algorithms/samples/BUILD.bazel index 43edf5bd29..75e932451d 100644 --- a/ortools/algorithms/samples/BUILD.bazel +++ b/ortools/algorithms/samples/BUILD.bazel @@ -11,8 +11,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -load(":code_samples.bzl", "code_sample_cc") +load(":code_samples.bzl", "code_sample_cc", "code_sample_java") code_sample_cc(name = "knapsack") code_sample_cc(name = "simple_knapsack_program") + +code_sample_java(name = "Knapsack") diff --git a/ortools/algorithms/samples/code_samples.bzl b/ortools/algorithms/samples/code_samples.bzl index 7fa30efc68..fac7a30066 100644 --- a/ortools/algorithms/samples/code_samples.bzl +++ b/ortools/algorithms/samples/code_samples.bzl @@ -31,3 +31,19 @@ def code_sample_cc(name): "//ortools/algorithms:knapsack_solver_lib", ], ) + +def code_sample_java(name): + native.java_binary( + name = name + "_java", + srcs = [name + ".java"], + main_class = "com.google.ortools.algorithms.samples." + name, + data = [ + "//ortools/java/com/google/ortools:libjniortools.so", + "//ortools/java/com/google/ortools:libjniortools.dylib", + "//ortools/java/com/google/ortools:jniortools.dll", + ], + deps = [ + "//ortools/algorithms/java:knapsacksolver", + "//ortools/java/com/google/ortools:Loader" + ], + ) diff --git a/ortools/base/BUILD.bazel b/ortools/base/BUILD.bazel index 5378e18008..e4b36f1aec 100644 --- a/ortools/base/BUILD.bazel +++ b/ortools/base/BUILD.bazel @@ -28,6 +28,16 @@ config_setting( constraint_values = ["@platforms//os:windows"], ) +filegroup( + name = "base_swig", + srcs = [ + "base.i", +# "//base:swig_includes", +# "//third_party/absl/base:swig_includes", + ], + visibility = ["//visibility:public"], +) + cc_library( name = "base", srcs = [ diff --git a/ortools/graph/java/BUILD.bazel b/ortools/graph/java/BUILD.bazel new file mode 100644 index 0000000000..0458320ab6 --- /dev/null +++ b/ortools/graph/java/BUILD.bazel @@ -0,0 +1,19 @@ +# Description: java wrapping of the C++ code at ../ + +load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") + +ortools_java_wrap_cc( + name = "graph", + src = "graph.i", + package = "com.google.ortools.graph", + module = "graph", + swig_includes = [ + "//ortools/base:base_swig", + ], + visibility = ["//visibility:public"], + deps = [ + "//ortools/graph:assignment", + "//ortools/graph:min_cost_flow", + "//ortools/graph:max_flow", + ], +) \ No newline at end of file diff --git a/ortools/graph/samples/BUILD.bazel b/ortools/graph/samples/BUILD.bazel index a0eadcbed0..55c934c486 100644 --- a/ortools/graph/samples/BUILD.bazel +++ b/ortools/graph/samples/BUILD.bazel @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -load(":code_samples.bzl", "code_sample_cc_py") +load(":code_samples.bzl", "code_sample_cc_py", "code_sample_java") code_sample_cc_py(name = "assignment_linear_sum_assignment") @@ -22,3 +22,13 @@ code_sample_cc_py(name = "balance_min_flow") code_sample_cc_py(name = "simple_max_flow_program") code_sample_cc_py(name = "simple_min_cost_flow_program") + +code_sample_java(name = "AssignmentLinearSumAssignment") + +code_sample_java(name = "AssignmentMinFlow") + +code_sample_java(name = "BalanceMinFlow") + +code_sample_java(name = "SimpleMaxFlowProgram") + +code_sample_java(name = "SimpleMinCostFlowProgram") diff --git a/ortools/graph/samples/SimpleMaxFlowProgram.java b/ortools/graph/samples/SimpleMaxFlowProgram.java index a34870f2b5..f19c22862a 100644 --- a/ortools/graph/samples/SimpleMaxFlowProgram.java +++ b/ortools/graph/samples/SimpleMaxFlowProgram.java @@ -21,6 +21,7 @@ import com.google.ortools.graph.MaxFlow; /** Minimal MaxFlow program. */ public final class SimpleMaxFlowProgram { public static void main(String[] args) throws Exception { + System.loadLibrary("jniortools"); Loader.loadNativeLibraries(); // [START solver] // Instantiate a SimpleMaxFlow solver. diff --git a/ortools/graph/samples/code_samples.bzl b/ortools/graph/samples/code_samples.bzl index 392b4fa240..4c46bf2b66 100644 --- a/ortools/graph/samples/code_samples.bzl +++ b/ortools/graph/samples/code_samples.bzl @@ -85,3 +85,19 @@ def code_sample_py(name): def code_sample_cc_py(name): code_sample_cc(name = name) code_sample_py(name = name) + +def code_sample_java(name): + native.java_binary( + name = name + "_java", + srcs = [name + ".java"], + main_class = "com.google.ortools.graph.samples." + name, + data = [ + "//ortools/java/com/google/ortools:libjniortools.so", + "//ortools/java/com/google/ortools:libjniortools.dylib", + "//ortools/java/com/google/ortools:jniortools.dll", + ], + deps = [ + "//ortools/graph/java:graph", + "//ortools/java/com/google/ortools:Loader" + ], + ) \ No newline at end of file diff --git a/ortools/init/BUILD.bazel b/ortools/init/BUILD.bazel index 5cb711c21f..11ae332531 100644 --- a/ortools/init/BUILD.bazel +++ b/ortools/init/BUILD.bazel @@ -18,6 +18,7 @@ cc_library( hdrs = ["init.h"], deps = [ "//ortools/base", + "//ortools/gurobi:environment", "//ortools/sat:cp_model_solver", "@com_google_absl//absl/flags:flag", ], diff --git a/ortools/init/java/BUILD.bazel b/ortools/init/java/BUILD.bazel new file mode 100644 index 0000000000..33964145bc --- /dev/null +++ b/ortools/init/java/BUILD.bazel @@ -0,0 +1,17 @@ +# Description: java wrapping of the C++ code at ../ + +load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") + +ortools_java_wrap_cc( + name = "init", + src = "init.i", + package = "com.google.ortools.init", + module = "operations_research_init", + swig_includes = [ + "//ortools/base:base_swig", + ], + visibility = ["//visibility:public"], + deps = [ + "//ortools/init", + ], +) \ No newline at end of file diff --git a/ortools/java/com/google/ortools/BUILD.bazel b/ortools/java/com/google/ortools/BUILD.bazel new file mode 100644 index 0000000000..d02ec225c6 --- /dev/null +++ b/ortools/java/com/google/ortools/BUILD.bazel @@ -0,0 +1,42 @@ +# Utilities to load native libraries in java or-tools. +java_library( + name = "Loader", + srcs = ["Loader.java"], + visibility = ["//visibility:public"], + deps = [ + "@maven//:net_java_dev_jna_jna", + ], +) + +cc_binary( + name = "libjniortools.so", + deps = [ + "//ortools/algorithms/java:knapsacksolver_cc", + "//ortools/graph/java:graph_cc", + "//ortools/init/java:init_cc", + ], + linkshared = True, + visibility = ["//visibility:public"], +) + +cc_binary( + name = "libjniortools.dylib", + deps = [ + "//ortools/algorithms/java:knapsacksolver_cc", + "//ortools/graph/java:graph_cc", + "//ortools/init/java:init_cc", + ], + linkshared = True, + visibility = ["//visibility:public"], +) + +cc_binary( + name = "jniortools.dll", + deps = [ + "//ortools/algorithms/java:knapsacksolver_cc", + "//ortools/graph/java:graph_cc", + "//ortools/init/java:init_cc", + ], + linkshared = True, + visibility = ["//visibility:public"], +) diff --git a/ortools/java/com/google/ortools/Loader.java b/ortools/java/com/google/ortools/Loader.java index 0041b0c61e..9f745feaf6 100644 --- a/ortools/java/com/google/ortools/Loader.java +++ b/ortools/java/com/google/ortools/Loader.java @@ -103,6 +103,12 @@ public class Loader { public static synchronized void loadNativeLibraries() { if (!loaded) { + try { + System.loadLibrary("jniortools"); + return; + } catch (UnsatisfiedLinkError exception) { + // Do nothing. + } try { URI resourceURI = getNativeResourceURI(); Path tempPath = unpackNativeResources(resourceURI); diff --git a/ortools/sat/BUILD.bazel b/ortools/sat/BUILD.bazel index 2155d173cb..cdb9172a87 100644 --- a/ortools/sat/BUILD.bazel +++ b/ortools/sat/BUILD.bazel @@ -16,6 +16,7 @@ load("@rules_cc//cc:defs.bzl", "cc_proto_library") load("@com_google_protobuf//:protobuf.bzl", "py_proto_library") +load("@rules_java//java:defs.bzl", "java_proto_library") package( default_visibility = ["//visibility:public"], @@ -37,6 +38,12 @@ py_proto_library( visibility = ["//visibility:public"], ) +java_proto_library( + name = "sat_parameters_java_proto", + visibility = ["//visibility:public"], + deps = [":sat_parameters_proto"], +) + cc_proto_library( name = "boolean_problem_cc_proto", deps = [":boolean_problem_proto"], @@ -63,6 +70,12 @@ py_proto_library( visibility = ["//visibility:public"], ) +java_proto_library( + name = "cp_model_java_proto", + visibility = ["//visibility:public"], + deps = [":cp_model_proto"], +) + cc_library( name = "cp_model", srcs = ["cp_model.cc"], diff --git a/ortools/sat/java/BUILD.bazel b/ortools/sat/java/BUILD.bazel new file mode 100644 index 0000000000..f97a128c00 --- /dev/null +++ b/ortools/sat/java/BUILD.bazel @@ -0,0 +1,24 @@ +# Description: java wrapping of the code in ../ + +# ortools_java_wrap_cc( +# name = "sat", +# srcs = ["sat.i"], +# # nlegacy_override_module = 1, +# package = "com.google.ortools.sat", +# swig_includes = [ +# "//ortools/base/base_i", +# "//ortools/util/java:sorted_interval_list.swig", +# "//ortools/util/java:vector.swig", +# ], +# visibility = ["//visibility:public"], +# deps = [ +# "//java/com/google/wrappers", +# "//net/proto/swig/java:protocol_message_swig", +# "//util/java:jni_helper", +# "//ortools/sat:cp_model_java_proto", +# "//ortools/sat:sat_parameters_java_proto", +# "//ortools/sat:swig_helper", +# "//ortools/util:sorted_interval_list", +# "//ortools/util/java:sorted_interval_list_swig", +# ], +# ) \ No newline at end of file diff --git a/ortools/util/java/BUILD.bazel b/ortools/util/java/BUILD.bazel new file mode 100644 index 0000000000..c8b755350d --- /dev/null +++ b/ortools/util/java/BUILD.bazel @@ -0,0 +1,7 @@ +filegroup( + name = "vector_swig", + srcs = [ + "vector.i", + ], + visibility = ["//visibility:public"], +) \ No newline at end of file