dependency: Add JDK system dependency

The JDK system dependency is important for detecting JDK include paths
that may be useful when developing a JNI interface.
pull/8645/head
Tristan Partin 4 years ago committed by Dylan Baker
parent 50cf8bcaba
commit 4c13aa30a1
  1. 1
      docs/markdown/Machine-files.md
  2. 16
      docs/markdown/snippets/jdk-system-dependency.md
  3. 5
      mesonbuild/dependencies/__init__.py
  4. 56
      mesonbuild/dependencies/dev.py
  5. 12
      mesonbuild/envconfig.py
  6. 5
      mesonbuild/environment.py
  7. 9
      test cases/java/9 jdk/lib/com_mesonbuild_JdkTest.c
  8. 21
      test cases/java/9 jdk/lib/com_mesonbuild_JdkTest.h
  9. 14
      test cases/java/9 jdk/lib/meson.build
  10. 11
      test cases/java/9 jdk/lib/native.c
  11. 18
      test cases/java/9 jdk/meson.build
  12. 15
      test cases/java/9 jdk/src/com/mesonbuild/JdkTest.java
  13. 17
      test cases/java/9 jdk/src/meson.build

@ -236,6 +236,7 @@ section.
`exe_wrapper` specified in `[binaries]` to run generated executables in CMake
subprojects. This setting has no effect if the `exe_wrapper` was not specified.
The default value is `true`. (*new in 0.56.0*)
- `java_home` is an absolute path pointing to the root of a Java installation.
### CMake variables

@ -0,0 +1,16 @@
## JDK System Dependency
When building projects such as those interacting with the JNI, you need access
to a few header files located in a Java installation. This system dependency
will add the correct include paths to your target. It assumes that either
`JAVA_HOME` will be set to a valid Java installation, or the default `javac` on
your system is a located in the `bin` directory of a Java installation. Note:
symlinks are resolved.
```meson
jdk = dependency('jdk', version : '>=1.8')
```
Currently this system dependency only works on `linux`, `win32`, and `darwin`.
This can easily be extended given the correct information about your compiler
and platform in an issue.

