Skip to content

Commit 230c20c

Browse files
committed
Extract esmf-native-support module from samm-cli
1 parent 5de6c1b commit 230c20c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3832
-1295
lines changed

core/esmf-aspect-meta-model-java/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@
2828
<name>ESMF Aspect Meta Model Java</name>
2929
<packaging>jar</packaging>
3030

31-
<properties>
32-
<generated-sources>${project.basedir}/src-gen</generated-sources>
33-
<build-time-sources>${project.basedir}/src-buildtime</build-time-sources>
34-
</properties>
35-
3631
<dependencies>
3732
<dependency>
3833
<groupId>org.eclipse.esmf</groupId>

core/esmf-native-support/pom.xml

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH
4+
~
5+
~ See the AUTHORS file(s) distributed with this work for additional
6+
~ information regarding authorship.
7+
~
8+
~ This Source Code Form is subject to the terms of the Mozilla Public
9+
~ License, v. 2.0. If a copy of the MPL was not distributed with this
10+
~ file, You can obtain one at https://mozilla.org/MPL/2.0/.
11+
~
12+
~ SPDX-License-Identifier: MPL-2.0
13+
-->
14+
15+
<project xmlns="http://maven.apache.org/POM/4.0.0"
16+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
17+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
18+
<modelVersion>4.0.0</modelVersion>
19+
20+
<parent>
21+
<groupId>org.eclipse.esmf</groupId>
22+
<artifactId>esmf-sdk-parent</artifactId>
23+
<version>DEV-SNAPSHOT</version>
24+
<relativePath>../../pom.xml</relativePath>
25+
</parent>
26+
27+
<artifactId>esmf-native-support</artifactId>
28+
<name>ESMF Native Support</name>
29+
30+
<properties>
31+
<skip.maven.shade>false</skip.maven.shade>
32+
</properties>
33+
34+
<dependencies>
35+
<dependency>
36+
<groupId>org.eclipse.esmf</groupId>
37+
<artifactId>esmf-aspect-model-starter</artifactId>
38+
</dependency>
39+
<dependency>
40+
<groupId>ch.qos.logback</groupId>
41+
<artifactId>logback-classic</artifactId>
42+
</dependency>
43+
<dependency>
44+
<groupId>org.graalvm.nativeimage</groupId>
45+
<artifactId>svm</artifactId>
46+
<scope>provided</scope>
47+
</dependency>
48+
49+
<dependency>
50+
<groupId>org.junit.jupiter</groupId>
51+
<artifactId>junit-jupiter</artifactId>
52+
<scope>test</scope>
53+
</dependency>
54+
<dependency>
55+
<groupId>org.assertj</groupId>
56+
<artifactId>assertj-core</artifactId>
57+
<scope>test</scope>
58+
</dependency>
59+
</dependencies>
60+
61+
<build>
62+
<plugins>
63+
<plugin>
64+
<artifactId>maven-resources-plugin</artifactId>
65+
<executions>
66+
<execution>
67+
<id>copy-resources</id>
68+
<!-- This needs to take place after the customize-resource-config exec-maven-plugin goal runs and before the package phase -->
69+
<phase>prepare-package</phase>
70+
<goals>
71+
<goal>copy-resources</goal>
72+
</goals>
73+
<configuration>
74+
<outputDirectory>${project.build.outputDirectory}/META-INF</outputDirectory>
75+
<resources>
76+
<resource>
77+
<directory>${generated-sources}/main/resources/META-INF</directory>
78+
<filtering>false</filtering>
79+
</resource>
80+
</resources>
81+
</configuration>
82+
</execution>
83+
</executions>
84+
</plugin>
85+
86+
<plugin>
87+
<groupId>org.apache.maven.plugins</groupId>
88+
<artifactId>maven-compiler-plugin</artifactId>
89+
<configuration>
90+
<compilerArgs>
91+
<arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
92+
<arg>--add-exports</arg>
93+
<arg>java.desktop/sun.awt=ALL-UNNAMED</arg>
94+
<arg>--add-exports</arg>
95+
<arg>java.desktop/sun.font=ALL-UNNAMED</arg>
96+
<arg>--add-exports</arg>
97+
<arg>org.graalvm.sdk/org.graalvm.nativeimage.impl=ALL-UNNAMED</arg>
98+
</compilerArgs>
99+
<showWarnings>false</showWarnings>
100+
<showDeprecation>false</showDeprecation>
101+
</configuration>
102+
</plugin>
103+
104+
<plugin>
105+
<groupId>org.apache.maven.plugins</groupId>
106+
<artifactId>maven-javadoc-plugin</artifactId>
107+
<configuration>
108+
<excludePackageNames>org.eclipse.esmf.substitution,org.eclipse.esmf.buildtime</excludePackageNames>
109+
</configuration>
110+
</plugin>
111+
112+
<plugin>
113+
<groupId>org.apache.maven.plugins</groupId>
114+
<artifactId>maven-surefire-plugin</artifactId>
115+
<configuration>
116+
<forkCount>1</forkCount>
117+
<reuseForks>false</reuseForks>
118+
<argLine>--add-exports org.graalvm.sdk/org.graalvm.nativeimage.impl=ALL-UNNAMED</argLine>
119+
</configuration>
120+
</plugin>
121+
122+
<!-- Executes build-time-only code -->
123+
<plugin>
124+
<groupId>org.codehaus.mojo</groupId>
125+
<artifactId>exec-maven-plugin</artifactId>
126+
<executions>
127+
<!-- Generate admin-shell reflection config -->
128+
<execution>
129+
<id>generate-admin-shell-reflection-config</id>
130+
<phase>process-classes</phase>
131+
<goals>
132+
<goal>java</goal>
133+
</goals>
134+
<configuration>
135+
<!-- The main class of your build-time scanning code -->
136+
<mainClass>org.eclipse.esmf.buildtime.Aas4jClassSetup</mainClass>
137+
<!-- Pass the directory where to write the properties file -->
138+
<commandlineArgs>${project.build.outputDirectory}</commandlineArgs>
139+
<cleanupDaemonThreads>false</cleanupDaemonThreads>
140+
</configuration>
141+
</execution>
142+
<!-- Execute adjustments of native configs that were generated during the tests -->
143+
<execution>
144+
<id>customize-resource-config</id>
145+
<phase>process-classes</phase>
146+
<goals>
147+
<goal>java</goal>
148+
</goals>
149+
<configuration>
150+
<!-- The main class of your build-time scanning code -->
151+
<mainClass>org.eclipse.esmf.buildtime.CustomizeGraalVmConfigs</mainClass>
152+
<!-- Pass the native image configs directory as a commandline param -->
153+
<commandlineArgs>${native-config-path}</commandlineArgs>
154+
<cleanupDaemonThreads>false</cleanupDaemonThreads>
155+
</configuration>
156+
</execution>
157+
</executions>
158+
</plugin>
159+
160+
<plugin>
161+
<groupId>org.apache.maven.plugins</groupId>
162+
<artifactId>maven-jar-plugin</artifactId>
163+
<configuration>
164+
<!-- Exclude build-time-only code from jar -->
165+
<excludes>
166+
<exclude>org/eclipse/esmf/buildtime/**</exclude>
167+
</excludes>
168+
</configuration>
169+
</plugin>
170+
</plugins>
171+
</build>
172+
</project>

tools/samm-cli/src/main/java/org/eclipse/esmf/buildtime/Aas4jClassSetup.java renamed to core/esmf-native-support/src/main/java/org/eclipse/esmf/buildtime/Aas4jClassSetup.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,17 @@
2020
import static org.eclipse.digitaltwin.aas4j.v3.dataformat.core.internal.util.ReflectionHelper.MODEL_PACKAGE_NAME;
2121
import static org.eclipse.digitaltwin.aas4j.v3.dataformat.core.internal.util.ReflectionHelper.MODEL_TYPE_SUPERCLASSES;
2222
import static org.eclipse.digitaltwin.aas4j.v3.dataformat.core.internal.util.ReflectionHelper.XML_MIXINS_PACKAGE_NAME;
23+
import static org.eclipse.esmf.nativefeatures.AssetAdministrationShellFeature.ADMINSHELL_PROPERTIES;
2324

24-
import java.io.File;
2525
import java.io.FileOutputStream;
2626
import java.io.IOException;
27+
import java.nio.file.Path;
2728
import java.util.ArrayList;
2829
import java.util.HashMap;
2930
import java.util.HashSet;
3031
import java.util.List;
3132
import java.util.Map;
3233
import java.util.Objects;
33-
import java.util.Properties;
3434
import java.util.Set;
3535
import java.util.stream.Collectors;
3636

@@ -70,14 +70,22 @@ public Aas4jClassSetup() {
7070
config.interfaces = scanAasInterfaces();
7171
config.enums = modelScan.getAllEnums().loadClasses( Enum.class );
7272
config.interfacesWithoutDefaultImplementation = getInterfacesWithoutDefaultImplementation( modelScan );
73+
config.classesInModelPackage = classesInPackage( MODEL_PACKAGE_NAME );
74+
config.classesInDefaultImplementationPackage = classesInPackage( DEFAULT_IMPLEMENTATION_PACKAGE_NAME );
75+
config.classesInJsonMixinsPackage = classesInPackage( JSON_MIXINS_PACKAGE_NAME );
76+
config.classesInXmlMixinsPackage = classesInPackage( XML_MIXINS_PACKAGE_NAME );
7377
}
7478

7579
public static void main( final String[] args ) throws IOException {
76-
final AdminShellConfig config = new Aas4jClassSetup().config;
77-
final Properties p = config.toProperties();
78-
final File out = new File( args[0] );
79-
final FileOutputStream outputStream = new FileOutputStream( out );
80-
p.store( outputStream, null );
80+
try ( final FileOutputStream outputStream = new FileOutputStream( Path.of( args[0] ).resolve( ADMINSHELL_PROPERTIES ).toFile() ) ) {
81+
new Aas4jClassSetup().config.toProperties().store( outputStream, null );
82+
}
83+
}
84+
85+
private Set<Class<?>> classesInPackage( final String packageName ) {
86+
try ( final ScanResult scanResult = new ClassGraph().enableAllInfo().acceptPackages( packageName ).scan() ) {
87+
return new HashSet<>( scanResult.getAllClasses().loadClasses() );
88+
}
8189
}
8290

8391
/**

tools/samm-cli/src/main/java/org/eclipse/esmf/buildtime/CustomizeGraalVmConfigs.java renamed to core/esmf-native-support/src/main/java/org/eclipse/esmf/buildtime/CustomizeGraalVmConfigs.java

Lines changed: 84 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH
2+
* Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH
33
*
44
* See the AUTHORS file(s) distributed with this work for additional
55
* information regarding authorship.
@@ -24,10 +24,11 @@
2424

2525
import com.fasterxml.jackson.databind.JsonNode;
2626
import com.fasterxml.jackson.databind.ObjectMapper;
27+
import com.fasterxml.jackson.databind.node.ObjectNode;
28+
import com.google.common.collect.Streams;
2729

2830
/**
29-
* This class runs at build time, after the executable jar for samm-cli has been executed with the GraalVM native image agent,
30-
* but before the native image compiler is started. It will adjust the configs that were created by the native image agent,
31+
* This class runs at build time. It will adjust the configs that were created by the native image agent,
3132
* which sometimes create configurations which lead to failures during either compilation or runtime.
3233
*/
3334
public class CustomizeGraalVmConfigs {
@@ -36,7 +37,7 @@ public static void main( final String[] args ) throws IOException {
3637
final File configsDirectory = new File( args[0] );
3738
if ( !configsDirectory.exists() || !configsDirectory.isDirectory() ) {
3839
System.err.println( "Warning: Native resource config directory " + configsDirectory + " not found, skipping customizing" );
39-
System.exit( 0 );
40+
return;
4041
}
4142
final Path configsPath = configsDirectory.toPath();
4243
adjustResourceConfig( configsPath.resolve( "resource-config.json" ).toFile() );
@@ -75,28 +76,94 @@ private static void adjustResourceConfig( final File resourceConfig ) throws IOE
7576
}
7677
}
7778

