func TestTopsortLongCycle(t *testing.T) { network := topsort.NewNetwork() network.AddNode("A", nil) network.AddNode("B", nil) network.AddNode("C", nil) network.AddNode("D", nil) network.AddNode("E", nil) network.AddNode("F", nil) network.AddNode("G", nil) network.AddNode("H", nil) network.AddNode("I", nil) network.AddEdge("A", "B") network.AddEdge("B", "C") network.AddEdge("C", "D") network.AddEdge("D", "E") network.AddEdge("E", "F") network.AddEdge("F", "G") network.AddEdge("G", "H") network.AddEdge("H", "I") network.AddEdge("I", "A") _, err := network.Sort() notok(t, err) }
func TestTopsortLong(t *testing.T) { /* A --> B -> C -> D -> E \ ^ \-> F */ network := topsort.NewNetwork() network.AddNode("A", nil) network.AddNode("B", nil) network.AddNode("C", nil) network.AddNode("D", nil) network.AddNode("E", nil) network.AddNode("F", nil) network.AddEdge("A", "B") network.AddEdge("A", "F") network.AddEdge("F", "B") network.AddEdge("B", "C") network.AddEdge("C", "D") network.AddEdge("D", "E") series, err := network.Sort() isok(t, err) assert(t, len(series) == 6) assert(t, series[0].Name == "A") assert(t, series[1].Name == "F") assert(t, series[2].Name == "B") assert(t, series[3].Name == "C") }
func TestTopsortCycle(t *testing.T) { network := topsort.NewNetwork() network.AddNode("foo", nil) network.AddNode("bar", nil) network.AddEdge("foo", "bar") network.AddEdge("bar", "foo") _, err := network.Sort() notok(t, err) }
func TestTopsortEasy(t *testing.T) { network := topsort.NewNetwork() network.AddNode("foo", nil) network.AddNode("bar", nil) network.AddEdge("foo", "bar") series, err := network.Sort() isok(t, err) assert(t, len(series) == 2) }
func newAlphaNetwork(start, end rune) *topsort.Network { network := topsort.NewNetwork() for c := start; c <= end; c++ { network.AddNode(string(c), nil) } return network }
func sortRepos(repos []string) ([]string, error) { if noSortFlag || len(repos) <= 1 { return repos, nil } network := topsort.NewNetwork() rs := []*Repo{} for _, repo := range repos { r, err := fetch(repo) if err != nil { return nil, err } rs = append(rs, r) network.AddNode(r.Identifier(), repo) network.AddNode(r.RepoName, repo) } for _, r := range rs { for _, entry := range r.Entries() { from, err := r.DockerFrom(&entry) if err != nil { return nil, err } if i := strings.IndexRune(from, ':'); i >= 0 { // we want "repo -> repo" relations, no tags from = from[:i] } if from == r.RepoName { // "a:a -> a:b" is OK (ignore that here -- see Repo.SortedEntries for that) continue } // TODO somehow reconcile/avoid "a:a -> b:b, b:b -> a:c" (which will exhibit here as cyclic) network.AddEdgeIfExists(from, r.Identifier()) network.AddEdgeIfExists(from, r.RepoName) } } nodes, err := network.Sort() if err != nil { return nil, err } ret := []string{} seen := map[string]bool{} for _, node := range nodes { repo := node.Value.(string) if seen[repo] { continue } seen[repo] = true ret = append(ret, repo) } return ret, nil }
func (r Repo) SortedEntries() ([]manifest.Manifest2822Entry, error) { entries := r.Entries() if noSortFlag || len(entries) <= 1 { return entries, nil } network := topsort.NewNetwork() for i, entry := range entries { for _, tag := range r.Tags("", false, entry) { network.AddNode(tag, &entries[i]) } } for _, entry := range entries { from, err := r.DockerFrom(&entry) if err != nil { return nil, err } for _, tag := range r.Tags("", false, entry) { network.AddEdgeIfExists(from, tag) } } nodes, err := network.Sort() if err != nil { return nil, err } seen := map[*manifest.Manifest2822Entry]bool{} ret := []manifest.Manifest2822Entry{} for _, node := range nodes { entry := node.Value.(*manifest.Manifest2822Entry) if seen[entry] { // TODO somehow reconcile "a:a -> b:b, b:b -> a:c" continue } ret = append(ret, *entry) seen[entry] = true } return ret, nil }
// Given a bunch of DSC objects, sort the packages topologically by // build order by looking at the relationship between the Build-Depends // field. func OrderDSCForBuild(dscs []DSC, arch dependency.Arch) ([]DSC, error) { sourceMapping := map[string]string{} network := topsort.NewNetwork() ret := []DSC{} /* * - Create binary -> source mapping. * - Error if two sources provide the same binary * - Create a node for each source * - Create an edge from the source -> source * - return sorted list of dsc files */ for _, dsc := range dscs { for _, binary := range dsc.Binaries { sourceMapping[binary] = dsc.Source } network.AddNode(dsc.Source, dsc) } for _, dsc := range dscs { concreteBuildDepends := dsc.BuildDepends.GetPossibilities(arch) for _, relation := range concreteBuildDepends { if val, ok := sourceMapping[relation.Name]; ok { err := network.AddEdge(val, dsc.Source) if err != nil { return nil, err } } } } nodes, err := network.Sort() if err != nil { return nil, err } for _, node := range nodes { ret = append(ret, node.Value.(DSC)) } return ret, nil }
func sortRepoObjects(rs []*Repo) ([]*Repo, error) { // short circuit if we don't have to go further if noSortFlag || len(rs) <= 1 { return rs, nil } network := topsort.NewNetwork() // a map of alternate tag names to the canonical "node name" for topsort purposes canonicalNodes := map[string]string{} canonicalRepos := map[string]*Repo{} for _, r := range rs { node := r.Identifier() for _, entry := range r.Entries() { for _, tag := range r.Tags("", false, entry) { if canonicalRepo, ok := canonicalRepos[tag]; ok && canonicalRepo.TagName != "" { // if we run into a duplicate, we want to prefer a specific tag over a full repo continue } canonicalNodes[tag] = node canonicalRepos[tag] = r } } network.AddNode(node, r) } for _, r := range rs { for _, entry := range r.Entries() { from, err := r.DockerFrom(&entry) if err != nil { return nil, err } from = latestizeRepoTag(from) fromNode, ok := canonicalNodes[from] if !ok { // if our FROM isn't in the list of things we're sorting, it isn't relevant in this context continue } // TODO somehow reconcile/avoid "a:a -> b:b, b:b -> a:c" (which will exhibit here as cyclic) for _, tag := range r.Tags("", false, entry) { if tagNode, ok := canonicalNodes[tag]; ok { if tagNode == fromNode { // don't be cyclic continue } if err := network.AddEdge(fromNode, tagNode); err != nil { return nil, err } } } } } nodes, err := network.Sort() if err != nil { return nil, err } ret := []*Repo{} for _, node := range nodes { ret = append(ret, node.Value.(*Repo)) } return ret, nil }
func cmdFamily(parents bool, c *cli.Context) error { depsRepos, err := repos(c.Bool("all"), c.Args()...) if err != nil { return cli.NewMultiError(fmt.Errorf(`failed gathering repo list`), err) } uniq := c.Bool("uniq") applyConstraints := c.Bool("apply-constraints") allRepos, err := repos(true) if err != nil { return cli.NewMultiError(fmt.Errorf(`failed gathering ALL repos list`), err) } // create network (all repos) network := topsort.NewNetwork() // add nodes for _, repo := range allRepos { r, err := fetch(repo) if err != nil { return cli.NewMultiError(fmt.Errorf(`failed fetching repo %q`, repo), err) } for _, entry := range r.Entries() { if applyConstraints && r.SkipConstraints(entry) { continue } for _, tag := range r.Tags("", false, entry) { network.AddNode(tag, entry) } } } // add edges for _, repo := range allRepos { r, err := fetch(repo) if err != nil { return cli.NewMultiError(fmt.Errorf(`failed fetching repo %q`, repo), err) } for _, entry := range r.Entries() { if applyConstraints && r.SkipConstraints(entry) { continue } from, err := r.DockerFrom(&entry) if err != nil { return cli.NewMultiError(fmt.Errorf(`failed fetching/scraping FROM for %q (tags %q)`, r.RepoName, entry.TagsString()), err) } for _, tag := range r.Tags("", false, entry) { network.AddEdge(from, tag) } } } // now the real work seen := map[*topsort.Node]bool{} for _, repo := range depsRepos { r, err := fetch(repo) if err != nil { return cli.NewMultiError(fmt.Errorf(`failed fetching repo %q`, repo), err) } for _, entry := range r.Entries() { if applyConstraints && r.SkipConstraints(entry) { continue } for _, tag := range r.Tags("", uniq, entry) { nodes := []*topsort.Node{} if parents { nodes = append(nodes, network.Get(tag).InboundEdges...) } else { nodes = append(nodes, network.Get(tag).OutboundEdges...) } for len(nodes) > 0 { node := nodes[0] nodes = nodes[1:] if seen[node] { continue } seen[node] = true fmt.Printf("%s\n", node.Name) if parents { nodes = append(nodes, node.InboundEdges...) } else { nodes = append(nodes, node.OutboundEdges...) } } } } } return nil }