Skip to content

Commit

Permalink
llcppg:current pkg info
Browse files Browse the repository at this point in the history
  • Loading branch information
luoliwoshang committed Feb 19, 2025
1 parent 9b3dc41 commit 45a82ba
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 0 deletions.
47 changes: 47 additions & 0 deletions _xtool/llcppsymg/_cmptest/config_test/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import (
"os"
"path/filepath"
"runtime"
"sort"
"strings"

"github.com/goplus/llcppg/_xtool/llcppsymg/config"
"github.com/goplus/llcppg/_xtool/llcppsymg/config/cfgparse"
"github.com/goplus/llcppg/types"
)

func main() {
Expand All @@ -17,6 +19,7 @@ func main() {
TestGenDylibPaths()
TestParseCFlags()
TestGenHeaderFilePath()
TestPkgHfileInfo()
}

func TestGetConf() {
Expand Down Expand Up @@ -330,3 +333,47 @@ func TestGenHeaderFilePath() {
fmt.Println()
}
}

func TestPkgHfileInfo() {
info := config.PkgHfileInfo(&types.Config{
CFlags: "-I./hfile -I ./thirdhfile",
Include: []string{"temp1.h", "temp2.h"},
}, []string{})
inters := sortMapOutput(info.Interfaces)

Check failure on line 342 in _xtool/llcppsymg/_cmptest/config_test/config.go

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18)

info.Interfaces undefined (type *config.PkgHfilesInfo has no field or method Interfaces)
impls := sortMapOutput(info.Impl)

Check failure on line 343 in _xtool/llcppsymg/_cmptest/config_test/config.go

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18)

info.Impl undefined (type *config.PkgHfilesInfo has no field or method Impl)
fmt.Println("interfaces", inters)
fmt.Println("implements", impls)

thirdhfile, err := filepath.Abs("./thirdhfile/third.h")
if err != nil {
panic(err)
}
tfileFound := false
stdioFound := false
for tfile := range info.ThirdFiles {

Check failure on line 353 in _xtool/llcppsymg/_cmptest/config_test/config.go

View workflow job for this annotation

GitHub Actions / test (macos-latest, 18)

info.ThirdFiles undefined (type *config.PkgHfilesInfo has no field or method ThirdFiles)
absTfile, err := filepath.Abs(tfile)
if err != nil {
panic(err)
}
if absTfile == thirdhfile {
tfileFound = true
println("third hfile found", tfile)
}
if strings.HasSuffix(absTfile, "stdio.h") {
stdioFound = true
}
}
if !tfileFound || !stdioFound {
panic("third hfile not found")
}
println("All third hfile found")
}

func sortMapOutput(m map[string]struct{}) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}
2 changes: 2 additions & 0 deletions _xtool/llcppsymg/_cmptest/config_test/hfile/temp1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include "tempimpl.h"
#include <third.h>
1 change: 1 addition & 0 deletions _xtool/llcppsymg/_cmptest/config_test/hfile/temp2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include <stdio.h>
Empty file.
Empty file.
12 changes: 12 additions & 0 deletions _xtool/llcppsymg/clangutils/clangutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package clangutils

import (
"errors"
"os"
"unsafe"

"github.com/goplus/llgo/c"
Expand Down Expand Up @@ -109,3 +110,14 @@ func GetInclusions(unit *clang.TranslationUnit, visitor InclusionVisitor) {
cfn(inced, ics)
}, unsafe.Pointer(&visitor))
}

// ComposeIncludes create Include list
// #include <file1.h>
// #include <file2.h>
func ComposeIncludes(files []string, outfile string) error {
var str string
for _, file := range files {
str += ("#include <" + file + ">\n")
}
return os.WriteFile(outfile, []byte(str), 0644)
}
100 changes: 100 additions & 0 deletions _xtool/llcppsymg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ package config

import (
"errors"
"os"
"path/filepath"
"strings"
"unsafe"

"github.com/goplus/llcppg/_xtool/llcppsymg/clangutils"
"github.com/goplus/llcppg/types"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/c/clang"
)

type Conf struct {
Expand Down Expand Up @@ -70,3 +75,98 @@ func GetBoolItem(obj *cjson.JSON, key string) bool {
}
return false
}

type PkgHfilesInfo struct {
Inters map[string]struct{} // From types.Config.Include
Impls map[string]struct{} // From same root of types.Config.Include
Thirds map[string]struct{} // Not Current Pkg's Files
}

// PkgHfileInfo analyzes header files dependencies and categorizes them into three groups:
// 1. Inters: Direct includes from types.Config.Include
// 2. Impls: Header files from the same root directory as Inters
// 3. Thirds: Header files from external sources
//
// The function works by:
// 1. Creating a temporary header file that includes all headers from conf.Include
// 2. Using clang to parse the translation unit and analyze includes
// 3. Categorizing includes based on their inclusion level and path relationship
func PkgHfileInfo(conf *types.Config, args []string) *PkgHfilesInfo {
info := &PkgHfilesInfo{
Inters: make(map[string]struct{}),
Impls: make(map[string]struct{}),
Thirds: make(map[string]struct{}),
}
outfile, err := os.CreateTemp("", "compose_*.h")
if err != nil {
panic(err)
}

cflags := append(args, strings.Fields(conf.CFlags)...)
clangutils.ComposeIncludes(conf.Include, outfile.Name())
index, unit, err := clangutils.CreateTranslationUnit(&clangutils.Config{
File: outfile.Name(),
Temp: false,
Args: cflags,
})
if err != nil {
panic(err)
}
defer unit.Dispose()
defer index.Dispose()

inters := []string{}
others := []string{} // impl & third
clangutils.GetInclusions(unit, func(inced clang.File, incins []clang.SourceLocation) {
// first level include is the conf.include's abs path
filename := clang.GoString(inced.FileName())
if len(incins) == 1 {
inters = append(inters, filename)
info.Inters[filename] = struct{}{}
} else {
// not in the first level include maybe impl or third hfile
_, inter := info.Inters[filename]
if len(incins) > 1 && !inter {
others = append(others, filename)
}
}
})
root, err := filepath.Abs(commonParentDir(inters))
if err != nil {
panic(err)
}
for _, f := range others {
file, err := filepath.Abs(f)
if err != nil {
panic(err)
}
if strings.HasPrefix(file, root) {
info.Impls[f] = struct{}{}
} else {
info.Thirds[f] = struct{}{}
}
}
return info
}

// commonParentDir finds the longest common parent directory path for a given slice of paths.
// For example, given paths ["/a/b/c/d", "/a/b/e/f"], it returns "/a/b".
func commonParentDir(paths []string) string {
if len(paths) == 0 {
return ""
}

parts := make([][]string, len(paths))
for i, path := range paths {
parts[i] = strings.Split(filepath.Dir(path), string(filepath.Separator))
}

for i := 0; i < len(parts[0]); i++ {
for j := 1; j < len(parts); j++ {
if i == len(parts[j]) || parts[j][i] != parts[0][i] {
return filepath.Join(parts[0][:i]...)
}
}
}
return filepath.Dir(paths[0])
}

0 comments on commit 45a82ba

Please sign in to comment.