llcppsymg:libs,cflags parse

This commit is contained in:
luoliwoshang
2024-10-18 11:25:53 +08:00
parent 905ed36afd
commit 151d3a9610
10 changed files with 477 additions and 368 deletions

View File

@@ -2,13 +2,21 @@ package main
import ( import (
"fmt" "fmt"
"os"
"path/filepath"
"runtime"
"strings" "strings"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config" "github.com/goplus/llgo/chore/_xtool/llcppsymg/config"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config/cfgparse"
) )
func main() { func main() {
TestGetConf() TestGetConf()
TestParseLibs()
TestGenDylibPaths()
TestParseCFlags()
TestGenHeaderFilePath()
} }
func TestGetConf() { func TestGetConf() {
@@ -61,3 +69,264 @@ func TestGetConf() {
fmt.Println() fmt.Println()
} }
} }
func TestParseLibs() {
fmt.Println("=== Test ParseLibs ===")
testCases := []struct {
name string
input string
}{
{
name: "Lua library",
input: "-L/opt/homebrew/lib -llua -lm",
},
{
name: "SQLite library",
input: "-L/opt/homebrew/opt/sqlite/lib -lsqlite3",
},
{
name: "INIReader library",
input: "-L/opt/homebrew/Cellar/inih/58/lib -lINIReader",
},
{
name: "Multiple library paths",
input: "-L/opt/homebrew/lib -L/usr/lib -llua",
},
{
name: "No valid library",
input: "-L/opt/homebrew/lib",
},
}
for _, tc := range testCases {
fmt.Printf("Test case: %s\n", tc.name)
fmt.Printf("Input: %s\n", tc.input)
conf := cfgparse.ParseLibs(tc.input)
fmt.Println("Paths:", conf.Paths)
fmt.Println("Names:", conf.Names)
}
}
func TestGenDylibPaths() {
fmt.Println("=== Test GenDylibPaths ===")
tempDir := os.TempDir()
tempDefaultPath := filepath.Join(tempDir, "symblib")
affix := ".dylib"
if runtime.GOOS == "linux" {
affix = ".so"
}
err := os.MkdirAll(tempDefaultPath, 0755)
if err != nil {
fmt.Printf("Failed to create temp default path: %v\n", err)
return
}
dylib1 := filepath.Join(tempDir, "libsymb1"+affix)
dylib2 := filepath.Join(tempDir, "libsymb2"+affix)
defaultDylib3 := filepath.Join(tempDefaultPath, "libsymb3"+affix)
os.Create(dylib1)
os.Create(dylib2)
os.Create(defaultDylib3)
defer os.Remove(dylib1)
defer os.Remove(dylib2)
defer os.Remove(defaultDylib3)
defer os.Remove(tempDefaultPath)
testCase := []struct {
name string
conf *cfgparse.Libs
defaultPaths []string
want []string
}{
{
name: "existing dylib",
conf: &cfgparse.Libs{
Names: []string{"symb1"},
Paths: []string{tempDir},
},
defaultPaths: []string{},
want: []string{dylib1},
},
{
name: "existing dylibs",
conf: &cfgparse.Libs{
Names: []string{"symb1", "symb2"},
Paths: []string{tempDir},
},
defaultPaths: []string{},
want: []string{dylib1, dylib2},
},
{
name: "existint default paths",
conf: &cfgparse.Libs{
Names: []string{"symb1", "symb3"},
Paths: []string{tempDir},
},
defaultPaths: []string{tempDefaultPath},
want: []string{dylib1, defaultDylib3},
},
{
name: "existint default paths & not found",
conf: &cfgparse.Libs{
Names: []string{"symb1", "symb3", "math"},
Paths: []string{tempDir},
},
defaultPaths: []string{tempDefaultPath},
want: []string{dylib1, defaultDylib3},
},
{
name: "no existing dylib",
conf: &cfgparse.Libs{
Names: []string{"notexist"},
Paths: []string{tempDir},
},
want: []string{},
},
}
for _, tc := range testCase {
fmt.Printf("Test case: %s\n", tc.name)
paths, notFounds, err := tc.conf.GenDylibPaths(tc.defaultPaths)
if len(notFounds) > 0 {
fmt.Println("notFounds", notFounds)
}
if err != nil {
fmt.Printf("Error: %v\n", err)
}
for _, path := range paths {
found := false
for _, wantPath := range tc.want {
if path == wantPath {
found = true
fileName := filepath.Base(path)
if runtime.GOOS == "linux" {
fileName = strings.TrimSuffix(fileName, ".so")
} else {
fileName = strings.TrimSuffix(fileName, ".dylib")
}
fmt.Printf("Path %s is in the expected paths\n", fileName)
break
}
}
if !found {
fmt.Printf("Path %s is not in the expected paths\n", path)
}
}
}
}
func TestParseCFlags() {
fmt.Println("=== Test ParseCFlags ===")
testCases := []struct {
name string
input string
}{
{
name: "Single include path",
input: "-I/usr/include",
},
{
name: "Multiple include paths",
input: "-I/usr/include -I/opt/homebrew/include",
},
{
name: "Include paths mixed with other flags",
input: "-I/usr/include -DDEBUG -I/opt/local/include -Wall",
},
{
name: "Empty input",
input: "",
},
}
for _, tc := range testCases {
fmt.Printf("Test case: %s\n", tc.name)
fmt.Printf("Input: %s\n", tc.input)
conf := cfgparse.ParseCFlags(tc.input)
fmt.Println("Paths:", conf.Paths)
}
}
func TestGenHeaderFilePath() {
fmt.Println("=== Test GenHeaderFilePath ===")
tempDir := os.TempDir()
temDir2 := filepath.Join(tempDir, "include")
tempFile1 := filepath.Join(tempDir, "test1.h")
tempFile2 := filepath.Join(tempDir, "test2.h")
tempFile3 := filepath.Join(temDir2, "test3.h")
os.MkdirAll(temDir2, 0755)
os.Create(tempFile1)
os.Create(tempFile2)
os.Create(tempFile3)
defer os.Remove(tempFile1)
defer os.Remove(tempFile2)
defer os.Remove(tempFile3)
defer os.Remove(temDir2)
testCases := []struct {
name string
cflags string
files []string
}{
{
name: "Valid files",
cflags: "-I" + tempDir,
files: []string{"test1.h", "test2.h"},
},
{
name: "Mixed existing and non-existing files",
cflags: "-I" + tempDir,
files: []string{"test1.h", "nonexistent.h"},
},
{
name: "Multiple include paths",
cflags: "-I" + tempDir + " -I" + temDir2,
files: []string{"test1.h", "test2.h", "test3.h"},
},
{
name: "No existing files",
cflags: "-I" + tempDir,
files: []string{"nonexistent1.h", "nonexistent2.h"},
},
{
name: "Empty file list",
cflags: "-I/usr/include",
files: []string{},
},
}
for _, tc := range testCases {
fmt.Printf("Test case: %s\n", tc.name)
fmt.Printf("Input files: %v\n", tc.files)
cflag := cfgparse.ParseCFlags(tc.cflags)
result, notFounds, err := cflag.GenHeaderFilePaths(tc.files)
if err != nil {
fmt.Printf("Error: %v\n", err)
}
if len(notFounds) > 0 {
fmt.Println("notFounds", notFounds)
}
if result != nil {
relativeResult := make([]string, len(result))
for i, path := range result {
relativeResult[i] = filepath.Base(path)
}
fmt.Printf("Output: %v\n", relativeResult)
}
fmt.Println()
}
}

