Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*.so
*.dylib
*.DS_Store
*_autogen.go

# Test binary, built with `go test -c`
*.test
Expand Down
148 changes: 148 additions & 0 deletions cmd/internal/base/base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// Package base defines shared basic pieces of the llgo command,
// in particular logging and the Command structure.
package base

import (
"bytes"
"flag"
"fmt"
"io"
"os"
"os/exec"
"strings"

"github.com/goplus/llcppg/config"
)

// A Command is an implementation of a llcppgx command
// like llcppgx gensym or llcppgx genpkg.
type Command struct {
// Run runs the command.
// The args are the arguments after the command name.
Run func(cmd *Command, args []string)

// UsageLine is the one-line usage message.
// The words between "llcppgx" and the first flag or argument in the line are taken to be the command name.
UsageLine string

// Short is the short description shown in the 'llcppgx help' output.
Short string

// Flag is a set of flags specific to this command.
Flag flag.FlagSet

// Commands lists the available commands and help topics.
// The order here is the order in which they are printed by 'llcppgx help'.
// Note that subcommands are in general best avoided.
Commands []*Command
}

// Llcppg command
var Llcppg = &Command{
UsageLine: "llcppgx",
Short: `llcppgx aims to be a tool for automatically generating LLGo bindings for C/C++ libraries.`,
// Commands initialized in package main
}

// LongName returns the command's long name: all the words in the usage line between "llcppgx" and a flag or argument,
func (c *Command) LongName() string {
name := c.UsageLine
if i := strings.Index(name, " ["); i >= 0 {
name = name[:i]
}
if name == "llcppgx" {
return ""
}
return strings.TrimPrefix(name, "llcppgx ")
}

// Name returns the command's short name: the last word in the usage line before a flag or argument.
func (c *Command) Name() string {
name := c.LongName()
if i := strings.LastIndex(name, " "); i >= 0 {
name = name[i+1:]
}
return name
}

// Usage show the command usage.
func (c *Command) Usage(w io.Writer) {
fmt.Fprintf(w, "%s\n\nUsage: %s\n", c.Short, c.UsageLine)

// restore output of flag
defer c.Flag.SetOutput(c.Flag.Output())

c.Flag.SetOutput(w)
c.Flag.PrintDefaults()
fmt.Fprintln(w)
}

// Runnable reports whether the command can be run; otherwise
// it is a documentation pseudo-command.
func (c *Command) Runnable() bool {
return c.Run != nil
}

func RunCmdWithName(cmd *Command, args []string, name string, out *io.PipeWriter) {
err := cmd.Flag.Parse(args)
if err != nil {
return
}

cfgFile := config.LLCPPG_CFG

bytesOfConf, err := config.MarshalConfigFile(cfgFile)
Check(err)

if cmd.Flag.NArg() == 0 {
args = append(args, "-")
}

nameCmd := exec.Command(name, args...)
nameCmd.Stdin = bytes.NewReader(bytesOfConf)
nameCmd.Stdout = os.Stdout
if out != nil {
nameCmd.Stdout = out
}
nameCmd.Stderr = os.Stderr
Check(nameCmd.Run())
}

func Check(err error) {
if err != nil {
panic(err)
}
}

// Usage is the usage-reporting function, filled in by package main
// but here for reference by other packages.
//
// flag.Usage func()

// CmdName - "build", "install", "list", "mod tidy", etc.
var CmdName string

// Main runs a command.
func Main(c *Command, app string, args []string) {
name := c.UsageLine
if i := strings.Index(name, " ["); i >= 0 {
c.UsageLine = app + name[i:]
}
c.Run(c, args)
}
17 changes: 17 additions & 0 deletions cmd/internal/gencfg/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package gencfg

import "flag"

var dependencies string
var extsString string
var excludes string
var cpp, help, tab bool

func addFlags(fs *flag.FlagSet) {
fs.BoolVar(&cpp, "cpp", false, "if it is C++ lib")
fs.BoolVar(&help, "help", false, "print help message")
fs.BoolVar(&tab, "tab", true, "generate .cfg config file with tab indent")
fs.StringVar(&excludes, "excludes", "", "exclude all header files in subdir of include example -excludes=\"internal impl\"")
fs.StringVar(&extsString, "exts", ".h", "extra include file extensions for example -exts=\".h .hpp .hh\"")
fs.StringVar(&dependencies, "deps", "", "deps for autofilling dependencies")
}
60 changes: 60 additions & 0 deletions cmd/internal/gencfg/gencfg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package gencfg

import (
"os"
"strings"

"github.com/goplus/llcppg/cmd/internal/base"
"github.com/goplus/llcppg/cmd/llcppcfg/gen"
"github.com/goplus/llcppg/config"
)

var Cmd = &base.Command{
UsageLine: "llcppg init",
Short: "init llcppg.cfg config file",
}

func init() {
Cmd.Run = runCmd
addFlags(&Cmd.Flag)
}

