Skip to content

Commit

Permalink
feat: Initial commit
Browse files Browse the repository at this point in the history
Co-authored-by: jamilraichouni <[email protected]>
  • Loading branch information
MoritzWeber0 and jamilraichouni committed Apr 26, 2024
0 parents commit 2f76fba
Show file tree
Hide file tree
Showing 21 changed files with 1,513 additions and 0 deletions.
71 changes: 71 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

FROM fedora:38
LABEL maintainer "Jamil André RAICHOUNI <[email protected]>"

# system pkg manager installs {{{
RUN dnf --color yes --refresh -y install \
# to clone bespoke eclipse-plugin-builders
gcc \
git \
# to develop Capella 5.x plugins
java-11-openjdk-devel \
# to develop Capella 6.x plugins
java-17-openjdk-devel \
maven \
python3-pip \
zsh
# }}}
# configure terminal {{{
SHELL ["/bin/zsh", "-c"]
ENV SHELL=/bin/zsh
RUN usermod --shell /bin/zsh root
RUN echo -e "alias :q='exit'\nalias :qa='exit'\nalias ls='ls --color'\nalias l='ls -lh'\nalias ll='ls -lha'\nalias vi='nvim'" >> /root/.zshrc
# }}}
# {{{ Neovim setup
RUN dnf --color yes --refresh -y install \
fd-find \
# for TSInstall dockerfile
libstdc++-devel \
neovim \
ripgrep
# ensure minimum setup in $HOME/.local/share/nvim:
RUN nvim --headless --cmd q
RUN mkdir -p /root/.config/nvim/ftplugin && mkdir -p /root/.config/nvim/lua/config
COPY java.lua /root/.config/nvim/ftplugin
COPY init.vim /root/.config/nvim/init.vim
COPY init.lua /root/.config/nvim/lua
COPY plugins.lua /root/.config/nvim/lua
COPY nvim-cmp.lua /root/.config/nvim/lua/config
COPY nvim-lspconfig.lua /root/.config/nvim/lua/config
COPY eclipse_plugin_builders.py /opt/eclipse_plugin_builders.py
RUN python3 -m venv /root/.venv && /root/.venv/bin/pip install click lxml
# }}}
# {{{ java-debug
WORKDIR /opt
RUN curl -L -o java-debug.tar.gz "https://github.com/microsoft/java-debug/archive/refs/tags/0.52.0.tar.gz" && \
tar xvzf java-debug.tar.gz && \
rm java-debug.tar.gz && \
mv java-debug-* java-debug
WORKDIR /opt/java-debug
RUN zsh -c "./mvnw clean install" && chown -R root:root . && \
find . -name "com.microsoft.java.debug.plugin-*.jar" -exec mv {} /opt \; && \
rm -rf /opt/java-debug
# }}}
# {{{ Eclipse JDT Language Server
RUN mkdir /opt/eclipse.jdt.ls
WORKDIR /opt/eclipse.jdt.ls
RUN curl -L -o jdt-language-server.tar.gz \
"https://www.eclipse.org/downloads/download.php?file=/jdtls/milestones/1.34.0/jdt-language-server-1.34.0-202404031240.tar.gz" && \
tar xvzf jdt-language-server.tar.gz && \
rm jdt-language-server.tar.gz && \
chown -R root:root .
ENV PATH=/opt/eclipse.jdt.ls/bin:$PATH
# }}}
ENV DISPLAY="host.docker.internal:0.0"
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk
RUN mkdir -p /workspaces/RUNTIME && mkdir /root/dev
RUN echo -e "source /root/.venv/bin/activate" >> /root/.zshrc
WORKDIR /root/dev
ENTRYPOINT ["/bin/zsh"]
327 changes: 327 additions & 0 deletions .devcontainer/eclipse_plugin_builders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
# Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0
"""Tools to build, pack, deploy Eclipse plugins.
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 plugin.
`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

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


@click.group()
def main() -> None:
"""Console script for eclipse_plugin_builders."""


def _third_party_lib_paths() -> list[pathlib.Path]:
"""Return the paths to the third-party libraries."""
classpath_root = _read_xml_file(".classpath")
third_party_lib_paths = classpath_root.xpath(
'classpathentry[@kind="lib" and '
'not(starts-with(@path, "/opt/capella_6.0.0"))]/@path'
)
return sorted([pathlib.Path(p) for p in third_party_lib_paths])


def compute_jar_name() -> str:
"""Compute and return the name of the jar file to be built."""
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
group_id = pom.xpath("//m:groupId", namespaces=ns)
artifact_id = pom.xpath("//m:artifactId", namespaces=ns)
version = pom.xpath("//m:version", namespaces=ns)
group_id = group_id[0].text if group_id else "unknown"
artifact_id = artifact_id[0].text if artifact_id else "unknown"
version = version[0].text if version else "unknown"
return f"{group_id}.{artifact_id}_{version}.jar"


def _output_and_jar_path() -> tuple[pathlib.Path, pathlib.Path]:
"""Return paths to output dir and the jar file to be built."""
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)
jar_name = compute_jar_name()
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


def _collect_target_platform_plugins(
target_path: pathlib.Path,
) -> list[lxml.etree.Element]:
"""Add the target platform plugins to the classpath."""
# Recursively find all src JARs:
sources: set[pathlib.Path] = set(target_path.glob("**/*.source_*.jar"))
# Recursively find all lib JARs:
dropins_jars = list(target_path.glob("dropins/**/*.jar"))
features_jars = list(target_path.glob("features/**/*.jar"))
jre_jars = list(target_path.glob("jre/**/*.jar"))
plugins_jars = list(target_path.glob("plugins/**/*.jar"))
libs = list(
set(dropins_jars + features_jars + jre_jars + plugins_jars) - sources
)
libs = [l for l in libs if not l.name == compute_jar_name()]
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:
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"))
return target_classpaths


@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="target/classes"),
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))
target_classpaths = _collect_target_platform_plugins(target_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 plugin.
Parameters
----------
target_path : pathlib.Path
The installation directory of an Eclipse/ Capella application
where the plugin 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 = _output_and_jar_path()
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()}`.")


def _get_bundle_classpath(third_party_lib_paths: list[pathlib.Path]) -> str:
lib_paths = sorted([p.name for p in _third_party_lib_paths()])
value = "."
if third_party_lib_paths:
value = ".,\n"
value += ",\n".join(f" lib/{p}" for p in lib_paths)
return f"Bundle-ClassPath: {value}"


def _update_bundle_classpath(
third_party_lib_paths: list[pathlib.Path],
) -> None:
manifest = MANIFEST_PATH.read_text(encoding="utf-8")
bundle_classpath = _get_bundle_classpath(third_party_lib_paths)
lines = manifest.splitlines()
manifest = ""
found_bundle_classpath = False
inside_bundle_classpath = False
for line in lines:
if line.startswith("Bundle-ClassPath:"):
found_bundle_classpath = True
manifest += bundle_classpath + "\n"
inside_bundle_classpath = True
continue
if inside_bundle_classpath:
if line.startswith(" "):
continue
else:
inside_bundle_classpath = False
manifest += line.rstrip() + "\n"
if bundle_classpath and not found_bundle_classpath:
if not manifest.endswith("\n"):
manifest += "\n"
manifest += bundle_classpath + "\n"
# ensure that the maximum line length is not exceeded
# max = 72
# manifest = "\n".join(
# line[:max] + "\n" + line[max:] if len(line) > max else line
# for line in manifest.splitlines()
# )
MANIFEST_PATH.write_text(manifest, encoding="utf-8")


@main.command()
def package() -> None:
"""Package the eclipse plugin."""
lib_dir = pathlib.Path("lib")
if lib_dir.is_dir():
shutil.rmtree(lib_dir)
lib_dir.mkdir()
third_party_lib_paths = _third_party_lib_paths()
if third_party_lib_paths:
for path in third_party_lib_paths:
dest = lib_dir / path.name
dest.unlink(missing_ok=True)
shutil.copy(path, dest)
_update_bundle_classpath(third_party_lib_paths)
for path in (MANIFEST_PATH, PLUGIN_XML_PATH):
if not path.is_file():
click.echo(f"`{path}` file not found.")
sys.exit(1)
output_path, jar_path = _output_and_jar_path()
jar_path.unlink(missing_ok=True)
jar_cmd = [
"jar",
"cfm",
str(jar_path),
str(MANIFEST_PATH),
"-C",
f"{output_path}/",
".",
str(PLUGIN_XML_PATH),
]
potential_additional_dirs = (
"lib",
"OSGI-INF",
)
for dir_ in potential_additional_dirs:
if pathlib.Path(dir_).is_dir() and list(pathlib.Path(dir_).iterdir()):
jar_cmd.append(f"{dir_}/")
jar_path.parent.mkdir(parents=True, exist_ok=True)
click.echo(f"Running command: {' '.join(jar_cmd)}")
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()
Loading

0 comments on commit 2f76fba

Please sign in to comment.