Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jamilraichouni committed Mar 18, 2024
1 parent c0cd7f5 commit 58f822c
Show file tree
Hide file tree
Showing 2 changed files with 229 additions and 5 deletions.
227 changes: 223 additions & 4 deletions eclipse_extension_builders/__main__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,46 @@
# Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0
"""Main entry point into eclipse_extension_builders."""
"""Tools to build, pack, deploy Eclipse extensions.
The present CLI needs the command line tools `jar` and `mvn` to be installed
and accessible via the user's PATH.
`jar` is part of the Java Development Kit (JDK) and is used to create the jar
file of the Eclipse extension.
`mvn` is the Maven build tool and is used to analyse the dependencies listed
in the `pom.xml` file to build the `.classpath` file.
"""

import pathlib
import shutil
import subprocess
import sys
import tempfile

import click
import lxml.builder
import lxml.etree

import eclipse_extension_builders

E = lxml.builder.ElementMaker()
MANIFEST_PATH = "META-INF/MANIFEST.MF"
PLUGIN_XML_PATH = "plugin.xml"
PATH_BLACKLIST = (
".pde.",
"/jre/",
"/org.eclipse.equinox.p2.repository/",
"ant",
"artifacts.jar",
"content.jar",
"ease",
"egit",
"jdt.debug",
"jgit",
"pydev",
)


