File: tutorial.md

package info (click to toggle)
yotta 0.20.5-8
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,880 kB
  • sloc: python: 11,279; makefile: 32
file content (349 lines) | stat: -rw-r--r-- 14,728 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
---
layout: default
title: Tutorial
section: tutorial/tutorial
---

# Tutorial
There are two sorts of things that `yotta` builds: modules, and executables. Modules are re-usable, and the source code for them is distributed in the `yotta` registry. Executables are stand-alone programs that depend on modules, and which are not normally published themselves.

We’ll look at creating and publishing a module, then using that module in a simple command line executable.

This tutorial assumes your are building and running natively on OS X or Linux,
For a guide to writing cross-compiled programs for mbed OS using yotta, see the
[mbed OS
documentation](https://docs.mbed.com/docs/getting-started-mbed-os/en/latest/FirstProjectmbedOS/).

<a name="Creating a Module"></a>
## Creating a Module
Before creating a module you should always check the [yotta registry](https://yottabuild.org) to see if another module already exists that does the same thing. If it does, think about submitting a pull-request to that module first.

Let’s create a module that provides a really simple logging framework:

### yotta init
First create a directory with the name we want, and `cd` into it.

```sh
# in these code examples, lines that you type have "> " at the beginning, other
# lines are output from the running program:
> mkdir simplelog
> cd simplelog
```

[yotta init](/../reference/commands.html#yotta-init), will create the module description for us, after asking a few questions. It’ll provide some helpful defaults, too.

```sh
> yotta init
Enter the module name: <simplelog> 
Enter the initial version: <0.0.0> 
Is this an executable (instead of a re-usable library module)? <no> 
Short description: Really simple logging.
Keywords:  <> logging, simple, example
Author: James Crosby <james.crosby@arm.com>
Repository url (where people can submit bugfixes): ssh://git@github.com/ARMmbed/simplelog
Homepage: http://github.com/ARMmbed/simplelog
What is the license for this project (Apache-2.0, ISC, MIT etc.)?  <Apache-2.0> 
> 
```

**Note:** you can use any valid [SPDX license
expression](/reference/licenses.html) for the license of your module, although
simple permissive open-source licenses like Apache-2.0 are encouraged, and will
make it easier for people to use your module and contribute improvements.

### Module Structure
`yotta init` also creates some directories for us to put things in:

```sh
> ls -l
total 4
-rw-r--r--  1 james  staff   420B 15 Sep 16:55 module.json
drwxr-xr-x  4 james  staff   136B 15 Sep 16:35 simplelog
drwxr-xr-x  4 james  staff   136B 15 Sep 16:41 source
drwxr-xr-x  4 james  staff   136B 15 Sep 16:41 test
```
Any source files placed in the `source` directory will be compiled automatically by yotta, when we build, and each source file placed into the `test` directory will be compiled into a separate executable (to learn more about testing with yotta see the [testing tutorial](/tutorial/testing.html).

Headers that define the interface to your module should be placed in the directory with the same name as your module (`simplelog` in this case). This pattern forces headers to be included with their full paths e.g. `#include "simplelog/log.h"`, instead of just `#include "log.h"`, which might be a header name that multiple different modules use.

### Implement!
The implementation of our logging module is just three files, a header file, an implementation file and a test:

The header file, `./simplelog/log.h`, declares the public interface to our module. It’s important to keep the public interface to the bare minimum (avoid including unnecessary headers, for example), as all users of your module (direct and indirect) will be exposed to the contents of your public header files.

For the same reason we prefix everything in the public header with the name of our module. Here we’re using the conventions that types are `UpperCamelCase`, functions are `lowerCamelCase`, and global variables are `Upper_Case_Underscore_Separated`. The exact conventions you use are up to you, but you **must** prefix all exported symbols.

```C
// header guard: C and C++ headers must guard themselves against multiple
// inclusion.
#ifndef SIMPLELOG_LOG_H
#define SIMPLELOG_LOG_H

// make sure the header works when included from C++
#ifdef __cplusplus
extern "C" {
#endif // def __cplusplus

// Log levels: note that we prefix everything with the module name. It's OK to
// use various Casing and underscore separated conventions to denote different
// types of symbols, since module names are forced to be lower case, and cannot
// include underscores (only - characters).
enum SimpleLogLevel{
    Simple_Log_Critical = 0,
    Simple_Log_Notice   = 1,
    Simple_Log_Error    = 2,
    Simple_Log_Warning  = 3,
    Simple_Log_Info     = 4,
    Simple_Log_Debug    = 100
};

// log the message at the given level
void simpleLog(enum SimpleLogLevel level, const char* msg);

// shortcut functions, still prefixed
void simpleLogError(const char* msg);
void simpleLogWarning(const char* msg);
void simpleLogInfo(const char* msg);
void simpleLogDebug(const char* msg);

#ifdef __cplusplus
} // extern "C"
#endif // def __cplusplus

// end the header inclusion guard
#endif // ndef SIMPLELOG_LOG_H
```

The source file, `./source/log.c`, simply implements the header, using `printf` to log messages:

```C
// include the libc header for printf
#include <stdio.h>

// include our own public header
#include "simplelog/log.h"

const char* prefixForLevel(enum SimpleLogLevel level){
    if(level <= Simple_Log_Critical){
        return "[critical]";
    }else if(level <= Simple_Log_Notice){
        return "[notice]";
    }else if(level <= Simple_Log_Error){
        return "[error]";
    }else if(level <= Simple_Log_Warning){
        return "[warning]";
    }else if(level <= Simple_Log_Info){
        return "[info]";
    }else{
        return "[debug]";
    }
}

// log the message at the given level
void simpleLog(enum SimpleLogLevel level, const char* msg){
    printf("%s %s\n", prefixForLevel(level), msg);
}

void simpleLogError(const char* msg){
    return simpleLog(Simple_Log_Error, msg);
}
void simpleLogWarning(const char* msg){
    return simpleLog(Simple_Log_Warning, msg);
}
void simpleLogInfo(const char* msg){
    return simpleLog(Simple_Log_Info, msg);
}
void simpleLogDebug(const char* msg){
    return simpleLog(Simple_Log_Debug, msg);
}
```

After implementing these files, our module's structure should look something
like this:

```sh
├── module.json
├── simplelog
│   └── log.h
├── source
│   └── simplelog.c
└── test
```

### yotta build
Now we have a header and implementation file, we can build our module!

```sh
> yotta build
info: generate for target: x86-osx-native 0.0.3 at /Dev/simplelog/yotta_targets/x86-osx-native
-- Configuring done
-- Generating done
-- Build files have been written to: /Dev/simplelog/build/x86-osx-native
Scanning dependencies of target simplelog
[100%] Building C object source/CMakeFiles/simplelog.dir/Dev/simplelog/source/simplelog.c.o
Linking C static library libsimplelog.a
[100%] Built target simplelog
```

Great, we’ve built our library! What next? Testing it, of course.

#### Test Test Test
Our module is not complete without a test that it’s working. For now we’ll just add a simple program, but ideally you would exercise all of your module’s API in one or more tests.

`yotta build` scans the `./test` directory, and will build a separate executable for each source file found there, so to add our test we can add a file called `./test/basic.c` with the following `main()` routine:

```C
#include "simplelog/log.h"

int main(){
    simpleLogDebug("hello simplelog!");
    return 0;
}
```

Note that if you are compiling a program for [mbed
OS](https://github.com/armmbed/mbed-os) with yotta, then the entry point is
[`app_start`](https://github.com/armmbed/minar#impact), instead of `main`.

Now, when we run `yotta build`, yotta will also build our test:

```sh
> yotta build
info: generate for target: x86-osx-native 0.0.3 at /Dev/simplelog/yotta_targets/x86-osx-native
-- Configuring done
-- Generating done
-- Build files have been written to: /Dev/simplelog/build/x86-osx-native
[ 50%] Built target simplelog
Scanning dependencies of target simplelog-test-basic
[100%] Building C object test/CMakeFiles/simplelog-basic.dir/Dev/simplelog/test/basic.c.o
Linking C executable simplelog-test-basic
[100%] Built target simplelog-test-basic
```

Now let’s run our test, you’ll find it at `./build/x86-osx-native/test/simplelog-test-basic` (more on that path name later):

```sh
> ./build/x86-osx-native/test/simplelog-test-basic
[debug] hello simplelog!
```

Looks like things are working! Now, more on that path. It’s made up of three parts:

 * **`./build/`**: the build directory, everything that `yotta` generates when building is placed in here.
 * `./build/`**`x86-osx-native`**: this is the *target* that `yotta` was building for. If you don’t explicitly set the target, then yotta builds to run on the current computer. (If you built on a linux or windows computer `x86-osx-native` would be different.)
 * `./build/x86-osx-native/`**`test/simplelog-test-basic`**: the path to the test. The test name is automatically prefixed with `<modulename>-test-` as executable names must be globally unique.

### A Few Words About Targets
In ARM we use `yotta` to build software for embedded devices – not just desktop computers. When you’re compiling the same software for lots of different devices you need a mechanism to do different things, and often to include different dependencies, for each of the different devices.

The `yotta target` command lets you do this. It defaults to the system you’re building on (`x86-osx-native` on mac, `x86-linux-native` on linux, etc.) You can display the current target by running `yotta target` with no arguments:`

```sh
> yotta target
x86-osx-native
```

If you set a different target, by running `yotta target <targetname>`, then `yotta` will build code for that target instead.

### yotta publish
The most important part of `yotta` is the ability to publish our module so other can use it. Now that we’ve tested our module, lets do that!

```
> yotta publish
info: generate archive extracting to "simplelog-0.0.0"
warning: no readme.md file detected
info: published latest version: 0.0.0
```

Now anyone can install and use our module in their own program. We should probably add some documentation, too. Do this by creating a readme.md that uses Markdown to describe what your module does.

Publishing publishes the *source* of the module, not the built binaries (as the binaries would only be useful to people building on exactly the same platform).

Note that if you also created a module called simplelog you won't be able to publish it – only one module can exist in the public registry with any given name, so if you've created a better logging module (simplelog doesn't make the bar very high), then you'll need to think of a new name and change it by editing your module.json file. See the [module.json reference](/../reference/module.html) for details.

<a name="Creating an Executable"></a>
## Creating an Executable
Executables are runnable programs that depend on any number of other modules, and add their own code to build a program that does something useful. `yotta` isn’t designed for *distributing* executables, so they shouldn’t normally be published to the public registry, but it works great for building them.

### yotta init
Let’s start by creating a new directory for our executable, and running `yotta init` again, if you previously created the `simplelog` module, then cd up one directory first: `cd ..`

```sh
> mkdir helloyotta
> cd helloyotta
helloyotta james$ yotta init
Enter the module name: <helloyotta>
...
Is this module an executable? <no> yes
```

Note that this time we answered **yes** to the last question. This makes `yotta` add a `"bin"` property in the `module.json` module description file that it generates. If we open this file in a text editor, we'll see that it says to create a binary executable out of the contents of the `./source` directory:

```json
{
  "name": "helloyotta",
  ...
  "bin": "./source"
}
```

### Add Dependencies!
Now we can tell `yotta` that we want to use the module we created earlier, `simplelog`.

```sh
> yotta install simplelog
info: get versions for x86-osx-native
info: get versions for simplelog
info: download simplelog
```

This has saved `simplelog` as a dependency in the `module.json` file, specifying that the version should be approximately equal to the current version. See the [`yotta install`](/../reference/commands.html#yotta-install) documentation for more details.

```json
  "dependencies": {
    "simplelog": "~0.0.0"
  },
```

`yotta install` also creates a `yotta_modules` directory in the module's directory, which it uses to store your dependencies. This, along with a corresponding `yotta_targets directory`, and the `build` directory, may be overwritten by `yotta` at any time, so generally shouldn't be modified, and you should not create files in them.

### Implement
When `yotta` builds, it will automatically arrange that our executable is linked against the dependencies we've specified, and that their header files are available, so we can create a single file in the `./source` directory to get a working executable. For this example, use a file called `./source/hello.cpp`:

```C
#include "simplelog/log.h"

int main(){
    simpleLog(Simple_Log_Info, "Hello yotta!");
    return 0;
}
```

### Build
Just like building a module, when we run `yotta build` yotta will arrange for everything necessary to be compiled, and produce a result:

```sh
> yotta build
info: generate for target: x86-osx-native 0.0.3 at /Dev/helloyotta/yotta_targets/x86-osx-native
-- Configuring done
-- Generating done
-- Build files have been written to: /Dev/helloyotta/build/x86-osx-native
Scanning dependencies of target simplelog
...
[100%] Building C object source/CMakeFiles/helloyotta.dir/Dev/helloyotta/source/hello.cpp.o
Linking C executable helloyotta
[100%] Built target helloyotta
```

Notice that `yotta` also built simplelog's library: as modules are distributed as source, they need to be compiled too.

Our executable will have been placed in `./build/x86-osx-native/source`, (or `./build/<targetname>/source` if you're compiling for a different target.). Let's run it:

```sh
> ./build/x86-osx-native/source/helloyotta
[info] Hello yotta!
```

### That's it!
That's all of the basic principles of using `yotta`, for more information check out the full [command reference](/../reference/commands.html).