github.com/goplus/llgo@v0.8.3/xtool/nm/index.go (about)

     1  /*
     2   * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package nm
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/md5"
    22  	"encoding/base64"
    23  	"log"
    24  	"os"
    25  	"path/filepath"
    26  	"strings"
    27  )
    28  
    29  type IndexBuilder struct {
    30  	nm *Cmd
    31  }
    32  
    33  func NewIndexBuilder(nm *Cmd) *IndexBuilder {
    34  	return &IndexBuilder{nm}
    35  }
    36  
    37  func (p *IndexBuilder) Index(fromDir []string, toDir string, progress func(path string)) error {
    38  	for _, dir := range fromDir {
    39  		if dir == "" {
    40  			continue
    41  		}
    42  		if e := p.IndexDir(dir, toDir, progress); e != nil {
    43  			if !os.IsNotExist(e) {
    44  				log.Println(e)
    45  			}
    46  		}
    47  	}
    48  	return nil
    49  }
    50  
    51  func (p *IndexBuilder) IndexDir(fromDir, toDir string, progress func(path string)) error {
    52  	if abs, e := filepath.Abs(fromDir); e == nil {
    53  		fromDir = abs
    54  	}
    55  	return filepath.WalkDir(fromDir, func(path string, d os.DirEntry, err error) error {
    56  		if err != nil {
    57  			return err
    58  		}
    59  		if d.IsDir() {
    60  			return nil
    61  		}
    62  		fname := d.Name()
    63  		switch filepath.Ext(fname) {
    64  		case ".a", ".dylib", ".tbd", ".so", ".dll", ".lib":
    65  			progress(path)
    66  			hash := md5.Sum([]byte(path))
    67  			hashStr := base64.RawURLEncoding.EncodeToString(hash[:])
    68  			outFile := filepath.Join(toDir, strings.TrimPrefix(fname, "lib")+hashStr+".pub")
    69  			e := p.IndexFile(path, outFile)
    70  			if e != nil {
    71  				log.Println(e)
    72  			}
    73  		}
    74  		return nil
    75  	})
    76  }
    77  
    78  func (p *IndexBuilder) IndexFile(arFile, outFile string) (err error) {
    79  	items, err := p.nm.List(arFile)
    80  	if err != nil {
    81  		if len(items) == 0 {
    82  			return
    83  		}
    84  	}
    85  	var b bytes.Buffer
    86  	b.WriteString("nm ")
    87  	b.WriteString(arFile)
    88  	b.WriteByte('\n')
    89  	nbase := b.Len()
    90  	for _, item := range items {
    91  		if item.File != "" {
    92  			b.WriteString("file ")
    93  			b.WriteString(item.File)
    94  			b.WriteByte('\n')
    95  		}
    96  		for _, sym := range item.Symbols {
    97  			switch sym.Type {
    98  			case Text, Data, BSS, Rodata, 'S', 'C', 'W', 'A':
    99  				b.WriteByte(byte(sym.Type))
   100  				b.WriteByte(' ')
   101  				b.WriteString(sym.Name)
   102  				b.WriteByte('\n')
   103  			case Undefined, LocalText, LocalData, LocalBSS, LocalASym, 'I', 'i', 'a', 'w':
   104  				/*
   105  					if sym.Type != Undefined && strings.Contains(sym.Name, "fprintf") {
   106  						log.Printf("skip symbol type %c: %s\n", sym.Type, sym.Name)
   107  					}
   108  				*/
   109  			default:
   110  				log.Printf("unknown symbol type %c: %s\n", sym.Type, sym.Name)
   111  			}
   112  		}
   113  	}
   114  	buf := b.Bytes()
   115  	if len(buf) <= nbase {
   116  		return
   117  	}
   118  	return os.WriteFile(outFile, buf, 0666)
   119  }