-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow any file to make use of custom descriptor.proto #97
Changes from all commits
debf947
0e9126b
a8534f9
83bd923
7b3ef03
dddfe5f
00ff313
cfd5cf1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -230,6 +230,9 @@ type executor struct { | |
cancel context.CancelFunc | ||
sym *linker.Symbols | ||
|
||
descriptorProtoCheck sync.Once | ||
descriptorProtoIsCustom bool | ||
|
||
mu sync.Mutex | ||
results map[string]*result | ||
} | ||
|
@@ -316,6 +319,18 @@ func (e errFailedToResolve) Unwrap() error { | |
return e.err | ||
} | ||
|
||
func (e *executor) hasOverrideDescriptorProto() bool { | ||
e.descriptorProtoCheck.Do(func() { | ||
defer func() { | ||
// ignore a panic here; just assume no custom descriptor.proto | ||
_ = recover() | ||
}() | ||
res, err := e.c.Resolver.FindFileByPath(descriptorProtoPath) | ||
e.descriptorProtoIsCustom = err == nil && res.Desc != standardImports[descriptorProtoPath] | ||
}) | ||
return e.descriptorProtoIsCustom | ||
} | ||
|
||
func (e *executor) doCompile(ctx context.Context, file string, r *result) { | ||
t := task{e: e, h: e.h.SubHandler(), r: r} | ||
if err := e.s.Acquire(ctx, 1); err != nil { | ||
|
@@ -326,7 +341,7 @@ func (e *executor) doCompile(ctx context.Context, file string, r *result) { | |
|
||
sr, err := e.c.Resolver.FindFileByPath(file) | ||
if err != nil { | ||
r.fail(errFailedToResolve{err, file}) | ||
r.fail(errFailedToResolve{err: err, path: file}) | ||
return | ||
} | ||
|
||
|
@@ -371,6 +386,8 @@ func (t *task) release() { | |
} | ||
} | ||
|
||
const descriptorProtoPath = "google/protobuf/descriptor.proto" | ||
|
||
func (t *task) asFile(ctx context.Context, name string, r SearchResult) (linker.File, error) { | ||
if r.Desc != nil { | ||
if r.Desc.Path() != name { | ||
|
@@ -385,12 +402,38 @@ func (t *task) asFile(ctx context.Context, name string, r SearchResult) (linker. | |
} | ||
|
||
var deps []linker.File | ||
if len(parseRes.FileDescriptorProto().Dependency) > 0 { | ||
t.r.setBlockedOn(parseRes.FileDescriptorProto().Dependency) | ||
fileDescriptorProto := parseRes.FileDescriptorProto() | ||
var wantsDescriptorProto bool | ||
imports := fileDescriptorProto.Dependency | ||
|
||
if t.e.hasOverrideDescriptorProto() { | ||
// we only consider implicitly including descriptor.proto if it's overridden | ||
if name != descriptorProtoPath { | ||
var includesDescriptorProto bool | ||
for _, dep := range fileDescriptorProto.Dependency { | ||
if dep == descriptorProtoPath { | ||
includesDescriptorProto = true | ||
break | ||
} | ||
} | ||
if !includesDescriptorProto { | ||
wantsDescriptorProto = true | ||
// make a defensive copy so we don't inadvertently mutate | ||
// slice's backing array when adding this implicit dep | ||
importsCopy := make([]string, len(imports)+1) | ||
copy(importsCopy, imports) | ||
importsCopy[len(imports)] = descriptorProtoPath | ||
imports = importsCopy | ||
} | ||
} | ||
} | ||
|
||
if len(imports) > 0 { | ||
t.r.setBlockedOn(imports) | ||
|
||
results := make([]*result, len(parseRes.FileDescriptorProto().Dependency)) | ||
results := make([]*result, len(fileDescriptorProto.Dependency)) | ||
checked := map[string]struct{}{} | ||
for i, dep := range parseRes.FileDescriptorProto().Dependency { | ||
for i, dep := range fileDescriptorProto.Dependency { | ||
pos := findImportPos(parseRes, dep) | ||
if name == dep { | ||
// doh! file imports itself | ||
|
@@ -405,7 +448,15 @@ func (t *task) asFile(ctx context.Context, name string, r SearchResult) (linker. | |
} | ||
results[i] = res | ||
} | ||
deps = make([]linker.File, len(results)) | ||
capacity := len(results) | ||
if wantsDescriptorProto { | ||
capacity++ | ||
} | ||
deps = make([]linker.File, len(results), capacity) | ||
var descriptorProtoRes *result | ||
if wantsDescriptorProto { | ||
descriptorProtoRes = t.e.compile(ctx, descriptorProtoPath) | ||
} | ||
|
||
// release our semaphore so dependencies can be processed w/out risk of deadlock | ||
t.e.s.Release(1) | ||
|
@@ -430,6 +481,17 @@ func (t *task) asFile(ctx context.Context, name string, r SearchResult) (linker. | |
return nil, ctx.Err() | ||
} | ||
} | ||
if descriptorProtoRes != nil { | ||
select { | ||
case <-descriptorProtoRes.ready: | ||
// descriptor.proto wasn't explicitly imported, so we can ignore a failure | ||
if descriptorProtoRes.err == nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I walked through this in the debugger and saw one non-nil error reported in the linker tests - There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Possibly. What test? If you look at the resolver it's using, it may be hard-coded to return a particular descriptor, regardless of the requested file name. In any event, since this is not an explicit dependency, I don't think we should fail here if the resolver can't supply something. If the explicitly import this file, then it will fail as expected. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll see if I can gather more details. I agree with the behavior just wanted to make sure there wasn't something we were missing. |
||
deps = append(deps, descriptorProtoRes.res) | ||
} | ||
case <-ctx.Done(): | ||
return nil, ctx.Err() | ||
} | ||
} | ||
|
||
// all deps resolved | ||
t.r.setBlockedOn(nil) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do this as a way to cheaply detect if descriptors are overridden. That way, if the file provided is not overridden, we can avoid the cost of wrapping the descriptor in a
linker.File
(which is not terribly expensive, but also not free since it must create an index of all symbols in the file).