1616
1717import org .eclipse .collections .api .factory .Lists ;
1818import org .eclipse .collections .api .factory .Sets ;
19+ import org .eclipse .collections .api .list .ListIterable ;
1920import org .eclipse .collections .api .list .MutableList ;
2021import org .eclipse .collections .api .set .MutableSet ;
2122import org .eclipse .collections .api .set .SetIterable ;
3839import org .finos .legend .pure .m3 .serialization .runtime .PureCompilerLoader ;
3940import org .finos .legend .pure .m3 .serialization .runtime .PureRuntime ;
4041import org .finos .legend .pure .m3 .serialization .runtime .PureRuntimeBuilder ;
42+ import org .finos .legend .pure .m3 .serialization .runtime .RepositoryComparator ;
4143import org .finos .legend .pure .m4 .exception .PureCompilationException ;
4244import org .finos .legend .pure .m4 .serialization .grammar .antlr .PureParserException ;
4345import org .slf4j .Logger ;
4446import org .slf4j .LoggerFactory ;
4547
48+ import java .io .IOException ;
49+ import java .io .UncheckedIOException ;
50+ import java .net .MalformedURLException ;
51+ import java .net .URL ;
52+ import java .net .URLClassLoader ;
4653import java .nio .file .Path ;
4754import java .nio .file .Paths ;
4855import java .util .Set ;
@@ -65,10 +72,20 @@ public static void main(String[] args)
6572
6673 public static void serializeModules (Path outputDirectory , Iterable <String > modules )
6774 {
68- serializeModules (outputDirectory , null , modules , null );
75+ serializeModules (outputDirectory , modules , modules == null );
76+ }
77+
78+ public static void serializeModules (Path outputDirectory , Iterable <String > modules , boolean serializeIndividually )
79+ {
80+ serializeModules (outputDirectory , null , modules , null , serializeIndividually );
6981 }
7082
7183 public static void serializeModules (Path outputDirectory , ClassLoader classLoader , Iterable <String > modules , Iterable <String > excludedModules )
84+ {
85+ serializeModules (outputDirectory , classLoader , modules , excludedModules , modules == null );
86+ }
87+
88+ public static void serializeModules (Path outputDirectory , ClassLoader classLoader , Iterable <String > modules , Iterable <String > excludedModules , boolean serializeIndividually )
7289 {
7390 long start = System .nanoTime ();
7491 SetIterable <String > moduleSet = (modules == null ) ? Sets .immutable .empty () :
@@ -87,9 +104,7 @@ public static void serializeModules(Path outputDirectory, ClassLoader classLoade
87104 try
88105 {
89106 FilePathProvider filePathProvider = FilePathProvider .builder ().withLoadedExtensions (currentClassLoader ).build ();
90- RepositoryInfo repositoryInfo = resolveRepositories (moduleSet , excludedModules , currentClassLoader , filePathProvider );
91- PureRuntime runtime = compile (currentClassLoader , repositoryInfo .toCompile );
92- serialize (outputDirectory , repositoryInfo .toSerialize , runtime , filePathProvider );
107+ serializeModules (outputDirectory , currentClassLoader , moduleSet , excludedModules , filePathProvider , serializeIndividually );
93108 }
94109 catch (Throwable t )
95110 {
@@ -107,18 +122,22 @@ public static void serializeModules(Path outputDirectory, ClassLoader classLoade
107122 }
108123 }
109124
110- private static RepositoryInfo resolveRepositories ( SetIterable <String > modules , Iterable <String > excludedModules , ClassLoader classLoader , FilePathProvider filePathProvider )
125+ private static void serializeModules ( Path outputDirectory , ClassLoader classLoader , SetIterable <String > modules , Iterable <String > excludedModules , FilePathProvider filePathProvider , boolean serializeIndividually )
111126 {
112- MutableList <CodeRepository > foundRepos = CodeRepositoryProviderHelper .findCodeRepositories (true ).toList ();
127+ // Build the full repository set (with exclusions applied)
128+ MutableList <CodeRepository > foundRepos = CodeRepositoryProviderHelper .findCodeRepositories (classLoader , true ).toList ();
113129 LOGGER .debug ("Found repositories: {}" , foundRepos .asLazy ().collect (CodeRepository ::getName ));
114- CodeRepositorySet .Builder builder = CodeRepositorySet .builder ().withCodeRepositories (foundRepos );
130+ CodeRepositorySet .Builder fullSetBuilder = CodeRepositorySet .builder ().withCodeRepositories (foundRepos );
115131 if (excludedModules != null )
116132 {
117- builder .withoutCodeRepositories (excludedModules );
133+ fullSetBuilder .withoutCodeRepositories (excludedModules );
118134 }
135+
136+ // Determine which modules to serialize
119137 SetIterable <String > toSerialize ;
120138 if (modules .isEmpty ())
121139 {
140+ // Serialize all modules that don't already have a manifest on the classpath
122141 SetIterable <String > excludedSet = (excludedModules == null ) ? Sets .immutable .empty () : Sets .mutable .withAll (excludedModules );
123142 toSerialize = foundRepos .collectIf (
124143 repo -> !excludedSet .contains (repo .getName ()) && classLoader .getResource (filePathProvider .getModuleManifestResourceName (repo .getName ())) == null ,
@@ -132,14 +151,73 @@ private static RepositoryInfo resolveRepositories(SetIterable<String> modules, I
132151 }
133152 if (toSerialize .notEmpty ())
134153 {
135- builder .subset (toSerialize );
154+ fullSetBuilder .subset (toSerialize );
155+ }
156+ CodeRepositorySet allRepositories = fullSetBuilder .build ();
157+
158+ if (serializeIndividually )
159+ {
160+ // Order the modules to serialize by dependency (dependencies first)
161+ ListIterable <String > orderedModules ;
162+ if (toSerialize .size () == 1 )
163+ {
164+ orderedModules = Lists .immutable .with (toSerialize .getAny ());
165+ }
166+ else
167+ {
168+ orderedModules = toSerialize .isEmpty () ?
169+ CodeRepository .toSortedRepositoryList (allRepositories .getRepositories ()).collect (CodeRepository ::getName ) :
170+ toSerialize .toSortedList (new RepositoryComparator (allRepositories .getRepositories ()));
171+ LOGGER .debug ("Serializing modules in order: {}" , orderedModules );
172+ }
173+
174+ // Create a class loader that includes the output directory so that previously serialized modules can be loaded
175+ try (URLClassLoader outputClassLoader = newClassLoaderWithOutputDirectory (classLoader , outputDirectory ))
176+ {
177+ PureCompilerLoader loader = PureCompilerLoader .newLoader (outputClassLoader );
178+ orderedModules .forEach (m -> serializeModules (outputDirectory , classLoader , Sets .immutable .with (m ), allRepositories , loader , filePathProvider ));
179+ }
180+ catch (IOException e )
181+ {
182+ throw new UncheckedIOException ("Error closing class loader" , e );
183+ }
184+ }
185+ else
186+ {
187+ serializeModules (outputDirectory , classLoader , toSerialize , allRepositories , PureCompilerLoader .newLoader (classLoader ), filePathProvider );
136188 }
137- CodeRepositorySet resolvedRepositories = builder .build ();
138- LOGGER .debug ("Resolved repositories: {}" , resolvedRepositories .getRepositoryNames ());
139- return new RepositoryInfo (resolvedRepositories , toSerialize );
140189 }
141190
142- private static PureRuntime compile (ClassLoader classLoader , CodeRepositorySet codeRepositories )
191+ private static void serializeModules (Path outputDirectory , ClassLoader classLoader , SetIterable <String > modulesToSerialize , CodeRepositorySet modulesToCompile , PureCompilerLoader loader , FilePathProvider filePathProvider )
192+ {
193+ long moduleStart = System .nanoTime ();
194+ LOGGER .info ("Starting compilation and serialization of {}" , (modulesToSerialize .size () == 1 ) ? modulesToSerialize .getAny () : modulesToSerialize );
195+ try
196+ {
197+ LOGGER .debug ("Compiling modules {}" , modulesToCompile .getRepositoryNames ());
198+ PureRuntime runtime = compile (classLoader , loader , modulesToCompile );
199+ serialize (outputDirectory , modulesToSerialize , runtime , filePathProvider );
200+ }
201+ finally
202+ {
203+ long moduleEnd = System .nanoTime ();
204+ LOGGER .info ("Finished compilation and serialization of {} in {}s" , (modulesToSerialize .size () == 1 ) ? modulesToSerialize .getAny () : modulesToSerialize , (moduleEnd - moduleStart ) / 1_000_000_000.0 );
205+ }
206+ }
207+
208+ private static URLClassLoader newClassLoaderWithOutputDirectory (ClassLoader parent , Path outputDirectory )
209+ {
210+ try
211+ {
212+ return new URLClassLoader (new URL []{outputDirectory .toUri ().toURL ()}, parent );
213+ }
214+ catch (MalformedURLException e )
215+ {
216+ throw new RuntimeException ("Error creating class loader with output directory: " + outputDirectory , e );
217+ }
218+ }
219+
220+ private static PureRuntime compile (ClassLoader classLoader , PureCompilerLoader loader , CodeRepositorySet codeRepositories )
143221 {
144222 long start = System .nanoTime ();
145223 LOGGER .info ("Starting compilation" );
@@ -150,7 +228,6 @@ private static PureRuntime compile(ClassLoader classLoader, CodeRepositorySet co
150228 .setTransactionalByDefault (false )
151229 .build ();
152230
153- PureCompilerLoader loader = PureCompilerLoader .newLoader (classLoader );
154231 MutableList <String > reposToLoad = Lists .mutable .empty ();
155232 MutableList <String > reposToCompile = Lists .mutable .empty ();
156233 codeRepositories .getRepositoryNames ().forEach (r -> (loader .canLoad (r ) ? reposToLoad : reposToCompile ).add (r ));
@@ -264,16 +341,4 @@ private static void serialize(Path outputDirectory, SetIterable<String> modules,
264341 LOGGER .info ("Finished serialization in {}s" , (end - start ) / 1_000_000_000.0 );
265342 }
266343 }
267-
268- private static class RepositoryInfo
269- {
270- private final CodeRepositorySet toCompile ;
271- private final SetIterable <String > toSerialize ;
272-
273- private RepositoryInfo (CodeRepositorySet toCompile , SetIterable <String > toSerialize )
274- {
275- this .toCompile = toCompile ;
276- this .toSerialize = toSerialize ;
277- }
278- }
279344}
0 commit comments