View File

@@ -18,6 +18,79 @@ Cplusplus: false
=== Test case: Invalid JSON === === Test case: Invalid JSON ===
Error: failed to parse config Error: failed to parse config
=== Test ParseLibs ===
Test case: Lua library
Input: -L/opt/homebrew/lib -llua -lm
Paths: [/opt/homebrew/lib]
Names: [lua m]
Test case: SQLite library
Input: -L/opt/homebrew/opt/sqlite/lib -lsqlite3
Paths: [/opt/homebrew/opt/sqlite/lib]
Names: [sqlite3]
Test case: INIReader library
Input: -L/opt/homebrew/Cellar/inih/58/lib -lINIReader
Paths: [/opt/homebrew/Cellar/inih/58/lib]
Names: [INIReader]
Test case: Multiple library paths
Input: -L/opt/homebrew/lib -L/usr/lib -llua
Paths: [/opt/homebrew/lib /usr/lib]
Names: [lua]
Test case: No valid library
Input: -L/opt/homebrew/lib
Paths: [/opt/homebrew/lib]
Names: []
=== Test GenDylibPaths ===
Test case: existing dylib
Path libsymb1 is in the expected paths
Test case: existing dylibs
Path libsymb1 is in the expected paths
Path libsymb2 is in the expected paths
Test case: existint default paths
Path libsymb1 is in the expected paths
Path libsymb3 is in the expected paths
Test case: existint default paths & not found
notFounds [math]
Path libsymb1 is in the expected paths
Path libsymb3 is in the expected paths
Test case: no existing dylib
notFounds [notexist]
Error: failed to find any libraries
=== Test ParseCFlags ===
Test case: Single include path
Input: -I/usr/include
Paths: [/usr/include]
Test case: Multiple include paths
Input: -I/usr/include -I/opt/homebrew/include
Paths: [/usr/include /opt/homebrew/include]
Test case: Include paths mixed with other flags
Input: -I/usr/include -DDEBUG -I/opt/local/include -Wall
Paths: [/usr/include /opt/local/include]
Test case: Empty input
Input:
Paths: []
=== Test GenHeaderFilePath ===
Test case: Valid files
Input files: [test1.h test2.h]
Output: [test1.h test2.h]
Test case: Mixed existing and non-existing files
Input files: [test1.h nonexistent.h]
notFounds [nonexistent.h]
Output: [test1.h]
Test case: Multiple include paths
Input files: [test1.h test2.h test3.h]
Output: [test1.h test2.h test3.h]
Test case: No existing files
Input files: [nonexistent1.h nonexistent2.h]
Error: failed to find any header files
notFounds [nonexistent1.h nonexistent2.h]
Test case: Empty file list
Input files: []
Error: failed to find any header files
#stderr #stderr

