diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index caf27e9724c..1deaacade00 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -41,3 +41,25 @@ jobs: echo "::error title=Inconsistent JavaDoc::JavaDoc-Basher discovered JavaDoc inconsistencies between implementations, which must be resolved." exit 1 fi + + check-jni-generator-consistency: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0 + - uses: actions/setup-java@de5a937a1dc73fbc1a67d7d1aa4bebc1082f3190 # v5.0.0 + with: + java-version: 25 + distribution: 'temurin' + cache: maven + - name: Run JNI generator + run: > + mvn --non-recursive --batch-mode --no-transfer-progress + -Pjni-generator exec:exec@run-jni-generator + - name: Verify no changes + run: | + if ! git diff --exit-code ; then + echo "::error title=Outdated JNI glue code::The generated native sources (os.c, *_stats.*, *_structs.* ...) do not match the Java native declarations. Regenerate them with 'mvn --non-recursive -Pjni-generator exec:exec@run-jni-generator' and commit the result." + exit 1 + fi diff --git a/AGENTS.md b/AGENTS.md index 77f3df037d4..d1f90fb6c28 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -59,7 +59,12 @@ mvn clean verify -DskipTests See `bundles/org.eclipse.swt/Readme.md#building-native-binaries` for instructions how to build the SWT native binaries. **CRITICAL**: Files like `os.c`, `os_stats.c`, `os_stats.h` are **auto-generated**. Never edit them directly! -Instead: modify Java source (e.g., `OS.java`), clean/rebuild the project, then run the native build command above. +Instead: modify Java source (e.g., `OS.java`), regenerate the C files, then run the native build command above. +To regenerate without the Eclipse IDE, run at the repository root: +```bash +mvn --non-recursive -Pjni-generator exec:exec@run-jni-generator +``` +The pull-request checks run this command and fail if the committed generated files are outdated. **CRITICAL**: Never commit any built native binary files to git. These are built and committed by the CI. This includes: - Linux: `libswt-*.so` @@ -198,7 +203,7 @@ display.asyncExec(() -> button.setText("Updated")); ### Adding GTK Functions 1. Add native method declaration to `OS.java` with JavaDoc annotations (`@param cast=`, `@method flags=dynamic`) -2. Clean and rebuild `org.eclipse.swt` project (regenerates `os.c`) +2. Regenerate `os.c` and friends: `mvn --non-recursive -Pjni-generator exec:exec@run-jni-generator` (or clean and rebuild the `org.eclipse.swt` project in the IDE) 3. Rebuild natives: `cd bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library && export GTK_VERSION=3.0 && ./build.sh install` ## Tips for AI Tools diff --git a/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTClass.java b/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTClass.java index a9aa666b68d..c01c5cf6de4 100644 --- a/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTClass.java +++ b/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTClass.java @@ -79,19 +79,18 @@ public ASTClass(String sourcePath, MetaData metaData) { packageName = unit.getPackage().getName().getFullyQualifiedName(); name = packageName + "." + simpleName; superclassName = type.getSuperclassType() != null ? type.getSuperclassType().toString() : null; - List imports = unit.imports(); + List imports = unit.imports(); this.imports = new String[imports.size()]; int count = 0; - for (ImportDeclaration imp : imports) { - this.imports[count++] = imp.getName().getFullyQualifiedName(); + for (Object imp : imports) { + this.imports[count++] = ((ImportDeclaration) imp).getName().getFullyQualifiedName(); } start = type.getStartPosition(); - + Javadoc doc = type.getJavadoc(); - List tags = null; if (doc != null) { - tags = doc.tags(); - for (TagElement tag : tags) { + for (Object element : doc.tags()) { + TagElement tag = (TagElement) element; if ("@jniclass".equals(tag.getTagName())) { String data = tag.fragments().get(0).toString(); setMetaData(data); @@ -102,9 +101,8 @@ public ASTClass(String sourcePath, MetaData metaData) { List fid = new ArrayList<>(); for (FieldDeclaration field : type.getFields()) { - List fragments = field.fragments(); - for (VariableDeclarationFragment fragment : fragments) { - fid.add(new ASTField(this, field, fragment)); + for (Object fragment : field.fragments()) { + fid.add(new ASTField(this, field, (VariableDeclarationFragment) fragment)); } } this.fields = fid.toArray(new ASTField[fid.size()]); diff --git a/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTField.java b/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTField.java index 061f7484770..313d24f93ef 100644 --- a/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTField.java +++ b/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTField.java @@ -32,10 +32,9 @@ public ASTField(ASTClass declaringClass, FieldDeclaration field, VariableDeclara start = field.getStartPosition(); Javadoc doc = field.getJavadoc(); - List tags = null; if (doc != null) { - tags = doc.tags(); - for (TagElement tag : tags) { + for (Object element : doc.tags()) { + TagElement tag = (TagElement) element; if ("@field".equals(tag.getTagName())) { String data = tag.fragments().get(0).toString(); setMetaData(data); diff --git a/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTMethod.java b/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTMethod.java index b01e79d8f24..c5fb28829f7 100644 --- a/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTMethod.java +++ b/bundles/org.eclipse.swt.tools/JNI Generation/org/eclipse/swt/tools/internal/ASTMethod.java @@ -37,10 +37,11 @@ public ASTMethod(ASTClass declaringClass, MethodDeclaration method) { start = method.getStartPosition(); Javadoc doc = method.getJavadoc(); - List tags = null; + List tags = null; if (doc != null) { tags = doc.tags(); - for (TagElement tag : tags) { + for (Object element : tags) { + TagElement tag = (TagElement) element; if ("@method".equals(tag.getTagName())) { String data = tag.fragments().get(0).toString(); setMetaData(data); @@ -49,19 +50,20 @@ public ASTMethod(ASTClass declaringClass, MethodDeclaration method) { } } returnType = new ASTType(declaringClass.resolver, method.getReturnType2(), method.getExtraDimensions()); - - List parameters = method.parameters(); + + List parameters = method.parameters(); paramTypes = new ASTType[parameters.size()]; this.parameters = new ASTParameter[paramTypes.length]; int i = 0; - for (Iterator iterator = parameters.iterator(); iterator.hasNext(); i++) { - SingleVariableDeclaration param = iterator.next(); + for (Iterator iterator = parameters.iterator(); iterator.hasNext(); i++) { + SingleVariableDeclaration param = (SingleVariableDeclaration) iterator.next(); paramTypes[i] = new ASTType(declaringClass.resolver, param.getType(), param.getExtraDimensions()); this.parameters[i] = new ASTParameter(this, i, param.getName().getIdentifier()); - + if (tags != null) { String name = param.getName().getIdentifier(); - for (TagElement tag : tags) { + for (Object element : tags) { + TagElement tag = (TagElement) element; if ("@param".equals(tag.getTagName())) { List fragments = tag.fragments(); if (fragments.size() >= 2 && name.equals(fragments.get(0).toString())) { diff --git a/bundles/org.eclipse.swt/Readme.md b/bundles/org.eclipse.swt/Readme.md index 03edc76b058..4936d7fbcfb 100644 --- a/bundles/org.eclipse.swt/Readme.md +++ b/bundles/org.eclipse.swt/Readme.md @@ -61,6 +61,21 @@ Running the snippets: * (Optionally) install CDT from marketplace if you want to work on C/Native parts of SWT. * You should be able to run snippets now. (_e.g._ `Snippet1`). +## Regenerating the JNI glue code + +The C files `os.c`, `*_stats.c/h` and `*_structs.c/h` next to `build.sh` in the `library` folders are generated +from the `native` method declarations (and their `@method`/`@param` JavaDoc annotations) in the Java sources. +Never edit them by hand. After changing a native declaration, regenerate them for all platforms from the CLI +(at the repository root): +```bash +mvn --non-recursive -Pjni-generator exec:exec@run-jni-generator +``` +and commit the changed C files together with the Java change. The pull-request checks run the same command and +fail when the committed C files are outdated. + +In the Eclipse IDE the same happens automatically when the **SWT Tools** feature is installed: +its `JNI Builder` regenerates the files on every build of the `org.eclipse.swt` project. + ## Building native binaries To build only SWT's native binaries, run from the CLI (at the repository root): diff --git a/pom.xml b/pom.xml index e632b7e0846..3b2488f4d1e 100644 --- a/pom.xml +++ b/pom.xml @@ -109,7 +109,6 @@ org.codehaus.mojo exec-maven-plugin - 3.6.3 run-javadoc-basher @@ -118,30 +117,48 @@ validate - true - bundles/org.eclipse.swt.tools - java -classpath JavadocBasher/org/eclipse/swt/tools/internal/JavadocBasher.java - true - - - org.eclipse.jdt - org.eclipse.jdt.core - [3.44.0,) - - - org.eclipse.platform - org.eclipse.jface - [3.38.0,) - - + + + + + + + jni-generator + + + + org.codehaus.mojo + exec-maven-plugin + + + run-jni-generator + + exec + + validate + + + -classpath + + JNI Generation/org/eclipse/swt/tools/internal/JNIGeneratorApp.java + all + + + + @@ -149,6 +166,35 @@ + + + + + org.codehaus.mojo + exec-maven-plugin + 3.6.3 + + true + java + bundles/org.eclipse.swt.tools + true + + + + org.eclipse.jdt + org.eclipse.jdt.core + [3.46.0,) + + + org.eclipse.platform + org.eclipse.jface + [3.39.100.0,) + + + + + org.eclipse.tycho