func TestResolverVersion(t *testing.T) { arch, err := dependency.ParseArch("amd64") isok(t, err) candidates, err := resolver.ReadFromBinaryIndex( strings.NewReader(testBinaryIndex), ) isok(t, err) assert(t, len(*candidates) == 3) dep, err := dependency.Parse("android-tools-fsutils (>= 1.0)") isok(t, err) possi := dep.GetAllPossibilities()[0] assert(t, candidates.Satisfies(*arch, possi) == true) dep, err = dependency.Parse("android-tools-fsutils (>= 1:1.0)") isok(t, err) possi = dep.GetAllPossibilities()[0] assert(t, candidates.Satisfies(*arch, possi) == false) dep, err = dependency.Parse("android-tools-fsutils (<= 1:1.0)") isok(t, err) possi = dep.GetAllPossibilities()[0] assert(t, candidates.Satisfies(*arch, possi) == true) dep, err = dependency.Parse("android-tools-fsutils (<= 0:0)") isok(t, err) possi = dep.GetAllPossibilities()[0] assert(t, candidates.Satisfies(*arch, possi) == false) dep, err = dependency.Parse("android-tools-fsutils (= 4.2.2+git20130529-5.1)") isok(t, err) possi = dep.GetAllPossibilities()[0] assert(t, candidates.Satisfies(*arch, possi) == true) dep, err = dependency.Parse("android-tools-fsutils (= 2.2.2+git20130529-5.1)") isok(t, err) possi = dep.GetAllPossibilities()[0] assert(t, candidates.Satisfies(*arch, possi) == false) dep, err = dependency.Parse("android-tools-fsutils (<< 4.2.2+git20130529-5.1)") isok(t, err) possi = dep.GetAllPossibilities()[0] assert(t, candidates.Satisfies(*arch, possi) == false) dep, err = dependency.Parse("android-tools-fsutils (<< 4.2.2+git20130529-6.1)") isok(t, err) possi = dep.GetAllPossibilities()[0] assert(t, candidates.Satisfies(*arch, possi) == true) dep, err = dependency.Parse("android-tools-fsutils (>> 4.2.2+git20130529-5.1)") isok(t, err) possi = dep.GetAllPossibilities()[0] assert(t, candidates.Satisfies(*arch, possi) == false) dep, err = dependency.Parse("android-tools-fsutils (>> 4.2.2+git20130529-4.1)") isok(t, err) possi = dep.GetAllPossibilities()[0] assert(t, candidates.Satisfies(*arch, possi) == true) }
func TestDoubleInvalidNotArch(t *testing.T) { _, err := dependency.Parse("foo [arch !foo]") notok(t, err) _, err = dependency.Parse("foo [arch!foo]") notok(t, err) }
func TestMultiarchParse(t *testing.T) { dep, err := dependency.Parse("foo:amd64") isok(t, err) assert(t, dep.Relations[0].Possibilities[0].Name == "foo") assert(t, dep.Relations[0].Possibilities[0].Arch.CPU == "amd64") dep, err = dependency.Parse("foo:amd64 [amd64 sparc]") isok(t, err) assert(t, dep.Relations[0].Possibilities[0].Name == "foo") assert(t, dep.Relations[0].Possibilities[0].Arch.CPU == "amd64") assert(t, dep.Relations[0].Possibilities[0].Architectures.Architectures[0].CPU == "amd64") assert(t, dep.Relations[0].Possibilities[0].Architectures.Architectures[1].CPU == "sparc") }
func TestInsaneRoundTrip(t *testing.T) { dep, err := dependency.Parse("foo:armhf <stage1 !cross> [amd64 i386] (>= 1.2:3.4~5.6-7.8~9.0) <!stage1 cross>") isok(t, err) assert(t, dep.String() == "foo:armhf [amd64 i386] (>= 1.2:3.4~5.6-7.8~9.0) <stage1 !cross> <!stage1 cross>") rtDep, err := dependency.Parse(dep.String()) isok(t, err) assert(t, dep.String() == rtDep.String()) dep.Relations[0].Possibilities[0].Architectures.Not = true assert(t, dep.String() == "foo:armhf [!amd64 !i386] (>= 1.2:3.4~5.6-7.8~9.0) <stage1 !cross> <!stage1 cross>") rtDep, err = dependency.Parse(dep.String()) isok(t, err) assert(t, dep.String() == rtDep.String()) }
func (para *Paragraph) getOptionalDependencyField(field string) dependency.Dependency { val := para.Values[field] dep, err := dependency.Parse(val) if err != nil { return dependency.Dependency{} } return *dep }
func TestSingleParse(t *testing.T) { dep, err := dependency.Parse("foo") isok(t, err) if dep.Relations[0].Possibilities[0].Name != "foo" { t.Fail() } }
func TestResolverDependsVersion(t *testing.T) { candidates, err := resolver.ReadFromBinaryIndex( strings.NewReader(testBinaryIndex), ) isok(t, err) assert(t, len(*candidates) == 3) arch, err := dependency.ParseArch("amd64") isok(t, err) dep, err := dependency.Parse("android-tools-fsutils (>= 1.0)") isok(t, err) assert(t, candidates.SatisfiesBuildDepends(*arch, *dep) == true) dep, err = dependency.Parse("android-tools-fsutils (>= 1.0), quix") isok(t, err) assert(t, candidates.SatisfiesBuildDepends(*arch, *dep) == false) }
func TestVersioning(t *testing.T) { dep, err := dependency.Parse("foo (>= 1.0)") isok(t, err) assert(t, len(dep.Relations) == 1) possi := dep.Relations[0].Possibilities[0] version := possi.Version assert(t, version.Operator == ">=") assert(t, version.Number == "1.0") }
func TestResolverBasics(t *testing.T) { arch, err := dependency.ParseArch("amd64") isok(t, err) candidates, err := resolver.ReadFromBinaryIndex( strings.NewReader(testBinaryIndex), ) isok(t, err) assert(t, len(*candidates) == 3) dep, err := dependency.Parse("baz") isok(t, err) possi := dep.GetAllPossibilities()[0] assert(t, candidates.Satisfies(*arch, possi) == false) dep, err = dependency.Parse("android-tools-fsutils") isok(t, err) possi = dep.GetAllPossibilities()[0] assert(t, candidates.Satisfies(*arch, possi) == true) }
func TestTwoPossibilities(t *testing.T) { dep, err := dependency.Parse("foo, bar | baz") isok(t, err) assert(t, len(dep.Relations) == 2) possi := dep.Relations[1].Possibilities assert(t, len(possi) == 2) assert(t, possi[0].Name == "bar") assert(t, possi[1].Name == "baz") }
func TestSliceAllParse(t *testing.T) { dep, err := dependency.Parse("foo, bar | baz") isok(t, err) els := dep.GetAllPossibilities() assert(t, len(els) == 3) assert(t, els[0].Name == "foo") assert(t, els[1].Name == "bar") assert(t, els[2].Name == "baz") }
func TestSingleArch(t *testing.T) { dep, err := dependency.Parse("foo [arch]") isok(t, err) assert(t, len(dep.Relations) == 1) possi := dep.Relations[0].Possibilities[0] arches := possi.Architectures.Architectures assert(t, len(arches) == 1) assert(t, arches[0].CPU == "arch") }
func TestArchSliceParse(t *testing.T) { dep, err := dependency.Parse("foo, bar [sparc] | baz") isok(t, err) arch, err := dependency.ParseArch("amd64") isok(t, err) els := dep.GetPossibilities(*arch) assert(t, len(els) == 2) assert(t, els[0].Name == "foo") assert(t, els[1].Name == "baz") }
func TestSingleSubstvar(t *testing.T) { dep, err := dependency.Parse("${foo:Depends}, bar, baz") isok(t, err) assert(t, len(dep.Relations) == 3) assert(t, dep.Relations[0].Possibilities[0].Name == "foo:Depends") assert(t, dep.Relations[1].Possibilities[0].Name == "bar") assert(t, dep.Relations[2].Possibilities[0].Name == "baz") assert(t, dep.Relations[0].Possibilities[0].Substvar) assert(t, !dep.Relations[1].Possibilities[0].Substvar) assert(t, !dep.Relations[2].Possibilities[0].Substvar) }
func TestBadArch(t *testing.T) { vers := []string{ "foo [amd64", "foo [amd6", "foo [amd", "foo [am", "foo [a", "foo [", } for _, ver := range vers { _, err := dependency.Parse(ver) notok(t, err) } }
func TestBadVersion(t *testing.T) { vers := []string{ "foo (>= 1.0", "foo (>= 1", "foo (>= ", "foo (>=", "foo (>", "foo (", } for _, ver := range vers { _, err := dependency.Parse(ver) notok(t, err) } }
func TestSliceSubParse(t *testing.T) { dep, err := dependency.Parse("${foo:Depends}, foo, bar | baz, ${bar:Depends}") isok(t, err) els := dep.GetAllPossibilities() assert(t, len(els) == 3) assert(t, els[0].Name == "foo") assert(t, els[1].Name == "bar") assert(t, els[2].Name == "baz") els = dep.GetSubstvars() assert(t, len(els) == 2) assert(t, els[0].Name == "foo:Depends") assert(t, els[1].Name == "bar:Depends") }
func TestVersioningOperators(t *testing.T) { opers := map[string]string{ ">=": "foo (>= 1.0)", "<=": "foo (<= 1.0)", ">>": "foo (>> 1.0)", "<<": "foo (<< 1.0)", "=": "foo (= 1.0)", } for operator, vstring := range opers { dep, err := dependency.Parse(vstring) isok(t, err) assert(t, len(dep.Relations) == 1) possi := dep.Relations[0].Possibilities[0] version := possi.Version assert(t, version.Operator == operator) assert(t, version.Number == "1.0") } }
func TestArchSetCompare(t *testing.T) { dep, err := dependency.Parse("foo [amd64], bar [!sparc]") isok(t, err) iAm, err := dependency.ParseArch("amd64") isok(t, err) fooArch := dep.Relations[0].Possibilities[0].Architectures barArch := dep.Relations[1].Possibilities[0].Architectures assert(t, fooArch.Matches(iAm)) assert(t, barArch.Matches(iAm)) iAmNot, err := dependency.ParseArch("armhf") isok(t, err) assert(t, !fooArch.Matches(iAmNot)) assert(t, barArch.Matches(iAmNot)) }
func TestDoubleArch(t *testing.T) { for depStr, not := range map[string]bool{ "foo [arch arch2]": false, "foo [!arch !arch2]": true, } { dep, err := dependency.Parse(depStr) isok(t, err) assert(t, len(dep.Relations) == 1) possi := dep.Relations[0].Possibilities[0] arches := possi.Architectures.Architectures assert(t, possi.Architectures.Not == not) assert(t, len(arches) == 2) assert(t, arches[0].CPU == "arch") assert(t, arches[1].CPU == "arch2") } }
func TestTwoStages(t *testing.T) { dep, err := dependency.Parse("foo <stage1 !cross> <!stage1 cross>") isok(t, err) possi := dep.Relations[0].Possibilities[0] assert(t, len(possi.StageSets) == 2) // <stage1 !cross> assert(t, len(possi.StageSets[0].Stages) == 2) assert(t, !possi.StageSets[0].Stages[0].Not) assert(t, possi.StageSets[0].Stages[0].Name == "stage1") assert(t, possi.StageSets[0].Stages[1].Not) assert(t, possi.StageSets[0].Stages[1].Name == "cross") // <!stage1 cross> assert(t, len(possi.StageSets[1].Stages) == 2) assert(t, possi.StageSets[1].Stages[0].Not) assert(t, possi.StageSets[1].Stages[0].Name == "stage1") assert(t, !possi.StageSets[1].Stages[1].Not) assert(t, possi.StageSets[1].Stages[1].Name == "cross") }
func TestBadStages(t *testing.T) { vers := []string{ "foo <stage1> <!cross", "foo <stage1> <!cros", "foo <stage1> <!cro", "foo <stage1> <!cr", "foo <stage1> <!c", "foo <stage1> <!", "foo <stage1> <", "foo <stage1", "foo <stag", "foo <sta", "foo <st", "foo <s", "foo <", } for _, ver := range vers { _, err := dependency.Parse(ver) notok(t, err) } }
func TestTwoArchitectures(t *testing.T) { _, err := dependency.Parse("foo [amd64] [sparc]") notok(t, err) }
func TestTwoVersions(t *testing.T) { _, err := dependency.Parse("foo (>= 1.0) (<= 2.0)") notok(t, err) }
func TestNoComma(t *testing.T) { _, err := dependency.Parse("foo bar") notok(t, err) }
func TestTwoRelations(t *testing.T) { dep, err := dependency.Parse("foo, bar") isok(t, err) assert(t, len(dep.Relations) == 2) }
func (para *Paragraph) getDependencyField(field string) (*dependency.Dependency, error) { if val, ok := para.Values[field]; ok { return dependency.Parse(val) } return nil, fmt.Errorf("Field `%s' Missing", field) }
func (s Source) BuildDepends() (*dependency.Dependency, error) { return dependency.Parse(s.Paragraph.Values["Build-Depends"]) }
func buildBin(dscFile string) (control.DSC, string) { dscDir := filepath.Dir(dscFile) dsc, err := control.ParseDscFile(dscFile) if err != nil { log.Fatalf("error: %v\n", err) } if err := dsc.Validate(); err != nil { log.Fatalf("error, validation failed: %v\n", err) } hasArch := false hasIndep := false for _, arch := range dsc.Architectures { if arch.CPU == "all" { hasIndep = true } else { hasArch = true } } img := fmt.Sprintf("gdbuild/bin:%s_%s", dsc.Source, scrubForDockerTag(dsc.Version.String())) // TODO parse this information from an image? optional commandline parameters? suite := "unstable" sources := aptsources.DebianSources(suite, "main") arch := "amd64" // prepend incoming so we get the latest and greatest sources = sources.Prepend(aptsources.Source{ Types: []string{"deb", "deb-src"}, URIs: []string{"http://incoming.debian.org/debian-buildd"}, Suites: []string{"buildd-" + suite}, Components: []string{"main"}, }) index, err := sources.FetchCandidates(arch) if err != nil { log.Fatalf("error: %v\n", err) } depArch, err := dependency.ParseArch(arch) if err != nil { log.Fatalf("error: %v\n", err) } buildEssential, err := dependency.Parse("build-essential, dpkg-dev, fakeroot") if err != nil { log.Fatalf("error: %v\n", err) } allCan := true bins := map[string]control.BinaryIndex{} binsSlice := []string{} // ugh Go RelLoop: for _, rel := range append(buildEssential.Relations, dsc.BuildDepends.Relations...) { canRel := false for _, possi := range rel.Possibilities { if possi.Substvar { continue } if bin, ok := bins[possi.Name]; ok && binSatPossi(depArch, bin, possi) { continue RelLoop } } PossiLoop: for _, possi := range rel.Possibilities { if possi.Substvar { continue } entries, ok := map[string][]control.BinaryIndex(*index)[possi.Name] if !ok { continue } for _, bin := range entries { if binSatPossi(depArch, bin, possi) { if existBin, ok := bins[bin.Package]; ok { log.Printf("uh oh, already chose %s=%s but want %s=%s for %q\n", existBin.Package, existBin.Version, bin.Package, bin.Version, possi) continue PossiLoop } bins[bin.Package] = bin binsSlice = append(binsSlice, bin.Package) canRel = true break PossiLoop } } } if !canRel { log.Printf("warning: unable to satisfy %q\n", rel) allCan = false } } sort.Strings(binsSlice) if !allCan { //log.Fatalf("Unsatisfied possi; exiting.\n") log.Println() log.Println("WARNING: Unsatisfied possi!") log.Println() } dockerfile := fmt.Sprintf("FROM debian:%s\n", suite) // TODO allow this to instead be "FROM scratch\nADD some-chroot-tarball.tar.* /\n" // see https://sources.debian.net/src/pbuilder/jessie/pbuilder-modules/#L306 // and https://sources.debian.net/src/pbuilder/jessie/pbuilder-modules/#L408 dockerfile += ` # setup environment configuration RUN { echo '#!/bin/sh'; echo 'exit 101'; } > /usr/sbin/policy-rc.d \ && chmod +x /usr/sbin/policy-rc.d RUN echo 'APT::Install-Recommends "false";' > /etc/apt/apt.conf.d/15gdbuild # put /tmp in a volume so it's always ephemeral (and performant) VOLUME /tmp ` // setup sources.list explicitly -- don't trust the tarball/base image dockerfile += fmt.Sprintf(` # setup sources.list RUN find /etc/apt/sources.list.d -type f -exec rm -v '{}' + \ && echo %q | tee /etc/apt/sources.list >&2 `, sources.ListString()) eatMyDataPrefix := "" if doEatMyData { eatMyDataPrefix = "eatmydata " dockerfile += ` RUN apt-get update && apt-get install -y \ eatmydata \ && rm -rf /var/lib/apt/lists/* ` } dockerfile += fmt.Sprintf(` RUN %sapt-get update && %sapt-get install -y \ `, eatMyDataPrefix, eatMyDataPrefix) for _, pkg := range binsSlice { bin := bins[pkg] dockerfile += fmt.Sprintf("\t\t%s=%s \\\n", bin.Package, bin.Version) } dockerfile += "\t&& rm -rf /var/lib/apt/lists/*\n" files := []string{dsc.Filename} for _, f := range dsc.Files { files = append(files, filepath.Join(dscDir, f.Filename)) } dockerfile += "COPY" for _, f := range files { dockerfile += " " + filepath.Base(f) } dockerfile += " /usr/src/.in/\n" if debBuildOptions := os.Getenv("DEB_BUILD_OPTIONS"); debBuildOptions != "" { dockerfile += fmt.Sprintf("\nENV DEB_BUILD_OPTIONS %s\n", debBuildOptions) } buildCommand := fmt.Sprintf("%sdpkg-buildpackage -uc -us -d -sa", eatMyDataPrefix) if doSASBS { buildCommandParts := []string{} if hasIndep { buildCommandParts = append( buildCommandParts, buildCommand+" -S", buildCommand+" -A", ) } if hasArch { buildCommandParts = append( buildCommandParts, buildCommand+" -S", buildCommand+" -B", ) } buildCommandParts = append( buildCommandParts, buildCommand+" -S", // end with a full build of "ALL THE THINGS" so we have good, consistent .changes and .dsc "rm ../*.changes ../*.dsc ../*.deb", buildCommand, ) buildCommand = strings.Join(buildCommandParts, " && ") } dockerfile += fmt.Sprintf(` WORKDIR /usr/src RUN chown -R nobody:nogroup . USER nobody:nogroup RUN dpkg-source -x %q pkg # work around overlayfs bugs (data inconsistency issues; see https://github.com/docker/docker/issues/10180) VOLUME /usr/src/pkg # rm: cannot remove 'pkg/.pc/xyz.patch': Directory not empty RUN (cd pkg && set -x && %s) \ && mkdir .out \ && { \ echo *.changes; \ awk '$1 == "Files:" { files = 1; next } /^ / && files { print $5 } /^[^ ]/ { files = 0 }' *.changes; \ echo .out/; \ } | xargs ln -v `, ".in/"+filepath.Base(dsc.Filename), buildCommand) err = dockerBuild(img, dockerfile, files...) if err != nil { log.Fatalf("error: %v\n", err) } return *dsc, img }