devtools/madb: device specifier flags
Users can now specify the devices on which the madb subcommand should
run, using command line flags. If no device specifiers are provided,
the command runs on all available devices and emulators.
Change-Id: I840c7a9b57fb04189aee90837bb738b3050286cd
diff --git a/doc.go b/doc.go
index c5201ad..3ca1637 100644
--- a/doc.go
+++ b/doc.go
@@ -15,41 +15,62 @@
madb [flags] <command>
The madb commands are:
- exec Run the provided adb command on all the specified devices
+ exec Run the provided adb command on all devices and emulators
concurrently
name Manage device nicknames
help Display help for commands or topics
+The madb flags are:
+ -d=false
+ Restrict the command to only run on real devices.
+ -e=false
+ Restrict the command to only run on emulators.
+ -n=
+ Comma-separated device serials, qualifiers, or nicknames (set by 'madb
+ name'). Command will be run only on specified devices.
+
The global flags are:
-metadata=<just specify -metadata to activate>
Displays metadata for the program and exits.
-time=false
Dump timing information to stderr before exiting the program.
-Madb exec - Run the provided adb command on all the specified devices concurrently
+Madb exec - Run the provided adb command on all devices and emulators concurrently
-Runs the provided adb command on all the specified devices concurrently.
+Runs the provided adb command on all devices and emulators concurrently.
For example, the following line:
madb -a exec push ./foo.txt /sdcard/foo.txt
copies the ./foo.txt file to /sdcard/foo.txt for all the currently connected
-Android devices (specified by -a flag).
+Android devices.
To see the list of available adb commands, type 'adb help'.
Usage:
madb exec [flags] <command>
-<command> is a normal adb command, which will be executed on all the specified
-devices.
+<command> is a normal adb command, which will be executed on all devices and
+emulators.
+
+The madb exec flags are:
+ -d=false
+ Restrict the command to only run on real devices.
+ -e=false
+ Restrict the command to only run on emulators.
+ -n=
+ Comma-separated device serials, qualifiers, or nicknames (set by 'madb
+ name'). Command will be run only on specified devices.
Madb name - Manage device nicknames
Manages device nicknames, which are meant to be more human-friendly compared to
the device serials provided by adb tool.
+NOTE: Device specifier flags (-d, -e, -n) are ignored in all 'madb name'
+commands.
+
Usage:
madb name [flags] <command>
@@ -59,6 +80,15 @@
list List all the existing nicknames.
clear-all Clear all the existing nicknames.
+The madb name flags are:
+ -d=false
+ Restrict the command to only run on real devices.
+ -e=false
+ Restrict the command to only run on emulators.
+ -n=
+ Comma-separated device serials, qualifiers, or nicknames (set by 'madb
+ name'). Command will be run only on specified devices.
+
Madb name set
Sets a human-friendly nickname that can be used when specifying the device in
@@ -92,6 +122,15 @@
device specifier (e.g., 'usb:3-3.4.2') obtained from 'adb devices -l' command
<nickname> is an alpha-numeric string with no special characters or spaces.
+The madb name set flags are:
+ -d=false
+ Restrict the command to only run on real devices.
+ -e=false
+ Restrict the command to only run on emulators.
+ -n=
+ Comma-separated device serials, qualifiers, or nicknames (set by 'madb
+ name'). Command will be run only on specified devices.
+
Madb name unset
Unsets a nickname assigned by the 'madb name set' command. Either the device
@@ -103,6 +142,15 @@
There should be only one argument, which is either the device serial or the
nickname.
+The madb name unset flags are:
+ -d=false
+ Restrict the command to only run on real devices.
+ -e=false
+ Restrict the command to only run on emulators.
+ -n=
+ Comma-separated device serials, qualifiers, or nicknames (set by 'madb
+ name'). Command will be run only on specified devices.
+
Madb name list
Lists all the currently stored nicknames of device serials.
@@ -110,6 +158,15 @@
Usage:
madb name list [flags]
+The madb name list flags are:
+ -d=false
+ Restrict the command to only run on real devices.
+ -e=false
+ Restrict the command to only run on emulators.
+ -n=
+ Comma-separated device serials, qualifiers, or nicknames (set by 'madb
+ name'). Command will be run only on specified devices.
+
Madb name clear-all
Clears all the currently stored nicknames of device serials.
@@ -117,6 +174,15 @@
Usage:
madb name clear-all [flags]
+The madb name clear-all flags are:
+ -d=false
+ Restrict the command to only run on real devices.
+ -e=false
+ Restrict the command to only run on emulators.
+ -n=
+ Comma-separated device serials, qualifiers, or nicknames (set by 'madb
+ name'). Command will be run only on specified devices.
+
Madb help - Display help for commands or topics
Help with no args displays the usage of the parent command.
diff --git a/exec.go b/exec.go
index 71470c4..7b5307f 100644
--- a/exec.go
+++ b/exec.go
@@ -16,21 +16,21 @@
var cmdMadbExec = &cmdline.Command{
Runner: cmdline.RunnerFunc(runMadbExec),
Name: "exec",
- Short: "Run the provided adb command on all the specified devices concurrently",
+ Short: "Run the provided adb command on all devices and emulators concurrently",
Long: `
-Runs the provided adb command on all the specified devices concurrently.
+Runs the provided adb command on all devices and emulators concurrently.
For example, the following line:
madb -a exec push ./foo.txt /sdcard/foo.txt
-copies the ./foo.txt file to /sdcard/foo.txt for all the currently connected Android devices (specified by -a flag).
+copies the ./foo.txt file to /sdcard/foo.txt for all the currently connected Android devices.
To see the list of available adb commands, type 'adb help'.
`,
ArgsName: "<command>",
ArgsLong: `
-<command> is a normal adb command, which will be executed on all the specified devices.
+<command> is a normal adb command, which will be executed on all devices and emulators.
`,
}
@@ -41,7 +41,7 @@
return err
}
- devices, err := getDevices(getDefaultNameFilePath())
+ devices, err := getSpecifiedDevices()
if err != nil {
return err
}
diff --git a/madb.go b/madb.go
index 085db51..2f7efb2 100644
--- a/madb.go
+++ b/madb.go
@@ -17,6 +17,18 @@
"v.io/x/lib/gosh"
)
+var (
+ allDevicesFlag bool
+ allEmulatorsFlag bool
+ devicesFlag string
+)
+
+func init() {
+ cmdMadb.Flags.BoolVar(&allDevicesFlag, "d", false, `Restrict the command to only run on real devices.`)
+ cmdMadb.Flags.BoolVar(&allEmulatorsFlag, "e", false, `Restrict the command to only run on emulators.`)
+ cmdMadb.Flags.StringVar(&devicesFlag, "n", "", `Comma-separated device serials, qualifiers, or nicknames (set by 'madb name'). Command will be run only on specified devices.`)
+}
+
var cmdMadb = &cmdline.Command{
Children: []*cmdline.Command{cmdMadbExec, cmdMadbName},
Name: "madb",
@@ -69,13 +81,13 @@
}
// Runs "adb devices -l" command, and parses the result to get all the device serial numbers.
-func getDevices(filename string) ([]device, error) {
+func getDevices(nicknameFile string) ([]device, error) {
sh := gosh.NewShell(gosh.Opts{})
defer sh.Cleanup()
output := sh.Cmd("adb", "devices", "-l").Stdout()
- nsm, err := readNicknameSerialMap(filename)
+ nsm, err := readNicknameSerialMap(nicknameFile)
if err != nil {
fmt.Fprintln(os.Stderr, "Warning: Could not read the nickname file.")
}
@@ -138,3 +150,73 @@
return result, nil
}
+
+// Gets all the devices specified by the device specifier flags.
+// Intended to be used by most of the madb sub-commands except for 'madb name'.
+func getSpecifiedDevices() ([]device, error) {
+ allDevices, err := getDevices(getDefaultNameFilePath())
+ if err != nil {
+ return nil, err
+ }
+
+ filtered := filterSpecifiedDevices(allDevices)
+
+ if len(filtered) == 0 {
+ return nil, fmt.Errorf("No devices matching the device specifiers.")
+ }
+
+ return filtered, nil
+}
+
+func filterSpecifiedDevices(devices []device) []device {
+ // If no device specifier flags are set, run on all devices and emulators.
+ if noDevicesSpecified() {
+ return devices
+ }
+
+ result := make([]device, 0, len(devices))
+
+ for _, d := range devices {
+ if shouldIncludeDevice(d) {
+ result = append(result, d)
+ }
+ }
+
+ return result
+}
+
+func noDevicesSpecified() bool {
+ return allDevicesFlag == false &&
+ allEmulatorsFlag == false &&
+ devicesFlag == ""
+}
+
+func shouldIncludeDevice(d device) bool {
+ if allDevicesFlag && d.Type == realDevice {
+ return true
+ }
+
+ if allEmulatorsFlag && d.Type == emulator {
+ return true
+ }
+
+ tokens := strings.Split(devicesFlag, ",")
+ for _, token := range tokens {
+ // Ignore empty tokens
+ if token == "" {
+ continue
+ }
+
+ if d.Serial == token || d.Nickname == token {
+ return true
+ }
+
+ for _, qualifier := range d.Qualifiers {
+ if qualifier == token {
+ return true
+ }
+ }
+ }
+
+ return false
+}
diff --git a/madb_test.go b/madb_test.go
index 2f0567e..1fbbf4b 100644
--- a/madb_test.go
+++ b/madb_test.go
@@ -116,3 +116,75 @@
t.Fatalf("unmatched results: got %v, want %v", got, want)
}
}
+
+func TestGetSpecifiedDevices(t *testing.T) {
+ // First, define some devices (three real devices, and two emulators).
+ d1 := device{
+ Serial: "deviceid01",
+ Type: realDevice,
+ Qualifiers: []string{"usb:3-3.4.3", "product:bullhead", "model:Nexus_5X", "device:bullhead"},
+ Nickname: "MyPhone",
+ }
+
+ d2 := device{
+ Serial: "deviceid02",
+ Type: realDevice,
+ Qualifiers: []string{"usb:3-3.4.1", "product:volantisg", "model:Nexus_9", "device:flounder_lte"},
+ Nickname: "",
+ }
+
+ e1 := device{
+ Serial: "emulator-5554",
+ Type: emulator,
+ Qualifiers: []string{"product:sdk_phone_armv7", "model:sdk_phone_armv7", "device:generic"},
+ Nickname: "ARMv7",
+ }
+
+ d3 := device{
+ Serial: "deviceid03",
+ Type: realDevice,
+ Qualifiers: []string{"usb:3-3.3", "product:bullhead", "model:Nexus_5X", "device:bullhead"},
+ Nickname: "SecondPhone",
+ }
+
+ e2 := device{
+ Serial: "emulator-5555",
+ Type: emulator,
+ Qualifiers: []string{"product:sdk_phone_armv7", "model:sdk_phone_armv7", "device:generic"},
+ Nickname: "",
+ }
+
+ allDevices := []device{d1, d2, e1, d3, e2}
+
+ type deviceFlags struct {
+ allDevices bool
+ allEmulators bool
+ devices string
+ }
+
+ type testCase struct {
+ flags deviceFlags
+ want []device
+ }
+
+ testCases := []testCase{
+ testCase{deviceFlags{false, false, ""}, allDevices}, // Nothing is specified
+ testCase{deviceFlags{true, true, ""}, allDevices}, // Both -d and -e are specified
+ testCase{deviceFlags{true, false, ""}, []device{d1, d2, d3}}, // Only -d is specified
+ testCase{deviceFlags{false, true, ""}, []device{e1, e2}}, // Only -e is specified
+ testCase{deviceFlags{false, false, "device:bullhead"}, []device{d1, d3}}, // Device qualifier
+ testCase{deviceFlags{false, false, "ARMv7,SecondPhone"}, []device{e1, d3}}, // Nicknames
+ testCase{deviceFlags{true, false, "ARMv7"}, []device{d1, d2, e1, d3}}, // Combinations
+ testCase{deviceFlags{false, true, "model:Nexus_9"}, []device{d2, e1, e2}}, // Combinations
+ }
+
+ for i, testCase := range testCases {
+ allDevicesFlag = testCase.flags.allDevices
+ allEmulatorsFlag = testCase.flags.allEmulators
+ devicesFlag = testCase.flags.devices
+
+ if got := filterSpecifiedDevices(allDevices); !reflect.DeepEqual(got, testCase.want) {
+ t.Fatalf("unmatched results for testCases[%v]: got %v, want %v", i, got, testCase.want)
+ }
+ }
+}
diff --git a/name.go b/name.go
index 6d1d5d7..c8773af 100644
--- a/name.go
+++ b/name.go
@@ -22,6 +22,8 @@
Long: `
Manages device nicknames, which are meant to be more human-friendly compared to
the device serials provided by adb tool.
+
+NOTE: Device specifier flags (-d, -e, -n) are ignored in all 'madb name' commands.
`,
}