-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: jamilraichouni <[email protected]>
- Loading branch information
0 parents
commit 2f76fba
Showing
21 changed files
with
1,513 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
Oops, something went wrong.