diff --git a/.github/workflows/deployment.yaml b/.github/workflows/deployment.yaml index 8cdf055..ca679e6 100644 --- a/.github/workflows/deployment.yaml +++ b/.github/workflows/deployment.yaml @@ -2,7 +2,7 @@ name: Deploy Conda and PyPI packages on: push: branches: - - master + - main jobs: deploy: @@ -10,7 +10,6 @@ jobs: strategy: matrix: os: [macos-latest, windows-latest, ubuntu-latest] - fail-fast: false # To not cancel other platforms when one fails name: Deploy on ${{ matrix.os }} env: JDK4PY_CONDA_CHANNEL: https://activeviam.jfrog.io/artifactory/jdk4py-conda-release @@ -19,17 +18,16 @@ jobs: - uses: actions/setup-python@v2 with: - python-version: "3.8" + python-version: 3.8 architecture: "x64" - - name: Set Java version - run: python scripts/set_versions.py + - name: Set environment variables + run: python scripts/set_environment.py - - uses: joschi/setup-jdk@v2 # We must use to the official action once https://github.com/actions/setup-java/pull/97 is closed + - uses: actions/setup-java@v2 with: - java-version: ${{ env.MAJOR_JAVA_VERSION }} - java-package: jdk # (jre, jdk, or jdk+fx) - defaults to jdk - architecture: x64 + distribution: adopt + java-version: ${{ env.JAVA_VERSION }} # Install Poetry - if: matrix.os == 'windows-latest' @@ -52,7 +50,7 @@ jobs: - name: Build wheel env: JDK4PY_BUILD_PLATFORM: ${{ matrix.os }} - run: poetry run python setup.py bdist_wheel + run: poetry run python setup.py bdist_wheel --build-number ${{ env.JDK4PY_BUILD_NUMBER }} - uses: actions/upload-artifact@v2 with: @@ -112,7 +110,8 @@ jobs: name: Deploy to PyPI [Windows] env: TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} - run: | # Install twine with conda as poetry is broken after installing conda + run: + | # Install twine with conda as poetry is broken after installing conda conda install twine twine upload dist/jdk4py-*.whl --username __token__ --password ${env:TWINE_PASSWORD} - if: matrix.os != 'windows-latest' diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 0dda93b..2cac0cd 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -2,7 +2,7 @@ name: Test on: pull_request: branches: - - master + - main jobs: test: @@ -10,8 +10,8 @@ jobs: strategy: matrix: os: [macos-latest, windows-latest, ubuntu-latest] - python: ["3.6", "3.7", "3.8"] - fail-fast: false # To not cancel other platforms when one fails + python: ["3.7", "3.8", "3.9"] + fail-fast: false name: Test on ${{ matrix.os }} with Python ${{ matrix.python }} steps: - uses: actions/checkout@v2 @@ -21,14 +21,13 @@ jobs: python-version: ${{ matrix.python }} architecture: "x64" - - name: Set Java version - run: python scripts/set_versions.py + - name: Set environment variables + run: python scripts/set_environment.py - - uses: joschi/setup-jdk@v2 # We must use to the official action once https://github.com/actions/setup-java/pull/97 is closed + - uses: actions/setup-java@v2 with: - java-version: ${{ env.MAJOR_JAVA_VERSION }} - java-package: jdk # (jre, jdk, or jdk+fx) - defaults to jdk - architecture: x64 + distribution: adopt + java-version: ${{ env.JAVA_VERSION }} # Install Poetry - if: matrix.os == 'windows-latest' @@ -44,12 +43,24 @@ jobs: - name: Build JDK run: poetry run python scripts/build_jdk.py + - name: Build wheel + env: + JDK4PY_BUILD_PLATFORM: ubuntu-latest + run: poetry run python setup.py bdist_wheel --build-number ${{ env.JDK4PY_BUILD_NUMBER }} + - name: Test run: poetry run pytest - package: # only on latest Ubuntu and Python + # No need to upload the package for each Python version + - if: matrix.python == '3.8' + uses: actions/upload-artifact@v2 + with: + name: jdk4py-${{ matrix.os }}.whl + path: dist/jdk4py-*.whl + + conda-package: runs-on: ubuntu-latest - name: Test packaging wheel and Conda + name: Test Conda packaging steps: - uses: actions/checkout@v2 @@ -58,14 +69,13 @@ jobs: python-version: 3.8 architecture: "x64" - - name: Set Java version - run: python scripts/set_versions.py + - name: Set environment variables + run: python scripts/set_environment.py - - uses: joschi/setup-jdk@v2 # We must use to the official action once https://github.com/actions/setup-java/pull/97 is closed + - uses: actions/setup-java@v2 with: - java-version: ${{ env.MAJOR_JAVA_VERSION }} - java-package: jdk - architecture: x64 + distribution: adopt + java-version: ${{ env.JAVA_VERSION }} - name: Install poetry run: pip3 install poetry @@ -76,15 +86,10 @@ jobs: - name: Build JDK run: poetry run python scripts/build_jdk.py - - name: Build wheel - env: - JDK4PY_BUILD_PLATFORM: ubuntu-latest - run: poetry run python setup.py bdist_wheel - - uses: conda-incubator/setup-miniconda@v2 with: channels: conda-forge conda-build-version: 3.20.0 - - name: Build Conda package on ubuntu and latest Python - run: conda build --output-folder dist . --no-anaconda-upload --debug + - name: Build Conda package + run: conda build --debug --no-anaconda-upload --output-folder dist . diff --git a/README.md b/README.md index 11b2b19..5178d96 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A packaged JDK for Python. ## Install -Java is made easy to install as a single pip library: +Java is made easy to install as a single Python package: ```bash pip install jdk4py @@ -23,8 +23,8 @@ conda install jdk4py jdk4py version contains 4 figures: - - The first 3 figures are the Java version - - The fourth is jdk4py specific: it starts at 0 for each Java version and then increases. +- The first 3 figures are the Java version +- The fourth is jdk4py specific: it starts at 0 for each Java version and then increases. ## API @@ -35,7 +35,7 @@ from jdk4py import execute_jar execute_jar("myJar.jar") ``` -Some JVM arguments can be provided, any additional argument will be passed to Popen: +Some JVM arguments can be provided, any additional argument will be passed to `Popen`: ```python execute_jar("myJar.jar", jvm_args=["-xmx=16G"], stdout=PIPE, stderr=PIPE) @@ -54,5 +54,5 @@ The Java version can be checked with: ```python >>> from jdk4py import JAVA_VERSION >>> JAVA_VERSION -'11.0.2' +'11.0.9' ``` diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index 6211400..7b2c856 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -8,9 +8,9 @@ source: path: ../ build: - number: 0 + binary_relocation: False # Without that, binaries are broken on macOS. include_recipe: False - binary_relocation: False # Without that binaries are broken on Mac OS. + number: {{ environ.get('JDK4PY_BUILD_NUMBER') }} requirements: build: @@ -33,9 +33,10 @@ about: home: https://github.com/atoti/jdk4py license: GNU General Public License v2.0 license_file: LICENSE - summary: 'Packaged JDK for Python.' + summary: Packaged JDK for Python. dev_url: https://github.com/atoti/jdk4py extra: recipe-maintainers: - fabiencelier + - tibdex diff --git a/jdk4py/__init__.py b/jdk4py/__init__.py index 33181fb..b8fd137 100644 --- a/jdk4py/__init__.py +++ b/jdk4py/__init__.py @@ -1,53 +1,45 @@ """JDK packaged for Python.""" -from typing import Union, List, Optional, Any from pathlib import Path from subprocess import Popen +from typing import Any, Collection, Optional, Union _PACKAGE_DIRECTORY = Path(__file__).parent JAVA_HOME = _PACKAGE_DIRECTORY.absolute() / "java-runtime" JAVA = JAVA_HOME / "bin" / "java" -JAVA_VERSION, LIB_VERSION, MAJOR_JAVA_VERSION = ( +JAVA_VERSION, LIB_VERSION = ( (_PACKAGE_DIRECTORY / filename).read_text().strip() - for filename in ("java_version.txt", "lib_version.txt", "major_java_version.txt") + for filename in ("java_version.txt", "lib_version.txt") ) __version__ = ".".join((JAVA_VERSION, LIB_VERSION)) def java( - java_args: List[str], + jvm_args: Collection[str], **popen_args: Any, ) -> Popen: """Run a Java process with the given arguments. Args: - jvm_args: The Java arguments, for instance ["HelloWorls.class", "-Xmx16G"] - popen_args: Additional arguments to pass to the Popen - - Returns: - The Popen process + jvm_args: The Java arguments, for instance: ``["HelloWorls.class", "-Xmx16G"]``. + popen_args: Additional arguments to pass to ``Popen``. """ - return Popen([str(JAVA), *java_args], **popen_args) + return Popen([str(JAVA), *jvm_args], **popen_args) def execute_jar( jar_path: Union[Path, str], - jvm_args: Optional[List[str]] = None, + jvm_args: Optional[Collection[str]] = None, **popen_args: Any, ) -> Popen: """Execute a JAR file. Args: - jar_path: The path to the JAR file - jvm_args: The JVM arguments, for instance ["-Xmx16G", "-Xms2G"] - popen_args: Additional arguments to pass to the Popen - - Returns: - The Popen process + jar_path: The path to the JAR file. + jvm_args: The JVM arguments, for instance ``["-Xmx16G", "-Xms2G"]``. + popen_args: Additional arguments to pass to ``Popen``. """ - if jvm_args is None: - jvm_args = [] - return java(["-jar", str(jar_path), *jvm_args], **popen_args) + return java(["-jar", str(jar_path), *(jvm_args or [])], **popen_args) diff --git a/jdk4py/build_number.txt b/jdk4py/build_number.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/jdk4py/build_number.txt @@ -0,0 +1 @@ +1 diff --git a/jdk4py/major_java_version.txt b/jdk4py/major_java_version.txt deleted file mode 100644 index b4de394..0000000 --- a/jdk4py/major_java_version.txt +++ /dev/null @@ -1 +0,0 @@ -11 diff --git a/scripts/build_jdk.py b/scripts/build_jdk.py index a381de0..f6e2092 100644 --- a/scripts/build_jdk.py +++ b/scripts/build_jdk.py @@ -1,10 +1,10 @@ -from subprocess import Popen, PIPE from pathlib import Path from shutil import rmtree +from subprocess import PIPE, Popen -PROJECT_FOLDER = Path(__file__).parent.parent -JAVA_PATH = PROJECT_FOLDER / "jdk4py" / "java-runtime" +_PROJECT_DIRECTORY = Path(__file__).parent.parent +_JAVA_PATH = _PROJECT_DIRECTORY / "jdk4py" / "java-runtime" _MODULES = [ "jdk.management.agent", @@ -13,12 +13,13 @@ "jdk.security.auth", "jdk.crypto.ec", "jdk.jfr", - "jdk.management.jfr" + "jdk.management.jfr", ] def build_java_executable_files(): - rmtree(JAVA_PATH, ignore_errors=True) + rmtree(_JAVA_PATH, ignore_errors=True) + process = Popen( [ "jlink", @@ -29,12 +30,13 @@ def build_java_executable_files(): "--add-modules", ",".join(_MODULES), "--output", - str(JAVA_PATH), + str(_JAVA_PATH), ], stdout=PIPE, stderr=PIPE, ) out, err = process.communicate() + if process.returncode == 0: print("Successfully built the Java executables.") return diff --git a/scripts/set_environment.py b/scripts/set_environment.py new file mode 100644 index 0000000..7c2aeb0 --- /dev/null +++ b/scripts/set_environment.py @@ -0,0 +1,28 @@ +from os import environ +from pathlib import Path +from typing import Mapping + +_PROJECT_DIRECTORY = Path(__file__).parent.parent +_SOURCE_DIRECTORY = _PROJECT_DIRECTORY / "jdk4py" + + +def set_env_variables_in_github_job(variables: Mapping[str, str]): + # See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable. + with open(environ["GITHUB_ENV"], "a") as environment_file: + for name, value in variables.items(): + environment_file.write(f"{name}={value}\n") + + +if __name__ == "__main__": + build_number, java_version, lib_version = ( + (_SOURCE_DIRECTORY / filename).read_text().strip() + for filename in ("build_number.txt", "java_version.txt", "lib_version.txt") + ) + + set_env_variables_in_github_job( + { + "JAVA_VERSION": java_version, + "JDK4PY_BUILD_NUMBER": build_number, + "JDK4PY_VERSION": ".".join((java_version, lib_version)), + } + ) diff --git a/scripts/set_versions.py b/scripts/set_versions.py deleted file mode 100644 index 1885a8a..0000000 --- a/scripts/set_versions.py +++ /dev/null @@ -1,36 +0,0 @@ -import os -from pathlib import Path - -_PROJECT_DIRECTORY = Path(__file__).parent.parent -_JDK4PY_DIRECTORY = _PROJECT_DIRECTORY / "jdk4py" -_MAJOR_JAVA_VERSION_FILENAME = "major_java_version.txt" -_JAVA_VERSION_FILENAME = "java_version.txt" -_LIB_VERSION_FILENAME = "lib_version.txt" - -def set_env_variable_in_github_job(name: str, value: str): - # See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable. - with open(os.environ["GITHUB_ENV"], "a") as environment_file: - environment_file.write(f"{name}={value}\n") - - -def set_java_version_env_variable_in_github_job(): - java_version = (_JDK4PY_DIRECTORY / _JAVA_VERSION_FILENAME).read_text().strip() - set_env_variable_in_github_job("JAVA_VERSION", java_version) - -def set_major_java_version_env_variable_in_github_job(): # To be removed after https://github.com/actions/setup-java/pull/97 is closed - java_version = (_JDK4PY_DIRECTORY / _MAJOR_JAVA_VERSION_FILENAME).read_text().strip() - set_env_variable_in_github_job("MAJOR_JAVA_VERSION", java_version) - -def set_jdk4py_version_env_variable_in_github_job(): - version = ".".join( - [ - (_JDK4PY_DIRECTORY / filename).read_text().strip() - for filename in (_JAVA_VERSION_FILENAME, _LIB_VERSION_FILENAME) - ] - ) - set_env_variable_in_github_job("JDK4PY_VERSION", version) - -if __name__ == "__main__": - set_java_version_env_variable_in_github_job() - set_jdk4py_version_env_variable_in_github_job() - set_major_java_version_env_variable_in_github_job() diff --git a/setup.py b/setup.py index 3943d99..7182db0 100644 --- a/setup.py +++ b/setup.py @@ -1,27 +1,29 @@ -import glob -import setuptools -import sys - -from os import path, environ +from os import environ from pathlib import Path +from sys import argv + +from setuptools import find_packages, setup _NAME = "jdk4py" +_PROJECT_DIRECTORY = Path(__file__).parent + +_SOURCE_DIRECTORY = _PROJECT_DIRECTORY / _NAME -_JAVA_FILES = [ - str(Path(f).relative_to(_NAME)) - for f in glob.glob(path.join(_NAME, "java-runtime", "**"), recursive=True) +_JAVA_RUNTIME_FOLDER = "java-runtime" + +_JAVA_FILES = [_JAVA_RUNTIME_FOLDER] + [ + str(path.relative_to(_SOURCE_DIRECTORY)) + for path in (_SOURCE_DIRECTORY / _JAVA_RUNTIME_FOLDER).rglob("*") ] -_PROJECT_DIR = Path(__file__).parent _JAVA_VERSION_FILENAME = "java_version.txt" -_JAVA_MAJOR_FILENAME = "major_java_version.txt" _LIB_VERSION_FILENAME = "lib_version.txt" _PLATFORM_ENV_VARIABLE = "JDK4PY_BUILD_PLATFORM" _VERSION = ".".join( [ - (_PROJECT_DIR / _NAME / filename).read_text().strip() + (_SOURCE_DIRECTORY / filename).read_text().strip() for filename in (_JAVA_VERSION_FILENAME, _LIB_VERSION_FILENAME) ] ) @@ -32,13 +34,13 @@ "windows-latest": "win_amd64", } -if "--plat-name" not in sys.argv and _PLATFORM_ENV_VARIABLE in environ: +if "--plat-name" not in argv and _PLATFORM_ENV_VARIABLE in environ: machine = environ[_PLATFORM_ENV_VARIABLE] platform = _PLATFORMS[machine] - sys.argv.append("--plat-name") - sys.argv.append(platform) + argv.append("--plat-name") + argv.append(platform) -setuptools.setup( +setup_args = dict( name=_NAME, version=_VERSION, author="atoti", @@ -47,13 +49,17 @@ long_description=Path("README.md").read_text(), long_description_content_type="text/markdown", url="https://github.com/atoti/jdk4py", - packages=setuptools.find_packages(exclude=["tests"]), - package_data={_NAME: [*_JAVA_FILES, _JAVA_VERSION_FILENAME, _JAVA_MAJOR_FILENAME, _LIB_VERSION_FILENAME]}, + packages=find_packages(exclude=["tests"]), + package_data={_NAME: [*_JAVA_FILES, _JAVA_VERSION_FILENAME, _LIB_VERSION_FILENAME]}, classifiers=[ - "Programming Language :: Python :: 3", - "Operating System :: OS Independent", "Development Status :: 4 - Beta", + "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", ], keywords=["jdk", "java", "jvm", "jre"], python_requires=">=3.7", ) + +if __name__ == "__main__": + setup(**setup_args) diff --git a/tests/test_jdk4py.py b/tests/test_jdk4py.py index 399e607..5eb9e98 100644 --- a/tests/test_jdk4py.py +++ b/tests/test_jdk4py.py @@ -1,32 +1,25 @@ from pathlib import Path from subprocess import PIPE +from jdk4py import JAVA, JAVA_HOME, JAVA_VERSION, execute_jar, java + _TESTS_DIRECTORY = Path(__file__).parent def test_java_version(): - from jdk4py import java, JAVA_VERSION - process = java(["--version"], stdout=PIPE, stderr=PIPE) out, err = process.communicate() + assert not err version = str(out).split("\n")[0].split(" ")[1] - assert version[:version.rindex(".")] == JAVA_VERSION - assert err == b"" + semver_version = version[: version.rindex(".")] + assert semver_version == JAVA_VERSION -def test_major_java_version(): - from jdk4py import MAJOR_JAVA_VERSION - - assert MAJOR_JAVA_VERSION == "11" def test_java_home(): - from jdk4py import JAVA, JAVA_HOME - assert JAVA == JAVA_HOME / "bin" / "java" def test_hello_world_jar(): - from jdk4py import execute_jar - # The JAR can be regenerated like that: # rm resources/*.class # rm resources/*.jar @@ -36,6 +29,6 @@ def test_hello_world_jar(): path = _TESTS_DIRECTORY / "resources" / "hello.jar" process = execute_jar(path.absolute(), stdout=PIPE, stderr=PIPE) out, err = process.communicate() - assert out == b"Hello, World\n" or out == b"Hello, World\r\n" - assert err == b"" + assert not err assert process.returncode == 0 + assert out == b"Hello, World\n" or out == b"Hello, World\r\n"