Last active
March 15, 2024 11:36
-
-
Save mrkvn/f503ed64b30640835f45f49de7314ce0 to your computer and use it in GitHub Desktop.
Run Python executable in Go binary
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"embed" | |
"io" | |
"io/fs" | |
"log" | |
"os" | |
"os/exec" | |
"path/filepath" | |
) | |
//go:embed all:dist/main | |
var embeddedFiles embed.FS | |
func main() { | |
if err := extractAndRunExecutable(); err != nil { | |
log.Fatalf("Failed to extract and run the executable: %v", err) | |
} | |
} | |
func extractDirectory(dst string, fsys embed.FS, root string) error { | |
return fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error { | |
if err != nil { | |
return err | |
} | |
relPath, err := filepath.Rel(root, path) | |
if err != nil { | |
log.Printf("Error walking through extracted directory: %v", err) | |
return err | |
} | |
// Construct the target path using the correctly obtained relPath | |
targetPath := filepath.Join(dst, relPath) | |
if d.IsDir() { | |
// Create directory | |
return os.MkdirAll(targetPath, 0755) | |
} else { | |
// Extract file | |
inFile, err := fsys.Open(path) | |
if err != nil { | |
return err | |
} | |
defer inFile.Close() | |
outFile, err := os.Create(targetPath) | |
if err != nil { | |
return err | |
} | |
defer outFile.Close() | |
_, err = io.Copy(outFile, inFile) | |
return err | |
} | |
}) | |
} | |
func extractAndRunExecutable() error { | |
// Create a temporary directory | |
tmpDir, err := os.MkdirTemp("", "tempdir") | |
if err != nil { | |
return err | |
} | |
defer os.RemoveAll(tmpDir) // Clean up after execution | |
// Extract the embedded directory structure | |
if err := extractDirectory(tmpDir, embeddedFiles, "dist/main"); err != nil { | |
return err | |
} | |
// Make the main executable file executable | |
binaryPath := filepath.Join(tmpDir, "main") | |
if err := os.Chmod(binaryPath, 0755); err != nil { | |
return err | |
} | |
// Execute the binary with its dependencies available | |
cmd := exec.Command(binaryPath) | |
cmd.Dir = tmpDir // Set the working directory to the temp directory | |
// Capture both stdout and stderr | |
var stdout, stderr bytes.Buffer | |
cmd.Stdout = &stdout | |
cmd.Stderr = &stderr | |
// Run the command | |
err = cmd.Run() | |
if err != nil { | |
log.Printf("Failed to execute the command: %v", err) | |
log.Printf("stdout: %s", stdout.String()) | |
log.Printf("stderr: %s", stderr.String()) | |
return err | |
} | |
log.Printf("Command stdout: %s", stdout.String()) | |
// return cmd.Run() | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Tested in MacOS M2 Pro
main.py
Python
alias file in thedist/main/_internal/
with the actual file.dist/main/
and paste it besidesmain.go
.This
main.go
file, effectively embed thedist/main/
in the Go binary and to run the Python executable, it copies those directories/files/dependencies in a temp directory, then execute the Python executable afterwards.