Skip to content

Commit

Permalink
fix: delete clients that no longer exist (#4741)
Browse files Browse the repository at this point in the history
Fixes: #4734

Note that this does not fix the 'leftover kotlin .class files' problem,
this needs to be fixed in Quarkus itself.
  • Loading branch information
stuartwdouglas authored Mar 3, 2025
1 parent 44b906c commit 379ffbd
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
Map<DeclRef, Type> typeAliasMap = new HashMap<>();
Map<DeclRef, String> nativeTypeAliasMap = new HashMap<>();
Map<DeclRef, List<EnumInfo>> enumVariantInfoMap = new HashMap<>();
Map<String, PackageOutput> packageOutputMap = new HashMap<>();
try (Stream<Path> pathStream = Files.list(context.inputDir())) {
for (var file : pathStream.toList()) {
String fileName = file.getFileName().toString();
Expand All @@ -68,8 +69,10 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
} catch (Exception e) {
throw new CodeGenException("Failed to parse " + file, e);
}
String packageName = PACKAGE_PREFIX + module.getName();
var output = packageOutputMap.computeIfAbsent(module.getName(),
(k) -> new PackageOutput(context.outDir(), packageName));
for (var decl : module.getDeclsList()) {
String packageName = PACKAGE_PREFIX + module.getName();
if (decl.hasTypeAlias()) {
var data = decl.getTypeAlias();
boolean handled = false;
Expand All @@ -85,7 +88,7 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
nativeName);
generateTypeAliasMapper(module.getName(), data, packageName,
Optional.of(nativeName),
context.outDir());
output);
handled = true;
break;
}
Expand All @@ -94,7 +97,7 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
}
if (!handled) {
generateTypeAliasMapper(module.getName(), data, packageName, Optional.empty(),
context.outDir());
output);
typeAliasMap.put(new DeclRef(module.getName(), data.getName()), data.getType());
}

Expand All @@ -108,61 +111,67 @@ public boolean trigger(CodeGenContext context) throws CodeGenException {
try {
for (var module : modules) {
String packageName = PACKAGE_PREFIX + module.getName();
var output = packageOutputMap.computeIfAbsent(module.getName(),
(k) -> new PackageOutput(context.outDir(), packageName));
for (var decl : module.getDeclsList()) {
if (decl.hasVerb()) {
var verb = decl.getVerb();
if (!verb.getExport()) {
continue;
}
generateVerb(module, verb, packageName, typeAliasMap, nativeTypeAliasMap, context.outDir());
generateVerb(module, verb, packageName, typeAliasMap, nativeTypeAliasMap, output);
} else if (decl.hasData()) {
var data = decl.getData();
if (!data.getExport()) {
continue;
}
generateDataObject(module, data, packageName, typeAliasMap, nativeTypeAliasMap, enumVariantInfoMap,
context.outDir());
output);

} else if (decl.hasEnum()) {
var data = decl.getEnum();
if (!data.getExport()) {
continue;
}
generateEnum(module, data, packageName, typeAliasMap, nativeTypeAliasMap, enumVariantInfoMap,
context.outDir());
output);
} else if (decl.hasTopic()) {
var data = decl.getTopic();
if (!data.getExport()) {
continue;
}
generateTopicConsumer(module, data, packageName, typeAliasMap, nativeTypeAliasMap,
context.outDir());
output);
}
}
}

} catch (Exception e) {
throw new CodeGenException(e);
}
for (var e : packageOutputMap.entrySet()) {
e.getValue().close();
}
return true;
}

protected abstract void generateTypeAliasMapper(String module, TypeAlias typeAlias, String packageName,
Optional<String> nativeTypeAlias, Path outputDir) throws IOException;
Optional<String> nativeTypeAlias, PackageOutput outputDir) throws IOException;

protected abstract void generateTopicConsumer(Module module, Topic data, String packageName,
Map<DeclRef, Type> typeAliasMap, Map<DeclRef, String> nativeTypeAliasMap, Path outputDir) throws IOException;
Map<DeclRef, Type> typeAliasMap, Map<DeclRef, String> nativeTypeAliasMap, PackageOutput outputDir)
throws IOException;

protected abstract void generateEnum(Module module, Enum data, String packageName, Map<DeclRef, Type> typeAliasMap,
Map<DeclRef, String> nativeTypeAliasMap, Map<DeclRef, List<EnumInfo>> enumVariantInfoMap, Path outputDir)
Map<DeclRef, String> nativeTypeAliasMap, Map<DeclRef, List<EnumInfo>> enumVariantInfoMap, PackageOutput outputDir)
throws IOException;

