OSDN Git Service

Add --start, --launch=<name> and --launch-list options to ndk-gdb.
authorDavid 'Digit' Turner <digit@google.com>
Tue, 27 Apr 2010 19:33:46 +0000 (12:33 -0700)
committerDavid 'Digit' Turner <digit@google.com>
Thu, 29 Apr 2010 19:26:01 +0000 (12:26 -0700)
Move all awk scripts to build/awk/ and rework them a bit

Add build/awk/xmlparser.awk to parse XML files into something that
is easier to process with awk. Its output is used by several
scripts now (extract-debuggable.awk, extract-package-name.awk and
the new extract-launchable.awk).

Also update documentation.

Change-Id: I50507abbb2b438aeea25a4e0521e6bf69ad86603

14 files changed:
build/awk/check-awk.awk [moved from build/check-awk.awk with 100% similarity]
build/awk/extract-debuggable.awk [new file with mode: 0644]
build/awk/extract-launchable.awk [new file with mode: 0644]
build/awk/extract-package-name.awk [new file with mode: 0644]
build/awk/extract-pid.awk [moved from build/core/extract-package-pid.awk with 100% similarity]
build/awk/extract-platform.awk [moved from build/core/extract-platform.awk with 100% similarity]
build/awk/xml.awk [new file with mode: 0644]
build/core/add-application.mk
build/core/extract-package-debuggable.awk [deleted file]
build/core/extract-package-name.awk [deleted file]
build/core/init.mk
build/core/setup-app.mk
docs/NDK-GDB.TXT
ndk-gdb

