mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-03 02:27:48 +00:00
125 lines
2.7 KiB
Go
125 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/build/constraint"
|
|
"go/doc"
|
|
"go/parser"
|
|
"go/token"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// buildTagSet creates the full set of tags that should evaluate to true
|
|
// for a given target, matching TinyGo's Config.BuildTags() behavior.
|
|
func buildTagSet(t Target) map[string]bool {
|
|
tags := make(map[string]bool)
|
|
for _, tag := range t.BuildTags {
|
|
tags[tag] = true
|
|
}
|
|
if t.GOOS != "" {
|
|
tags[t.GOOS] = true
|
|
}
|
|
if t.GOARCH != "" {
|
|
tags[t.GOARCH] = true
|
|
}
|
|
tags["tinygo"] = true
|
|
tags["purego"] = true
|
|
tags["osusergo"] = true
|
|
tags["math_big_pure_go"] = true
|
|
if t.GC != "" {
|
|
tags["gc."+t.GC] = true
|
|
}
|
|
if t.Scheduler != "" {
|
|
tags["scheduler."+t.Scheduler] = true
|
|
}
|
|
if t.Serial != "" {
|
|
tags["serial."+t.Serial] = true
|
|
}
|
|
// Go version tags — TinyGo currently tracks Go 1.22.
|
|
for i := 1; i <= 22; i++ {
|
|
tags[fmt.Sprintf("go1.%d", i)] = true
|
|
}
|
|
return tags
|
|
}
|
|
|
|
// extractBuildConstraint reads the file header (before the package clause)
|
|
// looking for a //go:build constraint line.
|
|
func extractBuildConstraint(data []byte) (constraint.Expr, error) {
|
|
scanner := bufio.NewScanner(bytes.NewReader(data))
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
trimmed := strings.TrimSpace(line)
|
|
if strings.HasPrefix(trimmed, "package ") {
|
|
break
|
|
}
|
|
if constraint.IsGoBuild(trimmed) {
|
|
return constraint.Parse(trimmed)
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// ExtractDocs filters .go files in pkgDir by the target's build tags,
|
|
// parses them, and returns a *doc.Package via go/doc.NewFromFiles.
|
|
func ExtractDocs(t Target, pkgDir string, allDecls bool) (*doc.Package, *token.FileSet, error) {
|
|
tags := buildTagSet(t)
|
|
|
|
entries, err := os.ReadDir(pkgDir)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
fset := token.NewFileSet()
|
|
var files []*ast.File
|
|
|
|
for _, e := range entries {
|
|
name := e.Name()
|
|
if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") {
|
|
continue
|
|
}
|
|
|
|
path := filepath.Join(pkgDir, name)
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
expr, err := extractBuildConstraint(data)
|
|
if err != nil {
|
|
continue // skip files with unparseable constraints
|
|
}
|
|
|
|
if expr != nil && !expr.Eval(func(tag string) bool { return tags[tag] }) {
|
|
continue
|
|
}
|
|
|
|
f, err := parser.ParseFile(fset, path, data, parser.ParseComments)
|
|
if err != nil {
|
|
continue // skip files with syntax errors
|
|
}
|
|
|
|
files = append(files, f)
|
|
}
|
|
|
|
if len(files) == 0 {
|
|
return nil, nil, fmt.Errorf("no matching files for target %s", t.Name)
|
|
}
|
|
|
|
var opts []any
|
|
if allDecls {
|
|
opts = append(opts, doc.AllDecls)
|
|
}
|
|
|
|
docPkg, err := doc.NewFromFiles(fset, files, "machine", opts...)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return docPkg, fset, nil
|
|
}
|