From 36c86efd2c282bf71b504aaf7456cdaa3d4814d7 Mon Sep 17 00:00:00 2001 From: Adwait Kumar Singh Date: Thu, 21 May 2026 00:01:13 -0700 Subject: [PATCH] Print sdk artifact sizes --- codegen/codegen-plugin/build.gradle.kts | 62 +++++++++++++++++++ .../AwsModelCodegenCompilationTest.java | 36 ++++++++++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/codegen/codegen-plugin/build.gradle.kts b/codegen/codegen-plugin/build.gradle.kts index 01492af872..6e9ae18c73 100644 --- a/codegen/codegen-plugin/build.gradle.kts +++ b/codegen/codegen-plugin/build.gradle.kts @@ -1,3 +1,5 @@ +import java.io.Serializable + plugins { id("smithy-java.codegen-plugin-conventions") id("smithy-java.publishing-conventions") @@ -96,3 +98,63 @@ tasks.test { configureIntegTests { awsModelTests = true } + +val artifactStatsDir = project.layout.buildDirectory.dir("reports/artifact-stats") + +class PrintArtifactSizeStats( + private val statsPath: String, +) : Action, + Serializable { + override fun execute(task: Task) { + val statsFile = File(statsPath, "artifact-size-stats.tsv") + if (!statsFile.exists()) { + return + } + + val entries = + statsFile + .readLines() + .filter { it.isNotBlank() } + .map { line -> + val (name, size) = line.split('\t') + name to size.toLong() + }.sortedBy { it.second } + + if (entries.isEmpty()) { + return + } + + val total = entries.sumOf { it.second } + val avg = total / entries.size + val (minName, minSize) = entries.first() + val (maxName, maxSize) = entries.last() + + fun humanReadable(bytes: Long): String = + when { + bytes < 1024 -> "$bytes B" + bytes < 1024 * 1024 -> "%.1f KB".format(bytes / 1024.0) + else -> "%.1f MB".format(bytes / (1024.0 * 1024.0)) + } + + println() + println("========== Compiled Artifact Size Statistics ==========") + println(" SDKs processed: ${entries.size}") + println(" Average size: ${humanReadable(avg)} (%,d bytes)".format(avg)) + println(" Min size: ${humanReadable(minSize)} (%,d bytes) -> $minName".format(minSize)) + println(" Max size: ${humanReadable(maxSize)} (%,d bytes) -> $maxName".format(maxSize)) + println(" Total: ${humanReadable(total)} (%,d bytes)".format(total)) + println("=======================================================") + println() + println("Top 5 largest SDKs:") + for ((name, size) in entries.takeLast(5).reversed()) { + println(" %-50s %s".format(name, humanReadable(size))) + } + } +} + +tasks.named("integ") { + val statsPath = artifactStatsDir.get().asFile.absolutePath + systemProperty("artifactStatsDir", statsPath) + outputs.dir(artifactStatsDir) + doLast(PrintArtifactSizeStats(statsPath)) +} diff --git a/codegen/codegen-plugin/src/it/java/software/amazon/smithy/java/codegen/AwsModelCodegenCompilationTest.java b/codegen/codegen-plugin/src/it/java/software/amazon/smithy/java/codegen/AwsModelCodegenCompilationTest.java index 310b623b67..72d19fc4cb 100644 --- a/codegen/codegen-plugin/src/it/java/software/amazon/smithy/java/codegen/AwsModelCodegenCompilationTest.java +++ b/codegen/codegen-plugin/src/it/java/software/amazon/smithy/java/codegen/AwsModelCodegenCompilationTest.java @@ -20,7 +20,10 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.tools.Diagnostic; @@ -33,6 +36,7 @@ import javax.tools.SimpleJavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Named; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.junit.jupiter.api.parallel.Execution; @@ -57,11 +61,26 @@ @EnabledIfSystemProperty(named = "awsModelsTests", matches = "true") class AwsModelCodegenCompilationTest { + private static final Map artifactSizes = new ConcurrentHashMap<>(); + private static final Set IGNORED_SDK_IDS = Set.of( "timestream-write", "timestream-query", "clouddirectory"); + @AfterAll + static void writeArtifactSizeStatistics() throws IOException { + var sb = new StringBuilder(); + artifactSizes.forEach((key, value) -> sb.append(key) + .append('\t') + .append(value) + .append('\n')); + + Path outputDir = Path.of(System.getProperty("artifactStatsDir", "build")); + Files.createDirectories(outputDir); + Files.writeString(outputDir.resolve("artifact-size-stats.tsv"), sb.toString()); + } + static Stream> awsModels() { return ModelDiscovery.findModels() .stream() @@ -148,6 +167,7 @@ private void generateAndCompile(URL modelUrl, String... modes) { fail(Arrays.toString(modes) + " compilation failed for " + service.getId() + ". Generated sources dumped to: " + dumpFile + "\n" + errors); } + artifactSizes.put(artifactName(modelUrl), fm.getTotalBytesWritten()); } } } catch (Throwable t) { @@ -201,13 +221,19 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) { } /** - * In-memory output manager — discards .class bytes. + * In-memory output manager — captures .class byte counts. */ private static class InMemoryFileManager extends ForwardingJavaFileManager { + private final AtomicLong totalBytesWritten = new AtomicLong(); + InMemoryFileManager(StandardJavaFileManager delegate) { super(delegate); } + long getTotalBytesWritten() { + return totalBytesWritten.get(); + } + @Override public JavaFileObject getJavaFileForOutput( JavaFileManager.Location location, @@ -220,7 +246,13 @@ public JavaFileObject getJavaFileForOutput( kind) { @Override public OutputStream openOutputStream() { - return new ByteArrayOutputStream(); + return new ByteArrayOutputStream() { + @Override + public void close() throws IOException { + super.close(); + totalBytesWritten.addAndGet(size()); + } + }; } }; }