diff --git a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/ShapeGenerationOrder.java b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/ShapeGenerationOrder.java
new file mode 100644
index 00000000000..23bb7abc81f
--- /dev/null
+++ b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/ShapeGenerationOrder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.smithy.codegen.core;
+
+/**
+ * Shapes order for code generation.
+ *
+ *
CodegenDirector order the shapes appropriately before feeding them to the code generators. See {@link
+ * software.amazon.smithy.codegen.core.directed.CodegenDirector#shapeGenerationOrder(ShapeGenerationOrder)}
+ */
+public enum ShapeGenerationOrder {
+ /**
+ * Shapes ordered in reverse-topological order. Also see {@link TopologicalIndex}
+ */
+ TOPOLOGICAL,
+
+ /**
+ * Shapes ordered alphabetically by their names.
+ */
+ ALPHABETICAL,
+
+ /**
+ * Shapes without order.
+ */
+ NONE
+}
diff --git a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/CodegenDirector.java b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/CodegenDirector.java
index b70bdecea68..f6c994bca56 100644
--- a/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/CodegenDirector.java
+++ b/smithy-codegen-core/src/main/java/software/amazon/smithy/codegen/core/directed/CodegenDirector.java
@@ -16,6 +16,7 @@
package software.amazon.smithy.codegen.core.directed;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.ServiceLoader;
@@ -26,6 +27,7 @@
import software.amazon.smithy.build.FileManifest;
import software.amazon.smithy.codegen.core.CodegenContext;
import software.amazon.smithy.codegen.core.ImportContainer;
+import software.amazon.smithy.codegen.core.ShapeGenerationOrder;
import software.amazon.smithy.codegen.core.SmithyIntegration;
import software.amazon.smithy.codegen.core.SymbolProvider;
import software.amazon.smithy.codegen.core.SymbolWriter;
@@ -75,6 +77,7 @@ public final class CodegenDirector<
private Supplier> integrationFinder;
private DirectedCodegen directedCodegen;
private final List> transforms = new ArrayList<>();
+ private ShapeGenerationOrder shapeGenerationOrder = ShapeGenerationOrder.TOPOLOGICAL;
/**
* Simplifies a Smithy model for code generation of a single service.
@@ -250,6 +253,18 @@ public void changeStringEnumsToEnumShapes(boolean synthesizeEnumNames) {
});
}
+ /**
+ * Sets the shapes order for code generation.
+ *
+ * CodegenDirector order the shapes appropriately before passing them to the code generators.
+ * The default order is topological, and can be overridden with this method
+ *
+ * @param order the order to use for the shape generation process.
+ */
+ public void shapeGenerationOrder(ShapeGenerationOrder order) {
+ this.shapeGenerationOrder = order;
+ }
+
/**
* Sorts all members of the model prior to codegen.
*
@@ -328,6 +343,7 @@ private void validateState() {
SmithyBuilder.requiredState("settings", settings);
SmithyBuilder.requiredState("fileManifest", fileManifest);
SmithyBuilder.requiredState("directedCodegen", directedCodegen);
+ SmithyBuilder.requiredState("shapeGenerationOrder", shapeGenerationOrder);
// Use a default integration finder implementation.
if (integrationFinder == null) {
@@ -390,16 +406,36 @@ private void registerInterceptors(C context, List integrations) {
}
private void generateShapesInService(C context, ServiceShape serviceShape) {
- LOGGER.fine(() -> "Generating shapes for " + directedCodegen.getClass().getName());
+ LOGGER.fine(() -> String.format("Generating shapes for %s in %s order",
+ directedCodegen.getClass().getName(), this.shapeGenerationOrder.name()));
Set shapes = new Walker(context.model()).walkShapes(serviceShape);
- TopologicalIndex topologicalIndex = TopologicalIndex.of(context.model());
ShapeGenerator generator = new ShapeGenerator<>(context, serviceShape, directedCodegen);
- for (Shape shape : topologicalIndex.getOrderedShapes()) {
- if (shapes.contains(shape)) {
- shape.accept(generator);
- }
+ List orderedShapes = new ArrayList<>();
+
+ switch (this.shapeGenerationOrder) {
+ case ALPHABETICAL:
+ orderedShapes.addAll(shapes);
+ orderedShapes.sort(Comparator.comparing(s -> s.getId().getName(serviceShape)));
+ break;
+ case NONE:
+ orderedShapes.addAll(shapes);
+ break;
+ case TOPOLOGICAL:
+ default:
+ TopologicalIndex topologicalIndex = TopologicalIndex.of(context.model());
+ for (Shape shape : topologicalIndex.getOrderedShapes()) {
+ if (shapes.contains(shape)) {
+ orderedShapes.add(shape);
+ }
+ }
+ for (Shape shape : topologicalIndex.getRecursiveShapes()) {
+ if (shapes.contains(shape)) {
+ orderedShapes.add(shape);
+ }
+ }
}
- for (Shape shape : topologicalIndex.getRecursiveShapes()) {
+
+ for (Shape shape : orderedShapes) {
if (shapes.contains(shape)) {
shape.accept(generator);
}
diff --git a/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CodegenDirectorTest.java b/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CodegenDirectorTest.java
index 79dc47eea1b..8c7f07273a9 100644
--- a/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CodegenDirectorTest.java
+++ b/smithy-codegen-core/src/test/java/software/amazon/smithy/codegen/core/directed/CodegenDirectorTest.java
@@ -26,6 +26,7 @@
import org.junit.jupiter.api.Test;
import software.amazon.smithy.build.FileManifest;
import software.amazon.smithy.build.MockManifest;
+import software.amazon.smithy.codegen.core.ShapeGenerationOrder;
import software.amazon.smithy.codegen.core.Symbol;
import software.amazon.smithy.codegen.core.SymbolProvider;
import software.amazon.smithy.codegen.core.WriterDelegator;
@@ -227,7 +228,7 @@ public void performsCodegenWithStringEnumsChangedToEnumShapes() {
}
@Test
- public void sortsShapes() {
+ public void sortsShapesWithDefaultTopologicalOrder() {
TestDirected testDirected = new TestDirected();
CodegenDirector runner
= new CodegenDirector<>();
@@ -260,4 +261,72 @@ public void sortsShapes() {
ShapeId.from("smithy.example#Foo")
));
}
+
+ @Test
+ public void testShapesGenerationWithAlphabeticalOrder() {
+ TestDirected testDirected = new TestDirected();
+ CodegenDirector runner
+ = new CodegenDirector<>();
+ FileManifest manifest = new MockManifest();
+ Model model = Model.assembler()
+ .addImport(getClass().getResource("needs-sorting.smithy"))
+ .assemble()
+ .unwrap();
+
+ runner.settings(new TestSettings());
+ runner.directedCodegen(testDirected);
+ runner.fileManifest(manifest);
+ runner.service(ShapeId.from("smithy.example#Foo"));
+ runner.model(model);
+ runner.integrationClass(TestIntegration.class);
+ runner.performDefaultCodegenTransforms();
+ runner.shapeGenerationOrder(ShapeGenerationOrder.ALPHABETICAL);
+ runner.run();
+
+ assertThat(testDirected.generatedShapes, contains(
+ ShapeId.from("smithy.example#A"),
+ ShapeId.from("smithy.example#B"),
+ ShapeId.from("smithy.example#C"),
+ ShapeId.from("smithy.example#D"),
+ ShapeId.from("smithy.example#FooOperationInput"),
+ ShapeId.from("smithy.example#FooOperationOutput"),
+ ShapeId.from("smithy.example#RecursiveA"),
+ ShapeId.from("smithy.example#RecursiveB"),
+ ShapeId.from("smithy.example#Foo")
+ ));
+ }
+
+ @Test
+ public void testShapesGenerationWithoutOrder() {
+ TestDirected testDirected = new TestDirected();
+ CodegenDirector runner
+ = new CodegenDirector<>();
+ FileManifest manifest = new MockManifest();
+ Model model = Model.assembler()
+ .addImport(getClass().getResource("needs-sorting.smithy"))
+ .assemble()
+ .unwrap();
+
+ runner.settings(new TestSettings());
+ runner.directedCodegen(testDirected);
+ runner.fileManifest(manifest);
+ runner.service(ShapeId.from("smithy.example#Foo"));
+ runner.model(model);
+ runner.integrationClass(TestIntegration.class);
+ runner.performDefaultCodegenTransforms();
+ runner.shapeGenerationOrder(ShapeGenerationOrder.NONE);
+ runner.run();
+
+ assertThat(testDirected.generatedShapes, contains(
+ ShapeId.from("smithy.example#FooOperationOutput"),
+ ShapeId.from("smithy.example#A"),
+ ShapeId.from("smithy.example#B"),
+ ShapeId.from("smithy.example#C"),
+ ShapeId.from("smithy.example#D"),
+ ShapeId.from("smithy.example#FooOperationInput"),
+ ShapeId.from("smithy.example#RecursiveA"),
+ ShapeId.from("smithy.example#RecursiveB"),
+ ShapeId.from("smithy.example#Foo")
+ ));
+ }
}