cmd.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. package cmd
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "runtime/trace"
  10. "github.com/spf13/cobra"
  11. "github.com/spf13/pflag"
  12. yaml "gopkg.in/yaml.v3"
  13. "github.com/kyleconroy/sqlc/internal/config"
  14. "github.com/kyleconroy/sqlc/internal/debug"
  15. "github.com/kyleconroy/sqlc/internal/tracer"
  16. )
  17. // Do runs the command logic.
  18. func Do(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
  19. rootCmd := &cobra.Command{Use: "sqlc", SilenceUsage: true}
  20. rootCmd.PersistentFlags().StringP("file", "f", "", "specify an alternate config file (default: sqlc.yaml)")
  21. rootCmd.PersistentFlags().BoolP("experimental", "x", false, "enable experimental features (default: false)")
  22. rootCmd.AddCommand(checkCmd)
  23. rootCmd.AddCommand(genCmd)
  24. rootCmd.AddCommand(initCmd)
  25. rootCmd.AddCommand(versionCmd)
  26. rootCmd.SetArgs(args)
  27. rootCmd.SetIn(stdin)
  28. rootCmd.SetOut(stdout)
  29. rootCmd.SetErr(stderr)
  30. ctx, cleanup, err := tracer.Start(context.Background())
  31. if err != nil {
  32. fmt.Printf("failed to start trace: %v\n", err)
  33. return 1
  34. }
  35. defer cleanup()
  36. if err := rootCmd.ExecuteContext(ctx); err == nil {
  37. return 0
  38. }
  39. if exitError, ok := err.(*exec.ExitError); ok {
  40. return exitError.ExitCode()
  41. }
  42. return 1
  43. }
  44. var version string
  45. var versionCmd = &cobra.Command{
  46. Use: "version",
  47. Short: "Print the sqlc version number",
  48. Run: func(cmd *cobra.Command, args []string) {
  49. if debug.Traced {
  50. defer trace.StartRegion(cmd.Context(), "version").End()
  51. }
  52. if version == "" {
  53. // When no version is set, return the next bug fix version
  54. // after the most recent tag
  55. fmt.Printf("%s\n", "v1.11.0")
  56. } else {
  57. fmt.Printf("%s\n", version)
  58. }
  59. },
  60. }
  61. var initCmd = &cobra.Command{
  62. Use: "init",
  63. Short: "Create an empty sqlc.yaml settings file",
  64. RunE: func(cmd *cobra.Command, args []string) error {
  65. if debug.Traced {
  66. defer trace.StartRegion(cmd.Context(), "init").End()
  67. }
  68. file := "sqlc.yaml"
  69. if f := cmd.Flag("file"); f != nil && f.Changed {
  70. file = f.Value.String()
  71. if file == "" {
  72. return fmt.Errorf("file argument is empty")
  73. }
  74. }
  75. if _, err := os.Stat(file); !os.IsNotExist(err) {
  76. return nil
  77. }
  78. blob, err := yaml.Marshal(config.V1GenerateSettings{Version: "1"})
  79. if err != nil {
  80. return err
  81. }
  82. return os.WriteFile(file, blob, 0644)
  83. },
  84. }
  85. type Env struct {
  86. ExperimentalFeatures bool
  87. }
  88. func ParseEnv(c *cobra.Command) Env {
  89. x := c.Flag("experimental")
  90. return Env{ExperimentalFeatures: x != nil && x.Changed}
  91. }
  92. func getConfigPath(stderr io.Writer, f *pflag.Flag) (string, string) {
  93. if f != nil && f.Changed {
  94. file := f.Value.String()
  95. if file == "" {
  96. fmt.Fprintln(stderr, "error parsing config: file argument is empty")
  97. os.Exit(1)
  98. }
  99. abspath, err := filepath.Abs(file)
  100. if err != nil {
  101. fmt.Fprintf(stderr, "error parsing config: absolute file path lookup failed: %s\n", err)
  102. os.Exit(1)
  103. }
  104. return filepath.Dir(abspath), filepath.Base(abspath)
  105. } else {
  106. wd, err := os.Getwd()
  107. if err != nil {
  108. fmt.Fprintln(stderr, "error parsing sqlc.json: file does not exist")
  109. os.Exit(1)
  110. }
  111. return wd, ""
  112. }
  113. }
  114. var genCmd = &cobra.Command{
  115. Use: "generate",
  116. Short: "Generate Go code from SQL",
  117. Run: func(cmd *cobra.Command, args []string) {
  118. if debug.Traced {
  119. defer trace.StartRegion(cmd.Context(), "generate").End()
  120. }
  121. stderr := cmd.ErrOrStderr()
  122. dir, name := getConfigPath(stderr, cmd.Flag("file"))
  123. output, err := Generate(cmd.Context(), ParseEnv(cmd), dir, name, stderr)
  124. if err != nil {
  125. os.Exit(1)
  126. }
  127. if debug.Traced {
  128. defer trace.StartRegion(cmd.Context(), "writefiles").End()
  129. }
  130. for filename, source := range output {
  131. os.MkdirAll(filepath.Dir(filename), 0755)
  132. if err := os.WriteFile(filename, []byte(source), 0644); err != nil {
  133. fmt.Fprintf(stderr, "%s: %s\n", filename, err)
  134. os.Exit(1)
  135. }
  136. }
  137. },
  138. }
  139. var checkCmd = &cobra.Command{
  140. Use: "compile",
  141. Short: "Statically check SQL for syntax and type errors",
  142. RunE: func(cmd *cobra.Command, args []string) error {
  143. if debug.Traced {
  144. defer trace.StartRegion(cmd.Context(), "compile").End()
  145. }
  146. stderr := cmd.ErrOrStderr()
  147. dir, name := getConfigPath(stderr, cmd.Flag("file"))
  148. if _, err := Generate(cmd.Context(), ParseEnv(cmd), dir, name, stderr); err != nil {
  149. os.Exit(1)
  150. }
  151. return nil
  152. },
  153. }