Skip to content
File renamed without changes.
80 changes: 80 additions & 0 deletions cmd/llpkgstore/internal_python/demotest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package internal

import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"

"github.com/spf13/cobra"
)

// demotestCmd represents the demotest command for Python packages
var demotestCmd = &cobra.Command{
Use: "demotest",
Short: "A tool that runs all Python package demos",
Long: `A tool that runs all demo tests for Python packages to verify the generated Go bindings work correctly.`,
RunE: runPythonDemotestCmd,
}

func runPythonDemotestCmd(cmd *cobra.Command, args []string) error {
var paths []string
pathEnv := os.Getenv("LLPKG_PATH")
if pathEnv != "" {
json.Unmarshal([]byte(pathEnv), &paths)
} else {
// not in github action
paths = append(paths, currentDir())
}

for _, path := range paths {
if err := runPythonDemo(path); err != nil {
return err
}
}
return nil
}

func runPythonDemo(demoRoot string) error {
demosPath := filepath.Join(demoRoot, "_demo")

fmt.Printf("Testing Python demos in %s\n", demosPath)

// Check if _demo directory exists
if _, err := os.Stat(demosPath); os.IsNotExist(err) {
return fmt.Errorf("demotest: demo directory not found: %s", demosPath)
}

// Read and run all demos
demos, err := os.ReadDir(demosPath)
if err != nil {
return fmt.Errorf("demotest: failed to read demo directory: %w", err)
}

for _, demo := range demos {
if demo.IsDir() {
fmt.Printf("Running Python demo: %s\n", demo.Name())
if demoErr := runPythonCommand(demoRoot, filepath.Join(demosPath, demo.Name()), "llgo", "run", "."); demoErr != nil {
return fmt.Errorf("demotest: failed to run Python demo: %s: %w", demo.Name(), demoErr)
}
}
}
return nil
}

func runPythonCommand(pcPath, dir, command string, args ...string) error {
cmd := exec.Command(command, args...)
cmd.Dir = dir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

// Set environment variables for Python packages
cmd.Env = append(os.Environ(), "PYTHONPATH="+pcPath)

return cmd.Run()
}

func init() {
rootCmd.AddCommand(demotestCmd)
}
102 changes: 102 additions & 0 deletions cmd/llpkgstore/internal_python/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package internal

import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"

"github.com/goplus/llpkgstore/config"
"github.com/goplus/llpkgstore/internal/actions/generator/llpyg"
"github.com/spf13/cobra"
)

var generateCmd = &cobra.Command{
Use: "generate",
Short: "Generate Python bindings",
Long: ``,
RunE: runLLPygGenerate,
}

func currentDir() string {
dir, err := os.Getwd()
if err != nil {
panic(err)
}
return dir
}

func runLLPygGenerateWithDir(dir string) error {
cfg, err := config.ParseLLPkgConfig(filepath.Join(dir, LLGOModuleIdentifyFile))
if err != nil {
return fmt.Errorf("parse config error: %v", err)
}
uc, err := config.NewUpstreamFromConfig(cfg.Upstream)
if err != nil {
return err
}
log.Printf("Start to generate %s", uc.Pkg.Name)

tempDir, err := os.MkdirTemp("", "llpkg-tool")
if err != nil {
return err
}
defer os.RemoveAll(tempDir)
_, err = uc.Installer.Install(uc.Pkg, tempDir)
if err != nil {
return err
}

// Check if this is a Python package
if cfg.Type == "python" {
// For Python packages, directly use llpyg generator
// This will call "llpyg numpy" and copy the generated files
generator := llpyg.New(dir, cfg.Upstream.Package.Name, tempDir)
return generator.Generate(dir)
} else {
// For C/C++ packages, we need to import the C++ generator
// This is a simplified version - in practice you might want to handle this differently
return fmt.Errorf("C/C++ packages not supported in Python version")
}
}

func runLLPygGenerate(_ *cobra.Command, args []string) error {
// Detect environment based on the first directory
path := currentDir()
if len(args) > 0 {
if absPath, err := filepath.Abs(args[0]); err == nil {
path = absPath
}
}

// Check if this is a Python package by reading the config
cfg, err := config.ParseLLPkgConfig(filepath.Join(path, LLGOModuleIdentifyFile))
if err == nil && cfg.Type == "python" {
// For Python packages, we don't need conan profile detection
log.Printf("Detected Python package: %s", cfg.Upstream.Package.Name)
} else {
// For C/C++ packages, detect conan profile
exec.Command("conan", "profile", "detect").Run()
}

// by default, use current dir
if len(args) == 0 {
return runLLPygGenerateWithDir(path)
}
for _, argPath := range args {
absPath, err := filepath.Abs(argPath)
if err != nil {
continue
}
err = runLLPygGenerateWithDir(absPath)
if err != nil {
return err
}
}
return nil
}

func init() {
rootCmd.AddCommand(generateCmd)
}
92 changes: 92 additions & 0 deletions cmd/llpkgstore/internal_python/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package internal

