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
89 changes: 88 additions & 1 deletion compiler/internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ import (
"strings"
"unsafe"

"golang.org/x/mod/module"
"golang.org/x/tools/go/ssa"

"github.com/goplus/llgo/compiler/cl"
"github.com/goplus/llgo/compiler/internal/env"
"github.com/goplus/llgo/compiler/internal/installer"
"github.com/goplus/llgo/compiler/internal/installer/githubreleases"
"github.com/goplus/llgo/compiler/internal/mockable"
"github.com/goplus/llgo/compiler/internal/mod"
"github.com/goplus/llgo/compiler/internal/packages"
"github.com/goplus/llgo/compiler/internal/typepatch"
"github.com/goplus/llgo/compiler/ssa/abi"
Expand All @@ -62,6 +66,9 @@ const (
)

const (
defaultLLPkgOwner = "goplus"
defaultLLPkgRepo = "llpkg"

debugBuild = packages.DebugPackagesLoad
)

Expand Down Expand Up @@ -112,6 +119,15 @@ func DefaultAppExt() string {
return ""
}

func defaultInstaller() installer.Installer {
return githubreleases.NewGHReleasesInstaller(map[string]string{
"owner": defaultLLPkgOwner,
"repo": defaultLLPkgRepo,
"platform": runtime.GOOS,
"arch": runtime.GOARCH,
})
}

// -----------------------------------------------------------------------------

const (
Expand Down Expand Up @@ -215,8 +231,10 @@ func Do(args []string, conf *Config) ([]Package, error) {
env := llvm.New("")
os.Setenv("PATH", env.BinDir()+":"+os.Getenv("PATH")) // TODO(xsw): check windows

installer := defaultInstaller()

output := conf.OutFile != ""
ctx := &context{env, cfg, progSSA, prog, dedup, patches, make(map[string]none), initial, mode, 0, output, make(map[*packages.Package]bool), make(map[*packages.Package]bool)}
ctx := &context{env, cfg, progSSA, prog, dedup, patches, make(map[string]none), initial, mode, 0, output, make(map[*packages.Package]bool), make(map[*packages.Package]bool), make(map[module.Version]string), installer}
pkgs, err := buildAllPkgs(ctx, initial, verbose)
check(err)
if mode == ModeGen {
Expand Down Expand Up @@ -281,6 +299,30 @@ type context struct {

needRt map[*packages.Package]bool
needPyInit map[*packages.Package]bool

llpkgMod map[module.Version]string
installer installer.Installer
}

// the pc for the deps of each package should be unique
// consider duplicate clib but different version
// exmaple: A requires zlib/1.3.1, B requires zlib/1.3.0
func setDepsPC(ctx *context, pkg *packages.Package) {
var pcDir []string
ver := module.Version{
Path: pkg.Module.Path,
Version: pkg.Module.Version,
}
if dir := ctx.llpkgMod[ver]; dir != "" {
pcDir = append(pcDir, dir)
}

if len(pcDir) > 0 {
if pkgPath, ok := os.LookupEnv("PKG_CONFIG_PATH"); ok {
pcDir = append(pcDir, pkgPath)
}
os.Setenv("PKG_CONFIG_PATH", strings.Join(pcDir, ":"))
}
}

func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs []*aPackage, err error) {
Expand All @@ -294,6 +336,7 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs
if len(errPkgs) > 0 {
return nil, fmt.Errorf("cannot build SSA for packages")
}

built := ctx.built
for _, aPkg := range pkgs {
pkg := aPkg.Package
Expand All @@ -302,6 +345,11 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs
continue
}
built[pkg.ID] = none{}

if isLLPkg(pkg) {
setDepsPC(ctx, pkg)
}

switch kind, param := cl.PkgKindOf(pkg.Types); kind {
case cl.PkgDeclOnly:
// skip packages that only contain declarations
Expand Down Expand Up @@ -696,6 +744,36 @@ type aPackage struct {

type Package = *aPackage

func isLLPkg(pkg *packages.Package) bool {
return pkg.Module != nil && strings.HasPrefix(pkg.Module.Path, mod.LLPkgPathPrefix)
}

func fetchLLPkg(ctx *context, ver module.Version, verbose bool) string {
llpkg, err := mod.ParseLLPkg(ver)
if err != nil {
return ""
}

dir, err := mod.LLPkgCacheDirByModule(ver)
check(err)

pkgConfigDir := filepath.Join(dir, "lib", "pkgconfig")

_, err = os.Stat(pkgConfigDir)
if os.IsNotExist(err) {
fmt.Printf("installing %s to %s\n", ver.String(), dir)

err = os.MkdirAll(dir, 0777)
check(err)
_, err = ctx.installer.Install(llpkg, dir)
if err != nil {
fmt.Fprintf(os.Stderr, "Warning: installing %s fail: %v\n", ver.String(), err)
}
}

return pkgConfigDir
}

func allPkgs(ctx *context, initial []*packages.Package, verbose bool) (all []*aPackage, errs []*packages.Package) {
prog := ctx.progSSA
built := ctx.built
Expand All @@ -705,6 +783,15 @@ func allPkgs(ctx *context, initial []*packages.Package, verbose bool) (all []*aP
if _, ok := built[pkgPath]; ok || strings.HasPrefix(pkgPath, altPkgPathPrefix) {
return
}
if isLLPkg(p) {
ver := module.Version{
Path: p.Module.Path,
Version: p.Module.Version,
}
if _, ok := ctx.llpkgMod[ver]; !ok {
ctx.llpkgMod[ver] = fetchLLPkg(ctx, ver, verbose)
}
}
var altPkg *packages.Cached
var ssaPkg = createSSAPkg(prog, p, verbose)
if llruntime.HasAltPkg(pkgPath) {
Expand Down
27 changes: 27 additions & 0 deletions compiler/internal/installer/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package installer

// LLPkgConfig represents the configuration structure parsed from llpkg.cfg files.
type LLPkgConfig struct {
Upstream UpstreamConfig `json:"upstream"`
}

// UpstreamConfig defines the upstream configuration containing installer settings and package metadata.
type UpstreamConfig struct {
Installer InstallerConfig `json:"installer"`
Package Package `json:"package"`
}

// InstallerConfig specifies the installer type and its configuration options.
// "name" field must match supported installers (e.g., "conan").
// "config" holds installer-specific parameters (optional).
type InstallerConfig struct {
Name string `json:"name"`
Config map[string]string `json:"config,omitempty"`
}

// Package defines the metadata required to identify and install a software library.
// The Name and Version fields provide precise identification of the library.
type Package struct {
Name string
Version string
}
33 changes: 33 additions & 0 deletions compiler/internal/installer/config/parse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package config

import (
"encoding/json"
"fmt"
"os"

"github.com/goplus/llgo/compiler/internal/installer"
)

// ParseLLPkgConfig reads and parses the llpkg.cfg configuration file
// Performs the following operations:
//
// 1. Opens and reads the configuration file.
// 2. Deserializes JSON content into LLPkgConfig struct.
// 3. Applies default values for missing parameters.
// 4. Returns parsed config or I/O/decoding errors.
func ParseLLPkgConfig(configPath string) (installer.LLPkgConfig, error) {
var config installer.LLPkgConfig
file, err := os.Open(configPath)
if err != nil {
return config, fmt.Errorf("failed to open config file: %w", err)
}
defer file.Close()

decoder := json.NewDecoder(file)
err = decoder.Decode(&config)
if err != nil {
return config, fmt.Errorf("failed to decode config file: %w", err)
}

return config, nil
}
Loading