|
15 | 15 | # See the License for the specific language governing permissions and
|
16 | 16 | # limitations under the License.
|
17 | 17 |
|
| 18 | +import sys |
| 19 | +from os.path import join, abspath, dirname, exists |
| 20 | +from os.path import basename, relpath, normpath, splitext |
| 21 | +from os import makedirs, walk |
| 22 | +import copy |
| 23 | +from shutil import rmtree |
| 24 | +import zipfile |
| 25 | +ROOT = abspath(join(dirname(__file__), "..")) |
| 26 | +sys.path.insert(0, ROOT) |
| 27 | + |
| 28 | +from tools.build_api import prepare_toolchain |
| 29 | +from tools.build_api import scan_resources |
| 30 | +from tools.toolchains import Resources |
18 | 31 | from tools.export import lpcxpresso, ds5_5, iar, makefile
|
19 | 32 | from tools.export import embitz, coide, kds, simplicity, atmelstudio
|
20 | 33 | from tools.export import sw4stm32, e2studio, zip, cmsis, uvision, cdt
|
@@ -105,3 +118,223 @@ def mcu_ide_matrix(verbose_html=False):
|
105 | 118 | if verbose_html:
|
106 | 119 | result = result.replace("&", "&")
|
107 | 120 | return result
|
| 121 | + |
| 122 | + |
| 123 | +def get_exporter_toolchain(ide): |
| 124 | + """ Return the exporter class and the toolchain string as a tuple |
| 125 | +
|
| 126 | + Positional arguments: |
| 127 | + ide - the ide name of an exporter |
| 128 | + """ |
| 129 | + return EXPORTERS[ide], EXPORTERS[ide].TOOLCHAIN |
| 130 | + |
| 131 | + |
| 132 | +def rewrite_basepath(file_name, resources, export_path, loc): |
| 133 | + """ Replace the basepath of filename with export_path |
| 134 | +
|
| 135 | + Positional arguments: |
| 136 | + file_name - the absolute path to a file |
| 137 | + resources - the resources object that the file came from |
| 138 | + export_path - the final destination of the file after export |
| 139 | + """ |
| 140 | + new_f = join(loc, relpath(file_name, resources.file_basepath[file_name])) |
| 141 | + resources.file_basepath[join(export_path, new_f)] = export_path |
| 142 | + return new_f |
| 143 | + |
| 144 | + |
| 145 | +def subtract_basepath(resources, export_path, loc=""): |
| 146 | + """ Rewrite all of the basepaths with the export_path |
| 147 | +
|
| 148 | + Positional arguments: |
| 149 | + resources - the resource object to rewrite the basepaths of |
| 150 | + export_path - the final destination of the resources with respect to the |
| 151 | + generated project files |
| 152 | + """ |
| 153 | + keys = ['s_sources', 'c_sources', 'cpp_sources', 'hex_files', |
| 154 | + 'objects', 'libraries', 'inc_dirs', 'headers', 'linker_script', |
| 155 | + 'lib_dirs'] |
| 156 | + for key in keys: |
| 157 | + vals = getattr(resources, key) |
| 158 | + if isinstance(vals, set): |
| 159 | + vals = list(vals) |
| 160 | + if isinstance(vals, list): |
| 161 | + new_vals = [] |
| 162 | + for val in vals: |
| 163 | + new_vals.append(rewrite_basepath(val, resources, export_path, |
| 164 | + loc)) |
| 165 | + if isinstance(getattr(resources, key), set): |
| 166 | + setattr(resources, key, set(new_vals)) |
| 167 | + else: |
| 168 | + setattr(resources, key, new_vals) |
| 169 | + elif vals: |
| 170 | + setattr(resources, key, rewrite_basepath(vals, resources, |
| 171 | + export_path, loc)) |
| 172 | + |
| 173 | + |
| 174 | +def generate_project_files(resources, export_path, target, name, toolchain, ide, |
| 175 | + macros=None): |
| 176 | + """Generate the project files for a project |
| 177 | +
|
| 178 | + Positional arguments: |
| 179 | + resources - a Resources object containing all of the files needed to build |
| 180 | + this project |
| 181 | + export_path - location to place project files |
| 182 | + name - name of the project |
| 183 | + toolchain - a toolchain class that corresponds to the toolchain used by the |
| 184 | + IDE or makefile |
| 185 | + ide - IDE name to export to |
| 186 | +
|
| 187 | + Optional arguments: |
| 188 | + macros - additional macros that should be defined within the exported |
| 189 | + project |
| 190 | + """ |
| 191 | + exporter_cls, _ = get_exporter_toolchain(ide) |
| 192 | + exporter = exporter_cls(target, export_path, name, toolchain, |
| 193 | + extra_symbols=macros, resources=resources) |
| 194 | + exporter.generate() |
| 195 | + files = exporter.generated_files |
| 196 | + return files, exporter |
| 197 | + |
| 198 | + |
| 199 | +def zip_export(file_name, prefix, resources, project_files, inc_repos): |
| 200 | + """Create a zip file from an exported project. |
| 201 | +
|
| 202 | + Positional Parameters: |
| 203 | + file_name - the file name of the resulting zip file |
| 204 | + prefix - a directory name that will prefix the entire zip file's contents |
| 205 | + resources - a resources object with files that must be included in the zip |
| 206 | + project_files - a list of extra files to be added to the root of the prefix |
| 207 | + directory |
| 208 | + """ |
| 209 | + with zipfile.ZipFile(file_name, "w") as zip_file: |
| 210 | + for prj_file in project_files: |
| 211 | + zip_file.write(prj_file, join(prefix, basename(prj_file))) |
| 212 | + for loc, res in resources.iteritems(): |
| 213 | + to_zip = ( |
| 214 | + res.headers + res.s_sources + res.c_sources +\ |
| 215 | + res.cpp_sources + res.libraries + res.hex_files + \ |
| 216 | + [res.linker_script] + res.bin_files + res.objects + \ |
| 217 | + res.json_files + res.lib_refs + res.lib_builds) |
| 218 | + if inc_repos: |
| 219 | + for directory in res.repo_dirs: |
| 220 | + for root, _, files in walk(directory): |
| 221 | + for repo_file in files: |
| 222 | + source = join(root, repo_file) |
| 223 | + to_zip.append(source) |
| 224 | + res.file_basepath[source] = res.base_path |
| 225 | + to_zip += res.repo_files |
| 226 | + for source in to_zip: |
| 227 | + if source: |
| 228 | + zip_file.write( |
| 229 | + source, |
| 230 | + join(prefix, loc, |
| 231 | + relpath(source, res.file_basepath[source]))) |
| 232 | + for source in res.lib_builds: |
| 233 | + target_dir, _ = splitext(source) |
| 234 | + dest = join(prefix, loc, |
| 235 | + relpath(target_dir, res.file_basepath[source]), |
| 236 | + ".bld", "bldrc") |
| 237 | + zip_file.write(source, dest) |
| 238 | + |
| 239 | + |
| 240 | + |
| 241 | +def export_project(src_paths, export_path, target, ide, libraries_paths=None, |
| 242 | + linker_script=None, notify=None, verbose=False, name=None, |
| 243 | + inc_dirs=None, jobs=1, silent=False, extra_verbose=False, |
| 244 | + config=None, macros=None, zip_proj=None, inc_repos=False, |
| 245 | + build_profile=None): |
| 246 | + """Generates a project file and creates a zip archive if specified |
| 247 | +
|
| 248 | + Positional Arguments: |
| 249 | + src_paths - a list of paths from which to find source files |
| 250 | + export_path - a path specifying the location of generated project files |
| 251 | + target - the mbed board/mcu for which to generate the executable |
| 252 | + ide - the ide for which to generate the project fields |
| 253 | +
|
| 254 | + Keyword Arguments: |
| 255 | + libraries_paths - paths to additional libraries |
| 256 | + linker_script - path to the linker script for the specified target |
| 257 | + notify - function is passed all events, and expected to handle notification |
| 258 | + of the user, emit the events to a log, etc. |
| 259 | + verbose - assigns the notify function to toolchains print_notify_verbose |
| 260 | + name - project name |
| 261 | + inc_dirs - additional include directories |
| 262 | + jobs - number of threads |
| 263 | + silent - silent build - no output |
| 264 | + extra_verbose - assigns the notify function to toolchains |
| 265 | + print_notify_verbose |
| 266 | + config - toolchain's config object |
| 267 | + macros - User-defined macros |
| 268 | + zip_proj - string name of the zip archive you wish to creat (exclude arg |
| 269 | + if you do not wish to create an archive |
| 270 | + """ |
| 271 | + |
| 272 | + # Convert src_path to a list if needed |
| 273 | + if isinstance(src_paths, dict): |
| 274 | + paths = sum(src_paths.values(), []) |
| 275 | + elif isinstance(src_paths, list): |
| 276 | + paths = src_paths[:] |
| 277 | + else: |
| 278 | + paths = [src_paths] |
| 279 | + |
| 280 | + # Extend src_paths wit libraries_paths |
| 281 | + if libraries_paths is not None: |
| 282 | + paths.extend(libraries_paths) |
| 283 | + |
| 284 | + if not isinstance(src_paths, dict): |
| 285 | + src_paths = {"": paths} |
| 286 | + |
| 287 | + # Export Directory |
| 288 | + if not exists(export_path): |
| 289 | + makedirs(export_path) |
| 290 | + |
| 291 | + _, toolchain_name = get_exporter_toolchain(ide) |
| 292 | + |
| 293 | + # Pass all params to the unified prepare_resources() |
| 294 | + toolchain = prepare_toolchain( |
| 295 | + paths, "", target, toolchain_name, macros=macros, jobs=jobs, |
| 296 | + notify=notify, silent=silent, verbose=verbose, |
| 297 | + extra_verbose=extra_verbose, config=config, build_profile=build_profile) |
| 298 | + # The first path will give the name to the library |
| 299 | + if name is None: |
| 300 | + name = basename(normpath(abspath(src_paths[0]))) |
| 301 | + |
| 302 | + # Call unified scan_resources |
| 303 | + resource_dict = {loc: scan_resources(path, toolchain, inc_dirs=inc_dirs) |
| 304 | + for loc, path in src_paths.iteritems()} |
| 305 | + resources = Resources() |
| 306 | + toolchain.build_dir = export_path |
| 307 | + config_header = toolchain.get_config_header() |
| 308 | + resources.headers.append(config_header) |
| 309 | + resources.file_basepath[config_header] = dirname(config_header) |
| 310 | + |
| 311 | + if zip_proj: |
| 312 | + subtract_basepath(resources, export_path) |
| 313 | + for loc, res in resource_dict.iteritems(): |
| 314 | + temp = copy.deepcopy(res) |
| 315 | + subtract_basepath(temp, export_path, loc) |
| 316 | + resources.add(temp) |
| 317 | + else: |
| 318 | + for _, res in resource_dict.iteritems(): |
| 319 | + resources.add(res) |
| 320 | + |
| 321 | + # Change linker script if specified |
| 322 | + if linker_script is not None: |
| 323 | + resources.linker_script = linker_script |
| 324 | + |
| 325 | + files, exporter = generate_project_files(resources, export_path, |
| 326 | + target, name, toolchain, ide, |
| 327 | + macros=macros) |
| 328 | + files.append(config_header) |
| 329 | + if zip_proj: |
| 330 | + for resource in resource_dict.values(): |
| 331 | + for label, res in resource.features.iteritems(): |
| 332 | + if label not in toolchain.target.features: |
| 333 | + resource.add(res) |
| 334 | + if isinstance(zip_proj, basestring): |
| 335 | + zip_export(join(export_path, zip_proj), name, resource_dict, files, |
| 336 | + inc_repos) |
| 337 | + else: |
| 338 | + zip_export(zip_proj, name, resource_dict, files, inc_repos) |
| 339 | + |
| 340 | + return exporter |
0 commit comments