similarity index 100%
rename from build/check-awk.awk
rename to build/awk/check-awk.awk
diff --git a/build/awk/extract-debuggable.awk b/build/awk/extract-debuggable.awk
new file mode 100644 (file)
index 0000000..7aa9ec8
--- /dev/null
@@ -0,0 +1,136 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# A nawk/gawk script used to extract the debuggable flag from an
+# application's manifest (i.e. AndroidManifest.xml). Usage:
+#
+#   awk -f <this-script> AndroidManifest.xml
+#
+
+BEGIN {
+    DEBUGGABLE = "";
+    while ( xml_event() ) {
+        # simply extract the 'android:debuggable' attribute value from
+        # the first <manifest><application> element we find.
+        if ( XML_TYPE == "BEGIN" && XML_TAG == "APPLICATION" &&
+             XML_RPATH == "APPLICATION/MANIFEST/" ) {
+            DEBUGGABLE = XML_ATTR["android:debuggable"];
+            break;
+        }
+    }
+    # ensure the value is either "true" or "false"
+    if ( DEBUGGABLE != "true" )
+        DEBUGGABLE = "false";
+
+    print DEBUGGABLE;
+}
+
+#
+# the following is copied directly from xml.awk - see this file for
+# usage and implementation details.
+#
+function xml_event () {
+    RS=">";
+    XML_TAG=XML_TYPE="";
+    split("", XML_ATTR);
+    while ( 1 ) {
+        if (_xml_closing) { # delayed direct tag closure
+            XML_TAG = _xml_closing;
+            XML_TYPE = "END";
+            _xml_closing = "";
+            _xml_exit(XML_TAG);
+            return 1;
+        }
+        if (getline <= 0) return 0; # read new input line
+        _xml_p = index($0, "<"); # get start marker
+        if (_xml_p == 0) return 0; # end of file (or malformed input)
+        $0 = substr($0, _xml_p) # remove anything before '<'
+        # ignore CData / Comments / Processing instructions / Declarations
+        if (_xml_in_section("<!\\[[Cc][Dd][Aa][Tt][Aa]\\[", "]]") ||
+            _xml_in_section("<!--", "--") ||
+            _xml_in_section("<\\?", "\\?") ||
+            _xml_in_section("<!", "")) {
+            continue;
+        }
+        if (substr($0, 1, 2) == "</") { # is it a closing tag ?
+            XML_TYPE = "END";
+            $0 = substr($0, 3);
+        } else { # nope, it's an opening one
+            XML_TYPE = "BEGIN";
+            $0 = substr($0, 2);
+        }
+        XML_TAG = $0
+        sub("[ \n\t/].*$", "", XML_TAG);  # extract tag name
+        XML_TAG = toupper(XML_TAG);       # uppercase it
+        if ( XML_TAG !~ /^[A-Z][-+_.:0-9A-Z]*$/ )  # validate it
+            _xml_panic("Invalid tag name: " XML_TAG);
+        if (XML_TYPE == "BEGIN") {  # update reverse path
+            _xml_enter(XML_TAG);
+        } else {
+            _xml_exit(XML_TAG);
+        }
+        sub("[^ \n\t]*[ \n\t]*", "", $0); # get rid of tag and spaces
+        while ($0) { # process attributes
+            if ($0 == "/") {  # deal with direct closing tag, e.g. </foo>
+                _xml_closing = XML_TAG; # record delayed tag closure.
+                break
+            }
+            _xml_attrib = $0;
+            sub(/=.*$/,"",_xml_attrib);  # extract attribute name
+            sub(/^[^=]*/,"",$0);         # remove it from record
+            _xml_attrib = tolower(_xml_attrib);
+            if ( _xml_attrib !~ /^[a-z][-+_0-9a-z:]*$/ ) # validate it
+                _xml_panic("Invalid attribute name: " _xml_attrib);
+            if (substr($0,1,2) == "=\"") { # value is ="something"
+                _xml_value = substr($0,3);
+                sub(/".*$/,"",_xml_value);
+                sub(/^="[^"]*"/,"",$0);
+            } else if (substr($0,1,2) == "='") { # value is ='something'
+                _xml_value = substr($0,3);
+                sub(/'.*$/,"",_xml_value);
+                sub(/^='[^']*'/,"",$0);
+            } else {
+                _xml_panic("Invalid attribute value syntax for " _xml_attrib ": " $0);
+            }
+            XML_ATTR[_xml_attrib] = _xml_value;  # store attribute name/value
+            sub(/^[ \t\n]*/,"",$0); # get rid of remaining leading spaces
+        }
+        return 1; # now return, XML_TYPE/TAG/ATTR/RPATH are set
+    }
+}
+
+function _xml_panic (msg) {
+    print msg > "/dev/stderr"
+    exit(1)
+}
+
+function _xml_in_section (sec_begin, sec_end) {
+    if (!match( $0, "^" sec_begin )) return 0;
+    while (!match($0, sec_end "$")) {
+        if (getline <= 0) _xml_panic("Unexpected EOF: " ERRNO);
+    }
+    return 1;
+}
+
+function _xml_enter (tag) {
+    XML_RPATH = tag "/" XML_RPATH;
+}
+
+function _xml_exit (tag) {
+    _xml_p = index(XML_RPATH, "/");
+    _xml_expected = substr(XML_RPATH, 1, _xml_p-1);
+    if (_xml_expected != XML_TAG)
+        _xml_panic("Unexpected close tag: " XML_TAG ", expecting " _xml_expected);
+    XML_RPATH = substr(XML_RPATH, _xml_p+1);
+}
diff --git a/build/awk/extract-launchable.awk b/build/awk/extract-launchable.awk
new file mode 100644 (file)
index 0000000..a4cfdf5
--- /dev/null
@@ -0,0 +1,186 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# A nawk/gawk script used to extract the list of launchable activities
+# from an application's manifest (i.e. AndroidManifest.xml). Usage:
+#
+#   awk -f <this-script> AndroidManifest.xml
+#
+
+#
+# Explanation:
+#
+# A given application can have several activities, and each activity
+# can have several intent filters. We want to only list, in the final
+# output, the activities which have a intent-filter that contains the
+# following elements:
+#
+#   <action android:name="android.intent.action.MAIN" />
+#   <category android:name="android.intent.category.LAUNCHER" />
+#
+# To do this, we need hooks called when entering and exiting <activity>
+# and <intent-filter> elements.
+#
+
+BEGIN {
+    while ( xml_event() ) {
+        # concat xml event type and tag for simpler comparisons
+        event = XML_TYPE "-" XML_TAG;
+        # When entering a new <activity>, extract its name and set
+        # the 'launchable' flag to false.
+        if ( event == "BEGIN-ACTIVITY" && 
+             XML_RPATH == "ACTIVITY/APPLICATION/MANIFEST/" ) {
+            name = XML_ATTR["android:name"];
+            launchable = 0;
+        }
+        # When exiting an <activity>, check that it has a name and
+        # is launchable. If so, print its name to the output
+        else if ( event == "END-ACTIVITY" &&
+                  XML_RPATH == "APPLICATION/MANIFEST/" ) {
+            if ( name && launchable ) {
+                print name;
+            }
+        }
+        # When entering an <intent-filter> inside an <activity>, clear
+        # the 'action' and 'category' variables. They are updated when
+        # we enter the corresponding elements within the intent-filter.
+        else if ( event == "BEGIN-INTENT-FILTER" &&
+                 XML_RPATH == "INTENT-FILTER/ACTIVITY/APPLICATION/MANIFEST/" ) {
+            action = ""
+            category = ""
+        }
+        # When exiting an <intent-filter>, set the 'launchable' flag to true
+        # for the current activity if both 'action' and 'category' have the
+        # correct name.
+        else if ( event == "END-INTENT-FILTER" &&
+                  XML_RPATH == "ACTIVITY/APPLICATION/MANIFEST/" ) {
+            if ( action == "android.intent.action.MAIN" &&
+                    category == "android.intent.category.LAUNCHER" ) {
+                    launchable = 1;
+            }
+        }
+        # When entering an <action> element inside an <intent-filter>, record
+        # its name.
+        else if ( event == "BEGIN-ACTION" &&
+                  XML_RPATH == "ACTION/INTENT-FILTER/ACTIVITY/APPLICATION/MANIFEST/" ) {
+            action = XML_ATTR["android:name"];
+        }
+        # When entering a <category> element inside an <intent-filter>, record
+        # its name.
+        else if ( event == "BEGIN-CATEGORY" &&
+                  XML_RPATH == "CATEGORY/INTENT-FILTER/ACTIVITY/APPLICATION/MANIFEST/" ) {
+            category = XML_ATTR["android:name"];
+        }
+    }
+}
+
+
+#
+# the following is copied directly from xml.awk - see this file for
+# usage and implementation details.
+#
+function xml_event () {
+    RS=">";
+    XML_TAG=XML_TYPE="";
+    split("", XML_ATTR);
+    while ( 1 ) {
+        if (_xml_closing) { # delayed direct tag closure
+            XML_TAG = _xml_closing;
+            XML_TYPE = "END";
+            _xml_closing = "";
+            _xml_exit(XML_TAG);
+            return 1;
+        }
+        if (getline <= 0) return 0; # read new input line
+        _xml_p = index($0, "<"); # get start marker
+        if (_xml_p == 0) return 0; # end of file (or malformed input)
+        $0 = substr($0, _xml_p) # remove anything before '<'
+        # ignore CData / Comments / Processing instructions / Declarations
+        if (_xml_in_section("<!\\[[Cc][Dd][Aa][Tt][Aa]\\[", "]]") ||
+            _xml_in_section("<!--", "--") ||
+            _xml_in_section("<\\?", "\\?") ||
+            _xml_in_section("<!", "")) {
+            continue;
+        }
+        if (substr($0, 1, 2) == "</") { # is it a closing tag ?
+            XML_TYPE = "END";
+            $0 = substr($0, 3);
+        } else { # nope, it's an opening one
+            XML_TYPE = "BEGIN";
+            $0 = substr($0, 2);
+        }
+        XML_TAG = $0
+        sub("[ \n\t/].*$", "", XML_TAG);  # extract tag name
+        XML_TAG = toupper(XML_TAG);       # uppercase it
+        if ( XML_TAG !~ /^[A-Z][-+_.:0-9A-Z]*$/ )  # validate it
+            _xml_panic("Invalid tag name: " XML_TAG);
+        if (XML_TYPE == "BEGIN") {  # update reverse path
+            _xml_enter(XML_TAG);
+        } else {
+            _xml_exit(XML_TAG);
+        }
+        sub("[^ \n\t]*[ \n\t]*", "", $0); # get rid of tag and spaces
+        while ($0) { # process attributes
+            if ($0 == "/") {  # deal with direct closing tag, e.g. </foo>
+                _xml_closing = XML_TAG; # record delayed tag closure.
+                break
+            }
+            _xml_attrib = $0;
+            sub(/=.*$/,"",_xml_attrib);  # extract attribute name
+            sub(/^[^=]*/,"",$0);         # remove it from record
+            _xml_attrib = tolower(_xml_attrib);
+            if ( _xml_attrib !~ /^[a-z][-+_0-9a-z:]*$/ ) # validate it
+                _xml_panic("Invalid attribute name: " _xml_attrib);
+            if (substr($0,1,2) == "=\"") { # value is ="something"
+                _xml_value = substr($0,3);
+                sub(/".*$/,"",_xml_value);
+                sub(/^="[^"]*"/,"",$0);
+            } else if (substr($0,1,2) == "='") { # value is ='something'
+                _xml_value = substr($0,3);
+                sub(/'.*$/,"",_xml_value);
+                sub(/^='[^']*'/,"",$0);
+            } else {
+                _xml_panic("Invalid attribute value syntax for " _xml_attrib ": " $0);
+            }
+            XML_ATTR[_xml_attrib] = _xml_value;  # store attribute name/value
+            sub(/^[ \t\n]*/,"",$0); # get rid of remaining leading spaces
+        }
+        return 1; # now return, XML_TYPE/TAG/ATTR/RPATH are set
+    }
+}
+
+function _xml_panic (msg) {
+    print msg > "/dev/stderr"
+    exit(1)
+}
+
+function _xml_in_section (sec_begin, sec_end) {
+    if (!match( $0, "^" sec_begin )) return 0;
+    while (!match($0, sec_end "$")) {
+        if (getline <= 0) _xml_panic("Unexpected EOF: " ERRNO);
+    }
+    return 1;
+}
+
+function _xml_enter (tag) {
+    XML_RPATH = tag "/" XML_RPATH;
+}
+
+function _xml_exit (tag) {
+    _xml_p = index(XML_RPATH, "/");
+    _xml_expected = substr(XML_RPATH, 1, _xml_p-1);
+    if (_xml_expected != XML_TAG)
+        _xml_panic("Unexpected close tag: " XML_TAG ", expecting " _xml_expected);
+    XML_RPATH = substr(XML_RPATH, _xml_p+1);
+}
diff --git a/build/awk/extract-package-name.awk b/build/awk/extract-package-name.awk
new file mode 100644 (file)
index 0000000..eefb070
--- /dev/null
@@ -0,0 +1,137 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# A nawk/gawk script used to extract the package name from an application's
+# manifest (i.e. AndroidManifest.xml). Usage is:
+#
+#   awk -f <this-script> AndroidManifest.xml
+#
+# The name itself is the value of the 'package' attribute in the
+# 'manifest' element.
+#
+
+BEGIN {
+    PACKAGE="";
+    while (xml_event()) {
+        # Simply extract the value of the 'name' attribute from
+        # the top-level <manifest> element.
+        if ( XML_TYPE == "BEGIN" && XML_RPATH == "MANIFEST/" ) {
+            PACKAGE = XML_ATTR["package"];
+            break;
+        }
+    }
+    if (!PACKAGE)
+        PACKAGE = "<none>";
+
+    print PACKAGE;
+}
+
+#
+# the following is copied directly from xml.awk - see this file for
+# usage and implementation details.
+#
+function xml_event () {
+    RS=">";
+    XML_TAG=XML_TYPE="";
+    split("", XML_ATTR);
+    while ( 1 ) {
+        if (_xml_closing) { # delayed direct tag closure
+            XML_TAG = _xml_closing;
+            XML_TYPE = "END";
+            _xml_closing = "";
+            _xml_exit(XML_TAG);
+            return 1;
+        }
+        if (getline <= 0) return 0; # read new input line
+        _xml_p = index($0, "<"); # get start marker
+        if (_xml_p == 0) return 0; # end of file (or malformed input)
+        $0 = substr($0, _xml_p) # remove anything before '<'
+        # ignore CData / Comments / Processing instructions / Declarations
+        if (_xml_in_section("<!\\[[Cc][Dd][Aa][Tt][Aa]\\[", "]]") ||
+            _xml_in_section("<!--", "--") ||
+            _xml_in_section("<\\?", "\\?") ||
+            _xml_in_section("<!", "")) {
+            continue;
+        }
+        if (substr($0, 1, 2) == "</") { # is it a closing tag ?
+            XML_TYPE = "END";
+            $0 = substr($0, 3);
+        } else { # nope, it's an opening one
+            XML_TYPE = "BEGIN";
+            $0 = substr($0, 2);
+        }
+        XML_TAG = $0
+        sub("[ \n\t/].*$", "", XML_TAG);  # extract tag name
+        XML_TAG = toupper(XML_TAG);       # uppercase it
+        if ( XML_TAG !~ /^[A-Z][-+_.:0-9A-Z]*$/ )  # validate it
+            _xml_panic("Invalid tag name: " XML_TAG);
+        if (XML_TYPE == "BEGIN") {  # update reverse path
+            _xml_enter(XML_TAG);
+        } else {
+            _xml_exit(XML_TAG);
+        }
+        sub("[^ \n\t]*[ \n\t]*", "", $0); # get rid of tag and spaces
+        while ($0) { # process attributes
+            if ($0 == "/") {  # deal with direct closing tag, e.g. </foo>
+                _xml_closing = XML_TAG; # record delayed tag closure.
+                break
+            }
+            _xml_attrib = $0;
+            sub(/=.*$/,"",_xml_attrib);  # extract attribute name
+            sub(/^[^=]*/,"",$0);         # remove it from record
+            _xml_attrib = tolower(_xml_attrib);
+            if ( _xml_attrib !~ /^[a-z][-+_0-9a-z:]*$/ ) # validate it
+                _xml_panic("Invalid attribute name: " _xml_attrib);
+            if (substr($0,1,2) == "=\"") { # value is ="something"
+                _xml_value = substr($0,3);
+                sub(/".*$/,"",_xml_value);
+                sub(/^="[^"]*"/,"",$0);
+            } else if (substr($0,1,2) == "='") { # value is ='something'
+                _xml_value = substr($0,3);
+                sub(/'.*$/,"",_xml_value);
+                sub(/^='[^']*'/,"",$0);
+            } else {
+                _xml_panic("Invalid attribute value syntax for " _xml_attrib ": " $0);
+            }
+            XML_ATTR[_xml_attrib] = _xml_value;  # store attribute name/value
+            sub(/^[ \t\n]*/,"",$0); # get rid of remaining leading spaces
+        }
+        return 1; # now return, XML_TYPE/TAG/ATTR/RPATH are set
+    }
+}
+
+function _xml_panic (msg) {
+    print msg > "/dev/stderr"
+    exit(1)
+}
+
+function _xml_in_section (sec_begin, sec_end) {
+    if (!match( $0, "^" sec_begin )) return 0;
+    while (!match($0, sec_end "$")) {
+        if (getline <= 0) _xml_panic("Unexpected EOF: " ERRNO);
+    }
+    return 1;
+}
+
+function _xml_enter (tag) {
+    XML_RPATH = tag "/" XML_RPATH;
+}
+
+function _xml_exit (tag) {
+    _xml_p = index(XML_RPATH, "/");
+    _xml_expected = substr(XML_RPATH, 1, _xml_p-1);
+    if (_xml_expected != XML_TAG)
+        _xml_panic("Unexpected close tag: " XML_TAG ", expecting " _xml_expected);
+    XML_RPATH = substr(XML_RPATH, _xml_p+1);
+}
diff --git a/build/awk/xml.awk b/build/awk/xml.awk
new file mode 100644 (file)
index 0000000..2cb28f5
--- /dev/null
@@ -0,0 +1,327 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Tiny XML parser implementation in awk.
+#
+# This file is not meant to be used directly, instead copy the
+# functions it defines here into your own script then specialize
+# it appropriately.
+#
+
+# See further below for usage instructions and implementation details.
+#
+
+# ---------------------------- cut here ---------------------------
+
+function xml_event () {
+    RS=">";
+    XML_TAG=XML_TYPE="";
+    split("", XML_ATTR);
+    while ( 1 ) {
+        if (_xml_closing) { # delayed direct tag closure
+            XML_TAG = _xml_closing;
+            XML_TYPE = "END";
+            _xml_closing = "";
+            _xml_exit(XML_TAG);
+            return 1;
+        }
+        if (getline <= 0) return 0; # read new input line
+        _xml_p = index($0, "<"); # get start marker
+        if (_xml_p == 0) return 0; # end of file (or malformed input)
+        $0 = substr($0, _xml_p) # remove anything before '<'
+        # ignore CData / Comments / Processing instructions / Declarations
+        if (_xml_in_section("<!\\[[Cc][Dd][Aa][Tt][Aa]\\[", "]]") ||
+            _xml_in_section("<!--", "--") ||
+            _xml_in_section("<\\?", "\\?") ||
+            _xml_in_section("<!", "")) {
+            continue;
+        }
+        if (substr($0, 1, 2) == "</") { # is it a closing tag ?
+            XML_TYPE = "END";
+            $0 = substr($0, 3);
+        } else { # nope, it's an opening one
+            XML_TYPE = "BEGIN";
+            $0 = substr($0, 2);
+        }
+        XML_TAG = $0
+        sub("[ \n\t/].*$", "", XML_TAG);  # extract tag name
+        XML_TAG = toupper(XML_TAG);       # uppercase it
+        if ( XML_TAG !~ /^[A-Z][-+_.:0-9A-Z]*$/ )  # validate it
+            _xml_panic("Invalid tag name: " XML_TAG);
+        if (XML_TYPE == "BEGIN") {  # update reverse path
+            _xml_enter(XML_TAG);
+        } else {
+            _xml_exit(XML_TAG);
+        }
+        sub("[^ \n\t]*[ \n\t]*", "", $0); # get rid of tag and spaces
+        while ($0) { # process attributes
+            if ($0 == "/") {  # deal with direct closing tag, e.g. </foo>
+                _xml_closing = XML_TAG; # record delayed tag closure.
+                break
+            }
+            _xml_attrib = $0;
+            sub(/=.*$/,"",_xml_attrib);  # extract attribute name
+            sub(/^[^=]*/,"",$0);         # remove it from record
+            _xml_attrib = tolower(_xml_attrib);
+            if ( _xml_attrib !~ /^[a-z][-+_0-9a-z:]*$/ ) # validate it
+                _xml_panic("Invalid attribute name: " _xml_attrib);
+            if (substr($0,1,2) == "=\"") { # value is ="something"
+                _xml_value = substr($0,3);
+                sub(/".*$/,"",_xml_value);
+                sub(/^="[^"]*"/,"",$0);
+            } else if (substr($0,1,2) == "='") { # value is ='something'
+                _xml_value = substr($0,3);
+                sub(/'.*$/,"",_xml_value);
+                sub(/^='[^']*'/,"",$0);
+            } else {
+                _xml_panic("Invalid attribute value syntax for " _xml_attrib ": " $0);
+            }
+            XML_ATTR[_xml_attrib] = _xml_value;  # store attribute name/value
+            sub(/^[ \t\n]*/,"",$0); # get rid of remaining leading spaces
+        }
+        return 1; # now return, XML_TYPE/TAG/ATTR/RPATH are set
+    }
+}
+
+function _xml_panic (msg) {
+    print msg > "/dev/stderr"
+    exit(1)
+}
+
+function _xml_in_section (sec_begin, sec_end) {
+    if (!match( $0, "^" sec_begin )) return 0;
+    while (!match($0, sec_end "$")) {
+        if (getline <= 0) _xml_panic("Unexpected EOF: " ERRNO);
+    }
+    return 1;
+}
+
+function _xml_enter (tag) {
+    XML_RPATH = tag "/" XML_RPATH;
+}
+
+function _xml_exit (tag) {
+    _xml_p = index(XML_RPATH, "/");
+    _xml_expected = substr(XML_RPATH, 1, _xml_p-1);
+    if (_xml_expected != XML_TAG)
+        _xml_panic("Unexpected close tag: " XML_TAG ", expecting " _xml_expected);
+    XML_RPATH = substr(XML_RPATH, _xml_p+1);
+}
+
+# ---------------------------- cut here ---------------------------
+
+# USAGE:
+#
+# The functions provided here are used to extract the tags and attributes of a
+# given XML file. They do not support extraction of data, CDATA, comments,
+# processing instructions and declarations at all.
+#
+# You should use this from the BEGIN {} action of your awk script (it will
+# not work from an END {} action).
+#
+# Call xml_event() in a while loop. This functions returns 1 for each XML
+# 'event' encountered, or 0 when the end of input is reached. Note that in
+# case of malformed output, an error will be printed and the script will
+# force an exit(1)
+#
+# After each succesful xml_event() call, the following variables will be set:
+#
+#    XML_TYPE:  type of event: "BEGIN" -> mean an opening tag, "END" a
+#               closing one.
+#
+#    XML_TAG:   name of the tag, always in UPPERCASE!
+#
+#    XML_ATTR:  a map of attributes for the type. Only set for "BEGIN" types.
+#               all attribute names are in lowercase.
+#
+#               beware: values are *not* unescaped !
+#
+#    XML_RPATH: the _reversed_ element path, using "/" as a separator.
+#               if you are within the <manifest><application> tag, then
+#               it will be set to "APPLICATION/MANIFEST/"
+#               (note the trailing slash).
+#
+
+# This is a simple example that dumps the output of the parsing.
+#
+BEGIN {
+    while ( xml_event() ) {
+        printf "XML_TYPE=%s XML_TAG=%s XML_RPATH=%s", XML_TYPE, XML_TAG, XML_RPATH;
+        if (XML_TYPE == "BEGIN") {
+            for (attr in XML_ATTR) {
+                printf " %s='%s'", attr, XML_ATTR[attr];
+            }
+        }
+        printf "\n";
+    }
+}
+
+# IMPLEMENTATION DETAILS:
+#
+# 1. '>' as the record separator:
+#
+# RS is set to '>' to use this character as the record separator, instead of
+# the default '\n'. This means that something like the following:
+#
+#   <foo><bar attrib="value">stuff</bar></foo>
+#
+# will be translated into the following successive 'records':
+#
+#  <foo
+#  <bar attrib="value"
+#  stuff</bar
+#  </foo
+#
+# Note that the '>' is never part of the records and thus will not be matched.
+# If the record does not contain a single '<', the input is either
+# malformed XML, or we reached the end of file with data after the last
+# '>'.
+#
+# Newlines in the original input are kept in the records as-is.
+#
+# 2. Getting rid of unwanted stuff:
+#
+# We don't need any of the data within elements, so we get rid of them by
+# simply ignoring anything before the '<' in the current record. This is
+# done with code like this:
+#
+#     p = index($0, "<");       # get index of '<'
+#     if (p == 0) -> return 0;  # malformed input or end of file
+#     $0 = substr($0, p+1);     # remove anything before the '<' in record
+#
+# We also want to ignore certain sections like CDATA, comments, declarations,
+# etc.. These begin with a certain pattern and end with another one, e.g.
+# "<!--" and "-->" for comments. This is handled by the _xml_in_section()
+# function that accepts two patterns as input:
+#
+#    sec_begin: is the pattern for the start of the record.
+#    sec_end:   is the pattern for the end of the record (minus trailing '>').
+#
+# The function deals with the fact that these section can embed a valid '>'
+# and will then span multiple records, i.e. something like:
+#
+#  <!-- A comment with an embedded > right here ! -->
+#
+# will be decomposed into two records:
+#
+#   "<!-- A comment with an embedded "
+#   " right here ! --"
+#
+# The function deals with this case, and exits when such a section is not
+# properly terminated in the input.
+#
+# _xml_in_section() returns 1 if an ignorable section was found, or 0 otherwise.
+#
+# 3. Extracting the tag name:
+#
+# </foo> is a closing tag, and <foo> an opening tag, this is handled
+# by the following code:
+#
+#       if (substr($0, 1, 2) == "</") {
+#           XML_TYPE = "END";
+#           $0 = substr($0, 3);
+#       } else {
+#           XML_TYPE = "BEGIN";
+#           $0 = substr($0, 2);
+#       }
+#
+# which defines XML_TYPE, and removes the leading "</" or "<" from the record.
+# The tag is later extracted and converted to uppercase with:
+#
+#       XML_TAG = $0                      # copy record
+#       sub("[ \n\t/].*$", "", XML_TAG);  # remove anything after tag name
+#       XML_TAG = toupper(XML_TAG);       # conver to uppercase
+#       # validate tag
+#       if ( XML_TAG !~ /^[A-Z][-+_.:0-9A-Z]*$/ ) -> panic
+#
+# Then the record is purged from the tag name and the spaces after it:
+#
+#       # get rid of tag and spaces after it in $0
+#       sub("[^ \n\t]*[ \n\t]*", "", $0);
+#
+# 4. Maintaining XML_RPATH:
+#
+# The _xml_enter() and _xml_exit() functions are called to maintain the
+# XML_RPATH variable when entering and exiting specific tags. _xml_exit()
+# will also validate the input, checking proper tag enclosure (or exit(1)
+# in case of error).
+#
+#       if (XML_TYPE == "BEGIN") {
+#           _xml_enter(XML_TAG);
+#       } else {
+#           _xml_exit(XML_TAG);
+#       }
+#
+# 5. Extracting attributes:
+#
+# A loop is implemented to parse attributes, the idea is to get the attribute
+# name, which is always followed by a '=' character:
+#
+#           _xml_attrib = $0;              # copy record.
+#           sub(/=.*$/,"",_xml_attrib);    # get rid of '=' and anything after.
+#           sub(/^[^=]*/,"",$0);           # remove attribute name from $0
+#           _xml_attrib = tolower(_xml_attrib);
+#           if ( _xml_attrib !~ /^[a-z][-+_0-9a-z:]*$/ )
+#               _xml_panic("Invalid attribute name: " _xml_attrib);
+#
+# Now get the value, which is enclosed by either (") or (')
+#
+#          if (substr($0,1,2) == "=\"") {        # if $0 begins with ="
+#               _xml_value = substr($0,3);       # extract value
+#               sub(/".*$/,"",_xml_value);  
+#               sub(/^="[^"]*"/,"",$0);          # remove it from $0
+#           } else if (substr($0,1,2) == "='") { # if $0 begins with ='
+#               _xml_value = substr($0,3);       # extract value
+#               sub(/'.*$/,"",_xml_value);
+#               sub(/^='[^']*'/,"",$0);          # remove it from $0
+#           } else {
+#               -> panic (malformed input)
+#           }
+#
+# After that, we simply store the value into the XML_ATTR associative
+# array, and cleanup $0 from leading spaces:
+#
+#           XML_ATTR[_xml_attrib] = _xml_value;
+#           sub(/^[ \t\n]*/,"",$0);
+#
+#
+# 6. Handling direct tag closure:
+#
+# When a tag is closed directly (as in <foo/>), A single '/' will be
+# parsed in the attribute parsing loop. We need to record this for the
+# next call to xml_event(), since the current one should return a"BEGIN"
+# for the "FOO" tag instead.
+#
+# We do this by setting the special _xml_closing variable, as in:
+#
+#          if ($0 == "/") {
+#               # record a delayed tag closure for the next call
+#               _xml_closing = XML_TAG;
+#               break
+#           }
+#
+# This variable is checked at the start of xml_event() like this:
+#
+#       # delayed tag closure - see below
+#       if (_xml_closing) {
+#           XML_TAG = _xml_closing;
+#           XML_TYPE = "END";
+#           _xml_closing = "";
+#           _xml_exit(XML_TAG);
+#           return 1;
+#       }
+#
+# Note the call to _xml_exit() to update XML_RPATH here.
+#
index 6e5805c..8b59ad5 100644 (file)
@@ -64,7 +64,7 @@ APP_PLATFORM := $(strip $(APP_PLATFORM))
 ifndef APP_PLATFORM
     _local_props := $(strip $(wildcard $(APP_PROJECT_PATH)/default.properties))
     ifdef _local_props
-        APP_PLATFORM := $(strip $(shell $(HOST_AWK) -f $(BUILD_SYSTEM)/extract-platform.awk < $(_local_props)))
+        APP_PLATFORM := $(strip $(shell $(HOST_AWK) -f $(BUILD_AWK)/extract-platform.awk < $(_local_props)))
         $(call ndk_log,  Found APP_PLATFORM=$(APP_PLATFORM) in $(_local_props))
     else
         APP_PLATFORM := android-3
diff --git a/build/core/extract-package-debuggable.awk b/build/core/extract-package-debuggable.awk
deleted file mode 100644 (file)
index 6b55a34..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# A nawk/gawk script used to extract the debuggable flag from an application's
-# manifest (i.e. AndroidManifest.xml).
-#
-# The name itself is the value of the 'android:debuggable' attribute in the
-# 'application' element.
-#
-
-BEGIN {
-    FS=" "
-    in_tag=0
-    regex1="android:debuggable=\"true\""
-    regex2="android:debuggable='true'"
-    DEBUGGABLE="false"
-}
-
-/<application/ {
-    in_tag=1
-}
-
-in_tag == 1 && /android:debuggable=/ {
-    if (match($0,regex1)) {
-        DEBUGGABLE=substr($0,RSTART+20,RLENGTH-21)
-    }
-    else if (match($0,regex2)) {
-        DEBUGGABLE=substr($0,RSTART+20,RLENGTH-21)
-    }
-}
-
-in_tag == 1 && />/ {
-    in_tag=0
-}
-
-END {
-    print DEBUGGABLE
-}
diff --git a/build/core/extract-package-name.awk b/build/core/extract-package-name.awk
deleted file mode 100644 (file)
index c2a28d8..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# A nawk/gawk script used to extract the package name from an application's
-# manifest (i.e. AndroidManifest.xml).
-#
-# The name itself is the value of the 'package' attribute in the
-# 'manifest' element.
-#
-
-BEGIN {
-    FS=" "
-    in_manifest=0
-    package_regex1="package=\"([[:alnum:].]+)\""
-    package_regex2="package='([[:alnum:].]+)'"
-    PACKAGE="<none>"
-}
-
-/<manifest/ {
-    in_manifest=1
-}
-
-in_manifest == 1 && /package=/ {
-    if (match($0,package_regex1)) {
-        PACKAGE=substr($0,RSTART+9,RLENGTH-10)
-    }
-    else if (match($0,package_regex2)) {
-        PACKAGE=substr($0,RSTART+9,RLENGTH-10)
-    }
-}
-
-in_manifest == 1 && />/ {
-    in_manifest=0
-}
-
-END {
-    print PACKAGE
-}
index c76a265..042f846 100644 (file)
@@ -161,7 +161,10 @@ else
     $(call ndk_log,Host awk tool from environment: $(HOST_AWK))
 endif
 
-AWK_TEST := $(shell $(HOST_AWK) -f $(NDK_ROOT)/build/check-awk.awk)
+# Location of all awk scripts we use
+BUILD_AWK := $(NDK_ROOT)/build/awk
+
+AWK_TEST := $(shell $(HOST_AWK) -f $(BUILD_AWK)/check-awk.awk)
 $(call ndk_log,Host awk test returned: $(AWK_TEST))
 ifneq ($(AWK_TEST),Pass)
     $(call __ndk_info,Host awk tool is outdated. Please define HOST_AWK to point to Gawk or Nawk !)
index b79acd8..1595786 100644 (file)
@@ -73,7 +73,7 @@ endif
 NDK_APP_DEBUGGABLE := false
 NDK_APP_MANIFEST := $(strip $(wildcard $(NDK_APP_PROJECT_PATH)/AndroidManifest.xml))
 ifdef NDK_APP_MANIFEST
-    NDK_APP_DEBUGGABLE := $(shell $(HOST_AWK) -f $(BUILD_SYSTEM)/extract-package-debuggable.awk $(NDK_APP_MANIFEST))
+    NDK_APP_DEBUGGABLE := $(shell $(HOST_AWK) -f $(BUILD_AWK)/extract-debuggable.awk $(NDK_APP_MANIFEST))
 endif
 
 ifdef NDK_LOG
index cba1667..a0cab59 100644 (file)
@@ -28,7 +28,7 @@ IMPORTANT: Native debugging can only work if *all* these conditions are met:
         In other words, your AndroidManifest.xml has an <application>
         element that sets the android:debuggable attribute to "true"
 
-    3. You are running your application on Android 2.2:
+    3. You are running your application on Android 2.2 (or higher):
 
         ndk-gdb will not work if you try to run your application on
         previous versions of the system. That does not mean that your
@@ -57,13 +57,6 @@ IMPORTANT: Native debugging can only work if *all* these conditions are met:
             not contain required support files and native debugging
             will not be possible.
 
-    4. Your application must be launched before ndk-gdb:
-
-        At the moment, it is not possible to directly launch your
-        application from ndk-gdb. You must thus start it before
-        invoking the script.
-
-
 'ndk-gdb' handles many error conditions and will dump an informative error
 message if it finds a problem. For example, it:
 
@@ -75,32 +68,63 @@ message if it finds a problem. For example, it:
       package name is also debuggable.
 
 
-When ndk-gdb launches succesfully, it stops the running application and
-gives you a GDB prompt. You can set breakpoints with 'b <location>' and
-resume execution with 'c' (for 'continue'). See the GDB manual for a list
-of commands.
+By default, ndk-gdb will search for an already-running application process,
+and will dump an error if it doesn't find one. You can however use the --start
+or --launch=<name> option to automatically start your activity before the
+debugging session.
 
-NOTE: ndk-gdb sets up GDB automatically so that it can find your sources
-      and generated libraries (with debug information) automatically.
+When it succesfully attaches to your application process, ndk-gdb will give
+you a normal GDB prompt, after setting up the session to properly look for
+your source files and symbol/debug versions of your generated native
+libraries.
+
+You can set breakpoints with 'b <location>' and resume execution with 'c'
+(for 'continue'). See the GDB manual for a list of commands.
 
 IMPORTANT: When quitting the GDB prompt, your debugged application process
            will be stopped! This is a gdb limitation.
 
+IMPORTANT: The GDB prompt will be preceded by a long list of error messages,
+           where gdb complains that it cannot find various system libraries
+           (e.g. libc.so, libstdc++.so, liblog.so, libcutils.so, etc...)
+
+           This is normal, because there are no symbol/debug versions of
+           these libraries corresponding to your target device on your
+           development machine. You can safely ignore these messages.
+
 II. Options:
 ------------
 
 To see a list of options, type 'ndk-gdb --help'. Notable ones are:
 
+  --verbose:
+    Print verbose information about the native debugging session setup.
+    Only needed to debug problems when you can't connect and that the
+    error messages printed by ndk-gdb are not enough.
+
   --force:
     By default, ndk-gdb aborts if it finds that another native debugging
     session is running on the same device. Using --force will kill the
     session, and replace it with a new one. Note that the debugged program
     is *not* killed and will be stopped again.
 
-  --verbose:
-    Print verbose information about the native debugging session setup.
-    Only needed to debug problems when you can't connect and that the
-    error messages printed by ndk-gdb are not enough.
+  --start:
+    By default, ndk-gdb will try to attach to an existing running instance
+    of your application on the target device. You can use --start to
+    explicitely launch your application before the debugging session.
+
+    NOTE: This launches the first launchable activity listed from your
+          application manifest. Use --launch=<name> to start another one.
+          See --launch-list to dump the list of such activites.
+
+  --launch=<name>:
+    This is similar to --start, except that it allows you to start a specific
+    activity from your application. This is only useful if your manifest
+    defines several launchable activities.
+
+  --launch-list:
+    Convenience option that prints the list of all launchable activity names
+    found in your application manifest. The first one will be used by --start
 
   --project=<path>:
     Specify application project directory. Useful if you want to launch
diff --git a/ndk-gdb b/ndk-gdb
index 93a930d..23462be 100755 (executable)
--- a/ndk-gdb
+++ b/ndk-gdb
@@ -41,6 +41,9 @@ OPTION_PROJECT=
 OPTION_FORCE=no
 OPTION_ADB=
 OPTION_EXEC=
+OPTION_START=no
+OPTION_LAUNCH=
+OPTION_LAUNCH_LIST=no
 
 check_parameter ()
 {
@@ -74,6 +77,12 @@ get_build_var_for_abi ()
     $GNUMAKE --no-print-dir -f $ANDROID_NDK_ROOT/build/core/build-local.mk -C $PROJECT DUMP_$1 APP_ABI=$2
 }
 
+# Used to run an awk script on the manifest
+run_awk_manifest_script ()
+{
+    $AWK_CMD -f $AWK_SCRIPTS/$1 $PROJECT/$MANIFEST
+}
+
 VERBOSE=no
 while [ -n "$1" ]; do
     opt="$1"
@@ -140,6 +149,15 @@ while [ -n "$1" ]; do
         --force)
             OPTION_FORCE="yes"
             ;;
+        --launch-list)
+            OPTION_LAUNCH_LIST="yes"
+            ;;
+        --launch=*)
+            OPTION_LAUNCH="$optarg"
+            ;;
+        --start)
+            OPTION_START=yes
+            ;;
         -*) # unknown options
             echo "ERROR: Unknown option '$opt', use --help for list of valid ones."
             exit 1
@@ -158,18 +176,24 @@ done
 if [ "$OPTION_HELP" = "yes" ] ; then
     echo "Usage: $PROGNAME [options]"
     echo ""
+    echo "Setup a gdb debugging session for your Android NDK application."
+    echo "Read $$NDK/docs/NDK-GDB.TXT for complete usage instructions."
+    echo ""
     echo "Valid options:"
     echo ""
     echo "    --help|-h|-?      Print this help"
     echo "    --verbose         Enable verbose mode"
-    echo "    --adb=<file>      Use specific adb command [$ADB_CMD]"
-    echo "    --awk=<file>      Use specific awk command [$AWK_CMD]"
-    echo "    --project=<path>  Specify application project path"
-    echo "    --port=<port>     Use tcp:localhost:<port> to communicate with gdbserver [$DEBUG_PORT]"
     echo "    --force           Kill existing debug session if it exists"
+    echo "    --start           Launch application instead of attaching to existing one"
+    echo "    --launch=<name>   Same as --start, but specify activity name (see below)"
+    echo "    --launch-list     List all launchable activity names from manifest"
+    echo "    --project=<path>  Specify application project path"
     echo "    -p <path>         Same as --project=<path>"
+    echo "    --port=<port>     Use tcp:localhost:<port> to communicate with gdbserver [$DEBUG_PORT]"
     echo "    --exec=<file>     Execute gdb initialization commands in <file> after connection"
     echo "    -x <file>         Same as --exec=<file>"
+    echo "    --adb=<file>      Use specific adb command [$ADB_CMD]"
+    echo "    --awk=<file>      Use specific awk command [$AWK_CMD]"
     echo "    -e                Connect to single emulator instance"
     echo "    -d                Connect to single target device"
     echo "    -s <serial>       Connect to specific emulator or device"
@@ -212,10 +236,10 @@ log "Using final ADB command: '$ADB_CMD'"
 
 
 # Check the awk tool
-AWK_SCRIPTS=$ANDROID_NDK_ROOT/build/core
-AWK_TEST=`$AWK_CMD -f $ANDROID_NDK_ROOT/build/check-awk.awk`
+AWK_SCRIPTS=$ANDROID_NDK_ROOT/build/awk
+AWK_TEST=`$AWK_CMD -f $AWK_SCRIPTS/check-awk.awk`
 if [ $? != 0 ] ; then
-    echo "ERROR: Could not run '$AWK_CMD' command. Do you have it installed properly ?"
+    echo "ERROR: Could not run '$AWK_CMD' command. Do you have it installed properly?"
     exit 1
 fi
 if [ "$AWK_TEST" != "Pass" ] ; then
@@ -262,7 +286,7 @@ else
 fi
 
 # Extract the package name from the manifest
-PACKAGE_NAME=`$AWK_CMD -f $AWK_SCRIPTS/extract-package-name.awk $PROJECT/$MANIFEST`
+PACKAGE_NAME=`run_awk_manifest_script extract-package-name.awk`
 log "Found package name: $PACKAGE_NAME"
 if [ $? != 0 -o "$PACKAGE_NAME" = "<none>" ] ; then
     echo "ERROR: Could not extract package name from $PROJECT/$MANIFEST."
@@ -270,8 +294,15 @@ if [ $? != 0 -o "$PACKAGE_NAME" = "<none>" ] ; then
     exit 1
 fi
 
+# If --launch-list is used, list all launchable activities, and be done with it
+if [ "$OPTION_LAUNCH_LIST" = "yes" ] ; then
+    log "Extracting list of launchable activities from manifest:"
+    run_awk_manifest_script extract-launchable.awk
+    exit 0
+fi
+
 # Check that the application is debuggable, or nothing will work
-DEBUGGABLE=`$AWK_CMD -f $AWK_SCRIPTS/extract-package-debuggable.awk $PROJECT/$MANIFEST`
+DEBUGGABLE=`run_awk_manifest_script extract-debuggable.awk`
 log "Found debuggable flag: $DEBUGGABLE"
 if [ $? != 0 -o "$DEBUGGABLE" != "true" ] ; then
     echo "ERROR: Package $PACKAGE_NAME is not debuggable ! Please fix your manifest,"
@@ -340,16 +371,58 @@ DATA_DIR=`echo "$DATA_DIR" | sed -e 's!\\r!!g'`
 log "Found data directory: '$DATA_DIR'"
 if [ $? != 0 ] ; then
     echo "ERROR: Could not extract package's data directory. Are you sure that"
-    echo "       your installed application is debuggable ?"
+    echo "       your installed application is debuggable?"
     exit 1
 fi
 
+# Launch the activity if needed
+if [ -n "$OPTION_START" ] ; then
+    # If --launch is used, ignore --start, otherwise extract the first
+    # launchable activity name from the manifest and use it as if --launch=<name>
+    # was used instead.
+    #
+    if [ -z "$OPTION_LAUNCH" ] ; then
+        OPTION_LAUNCH=`run_awk_manifest_script extract-launchable.awk | sed 2q`
+        if [ $? != 0 ] ; then
+            echo "ERROR: Could not extract name of launchable activity from manifest!"
+            echo "       Try to use --launch=<name> directly instead as a work-around."
+            exit 1
+        fi
+        log "Found first launchable activity: $OPTION_LAUNCH"
+        if [ -z "$OPTION_LAUNCH" ] ; then
+            echo "ERROR: It seems that your Application does not have any launchable activity!"
+            echo "       Please fix your manifest file and rebuild/re-install your application."
+            exit 1
+        fi
+    fi
+fi
+
+if [ -n "$OPTION_LAUNCH" ] ; then
+    log "Launching activity: $PACKAGE_NAME/$OPTION_LAUNCH"
+    run $ADB_CMD shell am start -n $PACKAGE_NAME/$OPTION_LAUNCH
+    if [ $? != 0 ] ; then
+        echo "ERROR: Could not launch specified activity: $OPTION_LAUNCH"
+        echo "       Use --launch-list to dump a list of valid values."
+        exit 1
+    fi
+    # Sleep a bit, it sometimes take one second to start properly
+    # Note that we use the 'sleep' command on the device here.
+    run $ADB_CMD shell sleep 1
+fi
+
 # Find the PID of the application being run
-PID=`$ADB_CMD shell ps | $AWK_CMD -f $AWK_SCRIPTS/extract-package-pid.awk -v PACKAGE=$PACKAGE_NAME`
+PID=`$ADB_CMD shell ps | $AWK_CMD -f $AWK_SCRIPTS/extract-pid.awk -v PACKAGE=$PACKAGE_NAME`
 log "Found running PID: $PID"
 if [ $? != 0 -o "$PID" = "0" ] ; then
     echo "ERROR: Could not extract PID of application on device/emulator."
-    echo "       Are you sure the application is already started ?"
+    if [ -n "$OPTION_LAUNCH" ] ; then
+        echo "       Weird, this probably means that the installed package does not"
+        echo "       match your current manifest. Try using the --verbose option and"
+        echo "       look at its output for details."
+    else
+        echo "       Are you sure the application is already started?"
+        echo "       Consider using --start or --launch=<name> if not."
+    fi
     exit 1
 fi
 
@@ -361,7 +434,7 @@ if [ -n "$GDBSERVER_PS" ] ; then
         exit 1
     fi
     log "Killing existing debugging session"
-    GDBSERVER_PID=`echo $GDBSERVER_PS | $AWK_CMD -f $AWK_SCRIPTS/extract-package-pid.awk -v PACKAGE=lib/gdbserver`
+    GDBSERVER_PID=`echo $GDBSERVER_PS | $AWK_CMD -f $AWK_SCRIPTS/extract-pid.awk -v PACKAGE=lib/gdbserver`
     if [ $GDBSERVER_PID != 0 ] ; then
         run $ADB_CMD shell kill -9 $GDBSERVER_PID
     fi
@@ -371,7 +444,7 @@ fi
 DEBUG_SOCKET=debug-socket
 run $ADB_CMD shell run-as $PACKAGE_NAME lib/gdbserver +$DEBUG_SOCKET --attach $PID &
 if [ $? != 0 ] ; then
-    echo "ERROR: Could not launch gdbserver on the device ?"
+    echo "ERROR: Could not launch gdbserver on the device?"
     exit 1
 fi
 log "Launched gdbserver succesfully."
@@ -380,8 +453,8 @@ log "Launched gdbserver succesfully."
 log "Setup network redirection"
 run $ADB_CMD forward tcp:$DEBUG_PORT localfilesystem:$DATA_DIR/$DEBUG_SOCKET
 if [ $? != 0 ] ; then
-    echo "ERROR: Could not setup network redirection to gdbserver ?"
-    echo "       Maybe using --port=<port> to use a different TCP port might help ?"
+    echo "ERROR: Could not setup network redirection to gdbserver?"
+    echo "       Maybe using --port=<port> to use a different TCP port might help?"
     exit 1
 fi