| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 | package herculesimport (	"fmt"	"reflect"	"sort"	"strings"	"unsafe"	"github.com/spf13/pflag")// PipelineItemRegistry contains all the known PipelineItem-s.type PipelineItemRegistry struct {	provided     map[string][]reflect.Type	registered   map[string]reflect.Type	flags        map[string]reflect.Type	featureFlags arrayFeatureFlags}// Register adds another PipelineItem to the registry.func (registry *PipelineItemRegistry) Register(example PipelineItem) {	t := reflect.TypeOf(example)	registry.registered[example.Name()] = t	if fpi, ok := example.(LeafPipelineItem); ok {		registry.flags[fpi.Flag()] = t	}	for _, dep := range example.Provides() {		ts := registry.provided[dep]		if ts == nil {			ts = []reflect.Type{}		}		ts = append(ts, t)		registry.provided[dep] = ts	}}// Summon searches for PipelineItem-s which provide the specified entity or named after// the specified string. It materializes all the found types and returns them.func (registry *PipelineItemRegistry) Summon(providesOrName string) []PipelineItem {	if registry.provided == nil {		return []PipelineItem{}	}	ts := registry.provided[providesOrName]	items := []PipelineItem{}	for _, t := range ts {		items = append(items, reflect.New(t.Elem()).Interface().(PipelineItem))	}	if t, exists := registry.registered[providesOrName]; exists {		items = append(items, reflect.New(t.Elem()).Interface().(PipelineItem))	}	return items}// GetLeaves returns all LeafPipelineItem-s registered.func (registry *PipelineItemRegistry) GetLeaves() []LeafPipelineItem {	keys := []string{}	for key := range registry.flags {		keys = append(keys, key)	}	sort.Strings(keys)	items := []LeafPipelineItem{}	for _, key := range keys {		items = append(items, reflect.New(registry.flags[key].Elem()).Interface().(LeafPipelineItem))	}	return items}// GetPlumbingItems returns all non-LeafPipelineItem-s registered.func (registry *PipelineItemRegistry) GetPlumbingItems() []PipelineItem {	keys := []string{}	for key := range registry.registered {		keys = append(keys, key)	}	sort.Strings(keys)	items := []PipelineItem{}	for _, key := range keys {		iface := reflect.New(registry.registered[key].Elem()).Interface()		if _, ok := iface.(LeafPipelineItem); !ok {			items = append(items, iface.(PipelineItem))		}	}	return items}type orderedFeaturedItems []FeaturedPipelineItemfunc (ofi orderedFeaturedItems) Len() int {	return len([]FeaturedPipelineItem(ofi))}func (ofi orderedFeaturedItems) Less(i, j int) bool {	cofi := []FeaturedPipelineItem(ofi)	return cofi[i].Name() < cofi[j].Name()}func (ofi orderedFeaturedItems) Swap(i, j int) {	cofi := []FeaturedPipelineItem(ofi)	cofi[i], cofi[j] = cofi[j], cofi[i]}// GetFeaturedItems returns all FeaturedPipelineItem-s registered.func (registry *PipelineItemRegistry) GetFeaturedItems() map[string][]FeaturedPipelineItem {	features := map[string][]FeaturedPipelineItem{}	for _, t := range registry.registered {		if fiface, ok := reflect.New(t.Elem()).Interface().(FeaturedPipelineItem); ok {			for _, f := range fiface.Features() {				list := features[f]				if list == nil {					list = []FeaturedPipelineItem{}				}				list = append(list, fiface)				features[f] = list			}		}	}	for _, vals := range features {		sort.Sort(orderedFeaturedItems(vals))	}	return features}type arrayFeatureFlags struct {	// Flags contains the features activated through the command line.	Flags []string	// Choices contains all registered features.	Choices map[string]bool}func (acf *arrayFeatureFlags) String() string {	return strings.Join([]string(acf.Flags), ", ")}func (acf *arrayFeatureFlags) Set(value string) error {	if _, exists := acf.Choices[value]; !exists {		return fmt.Errorf("feature \"%s\" is not registered", value)	}	acf.Flags = append(acf.Flags, value)	return nil}func (acf *arrayFeatureFlags) Type() string {	return "string"}// AddFlags inserts the cmdline options from PipelineItem.ListConfigurationOptions(),// FeaturedPipelineItem().Features() and LeafPipelineItem.Flag() into the global "flag" parser// built into the Go runtime.// Returns the "facts" which can be fed into PipelineItem.Configure() and the dictionary of// runnable analysis (LeafPipelineItem) choices. E.g. if "BurndownAnalysis" was activated// through "-burndown" cmdline argument, this mapping would contain ["BurndownAnalysis"] = *true.func (registry *PipelineItemRegistry) AddFlags(flagSet *pflag.FlagSet) (	map[string]interface{}, map[string]*bool) {	flags := map[string]interface{}{}	deployed := map[string]*bool{}	for name, it := range registry.registered {		formatHelp := func(desc string) string {			return fmt.Sprintf("%s [%s]", desc, name)		}		itemIface := reflect.New(it.Elem()).Interface()		for _, opt := range itemIface.(PipelineItem).ListConfigurationOptions() {			var iface interface{}			getPtr := func() unsafe.Pointer {				return unsafe.Pointer(uintptr(unsafe.Pointer(&iface)) + unsafe.Sizeof(&iface))			}			switch opt.Type {			case BoolConfigurationOption:				iface = interface{}(true)				ptr := (**bool)(getPtr())				*ptr = flagSet.Bool(opt.Flag, opt.Default.(bool), formatHelp(opt.Description))			case IntConfigurationOption:				iface = interface{}(0)				ptr := (**int)(getPtr())				*ptr = flagSet.Int(opt.Flag, opt.Default.(int), formatHelp(opt.Description))			case StringConfigurationOption:				iface = interface{}("")				ptr := (**string)(getPtr())				*ptr = flagSet.String(opt.Flag, opt.Default.(string), formatHelp(opt.Description))			case FloatConfigurationOption:				iface = interface{}(float32(0))				ptr := (**float32)(getPtr())				*ptr = flagSet.Float32(opt.Flag, opt.Default.(float32), formatHelp(opt.Description))			case StringsConfigurationOption:				iface = interface{}([]string{})				ptr := (**[]string)(getPtr())				*ptr = flagSet.StringSlice(opt.Flag, opt.Default.([]string), formatHelp(opt.Description))			}			flags[opt.Name] = iface		}		if fpi, ok := itemIface.(FeaturedPipelineItem); ok {			for _, f := range fpi.Features() {				registry.featureFlags.Choices[f] = true			}		}		if fpi, ok := itemIface.(LeafPipelineItem); ok {			deployed[fpi.Name()] = flagSet.Bool(				fpi.Flag(), false, fmt.Sprintf("Runs %s analysis.", fpi.Name()))		}	}	{		// Pipeline flags		iface := interface{}("")		ptr1 := (**string)(unsafe.Pointer(uintptr(unsafe.Pointer(&iface)) + unsafe.Sizeof(&iface)))		*ptr1 = flagSet.String("dump-dag", "", "Write the pipeline DAG to a Graphviz file.")		flags[ConfigPipelineDumpPath] = iface		iface = interface{}(true)		ptr2 := (**bool)(unsafe.Pointer(uintptr(unsafe.Pointer(&iface)) + unsafe.Sizeof(&iface)))		*ptr2 = flagSet.Bool("dry-run", false, "Do not run any analyses - only resolve the DAG. "+			"Useful for -dump-dag.")		flags[ConfigPipelineDryRun] = iface	}	features := []string{}	for f := range registry.featureFlags.Choices {		features = append(features, f)	}	flagSet.Var(®istry.featureFlags, "feature",		fmt.Sprintf("Enables the items which depend on the specified features. Can be specified "+			"multiple times. Available features: [%s] (see --feature below).",			strings.Join(features, ", ")))	return flags, deployed}// Registry contains all known pipeline item types.var Registry = &PipelineItemRegistry{	provided:     map[string][]reflect.Type{},	registered:   map[string]reflect.Type{},	flags:        map[string]reflect.Type{},	featureFlags: arrayFeatureFlags{Flags: []string{}, Choices: map[string]bool{}},}
 |