generate c++ source files from multiple proto files with import statements from directory structure in windows bat file

huangapple go评论54阅读模式
英文:

generate c++ source files from multiple proto files with import statements from directory structure in windows bat file

问题

С++项目在MS Visual Studio中。在添加新功能 - 支持gRPC时,对我来说看起来很合乎逻辑:1)在系统中添加gRPC 2)创建一个带有protoc命令行调用的批处理文件,并在预构建事件中调用它以从.proto文件生成.pb.cc/.h文件。问题在于如何使protoc为所有.proto文件生成C++文件,因为每个文件的protoc <.proto>调用会在找不到某些导入时产生错误,而一次性调用所有.proto文件的protoc(在一个命令中)会导致其他错误 - 一些对象定义了两次。是否有办法从批处理文件中调用protoc以在一些.proto文件中导入一些其他.proto文件时生成C++文件?

我有以下*.proto结构,其中A.proto导入B,Subscribe.proto导入A和B,Service.proto导入Subscribe。

  • protos/project1
  • protos/project1/Service.proto
  • protos/project1/Common/A.proto
  • protos/project1/Common/B.proto
  • protos/project1/Commands/Subscribe.proto

A.proto的内容是:

syntax = "proto3";

package control;

import "Common/B.proto"; 

message A {
    string Name = 1;
    repeated B bs = 2;
}

B.proto的内容是:

syntax = "proto3";

package control;

import "google/protobuf/timestamp.proto";

message B {
    optional string AName = 1;
    string Name = 2;

    google.protobuf.Timestamp SendTime = 3;
    BStatus Status = 4;

    optional tValueType ValueType = 5;
    oneof Value {
        sint64 sintValue = 6;
        uint64 uintValue = 7;
        string textValue = 8;
        google.protobuf.Timestamp timeValue = 9;
    }
}

enum tValueType {
    Unknown = 0;
    Byte = 1;
    Short = 2;
    Ushort = 3;
    Long = 4;
    Ulong = 5;
    Text = 6;
    Time = 7;
}

enum BStatus {
    Good = 0;
    Bad = 1;
}

Subscribe.proto的内容是:

syntax = "proto3";

import "Common/A.proto";

package control;

message SubscribeRequest {
    repeated A a = 1;
}

message SubscribeResponse {
    repeated A a = 1;
}

Service.proto的内容是:

syntax = "proto3";

import "Commands/Subscribe.proto";
import "google/protobuf/empty.proto";

package control;

service srv {
    rpc Subscribe(SubscribeRequest) returns (SubscribeResponse) {}
}

现在,我在protos目录中创建了一个批处理文件,用于构建C++源代码,有两种变体:

  • 一次性调用protoc,使用所有.proto文件的命令
  • 递归遍历目录结构,对每个.proto文件使用protoc的for循环

这两种情况都带来了错误。

  • 第一种情况是单个命令:
protoc.exe -I=. -I=./project1 --cpp_out=%out_cpp% -I=./project1/Common -I=./project1/Commands Subscribe.proto B.proto A.proto Service.proto

错误:

B.proto: "control.B.Value" is already defined in file "Common/B.proto".
B.proto: "control.B._AName" is already defined in file "Common/B.proto".
...
  • 第二种情况是for循环:

批处理文件的内容:

set out_cpp=%cd%\build
mkdir %out_cpp%
call :treeProcess %cd%
goto :eof

:treeProcess
for %%f in (*.proto) do protoc.exe -I=. --cpp_out=%out_cpp% %%f
for /D %%d in (*) do (
    cd %%d
    call :treeProcess %%d
    cd ..
)
exit /b

这带来了一些错误,并且只编译了Service.pb.h/cc和Tag.pb.h/cc(另外两个文件由于错误而未编译)。

批处理文件的内容:

