feat: add C++ support with ImGui/ImPlot framework and vendor submodules

Añade soporte C++ al registry: vendor submodules (glfw, imgui, implot, tracy),
sistema de build con CMake y toolchains cross-platform, runner C++ en fn CLI,
parser de tests Google Test, y funciones bash para build Linux/Windows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-06 23:46:36 +02:00
parent 0c759c1b66
commit 4b2bb6998a
36 changed files with 1065 additions and 0 deletions
+128
View File
@@ -0,0 +1,128 @@
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"fn-registry/registry"
)
func buildCppCommand(fn *registry.Function, registryRoot, absPath string, args []string) (*exec.Cmd, error) {
cppRoot := filepath.Join(registryRoot, "cpp")
buildDir := filepath.Join(cppRoot, "build", "linux")
// Ensure build directory exists and cmake is configured
if _, err := os.Stat(filepath.Join(buildDir, "CMakeCache.txt")); os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "[fn run] configuring cmake for cpp...\n")
configure := exec.Command("cmake", "-B", buildDir, "-S", cppRoot)
configure.Dir = registryRoot
configure.Stdout = os.Stderr
configure.Stderr = os.Stderr
if err := configure.Run(); err != nil {
return nil, fmt.Errorf("cmake configure failed: %w", err)
}
}
dir := filepath.Dir(absPath)
// Check if the function's directory has its own CMakeLists.txt (app with main)
localCMake := filepath.Join(dir, "CMakeLists.txt")
hasMain := false
if _, err := os.Stat(localCMake); err == nil {
hasMain = true
}
// Also check for main.cpp in the same directory
mainCpp := filepath.Join(dir, "main.cpp")
if _, err := os.Stat(mainCpp); err == nil {
hasMain = true
}
if hasMain {
// Build and run the app binary
targetName := filepath.Base(dir)
build := exec.Command("cmake", "--build", buildDir, "--target", targetName)
build.Dir = registryRoot
build.Stdout = os.Stderr
build.Stderr = os.Stderr
fmt.Fprintf(os.Stderr, "[fn run] building target %s...\n", targetName)
if err := build.Run(); err != nil {
return nil, fmt.Errorf("cmake build failed: %w", err)
}
// Find the built binary
binaryPath := findBinary(buildDir, targetName)
if binaryPath == "" {
return nil, fmt.Errorf("built binary %q not found in %s", targetName, buildDir)
}
cmd := exec.Command(binaryPath, args...)
cmd.Dir = dir
return cmd, nil
}
// Library code: compile-check only (like go vet)
build := exec.Command("cmake", "--build", buildDir)
build.Dir = registryRoot
build.Stdout = os.Stderr
build.Stderr = os.Stderr
fmt.Fprintf(os.Stderr, "[fn run] %s is library code — running compile check\n", fn.ID)
if err := build.Run(); err != nil {
return nil, fmt.Errorf("compile check failed: %w", err)
}
// Return a no-op command that just prints success
cmd := exec.Command("echo", fmt.Sprintf("[fn run] %s compiled successfully", fn.ID))
return cmd, nil
}
// findBinary searches for an executable in the build tree.
func findBinary(buildDir, name string) string {
// Common locations cmake puts binaries
candidates := []string{
filepath.Join(buildDir, name),
filepath.Join(buildDir, "apps", name, name),
}
for _, c := range candidates {
if info, err := os.Stat(c); err == nil && !info.IsDir() {
// Check if executable
if info.Mode()&0111 != 0 {
return c
}
}
}
// Walk the build directory as fallback
var found string
filepath.Walk(buildDir, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() {
return nil
}
if info.Name() == name && info.Mode()&0111 != 0 {
found = path
return filepath.SkipAll
}
return nil
})
// Also try without extension match for paths with subdirectories
if found == "" {
filepath.Walk(buildDir, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() {
return nil
}
base := strings.TrimSuffix(info.Name(), filepath.Ext(info.Name()))
if base == name && info.Mode()&0111 != 0 {
found = path
return filepath.SkipAll
}
return nil
})
}
return found
}
+2
View File
@@ -103,6 +103,8 @@ func buildCommand(fn *registry.Function, db *registry.DB, registryRoot, absPath
return buildBashCommand(absPath, args)
case "ts":
return buildTsCommand(registryRoot, absPath, args)
case "cpp":
return buildCppCommand(fn, registryRoot, absPath, args)
default:
return nil, fmt.Errorf("unsupported lang %q for execution", fn.Lang)
}