From 1693942cee8fbc55caef6a1313f683b6e9c0b220 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 12 May 2025 03:50:07 +0800 Subject: [PATCH] rm xtool/{cppkg,cpgithubpkg} --- cmd/llgo/cppkg_cmd.gox | 20 -- cmd/llgo/cppkg_install_cmd.gox | 38 ---- cmd/llgo/gop_autogen.go | 67 +------ go.mod | 7 +- go.sum | 2 - internal/github/commit.go | 76 -------- internal/github/release.go | 75 ------- internal/github/tag.go | 121 ------------ xtool/cpgithubpkg/cpgithubpkg.go | 227 ---------------------- xtool/cppkg/command.go | 88 --------- xtool/cppkg/conan.go | 323 ------------------------------- xtool/cppkg/cppkg.go | 61 ------ xtool/cppkg/manager.go | 221 --------------------- xtool/cppkg/revertproxy.go | 176 ----------------- 14 files changed, 10 insertions(+), 1492 deletions(-) delete mode 100644 cmd/llgo/cppkg_cmd.gox delete mode 100644 cmd/llgo/cppkg_install_cmd.gox delete mode 100644 internal/github/commit.go delete mode 100644 internal/github/release.go delete mode 100644 internal/github/tag.go delete mode 100644 xtool/cpgithubpkg/cpgithubpkg.go delete mode 100644 xtool/cppkg/command.go delete mode 100644 xtool/cppkg/conan.go delete mode 100644 xtool/cppkg/cppkg.go delete mode 100644 xtool/cppkg/manager.go delete mode 100644 xtool/cppkg/revertproxy.go diff --git a/cmd/llgo/cppkg_cmd.gox b/cmd/llgo/cppkg_cmd.gox deleted file mode 100644 index 3d660f6e..00000000 --- a/cmd/llgo/cppkg_cmd.gox +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2025 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. - */ - -short "Manage C/C++ packages" - -run => { - help -} diff --git a/cmd/llgo/cppkg_install_cmd.gox b/cmd/llgo/cppkg_install_cmd.gox deleted file mode 100644 index 29c8d880..00000000 --- a/cmd/llgo/cppkg_install_cmd.gox +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2025 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. - */ - -import ( - self "github.com/goplus/llgo/xtool/cppkg" -) - -use "install [flags] [packages]" - -short "Install a C/C++ package from github.com/goplus/cppkg" - -long `Installs a C/C++ package with the given name and version. For example: - -llgo cppkg install davegamble/cjson@1.7.18 -llgo cppkg install davegamble/cjson@latest -llgo cppkg install davegamble/cjson -` - -run args => { - if args.len < 1 { - help - return - } - - self.install args[0], self.DefaultFlags -} diff --git a/cmd/llgo/gop_autogen.go b/cmd/llgo/gop_autogen.go index d0ad9246..7c31549b 100644 --- a/cmd/llgo/gop_autogen.go +++ b/cmd/llgo/gop_autogen.go @@ -11,7 +11,6 @@ import ( "github.com/goplus/llgo/cmd/internal/run" "github.com/goplus/llgo/cmd/internal/test" "github.com/goplus/llgo/internal/env" - "github.com/goplus/llgo/xtool/cppkg" "github.com/qiniu/x/stringutil" "runtime" ) @@ -30,14 +29,6 @@ type Cmd_cmptest struct { xcmd.Command *App } -type Cmd_cppkg struct { - xcmd.Command - *App -} -type Cmd_cppkg_install struct { - xcmd.Command - *App -} type Cmd_get struct { xcmd.Command *App @@ -70,14 +61,12 @@ func (this *App) Main() { _gop_obj0 := &Cmd_build{App: this} _gop_obj1 := &Cmd_clean{App: this} _gop_obj2 := &Cmd_cmptest{App: this} - _gop_obj3 := &Cmd_cppkg{App: this} - _gop_obj4 := &Cmd_cppkg_install{App: this} - _gop_obj5 := &Cmd_get{App: this} - _gop_obj6 := &Cmd_install{App: this} - _gop_obj7 := &Cmd_run{App: this} - _gop_obj8 := &Cmd_test{App: this} - _gop_obj9 := &Cmd_version{App: this} - xcmd.Gopt_App_Main(this, _gop_obj0, _gop_obj1, _gop_obj2, _gop_obj3, _gop_obj4, _gop_obj5, _gop_obj6, _gop_obj7, _gop_obj8, _gop_obj9) + _gop_obj3 := &Cmd_get{App: this} + _gop_obj4 := &Cmd_install{App: this} + _gop_obj5 := &Cmd_run{App: this} + _gop_obj6 := &Cmd_test{App: this} + _gop_obj7 := &Cmd_version{App: this} + xcmd.Gopt_App_Main(this, _gop_obj0, _gop_obj1, _gop_obj2, _gop_obj3, _gop_obj4, _gop_obj5, _gop_obj6, _gop_obj7) } //line cmd/llgo/build_cmd.gox:20 func (this *Cmd_build) Main(_gop_arg0 string) { @@ -133,50 +122,6 @@ func (this *Cmd_cmptest) Main(_gop_arg0 string) { func (this *Cmd_cmptest) Classfname() string { return "cmptest" } -//line cmd/llgo/cppkg_cmd.gox:16 -func (this *Cmd_cppkg) Main(_gop_arg0 string) { - this.Command.Main(_gop_arg0) -//line cmd/llgo/cppkg_cmd.gox:16:1 - this.Short("Manage C/C++ packages") -//line cmd/llgo/cppkg_cmd.gox:18:1 - this.Run__0(func() { -//line cmd/llgo/cppkg_cmd.gox:19:1 - this.Help() - }) -} -func (this *Cmd_cppkg) Classfname() string { - return "cppkg" -} -//line cmd/llgo/cppkg_install_cmd.gox:20 -func (this *Cmd_cppkg_install) Main(_gop_arg0 string) { - this.Command.Main(_gop_arg0) -//line cmd/llgo/cppkg_install_cmd.gox:20:1 - this.Use("install [flags] [packages]") -//line cmd/llgo/cppkg_install_cmd.gox:22:1 - this.Short("Install a C/C++ package from github.com/goplus/cppkg") -//line cmd/llgo/cppkg_install_cmd.gox:24:1 - this.Long(`Installs a C/C++ package with the given name and version. For example: - -llgo cppkg install davegamble/cjson@1.7.18 -llgo cppkg install davegamble/cjson@latest -llgo cppkg install davegamble/cjson -`) -//line cmd/llgo/cppkg_install_cmd.gox:31:1 - this.Run__1(func(args []string) { -//line cmd/llgo/cppkg_install_cmd.gox:32:1 - if len(args) < 1 { -//line cmd/llgo/cppkg_install_cmd.gox:33:1 - this.Help() -//line cmd/llgo/cppkg_install_cmd.gox:34:1 - return - } -//line cmd/llgo/cppkg_install_cmd.gox:37:1 - cppkg.Install(args[0], cppkg.DefaultFlags) - }) -} -func (this *Cmd_cppkg_install) Classfname() string { - return "cppkg_install" -} //line cmd/llgo/get_cmd.gox:16 func (this *Cmd_get) Main(_gop_arg0 string) { this.Command.Main(_gop_arg0) diff --git a/go.mod b/go.mod index 5491dfe9..af34ed05 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.23 toolchain go1.24.1 require ( - github.com/goccy/go-yaml v1.17.1 github.com/goplus/cobra v1.9.11 //gop:class github.com/goplus/gogen v1.18.0 github.com/goplus/lib v0.2.0 @@ -13,10 +12,12 @@ require ( github.com/goplus/llvm v0.8.3 github.com/goplus/mod v0.16.1 github.com/qiniu/x v1.14.0 - golang.org/x/mod v0.23.0 golang.org/x/tools v0.30.0 ) -require golang.org/x/sync v0.11.0 // indirect +require ( + golang.org/x/mod v0.23.0 // indirect + golang.org/x/sync v0.11.0 // indirect +) replace github.com/goplus/llgo/runtime => ./runtime diff --git a/go.sum b/go.sum index dca05e62..baecf4f2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY= -github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/goplus/cobra v1.9.11 h1:ZimeEur2s/p+fSpgmby4kARH48YUdBOfYEWlDqMu+8s= diff --git a/internal/github/commit.go b/internal/github/commit.go deleted file mode 100644 index 3aa3a9cd..00000000 --- a/internal/github/commit.go +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2025 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 github - -import ( - "encoding/json" - "net/http" - "strings" -) - -// Author represents a github user or bot. -type Author struct { - Login string `json:"login"` // github-actions[bot] - ID int `json:"id"` // 41898282 - NodeID string `json:"node_id"` // MDM6Qm90NDE4OTgyODI= - AvatarURL string `json:"avatar_url"` // https://avatars.githubusercontent.com/in/15368?v=4 - URL string `json:"url"` // https://api.github.com/users/github-actions%5Bbot%5D - HtmlURL string `json:"html_url"` // https://github.com/apps/github-actions - Type string `json:"type"` // Bot - SiteAdmin bool `json:"site_admin"` // false -} - -// CommitAuthor represents the author of a GitHub commit. -type CommitAuthor struct { - Name string `json:"name"` // xushiwei - Email string `json:"email"` // x@goplus.org - Date string `json:"date"` // 2025-04-21T14:13:29Z -} - -// CommitSummary represents the summary of a GitHub commit. -type CommitSummary struct { - Author CommitAuthor `json:"author"` - Message string `json:"message"` // Merge pull request #2296 from goplus/main\n\nv1.4.0 -} - -// CommitDetail represents the details of a GitHub commit. -type CommitDetail struct { - NodeID string `json:"node_id"` // C_kwDOAtpGOtoAKDE2OGEwODlmOWY5ZTNhNDdhMTliMTRjZDczODQ4N2M2ZTJkMTMxYmE - Commit CommitSummary `json:"commit"` - Author Author `json:"author"` -} - -func commitURL(pkgPath, sha string) string { - return "https://api.github.com/repos/" + pkgPath + "/commits/" + sha -} - -// GetCommit retrieves the details of a specific commit from a GitHub repository. -func GetCommit(pkgPath, shaOrURL string) (ret *CommitDetail, err error) { - url := shaOrURL - if !strings.HasPrefix(shaOrURL, "https://") { - url = commitURL(pkgPath, shaOrURL) - } - resp, err := http.Get(url) - if err != nil { - return - } - defer resp.Body.Close() - - ret = new(CommitDetail) - err = json.NewDecoder(resp.Body).Decode(ret) - return -} diff --git a/internal/github/release.go b/internal/github/release.go deleted file mode 100644 index 696b82ba..00000000 --- a/internal/github/release.go +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2025 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 github - -import ( - "encoding/json" - "net/http" -) - -// ReleaseAsset represents a GitHub release asset. -type ReleaseAsset struct { - URL string `json:"url"` // https://api.github.com/repos/flintlib/flint/releases/assets/242245930 - ID int64 `json:"id"` // 242245930 - NodeID string `json:"node_id"` // RA_kwDOAC8YHs4OcGEq - Name string `json:"name"` // flint-3.2.2.tar.gz - ContentType string `json:"content_type"` // application/x-gtar - State string `json:"state"` // uploaded - Size int64 `json:"size"` // 123456 - DownloadCount int `json:"download_count"` // 176 - UpdatedAt string `json:"updated_at"` // 2025-03-31T08:54:16Z - BrowserDownloadURL string `json:"browser_download_url"` // https://github.com/flintlib/flint/releases/download/v3.2.2/flint-3.2.2.tar.gz -} - -// Release represents a GitHub release. -type Release struct { - URL string `json:"url"` // https://api.github.com/repos/flintlib/flint/releases/209285187 - ID int64 `json:"id"` // 209285187 - NodeID string `json:"node_id"` // RE_kwDOAC8YHs4MeXBD - TagName string `json:"tag_name"` // v3.2.2 - TargetCommitish string `json:"target_commitish"` // b8223680e38ad048355a421bf7f617bb6c5d5e12 - Name string `json:"name"` // FLINT v3.2.2 - PublishedAt string `json:"published_at"` // 2025-03-31T08:54:16Z - Body string `json:"body"` // Release Notes - TarballURL string `json:"tarball_url"` // https://api.github.com/repos/flintlib/flint/tarball/v3.2.2 - ZipballURL string `json:"zipball_url"` // https://api.github.com/repos/flintlib/flint/zipball/v3.2.2 - Author Author `json:"author"` - Assets []*ReleaseAsset `json:"assets"` - Prerelease bool `json:"prerelease"` -} - -// releaseURL constructs the URL for a GitHub release. -func releaseURL(pkgPath, ver string) string { - if ver == "" || ver == "latest" { - return "https://api.github.com/repos/" + pkgPath + "/releases/latest" - } - return "https://api.github.com/repos/" + pkgPath + "/releases/tags/" + ver -} - -// GetRelease fetches the release information from GitHub. -func GetRelease(pkgPath, ver string) (ret *Release, err error) { - url := releaseURL(pkgPath, ver) - resp, err := http.Get(url) - if err != nil { - return - } - defer resp.Body.Close() - - ret = new(Release) - err = json.NewDecoder(resp.Body).Decode(ret) - return -} diff --git a/internal/github/tag.go b/internal/github/tag.go deleted file mode 100644 index 04d464c0..00000000 --- a/internal/github/tag.go +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2025 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 github - -import ( - "encoding/json" - "errors" - "net/http" - "net/url" - "strconv" - "strings" -) - -var ( - ErrBreak = errors.New("break") - ErrNotFound = errors.New("not found") -) - -// Commit represents a commit in a GitHub repository. -type Commit struct { - SHA string `json:"sha"` - URL string `json:"url"` -} - -// Tag represents a GitHub tag. -type Tag struct { - Name string `json:"name"` - ZipballURL string `json:"zipball_url"` - TarballURL string `json:"tarball_url"` - Commit Commit `json:"commit"` - NodeID string `json:"node_id"` -} - -// tagsURL constructs the URL for fetching tags from a GitHub repository. -func tagsURL(pkgPath string) string { - return "https://api.github.com/repos/" + pkgPath + "/tags" -} - -// GetTag retrieves a specific tag from a GitHub repository. -func GetTag(pkgPath, ver string) (tag *Tag, err error) { - err = ErrNotFound - EnumTags(pkgPath, 0, func(tags []*Tag, page, total int) error { - for _, t := range tags { - if t.Name == ver { - tag = t - err = nil - return ErrBreak - } - } - return nil - }) - return -} - -// EnumTags enumerates the tags of a GitHub repository. -func EnumTags(pkgPath string, page int, pager func(tags []*Tag, page, total int) error) (err error) { - total := 0 - ubase := tagsURL(pkgPath) - -loop: - u := ubase + "?per_page=100&page=" + strconv.Itoa(page+1) - resp, err := http.Get(u) - if err != nil { - return - } - defer resp.Body.Close() - - var tags []*Tag - err = json.NewDecoder(resp.Body).Decode(&tags) - if err != nil { - return - } - - // Link: ; rel="next", - // ; rel="last" - if total == 0 { - const relLast = `rel="last"` - total = page + 1 - link := resp.Header.Get("Link") - for _, part := range strings.Split(link, ",") { - if strings.HasSuffix(part, relLast) { - left := strings.TrimSpace(part[:len(part)-len(relLast)]) - lastUrl := strings.TrimSuffix(strings.TrimPrefix(left, "<"), ">;") - if pos := strings.LastIndexByte(lastUrl, '?'); pos >= 0 { - if vals, e := url.ParseQuery(lastUrl[pos+1:]); e == nil { - if n, e := strconv.Atoi(vals.Get("page")); e == nil { - total = n - } - } - } - break - } - } - } - err = pager(tags, page, total) - if err != nil { - if err == ErrBreak { - err = nil - } - return - } - page++ - if page < total { - goto loop - } - return -} diff --git a/xtool/cpgithubpkg/cpgithubpkg.go b/xtool/cpgithubpkg/cpgithubpkg.go deleted file mode 100644 index b10b8b30..00000000 --- a/xtool/cpgithubpkg/cpgithubpkg.go +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (c) 2025 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 cpgithubpkg - -import ( - "log" - "os" - "os/exec" - "slices" - "strings" - - "github.com/goccy/go-yaml" - "golang.org/x/mod/semver" -) - -type version struct { - Folder string `yaml:"folder"` -} - -type config struct { - Versions map[string]version `yaml:"versions"` -} - -type template struct { - FromVer string `yaml:"from"` - Folder string `yaml:"folder"` - Tag string `yaml:"tag,omitempty"` // pattern with *, empty if dynamic tag -} - -type configEx struct { - PkgName string `yaml:"name"` - Versions map[string]version `yaml:"versions"` - Template template `yaml:"template"` -} - -// Main is the entry point for copying GitHub packages. -func Main(pkgName string) { - localDir := conanRoot() + "recipes/" + pkgName + "/" - - confFile := localDir + "config.yml" - b, err := os.ReadFile(confFile) - check(err) - - var conf config - err = yaml.Unmarshal(b, &conf) - check(err) - - tryCp := func(src map[string]any, ver string, v version) { - switch url := src["url"].(type) { - case string: - if pkgPath, tagPattern, ok := checkGithbPkg(url, ver); ok { - cpGithubPkg(pkgName, pkgPath, tagPattern, localDir, conf, ver, v) - } - case []any: - for _, u := range url { - url := u.(string) - if pkgPath, tagPattern, ok := checkGithbPkg(url, ver); ok { - cpGithubPkg(pkgName, pkgPath, tagPattern, localDir, conf, ver, v) - } - } - default: - log.Println("[INFO] skip source:", src) - } - } - - conandatas := make(map[string]conandata) // folder -> conandata - rangeVerDesc(conf.Versions, func(ver string, v version) { - cd, err := getConanData(conandatas, v.Folder, localDir) - if err != nil { - if os.IsNotExist(err) { - return - } - check(err) - } - - if src, ok := cd.Sources[ver]; ok { - switch src := src.(type) { - case map[string]any: - tryCp(src, ver, v) - case []any: - for _, u := range src { - tryCp(u.(map[string]any), ver, v) - } - default: - log.Panicln("[FATAL] source:", src) - } - } - }) -} - -func cpGithubPkg(pkgName, pkgPath, tagPattern, srcDir string, conf config, fromVer string, v version) { - destDir := cppkgRoot() + pkgPath - os.MkdirAll(destDir, os.ModePerm) - - err := exec.Command("cp", "-r", srcDir, destDir).Run() - check(err) - - confex := &configEx{ - PkgName: pkgName, - Versions: conf.Versions, - Template: template{ - FromVer: fromVer, - Folder: v.Folder, - Tag: tagPattern, - }, - } - b, err := yaml.Marshal(confex) - check(err) - - err = os.WriteFile(destDir+"/config.yml", b, os.ModePerm) - check(err) - - log.Println("[INFO] copy", pkgPath) - os.Exit(0) -} - -func checkGithbPkg(url, ver string) (pkgPath, tagPattern string, ok bool) { - const githubPrefix = "https://github.com/" - if strings.HasPrefix(url, githubPrefix) { - path := url[len(githubPrefix):] - parts := strings.SplitN(path, "/", 3) - if len(parts) == 3 { // user/repo/xxx - if pos := strings.Index(parts[2], ver); pos >= 0 { - userRepo := parts[0] + "/" + parts[1] - at := len(githubPrefix) + len(userRepo) + 1 + pos - tagPattern = tagPatternOf(url, ver, at) - pkgPath, ok = strings.ToLower(userRepo), true - } - } - } - return -} - -func tagPatternOf(url, ver string, at int) (tagPattern string) { - var tag string - if pos := strings.LastIndexByte(url[:at], '/'); pos >= 0 { - last := at + len(ver) - left := url[last:] - if end, ok := checkTagEnd(left); ok { - pos++ - tag = url[pos:last] - tagPattern = url[pos:at] + end - } - } - if tag == "" { - log.Println("[INFO] dynamic tag found:", url) - } - return -} - -func checkTagEnd(left string) (end string, ok bool) { - if n := len(left); n > 0 { - if left[0] == '/' { - return "*", true - } - if n >= 4 && left[0] == '.' { - ext := left[1:4] - return "*", ext == "tar" || ext == "zip" || ext == "tgz" - } - if strings.HasPrefix(left, "-stable.") { - return "*-stable", true - } - } - return "", false -} - -type conandata struct { - Sources map[string]any `yaml:"sources"` -} - -func getConanData(conandatas map[string]conandata, folder, localDir string) (ret conandata, err error) { - if v, ok := conandatas[folder]; ok { - return v, nil - } - file := localDir + folder + "/conandata.yml" - b, err := os.ReadFile(file) - if err != nil { - return - } - if err = yaml.Unmarshal(b, &ret); err != nil { - return - } - conandatas[folder] = ret - return -} - -func rangeVerDesc[V any](data map[string]V, f func(string, V)) { - keys := make([]string, 0, len(data)) - for k := range data { - keys = append(keys, "v"+k) - } - semver.Sort(keys) - for _, k := range slices.Backward(keys) { - k = k[1:] // remove 'v' - f(k, data[k]) - } -} - -func conanRoot() string { - home, _ := os.UserHomeDir() - return home + "/conan-center-index/" -} - -func cppkgRoot() string { - home, _ := os.UserHomeDir() - return home + "/cppkg/" -} - -func check(err error) { - if err != nil { - panic(err) - } -} diff --git a/xtool/cppkg/command.go b/xtool/cppkg/command.go deleted file mode 100644 index a72a986a..00000000 --- a/xtool/cppkg/command.go +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2025 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 cppkg - -import ( - "os" - "os/exec" - "strings" -) - -var ( - // ErrNotFound is the error resulting if a path search failed to find - // an executable file. - ErrNotFound = exec.ErrNotFound -) - -// Tool represents a tool that can be executed. -type Tool struct { - cmd string - installs [][]string -} - -// NewTool creates a new Tool instance with the specified tool and install commands. -func NewTool(cmd string, installs []string) *Tool { - inst := make([][]string, len(installs)) - for i, install := range installs { - inst[i] = strings.Split(install, " ") - } - return &Tool{ - cmd: cmd, - installs: inst, - } -} - -// New creates a new command with the specified arguments. -func (p *Tool) New(quietInstall bool, args ...string) (cmd *exec.Cmd, err error) { - app, err := p.Get(quietInstall) - if err != nil { - return - } - return exec.Command(app, args...), nil -} - -// Get retrieves the path of the command. -// If the command is not found, it attempts to install it using the specified -// install commands. -func (p *Tool) Get(quietInstall bool) (app string, err error) { - app, err = exec.LookPath(p.cmd) - if err == nil { - return - } - amPath, install, err := p.getAppManager() - if err != nil { - return - } - c := exec.Command(amPath, install[1:]...) - c.Stdout = os.Stdout - c.Stderr = os.Stderr - if err = c.Run(); err != nil { - return - } - return exec.LookPath(p.cmd) -} - -func (p *Tool) getAppManager() (amPath string, install []string, err error) { - for _, install = range p.installs { - am := install[0] - if amPath, err = exec.LookPath(am); err == nil { - return - } - } - err = ErrNotFound - return -} diff --git a/xtool/cppkg/conan.go b/xtool/cppkg/conan.go deleted file mode 100644 index 55c9a137..00000000 --- a/xtool/cppkg/conan.go +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (c) 2025 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 cppkg - -import ( - "crypto/md5" - "encoding/base64" - "encoding/hex" - "errors" - "fmt" - "io" - "net/http" - "os" - "os/exec" - "strings" - "time" - - "github.com/goccy/go-yaml" - "github.com/goplus/llgo/internal/github" - "github.com/qiniu/x/byteutil" - "github.com/qiniu/x/httputil" -) - -var conanCmd = NewTool("conan", []string{ - "brew install conan", - "pipx install conan", -}) - -type conandata struct { - Sources map[string]any `yaml:"sources"` -} - -func replaceVer(src any, fromVer, toVer string) any { - switch src := src.(type) { - case map[string]any: - doReplace(src, fromVer, toVer) - case []any: - for _, u := range src { - doReplace(u.(map[string]any), fromVer, toVer) - } - } - return src -} - -func doReplace(src map[string]any, fromVer, toVer string) { - switch url := src["url"].(type) { - case string: - src["url"] = strings.ReplaceAll(url, fromVer, toVer) - delete(src, "sha256") - // TODO(xsw): src["sha256"] = hash - case []any: - for i, u := range url { - url[i] = strings.ReplaceAll(u.(string), fromVer, toVer) - } - delete(src, "sha256") - // TODO(xsw): src["sha256"] = hash - } -} - -type githubRelease struct { - PublishedAt string -} - -func getRelease(pkg *Package, tagPattern string) (ret *githubRelease, err error) { - if tagPattern == "" { - return nil, ErrDynamicTag - } - if pkg.gr != nil { - return &githubRelease{PublishedAt: pkg.gr.PublishedAt}, nil - } - ver := strings.Replace(tagPattern, "*", pkg.Version, 1) - gr, err := github.GetRelease(pkg.Path, ver) - if err == nil { - ret = &githubRelease{PublishedAt: gr.PublishedAt} - return - } - t, err := github.GetTag(pkg.Path, ver) - if err != nil { - return - } - c, err := github.GetCommit(pkg.Path, t.Commit.URL) - if err == nil { - ret = &githubRelease{PublishedAt: c.Commit.Author.Date} - } - return -} - -// Install installs the specified package using Conan. -func (p *Manager) Install(pkg *Package, options []string, flags int) (buildDir string, err error) { - buildDir = p.BuildDir(pkg, options) - os.MkdirAll(buildDir, os.ModePerm) - - var rev string - var gr *githubRelease - var conandataYml, conanfilePy []byte - - conanfileDir := p.conanfileDir(pkg.Path, pkg.Folder) - pkgVer := pkg.Version - template := pkg.Template - if template != nil { - gr, err = getRelease(pkg, template.Tag) - if err != nil { - return - } - - err = copyDirR(conanfileDir, buildDir) - if err != nil { - return - } - - conanfilePy, err = os.ReadFile(buildDir + "/conanfile.py") - if err != nil { - return - } - - conandataFile := buildDir + "/conandata.yml" - conandataYml, err = os.ReadFile(conandataFile) - if err != nil { - return - } - var cd conandata - err = yaml.Unmarshal(conandataYml, &cd) - if err != nil { - return - } - fromVer := template.FromVer - source, ok := cd.Sources[fromVer] - if !ok { - return "", ErrVersionNotFound - } - cd.Sources = map[string]any{ - pkgVer: replaceVer(source, fromVer, pkgVer), - } - conandataYml, err = yaml.Marshal(cd) - if err != nil { - return - } - err = os.WriteFile(conandataFile, conandataYml, os.ModePerm) - if err != nil { - return - } - rev = recipeRevision(pkg, gr, conandataYml) - conanfileDir = buildDir - } - - outFile := buildDir + "/out.json" - out, err := os.Create(outFile) - if err == nil { - defer out.Close() - } else { - out = os.Stdout - } - - nameAndVer := pkg.Name + "/" + pkgVer - if template == nil { - err = conanInstall(nameAndVer, buildDir, conanfileDir, out, options, flags) - return - } - - logFile := "" - if flags&LogRevertProxy != 0 { - logFile = buildDir + "/rp.log" - } - err = remoteProxy(flags, logFile, func() error { - return conanInstall(nameAndVer, buildDir, conanfileDir, out, options, flags) - }, func(mux *http.ServeMux) { - base := "/v2/conans/" + nameAndVer - revbase := base + "/_/_/revisions/" + rev - mux.HandleFunc(base+"/_/_/latest", func(w http.ResponseWriter, r *http.Request) { - h := w.Header() - h.Set("Cache-Control", "public,max-age=300") - httputil.Reply(w, http.StatusOK, map[string]any{ - "revision": rev, - "time": gr.PublishedAt, - }) - }) - mux.HandleFunc(revbase+"/files", func(w http.ResponseWriter, r *http.Request) { - h := w.Header() - h.Set("Cache-Control", "public,max-age=3600") - empty := map[string]any{} - httputil.Reply(w, http.StatusOK, map[string]any{ - "files": map[string]any{ - "conan_export.tgz": empty, - "conanmanifest.txt": empty, - "conanfile.py": empty, - }, - }) - }) - mux.HandleFunc(revbase+"/files/conanfile.py", func(w http.ResponseWriter, r *http.Request) { - h := w.Header() - h.Set("Cache-Control", "public,max-age=3600") - h.Set("Content-Disposition", `attachment; filename="conanfile.py"`) - httputil.ReplyWith(w, http.StatusOK, "text/x-python", conanfilePy) - }) - const conanmanifest = "%d\nconandata.yml: %s\nconanfile.py: %s\n" - mux.HandleFunc(revbase+"/files/conanmanifest.txt", func(w http.ResponseWriter, r *http.Request) { - mtime, err := unixTime(gr.PublishedAt) - if err != nil { - replyError(w, err) - return - } - h := w.Header() - h.Set("Cache-Control", "public,max-age=3600") - h.Set("Content-Disposition", `attachment; filename="conanmanifest.txt"`) - data := fmt.Sprintf(conanmanifest, mtime, md5Of(conandataYml), md5Of(conanfilePy)) - 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) { - conanExportTgz, err := tgzOfConandata(buildDir) - if err != nil { - replyError(w, err) - return - } - h := w.Header() - h.Set("Cache-Control", "public,max-age=3600") - h.Set("Content-Disposition", `attachment; filename="conan_export.tgz"`) - httputil.ReplyWith(w, http.StatusOK, "application/x-gzip", conanExportTgz) - }) - }) - return -} - -func (p *Manager) BuildDir(pkg *Package, options []string) string { - 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 { - root := p.IndexRoot() - return root + "/" + pkgPath + "/" + pkgFolder -} - -func conanInstall(pkg, outDir, conanfileDir string, out io.Writer, options []string, flags int) (err error) { - args := make([]string, 0, 16) - args = append(args, "install", - "--requires", pkg, - "--generator", "PkgConfigDeps", - "--build", "missing", - "--format", "json", - "--output-folder", outDir, - ) - for _, opt := range options { - args = append(args, "--options", opt) - } - quietInstall := flags&ToolQuietInstall != 0 - cmd, err := conanCmd.New(quietInstall, args...) - if err != nil { - return - } - cmd.Dir = conanfileDir - cmd.Stderr = ConanStderr - cmd.Stdout = out - err = cmd.Run() - return -} - -var ( - // ConanStderr is the standard error output for command conan. - ConanStderr = os.Stderr -) - -func recipeRevision(_ *Package, _ *githubRelease, conandataYml []byte) string { - return md5Of(conandataYml) -} - -func md5Of(data []byte) string { - h := md5.New() - h.Write(data) - return hex.EncodeToString(h.Sum(nil)) -} - -func tgzOfConandata(outDir string) (_ []byte, err error) { - cmd := exec.Command("tar", "-czf", "conan_export.tgz", "conandata.yml") - cmd.Dir = outDir - err = cmd.Run() - if err != nil { - return - } - return os.ReadFile(outDir + "/conan_export.tgz") -} - -func unixTime(tstr string) (ret int64, err error) { - t, err := time.Parse(time.RFC3339, tstr) - if err == nil { - ret = t.Unix() - } - return -} - -func copyDirR(srcDir, destDir string) error { - if cp, err := exec.LookPath("cp"); err == nil { - return exec.Command(cp, "-r", "-p", srcDir+"/", destDir).Run() - } - if cp, err := exec.LookPath("xcopy"); err == nil { - // TODO(xsw): check xcopy - return exec.Command(cp, "/E", "/I", "/Y", srcDir+"/", destDir).Run() - } - return errors.New("copy command not found") -} diff --git a/xtool/cppkg/cppkg.go b/xtool/cppkg/cppkg.go deleted file mode 100644 index b578eb4f..00000000 --- a/xtool/cppkg/cppkg.go +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2025 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 cppkg - -import ( - "strings" -) - -const ( - // DefaultFlags is the default flags for package installation. - DefaultFlags = IndexAutoUpdate | ToolQuietInstall -) - -// Install installs a package with the given name and version. -// pkgAndVer: 7bitcoder/7bitconf@1.2.0 -func Install(pkgAndVer string, flags int) { - 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(cacheDir, pkgPath, ver string, options []string, flags int) (pkg *Package, buildDir string, err error) { - m, err := New(cacheDir) - if err != nil { - return - } - pkg, err = m.Lookup(pkgPath, ver, flags) - if err == nil { - buildDir, err = m.Install(pkg, options, flags) - } - return -} - -func parsePkgVer(pkg string) (string, string) { - parts := strings.SplitN(pkg, "@", 2) - if len(parts) == 1 { - return parts[0], "" - } - return parts[0], parts[1] -} - -func check(err error) { - if err != nil { - panic(err) - } -} diff --git a/xtool/cppkg/manager.go b/xtool/cppkg/manager.go deleted file mode 100644 index 0eac4dd6..00000000 --- a/xtool/cppkg/manager.go +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (c) 2025 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 cppkg - -import ( - "errors" - "os" - "strings" - - "github.com/goccy/go-yaml" - "github.com/goplus/llgo/internal/github" - "golang.org/x/mod/semver" -) - -var gitCmd = NewTool("git", []string{ - "brew install git", - "apt-get install git", -}) - -// Manager represents a package manager for C/C++ packages. -type Manager struct { - cacheDir string -} - -// New creates a new package manager. -func New(cacheDir string) (ret *Manager, err error) { - if cacheDir == "" { - cacheDir = CacheDir() - } - os.MkdirAll(cacheDir, os.ModePerm) - ret = &Manager{ - cacheDir: cacheDir, - } - 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 { - Folder string `yaml:"folder"` -} - -// Template represents a template for package versions. -type Template struct { - FromVer string `yaml:"from"` - Folder string `yaml:"folder"` - Tag string `yaml:"tag,omitempty"` // pattern with *, empty if dynamic tag -} - -type config struct { - PkgName string `yaml:"name"` - Versions map[string]version `yaml:"versions"` - 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. -type Package struct { - Name string - Path string - Version string - Folder string - Template *Template - - gr *github.Release // optional -} - -var ( - // ErrVersionNotFound is returned when the specified version is not found. - ErrVersionNotFound = errors.New("version not found") - - // ErrDynamicTag is returned when the tag is dynamic. - ErrDynamicTag = errors.New("dynamic tag") -) - -const ( - // IndexAutoUpdate is a flag to automatically update the index. - IndexAutoUpdate = 1 << iota - - // ToolQuietInstall is a flag to suppress output during installation. - ToolQuietInstall - - // LogRevertProxy is a flag to log revert proxy. - LogRevertProxy - - // KnownLatestVersion is a flag to use the known latest version. - KnownLatestVersion -) - -// Lookup looks up a package by its path and version. -func (p *Manager) Lookup(pkgPath, ver string, flags int) (_ *Package, err error) { - root := p.IndexRoot() - err = indexUpate(root, flags) - if err != nil { - return - } - pkgDir := root + "/" + pkgPath - confFile := pkgDir + "/config.yml" - b, err := os.ReadFile(confFile) - if err != nil { - return - } - var conf config - err = yaml.Unmarshal(b, &conf) - if err != nil { - return - } - - 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 == "" { - return nil, ErrDynamicTag - } - gr, e := github.GetRelease(pkgPath, "") - if e != nil { - return nil, e - } - ver, err = verByTag(gr.TagName, conf.Template.Tag) - if err != nil { - return - } - templ := conf.Template - return &Package{conf.PkgName, pkgPath, ver, templ.Folder, &templ, gr}, nil - } - - if v, ok := conf.Versions[ver]; ok { - return &Package{conf.PkgName, pkgPath, ver, v.Folder, nil, nil}, nil - } - if compareVer(ver, conf.Template.FromVer) < 0 { - err = ErrVersionNotFound - return - } - templ := conf.Template - return &Package{conf.PkgName, pkgPath, ver, templ.Folder, &templ, nil}, nil -} - -func (p *Manager) IndexRoot() string { - return p.cacheDir + "/index" -} - -func indexUpate(root string, flags int) (err error) { - if _, err = os.Stat(root + "/.git"); os.IsNotExist(err) { - os.RemoveAll(root) - return indexInit(root, flags) - } - if flags&IndexAutoUpdate != 0 { - quietInstall := flags&ToolQuietInstall != 0 - git, e := gitCmd.New(quietInstall, "pull", "--ff-only", "origin", "main") - if e != nil { - return e - } - git.Dir = root - git.Stdout = os.Stdout - git.Stderr = os.Stderr - err = git.Run() - } - return -} - -func indexInit(root string, flags int) (err error) { - quietInstall := flags&ToolQuietInstall != 0 - git, err := gitCmd.New(quietInstall, "clone", "https://github.com/goplus/cppkg.git", root) - if err != nil { - return - } - git.Stdout = os.Stdout - git.Stderr = os.Stderr - err = git.Run() - return -} - -func compareVer(v1, v2 string) int { - return semver.Compare("v"+v1, "v"+v2) -} - -func verByTag(tag, tagPattern string) (ver string, err error) { - if pos := strings.IndexByte(tagPattern, '*'); pos >= 0 { - prefix := tagPattern[:pos] - suffix := tagPattern[pos+1:] - if strings.HasPrefix(tag, prefix) && strings.HasSuffix(tag, suffix) { - ver = tag[pos : len(tag)-len(suffix)] - return - } - } - return "", errors.New("tag not match: " + tag + " with " + tagPattern) -} diff --git a/xtool/cppkg/revertproxy.go b/xtool/cppkg/revertproxy.go deleted file mode 100644 index ffc688d0..00000000 --- a/xtool/cppkg/revertproxy.go +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2025 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 cppkg - -import ( - "bytes" - "encoding/json" - "io" - stdlog "log" - "net/http" - "net/http/httptest" - "net/http/httputil" - "net/url" - "os" - "os/exec" - "strings" -) - -type rtHandler func(req *http.Request) (resp *http.Response, err error) - -func (p rtHandler) RoundTrip(req *http.Request) (*http.Response, error) { - return p(req) -} - -type teeReader struct { - rc io.ReadCloser - b bytes.Buffer - req *http.Request - resp *http.Response - log *stdlog.Logger -} - -func (p *teeReader) Read(b []byte) (n int, err error) { - n, err = p.rc.Read(b) - p.b.Write(b[:n]) - return -} - -func (p *teeReader) Close() error { - err := p.rc.Close() - if log := p.log; log != nil { - resp := *p.resp - resp.Body = io.NopCloser(&p.b) - var b bytes.Buffer - p.req.Write(&b) - resp.Write(&b) - log.Print(b.String()) - } - return err -} - -type response = httptest.ResponseRecorder - -func newResponse() *response { - return httptest.NewRecorder() -} - -type revertProxy = httptest.Server -type rpFunc = func(mux *http.ServeMux) - -const ( - passThrough = http.StatusNotFound -) - -func replyError(w http.ResponseWriter, _ error) { - w.WriteHeader(passThrough) -} - -func startRevertProxy(endpoint string, f rpFunc, log *stdlog.Logger) (_ *revertProxy, err error) { - rpURL, err := url.Parse(endpoint) - if err != nil { - return - } - var mux *http.ServeMux - if f != nil { - mux = http.NewServeMux() - f(mux) - } - proxy := httptest.NewServer(&httputil.ReverseProxy{ - Rewrite: func(r *httputil.ProxyRequest) { - r.SetURL(rpURL) - }, - Transport: rtHandler(func(req *http.Request) (resp *http.Response, err error) { - if mux != nil { - w := newResponse() - mux.ServeHTTP(w, req) - if w.Code != passThrough { - resp = w.Result() - } - } - if resp == nil { - resp, err = http.DefaultTransport.RoundTrip(req) - } - if err == nil && resp.Body != nil { - resp.Body = &teeReader{ - rc: resp.Body, - req: req, - resp: resp, - log: log, - } - } - return - }), - }) - return proxy, nil -} - -const ( - conanCenter = "conancenter" - conanEndpoint = "https://center2.conan.io" -) - -type remoteList []struct { - Name string `json:"name"` - URL string `json:"url"` -} - -func remoteProxy(flags int, logFile string, f func() error, rpf rpFunc) (err error) { - quietInstall := flags&ToolQuietInstall != 0 - app, err := conanCmd.Get(quietInstall) - if err != nil { - return - } - - endpoint := conanEndpoint - cmd := exec.Command(app, "remote", "list", "-f", "json") - if b, err := cmd.Output(); err == nil { - var rl remoteList - if json.Unmarshal(b, &rl) == nil { - for _, r := range rl { - if r.Name == conanCenter && strings.HasPrefix(r.URL, "https://") { - endpoint = r.URL - break - } - } - } - } - defer func() { - exec.Command(app, "remote", "add", "--force", conanCenter, endpoint).Run() - }() - - var log *stdlog.Logger - if logFile != "" { - f, err := os.Create(logFile) - if err == nil { - defer f.Close() - log = stdlog.New(f, "", stdlog.LstdFlags) - } - } - rp, err := startRevertProxy(conanEndpoint, rpf, log) - if err != nil { - return - } - defer rp.Close() - - err = exec.Command(app, "remote", "add", "--force", conanCenter, rp.URL).Run() - if err != nil { - return - } - - return f() -}