@ -21,7 +21,9 @@ from .base import ( # noqa: F401
InternalDependency, PkgConfigDependency, CMakeDependency,
find_external_dependency, get_dep_identifier, packages,
_packages_accept_language, DependencyFactory)
from .dev import ValgrindDependency, gmock_factory, gtest_factory, llvm_factory, zlib_factory
from .dev import (
ValgrindDependency, JDKSystemDependency, gmock_factory, gtest_factory,
llvm_factory, zlib_factory)
from .coarrays import coarray_factory
from .mpi import mpi_factory
from .scalapack import scalapack_factory
@ -196,6 +198,7 @@ packages.update({
'llvm': llvm_factory,
'valgrind': ValgrindDependency,
'zlib': zlib_factory,
'jdk': JDKSystemDependency,
'boost': BoostDependency,
'cuda': CudaDependency,

@ -18,6 +18,8 @@
import glob
import os
import re
import pathlib
import shutil
import typing as T
from .. import mesonlib, mlog
@ -32,6 +34,7 @@ from ..compilers.c import AppleClangCCompiler
from ..compilers.cpp import AppleClangCPPCompiler
if T.TYPE_CHECKING:
from ..envconfig import MachineInfo
from .. environment import Environment
@ -505,6 +508,59 @@ class ZlibSystemDependency(ExternalDependency):
return [DependencyMethods.SYSTEM]
class JDKSystemDependency(ExternalDependency):
def __init__(self, environment: 'Environment', kwargs: T.Dict[str, T.Any]):
super().__init__('jdk', environment, kwargs)
m = self.env.machines[self.for_machine]
if 'java' not in environment.coredata.compilers[self.for_machine]:
environment.detect_compiler_for('java', self.for_machine)
self.javac = environment.coredata.compilers[self.for_machine]['java']
self.version = self.javac.version
if 'version' in kwargs and not version_compare(self.version, kwargs['version']):
mlog.error(f'Incorrect JDK version found ({self.version}), wanted {kwargs["version"]}')
self.is_found = False
return
self.java_home = environment.properties[self.for_machine].get_java_home()
if not self.java_home:
self.java_home = pathlib.Path(shutil.which(self.javac.exelist[0])).resolve().parents[1]
platform_include_dir = self.__machine_info_to_platform_include_dir(m)
if platform_include_dir is None:
mlog.error("Could not find a JDK platform include directory for your OS, please open an issue or provide a pull request.")
self.is_found = False
return
java_home_include = self.java_home / 'include'
self.compile_args.append(f'-I{java_home_include}')
self.compile_args.append(f'-I{java_home_include / platform_include_dir}')
self.is_found = True
@staticmethod
def get_methods() -> T.List[DependencyMethods]:
return [DependencyMethods.SYSTEM]
@staticmethod
def __machine_info_to_platform_include_dir(m: 'MachineInfo') -> T.Optional[str]:
"""Translates the machine information to the platform-dependent include directory
When inspecting a JDK release tarball or $JAVA_HOME, inside the `include/` directory is a
platform dependent folder that must be on the target's include path in addition to the
parent `include/` directory.
"""
if m.is_linux():
return 'linux'
elif m.is_windows():
return 'win32'
elif m.is_darwin():
return 'darwin'
return None
llvm_factory = DependencyFactory(
'LLVM',
[DependencyMethods.CMAKE, DependencyMethods.CONFIG_TOOL],

@ -136,9 +136,9 @@ class CMakeSkipCompilerTest(Enum):
class Properties:
def __init__(
self,
properties: T.Optional[T.Dict[str, T.Union[str, bool, int, T.List[str]]]] = None,
properties: T.Optional[T.Dict[str, T.Optional[T.Union[str, bool, int, T.List[str]]]]] = None,
):
self.properties = properties or {} # type: T.Dict[str, T.Union[str, bool, int, T.List[str]]]
self.properties = properties or {} # type: T.Dict[str, T.Optional[T.Union[str, bool, int, T.List[str]]]]
def has_stdlib(self, language: str) -> bool:
return language + '_stdlib' in self.properties
@ -210,13 +210,17 @@ class Properties:
assert isinstance(res, bool)
return res
def get_java_home(self) -> T.Optional[Path]:
value = T.cast(T.Optional[str], self.properties.get('java_home'))
return Path(value) if value else None
def __eq__(self, other: object) -> bool:
if isinstance(other, type(self)):
return self.properties == other.properties
return NotImplemented
# TODO consider removing so Properties is less freeform
def __getitem__(self, key: str) -> T.Union[str, bool, int, T.List[str]]:
def __getitem__(self, key: str) -> T.Optional[T.Union[str, bool, int, T.List[str]]]:
return self.properties[key]
# TODO consider removing so Properties is less freeform
@ -224,7 +228,7 @@ class Properties:
return item in self.properties
# TODO consider removing, for same reasons as above
def get(self, key: str, default: T.Union[str, bool, int, T.List[str]] = None) -> T.Union[str, bool, int, T.List[str]]:
def get(self, key: str, default: T.Optional[T.Union[str, bool, int, T.List[str]]] = None) -> T.Optional[T.Union[str, bool, int, T.List[str]]]:
return self.properties.get(key, default)
class MachineInfo:

@ -881,12 +881,13 @@ class Environment:
self.binaries[for_machine].binaries.setdefault(name, mesonlib.split_args(p_env))
def _set_default_properties_from_env(self) -> None:
"""Properties which can alkso be set from the environment."""
"""Properties which can also be set from the environment."""
# name, evar, split
opts: T.List[T.Tuple[str, T.List[str], bool]] = [
('boost_includedir', ['BOOST_INCLUDEDIR'], False),
('boost_librarydir', ['BOOST_LIBRARYDIR'], False),
('boost_root', ['BOOST_ROOT', 'BOOSTROOT'], True),
('java_home', ['JAVA_HOME'], False),
]
for (name, evars, split), for_machine in itertools.product(opts, MachineChoice):
@ -944,7 +945,7 @@ class Environment:
def is_library(self, fname):
return is_library(fname)
def lookup_binary_entry(self, for_machine: MachineChoice, name: str) -> T.List[str]:
def lookup_binary_entry(self, for_machine: MachineChoice, name: str) -> T.Optional[T.List[str]]:
return self.binaries[for_machine].lookup_entry(name)
@staticmethod

@ -0,0 +1,9 @@
#include <jni.h>
#include "com_mesonbuild_JdkTest.h"
JNIEXPORT jint JNICALL Java_com_mesonbuild_JdkTest_jdk_1test
(JNIEnv *env, jclass clazz)
{
return (jint)0xdeadbeef;
}

@ -0,0 +1,21 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_mesonbuild_JdkTest */
#ifndef _Included_com_mesonbuild_JdkTest
#define _Included_com_mesonbuild_JdkTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_mesonbuild_JdkTest
* Method: jdk_test
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_mesonbuild_JdkTest_jdk_1test
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,14 @@
sources = files(
'native.c',
'com_mesonbuild_JdkTest.c',
)
jdkjava = shared_module(
'jdkjava',
sources,
dependencies : [jdk],
)
jdkjava_dep = declare_dependency(
link_with : jdkjava,
)

@ -0,0 +1,11 @@
#include <jni.h>
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved)
{
return JNI_VERSION_1_8;
}
JNIEXPORT void JNICALL
JNI_OnUnload(JavaVM *vm, void *reserved)
{}

@ -0,0 +1,18 @@
project('jdkjava', ['c', 'java'])
if build_machine.system() == 'cygwin'
error('MESON_SKIP_TEST: cygwin test failures')
endif
if build_machine.system() == 'windows' and build_machine.cpu_family() == 'x86'
error('MESON_SKIP_TEST: failing builds on 32bit Windows because a 32bit JDK isn not available in the Azure Pipelines Windows images')
endif
fs = import('fs')
java = find_program('java')
jdk = dependency('jdk', version : '>=1.8')
subdir('lib')
subdir('src')

@ -0,0 +1,15 @@
package com.mesonbuild;
public final class JdkTest {
private static native int jdk_test();
public static void main(String[] args) {
if (jdk_test() != 0xdeadbeef) {
throw new RuntimeException("jdk_test() did not return 0");
}
}
static {
System.loadLibrary("jdkjava");
}
}

@ -0,0 +1,17 @@
jdkjar = jar(
'jdkjar',
'com' / 'mesonbuild' / 'JdkTest.java',
main_class : 'com.mesonbuild.JdkTest',
)
test(
'jdktest',
java,
args: [
'-Djava.library.path=@0@'.format(fs.parent(jdkjava.full_path())),
'-jar',
jdkjar,
],
protocol : 'exitcode',
depends : [jdkjava],
)
Loading…
Cancel
Save