Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
12d90af
chore: start modify xxh
Aug 20, 2025
9b48768
feat: Divide different cmd commands
Aug 21, 2025
c76b538
feat: Add the llpkgstore verfication function(python)
Aug 21, 2025
000e341
feat: Add the llpkgstore install function(python)
Aug 21, 2025
0d6554e
feat: Add the llpkgstore issueclose function(python)
Aug 21, 2025
fa9e456
fix: fix the generate bug
Aug 21, 2025
5b712ae
feat: Modify Chinese to English
Aug 21, 2025
052760b
feat: Add the llpkgstore demotest function(python)
Aug 21, 2025
cd9f126
feat: Add the llpkgstore release,label,post function(python)
Aug 21, 2025
22d2d53
fix: fix the llpkgstore bug
Aug 22, 2025
340850a
feat: Add the llpkgstore postprocessing function(python)
Aug 25, 2025
ceb3532
feat: Add the version information to llpkgstore.json
Aug 25, 2025
734da10
feat: modify post+release function(Python)
Aug 26, 2025
8485cc1
chore: Optimize the code
Aug 26, 2025
c4e60fb
chore: Optimize the code
Aug 26, 2025
931462c
feat: creat release if release already exists
Aug 26, 2025
0dd3a6d
feat: Remove llpkgstore.json from python, and the version will no lon…
Aug 26, 2025
1e7c5cb
feat: tag the version based on the commit information
Aug 27, 2025
8404f43
fix: bug_modify
Aug 27, 2025
3da015c
fix: bug_modify
Aug 28, 2025
5f467bc
feat: generate_modify
Aug 28, 2025
cd68035
docx: add llpkgstore.md
Sep 1, 2025
35dc243
feat: verrsion modify
Sep 5, 2025
f36243e
chore: Code improvement
Sep 5, 2025
6d72569
docx: modify_doc, add project.md
Sep 5, 2025
b215634
feat: modify generate
Sep 5, 2025
eccdf75
docx: add architecture.md
Sep 5, 2025
cc7d333
docx: modify-docs
Sep 5, 2025
c24ada5
feat: Improve the verification function of llpkgstored
Sep 5, 2025
8c6f233
fix: fix bug
Sep 5, 2025
09a326d
fix: fix the bug
Sep 5, 2025
0e3a091
feat: veify Release-as: tabulate/v0.0.25
Sep 5, 2025
5b1cde7
chore: Improve the code
Sep 5, 2025
e798886
Fix the 'actions: no artifact found' error in CI
Sep 5, 2025
545ed11
docx: add version-mapping-logic.md
Sep 5, 2025
4e642f1
feat: allow coverage
Sep 8, 2025
45bcf38
fix: modify bug
Sep 8, 2025
d0ae52e
fix: fix the bug
Sep 10, 2025
2da3b2e
feat: del llpkgstore.json
Sep 10, 2025
fe9c7fb
feat: Update Python package tag format from Python_package to py
Sep 10, 2025
16af0d8
feat: Give priority to using packages in the system environment and o…
Sep 15, 2025
f45867b
chore: Modify the Chinese annotations into English
Sep 15, 2025
b0249d0
feat: Move internal_cpp and internal_python under internal
Sep 15, 2025
f284aa4
Modify internal_cpp and internal_python to cpp and python respectively
Sep 15, 2025
8b91690
docx: Revise the project documentation and architecture documentation
Sep 15, 2025
e5fb688
Modify the project documentation and architecture documentation to En…
Sep 15, 2025
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
20 changes: 20 additions & 0 deletions _demo/llpkg.cfg.example

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid file extension name, should be llpkg.cfg

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"type": "python",
"upstream": {
"installer": {
"name": "pip",
"config": {
"options": ""
}
},
"package": {
"name": "numpy",
"version": "1.26.4"
}
},
"llpyg": {
"output_dir": "./generated",
"mod_name": "github.com/PengPengPeng717/llpkg/numpy",
"mod_depth": 2
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
}
171 changes: 171 additions & 0 deletions cmd/llpkgstore/internal/python/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package internal

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

"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
}

// isPackageInstalledInSystem checks if the specified package is already installed in the system environment
func isPackageInstalledInSystem(packageName string) bool {
// Method 1: Try to import the package directly
if canImportPackage(packageName) {
return true
}

// Method 2: Check pip list output
if isPackageInPipList(packageName) {
return true
}

return false
}

// canImportPackage tries to import the package to check if it's installed
func canImportPackage(packageName string) bool {
cmd := exec.Command("python3", "-c", fmt.Sprintf("import %s; print('OK')", packageName))
output, err := cmd.CombinedOutput()
if err != nil {
log.Printf("Package %s import test failed: %v", packageName, err)
return false
}

// Check if output contains "OK"
result := strings.TrimSpace(string(output))
return strings.Contains(result, "OK")
}

// isPackageInPipList checks if the package is in pip list
func isPackageInPipList(packageName string) bool {
cmd := exec.Command("pip3", "list")
output, err := cmd.CombinedOutput()
if err != nil {
log.Printf("Failed to run pip3 list: %v", err)
return false
}

// Check if package name is in the output
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, packageName) {
return true
}
}

return false
}

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)

// Prioritize checking if package is already installed in system environment
var pythonDir string
var tempDir string
var needCleanup bool

// Check if the package already exists in system environment
if isPackageInstalledInSystem(uc.Pkg.Name) {
log.Printf("Package %s found in system environment, using system installation", uc.Pkg.Name)
pythonDir = "" // Use system environment, no need to set PYTHONPATH
} else {
log.Printf("Package %s not found in system environment, installing to temporary directory", uc.Pkg.Name)
tempDir, err = os.MkdirTemp("", "llpkg-tool")
if err != nil {
return err
}
needCleanup = true
defer func() {
if needCleanup {
os.RemoveAll(tempDir)
}
}()

_, err = uc.Installer.Install(uc.Pkg, tempDir)
if err != nil {
return err
}
pythonDir = tempDir
}

// 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, pythonDir)
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)
}
Loading