D:\tmp\protos>call :treeProcess D:\tmp\protos
D:\tmp\protos>for %f in (*.proto) do c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build %f
D:\tmp\protos>for / %d in (*) do (
cd %d
call :treeProcess %d
cd ..
)
D:\tmp\protos>(
cd build
call :treeProcess build
cd ..
)
D:\tmp\protos\build>for %f in (*.proto) do c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build %f
D:\tmp\protos\build>for / %d in (*) do (
cd %d
call :treeProcess %d
cd ..
)
D:\tmp\protos\build>exit /b
D:\tmp\protos>(
cd project1
call :treeProcess project1
cd ..
)
D:\tmp\protos\project1>for %f in (*.proto) do c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build %f
D:\tmp\protos\project1>c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build Service.proto
Service.proto:4:1: warning: Import google/protobuf/empty.proto is unused.
D:\tmp\protos\project1>for / %d in (*) do (
cd %d
call :treeProcess %d
cd ..
)
D:\tmp\protos\project1>(
cd Commands
call :treeProcess Commands
cd ..
)
D:\tmp\protos\project1\Commands>for %f in (*.proto) do c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build %f
D:\tmp\protos\project1\Commands>c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build Subscribe.proto
Common/A.proto: File not found.
Subscribe.proto:3:1: Import "Common/A.proto" was not found or had errors.
Subscribe.proto:8:14: "A" is not defined.
Subscribe.proto:12:14:
<details>
<summary>英文:</summary>
С++ project in MS Visual studio. When adding a new feature - support of the grpc, it looked quite logic to me 1) add grpc in the system 2) create a bat file with command-line call of protoc and call it in pre-build event to generate pb.cc/h files out of proto files.
Problem is to make protoc generate c++ files for all proto files, because the call of protoc &lt;.proto&gt; per each file brings errors when some imports are not found, and the call of protoc for all proto files at once (in one command) bings other error - some of the objects are defined twice.
Is there any way to call protoc from bat file to generate c++ files when some of proto files are imported in some others?
I have following *.proto structure, where A.proto imports B, and Subscribe.proto imports A and B, and Service.proto imports Subscribe.
- protos/project1
- protos/project1/Service.proto
- protos/project1/Common/A.proto
- protos/project1/Common/B.proto
- protos/project1/Commands/Subscribe.proto
Content of A.proto is:
syntax = &quot;proto3&quot;;
package control;
import &quot;Common/B.proto&quot;; 
message A {
string Name = 1;
repeated B bs = 2;
}
Content of B.proto is:
syntax = &quot;proto3&quot;;
package control;
import &quot;google/protobuf/timestamp.proto&quot;;
message B {
optional string AName = 1;
string Name = 2;
google.protobuf.Timestamp SendTime = 3;
BStatus Status = 4;
optional tValueType ValueType = 5;
oneof Value {
sint64 sintValue = 6;
uint64 uintValue = 7;
string textValue = 8;
google.protobuf.Timestamp timeValue = 9;
}
}
enum tValueType {
Unknown = 0;
Byte = 1;
Short = 2;
Ushort = 3;
Long = 4;
Ulong = 5;
Text = 6;
Time = 7;
}
enum BStatus {
Good = 0;
Bad = 1;
}
Content of Subscribe.proto is:
syntax = &quot;proto3&quot;;
import &quot;Common/A.proto&quot;;
package control;
message SubscribeRequest {
repeated A a = 1;
}
message SubscribeResponse {
repeated A a = 1;
}
and content of Service.proto:
syntax = &quot;proto3&quot;;
import &quot;Commands/Subscribe.proto&quot;;
import &quot;google/protobuf/empty.proto&quot;;
package control;
service srv {
rpc Subscribe(SubscribeRequest) returns (SubscribeResponse) {}
}
Now I make a bat file in protos directory, to build C++ sources, with 2 variants: 
- single command to call protoc with all proto files at once,
- for loop over directory structure recursively to call protoc per every proto file
Both cases bring me errors.
- **first case is single command:**
&lt;!-- --&gt;
protoc.exe -I=. -I=./project1 --cpp_out=%out_cpp% -I=./project1/Common -I=./project1/Commands Subscribe.proto B.proto A.proto Service.proto
error:
D:\tmp\protos&gt;c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. -I=./project1 --cpp_out=D:\tmp\protos\build -I=./project1/Common -I=./project1/Commands Subscribe.proto B.proto A.proto Service.proto
B.proto: &quot;control.B.Value&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto: &quot;control.B._AName&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto: &quot;control.B._ValueType&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:8:21: &quot;control.B.AName&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:9:12: &quot;control.B.Name&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:11:31: &quot;control.B.SendTime&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:12:17: &quot;control.B.Status&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:14:25: &quot;control.B.ValueType&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:16:16: &quot;control.B.sintValue&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:17:16: &quot;control.B.uintValue&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:18:16: &quot;control.B.textValue&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:19:35: &quot;control.B.timeValue&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:7:9: &quot;control.B&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:24:5: &quot;control.Unknown&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:24:5: Note that enum values use C++ scoping rules, meaning that enum values are siblings of their type, not children of it.  Therefore, &quot;Unknown&quot; must be unique within &quot;control&quot;, not just within &quot;tValueType&quot;.
B.proto:25:5: &quot;control.Byte&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:25:5: Note that enum values use C++ scoping rules, meaning that enum values are siblings of their type, not children of it.  Therefore, &quot;Byte&quot; must be unique within &quot;control&quot;, not just within &quot;tValueType&quot;.
B.proto:26:5: &quot;control.Short&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:26:5: Note that enum values use C++ scoping rules, meaning that enum values are siblings of their type, not children of it.  Therefore, &quot;Short&quot; must be unique within &quot;control&quot;, not just within &quot;tValueType&quot;.
B.proto:27:5: &quot;control.Ushort&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:27:5: Note that enum values use C++ scoping rules, meaning that enum values are siblings of their type, not children of it.  Therefore, &quot;Ushort&quot; must be unique within &quot;control&quot;, not just within &quot;tValueType&quot;.
B.proto:28:5: &quot;control.Long&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:28:5: Note that enum values use C++ scoping rules, meaning that enum values are siblings of their type, not children of it.  Therefore, &quot;Long&quot; must be unique within &quot;control&quot;, not just within &quot;tValueType&quot;.
B.proto:29:5: &quot;control.Ulong&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:29:5: Note that enum values use C++ scoping rules, meaning that enum values are siblings of their type, not children of it.  Therefore, &quot;Ulong&quot; must be unique within &quot;control&quot;, not just within &quot;tValueType&quot;.
B.proto:30:5: &quot;control.Text&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:30:5: Note that enum values use C++ scoping rules, meaning that enum values are siblings of their type, not children of it.  Therefore, &quot;Text&quot; must be unique within &quot;control&quot;, not just within &quot;tValueType&quot;.
B.proto:31:5: &quot;control.Time&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:31:5: Note that enum values use C++ scoping rules, meaning that enum values are siblings of their type, not children of it.  Therefore, &quot;Time&quot; must be unique within &quot;control&quot;, not just within &quot;tValueType&quot;.
B.proto:23:6: &quot;control.tValueType&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:35:5: &quot;control.Good&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:35:5: Note that enum values use C++ scoping rules, meaning that enum values are siblings of their type, not children of it.  Therefore, &quot;Good&quot; must be unique within &quot;control&quot;, not just within &quot;BStatus&quot;.
B.proto:36:5: &quot;control.Bad&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:36:5: Note that enum values use C++ scoping rules, meaning that enum values are siblings of their type, not children of it.  Therefore, &quot;Bad&quot; must be unique within &quot;control&quot;, not just within &quot;BStatus&quot;.
B.proto:34:6: &quot;control.BStatus&quot; is already defined in file &quot;Common/B.proto&quot;.
B.proto:12:9: &quot;control.BStatus&quot; seems to be defined in &quot;Common/B.proto&quot;, which is not imported by &quot;B.proto&quot;.  To use it here, please add the necessary import.
B.proto:14:14: &quot;control.tValueType&quot; seems to be defined in &quot;Common/B.proto&quot;, which is not imported by &quot;B.proto&quot;.  To use it here, please add the necessary import.
- **second case is for loop:**
content of bat file:
&lt;!-- --&gt;
set out_cpp=%cd%\build
mkdir %out_cpp%
call :treeProcess %cd%
goto :eof
:treeProcess
for %%f in (*.proto) do protoc.exe -I=. --cpp_out=%out_cpp% %%f
for /D %%d in (*) do (
cd %%d
call :treeProcess %%d
cd ..
)
exit /b
it brings some errors, and compile only Service.pb.h/cc and Tag.pb.h/cc (two other files are not compiled due to errors)
D:\tmp\protos&gt;call :treeProcess D:\tmp\protos
D:\tmp\protos&gt;for %f in (*.proto) do c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build %f
D:\tmp\protos&gt;for / %d in (*) do (
cd %d
call :treeProcess %d
cd ..
)
D:\tmp\protos&gt;(
cd build
call :treeProcess build
cd ..
)
D:\tmp\protos\build&gt;for %f in (*.proto) do c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build %f
D:\tmp\protos\build&gt;for / %d in (*) do (
cd %d
call :treeProcess %d
cd ..
)
D:\tmp\protos\build&gt;exit /b
D:\tmp\protos&gt;(
cd project1
call :treeProcess project1
cd ..
)
D:\tmp\protos\project1&gt;for %f in (*.proto) do c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build %f
D:\tmp\protos\project1&gt;c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build Service.proto
Service.proto:4:1: warning: Import google/protobuf/empty.proto is unused.
D:\tmp\protos\project1&gt;for / %d in (*) do (
cd %d
call :treeProcess %d
cd ..
)
D:\tmp\protos\project1&gt;(
cd Commands
call :treeProcess Commands
cd ..
)
D:\tmp\protos\project1\Commands&gt;for %f in (*.proto) do c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build %f
D:\tmp\protos\project1\Commands&gt;c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build Subscribe.proto
Common/A.proto: File not found.
Subscribe.proto:3:1: Import &quot;Common/A.proto&quot; was not found or had errors.
Subscribe.proto:8:14: &quot;A&quot; is not defined.
Subscribe.proto:12:14: &quot;A&quot; is not defined.
D:\tmp\protos\project1\Commands&gt;for / %d in (*) do (
cd %d
call :treeProcess %d
cd ..
)
D:\tmp\protos\project1\Commands&gt;exit /b
D:\tmp\protos\project1&gt;(
cd Common
call :treeProcess Common
cd ..
)
D:\tmp\protos\project1\Common&gt;for %f in (*.proto) do c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build %f
D:\tmp\protos\project1\Common&gt;c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build A.proto
Common/B.proto: File not found.
A.proto:5:1: Import &quot;Common/B.proto&quot; was not found or had errors.
A.proto:9:14: &quot;B&quot; is not defined.
D:\tmp\protos\project1\Common&gt;c:\dev\vcpkg\installed\x64-windows\tools\protobuf\protoc.exe -I=. --cpp_out=D:\tmp\protos\build B.proto
D:\tmp\protos\project1\Common&gt;for / %d in (*) do (
cd %d
call :treeProcess %d
cd ..
)
D:\tmp\protos\project1\Common&gt;exit /b
D:\tmp\protos\project1&gt;exit /b
D:\tmp\protos&gt;exit /b
I searched for hours, but did not find proper solution to generate all proto files properly.
Is this possible at all from bat file under windows? Please advise how to do this.
(under linux I did this using CMakeLists.txt with the exact identical content and structure)
</details>
# 答案1
**得分**: 0
After many attempts to make protoc working from batch file,
I gave it up and switched to cmake .
so, cmake is the shortest way to generate source files from protos.
<details>
<summary>英文:</summary>
After many attempts to make protoc working from batch file,
I gave it up and switched to cmake .
so, cmake is the shortest way to generate source files from protos.
</details>

huangapple
  • 本文由 发表于 2023年3月12日 15:10:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/75711569.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定