# 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
import re
import shutil
import json
+import serial.tools.list_ports
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...
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)
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:
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
"""
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)
def flasher_path(f):
return os.path.relpath(os.path.join(args.build_dir, f))
- if key != "project":
+ if key != "project": # flashing a single item
cmd = ""
- if key == "bootloader":
+ 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:
+ else: # flashing the whole project
cmd = " ".join(flasher_args["write_flash_args"]) + " "
- for o,f in flasher_args["flash_files"].items():
+ 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" % (
args.port or "(PORT)",
args.baud,
cmd.strip()))
- print("or run 'idf.py %s'" % (key + "-flash" if key != "project" else "flash",))
+ 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")
"fullclean": ( fullclean, [], [] ),
"reconfigure": ( reconfigure, [], [ "menuconfig" ] ),
"menuconfig": ( build_target, [], [] ),
+ "confserver": ( build_target, [], [] ),
"size": ( build_target, [ "app" ], [] ),
"size-components": ( build_target, [ "app" ], [] ),
"size-files": ( build_target, [ "app" ], [] ),
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])