github.com/please-build/go-rules/tools/please_go@v0.0.0-20240319165128-ea27d6f5caba/please_go.go (about) 1 package main 2 3 import ( 4 "log" 5 "os" 6 "path/filepath" 7 "runtime/debug" 8 "strings" 9 10 "github.com/peterebden/go-cli-init/v5/flags" 11 12 "github.com/please-build/go-rules/tools/please_go/cover" 13 "github.com/please-build/go-rules/tools/please_go/covervars" 14 "github.com/please-build/go-rules/tools/please_go/embed" 15 "github.com/please-build/go-rules/tools/please_go/filter" 16 "github.com/please-build/go-rules/tools/please_go/generate" 17 "github.com/please-build/go-rules/tools/please_go/goget" 18 "github.com/please-build/go-rules/tools/please_go/install" 19 "github.com/please-build/go-rules/tools/please_go/modinfo" 20 "github.com/please-build/go-rules/tools/please_go/packageinfo" 21 "github.com/please-build/go-rules/tools/please_go/test" 22 ) 23 24 var opts = struct { 25 Usage string 26 27 Install struct { 28 BuildTags []string `long:"build_tag" description:"Any build tags to apply to the build"` 29 SrcRoot string `short:"r" long:"src_root" description:"The src root of the module to inspect" default:"."` 30 ModuleName string `short:"n" long:"module_name" description:"The name of the module" required:"true"` 31 ImportConfig string `short:"i" long:"importcfg" description:"The import config for the modules dependencies" required:"true"` 32 LDFlags string `long:"ld_flags" description:"Any additional flags to apply to the C linker" env:"LDFLAGS"` 33 CFlags string `long:"c_flags" description:"Any additional flags to apply when compiling C" env:"CFLAGS"` 34 GoTool string `short:"g" long:"go_tool" description:"The location of the go binary" default:"go"` 35 CCTool string `short:"c" long:"cc_tool" description:"The c compiler to use"` 36 Out string `short:"o" long:"out" description:"The output directory to put compiled artifacts in" required:"true"` 37 TrimPath string `short:"t" long:"trim_path" description:"Removes prefix from recorded source file paths."` 38 PackageConfigTool string `short:"p" long:"pkg_config_tool" env:"PKG_CONFIG_TOOL" description:"The path to the pkg config" default:"pkg-config"` 39 Args struct { 40 Packages []string `positional-arg-name:"packages" description:"The packages to compile"` 41 } `positional-args:"true" required:"true"` 42 } `command:"install" alias:"i" description:"Compile a go module similarly to 'go install'"` 43 Test struct { 44 GoTool string `short:"g" long:"go_tool" description:"The location of the go binary" env:"TOOLS_GO" default:"go"` 45 Dir string `short:"d" long:"dir" description:"Directory to search for Go package files for coverage"` 46 Exclude []string `short:"x" long:"exclude" default:"third_party/go" description:"Directories to exclude from search"` 47 Output string `short:"o" long:"output" description:"Output filename" required:"true"` 48 TestPackage string `short:"t" long:"test_package" description:"The import path of the test package"` 49 Benchmark bool `short:"b" long:"benchmark" description:"Whether to run benchmarks instead of tests"` 50 External bool `long:"external" description:"Whether the test is external or not"` 51 Args struct { 52 Sources []string `positional-arg-name:"sources" description:"Test source files" required:"true"` 53 } `positional-args:"true" required:"true"` 54 } `command:"testmain" alias:"t" description:"Generates a go main package to run the tests in a package."` 55 CoverVars struct { 56 ImportPath string `short:"i" long:"import_path" description:"The import path for the source files"` 57 Args struct { 58 Sources []string `positional-arg-name:"sources" description:"Source files to generate embed config for"` 59 } `positional-args:"true"` 60 } `command:"covervars" description:"Generates coverage variable config for a set of go src files"` 61 Cover struct { 62 GoTool string `short:"g" long:"go" default:"go" env:"TOOLS_GO" description:"Go binary to run"` 63 CoverTool string `short:"t" long:"go_cover" env:"TOOLS_COVER" description:"Go coverage tool to run"` 64 CoverageCfg string `short:"c" long:"covcfg" required:"true" description:"Output coveragecfg file to feed into go tool compile"` 65 Output string `short:"o" long:"output" required:"true" description:"File that will contain output names of modified files"` 66 Pkg string `long:"pkg" env:"PKG_DIR" description:"Package that we're in within the repo"` 67 PkgName string `short:"p" long:"pkg_name" hidden:"true" description:"Deprecated, has no effect"` 68 Args struct { 69 Sources []string `positional-arg-name:"sources" required:"true" description:"Source files to generate embed config for"` 70 } `positional-args:"true"` 71 } `command:"cover" description:"Generates coverage information for a package."` 72 Filter struct { 73 Tags []string `short:"t" long:"tags" description:"Additional build tags to apply"` 74 Args struct { 75 Sources []string `positional-arg-name:"sources" description:"Source files to filter"` 76 } `positional-args:"true"` 77 } `command:"filter" alias:"f" description:"Filter go sources based on the go build tag rules."` 78 Embed struct { 79 Args struct { 80 Sources []string `positional-arg-name:"sources" description:"Source files to generate embed config for"` 81 } `positional-args:"true"` 82 } `command:"embed" alias:"e" description:"Generate embed config for a set of Go source files"` 83 PackageInfo struct { 84 ImportPath string `short:"i" long:"import_path" description:"Go import path (e.g. github.com/please-build/go-rules)"` 85 Pkg string `long:"pkg" env:"PKG_DIR" description:"Package that we're in within the repo"` 86 ImportMap map[string]string `short:"m" long:"import_map" description:"Existing map of imports"` 87 Subrepo string `short:"s" long:"subrepo" description:"Subrepo root that this package is within"` 88 Module string `long:"mod" description:"The module this is within, if present"` 89 } `command:"package_info" alias:"p" description:"Creates an info file about a Go package"` 90 ModuleInfo struct { 91 ModulePath string `short:"m" long:"module_path" required:"true" description:"Import path of the module in question"` 92 Srcs string `long:"srcs" env:"SRCS_SRCS" required:"true" description:"Source files of the module"` 93 ImportConfig string `long:"importconfig" env:"SRCS_IC" description:"Importconfig file for locating gc export data"` 94 Packages []string `short:"p" long:"packages" description:"Packages to include in the module"` 95 } `command:"module_info" alias:"m" description:"Creates an info file about a series of packages in a go_module"` 96 Generate struct { 97 SrcRoot string `short:"r" long:"src_root" description:"The src root of the module to inspect"` 98 ImportPath string `long:"import_path" description:"overrides the module's import path. If not set, the import path from the go.mod will be used.'"` 99 ThirdPartyFolder string `short:"t" long:"third_part_folder" description:"The folder containing the third party subrepos" default:"third_party/go"` 100 ModFile string `long:"mod_file" description:"Path to the host repo mod file to use to resolve dependencies against (dependencies will be resolved against the module as well if it exists)"` 101 Module string `long:"module" description:"The name of the current module"` 102 Version string `long:"version" description:"The version of the current module"` 103 Install []string `long:"install" description:"The packages to add to the :install alias"` 104 BuildTags []string `long:"build_tag" description:"Any build tags to apply to the build"` 105 Subrepo string `long:"subrepo" description:"The subrepo root to output into"` 106 Args struct { 107 Requirements []string `positional-arg-name:"requirements" description:"Any module requirements not included in the go.mod"` 108 } `positional-args:"true"` 109 } `command:"generate" alias:"g" description:"Generate build targets for a Go module"` 110 GoGet struct { 111 ModFile string `short:"m" long:"mod_file" description:"A go.mod file to use as a set of reuirementzs"` 112 Args struct { 113 Requirements []string `positional-arg-name:"requirements" description:"a set of module@version pairs"` 114 } `positional-args:"true"` 115 } `command:"get" description:"Generate go_get rules"` 116 ModInfo struct { 117 GoTool string `short:"g" long:"go" env:"TOOLS_GO" required:"true" description:"The Go tool we'll use"` 118 ModulePath string `short:"m" long:"module_path" description:"The path for the module being built"` 119 Pkg string `short:"p" long:"package" env:"PKG_DIR" description:"The package directory within the repo"` 120 BuildMode string `short:"b" long:"build_mode" default:"exe" description:"The Go build mode being used"` 121 Out string `short:"o" long:"out" env:"OUT" required:"true" description:"File to write the output to"` 122 Write bool `short:"w" long:"write" hidden:"true" description:"Print this binary's own modinfo"` 123 CgoEnabled string `short:"c" long:"cgo_enabled" env:"CGO_ENABLED" description:"Whether cgo is enabled or not"` 124 GoOS string `long:"goos" env:"OS" description:"OS we're compiling for"` 125 GoArch string `long:"goarch" env:"ARCH" description:"Architecture we're compiling for"` 126 } `command:"modinfo" description:"Generates Go modinfo for the linter"` 127 GenerateModuleVersion struct { 128 ModulePath string `short:"m" long:"module_path" required:"true" description:"The module's path"` 129 Version string `long:"version" required:"true" description:"The module's (semantic) version number"` 130 Validate bool `long:"validate" description:"Check validity of the given module import path and version number"` 131 Out string `short:"o" long:"out" env:"OUT" required:"true" description:"File to write the output to"` 132 } `command:"generate_module_version" description:"Generates a module version file for a third-party Go module"` 133 }{ 134 Usage: ` 135 please-go is used by the go build rules to compile and test go modules and packages. 136 137 Unlike 'go build', this tool doesn't rely on the go path or modules to find packages. Instead it takes in 138 a go import config just like 'go tool compile/link -importcfg'. 139 `, 140 } 141 142 var subCommands = map[string]func() int{ 143 "install": func() int { 144 pleaseGoInstall := install.New( 145 opts.Install.BuildTags, 146 opts.Install.SrcRoot, 147 opts.Install.ModuleName, 148 opts.Install.ImportConfig, 149 opts.Install.LDFlags, 150 opts.Install.CFlags, 151 mustResolvePath(opts.Install.GoTool), 152 mustResolvePath(opts.Install.CCTool), 153 opts.Install.PackageConfigTool, 154 opts.Install.Out, 155 opts.Install.TrimPath, 156 ) 157 if err := pleaseGoInstall.Install(opts.Install.Args.Packages); err != nil { 158 log.Fatal(err) 159 } 160 return 0 161 }, 162 "testmain": func() int { 163 test.PleaseGoTest(opts.Test.GoTool, opts.Test.Dir, opts.Test.TestPackage, opts.Test.Output, opts.Test.Args.Sources, opts.Test.Exclude, opts.Test.Benchmark, opts.Test.External) 164 return 0 165 }, 166 "cover": func() int { 167 if err := cover.WriteCoverage(opts.Cover.GoTool, opts.Cover.CoverTool, opts.Cover.CoverageCfg, opts.Cover.Output, opts.Cover.Pkg, opts.Cover.Args.Sources); err != nil { 168 log.Fatalf("failed to write coverage: %s", err) 169 } 170 return 0 171 }, 172 "covervars": func() int { 173 covervars.GenCoverVars(os.Stdout, opts.CoverVars.ImportPath, opts.CoverVars.Args.Sources) 174 return 0 175 }, 176 "filter": func() int { 177 filter.Filter(opts.Filter.Tags, opts.Filter.Args.Sources) 178 return 0 179 }, 180 "embed": func() int { 181 if err := embed.WriteEmbedConfig(opts.Embed.Args.Sources, os.Stdout); err != nil { 182 log.Fatalf("failed to generate embed config: %v", err) 183 } 184 return 0 185 }, 186 "generate": func() int { 187 gen := opts.Generate 188 g := generate.New(gen.SrcRoot, gen.ThirdPartyFolder, gen.ModFile, gen.Module, gen.Version, gen.Subrepo, []string{"BUILD", "BUILD.plz"}, gen.Args.Requirements, gen.Install, gen.BuildTags) 189 if err := g.Generate(); err != nil { 190 log.Fatalf("failed to generate go rules: %v", err) 191 } 192 return 0 193 }, 194 "get": func() int { 195 if opts.GoGet.ModFile != "" { 196 if err := goget.GetMod(opts.GoGet.ModFile); err != nil { 197 log.Fatalf("failed to generate go rules: %v", err) 198 } 199 return 0 200 } 201 if err := goget.GoGet(opts.GoGet.Args.Requirements); err != nil { 202 log.Fatalf("failed to generate go rules: %v", err) 203 } 204 return 0 205 }, 206 "package_info": func() int { 207 pi := opts.PackageInfo 208 if err := packageinfo.WritePackageInfo(pi.ImportPath, pi.Pkg, "", pi.ImportMap, nil, pi.Subrepo, pi.Module, os.Stdout); err != nil { 209 log.Fatalf("failed to write package info: %s", err) 210 } 211 return 0 212 }, 213 "module_info": func() int { 214 mi := opts.ModuleInfo 215 if err := packageinfo.WritePackageInfo(mi.ModulePath, mi.Srcs, mi.ImportConfig, nil, mi.Packages, "", "", os.Stdout); err != nil { 216 log.Fatalf("failed to write module info: %s", err) 217 } 218 return 0 219 }, 220 "modinfo": func() int { 221 mi := opts.ModInfo 222 if mi.Write { 223 info, ok := debug.ReadBuildInfo() 224 if !ok { 225 log.Fatalf("build info not available") 226 } 227 os.Stderr.Write([]byte(info.String() + "\n")) 228 } 229 if err := modinfo.WriteModInfo(mi.GoTool, mi.ModulePath, filepath.Join(mi.ModulePath, mi.Pkg), mi.BuildMode, mi.CgoEnabled, mi.GoOS, mi.GoArch, mi.Out); err != nil { 230 log.Fatalf("failed to write modinfo: %s", err) 231 } 232 return 0 233 }, 234 "generate_module_version": func() int { 235 mv := opts.GenerateModuleVersion 236 if err := modinfo.WriteModuleVersion(mv.ModulePath, mv.Version, mv.Validate, mv.Out); err != nil { 237 log.Fatalf("failed to generate module version file: %v", err) 238 } 239 return 0 240 }, 241 } 242 243 func main() { 244 command := flags.ParseFlagsOrDie("please-go", &opts, nil) 245 os.Exit(subCommands[command]()) 246 } 247 248 // mustResolvePath converts a relative path to absolute if it has any separators in it. 249 func mustResolvePath(in string) string { 250 if in == "" { 251 return in 252 } 253 if !filepath.IsAbs(in) && strings.ContainsRune(in, filepath.Separator) { 254 abs, err := filepath.Abs(in) 255 if err != nil { 256 log.Fatalf("Failed to make %s absolute: %s", in, err) 257 } 258 return abs 259 } 260 return in 261 }