Conversation
Signed-off-by: Jonathan Perry <yonch@yonch.com>
- replace the direct command execution for linking multiple BPF object files with a new `Link` function in the `gen` package. - add unit tests for linking Signed-off-by: Jonathan Perry <yonch@yonch.com>
- Moved the temporary object renaming logic from compileOne() to convert() - Simplified naming and cleanup Signed-off-by: Jonathan Perry <yonch@yonch.com>
Signed-off-by: Jonathan Perry <yonch@yonch.com>
…fic endianness handling Signed-off-by: Jonathan Perry <yonch@yonch.com>
… name Signed-off-by: Jonathan Perry <yonch@yonch.com>
ti-mo
left a comment
There was a problem hiding this comment.
This looks good overall, thank you! Left a few nits.
| Source: filepath.Join(dir, "func1.c"), | ||
| Dest: obj1, | ||
| }) | ||
| if err != nil { |
There was a problem hiding this comment.
Here and everywhere: use qt.Assert().
| (usually some version of clang). | ||
| source files are C files that are compiled using the specified compiler | ||
| (usually some version of clang) and linked together into a single | ||
| BPF program. |
There was a problem hiding this comment.
Specifying multiple source files incurs an extra dependency (bpftool). I'd document that clearly here.
| for _, source := range b2g.sourceFiles { | ||
| if _, err := os.Stat(source); os.IsNotExist(err) { | ||
| return fmt.Errorf("file %s doesn't exist", source) | ||
| } else if err != nil { |
There was a problem hiding this comment.
} else ... { is not necessary here since the opposing case returns. The typical pattern is:
_, err := ...
if errors.Is(err, fs.ErrNotExist) { // Note: os.IsFoo functions are unofficially deprecated.
return fmt.Errorf(...)
}
if err != nil {
return err
}Also, the PathError returned by os.Stat always contains the path provided to the function call, so it's not necessary to generate your own error. Doing so also swallows the fs.ErrNotExist sentinel, making error handling confusing for the user.
| for _, source := range b2g.sourceFiles { | ||
| // Determine the target object file name | ||
| var targetObjFileName string | ||
| if len(b2g.sourceFiles) > 1 { |
There was a problem hiding this comment.
Consider initializing targetObjFileName := objFileName and drop the the else below.
| for _, deps := range depsSlices { | ||
| for _, dep := range deps { | ||
| // If we've seen this file before, merge prerequisites | ||
| if existing, ok := merged[dep.file]; ok { |
There was a problem hiding this comment.
Consider removing one level of indentation for the majority of the body using continue:
existing, ok := merged[dep.file]
if !ok {
// First time seeing this file, just copy prerequisites
merged[dep.file] = make([]string, len(dep.prerequisites))
copy(merged[dep.file], dep.prerequisites)
continue
}
...
lmb
left a comment
There was a problem hiding this comment.
Agree with Timo, this is nice :) Be aware that libbpf linking may produce linked object files which we don't parse: #739 I don't think it's a pre-requisite to get this fixed, but something to be aware of which might make this less useful to you.
|
|
||
| // LinkArgs specifies the arguments for linking multiple BPF object files together. | ||
| type LinkArgs struct { | ||
| // Destination object file name |
There was a problem hiding this comment.
Mention that paths must be absolute?
| ) | ||
|
|
||
| // LinkArgs specifies the arguments for linking multiple BPF object files together. | ||
| type LinkArgs struct { |
There was a problem hiding this comment.
Document that this only supports host endianness.
| t.Fatal("Can't get module root:", err) | ||
| } | ||
|
|
||
| if _, err := os.Stat(filepath.Join(modRoot, "go.mod")); os.IsNotExist(err) { |
There was a problem hiding this comment.
Can you move creating the temporary module into a helper instead of copying the code?
| {"/foo/third.c", []string{"/foo/bar.h"}}, | ||
| } | ||
|
|
||
| if !reflect.DeepEqual(merged, want) { |
There was a problem hiding this comment.
| if !reflect.DeepEqual(merged, want) { | |
| qt.Assert(t, qt.DeepEquals(merged, want)) |
| // If we've seen this file before, merge prerequisites | ||
| if existing, ok := merged[dep.file]; ok { | ||
| // Combine prerequisites, avoiding duplicates | ||
| prereqs := make(map[string]struct{}) |
There was a problem hiding this comment.
I think you might be able to simplify this by appending to a slice. At the end, sort the slice and use https://pkg.go.dev/slices#Compact in a second pass.
| }) | ||
|
|
||
| want := []dependency{ | ||
| {"/foo/main.c", []string{"/foo/bar.h", "/foo/baz.h", "/foo/qux.h"}}, |
There was a problem hiding this comment.
How can we get different dependencies for two compilations of the same file? Wouldn't it be more appropriate to check that both sets of dependencies are identical? Or that the sets are disjoint?
| if err != nil { | ||
| return nil, fmt.Errorf("convert source file to absolute path: %w", err) | ||
| } | ||
| b2g.sourceFiles[i] = absPath |
There was a problem hiding this comment.
Nit: just appending to b2g.sourceFiles without pre-allocating the slice is probably fine / easier to reason about.
| b2g.sourceFiles[i] = absPath | |
| b2g.sourceFiles = append(b2g.sourceFiles, absPath) |
See issue #1758
Has been used successfully in unvariance/collector#109, PR unvariance/collector#110.
Tested in a dev container on MacOs and on a Linux bare metal box.
Closes #1758