protected abstract void generateDataObject(Module module, Data data, String packageName, Map<DeclRef, Type> typeAliasMap,
Map<DeclRef, String> nativeTypeAliasMap, Map<DeclRef, List<EnumInfo>> enumVariantInfoMap, Path outputDir)
Map<DeclRef, String> nativeTypeAliasMap, Map<DeclRef, List<EnumInfo>> enumVariantInfoMap, PackageOutput outputDir)
throws IOException;

protected abstract void generateVerb(Module module, Verb verb, String packageName, Map<DeclRef, Type> typeAliasMap,
Map<DeclRef, String> nativeTypeAliasMap, Path outputDir) throws IOException;
Map<DeclRef, String> nativeTypeAliasMap, PackageOutput outputDir) throws IOException;

@Override
public boolean shouldRun(Path sourceDir, Config config) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package xyz.block.ftl.deployment;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.jboss.logging.Logger;

import io.quarkus.deployment.util.FileUtil;

public class PackageOutput implements AutoCloseable {

private static final Logger log = Logger.getLogger(PackageOutput.class);

private final Path outputDir;
private final String packageName;
private final Map<String, StringBuilder> files = new HashMap<>();

public PackageOutput(Path outputDir, String packageName) {
this.outputDir = outputDir;
this.packageName = packageName.replaceAll("\\.", "/");
}

public StringBuilder writeKotlin(String fileName) throws IOException {
StringBuilder value = new StringBuilder();
files.put(packageName + "/" + fileName + ".kt", value);
return value;
}

public StringBuilder writeJava(String fileName) throws IOException {
StringBuilder value = new StringBuilder();
files.put(fileName, value);
return value;
}

@Override
public void close() {
try {
Path packageDir = outputDir.resolve(packageName);
if (Files.exists(packageDir)) {
try (var s = Files.list(packageDir)) {
s.forEach(path -> {
if (!files.containsKey(path.getFileName().toString())) {
try {
FileUtil.deleteIfExists(path);
} catch (IOException e) {
log.errorf(e, "Failed to delete path: %s", path);
}
}
});
}
}
Files.createDirectories(packageDir);
for (var e : files.entrySet()) {
Path target = outputDir.resolve(e.getKey());
byte[] fileBytes = e.getValue().toString().getBytes(StandardCharsets.UTF_8);
if (Files.exists(target)) {
var existing = Files.readAllBytes(target);
if (Arrays.equals(fileBytes, existing)) {
continue;
}
}
Files.write(target, fileBytes);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package xyz.block.ftl.javalang.deployment;

import java.io.IOException;
import java.nio.file.Path;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -34,6 +33,7 @@
import xyz.block.ftl.TypeAliasMapper;
import xyz.block.ftl.VerbClient;
import xyz.block.ftl.deployment.JVMCodeGenerator;
import xyz.block.ftl.deployment.PackageOutput;
import xyz.block.ftl.deployment.VerbType;
import xyz.block.ftl.schema.v1.Data;
import xyz.block.ftl.schema.v1.Enum;
Expand All @@ -51,7 +51,7 @@ public class JavaCodeGenerator extends JVMCodeGenerator {

@Override
protected void generateTypeAliasMapper(String module, xyz.block.ftl.schema.v1.TypeAlias typeAlias,
String packageName, Optional<String> nativeTypeAlias, Path outputDir) throws IOException {
String packageName, Optional<String> nativeTypeAlias, PackageOutput outputDir) throws IOException {
TypeSpec.Builder typeBuilder = TypeSpec.interfaceBuilder(className(typeAlias.getName()) + TYPE_MAPPER)
.addAnnotation(AnnotationSpec.builder(TypeAlias.class)
.addMember("name", "\"" + typeAlias.getName() + "\"")
Expand All @@ -71,11 +71,11 @@ protected void generateTypeAliasMapper(String module, xyz.block.ftl.schema.v1.Ty
TypeSpec theType = typeBuilder.build();
JavaFile javaFile = JavaFile.builder(packageName, theType)
.build();
javaFile.writeTo(outputDir);
javaFile.writeTo(outputDir.writeJava(javaFile.toJavaFileObject().getName()));
}

protected void generateTopicConsumer(Module module, Topic data, String packageName, Map<DeclRef, Type> typeAliasMap,
Map<DeclRef, String> nativeTypeAliasMap, Path outputDir) throws IOException {
Map<DeclRef, String> nativeTypeAliasMap, PackageOutput outputDir) throws IOException {
String thisType = className(data.getName());

TypeSpec.Builder dataBuilder = TypeSpec.interfaceBuilder(thisType)
Expand All @@ -102,11 +102,11 @@ protected void generateTopicConsumer(Module module, Topic data, String packageNa
JavaFile javaFile = JavaFile.builder(packageName, dataBuilder.build())
.build();

javaFile.writeTo(outputDir);
javaFile.writeTo(outputDir.writeJava(javaFile.toJavaFileObject().getName()));
}

protected void generateEnum(Module module, Enum ennum, String packageName, Map<DeclRef, Type> typeAliasMap,
Map<DeclRef, String> nativeTypeAliasMap, Map<DeclRef, List<EnumInfo>> enumVariantInfoMap, Path outputDir)
Map<DeclRef, String> nativeTypeAliasMap, Map<DeclRef, List<EnumInfo>> enumVariantInfoMap, PackageOutput outputDir)
throws IOException {
String interfaceType = className(ennum.getName());
if (ennum.hasType()) {
Expand All @@ -131,7 +131,7 @@ protected void generateEnum(Module module, Enum ennum, String packageName, Map<D
dataBuilder.addEnumConstant(i.getName(), TypeSpec.anonymousClassBuilder(format, value).build());
}
JavaFile javaFile = JavaFile.builder(packageName, dataBuilder.build()).build();
javaFile.writeTo(outputDir);
javaFile.writeTo(outputDir.writeJava(javaFile.toJavaFileObject().getName()));
} else {
// Enums without a type are (confusingly) "type enums". Java can't represent these directly, so we use a
// sealed class
Expand Down Expand Up @@ -187,16 +187,16 @@ protected void generateEnum(Module module, Enum ennum, String packageName, Map<D
addTypeEnumInterfaceMethods(packageName, interfaceType, dataBuilder, name, valueType,
variantValuesTypes, false);
JavaFile javaFile = JavaFile.builder(packageName, dataBuilder.build()).build();
javaFile.writeTo(outputDir);
javaFile.writeTo(outputDir.writeJava(javaFile.toJavaFileObject().getName()));
}
}
JavaFile javaFile = JavaFile.builder(packageName, interfaceBuilder.build()).build();
javaFile.writeTo(outputDir);
javaFile.writeTo(outputDir.writeJava(javaFile.toJavaFileObject().getName()));
}
}

protected void generateDataObject(Module module, Data data, String packageName, Map<DeclRef, Type> typeAliasMap,
Map<DeclRef, String> nativeTypeAliasMap, Map<DeclRef, List<EnumInfo>> enumVariantInfoMap, Path outputDir)
Map<DeclRef, String> nativeTypeAliasMap, Map<DeclRef, List<EnumInfo>> enumVariantInfoMap, PackageOutput outputDir)
throws IOException {
String thisType = className(data.getName());
TypeSpec.Builder dataBuilder = TypeSpec.classBuilder(thisType)
Expand Down Expand Up @@ -267,11 +267,11 @@ protected void generateDataObject(Module module, Data data, String packageName,
JavaFile javaFile = JavaFile.builder(packageName, dataBuilder.build())
.build();

javaFile.writeTo(outputDir);
javaFile.writeTo(outputDir.writeJava(javaFile.toJavaFileObject().getName()));
}

protected void generateVerb(Module module, Verb verb, String packageName, Map<DeclRef, Type> typeAliasMap,
Map<DeclRef, String> nativeTypeAliasMap, Path outputDir)
Map<DeclRef, String> nativeTypeAliasMap, PackageOutput outputDir)
throws IOException {
String verbName = verb.getName();
TypeSpec.Builder clientBuilder = TypeSpec.interfaceBuilder(className(verbName) + CLIENT)
Expand All @@ -297,7 +297,7 @@ protected void generateVerb(Module module, Verb verb, String packageName, Map<De
}
clientBuilder.addMethod(callMethod.build());
JavaFile javaFile = JavaFile.builder(packageName, clientBuilder.build()).build();
javaFile.writeTo(outputDir);
javaFile.writeTo(outputDir.writeJava(javaFile.toJavaFileObject().getName()));
}

private String toJavaName(String name) {
Expand Down
Loading

0 comments on commit 379ffbd

Please # to comment.