Skip to content

Commit 1fdcb55

Browse files
authored
Merge pull request #1080 from xushiwei/q
xtool/cpgithubpkg
2 parents 438b0bd + 16cec9f commit 1fdcb55

File tree

2 files changed

+228
-1
lines changed

2 files changed

+228
-1
lines changed

.github/codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ coverage:
88
- "internal/packages"
99
- "internal/typepatch"
1010
- "internal/github"
11-
- "xtool/cppkg"
11+
- "xtool"

xtool/cpgithubpkg/cpgithubpkg.go

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
/*
2+
* Copyright (c) 2025 The GoPlus Authors (goplus.org). All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package cpgithubpkg
18+
19+
import (
20+
"log"
21+
"os"
22+
"os/exec"
23+
"slices"
24+
"strings"
25+
26+
"github.com/goccy/go-yaml"
27+
"golang.org/x/mod/semver"
28+
)
29+
30+
type version struct {
31+
Folder string `yaml:"folder"`
32+
}
33+
34+
type config struct {
35+
Versions map[string]version `yaml:"versions"`
36+
}
37+
38+
type template struct {
39+
FromVer string `yaml:"from"`
40+
Folder string `yaml:"folder"`
41+
Tag string `yaml:"tag,omitempty"` // pattern with *, empty if dynamic tag
42+
}
43+
44+
type configEx struct {
45+
PkgName string `yaml:"name"`
46+
Versions map[string]version `yaml:"versions"`
47+
Template template `yaml:"template"`
48+
}
49+
50+
// Main is the entry point for copying GitHub packages.
51+
func Main(pkgName string) {
52+
localDir := conanRoot() + "recipes/" + pkgName + "/"
53+
54+
confFile := localDir + "config.yml"
55+
b, err := os.ReadFile(confFile)
56+
check(err)
57+
58+
var conf config
59+
err = yaml.Unmarshal(b, &conf)
60+
check(err)
61+
62+
tryCp := func(src map[string]any, ver string, v version) {
63+
switch url := src["url"].(type) {
64+
case string:
65+
if pkgPath, tagPattern, ok := checkGithbPkg(url, ver); ok {
66+
cpGithubPkg(pkgName, pkgPath, tagPattern, localDir, conf, ver, v)
67+
}
68+
case []any:
69+
for _, u := range url {
70+
url := u.(string)
71+
if pkgPath, tagPattern, ok := checkGithbPkg(url, ver); ok {
72+
cpGithubPkg(pkgName, pkgPath, tagPattern, localDir, conf, ver, v)
73+
}
74+
}
75+
default:
76+
log.Println("[INFO] skip source:", src)
77+
}
78+
}
79+
80+
conandatas := make(map[string]conandata) // folder -> conandata
81+
rangeVerDesc(conf.Versions, func(ver string, v version) {
82+
cd, err := getConanData(conandatas, v.Folder, localDir)
83+
if err != nil {
84+
if os.IsNotExist(err) {
85+
return
86+
}
87+
check(err)
88+
}
89+
90+
if src, ok := cd.Sources[ver]; ok {
91+
switch src := src.(type) {
92+
case map[string]any:
93+
tryCp(src, ver, v)
94+
case []any:
95+
for _, u := range src {
96+
tryCp(u.(map[string]any), ver, v)
97+
}
98+
default:
99+
log.Panicln("[FATAL] source:", src)
100+
}
101+
}
102+
})
103+
}
104+
105+
func cpGithubPkg(pkgName, pkgPath, tagPattern, srcDir string, conf config, fromVer string, v version) {
106+
destDir := cppkgRoot() + pkgPath
107+
os.MkdirAll(destDir, os.ModePerm)
108+
109+
err := exec.Command("cp", "-r", srcDir, destDir).Run()
110+
check(err)
111+
112+
confex := &configEx{
113+
PkgName: pkgName,
114+
Versions: conf.Versions,
115+
Template: template{
116+
FromVer: fromVer,
117+
Folder: v.Folder,
118+
Tag: tagPattern,
119+
},
120+
}
121+
b, err := yaml.Marshal(confex)
122+
check(err)
123+
124+
err = os.WriteFile(destDir+"/config.yml", b, os.ModePerm)
125+
check(err)
126+
127+
log.Println("[INFO] copy", pkgPath)
128+
os.Exit(0)
129+
}
130+
131+
func checkGithbPkg(url, ver string) (pkgPath, tagPattern string, ok bool) {
132+
const githubPrefix = "https://github.com/"
133+
if strings.HasPrefix(url, githubPrefix) {
134+
path := url[len(githubPrefix):]
135+
parts := strings.SplitN(path, "/", 3)
136+
if len(parts) == 3 { // user/repo/xxx
137+
if pos := strings.Index(parts[2], ver); pos >= 0 {
138+
userRepo := parts[0] + "/" + parts[1]
139+
at := len(githubPrefix) + len(userRepo) + 1 + pos
140+
tagPattern = tagPatternOf(url, ver, at)
141+
pkgPath, ok = strings.ToLower(userRepo), true
142+
}
143+
}
144+
}
145+
return
146+
}
147+
148+
func tagPatternOf(url, ver string, at int) (tagPattern string) {
149+
var tag string
150+
if pos := strings.LastIndexByte(url[:at], '/'); pos >= 0 {
151+
last := at + len(ver)
152+
left := url[last:]
153+
if end, ok := checkTagEnd(left); ok {
154+
pos++
155+
tag = url[pos:last]
156+
tagPattern = url[pos:at] + end
157+
}
158+
}
159+
if tag == "" {
160+
log.Println("[INFO] dynamic tag found:", url)
161+
}
162+
return
163+
}
164+
165+
func checkTagEnd(left string) (end string, ok bool) {
166+
if n := len(left); n > 0 {
167+
if left[0] == '/' {
168+
return "*", true
169+
}
170+
if n >= 4 && left[0] == '.' {
171+
ext := left[1:4]
172+
return "*", ext == "tar" || ext == "zip" || ext == "tgz"
173+
}
174+
if strings.HasPrefix(left, "-stable.") {
175+
return "*-stable", true
176+
}
177+
}
178+
return "", false
179+
}
180+
181+
type conandata struct {
182+
Sources map[string]any `yaml:"sources"`
183+
}
184+
185+
func getConanData(conandatas map[string]conandata, folder, localDir string) (ret conandata, err error) {
186+
if v, ok := conandatas[folder]; ok {
187+
return v, nil
188+
}
189+
file := localDir + folder + "/conandata.yml"
190+
b, err := os.ReadFile(file)
191+
if err != nil {
192+
return
193+
}
194+
if err = yaml.Unmarshal(b, &ret); err != nil {
195+
return
196+
}
197+
conandatas[folder] = ret
198+
return
199+
}
200+
201+
func rangeVerDesc[V any](data map[string]V, f func(string, V)) {
202+
keys := make([]string, 0, len(data))
203+
for k := range data {
204+
keys = append(keys, "v"+k)
205+
}
206+
semver.Sort(keys)
207+
for _, k := range slices.Backward(keys) {
208+
k = k[1:] // remove 'v'
209+
f(k, data[k])
210+
}
211+
}
212+
213+
func conanRoot() string {
214+
home, _ := os.UserHomeDir()
215+
return home + "/conan-center-index/"
216+
}
217+
218+
func cppkgRoot() string {
219+
home, _ := os.UserHomeDir()
220+
return home + "/cppkg/"
221+
}
222+
223+
func check(err error) {
224+
if err != nil {
225+
panic(err)
226+
}
227+
}

0 commit comments

Comments
 (0)