From 2e845bc12f10ee3e3f455f2ca806cf59124ba211 Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Thu, 6 Mar 2025 18:18:32 +0800 Subject: [PATCH 1/9] minor refactor --- .../postprocessor/implementation/CodeFormatterUtil.java | 6 +++--- .../http/client/generator/mgmt/template/SampleTemplate.java | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java index 2d230031e31..24776929d9a 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java @@ -41,10 +41,10 @@ public static void formatCode(Map files, NewPlugin plugin) { * * @param files The files to format. The entry is filename and content. * @return the files after format. - * @throws Exception If code formatting fails. + * @throws RuntimeException If code formatting fails. */ - public static List formatCode(Collection> files) throws Exception { - return formatCodeInternal(files).stream().map(Map.Entry::getValue).collect(Collectors.toList()); + public static List formatCode(Map files) { + return formatCodeInternal(files.entrySet()).stream().map(Map.Entry::getValue).collect(Collectors.toList()); } private static List> formatCodeInternal(Collection> files) { diff --git a/packages/http-client-java/generator/http-client-generator-mgmt/src/main/java/com/microsoft/typespec/http/client/generator/mgmt/template/SampleTemplate.java b/packages/http-client-java/generator/http-client-generator-mgmt/src/main/java/com/microsoft/typespec/http/client/generator/mgmt/template/SampleTemplate.java index 007c242e448..4f2cb5ae39e 100644 --- a/packages/http-client-java/generator/http-client-generator-mgmt/src/main/java/com/microsoft/typespec/http/client/generator/mgmt/template/SampleTemplate.java +++ b/packages/http-client-java/generator/http-client-generator-mgmt/src/main/java/com/microsoft/typespec/http/client/generator/mgmt/template/SampleTemplate.java @@ -24,9 +24,8 @@ public String write(List examples, List sampleJavaFiles assert examples.size() == sampleJavaFiles.size(); // clean up copyright etc. - List> javaFiles = sampleJavaFiles.stream() - .map(e -> Map.entry(e.getFilePath(), cleanJavaFile(e))) - .collect(Collectors.toList()); + Map javaFiles + = sampleJavaFiles.stream().collect(Collectors.toMap(JavaFile::getFilePath, SampleTemplate::cleanJavaFile)); // format code List javaFileContents; try { From 0e24edd632803b88c3ea3c51a9bb3a35993e8896 Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Thu, 6 Mar 2025 19:22:46 +0800 Subject: [PATCH 2/9] when spotless fail, loop the files and log failed file and content --- .../implementation/CodeFormatterUtil.java | 57 ++++++++++++++++++- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java index 24776929d9a..6ffaf4d15f6 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java @@ -3,9 +3,11 @@ package com.microsoft.typespec.http.client.generator.core.postprocessor.implementation; +import com.microsoft.typespec.http.client.generator.core.Javagen; import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils; import com.microsoft.typespec.http.client.generator.core.extension.base.util.FileUtils; import com.microsoft.typespec.http.client.generator.core.extension.plugin.NewPlugin; +import com.microsoft.typespec.http.client.generator.core.extension.plugin.PluginLogger; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; @@ -18,12 +20,22 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.slf4j.Logger; /** * Utility class that handles code formatting. */ public final class CodeFormatterUtil { + + private static final Logger LOGGER = new PluginLogger(Javagen.getPluginInstance(), CodeFormatterUtil.class); + + private static final Pattern SPOTLESS_ERROR_PATTERN + = Pattern.compile("^(\\d+):\\d+: error: (.*)$", Pattern.MULTILINE); + private static final int SPOTLESS_FILE_CONTENT_RANGE = 3; + /** * Formats the given files by removing unused imports and applying Eclipse code formatting. * @@ -31,8 +43,41 @@ public final class CodeFormatterUtil { * @param plugin The plugin to use to write the formatted files. */ public static void formatCode(Map files, NewPlugin plugin) { - for (Map.Entry file : formatCodeInternal(files.entrySet())) { - plugin.writeFile(file.getKey(), file.getValue(), null); + try { + for (Map.Entry file : formatCodeInternal(files.entrySet())) { + plugin.writeFile(file.getKey(), file.getValue(), null); + } + } catch (SpotlessException ex) { + // format one file at a time, to give better error diagnostics + for (Map.Entry file : files.entrySet()) { + try { + formatCodeInternal(List.of(file)); + } catch (RuntimeException e) { + Matcher matcher = SPOTLESS_ERROR_PATTERN.matcher(e.getMessage()); + String content = file.getValue(); + if (matcher.find()) { + int lineNumber = Integer.parseInt(matcher.group(1)); + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("line: ") + .append(lineNumber) + .append(", error: ") + .append(matcher.group(2)) + .append("\n"); + + // line number from log start from 1 + String[] lines = content.split("\n"); + int lineBegin = Math.max(0, lineNumber - 1 - SPOTLESS_FILE_CONTENT_RANGE); + int lineEnd = Math.min(lines.length - 1, lineNumber - 1 + SPOTLESS_FILE_CONTENT_RANGE); + for (int i = lineBegin; i <= lineEnd; ++i) { + stringBuilder.append(i + 1).append(" ").append(lines[i]).append("\n"); + } + content = stringBuilder.toString(); + } + LOGGER.error("Failed to format file '{}'\n{}", file.getKey(), content); + } + } + throw ex; } } @@ -101,7 +146,7 @@ private static void attemptMavenSpotless(Path pomPath) { if (process.isAlive() || process.exitValue() != 0) { process.destroyForcibly(); - throw new RuntimeException( + throw new SpotlessException( "Spotless failed to complete within 300 seconds or failed with an error code. " + Files.readString(outputFile.toPath())); } @@ -109,4 +154,10 @@ private static void attemptMavenSpotless(Path pomPath) { throw new RuntimeException("Failed to run Spotless on generated code.", ex); } } + + private static final class SpotlessException extends RuntimeException { + public SpotlessException(String message) { + super(message); + } + } } From acc8a923b204cd966439a9f254492b2b360c9eab Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Thu, 6 Mar 2025 19:38:50 +0800 Subject: [PATCH 3/9] update logger --- .../core/postprocessor/Postprocessor.java | 2 +- .../implementation/CodeFormatterUtil.java | 14 +++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/Postprocessor.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/Postprocessor.java index d44ce8ec440..4aad12e272a 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/Postprocessor.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/Postprocessor.java @@ -119,7 +119,7 @@ public static void writeToFiles(Map javaFiles, NewPlugin plugin, } try { - CodeFormatterUtil.formatCode(javaFiles, plugin); + CodeFormatterUtil.formatCode(javaFiles, plugin, logger); } catch (Exception ex) { throw new RuntimeException(ex); } diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java index 6ffaf4d15f6..8998f823c6f 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java @@ -3,11 +3,9 @@ package com.microsoft.typespec.http.client.generator.core.postprocessor.implementation; -import com.microsoft.typespec.http.client.generator.core.Javagen; import com.microsoft.typespec.http.client.generator.core.customization.implementation.Utils; import com.microsoft.typespec.http.client.generator.core.extension.base.util.FileUtils; import com.microsoft.typespec.http.client.generator.core.extension.plugin.NewPlugin; -import com.microsoft.typespec.http.client.generator.core.extension.plugin.PluginLogger; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; @@ -30,8 +28,6 @@ */ public final class CodeFormatterUtil { - private static final Logger LOGGER = new PluginLogger(Javagen.getPluginInstance(), CodeFormatterUtil.class); - private static final Pattern SPOTLESS_ERROR_PATTERN = Pattern.compile("^(\\d+):\\d+: error: (.*)$", Pattern.MULTILINE); private static final int SPOTLESS_FILE_CONTENT_RANGE = 3; @@ -42,7 +38,7 @@ public final class CodeFormatterUtil { * @param files The files to format. * @param plugin The plugin to use to write the formatted files. */ - public static void formatCode(Map files, NewPlugin plugin) { + public static void formatCode(Map files, NewPlugin plugin, Logger logger) { try { for (Map.Entry file : formatCodeInternal(files.entrySet())) { plugin.writeFile(file.getKey(), file.getValue(), null); @@ -59,11 +55,7 @@ public static void formatCode(Map files, NewPlugin plugin) { int lineNumber = Integer.parseInt(matcher.group(1)); StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("line: ") - .append(lineNumber) - .append(", error: ") - .append(matcher.group(2)) - .append("\n"); + stringBuilder.append(matcher.group(0)).append("\n"); // line number from log start from 1 String[] lines = content.split("\n"); @@ -74,7 +66,7 @@ public static void formatCode(Map files, NewPlugin plugin) { } content = stringBuilder.toString(); } - LOGGER.error("Failed to format file '{}'\n{}", file.getKey(), content); + logger.error("Failed to format file '{}'\n{}", file.getKey(), content); } } throw ex; From 803836b45d58dabefd38977beb60bf52a2a5e2a1 Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Thu, 6 Mar 2025 19:53:21 +0800 Subject: [PATCH 4/9] comments --- .../implementation/CodeFormatterUtil.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java index 8998f823c6f..bcf54519edc 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java @@ -49,20 +49,24 @@ public static void formatCode(Map files, NewPlugin plugin, Logge try { formatCodeInternal(List.of(file)); } catch (RuntimeException e) { - Matcher matcher = SPOTLESS_ERROR_PATTERN.matcher(e.getMessage()); + // by default, log the whole file String content = file.getValue(); + + // if we can find the line number from the error message, refine the "content" to the part of file + // around the line + Matcher matcher = SPOTLESS_ERROR_PATTERN.matcher(e.getMessage()); if (matcher.find()) { int lineNumber = Integer.parseInt(matcher.group(1)); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(matcher.group(0)).append("\n"); - // line number from log start from 1 + // line number from log starts from 1 String[] lines = content.split("\n"); - int lineBegin = Math.max(0, lineNumber - 1 - SPOTLESS_FILE_CONTENT_RANGE); - int lineEnd = Math.min(lines.length - 1, lineNumber - 1 + SPOTLESS_FILE_CONTENT_RANGE); - for (int i = lineBegin; i <= lineEnd; ++i) { - stringBuilder.append(i + 1).append(" ").append(lines[i]).append("\n"); + int lineIndexBegin = Math.max(0, lineNumber - 1 - SPOTLESS_FILE_CONTENT_RANGE); + int lineIndexEnd = Math.min(lines.length - 1, lineNumber - 1 + SPOTLESS_FILE_CONTENT_RANGE); + for (int lineIndex = lineIndexBegin; lineIndex <= lineIndexEnd; ++lineIndex) { + stringBuilder.append(lineIndex + 1).append(" ").append(lines[lineIndex]).append("\n"); } content = stringBuilder.toString(); } From b20f01de767f4aa8644c8f2b965b4053ae6f7ca2 Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Thu, 6 Mar 2025 23:15:21 +0800 Subject: [PATCH 5/9] do not throw error out of Main --- .../implementation/CodeFormatterUtil.java | 2 +- .../main/resources/simplelogger.properties | 35 ---------- .../typespec/http/client/generator/Main.java | 67 ++++++++++--------- .../main/resources/simplelogger.properties | 5 +- 4 files changed, 40 insertions(+), 69 deletions(-) delete mode 100644 packages/http-client-java/generator/http-client-generator-core/src/main/resources/simplelogger.properties diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java index bcf54519edc..3e5ed317fdd 100644 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java +++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/postprocessor/implementation/CodeFormatterUtil.java @@ -143,7 +143,7 @@ private static void attemptMavenSpotless(Path pomPath) { if (process.isAlive() || process.exitValue() != 0) { process.destroyForcibly(); throw new SpotlessException( - "Spotless failed to complete within 300 seconds or failed with an error code. " + "Spotless failed to complete within 300 seconds or failed with an error code. Output:\n" + Files.readString(outputFile.toPath())); } } catch (IOException | InterruptedException ex) { diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/resources/simplelogger.properties b/packages/http-client-java/generator/http-client-generator-core/src/main/resources/simplelogger.properties deleted file mode 100644 index 1a392fcac60..00000000000 --- a/packages/http-client-java/generator/http-client-generator-core/src/main/resources/simplelogger.properties +++ /dev/null @@ -1,35 +0,0 @@ -# SLF4J's SimpleLogger configuration file -# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. -org.slf4j.simpleLogger.logFile=System.out - -# Default logging detail level for all instances of SimpleLogger. -# Must be one of ("trace", "debug", "info", "warn", or "error"). -# If not specified, defaults to "info". -org.slf4j.simpleLogger.defaultLogLevel=error - -# Logging detail level for a SimpleLogger instance named "xxxxx". -# Must be one of ("trace", "debug", "info", "warn", or "error"). -# If not specified, the default logging detail level is used. -#org.slf4j.simpleLogger.log.xxxxx= - -# Set to true if you want the current date and time to be included in output messages. -# Default is false, and will output the number of milliseconds elapsed since startup. -#org.slf4j.simpleLogger.showDateTime=false - -# The date and time format to be used in the output messages. -# The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat. -# If the format is not specified or is invalid, the default format is used. -# The default format is yyyy-MM-dd HH:mm:ss:SSS Z. -#org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z - -# Set to true if you want to output the current thread name. -# Defaults to true. -#org.slf4j.simpleLogger.showThreadName=true - -# Set to true if you want the Logger instance name to be included in output messages. -# Defaults to true. -#org.slf4j.simpleLogger.showLogName=true - -# Set to true if you want the last component of the name to be included in output messages. -# Defaults to false. -#org.slf4j.simpleLogger.showShortLogName=false diff --git a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/Main.java b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/Main.java index 5821ec445ed..8b3d71842e0 100644 --- a/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/Main.java +++ b/packages/http-client-java/generator/http-client-generator/src/main/java/com/microsoft/typespec/http/client/generator/Main.java @@ -50,45 +50,52 @@ public class Main { // java -jar target/azure-typespec-extension-jar-with-dependencies.jar public static void main(String[] args) throws IOException { - // parameters - String inputYamlFileName = DEFAULT_OUTPUT_DIR + "code-model.yaml"; - if (args.length >= 1) { - inputYamlFileName = args[0]; - } + try { + // parameters + String inputYamlFileName = DEFAULT_OUTPUT_DIR + "code-model.yaml"; + if (args.length >= 1) { + inputYamlFileName = args[0]; + } + + LOGGER.info("Code model file: {}", inputYamlFileName); - LOGGER.info("Code model file: {}", inputYamlFileName); + // load code-model.yaml + CodeModel codeModel = loadCodeModel(inputYamlFileName); - // load code-model.yaml - CodeModel codeModel = loadCodeModel(inputYamlFileName); + EmitterOptions emitterOptions = loadEmitterOptions(codeModel); - EmitterOptions emitterOptions = loadEmitterOptions(codeModel); + boolean sdkIntegration = true; + String outputDir = emitterOptions.getOutputDir(); + Path outputDirPath = Paths.get(outputDir); + if (Files.exists(outputDirPath)) { + if (emitterOptions.getArm()) { + // check ../../parents/azure-client-sdk-parent + sdkIntegration = Files.exists(Paths.get(outputDir, "../../parents/azure-client-sdk-parent")); + } else { + try (Stream filestream = Files.list(outputDirPath)) { + Set filenames = filestream.map(p -> p.getFileName().toString()) + .map(name -> name.toLowerCase(Locale.ROOT)) + .collect(Collectors.toSet()); + + // if there is already pom and source, do not overwrite them (includes README.md, CHANGELOG.md + // etc.) + sdkIntegration = !filenames.containsAll(Arrays.asList("pom.xml", "src")); + } + } + } - boolean sdkIntegration = true; - String outputDir = emitterOptions.getOutputDir(); - Path outputDirPath = Paths.get(outputDir); - if (Files.exists(outputDirPath)) { if (emitterOptions.getArm()) { - // check ../../parents/azure-client-sdk-parent - sdkIntegration = Files.exists(Paths.get(outputDir, "../../parents/azure-client-sdk-parent")); + handleFluent(codeModel, emitterOptions, sdkIntegration); } else { - try (Stream filestream = Files.list(outputDirPath)) { - Set filenames = filestream.map(p -> p.getFileName().toString()) - .map(name -> name.toLowerCase(Locale.ROOT)) - .collect(Collectors.toSet()); - - // if there is already pom and source, do not overwrite them (includes README.md, CHANGELOG.md etc.) - sdkIntegration = !filenames.containsAll(Arrays.asList("pom.xml", "src")); - } + handleDPG(codeModel, emitterOptions, sdkIntegration, outputDir); } - } - if (emitterOptions.getArm()) { - handleFluent(codeModel, emitterOptions, sdkIntegration); - } else { - handleDPG(codeModel, emitterOptions, sdkIntegration, outputDir); + // ensure the process exits as expected + System.exit(0); + } catch (Exception e) { + LOGGER.error("Unhandled error.", e); + System.exit(1); } - // ensure the process exits as expected - System.exit(0); } private static void handleFluent(CodeModel codeModel, EmitterOptions emitterOptions, boolean sdkIntegration) { diff --git a/packages/http-client-java/generator/http-client-generator/src/main/resources/simplelogger.properties b/packages/http-client-java/generator/http-client-generator/src/main/resources/simplelogger.properties index 5f370f28d7b..9ef1ac091d3 100644 --- a/packages/http-client-java/generator/http-client-generator/src/main/resources/simplelogger.properties +++ b/packages/http-client-java/generator/http-client-generator/src/main/resources/simplelogger.properties @@ -2,11 +2,10 @@ # Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. org.slf4j.simpleLogger.logFile=System.out - # Default logging detail level for all instances of SimpleLogger. # Must be one of ("trace", "debug", "info", "warn", or "error"). # If not specified, defaults to "info". -org.slf4j.simpleLogger.defaultLogLevel=error +org.slf4j.simpleLogger.defaultLogLevel=warn # Logging detail level for a SimpleLogger instance named "xxxxx". # Must be one of ("trace", "debug", "info", "warn", or "error"). @@ -25,7 +24,7 @@ org.slf4j.simpleLogger.defaultLogLevel=error # Set to true if you want to output the current thread name. # Defaults to true. -#org.slf4j.simpleLogger.showThreadName=true +org.slf4j.simpleLogger.showThreadName=false # Set to true if you want the Logger instance name to be included in output messages. # Defaults to true. From 1a3ad3c6729b958e7c1c9059390a8c89718a9a25 Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Thu, 6 Mar 2025 23:40:21 +0800 Subject: [PATCH 6/9] parse log in emitter --- .../http-client-java/emitter/src/emitter.ts | 73 +++++++++++++++++-- packages/http-client-java/emitter/src/lib.ts | 12 +++ 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/packages/http-client-java/emitter/src/emitter.ts b/packages/http-client-java/emitter/src/emitter.ts index 0609ebda8f1..0b9f8fdb513 100644 --- a/packages/http-client-java/emitter/src/emitter.ts +++ b/packages/http-client-java/emitter/src/emitter.ts @@ -1,4 +1,10 @@ -import { EmitContext, getNormalizedAbsolutePath, NoTarget, resolvePath } from "@typespec/compiler"; +import { + EmitContext, + getNormalizedAbsolutePath, + NoTarget, + Program, + resolvePath, +} from "@typespec/compiler"; import { promises } from "fs"; import { dump } from "js-yaml"; import { dirname } from "path"; @@ -106,7 +112,8 @@ export async function $onEmit(context: EmitContext) { javaArgs.push(codeModelFileName); try { const result = await spawnAsync("java", javaArgs, { stdio: "pipe" }); - trace(program, `Code generation log: ${result.stdout}`); + reportJarOutput(program, result.stdout); + // trace(program, `Code generation log: ${result.stdout}`); } catch (error: any) { if (error && "code" in error && error["code"] === "ENOENT") { reportDiagnostic(program, { @@ -114,18 +121,19 @@ export async function $onEmit(context: EmitContext) { target: NoTarget, }); } else { + if (error instanceof SpawnError) { + reportJarOutput(program, error.stdout); + // trace(program, `Code generation log: ${error.stdout}`); + } + // error in Java codegen, report as unknown error reportDiagnostic(program, { code: "unknown-error", format: { - errorMessage: `The emitter was unable to generate client code from this TypeSpec, please open an issue on https://github.com/microsoft/typespec, include TypeSpec source and all the diagnostic information in your submission.\nOutput: ${error.stdout}\nError: ${error.stderr}`, + errorMessage: `The emitter was unable to generate client code from this TypeSpec, please open an issue on https://github.com/microsoft/typespec, include TypeSpec source and all the diagnostic information in your submission.`, }, target: NoTarget, }); - if (error instanceof SpawnError) { - trace(program, `Code generation log: ${error.stdout}`); - trace(program, `Code generation error: ${error.stderr}`); - } } } @@ -135,3 +143,54 @@ export async function $onEmit(context: EmitContext) { } } } + +function reportJarOutput(program: Program, jarOutput: string) { + const lines = jarOutput.split("\n"); + const logs: Array = []; + + // parse stdout to array of logs + let currentLog = undefined; + for (const line of lines) { + if ( + line.startsWith("TRACE ") || + line.startsWith("DEBUG ") || + line.startsWith("INFO ") || + line.startsWith("WARN ") || + line.startsWith("ERROR ") + ) { + if (currentLog) { + logs.push(currentLog); + } + currentLog = line; + } else if (currentLog) { + currentLog = currentLog + "\n" + line; + } + } + if (currentLog) { + logs.push(currentLog); + } + + // trace or report the logs, according to log level + for (const log of logs) { + if (log.startsWith("ERROR ")) { + reportDiagnostic(program, { + code: "jar-error", + format: { + errorMessage: log.substring(6), + }, + target: NoTarget, + }); + } else if (log.startsWith("WARN ")) { + reportDiagnostic(program, { + code: "jar-warning", + format: { + warningMessage: log.substring(5), + }, + target: NoTarget, + }); + } else { + const index = log.indexOf(" "); + trace(program, log.substring(index + 1)); + } + } +} diff --git a/packages/http-client-java/emitter/src/lib.ts b/packages/http-client-java/emitter/src/lib.ts index f4885e66dbc..ae516073bc4 100644 --- a/packages/http-client-java/emitter/src/lib.ts +++ b/packages/http-client-java/emitter/src/lib.ts @@ -100,6 +100,12 @@ export const $lib = createTypeSpecLibrary({ default: paramMessage`An unknown error occurred. ${"errorMessage"}`, }, }, + "jar-error": { + severity: "error", + messages: { + default: paramMessage`${"errorMessage"}`, + }, + }, "invalid-java-sdk-dependency": { severity: "error", messages: { @@ -132,6 +138,12 @@ export const $lib = createTypeSpecLibrary({ }, // warning + "jar-warning": { + severity: "warning", + messages: { + default: paramMessage`${"warningMessage"}`, + }, + }, "no-service": { severity: "warning", messages: { From e9a529047aff07530443983cebd55ed918be236a Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Fri, 7 Mar 2025 11:23:49 +0800 Subject: [PATCH 7/9] minor --- packages/http-client-java/emitter/src/emitter.ts | 4 ++-- packages/http-client-java/emitter/src/lib.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/http-client-java/emitter/src/emitter.ts b/packages/http-client-java/emitter/src/emitter.ts index 0b9f8fdb513..145860929b6 100644 --- a/packages/http-client-java/emitter/src/emitter.ts +++ b/packages/http-client-java/emitter/src/emitter.ts @@ -174,7 +174,7 @@ function reportJarOutput(program: Program, jarOutput: string) { for (const log of logs) { if (log.startsWith("ERROR ")) { reportDiagnostic(program, { - code: "jar-error", + code: "generator-error", format: { errorMessage: log.substring(6), }, @@ -182,7 +182,7 @@ function reportJarOutput(program: Program, jarOutput: string) { }); } else if (log.startsWith("WARN ")) { reportDiagnostic(program, { - code: "jar-warning", + code: "generator-warning", format: { warningMessage: log.substring(5), }, diff --git a/packages/http-client-java/emitter/src/lib.ts b/packages/http-client-java/emitter/src/lib.ts index ae516073bc4..35cfe596e33 100644 --- a/packages/http-client-java/emitter/src/lib.ts +++ b/packages/http-client-java/emitter/src/lib.ts @@ -100,7 +100,7 @@ export const $lib = createTypeSpecLibrary({ default: paramMessage`An unknown error occurred. ${"errorMessage"}`, }, }, - "jar-error": { + "generator-error": { severity: "error", messages: { default: paramMessage`${"errorMessage"}`, @@ -138,7 +138,7 @@ export const $lib = createTypeSpecLibrary({ }, // warning - "jar-warning": { + "generator-warning": { severity: "warning", messages: { default: paramMessage`${"warningMessage"}`, From e466175563e0327fe94184fc0a2b622ca9bf84d9 Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Fri, 7 Mar 2025 14:52:29 +0800 Subject: [PATCH 8/9] error on empty model name --- .../http-client-java/emitter/src/code-model-builder.ts | 8 +++++++- packages/http-client-java/emitter/src/lib.ts | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/http-client-java/emitter/src/code-model-builder.ts b/packages/http-client-java/emitter/src/code-model-builder.ts index 5bc7e1a8ddc..e2568c10b49 100644 --- a/packages/http-client-java/emitter/src/code-model-builder.ts +++ b/packages/http-client-java/emitter/src/code-model-builder.ts @@ -2472,8 +2472,14 @@ export class CodeModelBuilder { private processObjectSchema(type: SdkModelType, name: string): ObjectSchema { const rawModelType = type.__raw; + if (!name && !type.name) { + reportDiagnostic(this.program, { + code: "empty-name", + target: rawModelType ?? NoTarget, + }); + } const namespace = getNamespace(rawModelType); - const objectSchema = new ObjectSchema(name, type.doc ?? "", { + const objectSchema = new ObjectSchema(type.name ?? name, type.doc ?? "", { summary: type.summary, language: { default: { diff --git a/packages/http-client-java/emitter/src/lib.ts b/packages/http-client-java/emitter/src/lib.ts index 35cfe596e33..ae9bac3bd27 100644 --- a/packages/http-client-java/emitter/src/lib.ts +++ b/packages/http-client-java/emitter/src/lib.ts @@ -136,6 +136,12 @@ export const $lib = createTypeSpecLibrary({ multipartFormData: paramMessage`Unrecognized type for multipart form data, kind '${"typeKind"}'.`, }, }, + "empty-name": { + severity: "error", + messages: { + default: "Name from TCGC is empty.", + }, + }, // warning "generator-warning": { From 8eefe15a392caa3318d25f1fcf6d1b0c2e3da0c5 Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Fri, 7 Mar 2025 18:16:42 +0800 Subject: [PATCH 9/9] fix deprecated in local case --- .../generator/http-client-generator-test/tsp/response.tsp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/http-client-java/generator/http-client-generator-test/tsp/response.tsp b/packages/http-client-java/generator/http-client-generator-test/tsp/response.tsp index 60fd1335807..4bc7fc0662f 100644 --- a/packages/http-client-java/generator/http-client-generator-test/tsp/response.tsp +++ b/packages/http-client-java/generator/http-client-generator-test/tsp/response.tsp @@ -22,8 +22,8 @@ enum ApiVersions { op RpcOperationWithAdditionalResponse< TParams, TResponse extends TypeSpec.Reflection.Model, - TAdditionalResponse extends object, - Traits extends object = {}, + TAdditionalResponse extends {}, + Traits extends {} = {}, TErrorResponse = Azure.Core.Foundations.ErrorResponse > is Foundations.Operation< TParams & Azure.Core.Traits.Private.TraitProperties,