From 22c427bb969c26077ec9f230f9b532a73522da7c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Fri, 14 Feb 2014 22:04:48 +0400 Subject: [PATCH] Parsing & generation of source package stanzas. --- debian/package.go | 151 +++++++++++++++++++++++++++++++++++------ debian/package_test.go | 87 +++++++++++++++++++++++- 2 files changed, 215 insertions(+), 23 deletions(-) diff --git a/debian/package.go b/debian/package.go index 5d7b68a8..d1c61cb5 100644 --- a/debian/package.go +++ b/debian/package.go @@ -37,16 +37,25 @@ func (f *PackageFile) Verify(packageRepo *Repository) (bool, error) { // Package is single instance of Debian package type Package struct { + // Is this source package + IsSource bool + // Basic package properties Name string Version string Architecture string - Source string - Provides []string + // If this source package, this field holds "real" architecture value, + // while Architecture would be equal to "source" + SourceArchitecture string + // For binary package, name of source package + Source string + Provides []string // Various dependencies - Depends []string - PreDepends []string - Suggests []string - Recommends []string + Depends []string + BuildDepends []string + BuildDependsInDep []string + PreDepends []string + Suggests []string + Recommends []string // Files in package Files []PackageFile // Extra information from stanza @@ -112,6 +121,83 @@ func NewPackageFromControlFile(input Stanza) *Package { return result } +// NewSourcePackageFromControlFile creates Package from parsed Debian control file for source package +func NewSourcePackageFromControlFile(input Stanza) (*Package, error) { + result := &Package{ + IsSource: true, + Name: input["Package"], + Version: input["Version"], + Architecture: "source", + SourceArchitecture: input["Architecture"], + } + + delete(input, "Package") + delete(input, "Version") + delete(input, "Architecture") + + parseSums := func(field string, setter func(sum *utils.ChecksumInfo, data string)) error { + for _, line := range strings.Split(input[field], "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + parts := strings.Fields(line) + + if len(parts) != 3 { + return fmt.Errorf("unparseable hash sum line: %#v", line) + } + + size, err := strconv.ParseInt(parts[1], 10, 64) + if err != nil { + return fmt.Errorf("unable to parse size: %s", err) + } + + found := false + pos := 0 + + for i, file := range result.Files { + if file.Filename == parts[2] { + found = true + pos = i + break + } + } + + if !found { + result.Files = append(result.Files, PackageFile{Filename: parts[2]}) + pos = len(result.Files) - 1 + } + + result.Files[pos].Checksums.Size = size + setter(&result.Files[pos].Checksums, parts[0]) + } + + delete(input, field) + + return nil + } + + err := parseSums("Files", func(sum *utils.ChecksumInfo, data string) { sum.MD5 = data }) + if err != nil { + return nil, err + } + err = parseSums("Checksums-Sha1", func(sum *utils.ChecksumInfo, data string) { sum.SHA1 = data }) + if err != nil { + return nil, err + } + err = parseSums("Checksums-Sha256", func(sum *utils.ChecksumInfo, data string) { sum.SHA256 = data }) + if err != nil { + return nil, err + } + + result.BuildDepends = parseDependencies(input, "Build-Depends") + result.BuildDependsInDep = parseDependencies(input, "Build-Depends-Indep") + + result.Extra = input + + return result, nil +} + // Key returns unique key identifying package func (p *Package) Key() []byte { return []byte("P" + p.Architecture + " " + p.Name + " " + p.Version) @@ -169,18 +255,38 @@ func (p *Package) Stanza() (result Stanza) { result = p.Extra.Copy() result["Package"] = p.Name result["Version"] = p.Version - result["Filename"] = p.Files[0].Filename - result["Architecture"] = p.Architecture - result["Source"] = p.Source - if p.Files[0].Checksums.MD5 != "" { - result["MD5sum"] = p.Files[0].Checksums.MD5 + if p.IsSource { + result["Architecture"] = p.SourceArchitecture + } else { + result["Architecture"] = p.Architecture + result["Source"] = p.Source } - if p.Files[0].Checksums.SHA1 != "" { - result["SHA1"] = " " + p.Files[0].Checksums.SHA1 - } - if p.Files[0].Checksums.SHA256 != "" { - result["SHA256"] = " " + p.Files[0].Checksums.SHA256 + + if p.IsSource { + md5, sha1, sha256 := make([]string, len(p.Files)), make([]string, len(p.Files)), make([]string, len(p.Files)) + + for i, f := range p.Files { + md5[i] = fmt.Sprintf(" %s %d %s\n", f.Checksums.MD5, f.Checksums.Size, f.Filename) + sha1[i] = fmt.Sprintf(" %s %d %s\n", f.Checksums.SHA1, f.Checksums.Size, f.Filename) + sha256[i] = fmt.Sprintf(" %s %d %s\n", f.Checksums.SHA256, f.Checksums.Size, f.Filename) + } + + result["Files"] = strings.Join(md5, "") + result["Checksums-Sha1"] = strings.Join(sha1, "") + result["Checksums-Sha256"] = strings.Join(sha256, "") + } else { + result["Filename"] = p.Files[0].Filename + if p.Files[0].Checksums.MD5 != "" { + result["MD5sum"] = p.Files[0].Checksums.MD5 + } + if p.Files[0].Checksums.SHA1 != "" { + result["SHA1"] = " " + p.Files[0].Checksums.SHA1 + } + if p.Files[0].Checksums.SHA256 != "" { + result["SHA256"] = " " + p.Files[0].Checksums.SHA256 + } + result["Size"] = fmt.Sprintf("%d", p.Files[0].Checksums.Size) } if p.Depends != nil { @@ -198,8 +304,12 @@ func (p *Package) Stanza() (result Stanza) { if p.Provides != nil { result["Provides"] = strings.Join(p.Provides, ", ") } - - result["Size"] = fmt.Sprintf("%d", p.Files[0].Checksums.Size) + if p.BuildDepends != nil { + result["Build-Depends"] = strings.Join(p.BuildDepends, ", ") + } + if p.BuildDependsInDep != nil { + result["Build-Depends-Indep"] = strings.Join(p.BuildDependsInDep, ", ") + } return } @@ -216,11 +326,12 @@ func (p *Package) Equals(p2 *Package) bool { } } - return p.Name == p2.Name && p.Version == p2.Version && + return p.Name == p2.Name && p.Version == p2.Version && p.SourceArchitecture == p2.SourceArchitecture && p.Architecture == p2.Architecture && utils.StrSlicesEqual(p.Depends, p2.Depends) && utils.StrSlicesEqual(p.PreDepends, p2.PreDepends) && utils.StrSlicesEqual(p.Suggests, p2.Suggests) && utils.StrSlicesEqual(p.Recommends, p2.Recommends) && utils.StrMapsEqual(p.Extra, p2.Extra) && - p.Source == p2.Source && utils.StrSlicesEqual(p.Provides, p2.Provides) + p.Source == p2.Source && utils.StrSlicesEqual(p.Provides, p2.Provides) && utils.StrSlicesEqual(p.BuildDepends, p2.BuildDepends) && + utils.StrSlicesEqual(p.BuildDependsInDep, p2.BuildDependsInDep) && p.IsSource == p2.IsSource } // LinkFromPool links package file from pool to dist's pool location diff --git a/debian/package_test.go b/debian/package_test.go index d111af70..bcc55008 100644 --- a/debian/package_test.go +++ b/debian/package_test.go @@ -1,6 +1,7 @@ package debian import ( + "bytes" "github.com/smira/aptly/database" "github.com/smira/aptly/utils" . "launchpad.net/gocheck" @@ -8,16 +9,18 @@ import ( "path/filepath" ) -var packageStanza = Stanza{"Source": "alien-arena", "Pre-Depends": "dpkg (>= 1.6)", "Suggests": "alien-arena-mars", "Recommends": "aliean-arena-luna", "Depends": "libc6 (>= 2.7), alien-arena-data (>= 7.40)", "Filename": "pool/contrib/a/alien-arena/alien-arena-common_7.40-2_i386.deb", "SHA1": " 46955e48cad27410a83740a21d766ce362364024", "SHA256": " eb4afb9885cba6dc70cccd05b910b2dbccc02c5900578be5e99f0d3dbf9d76a5", "Priority": "extra", "Maintainer": "Debian Games Team ", "Description": "Common files for Alien Arena client and server ALIEN ARENA is a standalone 3D first person online deathmatch shooter\n crafted from the original source code of Quake II and Quake III, released\n by id Software under the GPL license. With features including 32 bit\n graphics, new particle engine and effects, light blooms, reflective water,\n hi resolution textures and skins, hi poly models, stain maps, ALIEN ARENA\n pushes the envelope of graphical beauty rivaling today's top games.\n .\n This package installs the common files for Alien Arena.\n", "Homepage": "http://red.planetarena.org", "Tag": "role::app-data, role::shared-lib, special::auto-inst-parts", "Installed-Size": "456", "Version": "7.40-2", "Replaces": "alien-arena (<< 7.33-1)", "Size": "187518", "MD5sum": "1e8cba92c41420aa7baa8a5718d67122", "Package": "alien-arena-common", "Section": "contrib/games", "Architecture": "i386"} - type PackageSuite struct { - stanza Stanza + stanza Stanza + sourceStanza Stanza } var _ = Suite(&PackageSuite{}) func (s *PackageSuite) SetUpTest(c *C) { s.stanza = packageStanza.Copy() + + buf := bytes.NewBufferString(sourcePackageMeta) + s.sourceStanza, _ = NewControlFileReader(buf).ReadStanza() } func (s *PackageSuite) TestPackageFileVerify(c *C) { @@ -58,6 +61,7 @@ func (s *PackageSuite) TestPackageFileVerify(c *C) { func (s *PackageSuite) TestNewFromPara(c *C) { p := NewPackageFromControlFile(s.stanza) + c.Check(p.IsSource, Equals, false) c.Check(p.Name, Equals, "alien-arena-common") c.Check(p.Version, Equals, "7.40-2") c.Check(p.Architecture, Equals, "i386") @@ -69,6 +73,37 @@ func (s *PackageSuite) TestNewFromPara(c *C) { c.Check(p.Depends, DeepEquals, []string{"libc6 (>= 2.7)", "alien-arena-data (>= 7.40)"}) } +func (s *PackageSuite) TestNewSourceFromPara(c *C) { + p, err := NewSourcePackageFromControlFile(s.sourceStanza) + + c.Check(err, IsNil) + c.Check(p.IsSource, Equals, true) + c.Check(p.Name, Equals, "access-modifier-checker") + c.Check(p.Version, Equals, "1.0-4") + c.Check(p.Architecture, Equals, "source") + c.Check(p.SourceArchitecture, Equals, "all") + c.Check(p.Provides, IsNil) + c.Check(p.BuildDepends, DeepEquals, []string{"cdbs", "debhelper (>= 7)", "default-jdk", "maven-debian-helper"}) + c.Check(p.BuildDependsInDep, DeepEquals, []string{"default-jdk-doc", "junit (>= 3.8.1)", "libannotation-indexer-java (>= 1.3)", "libannotation-indexer-java-doc", "libasm3-java", "libmaven-install-plugin-java", "libmaven-javadoc-plugin-java", "libmaven-scm-java", "libmaven2-core-java", "libmaven2-core-java-doc", "libmetainf-services-java", "libmetainf-services-java-doc", "libmaven-plugin-tools-java (>= 2.8)"}) + c.Check(p.Files, HasLen, 3) + + c.Check(p.Files[0].Filename, Equals, "access-modifier-checker_1.0-4.dsc") + c.Check(p.Files[0].Checksums.Size, Equals, int64(2583)) + c.Check(p.Files[0].Checksums.MD5, Equals, "eb2a7a57c92ebac9d3d1c2d2d719f290") + c.Check(p.Files[0].Checksums.SHA1, Equals, "a70b87bba4391c0138cc9cc75efa747c4eeea2d7") + c.Check(p.Files[0].Checksums.SHA256, Equals, "36ae7f68974bd69450229244510faa8f8056709913770218821069e6cce6b4b8") + + c.Check(p.Files[1].Filename, Equals, "access-modifier-checker_1.0.orig.tar.gz") + c.Check(p.Files[1].Checksums.Size, Equals, int64(10049)) + c.Check(p.Files[1].Checksums.MD5, Equals, "f280abdc753beea4243d99530f023f12") + c.Check(p.Files[1].Checksums.SHA1, Equals, "c5bea16095ae1685941193370d20756e036d61a7") + c.Check(p.Files[1].Checksums.SHA256, Equals, "9d362301892bd647a361755bde28f0a39644a5bbb2ff5300060373ee29d9d87a") + + c.Check(p.Files[2].Filename, Equals, "access-modifier-checker_1.0-4.debian.tar.gz") + + c.Check(p.Depends, IsNil) +} + func (s *PackageSuite) TestWithProvides(c *C) { s.stanza["Provides"] = "arena" p := NewPackageFromControlFile(s.stanza) @@ -101,6 +136,11 @@ func (s *PackageSuite) TestStanza(c *C) { stanza := p.Stanza() c.Assert(stanza, DeepEquals, s.stanza) + + p, _ = NewSourcePackageFromControlFile(s.sourceStanza.Copy()) + stanza = p.Stanza() + + c.Assert(stanza, DeepEquals, s.sourceStanza) } func (s *PackageSuite) TestString(c *C) { @@ -120,6 +160,11 @@ func (s *PackageSuite) TestEquals(c *C) { p2 = NewPackageFromControlFile(packageStanza.Copy()) p2.Files[0].Checksums.MD5 = "abcdefabcdef" c.Check(p.Equals(p2), Equals, false) + + so, _ := NewSourcePackageFromControlFile(s.sourceStanza.Copy()) + so2, _ := NewSourcePackageFromControlFile(s.sourceStanza.Copy()) + + c.Check(so.Equals(so2), Equals, true) } func (s *PackageSuite) TestMatchesArchitecture(c *C) { @@ -267,3 +312,39 @@ func (s *PackageCollectionSuite) TestDeleteByKey(c *C) { _, err = s.collection.ByKey(s.p.Key()) c.Check(err, ErrorMatches, "key not found") } + +var packageStanza = Stanza{"Source": "alien-arena", "Pre-Depends": "dpkg (>= 1.6)", "Suggests": "alien-arena-mars", "Recommends": "aliean-arena-luna", "Depends": "libc6 (>= 2.7), alien-arena-data (>= 7.40)", "Filename": "pool/contrib/a/alien-arena/alien-arena-common_7.40-2_i386.deb", "SHA1": " 46955e48cad27410a83740a21d766ce362364024", "SHA256": " eb4afb9885cba6dc70cccd05b910b2dbccc02c5900578be5e99f0d3dbf9d76a5", "Priority": "extra", "Maintainer": "Debian Games Team ", "Description": "Common files for Alien Arena client and server ALIEN ARENA is a standalone 3D first person online deathmatch shooter\n crafted from the original source code of Quake II and Quake III, released\n by id Software under the GPL license. With features including 32 bit\n graphics, new particle engine and effects, light blooms, reflective water,\n hi resolution textures and skins, hi poly models, stain maps, ALIEN ARENA\n pushes the envelope of graphical beauty rivaling today's top games.\n .\n This package installs the common files for Alien Arena.\n", "Homepage": "http://red.planetarena.org", "Tag": "role::app-data, role::shared-lib, special::auto-inst-parts", "Installed-Size": "456", "Version": "7.40-2", "Replaces": "alien-arena (<< 7.33-1)", "Size": "187518", "MD5sum": "1e8cba92c41420aa7baa8a5718d67122", "Package": "alien-arena-common", "Section": "contrib/games", "Architecture": "i386"} + +const sourcePackageMeta = `Package: access-modifier-checker +Binary: libaccess-modifier-checker-java, libaccess-modifier-checker-java-doc +Version: 1.0-4 +Maintainer: Debian Java Maintainers +Uploaders: James Page +Build-Depends: cdbs, debhelper (>= 7), default-jdk, maven-debian-helper +Build-Depends-Indep: default-jdk-doc, junit (>= 3.8.1), libannotation-indexer-java (>= 1.3), libannotation-indexer-java-doc, libasm3-java, libmaven-install-plugin-java, libmaven-javadoc-plugin-java, libmaven-scm-java, libmaven2-core-java, libmaven2-core-java-doc, libmetainf-services-java, libmetainf-services-java-doc, libmaven-plugin-tools-java (>= 2.8) +Architecture: all +Standards-Version: 3.9.3 +Format: 3.0 (quilt) +Files: + eb2a7a57c92ebac9d3d1c2d2d719f290 2583 access-modifier-checker_1.0-4.dsc + f280abdc753beea4243d99530f023f12 10049 access-modifier-checker_1.0.orig.tar.gz + 182b18d7a4bfd1a4e6bfda886f986766 4310 access-modifier-checker_1.0-4.debian.tar.gz +Dm-Upload-Allowed: yes +Vcs-Browser: http://git.debian.org/?p=pkg-java/access-modifier-checker.git +Vcs-Git: git://git.debian.org/git/pkg-java/access-modifier-checker.git +Checksums-Sha1: + a70b87bba4391c0138cc9cc75efa747c4eeea2d7 2583 access-modifier-checker_1.0-4.dsc + c5bea16095ae1685941193370d20756e036d61a7 10049 access-modifier-checker_1.0.orig.tar.gz + 54033d40ae2f35eb147df0876c649eb9d2eb86bc 4310 access-modifier-checker_1.0-4.debian.tar.gz +Checksums-Sha256: + 36ae7f68974bd69450229244510faa8f8056709913770218821069e6cce6b4b8 2583 access-modifier-checker_1.0-4.dsc + 9d362301892bd647a361755bde28f0a39644a5bbb2ff5300060373ee29d9d87a 10049 access-modifier-checker_1.0.orig.tar.gz + 23852c63f7b5511b4c827a443ff0f92926fc71efc57dc47b49cb2fd9136d8f95 4310 access-modifier-checker_1.0-4.debian.tar.gz +Homepage: https://github.com/kohsuke/access-modifier +Package-List: + libaccess-modifier-checker-java deb java optional + libaccess-modifier-checker-java-doc deb doc optional +Directory: pool/main/a/access-modifier-checker +Priority: source +Section: java +`