diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index d969719..6dcc2c4 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -24,53 +24,52 @@ import copy import json import subprocess import sys -from os import environ, listdir, makedirs, rename, pathsep -from os.path import ( - abspath, - basename, - dirname, - getmtime, - isabs, - isdir, - isfile, - join, - realpath, - relpath, -) +import os import click from SCons.Script import ( ARGUMENTS, COMMAND_LINE_TARGETS, - AlwaysBuild, DefaultEnvironment, ) -from platformio.builder.tools.piolib import ProjectAsLibBuilder -from platformio.fs import to_unix_path -from platformio.proc import exec_command, where_is_program +from platformio import fs +from platformio.proc import exec_command from platformio.util import get_systype +from platformio.builder.tools.piolib import ProjectAsLibBuilder env = DefaultEnvironment() -platform = env.PioPlatform() - env.SConscript("_embed_files.py", exports="env") -FRAMEWORK_DIR = platform.get_package_dir("framework-espidf") -assert FRAMEWORK_DIR and isdir(FRAMEWORK_DIR) +platform = env.PioPlatform() +board = env.BoardConfig() +mcu = board.get("build.mcu", "esp32") +idf_variant = board.get( + "build.esp-idf.variant", "esp32s2beta" if mcu == "esp32s2" else "esp32" +) +FRAMEWORK_DIR = platform.get_package_dir("framework-espidf") +TOOLCHAIN_DIR = platform.get_package_dir( + "toolchain-xtensa%s" % ("32s2" if mcu == "esp32s2" else "32") +) +assert os.path.isdir(FRAMEWORK_DIR) + +# Arduino framework as a component is not compatible with ESP-IDF >=4.1 if "arduino" in env.subst("$PIOFRAMEWORK"): ARDUINO_FRAMEWORK_DIR = platform.get_package_dir("framework-arduinoespressif32") # Possible package names in 'package@version' format is not compatible with CMake - if "@" in basename(ARDUINO_FRAMEWORK_DIR): - new_path = join( - dirname(ARDUINO_FRAMEWORK_DIR), - basename(ARDUINO_FRAMEWORK_DIR).replace("@", "-"), + if "@" in os.path.basename(ARDUINO_FRAMEWORK_DIR): + new_path = os.path.join( + os.path.dirname(ARDUINO_FRAMEWORK_DIR), + os.path.basename(ARDUINO_FRAMEWORK_DIR).replace("@", "-"), ) - rename(ARDUINO_FRAMEWORK_DIR, new_path) + os.rename(ARDUINO_FRAMEWORK_DIR, new_path) ARDUINO_FRAMEWORK_DIR = new_path - assert ARDUINO_FRAMEWORK_DIR and isdir(ARDUINO_FRAMEWORK_DIR) + assert ARDUINO_FRAMEWORK_DIR and os.path.isdir(ARDUINO_FRAMEWORK_DIR) + +BUILD_DIR = env.subst("$BUILD_DIR") +CMAKE_API_REPLY_PATH = os.path.join(".cmake", "api", "v1", "reply") try: import future @@ -79,23 +78,28 @@ try: except ImportError: env.Execute( env.VerboseAction( - '$PYTHONEXE -m pip install "cryptography>=2.1.4" "future>=0.15.2" "pyparsing>=2.0.3,<2.4.0"', + '$PYTHONEXE -m pip install "cryptography>=2.1.4" "future>=0.15.2" "pyparsing>=2.0.3,<2.4.0" ', "Installing ESP-IDF's Python dependencies", ) ) -platform = env.PioPlatform() -board = env.BoardConfig() +# a special "esp-windows-curses" python package is required on Windows for Menuconfig +if "windows" in get_systype(): + import pkg_resources -FRAMEWORK_DIR = platform.get_package_dir("framework-espidf") -assert isdir(FRAMEWORK_DIR) - -BUILD_DIR = env.subst("$BUILD_DIR") -CMAKE_API_REPLY_PATH = join(".cmake", "api", "v1", "reply") + if "esp-windows-curses" not in {pkg.key for pkg in pkg_resources.working_set}: + env.Execute( + env.VerboseAction( + '$PYTHONEXE -m pip install "file://%s/tools/kconfig_new/esp-windows-curses"' + % FRAMEWORK_DIR, + "Installing windows-curses package", + ) + ) def get_project_lib_includes(env): project = ProjectAsLibBuilder(env, "$PROJECT_DIR") + project.install_dependencies() project.search_deps_recursive() paths = [] @@ -111,25 +115,27 @@ def get_project_lib_includes(env): def is_cmake_reconfigure_required(cmake_api_reply_dir): - cmake_cache_file = join(BUILD_DIR, "CMakeCache.txt") + cmake_cache_file = os.path.join(BUILD_DIR, "CMakeCache.txt") cmake_txt_files = [ - join(env.subst("$PROJECT_DIR"), "CMakeLists.txt"), - join(env.subst("$PROJECT_SRC_DIR"), "CMakeLists.txt") + os.path.join(env.subst("$PROJECT_DIR"), "CMakeLists.txt"), + os.path.join(env.subst("$PROJECT_SRC_DIR"), "CMakeLists.txt"), ] - cmake_preconf_dir = join(BUILD_DIR, "config") - sdkconfig = join(env.subst("$PROJECT_DIR"), "sdkconfig") + cmake_preconf_dir = os.path.join(BUILD_DIR, "config") + sdkconfig = os.path.join(env.subst("$PROJECT_DIR"), "sdkconfig") for d in (cmake_api_reply_dir, cmake_preconf_dir): - if not isdir(d) or not listdir(d): + if not os.path.isdir(d) or not os.listdir(d): return True - if not isfile(cmake_cache_file): + if not os.path.isfile(cmake_cache_file): return True - if not isfile(join(BUILD_DIR, "build.ninja")): + if not os.path.isfile(os.path.join(BUILD_DIR, "build.ninja")): return True - if isfile(sdkconfig) and getmtime(sdkconfig) > getmtime(cmake_cache_file): + if os.path.isfile(sdkconfig) and os.path.getmtime(sdkconfig) > os.path.getmtime( + cmake_cache_file + ): return True if any( - getmtime(f) > getmtime(cmake_cache_file) + os.path.getmtime(f) > os.path.getmtime(cmake_cache_file) for f in cmake_txt_files + [cmake_preconf_dir] ): return True @@ -139,10 +145,10 @@ def is_cmake_reconfigure_required(cmake_api_reply_dir): def is_proper_idf_project(): return all( - isfile(path) + os.path.isfile(path) for path in ( - join(env.subst("$PROJECT_DIR"), "CMakeLists.txt"), - join(env.subst("$PROJECT_SRC_DIR"), "CMakeLists.txt"), + os.path.join(env.subst("$PROJECT_DIR"), "CMakeLists.txt"), + os.path.join(env.subst("$PROJECT_SRC_DIR"), "CMakeLists.txt"), ) ) @@ -159,7 +165,7 @@ def normalize_path(path): project_dir = env.subst("$PROJECT_DIR") if project_dir in path: path = path.replace(project_dir, "${CMAKE_SOURCE_DIR}") - return to_unix_path(path) + return fs.to_unix_path(path) def create_default_project_files(): @@ -175,33 +181,30 @@ FILE(GLOB_RECURSE app_sources %s/*.*) idf_component_register(SRCS ${app_sources}) """ - if not listdir(join(env.subst("$PROJECT_SRC_DIR"))): + if not os.listdir(os.path.join(env.subst("$PROJECT_SRC_DIR"))): # create a default main file to make CMake happy during first init - with open(join(env.subst("$PROJECT_SRC_DIR"), "main.c"), "w") as fp: + with open(os.path.join(env.subst("$PROJECT_SRC_DIR"), "main.c"), "w") as fp: fp.write("void app_main() {}") project_dir = env.subst("$PROJECT_DIR") - if not isfile(join(project_dir, "CMakeLists.txt")): - with open(join(project_dir, "CMakeLists.txt"), "w") as fp: - fp.write(root_cmake_tpl % basename(project_dir)) + if not os.path.isfile(os.path.join(project_dir, "CMakeLists.txt")): + with open(os.path.join(project_dir, "CMakeLists.txt"), "w") as fp: + fp.write(root_cmake_tpl % os.path.basename(project_dir)) project_src_dir = env.subst("$PROJECT_SRC_DIR") - if not isfile(join(project_src_dir, "CMakeLists.txt")): - with open(join(project_src_dir, "CMakeLists.txt"), "w") as fp: - fp.write( - prj_cmake_tpl - % normalize_path(env.subst("$PROJECT_SRC_DIR")) - ) + if not os.path.isfile(os.path.join(project_src_dir, "CMakeLists.txt")): + with open(os.path.join(project_src_dir, "CMakeLists.txt"), "w") as fp: + fp.write(prj_cmake_tpl % normalize_path(env.subst("$PROJECT_SRC_DIR"))) def get_cmake_code_model(src_dir, build_dir, extra_args=None): - cmake_api_dir = join(build_dir, ".cmake", "api", "v1") - cmake_api_query_dir = join(cmake_api_dir, "query") - cmake_api_reply_dir = join(cmake_api_dir, "reply") - query_file = join(cmake_api_query_dir, "codemodel-v2") + cmake_api_dir = os.path.join(build_dir, ".cmake", "api", "v1") + cmake_api_query_dir = os.path.join(cmake_api_dir, "query") + cmake_api_reply_dir = os.path.join(cmake_api_dir, "reply") + query_file = os.path.join(cmake_api_query_dir, "codemodel-v2") - if not isfile(query_file): - makedirs(dirname(query_file)) + if not os.path.isfile(query_file): + os.makedirs(os.path.dirname(query_file)) open(query_file, "a").close() # create an empty file if not is_proper_idf_project(): @@ -210,14 +213,14 @@ def get_cmake_code_model(src_dir, build_dir, extra_args=None): if is_cmake_reconfigure_required(cmake_api_reply_dir): run_cmake(src_dir, build_dir, extra_args) - if not isdir(cmake_api_reply_dir) or not listdir(cmake_api_reply_dir): + if not os.path.isdir(cmake_api_reply_dir) or not os.listdir(cmake_api_reply_dir): sys.stderr.write("Error: Couldn't find CMake API response file\n") env.Exit(1) codemodel = {} - for target in listdir(cmake_api_reply_dir): + for target in os.listdir(cmake_api_reply_dir): if target.startswith("codemodel-v2"): - with open(join(cmake_api_reply_dir, target), "r") as fp: + with open(os.path.join(cmake_api_reply_dir, target), "r") as fp: codemodel = json.load(fp) assert codemodel["version"]["major"] == 2 @@ -225,26 +228,25 @@ def get_cmake_code_model(src_dir, build_dir, extra_args=None): def populate_idf_env_vars(idf_env): - idf_env["IDF_PATH"] = platform.get_package_dir("framework-espidf") - + idf_env["IDF_PATH"] = FRAMEWORK_DIR additional_packages = [ - join(platform.get_package_dir("toolchain-xtensa32"), "bin"), - join(platform.get_package_dir("toolchain-esp32ulp"), "bin"), + os.path.join(TOOLCHAIN_DIR, "bin"), + os.path.join(platform.get_package_dir("toolchain-%sulp" % mcu), "bin"), platform.get_package_dir("tool-ninja"), - join(platform.get_package_dir("tool-cmake"), "bin"), - dirname(where_is_program("python")), + os.path.join(platform.get_package_dir("tool-cmake"), "bin"), + os.path.dirname(env.subst("$PYTHONEXE")), ] if "windows" in get_systype(): additional_packages.append(platform.get_package_dir("tool-mconf")) - idf_env["PATH"] = pathsep.join(additional_packages + [idf_env["PATH"]]) + idf_env["PATH"] = os.pathsep.join(additional_packages + [idf_env["PATH"]]) def get_target_config(project_configs, target_index, cmake_api_reply_dir): target_json = project_configs.get("targets")[target_index].get("jsonFile", "") - target_config_file = join(cmake_api_reply_dir, target_json) - if not isfile(target_config_file): + target_config_file = os.path.join(cmake_api_reply_dir, target_json) + if not os.path.isfile(target_config_file): sys.stderr.write("Error: Couldn't find target config %s\n" % target_json) env.Exit(1) @@ -269,12 +271,12 @@ def build_library(default_env, lib_config, project_src_dir, prepend_dir=None): lib_name = lib_config["nameOnDisk"] lib_path = lib_config["paths"]["build"] if prepend_dir: - lib_path = join(prepend_dir, lib_path) + lib_path = os.path.join(prepend_dir, lib_path) lib_objects = compile_source_files( lib_config, default_env, project_src_dir, prepend_dir ) return default_env.Library( - target=join("$BUILD_DIR", lib_path, lib_name), source=lib_objects + target=os.path.join("$BUILD_DIR", lib_path, lib_name), source=lib_objects ) @@ -331,17 +333,17 @@ def extract_link_args(target_config): elif fragment.startswith("-") and not fragment.startswith("-l"): # CMake mistakenly marks LINKFLAGS as libraries link_args["LINKFLAGS"].extend(args) - elif isfile(fragment) and isabs(fragment): + elif os.path.isfile(fragment) and os.path.isabs(fragment): # In case of precompiled archives from framework package - lib_path = dirname(fragment) + lib_path = os.path.dirname(fragment) if lib_path not in link_args["LIBPATH"]: - link_args["LIBPATH"].append(dirname(fragment)) + link_args["LIBPATH"].append(os.path.dirname(fragment)) link_args["LIBS"].extend( - [basename(l) for l in args if l.endswith(".a")] + [os.path.basename(lib) for lib in args if lib.endswith(".a")] ) elif fragment.endswith(".a"): link_args["__LIB_DEPS"].extend( - [basename(l) for l in args if l.endswith(".a")] + [os.path.basename(lib) for lib in args if lib.endswith(".a")] ) return link_args @@ -394,8 +396,8 @@ def get_app_flags(app_config, default_config): def get_sdk_configuration(): - config_path = join(env.subst("$BUILD_DIR"), "config", "sdkconfig.json") - if not isfile(config_path): + config_path = os.path.join(BUILD_DIR, "config", "sdkconfig.json") + if not os.path.isfile(config_path): print('Warning: Could not find "sdkconfig.json" file\n') try: @@ -410,38 +412,56 @@ def find_framework_service_files(search_path, sdk_config): result["lf_files"] = list() result["kconfig_files"] = list() result["kconfig_build_files"] = list() - for d in listdir(search_path): - path = join(search_path, d) - if not isdir(path): + for d in os.listdir(search_path): + path = os.path.join(search_path, d) + if not os.path.isdir(path): continue - for f in listdir(path): - if f == "linker.lf": - result["lf_files"].append(join(path, f)) + for f in os.listdir(path): + # Skip hardware specific files as they will be added later + if f == "linker.lf" and not os.path.basename(path).startswith("esp32"): + result["lf_files"].append(os.path.join(path, f)) elif f == "Kconfig.projbuild": - result["kconfig_build_files"].append(join(path, f)) + result["kconfig_build_files"].append(os.path.join(path, f)) elif f == "Kconfig": - result["kconfig_files"].append(join(path, f)) + result["kconfig_files"].append(os.path.join(path, f)) - result["lf_files"].extend([ - join(FRAMEWORK_DIR, "components", "esp32", "ld", "esp32_fragments.lf"), - join(FRAMEWORK_DIR, "components", "newlib", "newlib.lf") - ]) + result["lf_files"].extend( + [ + os.path.join( + FRAMEWORK_DIR, + "components", + idf_variant, + "ld", + "%s_fragments.lf" % idf_variant, + ), + os.path.join( + FRAMEWORK_DIR, + "components", + idf_variant, + "linker.lf", + ), + os.path.join(FRAMEWORK_DIR, "components", "newlib", "newlib.lf"), + ] + ) if sdk_config.get("SPIRAM_CACHE_WORKAROUND", False): - result["lf_files"].append(join( - FRAMEWORK_DIR, "components", "newlib", "esp32-spiram-rom-functions-c.lf")) + result["lf_files"].append( + os.path.join( + FRAMEWORK_DIR, "components", "newlib", "esp32-spiram-rom-functions-c.lf" + ) + ) return result def create_custom_libraries_list(ldgen_libraries_file, ignore_targets): - if not isfile(ldgen_libraries_file): + if not os.path.isfile(ldgen_libraries_file): sys.stderr.write("Error: Couldn't find the list of framework libraries\n") env.Exit(1) pio_libraries_file = ldgen_libraries_file + "_pio" - if isfile(pio_libraries_file): + if os.path.isfile(pio_libraries_file): return pio_libraries_file lib_paths = [] @@ -462,26 +482,24 @@ def create_custom_libraries_list(ldgen_libraries_file, ignore_targets): def generate_project_ld_script(sdk_config, ignore_targets=None): ignore_targets = ignore_targets or [] project_files = find_framework_service_files( - join(FRAMEWORK_DIR, "components"), sdk_config + os.path.join(FRAMEWORK_DIR, "components"), sdk_config ) # Create a new file to avoid automatically generated library entry as files from # this library are built internally by PlatformIO libraries_list = create_custom_libraries_list( - join(env.subst("$BUILD_DIR"), "ldgen_libraries"), ignore_targets + os.path.join(BUILD_DIR, "ldgen_libraries"), ignore_targets ) args = { - "script": join(FRAMEWORK_DIR, "tools", "ldgen", "ldgen.py"), - "config": join(env.subst("$PROJECT_DIR"), "sdkconfig"), + "script": os.path.join(FRAMEWORK_DIR, "tools", "ldgen", "ldgen.py"), + "config": os.path.join(env.subst("$PROJECT_DIR"), "sdkconfig"), "fragments": " ".join(['"%s"' % f for f in project_files.get("lf_files")]), - "kconfig": join(FRAMEWORK_DIR, "Kconfig"), - "env_file": join("$BUILD_DIR", "config.env"), + "kconfig": os.path.join(FRAMEWORK_DIR, "Kconfig"), + "env_file": os.path.join("$BUILD_DIR", "config.env"), "libraries_list": libraries_list, - "objdump": join( - platform.get_package_dir("toolchain-xtensa32"), - "bin", - env.subst("$CC").replace("-gcc", "-objdump"), + "objdump": os.path.join( + TOOLCHAIN_DIR, "bin", env.subst("$CC").replace("-gcc", "-objdump"), ), } @@ -494,8 +512,14 @@ def generate_project_ld_script(sdk_config, ignore_targets=None): ).format(**args) return env.Command( - join("$BUILD_DIR", "esp32.project.ld"), - join(FRAMEWORK_DIR, "components", "esp32", "ld", "esp32.project.ld.in"), + os.path.join("$BUILD_DIR", "%s.project.ld" % idf_variant), + os.path.join( + FRAMEWORK_DIR, + "components", + idf_variant, + "ld", + "%s.project.ld.in" % idf_variant, + ), env.VerboseAction(cmd, "Generating project linker script $TARGET"), ) @@ -546,17 +570,21 @@ def compile_source_files(config, default_env, project_src_dir, prepend_dir=None) compile_group_idx = source.get("compileGroupIndex") if compile_group_idx is not None: src_path = source.get("path") - if not isabs(src_path): + if not os.path.isabs(src_path): # For cases when sources are located near CMakeLists.txt - src_path = join(project_src_dir, src_path) + src_path = os.path.join(project_src_dir, src_path) local_path = config["paths"]["source"] - if not isabs(local_path): - local_path = join(project_src_dir, config["paths"]["source"]) - obj_path = join("$BUILD_DIR", prepend_dir or "", config["paths"]["build"]) + if not os.path.isabs(local_path): + local_path = os.path.join(project_src_dir, config["paths"]["source"]) + obj_path = os.path.join( + "$BUILD_DIR", prepend_dir or "", config["paths"]["build"] + ) objects.append( build_envs[compile_group_idx].StaticObject( - target=join(obj_path, relpath(src_path, local_path) + ".o"), - source=realpath(src_path), + target=os.path.join( + obj_path, os.path.relpath(src_path, local_path) + ".o" + ), + source=os.path.realpath(src_path), ) ) @@ -564,7 +592,7 @@ def compile_source_files(config, default_env, project_src_dir, prepend_dir=None) def run_tool(cmd): - idf_env = environ.copy() + idf_env = os.environ.copy() populate_idf_env_vars(idf_env) result = exec_command(cmd, env=idf_env) @@ -579,14 +607,14 @@ def run_tool(cmd): def RunMenuconfig(target, source, env): - idf_env = environ.copy() + idf_env = os.environ.copy() populate_idf_env_vars(idf_env) rc = subprocess.call( [ - join(platform.get_package_dir("tool-cmake"), "bin", "cmake"), + os.path.join(platform.get_package_dir("tool-cmake"), "bin", "cmake"), "--build", - env.subst("$BUILD_DIR"), + BUILD_DIR, "--target", "menuconfig", ], @@ -598,9 +626,29 @@ def RunMenuconfig(target, source, env): env.Exit(1) +def ReconfigureProject(target, source, env): + idf_env = os.environ.copy() + populate_idf_env_vars(idf_env) + + rc = subprocess.call( + [ + os.path.join(platform.get_package_dir("tool-cmake"), "bin", "cmake"), + "--build", + BUILD_DIR, + "--target", + "reconfigure", + ], + env=idf_env, + ) + + if rc != 0: + sys.stderr.write("Error: Couldn't execute 'reconfigure' target.\n") + env.Exit(1) + + def run_cmake(src_dir, build_dir, extra_args=None): cmd = [ - join(platform.get_package_dir("tool-cmake") or "", "bin", "cmake"), + os.path.join(platform.get_package_dir("tool-cmake") or "", "bin", "cmake"), "-S", src_dir, "-B", @@ -642,16 +690,21 @@ def find_lib_deps(components_map, elf_config, link_args, ignore_components=None) def build_bootloader(): - bootloader_src_dir = join(FRAMEWORK_DIR, "components", "bootloader", "subproject") + bootloader_src_dir = os.path.join( + FRAMEWORK_DIR, "components", "bootloader", "subproject" + ) code_model = get_cmake_code_model( bootloader_src_dir, - join(BUILD_DIR, "bootloader"), + os.path.join(BUILD_DIR, "bootloader"), [ - "-DIDF_TARGET=esp32", + "-DIDF_TARGET=" + idf_variant, "-DPYTHON_DEPS_CHECKED=1", - "-DIDF_PATH=" + platform.get_package_dir("framework-espidf"), - "-DSDKCONFIG=" + join(env.subst("$PROJECT_DIR"), "sdkconfig"), - "-DEXTRA_COMPONENT_DIRS=" + join(FRAMEWORK_DIR, "components", "bootloader"), + "-DPYTHON=" + env.subst("$PYTHONEXE"), + "-DIDF_PATH=" + FRAMEWORK_DIR, + "-DSDKCONFIG=" + os.path.join(env.subst("$PROJECT_DIR"), "sdkconfig"), + "-DLEGACY_INCLUDE_COMMON_HEADERS=", + "-DEXTRA_COMPONENT_DIRS=" + + os.path.join(FRAMEWORK_DIR, "components", "bootloader"), ], ) @@ -660,7 +713,8 @@ def build_bootloader(): env.Exit(1) target_configs = load_target_configurations( - code_model, join(BUILD_DIR, "bootloader", CMAKE_API_REPLY_PATH) + code_model, + os.path.join(BUILD_DIR, "bootloader", ".cmake", "api", "v1", "reply"), ) elf_config = get_project_elf(target_configs) @@ -692,8 +746,10 @@ def build_bootloader(): ) return bootloader_env.ElfToBin( - join("$BUILD_DIR", "bootloader"), - bootloader_env.Program(join("$BUILD_DIR", "bootloader.elf"), bootloader_libs), + os.path.join("$BUILD_DIR", "bootloader"), + bootloader_env.Program( + os.path.join("$BUILD_DIR", "bootloader.elf"), bootloader_libs + ), ) @@ -743,17 +799,17 @@ def generate_default_component(): file(GLOB component_sources *.c* *.S) idf_component_register(SRCS ${component_sources}) """ - dummy_component_path = join(BUILD_DIR, "__pio_env") - if not isdir(dummy_component_path): - makedirs(dummy_component_path) + dummy_component_path = os.path.join(BUILD_DIR, "__pio_env") + if not os.path.isdir(dummy_component_path): + os.makedirs(dummy_component_path) for ext in (".cpp", ".c", ".S"): - dummy_file = join(dummy_component_path, "__dummy" + ext) - if not isfile(dummy_file): + dummy_file = os.path.join(dummy_component_path, "__dummy" + ext) + if not os.path.isfile(dummy_file): open(dummy_file, "a").close() - component_cmake = join(dummy_component_path, "CMakeLists.txt") - if not isfile(component_cmake): + component_cmake = os.path.join(dummy_component_path, "CMakeLists.txt") + if not os.path.isfile(component_cmake): with open(component_cmake, "w") as fp: fp.write(prj_cmake_tpl) @@ -767,16 +823,32 @@ def find_default_component(target_configs): return "" +def create_verion_file(): + version_file = os.path.join(FRAMEWORK_DIR, "version.txt") + if not os.path.isfile(version_file): + with open(version_file, "w") as fp: + fp.write(platform.get_package_version("framework-espidf")) + + +# +# ESP-IDF package doesn't contain .git folder, instead package version is specified +# in a special file "version.h" in the root folder of the package +# + +create_verion_file() + # # Generate final linker script # if not board.get("build.ldscript", ""): linker_script = env.Command( - join("$BUILD_DIR", "esp32_out.ld"), + os.path.join("$BUILD_DIR", "%s_out.ld" % idf_variant), board.get( "build.esp-idf.ldscript", - join(FRAMEWORK_DIR, "components", "esp32", "ld", "esp32.ld"), + os.path.join( + FRAMEWORK_DIR, "components", idf_variant, "ld", "%s.ld" % idf_variant + ), ), env.VerboseAction( '$CC -I"$BUILD_DIR/config" -C -P -x c -E $SOURCE -o $TARGET', @@ -785,29 +857,31 @@ if not board.get("build.ldscript", ""): ) env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", linker_script) - env.Replace(LDSCRIPT_PATH="esp32_out.ld") + env.Replace(LDSCRIPT_PATH="%s_out.ld" % idf_variant) # # Generate partition table # -fwpartitions_dir = join(FRAMEWORK_DIR, "components", "partition_table") +fwpartitions_dir = os.path.join(FRAMEWORK_DIR, "components", "partition_table") partitions_csv = board.get("build.partitions", "partitions_singleapp.csv") env.Replace( - PARTITIONS_TABLE_CSV=abspath( - join(fwpartitions_dir, partitions_csv) - if isfile(join(fwpartitions_dir, partitions_csv)) + PARTITIONS_TABLE_CSV=os.path.abspath( + os.path.join(fwpartitions_dir, partitions_csv) + if os.path.isfile(os.path.join(fwpartitions_dir, partitions_csv)) else partitions_csv ) ) partition_table = env.Command( - join("$BUILD_DIR", "partitions.bin"), + os.path.join("$BUILD_DIR", "partitions.bin"), "$PARTITIONS_TABLE_CSV", env.VerboseAction( '"$PYTHONEXE" "%s" -q --flash-size "%s" $SOURCE $TARGET' % ( - join(FRAMEWORK_DIR, "components", "partition_table", "gen_esp32part.py"), + os.path.join( + FRAMEWORK_DIR, "components", "partition_table", "gen_esp32part.py" + ), board.get("upload.flash_size", "4MB"), ), "Generating partitions $TARGET", @@ -833,7 +907,7 @@ if env.subst("$SRC_FILTER"): ) ) -if isfile(join(env.subst("$PROJECT_SRC_DIR"), "sdkconfig.h")): +if os.path.isfile(os.path.join(env.subst("$PROJECT_SRC_DIR"), "sdkconfig.h")): print( "Warning! Starting with ESP-IDF v4.0, new project structure is required: \n" "https://docs.platformio.org/en/latest/frameworks/espidf.html#project-structure" @@ -847,7 +921,7 @@ if isfile(join(env.subst("$PROJECT_SRC_DIR"), "sdkconfig.h")): # default 'src' folder we need to add this as an extra component. If there is no 'main' # folder CMake won't generate dependencies properly extra_components = [generate_default_component()] -if env.subst("$PROJECT_SRC_DIR") != join(env.subst("$PROJECT_DIR"), "main"): +if env.subst("$PROJECT_SRC_DIR") != os.path.join(env.subst("$PROJECT_DIR"), "main"): extra_components.append(env.subst("$PROJECT_SRC_DIR")) if "arduino" in env.subst("$PIOFRAMEWORK"): extra_components.append(ARDUINO_FRAMEWORK_DIR) @@ -856,8 +930,11 @@ print("Reading CMake configuration...") project_codemodel = get_cmake_code_model( env.subst("$PROJECT_DIR"), BUILD_DIR, - ["-DEXTRA_COMPONENT_DIRS:PATH=" + ";".join(extra_components)] + - click.parser.split_arg_string(board.get("build.cmake_extra_args", "")) + [ + "-DIDF_TARGET=" + idf_variant, + "-DEXTRA_COMPONENT_DIRS:PATH=" + ";".join(extra_components), + ] + + click.parser.split_arg_string(board.get("build.cmake_extra_args", "")), ) if not project_codemodel: @@ -865,13 +942,13 @@ if not project_codemodel: env.Exit(1) target_configs = load_target_configurations( - project_codemodel, join(BUILD_DIR, CMAKE_API_REPLY_PATH) + project_codemodel, os.path.join(BUILD_DIR, CMAKE_API_REPLY_PATH) ) sdk_config = get_sdk_configuration() -project_target_name = "__idf_%s" % basename(env.subst("$PROJECT_SRC_DIR")) +project_target_name = "__idf_%s" % os.path.basename(env.subst("$PROJECT_SRC_DIR")) if project_target_name not in target_configs: sys.stderr.write("Error: Couldn't find the main target of the project!\n") env.Exit(1) @@ -887,7 +964,8 @@ if project_target_name != "__idf_main" and "__idf_main" in target_configs: env.Exit(1) project_ld_scipt = generate_project_ld_script( - sdk_config, [project_target_name, "__pio_env"]) + sdk_config, [project_target_name, "__pio_env"] +) env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", project_ld_scipt) elf_config = get_project_elf(target_configs) @@ -925,8 +1003,21 @@ env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", build_bootloader()) # Target: ESP-IDF menuconfig # -env.AddPlatformTarget("menuconfig", None, [env.VerboseAction( - RunMenuconfig, "Running menuconfig...")], "Run Menuconfig") +env.AddPlatformTarget( + "menuconfig", + None, + [env.VerboseAction(RunMenuconfig, "Running menuconfig...")], + "Run Menuconfig", +) + + +env.AddPlatformTarget( + "reconfigure", + None, + [env.VerboseAction(ReconfigureProject, "Reconfiguring project...")], + "Reconfigure Project", +) + # # Process main parts of the framework @@ -943,7 +1034,7 @@ link_args["LINKFLAGS"] = sorted(list(set(link_args["LINKFLAGS"]) - set(extra_fla # remove the main linker script flags '-T esp32_out.ld' try: - ld_index = extra_flags.index("esp32_out.ld") + ld_index = extra_flags.index("%s_out.ld" % idf_variant) extra_flags.pop(ld_index) extra_flags.pop(ld_index - 1) except: @@ -953,6 +1044,7 @@ except: # Process project sources # + # Remove project source files from following build stages as they're # built as part of the framework def _skip_prj_source_files(node): @@ -1002,8 +1094,8 @@ env.Prepend( LINKFLAGS=extra_flags, LIBS=libs, FLASH_EXTRA_IMAGES=[ - ("0x1000", join("$BUILD_DIR", "bootloader.bin")), - ("0x8000", join("$BUILD_DIR", "partitions.bin")), + ("0x1000", os.path.join("$BUILD_DIR", "bootloader.bin")), + ("0x8000", os.path.join("$BUILD_DIR", "partitions.bin")), ], ) @@ -1013,13 +1105,14 @@ env.Prepend( action = copy.deepcopy(env["BUILDERS"]["ElfToBin"].action) action.cmd_list = env["BUILDERS"]["ElfToBin"].action.cmd_list.replace( - "-o", "--elf-sha256-offset 0xb0 -o") + "-o", "--elf-sha256-offset 0xb0 -o" +) env["BUILDERS"]["ElfToBin"].action = action # # Compile ULP sources in 'ulp' folder # -ulp_dir = join(env.subst("$PROJECT_DIR"), "ulp") -if isdir(ulp_dir) and listdir(ulp_dir): - env.SConscript("ulp.py", exports="env project_config") +ulp_dir = os.path.join(env.subst("$PROJECT_DIR"), "ulp") +if os.path.isdir(ulp_dir) and os.listdir(ulp_dir): + env.SConscript("ulp.py", exports="env project_config idf_variant") diff --git a/builder/frameworks/ulp.py b/builder/frameworks/ulp.py index e797cdd..ffd80bc 100644 --- a/builder/frameworks/ulp.py +++ b/builder/frameworks/ulp.py @@ -13,14 +13,13 @@ # limitations under the License. import os -import sys -from SCons.Script import COMMAND_LINE_TARGETS, Import, Return +from SCons.Script import Import from platformio.util import get_systype from platformio.proc import where_is_program -Import("env project_config") +Import("env project_config idf_variant") ulp_env = env.Clone() platform = ulp_env.PioPlatform() @@ -79,7 +78,7 @@ def generate_ulp_config(target_config): "components", "ulp", "cmake", - "toolchain-ulp.cmake", + "toolchain-%s-ulp.cmake" % idf_variant, ), '-DULP_S_SOURCES="%s"' % ";".join(ulp_sources), "-DULP_APP_NAME=ulp_main", @@ -87,7 +86,7 @@ def generate_ulp_config(target_config): '-DCOMPONENT_INCLUDES="%s"' % ";".join(get_component_includes(target_config)), "-DIDF_PATH=" + FRAMEWORK_DIR, "-DSDKCONFIG=" + os.path.join(BUILD_DIR, "config", "sdkconfig.h"), - "-DPYTHON=python", + "-DPYTHON=" + env.subst("$PYTHONEXE"), "-GNinja", "-B", ULP_BUILD_DIR, diff --git a/platform.json b/platform.json index 4bdf029..22e03f2 100644 --- a/platform.json +++ b/platform.json @@ -68,7 +68,13 @@ "type": "toolchain", "optional": true, "owner": "platformio", - "version": "~1.22851.190618" + "version": "~1.22851.0" + }, + "toolchain-esp32s2ulp": { + "type": "toolchain", + "optional": true, + "owner": "platformio", + "version": "~1.22851.0" }, "framework-arduinoespressif32": { "type": "framework", @@ -86,7 +92,7 @@ "type": "framework", "optional": true, "owner": "platformio", - "version": "~3.40001.0" + "version": "~3.40100.0" }, "framework-simba": { "type": "framework", diff --git a/platform.py b/platform.py index 4ee80e4..8f85616 100644 --- a/platform.py +++ b/platform.py @@ -26,25 +26,29 @@ class Espressif32Platform(PlatformBase): self, variables, targets) board_config = self.board_config(variables.get("board")) - mcu = variables.get("board_build.mcu", board_config.get( - "build.mcu", "esp32")) + mcu = variables.get("board_build.mcu", board_config.get("build.mcu", "esp32")) + frameworks = variables.get("pioframework", []) if "buildfs" in targets: self.packages['tool-mkspiffs']['optional'] = False if variables.get("upload_protocol"): self.packages['tool-openocd-esp32']['optional'] = False if isdir("ulp"): self.packages['toolchain-esp32ulp']['optional'] = False - if "espidf" in variables.get("pioframework", []): + if "espidf" in frameworks: for p in self.packages: if p in ("tool-cmake", "tool-ninja", "toolchain-%sulp" % mcu): self.packages[p]["optional"] = False elif p in ("tool-mconf", "tool-idf") and "windows" in get_systype(): self.packages[p]['optional'] = False self.packages['toolchain-xtensa32']['version'] = "~2.80200.0" + if "arduino" in frameworks: + # Arduino component is not compatible with ESP-IDF >=4.1 + self.packages['framework-espidf']['version'] = "~3.40001.0" # ESP32-S2 toolchain is identical for both Arduino and ESP-IDF if mcu == "esp32s2": self.packages.pop("toolchain-xtensa32", None) self.packages['toolchain-xtensa32s2']['optional'] = False + self.packages['toolchain-esp32s2ulp']['optional'] = False self.packages['tool-esptoolpy']['version'] = "~1.30000.0" build_core = variables.get(