diff --git a/tools/build.py b/tools/build.py index d988a0a649..7915ed03a4 100644 --- a/tools/build.py +++ b/tools/build.py @@ -27,7 +27,7 @@ ROOT = abspath(join(dirname(__file__), "..")) sys.path.insert(0, ROOT) -from tools.toolchains import TOOLCHAINS +from tools.toolchains import TOOLCHAINS, TOOLCHAIN_CLASSES, TOOLCHAIN_PATHS from tools.toolchains import mbedToolchain from tools.targets import TARGET_NAMES, TARGET_MAP from tools.options import get_default_options_parser @@ -161,6 +161,7 @@ if __name__ == '__main__': print mcu_toolchain_matrix(platform_filter=options.general_filter_regex) exit(0) + # Get target list targets = options.mcu if options.mcu else TARGET_NAMES @@ -212,6 +213,11 @@ if __name__ == '__main__': # CPPCHECK code validation if options.cppcheck_validation: for toolchain in toolchains: + if not TOOLCHAIN_CLASSES[toolchain].check_executable(): + search_path = TOOLCHAIN_PATHS[toolchain] or "No path set" + args_error(parser, "Could not find executable for %s.\n" + "Currently set search path: %s" + % (toolchain, search_path)) for target in targets: try: mcu = TARGET_MAP[target] diff --git a/tools/make.py b/tools/make.py index 0541d5e760..4434931f8e 100644 --- a/tools/make.py +++ b/tools/make.py @@ -21,7 +21,7 @@ TEST BUILD & RUN import sys from time import sleep from shutil import copy -from os.path import join, abspath, dirname, isfile, isdir +from os.path import join, abspath, dirname # Be sure that the tools directory is in the search path ROOT = abspath(join(dirname(__file__), "..")) @@ -46,8 +46,7 @@ from tools.build_api import mcu_toolchain_matrix from utils import argparse_filestring_type from utils import argparse_many from utils import argparse_dir_not_parent -from argparse import ArgumentTypeError -from tools.toolchains import mbedToolchain +from tools.toolchains import mbedToolchain, TOOLCHAIN_CLASSES, TOOLCHAIN_PATHS from tools.settings import CLI_COLOR_MAP if __name__ == '__main__': @@ -232,6 +231,12 @@ if __name__ == '__main__': else: notify = None + if not TOOLCHAIN_CLASSES[toolchain].check_executable(): + search_path = TOOLCHAIN_PATHS[toolchain] or "No path set" + args_error(parser, "Could not find executable for %s.\n" + "Currently set search path: %s" + %(toolchain,search_path)) + # Test for test_no in p: test = Test(test_no) diff --git a/tools/test.py b/tools/test.py index 101af3fb33..5b8f9d97a3 100644 --- a/tools/test.py +++ b/tools/test.py @@ -35,7 +35,7 @@ from tools.utils import mkdir, ToolException, NotSupportedException, args_error from tools.test_exporters import ReportExporter, ResultExporterType from utils import argparse_filestring_type, argparse_lowercase_type, argparse_many from utils import argparse_dir_not_parent -from tools.toolchains import mbedToolchain +from tools.toolchains import mbedToolchain, TOOLCHAIN_PATHS, TOOLCHAIN_CLASSES from tools.settings import CLI_COLOR_MAP if __name__ == '__main__': @@ -115,6 +115,12 @@ if __name__ == '__main__': args_error(parser, "argument -t/--tool is required") toolchain = options.tool[0] + if not TOOLCHAIN_CLASSES[toolchain].check_executable(): + search_path = TOOLCHAIN_PATHS[toolchain] or "No path set" + args_error(parser, "Could not find executable for %s.\n" + "Currently set search path: %s" + % (toolchain, search_path)) + # Find all tests in the relevant paths for path in all_paths: all_tests.update(find_tests(path, mcu, toolchain, options.options, @@ -196,6 +202,7 @@ if __name__ == '__main__': print "Failed to build library" else: # Build all the tests + test_build_success, test_build = build_tests(tests, [options.build_dir], options.build_dir, mcu, toolchain, options=options.options, clean=options.clean, diff --git a/tools/toolchains/__init__.py b/tools/toolchains/__init__.py index 3252c21e1b..0e1c7c3d46 100644 --- a/tools/toolchains/__init__.py +++ b/tools/toolchains/__init__.py @@ -27,6 +27,7 @@ from inspect import getmro from copy import deepcopy from tools.config import Config from abc import ABCMeta, abstractmethod +from distutils.spawn import find_executable from multiprocessing import Pool, cpu_count from tools.utils import run_cmd, mkdir, rel_path, ToolException, NotSupportedException, split_path, compile_worker @@ -188,23 +189,6 @@ LEGACY_TOOLCHAIN_NAMES = { } -def check_toolchain_path(function): - """Check if the path to toolchain is valid. Exit if not. - Use this function as a decorator. Causes a system exit if the path does - not exist. Execute the function as normal if the path does exist. - - Positional arguments: - function -- the function to decorate - """ - def perform_check(self, *args, **kwargs): - if not exists(self.toolchain_path) and not exists(self.toolchain_path+'.exe'): - error_string = 'Could not find executable for %s.\n Currently ' \ - 'set search path: %s'% (self.name, self.toolchain_path) - raise Exception(error_string) - return function(self, *args, **kwargs) - return perform_check - - class mbedToolchain: # Verbose logging VERBOSE = True @@ -723,7 +707,6 @@ class mbedToolchain: # THIS METHOD IS BEING CALLED BY THE MBED ONLINE BUILD SYSTEM # ANY CHANGE OF PARAMETERS OR RETURN VALUES WILL BREAK COMPATIBILITY - @check_toolchain_path def compile_sources(self, resources, build_path, inc_dirs=None): # Web IDE progress bar for project build files_to_compile = resources.s_sources + resources.c_sources + resources.cpp_sources @@ -922,7 +905,6 @@ class mbedToolchain: else: raise ToolException(_stderr) - @check_toolchain_path def build_library(self, objects, dir, name): needed_update = False lib = self.STD_LIB_NAME % name @@ -934,7 +916,6 @@ class mbedToolchain: return needed_update - @check_toolchain_path def link_program(self, r, tmp_path, name): needed_update = False ext = 'bin' @@ -1114,6 +1095,51 @@ class mbedToolchain: self.config_processed = True return self.config_file + @staticmethod + def generic_check_executable(tool_key, executable_name, levels_up, + nested_dir=None): + """ + Positional args: + tool_key: the key to index TOOLCHAIN_PATHS + executable_name: the toolchain's named executable (ex. armcc) + levels_up: each toolchain joins the toolchain_path, some + variable directories (bin, include), and the executable name, + so the TOOLCHAIN_PATH value must be appropriately distanced + + Keyword args: + nested_dir: the directory within TOOLCHAIN_PATHS where the executable + is found (ex: 'bin' for ARM\bin\armcc (necessary to check for path + that will be used by toolchain's compile) + + Returns True if the executable location specified by the user + exists and is valid OR the executable can be found on the PATH. + Returns False otherwise. + """ + # Search PATH if user did not specify a path or specified path doesn't + # exist. + if not TOOLCHAIN_PATHS[tool_key] or not exists(TOOLCHAIN_PATHS[tool_key]): + exe = find_executable(executable_name) + if not exe: + return False + for level in range(levels_up): + # move up the specified number of directories + exe = dirname(exe) + TOOLCHAIN_PATHS[tool_key] = exe + if nested_dir: + subdir = join(TOOLCHAIN_PATHS[tool_key], nested_dir, + executable_name) + else: + subdir = join(TOOLCHAIN_PATHS[tool_key],executable_name) + # User could have specified a path that exists but does not contain exe + return exists(subdir) or exists(subdir +'.exe') + + @abstractmethod + def check_executable(self): + """Returns True if the executable (armcc) location specified by the + user exists OR the executable can be found on the PATH. + Returns False otherwise.""" + raise NotImplemented + @abstractmethod def get_config_option(self, config_header): """Generate the compiler option that forces the inclusion of the configuration diff --git a/tools/toolchains/arm.py b/tools/toolchains/arm.py index a7ecc848a4..7ec6bad145 100644 --- a/tools/toolchains/arm.py +++ b/tools/toolchains/arm.py @@ -16,7 +16,6 @@ limitations under the License. """ import re from os.path import join, dirname, splitext, basename -from distutils.spawn import find_executable from tools.toolchains import mbedToolchain, TOOLCHAIN_PATHS from tools.hooks import hook_tool @@ -42,6 +41,13 @@ class ARM(mbedToolchain): 'ld': [], } + @staticmethod + def check_executable(): + """Returns True if the executable (armcc) location specified by the + user exists OR the executable can be found on the PATH. + Returns False otherwise.""" + return mbedToolchain.generic_check_executable("ARM", 'armcc', 2, 'bin') + def __init__(self, target, options=None, notify=None, macros=None, silent=False, extra_verbose=False): mbedToolchain.__init__(self, target, options, notify, macros, silent, extra_verbose=extra_verbose) @@ -56,11 +62,6 @@ class ARM(mbedToolchain): else: cpu = target.core - if not TOOLCHAIN_PATHS['ARM']: - exe = find_executable('armcc') - if exe: - TOOLCHAIN_PATHS['ARM'] = dirname(dirname(exe)) - ARM_BIN = join(TOOLCHAIN_PATHS['ARM'], "bin") ARM_INC = join(TOOLCHAIN_PATHS['ARM'], "include") @@ -86,8 +87,6 @@ class ARM(mbedToolchain): self.ar = join(ARM_BIN, "armar") self.elf2bin = join(ARM_BIN, "fromelf") - self.toolchain_path = TOOLCHAIN_PATHS['ARM'] - def parse_dependencies(self, dep_path): dependencies = [] for line in open(dep_path).readlines(): diff --git a/tools/toolchains/gcc.py b/tools/toolchains/gcc.py index 27bc11fede..451711a93d 100644 --- a/tools/toolchains/gcc.py +++ b/tools/toolchains/gcc.py @@ -15,8 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. """ import re -from os.path import join, basename, splitext, dirname, exists -from distutils.spawn import find_executable +from os.path import join, basename, splitext from tools.toolchains import mbedToolchain, TOOLCHAIN_PATHS from tools.hooks import hook_tool @@ -111,11 +110,6 @@ class GCC(mbedToolchain): self.ar = join(tool_path, "arm-none-eabi-ar") self.elf2bin = join(tool_path, "arm-none-eabi-objcopy") - if tool_path: - self.toolchain_path = main_cc - else: - self.toolchain_path = find_executable("arm-none-eabi-gcc") or '' - def parse_dependencies(self, dep_path): dependencies = [] buff = open(dep_path).readlines() @@ -275,6 +269,13 @@ class GCC(mbedToolchain): class GCC_ARM(GCC): + @staticmethod + def check_executable(): + """Returns True if the executable (arm-none-eabi-gcc) location + specified by the user exists OR the executable can be found on the PATH. + Returns False otherwise.""" + return mbedToolchain.generic_check_executable("GCC_ARM", 'arm-none-eabi-gcc', 1) + def __init__(self, target, options=None, notify=None, macros=None, silent=False, extra_verbose=False): GCC.__init__(self, target, options, notify, macros, silent, TOOLCHAIN_PATHS['GCC_ARM'], extra_verbose=extra_verbose) @@ -300,6 +301,13 @@ class GCC_ARM(GCC): class GCC_CR(GCC): + @staticmethod + def check_executable(): + """Returns True if the executable (arm-none-eabi-gcc) location + specified by the user exists OR the executable can be found on the PATH. + Returns False otherwise.""" + return mbedToolchain.generic_check_executable("GCC_CR", 'arm-none-eabi-gcc', 1) + def __init__(self, target, options=None, notify=None, macros=None, silent=False, extra_verbose=False): GCC.__init__(self, target, options, notify, macros, silent, TOOLCHAIN_PATHS['GCC_CR'], extra_verbose=extra_verbose) diff --git a/tools/toolchains/iar.py b/tools/toolchains/iar.py index 74591ab8cd..2d148b9bc2 100644 --- a/tools/toolchains/iar.py +++ b/tools/toolchains/iar.py @@ -16,8 +16,7 @@ limitations under the License. """ import re from os import remove -from os.path import join, exists, dirname, splitext, exists -from distutils.spawn import find_executable +from os.path import join, splitext from tools.toolchains import mbedToolchain, TOOLCHAIN_PATHS from tools.hooks import hook_tool @@ -45,6 +44,13 @@ class IAR(mbedToolchain): 'ld': ["--skip_dynamic_initialization", "--threaded_lib"], } + @staticmethod + def check_executable(): + """Returns True if the executable (arm-none-eabi-gcc) location + specified by the user exists OR the executable can be found on the PATH. + Returns False otherwise.""" + return mbedToolchain.generic_check_executable("IAR", 'iccarm', 2, "bin") + def __init__(self, target, options=None, notify=None, macros=None, silent=False, extra_verbose=False): mbedToolchain.__init__(self, target, options, notify, macros, silent, extra_verbose=extra_verbose) if target.core == "Cortex-M7F" or target.core == "Cortex-M7FD": @@ -52,11 +58,6 @@ class IAR(mbedToolchain): else: cpuchoice = target.core - if not TOOLCHAIN_PATHS['IAR']: - exe = find_executable('iccarm') - if exe: - TOOLCHAIN_PATHS['IAR'] = dirname(dirname(exe)) - # flags_cmd are used only by our scripts, the project files have them already defined, # using this flags results in the errors (duplication) # asm accepts --cpu Core or --fpu FPU, not like c/c++ --cpu=Core @@ -108,8 +109,6 @@ class IAR(mbedToolchain): self.ar = join(IAR_BIN, "iarchive") self.elf2bin = join(IAR_BIN, "ielftool") - self.toolchain_path = TOOLCHAIN_PATHS['IAR'] - def parse_dependencies(self, dep_path): return [(self.CHROOT if self.CHROOT else '')+path.strip() for path in open(dep_path).readlines() if (path and not path.isspace())]