func buildSrc(dir string) (control.Control, changelog.ChangelogEntry, string) { con, err := control.ParseControlFile(filepath.Join(dir, "debian/control")) if err != nil { log.Fatalf("error: %v\n", err) } chg, err := changelog.ParseFileOne(filepath.Join(dir, "debian/changelog")) if err != nil { log.Fatalf("error: %v\n", err) } img := fmt.Sprintf("gdbuild/src:%s_%s", con.Source.Source, scrubForDockerTag(chg.Version.String())) dockerfile := "FROM debian:unstable\n" // TODO allow this to instead be "FROM scratch\nADD some-chroot-tarball.tar.* /\n" dockerfile += ` RUN apt-get update && apt-get install -y --no-install-recommends \ dpkg-dev \ && rm -rf /var/lib/apt/lists/* WORKDIR /usr/src ` files := []string{} if !chg.Version.IsNative() { origBase := fmt.Sprintf("%s_%s.orig", con.Source.Source, chg.Version.Version) origs := []string{} for _, tarballDir := range tarballDirs { if !filepath.IsAbs(tarballDir) { tarballDir = filepath.Join(dir, tarballDir) } tarballs, err := filepath.Glob(filepath.Join(tarballDir, origBase+".tar.*")) if err != nil { log.Fatalf("error: %v\n", err) } if len(tarballs) > 0 { if len(tarballs) > 1 { log.Fatalf("error: found multiple base orig tarballs: %v\n", tarballs) } orig := tarballs[0] origs = append(origs, orig) tarballs, err = filepath.Glob(filepath.Join(tarballDir, origBase+"-*.tar.*")) if err != nil { log.Fatalf("error: %v\n", err) } origs = append(origs, tarballs...) break } } if len(origs) < 1 { log.Fatalf("error: unable to find orig tarball(s); searched for %s in %v\n", origBase+".tar.*", tarballDirs) } files = append(files, origs...) files = append(files, filepath.Join(dir, "debian")) dockerfile += "COPY" for _, f := range origs { dockerfile += " " + filepath.Base(f) } dockerfile += " /usr/src/.out/\n" dockerfile += fmt.Sprintf(`RUN set -ex \ && for f in .out/%q.tar.* .out/%q-*.tar.*; do \ if [ -e "$f" ]; then \ ln -s "$f" ./; \ fi; \ done `, origBase, origBase) dockerfile += "\n# origtargz --unpack\n" re := regexp.MustCompile(fmt.Sprintf(`^%s(?:-(.*))?\.tar\..*$`, regexp.QuoteMeta(origBase))) dockerfile += "RUN set -ex" for _, f := range origs { orig := filepath.Base(f) targetDir := "pkg" matches := re.FindStringSubmatch(orig) if matches != nil && matches[1] != "" { targetDir += "/" + matches[1] } dockerfile += fmt.Sprintf(" \\\n\t&& mkdir %q && tar -xC %q -f %q --strip-components=1", targetDir, targetDir, orig) } dockerfile += " \\\n\t&& rm -rf /usr/src/pkg/debian\n" dockerfile += "\nCOPY debian /usr/src/pkg/debian\n" } else { absDir, err := filepath.Abs(dir) if err != nil { log.Fatalf("error: %v\n", err) } files = append(files, absDir) dockerfile += fmt.Sprintf("COPY %s /usr/src/pkg\n", filepath.Base(absDir)) } outVer := chg.Version outVer.Epoch = 0 pkgVer := con.Source.Source + "_" + outVer.String() links := fmt.Sprintf("%q %q", pkgVer+".dsc", pkgVer+"_source.changes") if chg.Version.IsNative() { links += fmt.Sprintf(" %q.tar.*", pkgVer) } else { links += fmt.Sprintf(" %q.debian.tar.*", pkgVer) links += fmt.Sprintf(" %q.diff.*", pkgVer) } dockerfile += fmt.Sprintf(` # 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 && dpkg-buildpackage -uc -us -S -nc) \ && mkdir -p .out \ && set -ex \ && for f in %s; do \ if [ -e "$f" ]; then \ ln "$f" .out/; \ fi; \ done `, links) err = dockerBuild(img, dockerfile, files...) if err != nil { log.Fatalf("error: %v\n", err) } return *con, *chg, img }
func main() { log.SetFlags(log.Lshortfile) // TODO configurable path? perhaps allow for an optional *.dsc instead? con, err := control.ParseControlFile("debian/control") if err != nil { log.Fatalf("error: %v\n", err) } chg, err := changelog.ParseFileOne("debian/changelog") if err != nil { log.Fatalf("error: %v\n", err) } // TODO configurable or something to avoid guesswork targetSuite := chg.Target if targetSuite == "UNRELEASED" { // check for "Upload to XYZ." or "Rebuild for XYZ." in changelog re := regexp.MustCompile(`^\s*\*?\s*(Upload\s+to|Rebuild\s+for)\s+(\S+?)\.?(\s+|$)`) matches := re.FindStringSubmatch(chg.Changelog) if matches != nil { targetSuite = matches[2] } else { targetSuite = "unstable" } } // TODO configurable (or auto-sensed from the mirror and/or package source) arches := []string{"amd64", "i386"} components := []string{"main", "contrib", "non-free"} fmt.Printf("Target: %s (%s)\n", targetSuite, chg.Target) fmt.Printf("Architectures: %s\n", strings.Join(arches, " ")) fmt.Printf("Components: %s\n", strings.Join(components, " ")) fmt.Printf("Source: %s\n", con.Source.Source) fmt.Printf("Version: %s\n", chg.Version) fmt.Printf("\n") indexSources := aptsources.DebianSources(targetSuite, components...) index, err := indexSources.FetchCandidates(arches...) if err != nil { log.Fatalf("error: %v\n", err) } incoming := NewTarget( "http://incoming.debian.org/debian-buildd", []string{"buildd-" + targetSuite}, components, arches, ) if err = incoming.Fetch(); err != nil { log.Fatalf("error: %v\n", err) } newQueue, err := dnew.ParseNewUrl(dnew.New822) if err != nil { log.Fatalf("error: %v\n", err) } newBinaries := map[string]dnew.NewEntry{} for _, newPkg := range newQueue { for _, newBin := range newPkg.Binary { newBinaries[newBin] = newPkg } } allDeps := dependency.Dependency{} binRelation := dependency.Relation{} for _, bin := range con.Binaries { binRelation.Possibilities = append(binRelation.Possibilities, dependency.Possibility{ Name: bin.Package, Version: &dependency.VersionRelation{ Operator: "=", Number: chg.Version.String(), }, }) } allDeps.Relations = append(allDeps.Relations, binRelation) allDeps.Relations = append(allDeps.Relations, con.Source.BuildDepends.Relations...) allDeps.Relations = append(allDeps.Relations, con.Source.BuildDependsIndep.Relations...) for _, bin := range con.Binaries { allDeps.Relations = append(allDeps.Relations, bin.Depends.Relations...) allDeps.Relations = append(allDeps.Relations, bin.Recommends.Relations...) allDeps.Relations = append(allDeps.Relations, bin.Suggests.Relations...) allDeps.Relations = append(allDeps.Relations, bin.Enhances.Relations...) allDeps.Relations = append(allDeps.Relations, bin.PreDepends.Relations...) } depArch, err := dependency.ParseArch("any") if err != nil { log.Fatalf("error: %v\n", err) } seenRelations := map[string]bool{} for _, relation := range allDeps.Relations { relationString := relation.String() if seenRelations[relationString] { continue } seenRelations[relationString] = true oneCan := false notes := []string{} for _, possi := range relation.Possibilities { if possi.Substvar { //fmt.Printf("ignoring substvar %s\n", possi) continue } can, why, _ := index.ExplainSatisfies(*depArch, possi) if !can { inCan, _, incomingBins := incoming.ExplainSatisfies(*depArch, possi) if !inCan { if newPkg, ok := newBinaries[possi.Name]; ok { newUrl := fmt.Sprintf("https://ftp-master.debian.org/new/%s_%s.html", newPkg.Source, newPkg.Version[0]) notes = append(notes, fmt.Sprintf("NEW (%s): %s", possi.Name, newUrl)) } else { notes = append(notes, why) } } else { notes = append(notes, fmt.Sprintf("incoming (%s): %s", possi.Name, incoming.UrlTo(incomingBins[0]))) } } else { oneCan = true // TODO figure out how we can incorporate this ("Section: oldlibs" from debian/control doesn't propagate to the Packages file on the mirror, so we'd have to parse the .deb itself to get this info, which is fairly untenable) /* // NOTE "bins" is the last return value in the call to "index.ExplainSatisfies" above for _, bin := range bins { if bin.Section == "oldlibs" { oneCan = false notes = append(notes, fmt.Sprintf(`%s (%s) is "Section: oldlibs", which suggests it is likely transitional`, bin.Package, bin.Version.String())) } } */ } } if ignoreRelationSecondaryFails && oneCan { continue } if len(notes) > 0 { fmt.Printf("Relation: %s\n", relation) if len(notes) > 1 { fmt.Printf("Notes:\n %s\n", strings.Join(notes, "\n ")) } else { fmt.Printf("Notes: %s\n", notes[0]) } fmt.Printf("\n") } } }