Skip to content

Commit ee8327a

Browse files
committed
Improve detail of exporter addition docs
1 parent 7b3a9ef commit ee8327a

File tree

1 file changed

+111
-17
lines changed

1 file changed

+111
-17
lines changed

docs/cont/adding_exporters.md

Lines changed: 111 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
11
# Adding exporters
22

3-
This is a guide for adding exporters to the mbed-os tools. It covers the structure of the export subsystem and the individual exporter.
3+
This is a guide for adding exporters to the mbed-os tools. First, This document describes what an exporter is and what rules it follows. Then, it covers the structure of the export subsystem and the individual exporter. Finally, this document gives some implementation suggestions.
44

55
<span class="notes">**Note:** All paths are relative to [https://github.com/ARMmbed/mbed-os/](https://github.com/ARMmbed/mbed-os/).</span>
66

7-
## Export subsystem structure
7+
# What is an exporter
8+
9+
An exporter is a python plugin to the mbed OS tools that convert a project using mbed CLI into one specialized for a particular IDE. For the best user experience, an exporter should:
10+
- Take input from the resource scan.
11+
- Use the flags in the build profiles.
12+
- Have a single template file for each file type they produce. For example, an eclipse CDT project would have one template for `.project` files and one for `.cproject` files.
13+
- Not call mbed CLI. It is possible to export from the website, which will not include mbed CLI in the resulting zip.
14+
15+
16+
# Export subsystem structure
817

918
The export subsystem is organized as a group of common code and a group of IDE or toolchain specific plugins.
1019

1120
The **common code** is contained in four files:
1221

1322
* `tools/project.py` contains the command-line interface and handles the differences between mbed OS 2 tests and mbed OS 5 projects.
14-
* `tools/project_api.py` contains a high-level API for use by the mbed Online Compiler and mbed CLI. Responsible for doing boilerplate-like things, such as scanning for resources.
15-
* `tools/export/__init__.py` contains the mapping of exporter names to plugin classes, and handles printing of toolchain support information.
23+
* `tools/export/__init__.py` contains a high-level API for use by the mbed Online Compiler and mbed CLI. Responsible for doing boilerplate-like things, such as scanning for resources.
1624
* `tools/export/exporters.py` contains the base class for all plugins. It offers useful exporter-specific actions.
1725

1826
An **IDE or toolchain specific plugin** is a Python class that inherits from the `Exporter` class and is listed in the `tools/export/__init__.py` exporter map.
1927

20-
### Common code
28+
## Common code
2129

2230
The common code does two things: setting things up for the plugins, and providing a library of useful tools for plugins to use.
2331

24-
___Setup___
32+
### Setup
2533

2634
The setup code scans for the resources used in the export process and collects the configuration required to build the project at hand.
2735

@@ -33,15 +41,15 @@ These steps construct an object of one of the exporter plugin classes listed in
3341
* `flags` the flags that the mbedToolchain instance will use to compile the `c/cpp/asm` files if invoked.
3442
* `resources` a `Resources` object that contains many lists of files that an exporter will find useful, such as C and Cpp sources and header search paths. The plugin should use only the attributes of the Resources object because the methods are only used during setup time. You can view all available Resources class attributes in `tools/toolchains/__init__.py`.
3543

36-
___Plugin tools___
44+
### Plugin tools
3745

3846
The other half of the common code is a library for use by a plugin. This API includes:
3947

4048
* `gen_file` use Jinja2 to generate a file from a template.
4149
* `get_source_paths` returns a list of directories that contain assembly, C, C++ files and so on.
4250
* `group_project_files` group all files passed in by their containing directory. The groups are suitable for an IDE.
4351

44-
### Plugin code
52+
## Plugin code
4553

4654
Plugin code is contained within a subdirectory of the `tools/export` directory named after the IDE or toolchain that the plugin is exporting for.
4755
For example, the uVision exporter's templates and Python code is contained within the directory `tools/export/uvision` and the Makefile exporter's code and templates within `tools/export/makefile`.
@@ -52,39 +60,39 @@ The Python code for the plugin should be:
5260
1. Imported into `tools/export/__init__.py`.
5361
1. Added to the exporter map.
5462

55-
___The `generate` method___
63+
### The `generate` method
5664

5765
Each exporter is expected to implement one method, `generate`, which is responsible for creating all of the required project files for the IDE or toolchain that the plugin targets.
5866

5967
This method may use any of the attributes and APIs included by the common code.
6068

61-
___The `TARGETS` class variable___
69+
### The `TARGETS` class variable
6270

6371
Each exporter reports its specific target support through a class varibale, `TARGETS`. This class variable is simply a list of targets to which you can export. Requesting an export to a target that's not on the list will generate an error.
6472

65-
___The `TOOLCHAIN` class variable___
73+
### The `TOOLCHAIN` class variable
6674

6775
Each exporter reports its specific toolchain it will use to compile the source code through a class variable `TOOLCHAIN`.
6876

69-
___The `NAME` class variable___
77+
### The `NAME` class variable
7078

7179
Each exporter reports the name of the exporter through the class variable `NAME`. This matches the key in the `tools/export/__init__.py` exporter map.
7280

73-
___The `build` method___
81+
### The `build` method
7482

7583
A plugin that would like to be tested by CI may implement the `build` method.
7684

7785
This method runs after `generate` on an object that inherits from `Exporter`. It is responsible for invoking the build tools that the IDE or toolchain needs when a user instructs it to compile. It must return `0` on success or `-1` on failure.
7886

79-
## Implementing an example plugin
87+
# Implementing an example plugin
8088

8189
In this section, we walk through implementing a simple exporter, `my_makefile`, which is a simplified Makefile using one template.
8290

8391
We will create two files and discuss their contents: `__init__.py` with the Python plugin code, and `Makefile.tmpl` with the template.
8492

8593
As this plugin is named `my_makefile`, all of the support code will be placed into `tools/export/my_makefile`.
8694

87-
### Python code for `__init__.py`
95+
## Python code for `__init__.py`
8896

8997
First, we will make our class a subclass of Exporter:
9098
```python
@@ -118,7 +126,7 @@ TARGETS = [target for target, obj in TARGET_MAP.iteritems()
118126
if "GCC_ARM" in obj.supported_toolchains]
119127
```
120128

121-
#### Implementing the `generate` method
129+
### Implementing the `generate` method
122130

123131
To generate our Makefile, we need a list of object files the executable will use. We can construct the list from the sources if we replace the extensions with `.o`.
124132

@@ -158,7 +166,7 @@ To render our template, we pass the template file name, the context and the dest
158166
self.gen_file('my_makefile/Makefile.tmpl', ctx, 'Makefile')
159167
```
160168

161-
### Template
169+
## Template
162170

163171
Now that we have a context object, and we have passed off control to the Jinja2 template rendering engine, we can look at the template Makefile, `tools/export/my_makefile/Makefile.tmpl`.
164172

@@ -230,3 +238,89 @@ $(PROJECT).elf: $(OBJECTS) $(SYS_OBJECTS) $(LINKER_SCRIPT)
230238
+@echo "link: $(notdir $@)"
231239
@$(LD) -T $(filter %{{link_script_ext}}, $^) $(LIBRARY_PATHS) --output $@ $(filter %.o, $^) $(LIBRARIES)
232240
```
241+
242+
# Suggested implementation
243+
244+
There are several paths forward that can lead to an easily maintained exporter:
245+
- Specialize or alias the GNU ARM Eclipse exporter.
246+
- Specialize or alias the Eclipse + Make exporter.
247+
- Specialize the Make exporter.
248+
249+
## GNU ARM Eclipse
250+
251+
If your IDE uses Eclipse and uses the GNU ARM Eclipse plugin, then you should
252+
specialize or alias your exporter with the generic GNU ARM Eclipse.
253+
254+
### Alias
255+
256+
If you do not need any specialization of the export, then replace your
257+
exporters class in the `EXPORT_MAP` with the `GNUARMEclipse` class. For example,
258+
if KDS met all of these requirements, we could:
259+
260+
```diff
261+
EXPORTERS = {
262+
'iar': iar.IAR,
263+
'embitz' : embitz.EmBitz,
264+
'coide' : coide.CoIDE,
265+
+ 'kds' : gnuarmeclipse.GNUARMEclipse,
266+
'simplicityv3' : simplicity.SimplicityV3,
267+
'atmelstudio' : atmelstudio.AtmelStudio,
268+
'sw4stm32' : sw4stm32.Sw4STM32,
269+
```
270+
271+
### Specialization
272+
273+
If you need more specialization and are using an Eclipse based IDE and the GNU
274+
ARM Eclipse plugin, then your exporter class inherits from the `GNUARMEclipse`
275+
class. For example (with KDS again):
276+
277+
```python
278+
from tools.export.exporters.gnuarmeclipse import GNUARMEcilpse
279+
280+
class KDS(GNUARMEcilpse):
281+
NAME = 'Kinetis Design Studio'
282+
TOOLCHAIN = 'GCC_ARM'
283+
...
284+
285+
def generate(self):
286+
"""Generate eclipes project files, and some KDS specific files"""
287+
super(KDS, self).generate()
288+
...
289+
290+
```
291+
292+
After inheriting from the `GNUARMEclipse` class, specialize the generate method
293+
in any way you need.
294+
295+
## Eclipse + Make
296+
297+
If your IDE uses Eclipse and does not use the GNU ARM Eclipse plugin, you
298+
can use the "Unmanaged makefile" Eclipse exporter classes, `EclipseGcc`,
299+
`EclipseArmc5` and `EclipseIar`. Much like the GNU ARM Eclipse section, you may
300+
decide to alias or specialize.
301+
302+
## Make
303+
304+
If your IDE is not Eclipse based but can still use a Makefile, then you can
305+
specialize the Makefile exporter. Specializing the Makefile is actually how we
306+
implement the Eclipse + Make exporter.
307+
308+
Creating an exporter based on the Makefile exporter is a two step process:
309+
inherit from the appropriate Makefile class, and call its generate method.
310+
Taking Eclipse + Make using GCC_ARM as an example, your exporter will look like:
311+
312+
```python
313+
class EclipseGcc(GccArm):
314+
NAME = "Eclipse-GCC-ARM"
315+
```
316+
317+
Your generate method will look similar to:
318+
319+
320+
```python
321+
def generate(self):
322+
"""Generate Makefile, .cproject & .project Eclipse project file,
323+
py_ocd_settings launch file, and software link .p2f file
324+
"""
325+
super(EclipseGcc, self).generate()
326+
...

0 commit comments

Comments
 (0)