Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github: [itsfuad]
buy_me_a_coffee: itsfuad
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ bindings
*.lib
*.exp
*.def
*.obj
*.manifest
4 changes: 2 additions & 2 deletions binding/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package binding

import (
"fmt"
"html/template"
"os"
"path/filepath"
"runtime"
"text/template"

"cp2p/config"
)
Expand Down Expand Up @@ -57,7 +57,7 @@ func (g *Generator) generate() error {
}

func (g *Generator) generateBindingCode(file *os.File) error {
// Define the template for the Python binding
// Define the template for the Python binding using html/template for security
tmpl := template.Must(template.New("binding").Parse(pythonBindingTemplate))

// Define type mappings
Expand Down
17 changes: 15 additions & 2 deletions compiler/compile.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package compiler

import (
"context"
"fmt"
"os"
"os/exec"
Expand Down Expand Up @@ -62,7 +63,13 @@ call "%s" %s
}

// Run the batch file
cmd := exec.Command(compiler.EnvSetup.SetupCmd, batchFile)
// Validate paths are safe
if !filepath.IsAbs(compiler.EnvSetup.SetupCmd) || !filepath.IsAbs(batchFile) {
return "", fmt.Errorf("invalid command or batch file path")
}

ctx := context.Background()
cmd := exec.CommandContext(ctx, compiler.EnvSetup.SetupCmd, batchFile)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
Expand All @@ -72,7 +79,13 @@ call "%s" %s
}

// For compilers that don't need environment setup, run directly
cmd := exec.Command(compiler.Path, args...)
// Validate compiler path is safe
if !filepath.IsAbs(compiler.Path) {
return "", fmt.Errorf("invalid compiler path: %s", compiler.Path)
}

ctx := context.Background()
cmd := exec.CommandContext(ctx, compiler.Path, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

Expand Down
166 changes: 88 additions & 78 deletions compiler/detect.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
package compiler

import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)

const (
ErrInvalidCompilerPath = "invalid compiler path: %s"

Check failure on line 14 in compiler/detect.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported const ErrInvalidCompilerPath should have comment (or a comment on this block) or be unexported

Check failure on line 14 in compiler/detect.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported const ErrInvalidCompilerPath should have comment (or a comment on this block) or be unexported
ErrNoCompilerFound = "no supported compiler found"
ErrNoWindowsCompiler = "no supported compiler found on Windows"
ErrUnsupportedOS = "unsupported operating system: %s"
ErrUnsupportedCompiler = "unsupported compiler type: %s"
ErrCompilerNotFound = "compiler not found: %s"
ErrVersionCheckFailed = "failed to get compiler version: %v"
)

// CompilerType represents the type of C++ compiler
type CompilerType string

Check failure on line 24 in compiler/detect.go

View workflow job for this annotation

GitHub Actions / build-and-test

type name will be used as compiler.CompilerType by other packages, and that stutters; consider calling this Type

Check failure on line 24 in compiler/detect.go

View workflow job for this annotation

GitHub Actions / build-and-test

type name will be used as compiler.CompilerType by other packages, and that stutters; consider calling this Type

const (
CompilerGCC CompilerType = "gcc"

Check failure on line 27 in compiler/detect.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported const CompilerGCC should have comment (or a comment on this block) or be unexported

Check failure on line 27 in compiler/detect.go

View workflow job for this annotation

GitHub Actions / build-and-test

exported const CompilerGCC should have comment (or a comment on this block) or be unexported
CompilerClang CompilerType = "clang"
CompilerMSVC CompilerType = "msvc"
CompilerAuto CompilerType = "auto"
)

// CompilerInfo contains information about the detected compiler
type CompilerInfo struct {

Check failure on line 34 in compiler/detect.go

View workflow job for this annotation

GitHub Actions / build-and-test

type name will be used as compiler.CompilerInfo by other packages, and that stutters; consider calling this Info

Check failure on line 34 in compiler/detect.go

View workflow job for this annotation

GitHub Actions / build-and-test

type name will be used as compiler.CompilerInfo by other packages, and that stutters; consider calling this Info
Type CompilerType
Version string
Path string
Expand All @@ -29,7 +40,7 @@
}

// CompilerEnvSetup contains information about how to set up the compiler's environment
type CompilerEnvSetup struct {

Check failure on line 43 in compiler/detect.go

View workflow job for this annotation

GitHub Actions / build-and-test

type name will be used as compiler.CompilerEnvSetup by other packages, and that stutters; consider calling this EnvSetup

Check failure on line 43 in compiler/detect.go

View workflow job for this annotation

GitHub Actions / build-and-test

type name will be used as compiler.CompilerEnvSetup by other packages, and that stutters; consider calling this EnvSetup
SetupScript string // Path to environment setup script (e.g., vcvarsall.bat for MSVC)
SetupArgs []string // Arguments for the setup script
SetupCmd string // Command to run the setup script (e.g., "cmd /c" for MSVC)
Expand All @@ -48,7 +59,7 @@
case "linux", "darwin":
return detectUnixCompiler()
default:
return nil, fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
return nil, fmt.Errorf(ErrUnsupportedOS, runtime.GOOS)
}
}

Expand All @@ -59,9 +70,12 @@
case CompilerClang:
return checkClang()
case CompilerMSVC:
if runtime.GOOS != "windows" {
return nil, fmt.Errorf("MSVC compiler is only supported on Windows")
}
return checkMSVC()
default:
return nil, fmt.Errorf("unsupported compiler type: %s", compiler)
return nil, fmt.Errorf(ErrUnsupportedCompiler, compiler)
}
}

Expand All @@ -76,7 +90,7 @@
return info, nil
}

