pkgbuild.go 14 KB

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