pkgbuild.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. package pkgbuild
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io/ioutil"
  6. "strconv"
  7. "strings"
  8. )
  9. // Dependency describes a dependency with min and max version, if any.
  10. type Dependency struct {
  11. Name string // dependency name
  12. MinVer *CompleteVersion // min version
  13. sgt bool // defines if min version is strictly greater than
  14. MaxVer *CompleteVersion // max version
  15. slt bool // defines if max version is strictly less than
  16. }
  17. // PKGBUILD is a struct describing a parsed PKGBUILD file.
  18. // Required fields are:
  19. // pkgname
  20. // pkgver
  21. // pkgrel
  22. // arch
  23. // (license) - not required but recommended
  24. //
  25. // parsing a PKGBUILD file without these fields will fail
  26. type PKGBUILD struct {
  27. Pkgnames []string
  28. Pkgver Version // required
  29. Pkgrel Version // required
  30. Pkgdir string
  31. Epoch int
  32. Pkgbase string
  33. Pkgdesc string
  34. Arch []string // required
  35. URL string
  36. License []string // recommended
  37. Groups []string
  38. Depends []*Dependency
  39. Optdepends []string
  40. Makedepends []*Dependency
  41. Checkdepends []*Dependency
  42. Provides []string
  43. Conflicts []string
  44. Replaces []string
  45. Backup []string
  46. Options []string
  47. Install string
  48. Changelog string
  49. Source []string
  50. Noextract []string
  51. Md5sums []string
  52. Sha1sums []string
  53. Sha224sums []string
  54. Sha256sums []string
  55. Sha384sums []string
  56. Sha512sums []string
  57. Validpgpkeys []string
  58. }
  59. // Newer is true if p has a higher version number than p2
  60. func (p *PKGBUILD) Newer(p2 *PKGBUILD) bool {
  61. if p.Epoch < p2.Epoch {
  62. return false
  63. }
  64. if p.Pkgver.bigger(p2.Pkgver) {
  65. return true
  66. }
  67. if p2.Pkgver.bigger(p.Pkgver) {
  68. return false
  69. }
  70. return p.Pkgrel > p2.Pkgrel
  71. }
  72. // Older is true if p has a smaller version number than p2
  73. func (p *PKGBUILD) Older(p2 *PKGBUILD) bool {
  74. if p.Epoch < p2.Epoch {
  75. return true
  76. }
  77. if p2.Pkgver.bigger(p.Pkgver) {
  78. return true
  79. }
  80. if p.Pkgver.bigger(p2.Pkgver) {
  81. return false
  82. }
  83. return p.Pkgrel < p2.Pkgrel
  84. }
  85. // Version returns the full version of the PKGBUILD (including epoch and rel)
  86. func (p *PKGBUILD) Version() string {
  87. if p.Epoch > 0 {
  88. return fmt.Sprintf("%d:%s-%s", p.Epoch, p.Pkgver, p.Pkgrel)
  89. }
  90. return fmt.Sprintf("%s-%s", p.Pkgver, p.Pkgrel)
  91. }
  92. // CompleteVersion returns a Complete version struct including version, rel and
  93. // epoch.
  94. func (p *PKGBUILD) CompleteVersion() CompleteVersion {
  95. return CompleteVersion{
  96. Version: p.Pkgver,
  97. Epoch: uint8(p.Epoch),
  98. Pkgrel: p.Pkgrel,
  99. }
  100. }
  101. // BuildDepends is Depends, MakeDepends and CheckDepends combined.
  102. func (p *PKGBUILD) BuildDepends() []*Dependency {
  103. // TODO real merge
  104. deps := make([]*Dependency, len(p.Depends)+len(p.Makedepends)+len(p.Checkdepends))
  105. deps = append(p.Depends, p.Makedepends...)
  106. deps = append(deps, p.Checkdepends...)
  107. return deps
  108. }
  109. // IsDevel returns true if package contains devel packages (-{bzr,git,svn,hg})
  110. // TODO: more robust check.
  111. func (p *PKGBUILD) IsDevel() bool {
  112. for _, name := range p.Pkgnames {
  113. if strings.HasSuffix(name, "-git") {
  114. return true
  115. }
  116. if strings.HasSuffix(name, "-svn") {
  117. return true
  118. }
  119. if strings.HasSuffix(name, "-hg") {
  120. return true
  121. }
  122. if strings.HasSuffix(name, "-bzr") {
  123. return true
  124. }
  125. }
  126. return false
  127. }
  128. // MustParseSRCINFO must parse the .SRCINFO given by path or it will panic
  129. func MustParseSRCINFO(path string) *PKGBUILD {
  130. pkgbuild, err := ParseSRCINFO(path)
  131. if err != nil {
  132. panic(err)
  133. }
  134. return pkgbuild
  135. }
  136. // ParseSRCINFO parses .SRCINFO file given by path.
  137. // This is a safe alternative to ParsePKGBUILD given that a .SRCINFO file is
  138. // available
  139. func ParseSRCINFO(path string) (*PKGBUILD, error) {
  140. f, err := ioutil.ReadFile(path)
  141. if err != nil {
  142. return nil, fmt.Errorf("unable to read file: %s, %s", path, err.Error())
  143. }
  144. return parsePKGBUILD(string(f))
  145. }
  146. // ParseSRCINFOContent parses a .SRCINFO formatted byte slice.
  147. // This is a safe alternative to ParsePKGBUILD given that the .SRCINFO content
  148. // is available
  149. func ParseSRCINFOContent(content []byte) (*PKGBUILD, error) {
  150. return parsePKGBUILD(string(content))
  151. }
  152. // parse a PKGBUILD and check that the required fields has a non-empty value
  153. func parsePKGBUILD(input string) (*PKGBUILD, error) {
  154. pkgb, err := parse(input)
  155. if err != nil {
  156. return nil, err
  157. }
  158. if !validPkgver(string(pkgb.Pkgver)) {
  159. return nil, fmt.Errorf("invalid pkgver: %s", pkgb.Pkgver)
  160. }
  161. if len(pkgb.Arch) == 0 {
  162. return nil, fmt.Errorf("Arch missing")
  163. }
  164. if len(pkgb.Pkgnames) == 0 {
  165. return nil, fmt.Errorf("missing pkgname")
  166. }
  167. for _, name := range pkgb.Pkgnames {
  168. if !validPkgname(name) {
  169. return nil, fmt.Errorf("invalid pkgname: %s", name)
  170. }
  171. }
  172. return pkgb, nil
  173. }
  174. // parses a SRCINFO formatted PKGBUILD
  175. func parse(input string) (*PKGBUILD, error) {
  176. var pkgbuild *PKGBUILD
  177. var next item
  178. lexer := lex(input)
  179. Loop:
  180. for {
  181. token := lexer.nextItem()
  182. // strip arch from source_arch like constructs
  183. witharch := strings.SplitN(token.val, "_", 2)
  184. if len(witharch) == 2 {
  185. found := false
  186. for _, arch := range pkgbuild.Arch {
  187. if arch == witharch[1] {
  188. token.val = witharch[0]
  189. found = true
  190. break
  191. }
  192. }
  193. if !found {
  194. return nil, fmt.Errorf("unsupported arch for variable: %s", token.val)
  195. }
  196. }
  197. switch token.typ {
  198. case itemPkgbase:
  199. next = lexer.nextItem()
  200. pkgbuild = &PKGBUILD{Epoch: 0, Pkgbase: next.val}
  201. case itemPkgname:
  202. next = lexer.nextItem()
  203. pkgbuild.Pkgnames = append(pkgbuild.Pkgnames, next.val)
  204. case itemPkgver:
  205. next = lexer.nextItem()
  206. version, err := parseVersion(next.val)
  207. if err != nil {
  208. return nil, err
  209. }
  210. pkgbuild.Pkgver = version
  211. case itemPkgrel:
  212. next = lexer.nextItem()
  213. rel, err := parseVersion(next.val)
  214. if err != nil {
  215. return nil, err
  216. }
  217. pkgbuild.Pkgrel = rel
  218. case itemPkgdir:
  219. next = lexer.nextItem()
  220. pkgbuild.Pkgdir = next.val
  221. case itemEpoch:
  222. next = lexer.nextItem()
  223. epoch, err := strconv.ParseInt(next.val, 10, 0)
  224. if err != nil {
  225. return nil, err
  226. }
  227. if epoch < 0 {
  228. return nil, fmt.Errorf("invalid epoch: %d", epoch)
  229. }
  230. pkgbuild.Epoch = int(epoch)
  231. case itemPkgdesc:
  232. next = lexer.nextItem()
  233. pkgbuild.Pkgdesc = next.val
  234. case itemArch:
  235. next = lexer.nextItem()
  236. pkgbuild.Arch = append(pkgbuild.Arch, next.val)
  237. case itemURL:
  238. next = lexer.nextItem()
  239. pkgbuild.URL = next.val
  240. case itemLicense:
  241. next = lexer.nextItem()
  242. pkgbuild.License = append(pkgbuild.License, next.val)
  243. case itemGroups:
  244. next = lexer.nextItem()
  245. pkgbuild.Groups = append(pkgbuild.Groups, next.val)
  246. case itemDepends:
  247. next = lexer.nextItem()
  248. deps, err := parseDependency(next.val, pkgbuild.Depends)
  249. if err != nil {
  250. return nil, err
  251. }
  252. pkgbuild.Depends = deps
  253. case itemOptdepends:
  254. next = lexer.nextItem()
  255. pkgbuild.Optdepends = append(pkgbuild.Optdepends, next.val)
  256. case itemMakedepends:
  257. next = lexer.nextItem()
  258. deps, err := parseDependency(next.val, pkgbuild.Makedepends)
  259. if err != nil {
  260. return nil, err
  261. }
  262. pkgbuild.Makedepends = deps
  263. case itemCheckdepends:
  264. next = lexer.nextItem()
  265. deps, err := parseDependency(next.val, pkgbuild.Checkdepends)
  266. if err != nil {
  267. return nil, err
  268. }
  269. pkgbuild.Checkdepends = deps
  270. case itemProvides:
  271. next = lexer.nextItem()
  272. pkgbuild.Provides = append(pkgbuild.Provides, next.val)
  273. case itemConflicts:
  274. next = lexer.nextItem()
  275. pkgbuild.Conflicts = append(pkgbuild.Conflicts, next.val)
  276. case itemReplaces:
  277. next = lexer.nextItem()
  278. pkgbuild.Replaces = append(pkgbuild.Replaces, next.val)
  279. case itemBackup:
  280. next = lexer.nextItem()
  281. pkgbuild.Backup = append(pkgbuild.Backup, next.val)
  282. case itemOptions:
  283. next = lexer.nextItem()
  284. pkgbuild.Options = append(pkgbuild.Options, next.val)
  285. case itemInstall:
  286. next = lexer.nextItem()
  287. pkgbuild.Install = next.val
  288. case itemChangelog:
  289. next = lexer.nextItem()
  290. pkgbuild.Changelog = next.val
  291. case itemSource:
  292. next = lexer.nextItem()
  293. pkgbuild.Source = append(pkgbuild.Source, next.val)
  294. case itemNoextract:
  295. next = lexer.nextItem()
  296. pkgbuild.Noextract = append(pkgbuild.Noextract, next.val)
  297. case itemMd5sums:
  298. next = lexer.nextItem()
  299. pkgbuild.Md5sums = append(pkgbuild.Md5sums, next.val)
  300. case itemSha1sums:
  301. next = lexer.nextItem()
  302. pkgbuild.Sha1sums = append(pkgbuild.Sha1sums, next.val)
  303. case itemSha224sums:
  304. next = lexer.nextItem()
  305. pkgbuild.Sha224sums = append(pkgbuild.Sha224sums, next.val)
  306. case itemSha256sums:
  307. next = lexer.nextItem()
  308. pkgbuild.Sha256sums = append(pkgbuild.Sha256sums, next.val)
  309. case itemSha384sums:
  310. next = lexer.nextItem()
  311. pkgbuild.Sha384sums = append(pkgbuild.Sha384sums, next.val)
  312. case itemSha512sums:
  313. next = lexer.nextItem()
  314. pkgbuild.Sha512sums = append(pkgbuild.Sha512sums, next.val)
  315. case itemValidpgpkeys:
  316. next = lexer.nextItem()
  317. pkgbuild.Validpgpkeys = append(pkgbuild.Validpgpkeys, next.val)
  318. case itemEndSplit:
  319. case itemError:
  320. return nil, fmt.Errorf(token.val)
  321. case itemEOF:
  322. break Loop
  323. default:
  324. return nil, fmt.Errorf("invalid variable: %s", token.val)
  325. }
  326. }
  327. return pkgbuild, nil
  328. }
  329. // parse and validate a version string
  330. func parseVersion(s string) (Version, error) {
  331. if validPkgver(s) {
  332. return Version(s), nil
  333. }
  334. return "", fmt.Errorf("invalid version string: %s", s)
  335. }
  336. // check if name is a valid pkgname format
  337. func validPkgname(name string) bool {
  338. if len(name) < 1 {
  339. return false
  340. }
  341. if name[0] == '-' {
  342. return false
  343. }
  344. for _, r := range name {
  345. if !isValidPkgnameChar(uint8(r)) {
  346. return false
  347. }
  348. }
  349. return true
  350. }
  351. // check if version is a valid pkgver format
  352. func validPkgver(version string) bool {
  353. if len(version) < 1 {
  354. return false
  355. }
  356. if !isAlphaNumeric(version[0]) {
  357. return false
  358. }
  359. for _, r := range version[1:] {
  360. if !isValidPkgverChar(uint8(r)) {
  361. return false
  362. }
  363. }
  364. return true
  365. }
  366. // ParseDeps parses a string slice of dependencies into a slice of Dependency
  367. // objects.
  368. func ParseDeps(deps []string) ([]*Dependency, error) {
  369. var err error
  370. dependencies := make([]*Dependency, 0)
  371. for _, dep := range deps {
  372. dependencies, err = parseDependency(dep, dependencies)
  373. if err != nil {
  374. return nil, err
  375. }
  376. }
  377. return dependencies, nil
  378. }
  379. // parse dependency with possible version restriction
  380. func parseDependency(dep string, deps []*Dependency) ([]*Dependency, error) {
  381. var name string
  382. var dependency *Dependency
  383. if dep == "" {
  384. return deps, nil
  385. }
  386. if dep[0] == '-' {
  387. return nil, fmt.Errorf("invalid dependency name")
  388. }
  389. i := 0
  390. for _, c := range dep {
  391. if !isValidPkgnameChar(uint8(c)) {
  392. break
  393. }
  394. i++
  395. }
  396. // check if the dependency has been set before
  397. name = dep[0:i]
  398. for _, d := range deps {
  399. if d.Name == name {
  400. dependency = d
  401. }
  402. }
  403. if dependency == nil {
  404. dependency = &Dependency{
  405. Name: name,
  406. sgt: false,
  407. slt: false,
  408. }
  409. deps = append(deps, dependency)
  410. }
  411. if len(dep) == len(name) {
  412. return deps, nil
  413. }
  414. var eq bytes.Buffer
  415. for _, c := range dep[i:] {
  416. if c == '<' || c == '>' || c == '=' {
  417. i++
  418. eq.WriteRune(c)
  419. continue
  420. }
  421. break
  422. }
  423. version, err := NewCompleteVersion(dep[i:])
  424. if err != nil {
  425. return nil, err
  426. }
  427. switch eq.String() {
  428. case "==":
  429. dependency.MinVer = version
  430. dependency.MaxVer = version
  431. case "<=":
  432. dependency.MaxVer = version
  433. case ">=":
  434. dependency.MinVer = version
  435. case "<":
  436. dependency.MaxVer = version
  437. dependency.slt = true
  438. case ">":
  439. dependency.MinVer = version
  440. dependency.sgt = true
  441. }
  442. return deps, nil
  443. }
  444. // isLowerAlpha reports whether c is a lowercase alpha character
  445. func isLowerAlpha(c uint8) bool {
  446. return 'a' <= c && c <= 'z'
  447. }
  448. // check if c is a valid pkgname char
  449. func isValidPkgnameChar(c uint8) bool {
  450. return isLowerAlpha(c) || isDigit(c) || c == '@' || c == '.' || c == '_' || c == '+' || c == '-'
  451. }
  452. // check if c is a valid pkgver char
  453. func isValidPkgverChar(c uint8) bool {
  454. return isAlphaNumeric(c) || c == '_' || c == '+' || c == '.' || c == '~'
  455. }