func runCmd(cmd *base.Command, args []string) {

if err := cmd.Flag.Parse(args); err != nil {
return
}

name := ""
if len(cmd.Flag.Args()) > 0 {
name = cmd.Flag.Arg(0)
}

if len(name) == 0 {
return
}

exts := strings.Fields(extsString)
deps := strings.Fields(dependencies)

excludeSubdirs := []string{}
if len(excludes) > 0 {
excludeSubdirs = strings.Fields(excludes)
}
var flagMode gen.FlagMode
if cpp {
flagMode |= gen.WithCpp
}
if tab {
flagMode |= gen.WithTab
}
buf, err := gen.Do(gen.NewConfig(name, flagMode, exts, deps, excludeSubdirs))
if err != nil {
panic(err)
}
outFile := config.LLCPPG_CFG
err = os.WriteFile(outFile, buf, 0600)
if err != nil {
panic(err)
}
}
13 changes: 13 additions & 0 deletions cmd/internal/genpkg/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package genpkg

import (
"flag"
)

var modulePath string
var verbose bool

func addFlags(fs *flag.FlagSet) {
fs.BoolVar(&verbose, "v", false, "enable verbose output")
fs.StringVar(&modulePath, "mod", "", "the module path of the generated code,if not set,will not init a new module")
}
75 changes: 75 additions & 0 deletions cmd/internal/genpkg/genpkg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package genpkg

import (
"bytes"
"fmt"
"io"
"os"
"os/exec"

"github.com/goplus/llcppg/cmd/internal/base"
"github.com/goplus/llcppg/config"
"golang.org/x/mod/module"
)

var Cmd = &base.Command{
UsageLine: "llcppg genpkg",
Short: "generate a go package by signature information of symbols",
}

func init() {
Cmd.Run = runCmd
addFlags(&Cmd.Flag)
}

func runCmd(cmd *base.Command, args []string) {
err := cmd.Flag.Parse(args)
if err != nil {
return
}

err = module.CheckPath(modulePath)
base.Check(err)

cfgFile := config.LLCPPG_CFG

config.HandleMarshalConfigFile(cfgFile, func(b []byte, err error) {

base.Check(err)

r, w := io.Pipe()

errCh := make(chan error, 1)
go func() {
defer w.Close()
llcppsigfetchCmdArgs := make([]string, 0)
if verbose {
llcppsigfetchCmdArgs = append(llcppsigfetchCmdArgs, "-v")
}
if cmd.Flag.NArg() == 0 {
llcppsigfetchCmdArgs = append(llcppsigfetchCmdArgs, "-")
}
llcppsigfetchCmd := exec.Command("llcppsigfetch", llcppsigfetchCmdArgs...)
llcppsigfetchCmd.Stdin = bytes.NewReader(b)
llcppsigfetchCmd.Stdout = w
llcppsigfetchCmd.Stderr = os.Stderr
errCh <- llcppsigfetchCmd.Run()
}()

gogensigCmdArgs := make([]string, 0)
gogensigCmdArgs = append(gogensigCmdArgs, fmt.Sprintf("-mod=%s", modulePath))
if verbose {
gogensigCmdArgs = append(gogensigCmdArgs, "-v")
}
gogensigCmd := exec.Command("gogensig", gogensigCmdArgs...)
gogensigCmd.Stdin = r
gogensigCmd.Stdout = os.Stdout
gogensigCmd.Stderr = os.Stderr
err = gogensigCmd.Run()
base.Check(err)

if fetchErr := <-errCh; fetchErr != nil {
base.Check(fetchErr)
}
})
}
9 changes: 9 additions & 0 deletions cmd/internal/gensig/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package gensig

import "flag"

var verbose bool

func addFlags(fs *flag.FlagSet) {
fs.BoolVar(&verbose, "v", false, "enable verbose output")
}
19 changes: 19 additions & 0 deletions cmd/internal/gensig/gensig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package gensig

import (
"github.com/goplus/llcppg/cmd/internal/base"
)

var Cmd = &base.Command{
UsageLine: "llcppg gensig",
Short: "generate signature information of C/C++ symbols",
}

func init() {
Cmd.Run = runCmd
addFlags(&Cmd.Flag)
}

func runCmd(cmd *base.Command, args []string) {
base.RunCmdWithName(cmd, args, "llcppsigfetch", nil)
}
9 changes: 9 additions & 0 deletions cmd/internal/gensym/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package gensym

import "flag"

var verbose bool

func addFlags(fs *flag.FlagSet) {
fs.BoolVar(&verbose, "v", false, "enable verbose output")
}
19 changes: 19 additions & 0 deletions cmd/internal/gensym/gensym.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package gensym

import (
"github.com/goplus/llcppg/cmd/internal/base"
)

var Cmd = &base.Command{
UsageLine: "llcppg gensym",
Short: "generate symbol table for a C/C++ library",
}

func init() {
Cmd.Run = runCmd
addFlags(&Cmd.Flag)
}

func runCmd(cmd *base.Command, args []string) {
base.RunCmdWithName(cmd, args, "llcppsymg", nil)
}
Loading
Loading