genbindings/main: support custom clang ast node matchers

This commit is contained in:
mappu 2024-10-20 17:58:00 +13:00
parent 20f6d7878b
commit d639160886
2 changed files with 37 additions and 20 deletions

View File

@ -9,6 +9,7 @@ import (
"log" "log"
"os" "os"
"os/exec" "os/exec"
"strings"
"sync" "sync"
"time" "time"
) )
@ -18,7 +19,26 @@ const (
ClangRetryDelay = 3 * time.Second ClangRetryDelay = 3 * time.Second
) )
func clangExec(ctx context.Context, clangBin, inputHeader string, cflags []string) ([]interface{}, error) { type ClangMatcher func(astNodeFilename string) bool
func ClangMatchSameHeaderDefinitionOnly(astNodeFilename string) bool {
return astNodeFilename == ""
}
type clangMatchUnderPath struct {
basePath string
}
func (c *clangMatchUnderPath) Match(astNodeFilename string) bool {
if astNodeFilename == "" {
return true
}
return strings.HasPrefix(astNodeFilename, c.basePath)
}
//
func clangExec(ctx context.Context, clangBin, inputHeader string, cflags []string, matcher ClangMatcher) ([]interface{}, error) {
clangArgs := []string{`-x`, `c++`} clangArgs := []string{`-x`, `c++`}
clangArgs = append(clangArgs, cflags...) clangArgs = append(clangArgs, cflags...)
@ -44,7 +64,7 @@ func clangExec(ctx context.Context, clangBin, inputHeader string, cflags []strin
wg.Add(1) wg.Add(1)
go func() { go func() {
defer wg.Done() defer wg.Done()
inner, innerErr = clangStripUpToFile(pr, inputHeader) inner, innerErr = clangStripUpToFile(pr, matcher)
}() }()
err = cmd.Wait() err = cmd.Wait()
@ -57,10 +77,10 @@ func clangExec(ctx context.Context, clangBin, inputHeader string, cflags []strin
return inner, innerErr return inner, innerErr
} }
func mustClangExec(ctx context.Context, clangBin, inputHeader string, cflags []string) []interface{} { func mustClangExec(ctx context.Context, clangBin, inputHeader string, cflags []string, matcher ClangMatcher) []interface{} {
for i := 0; i < ClangMaxRetries; i++ { for i := 0; i < ClangMaxRetries; i++ {
astInner, err := clangExec(ctx, clangBin, inputHeader, cflags) astInner, err := clangExec(ctx, clangBin, inputHeader, cflags, matcher)
if err != nil { if err != nil {
// Log and continue with next retry // Log and continue with next retry
log.Printf("WARNING: Clang execution failed: %v", err) log.Printf("WARNING: Clang execution failed: %v", err)
@ -83,7 +103,7 @@ func mustClangExec(ctx context.Context, clangBin, inputHeader string, cflags []s
// This cleans out everything in the translation unit that came from an // This cleans out everything in the translation unit that came from an
// #included file. // #included file.
// @ref https://stackoverflow.com/a/71128654 // @ref https://stackoverflow.com/a/71128654
func clangStripUpToFile(stdout io.Reader, inputFilePath string) ([]interface{}, error) { func clangStripUpToFile(stdout io.Reader, matcher ClangMatcher) ([]interface{}, error) {
var obj = map[string]interface{}{} var obj = map[string]interface{}{}
err := json.NewDecoder(stdout).Decode(&obj) err := json.NewDecoder(stdout).Decode(&obj)
@ -108,10 +128,8 @@ func clangStripUpToFile(stdout io.Reader, inputFilePath string) ([]interface{},
return nil, errors.New("entry is not a map") return nil, errors.New("entry is not a map")
} }
if _, ok := entry["isImplicit"]; ok { // Check where this AST node came from, if it was directly written
// Don't keep // in this header or if it as part of an #include
continue
}
var match_filename = "" var match_filename = ""
@ -140,16 +158,13 @@ func clangStripUpToFile(stdout io.Reader, inputFilePath string) ([]interface{},
// log.Printf("# name=%v kind=%v filename=%q\n", entry["name"], entry["kind"], match_filename) // log.Printf("# name=%v kind=%v filename=%q\n", entry["name"], entry["kind"], match_filename)
if match_filename == "" { if matcher(match_filename) {
// Keep // Keep
ret = append(ret, entry) ret = append(ret, entry)
} else if match_filename != inputFilePath {
// Skip this
} else {
// Keep this
// ret = append(ret, entry)
} }
// Otherwise, discard this AST node, it comes from some imported file
// that we will likely scan separately
} }
return ret, nil return ret, nil

View File

@ -113,6 +113,7 @@ func main() {
*clang, *clang,
strings.Fields(pkgConfigCflags("Qt5Widgets")), strings.Fields(pkgConfigCflags("Qt5Widgets")),
filepath.Join(*outDir, "qt"), filepath.Join(*outDir, "qt"),
ClangMatchSameHeaderDefinitionOnly,
) )
generate( generate(
@ -123,10 +124,11 @@ func main() {
*clang, *clang,
strings.Fields(pkgConfigCflags("Qt5PrintSupport")), strings.Fields(pkgConfigCflags("Qt5PrintSupport")),
filepath.Join(*outDir, "qt/qprintsupport"), filepath.Join(*outDir, "qt/qprintsupport"),
ClangMatchSameHeaderDefinitionOnly,
) )
} }
func generate(packageName string, srcDirs []string, clangBin string, cflags []string, outDir string) { func generate(packageName string, srcDirs []string, clangBin string, cflags []string, outDir string, matcher ClangMatcher) {
var includeFiles []string var includeFiles []string
for _, srcDir := range srcDirs { for _, srcDir := range srcDirs {
@ -152,7 +154,7 @@ func generate(packageName string, srcDirs []string, clangBin string, cflags []st
// PASS 0 (Fill clang cache) // PASS 0 (Fill clang cache)
// //
generateClangCaches(includeFiles, clangBin, cflags) generateClangCaches(includeFiles, clangBin, cflags, matcher)
// The cache should now be fully populated. // The cache should now be fully populated.
@ -279,7 +281,7 @@ func generate(packageName string, srcDirs []string, clangBin string, cflags []st
log.Printf("Processing %d file(s) completed", len(includeFiles)) log.Printf("Processing %d file(s) completed", len(includeFiles))
} }
func generateClangCaches(includeFiles []string, clangBin string, cflags []string) { func generateClangCaches(includeFiles []string, clangBin string, cflags []string, matcher ClangMatcher) {
var clangChan = make(chan string, 0) var clangChan = make(chan string, 0)
var clangWg sync.WaitGroup var clangWg sync.WaitGroup
@ -301,7 +303,7 @@ func generateClangCaches(includeFiles []string, clangBin string, cflags []string
// Parse the file // Parse the file
// This seems to intermittently fail, so allow retrying // This seems to intermittently fail, so allow retrying
astInner := mustClangExec(ctx, clangBin, inputHeader, cflags) astInner := mustClangExec(ctx, clangBin, inputHeader, cflags, matcher)
// Write to cache // Write to cache
jb, err := json.MarshalIndent(astInner, "", "\t") jb, err := json.MarshalIndent(astInner, "", "\t")