Skip to content

Commit 510c4de

Browse files
committed
PlatformIO as Continuous Integration tool for embedded projects // Resolve #108
1 parent 7822a92 commit 510c4de

File tree

6 files changed

+278
-2
lines changed

6 files changed

+278
-2
lines changed

HISTORY.rst

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ Release History
44
2.0.0 (2015-??-??)
55
------------------
66

7-
* Implemented PlatformIO CLI 2.0: "platform" related commands have been
7+
* PlatformIO CLI 2.0: "platform" related commands have been
88
moved to ``platformio platforms`` subcommand
99
(`issue #158 <https://github.com/platformio/platformio/issues/158>`_)
10+
* PlatformIO as Continuous Integration (CI) tool for embedded projects
11+
(`issue #108 <https://github.com/platformio/platformio/issues/108>`_)
1012
* Created `PlatformIO gitter.im <https://gitter.im/platformio/platformio>`_ room
1113
(`issue #174 <https://github.com/platformio/platformio/issues/174>`_)
1214
* Added global ``-f, --force`` option which will force to accept any

docs/userguide/cmd_ci.rst

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
.. _cmd_ci:
2+
3+
platformio ci
4+
=============
5+
6+
.. contents::
7+
8+
Usage
9+
-----
10+
11+
.. code-block:: bash
12+
13+
platformio ci [OPTIONS] [SRC]
14+
15+
16+
Description
17+
-----------
18+
19+
`Continuous integration (CI, wiki) <http://en.wikipedia.org/wiki/Continuous_integration>`_
20+
is the practice, in software engineering, of merging all developer working
21+
copies with a shared mainline several times a day.
22+
23+
:ref:`cmd_ci` command is conceived of as "hot key" for building project with
24+
arbitrary source code structure. In a nutshell, using ``SRC`` and
25+
:option:`platformio ci --lib` contents PlatformIO initialises via
26+
:ref:`cmd_init` new project in :option:`platformio ci --build-dir`
27+
with the build environments (using :option:`platformio ci --board` or
28+
:option:`platformio ci --project-conf`) and processes them via :ref:`cmd_run`
29+
command.
30+
31+
:ref:`cmd_ci` command is intended to be used in combination with the build
32+
servers and the popular
33+
`Continuous Integration Software <http://en.wikipedia.org/wiki/Comparison_of_continuous_integration_software>`_.
34+
35+
By integrating regularly, you can detect errors quickly, and locate them more
36+
easily.
37+
38+
.. note::
39+
:ref:`cmd_ci` command accepts **multiple** ``SRC`` arguments,
40+
:option:`platformio ci --lib` and :option:`platformio ci --exclude` options
41+
which can be a path to directory, file or
42+
`Glob Pattern <http://en.wikipedia.org/wiki/Glob_(programming)>`_.
43+
44+
Options
45+
-------
46+
47+
.. program:: platformio ci
48+
49+
.. option::
50+
-l, --lib
51+
52+
Source code which will be copied to ``%build_dir%/lib`` directly.
53+
54+
If :option:`platformio ci --lib` is a path to file (not to directory), then
55+
PlatformIO will create temporary directory within ``%build_dir%/lib`` and copy
56+
the rest files into it.
57+
58+
59+
.. option::
60+
--exclude
61+
62+
Exclude directories and/-or files from :option:`platformio ci --build-dir`. The
63+
path must be relative to PlatformIO project within
64+
:option:`platformio ci --build-dir`.
65+
66+
For example, exclude from project ``src`` directory:
67+
68+
* ``examples`` folder
69+
* ``*.h`` files from ``foo`` folder
70+
71+
.. code-block:: bash
72+
73+
platformio ci --exclude=src/examples --exclude=src/foo/*.h [SRC]
74+
75+
.. option::
76+
--board, -b
77+
78+
Build project with automatically pre-generated environments based on board
79+
settings.
80+
81+
For more details please look into :option:`platformio init --board`.
82+
83+
.. option::
84+
--build-dir
85+
86+
Path to directory where PlatformIO will initialise new project. By default it's
87+
temporary directory within your operation system.
88+
89+
.. note::
90+
91+
This directory will be removed at the end of build process. If you want to
92+
keep it, please use :option:`platformio ci --keep-build-dir`.
93+
94+
.. option::
95+
--keep-build-dir
96+
97+
Don't remove :option:`platformio ci --build-dir` after build process.
98+
99+
.. option::
100+
--project-conf
101+
102+
Buid project using pre-configured :ref:`projectconf`.
103+
104+
Examples
105+
--------
106+
107+
1. Integration `Travis.CI <http://travis-ci.org/>`_ for GitHub
108+
`USB_Host_Shield_2.0 <https://github.com/felis/USB_Host_Shield_2.0>`_
109+
project. The ``.travis.yml`` configuration file:
110+
111+
.. code-block:: yaml
112+
113+
language: python
114+
python:
115+
- "2.7"
116+
117+
env:
118+
- PLATFORMIO_CI_SRC=examples/Bluetooth/PS3SPP/PS3SPP.ino
119+
- PLATFORMIO_CI_SRC=examples/pl2303/pl2303_gps/pl2303_gps.ino
120+
121+
install:
122+
- python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)"
123+
124+
script:
125+
- platformio ci --lib="." --board=uno --board=teensy31 --board=due

docs/userguide/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Commands
4444
:maxdepth: 2
4545

4646
cmd_boards
47+
cmd_ci
4748
cmd_init
4849
platformio lib <lib/index>
4950
platformio platforms <platforms/index>

platformio/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright (C) Ivan Kravets <[email protected]>
22
# See LICENSE for details.
33

4-
VERSION = (2, 0, "0.dev4")
4+
VERSION = (2, 0, "0.dev5")
55
__version__ = ".".join([str(s) for s in VERSION])
66

77
__title__ = "platformio"

platformio/commands/ci.py

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Copyright (C) Ivan Kravets <[email protected]>
2+
# See LICENSE for details.
3+
4+
from glob import glob
5+
from os import environ, makedirs, remove
6+
from os.path import basename, isdir, isfile, join
7+
from shutil import copyfile, copytree, rmtree
8+
from tempfile import mkdtemp
9+
10+
import click
11+
12+
from platformio import app
13+
from platformio.commands.init import cli as cmd_init
14+
from platformio.commands.run import cli as cmd_run
15+
from platformio.exception import CIBuildEnvsEmpty
16+
from platformio.util import get_boards
17+
18+
19+
def validate_path(ctx, param, value): # pylint: disable=W0613
20+
invalid_path = None
21+
for p in value:
22+
if not glob(p):
23+
invalid_path = p
24+
break
25+
try:
26+
assert invalid_path is None
27+
return value
28+
except AssertionError:
29+
raise click.BadParameter("Found invalid path: %s" % invalid_path)
30+
31+
32+
def validate_boards(ctx, param, value): # pylint: disable=W0613
33+
unknown_boards = set(value) - set(get_boards().keys())
34+
try:
35+
assert not unknown_boards
36+
return value
37+
except AssertionError:
38+
raise click.BadParameter(
39+
"%s. Please search for the board types using "
40+
"`platformio boards` command" % ", ".join(unknown_boards))
41+
42+
43+
@click.command("ci", short_help="Continuous Integration")
44+
@click.argument("src", nargs=-1, callback=validate_path)
45+
@click.option("--lib", "-l", multiple=True, callback=validate_path)
46+
@click.option("--exclude", multiple=True)
47+
@click.option("--board", "-b", multiple=True, callback=validate_boards)
48+
@click.option("--build-dir", default=mkdtemp,
49+
type=click.Path(exists=True, file_okay=False, dir_okay=True,
50+
writable=True, resolve_path=True))
51+
@click.option("--keep-build-dir", is_flag=True)
52+
@click.option("--project-conf",
53+
type=click.Path(exists=True, file_okay=True, dir_okay=False,
54+
readable=True, resolve_path=True))
55+
@click.pass_context
56+
def cli(ctx, src, lib, exclude, board, # pylint: disable=R0913
57+
build_dir, keep_build_dir, project_conf):
58+
59+
if not src:
60+
src = environ.get("PLATFORMIO_CI_SRC", "").split(":")
61+
if not src:
62+
raise click.BadParameter("Missing argument 'src'")
63+
64+
try:
65+
app.set_session_var("force_option", True)
66+
_clean_dir(build_dir)
67+
68+
for dir_name, patterns in dict(lib=lib, src=src).iteritems():
69+
if not patterns:
70+
continue
71+
contents = []
72+
for p in patterns:
73+
contents += glob(p)
74+
_copy_contents(join(build_dir, dir_name), contents)
75+
76+
if project_conf and isfile(project_conf):
77+
copyfile(project_conf, join(build_dir, "platformio.ini"))
78+
elif not board:
79+
raise CIBuildEnvsEmpty()
80+
81+
if exclude:
82+
_exclude_contents(build_dir, exclude)
83+
84+
# initialise project
85+
ctx.invoke(cmd_init, project_dir=build_dir, board=board,
86+
disable_auto_uploading=True)
87+
88+
# process project
89+
ctx.invoke(cmd_run, project_dir=build_dir)
90+
finally:
91+
if not keep_build_dir:
92+
rmtree(build_dir)
93+
94+
95+
def _clean_dir(dirpath):
96+
rmtree(dirpath)
97+
makedirs(dirpath)
98+
99+
100+
def _copy_contents(dst_dir, contents):
101+
items = {
102+
"dirs": set(),
103+
"files": set()
104+
}
105+
106+
for path in contents:
107+
if isdir(path):
108+
items['dirs'].add(path)
109+
elif isfile(path):
110+
items['files'].add(path)
111+
112+
dst_dir_name = basename(dst_dir)
113+
114+
if dst_dir_name == "src" and len(items['dirs']) == 1:
115+
copytree(list(items['dirs']).pop(), dst_dir)
116+
else:
117+
makedirs(dst_dir)
118+
for d in items['dirs']:
119+
copytree(d, join(dst_dir, basename(d)))
120+
121+
if not items['files']:
122+
return
123+
124+
if dst_dir_name == "lib":
125+
dst_dir = join(dst_dir, mkdtemp(dir=dst_dir))
126+
127+
for f in items['files']:
128+
copyfile(f, join(dst_dir, basename(f)))
129+
130+
131+
def _exclude_contents(dst_dir, patterns):
132+
contents = []
133+
for p in patterns:
134+
contents += glob(join(dst_dir, p))
135+
for path in contents:
136+
if isdir(path):
137+
rmtree(path)
138+
elif isfile(path):
139+
remove(path)

platformio/exception.py

+9
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,15 @@ class UpgraderFailed(PlatformioException):
165165
MESSAGE = "An error occurred while upgrading PlatformIO"
166166

167167

168+
class CIBuildEnvsEmpty(PlatformioException):
169+
170+
MESSAGE = (
171+
"Can't find PlatformIO build environments.\nPlease specify `--board` "
172+
"or path to `platformio.ini` with predefined environments using "
173+
"`--project-conf` option"
174+
)
175+
176+
168177
class SConsNotInstalled(PlatformioException):
169178

170179
MESSAGE = (

0 commit comments

Comments
 (0)