xtool/cppkg: InstallPkg KnownLatestVersion

This commit is contained in:
xushiwei
2025-05-08 08:10:56 +08:00
parent 2301a4834d
commit 0f79cad5a7
3 changed files with 82 additions and 33 deletions

View File

@@ -18,6 +18,7 @@ package cppkg
import ( import (
"crypto/md5" "crypto/md5"
"encoding/base64"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
@@ -30,6 +31,7 @@ import (
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/goplus/llgo/internal/github" "github.com/goplus/llgo/internal/github"
"github.com/qiniu/x/byteutil"
"github.com/qiniu/x/httputil" "github.com/qiniu/x/httputil"
) )
@@ -98,9 +100,9 @@ func getRelease(pkg *Package, tagPattern string) (ret *githubRelease, err error)
} }
// Install installs the specified package using Conan. // Install installs the specified package using Conan.
func (p *Manager) Install(pkg *Package, flags int) (err error) { func (p *Manager) Install(pkg *Package, options []string, flags int) (buildDir string, err error) {
outDir := p.outDir(pkg) buildDir = p.BuildDir(pkg, options)
os.MkdirAll(outDir, os.ModePerm) os.MkdirAll(buildDir, os.ModePerm)
var rev string var rev string
var gr *githubRelease var gr *githubRelease
@@ -115,17 +117,17 @@ func (p *Manager) Install(pkg *Package, flags int) (err error) {
return return
} }
err = copyDirR(conanfileDir, outDir) err = copyDirR(conanfileDir, buildDir)
if err != nil { if err != nil {
return return
} }
conanfilePy, err = os.ReadFile(outDir + "/conanfile.py") conanfilePy, err = os.ReadFile(buildDir + "/conanfile.py")
if err != nil { if err != nil {
return return
} }
conandataFile := outDir + "/conandata.yml" conandataFile := buildDir + "/conandata.yml"
conandataYml, err = os.ReadFile(conandataFile) conandataYml, err = os.ReadFile(conandataFile)
if err != nil { if err != nil {
return return
@@ -138,7 +140,7 @@ func (p *Manager) Install(pkg *Package, flags int) (err error) {
fromVer := template.FromVer fromVer := template.FromVer
source, ok := cd.Sources[fromVer] source, ok := cd.Sources[fromVer]
if !ok { if !ok {
return ErrVersionNotFound return "", ErrVersionNotFound
} }
cd.Sources = map[string]any{ cd.Sources = map[string]any{
pkgVer: replaceVer(source, fromVer, pkgVer), pkgVer: replaceVer(source, fromVer, pkgVer),
@@ -152,10 +154,10 @@ func (p *Manager) Install(pkg *Package, flags int) (err error) {
return return
} }
rev = recipeRevision(pkg, gr, conandataYml) rev = recipeRevision(pkg, gr, conandataYml)
conanfileDir = outDir conanfileDir = buildDir
} }
outFile := outDir + "/out.json" outFile := buildDir + "/out.json"
out, err := os.Create(outFile) out, err := os.Create(outFile)
if err == nil { if err == nil {
defer out.Close() defer out.Close()
@@ -165,15 +167,16 @@ func (p *Manager) Install(pkg *Package, flags int) (err error) {
nameAndVer := pkg.Name + "/" + pkgVer nameAndVer := pkg.Name + "/" + pkgVer
if template == nil { if template == nil {
return conanInstall(nameAndVer, outDir, conanfileDir, out, flags) err = conanInstall(nameAndVer, buildDir, conanfileDir, out, options, flags)
return
} }
logFile := "" logFile := ""
if flags&LogRevertProxy != 0 { if flags&LogRevertProxy != 0 {
logFile = outDir + "/rp.log" logFile = buildDir + "/rp.log"
} }
return remoteProxy(flags, logFile, func() error { err = remoteProxy(flags, logFile, func() error {
return conanInstall(nameAndVer, outDir, conanfileDir, out, flags) return conanInstall(nameAndVer, buildDir, conanfileDir, out, options, flags)
}, func(mux *http.ServeMux) { }, func(mux *http.ServeMux) {
base := "/v2/conans/" + nameAndVer base := "/v2/conans/" + nameAndVer
revbase := base + "/_/_/revisions/" + rev revbase := base + "/_/_/revisions/" + rev
@@ -217,7 +220,7 @@ func (p *Manager) Install(pkg *Package, flags int) (err error) {
httputil.ReplyWithStream(w, http.StatusOK, "text/plain", strings.NewReader(data), int64(len(data))) httputil.ReplyWithStream(w, http.StatusOK, "text/plain", strings.NewReader(data), int64(len(data)))
}) })
mux.HandleFunc(revbase+"/files/conan_export.tgz", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc(revbase+"/files/conan_export.tgz", func(w http.ResponseWriter, r *http.Request) {
conanExportTgz, err := tgzOfConandata(outDir) conanExportTgz, err := tgzOfConandata(buildDir)
if err != nil { if err != nil {
replyError(w, err) replyError(w, err)
return return
@@ -228,19 +231,31 @@ func (p *Manager) Install(pkg *Package, flags int) (err error) {
httputil.ReplyWith(w, http.StatusOK, "application/x-gzip", conanExportTgz) httputil.ReplyWith(w, http.StatusOK, "application/x-gzip", conanExportTgz)
}) })
}) })
return
} }
func (p *Manager) outDir(pkg *Package) string { func (p *Manager) BuildDir(pkg *Package, options []string) string {
return p.cacheDir + "/build/" + pkg.Name + "@" + pkg.Version dir := p.cacheDir + "/build/" + pkg.Name + "@" + pkg.Version
if options != nil {
h := md5.New()
for _, opt := range options {
h.Write(byteutil.Bytes(opt))
}
hash := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
dir += "/" + hash
} else {
dir += "/static"
}
return dir
} }
func (p *Manager) conanfileDir(pkgPath, pkgFolder string) string { func (p *Manager) conanfileDir(pkgPath, pkgFolder string) string {
root := p.indexRoot() root := p.IndexRoot()
return root + "/" + pkgPath + "/" + pkgFolder return root + "/" + pkgPath + "/" + pkgFolder
} }
func conanInstall(pkg, outDir, conanfileDir string, out io.Writer, flags int) (err error) { func conanInstall(pkg, outDir, conanfileDir string, out io.Writer, options []string, flags int) (err error) {
args := make([]string, 0, 12) args := make([]string, 0, 16)
args = append(args, "install", args = append(args, "install",
"--requires", pkg, "--requires", pkg,
"--generator", "PkgConfigDeps", "--generator", "PkgConfigDeps",
@@ -248,6 +263,9 @@ func conanInstall(pkg, outDir, conanfileDir string, out io.Writer, flags int) (e
"--format", "json", "--format", "json",
"--output-folder", outDir, "--output-folder", outDir,
) )
for _, opt := range options {
args = append(args, "--options", opt)
}
quietInstall := flags&ToolQuietInstall != 0 quietInstall := flags&ToolQuietInstall != 0
cmd, err := conanCmd.New(quietInstall, args...) cmd, err := conanCmd.New(quietInstall, args...)
if err != nil { if err != nil {

View File

@@ -29,15 +29,21 @@ const (
// pkgAndVer: 7bitcoder/7bitconf@1.2.0 // pkgAndVer: 7bitcoder/7bitconf@1.2.0
func Install(pkgAndVer string, flags int) { func Install(pkgAndVer string, flags int) {
pkgPath, ver := parsePkgVer(pkgAndVer) pkgPath, ver := parsePkgVer(pkgAndVer)
_, _, err := InstallPkg(pkgPath, ver, nil, flags)
check(err)
}
// InstallPkg installs a package with the given package path and version.
func InstallPkg(pkgPath, ver string, options []string, flags int) (pkg *Package, buildDir string, err error) {
m, err := New("") m, err := New("")
check(err) if err != nil {
return
pkg, err := m.Lookup(pkgPath, ver, flags) }
check(err) pkg, err = m.Lookup(pkgPath, ver, flags)
if err == nil {
err = m.Install(pkg, flags) buildDir, err = m.Install(pkg, options, flags)
check(err) }
return
} }
func parsePkgVer(pkg string) (string, string) { func parsePkgVer(pkg string) (string, string) {

View File

@@ -36,13 +36,10 @@ type Manager struct {
cacheDir string cacheDir string
} }
// New creates a new package manager.
func New(cacheDir string) (ret *Manager, err error) { func New(cacheDir string) (ret *Manager, err error) {
if cacheDir == "" { if cacheDir == "" {
cacheDir, err = os.UserCacheDir() cacheDir = CacheDir()
if err != nil {
return
}
cacheDir += "/cppkg"
} }
os.MkdirAll(cacheDir, os.ModePerm) os.MkdirAll(cacheDir, os.ModePerm)
ret = &Manager{ ret = &Manager{
@@ -51,6 +48,15 @@ func New(cacheDir string) (ret *Manager, err error) {
return return
} }
// CacheDir returns the cache directory to manage C/C++ packages.
func CacheDir() string {
cacheDir, err := os.UserCacheDir()
if err != nil {
panic(err)
}
return cacheDir + "/cppkg"
}
type version struct { type version struct {
Folder string `yaml:"folder"` Folder string `yaml:"folder"`
} }
@@ -68,6 +74,17 @@ type config struct {
Template Template `yaml:"template"` Template Template `yaml:"template"`
} }
// getKnownLatestVer returns the latest known version and its details.
// It returns empty version if no known version is found.
func (p *config) getKnownLatestVer() (ver string, v version) {
for ver1, v1 := range p.Versions {
if ver == "" || compareVer(ver1, ver) > 0 {
ver, v = ver1, v1
}
}
return
}
// Package represents a C/C++ package. // Package represents a C/C++ package.
type Package struct { type Package struct {
Name string Name string
@@ -96,11 +113,14 @@ const (
// LogRevertProxy is a flag to log revert proxy. // LogRevertProxy is a flag to log revert proxy.
LogRevertProxy LogRevertProxy
// KnownLatestVersion is a flag to use the known latest version.
KnownLatestVersion
) )
// Lookup looks up a package by its path and version. // Lookup looks up a package by its path and version.
func (p *Manager) Lookup(pkgPath, ver string, flags int) (_ *Package, err error) { func (p *Manager) Lookup(pkgPath, ver string, flags int) (_ *Package, err error) {
root := p.indexRoot() root := p.IndexRoot()
err = indexUpate(root, flags) err = indexUpate(root, flags)
if err != nil { if err != nil {
return return
@@ -118,6 +138,11 @@ func (p *Manager) Lookup(pkgPath, ver string, flags int) (_ *Package, err error)
} }
if ver == "" || ver == "latest" { if ver == "" || ver == "latest" {
if flags&KnownLatestVersion != 0 {
if ver, v := conf.getKnownLatestVer(); ver != "" {
return &Package{conf.PkgName, pkgPath, ver, v.Folder, nil, nil}, nil
}
}
if conf.Template.Tag == "" { if conf.Template.Tag == "" {
return nil, ErrDynamicTag return nil, ErrDynamicTag
} }
@@ -144,7 +169,7 @@ func (p *Manager) Lookup(pkgPath, ver string, flags int) (_ *Package, err error)
return &Package{conf.PkgName, pkgPath, ver, templ.Folder, &templ, nil}, nil return &Package{conf.PkgName, pkgPath, ver, templ.Folder, &templ, nil}, nil
} }
func (p *Manager) indexRoot() string { func (p *Manager) IndexRoot() string {
return p.cacheDir + "/index" return p.cacheDir + "/index"
} }