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

Commit fc2ff43

Browse files
committed
fix(grpc): 🐛 Fix gRPC doc generation for proto files organized in packages
- Closes #1116 - Fixes the core issue: Resolves the problem where grpc-xxx goals fail when .proto files are organized using packages (e.g., base/enum.proto, file/fileops.proto). The root cause was an incorrect --proto_path calculation. - Enhances path handling: Refactored getUniqueParentDirectories to correctly find the deepest common ancestor directory, ensuring --proto_path points to the actual root of the .proto files. - Improves command robustness: Uses absolute paths for protoc, protoc-gen-doc, and all input .proto files to guarantee reliable execution across different environments. - Adds comprehensive logging: Logs the final protoc command for debugging and adds [protoc] prefix to plugin output, making it easier to distinguish between smart-doc logs and protoc errors.
1 parent a42652e commit fc2ff43

File tree

2 files changed

+86
-16
lines changed

2 files changed

+86
-16
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: 85 additions & 15 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;
4046
import com.thoughtworks.qdox.model.JavaClass;
4147

42-
import java.io.*;
48+
import java.io.BufferedReader;
49+
import java.io.File;
50+
import java.io.IOException;
51+
import java.io.InputStream;
52+
import java.io.InputStreamReader;
4353
import java.nio.file.Files;
4454
import java.nio.file.Path;
4555
import java.nio.file.Paths;
4656
import java.nio.file.StandardCopyOption;
4757
import java.nio.file.attribute.PosixFilePermissions;
48-
import java.util.*;
58+
import java.util.ArrayList;
59+
import java.util.Collection;
60+
import java.util.Collections;
61+
import java.util.HashSet;
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;
@@ -344,7 +361,8 @@ private void executeCommand(List<String> command, ProtoInfo protoInfo) {
344361
try {
345362
Process process = processBuilder.start();
346363

347-
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), log::warning);
364+
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(),
365+
line -> log.warning("[protoc] " + line));
348366
Thread outputThread = new Thread(outputGobbler);
349367
outputThread.start();
350368

@@ -354,7 +372,7 @@ private void executeCommand(List<String> command, ProtoInfo protoInfo) {
354372
outputThread.join();
355373

356374
if (exitCode != 0) {
357-
log.warning("Error executing command for files");
375+
log.warning("protoc command failed with exit code: " + exitCode);
358376
}
359377
}
360378
catch (IOException | InterruptedException e) {
@@ -370,23 +388,75 @@ private void executeCommand(List<String> command, ProtoInfo protoInfo) {
370388
*/
371389
private List<String> buildProtocCommand(Set<String> protoFiles, ProtoInfo protoInfo) {
372390
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());
391+
392+
// Add the protoc executable (using absolute path)
393+
command.add(Paths.get(protoInfo.getProtocPath()).toAbsolutePath().toString());
394+
395+
// Calculate the common root directory for all .proto files
396+
Set<String> commonRoots = this.getUniqueParentDirectories(protoFiles);
397+
if (commonRoots.isEmpty()) {
398+
log.warning("No common root directory found for proto files.");
399+
return Collections.emptyList();
400+
}
401+
String commonRootPath = commonRoots.iterator().next();
402+
403+
// Set --proto_path to the common root directory
404+
command.add("--proto_path=" + commonRootPath);
405+
406+
// Set output options
407+
command.add("--doc_out=" + Paths.get(protoInfo.getTargetJsonDirectoryPath()).toAbsolutePath());
376408
command.add("--doc_opt=json," + protoInfo.getJsonName());
377-
command.addAll(protoFiles);
378-
command.add("--plugin=protoc-gen-doc=" + protoInfo.getProtocGenDocPath());
409+
410+
// Set the plugin path (using absolute path)
411+
command.add("--plugin=protoc-gen-doc=" + Paths.get(protoInfo.getProtocGenDocPath()).toAbsolutePath());
412+
413+
// Add the absolute paths of the .proto files
414+
for (String filePath : protoFiles) {
415+
Path file = Paths.get(filePath).toAbsolutePath().normalize();
416+
command.add(file.toString());
417+
}
418+
379419
return command;
380420
}
381421

382422
/**
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.
423+
* Get the unique parent directories of the given set of files. This method finds the
424+
* deepest common ancestor directory among all proto files.
425+
* @param files The set of .proto file paths.
426+
* @return The set containing the common root directory path.
386427
*/
387428
private Set<String> getUniqueParentDirectories(Set<String> files) {
429+
if (files.isEmpty()) {
430+
return Collections.emptySet();
431+
}
432+
433+
// Convert all paths to Path objects
434+
List<Path> filePaths = files.stream()
435+
.map(Paths::get)
436+
.map(Path::toAbsolutePath)
437+
.map(Path::normalize)
438+
.collect(Collectors.toList());
439+
440+
// Use the first path as the initial common root
441+
Path commonRoot = filePaths.get(0);
442+
443+
// Compare with all other paths, gradually shorten commonRoot until it is a prefix
444+
// of all paths
445+
for (Path path : filePaths) {
446+
while (!path.startsWith(commonRoot)) {
447+
commonRoot = commonRoot.getParent();
448+
if (commonRoot == null) {
449+
// If we've gone up to null, there's no common parent, return the root
450+
// of the path
451+
commonRoot = path.getRoot();
452+
break;
453+
}
454+
}
455+
}
456+
457+
// Return the found common root directory
388458
Set<String> directories = new HashSet<>();
389-
files.forEach(file -> directories.add(new File(file).getParent()));
459+
directories.add(commonRoot.toString());
390460
return directories;
391461
}
392462

0 commit comments

Comments
 (0)