diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml new file mode 100644 index 000000000..6c19e9b4b --- /dev/null +++ b/.mvn/extensions.xml @@ -0,0 +1,8 @@ + + + + org.eclipse.tycho + tycho-build + ${tycho.version} + + diff --git a/.mvn/maven.config b/.mvn/maven.config new file mode 100644 index 000000000..41217c968 --- /dev/null +++ b/.mvn/maven.config @@ -0,0 +1 @@ +-Dtycho.version=5.0.2 diff --git a/AGENTS.md b/AGENTS.md index 77ea18a9d..6fe4580db 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 `true` 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`. diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGenerator.xtend b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGenerator.xtend index be2ed319b..083ddc9b2 100644 --- a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGenerator.xtend +++ b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/CheckGenerator.xtend @@ -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.* @@ -36,15 +37,16 @@ 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) @@ -52,7 +54,7 @@ class CheckGenerator extends JvmModelGenerator { // 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) } } } diff --git a/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/LfNormalizingFileSystemAccess.java b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/LfNormalizingFileSystemAccess.java new file mode 100644 index 000000000..53dbd1f2a --- /dev/null +++ b/com.avaloq.tools.ddk.check.core/src/com/avaloq/tools/ddk/check/generator/LfNormalizingFileSystemAccess.java @@ -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. + * + *

Implements {@link IFileSystemAccess2} so that {@code instanceof} checks in the framework + * (e.g., in {@code JvmModelGenerator}) continue to work and no behavior is lost.

+ */ +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$ + } + +} diff --git a/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/generator/CheckCfgGenerator.xtend b/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/generator/CheckCfgGenerator.xtend index 9b305ef40..e7a49aa53 100644 --- a/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/generator/CheckCfgGenerator.xtend +++ b/com.avaloq.tools.ddk.checkcfg.core/src/com/avaloq/tools/ddk/checkcfg/generator/CheckCfgGenerator.xtend @@ -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.* @@ -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) } } diff --git a/com.avaloq.tools.ddk.xtext.export.test/pom.xml b/com.avaloq.tools.ddk.xtext.export.test/pom.xml index aad43622d..71bae1bae 100644 --- a/com.avaloq.tools.ddk.xtext.export.test/pom.xml +++ b/com.avaloq.tools.ddk.xtext.export.test/pom.xml @@ -10,4 +10,85 @@ com.avaloq.tools.ddk.xtext.export.test eclipse-test-plugin - \ No newline at end of file + + + + + org.eclipse.tycho + tycho-surefire-plugin + ${tycho.version} + + false + ${ddk.skipPerModuleTests} + com.avaloq.tools.ddk.xtext.test.export.ExportTestSuite + false + false + true + ${test.timeout} + + -Dorg.osgi.framework.system.packages.extra=jdk.incubator.vector -Dslf4j.internal.verbosity=ERROR -Dlogback.configurationFile="${runtime.logbackConfig}" ${test.javaOptions} + -pluginCustomization ${runtime.pluginCustomization} + ${runtime.product} + org.eclipse.ui.ide.workbench + + + p2-installable-unit + org.eclipse.platform.feature.group + + + + + + org.eclipse.tycho + target-platform-configuration + ${tycho.version} + + + + + eclipse-feature + org.eclipse.xtext.sdk + 0.0.0 + + + eclipse-feature + org.eclipse.pde + 0.0.0 + + + eclipse-feature + org.eclipse.help + 0.0.0 + + + eclipse-feature + org.eclipse.rcp + 0.0.0 + + + eclipse-plugin + org.eclipse.equinox.event + 0.0.0 + + + + + + + + diff --git a/ddk-parent/pom.xml b/ddk-parent/pom.xml index a32edb44c..061b270e7 100644 --- a/ddk-parent/pom.xml +++ b/ddk-parent/pom.xml @@ -56,6 +56,17 @@ 7.24.0 5.0.2 2.42.0 + + + true @@ -425,5 +436,19 @@ x86_64 + + macosx + + + mac + + + + macosx + cocoa + aarch64 + ${runtime.javaOptions} -XstartOnFirstThread + +