]> granicus.if.org Git - esp-idf/blobdiff - tools/idf.py
Merge branch 'bugfix/use_component_srcs' into 'master'
[esp-idf] / tools / idf.py
index 247fd3b9fdfcc18b50f74565f923ffada9e74e60..d68834a45f31c26c467734eefbfaa03e0e23c350 100755 (executable)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+
+# Note: we don't check for Python build-time dependencies until
+# check_environment() function below. If possible, avoid importing
+# any external libraries here - put in external script, or import in
+# their specific function instead.
 import sys
 import argparse
 import os
@@ -30,6 +35,7 @@ import multiprocessing
 import re
 import shutil
 import json
+import serial.tools.list_ports
 
 class FatalError(RuntimeError):
     """
@@ -41,7 +47,7 @@ class FatalError(RuntimeError):
 PYTHON=sys.executable
 
 # note: os.environ changes don't automatically propagate to child processes,
-# you have to pass this in explicitly
+# you have to pass env=os.environ explicitly anywhere that we create a process
 os.environ["PYTHON"]=sys.executable
 
 # Make flavors, across the various kinds of Windows environments & POSIX...
@@ -95,8 +101,18 @@ def check_environment():
             print("WARNING: IDF_PATH environment variable is set to %s but idf.py path indicates IDF directory %s. Using the environment variable directory, but results may be unexpected..."
                   % (set_idf_path, detected_idf_path))
     else:
+        print("Setting IDF_PATH environment variable: %s" % detected_idf_path)
         os.environ["IDF_PATH"] = detected_idf_path
 
+    # check Python dependencies
+    print("Checking Python dependencies...")
+    try:
+        subprocess.check_call([ os.environ["PYTHON"],
+                                os.path.join(os.environ["IDF_PATH"], "tools", "check_python_dependencies.py")],
+                              env=os.environ)
+    except subprocess.CalledProcessError:
+        raise SystemExit(1)
+
 def executable_exists(args):
     try:
         subprocess.check_output(args)
@@ -142,7 +158,7 @@ def _ensure_build_directory(args, always_run_cmake=False):
         if args.generator is None:
             args.generator = detect_cmake_generator()
         try:
-            cmake_args = ["cmake", "-G", args.generator]
+            cmake_args = ["cmake", "-G", args.generator, "-DPYTHON_DEPS_CHECKED=1"]
             if not args.no_warnings:
                 cmake_args += [ "--warn-uninitialized" ]
             if args.no_ccache:
@@ -221,9 +237,10 @@ def build_target(target_name, args):
 
 def _get_esptool_args(args):
     esptool_path = os.path.join(os.environ["IDF_PATH"], "components/esptool_py/esptool/esptool.py")
+    if args.port is None:
+        args.port = get_default_serial_port()
     result = [ PYTHON, esptool_path ]
-    if args.port is not None:
-        result += [ "-p", args.port ]
+    result += [ "-p", args.port ]
     result += [ "-b", str(args.baud) ]
     return result
 
@@ -252,6 +269,8 @@ def monitor(action, args):
     """
     Run idf_monitor.py to watch build output
     """
+    if args.port is None:
+        args.port = get_default_serial_port()
     desc_path = os.path.join(args.build_dir, "project_description.json")
     if not os.path.exists(desc_path):
         _ensure_build_directory(args)
@@ -267,9 +286,13 @@ def monitor(action, args):
         monitor_args += [ "-p", args.port ]
     monitor_args += [ "-b", project_desc["monitor_baud"] ]
     monitor_args += [ elf_file ]
+
+    idf_py = [ PYTHON ] + get_commandline_options()  # commands to re-run idf.py
+    monitor_args += [ "-m", " ".join("'%s'" % a for a in idf_py) ]
+
     if "MSYSTEM" is os.environ:
         monitor_args = [ "winpty" ] + monitor_args
-    _run_tool("idf_monitor", monitor_args, args.build_dir)
+    _run_tool("idf_monitor", monitor_args, args.project_dir)
 
 
 def clean(action, args):
@@ -305,34 +328,115 @@ def fullclean(action, args):
         else:
             os.remove(f)
 
+def print_closing_message(args):
+    # print a closing message of some kind
+    #
+
+    if "flash" in str(args.actions):
+        print("Done")
+        return
+
+    # Otherwise, if we built any binaries print a message about
+    # how to flash them
+    def print_flashing_message(title, key):
+        print("\n%s build complete. To flash, run this command:" % title)
+
+        with open(os.path.join(args.build_dir, "flasher_args.json")) as f:
+            flasher_args = json.load(f)
+
+        def flasher_path(f):
+            return os.path.relpath(os.path.join(args.build_dir, f))
+
+        if key != "project":  # flashing a single item
+            cmd = ""
+            if key == "bootloader":  # bootloader needs --flash-mode, etc to be passed in
+                cmd = " ".join(flasher_args["write_flash_args"]) + " "
+
+            cmd += flasher_args[key]["offset"] + " "
+            cmd += flasher_path(flasher_args[key]["file"])
+        else:  # flashing the whole project
+            cmd = " ".join(flasher_args["write_flash_args"]) + " "
+            flash_items = sorted(((o,f) for (o,f) in flasher_args["flash_files"].items() if len(o) > 0),
+                                 key = lambda x: int(x[0], 0))
+            for o,f in flash_items:
+                cmd += o + " " + flasher_path(f) + " "
+
+        print("%s -p %s -b %s write_flash %s" % (
+            os.path.relpath("%s/components/esptool_py/esptool/esptool.py" % os.environ["IDF_PATH"]),
+            args.port or "(PORT)",
+            args.baud,
+            cmd.strip()))
+        print("or run 'idf.py -p %s %s'" % (args.port or "(PORT)", key + "-flash" if key != "project" else "flash",))
+
+    if "all" in args.actions or "build" in args.actions:
+        print_flashing_message("Project", "project")
+    else:
+        if "app" in args.actions:
+            print_flashing_message("App", "app")
+        if "partition_table" in args.actions:
+            print_flashing_message("Partition Table", "partition_table")
+        if "bootloader" in args.actions:
+            print_flashing_message("Bootloader", "bootloader")
+
 ACTIONS = {
     # action name : ( function (or alias), dependencies, order-only dependencies )
     "all" :                  ( build_target, [], [ "reconfigure", "menuconfig", "clean", "fullclean" ] ),
     "build":                 ( "all",        [], [] ),  # build is same as 'all' target
     "clean":                 ( clean,        [], [ "fullclean" ] ),
     "fullclean":             ( fullclean,    [], [] ),
-    "reconfigure":           ( reconfigure,  [], [] ),
+    "reconfigure":           ( reconfigure,  [], [ "menuconfig" ] ),
     "menuconfig":            ( build_target, [], [] ),
-    "size":                  ( build_target, [], [ "app" ] ),
-    "size-components":       ( build_target, [], [ "app" ] ),
-    "size-files":            ( build_target, [], [ "app" ] ),
+    "confserver":            ( build_target, [], [] ),
+    "size":                  ( build_target, [ "app" ], [] ),
+    "size-components":       ( build_target, [ "app" ], [] ),
+    "size-files":            ( build_target, [ "app" ], [] ),
     "bootloader":            ( build_target, [], [] ),
     "bootloader-clean":      ( build_target, [], [] ),
-    "bootloader-flash":      ( flash,        [ "bootloader" ], [] ),
+    "bootloader-flash":      ( flash,        [ "bootloader" ], [ "erase_flash"] ),
     "app":                   ( build_target, [], [ "clean", "fullclean", "reconfigure" ] ),
-    "app-flash":             ( flash,        [], [ "app" ]),
+    "app-flash":             ( flash,        [ "app" ], [ "erase_flash"]),
     "partition_table":       ( build_target, [], [ "reconfigure" ] ),
-    "partition_table-flash": ( flash,        [ "partition_table" ], []),
-    "flash":                 ( flash,        [ "all" ], [ ] ),
+    "partition_table-flash": ( flash,        [ "partition_table" ], [ "erase_flash" ]),
+    "flash":                 ( flash,        [ "all" ], [ "erase_flash" ] ),
     "erase_flash":           ( erase_flash,  [], []),
     "monitor":               ( monitor,      [], [ "flash", "partition_table-flash", "bootloader-flash", "app-flash" ]),
 }
 
 
+def get_commandline_options():
+    """ Return all the command line options up to but not including the action """
+    result = []
+    for a in sys.argv:
+        if a in ACTIONS.keys():
+            break
+        else:
+            result.append(a)
+    return result
+
+def get_default_serial_port():
+    """ Return a default serial port. esptool can do this (smarter), but it can create
+    inconsistencies where esptool.py uses one port and idf_monitor uses another.
+
+    Same logic as esptool.py search order, reverse sort by name and choose the first port.
+    """
+    ports = list(reversed(sorted(
+        p.device for p in serial.tools.list_ports.comports() )))
+    try:
+        print ("Choosing default port %s (use '-p PORT' option to set a specific serial port)" % ports[0])
+        return ports[0]
+    except IndexError:
+        raise RuntimeError("No serial ports found. Connect a device, or use '-p PORT' option to set a specific port.")
+
+
 def main():
+    if sys.version_info[0] != 2 or sys.version_info[1] != 7:
+        raise FatalError("ESP-IDF currently only supports Python 2.7, and this is Python %d.%d.%d. Search for 'Setting the Python Interpreter' in the ESP-IDF docs for some tips to handle this." % sys.version_info[:3])
+
     parser = argparse.ArgumentParser(description='ESP-IDF build management tool')
-    parser.add_argument('-p', '--port', help="Serial port", default=None)
-    parser.add_argument('-b', '--baud', help="Baud rate", default=460800)
+    parser.add_argument('-p', '--port', help="Serial port",
+                        default=os.environ.get('ESPPORT', None))
+    parser.add_argument('-b', '--baud', help="Baud rate",
+                        default=os.environ.get('ESPBAUD', 460800))
     parser.add_argument('-C', '--project-dir', help="Project directory", default=os.getcwd())
     parser.add_argument('-B', '--build-dir', help="Build directory", default=None)
     parser.add_argument('-G', '--generator', help="Cmake generator", choices=GENERATOR_CMDS.keys())
@@ -374,10 +478,12 @@ def main():
 
         completed_actions.add(action)
 
-    while len(args.actions) > 0:
-        execute_action(args.actions[0], args.actions[1:])
-        args.actions.pop(0)
+    actions = list(args.actions)
+    while len(actions) > 0:
+        execute_action(actions[0], actions[1:])
+        actions.pop(0)
 
+    print_closing_message(args)
 
 if __name__ == "__main__":
     try: