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:
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user