View File

@@ -1,22 +1,4 @@
#stdout #stdout
=== Test GenHeaderFilePath ===
Test case: Valid files
Input files: [test1.h test2.h]
Output: [test1.h test2.h]
Test case: Mixed existing and non-existing files
Input files: [test1.h nonexistent.h]
Error: some files not found or inaccessible: [file not found: nonexistent.h]
Output: [test1.h]
Test case: No existing files
Input files: [nonexistent1.h nonexistent2.h]
Error: some files not found or inaccessible: [file not found: nonexistent1.h file not found: nonexistent2.h]
Test case: Empty file list
Input files: []
Error: no valid header files
=== Test NewSymbolProcessor === === Test NewSymbolProcessor ===
Before: No prefixes After: Prefixes: [lua_ luaL_] Before: No prefixes After: Prefixes: [lua_ luaL_]

View File

@@ -2,15 +2,12 @@ package main
import ( import (
"fmt" "fmt"
"os"
"path/filepath"
"sort" "sort"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/parse" "github.com/goplus/llgo/chore/_xtool/llcppsymg/parse"
) )
func main() { func main() {
TestGenHeaderFilePath()
TestNewSymbolProcessor() TestNewSymbolProcessor()
TestRemovePrefix() TestRemovePrefix()
TestToGoName() TestToGoName()
@@ -19,64 +16,6 @@ func main() {
TestParseHeaderFile() TestParseHeaderFile()
} }
func TestGenHeaderFilePath() {
fmt.Println("=== Test GenHeaderFilePath ===")
tempDir := os.TempDir()
tempFile1 := filepath.Join(tempDir, "test1.h")
tempFile2 := filepath.Join(tempDir, "test2.h")
os.Create(tempFile1)
os.Create(tempFile2)
defer os.Remove(tempFile1)
defer os.Remove(tempFile2)
testCases := []struct {
name string
cflags string
files []string
}{
{
name: "Valid files",
cflags: "-I" + tempDir,
files: []string{"test1.h", "test2.h"},
},
{
name: "Mixed existing and non-existing files",
cflags: "-I" + tempDir,
files: []string{"test1.h", "nonexistent.h"},
},
{
name: "No existing files",
cflags: "-I" + tempDir,
files: []string{"nonexistent1.h", "nonexistent2.h"},
},
{
name: "Empty file list",
cflags: "-I/usr/include",
files: []string{},
},
}
for _, tc := range testCases {
fmt.Printf("Test case: %s\n", tc.name)
fmt.Printf("Input files: %v\n", tc.files)
result, err := parse.GenHeaderFilePath(tc.cflags, tc.files)
if err != nil {
fmt.Printf("Error: %v\n", err)
}
if result != nil {
relativeResult := make([]string, len(result))
for i, path := range result {
relativeResult[i] = filepath.Base(path)
}
fmt.Printf("Output: %v\n", relativeResult)
}
fmt.Println()
}
}
func TestNewSymbolProcessor() { func TestNewSymbolProcessor() {
fmt.Println("=== Test NewSymbolProcessor ===") fmt.Println("=== Test NewSymbolProcessor ===")
process := parse.NewSymbolProcessor([]string{"lua_", "luaL_"}) process := parse.NewSymbolProcessor([]string{"lua_", "luaL_"})

View File

@@ -1,35 +1,4 @@
#stdout #stdout
=== Test ParseLibConfig ===
Test case: Lua library
Input: -L/opt/homebrew/lib -llua -lm
Paths: [/opt/homebrew/lib]
Names: [lua m]
Test case: SQLite library
Input: -L/opt/homebrew/opt/sqlite/lib -lsqlite3
Paths: [/opt/homebrew/opt/sqlite/lib]
Names: [sqlite3]
Test case: INIReader library
Input: -L/opt/homebrew/Cellar/inih/58/lib -lINIReader
Paths: [/opt/homebrew/Cellar/inih/58/lib]
Names: [INIReader]
Test case: Multiple library paths
Input: -L/opt/homebrew/lib -L/usr/lib -llua
Paths: [/opt/homebrew/lib /usr/lib]
Names: [lua]
Test case: No valid library
Input: -L/opt/homebrew/lib
Paths: [/opt/homebrew/lib]
Names: []
=== Test GenDylibPaths ===
Test case: existing dylib
Path libsymb1 is in the expected paths
Test case: existing dylibs
Path libsymb1 is in the expected paths
Path libsymb2 is in the expected paths
Test case: existint default paths
Path libsymb1 is in the expected paths
Path libsymb3 is in the expected paths
Test case: no existing dylib
=== Test GetCommonSymbols === === Test GetCommonSymbols ===
Test Case: Lua symbols Test Case: Lua symbols

View File

@@ -3,10 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"runtime"
"sort" "sort"
"strings"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/parse" "github.com/goplus/llgo/chore/_xtool/llcppsymg/parse"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/symbol" "github.com/goplus/llgo/chore/_xtool/llcppsymg/symbol"
@@ -15,158 +12,11 @@ import (
) )
func main() { func main() {
TestParseLibConfig()
TestGenDylibPaths()
TestGetCommonSymbols() TestGetCommonSymbols()
TestReadExistingSymbolTable() TestReadExistingSymbolTable()
TestGenSymbolTableData() TestGenSymbolTableData()
} }
func TestParseLibConfig() {
fmt.Println("=== Test ParseLibConfig ===")
testCases := []struct {
name string
input string
}{
{
name: "Lua library",
input: "-L/opt/homebrew/lib -llua -lm",
},
{
name: "SQLite library",
input: "-L/opt/homebrew/opt/sqlite/lib -lsqlite3",
},
{
name: "INIReader library",
input: "-L/opt/homebrew/Cellar/inih/58/lib -lINIReader",
},
{
name: "Multiple library paths",
input: "-L/opt/homebrew/lib -L/usr/lib -llua",
},
{
name: "No valid library",
input: "-L/opt/homebrew/lib",
},
}
for _, tc := range testCases {
fmt.Printf("Test case: %s\n", tc.name)
fmt.Printf("Input: %s\n", tc.input)
conf := symbol.ParseLibConfig(tc.input)
fmt.Println("Paths:", conf.Paths)
fmt.Println("Names:", conf.Names)
}
}
func TestGenDylibPaths() {
fmt.Println("=== Test GenDylibPaths ===")
tempDir := os.TempDir()
tempDefaultPath := filepath.Join(tempDir, "symblib")
affix := ".dylib"
if runtime.GOOS == "linux" {
affix = ".so"
}
err := os.MkdirAll(tempDefaultPath, 0755)
if err != nil {
fmt.Printf("Failed to create temp default path: %v\n", err)
return
}
dylib1 := filepath.Join(tempDir, "libsymb1"+affix)
dylib2 := filepath.Join(tempDir, "libsymb2"+affix)
defaultDylib3 := filepath.Join(tempDefaultPath, "libsymb3"+affix)
os.Create(dylib1)
os.Create(dylib2)
os.Create(defaultDylib3)
defer os.Remove(dylib1)
defer os.Remove(dylib2)
defer os.Remove(defaultDylib3)
defer os.Remove(tempDefaultPath)
testCase := []struct {
name string
conf *symbol.LibConfig
defaultPaths []string
want []string
wantErr bool
}{
{
name: "existing dylib",
conf: &symbol.LibConfig{
Names: []string{"symb1"},
Paths: []string{tempDir},
},
defaultPaths: []string{},
want: []string{dylib1},
},
{
name: "existing dylibs",
conf: &symbol.LibConfig{
Names: []string{"symb1", "symb2"},
Paths: []string{tempDir},
},
defaultPaths: []string{},
want: []string{dylib1, dylib2},
},
{
name: "existint default paths",
conf: &symbol.LibConfig{
Names: []string{"symb1", "symb3"},
Paths: []string{tempDir},
},
defaultPaths: []string{tempDefaultPath},
want: []string{dylib1, defaultDylib3},
},
{
name: "no existing dylib",
conf: &symbol.LibConfig{
Names: []string{"notexist"},
Paths: []string{tempDir},
},
want: []string{},
wantErr: true,
},
}
for _, tc := range testCase {
fmt.Printf("Test case: %s\n", tc.name)
paths, err := symbol.GenDylibPaths(tc.conf, tc.defaultPaths)
if tc.wantErr {
if err == nil {
fmt.Printf("Expected error, but got nil\n")
}
} else {
if err != nil {
fmt.Printf("Unexpected error: %v\n", err)
}
for _, path := range paths {
found := false
for _, wantPath := range tc.want {
if path == wantPath {
found = true
fileName := filepath.Base(path)
if runtime.GOOS == "linux" {
fileName = strings.TrimSuffix(fileName, ".so")
} else {
fileName = strings.TrimSuffix(fileName, ".dylib")
}
fmt.Printf("Path %s is in the expected paths\n", fileName)
break
}
}
if !found {
fmt.Printf("Path %s is not in the expected paths\n", path)
}
}
}
}
}
func TestGetCommonSymbols() { func TestGetCommonSymbols() {
fmt.Println("=== Test GetCommonSymbols ===") fmt.Println("=== Test GetCommonSymbols ===")
testCases := []struct { testCases := []struct {

View File

@@ -0,0 +1,109 @@
package cfgparse
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
)
// Note: This package is not placed under the 'config' package because 'config'
// depends on 'cjson'. The parsing of Libs and cflags is intended to be usable
// by both llgo and go, without introducing additional dependencies.
type Libs struct {
Paths []string // Dylib Path
Names []string
}
type CFlags struct {
Paths []string // Include Path
}
func ParseLibs(libs string) *Libs {
parts := strings.Fields(libs)
lbs := &Libs{}
for _, part := range parts {
if strings.HasPrefix(part, "-L") {
lbs.Paths = append(lbs.Paths, part[2:])
} else if strings.HasPrefix(part, "-l") {
lbs.Names = append(lbs.Names, part[2:])
}
}
return lbs
}
// searches for each library name in the provided paths and default paths,
// appending the appropriate file extension (.dylib for macOS, .so for Linux).
//
// Example: For "-L/opt/homebrew/lib -llua -lm":
// - It will search for liblua.dylib (on macOS) or liblua.so (on Linux)
// - System libs like -lm are ignored and included in notFound
//
// So error is returned if no libraries found at all.
func (l *Libs) GenDylibPaths(defaultPaths []string) ([]string, []string, error) {
var foundPaths []string
var notFound []string
affix := ".dylib"
if runtime.GOOS == "linux" {
affix = ".so"
}
searchPaths := append(l.Paths, defaultPaths...)
for _, name := range l.Names {
var foundPath string
for _, path := range searchPaths {
dylibPath := filepath.Join(path, "lib"+name+affix)
if _, err := os.Stat(dylibPath); err == nil {
foundPath = dylibPath
break
}
}
if foundPath != "" {
foundPaths = append(foundPaths, foundPath)
} else {
notFound = append(notFound, name)
}
}
if len(foundPaths) == 0 {
return nil, notFound, fmt.Errorf("failed to find any libraries")
}
return foundPaths, notFound, nil
}
func ParseCFlags(cflags string) *CFlags {
parts := strings.Fields(cflags)
cf := &CFlags{}
for _, part := range parts {
if strings.HasPrefix(part, "-I") {
cf.Paths = append(cf.Paths, part[2:])
}
}
return cf
}
func (cf *CFlags) GenHeaderFilePaths(files []string) ([]string, []string, error) {
var foundPaths []string
var notFound []string
for _, file := range files {
var found bool
for _, path := range cf.Paths {
fullPath := filepath.Join(path, file)
if _, err := os.Stat(fullPath); err == nil {
foundPaths = append(foundPaths, fullPath)
found = true
break
}
}
if !found {
notFound = append(notFound, file)
}
}
if len(foundPaths) == 0 {
return nil, notFound, fmt.Errorf("failed to find any header files")
}
return foundPaths, notFound, nil
}

View File

@@ -23,6 +23,7 @@ import (
"strings" "strings"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config" "github.com/goplus/llgo/chore/_xtool/llcppsymg/config"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config/cfgparse"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/parse" "github.com/goplus/llgo/chore/_xtool/llcppsymg/parse"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/symbol" "github.com/goplus/llgo/chore/_xtool/llcppsymg/symbol"
) )
@@ -79,11 +80,15 @@ func main() {
symbols, err := symbol.ParseDylibSymbols(conf.Libs) symbols, err := symbol.ParseDylibSymbols(conf.Libs)
check(err) check(err)
filepaths, err := parse.GenHeaderFilePath(conf.CFlags, conf.Include) cflag := cfgparse.ParseCFlags(conf.CFlags)
filepaths, notFounds, err := cflag.GenHeaderFilePaths(conf.Include)
check(err) check(err)
if verbose { if verbose {
fmt.Println("filepaths", filepaths) fmt.Println("header file paths", filepaths)
if len(notFounds) > 0 {
fmt.Println("not found header files", notFounds)
}
} }
headerInfos, err := parse.ParseHeaderFile(filepaths, conf.TrimPrefixes, conf.Cplusplus, false) headerInfos, err := parse.ParseHeaderFile(filepaths, conf.TrimPrefixes, conf.Cplusplus, false)

View File

@@ -2,9 +2,6 @@ package parse
import ( import (
"errors" "errors"
"fmt"
"os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
@@ -189,37 +186,3 @@ func ParseHeaderFile(files []string, Prefixes []string, isCpp bool, isTemp bool)
index.Dispose() index.Dispose()
return processer.SymbolMap, nil return processer.SymbolMap, nil
} }
func GenHeaderFilePath(cflags string, files []string) ([]string, error) {
prefixPath := strings.TrimPrefix(cflags, "-I")
var validPaths []string
var errs []string
for _, file := range files {
if file == "" {
continue
}
fullPath := filepath.Join(prefixPath, file)
if f, err := os.Open(fullPath); err != nil {
if os.IsNotExist(err) {
errs = append(errs, fmt.Sprintf("file not found: %s", file))
} else {
errs = append(errs, fmt.Sprintf("error accessing file %s: %v", file, err))
}
} else {
f.Close()
validPaths = append(validPaths, fullPath)
}
}
if len(validPaths) == 0 && len(errs) == 0 {
return nil, fmt.Errorf("no valid header files")
}
if len(errs) > 0 {
return validPaths, fmt.Errorf("some files not found or inaccessible: %v", errs)
}
return validPaths, nil
}

View File

@@ -4,7 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"path/filepath"
"runtime" "runtime"
"strings" "strings"
"unsafe" "unsafe"
@@ -12,6 +11,7 @@ import (
"github.com/goplus/llgo/c" "github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson" "github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config" "github.com/goplus/llgo/chore/_xtool/llcppsymg/config"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/config/cfgparse"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/parse" "github.com/goplus/llgo/chore/_xtool/llcppsymg/parse"
"github.com/goplus/llgo/chore/llcppg/types" "github.com/goplus/llgo/chore/llcppg/types"
"github.com/goplus/llgo/xtool/nm" "github.com/goplus/llgo/xtool/nm"
@@ -20,73 +20,18 @@ import (
type dbgFlags = int type dbgFlags = int
const ( const (
DbgConf dbgFlags = 1 << iota DbgSymbol dbgFlags = 1 << iota
DbgSymbol DbgFlagAll = DbgSymbol
DbgFlagAll = DbgConf | DbgSymbol
) )
var ( var (
debugConf bool
debugSymbol bool debugSymbol bool
) )
func SetDebug(dbgFlags dbgFlags) { func SetDebug(dbgFlags dbgFlags) {
debugConf = (dbgFlags & DbgConf) != 0
debugSymbol = (dbgFlags & DbgSymbol) != 0 debugSymbol = (dbgFlags & DbgSymbol) != 0
} }
type LibConfig struct {
Paths []string
Names []string
}
func ParseLibConfig(lib string) *LibConfig {
parts := strings.Fields(lib)
config := &LibConfig{}
for _, part := range parts {
if strings.HasPrefix(part, "-L") {
config.Paths = append(config.Paths, part[2:])
} else if strings.HasPrefix(part, "-l") {
config.Names = append(config.Names, part[2:])
}
}
return config
}
func GenDylibPaths(config *LibConfig, defaultPaths []string) ([]string, error) {
var foundPaths []string
var notFound []string
affix := ".dylib"
if runtime.GOOS == "linux" {
affix = ".so"
}
searchPaths := append(config.Paths, defaultPaths...)
for _, name := range config.Names {
var foundPath string
for _, path := range searchPaths {
dylibPath := filepath.Join(path, "lib"+name+affix)
if _, err := os.Stat(dylibPath); err == nil {
foundPath = dylibPath
}
}
if foundPath != "" {
foundPaths = append(foundPaths, foundPath)
} else {
notFound = append(notFound, name)
}
}
if len(notFound) > 0 && debugSymbol {
fmt.Printf("Warning: Some libraries were not found: %s\n", strings.Join(notFound, ", "))
}
if len(foundPaths) == 0 {
return nil, fmt.Errorf("failed to find any libraries")
}
return foundPaths, nil
}
// ParseDylibSymbols parses symbols from dynamic libraries specified in the lib string. // ParseDylibSymbols parses symbols from dynamic libraries specified in the lib string.
// It handles multiple libraries (e.g., -L/opt/homebrew/lib -llua -lm) and returns // It handles multiple libraries (e.g., -L/opt/homebrew/lib -llua -lm) and returns
// symbols if at least one library is successfully parsed. Errors from inaccessible // symbols if at least one library is successfully parsed. Errors from inaccessible
@@ -94,23 +39,28 @@ func GenDylibPaths(config *LibConfig, defaultPaths []string) ([]string, error) {
// //
// Returns symbols and nil error if any symbols are found, or nil and error if none found. // Returns symbols and nil error if any symbols are found, or nil and error if none found.
func ParseDylibSymbols(lib string) ([]*nm.Symbol, error) { func ParseDylibSymbols(lib string) ([]*nm.Symbol, error) {
if debugConf { if debugSymbol {
fmt.Println("ParseDylibSymbols:from", lib) fmt.Println("ParseDylibSymbols:from", lib)
} }
conf := ParseLibConfig(lib) sysPaths := getSysLibPaths()
if debugConf { lbs := cfgparse.ParseLibs(lib)
if debugSymbol {
fmt.Println("ParseDylibSymbols:LibConfig Parse To") fmt.Println("ParseDylibSymbols:LibConfig Parse To")
fmt.Println("conf.Names: ", conf.Names) fmt.Println("libs.Names: ", lbs.Names)
fmt.Println("conf.Paths: ", conf.Paths) fmt.Println("libs.Paths: ", lbs.Paths)
} }
defaultPaths := getSysLibPaths() dylibPaths, notFounds, err := lbs.GenDylibPaths(sysPaths)
dylibPaths, err := GenDylibPaths(conf, defaultPaths)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to generate some dylib paths: %v", err) return nil, fmt.Errorf("failed to generate some dylib paths: %v", err)
} }
if debugSymbol { if debugSymbol {
fmt.Println("ParseDylibSymbols:dylibPaths", dylibPaths) fmt.Println("ParseDylibSymbols:dylibPaths", dylibPaths)
if len(notFounds) > 0 {
fmt.Println("ParseDylibSymbols:not found libname", notFounds)
} else {
fmt.Println("ParseDylibSymbols:every library is found")
}
} }
var symbols []*nm.Symbol var symbols []*nm.Symbol
@@ -151,7 +101,7 @@ func ParseDylibSymbols(lib string) ([]*nm.Symbol, error) {
func getSysLibPaths() []string { func getSysLibPaths() []string {
var paths []string var paths []string
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
if debugConf { if debugSymbol {
fmt.Println("getSysLibPaths:find sys lib path from linux") fmt.Println("getSysLibPaths:find sys lib path from linux")
} }
paths = []string{ paths = []string{
@@ -159,13 +109,13 @@ func getSysLibPaths() []string {
"/usr/local/lib", "/usr/local/lib",
} }
paths = append(paths, getPath("/etc/ld.so.conf")...) paths = append(paths, getPath("/etc/ld.so.conf")...)
if debugConf && len(paths) == 0 { if debugSymbol && len(paths) == 0 {
fmt.Println("getSysLibPaths:/etc/ld.so.conf havent find any path") fmt.Println("getSysLibPaths:/etc/ld.so.conf havent find any path")
} }
confd := "/etc/ld.so.conf.d" confd := "/etc/ld.so.conf.d"
dir, err := os.Stat(confd) dir, err := os.Stat(confd)
if err != nil || !dir.IsDir() { if err != nil || !dir.IsDir() {
if debugConf { if debugSymbol {
fmt.Println("getSysLibPaths:/etc/ld.so.conf.d not found or not dir") fmt.Println("getSysLibPaths:/etc/ld.so.conf.d not found or not dir")
} }
return paths return paths
@@ -183,7 +133,7 @@ func getSysLibPaths() []string {
} }
func getPath(file string) []string { func getPath(file string) []string {
if debugConf { if debugSymbol {
fmt.Println("getPath:from", file) fmt.Println("getPath:from", file)
} }
var paths []string var paths []string