@click.command()
@click.group()
@click.version_option(
version=eclipse_extension_builders.__version__,
prog_name="eclipse-extension-builders",
Expand All @@ -17,5 +50,191 @@ def main() -> None:
"""Console script for eclipse_extension_builders."""


def _paths() -> tuple[pathlib.Path, pathlib.Path]:
"""Return the path to the jar file."""
classpath_root = _read_xml_file(".classpath")
output = classpath_root.xpath('//classpathentry[@kind="output"]')
if not output:
click.echo(
"Output directory not found. Missing `classpathentry` with kind "
"`output` in `.classpath` file."
)
sys.exit(1)
output_path = pathlib.Path(output[0].get("path"))
if not list(output_path.iterdir()):
click.echo(f"Output directory `{output_path}` is empty.")
sys.exit(1)
pom = _read_xml_file("pom.xml")
# get the namespace from the root element
ns = {"m": "http://maven.apache.org/POM/4.0.0"} # Register the namespace
artifact_id = pom.xpath("//m:artifactId", namespaces=ns)
version = pom.xpath("//m:version", namespaces=ns)
artifact_id = artifact_id[0].text if artifact_id else "unknown"
version = version[0].text if version else "unknown"
jar_name = f"{artifact_id}-{version}.jar"
jar_path = pathlib.Path("target") / jar_name
return output_path, jar_path


def _read_xml_file(path: str) -> lxml.etree.Element:
"""Read the classpath file."""
if not pathlib.Path(path).exists():
click.echo(f"`File {path}` not found.")
sys.exit(1)
tree = lxml.etree.parse(path)
return tree


@main.command()
@click.argument("target_path", type=click.Path(exists=True, dir_okay=True))
def build_classpath(target_path: pathlib.Path) -> None:
"""Build `.classpath` file.
Parameters
----------
target_path : pathlib.Path
The installation directory of an Eclipse/ Capella application
that will be references as target platform to build the classpath.
"""
target_path = pathlib.Path(target_path)
if not target_path.is_dir():
click.echo(
f"Target platform installation dir `{target_path}` not found."
)
sys.exit(1)
classpaths = [
E.classpathentry(kind="src", path="src", including="**/*.java"),
E.classpathentry(kind="output", path="bin"),
E.classpathentry(
kind="con",
path=(
"org.eclipse.jdt.launching.JRE_CONTAINER/"
"org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/"
"JavaSE-17"
),
),
]
with tempfile.NamedTemporaryFile(mode="w", delete=False) as w:
mvn_cmd = [
"mvn",
"-q",
"dependency:build-classpath",
f"-Dmdep.outputFile={w.name}",
]
# Run command and wait:
subprocess.run(mvn_cmd, check=True)
with open(w.name, "r", encoding="utf-8") as tmp:
# Replace all colons with newlines and sort the lines:
classpath_3rdparty = tmp.read().replace(":", "\n").splitlines()
classpath_3rdparty.sort()
for path in classpath_3rdparty:
classpaths.append(E.classpathentry(kind="lib", path=path))
# get .jar files from target dir
# Recursively find all src JARs:
sources: set[pathlib.Path] = set(target_path.glob("**/*.source_*.jar"))
# Recursively find all lib JARs:
libs = list(set(target_path.glob("**/*.jar")) - sources)
srcs = list(sources)
target_classpaths = []
for src in srcs:
skip = False
for pattern in PATH_BLACKLIST:
skip = pattern in str(src)
if skip:
break
if skip:
continue
# get parent dir
parent = src.parent
# get base name
base = src.name
lib = parent / base.replace(".source_", "_")
try:
libs.remove(lib)
except ValueError:
# print(f"Source JAR {src} has no matching lib!")
pass
if lib.is_file() and src.is_file():
target_classpaths.append(
E.classpathentry(
kind="lib", path=str(lib), sourcepath=str(src)
)
)
for lib in libs:
skip = False
for pattern in PATH_BLACKLIST:
skip = pattern in str(lib)
if skip:
break
if skip:
continue
if lib.is_file():
target_classpaths.append(
E.classpathentry(kind="lib", path=str(lib))
)
target_classpaths.sort(key=lambda x: x.get("path"))
classpath = E.classpath(*(classpaths + target_classpaths))
tree = lxml.etree.ElementTree(classpath)
xml_string = lxml.etree.tostring(
tree, xml_declaration=True, encoding="utf-8", pretty_print=True
)
pathlib.Path(".classpath").write_bytes(xml_string)


@main.command()
@click.argument("target_path", type=click.Path(exists=True, dir_okay=True))
def deploy(target_path: pathlib.Path) -> None:
"""Deploy the eclipse extension.
Parameters
----------
target_path : pathlib.Path
The installation directory of an Eclipse/ Capella application
where the extension will be deployed into the subdirectory `dropins`.
"""
target_path = pathlib.Path(target_path) / "dropins"
if not target_path.is_dir():
click.echo(f"Target directory `{target_path}` not found.")
sys.exit(1)
_, jar_path = _paths()
dest = target_path / jar_path.name
dest.unlink(missing_ok=True)
shutil.copy(jar_path, dest)
if dest.is_file():
click.echo(f"Deployed `{dest.resolve()}`.")


@main.command()
def package() -> None:
"""Package the eclipse extension."""
for path in (MANIFEST_PATH, PLUGIN_XML_PATH):
if not pathlib.Path(path).is_file():
click.echo(f"`{path}` file not found.")
sys.exit(1)
output_path, jar_path = _paths()
jar_path.unlink(missing_ok=True)
jar_cmd = [
"jar",
"-cmf",
MANIFEST_PATH,
str(jar_path),
"-C",
str(output_path),
".",
PLUGIN_XML_PATH,
]
jar_path.parent.mkdir(parents=True, exist_ok=True)
subprocess.run(jar_cmd, check=True)
if jar_path.is_file():
click.echo(f"Created `{jar_path.resolve()}`.")


# Define another subcommand
@main.command()
def clean() -> None:
"""Clean the build artifacts."""
click.echo("Cleaning build artifacts...")


if __name__ == "__main__":
main()
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ classifiers = [
]
dependencies = [
"click",
"lxml",
]

[project.urls]
Expand Down Expand Up @@ -87,7 +88,7 @@ python_version = "3.10"
[[tool.mypy.overrides]]
# Untyped third party libraries
module = [
# ...
"lxml.*",
]
ignore_missing_imports = true

Expand All @@ -114,6 +115,10 @@ ignore-long-lines = '^\s*(?:(?:__ |\.\. __: )?https?://[^ ]+$|def test_.*|[A-Za-
load-plugins = [
"pylint.extensions.mccabe",
]
extension-pkg-allow-list = [
"lxml.builder",
"lxml.etree",
]
max-complexity = 14
max-line-length = 79

Expand Down

0 comments on commit 58f822c

Please sign in to comment.