return nil, fmt.Errorf("no supported compiler found on Windows")
return nil, errors.New(ErrNoWindowsCompiler)
}

func detectUnixCompiler() (*CompilerInfo, error) {
Expand All @@ -90,19 +104,38 @@
return info, nil
}

return nil, fmt.Errorf("no supported compiler found")
return nil, errors.New(ErrNoCompilerFound)
}

func checkGCC() (*CompilerInfo, error) {
cmd := exec.Command("g++", "--version")
output, err := cmd.Output()
// Try different possible GCC names based on OS
compilerNames := []string{"g++", "gcc"}
if runtime.GOOS == "windows" {
compilerNames = append(compilerNames, "mingw32-g++", "x86_64-w64-mingw32-g++")
}

var path string
var err error
for _, name := range compilerNames {
path, err = exec.LookPath(name)
if err == nil {
break
}
}
if err != nil {
return nil, err
return nil, fmt.Errorf(ErrCompilerNotFound, "g++")
}

// Validate path is safe
if !filepath.IsAbs(path) {
return nil, fmt.Errorf(ErrInvalidCompilerPath, path)
}

path, err := exec.LookPath("g++")
ctx := context.Background()
cmd := exec.CommandContext(ctx, path, "--version")
output, err := cmd.Output()
if err != nil {
return nil, err
return nil, fmt.Errorf(ErrVersionCheckFailed, err)
}

return &CompilerInfo{
Expand All @@ -113,15 +146,34 @@
}

func checkClang() (*CompilerInfo, error) {
cmd := exec.Command("clang++", "--version")
output, err := cmd.Output()
// Try different possible Clang names based on OS
compilerNames := []string{"clang++", "clang"}
if runtime.GOOS == "windows" {
compilerNames = append(compilerNames, "llvm-clang++")
}

var path string
var err error
for _, name := range compilerNames {
path, err = exec.LookPath(name)
if err == nil {
break
}
}
if err != nil {
return nil, err
return nil, fmt.Errorf(ErrCompilerNotFound, "clang++")
}

// Validate path is safe
if !filepath.IsAbs(path) {
return nil, fmt.Errorf(ErrInvalidCompilerPath, path)
}

path, err := exec.LookPath("clang++")
ctx := context.Background()
cmd := exec.CommandContext(ctx, path, "--version")
output, err := cmd.Output()
if err != nil {
return nil, err
return nil, fmt.Errorf(ErrVersionCheckFailed, err)
}

return &CompilerInfo{
Expand All @@ -131,88 +183,46 @@
}, nil
}

func findMSVCIncludePath(vsPath string) string {
msvcPath := filepath.Join(vsPath, "VC\\Tools\\MSVC")
entries, err := os.ReadDir(msvcPath)
if err != nil {
return ""
}
for _, entry := range entries {
if entry.IsDir() {
return filepath.Join(msvcPath, entry.Name(), "include")
}
}
return ""
}

func findSDKIncludePath() string {
sdkPath := "C:\\Program Files (x86)\\Windows Kits\\10\\Include"
entries, err := os.ReadDir(sdkPath)
if err != nil {
return ""
}
for _, entry := range entries {
if entry.IsDir() {
return filepath.Join(sdkPath, entry.Name(), "ucrt")
}
}
return ""
}

func checkMSVC() (*CompilerInfo, error) {
// First check if cl.exe is available
path, err := exec.LookPath("cl.exe")
if err != nil {
return nil, err
return nil, fmt.Errorf(ErrCompilerNotFound, "cl.exe")
}

// Validate path is safe
if !filepath.IsAbs(path) {
return nil, fmt.Errorf(ErrInvalidCompilerPath, path)
}

// Get the version info from cl.exe
cmd := exec.Command("cl.exe")
ctx := context.Background()
cmd := exec.CommandContext(ctx, path)
output, err := cmd.Output()
if err != nil {
return nil, err
}

// Find Visual Studio path by looking for cl.exe's parent directory
vsPath := ""
dir := filepath.Dir(path)
for {
if dir == "" || dir == "." || dir == "/" {
break
}
if strings.Contains(filepath.Base(dir), "Microsoft Visual Studio") {
vsPath = dir
break
}
dir = filepath.Dir(dir)
return nil, fmt.Errorf(ErrVersionCheckFailed, err)
}

// Find include paths relative to cl.exe location
includePaths := []string{}
var envSetup *CompilerEnvSetup
if vsPath != "" {
if msvcPath := findMSVCIncludePath(vsPath); msvcPath != "" {
includePaths = append(includePaths, msvcPath)
}
if sdkPath := findSDKIncludePath(); sdkPath != "" {
includePaths = append(includePaths, sdkPath)
}
compilerDir := filepath.Dir(path)

// Set up MSVC environment configuration
vcvarsall := filepath.Join(vsPath, "VC\\Auxiliary\\Build\\vcvarsall.bat")
if _, err := os.Stat(vcvarsall); err == nil {
envSetup = &CompilerEnvSetup{
SetupScript: vcvarsall,
SetupArgs: []string{"x64"},
SetupCmd: "cmd /c",
}
}
// Look for include directory in the same directory as cl.exe
includeDir := filepath.Join(compilerDir, "include")
if _, err := os.Stat(includeDir); err == nil {
includePaths = append(includePaths, includeDir)
}

// Look for include directory in parent directory
parentIncludeDir := filepath.Join(filepath.Dir(compilerDir), "include")
if _, err := os.Stat(parentIncludeDir); err == nil {
includePaths = append(includePaths, parentIncludeDir)
}

return &CompilerInfo{
Type: CompilerMSVC,
Version: string(output),
Path: path,
IncludePaths: includePaths,
EnvSetup: envSetup,
}, nil
}
Loading
Loading