/* * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package nm import ( "bytes" "crypto/md5" "encoding/base64" "log" "os" "path/filepath" "strings" ) type IndexBuilder struct { nm *Cmd } func NewIndexBuilder(nm *Cmd) *IndexBuilder { return &IndexBuilder{nm} } func (p *IndexBuilder) Index(fromDir []string, toDir string, progress func(path string)) error { for _, dir := range fromDir { if dir == "" { continue } if e := p.IndexDir(dir, toDir, progress); e != nil { if !os.IsNotExist(e) { log.Println(e) } } } return nil } func (p *IndexBuilder) IndexDir(fromDir, toDir string, progress func(path string)) error { if abs, e := filepath.Abs(fromDir); e == nil { fromDir = abs } return filepath.WalkDir(fromDir, func(path string, d os.DirEntry, err error) error { if err != nil { return err } if d.IsDir() { return nil } fname := d.Name() switch filepath.Ext(fname) { case ".a", ".dylib", ".tbd", ".so", ".dll", ".lib": progress(path) hash := md5.Sum([]byte(path)) hashStr := base64.RawURLEncoding.EncodeToString(hash[:]) outFile := filepath.Join(toDir, strings.TrimPrefix(fname, "lib")+hashStr+".pub") e := p.IndexFile(path, outFile) if e != nil { log.Println(e) } } return nil }) } func (p *IndexBuilder) IndexFile(arFile, outFile string) (err error) { items, err := p.nm.List(arFile) if err != nil { return } var b bytes.Buffer b.WriteString("nm ") b.WriteString(arFile) b.WriteByte('\n') nbase := b.Len() for _, item := range items { if item.File != "" { b.WriteString("file ") b.WriteString(item.File) b.WriteByte('\n') } for _, sym := range item.Symbols { switch sym.Type { case Text, Data, BSS, Rodata, 'S', 'C', 'W', 'A': b.WriteByte(byte(sym.Type)) b.WriteByte(' ') b.WriteString(sym.Name) b.WriteByte('\n') case Undefined, LocalText, LocalData, LocalBSS, LocalASym, 'I', 'i', 'a', 'w': /* if sym.Type != Undefined && strings.Contains(sym.Name, "fprintf") { log.Printf("skip symbol type %c: %s\n", sym.Type, sym.Name) } */ default: log.Printf("unknown symbol type %c: %s\n", sym.Type, sym.Name) } } } buf := b.Bytes() if len(buf) <= nbase { return } return os.WriteFile(outFile, buf, 0666) }