Beispiel #1
0
func (m *Generator) generateToolSourceCode(pkg *codegen.Package) {
	file := pkg.CreateSourceFile("main.go")
	imports := append(m.Imports,
		codegen.SimpleImport("fmt"),
		codegen.SimpleImport("os"),
		codegen.SimpleImport("strings"),
		codegen.SimpleImport("github.com/goadesign/goa/dslengine"),
		codegen.NewImport("_", filepath.ToSlash(codegen.DesignPackagePath)),
	)
	file.WriteHeader("Code Generator", "main", imports)
	tmpl, err := template.New("generator").Parse(mainTmpl)
	if err != nil {
		panic(err) // bug
	}
	pkgName, err := codegen.PackageName(pkg.Abs())
	if err != nil {
		panic(err)
	}
	context := map[string]string{
		"Genfunc":       m.Genfunc,
		"DesignPackage": codegen.DesignPackagePath,
		"PkgName":       pkgName,
	}
	err = tmpl.Execute(file, context)
	if err != nil {
		panic(err) // bug
	}
}
Beispiel #2
0
// Generate compiles and runs the generator and returns the generated filenames.
func (m *Generator) Generate() ([]string, error) {
	if codegen.OutputDir == "" {
		return nil, fmt.Errorf("missing output directory specification")
	}

	if codegen.DesignPackagePath == "" {
		return nil, fmt.Errorf("missing design package path specification")
	}

	if os.Getenv("GOPATH") == "" {
		return nil, fmt.Errorf("GOPATH not set")
	}

	// Create output directory
	if err := os.MkdirAll(codegen.OutputDir, 0755); err != nil {
		return nil, err
	}

	// Create temporary workspace used for generation
	w, err := codegen.NewWorkspace("goagen")
	if err != nil {
		if _, ok := err.(*os.PathError); ok {
			err = fmt.Errorf(`invalid output directory path "%s"`, codegen.OutputDir)
		}
		return nil, err
	}
	defer func() {
		if !codegen.Debug {
			w.Delete()
		}
	}()
	if codegen.Debug {
		fmt.Printf("goagen source dir: %s\n", w.Path)
	}

	// Figure out design package name from its path
	path, err := codegen.PackageSourcePath(codegen.DesignPackagePath)
	if err != nil {
		return nil, err
	}
	pkgName, err := codegen.PackageName(path)
	if err != nil {
		return nil, err
	}

	// Generate tool source code.
	p, err := w.NewPackage(pkgName)
	if err != nil {
		return nil, err
	}
	m.generateToolSourceCode(p)

	// Compile and run generated tool.
	genbin, err := p.Compile("goagen")
	if err != nil {
		return nil, err
	}
	return m.spawn(genbin)
}
Beispiel #3
0
func runGen(c *cobra.Command) ([]string, error) {
	pkgPath := c.Flag("pkg-path").Value.String()
	pkgSrcPath, err := codegen.PackageSourcePath(pkgPath)
	if err != nil {
		return nil, fmt.Errorf("invalid plugin package import path: %s", err)
	}
	pkgName, err := codegen.PackageName(pkgSrcPath)
	if err != nil {
		return nil, fmt.Errorf("invalid plugin package import path: %s", err)
	}
	return generate(pkgName, pkgPath, c)
}
Beispiel #4
0
func run(pkg string, c *cobra.Command) ([]string, error) {
	pkgPath := fmt.Sprintf("github.com/goadesign/goa/goagen/gen_%s", pkg[3:])
	pkgSrcPath, err := codegen.PackageSourcePath(pkgPath)
	if err != nil {
		return nil, fmt.Errorf("invalid plugin package import path: %s", err)
	}
	pkgName, err := codegen.PackageName(pkgSrcPath)
	if err != nil {
		return nil, fmt.Errorf("invalid package import path: %s", err)
	}
	return generate(pkgName, pkgPath, c)
}
Beispiel #5
0
// BuildEncoders builds the template data needed to render the given encoding definitions.
// This extra map is needed to handle the case where a single encoding definition maps to multiple
// encoding packages. The data is indexed by mime type.
func BuildEncoders(info []*design.EncodingDefinition, encoder bool) ([]*EncoderTemplateData, error) {
	if len(info) == 0 {
		return nil, nil
	}
	// knownStdPackages lists the stdlib packages known by BuildEncoders
	var knownStdPackages = map[string]string{
		"encoding/json": "json",
		"encoding/xml":  "xml",
		"encoding/gob":  "gob",
	}
	encs := normalizeEncodingDefinitions(info)
	data := make([]*EncoderTemplateData, len(encs))
	defaultMediaType := info[0].MIMETypes[0]
	for i, enc := range encs {
		var pkgName string
		if name, ok := knownStdPackages[enc.PackagePath]; ok {
			pkgName = name
		} else {
			srcPath, err := codegen.PackageSourcePath(enc.PackagePath)
			if err != nil {
				return nil, fmt.Errorf("failed to locate package source of %s (%s)",
					enc.PackagePath, err)
			}
			pkgName, err = codegen.PackageName(srcPath)
			if err != nil {
				return nil, fmt.Errorf("failed to load package %s (%s)",
					enc.PackagePath, err)
			}
		}
		isDefault := false
		for _, m := range enc.MIMETypes {
			if m == defaultMediaType {
				isDefault = true
			}
		}
		d := &EncoderTemplateData{
			PackagePath: enc.PackagePath,
			PackageName: pkgName,
			Function:    enc.Function,
			MIMETypes:   enc.MIMETypes,
			Default:     isDefault,
		}
		data[i] = d
	}
	return data, nil
}
Beispiel #6
0
// BuildEncoderMap builds the template data needed to render the given encoding definitions.
// This extra map is needed to handle the case where a single encoding definition maps to multiple
// encoding packages. The data is indexed by encoder Go package path.
func BuildEncoderMap(info []*design.EncodingDefinition, encoder bool) (map[string]*EncoderTemplateData, error) {
	if len(info) == 0 {
		return nil, nil
	}
	packages := make(map[string]map[string]bool)
	for _, enc := range info {
		supporting := enc.SupportingPackages()
		if supporting == nil {
			// shouldn't happen - DSL validation shouldn't allow it - be graceful
			continue
		}
		for ppath, mimeTypes := range supporting {
			if _, ok := packages[ppath]; !ok {
				packages[ppath] = make(map[string]bool)
			}
			for _, m := range mimeTypes {
				packages[ppath][m] = true
			}
		}
	}
	data := make(map[string]*EncoderTemplateData, len(packages))
	if len(info[0].MIMETypes) == 0 {
		return nil, fmt.Errorf("No mime type associated with encoding info for package %s", info[0].PackagePath)
	}
	defaultMediaType := info[0].MIMETypes[0]
	for p, ms := range packages {
		pkgName := "goa"
		if !design.IsGoaEncoder(p) {
			srcPath, err := codegen.PackageSourcePath(p)
			if err == nil {
				pkgName, err = codegen.PackageName(srcPath)
			}
			if err != nil {
				return nil, fmt.Errorf("failed to load package %s (%s)", p, err)
			}
		}
		mimeTypes := make([]string, len(ms))
		isDefault := false
		i := 0
		for m := range ms {
			if m == defaultMediaType {
				isDefault = true
			}
			mimeTypes[i] = m
			i++
		}
		first := mimeTypes[0]
		sort.Strings(mimeTypes)
		var factory string
		if encoder {
			if !design.IsGoaEncoder(p) {
				factory = "EncoderFactory"
			} else {
				factory = design.KnownEncoders[first][1]
			}
		} else {
			if !design.IsGoaEncoder(p) {
				factory = "DecoderFactory"
			} else {
				factory = design.KnownEncoders[first][2]
			}
		}
		d := &EncoderTemplateData{
			PackagePath: p,
			PackageName: pkgName,
			Factory:     factory,
			MIMETypes:   mimeTypes,
			Default:     isDefault,
		}
		data[p] = d
	}
	return data, nil
}
Beispiel #7
0
// Generate compiles and runs the generator and returns the generated filenames.
func (m *Generator) Generate() ([]string, error) {
	if codegen.OutputDir == "" {
		return nil, fmt.Errorf("missing output directory specification")
	}

	if codegen.DesignPackagePath == "" {
		return nil, fmt.Errorf("missing design package path specification")
	}

	if os.Getenv("GOPATH") == "" {
		return nil, fmt.Errorf("GOPATH not set")
	}

	// Create output directory
	if err := os.MkdirAll(codegen.OutputDir, 0755); err != nil {
		return nil, err
	}

	// Create temporary workspace used for generation
	wd, err := os.Getwd()
	if err != nil {
		return nil, err
	}
	tmpDir, err := ioutil.TempDir(wd, "goagen")
	if err != nil {
		if _, ok := err.(*os.PathError); ok {
			err = fmt.Errorf(`invalid output directory path "%s"`, codegen.OutputDir)
		}
		return nil, err
	}
	defer func() {
		if !codegen.Debug {
			os.RemoveAll(tmpDir)
		}
	}()
	if codegen.Debug {
		fmt.Printf("** Code generator source dir: %s\n", tmpDir)
	}

	// Figure out design package name from its path
	path, err := codegen.PackageSourcePath(codegen.DesignPackagePath)
	if err != nil {
		return nil, err
	}
	pkgName, err := codegen.PackageName(path)
	if err != nil {
		return nil, err
	}

	// Generate tool source code.
	pkgPath := filepath.Join(tmpDir, pkgName)
	p, err := codegen.PackageFor(pkgPath)
	if err != nil {
		return nil, err
	}
	m.generateToolSourceCode(p)

	// Compile and run generated tool.
	if codegen.Debug {
		fmt.Printf("** Compiling with:\n%s", strings.Join(os.Environ(), "\n"))
	}
	genbin, err := p.Compile("goagen")
	if err != nil {
		return nil, err
	}
	return m.spawn(genbin)
}