79+
private static Class<?> instantiateParameterType( final String typeName ) {
80+
for ( final Class<?> simpleType : List.of(
81+
boolean.class, byte.class, int.class, long.class, float.class, double.class,
82+
boolean[].class, byte[].class, int[].class, long[].class, float[].class, double[].class
83+
) ) {
84+
if ( typeName.equals( simpleType.getSimpleName() ) ) {
85+
return simpleType;
86+
}
87+
}
88+
89+
try {
90+
return Class.forName( typeName, false, CustomizeGraalVmConfigs.class.getClassLoader() );
91+
} catch ( final ClassNotFoundException e ) {
92+
return null;
93+
}
94+
}
95+
96+
private static void removeNonExistingtMethods( final JsonNode entry, final Class<?> clazz ) {
97+
final JsonNode methods = entry.get( "methods" );
98+
if ( methods == null ) {
99+
return;
100+
}
101+
for ( final Iterator<JsonNode> it = methods.elements(); it.hasNext(); ) {
102+
final JsonNode method = it.next();
103+
try {
104+
final String methodName = method.get( "name" ).asText();
105+
final Class<?>[] parameterTypes = Streams.stream( method.get( "parameterTypes" ).elements() )
106+
.map( param -> instantiateParameterType( param.asText() ) )
107+
.toArray( Class<?>[]::new );
108+
if ( !methodName.equals( "<init>" ) ) {
109+
clazz.getMethod( methodName, parameterTypes );
110+
}
111+
} catch ( final Throwable t ) {
112+
// The method can not be found via reflection, so it can be considered a spurious entry. Remove it.
113+
it.remove();
114+
}
115+
}
116+
if ( !methods.elements().hasNext() ) {
117+
( (ObjectNode) entry ).remove( "methods" );
118+
}
119+
}
120+
121+
private static boolean shouldEntryBeRemovedFromReflectConfig( final JsonNode entry ) {
122+
final String className = entry.get( "name" ).asText();
123+
for ( final String nonReflectionClass : List.of(
124+
// These three reflection entries are added by the native image agent, but cause the native compilation to fail
125+
"jdk.internal.loader.BuiltinClassLoader",
126+
"jdk.internal.loader.ClassLoaders$AppClassLoader",
127+
"jdk.internal.loader.ClassLoaders$PlatformClassLoader",
128+
// The remaining exludes are generated by the native-image agent during test runs and are explicitly not
129+
// needed at native-image compile time
130+
"org.eclipse.esmf.test.",
131+
"test.test.test",
132+
"org.junit.",
133+
"com.sun.tools.javac"
134+
) ) {
135+
if ( className.contains( nonReflectionClass ) ) {
136+
return true;
137+
}
138+
}
139+
140+
if ( className.matches( "org\\.eclipse\\.esmf\\.*Test" ) ) {
141+
return true;
142+
}
143+
144+
try {
145+
final Class<?> clazz = Class.forName( className, false, CustomizeGraalVmConfigs.class.getClassLoader() );
146+
removeNonExistingtMethods( entry, clazz );
147+
return false;
148+
} catch ( final ClassNotFoundException e ) {
149+
// If the class can not be instantiated, it's also a spurious entry added by the native-image agent; remove it.
150+
return true;
151+
}
152+
}
153+
78154
private static void adjustReflectConfig( final File reflectConfig ) throws IOException {
79155
if ( !reflectConfig.exists() ) {
80156
System.err.println( "Warning: Native resource config " + reflectConfig + " not found, skipping customizing" );
81157
return;
82158
}
83159

84-
final List<Predicate<JsonNode>> reflectEntriesToDelete = List.of(
85-
// These three reflection entries are added by the native image agent, but cause the native compilation to fail
86-
includeNode -> includeNode.asText().contains( "jdk.internal.loader.BuiltinClassLoader" ),
87-
includeNode -> includeNode.asText().contains( "jdk.internal.loader.ClassLoaders$AppClassLoader" ),
88-
includeNode -> includeNode.asText().contains( "jdk.internal.loader.ClassLoaders$PlatformClassLoader" )
89-
);
90160
final String content = Files.readString( reflectConfig.toPath() );
91161
final ObjectMapper mapper = new ObjectMapper();
92162
final JsonNode root = mapper.readTree( content );
93-
for ( final Iterator<JsonNode> i = root.elements(); i.hasNext(); ) {
94-
final JsonNode include = i.next();
95-
for ( final Predicate<JsonNode> decideIfNodeShouldBeDeleted : reflectEntriesToDelete ) {
96-
final JsonNode name = include.get( "name" );
97-
if ( name != null && decideIfNodeShouldBeDeleted.test( include.get( "name" ) ) ) {
98-
i.remove();
99-
}
163+
for ( final Iterator<JsonNode> it = root.elements(); it.hasNext(); ) {
164+
final JsonNode entry = it.next();
165+
if ( shouldEntryBeRemovedFromReflectConfig( entry ) ) {
166+
it.remove();
100167
}
101168
}
102169

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH
3+
*
4+
* See the AUTHORS file(s) distributed with this work for additional
5+
* information regarding authorship.
6+
*
7+
* This Source Code Form is subject to the terms of the Mozilla Public
8+
* License, v. 2.0. If a copy of the MPL was not distributed with this
9+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
10+
*
11+
* SPDX-License-Identifier: MPL-2.0
12+
*/
13+
14+
/**
15+
* The org.eclipse.esmf.buildtime package contains classes that are exclusively used (compiled & executed) during the build.
16+
* Classes in the package are self-contained programs (with a main() method) that create or modify files, e.g., resources.
17+
* The classes from this package are explicitly excluded from the jar of the module. Execution of the classes and configuration of command
18+
* line arguments is done in pom.xml.
19+
*/
20+
package org.eclipse.esmf.buildtime;

0 commit comments

Comments
 (0)