2023 年 4 月 20 日發布的變更
Ruby 產生器變更
此 GitHub PR 將出現在 23.x 版本中,它會變更 Ruby 程式碼產生器以發出序列化的 proto,而非 DSL。
它會從程式碼產生器中移除 DSL,以預期將 DSL 分割成個別的套件。
假設有一個像這樣的 .proto 檔案
syntax = "proto3";
package pkg;
message TestMessage {
optional int32 i32 = 1;
optional TestMessage msg = 2;
}
先前的產生程式碼
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: protoc_explorer/main.proto
require 'google/protobuf'
Google::Protobuf::DescriptorPool.generated_pool.build do
add_file("test.proto", :syntax => :proto3) do
add_message "pkg.TestMessage" do
proto3_optional :i32, :int32, 1
proto3_optional :msg, :message, 2, "pkg.TestMessage"
end
end
end
module Pkg
TestMessage = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("pkg.TestMessage").msgclass
end
變更後的產生程式碼
# frozen_string_literal: true
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: test.proto
require 'google/protobuf'
descriptor_data = "\n\ntest.proto\x12\x03pkg\"S\n\x0bTestMessage\x12\x10\n\x03i32\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\"\n\x03msg\x18\x02 \x01(\x0b\x32\x10.pkg.TestMessageH\x01\x88\x01\x01\x42\x06\n\x04_i32B\x06\n\x04_msgb\x06proto3"
begin
Google::Protobuf::DescriptorPool.generated_pool.add_serialized_file(descriptor_data)
rescue TypeError => e
# <compatibility code, see below>
end
module Pkg
TestMessage = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("pkg.TestMessage").msgclass
end
此變更修正了先前存在幾乎所有剩餘的一致性問題。這是從 DSL (會遺失資訊) 移至序列化的描述元 (會保留所有資訊) 的副作用。
回溯相容性
此變更應與 2021 年 9 月發布的 Ruby Protobuf >= 3.18.0 100% 相容。此外,它應與所有現有的使用者和部署相容。
為了達到這種程度的回溯相容性,**有**一些特殊相容性程式碼被插入,您應該注意。若沒有相容性程式碼,有一個邊緣案例可能會破壞回溯相容性。先前的程式碼在某方面較為寬鬆,而新的程式碼將會更加嚴格。
當使用完整的序列化描述元時,它會包含此檔案匯入的所有 .proto
檔案的清單 (而 DSL 從未正確加入相依性)。請參閱 descriptor.proto
中的程式碼。
add_serialized_file
會驗證描述元中列出的所有相依性,先前是否已使用 add_serialized_file
加入。一般來說,這應該是沒問題的,因為產生的程式碼將會包含所有相依性的 Ruby require
陳述式,並且如果先前未在 DescriptorPool
中定義所相依的類型,則描述元將無法載入。
但是,如果檔案路徑周圍存在模糊不清的情況,則可能會出現問題。例如,請考慮以下情境
// foo/bar.proto
syntax = "proto2";
message Bar {}
// foo/baz.proto
syntax = "proto2";
import "bar.proto";
message Baz {
optional Bar bar = 1;
}
如果您像這樣叫用 protoc
,它將會正確運作
$ protoc --ruby_out=. -Ifoo foo/bar.proto foo/baz.proto
$ RUBYLIB=. ruby baz_pb.rb
但是,如果您像這樣叫用 protoc
,而且沒有任何相容性程式碼,則會無法載入
$ protoc --ruby_out=. -I. -Ifoo foo/baz.proto
$ protoc --ruby_out=. -I. -Ifoo foo/bar.proto
$ RUBYLIB=foo ruby foo/baz_pb.rb
foo/baz_pb.rb:10:in `add_serialized_file': Unable to build file to DescriptorPool: Depends on file 'bar.proto', but it has not been loaded (Google::Protobuf::TypeError)
from foo/baz_pb.rb:10:in `<main>'
問題在於,bar.proto
是以兩個不同的標準名稱來參照:bar.proto
和 foo/bar.proto
。這是一個使用者錯誤:每個匯入應該始終使用一致的完整路徑來參照。希望這類使用者錯誤會很少見,但沒有嘗試就難以得知。
如果我們偵測到已發生此邊緣案例,則此變更中的程式碼會使用 warn
印出警告
$ RUBYLIB=foo ruby foo/baz_pb.rb
Warning: Protobuf detected an import path issue while loading generated file foo/baz_pb.rb
- foo/baz.proto imports bar.proto, but that import was loaded as foo/bar.proto
Each proto file must use a consistent fully-qualified name.
This will become an error in the next major version.
在這種情況下,有兩種可能的修正方法。其中一種是對匯入始終使用名稱 bar.proto
(移除 -I.
)。另一種是對匯入始終使用名稱 foo/bar.proto
(將匯入行變更為 import "foo/bar.proto"
並移除 -Ifoo
)。
我們計劃在下一個主要版本中移除此相容性程式碼。