|
| 1 | +""" |
| 2 | +mbed SDK |
| 3 | +Copyright (c) 2011-2016 ARM Limited |
| 4 | +
|
| 5 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | +you may not use this file except in compliance with the License. |
| 7 | +You may obtain a copy of the License at |
| 8 | +
|
| 9 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +
|
| 11 | +Unless required by applicable law or agreed to in writing, software |
| 12 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | +See the License for the specific language governing permissions and |
| 15 | +limitations under the License. |
| 16 | +""" |
| 17 | +import re |
| 18 | +import shutil |
| 19 | +from os import remove, getcwd, chdir, mkdir |
| 20 | +from os.path import basename, exists |
| 21 | +from subprocess import Popen, PIPE |
| 22 | + |
| 23 | +from jinja2.exceptions import TemplateNotFound |
| 24 | + |
| 25 | +from tools.export.exporters import Exporter, apply_supported_whitelist |
| 26 | +from tools.targets import TARGET_MAP |
| 27 | + |
| 28 | + |
| 29 | +class CMake(Exporter): |
| 30 | + """Generic CMake template that mimics the behavior of the python build |
| 31 | + system |
| 32 | + """ |
| 33 | + |
| 34 | + TEMPLATE = 'CMakeLists.txt' |
| 35 | + |
| 36 | + MBED_CONFIG_HEADER_SUPPORTED = True |
| 37 | + |
| 38 | + PREPROCESS_ASM = False |
| 39 | + |
| 40 | + POST_BINARY_WHITELIST = set([ |
| 41 | + "MCU_NRF51Code.binary_hook", |
| 42 | + "TEENSY3_1Code.binary_hook", |
| 43 | + "LPCTargetCode.lpc_patch", |
| 44 | + "LPC4088Code.binary_hook" |
| 45 | + ]) |
| 46 | + |
| 47 | + @classmethod |
| 48 | + def is_target_supported(cls, target_name): |
| 49 | + target = TARGET_MAP[target_name] |
| 50 | + return apply_supported_whitelist( |
| 51 | + cls.TOOLCHAIN, cls.POST_BINARY_WHITELIST, target) |
| 52 | + |
| 53 | + def generate(self): |
| 54 | + """Generate the CMakefiles.txt |
| 55 | + """ |
| 56 | + self.resources.win_to_unix() |
| 57 | + |
| 58 | + # get all source files including headers, adding headers allows IDEs to detect which files |
| 59 | + # belong to the project, otherwise headers may be greyed out and not work with inspection |
| 60 | + # (that is true for CLion and definitely for Visual Code) |
| 61 | + srcs = set(self.resources.c_sources + |
| 62 | + self.resources.cpp_sources + |
| 63 | + self.resources.s_sources + |
| 64 | + self.resources.headers) |
| 65 | + srcs = [re.sub(r'^[.]/', '', f) for f in srcs] |
| 66 | + |
| 67 | + # additional libraries |
| 68 | + libraries = [self.prepare_lib(basename(lib)) for lib in self.resources.libraries] |
| 69 | + sys_libs = [self.prepare_sys_lib(lib) for lib in self.toolchain.sys_libs] |
| 70 | + |
| 71 | + # sort includes reverse, so the deepest dir comes first (ensures short includes) |
| 72 | + includes = sorted([re.sub(r'^[.]/', '', l) for l in self.resources.inc_dirs], reverse=True) |
| 73 | + |
| 74 | + ctx = { |
| 75 | + 'name': self.project_name, |
| 76 | + 'target': self.target, |
| 77 | + 'sources': sorted(srcs), |
| 78 | + 'libraries': libraries, |
| 79 | + 'ld_sys_libs': sys_libs, |
| 80 | + 'include_paths': includes, |
| 81 | + 'library_paths': sorted([re.sub(r'^[.]/', '', l) for l in self.resources.lib_dirs]), |
| 82 | + 'linker_script': self.resources.linker_script, |
| 83 | + 'hex_files': self.resources.hex_files, |
| 84 | + 'ar': basename(self.toolchain.ar), |
| 85 | + 'cc': basename(self.toolchain.cc[0]), |
| 86 | + 'cc_flags': " ".join(flag for flag in self.toolchain.cc[1:] if not flag == "-c"), |
| 87 | + 'cxx': basename(self.toolchain.cppc[0]), |
| 88 | + 'cxx_flags': " ".join(flag for flag in self.toolchain.cppc[1:] if not flag == "-c"), |
| 89 | + 'asm': basename(self.toolchain.asm[0]), |
| 90 | + 'asm_flags': " ".join(flag for flag in self.toolchain.asm[1:] if not flag == "-c"), |
| 91 | + 'symbols': sorted(self.toolchain.get_symbols()), |
| 92 | + 'ld': basename(self.toolchain.ld[0]), |
| 93 | + # fix the missing underscore '_' (see |
| 94 | + 'ld_flags': re.sub("--wrap,_(?!_)", "--wrap,__", " ".join(self.toolchain.ld[1:])), |
| 95 | + 'elf2bin': basename(self.toolchain.elf2bin), |
| 96 | + 'link_script_ext': self.toolchain.LINKER_EXT, |
| 97 | + 'link_script_option': self.LINK_SCRIPT_OPTION, |
| 98 | + 'user_library_flag': self.USER_LIBRARY_FLAG, |
| 99 | + 'needs_asm_preproc': self.PREPROCESS_ASM, |
| 100 | + } |
| 101 | + |
| 102 | + if hasattr(self.toolchain, "preproc"): |
| 103 | + ctx['pp'] = basename(self.toolchain.preproc[0]) |
| 104 | + ctx['pp_flags'] = " ".join(self.toolchain.preproc[1:] + |
| 105 | + self.toolchain.ld[1:]) |
| 106 | + else: |
| 107 | + ctx['pp'] = None |
| 108 | + ctx['pp_flags'] = None |
| 109 | + |
| 110 | + try: |
| 111 | + self.gen_file('cmake/%s.tmpl' % self.TEMPLATE, ctx, 'CMakeLists.txt') |
| 112 | + except TemplateNotFound: |
| 113 | + pass |
| 114 | + |
| 115 | + @staticmethod |
| 116 | + def build(project_name, log_name="build_log.txt", cleanup=True): |
| 117 | + """ Build Make project """ |
| 118 | + |
| 119 | + # change into our build directory |
| 120 | + current_dir = getcwd() |
| 121 | + if not exists("BUILD"): |
| 122 | + mkdir("BUILD") |
| 123 | + chdir("BUILD") |
| 124 | + |
| 125 | + # > run cmake initial command |
| 126 | + cmd = ["cmake", ".."] |
| 127 | + |
| 128 | + # Build the project |
| 129 | + p = Popen(cmd, stdout=PIPE, stderr=PIPE) |
| 130 | + out, err = p.communicate() |
| 131 | + ret_code = p.returncode |
| 132 | + |
| 133 | + if ret_code == 0: |
| 134 | + # we create the cmake files inside BUILD, change into and run cmake |
| 135 | + |
| 136 | + # > run make -j |
| 137 | + cmd = ["make", "-j"] |
| 138 | + |
| 139 | + p = Popen(cmd, stdout=PIPE, stderr=PIPE) |
| 140 | + out, err = p.communicate() |
| 141 | + ret_code = p.returncode |
| 142 | + |
| 143 | + # go back to the original directory |
| 144 | + chdir(current_dir) |
| 145 | + |
| 146 | + out_string = "=" * 10 + "STDOUT" + "=" * 10 + "\n" |
| 147 | + out_string += out |
| 148 | + out_string += "=" * 10 + "STDERR" + "=" * 10 + "\n" |
| 149 | + out_string += err |
| 150 | + |
| 151 | + if ret_code == 0: |
| 152 | + out_string += "SUCCESS" |
| 153 | + else: |
| 154 | + out_string += "FAILURE" |
| 155 | + |
| 156 | + print out_string |
| 157 | + |
| 158 | + if log_name: |
| 159 | + # Write the output to the log file |
| 160 | + with open(log_name, 'w+') as f: |
| 161 | + f.write(out_string) |
| 162 | + |
| 163 | + # Cleanup the exported and built files |
| 164 | + if cleanup: |
| 165 | + remove("CMakeLists.txt") |
| 166 | + remove(log_name) |
| 167 | + # legacy .build directory cleaned if exists |
| 168 | + if exists('.build'): |
| 169 | + shutil.rmtree('.build') |
| 170 | + if exists('BUILD'): |
| 171 | + shutil.rmtree('BUILD') |
| 172 | + |
| 173 | + if ret_code != 0: |
| 174 | + # Seems like something went wrong. |
| 175 | + return -1 |
| 176 | + else: |
| 177 | + return 0 |
| 178 | + |
| 179 | + |
| 180 | +class GccArm(CMake): |
| 181 | + """GCC ARM specific cmake target""" |
| 182 | + NAME = 'CMake-GCC-ARM' |
| 183 | + TOOLCHAIN = "GCC_ARM" |
| 184 | + LINK_SCRIPT_OPTION = "-T" |
| 185 | + USER_LIBRARY_FLAG = "-L" |
| 186 | + |
| 187 | + @staticmethod |
| 188 | + def prepare_lib(libname): |
| 189 | + if "lib" == libname[:3]: |
| 190 | + libname = libname[3:-2] |
| 191 | + return "-l" + libname |
| 192 | + |
| 193 | + @staticmethod |
| 194 | + def prepare_sys_lib(libname): |
| 195 | + return "-l" + libname |
| 196 | + |
| 197 | +# class Arm(CMake): |
| 198 | +# """ARM Compiler generic cmake target""" |
| 199 | +# LINK_SCRIPT_OPTION = "--scatter" |
| 200 | +# USER_LIBRARY_FLAG = "--userlibpath " |
| 201 | +# |
| 202 | +# @staticmethod |
| 203 | +# def prepare_lib(libname): |
| 204 | +# return libname |
| 205 | +# |
| 206 | +# @staticmethod |
| 207 | +# def prepare_sys_lib(libname): |
| 208 | +# return libname |
| 209 | +# |
| 210 | +# def generate(self): |
| 211 | +# if self.resources.linker_script: |
| 212 | +# new_script = self.toolchain.correct_scatter_shebang( |
| 213 | +# self.resources.linker_script) |
| 214 | +# if new_script is not self.resources.linker_script: |
| 215 | +# self.resources.linker_script = new_script |
| 216 | +# self.generated_files.append(new_script) |
| 217 | +# return super(Arm, self).generate() |
| 218 | +# |
| 219 | +# |
| 220 | +# class Armc5(Arm): |
| 221 | +# """ARM Compiler 5 (armcc) specific makefile target""" |
| 222 | +# NAME = 'CMake-ARMc5' |
| 223 | +# TOOLCHAIN = "ARM" |
| 224 | +# PREPROCESS_ASM = True |
| 225 | +# |
| 226 | +# |
| 227 | +# class Armc6(Arm): |
| 228 | +# """ARM Compiler 6 (armclang) specific generic makefile target""" |
| 229 | +# NAME = 'CMake-ARMc6' |
| 230 | +# TOOLCHAIN = "ARMC6" |
| 231 | +# |
| 232 | +# |
| 233 | +# class IAR(CMake): |
| 234 | +# """IAR specific cmake target""" |
| 235 | +# NAME = 'CMake-IAR' |
| 236 | +# TOOLCHAIN = "IAR" |
| 237 | +# LINK_SCRIPT_OPTION = "--config" |
| 238 | +# USER_LIBRARY_FLAG = "-L" |
| 239 | +# |
| 240 | +# @staticmethod |
| 241 | +# def prepare_lib(libname): |
| 242 | +# if "lib" == libname[:3]: |
| 243 | +# libname = libname[3:] |
| 244 | +# return "-l" + splitext(libname)[0] |
| 245 | +# |
| 246 | +# @staticmethod |
| 247 | +# def prepare_sys_lib(libname): |
| 248 | +# if "lib" == libname[:3]: |
| 249 | +# libname = libname[3:] |
| 250 | +# return "-l" + splitext(libname)[0] |
0 commit comments