package languages import ( "regexp" "strings" "github.com/zzet/gortex/internal/graph" "github.com/zzet/gortex/internal/parser" ) // CMake is command-call-structured. `function(NAME ...)` / // `macro(NAME ...)` introduce callable blocks terminated by // `endfunction()` / `endmacro()`; `add_library` / `add_executable` // declare build targets (modelled as function nodes); `set(NAME ...)` // declares variables; `include(...)` and `add_subdirectory(...)` // are imports. var ( cmakeFunctionRe = regexp.MustCompile(`(?mi)^\s*function\s*\(\s*([A-Za-z_][\w-]*)`) cmakeMacroRe = regexp.MustCompile(`(?mi)^\s*macro\s*\(\s*([A-Za-z_][\w-]*)`) cmakeIncludeRe = regexp.MustCompile(`(?mi)^\s*include\s*\(\s*([^)\s]+)`) cmakeAddSubdirRe = regexp.MustCompile(`(?mi)^\s*add_subdirectory\s*\(\s*([^)\s]+)`) cmakeSetRe = regexp.MustCompile(`(?mi)^\s*set\s*\(\s*([A-Za-z_][\w]*)`) cmakeAddLibraryRe = regexp.MustCompile(`(?mi)^\s*add_library\s*\(\s*([A-Za-z_][\w-]*)`) cmakeAddExecutableRe = regexp.MustCompile(`(?mi)^\s*add_executable\s*\(\s*([A-Za-z_][\w-]*)`) ) // CMakeExtractor extracts CMake source using regex. type CMakeExtractor struct{} func NewCMakeExtractor() *CMakeExtractor { return &CMakeExtractor{} } func (e *CMakeExtractor) Language() string { return "cmake" } func (e *CMakeExtractor) Extensions() []string { return []string{".cmake", "CMakeLists.txt"} } func (e *CMakeExtractor) Extract(filePath string, src []byte) (*parser.ExtractionResult, error) { lines := strings.Split(string(src), "\n") result := &parser.ExtractionResult{} fileNode := &graph.Node{ ID: filePath, Kind: graph.KindFile, Name: filePath, FilePath: filePath, StartLine: 1, EndLine: len(lines), Language: "cmake", } result.Nodes = append(result.Nodes, fileNode) seen := make(map[string]bool) add := func(name string, kind graph.NodeKind, start, end int) { if name == "" { return } id := filePath + "::" + name if seen[id] { return } seen[id] = true result.Nodes = append(result.Nodes, &graph.Node{ ID: id, Kind: kind, Name: name, FilePath: filePath, StartLine: start, EndLine: end, Language: "cmake", }) result.Edges = append(result.Edges, &graph.Edge{ From: fileNode.ID, To: id, Kind: graph.EdgeDefines, FilePath: filePath, Line: start, }) } for _, m := range cmakeFunctionRe.FindAllSubmatchIndex(src, -1) { name := string(src[m[2]:m[3]]) line := lineAt(src, m[0]) add(name, graph.KindFunction, line, findKeywordBlockEnd(lines, line, "endfunction")) } for _, m := range cmakeMacroRe.FindAllSubmatchIndex(src, -1) { name := string(src[m[2]:m[3]]) line := lineAt(src, m[0]) add(name, graph.KindFunction, line, findKeywordBlockEnd(lines, line, "endmacro")) } for _, m := range cmakeAddLibraryRe.FindAllSubmatchIndex(src, -1) { name := string(src[m[2]:m[3]]) line := lineAt(src, m[0]) add(name, graph.KindFunction, line, line) } for _, m := range cmakeAddExecutableRe.FindAllSubmatchIndex(src, -1) { name := string(src[m[2]:m[3]]) line := lineAt(src, m[0]) add(name, graph.KindFunction, line, line) } for _, m := range cmakeSetRe.FindAllSubmatchIndex(src, -1) { name := string(src[m[2]:m[3]]) line := lineAt(src, m[0]) add(name, graph.KindVariable, line, line) } for _, m := range cmakeIncludeRe.FindAllSubmatchIndex(src, -1) { mod := strings.Trim(string(src[m[2]:m[3]]), `"`) line := lineAt(src, m[0]) result.Edges = append(result.Edges, &graph.Edge{ From: fileNode.ID, To: "unresolved::import::" + mod, Kind: graph.EdgeImports, FilePath: filePath, Line: line, }) } for _, m := range cmakeAddSubdirRe.FindAllSubmatchIndex(src, -1) { mod := strings.Trim(string(src[m[2]:m[3]]), `"`) line := lineAt(src, m[0]) result.Edges = append(result.Edges, &graph.Edge{ From: fileNode.ID, To: "unresolved::import::" + mod, Kind: graph.EdgeImports, FilePath: filePath, Line: line, }) } return result, nil } var _ parser.Extractor = (*CMakeExtractor)(nil)