Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .mvn/extensions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<extensions>
<extension>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-build</artifactId>
<version>${tycho.version}</version>
</extension>
</extensions>
1 change: 1 addition & 0 deletions .mvn/maven.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-Dtycho.version=5.0.2
15 changes: 15 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ export WORKSPACE=$(pwd)
- **Aggregator module**: `com.avaloq.tools.ddk.xtext.test`
- **UI tests**: Require virtual display (xvfb on Linux) or `-XstartOnFirstThread` (macOS)

### macOS developer setup

SWT on Cocoa requires the Display to be created on the main thread. UI tests
therefore need `-XstartOnFirstThread`. There are two contexts:

- **`mvn verify` / CI**: the `macosx` profile in `ddk-parent/pom.xml` (activated
on `family=mac`) adds the flag automatically. No setup required.
- **Running launches from Eclipse**: add `-XstartOnFirstThread` to your JRE's
default VM arguments — `Window > Preferences > Java > Installed JREs > [your
JDK] > Edit > Default VM arguments`. PDE launches with `append.args=true`
inherit this on macOS only. The flag is not hardcoded in the `.launch` files
because the JVM on Windows and Linux rejects it as an "Unrecognized VM
option". This matches Eclipse Platform's own UI test launches (which omit
the flag and rely on the same JRE-default-args mechanism).

### Aggregator pattern — important

The project runs **all tests through one aggregator module**, not per-`.test`-module. `ddk-parent/pom.xml` sets `<skip>true</skip>` on `tycho-surefire-plugin` globally; only `com.avaloq.tools.ddk.xtext.test` overrides it with its own full tycho-surefire configuration. Inside that module, `src/com/avaloq/tools/ddk/xtext/AllTests.java` is a JUnit 5 `@Suite` that `@SelectClasses` from ~14 per-module `*TestSuite` classes (`ExportTestSuite`, `CheckCoreTestSuite`, `TypeSystemTestSuite`, `CheckUiTestSuite`, etc.). Those other `.test` bundles are on `xtext.test`'s OSGi classpath via `Require-Bundle`, so their test classes get discovered and executed inside the single Eclipse runtime spun up for `xtext.test`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.google.inject.Inject
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.AbstractFileSystemAccess
import org.eclipse.xtext.generator.IFileSystemAccess
import org.eclipse.xtext.generator.IFileSystemAccess2
import org.eclipse.xtext.xbase.compiler.JvmModelGenerator

