Esempio n. 1
0
func loadBabelFiles(args []string) (*idl.Idl, error) {
	// initialize map to track processed files
	processedFiles := make(map[string]bool)

	// create base IDL to aggregate into
	var midl idl.Idl
	midl.Init()
	midl.AddDefaultNamespace("babelrpc.io", "Foo/Bar")

	// load specified IDL files into it
	for _, infilePat := range args {
		infiles, err := filepath.Glob(infilePat)
		if err != nil {
			return nil, errors.New(fmt.Sprintf("Cannot glob files: %s\n", err))
		}
		if len(infiles) == 0 {
			log.Printf("Warning: No files match \"%s\"\n", infilePat)
		}
		for _, infile := range infiles {
			_, ok := processedFiles[infile]
			if ok {
				log.Printf("Already processed %s\n", infile)
			} else {
				processedFiles[infile] = true
				// fmt.Printf("%s:\n", infile)
				bidl, err := parser.ParseIdl(infile, "test")
				if err != nil {
					return nil, errors.New(fmt.Sprintf("Parsing error in %s: %s\n", infile, err))
				}

				midl.Imports = append(midl.Imports, bidl)
			}
		}
	}

	// validate combined babel
	err := midl.Validate("test")
	if err != nil {
		log.Printf("Warning: Combined IDL does not validate: %s\n", err)
	}

	return &midl, nil
}
Esempio n. 2
0
// main entry point
func main() {
	// recover panicking parser
	defer func() {
		if r := recover(); r != nil {
			e, y := r.(*idl.Error)
			if y {
				fmt.Fprintln(os.Stderr, e.Error())
				os.Exit(e.Code)
			} else {
				fmt.Fprintf(os.Stderr, "Exiting due to fatal error:\n%s\n", r)
				os.Exit(1)
			}
		}
	}()

	templatesDir := flag.String("templates", generator.LocateTemplateDir(), "Overrides the location of the templates folder")
	outputJson := flag.Bool("json", false, "Output parse tree in JSON format")
	lang := flag.String("lang", "", "Generate code with given language csharp|java")
	outputDir := flag.String("output", "", "Output folder for generates files, defaults to gen-<<lang>>")
	inc := flag.Bool("inc", false, "Generate included files too")
	getHelp := flag.Bool("help", false, "Get command-line help")
	scopes := flag.String("scopes", "", "Comma-separated list of scopes to enable (no spaces)")
	options := flag.String("options", "", "Comma-separated list of key=value pairs for generator")
	genClient := flag.Bool("client", false, "Generate client files")
	genServer := flag.Bool("server", false, "Generate server files")
	genModel := flag.Bool("model", false, "Generate model files")
	serverType := flag.String("servertype", "", "Optional language-specific server type")
	ver := flag.Bool("version", false, "Display Babel version number")
	nsMatch := flag.String("ns", "", "Optionally matches files only if namespace starts with this")
	flag.Parse()

	if *getHelp {
		fmt.Printf("The babel command generates source files from Babel IDL files.\n\n")
		fmt.Printf("babel -lang <language> [optional flags] <filePattern> [filePattern...]\n\n")

		flag.PrintDefaults()

		fmt.Printf(`
If none of -model, -client, or -server are specified, then all three are generated.

Use -scopes to enable attributes that are qualified with a scope.

-options are values that are specific to each language. See the documenation for more information.
	ASP supports "ext", which can be "vbs" or "asp".
	C# supports "controller", which can be used to override the controller base class.
	C# supports "output", which can be used to define output directory options.  Supported options are:
		ns-flat - code is generated in a single (flat) output directory corresponding to the namespace.
		ns-nested - code is generated in a multiple (nested) output directories corresponding to the namespace.

Babel accepts quite flexible file patterns - and accepts more than one. Here are some examples:

	*.babel         All babel files in the current folder
	*/*.babel       All babel files in every immediate folder
	foo/*/*.babel   All babel files in each folder of foo

`)

		os.Exit(0)
	}

	if *ver {
		fmt.Printf("Babel Version %s\n", BABEL_VERSION)
		os.Exit(0)
	}

	if len(flag.Args()) == 0 {
		fmt.Printf("Please specify files to process or -help for options.\n")
		os.Exit(0)
	}

	if strings.TrimSpace(*lang) == "" {
		fmt.Fprintf(os.Stderr, "-lang must be specified\n")
		os.Exit(1)
	}

	if *genClient == false && *genServer == false && *genModel == false {
		*genClient = true
		*genServer = true
		*genModel = true
	}

	if *outputDir == "" {
		*outputDir = "gen-" + *lang
	}

	if *outputDir != "" {
		err := os.MkdirAll(*outputDir, os.ModePerm)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error creating output directory: %s\n", err)
			os.Exit(2)
		}
	}

	theScopes := make([]string, 0)
	for _, s := range strings.Split(*scopes, ",") {
		sc := strings.TrimSpace(s)
		if sc != "" {
			theScopes = append(theScopes, sc)
		}
	}

	theOptions := make(map[string]string)
	for _, s := range strings.Split(*options, ",") {
		sv := strings.TrimSpace(s)
		if sv != "" {
			kv := strings.Split(sv, "=")
			if len(kv) != 2 || strings.TrimSpace(kv[0]) == "" {
				fmt.Fprintf(os.Stderr, "Invalid options: %s\n", s)
				os.Exit(3)
			}
			theOptions[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1])
		}
	}

	gen, err := generator.New(*lang, &generator.Arguments{
		OutputDir:   *outputDir,
		TemplateDir: *templatesDir,
		Scopes:      theScopes,
		GenServer:   *genServer,
		GenClient:   *genClient,
		GenModel:    *genModel,
		Options:     theOptions,
		ServerType:  *serverType})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error creating %s generator: %s\n", *lang, err)
		os.Exit(4)
	}

	processedFiles := make(map[string]bool)
	generatedFiles := make(map[string]bool)

	for _, infilePat := range flag.Args() {
		infiles, err := filepath.Glob(infilePat)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Cannot glob files:\n%s\n", err)
			os.Exit(5)
		}
		if len(infiles) == 0 {
			fmt.Fprintf(os.Stderr, "Warning: No files match \"%s\"\n", infilePat)
		}
		for _, infile := range infiles {
			_, ok := processedFiles[infile]
			if ok {
				fmt.Fprintf(os.Stderr, "Already processed %s\n", infile)
			} else {
				processedFiles[infile] = true
				fmt.Printf("%s:\n", infile)
				bidl, err := parser.ParseIdl(infile, *lang)
				if err != nil {
					fmt.Fprintf(os.Stderr, "Parsing error:\n%s\n", err)
					os.Exit(6)
				}

				if strings.HasPrefix(bidl.Namespaces["#default"], *nsMatch) {
					if *outputJson {
						b, err := json.MarshalIndent(bidl, "", "  ")
						if err != nil {
							fmt.Fprintf(os.Stderr, "json error: %s\n", err)
						}
						fmt.Println(string(b))
						continue
					}

					files, err := gen.GenerateCode(bidl)
					if err != nil {
						fmt.Fprintf(os.Stderr, "Error generating files for %s: %s\n", filepath.FromSlash(bidl.Filename), err)
						os.Exit(7)
					}
					fmt.Println("\t" + strings.Join(files, "\n\t"))
					for _, gfn := range files {
						_, ok := generatedFiles[gfn]
						if ok {
							fmt.Fprintf(os.Stderr, "Error - file %s generated in a prior step and overwritten while processing %s\n", gfn, filepath.FromSlash(bidl.Filename))
							os.Exit(8)
						} else {
							generatedFiles[gfn] = true
						}
					}
				} else {
					fmt.Println("\tSkipped.")
				}

				if *inc {
					for _, imp := range bidl.UniqueImports() {
						impFn := filepath.FromSlash(imp.Filename)
						_, ok := processedFiles[impFn]
						if ok {
							fmt.Fprintf(os.Stderr, "Already processed %s\n", impFn)
						} else {
							processedFiles[impFn] = true
							fmt.Printf("%s:\n", impFn)

							if strings.HasPrefix(imp.Namespaces["#default"], *nsMatch) {
								ifiles, err := gen.GenerateCode(imp)
								if err != nil {
									fmt.Fprintf(os.Stderr, "Error generating files for %s: %s\n", impFn, err)
									os.Exit(9)
								}
								fmt.Println("\t" + strings.Join(ifiles, "\n\t"))
								for _, gfn := range ifiles {
									_, ok := generatedFiles[gfn]
									if ok {
										fmt.Fprintf(os.Stderr, "Error - file %s generated in a prior step and overwritten while processing %s\n", gfn, impFn)
										os.Exit(10)
									} else {
										generatedFiles[gfn] = true
									}
								}
							} else {
								fmt.Println("\tSkipped.")
							}
						}
					}
				}
			}
		}
	}
}
Esempio n. 3
0
// main entry point
func main() {
	/*
		// recover panicking parser
		defer func() {
			if r := recover(); r != nil {
				e, y := r.(*idl.Error)
				if y {
					fmt.Fprintln(os.Stderr, e.Error())
					os.Exit(e.Code)
				} else {
					fmt.Fprintf(os.Stderr, "Exiting due to fatal error:\n%s\n", r)
					os.Exit(1)
				}
			}
		}()
	*/

	var format string
	flag.StringVar(&format, "format", "json", "Specifies output format - can be json or yaml")

	var output string
	flag.StringVar(&output, "out", "", "Specifies the file to write to")

	var host string
	flag.StringVar(&host, "host", "localhost", "Specifies the host to include in the file, for example localhost:8080")

	var basePath string
	flag.StringVar(&basePath, "basepath", "/", "Specifies the base path to include in the file, for example /foo/bar")

	var title string
	flag.StringVar(&title, "title", "My Application", "Sets the application title")

	flag.BoolVar(&flatten, "flat", false, "Flatten composed objects into a single object definition")

	var version string
	flag.StringVar(&version, "version", "1.0", "Sets the API version")

	flag.BoolVar(&restful, "rest", false, "Process @rest annotations (resulting Swagger won't be able to invoke Babel services)")

	flag.BoolVar(&swagInt, "int64", false, "When -rest is enabled, format int64 Swagger-style instead of Babel-style")

	flag.BoolVar(&genErr, "error", false, "When -rest is enabled, still include the Babel error definition")

	flag.Parse()

	if format != "json" && format != "yaml" {
		fmt.Printf("-format must be json or yaml.\n")
		os.Exit(0)
	}

	if swagInt && !restful {
		fmt.Fprintf(os.Stderr, "Warning: Ignoring -int64 because -rest is not set.\n")
		swagInt = false
	}

	if len(flag.Args()) == 0 {
		fmt.Printf("Please specify files to process or -help for options.\n")
		os.Exit(0)
	}

	// initialize map to track processed files
	processedFiles := make(map[string]bool)

	// create base IDL to aggregate into
	var midl idl.Idl
	midl.Init()
	midl.AddDefaultNamespace("babelrpc.io", "Foo/Bar")

	// load specified IDL files into it
	for _, infilePat := range flag.Args() {
		infiles, err := filepath.Glob(infilePat)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Cannot glob files: %s\n", err)
			os.Exit(5)
		}
		if len(infiles) == 0 {
			fmt.Fprintf(os.Stderr, "Warning: No files match \"%s\"\n", infilePat)
		}
		for _, infile := range infiles {
			_, ok := processedFiles[infile]
			if ok {
				fmt.Fprintf(os.Stderr, "Already processed %s\n", infile)
			} else {
				processedFiles[infile] = true
				// fmt.Printf("%s:\n", infile)
				bidl, err := parser.ParseIdl(infile, "test")
				if err != nil {
					fmt.Fprintf(os.Stderr, "Parsing error in %s: %s\n", infile, err)
					os.Exit(6)
				}

				midl.Imports = append(midl.Imports, bidl)
			}
		}
	}

	// validate combined babel
	err := midl.Validate("test")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Warning: Combined IDL does not validate: %s\n", err)
	}

	// convert to swagger

	var swag swagger2.Swagger
	swag.Swagger = "2.0"
	swag.Host = host
	swag.BasePath = basePath
	swag.Consumes = []string{"application/json"}
	swag.Produces = []string{"application/json"}
	swag.Tags = []swagger2.Tag{swagger2.Tag{Name: "babel"}}
	swag.Info.Title = title
	swag.Info.Version = version
	swag.Definitions = make(swagger2.Definitions, 0)

	// parse and attach error model (not included with REST unless -genErr is used)
	if !restful || genErr {
		errorB := Lookup("/error.json")
		errorS, err := swagger2.LoadJson(errorB)
		if err != nil {
			panic("Cannot parse embedded error file")
		}
		// attach error definitions
		for sx, sy := range errorS.Definitions {
			swag.Definitions[sx] = sy
		}
	}

	// Glob together file-level commebts
	sarr := make([]string, 0)
	for _, idl := range midl.Imports {
		if len(idl.Comments) > 0 {
			sarr = append(sarr, idl.Filename+":\n\n")
			sarr = append(sarr, idl.Comments...)
		}
	}
	swag.Info.Description = strings.Join(sarr, "\n\n")
	if swag.Info.Description != "" {
		swag.Info.Description += "\n\n"
	}
	gentxt := "Generated on " + time.Now().Format(time.RFC1123) + " via:\n\n\tbabel2swagger"
	if format != "json" {
		gentxt += " -format " + format
	}
	if flatten {
		gentxt += " -flat"
	}
	if restful {
		gentxt += " -rest"
		if swagInt {
			gentxt += " -int64"
		}
		if genErr {
			gentxt += " -error"
		}
	}
	if host != "localhost" {
		gentxt += " -host " + host
	}
	if basePath != "/" {
		gentxt += " -basepath " + basePath
	}
	if version != "1.0" {
		gentxt += " -version " + version
	}
	if title != "My Application" {
		gentxt += " -title \"" + title + "\""
	}
	for _, g := range flag.Args() {
		gentxt += " \"" + g + "\""
	}
	swag.Info.Description += gentxt

	// add structs to swagger definitions
	for _, st := range allStructs(&midl) {
		swag.Definitions[st.Name] = *structToSchema(&midl, st)
	}

	// add enums to swagger definitions
	// SWAGGER-BUG: swagger-ui doesn't like it this way - need expand them out individually as strings
	/*
		for _, en := range allEnums(&midl) {
			swag.Definitions[en.Name] = *enumToSchema(&midl, en)
		}
	*/

	// track whether we find any empty parameters
	hasEmptyParms := false

	// add service/methods to paths
	swag.Paths = make(swagger2.Paths, 0)
	for _, svc := range allServices(&midl) {
		if restful {
			err := addRestService(&swag, &midl, svc)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error: Cannot generate REST service: %s\n", err)
				os.Exit(11)
			}
		} else {
			svcComments := strings.Join(svc.Comments, "\n")
			/*
				Seems like these belong somewhere else
				if svcComments != "" {
					swag.Info.Description += "\n" + svcComments
				}
			*/
			for _, mth := range svc.Methods {
				var p swagger2.PathItem
				p.Post = new(swagger2.Operation)
				p.Post.Tags = []string{svc.Name}
				mthComments := strings.Join(mth.Comments, "\n")
				theseArgs := make([]string, 0)
				for _, s := range mth.Parameters {
					// SWAGGER-BUG: Swagger should be HTML escaping this
					theseArgs = append(theseArgs, html.EscapeString(s.Type.String())+" "+s.Name)
				}
				// SWAGGER-BUG: Swagger should be HTML escaping this
				p.Post.Description = html.EscapeString(mth.Returns.String()) + " " + mth.Name + "(" + strings.Join(theseArgs, ", ") + ")"
				if mthComments != "" {
					p.Post.Description += "\n\n" + mthComments
				}
				if svcComments != "" {
					p.Post.Description += "\n\n" + svc.Name + ": " + svcComments
				}
				p.Post.OperationId = svc.Name + "_" + mth.Name
				p.Post.Summary = svc.Name + "." + mth.Name
				p.Post.Parameters = make([]swagger2.Parameter, 0)
				var parm swagger2.Parameter
				parm.Name = "request"
				parm.In = "body"
				parm.Required = new(bool)
				if mth.HasParameters() {
					oname := strings.Title(svc.Name) + strings.Title(mth.Name) + "RequestArgs"
					swag.Definitions[oname] = *parmsToSchema(&midl, mth)
					parm.Schema = &swagger2.Schema{ItemsDef: swagger2.ItemsDef{Ref: "#/definitions/" + oname}}
					parm.Description = "Request definition"
					*parm.Required = true
				} else {
					parm.Schema = &swagger2.Schema{ItemsDef: swagger2.ItemsDef{Ref: "#/definitions/EmptyRequestArgs"}}
					hasEmptyParms = true
					*parm.Required = false
				}
				p.Post.Parameters = append(p.Post.Parameters, parm)
				p.Post.Responses = make(swagger2.Responses, 0)
				// SWAGGER-BUG: note that swagger-ui does not show primitive types for responses, even though they are allowed.
				p.Post.Responses["200"] = swagger2.Response{
					Description: "Response of type " + html.EscapeString(mth.Returns.String()),
					Schema:      returnsToSchema(&midl, mth.Returns),
				}
				p.Post.Responses["default"] = swagger2.Response{
					Description: "error",
					Schema:      &swagger2.Schema{ItemsDef: swagger2.ItemsDef{Ref: "#/definitions/ServiceError"}},
				}
				swag.Paths["/"+svc.Name+"/"+mth.Name] = p
			}
		}
	}

	// Add empty parameter definition if needed
	if hasEmptyParms {
		swag.Definitions["EmptyRequestArgs"] = swagger2.Schema{ItemsDef: swagger2.ItemsDef{Type: "object"}, Description: "Empty object"}
	}

	// Validate swagger
	errs := swag.Validate()
	if len(errs) > 0 {
		fmt.Fprintf(os.Stderr, "Warning: Swagger does not validate\n%s\n", swagger2.ErrorList(errs).Indent("\t"))
	}

	// open output
	var outfile io.WriteCloser
	outfile = os.Stdout
	if output != "" {
		outfile, err = os.Create(output)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: Cannot open output file %s\n", output)
			os.Exit(9)
		}
		defer outfile.Close()
	}

	// Write output
	if format == "json" {
		s, err := swag.Json()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Cannot convert to JSON: %s\n", err)
			os.Exit(7)
		}
		fmt.Fprintln(outfile, string(s))
	} else {
		s, err := swag.Yaml()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Cannot convert to YAML: %s\n", err)
			os.Exit(8)
		}
		fmt.Fprintln(outfile, string(s))
	}
}
Esempio n. 4
0
func parseIdl() (*idl.Idl, error) {
	return parser.ParseIdl("testfile.babel", "test")
}