Skip to content
This repository was archived by the owner on Dec 4, 2025. It is now read-only.

Commit 91cd0d5

Browse files
authored
Merge pull request #1117 from linwumingshi/fix/issuce-1116
fix(grpc): 🐛 Fix gRPC doc generation for proto files organized in packages
2 parents a42652e + f585368 commit 91cd0d5

File tree

2 files changed

+87
-19
lines changed

2 files changed

+87
-19
lines changed

src/main/java/com/ly/doc/model/grpc/ProtoInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ private ProtoInfo() {
8787
String os = System.getProperty("os.name").toLowerCase();
8888
String arch = System.getProperty("os.arch").toLowerCase();
8989

90-
log.info("The [os.name] is:" + os + ";[os.arch] is: " + arch);
90+
log.info("The [os.name] is: " + os + " ; [os.arch] is: " + arch);
9191

9292
this.setTargetJsonDirectoryPath(targetJsonPath);
9393
this.setJsonName("combined.json");

src/main/java/com/ly/doc/template/GRpcDocBuildTemplate.java

Lines changed: 86 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2018-2024 smart-doc
2+
* Copyright (C) 2018-2025 smart-doc
33
*
44
* Licensed to the Apache Software Foundation (ASF) under one
55
* or more contributor license agreements. See the NOTICE file
@@ -34,18 +34,35 @@
3434
import com.ly.doc.model.grpc.GrpcApiDoc;
3535
import com.ly.doc.model.grpc.GrpcJavaMethod;
3636
import com.ly.doc.model.grpc.ProtoInfo;
37-
import com.ly.doc.model.grpc.proto.*;
37+
import com.ly.doc.model.grpc.proto.EnumDefinition;
38+
import com.ly.doc.model.grpc.proto.EnumValue;
39+
import com.ly.doc.model.grpc.proto.Message;
40+
import com.ly.doc.model.grpc.proto.MessageField;
41+
import com.ly.doc.model.grpc.proto.ProtoJson;
42+
import com.ly.doc.model.grpc.proto.Service;
43+
import com.ly.doc.model.grpc.proto.ServiceMethod;
3844
import com.ly.doc.utils.DocUtil;
3945
import com.power.common.util.FileUtil;
46+
import com.power.common.util.StringUtil;
4047
import com.thoughtworks.qdox.model.JavaClass;
4148

42-
import java.io.*;
49+
import java.io.BufferedReader;
50+
import java.io.File;
51+
import java.io.IOException;
52+
import java.io.InputStream;
53+
import java.io.InputStreamReader;
4354
import java.nio.file.Files;
4455
import java.nio.file.Path;
4556
import java.nio.file.Paths;
4657
import java.nio.file.StandardCopyOption;
4758
import java.nio.file.attribute.PosixFilePermissions;
48-
import java.util.*;
59+
import java.util.ArrayList;
60+
import java.util.Collection;
61+
import java.util.Collections;
62+
import java.util.List;
63+
import java.util.Map;
64+
import java.util.Objects;
65+
import java.util.Set;
4966
import java.util.concurrent.atomic.AtomicInteger;
5067
import java.util.function.Consumer;
5168
import java.util.function.Function;
@@ -315,6 +332,9 @@ private void copyResourceFile(String resourcePath, String targetPath) {
315332
*/
316333
public void executeProtocCommands(Set<String> protoFiles, ProtoInfo protoInfo) {
317334
List<String> command = this.buildProtocCommand(protoFiles, protoInfo);
335+
if (command.isEmpty()) {
336+
return;
337+
}
318338
this.executeCommand(command, protoInfo);
319339
}
320340

@@ -344,7 +364,8 @@ private void executeCommand(List<String> command, ProtoInfo protoInfo) {
344364
try {
345365
Process process = processBuilder.start();
346366

347-
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), log::warning);
367+
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(),
368+
line -> log.warning("[protoc] " + line));
348369
Thread outputThread = new Thread(outputGobbler);
349370
outputThread.start();
350371

@@ -354,7 +375,7 @@ private void executeCommand(List<String> command, ProtoInfo protoInfo) {
354375
outputThread.join();
355376

356377
if (exitCode != 0) {
357-
log.warning("Error executing command for files");
378+
log.warning("protoc command failed with exit code: " + exitCode);
358379
}
359380
}
360381
catch (IOException | InterruptedException e) {
@@ -370,24 +391,71 @@ private void executeCommand(List<String> command, ProtoInfo protoInfo) {
370391
*/
371392
private List<String> buildProtocCommand(Set<String> protoFiles, ProtoInfo protoInfo) {
372393
List<String> command = new ArrayList<>();
373-
command.add(protoInfo.getProtocPath());
374-
command.add("--proto_path=" + String.join(";", this.getUniqueParentDirectories(protoFiles)));
375-
command.add("--doc_out=" + protoInfo.getTargetJsonDirectoryPath());
394+
395+
// Add the protoc executable (using absolute path)
396+
command.add(Paths.get(protoInfo.getProtocPath()).toAbsolutePath().toString());
397+
398+
// Find the common root directory for all .proto files
399+
String commonRootPath = this.findCommonRootDirectory(protoFiles);
400+
if (StringUtil.isEmpty(commonRootPath)) {
401+
log.warning("No common root directory found for proto files.");
402+
return Collections.emptyList();
403+
}
404+
405+
// Set --proto_path to the common root directory
406+
command.add("--proto_path=" + commonRootPath);
407+
408+
// Set output options
409+
command.add("--doc_out=" + Paths.get(protoInfo.getTargetJsonDirectoryPath()).toAbsolutePath());
376410
command.add("--doc_opt=json," + protoInfo.getJsonName());
377-
command.addAll(protoFiles);
378-
command.add("--plugin=protoc-gen-doc=" + protoInfo.getProtocGenDocPath());
411+
412+
// Set the plugin path (using absolute path)
413+
command.add("--plugin=protoc-gen-doc=" + Paths.get(protoInfo.getProtocGenDocPath()).toAbsolutePath());
414+
415+
// Add the absolute paths of the .proto files
416+
for (String filePath : protoFiles) {
417+
Path file = Paths.get(filePath).toAbsolutePath().normalize();
418+
command.add(file.toString());
419+
}
420+
379421
return command;
380422
}
381423

382424
/**
383-
* Get the unique parent directories of the given set of files.
384-
* @param files The set of files.
385-
* @return The set of unique parent directories.
425+
* Find the deepest common ancestor directory for the given set of files.
426+
* @param files The set of .proto file paths.
427+
* @return The common root directory path, or null if the set is empty.
386428
*/
387-
private Set<String> getUniqueParentDirectories(Set<String> files) {
388-
Set<String> directories = new HashSet<>();
389-
files.forEach(file -> directories.add(new File(file).getParent()));
390-
return directories;
429+
private String findCommonRootDirectory(Set<String> files) {
430+
if (files.isEmpty()) {
431+
return null;
432+
}
433+
434+
// Convert all paths to Path objects
435+
List<Path> filePaths = files.stream()
436+
.map(Paths::get)
437+
.map(Path::toAbsolutePath)
438+
.map(Path::normalize)
439+
.collect(Collectors.toList());
440+
441+
// Use the first path as the initial common root
442+
Path commonRoot = filePaths.get(0);
443+
444+
// Compare with all other paths, gradually shorten commonRoot until it is a prefix
445+
// of all paths
446+
for (Path path : filePaths) {
447+
while (!path.startsWith(commonRoot)) {
448+
commonRoot = commonRoot.getParent();
449+
if (commonRoot == null) {
450+
// If we've gone up to null, there's no common parent, return the root
451+
// of the path
452+
commonRoot = path.getRoot();
453+
break;
454+
}
455+
}
456+
}
457+
458+
return commonRoot.toString();
391459
}
392460

393461
/**

0 commit comments

Comments
 (0)