|
15 | 15 | package cobra
|
16 | 16 |
|
17 | 17 | import (
|
| 18 | + "os" |
| 19 | + "os/exec" |
| 20 | + "path/filepath" |
| 21 | + "runtime" |
| 22 | + "strings" |
18 | 23 | "testing"
|
19 | 24 | "text/template"
|
20 | 25 | )
|
@@ -222,3 +227,75 @@ func TestRpad(t *testing.T) {
|
222 | 227 | })
|
223 | 228 | }
|
224 | 229 | }
|
| 230 | + |
| 231 | +// TestDeadcodeElimination checks that a simple program using cobra in its |
| 232 | +// default configuration is linked taking full advantage of the linker's |
| 233 | +// deadcode elimination step. |
| 234 | +// |
| 235 | +// If reflect.Value.MethodByName/reflect.Value.Method are reachable the |
| 236 | +// linker will not always be able to prove that exported methods are |
| 237 | +// unreachable, making deadcode elimination less effective. Using |
| 238 | +// text/template and html/template makes reflect.Value.MethodByName |
| 239 | +// reachable. |
| 240 | +// Since cobra can use text/template templates this test checks that in its |
| 241 | +// default configuration that code path can be proven to be unreachable by |
| 242 | +// the linker. |
| 243 | +// |
| 244 | +// See also: https://github.com/spf13/cobra/pull/1956 |
| 245 | +func TestDeadcodeElimination(t *testing.T) { |
| 246 | + if runtime.GOOS == "windows" { |
| 247 | + t.Skip("go tool nm fails on windows") |
| 248 | + } |
| 249 | + |
| 250 | + // check that a simple program using cobra in its default configuration is |
| 251 | + // linked with deadcode elimination enabled. |
| 252 | + const ( |
| 253 | + dirname = "test_deadcode" |
| 254 | + progname = "test_deadcode_elimination" |
| 255 | + ) |
| 256 | + _ = os.Mkdir(dirname, 0770) |
| 257 | + defer os.RemoveAll(dirname) |
| 258 | + filename := filepath.Join(dirname, progname+".go") |
| 259 | + err := os.WriteFile(filename, []byte(`package main |
| 260 | +
|
| 261 | +import ( |
| 262 | + "fmt" |
| 263 | + "os" |
| 264 | +
|
| 265 | + "github.com/spf13/cobra" |
| 266 | +) |
| 267 | +
|
| 268 | +var rootCmd = &cobra.Command{ |
| 269 | + Version: "1.0", |
| 270 | + Use: "example_program", |
| 271 | + Short: "example_program - test fixture to check that deadcode elimination is allowed", |
| 272 | + Run: func(cmd *cobra.Command, args []string) { |
| 273 | + fmt.Println("hello world") |
| 274 | + }, |
| 275 | + Aliases: []string{"alias1", "alias2"}, |
| 276 | + Example: "stringer --help", |
| 277 | +} |
| 278 | +
|
| 279 | +func main() { |
| 280 | + if err := rootCmd.Execute(); err != nil { |
| 281 | + fmt.Fprintf(os.Stderr, "Whoops. There was an error while executing your CLI '%s'", err) |
| 282 | + os.Exit(1) |
| 283 | + } |
| 284 | +} |
| 285 | +`), 0600) |
| 286 | + if err != nil { |
| 287 | + t.Fatalf("could not write test program: %v", err) |
| 288 | + } |
| 289 | + buf, err := exec.Command("go", "build", filename).CombinedOutput() |
| 290 | + if err != nil { |
| 291 | + t.Fatalf("could not compile test program: %s", string(buf)) |
| 292 | + } |
| 293 | + defer os.Remove(progname) |
| 294 | + buf, err = exec.Command("go", "tool", "nm", progname).CombinedOutput() |
| 295 | + if err != nil { |
| 296 | + t.Fatalf("could not run go tool nm: %v", err) |
| 297 | + } |
| 298 | + if strings.Contains(string(buf), "MethodByName") { |
| 299 | + t.Error("compiled programs contains MethodByName symbol") |
| 300 | + } |
| 301 | +} |
0 commit comments