import static org.eclipse.xtext.xbase.lib.IteratorExtensions.*
Expand All @@ -36,23 +37,24 @@ class CheckGenerator extends JvmModelGenerator {
@Inject ICheckGeneratorConfigProvider generatorConfigProvider;

override void doGenerate(Resource resource, IFileSystemAccess fsa) {
super.doGenerate(resource, fsa); // Generate validator, catalog, and preference initializer from inferred Jvm models.
val lfFsa = new LfNormalizingFileSystemAccess(fsa as IFileSystemAccess2)
super.doGenerate(resource, lfFsa); // Generate validator, catalog, and preference initializer from inferred Jvm models.
val config = generatorConfigProvider.get(resource?.URI);
for (catalog : toIterable(resource.allContents).filter(typeof(CheckCatalog))) {

fsa.generateFile(catalog.issueCodesFilePath, catalog.compileIssueCodes)
fsa.generateFile(catalog.standaloneSetupPath, catalog.compileStandaloneSetup)
lfFsa.generateFile(catalog.issueCodesFilePath, catalog.compileIssueCodes)
lfFsa.generateFile(catalog.standaloneSetupPath, catalog.compileStandaloneSetup)

// change output path for service registry
fsa.generateFile(
lfFsa.generateFile(
CheckUtil::serviceRegistryClassName,
CheckGeneratorConstants::CHECK_REGISTRY_OUTPUT,
catalog.generateServiceRegistry(CheckUtil::serviceRegistryClassName, fsa)
)
// generate documentation for SCA-checks only
if(config !== null && (config.doGenerateDocumentationForAllChecks || !config.generateLanguageInternalChecks)){
// change output path for html files to docs/
fsa.generateFile(catalog.docFileName, CheckGeneratorConstants::CHECK_DOC_OUTPUT, catalog.compileDoc)
lfFsa.generateFile(catalog.docFileName, CheckGeneratorConstants::CHECK_DOC_OUTPUT, catalog.compileDoc)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*******************************************************************************
* Copyright (c) 2016 Avaloq Group AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Avaloq Group AG - initial API and implementation
*******************************************************************************/

package com.avaloq.tools.ddk.check.generator;

import java.io.InputStream;

import org.eclipse.emf.common.util.URI;
import org.eclipse.xtext.generator.IFileSystemAccess2;

import com.google.common.base.Preconditions;


/**
* A delegating {@link IFileSystemAccess2} that normalizes line endings to LF ({@code \n})
* before writing content. This ensures generated files are platform-independent regardless
* of the OS on which the build runs.
*
* <p>Implements {@link IFileSystemAccess2} so that {@code instanceof} checks in the framework
* (e.g., in {@code JvmModelGenerator}) continue to work and no behavior is lost.</p>
*/
public class LfNormalizingFileSystemAccess implements IFileSystemAccess2 {

private final IFileSystemAccess2 delegate;

/**
* Wraps the given delegate. Callers that hold the weaker {@link org.eclipse.xtext.generator.IFileSystemAccess}
* (e.g. from Xtext's {@code Generator2#doGenerate(Resource, IFileSystemAccess)}) must cast at the
* call site — every default Xtext FSA implementation is also an {@link IFileSystemAccess2}.
*
* @param delegate the delegate to wrap, must not be {@code null}
*/
public LfNormalizingFileSystemAccess(final IFileSystemAccess2 delegate) {
this.delegate = Preconditions.checkNotNull(delegate);
}

@Override
public void generateFile(final String fileName, final CharSequence contents) {
delegate.generateFile(fileName, normalizeLineEndings(contents));
}

@Override
public void generateFile(final String fileName, final String outputConfigName, final CharSequence contents) {
delegate.generateFile(fileName, outputConfigName, normalizeLineEndings(contents));
}

@Override
public void deleteFile(final String fileName) {
delegate.deleteFile(fileName);
}

@Override
public void generateFile(final String fileName, final InputStream content) {
delegate.generateFile(fileName, content);
}

@Override
public void generateFile(final String fileName, final String outputConfigName, final InputStream content) {
delegate.generateFile(fileName, outputConfigName, content);
}

@Override
public URI getURI(final String fileName, final String outputConfigName) {
return delegate.getURI(fileName, outputConfigName);
}

@Override
public URI getURI(final String fileName) {
return delegate.getURI(fileName);
}

@Override
public void deleteFile(final String fileName, final String outputConfigName) {
delegate.deleteFile(fileName, outputConfigName);
}

@Override
public InputStream readBinaryFile(final String fileName, final String outputConfigName) {
return delegate.readBinaryFile(fileName, outputConfigName);
}

@Override
public InputStream readBinaryFile(final String fileName) {
return delegate.readBinaryFile(fileName);
}

@Override
public CharSequence readTextFile(final String fileName, final String outputConfigName) {
return delegate.readTextFile(fileName, outputConfigName);
}

@Override
public CharSequence readTextFile(final String fileName) {
return delegate.readTextFile(fileName);
}

@Override
public boolean isFile(final String path, final String outputConfigurationName) {
return delegate.isFile(path, outputConfigurationName);
}

@Override
public boolean isFile(final String path) {
return delegate.isFile(path);
}

private static CharSequence normalizeLineEndings(final CharSequence content) {
if (content == null) {
return null;
}
String text = content.toString();
if (text.indexOf('\r') < 0) {
return content;
}
return text.replace("\r\n", "\n").replace("\r", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
*******************************************************************************/
package com.avaloq.tools.ddk.checkcfg.generator

import com.avaloq.tools.ddk.check.generator.LfNormalizingFileSystemAccess
import com.avaloq.tools.ddk.check.runtime.configuration.ICheckConfigurationStoreService
import com.avaloq.tools.ddk.checkcfg.checkcfg.CheckConfiguration
import com.google.inject.Inject
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.AbstractFileSystemAccess
import org.eclipse.xtext.generator.IFileSystemAccess
import org.eclipse.xtext.generator.IFileSystemAccess2
import org.eclipse.xtext.generator.IGenerator

import static org.eclipse.xtext.xbase.lib.IteratorExtensions.*
Expand All @@ -37,8 +39,9 @@ class CheckCfgGenerator implements IGenerator {
if (fsa instanceof AbstractFileSystemAccess) {
fsa.setOutputPath(outputPath)
}
val lfFsa = new LfNormalizingFileSystemAccess(fsa as IFileSystemAccess2)
for (configuration:toIterable(resource.allContents).filter(typeof(CheckConfiguration))) {
fsa.generateFile(configuration.fileName, configuration.compile)
lfFsa.generateFile(configuration.fileName, configuration.compile)
}
}

Expand Down
83 changes: 82 additions & 1 deletion com.avaloq.tools.ddk.xtext.export.test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,85 @@
<artifactId>com.avaloq.tools.ddk.xtext.export.test</artifactId>
<packaging>eclipse-test-plugin</packaging>

</project>
<!--
Opt-in to per-module test execution. Default (`ddk.skipPerModuleTests=true`)
leaves test execution to the aggregator in `com.avaloq.tools.ddk.xtext.test`,
so default `mvn verify` runs the export tests exactly once. Focused
iteration uses:

mvn verify -pl :com.avaloq.tools.ddk.xtext.export.test -am \
-Dddk.skipPerModuleTests=false -f ./ddk-parent/pom.xml
-->
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-surefire-plugin</artifactId>
<version>${tycho.version}</version>
<configuration>
<trimStackTrace>false</trimStackTrace>
<skip>${ddk.skipPerModuleTests}</skip>
<testClass>com.avaloq.tools.ddk.xtext.test.export.ExportTestSuite</testClass>
<failIfNoTests>false</failIfNoTests>
<useUIThread>false</useUIThread>
<useUIHarness>true</useUIHarness>
<forkedProcessTimeoutInSeconds>${test.timeout}</forkedProcessTimeoutInSeconds>
<!--
`-Dorg.osgi.framework.system.packages.extra=jdk.incubator.vector`
is required on macOS where Adoptium/Temurin JDKs do not expose
the incubator module that Lucene 9.12 (transitively required by
org.eclipse.help.base, pulled in by `org.eclipse.help` feature)
imports. Without it, the Eclipse OSGi framework fails to resolve
org.eclipse.help.base at runtime startup.
-->
<argLine>-Dorg.osgi.framework.system.packages.extra=jdk.incubator.vector -Dslf4j.internal.verbosity=ERROR -Dlogback.configurationFile="${runtime.logbackConfig}" ${test.javaOptions}</argLine>
<appArgLine>-pluginCustomization ${runtime.pluginCustomization}</appArgLine>
<product>${runtime.product}</product>
<application>org.eclipse.ui.ide.workbench</application>
<dependencies>
<dependency>
<type>p2-installable-unit</type>
<artifactId>org.eclipse.platform.feature.group</artifactId>
</dependency>
</dependencies>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>target-platform-configuration</artifactId>
<version>${tycho.version}</version>
<configuration>
<dependency-resolution>
<extraRequirements>
<requirement>
<type>eclipse-feature</type>
<id>org.eclipse.xtext.sdk</id>
<versionRange>0.0.0</versionRange>
</requirement>
<requirement>
<type>eclipse-feature</type>
<id>org.eclipse.pde</id>
<versionRange>0.0.0</versionRange>
</requirement>
<requirement>
<type>eclipse-feature</type>
<id>org.eclipse.help</id>
<versionRange>0.0.0</versionRange>
</requirement>
<requirement>
<type>eclipse-feature</type>
<id>org.eclipse.rcp</id>
<versionRange>0.0.0</versionRange>
</requirement>
<requirement>
<type>eclipse-plugin</type>
<id>org.eclipse.equinox.event</id>
<versionRange>0.0.0</versionRange>
</requirement>
</extraRequirements>
</dependency-resolution>
</configuration>
</plugin>
</plugins>
</build>
</project>
25 changes: 25 additions & 0 deletions ddk-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@
<pmd.version>7.24.0</pmd.version>
<tycho.version>5.0.2</tycho.version>
<xtend.version>2.42.0</xtend.version>

<!--
Opt-in flag for per-module tycho-surefire execution. Defaults to `true`
so that the aggregator in `com.avaloq.tools.ddk.xtext.test` remains the
single point of test execution (no double-execution of tests already
pulled in via the aggregator's Require-Bundle path). Per-module test
bundles that have their own tycho-surefire configuration should bind
their `<skip>` to this property so that focused dev workflows can opt
in via `-Dddk.skipPerModuleTests=false -pl :the.module -am`.
-->
<ddk.skipPerModuleTests>true</ddk.skipPerModuleTests>
</properties>

<modules>
Expand Down Expand Up @@ -425,5 +436,19 @@
<osgi.arch>x86_64</osgi.arch>
</properties>
</profile>
<profile>
<id>macosx</id>
<activation>
<os>
<family>mac</family>
</os>
</activation>
<properties>
<osgi.os>macosx</osgi.os>
<osgi.ws>cocoa</osgi.ws>
<osgi.arch>aarch64</osgi.arch>
<test.javaOptions>${runtime.javaOptions} -XstartOnFirstThread</test.javaOptions>
</properties>
</profile>
</profiles>
</project>