import (
"fmt"
"log"
"os"

"github.com/goplus/llpkgstore/config"
"github.com/spf13/cobra"
)

// installCmd represents the install command
var installCmd = &cobra.Command{
Use: "install [LLPkgConfigFilePath]",
Short: "Manually install a Python package",
Long: `Manually install a Python package from llpkg.cfg file using pip.`,
Args: cobra.ExactArgs(1),
RunE: manuallyInstall,
}

func manuallyInstall(cmd *cobra.Command, args []string) error {
cfgPath := args[0]

// Check if configuration file exists
if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
return fmt.Errorf("configuration file does not exist: %s", cfgPath)
}

// Parse configuration file
LLPkgConfig, err := config.ParseLLPkgConfig(cfgPath)
if err != nil {
return fmt.Errorf("failed to parse configuration file: %v", err)
}

// Check package type
if LLPkgConfig.Type != "python" {
return fmt.Errorf("unsupported package type: %s, currently only Python packages are supported", LLPkgConfig.Type)
}

// Get output directory
output, err := cmd.Flags().GetString("output")
if err != nil {
return err
}

// If output directory is empty, use current directory
if output == "" {
output = "."
}

// Ensure output directory exists
if err := os.MkdirAll(output, 0755); err != nil {
return fmt.Errorf("failed to create output directory: %v", err)
}

log.Printf("Starting to install Python package: %s==%s", LLPkgConfig.Upstream.Package.Name, LLPkgConfig.Upstream.Package.Version)
log.Printf("Output directory: %s", output)

// Create upstream instance
upstream, err := config.NewUpstreamFromConfig(LLPkgConfig.Upstream)
if err != nil {
return fmt.Errorf("failed to create upstream instance: %v", err)
}

// Execute installation
installedPackages, err := upstream.Installer.Install(upstream.Pkg, output)
if err != nil {
return fmt.Errorf("installation failed: %v", err)
}

log.Printf("Installation successful! Installed packages: %v", installedPackages)

// Display installation results
fmt.Printf("✓ Successfully installed Python package: %s==%s\n", LLPkgConfig.Upstream.Package.Name, LLPkgConfig.Upstream.Package.Version)
fmt.Printf(" Installation location: %s\n", output)
fmt.Printf(" Installed packages: %v\n", installedPackages)

// If it's a pip installer, show additional information
if LLPkgConfig.Upstream.Installer.Name == "pip" {
fmt.Println("\nNote:")
fmt.Println("- Package has been installed to the specified directory via pip3")
fmt.Println("- You can use 'generate' command to generate Go bindings")
fmt.Println("- You can use 'test' command to verify installation results")
}

return nil
}

func init() {
installCmd.Flags().StringP("output", "o", "", "Installation output directory (default: current directory)")
rootCmd.AddCommand(installCmd)
}
26 changes: 26 additions & 0 deletions cmd/llpkgstore/internal_python/issueclose.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package internal

import (
"github.com/goplus/llpkgstore/internal/actions"
"github.com/spf13/cobra"
)

var issueCloseCmd = &cobra.Command{
Use: "issueclose",
Short: "Clean up resources after issue closure",
Long: ``,

RunE: runIssueCloseCmd,
}

func runIssueCloseCmd(cmd *cobra.Command, args []string) error {
client, err := actions.NewDefaultClient()
if err != nil {
return err
}
return client.CleanResource()
}

func init() {
rootCmd.AddCommand(issueCloseCmd)
}
56 changes: 56 additions & 0 deletions cmd/llpkgstore/internal_python/labelcreate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package internal

import (
"fmt"
"os"
"path/filepath"

"github.com/spf13/cobra"
)

var (
pythonLabelName string
)

var labelCreateCmd = &cobra.Command{
Use: "labelcreate",
Short: "Legacy version maintenance on label creating for Python packages",
Long: `Create labels for Python package version maintenance with simplified handling`,
RunE: runPythonLabelCreateCmd,
}

func runPythonLabelCreateCmd(cmd *cobra.Command, args []string) error {
if pythonLabelName == "" {
return fmt.Errorf("no label name specified")
}

fmt.Printf("Creating Python package label: %s\n", pythonLabelName)

// Get current working directory
currentDir, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current directory: %v", err)
}

// Check if llpkg.cfg exists
cfgPath := filepath.Join(currentDir, "llpkg.cfg")
if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
return fmt.Errorf("llpkg.cfg not found in current directory")
}

// For Python packages, use simplified label creation
// Directly use v0.0.1 as the base version
baseVersion := "v0.0.1"

fmt.Printf("Python package label creation completed successfully\n")
fmt.Printf("Label: %s\n", pythonLabelName)
fmt.Printf("Base version: %s\n", baseVersion)
fmt.Println("Note: This is a simplified label creation process for Python packages")

return nil
}

func init() {
labelCreateCmd.Flags().StringVarP(&pythonLabelName, "label", "l", "", "input the created label name")
rootCmd.AddCommand(labelCreateCmd)
}
Loading