From 8d751948844a466173080a9d6fc27fd2a87d1b3b Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 16 Apr 2015 17:04:45 +0100 Subject: [PATCH 01/78] Add global -f, --force option which will force to accept any confirmation prompts // Resolve #152 --- platformio/__init__.py | 2 +- platformio/__main__.py | 10 ++++++---- platformio/app.py | 23 ++++++++++++++++++++--- platformio/maintenance.py | 3 ++- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/platformio/__init__.py b/platformio/__init__.py index 2aa935d0..1f49e63a 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -VERSION = (1, 4, 0) +VERSION = (2, 0, "0.dev0") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" diff --git a/platformio/__main__.py b/platformio/__main__.py index fbd34974..a3de3544 100644 --- a/platformio/__main__.py +++ b/platformio/__main__.py @@ -37,14 +37,16 @@ class PlatformioCLI(click.MultiCommand): # pylint: disable=R0904 @click.command(cls=PlatformioCLI) @click.version_option(__version__, prog_name="PlatformIO") +@click.option("--force", "-f", is_flag=True, + help="Force to accept any confirmation prompts") @click.pass_context -def cli(ctx): - maintenance.on_platformio_start(ctx) +def cli(ctx, force): + maintenance.on_platformio_start(ctx, force) @cli.resultcallback() @click.pass_context -def process_result(ctx, result): +def process_result(ctx, result, force): # pylint: disable=W0613 maintenance.on_platformio_end(ctx, result) @@ -54,7 +56,7 @@ def main(): # /en/latest/security.html#insecureplatformwarning requests.packages.urllib3.disable_warnings() - cli(None) + cli(None, None) except Exception as e: # pylint: disable=W0703 if not isinstance(e, exception.ReturnErrorCode): maintenance.on_platformio_exception(e) diff --git a/platformio/app.py b/platformio/app.py index fe299d6a..2060d552 100644 --- a/platformio/app.py +++ b/platformio/app.py @@ -47,6 +47,11 @@ DEFAULT_SETTINGS = { } +SESSION_VARS = { + "force_option": False +} + + class State(object): def __init__(self, path=None): @@ -101,9 +106,12 @@ def set_state_item(name, value): def get_setting(name): - # disable prompts for Continuous Integration systems - if name == "enable_prompts" and getenv("CI", "").lower() == "true": - return False + if name == "enable_prompts": + # disable prompts for Continuous Integration systems + # and when global "--force" option is set + if any([getenv("CI", "").lower() == "true", + get_session_var("force_option")]): + return False _env_name = "PLATFORMIO_SETTING_%s" % name.upper() if _env_name in environ: @@ -127,3 +135,12 @@ def reset_settings(): with State() as data: if "settings" in data: del data['settings'] + + +def get_session_var(name, default=None): + return SESSION_VARS.get(name, default) + + +def set_session_var(name, value): + assert name in SESSION_VARS + SESSION_VARS[name] = value diff --git a/platformio/maintenance.py b/platformio/maintenance.py index 87d673d4..4a00efe9 100644 --- a/platformio/maintenance.py +++ b/platformio/maintenance.py @@ -20,7 +20,8 @@ from platformio.platforms.base import PlatformFactory from platformio.util import get_home_dir, get_lib_dir -def on_platformio_start(ctx): +def on_platformio_start(ctx, force): + app.set_session_var("force_option", force) telemetry.on_command(ctx) after_upgrade(ctx) From a9942e675f1ea6d7e9ba6a302f1cdbb41965c68b Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 16 Apr 2015 17:34:22 +0100 Subject: [PATCH 02/78] Allow to choose which library to update // Resolve #168 --- HISTORY.rst | 9 +++++++++ docs/userguide/lib/cmd_update.rst | 21 +++++++++++++++++++-- platformio/commands/lib.py | 9 +++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 9bbcf11f..41d4c90c 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,6 +1,15 @@ Release History =============== +2.0.0 (2015-??-??) +------------------ + +* Added global ``-f, --force`` option which will force to accept any + confirmation prompts (`issue #152 `_) +* Allowed to choose which library to update + (`issue #168 `_) + + 1.4.0 (2015-04-11) ------------------ diff --git a/docs/userguide/lib/cmd_update.rst b/docs/userguide/lib/cmd_update.rst index f719fa13..f58fab4c 100644 --- a/docs/userguide/lib/cmd_update.rst +++ b/docs/userguide/lib/cmd_update.rst @@ -10,7 +10,7 @@ Usage .. code-block:: bash - platformio lib update + platformio lib update [LIBRARY_ID] Description @@ -22,6 +22,8 @@ Check or update installed libraries Examples -------- +1. Update all installed libraries + .. code-block:: bash $ platformio lib update @@ -36,8 +38,23 @@ Examples # Updating [ 13 ] Adafruit-GFX library: # Versions: Current=a9e5bc4707, Latest=a9e5bc4707 [Up-to-date] # Updating [ 1 ] OneWire library: - # Versions: Current=2.2, Latest=2.2 [Up-to-date] + # Versions: Current=2.2, Latest=2.2 [Up-to-date] # Updating [ 4 ] IRremote library: # Versions: Current=f2dafe5030, Latest=f2dafe5030 [Up-to-date] # Updating [ 14 ] Adafruit-9DOF-Unified library: # Versions: Current=b2f07242ac, Latest=b2f07242ac [Up-to-date] + +2. Update specified libraries + +.. code-block:: bash + + $ platformio lib update 1 59 + # Updating [ 1 ] OneWire library: + # Versions: Current=2.2, Latest=2.2 [Up-to-date] + # Updating [ 59 ] USB-Host-Shield-20 library: + # Versions: Current=fcab83dcb3, Latest=c61f9ce1c2 [Out-of-date] + # The library #59 'USB-Host-Shield-20' has been successfully uninstalled! + # Installing library [ 59 ]: + # Downloading [####################################] 100% + # Unpacking [####################################] 100% + # The library #59 'USB-Host-Shield-20' has been successfully installed! diff --git a/platformio/commands/lib.py b/platformio/commands/lib.py index 91c8dced..da3d2b19 100644 --- a/platformio/commands/lib.py +++ b/platformio/commands/lib.py @@ -209,13 +209,18 @@ def lib_show(libid): @cli.command("update", short_help="Update installed libraries") +@click.argument("libid", type=click.INT, nargs=-1, required=False, + metavar="[LIBRARY_ID]") @click.pass_context -def lib_update(ctx): +def lib_update(ctx, libid): lm = LibraryManager(get_lib_dir()) for id_, latest_version in (lm.get_latest_versions() or {}).items(): + if libid and int(id_) not in libid: + continue + info = lm.get_info(int(id_)) - click.echo("Updating [ %s ] %s library:" % ( + click.echo("Updating [ %s ] %s library:" % ( click.style(id_, fg="yellow"), click.style(info['name'], fg="cyan"))) From b52ff11d3c3b5a057988c20cc3633d2a3287095c Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 17 Apr 2015 12:28:10 +0100 Subject: [PATCH 03/78] Disabled automatic updates by default for platforms, packages and libraries // Resolve #171 --- HISTORY.rst | 2 ++ platformio/app.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 41d4c90c..cbcf528d 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -8,6 +8,8 @@ Release History confirmation prompts (`issue #152 `_) * Allowed to choose which library to update (`issue #168 `_) +* Disabled automatic updates by default for platforms, packages and libraries + (`issue #171 `_) 1.4.0 (2015-04-11) diff --git a/platformio/app.py b/platformio/app.py index 2060d552..c7776827 100644 --- a/platformio/app.py +++ b/platformio/app.py @@ -24,11 +24,11 @@ DEFAULT_SETTINGS = { }, "auto_update_platforms": { "description": "Automatically update platforms (Yes/No)", - "value": True + "value": False }, "auto_update_libraries": { "description": "Automatically update libraries (Yes/No)", - "value": True + "value": False }, "enable_telemetry": { "description": ("Shares commands, platforms and libraries usage" From 07022bb3be88055a1a00488aae73564563ad5723 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 17 Apr 2015 12:37:03 +0100 Subject: [PATCH 04/78] Implement PlatformIO CLI 2.0 // Resolve #158 --- HISTORY.rst | 3 + platformio/__init__.py | 2 +- platformio/commands/install.py | 20 ---- platformio/commands/list.py | 34 ------- platformio/commands/platforms.py | 154 +++++++++++++++++++++++++++++++ platformio/commands/run.py | 5 +- platformio/commands/search.py | 46 --------- platformio/commands/show.py | 46 --------- platformio/commands/uninstall.py | 17 ---- platformio/commands/update.py | 22 ++--- platformio/maintenance.py | 19 ++-- tests/commands/test_boards.py | 16 ++-- 12 files changed, 190 insertions(+), 194 deletions(-) delete mode 100644 platformio/commands/install.py delete mode 100644 platformio/commands/list.py create mode 100644 platformio/commands/platforms.py delete mode 100644 platformio/commands/search.py delete mode 100644 platformio/commands/show.py delete mode 100644 platformio/commands/uninstall.py diff --git a/HISTORY.rst b/HISTORY.rst index cbcf528d..f2fc641a 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,6 +4,9 @@ Release History 2.0.0 (2015-??-??) ------------------ +* Implemented PlatformIO CLI 2.0: "platform" related commands have been + moved to ``platformio platforms`` subcommand + (`issue #158 `_) * Added global ``-f, --force`` option which will force to accept any confirmation prompts (`issue #152 `_) * Allowed to choose which library to update diff --git a/platformio/__init__.py b/platformio/__init__.py index 1f49e63a..c6851e7e 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -VERSION = (2, 0, "0.dev0") +VERSION = (2, 0, "0.dev1") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" diff --git a/platformio/commands/install.py b/platformio/commands/install.py deleted file mode 100644 index af476f43..00000000 --- a/platformio/commands/install.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) Ivan Kravets -# See LICENSE for details. - -import click - -from platformio.platforms.base import PlatformFactory - - -@click.command("install", short_help="Install new platforms") -@click.argument("platforms", nargs=-1) -@click.option("--with-package", multiple=True, metavar="") -@click.option("--without-package", multiple=True, metavar="") -@click.option("--skip-default-package", is_flag=True) -def cli(platforms, with_package, without_package, skip_default_package): - - for platform in platforms: - p = PlatformFactory.newPlatform(platform) - if p.install(with_package, without_package, skip_default_package): - click.secho("The platform '%s' has been successfully installed!" % - platform, fg="green") diff --git a/platformio/commands/list.py b/platformio/commands/list.py deleted file mode 100644 index c47a95b8..00000000 --- a/platformio/commands/list.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) Ivan Kravets -# See LICENSE for details. - -import json - -import click - -from platformio.platforms.base import PlatformFactory - - -@click.command("list", short_help="List installed platforms") -@click.option("--json-output", is_flag=True) -def cli(json_output): - - installed_platforms = PlatformFactory.get_platforms( - installed=True).keys() - installed_platforms.sort() - - data = [] - for platform in installed_platforms: - p = PlatformFactory.newPlatform(platform) - data.append({ - "name": platform, - "packages": p.get_installed_packages() - }) - - if json_output: - click.echo(json.dumps(data)) - else: - for item in data: - click.echo("{name:<20} with packages: {pkgs}".format( - name=click.style(item['name'], fg="cyan"), - pkgs=", ".join(item['packages']) - )) diff --git a/platformio/commands/platforms.py b/platformio/commands/platforms.py new file mode 100644 index 00000000..b3781074 --- /dev/null +++ b/platformio/commands/platforms.py @@ -0,0 +1,154 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +import json +from datetime import datetime + +import click + +from platformio import app +from platformio.exception import PlatformNotInstalledYet +from platformio.pkgmanager import PackageManager +from platformio.platforms.base import PlatformFactory + + +@click.group(short_help="Platforms and Packages Manager") +def cli(): + pass + + +@cli.command("install", short_help="Install new platforms") +@click.argument("platforms", nargs=-1) +@click.option("--with-package", multiple=True, metavar="") +@click.option("--without-package", multiple=True, metavar="") +@click.option("--skip-default-package", is_flag=True) +def platforms_install(platforms, with_package, without_package, + skip_default_package): + for platform in platforms: + p = PlatformFactory.newPlatform(platform) + if p.install(with_package, without_package, skip_default_package): + click.secho("The platform '%s' has been successfully installed!" % + platform, fg="green") + + +@cli.command("list", short_help="List installed platforms") +@click.option("--json-output", is_flag=True) +def platforms_list(json_output): + + installed_platforms = PlatformFactory.get_platforms( + installed=True).keys() + installed_platforms.sort() + + data = [] + for platform in installed_platforms: + p = PlatformFactory.newPlatform(platform) + data.append({ + "name": platform, + "packages": p.get_installed_packages() + }) + + if json_output: + click.echo(json.dumps(data)) + else: + for item in data: + click.echo("{name:<20} with packages: {pkgs}".format( + name=click.style(item['name'], fg="cyan"), + pkgs=", ".join(item['packages']) + )) + + +@cli.command("search", short_help="Search for development platforms") +@click.argument("query", required=False) +@click.option("--json-output", is_flag=True) +def platforms_search(query, json_output): + + data = [] + platforms = PlatformFactory.get_platforms().keys() + platforms.sort() + for platform in platforms: + p = PlatformFactory.newPlatform(platform) + type_ = p.get_type() + description = p.get_description() + + if query == "all": + query = "" + + search_data = "%s %s %s" % (type_, description, p.get_packages()) + if query and query.lower() not in search_data.lower(): + continue + + data.append({ + "type": type_, + "description": description, + "packages": p.get_packages() + }) + + if json_output: + click.echo(json.dumps(data)) + else: + for item in data: + click.secho(item['type'], fg="cyan", nl=False) + click.echo(" (available packages: %s)" % ", ".join( + p.get_packages().keys())) + click.secho("-" * len(item['type']), fg="cyan") + click.echo(item['description']) + click.echo() + + +@cli.command("show", short_help="Show details about installed platform") +@click.argument("platform") +@click.pass_context +def platforms_show(ctx, platform): + + installed_platforms = PlatformFactory.get_platforms( + installed=True).keys() + + if platform not in installed_platforms: + if (not app.get_setting("enable_prompts") or + click.confirm("The platform '%s' has not been installed yet. " + "Would you like to install it now?" % platform)): + ctx.invoke(platforms_install, platforms=[platform]) + else: + raise PlatformNotInstalledYet(platform) + + p = PlatformFactory.newPlatform(platform) + click.echo("{name:<20} - {description} [ {url} ]".format( + name=click.style(p.get_type(), fg="cyan"), + description=p.get_description(), url=p.get_vendor_url())) + + installed_packages = PackageManager.get_installed() + for name in p.get_installed_packages(): + data = installed_packages[name] + pkgalias = p.get_pkg_alias(name) + click.echo("----------") + click.echo("Package: %s" % click.style(name, fg="yellow")) + if pkgalias: + click.echo("Alias: %s" % pkgalias) + click.echo("Version: %d" % int(data['version'])) + click.echo("Installed: %s" % datetime.fromtimestamp( + data['time']).strftime("%Y-%m-%d %H:%M:%S")) + + +@cli.command("uninstall", short_help="Uninstall platforms") +@click.argument("platforms", nargs=-1) +def platforms_uninstall(platforms): + + for platform in platforms: + p = PlatformFactory.newPlatform(platform) + if p.uninstall(): + click.secho("The platform '%s' has been successfully " + "uninstalled!" % platform, fg="green") + + +@cli.command("update", short_help="Update installed Platforms and Packages") +def platforms_update(): + + installed_platforms = PlatformFactory.get_platforms( + installed=True).keys() + installed_platforms.sort() + + for platform in installed_platforms: + click.echo("\nPlatform %s" % click.style(platform, fg="cyan")) + click.echo("--------") + p = PlatformFactory.newPlatform(platform) + p.update() diff --git a/platformio/commands/run.py b/platformio/commands/run.py index d4c5bd8a..8d3d792f 100644 --- a/platformio/commands/run.py +++ b/platformio/commands/run.py @@ -9,7 +9,8 @@ from time import time import click from platformio import app, exception, telemetry, util -from platformio.commands.install import cli as cmd_install +from platformio.commands.platforms import \ + platforms_install as cmd_platforms_install from platformio.platforms.base import PlatformFactory @@ -120,7 +121,7 @@ def _run_environment(ctx, name, options, targets, upload_port): not app.get_setting("enable_prompts") or click.confirm("The platform '%s' has not been installed yet. " "Would you like to install it now?" % platform))): - ctx.invoke(cmd_install, platforms=[platform]) + ctx.invoke(cmd_platforms_install, platforms=[platform]) p = PlatformFactory.newPlatform(platform) return p.run(variables, envtargets) diff --git a/platformio/commands/search.py b/platformio/commands/search.py deleted file mode 100644 index d3b7d69a..00000000 --- a/platformio/commands/search.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (C) Ivan Kravets -# See LICENSE for details. - -import json - -import click - -from platformio.platforms.base import PlatformFactory - - -@click.command("search", short_help="Search for development platforms") -@click.argument("query", required=False) -@click.option("--json-output", is_flag=True) -def cli(query, json_output): - - data = [] - platforms = PlatformFactory.get_platforms().keys() - platforms.sort() - for platform in platforms: - p = PlatformFactory.newPlatform(platform) - type_ = p.get_type() - description = p.get_description() - - if query == "all": - query = "" - - search_data = "%s %s %s" % (type_, description, p.get_packages()) - if query and query.lower() not in search_data.lower(): - continue - - data.append({ - "type": type_, - "description": description, - "packages": p.get_packages() - }) - - if json_output: - click.echo(json.dumps(data)) - else: - for item in data: - click.secho(item['type'], fg="cyan", nl=False) - click.echo(" (available packages: %s)" % ", ".join( - p.get_packages().keys())) - click.secho("-" * len(item['type']), fg="cyan") - click.echo(item['description']) - click.echo() diff --git a/platformio/commands/show.py b/platformio/commands/show.py deleted file mode 100644 index f0913ff9..00000000 --- a/platformio/commands/show.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (C) Ivan Kravets -# See LICENSE for details. - -from datetime import datetime - -import click - -from platformio import app -from platformio.commands.install import cli as cmd_install -from platformio.exception import PlatformNotInstalledYet -from platformio.pkgmanager import PackageManager -from platformio.platforms.base import PlatformFactory - - -@click.command("show", short_help="Show details about installed platform") -@click.argument("platform") -@click.pass_context -def cli(ctx, platform): - - installed_platforms = PlatformFactory.get_platforms( - installed=True).keys() - - if platform not in installed_platforms: - if (not app.get_setting("enable_prompts") or - click.confirm("The platform '%s' has not been installed yet. " - "Would you like to install it now?" % platform)): - ctx.invoke(cmd_install, platforms=[platform]) - else: - raise PlatformNotInstalledYet(platform) - - p = PlatformFactory.newPlatform(platform) - click.echo("{name:<20} - {description} [ {url} ]".format( - name=click.style(p.get_type(), fg="cyan"), - description=p.get_description(), url=p.get_vendor_url())) - - installed_packages = PackageManager.get_installed() - for name in p.get_installed_packages(): - data = installed_packages[name] - pkgalias = p.get_pkg_alias(name) - click.echo("----------") - click.echo("Package: %s" % click.style(name, fg="yellow")) - if pkgalias: - click.echo("Alias: %s" % pkgalias) - click.echo("Version: %d" % int(data['version'])) - click.echo("Installed: %s" % datetime.fromtimestamp( - data['time']).strftime("%Y-%m-%d %H:%M:%S")) diff --git a/platformio/commands/uninstall.py b/platformio/commands/uninstall.py deleted file mode 100644 index a14569e2..00000000 --- a/platformio/commands/uninstall.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (C) Ivan Kravets -# See LICENSE for details. - -import click - -from platformio.platforms.base import PlatformFactory - - -@click.command("uninstall", short_help="Uninstall platforms") -@click.argument("platforms", nargs=-1) -def cli(platforms): - - for platform in platforms: - p = PlatformFactory.newPlatform(platform) - if p.uninstall(): - click.secho("The platform '%s' has been successfully " - "uninstalled!" % platform, fg="green") diff --git a/platformio/commands/update.py b/platformio/commands/update.py index 9091e31e..8afdc5ed 100644 --- a/platformio/commands/update.py +++ b/platformio/commands/update.py @@ -3,18 +3,14 @@ import click -from platformio.platforms.base import PlatformFactory +from platformio.commands.lib import lib_update as cmd_lib_update +from platformio.commands.platforms import \ + platforms_update as cmd_platforms_update -@click.command("update", short_help="Update installed platforms") -def cli(): - - installed_platforms = PlatformFactory.get_platforms( - installed=True).keys() - installed_platforms.sort() - - for platform in installed_platforms: - click.echo("\nPlatform %s" % click.style(platform, fg="cyan")) - click.echo("--------") - p = PlatformFactory.newPlatform(platform) - p.update() +@click.command("update", + short_help="Update installed Platforms, Packages and Libraries") +@click.pass_context +def cli(ctx): + ctx.invoke(cmd_platforms_update) + ctx.invoke(cmd_lib_update) diff --git a/platformio/maintenance.py b/platformio/maintenance.py index 4a00efe9..36a91f4e 100644 --- a/platformio/maintenance.py +++ b/platformio/maintenance.py @@ -11,9 +11,11 @@ from time import time import click from platformio import __version__, app, exception, telemetry -from platformio.commands.install import cli as cmd_install from platformio.commands.lib import lib_update as cmd_libraries_update -from platformio.commands.update import cli as cli_update +from platformio.commands.platforms import \ + platforms_install as cmd_platforms_install +from platformio.commands.platforms import \ + platforms_update as cmd_platforms_update from platformio.commands.upgrade import get_latest_version from platformio.libmanager import LibraryManager from platformio.platforms.base import PlatformFactory @@ -90,7 +92,7 @@ class Upgrader(object): remove(join(get_home_dir(), fname)) if prev_platforms: - ctx.invoke(cmd_install, platforms=prev_platforms) + ctx.invoke(cmd_platforms_install, platforms=prev_platforms) return True @@ -98,8 +100,7 @@ class Upgrader(object): installed_platforms = PlatformFactory.get_platforms( installed=True).keys() if installed_platforms: - ctx.invoke(cmd_install, platforms=installed_platforms) - ctx.invoke(cli_update) + ctx.invoke(cmd_platforms_install, platforms=installed_platforms) return True @@ -134,6 +135,8 @@ def after_upgrade(ctx): u = Upgrader(last_version, __version__) if u.run(ctx): app.set_state_item("last_version", __version__) + ctx.invoke(cmd_platforms_update) + click.secho("PlatformIO has been successfully upgraded to %s!\n" % __version__, fg="green") @@ -195,14 +198,14 @@ def check_internal_updates(ctx, what): if not app.get_setting("auto_update_" + what): click.secho("Please update them via ", fg="yellow", nl=False) - click.secho("`platformio %supdate`" % - ("lib " if what == "libraries" else ""), + click.secho("`platformio %s update`" % + ("lib" if what == "libraries" else "platforms"), fg="cyan", nl=False) click.secho(" command.\n", fg="yellow") else: click.secho("Please wait while updating %s ..." % what, fg="yellow") if what == "platforms": - ctx.invoke(cli_update) + ctx.invoke(cmd_platforms_update) elif what == "libraries": ctx.invoke(cmd_libraries_update) click.echo() diff --git a/tests/commands/test_boards.py b/tests/commands/test_boards.py index 4a96d2e5..62a7f102 100644 --- a/tests/commands/test_boards.py +++ b/tests/commands/test_boards.py @@ -5,13 +5,15 @@ import json from os.path import isfile, join from platformio import util -from platformio.commands.boards import cli as boards_cli -from platformio.commands.install import cli as install_cli -from platformio.commands.search import cli as search_cli +from platformio.commands.boards import cli as cmd_boards +from platformio.commands.platforms import \ + platforms_install as cmd_platforms_install +from platformio.commands.platforms import \ + platforms_search as cmd_platforms_search def test_board_json_output(platformio_setup, clirunner, validate_cliresult): - result = clirunner.invoke(boards_cli, ["cortex", "--json-output"]) + result = clirunner.invoke(cmd_boards, ["cortex", "--json-output"]) validate_cliresult(result) boards = json.loads(result.output) assert isinstance(boards, dict) @@ -19,7 +21,7 @@ def test_board_json_output(platformio_setup, clirunner, validate_cliresult): def test_board_raw_output(platformio_setup, clirunner, validate_cliresult): - result = clirunner.invoke(boards_cli, ["energia"]) + result = clirunner.invoke(cmd_boards, ["energia"]) validate_cliresult(result) assert "titiva" in result.output @@ -29,7 +31,7 @@ def test_board_options(platformio_setup, clirunner, validate_cliresult): ["build", "platform", "upload", "name"]) # fetch available platforms - result = clirunner.invoke(search_cli, ["--json-output"]) + result = clirunner.invoke(cmd_platforms_search, ["--json-output"]) validate_cliresult(result) search_result = json.loads(result.output) assert isinstance(search_result, list) @@ -43,7 +45,7 @@ def test_board_options(platformio_setup, clirunner, validate_cliresult): def test_board_ldscripts(platformio_setup, clirunner, validate_cliresult): result = clirunner.invoke( - install_cli, [ + cmd_platforms_install, [ "ststm32", "--skip-default-package", "--with-package=ldscripts" From 1cf026d9568b228cc6494ec4c4b6751290154674 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 17 Apr 2015 15:32:33 +0100 Subject: [PATCH 05/78] Fix platform tests according refactor changes --- tests/commands/test_list.py | 22 ----------------- tests/commands/test_platforms.py | 42 ++++++++++++++++++++++++++++++++ tests/commands/test_search.py | 22 ----------------- 3 files changed, 42 insertions(+), 44 deletions(-) delete mode 100644 tests/commands/test_list.py create mode 100644 tests/commands/test_platforms.py delete mode 100644 tests/commands/test_search.py diff --git a/tests/commands/test_list.py b/tests/commands/test_list.py deleted file mode 100644 index e40b5926..00000000 --- a/tests/commands/test_list.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (C) Ivan Kravets -# See LICENSE for details. - -import json - -from platformio.commands.list import cli - - -def test_list_json_output(clirunner, validate_cliresult): - result = clirunner.invoke(cli, ["--json-output"]) - validate_cliresult(result) - list_result = json.loads(result.output) - assert isinstance(list_result, list) - assert len(list_result) - platforms = [item['name'] for item in list_result] - assert "titiva" in platforms - - -def test_list_raw_output(clirunner, validate_cliresult): - result = clirunner.invoke(cli) - validate_cliresult(result) - assert "teensy" in result.output diff --git a/tests/commands/test_platforms.py b/tests/commands/test_platforms.py new file mode 100644 index 00000000..310967a2 --- /dev/null +++ b/tests/commands/test_platforms.py @@ -0,0 +1,42 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +import json + +from platformio.commands.platforms import \ + platforms_list as cmd_platforms_list +from platformio.commands.platforms import \ + platforms_search as cmd_platforms_search + + +def test_list_json_output(clirunner, validate_cliresult): + result = clirunner.invoke(cmd_platforms_list, ["--json-output"]) + validate_cliresult(result) + list_result = json.loads(result.output) + assert isinstance(list_result, list) + assert len(list_result) + platforms = [item['name'] for item in list_result] + assert "titiva" in platforms + + +def test_list_raw_output(clirunner, validate_cliresult): + result = clirunner.invoke(cmd_platforms_list) + validate_cliresult(result) + assert "teensy" in result.output + + +def test_search_json_output(clirunner, validate_cliresult): + result = clirunner.invoke(cmd_platforms_search, + ["arduino", "--json-output"]) + validate_cliresult(result) + search_result = json.loads(result.output) + assert isinstance(search_result, list) + assert len(search_result) + platforms = [item['type'] for item in search_result] + assert "atmelsam" in platforms + + +def test_search_raw_output(clirunner, validate_cliresult): + result = clirunner.invoke(cmd_platforms_search, ["arduino"]) + validate_cliresult(result) + assert "teensy" in result.output diff --git a/tests/commands/test_search.py b/tests/commands/test_search.py deleted file mode 100644 index 4179f1b2..00000000 --- a/tests/commands/test_search.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (C) Ivan Kravets -# See LICENSE for details. - -import json - -from platformio.commands.search import cli - - -def test_search_json_output(clirunner, validate_cliresult): - result = clirunner.invoke(cli, ["arduino", "--json-output"]) - validate_cliresult(result) - search_result = json.loads(result.output) - assert isinstance(search_result, list) - assert len(search_result) - platforms = [item['type'] for item in search_result] - assert "atmelsam" in platforms - - -def test_search_raw_output(clirunner, validate_cliresult): - result = clirunner.invoke(cli, ["arduino"]) - validate_cliresult(result) - assert "teensy" in result.output From ee962c1919c1af0f268e979639eca522f387850a Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 20 Apr 2015 16:19:59 +0100 Subject: [PATCH 06/78] Created PlatformIO gitter.im room --- HISTORY.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index f2fc641a..193143d6 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -7,6 +7,8 @@ Release History * Implemented PlatformIO CLI 2.0: "platform" related commands have been moved to ``platformio platforms`` subcommand (`issue #158 `_) +* Created `PlatformIO gitter.im `_ room + (`issue #174 `_) * Added global ``-f, --force`` option which will force to accept any confirmation prompts (`issue #152 `_) * Allowed to choose which library to update From 71261023defad05df76c59c9a00a97eff82ac97c Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 20 Apr 2015 17:20:27 +0100 Subject: [PATCH 07/78] Enhance commands output --- platformio/commands/boards.py | 9 ++++++--- platformio/commands/lib.py | 4 +++- platformio/commands/platforms.py | 3 ++- platformio/commands/serialports.py | 2 +- platformio/commands/settings.py | 3 ++- platformio/maintenance.py | 9 ++++++++- 6 files changed, 22 insertions(+), 8 deletions(-) diff --git a/platformio/commands/boards.py b/platformio/commands/boards.py index 1bb89f21..0681623a 100644 --- a/platformio/commands/boards.py +++ b/platformio/commands/boards.py @@ -18,6 +18,7 @@ def cli(query, json_output): # pylint: disable=R0912 BOARDLIST_TPL = ("{type:<30} {mcu:<14} {frequency:<8} " " {flash:<7} {ram:<6} {name}") + terminal_width, _ = click.get_terminal_size() grpboards = {} for type_, data in get_boards().items(): @@ -31,12 +32,14 @@ def cli(query, json_output): # pylint: disable=R0912 if query.lower() not in search_data.lower(): continue - click.echo("\nPlatform: %s" % platform) - click.echo("-" * 75) + click.echo("") + click.echo("Platform: ", nl=False) + click.secho(platform, bold=True) + click.echo("-" * terminal_width) click.echo(BOARDLIST_TPL.format( type=click.style("Type", fg="cyan"), mcu="MCU", frequency="Frequency", flash="Flash", ram="RAM", name="Name")) - click.echo("-" * 75) + click.echo("-" * terminal_width) for type_, data in sorted(boards.items(), key=lambda b: b[1]['name']): if query: diff --git a/platformio/commands/lib.py b/platformio/commands/lib.py index da3d2b19..f90ce614 100644 --- a/platformio/commands/lib.py +++ b/platformio/commands/lib.py @@ -21,7 +21,9 @@ def echo_liblist_header(): authornames="Authors", description="Description" )) - click.echo("-" * 85) + + terminal_width, _ = click.get_terminal_size() + click.echo("-" * terminal_width) def echo_liblist_item(item): diff --git a/platformio/commands/platforms.py b/platformio/commands/platforms.py index b3781074..36fa9b0b 100644 --- a/platformio/commands/platforms.py +++ b/platformio/commands/platforms.py @@ -86,11 +86,12 @@ def platforms_search(query, json_output): if json_output: click.echo(json.dumps(data)) else: + terminal_width, _ = click.get_terminal_size() for item in data: click.secho(item['type'], fg="cyan", nl=False) click.echo(" (available packages: %s)" % ", ".join( p.get_packages().keys())) - click.secho("-" * len(item['type']), fg="cyan") + click.echo("-" * terminal_width) click.echo(item['description']) click.echo() diff --git a/platformio/commands/serialports.py b/platformio/commands/serialports.py index c52f27da..5c241d03 100644 --- a/platformio/commands/serialports.py +++ b/platformio/commands/serialports.py @@ -25,7 +25,7 @@ def serialports_list(json_output): for item in get_serialports(): click.secho(item['port'], fg="cyan") - click.echo("----------") + click.echo("-" * len(item['port'])) click.echo("Hardware ID: %s" % item['hwid']) click.echo("Description: %s" % item['description']) click.echo("") diff --git a/platformio/commands/settings.py b/platformio/commands/settings.py index f239559f..e600e197 100644 --- a/platformio/commands/settings.py +++ b/platformio/commands/settings.py @@ -16,6 +16,7 @@ def cli(): def settings_get(name): list_tpl = "{name:<40} {value:<35} {description}" + terminal_width, _ = click.get_terminal_size() click.echo(list_tpl.format( name=click.style("Name", fg="cyan"), @@ -23,7 +24,7 @@ def settings_get(name): click.style(" [Default]", fg="yellow")), description="Description" )) - click.echo("-" * 90) + click.echo("-" * terminal_width) for _name, _data in sorted(app.DEFAULT_SETTINGS.items()): if name and name != _name: diff --git a/platformio/maintenance.py b/platformio/maintenance.py index 36a91f4e..c7976452 100644 --- a/platformio/maintenance.py +++ b/platformio/maintenance.py @@ -193,6 +193,10 @@ def check_internal_updates(ctx, what): if not outdated_items: return + terminal_width, _ = click.get_terminal_size() + + click.echo("") + click.echo("*" * terminal_width) click.secho("There are the new updates for %s (%s)" % (what, ", ".join(outdated_items)), fg="yellow") @@ -201,7 +205,7 @@ def check_internal_updates(ctx, what): click.secho("`platformio %s update`" % ("lib" if what == "libraries" else "platforms"), fg="cyan", nl=False) - click.secho(" command.\n", fg="yellow") + click.secho(" command.", fg="yellow") else: click.secho("Please wait while updating %s ..." % what, fg="yellow") if what == "platforms": @@ -212,3 +216,6 @@ def check_internal_updates(ctx, what): telemetry.on_event(category="Auto", action="Update", label=what.title()) + + click.echo("*" * terminal_width) + click.echo("") From 5af3b9b7c98c88a28e515b93142af65aa0ee493b Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 20 Apr 2015 19:55:18 +0100 Subject: [PATCH 08/78] Validate passed boards before project initialization --- platformio/commands/init.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/platformio/commands/init.py b/platformio/commands/init.py index 1c32ecca..12e70990 100644 --- a/platformio/commands/init.py +++ b/platformio/commands/init.py @@ -11,13 +11,25 @@ from platformio import app, exception from platformio.util import get_boards, get_source_dir +def validate_boards(ctx, param, value): + unknown_boards = set(value) - set(get_boards().keys()) + try: + assert not unknown_boards + return value + except AssertionError: + raise click.BadParameter( + "%s. Please search for the board types using " + "`platformio boards` command" % ", ".join(unknown_boards)) + + @click.command("init", short_help="Initialize new PlatformIO based project") @click.option("--project-dir", "-d", default=getcwd, type=click.Path(exists=True, file_okay=False, dir_okay=True, writable=True, resolve_path=True)) -@click.option("--board", "-b", multiple=True, metavar="TYPE") +@click.option("--board", "-b", multiple=True, metavar="TYPE", + callback=validate_boards) @click.option("--disable-auto-uploading", is_flag=True) -def cli(project_dir, board, disable_auto_uploading): +def cli(project_dir, board, disable_auto_uploading, ide): project_file = join(project_dir, "platformio.ini") src_dir = join(project_dir, "src") @@ -33,11 +45,12 @@ def cli(project_dir, board, disable_auto_uploading): # ask about auto-uploading if board and app.get_setting("enable_prompts"): disable_auto_uploading = not click.confirm( - "\nWould you like to enable firmware auto-uploading when project " + "Would you like to enable firmware auto-uploading when project " "is successfully built using `platformio run` command? \n" "Don't forget that you can upload firmware manually using " "`platformio run --target upload` command." ) + click.echo("") if project_dir == getcwd(): click.secho("\nThe current working directory", fg="yellow", nl=False) @@ -45,9 +58,10 @@ def cli(project_dir, board, disable_auto_uploading): click.secho( "will be used for the new project.\n" "You can specify another project directory via\n" - "`platformio init -d %PATH_TO_THE_PROJECT_DIR%` command.\n", + "`platformio init -d %PATH_TO_THE_PROJECT_DIR%` command.", fg="yellow" ) + click.echo("") click.echo("The next files/directories will be created in %s" % click.style(project_dir, fg="cyan")) @@ -70,7 +84,7 @@ def cli(project_dir, board, disable_auto_uploading): if board: fill_project_envs(project_file, board, disable_auto_uploading) click.secho( - "Project has been successfully initialized!\nUseful commands:\n" + "\nProject has been successfully initialized!\nUseful commands:\n" "`platformio run` - process/build project from the current " "directory\n" "`platformio run --target upload` or `platformio run -t upload` " From 4d8cbc1349addcdc63f3e81159e94a2aeccbe897 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 22 Apr 2015 14:21:26 +0100 Subject: [PATCH 09/78] Fix bug with creating copies of source files // Resolve #177 --- HISTORY.rst | 2 ++ platformio/__init__.py | 2 +- platformio/builder/tools/platformio.py | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 193143d6..89ee0785 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -15,6 +15,8 @@ Release History (`issue #168 `_) * Disabled automatic updates by default for platforms, packages and libraries (`issue #171 `_) +* Fixed bug with creating copies of source files + (`issue #177 `_) 1.4.0 (2015-04-11) diff --git a/platformio/__init__.py b/platformio/__init__.py index c6851e7e..ffc095fc 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -VERSION = (2, 0, "0.dev1") +VERSION = (2, 0, "0.dev2") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py index 8402e6e3..a45b526d 100644 --- a/platformio/builder/tools/platformio.py +++ b/platformio/builder/tools/platformio.py @@ -31,7 +31,7 @@ def BuildFirmware(env): firmenv = env.Clone() vdirs = firmenv.VariantDirRecursive( - join("$BUILD_DIR", "src"), "$PROJECTSRC_DIR") + join("$BUILD_DIR", "src"), "$PROJECTSRC_DIR", duplicate=False) # build dependent libs deplibs = firmenv.BuildDependentLibraries("$PROJECTSRC_DIR") @@ -365,7 +365,7 @@ def ConvertInoToCpp(env): if not data: return - tmpcpp_file = join(env.subst("$PROJECTSRC_DIR"), "piomain.cpp") + tmpcpp_file = join(env.subst("$PROJECTSRC_DIR"), "tmp_ino_to.cpp") with open(tmpcpp_file, "w") as f: f.write(data) From 60863a43042b7be6e4fb2ef40c366c02ad951c51 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 22 Apr 2015 14:58:42 +0100 Subject: [PATCH 10/78] Fix PyLint warnings --- platformio/commands/init.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio/commands/init.py b/platformio/commands/init.py index 12e70990..750e4a62 100644 --- a/platformio/commands/init.py +++ b/platformio/commands/init.py @@ -11,7 +11,7 @@ from platformio import app, exception from platformio.util import get_boards, get_source_dir -def validate_boards(ctx, param, value): +def validate_boards(ctx, param, value): # pylint: disable=W0613 unknown_boards = set(value) - set(get_boards().keys()) try: assert not unknown_boards @@ -29,7 +29,7 @@ def validate_boards(ctx, param, value): @click.option("--board", "-b", multiple=True, metavar="TYPE", callback=validate_boards) @click.option("--disable-auto-uploading", is_flag=True) -def cli(project_dir, board, disable_auto_uploading, ide): +def cli(project_dir, board, disable_auto_uploading): project_file = join(project_dir, "platformio.ini") src_dir = join(project_dir, "src") From 47b8a4dd69697dab283b114729bc7d26cbe2dee2 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 22 Apr 2015 15:24:45 +0100 Subject: [PATCH 11/78] Fix test with incorrect board --- tests/commands/test_init.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/commands/test_init.py b/tests/commands/test_init.py index 0d816ff2..d951f760 100644 --- a/tests/commands/test_init.py +++ b/tests/commands/test_init.py @@ -71,4 +71,6 @@ def test_init_disable_auto_uploading(platformio_setup, clirunner, def test_init_incorrect_board(clirunner): result = clirunner.invoke(cli, ["-b", "missed_board"]) - assert isinstance(result.exception, exception.UnknownBoard) + assert result.exit_code == 2 + assert 'Error: Invalid value for "--board" / "-b"' in result.output + assert isinstance(result.exception, SystemExit) From 0d648074e4f65e52a6ef60244b55235234d498e7 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 23 Apr 2015 12:40:19 +0100 Subject: [PATCH 12/78] Allow to add more boards to existing platformio.ini // Resolve #167 --- HISTORY.rst | 3 ++ platformio/commands/init.py | 87 +++++++++++++++++++------------------ platformio/exception.py | 6 --- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 89ee0785..33713b31 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -11,6 +11,9 @@ Release History (`issue #174 `_) * Added global ``-f, --force`` option which will force to accept any confirmation prompts (`issue #152 `_) +* Allowed to add more boards to existing + `platformio.ini `__ + (`issue #167 `_) * Allowed to choose which library to update (`issue #168 `_) * Disabled automatic updates by default for platforms, packages and libraries diff --git a/platformio/commands/init.py b/platformio/commands/init.py index 750e4a62..cd301978 100644 --- a/platformio/commands/init.py +++ b/platformio/commands/init.py @@ -31,17 +31,6 @@ def validate_boards(ctx, param, value): # pylint: disable=W0613 @click.option("--disable-auto-uploading", is_flag=True) def cli(project_dir, board, disable_auto_uploading): - project_file = join(project_dir, "platformio.ini") - src_dir = join(project_dir, "src") - lib_dir = join(project_dir, "lib") - if all([isfile(project_file), isdir(src_dir), isdir(lib_dir)]): - raise exception.ProjectInitialized() - - builtin_boards = set(get_boards().keys()) - if board and not set(board).issubset(builtin_boards): - raise exception.UnknownBoard( - ", ".join(set(board).difference(builtin_boards))) - # ask about auto-uploading if board and app.get_setting("enable_prompts"): disable_auto_uploading = not click.confirm( @@ -56,7 +45,7 @@ def cli(project_dir, board, disable_auto_uploading): click.secho("\nThe current working directory", fg="yellow", nl=False) click.secho(" %s " % project_dir, fg="cyan", nl=False) click.secho( - "will be used for the new project.\n" + "will be used for project.\n" "You can specify another project directory via\n" "`platformio init -d %PATH_TO_THE_PROJECT_DIR%` command.", fg="yellow" @@ -72,49 +61,63 @@ def cli(project_dir, board, disable_auto_uploading): click.echo("%s - Put here project specific or 3-rd party libraries" % click.style("lib", fg="cyan")) - if (not app.get_setting("enable_prompts") or - click.confirm("Do you want to continue?")): - - for d in (src_dir, lib_dir): - if not isdir(d): - makedirs(d) - if not isfile(project_file): - copyfile(join(get_source_dir(), "projectconftpl.ini"), - project_file) - if board: - fill_project_envs(project_file, board, disable_auto_uploading) - click.secho( - "\nProject has been successfully initialized!\nUseful commands:\n" - "`platformio run` - process/build project from the current " - "directory\n" - "`platformio run --target upload` or `platformio run -t upload` " - "- upload firmware to embedded board\n" - "`platformio run --target clean` - clean project (remove compiled " - "files)", - fg="green" - ) - else: + if (app.get_setting("enable_prompts") and + not click.confirm("Do you want to continue?")): raise exception.AbortedByUser() + project_file = join(project_dir, "platformio.ini") + src_dir = join(project_dir, "src") + lib_dir = join(project_dir, "lib") + + for d in (src_dir, lib_dir): + if not isdir(d): + makedirs(d) + + if not isfile(project_file): + copyfile(join(get_source_dir(), "projectconftpl.ini"), + project_file) + + if board: + fill_project_envs(project_file, board, disable_auto_uploading) + + click.secho( + "\nProject has been successfully initialized!\nUseful commands:\n" + "`platformio run` - process/build project from the current " + "directory\n" + "`platformio run --target upload` or `platformio run -t upload` " + "- upload firmware to embedded board\n" + "`platformio run --target clean` - clean project (remove compiled " + "files)", + fg="green" + ) + def fill_project_envs(project_file, board_types, disable_auto_uploading): builtin_boards = get_boards() content = [] - for type_ in board_types: - if type_ not in builtin_boards: - continue - else: - content.append("") + used_envs = [] + with open(project_file) as f: + used_envs = [l.strip() for l in f.read().splitlines() if + l.strip().startswith("[env:")] + + for type_ in board_types: data = builtin_boards[type_] + env_name = "[env:autogen_%s]" % type_ + + if env_name in used_envs: + continue + + content.append("") + content.append(env_name) + content.append("platform = %s" % data['platform']) + # find default framework for board frameworks = data.get("frameworks") - content.append("[env:autogen_%s]" % type_) - content.append("platform = %s" % data['platform']) if frameworks: content.append("framework = %s" % frameworks[0]) - content.append("board = %s" % type_) + content.append("board = %s" % type_) content.append("%stargets = upload" % ("# " if disable_auto_uploading else "")) diff --git a/platformio/exception.py b/platformio/exception.py index 451e7761..daaa176e 100644 --- a/platformio/exception.py +++ b/platformio/exception.py @@ -96,12 +96,6 @@ class UnsupportedArchiveType(PlatformioException): MESSAGE = "Can not unpack file '%s'" -class ProjectInitialized(PlatformioException): - - MESSAGE = ("Project is already initialized. " - "Process it with `platformio run` command") - - class ProjectEnvsNotAvailable(PlatformioException): MESSAGE = "Please setup environments in `platformio.ini` file." From 476de84dc5729b263e82543de742e2a1219f6d71 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 23 Apr 2015 12:54:59 +0100 Subject: [PATCH 13/78] Allow to specify environment prefix when initialise project // Resolve #182 --- HISTORY.rst | 2 ++ docs/userguide/cmd_init.rst | 8 ++++++++ platformio/commands/init.py | 15 +++++++++++---- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 33713b31..b765373a 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -16,6 +16,8 @@ Release History (`issue #167 `_) * Allowed to choose which library to update (`issue #168 `_) +* Allowed to specify `platformio init --env-prefix `__ when initialise/update project + (`issue #182 `_) * Disabled automatic updates by default for platforms, packages and libraries (`issue #171 `_) * Fixed bug with creating copies of source files diff --git a/docs/userguide/cmd_init.rst b/docs/userguide/cmd_init.rst index 4890adf1..637cdafd 100644 --- a/docs/userguide/cmd_init.rst +++ b/docs/userguide/cmd_init.rst @@ -54,6 +54,14 @@ If you initialise project with the specified ``--board``, then *PlatformIO* will create environment with enabled firmware auto-uploading. This option allows you to disable firmware auto-uploading by default. +.. option:: + --env-prefix + +An environment prefix which will be used with pair in board type. The default +value is ``autogen_``. For example, the default environment name for +``teensy_31`` board will be ``[env:autogen_teensy_31]``. + + Examples -------- diff --git a/platformio/commands/init.py b/platformio/commands/init.py index cd301978..a77d753a 100644 --- a/platformio/commands/init.py +++ b/platformio/commands/init.py @@ -29,7 +29,8 @@ def validate_boards(ctx, param, value): # pylint: disable=W0613 @click.option("--board", "-b", multiple=True, metavar="TYPE", callback=validate_boards) @click.option("--disable-auto-uploading", is_flag=True) -def cli(project_dir, board, disable_auto_uploading): +@click.option("--env-prefix", default="autogen_") +def cli(project_dir, board, disable_auto_uploading, env_prefix): # ask about auto-uploading if board and app.get_setting("enable_prompts"): @@ -78,7 +79,8 @@ def cli(project_dir, board, disable_auto_uploading): project_file) if board: - fill_project_envs(project_file, board, disable_auto_uploading) + fill_project_envs( + project_file, board, disable_auto_uploading, env_prefix) click.secho( "\nProject has been successfully initialized!\nUseful commands:\n" @@ -92,7 +94,8 @@ def cli(project_dir, board, disable_auto_uploading): ) -def fill_project_envs(project_file, board_types, disable_auto_uploading): +def fill_project_envs(project_file, board_types, disable_auto_uploading, + env_prefix): builtin_boards = get_boards() content = [] used_envs = [] @@ -103,7 +106,7 @@ def fill_project_envs(project_file, board_types, disable_auto_uploading): for type_ in board_types: data = builtin_boards[type_] - env_name = "[env:autogen_%s]" % type_ + env_name = "[env:%s%s]" % (env_prefix, type_) if env_name in used_envs: continue @@ -121,5 +124,9 @@ def fill_project_envs(project_file, board_types, disable_auto_uploading): content.append("%stargets = upload" % ("# " if disable_auto_uploading else "")) + if not content: + return + with open(project_file, "a") as f: + content.append("") f.write("\n".join(content)) From 31a2880c3c123069610abf61a9417d9203c18d82 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 23 Apr 2015 13:09:41 +0100 Subject: [PATCH 14/78] Improve carols with Bountysource --- platformio/maintenance.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/platformio/maintenance.py b/platformio/maintenance.py index c7976452..a44f454f 100644 --- a/platformio/maintenance.py +++ b/platformio/maintenance.py @@ -109,8 +109,12 @@ def after_upgrade(ctx): if last_version == __version__: return + terminal_width, _ = click.get_terminal_size() + # promotion - click.echo("\nIf you like %s, please:" % ( + click.echo("") + click.echo("*" * terminal_width) + click.echo("If you like %s, please:" % ( click.style("PlatformIO", fg="cyan") )) click.echo( @@ -123,7 +127,13 @@ def after_upgrade(ctx): click.style("give", fg="cyan"), click.style("https://github.com/platformio/platformio", fg="cyan") )) - click.secho("Thanks a lot!\n", fg="green") + click.echo("- %s for the new features/issues on Bountysource > %s" % ( + click.style("vote", fg="cyan"), + click.style("https://www.bountysource.com/teams/platformio/issues", + fg="cyan") + )) + click.echo("*" * terminal_width) + click.echo("") if last_version == "0.0.0": app.set_state_item("last_version", __version__) @@ -162,13 +172,19 @@ def check_platformio_upgrade(): Upgrader.version_to_int(__version__)): return + terminal_width, _ = click.get_terminal_size() + + click.echo("") + click.echo("*" * terminal_width) click.secho("There is a new version %s of PlatformIO available.\n" "Please upgrade it via " % latest_version, fg="yellow", nl=False) click.secho("platformio upgrade", fg="cyan", nl=False) click.secho(" command.\nChanges: ", fg="yellow", nl=False) - click.secho("http://docs.platformio.org/en/latest/history.html\n", + click.secho("http://docs.platformio.org/en/latest/history.html", fg="cyan") + click.echo("*" * terminal_width) + click.echo("") def check_internal_updates(ctx, what): From 97044bf5a54d7573b0a2b5fb46219de81cc806ed Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 23 Apr 2015 14:11:30 +0100 Subject: [PATCH 15/78] Improve cache logic for lookup operations --- platformio/pkgmanager.py | 14 ++--- platformio/platforms/base.py | 49 +++++++-------- platformio/util.py | 114 +++++++++++++++++++++++------------ 3 files changed, 106 insertions(+), 71 deletions(-) diff --git a/platformio/pkgmanager.py b/platformio/pkgmanager.py index bd0e816f..b60332af 100644 --- a/platformio/pkgmanager.py +++ b/platformio/pkgmanager.py @@ -8,28 +8,24 @@ from time import time import click -from platformio import exception, telemetry +from platformio import exception, telemetry, util from platformio.app import get_state_item, set_state_item from platformio.downloader import FileDownloader from platformio.unpacker import FileUnpacker -from platformio.util import get_api_result, get_home_dir, get_systype class PackageManager(object): def __init__(self): - self._package_dir = join(get_home_dir(), "packages") + self._package_dir = join(util.get_home_dir(), "packages") if not isdir(self._package_dir): makedirs(self._package_dir) assert isdir(self._package_dir) @classmethod + @util.memoized def get_manifest(cls): - try: - return cls._cached_manifest - except AttributeError: - cls._cached_manifest = get_api_result("/packages/manifest") - return cls._cached_manifest + return util.get_api_result("/packages/manifest") @staticmethod def download(url, dest_dir, sha1=None): @@ -63,7 +59,7 @@ class PackageManager(object): raise exception.UnknownPackage(name) # check system platform - systype = get_systype() + systype = util.get_systype() builds = ([b for b in manifest[name] if b['system'] == "all" or systype in b['system']]) if not builds: diff --git a/platformio/platforms/base.py b/platformio/platforms/base.py index 9be68191..d91118ec 100644 --- a/platformio/platforms/base.py +++ b/platformio/platforms/base.py @@ -132,32 +132,33 @@ class PlatformFactory(object): return module @classmethod - def get_platforms(cls, installed=False): + @util.memoized + def _lookup_platforms(cls): platforms = {} - - try: - platforms = cls.get_platforms_cache - except AttributeError: - for d in (util.get_home_dir(), util.get_source_dir()): - pdir = join(d, "platforms") - if not isdir(pdir): + for d in (util.get_home_dir(), util.get_source_dir()): + pdir = join(d, "platforms") + if not isdir(pdir): + continue + for p in listdir(pdir): + if (p in ("__init__.py", "base.py") or not + p.endswith(".py")): continue - for p in listdir(pdir): - if (p in ("__init__.py", "base.py") or not - p.endswith(".py")): - continue - type_ = p[:-3] - path = join(pdir, p) - try: - isplatform = hasattr( - cls.load_module(type_, path), - cls.get_clsname(type_) - ) - if isplatform: - platforms[type_] = path - except exception.UnknownPlatform: - pass - cls.get_platforms_cache = platforms + type_ = p[:-3] + path = join(pdir, p) + try: + isplatform = hasattr( + cls.load_module(type_, path), + cls.get_clsname(type_) + ) + if isplatform: + platforms[type_] = path + except exception.UnknownPlatform: + pass + return platforms + + @classmethod + def get_platforms(cls, installed=False): + platforms = cls._lookup_platforms() if not installed: return platforms diff --git a/platformio/util.py b/platformio/util.py index 82582573..a4faf4ca 100644 --- a/platformio/util.py +++ b/platformio/util.py @@ -1,6 +1,8 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. +import collections +import functools import json import os import re @@ -53,6 +55,39 @@ class AsyncPipe(Thread): self.join() +class memoized(object): + ''' + Decorator. Caches a function's return value each time it is called. + If called later with the same arguments, the cached value is returned + (not reevaluated). + https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize + ''' + + def __init__(self, func): + self.func = func + self.cache = {} + + def __call__(self, *args): + if not isinstance(args, collections.Hashable): + # uncacheable. a list, for instance. + # better to not cache than blow up. + return self.func(*args) + if args in self.cache: + return self.cache[args] + else: + value = self.func(*args) + self.cache[args] = value + return value + + def __repr__(self): + '''Return the function's docstring.''' + return self.func.__doc__ + + def __get__(self, obj, objtype): + '''Support instance methods.''' + return functools.partial(self.__call__, obj) + + def get_systype(): data = uname() return ("%s_%s" % (data[0], data[4])).lower() @@ -239,22 +274,24 @@ def get_api_result(path, params=None, data=None): return result -def get_boards(type_=None): +@memoized +def _lookup_boards(): boards = {} - try: - boards = get_boards._cache # pylint: disable=W0212 - except AttributeError: - bdirs = [join(get_source_dir(), "boards")] - if isdir(join(get_home_dir(), "boards")): - bdirs.append(join(get_home_dir(), "boards")) + bdirs = [join(get_source_dir(), "boards")] + if isdir(join(get_home_dir(), "boards")): + bdirs.append(join(get_home_dir(), "boards")) - for bdir in bdirs: - for json_file in os.listdir(bdir): - if not json_file.endswith(".json"): - continue - with open(join(bdir, json_file)) as f: - boards.update(json.load(f)) - get_boards._cache = boards # pylint: disable=W0212 + for bdir in bdirs: + for json_file in os.listdir(bdir): + if not json_file.endswith(".json"): + continue + with open(join(bdir, json_file)) as f: + boards.update(json.load(f)) + return boards + + +def get_boards(type_=None): + boards = _lookup_boards() if type_ is None: return boards @@ -264,33 +301,34 @@ def get_boards(type_=None): return boards[type_] -def get_frameworks(type_=None): +@memoized +def _lookup_frameworks(): frameworks = {} + frameworks_path = join( + get_source_dir(), "builder", "scripts", "frameworks") - try: - frameworks = get_frameworks._cache # pylint: disable=W0212 - except AttributeError: - frameworks_path = join( - get_source_dir(), "builder", "scripts", "frameworks") + frameworks_list = [f[:-3] for f in os.listdir(frameworks_path) + if not f.startswith("__") and f.endswith(".py")] + for _type in frameworks_list: + script_path = join(frameworks_path, "%s.py" % _type) + with open(script_path) as f: + fcontent = f.read() + assert '"""' in fcontent + _doc_start = fcontent.index('"""') + 3 + fdoc = fcontent[ + _doc_start:fcontent.index('"""', _doc_start)].strip() + doclines = [l.strip() for l in fdoc.splitlines() if l.strip()] + frameworks[_type] = { + "name": doclines[0], + "description": " ".join(doclines[1:-1]), + "url": doclines[-1], + "script": script_path + } + return frameworks - frameworks_list = [f[:-3] for f in os.listdir(frameworks_path) - if not f.startswith("__") and f.endswith(".py")] - for _type in frameworks_list: - script_path = join(frameworks_path, "%s.py" % _type) - with open(script_path) as f: - fcontent = f.read() - assert '"""' in fcontent - _doc_start = fcontent.index('"""') + 3 - fdoc = fcontent[ - _doc_start:fcontent.index('"""', _doc_start)].strip() - doclines = [l.strip() for l in fdoc.splitlines() if l.strip()] - frameworks[_type] = { - "name": doclines[0], - "description": " ".join(doclines[1:-1]), - "url": doclines[-1], - "script": script_path - } - get_frameworks._cache = frameworks # pylint: disable=W0212 + +def get_frameworks(type_=None): + frameworks = _lookup_frameworks() if type_ is None: return frameworks From 6fd07e1e5692c3bcab10cd6844e8336d6cc52c15 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 24 Apr 2015 14:43:13 +0100 Subject: [PATCH 16/78] Initial support of PlatformIO Project Generator // Issue #151 --- .isort.cfg | 2 +- platformio/__init__.py | 2 +- platformio/commands/init.py | 9 ++- platformio/ide/__init__.py | 2 + platformio/ide/projectgenerator.py | 63 +++++++++++++++ platformio/ide/tpls/eclipse/.cproject.tpl | 81 +++++++++++++++++++ platformio/ide/tpls/eclipse/.project.tpl | 27 +++++++ .../ide/tpls/qtcreator/platformio.pro.tpl | 19 +++++ .../platformio.sublime-project.tpl | 45 +++++++++++ .../platformio.vcxproj.filters.tpl | 29 +++++++ .../tpls/visualstudio/platformio.vcxproj.tpl | 62 ++++++++++++++ requirements.txt | 1 + setup.py | 10 ++- 13 files changed, 348 insertions(+), 4 deletions(-) create mode 100644 platformio/ide/__init__.py create mode 100644 platformio/ide/projectgenerator.py create mode 100644 platformio/ide/tpls/eclipse/.cproject.tpl create mode 100644 platformio/ide/tpls/eclipse/.project.tpl create mode 100644 platformio/ide/tpls/qtcreator/platformio.pro.tpl create mode 100644 platformio/ide/tpls/sublimetext/platformio.sublime-project.tpl create mode 100644 platformio/ide/tpls/visualstudio/platformio.vcxproj.filters.tpl create mode 100644 platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl diff --git a/.isort.cfg b/.isort.cfg index 72280366..3dbd6d91 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -1,3 +1,3 @@ [settings] line_length=79 -known_third_party=click,requests,serial,SCons,pytest +known_third_party=click,requests,serial,SCons,pytest,bottle diff --git a/platformio/__init__.py b/platformio/__init__.py index ffc095fc..9e004963 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -VERSION = (2, 0, "0.dev2") +VERSION = (2, 0, "0.dev3") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" diff --git a/platformio/commands/init.py b/platformio/commands/init.py index a77d753a..229a1c23 100644 --- a/platformio/commands/init.py +++ b/platformio/commands/init.py @@ -8,6 +8,7 @@ from shutil import copyfile import click from platformio import app, exception +from platformio.ide.projectgenerator import ProjectGenerator from platformio.util import get_boards, get_source_dir @@ -28,9 +29,11 @@ def validate_boards(ctx, param, value): # pylint: disable=W0613 writable=True, resolve_path=True)) @click.option("--board", "-b", multiple=True, metavar="TYPE", callback=validate_boards) +@click.option("--ide", + type=click.Choice(ProjectGenerator.get_supported_ides())) @click.option("--disable-auto-uploading", is_flag=True) @click.option("--env-prefix", default="autogen_") -def cli(project_dir, board, disable_auto_uploading, env_prefix): +def cli(project_dir, board, ide, disable_auto_uploading, env_prefix): # ask about auto-uploading if board and app.get_setting("enable_prompts"): @@ -82,6 +85,10 @@ def cli(project_dir, board, disable_auto_uploading, env_prefix): fill_project_envs( project_file, board, disable_auto_uploading, env_prefix) + if ide: + pg = ProjectGenerator(project_dir, ide) + pg.generate() + click.secho( "\nProject has been successfully initialized!\nUseful commands:\n" "`platformio run` - process/build project from the current " diff --git a/platformio/ide/__init__.py b/platformio/ide/__init__.py new file mode 100644 index 00000000..ca6f0304 --- /dev/null +++ b/platformio/ide/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. diff --git a/platformio/ide/projectgenerator.py b/platformio/ide/projectgenerator.py new file mode 100644 index 00000000..3ce36a6b --- /dev/null +++ b/platformio/ide/projectgenerator.py @@ -0,0 +1,63 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from glob import glob +from os import listdir +from os.path import basename, isdir, join + +import bottle + +from platformio import util + + +class ProjectGenerator(object): + + def __init__(self, project_dir, ide): + self.project_dir = project_dir + self.ide = ide + self._tplvars = self._gather_tplvars() + + @staticmethod + def get_supported_ides(): + tpls_dir = join(util.get_source_dir(), "ide", "tpls") + return [d for d in listdir(tpls_dir) + if isdir(join(tpls_dir, d))] + + @staticmethod + def get_project_env(): + data = {} + config = util.get_project_config() + for section in config.sections(): + if not section.startswith("env:"): + continue + for k, v in config.items(section): + data[k] = v + return data + + def get_project_name(self): + return basename(self.project_dir) + + def get_tpls(self): + tpls_dir = join(util.get_source_dir(), "ide", "tpls", self.ide) + return glob(join(tpls_dir, ".*.tpl")) + glob(join(tpls_dir, "*.tpl")) + + def generate(self): + for tpl_path in self.get_tpls(): + file_name = basename(tpl_path)[:-4] + with open(join(self.project_dir, file_name), "w") as f: + f.write(self._render_tpl(tpl_path)) + + def _render_tpl(self, tpl_path): + content = "" + with open(tpl_path) as f: + content = f.read() + return bottle.template(content, **self._tplvars) + + def _gather_tplvars(self): + data = self.get_project_env() + + data.update({ + "project_name": self.get_project_name() + }) + + return data diff --git a/platformio/ide/tpls/eclipse/.cproject.tpl b/platformio/ide/tpls/eclipse/.cproject.tpl new file mode 100644 index 00000000..7ed43a49 --- /dev/null +++ b/platformio/ide/tpls/eclipse/.cproject.tpl @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platformio/ide/tpls/eclipse/.project.tpl b/platformio/ide/tpls/eclipse/.project.tpl new file mode 100644 index 00000000..ac49f548 --- /dev/null +++ b/platformio/ide/tpls/eclipse/.project.tpl @@ -0,0 +1,27 @@ + + + {{project_name}} + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/platformio/ide/tpls/qtcreator/platformio.pro.tpl b/platformio/ide/tpls/qtcreator/platformio.pro.tpl new file mode 100644 index 00000000..22188cd8 --- /dev/null +++ b/platformio/ide/tpls/qtcreator/platformio.pro.tpl @@ -0,0 +1,19 @@ +win32 { + HOMEDIR += $$(USERPROFILE) +} +else { + HOMEDIR += $$(PWD) +} + +INCLUDEPATH += "$$HOMEDIR/.platformio/packages/framework-arduinoavr/cores/arduino" +INCLUDEPATH += "$$HOMEDIR/.platformio/packages/toolchain-atmelavr/avr/include" + +win32:INCLUDEPATH ~= s,/,\\,g + +# DEFINES += __AVR_ATmega328__ + +OTHER_FILES += \ + platformio.ini + +SOURCES += \ + src/main.c diff --git a/platformio/ide/tpls/sublimetext/platformio.sublime-project.tpl b/platformio/ide/tpls/sublimetext/platformio.sublime-project.tpl new file mode 100644 index 00000000..8054bd59 --- /dev/null +++ b/platformio/ide/tpls/sublimetext/platformio.sublime-project.tpl @@ -0,0 +1,45 @@ +{ + "build_systems": + [ + { + "cmd": + [ + "platformio", + "run" + ], + "name": "{{project_name}}", + "variants": + [ + { + "cmd": + [ + "platformio", + "--force", + "run", + "--target", + "clean" + ], + "name": "Clean" + }, + { + "cmd": + [ + "platformio", + "--force", + "run", + "--target", + "upload" + ], + "name": "Upload" + } + ], + "working_dir": "${project_path:${folder}}" + } + ], + "folders": + [ + { + "path": "." + } + ] +} diff --git a/platformio/ide/tpls/visualstudio/platformio.vcxproj.filters.tpl b/platformio/ide/tpls/visualstudio/platformio.vcxproj.filters.tpl new file mode 100644 index 00000000..87bc92f6 --- /dev/null +++ b/platformio/ide/tpls/visualstudio/platformio.vcxproj.filters.tpl @@ -0,0 +1,29 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {cad450ef-1a84-42d4-a5b5-a1736b8833d3} + + + + + + + + + Source Files\src + + + \ No newline at end of file diff --git a/platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl b/platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl new file mode 100644 index 00000000..4da8da32 --- /dev/null +++ b/platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl @@ -0,0 +1,62 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {0FA9C3A8-452B-41EF-A418-9102B170F49F} + MakeFileProj + + + + Makefile + true + v120 + + + Makefile + false + v120 + + + + + + + + + + + + + platformio --force run + platformio --force run --target clean + WIN32;_DEBUG;$(NMakePreprocessorDefinitions) + $(HOMEDRIVE)$(HOMEPATH)\.platformio\packages\toolchain-atmelavr\avr\include;$(HOMEDRIVE)$(HOMEPATH)\.platformio\packages\framework-arduinoavr\cores\arduino;$(NMakeIncludeSearchPath) + + + platformio run + platformio run -t clean + WIN32;NDEBUG;$(NMakePreprocessorDefinitions) + $(HOMEDRIVE)$(HOMEPATH)\.platformio\packages\toolchain-atmelavr\avr\include;$(HOMEDRIVE)$(HOMEPATH)\.platformio\packages\framework-arduinoavr\cores\arduino;$(NMakeIncludeSearchPath) + + + + + + + + + + + + + + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 6a5ccae6..a540932f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +bottle=0.12.8 click==3.3 colorama==0.3.3 pyserial==2.7 diff --git a/setup.py b/setup.py index 9a01f68c..41b5a586 100644 --- a/setup.py +++ b/setup.py @@ -18,13 +18,21 @@ setup( url=__url__, license=__license__, install_requires=[ + "bottle", "click>=3.0", "pyserial", "requests>=2.4.0", # "SCons" ] + (["colorama"] if system() == "Windows" else []), packages=find_packages(), - package_data={"platformio": ["projectconftpl.ini", "boards/*.json"]}, + package_data={ + "platformio": [ + "projectconftpl.ini", + "boards/*.json", + "ide/tpls/*/.*.tpl", + "ide/tpls/*/*.tpl" + ] + }, entry_points={ "console_scripts": [ "platformio = platformio.__main__:main" From c79114c345c01a13fb65dfaf13007c2c15746c9d Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 24 Apr 2015 14:53:07 +0100 Subject: [PATCH 17/78] Fix build system to "PlatformIO" for ST --- platformio/ide/tpls/sublimetext/platformio.sublime-project.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio/ide/tpls/sublimetext/platformio.sublime-project.tpl b/platformio/ide/tpls/sublimetext/platformio.sublime-project.tpl index 8054bd59..4c4855a2 100644 --- a/platformio/ide/tpls/sublimetext/platformio.sublime-project.tpl +++ b/platformio/ide/tpls/sublimetext/platformio.sublime-project.tpl @@ -7,7 +7,7 @@ "platformio", "run" ], - "name": "{{project_name}}", + "name": "PlatformIO", "variants": [ { From d415cbbe45b8547b03f150b28706b208fd7dc71d Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 24 Apr 2015 15:33:29 +0100 Subject: [PATCH 18/78] Require minimum 1 argument for the "install" command --- platformio/commands/platforms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio/commands/platforms.py b/platformio/commands/platforms.py index 8d8486c6..54a4721d 100644 --- a/platformio/commands/platforms.py +++ b/platformio/commands/platforms.py @@ -18,7 +18,7 @@ def cli(): @cli.command("install", short_help="Install new platforms") -@click.argument("platforms", nargs=-1) +@click.argument("platforms", nargs=-1, required=True) @click.option("--with-package", multiple=True, metavar="") @click.option("--without-package", multiple=True, metavar="") @click.option("--skip-default-package", is_flag=True) From 0bf7e68ea55a9f2aadf033e091cfe3034527c4ac Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 24 Apr 2015 15:37:08 +0100 Subject: [PATCH 19/78] Require minimum 1 argument for the "uninstall" command --- platformio/commands/platforms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio/commands/platforms.py b/platformio/commands/platforms.py index 54a4721d..bdfae393 100644 --- a/platformio/commands/platforms.py +++ b/platformio/commands/platforms.py @@ -131,7 +131,7 @@ def platforms_show(ctx, platform): @cli.command("uninstall", short_help="Uninstall platforms") -@click.argument("platforms", nargs=-1) +@click.argument("platforms", nargs=-1, required=True) def platforms_uninstall(platforms): for platform in platforms: From 36ad6efa258ef15c6f523f5695e113ad301e21a1 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 24 Apr 2015 15:48:32 +0100 Subject: [PATCH 20/78] Handle obsoleted commands // Issue #158 --- platformio/__main__.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/platformio/__main__.py b/platformio/__main__.py index a3de3544..892b36c0 100644 --- a/platformio/__main__.py +++ b/platformio/__main__.py @@ -31,9 +31,23 @@ class PlatformioCLI(click.MultiCommand): # pylint: disable=R0904 mod = __import__("platformio.commands." + name, None, None, ["cli"]) except ImportError: - raise exception.UnknownCLICommand(name) + try: + return self._handle_obsolate_command(name) + except AttributeError: + raise exception.UnknownCLICommand(name) return mod.cli + def _handle_obsolate_command(self, name): + if name in ("install", "list", "search", "show", "uninstall"): + click.secho( + "Warning! `platformio %s` command is obsoleted! Please use " + "`platformio platforms %s`" % (name, name), + fg="red" + ) + from platformio.commands import platforms + return getattr(platforms, "platforms_" + name) + raise AttributeError() + @click.command(cls=PlatformioCLI) @click.version_option(__version__, prog_name="PlatformIO") From 97d229232d1ca902c4bab55973999607e628f6ec Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 24 Apr 2015 15:51:08 +0100 Subject: [PATCH 21/78] Correct warning about obsoleted commands --- platformio/__main__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platformio/__main__.py b/platformio/__main__.py index 892b36c0..d0e910c8 100644 --- a/platformio/__main__.py +++ b/platformio/__main__.py @@ -40,8 +40,9 @@ class PlatformioCLI(click.MultiCommand): # pylint: disable=R0904 def _handle_obsolate_command(self, name): if name in ("install", "list", "search", "show", "uninstall"): click.secho( - "Warning! `platformio %s` command is obsoleted! Please use " - "`platformio platforms %s`" % (name, name), + "Warning! `platformio %s` command is obsoleted and will be " + "removed in the next release! Please use " + "`platformio platforms %s` instead." % (name, name), fg="red" ) from platformio.commands import platforms From 8b8d5c360ca8f8a2b41c8827c3b9d95f71fad472 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 24 Apr 2015 16:34:14 +0100 Subject: [PATCH 22/78] Add prototypes for includes/srcfiles --- platformio/ide/projectgenerator.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/platformio/ide/projectgenerator.py b/platformio/ide/projectgenerator.py index 3ce36a6b..7873d801 100644 --- a/platformio/ide/projectgenerator.py +++ b/platformio/ide/projectgenerator.py @@ -37,6 +37,12 @@ class ProjectGenerator(object): def get_project_name(self): return basename(self.project_dir) + def get_includes(self): + return [] + + def get_srcfiles(self): + return [] + def get_tpls(self): tpls_dir = join(util.get_source_dir(), "ide", "tpls", self.ide) return glob(join(tpls_dir, ".*.tpl")) + glob(join(tpls_dir, "*.tpl")) @@ -45,6 +51,7 @@ class ProjectGenerator(object): for tpl_path in self.get_tpls(): file_name = basename(tpl_path)[:-4] with open(join(self.project_dir, file_name), "w") as f: + print tpl_path f.write(self._render_tpl(tpl_path)) def _render_tpl(self, tpl_path): @@ -57,7 +64,9 @@ class ProjectGenerator(object): data = self.get_project_env() data.update({ - "project_name": self.get_project_name() + "project_name": self.get_project_name(), + "includes": self.get_includes(), + "srcfiles": self.get_srcfiles() }) return data From c14b436aec4ad19ee9f71c4918fcac712168b84d Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 24 Apr 2015 17:17:24 +0100 Subject: [PATCH 23/78] Fix encoding for Project Generator --- platformio/ide/projectgenerator.py | 3 +-- .../ide/tpls/visualstudio/platformio.vcxproj.filters.tpl | 2 +- platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/platformio/ide/projectgenerator.py b/platformio/ide/projectgenerator.py index 7873d801..f086eabe 100644 --- a/platformio/ide/projectgenerator.py +++ b/platformio/ide/projectgenerator.py @@ -51,8 +51,7 @@ class ProjectGenerator(object): for tpl_path in self.get_tpls(): file_name = basename(tpl_path)[:-4] with open(join(self.project_dir, file_name), "w") as f: - print tpl_path - f.write(self._render_tpl(tpl_path)) + f.write(self._render_tpl(tpl_path).encode("utf8")) def _render_tpl(self, tpl_path): content = "" diff --git a/platformio/ide/tpls/visualstudio/platformio.vcxproj.filters.tpl b/platformio/ide/tpls/visualstudio/platformio.vcxproj.filters.tpl index 87bc92f6..1b4a764e 100644 --- a/platformio/ide/tpls/visualstudio/platformio.vcxproj.filters.tpl +++ b/platformio/ide/tpls/visualstudio/platformio.vcxproj.filters.tpl @@ -26,4 +26,4 @@ Source Files\src - \ No newline at end of file + diff --git a/platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl b/platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl index 4da8da32..54d5be85 100644 --- a/platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl +++ b/platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl @@ -59,4 +59,4 @@ - \ No newline at end of file + From 1033e827c07b55379f2e800058f55f1b94127438 Mon Sep 17 00:00:00 2001 From: Valeriy Koval Date: Mon, 27 Apr 2015 18:27:26 +0300 Subject: [PATCH 24/78] Add example with creating especial platform in documentation --- docs/librarymanager/creating.rst | 115 +++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/docs/librarymanager/creating.rst b/docs/librarymanager/creating.rst index 1db24f56..c54b564e 100644 --- a/docs/librarymanager/creating.rst +++ b/docs/librarymanager/creating.rst @@ -134,3 +134,118 @@ The registration requirements: Now, you can :ref:`register ` your library and allow others to :ref:`install ` it. + +Examples +-------- + +Create :ref:`platform_ststm32` based platform which uses GDB for uploading + +First of all, we need to create new folder ``platforms`` :ref:`projectconf_pio_home_dir` and copy there two files: + +1. Platform manifest file ``ststm32gdb.py`` with the next content: + +.. code-block:: python + + import os + + from platformio.platforms.ststm32 import Ststm32Platform + + + class Ststm32gdbPlatform(Ststm32Platform): + + """ + ST STM32 using GDB as uploader + + http://www.st.com/web/en/catalog/mmc/FM141/SC1169?sc=stm32 + """ + + def get_build_script(self): + + return os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "ststm32gdb-builder.py" + ) + +2. Build script file ``ststm32gdb-builder.py`` with the next content: + +.. code-block:: python + + """ + Builder for ST STM32 Series ARM microcontrollers with GDB upload. + """ + + from os.path import join + + from SCons.Script import (COMMAND_LINE_TARGETS, AlwaysBuild, Default, + DefaultEnvironment, SConscript) + + + env = DefaultEnvironment() + + SConscript(env.subst(join("$PIOBUILDER_DIR", "scripts", "basearm.py"))) + + env.Replace( + UPLOADER=join( + "$PIOPACKAGES_DIR", "toolchain-gccarmnoneeabi", + "bin", "arm-none-eabi-gdb" + ), + UPLOADERFLAGS=[ + join("$BUILD_DIR", "firmware.elf"), + "-batch", + "-x", join("$PROJECT_DIR", "upload.gdb") + ], + + UPLOADCMD="$UPLOADER $UPLOADERFLAGS" + ) + + env.Append( + CPPDEFINES=[ + "${BOARD_OPTIONS['build']['variant'].upper()}" + ], + + LINKFLAGS=[ + "-nostartfiles", + "-nostdlib" + ] + ) + + # + # Target: Build executable and linkable firmware + # + + target_elf = env.BuildFirmware() + + # + # Target: Build the .bin file + # + + if "uploadlazy" in COMMAND_LINE_TARGETS: + target_firm = join("$BUILD_DIR", "firmware.bin") + else: + target_firm = env.ElfToBin(join("$BUILD_DIR", "firmware"), target_elf) + + # + # Target: Print binary size + # + + target_size = env.Alias("size", target_elf, "$SIZEPRINTCMD") + AlwaysBuild(target_size) + + # + # Target: Upload by default .bin file + # + + upload = env.Alias( + ["upload", "uploadlazy"], target_firm, "$UPLOADCMD") + AlwaysBuild(upload) + + # + # Target: Define targets + # + + Default([target_firm, target_size]) + +You should see ststm32gdb platform in ``platformio search`` command output. +Now, you can install new platform via :ref:`platformio install ststm32gdb ` command. + +For more detailed information how to use this platform please follow to `issue 175 `_ From 49bd13adc4ab51636744e613179328313acfcf61 Mon Sep 17 00:00:00 2001 From: Valeriy Koval Date: Mon, 27 Apr 2015 20:25:47 +0300 Subject: [PATCH 25/78] Move documentation example with creating especial platform to right place. --- docs/librarymanager/creating.rst | 114 -------------------------- docs/platforms/creating_platform.rst | 115 +++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 114 deletions(-) diff --git a/docs/librarymanager/creating.rst b/docs/librarymanager/creating.rst index c54b564e..88011f12 100644 --- a/docs/librarymanager/creating.rst +++ b/docs/librarymanager/creating.rst @@ -135,117 +135,3 @@ The registration requirements: Now, you can :ref:`register ` your library and allow others to :ref:`install ` it. -Examples --------- - -Create :ref:`platform_ststm32` based platform which uses GDB for uploading - -First of all, we need to create new folder ``platforms`` :ref:`projectconf_pio_home_dir` and copy there two files: - -1. Platform manifest file ``ststm32gdb.py`` with the next content: - -.. code-block:: python - - import os - - from platformio.platforms.ststm32 import Ststm32Platform - - - class Ststm32gdbPlatform(Ststm32Platform): - - """ - ST STM32 using GDB as uploader - - http://www.st.com/web/en/catalog/mmc/FM141/SC1169?sc=stm32 - """ - - def get_build_script(self): - - return os.path.join( - os.path.dirname(os.path.realpath(__file__)), - "ststm32gdb-builder.py" - ) - -2. Build script file ``ststm32gdb-builder.py`` with the next content: - -.. code-block:: python - - """ - Builder for ST STM32 Series ARM microcontrollers with GDB upload. - """ - - from os.path import join - - from SCons.Script import (COMMAND_LINE_TARGETS, AlwaysBuild, Default, - DefaultEnvironment, SConscript) - - - env = DefaultEnvironment() - - SConscript(env.subst(join("$PIOBUILDER_DIR", "scripts", "basearm.py"))) - - env.Replace( - UPLOADER=join( - "$PIOPACKAGES_DIR", "toolchain-gccarmnoneeabi", - "bin", "arm-none-eabi-gdb" - ), - UPLOADERFLAGS=[ - join("$BUILD_DIR", "firmware.elf"), - "-batch", - "-x", join("$PROJECT_DIR", "upload.gdb") - ], - - UPLOADCMD="$UPLOADER $UPLOADERFLAGS" - ) - - env.Append( - CPPDEFINES=[ - "${BOARD_OPTIONS['build']['variant'].upper()}" - ], - - LINKFLAGS=[ - "-nostartfiles", - "-nostdlib" - ] - ) - - # - # Target: Build executable and linkable firmware - # - - target_elf = env.BuildFirmware() - - # - # Target: Build the .bin file - # - - if "uploadlazy" in COMMAND_LINE_TARGETS: - target_firm = join("$BUILD_DIR", "firmware.bin") - else: - target_firm = env.ElfToBin(join("$BUILD_DIR", "firmware"), target_elf) - - # - # Target: Print binary size - # - - target_size = env.Alias("size", target_elf, "$SIZEPRINTCMD") - AlwaysBuild(target_size) - - # - # Target: Upload by default .bin file - # - - upload = env.Alias( - ["upload", "uploadlazy"], target_firm, "$UPLOADCMD") - AlwaysBuild(upload) - - # - # Target: Define targets - # - - Default([target_firm, target_size]) - -You should see ststm32gdb platform in ``platformio search`` command output. -Now, you can install new platform via :ref:`platformio install ststm32gdb ` command. - -For more detailed information how to use this platform please follow to `issue 175 `_ diff --git a/docs/platforms/creating_platform.rst b/docs/platforms/creating_platform.rst index 811a4038..1bb556cb 100644 --- a/docs/platforms/creating_platform.rst +++ b/docs/platforms/creating_platform.rst @@ -300,3 +300,118 @@ Installation Now, you can use ``test`` for the :ref:`projectconf_env_platform` option in :ref:`projectconf`. + +Examples +-------- + +Create :ref:`platform_ststm32` based platform which uses GDB for uploading + +First of all, we need to create new folder ``platforms`` :ref:`projectconf_pio_home_dir` and copy there two files: + +1. Platform manifest file ``ststm32gdb.py`` with the next content: + +.. code-block:: python + + import os + + from platformio.platforms.ststm32 import Ststm32Platform + + + class Ststm32gdbPlatform(Ststm32Platform): + + """ + ST STM32 using GDB as uploader + + http://www.st.com/web/en/catalog/mmc/FM141/SC1169?sc=stm32 + """ + + def get_build_script(self): + + return os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "ststm32gdb-builder.py" + ) + +2. Build script file ``ststm32gdb-builder.py`` with the next content: + +.. code-block:: python + + """ + Builder for ST STM32 Series ARM microcontrollers with GDB upload. + """ + + from os.path import join + + from SCons.Script import (COMMAND_LINE_TARGETS, AlwaysBuild, Default, + DefaultEnvironment, SConscript) + + + env = DefaultEnvironment() + + SConscript(env.subst(join("$PIOBUILDER_DIR", "scripts", "basearm.py"))) + + env.Replace( + UPLOADER=join( + "$PIOPACKAGES_DIR", "toolchain-gccarmnoneeabi", + "bin", "arm-none-eabi-gdb" + ), + UPLOADERFLAGS=[ + join("$BUILD_DIR", "firmware.elf"), + "-batch", + "-x", join("$PROJECT_DIR", "upload.gdb") + ], + + UPLOADCMD="$UPLOADER $UPLOADERFLAGS" + ) + + env.Append( + CPPDEFINES=[ + "${BOARD_OPTIONS['build']['variant'].upper()}" + ], + + LINKFLAGS=[ + "-nostartfiles", + "-nostdlib" + ] + ) + + # + # Target: Build executable and linkable firmware + # + + target_elf = env.BuildFirmware() + + # + # Target: Build the .bin file + # + + if "uploadlazy" in COMMAND_LINE_TARGETS: + target_firm = join("$BUILD_DIR", "firmware.bin") + else: + target_firm = env.ElfToBin(join("$BUILD_DIR", "firmware"), target_elf) + + # + # Target: Print binary size + # + + target_size = env.Alias("size", target_elf, "$SIZEPRINTCMD") + AlwaysBuild(target_size) + + # + # Target: Upload by default .bin file + # + + upload = env.Alias( + ["upload", "uploadlazy"], target_firm, "$UPLOADCMD") + AlwaysBuild(upload) + + # + # Target: Define targets + # + + Default([target_firm, target_size]) + +You should see ``ststm32gdb`` platform in :ref:`platformio search ` command output. +Now, you can install new platform via :ref:`platformio install ststm32gdb ` command. + +For more detailed information how to use this platform please follow to `issue 175 `_ From bdcf89bf32f953cffb0c958919a357b4319345a0 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 27 Apr 2015 18:48:16 +0100 Subject: [PATCH 26/78] Correct spelling in Creating platform example --- docs/platforms/creating_platform.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/platforms/creating_platform.rst b/docs/platforms/creating_platform.rst index 1bb556cb..1c5c0505 100644 --- a/docs/platforms/creating_platform.rst +++ b/docs/platforms/creating_platform.rst @@ -301,12 +301,14 @@ Installation Now, you can use ``test`` for the :ref:`projectconf_env_platform` option in :ref:`projectconf`. -Examples --------- +Example +------- -Create :ref:`platform_ststm32` based platform which uses GDB for uploading +Let's use the real example which was requested by our user in `issue 175 `_. Need to add support for uploading firmware using GDB to +:ref:`platform_ststm32`. -First of all, we need to create new folder ``platforms`` :ref:`projectconf_pio_home_dir` and copy there two files: +First of all, need to create new folder ``platforms`` in :ref:`projectconf_pio_home_dir` +and copy there two files: 1. Platform manifest file ``ststm32gdb.py`` with the next content: @@ -411,7 +413,5 @@ First of all, we need to create new folder ``platforms`` :ref:`projectconf_pio_h Default([target_firm, target_size]) -You should see ``ststm32gdb`` platform in :ref:`platformio search ` command output. -Now, you can install new platform via :ref:`platformio install ststm32gdb ` command. - -For more detailed information how to use this platform please follow to `issue 175 `_ +Now, we should see ``ststm32gdb`` platform using :ref:`cmd_search` command output +and can install it via :ref:`platformio platforms install ststm32gdb ` command. From 7b80e176644ec20392bae4a18a9495b546e81543 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 28 Apr 2015 13:13:17 +0100 Subject: [PATCH 27/78] Improve handling of platforms commands for telemetry --- platformio/telemetry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio/telemetry.py b/platformio/telemetry.py index 55b26f21..87762dbd 100644 --- a/platformio/telemetry.py +++ b/platformio/telemetry.py @@ -96,7 +96,7 @@ class MeasurementProtocol(TelemetryBase): if not args: return - if args[0] in ("lib", "serialports", "settings"): + if args[0] in ("lib", "platforms", "serialports", "settings"): cmd_path = args[:2] else: cmd_path = args[:1] From cd842d5a86398959cd31f38c3f651ac89bf9b445 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 29 Apr 2015 18:17:14 +0100 Subject: [PATCH 28/78] Put UserAgent for all http requests --- platformio/commands/upgrade.py | 13 +++++++------ platformio/downloader.py | 12 +++++++----- platformio/telemetry.py | 8 ++++---- platformio/util.py | 13 ++++++++----- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/platformio/commands/upgrade.py b/platformio/commands/upgrade.py index 7e57579e..263401de 100644 --- a/platformio/commands/upgrade.py +++ b/platformio/commands/upgrade.py @@ -4,9 +4,8 @@ import click import requests -from platformio import __version__ +from platformio import __version__, util from platformio.exception import GetLatestVersionError -from platformio.util import exec_command @click.command("upgrade", @@ -22,9 +21,9 @@ def cli(): click.secho("Please wait while upgrading PlatformIO ...", fg="yellow") - pip_result = exec_command(["pip", "install", "--upgrade", - "platformio"]) - pio_result = exec_command(["platformio", "--version"]) + pip_result = util.exec_command(["pip", "install", "--upgrade", + "platformio"]) + pio_result = util.exec_command(["platformio", "--version"]) if last in pio_result['out'].strip(): click.secho("PlatformIO has been successfully upgraded to %s" % @@ -37,7 +36,9 @@ def cli(): def get_latest_version(): try: pkgdata = requests.get( - "https://pypi.python.org/pypi/platformio/json").json() + "https://pypi.python.org/pypi/platformio/json", + headers=util.get_request_defheaders() + ).json() return pkgdata['info']['version'] except: raise GetLatestVersionError() diff --git a/platformio/downloader.py b/platformio/downloader.py index df9f5b64..a3aa37f9 100644 --- a/platformio/downloader.py +++ b/platformio/downloader.py @@ -9,9 +9,9 @@ from time import mktime from click import progressbar from requests import get +from platformio import util from platformio.exception import (FDSHASumMismatch, FDSizeMismatch, FDUnrecognizedStatusCode) -from platformio.util import change_filemtime, exec_command class FileDownloader(object): @@ -27,7 +27,8 @@ class FileDownloader(object): self.set_destination(join(dest_dir, self._fname)) self._progressbar = None - self._request = get(url, stream=True) + self._request = get(url, stream=True, + headers=util.get_request_defheaders()) if self._request.status_code != 200: raise FDUnrecognizedStatusCode(self._request.status_code, url) @@ -66,11 +67,12 @@ class FileDownloader(object): dlsha1 = None try: - result = exec_command(["sha1sum", self._destination]) + result = util.exec_command(["sha1sum", self._destination]) dlsha1 = result['out'] except OSError: try: - result = exec_command(["shasum", "-a", "1", self._destination]) + result = util.exec_command( + ["shasum", "-a", "1", self._destination]) dlsha1 = result['out'] except OSError: pass @@ -83,7 +85,7 @@ class FileDownloader(object): def _preserve_filemtime(self, lmdate): timedata = parsedate_tz(lmdate) lmtime = mktime(timedata[:9]) - change_filemtime(self._destination, lmtime) + util.change_filemtime(self._destination, lmtime) def __del__(self): self._request.close() diff --git a/platformio/telemetry.py b/platformio/telemetry.py index 87762dbd..3662b7f4 100644 --- a/platformio/telemetry.py +++ b/platformio/telemetry.py @@ -12,8 +12,7 @@ from time import time import click import requests -from platformio import __version__, app -from platformio.util import exec_command, get_systype +from platformio import __version__, app, util class TelemetryBase(object): @@ -77,7 +76,7 @@ class MeasurementProtocol(TelemetryBase): dpdata.append("Click/%s" % click.__version__) # dpdata.append("Requests/%s" % requests.__version__) try: - result = exec_command(["scons", "--version"]) + result = util.exec_command(["scons", "--version"]) match = re.search(r"engine: v([\d\.]+)", result['out']) if match: dpdata.append("SCons/%s" % match.group(1)) @@ -86,7 +85,7 @@ class MeasurementProtocol(TelemetryBase): self['an'] = " ".join(dpdata) def _prefill_custom_data(self): - self['cd1'] = get_systype() + self['cd1'] = util.get_systype() self['cd2'] = "Python/%s %s" % (platform.python_version(), platform.platform()) self['cd4'] = 1 if app.get_setting("enable_prompts") else 0 @@ -155,6 +154,7 @@ class MPDataPusher(threading.Thread): r = self.http_session().post( "https://ssl.google-analytics.com/collect", data=data, + headers=util.get_request_defheaders(), timeout=3 ) r.raise_for_status() diff --git a/platformio/util.py b/platformio/util.py index a4faf4ca..806225fc 100644 --- a/platformio/util.py +++ b/platformio/util.py @@ -242,19 +242,22 @@ def get_logicaldisks(): return disks +def get_request_defheaders(): + return {"User-Agent": "PlatformIO/%s %s" % ( + __version__, requests.utils.default_user_agent())} + + def get_api_result(path, params=None, data=None): result = None r = None try: - headers = {"User-Agent": "PlatformIO/%s %s" % ( - __version__, requests.utils.default_user_agent())} - if data: r = requests.post(__apiurl__ + path, params=params, data=data, - headers=headers) + headers=get_request_defheaders()) else: - r = requests.get(__apiurl__ + path, params=params, headers=headers) + r = requests.get(__apiurl__ + path, params=params, + headers=get_request_defheaders()) result = r.json() r.raise_for_status() except requests.exceptions.HTTPError as e: From 47903a7ac9765e1fdd8ed84142d8f8cba9a44bb6 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 30 Apr 2015 14:14:57 +0100 Subject: [PATCH 29/78] Update User Guide documentation for PlatformIO 2.0 // Issue #158 --- docs/platforms/creating_platform.rst | 8 +- docs/userguide/cmd_update.rst | 3 +- docs/userguide/index.rst | 6 +- .../userguide/{ => platforms}/cmd_install.rst | 12 +-- docs/userguide/{ => platforms}/cmd_list.rst | 10 +- docs/userguide/{ => platforms}/cmd_search.rst | 14 +-- docs/userguide/{ => platforms}/cmd_show.rst | 10 +- .../{ => platforms}/cmd_uninstall.rst | 10 +- docs/userguide/platforms/cmd_update.rst | 99 +++++++++++++++++++ docs/userguide/platforms/index.rst | 22 +++++ 10 files changed, 156 insertions(+), 38 deletions(-) rename docs/userguide/{ => platforms}/cmd_install.rst (84%) rename docs/userguide/{ => platforms}/cmd_list.rst (89%) rename docs/userguide/{ => platforms}/cmd_search.rst (94%) rename docs/userguide/{ => platforms}/cmd_show.rst (81%) rename docs/userguide/{ => platforms}/cmd_uninstall.rst (69%) create mode 100644 docs/userguide/platforms/cmd_update.rst create mode 100644 docs/userguide/platforms/index.rst diff --git a/docs/platforms/creating_platform.rst b/docs/platforms/creating_platform.rst index 1c5c0505..656c6497 100644 --- a/docs/platforms/creating_platform.rst +++ b/docs/platforms/creating_platform.rst @@ -294,9 +294,9 @@ Installation 1. Create ``platforms`` directory in :ref:`projectconf_pio_home_dir` if it doesn't exists. 2. Copy ``test.py`` and ``test-builder.py`` files to ``platforms`` directory. -3. Search available platforms via :ref:`cmd_search` command. You should see +3. Search available platforms via :ref:`cmd_platforms_search` command. You should see ``test`` platform. -4. Install ``test`` platform via :ref:`cmd_install` command. +4. Install ``test`` platform via :ref:`cmd_platforms_install` command. Now, you can use ``test`` for the :ref:`projectconf_env_platform` option in :ref:`projectconf`. @@ -413,5 +413,5 @@ and copy there two files: Default([target_firm, target_size]) -Now, we should see ``ststm32gdb`` platform using :ref:`cmd_search` command output -and can install it via :ref:`platformio platforms install ststm32gdb ` command. +Now, we should see ``ststm32gdb`` platform using :ref:`cmd_platforms_search` command output +and can install it via :ref:`platformio platforms install ststm32gdb ` command. diff --git a/docs/userguide/cmd_update.rst b/docs/userguide/cmd_update.rst index b01cf9b4..60609fe2 100644 --- a/docs/userguide/cmd_update.rst +++ b/docs/userguide/cmd_update.rst @@ -16,7 +16,8 @@ Usage Description ----------- -Check or update installed :ref:`Platforms ` +Check or update installed :ref:`Platforms ` and +:ref:`Libraries ` Examples diff --git a/docs/userguide/index.rst b/docs/userguide/index.rst index a2db09ce..7d91e152 100644 --- a/docs/userguide/index.rst +++ b/docs/userguide/index.rst @@ -16,14 +16,10 @@ To print all available commands and options use: cmd_boards cmd_init - cmd_install platformio lib - cmd_list + platformio platforms cmd_run - cmd_search cmd_serialports cmd_settings - cmd_show - cmd_uninstall cmd_update cmd_upgrade diff --git a/docs/userguide/cmd_install.rst b/docs/userguide/platforms/cmd_install.rst similarity index 84% rename from docs/userguide/cmd_install.rst rename to docs/userguide/platforms/cmd_install.rst index 33557ca6..6f7dc8a8 100644 --- a/docs/userguide/cmd_install.rst +++ b/docs/userguide/platforms/cmd_install.rst @@ -1,7 +1,7 @@ -.. _cmd_install: +.. _cmd_platforms_install: -platformio install -================== +platformio platforms install +============================ .. contents:: @@ -10,7 +10,7 @@ Usage .. code-block:: bash - platformio install [OPTIONS] [PLATFORMS] + platformio platforms install [OPTIONS] [PLATFORMS] Description @@ -50,7 +50,7 @@ Examples .. code-block:: bash - $ platformio install timsp430 + $ platformio platforms install timsp430 Installing toolchain-timsp430 package: Downloading [####################################] 100% Unpacking [####################################] 100% @@ -68,7 +68,7 @@ Examples .. code-block:: bash - $ platformio install timsp430 --skip-default-package --with-package=uploader + $ platformio platforms install timsp430 --skip-default-package --with-package=uploader Installing tool-mspdebug package: Downloading [####################################] 100% Unpacking [####################################] 100% diff --git a/docs/userguide/cmd_list.rst b/docs/userguide/platforms/cmd_list.rst similarity index 89% rename from docs/userguide/cmd_list.rst rename to docs/userguide/platforms/cmd_list.rst index 1bc6b745..594defe6 100644 --- a/docs/userguide/cmd_list.rst +++ b/docs/userguide/platforms/cmd_list.rst @@ -1,7 +1,7 @@ -.. _cmd_list: +.. _cmd_platforms_list: -platformio list -=============== +platformio platforms list +========================= .. contents:: @@ -10,7 +10,7 @@ Usage .. code-block:: bash - platformio list [OPTIONS] + platformio platforms list [OPTIONS] Description @@ -31,7 +31,7 @@ Examples .. code-block:: bash - $ platformio list + $ platformio platforms list atmelavr with packages: toolchain-atmelavr, tool-avrdude, framework-arduinoavr, tool-micronucleus atmelsam with packages: framework-arduinosam, ldscripts, toolchain-gccarmnoneeabi, tool-bossac freescalekinetis with packages: framework-mbed, toolchain-gccarmnoneeabi diff --git a/docs/userguide/cmd_search.rst b/docs/userguide/platforms/cmd_search.rst similarity index 94% rename from docs/userguide/cmd_search.rst rename to docs/userguide/platforms/cmd_search.rst index 304c7fc6..b77bed41 100644 --- a/docs/userguide/cmd_search.rst +++ b/docs/userguide/platforms/cmd_search.rst @@ -1,7 +1,7 @@ -.. _cmd_search: +.. _cmd_platforms_search: -platformio search -================= +platformio platforms search +=========================== .. contents:: @@ -10,7 +10,7 @@ Usage .. code-block:: bash - platformio search QUERY [OPTIONS] + platformio platforms search QUERY [OPTIONS] Description @@ -34,7 +34,7 @@ Examples .. code-block:: bash - $ platformio search + $ platformio platforms search atmelavr (available packages: ldscripts, toolchain-gccarmnoneeabi, tool-lm4flash, framework-opencm3, framework-energiativa) -------- Atmel AVR 8- and 32-bit MCUs deliver a unique combination of performance... @@ -75,7 +75,7 @@ Examples .. code-block:: bash - $ platformio search ti + $ platformio platforms search ti timsp430 (available packages: ldscripts, toolchain-gccarmnoneeabi, tool-lm4flash, framework-opencm3, framework-energiativa) -------- MSP430 microcontrollers (MCUs) from Texas Instruments (TI) are ... @@ -88,7 +88,7 @@ Examples .. code-block:: bash - $ platformio search mbed + $ platformio platforms search mbed freescalekinetis (available packages: ldscripts, toolchain-gccarmnoneeabi, tool-lm4flash, framework-opencm3, framework-energiativa) ---------------- Freescale Kinetis Microcontrollers is family of multiple hardware- and ... diff --git a/docs/userguide/cmd_show.rst b/docs/userguide/platforms/cmd_show.rst similarity index 81% rename from docs/userguide/cmd_show.rst rename to docs/userguide/platforms/cmd_show.rst index c109117f..1948b4f2 100644 --- a/docs/userguide/cmd_show.rst +++ b/docs/userguide/platforms/cmd_show.rst @@ -1,7 +1,7 @@ -.. _cmd_show: +.. _cmd_platforms_show: -platformio show -=============== +platformio platforms show +========================= .. contents:: @@ -10,7 +10,7 @@ Usage .. code-block:: bash - platformio show PLATFORM + platformio platforms show PLATFORM Description @@ -24,7 +24,7 @@ Examples .. code-block:: bash - $ platformio show atmelavr + $ platformio platforms show atmelavr atmelavr - An embedded platform for Atmel AVR microcontrollers (with Arduino Framework) ---------- Package: toolchain-atmelavr diff --git a/docs/userguide/cmd_uninstall.rst b/docs/userguide/platforms/cmd_uninstall.rst similarity index 69% rename from docs/userguide/cmd_uninstall.rst rename to docs/userguide/platforms/cmd_uninstall.rst index ef6348f1..a7e307f9 100644 --- a/docs/userguide/cmd_uninstall.rst +++ b/docs/userguide/platforms/cmd_uninstall.rst @@ -1,7 +1,7 @@ -.. _cmd_uninstall: +.. _cmd_platforms_uninstall: -platformio uninstall -==================== +platformio platforms uninstall +============================== .. contents:: @@ -10,7 +10,7 @@ Usage .. code-block:: bash - platformio uninstall PLATFORM + platformio platforms uninstall PLATFORM Description @@ -24,7 +24,7 @@ Examples .. code-block:: bash - $ platformio uninstall timsp430 + $ platformio platforms uninstall timsp430 Uninstalling toolchain-timsp430 package: [OK] Uninstalling tool-mspdebug package: [OK] Uninstalling framework-energiamsp430 package: [OK] diff --git a/docs/userguide/platforms/cmd_update.rst b/docs/userguide/platforms/cmd_update.rst new file mode 100644 index 00000000..398d9527 --- /dev/null +++ b/docs/userguide/platforms/cmd_update.rst @@ -0,0 +1,99 @@ +.. _cmd_platforms_update: + +platformio platforms update +=========================== + +.. contents:: + +Usage +----- + +.. code-block:: bash + + platformio platforms update + + +Description +----------- + +Check or update installed :ref:`Platforms ` + + +Examples +-------- + +.. code-block:: bash + + $ platformio platforms update + + Platform atmelavr + -------- + Updating toolchain-atmelavr package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating tool-avrdude package: + Versions: Current=2, Latest=2 [Up-to-date] + Updating framework-arduinoavr package: + Versions: Current=12, Latest=12 [Up-to-date] + Updating tool-micronucleus package: + Versions: Current=1, Latest=1 [Up-to-date] + + Platform atmelsam + -------- + Updating framework-arduinosam package: + Versions: Current=3, Latest=3 [Up-to-date] + Updating ldscripts package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating toolchain-gccarmnoneeabi package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating tool-bossac package: + Versions: Current=1, Latest=1 [Up-to-date] + + Platform stm32 + -------- + Updating toolchain-gccarmnoneeabi package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating tool-stlink package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating framework-spl package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating framework-cmsis package: + Versions: Current=2, Latest=2 [Up-to-date] + Updating framework-opencm3 package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating ldscripts package: + Versions: Current=1, Latest=1 [Up-to-date] + + Platform teensy + -------- + Updating toolchain-atmelavr package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating ldscripts package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating framework-arduinoteensy package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating toolchain-gccarmnoneeabi package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating tool-teensy package: + Versions: Current=1, Latest=1 [Up-to-date] + + Platform timsp430 + -------- + Updating toolchain-timsp430 package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating tool-mspdebug package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating framework-energiamsp430 package: + Versions: Current=2, Latest=2 [Up-to-date] + + Platform titiva + -------- + Updating ldscripts package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating toolchain-gccarmnoneeabi package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating tool-lm4flash package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating framework-opencm3 package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating framework-energiativa package: + Versions: Current=4, Latest=4 [Up-to-date] diff --git a/docs/userguide/platforms/index.rst b/docs/userguide/platforms/index.rst new file mode 100644 index 00000000..7a582752 --- /dev/null +++ b/docs/userguide/platforms/index.rst @@ -0,0 +1,22 @@ +.. _userguide_platforms: + +Platforms Manager +================= + +To print all available commands and options use: + +.. code-block:: bash + + $ platformio platforms --help + $ platformio platforms COMMAND --help + + +.. toctree:: + :maxdepth: 2 + + cmd_install + cmd_list + cmd_search + cmd_show + cmd_uninstall + cmd_update From 6967e936a6d3b72a35a09054f6832bbbd384316a Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 1 May 2015 12:06:34 +0100 Subject: [PATCH 30/78] Fix CPPFLAGS example for build flags --- docs/projectconf.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/projectconf.rst b/docs/projectconf.rst index df508ed5..b33f0ea7 100644 --- a/docs/projectconf.rst +++ b/docs/projectconf.rst @@ -226,7 +226,7 @@ processes: * - Format - Scope - Description - * - ``Wp,option`` + * - ``-Wp,option`` - CPPFLAGS - Bypass the compiler driver and pass *option* directly through to the preprocessor From ab9a7e05c8a528900f4669956ed580f1c98cc9a9 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 1 May 2015 12:54:45 +0100 Subject: [PATCH 31/78] Use "lib_dir" by default for LibraryManager --- platformio/commands/lib.py | 12 ++++++------ platformio/libmanager.py | 6 +++--- platformio/maintenance.py | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/platformio/commands/lib.py b/platformio/commands/lib.py index f90ce614..7bf31383 100644 --- a/platformio/commands/lib.py +++ b/platformio/commands/lib.py @@ -7,7 +7,7 @@ import click from platformio import app, exception from platformio.libmanager import LibraryManager -from platformio.util import get_api_result, get_lib_dir +from platformio.util import get_api_result LIBLIST_TPL = ("[{id:^14}] {name:<25} {compatibility:<30} " "\"{authornames}\": {description}") @@ -100,7 +100,7 @@ def lib_search(query, **filters): @click.option("-v", "--version") @click.pass_context def lib_install(ctx, libid, version): - lm = LibraryManager(get_lib_dir()) + lm = LibraryManager() for id_ in libid: click.echo( "Installing library [ %s ]:" % click.style(str(id_), fg="green")) @@ -149,7 +149,7 @@ def lib_install_dependency(ctx, data): @cli.command("uninstall", short_help="Uninstall libraries") @click.argument("libid", type=click.INT, nargs=-1) def lib_uninstall(libid): - lm = LibraryManager(get_lib_dir()) + lm = LibraryManager() for id_ in libid: info = lm.get_info(id_) if lm.uninstall(id_): @@ -160,7 +160,7 @@ def lib_uninstall(libid): @cli.command("list", short_help="List installed libraries") @click.option("--json-output", is_flag=True) def lib_list(json_output): - lm = LibraryManager(get_lib_dir()) + lm = LibraryManager() items = lm.get_installed().values() if json_output: @@ -179,7 +179,7 @@ def lib_list(json_output): @cli.command("show", short_help="Show details about installed library") @click.argument("libid", type=click.INT) def lib_show(libid): - lm = LibraryManager(get_lib_dir()) + lm = LibraryManager() info = lm.get_info(libid) click.secho(info['name'], fg="cyan") click.echo("-" * len(info['name'])) @@ -215,7 +215,7 @@ def lib_show(libid): metavar="[LIBRARY_ID]") @click.pass_context def lib_update(ctx, libid): - lm = LibraryManager(get_lib_dir()) + lm = LibraryManager() for id_, latest_version in (lm.get_latest_versions() or {}).items(): if libid and int(id_) not in libid: continue diff --git a/platformio/libmanager.py b/platformio/libmanager.py index ae577d15..72d9665c 100644 --- a/platformio/libmanager.py +++ b/platformio/libmanager.py @@ -12,15 +12,15 @@ from platformio import telemetry from platformio.downloader import FileDownloader from platformio.exception import LibAlreadyInstalledError, LibNotInstalledError from platformio.unpacker import FileUnpacker -from platformio.util import get_api_result +from platformio.util import get_api_result, get_lib_dir class LibraryManager(object): CONFIG_NAME = ".library.json" - def __init__(self, lib_dir): - self.lib_dir = lib_dir + def __init__(self, lib_dir=None): + self.lib_dir = lib_dir or get_lib_dir() @staticmethod def download(url, dest_dir): diff --git a/platformio/maintenance.py b/platformio/maintenance.py index a44f454f..b67824b9 100644 --- a/platformio/maintenance.py +++ b/platformio/maintenance.py @@ -203,7 +203,7 @@ def check_internal_updates(ctx, what): if p.is_outdated(): outdated_items.append(platform) elif what == "libraries": - lm = LibraryManager(get_lib_dir()) + lm = LibraryManager() outdated_items = lm.get_outdated() if not outdated_items: From 0a474d34d8537c566fa3d7223b0deafc3d0ad29a Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 1 May 2015 13:00:33 +0100 Subject: [PATCH 32/78] Cache get_installed_libs result --- platformio/libmanager.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/platformio/libmanager.py b/platformio/libmanager.py index 72d9665c..40f015ff 100644 --- a/platformio/libmanager.py +++ b/platformio/libmanager.py @@ -8,11 +8,10 @@ from os.path import isdir, isfile, join from shutil import rmtree from tempfile import gettempdir -from platformio import telemetry +from platformio import telemetry, util from platformio.downloader import FileDownloader from platformio.exception import LibAlreadyInstalledError, LibNotInstalledError from platformio.unpacker import FileUnpacker -from platformio.util import get_api_result, get_lib_dir class LibraryManager(object): @@ -20,7 +19,7 @@ class LibraryManager(object): CONFIG_NAME = ".library.json" def __init__(self, lib_dir=None): - self.lib_dir = lib_dir or get_lib_dir() + self.lib_dir = lib_dir or util.get_lib_dir() @staticmethod def download(url, dest_dir): @@ -33,6 +32,7 @@ class LibraryManager(object): fu = FileUnpacker(pkgpath, dest_dir) return fu.start() + @util.memoized def get_installed(self): items = {} if not isdir(self.lib_dir): @@ -49,7 +49,7 @@ class LibraryManager(object): lib_ids = [str(item['id']) for item in self.get_installed().values()] if not lib_ids: return None - return get_api_result("/lib/version/" + str(",".join(lib_ids))) + return util.get_api_result("/lib/version/" + str(",".join(lib_ids))) def get_outdated(self): outdated = [] @@ -75,8 +75,10 @@ class LibraryManager(object): if self.is_installed(id_): raise LibAlreadyInstalledError() - dlinfo = get_api_result("/lib/download/" + str(id_), - dict(version=version) if version else None) + dlinfo = util.get_api_result( + "/lib/download/" + str(id_), + dict(version=version) if version else None + ) dlpath = None tmplib_dir = join(self.lib_dir, str(id_)) try: From 7391dfa93691e3dfd8b5dd7cbd38edf9d6be5823 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 1 May 2015 13:48:06 +0100 Subject: [PATCH 33/78] Fix caching for installed platforms and libraries --- platformio/libmanager.py | 1 - platformio/platforms/base.py | 1 - 2 files changed, 2 deletions(-) diff --git a/platformio/libmanager.py b/platformio/libmanager.py index 40f015ff..0dfd220d 100644 --- a/platformio/libmanager.py +++ b/platformio/libmanager.py @@ -32,7 +32,6 @@ class LibraryManager(object): fu = FileUnpacker(pkgpath, dest_dir) return fu.start() - @util.memoized def get_installed(self): items = {} if not isdir(self.lib_dir): diff --git a/platformio/platforms/base.py b/platformio/platforms/base.py index d91118ec..e1bcc20f 100644 --- a/platformio/platforms/base.py +++ b/platformio/platforms/base.py @@ -132,7 +132,6 @@ class PlatformFactory(object): return module @classmethod - @util.memoized def _lookup_platforms(cls): platforms = {} for d in (util.get_home_dir(), util.get_source_dir()): From 424e87a811f774e8c622dfec49b947849fe95501 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 1 May 2015 13:49:18 +0100 Subject: [PATCH 34/78] Add library dependencies using "install_libs" option in platformio.ini // Resolve #134 --- HISTORY.rst | 3 +++ docs/projectconf.rst | 16 ++++++++++++++++ platformio/commands/run.py | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index b765373a..2cf461a3 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -11,6 +11,9 @@ Release History (`issue #174 `_) * Added global ``-f, --force`` option which will force to accept any confirmation prompts (`issue #152 `_) +* Added library dependencies using ``install_libs`` option in + `platformio.ini `__ + (`issue #134 `_) * Allowed to add more boards to existing `platformio.ini `__ (`issue #167 `_) diff --git a/docs/projectconf.rst b/docs/projectconf.rst index b33f0ea7..5242e3aa 100644 --- a/docs/projectconf.rst +++ b/docs/projectconf.rst @@ -309,6 +309,22 @@ but will be applied only for the project source code from This option can be overridden by global environment variable :ref:`envvar_PLATFORMIO_SRCBUILD_FLAGS`. +``install_libs`` +^^^^^^^^^^^^^^^^ + +Specify dependent libraries which should be installed before environment +process. The only library IDs are allowed. Multiple libraries can be passed +using comma ``,`` sign. + +You can obtain library IDs using :ref:`cmd_lib_search` command. + +Example: + +.. code-block:: ini + + [env:depends_on_some_libs] + install_libs = 1,13,19 + ``ignore_libs`` ^^^^^^^^^^^^^^^ diff --git a/platformio/commands/run.py b/platformio/commands/run.py index 8d3d792f..0a49d513 100644 --- a/platformio/commands/run.py +++ b/platformio/commands/run.py @@ -9,8 +9,10 @@ from time import time import click from platformio import app, exception, telemetry, util +from platformio.commands.lib import lib_install as cmd_lib_install from platformio.commands.platforms import \ platforms_install as cmd_platforms_install +from platformio.libmanager import LibraryManager from platformio.platforms.base import PlatformFactory @@ -115,6 +117,16 @@ def _run_environment(ctx, name, options, targets, upload_port): telemetry.on_run_environment(options, envtargets) + # install platform and libs dependencies + _autoinstall_env_platform(ctx, platform) + if "install_libs" in options: + _autoinstall_env_libs(ctx, options['install_libs']) + + p = PlatformFactory.newPlatform(platform) + return p.run(variables, envtargets) + + +def _autoinstall_env_platform(ctx, platform): installed_platforms = PlatformFactory.get_platforms( installed=True).keys() if (platform not in installed_platforms and ( @@ -123,5 +135,21 @@ def _run_environment(ctx, name, options, targets, upload_port): "Would you like to install it now?" % platform))): ctx.invoke(cmd_platforms_install, platforms=[platform]) - p = PlatformFactory.newPlatform(platform) - return p.run(variables, envtargets) + +def _autoinstall_env_libs(ctx, libids_list): + require_libs = [int(l.strip()) for l in libids_list.split(",")] + installed_libs = [ + l['id'] for l in LibraryManager().get_installed().values() + ] + + not_intalled_libs = set(require_libs) - set(installed_libs) + if not require_libs or not not_intalled_libs: + return + + if (not app.get_setting("enable_prompts") or + click.confirm( + "The libraries with IDs '%s' have not been installed yet. " + "Would you like to install them now?" % + ", ".join([str(i) for i in not_intalled_libs]) + )): + ctx.invoke(cmd_lib_install, libid=not_intalled_libs) From 125c7b2aba75fa06ec4797d78a9ea6a5b5abaa13 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 1 May 2015 17:51:20 +0100 Subject: [PATCH 35/78] Revert caching for development platforms --- platformio/platforms/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio/platforms/base.py b/platformio/platforms/base.py index e1bcc20f..d91118ec 100644 --- a/platformio/platforms/base.py +++ b/platformio/platforms/base.py @@ -132,6 +132,7 @@ class PlatformFactory(object): return module @classmethod + @util.memoized def _lookup_platforms(cls): platforms = {} for d in (util.get_home_dir(), util.get_source_dir()): From f001c087ec87e1a6cfc3a711e05ad0d8abd236a0 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 5 May 2015 15:47:21 +0100 Subject: [PATCH 36/78] Allow to specify libraries which are compatible with build environment using use_libs option in platformio.ini --- HISTORY.rst | 19 +++++++++++++------ docs/projectconf.rst | 13 +++++++++++++ platformio/__init__.py | 2 +- platformio/builder/main.py | 1 + platformio/builder/tools/platformio.py | 12 +++++++++--- 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 2cf461a3..b5063b09 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -11,11 +11,17 @@ Release History (`issue #174 `_) * Added global ``-f, --force`` option which will force to accept any confirmation prompts (`issue #152 `_) -* Added library dependencies using ``install_libs`` option in - `platformio.ini `__ +* Allowed to add library dependencies for build environment using + `install_libs `_ + option in ``platformio.ini`` (`issue #134 `_) -* Allowed to add more boards to existing - `platformio.ini `__ +* Allowed to specify libraries which are compatible with build environment using + `use_libs `_ + option in ``platformio.ini`` + (`issue #148 `_) +* Allowed to add more boards to PlatformIO project with + `platformio init --board `__ + command (`issue #167 `_) * Allowed to choose which library to update (`issue #168 `_) @@ -45,8 +51,9 @@ Release History 1.6.3 version (`issue #156 `_) * Upgraded `Energia Framework `__ to 0101E0015 version (`issue #146 `_) -* Upgraded `Arduino Framework with Teensy Core `_ to - 1.22 version (`issue #162 `_, +* Upgraded `Arduino Framework with Teensy Core `_ + to 1.22 version + (`issue #162 `_, `issue #170 `_) * Fixed exceptions with PlatformIO auto-updates when Internet connection isn't active diff --git a/docs/projectconf.rst b/docs/projectconf.rst index 5242e3aa..698a8762 100644 --- a/docs/projectconf.rst +++ b/docs/projectconf.rst @@ -325,6 +325,19 @@ Example: [env:depends_on_some_libs] install_libs = 1,13,19 +``use_libs`` +^^^^^^^^^^^^ + +Specify libraries which should be used by ``Library Dependency Finder`` with +the highest priority. + +Example: + +.. code-block:: ini + + [env:libs_with_highest_priority] + use_libs = OneWire_ID1 + ``ignore_libs`` ^^^^^^^^^^^^^^^ diff --git a/platformio/__init__.py b/platformio/__init__.py index 9e004963..1d3f6c57 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -VERSION = (2, 0, "0.dev3") +VERSION = (2, 0, "0.dev4") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" diff --git a/platformio/builder/main.py b/platformio/builder/main.py index a68aceb9..45ec63d6 100644 --- a/platformio/builder/main.py +++ b/platformio/builder/main.py @@ -37,6 +37,7 @@ commonvars.AddVariables( ("BUILD_FLAGS",), ("SRCBUILD_FLAGS",), ("IGNORE_LIBS",), + ("USE_LIBS",), # board options ("BOARD",), diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py index a45b526d..b70cc2c0 100644 --- a/platformio/builder/tools/platformio.py +++ b/platformio/builder/tools/platformio.py @@ -131,9 +131,11 @@ def BuildLibrary(env, variant_dir, library_dir, ignore_files=None): def BuildDependentLibraries(env, src_dir): # pylint: disable=R0914 - INCLUDES_RE = re.compile(r"^\s*#include\s+(\<|\")([^\>\"\']+)(?:\>|\")", - re.M) + INCLUDES_RE = re.compile( + r"^\s*#include\s+(\<|\")([^\>\"\']+)(?:\>|\")", re.M) LIBSOURCE_DIRS = [env.subst(d) for d in env.get("LIBSOURCE_DIRS", [])] + USE_LIBS = [l.strip() for l in env.get("USE_LIBS", "").split(",") + if l.strip()] # start internal prototypes @@ -174,7 +176,10 @@ def BuildDependentLibraries(env, src_dir): # pylint: disable=R0914 if not isdir(lsd_dir): continue - for ld in listdir(lsd_dir): + for ld in USE_LIBS + listdir(lsd_dir): + if not isdir(join(lsd_dir, ld)): + continue + inc_path = normpath(join(lsd_dir, ld, self.name)) try: lib_dir = inc_path[:inc_path.index( @@ -205,6 +210,7 @@ def BuildDependentLibraries(env, src_dir): # pylint: disable=R0914 "libs": set(), "ordered": set() } + state = _process_src_dir(state, env.subst(src_dir)) result = [] From 5469b98057b1c471e807920f469f63d1bcb038f3 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 5 May 2015 16:15:44 +0100 Subject: [PATCH 37/78] Improve documentation for User Guide --- docs/userguide/index.rst | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/userguide/index.rst b/docs/userguide/index.rst index 7d91e152..21d25d41 100644 --- a/docs/userguide/index.rst +++ b/docs/userguide/index.rst @@ -3,7 +3,33 @@ User Guide ========== -To print all available commands and options use: +.. contents:: + +Usage +----- + +.. code-block:: bash + + platformio [OPTIONS] COMMAND + +Options +------- + +.. option:: + --force, - f + +Force to accept any confirmation prompts. This option allows to avoid an issue +with :ref:`faq_troubleshooting_pioblocksprompt` + +.. option:: + --version + +Show the version of PlatformIO + +.. option:: + --help + +Show help for the available options and commands .. code-block:: bash @@ -11,6 +37,9 @@ To print all available commands and options use: $ platformio COMMAND --help +Commands +-------- + .. toctree:: :maxdepth: 2 From 945b1324af18662ad49c00fd01be50d5c6d42ba3 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 6 May 2015 11:17:38 +0100 Subject: [PATCH 38/78] Fix gathering of _tplvars --- platformio/ide/projectgenerator.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/platformio/ide/projectgenerator.py b/platformio/ide/projectgenerator.py index f086eabe..45fdd3d8 100644 --- a/platformio/ide/projectgenerator.py +++ b/platformio/ide/projectgenerator.py @@ -15,7 +15,9 @@ class ProjectGenerator(object): def __init__(self, project_dir, ide): self.project_dir = project_dir self.ide = ide - self._tplvars = self._gather_tplvars() + self._tplvars = {} + + self._gather_tplvars() @staticmethod def get_supported_ides(): @@ -60,12 +62,10 @@ class ProjectGenerator(object): return bottle.template(content, **self._tplvars) def _gather_tplvars(self): - data = self.get_project_env() + self._tplvars.update(self.get_project_env()) - data.update({ + self._tplvars.update({ "project_name": self.get_project_name(), "includes": self.get_includes(), "srcfiles": self.get_srcfiles() }) - - return data From 04f246ea9d7a76e05b6402afa14e52f23e28dc41 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 6 May 2015 11:28:56 +0100 Subject: [PATCH 39/78] Remove "autogen_" prefix from default env --- docs/userguide/cmd_init.rst | 7 ++++--- platformio/commands/init.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/userguide/cmd_init.rst b/docs/userguide/cmd_init.rst index 637cdafd..fa0d8e43 100644 --- a/docs/userguide/cmd_init.rst +++ b/docs/userguide/cmd_init.rst @@ -57,9 +57,10 @@ allows you to disable firmware auto-uploading by default. .. option:: --env-prefix -An environment prefix which will be used with pair in board type. The default -value is ``autogen_``. For example, the default environment name for -``teensy_31`` board will be ``[env:autogen_teensy_31]``. +An environment prefix which will be used with pair in board type. + +For example, the default environment name for ``teensy_31`` board will +be ``[env:teensy_31]``. Examples diff --git a/platformio/commands/init.py b/platformio/commands/init.py index 229a1c23..4799d5fc 100644 --- a/platformio/commands/init.py +++ b/platformio/commands/init.py @@ -32,7 +32,7 @@ def validate_boards(ctx, param, value): # pylint: disable=W0613 @click.option("--ide", type=click.Choice(ProjectGenerator.get_supported_ides())) @click.option("--disable-auto-uploading", is_flag=True) -@click.option("--env-prefix", default="autogen_") +@click.option("--env-prefix", default="") def cli(project_dir, board, ide, disable_auto_uploading, env_prefix): # ask about auto-uploading From eeeed1ba08bd1781eb302ff663a216401ba83fd2 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 6 May 2015 17:29:58 +0100 Subject: [PATCH 40/78] =?UTF-8?q?Allow=20to=20run=20project=20with=20platf?= =?UTF-8?q?ormio=20run=20=E2=80=93project-dir=20option=20without=20changin?= =?UTF-8?q?g=20the=20current=20working=20directory=20//=20Resolve=20#192?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HISTORY.rst | 3 ++ docs/userguide/cmd_run.rst | 6 +++ platformio/commands/run.py | 81 +++++++++++++++++++++----------------- 3 files changed, 54 insertions(+), 36 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index b5063b09..2de01062 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -11,6 +11,9 @@ Release History (`issue #174 `_) * Added global ``-f, --force`` option which will force to accept any confirmation prompts (`issue #152 `_) +* Allowed to run project with `platformio run --project-dir `_ option without changing the current working + directory + (`issue #192 `_) * Allowed to add library dependencies for build environment using `install_libs `_ option in ``platformio.ini`` diff --git a/docs/userguide/cmd_run.rst b/docs/userguide/cmd_run.rst index 2dc833f4..9c170794 100644 --- a/docs/userguide/cmd_run.rst +++ b/docs/userguide/cmd_run.rst @@ -39,6 +39,12 @@ Process specified targets Upload port of embedded board. To print all available ports use :ref:`cmd_serialports` command +.. option:: + --build-dir + +Specify the path to project directory. By default, ``--build-dir`` is equal to +current working directory (``CWD``). + Examples -------- diff --git a/platformio/commands/run.py b/platformio/commands/run.py index 0a49d513..e29d1366 100644 --- a/platformio/commands/run.py +++ b/platformio/commands/run.py @@ -2,6 +2,7 @@ # See LICENSE for details. from datetime import datetime +from os import chdir, getcwd from os.path import getmtime, isdir, join from shutil import rmtree from time import time @@ -20,52 +21,60 @@ from platformio.platforms.base import PlatformFactory @click.option("--environment", "-e", multiple=True, metavar="") @click.option("--target", "-t", multiple=True, metavar="") @click.option("--upload-port", metavar="") +@click.option("--project-dir", default=getcwd, + type=click.Path(exists=True, file_okay=False, dir_okay=True, + writable=True, resolve_path=True)) @click.pass_context -def cli(ctx, environment, target, upload_port): +def cli(ctx, environment, target, upload_port, project_dir): + initial_cwd = getcwd() + chdir(project_dir) + try: + config = util.get_project_config() - config = util.get_project_config() + if not config.sections(): + raise exception.ProjectEnvsNotAvailable() - if not config.sections(): - raise exception.ProjectEnvsNotAvailable() + unknown = set(environment) - set([s[4:] for s in config.sections()]) + if unknown: + raise exception.UnknownEnvNames(", ".join(unknown)) - unknown = set(environment) - set([s[4:] for s in config.sections()]) - if unknown: - raise exception.UnknownEnvNames(", ".join(unknown)) + # remove ".pioenvs" if project config is modified + _pioenvs_dir = util.get_pioenvs_dir() + if (isdir(_pioenvs_dir) and + getmtime(join(util.get_project_dir(), "platformio.ini")) > + getmtime(_pioenvs_dir)): + rmtree(_pioenvs_dir) - # remove ".pioenvs" if project config is modified - _pioenvs_dir = util.get_pioenvs_dir() - if (isdir(_pioenvs_dir) and - getmtime(join(util.get_project_dir(), "platformio.ini")) > - getmtime(_pioenvs_dir)): - rmtree(_pioenvs_dir) + found_error = False + _first_done = False + for section in config.sections(): + # skip main configuration section + if section == "platformio": + continue + elif section[:4] != "env:": + raise exception.InvalidEnvName(section) - found_error = False - _first_done = False - for section in config.sections(): - # skip main configuration section - if section == "platformio": - continue - elif section[:4] != "env:": - raise exception.InvalidEnvName(section) + envname = section[4:] + if environment and envname not in environment: + # echo("Skipped %s environment" % style(envname, fg="yellow")) + continue - envname = section[4:] - if environment and envname not in environment: - # echo("Skipped %s environment" % style(envname, fg="yellow")) - continue + options = {} + for k, v in config.items(section): + options[k] = v - options = {} - for k, v in config.items(section): - options[k] = v + if _first_done: + click.echo() - if _first_done: - click.echo() + if not process_environment( + ctx, envname, options, target, upload_port): + found_error = True + _first_done = True - if not process_environment(ctx, envname, options, target, upload_port): - found_error = True - _first_done = True - - if found_error: - raise exception.ReturnErrorCode() + if found_error: + raise exception.ReturnErrorCode() + finally: + chdir(initial_cwd) def process_environment(ctx, name, options, targets, upload_port): From 92e81ee61909fe903b8a9b5052829317070b2b03 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 6 May 2015 18:07:17 +0100 Subject: [PATCH 41/78] Cleanup code --- platformio/__main__.py | 3 ++- platformio/commands/run.py | 51 ++++++++++++++++++++------------------ platformio/maintenance.py | 2 +- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/platformio/__main__.py b/platformio/__main__.py index d0e910c8..a4e44146 100644 --- a/platformio/__main__.py +++ b/platformio/__main__.py @@ -37,7 +37,8 @@ class PlatformioCLI(click.MultiCommand): # pylint: disable=R0904 raise exception.UnknownCLICommand(name) return mod.cli - def _handle_obsolate_command(self, name): + @staticmethod + def _handle_obsolate_command(name): if name in ("install", "list", "search", "show", "uninstall"): click.secho( "Warning! `platformio %s` command is obsoleted and will be " diff --git a/platformio/commands/run.py b/platformio/commands/run.py index e29d1366..5cbd650f 100644 --- a/platformio/commands/run.py +++ b/platformio/commands/run.py @@ -45,39 +45,42 @@ def cli(ctx, environment, target, upload_port, project_dir): getmtime(_pioenvs_dir)): rmtree(_pioenvs_dir) - found_error = False - _first_done = False + results = [] for section in config.sections(): - # skip main configuration section - if section == "platformio": - continue - elif section[:4] != "env:": - raise exception.InvalidEnvName(section) - - envname = section[4:] - if environment and envname not in environment: - # echo("Skipped %s environment" % style(envname, fg="yellow")) - continue - - options = {} - for k, v in config.items(section): - options[k] = v - - if _first_done: + if results and results[-1] is not None: click.echo() - if not process_environment( - ctx, envname, options, target, upload_port): - found_error = True - _first_done = True + results.append(_process_conf_section( + ctx, config, section, environment, target, upload_port)) - if found_error: + if not all([r for r in results if r is not None]): raise exception.ReturnErrorCode() finally: chdir(initial_cwd) -def process_environment(ctx, name, options, targets, upload_port): +def _process_conf_section(ctx, config, section, # pylint: disable=R0913 + environment, target, upload_port): + # skip main configuration section + if section == "platformio": + return None + + if section[:4] != "env:": + raise exception.InvalidEnvName(section) + + envname = section[4:] + if environment and envname not in environment: + # echo("Skipped %s environment" % style(envname, fg="yellow")) + return None + + options = {} + for k, v in config.items(section): + options[k] = v + + return _process_environment(ctx, envname, options, target, upload_port) + + +def _process_environment(ctx, name, options, targets, upload_port): terminal_width, _ = click.get_terminal_size() start_time = time() diff --git a/platformio/maintenance.py b/platformio/maintenance.py index b67824b9..89d7fbd3 100644 --- a/platformio/maintenance.py +++ b/platformio/maintenance.py @@ -19,7 +19,7 @@ from platformio.commands.platforms import \ from platformio.commands.upgrade import get_latest_version from platformio.libmanager import LibraryManager from platformio.platforms.base import PlatformFactory -from platformio.util import get_home_dir, get_lib_dir +from platformio.util import get_home_dir def on_platformio_start(ctx, force): From 19b7c730aecb7bebcf3a0a5dd0980d96e9102f0a Mon Sep 17 00:00:00 2001 From: Valeriy Koval Date: Thu, 7 May 2015 17:21:44 +0300 Subject: [PATCH 42/78] Update project generator and templates. --- platformio/ide/projectgenerator.py | 14 +- platformio/ide/tpls/eclipse/.cproject.tpl | 37 +-- .../ide/tpls/qtcreator/platformio.pro.tpl | 13 +- .../tpls/qtcreator/platformio.pro.user.tpl | 231 ++++++++++++++++++ .../tpls/visualstudio/platformio.vcxproj.tpl | 12 +- 5 files changed, 280 insertions(+), 27 deletions(-) create mode 100644 platformio/ide/tpls/qtcreator/platformio.pro.user.tpl diff --git a/platformio/ide/projectgenerator.py b/platformio/ide/projectgenerator.py index 45fdd3d8..ebdaa2fb 100644 --- a/platformio/ide/projectgenerator.py +++ b/platformio/ide/projectgenerator.py @@ -2,7 +2,7 @@ # See LICENSE for details. from glob import glob -from os import listdir +from os import listdir, walk from os.path import basename, isdir, join import bottle @@ -32,6 +32,7 @@ class ProjectGenerator(object): for section in config.sections(): if not section.startswith("env:"): continue + data['env_name'] = section[4:] for k, v in config.items(section): data[k] = v return data @@ -43,6 +44,13 @@ class ProjectGenerator(object): return [] def get_srcfiles(self): + result = [] + for root, _, files in walk(util.get_projectsrc_dir()): + for f in files: + result.append(join(root, f)) + return result + + def get_defines(self): return [] def get_tpls(self): @@ -67,5 +75,7 @@ class ProjectGenerator(object): self._tplvars.update({ "project_name": self.get_project_name(), "includes": self.get_includes(), - "srcfiles": self.get_srcfiles() + "srcfiles": self.get_srcfiles(), + "defines": self.get_defines(), + "project_dir": self.project_dir }) diff --git a/platformio/ide/tpls/eclipse/.cproject.tpl b/platformio/ide/tpls/eclipse/.cproject.tpl index 7ed43a49..ccd645ed 100644 --- a/platformio/ide/tpls/eclipse/.cproject.tpl +++ b/platformio/ide/tpls/eclipse/.cproject.tpl @@ -15,7 +15,7 @@ - + @@ -23,35 +23,40 @@ @@ -63,7 +68,7 @@ - + @@ -74,7 +79,7 @@ - + diff --git a/platformio/ide/tpls/qtcreator/platformio.pro.tpl b/platformio/ide/tpls/qtcreator/platformio.pro.tpl index 22188cd8..d809a9b3 100644 --- a/platformio/ide/tpls/qtcreator/platformio.pro.tpl +++ b/platformio/ide/tpls/qtcreator/platformio.pro.tpl @@ -5,15 +5,20 @@ else { HOMEDIR += $$(PWD) } -INCLUDEPATH += "$$HOMEDIR/.platformio/packages/framework-arduinoavr/cores/arduino" -INCLUDEPATH += "$$HOMEDIR/.platformio/packages/toolchain-atmelavr/avr/include" +% for include in includes: +INCLUDEPATH += "{{include}}" +% end win32:INCLUDEPATH ~= s,/,\\,g -# DEFINES += __AVR_ATmega328__ +% for define in defines: +DEFINES += "{{define}}" +% end OTHER_FILES += \ platformio.ini SOURCES += \ - src/main.c + % for file in srcfiles: + {{file}} + % end diff --git a/platformio/ide/tpls/qtcreator/platformio.pro.user.tpl b/platformio/ide/tpls/qtcreator/platformio.pro.user.tpl new file mode 100644 index 00000000..72ecb9ad --- /dev/null +++ b/platformio/ide/tpls/qtcreator/platformio.pro.user.tpl @@ -0,0 +1,231 @@ + + + + + + EnvironmentId + {00248b6a-4380-4f03-8dea-a03053177907} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + {{project_name}} + {{project_name}} + qt.53.win32_mingw482_kit + 1 + 0 + 0 + + {{project_dir}} + + + true + --force run + platformio + %{buildDir} + Custom Process Step + + ProjectExplorer.ProcessStep + + 1 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + --force run -t clean + platformio + %{buildDir} + Custom Process Step + + ProjectExplorer.ProcessStep + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + + {{project_dir}} + + + true + --force run + platformio + %{buildDir} + Custom Process Step + + ProjectExplorer.ProcessStep + + 1 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + --force run -t clean + platformio + %{buildDir} + Custom Process Step + + ProjectExplorer.ProcessStep + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 2 + + + 0 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy locally + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + platformio + + Qt4ProjectManager.Qt4RunConfiguration:{{project_dir}}/platformio.pro + + platformio.pro + false + false + + 3768 + false + true + false + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 16 + + + Version + 16 + + diff --git a/platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl b/platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl index 54d5be85..1a069d7e 100644 --- a/platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl +++ b/platformio/ide/tpls/visualstudio/platformio.vcxproj.tpl @@ -38,14 +38,14 @@ platformio --force run platformio --force run --target clean - WIN32;_DEBUG;$(NMakePreprocessorDefinitions) - $(HOMEDRIVE)$(HOMEPATH)\.platformio\packages\toolchain-atmelavr\avr\include;$(HOMEDRIVE)$(HOMEPATH)\.platformio\packages\framework-arduinoavr\cores\arduino;$(NMakeIncludeSearchPath) + {{";".join(defines)}} + {{";".join(includes)}} platformio run platformio run -t clean - WIN32;NDEBUG;$(NMakePreprocessorDefinitions) - $(HOMEDRIVE)$(HOMEPATH)\.platformio\packages\toolchain-atmelavr\avr\include;$(HOMEDRIVE)$(HOMEPATH)\.platformio\packages\framework-arduinoavr\cores\arduino;$(NMakeIncludeSearchPath) + {";".join(defines)}} + {{";".join(includes)}} @@ -54,7 +54,9 @@ - + % for file in srcfiles: + + % end From 7822a929f39b4b9ba823d6e350b7fd4589e954ca Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 7 May 2015 15:53:06 +0100 Subject: [PATCH 43/78] Fix tests after removing "autogen" prefix for init project --- examples/wiring-blink/platformio.ini | 8 ++++---- tests/commands/test_init.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/wiring-blink/platformio.ini b/examples/wiring-blink/platformio.ini index a1445967..33d4a63f 100644 --- a/examples/wiring-blink/platformio.ini +++ b/examples/wiring-blink/platformio.ini @@ -5,22 +5,22 @@ ; http://docs.platformio.org/en/latest/projectconf.html ; -[env:autogen_uno] +[env:uno] platform = atmelavr framework = arduino board = uno -[env:autogen_teensy31] +[env:teensy31] platform = teensy framework = arduino board = teensy31 -[env:autogen_lpmsp430g2553] +[env:lpmsp430g2553] platform = timsp430 framework = energia board = lpmsp430g2553 -[env:autogen_lptm4c1230c3pm] +[env:lptm4c1230c3pm] platform = titiva framework = energia board = lptm4c1230c3pm diff --git a/tests/commands/test_init.py b/tests/commands/test_init.py index d951f760..d354982e 100644 --- a/tests/commands/test_init.py +++ b/tests/commands/test_init.py @@ -46,9 +46,9 @@ def test_init_special_board(platformio_setup, clirunner, validate_cliresult): ("targets", "upload") ] - assert config.has_section("env:autogen_uno") + assert config.has_section("env:uno") assert len(set(expected_result).symmetric_difference( - set(config.items("env:autogen_uno")))) == 0 + set(config.items("env:uno")))) == 0 def test_init_disable_auto_uploading(platformio_setup, clirunner, @@ -64,9 +64,9 @@ def test_init_disable_auto_uploading(platformio_setup, clirunner, ("framework", "arduino"), ("board", "uno") ] - assert config.has_section("env:autogen_uno") + assert config.has_section("env:uno") assert len(set(expected_result).symmetric_difference( - set(config.items("env:autogen_uno")))) == 0 + set(config.items("env:uno")))) == 0 def test_init_incorrect_board(clirunner): From 510c4decb7c4bcae9b2dd95b05880e04841636f9 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 7 May 2015 16:20:53 +0100 Subject: [PATCH 44/78] PlatformIO as Continuous Integration tool for embedded projects // Resolve #108 --- HISTORY.rst | 4 +- docs/userguide/cmd_ci.rst | 125 ++++++++++++++++++++++++++++++++++ docs/userguide/index.rst | 1 + platformio/__init__.py | 2 +- platformio/commands/ci.py | 139 ++++++++++++++++++++++++++++++++++++++ platformio/exception.py | 9 +++ 6 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 docs/userguide/cmd_ci.rst create mode 100644 platformio/commands/ci.py diff --git a/HISTORY.rst b/HISTORY.rst index 2de01062..29867dd9 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,9 +4,11 @@ Release History 2.0.0 (2015-??-??) ------------------ -* Implemented PlatformIO CLI 2.0: "platform" related commands have been +* PlatformIO CLI 2.0: "platform" related commands have been moved to ``platformio platforms`` subcommand (`issue #158 `_) +* PlatformIO as Continuous Integration (CI) tool for embedded projects + (`issue #108 `_) * Created `PlatformIO gitter.im `_ room (`issue #174 `_) * Added global ``-f, --force`` option which will force to accept any diff --git a/docs/userguide/cmd_ci.rst b/docs/userguide/cmd_ci.rst new file mode 100644 index 00000000..cb7e6642 --- /dev/null +++ b/docs/userguide/cmd_ci.rst @@ -0,0 +1,125 @@ +.. _cmd_ci: + +platformio ci +============= + +.. contents:: + +Usage +----- + +.. code-block:: bash + + platformio ci [OPTIONS] [SRC] + + +Description +----------- + +`Continuous integration (CI, wiki) `_ +is the practice, in software engineering, of merging all developer working +copies with a shared mainline several times a day. + +:ref:`cmd_ci` command is conceived of as "hot key" for building project with +arbitrary source code structure. In a nutshell, using ``SRC`` and +:option:`platformio ci --lib` contents PlatformIO initialises via +:ref:`cmd_init` new project in :option:`platformio ci --build-dir` +with the build environments (using :option:`platformio ci --board` or +:option:`platformio ci --project-conf`) and processes them via :ref:`cmd_run` +command. + +:ref:`cmd_ci` command is intended to be used in combination with the build +servers and the popular +`Continuous Integration Software `_. + +By integrating regularly, you can detect errors quickly, and locate them more +easily. + +.. note:: + :ref:`cmd_ci` command accepts **multiple** ``SRC`` arguments, + :option:`platformio ci --lib` and :option:`platformio ci --exclude` options + which can be a path to directory, file or + `Glob Pattern `_. + +Options +------- + +.. program:: platformio ci + +.. option:: + -l, --lib + +Source code which will be copied to ``%build_dir%/lib`` directly. + +If :option:`platformio ci --lib` is a path to file (not to directory), then +PlatformIO will create temporary directory within ``%build_dir%/lib`` and copy +the rest files into it. + + +.. option:: + --exclude + +Exclude directories and/-or files from :option:`platformio ci --build-dir`. The +path must be relative to PlatformIO project within +:option:`platformio ci --build-dir`. + +For example, exclude from project ``src`` directory: + +* ``examples`` folder +* ``*.h`` files from ``foo`` folder + +.. code-block:: bash + + platformio ci --exclude=src/examples --exclude=src/foo/*.h [SRC] + +.. option:: + --board, -b + +Build project with automatically pre-generated environments based on board +settings. + +For more details please look into :option:`platformio init --board`. + +.. option:: + --build-dir + +Path to directory where PlatformIO will initialise new project. By default it's +temporary directory within your operation system. + +.. note:: + + This directory will be removed at the end of build process. If you want to + keep it, please use :option:`platformio ci --keep-build-dir`. + +.. option:: + --keep-build-dir + +Don't remove :option:`platformio ci --build-dir` after build process. + +.. option:: + --project-conf + +Buid project using pre-configured :ref:`projectconf`. + +Examples +-------- + +1. Integration `Travis.CI `_ for GitHub + `USB_Host_Shield_2.0 `_ + project. The ``.travis.yml`` configuration file: + +.. code-block:: yaml + + language: python + python: + - "2.7" + + env: + - PLATFORMIO_CI_SRC=examples/Bluetooth/PS3SPP/PS3SPP.ino + - PLATFORMIO_CI_SRC=examples/pl2303/pl2303_gps/pl2303_gps.ino + + install: + - python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + + script: + - platformio ci --lib="." --board=uno --board=teensy31 --board=due diff --git a/docs/userguide/index.rst b/docs/userguide/index.rst index 21d25d41..e849ed58 100644 --- a/docs/userguide/index.rst +++ b/docs/userguide/index.rst @@ -44,6 +44,7 @@ Commands :maxdepth: 2 cmd_boards + cmd_ci cmd_init platformio lib platformio platforms diff --git a/platformio/__init__.py b/platformio/__init__.py index 1d3f6c57..83054086 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -VERSION = (2, 0, "0.dev4") +VERSION = (2, 0, "0.dev5") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" diff --git a/platformio/commands/ci.py b/platformio/commands/ci.py new file mode 100644 index 00000000..43e56ac7 --- /dev/null +++ b/platformio/commands/ci.py @@ -0,0 +1,139 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from glob import glob +from os import environ, makedirs, remove +from os.path import basename, isdir, isfile, join +from shutil import copyfile, copytree, rmtree +from tempfile import mkdtemp + +import click + +from platformio import app +from platformio.commands.init import cli as cmd_init +from platformio.commands.run import cli as cmd_run +from platformio.exception import CIBuildEnvsEmpty +from platformio.util import get_boards + + +def validate_path(ctx, param, value): # pylint: disable=W0613 + invalid_path = None + for p in value: + if not glob(p): + invalid_path = p + break + try: + assert invalid_path is None + return value + except AssertionError: + raise click.BadParameter("Found invalid path: %s" % invalid_path) + + +def validate_boards(ctx, param, value): # pylint: disable=W0613 + unknown_boards = set(value) - set(get_boards().keys()) + try: + assert not unknown_boards + return value + except AssertionError: + raise click.BadParameter( + "%s. Please search for the board types using " + "`platformio boards` command" % ", ".join(unknown_boards)) + + +@click.command("ci", short_help="Continuous Integration") +@click.argument("src", nargs=-1, callback=validate_path) +@click.option("--lib", "-l", multiple=True, callback=validate_path) +@click.option("--exclude", multiple=True) +@click.option("--board", "-b", multiple=True, callback=validate_boards) +@click.option("--build-dir", default=mkdtemp, + type=click.Path(exists=True, file_okay=False, dir_okay=True, + writable=True, resolve_path=True)) +@click.option("--keep-build-dir", is_flag=True) +@click.option("--project-conf", + type=click.Path(exists=True, file_okay=True, dir_okay=False, + readable=True, resolve_path=True)) +@click.pass_context +def cli(ctx, src, lib, exclude, board, # pylint: disable=R0913 + build_dir, keep_build_dir, project_conf): + + if not src: + src = environ.get("PLATFORMIO_CI_SRC", "").split(":") + if not src: + raise click.BadParameter("Missing argument 'src'") + + try: + app.set_session_var("force_option", True) + _clean_dir(build_dir) + + for dir_name, patterns in dict(lib=lib, src=src).iteritems(): + if not patterns: + continue + contents = [] + for p in patterns: + contents += glob(p) + _copy_contents(join(build_dir, dir_name), contents) + + if project_conf and isfile(project_conf): + copyfile(project_conf, join(build_dir, "platformio.ini")) + elif not board: + raise CIBuildEnvsEmpty() + + if exclude: + _exclude_contents(build_dir, exclude) + + # initialise project + ctx.invoke(cmd_init, project_dir=build_dir, board=board, + disable_auto_uploading=True) + + # process project + ctx.invoke(cmd_run, project_dir=build_dir) + finally: + if not keep_build_dir: + rmtree(build_dir) + + +def _clean_dir(dirpath): + rmtree(dirpath) + makedirs(dirpath) + + +def _copy_contents(dst_dir, contents): + items = { + "dirs": set(), + "files": set() + } + + for path in contents: + if isdir(path): + items['dirs'].add(path) + elif isfile(path): + items['files'].add(path) + + dst_dir_name = basename(dst_dir) + + if dst_dir_name == "src" and len(items['dirs']) == 1: + copytree(list(items['dirs']).pop(), dst_dir) + else: + makedirs(dst_dir) + for d in items['dirs']: + copytree(d, join(dst_dir, basename(d))) + + if not items['files']: + return + + if dst_dir_name == "lib": + dst_dir = join(dst_dir, mkdtemp(dir=dst_dir)) + + for f in items['files']: + copyfile(f, join(dst_dir, basename(f))) + + +def _exclude_contents(dst_dir, patterns): + contents = [] + for p in patterns: + contents += glob(join(dst_dir, p)) + for path in contents: + if isdir(path): + rmtree(path) + elif isfile(path): + remove(path) diff --git a/platformio/exception.py b/platformio/exception.py index daaa176e..22b5978a 100644 --- a/platformio/exception.py +++ b/platformio/exception.py @@ -165,6 +165,15 @@ class UpgraderFailed(PlatformioException): MESSAGE = "An error occurred while upgrading PlatformIO" +class CIBuildEnvsEmpty(PlatformioException): + + MESSAGE = ( + "Can't find PlatformIO build environments.\nPlease specify `--board` " + "or path to `platformio.ini` with predefined environments using " + "`--project-conf` option" + ) + + class SConsNotInstalled(PlatformioException): MESSAGE = ( From a1bad33d9dec989e355fa6337bbcee8f163191d6 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 7 May 2015 16:44:56 +0100 Subject: [PATCH 45/78] Add "program" to command options --- docs/userguide/cmd_ci.rst | 2 +- docs/userguide/cmd_init.rst | 5 ++++- docs/userguide/cmd_run.rst | 2 ++ docs/userguide/cmd_serialports.rst | 4 ++++ docs/userguide/index.rst | 2 ++ docs/userguide/lib/cmd_install.rst | 2 ++ docs/userguide/lib/cmd_list.rst | 2 ++ docs/userguide/lib/cmd_search.rst | 2 ++ docs/userguide/platforms/cmd_install.rst | 2 ++ docs/userguide/platforms/cmd_list.rst | 2 ++ docs/userguide/platforms/cmd_search.rst | 2 ++ 11 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/userguide/cmd_ci.rst b/docs/userguide/cmd_ci.rst index cb7e6642..a9b7b153 100644 --- a/docs/userguide/cmd_ci.rst +++ b/docs/userguide/cmd_ci.rst @@ -47,7 +47,7 @@ Options .. program:: platformio ci .. option:: - -l, --lib + --lib, -l Source code which will be copied to ``%build_dir%/lib`` directly. diff --git a/docs/userguide/cmd_init.rst b/docs/userguide/cmd_init.rst index fa0d8e43..d7c5170a 100644 --- a/docs/userguide/cmd_init.rst +++ b/docs/userguide/cmd_init.rst @@ -29,6 +29,8 @@ This command will create: Options ------- +.. program:: platformio init + .. option:: --project-dir, -d @@ -50,7 +52,8 @@ The full list with pre-configured boards is available here :ref:`platforms`. .. option:: --disable-auto-uploading -If you initialise project with the specified ``--board``, then *PlatformIO* +If you initialise project with the specified +:option:`platformio init --board``, then *PlatformIO* will create environment with enabled firmware auto-uploading. This option allows you to disable firmware auto-uploading by default. diff --git a/docs/userguide/cmd_run.rst b/docs/userguide/cmd_run.rst index 9c170794..9daa9dda 100644 --- a/docs/userguide/cmd_run.rst +++ b/docs/userguide/cmd_run.rst @@ -22,6 +22,8 @@ Process environments which are defined in :ref:`projectconf` file Options ------- +.. program:: platformio run + .. option:: -e, --environment diff --git a/docs/userguide/cmd_serialports.rst b/docs/userguide/cmd_serialports.rst index 360877ec..53bae993 100644 --- a/docs/userguide/cmd_serialports.rst +++ b/docs/userguide/cmd_serialports.rst @@ -24,6 +24,8 @@ List available `Serial Ports `_ Options ~~~~~~~ +.. program:: platformio serialports list + .. option:: --json-output @@ -99,6 +101,8 @@ To control *monitor* please use these "hot keys": Options ~~~~~~~ +.. program:: platformio serialports monitor + .. option:: -p, --port diff --git a/docs/userguide/index.rst b/docs/userguide/index.rst index e849ed58..6c52a9e5 100644 --- a/docs/userguide/index.rst +++ b/docs/userguide/index.rst @@ -15,6 +15,8 @@ Usage Options ------- +.. program:: platformio + .. option:: --force, - f diff --git a/docs/userguide/lib/cmd_install.rst b/docs/userguide/lib/cmd_install.rst index 854401b7..bb4dd95f 100644 --- a/docs/userguide/lib/cmd_install.rst +++ b/docs/userguide/lib/cmd_install.rst @@ -22,6 +22,8 @@ Install new library by specified Options ------- +.. program:: platformio lib install + .. option:: -v, --version diff --git a/docs/userguide/lib/cmd_list.rst b/docs/userguide/lib/cmd_list.rst index 2551704b..07c68bc3 100644 --- a/docs/userguide/lib/cmd_list.rst +++ b/docs/userguide/lib/cmd_list.rst @@ -21,6 +21,8 @@ List installed libraries Options ~~~~~~~ +.. program:: platformio lib list + .. option:: --json-output diff --git a/docs/userguide/lib/cmd_search.rst b/docs/userguide/lib/cmd_search.rst index 84899256..41bd8199 100644 --- a/docs/userguide/lib/cmd_search.rst +++ b/docs/userguide/lib/cmd_search.rst @@ -61,6 +61,8 @@ For more detail information please go to Options ------- +.. program:: platformio lib search + .. option:: -a, --author diff --git a/docs/userguide/platforms/cmd_install.rst b/docs/userguide/platforms/cmd_install.rst index 6f7dc8a8..ced397f8 100644 --- a/docs/userguide/platforms/cmd_install.rst +++ b/docs/userguide/platforms/cmd_install.rst @@ -27,6 +27,8 @@ There are several predefined aliases for packages, such as: Options ------- +.. program:: platformio platforms install + .. option:: --with-package diff --git a/docs/userguide/platforms/cmd_list.rst b/docs/userguide/platforms/cmd_list.rst index 594defe6..90e5d9a5 100644 --- a/docs/userguide/platforms/cmd_list.rst +++ b/docs/userguide/platforms/cmd_list.rst @@ -21,6 +21,8 @@ List installed :ref:`Platforms ` Options ~~~~~~~ +.. program:: platformio platforms list + .. option:: --json-output diff --git a/docs/userguide/platforms/cmd_search.rst b/docs/userguide/platforms/cmd_search.rst index b77bed41..20d9e42e 100644 --- a/docs/userguide/platforms/cmd_search.rst +++ b/docs/userguide/platforms/cmd_search.rst @@ -21,6 +21,8 @@ Search for development :ref:`Platforms ` Options ~~~~~~~ +.. program:: platformio platforms search + .. option:: --json-output From 44eaf1db22867f3d300ab49d14d8161afc3417c5 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 7 May 2015 17:02:53 +0100 Subject: [PATCH 46/78] Document PLATFORMIO_CI_SRC environment variable --- docs/userguide/cmd_ci.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/userguide/cmd_ci.rst b/docs/userguide/cmd_ci.rst index a9b7b153..a036d2e7 100644 --- a/docs/userguide/cmd_ci.rst +++ b/docs/userguide/cmd_ci.rst @@ -41,6 +41,11 @@ easily. which can be a path to directory, file or `Glob Pattern `_. +.. note:: + You can omit ``SRC`` argument and set path (multiple paths are allowed + denoting with ``:``) to + ``PLATFORMIO_CI_SRC`` `Environment variable `_ + Options ------- From 72ebe0ec1bf63d52cbceeff9e11c31740c79e8d0 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 7 May 2015 18:40:53 +0100 Subject: [PATCH 47/78] Fix PyLint warnings --- platformio/ide/projectgenerator.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/platformio/ide/projectgenerator.py b/platformio/ide/projectgenerator.py index ebdaa2fb..3f09249c 100644 --- a/platformio/ide/projectgenerator.py +++ b/platformio/ide/projectgenerator.py @@ -40,17 +40,20 @@ class ProjectGenerator(object): def get_project_name(self): return basename(self.project_dir) - def get_includes(self): + @staticmethod + def get_includes(): return [] - def get_srcfiles(self): + @staticmethod + def get_srcfiles(): result = [] for root, _, files in walk(util.get_projectsrc_dir()): for f in files: result.append(join(root, f)) return result - def get_defines(self): + @staticmethod + def get_defines(): return [] def get_tpls(self): From 40322e0f9b69b301c89d93782b3555007d8d44be Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 8 May 2015 17:32:14 +0100 Subject: [PATCH 48/78] Allow to print dump of SCons environment using "envdump" target --- platformio/builder/tools/platformio.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py index b70cc2c0..90e9f41d 100644 --- a/platformio/builder/tools/platformio.py +++ b/platformio/builder/tools/platformio.py @@ -6,7 +6,8 @@ import re from os import getenv, listdir, remove, sep, walk from os.path import basename, dirname, isdir, isfile, join, normpath -from SCons.Script import Exit, SConscript, SConscriptChdir +from SCons.Script import (COMMAND_LINE_TARGETS, Exit, SConscript, + SConscriptChdir) from SCons.Util import case_sensitive_suffixes from platformio.util import pioversion_to_intstr @@ -60,6 +61,10 @@ def BuildFirmware(env): *pioversion_to_intstr())] ) + if "envdump" in COMMAND_LINE_TARGETS: + print env.Dump() + Exit() + return firmenv.Program( join("$BUILD_DIR", "firmware"), [firmenv.GlobCXXFiles(vdir) for vdir in vdirs], From ccfc9afba7e5881da5d7e41537c07247b2058cb7 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 15 May 2015 13:56:19 +0200 Subject: [PATCH 49/78] Fix absolute paths for CI --- platformio/__init__.py | 2 +- platformio/commands/ci.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/platformio/__init__.py b/platformio/__init__.py index 83054086..ec20ef26 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -VERSION = (2, 0, "0.dev5") +VERSION = (2, 0, "0.dev6") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" diff --git a/platformio/commands/ci.py b/platformio/commands/ci.py index 43e56ac7..2218eb76 100644 --- a/platformio/commands/ci.py +++ b/platformio/commands/ci.py @@ -3,7 +3,7 @@ from glob import glob from os import environ, makedirs, remove -from os.path import basename, isdir, isfile, join +from os.path import abspath, basename, isdir, isfile, join from shutil import copyfile, copytree, rmtree from tempfile import mkdtemp @@ -104,6 +104,7 @@ def _copy_contents(dst_dir, contents): } for path in contents: + path = abspath(path) if isdir(path): items['dirs'].add(path) elif isfile(path): @@ -133,6 +134,7 @@ def _exclude_contents(dst_dir, patterns): for p in patterns: contents += glob(join(dst_dir, p)) for path in contents: + path = abspath(path) if isdir(path): rmtree(path) elif isfile(path): From 4a132941f63d3d64aadeb714d2bf4e1d56a86aab Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 15 May 2015 22:31:55 +0200 Subject: [PATCH 50/78] Reorder short version of options --- docs/userguide/cmd_ci.rst | 4 ++-- docs/userguide/cmd_init.rst | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/userguide/cmd_ci.rst b/docs/userguide/cmd_ci.rst index a036d2e7..f5743bff 100644 --- a/docs/userguide/cmd_ci.rst +++ b/docs/userguide/cmd_ci.rst @@ -52,7 +52,7 @@ Options .. program:: platformio ci .. option:: - --lib, -l + -l, --lib Source code which will be copied to ``%build_dir%/lib`` directly. @@ -78,7 +78,7 @@ For example, exclude from project ``src`` directory: platformio ci --exclude=src/examples --exclude=src/foo/*.h [SRC] .. option:: - --board, -b + -b, --board Build project with automatically pre-generated environments based on board settings. diff --git a/docs/userguide/cmd_init.rst b/docs/userguide/cmd_init.rst index d7c5170a..a84694ff 100644 --- a/docs/userguide/cmd_init.rst +++ b/docs/userguide/cmd_init.rst @@ -32,12 +32,12 @@ Options .. program:: platformio init .. option:: - --project-dir, -d + -d, --project-dir A path to the directory where *PlatformIO* will initialise new project. .. option:: - --board, -b + -b, --board If you specify board ``type`` (you can pass multiple ``--board`` options), then *PlatformIO* will automatically generate environment for :ref:`projectconf` and @@ -53,7 +53,7 @@ The full list with pre-configured boards is available here :ref:`platforms`. --disable-auto-uploading If you initialise project with the specified -:option:`platformio init --board``, then *PlatformIO* +:option:`platformio init --board`, then *PlatformIO* will create environment with enabled firmware auto-uploading. This option allows you to disable firmware auto-uploading by default. From c7b034b1541f993d0a90a94ca5e7d42d0cd8d7c2 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 15 May 2015 22:40:29 +0200 Subject: [PATCH 51/78] Add verbosity option for "run" and "ci" commands --- HISTORY.rst | 15 +-- docs/userguide/cmd_ci.rst | 8 ++ docs/userguide/cmd_run.rst | 15 +++ platformio/commands/ci.py | 5 +- platformio/commands/run.py | 175 +++++++++++++++++-------------- platformio/platforms/atmelavr.py | 4 +- platformio/platforms/base.py | 42 ++++++-- platformio/platforms/teensy.py | 4 +- 8 files changed, 167 insertions(+), 101 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index ef32bb7c..2372d1a5 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -11,26 +11,27 @@ Release History (`issue #108 `_) * Created `PlatformIO gitter.im `_ room (`issue #174 `_) -* Added global ``-f, --force`` option which will force to accept any +* Global ``-f, --force`` option which will force to accept any confirmation prompts (`issue #152 `_) -* Allowed to run project with `platformio run --project-dir `_ option without changing the current working +* Run project with `platformio run --project-dir `_ option without changing the current working directory (`issue #192 `_) -* Allowed to add library dependencies for build environment using +* Control verbosity of `platformio run `_ command via ``-v/--verbose`` option +* Add library dependencies for build environment using `install_libs `_ option in ``platformio.ini`` (`issue #134 `_) -* Allowed to specify libraries which are compatible with build environment using +* Specify libraries which are compatible with build environment using `use_libs `_ option in ``platformio.ini`` (`issue #148 `_) -* Allowed to add more boards to PlatformIO project with +* Add more boards to PlatformIO project with `platformio init --board `__ command (`issue #167 `_) -* Allowed to choose which library to update +* Choose which library to update (`issue #168 `_) -* Allowed to specify `platformio init --env-prefix `__ when initialise/update project +* Specify `platformio init --env-prefix `__ when initialise/update project (`issue #182 `_) * Disabled automatic updates by default for platforms, packages and libraries (`issue #171 `_) diff --git a/docs/userguide/cmd_ci.rst b/docs/userguide/cmd_ci.rst index f5743bff..3c2842d4 100644 --- a/docs/userguide/cmd_ci.rst +++ b/docs/userguide/cmd_ci.rst @@ -106,6 +106,14 @@ Don't remove :option:`platformio ci --build-dir` after build process. Buid project using pre-configured :ref:`projectconf`. +.. option:: + -v, --verbose + +Shows details about the results of processing environments. More details +:option:`platformio run --verbose` + +By default, verbosity level is set to 1 (only errors will be printed). + Examples -------- diff --git a/docs/userguide/cmd_run.rst b/docs/userguide/cmd_run.rst index 9daa9dda..468f22cd 100644 --- a/docs/userguide/cmd_run.rst +++ b/docs/userguide/cmd_run.rst @@ -47,6 +47,21 @@ Upload port of embedded board. To print all available ports use Specify the path to project directory. By default, ``--build-dir`` is equal to current working directory (``CWD``). +.. option:: + -v, --verbose + +Shows details about the results of processing environments. Each instance of +``--verbose`` on the command line increases the verbosity level by one, so if +you need more details on the output, specify it twice. + +There 3 levels of verbosity: + +1. ``-v`` - output errors only +2. ``-vv`` - output errors and warnings +3. ``-vvv`` - output errors, warnings and additional information + +By default, verbosity level is set to 3 (maximum information). + Examples -------- diff --git a/platformio/commands/ci.py b/platformio/commands/ci.py index 2218eb76..a9f24be5 100644 --- a/platformio/commands/ci.py +++ b/platformio/commands/ci.py @@ -52,9 +52,10 @@ def validate_boards(ctx, param, value): # pylint: disable=W0613 @click.option("--project-conf", type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True)) +@click.option("--verbose", "-v", count=True, default=1) @click.pass_context def cli(ctx, src, lib, exclude, board, # pylint: disable=R0913 - build_dir, keep_build_dir, project_conf): + build_dir, keep_build_dir, project_conf, verbose): if not src: src = environ.get("PLATFORMIO_CI_SRC", "").split(":") @@ -86,7 +87,7 @@ def cli(ctx, src, lib, exclude, board, # pylint: disable=R0913 disable_auto_uploading=True) # process project - ctx.invoke(cmd_run, project_dir=build_dir) + ctx.invoke(cmd_run, project_dir=build_dir, verbose=verbose) finally: if not keep_build_dir: rmtree(build_dir) diff --git a/platformio/commands/run.py b/platformio/commands/run.py index 5cbd650f..b61544ba 100644 --- a/platformio/commands/run.py +++ b/platformio/commands/run.py @@ -24,8 +24,10 @@ from platformio.platforms.base import PlatformFactory @click.option("--project-dir", default=getcwd, type=click.Path(exists=True, file_okay=False, dir_okay=True, writable=True, resolve_path=True)) +@click.option("--verbose", "-v", count=True, default=3) @click.pass_context -def cli(ctx, environment, target, upload_port, project_dir): +def cli(ctx, environment, target, upload_port, # pylint: disable=R0913,R0914 + project_dir, verbose): initial_cwd = getcwd() chdir(project_dir) try: @@ -47,98 +49,111 @@ def cli(ctx, environment, target, upload_port, project_dir): results = [] for section in config.sections(): - if results and results[-1] is not None: + # skip main configuration section + if section == "platformio": + continue + + if not section.startswith("env:"): + raise exception.InvalidEnvName(section) + + envname = section[4:] + if environment and envname not in environment: + # echo("Skipped %s environment" % style(envname, fg="yellow")) + continue + + if results: click.echo() - results.append(_process_conf_section( - ctx, config, section, environment, target, upload_port)) + options = {} + for k, v in config.items(section): + options[k] = v - if not all([r for r in results if r is not None]): + ep = EnvironmentProcessor( + ctx, envname, options, target, upload_port, verbose) + results.append(ep.process()) + + if not all(results): raise exception.ReturnErrorCode() finally: chdir(initial_cwd) -def _process_conf_section(ctx, config, section, # pylint: disable=R0913 - environment, target, upload_port): - # skip main configuration section - if section == "platformio": - return None +class EnvironmentProcessor(object): - if section[:4] != "env:": - raise exception.InvalidEnvName(section) + def __init__(self, cmd_ctx, name, options, # pylint: disable=R0913 + targets, upload_port, verbose): + self.cmd_ctx = cmd_ctx + self.name = name + self.options = options + self.targets = targets + self.upload_port = upload_port + self.verbose_level = int(verbose) - envname = section[4:] - if environment and envname not in environment: - # echo("Skipped %s environment" % style(envname, fg="yellow")) - return None + def process(self): + terminal_width, _ = click.get_terminal_size() + start_time = time() - options = {} - for k, v in config.items(section): - options[k] = v + click.echo("[%s] Processing %s (%s)" % ( + datetime.now().strftime("%c"), + click.style(self.name, fg="cyan", bold=True), + ", ".join(["%s: %s" % (k, v) for k, v in self.options.iteritems()]) + )) + click.secho("-" * terminal_width, bold=True) - return _process_environment(ctx, envname, options, target, upload_port) + result = self._run() + + is_error = result['returncode'] != 0 + summary_text = " Took %.2f seconds " % (time() - start_time) + half_line = "=" * ((terminal_width - len(summary_text) - 10) / 2) + click.echo("%s [%s]%s%s" % ( + half_line, + (click.style(" ERROR ", fg="red", bold=True) + if is_error else click.style("SUCCESS", fg="green", bold=True)), + summary_text, + half_line + ), err=is_error) + + return not is_error + + def _get_build_variables(self): + variables = ["PIOENV=" + self.name] + if self.upload_port: + variables.append("UPLOAD_PORT=%s" % self.upload_port) + for k, v in self.options.items(): + k = k.upper() + if k == "TARGETS" or (k == "UPLOAD_PORT" and self.upload_port): + continue + variables.append("%s=%s" % (k.upper(), v)) + return variables + + def _get_build_targets(self): + targets = [] + if self.targets: + targets = [t for t in self.targets] + elif "targets" in self.options: + targets = self.options['targets'].split() + return targets + + def _run(self): + if "platform" not in self.options: + raise exception.UndefinedEnvPlatform(self.name) + + platform = self.options['platform'] + build_vars = self._get_build_variables() + build_targets = self._get_build_targets() + + telemetry.on_run_environment(self.options, build_targets) + + # install platform and libs dependencies + _autoinstall_platform(self.cmd_ctx, platform) + if "install_libs" in self.options: + _autoinstall_libs(self.cmd_ctx, self.options['install_libs']) + + p = PlatformFactory.newPlatform(platform) + return p.run(build_vars, build_targets, self.verbose_level) -def _process_environment(ctx, name, options, targets, upload_port): - terminal_width, _ = click.get_terminal_size() - start_time = time() - - click.echo("[%s] Processing %s (%s)" % ( - datetime.now().strftime("%c"), - click.style(name, fg="cyan", bold=True), - ", ".join(["%s: %s" % (k, v) for k, v in options.iteritems()]) - )) - click.secho("-" * terminal_width, bold=True) - - result = _run_environment(ctx, name, options, targets, upload_port) - - is_error = result['returncode'] != 0 - summary_text = " Took %.2f seconds " % (time() - start_time) - half_line = "=" * ((terminal_width - len(summary_text) - 10) / 2) - click.echo("%s [%s]%s%s" % ( - half_line, - (click.style(" ERROR ", fg="red", bold=True) - if is_error else click.style("SUCCESS", fg="green", bold=True)), - summary_text, - half_line - ), err=is_error) - - return not is_error - - -def _run_environment(ctx, name, options, targets, upload_port): - variables = ["PIOENV=" + name] - if upload_port: - variables.append("UPLOAD_PORT=%s" % upload_port) - for k, v in options.items(): - k = k.upper() - if k == "TARGETS" or (k == "UPLOAD_PORT" and upload_port): - continue - variables.append("%s=%s" % (k.upper(), v)) - - envtargets = [] - if targets: - envtargets = [t for t in targets] - elif "targets" in options: - envtargets = options['targets'].split() - - if "platform" not in options: - raise exception.UndefinedEnvPlatform(name) - platform = options['platform'] - - telemetry.on_run_environment(options, envtargets) - - # install platform and libs dependencies - _autoinstall_env_platform(ctx, platform) - if "install_libs" in options: - _autoinstall_env_libs(ctx, options['install_libs']) - - p = PlatformFactory.newPlatform(platform) - return p.run(variables, envtargets) - - -def _autoinstall_env_platform(ctx, platform): +def _autoinstall_platform(ctx, platform): installed_platforms = PlatformFactory.get_platforms( installed=True).keys() if (platform not in installed_platforms and ( @@ -148,7 +163,7 @@ def _autoinstall_env_platform(ctx, platform): ctx.invoke(cmd_platforms_install, platforms=[platform]) -def _autoinstall_env_libs(ctx, libids_list): +def _autoinstall_libs(ctx, libids_list): require_libs = [int(l.strip()) for l in libids_list.split(",")] installed_libs = [ l['id'] for l in LibraryManager().get_installed().values() diff --git a/platformio/platforms/atmelavr.py b/platformio/platforms/atmelavr.py index 9c7bc243..23486589 100644 --- a/platformio/platforms/atmelavr.py +++ b/platformio/platforms/atmelavr.py @@ -47,7 +47,7 @@ class AtmelavrPlatform(BasePlatform): else: BasePlatform.on_run_err(self, line) - def run(self, variables, targets): + def run(self, variables, targets, verbose): for v in variables: if "BOARD=" not in v: continue @@ -58,4 +58,4 @@ class AtmelavrPlatform(BasePlatform): tuploader = "tool-micronucleus" self.PACKAGES[tuploader]['alias'] = "uploader" break - return BasePlatform.run(self, variables, targets) + return BasePlatform.run(self, variables, targets, verbose) diff --git a/platformio/platforms/base.py b/platformio/platforms/base.py index d91118ec..b1305c3f 100644 --- a/platformio/platforms/base.py +++ b/platformio/platforms/base.py @@ -190,6 +190,12 @@ class BasePlatform(object): def __init__(self): self._found_error = False + self._last_echo_line = None + + # 1 = errors + # 2 = 1 + warnings + # 3 = 2 + others + self._verbose_level = 3 def get_type(self): return self.__class__.__name__[:-8].lower() @@ -306,10 +312,12 @@ class BasePlatform(object): obsolated = pm.get_outdated() return not set(self.get_packages().keys()).isdisjoint(set(obsolated)) - def run(self, variables, targets): + def run(self, variables, targets, verbose): assert isinstance(variables, list) assert isinstance(targets, list) + self._verbose_level = int(verbose) + installed_platforms = PlatformFactory.get_platforms( installed=True).keys() installed_packages = PackageManager.get_installed() @@ -356,16 +364,34 @@ class BasePlatform(object): if self._found_error: result['returncode'] = 1 + if self._last_echo_line == ".": + click.echo("") + return result - def on_run_out(self, line): # pylint: disable=R0201 - fg = None - if "is up to date" in line: - fg = "green" - click.secho(line, fg=fg) + def on_run_out(self, line): + self._echo_line(line, level=3) - def on_run_err(self, line): # pylint: disable=R0201 + def on_run_err(self, line): is_error = self.LINE_ERROR_RE.search(line) is not None if is_error: self._found_error = True - click.secho(line, err=True, fg="red" if is_error else "yellow") + self._echo_line(line, level=1 if is_error else 2) + + def _echo_line(self, line, level): + assert 1 <= level <= 3 + + fg = ("red", "yellow", None)[level - 1] + if level == 3 and "is up to date" in line: + fg = "green" + + if level > self._verbose_level: + click.secho(".", fg=fg, err=level < 3, nl=False) + self._last_echo_line = "." + return + + if self._last_echo_line == ".": + click.echo("") + self._last_echo_line = line + + click.secho(line, fg=fg, err=level < 3) diff --git a/platformio/platforms/teensy.py b/platformio/platforms/teensy.py index cc79c3ee..07316d15 100644 --- a/platformio/platforms/teensy.py +++ b/platformio/platforms/teensy.py @@ -44,7 +44,7 @@ class TeensyPlatform(BasePlatform): def get_name(self): return "Teensy" - def run(self, variables, targets): + def run(self, variables, targets, verbose): for v in variables: if "BOARD=" not in v: continue @@ -56,4 +56,4 @@ class TeensyPlatform(BasePlatform): tpackage = "toolchain-gccarmnoneeabi" self.PACKAGES[tpackage]['alias'] = "toolchain" break - return BasePlatform.run(self, variables, targets) + return BasePlatform.run(self, variables, targets, verbose) From 8673bc15bcf2646c94c0a019a9d4db7837ff0a12 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 15 May 2015 22:50:39 +0200 Subject: [PATCH 52/78] Add --force option to avoid blocking caused by prompts --- docs/faq.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/faq.rst b/docs/faq.rst index b86e0790..6065be35 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -69,6 +69,7 @@ If you are going to run *PlatformIO* from **subprocess**, you **MUST DISABLE** all prompts. It will allow you to avoid blocking. There are a few options: +- using :option:`platformio --force` option before each command - using environment variable :ref:`PLATFORMIO_SETTING_ENABLE_PROMPTS=No ` - disable global setting ``enable_prompts`` via :ref:`cmd_settings` command - masking under Continuous Integration system via environment variable @@ -87,7 +88,7 @@ Answered in `issue #144 Date: Fri, 15 May 2015 23:35:59 +0200 Subject: [PATCH 53/78] Add links to Ivan Kravets interview for IT Hare --- docs/articles.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/articles.rst b/docs/articles.rst index 07f04723..5f708816 100644 --- a/docs/articles.rst +++ b/docs/articles.rst @@ -4,11 +4,13 @@ Articles about us ================= .. note:: - If you've writeen article about PlatformIO and would like it listed on + If you've written article about PlatformIO and would like it listed on this page, `please edit this page `_. Here are recent articles about PlatformIO: +* May 11, 2015 - **IT Hare** - `From Web Developer to Embedded One: Interview with Ivan Kravets, The Guy Behind PlatformIO. Part II `_ +* May 4, 2015 - **IT Hare** - `From Web Developer to Embedded One: Interview with Ivan Kravets, The Guy Behind PlatformIO. Part I `_ * April 17, 2015 - **Michael Ball** - `PlatformIO - A Cross-Platform Code Builder and Missing Library Manager `_ * March 23, 2015 - **Atmel** - `Cross-board and cross-vendor embedded development with PlatformIO `_ * March 22, 2015 - **Mark VandeWettering** - `Discovered a new tool for embedded development: PlatformIO `_ From f414b91b084b66c1f98786a1b0e51e76da269d5f Mon Sep 17 00:00:00 2001 From: Lutino Date: Sun, 17 May 2015 16:27:09 -0700 Subject: [PATCH 54/78] Update qtcreator.rst Edited the file to include an additional step for the initalization of the platformio project --- docs/ide/qtcreator.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/ide/qtcreator.rst b/docs/ide/qtcreator.rst index 19705b5a..7a7e6269 100644 --- a/docs/ide/qtcreator.rst +++ b/docs/ide/qtcreator.rst @@ -10,7 +10,7 @@ This software can be used with: * all availalbe :ref:`platforms` * all availalbe :ref:`frameworks` -Refer to the `Sublime Text Documentation `_ +Refer to the `Qt-creator Manual `_ page for more detailed information. .. contents:: @@ -33,7 +33,7 @@ Secondly, we need to configure project with PlatformIO source code builder (clic .. image:: ../_static/ide-platformio-qtcreator-3.png -Thirdly, we need to add directories with header files using project file. Please fill this file with the next contents: +Thirdly, change project file by adding path to directories with header files. Please edit project file to match the following contents: .. code-block:: none @@ -55,8 +55,8 @@ First program in Qt Creator ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Simple "Blink" project will consist from two files: - -1. Main "C" source file named ``main.c`` must be located in the ``src`` directory. +1. In the console, navigate to the root of your project folder and initialize platformio project with ``platformio init`` +2. The main "C" source file named ``main.c`` must be located in the ``src`` directory. Let's create new text file named ``main.c`` using ``Menu: New File or Project → General → Text File``: .. image:: ../_static/ide-platformio-qtcreator-5.png @@ -81,11 +81,11 @@ Copy the source code which is described below to file ``main.c``. delay(1000); // wait for a second } -2. Project Configuration File named ``platformio.ini`` must be located in the project root directory. +3. Locate the project configuration file named ``platformio.ini`` at the root of the project directory and open it. .. image:: ../_static/ide-platformio-qtcreator-6.png -Copy the source code which is described below to it. +Edit the content to match the code described below. .. code-block:: none From 82a0ec0d2ba080c98520272ba0659a37846c1631 Mon Sep 17 00:00:00 2001 From: Valeriy Koval Date: Mon, 18 May 2015 16:43:33 +0300 Subject: [PATCH 55/78] Add Armstrap Eagle boards. --- platformio/boards/ststm32.json | 60 ++++++++++++++++++++ platformio/builder/scripts/frameworks/spl.py | 2 + 2 files changed, 62 insertions(+) diff --git a/platformio/boards/ststm32.json b/platformio/boards/ststm32.json index 63c188ee..a5fd3475 100644 --- a/platformio/boards/ststm32.json +++ b/platformio/boards/ststm32.json @@ -330,5 +330,65 @@ }, "url": "https://developer.mbed.org/platforms/ST-Nucleo-L152RE/", "vendor": "ST" + }, + "armstrap_eagle512": { + "build": { + "core": "stm32", + "extra_flags": "-DSTM32F40_41xxx", + "f_cpu": "168000000L", + "ldscript": "armstrap_eagle_512.ld", + "cpu": "cortex-m4", + "mcu": "stm32f407vet6", + "variant": "stm32f4" + }, + "frameworks": ["cmsis", "spl"], + "name": "Armstrap Eagle 512", + "platform": "ststm32", + "upload": { + "maximum_ram_size": 196608, + "maximum_size": 524288 + }, + "url": "http://docs.armstrap.org/en/latest/hardware-overview.html", + "vendor": "Armstrap" + }, + "armstrap_eagle1024": { + "build": { + "core": "stm32", + "extra_flags": "-DSTM32F40_41xxx", + "f_cpu": "168000000L", + "ldscript": "armstrap_eagle_1024.ld", + "cpu": "cortex-m4", + "mcu": "stm32f417vgt6", + "variant": "stm32f4" + }, + "frameworks": ["cmsis", "spl"], + "name": "Armstrap Eagle 1024", + "platform": "ststm32", + "upload": { + "maximum_ram_size": 196608, + "maximum_size": 1048576 + }, + "url": "http://docs.armstrap.org/en/latest/hardware-overview.html", + "vendor": "Armstrap" + }, + "armstrap_eagle2048": { + "build": { + "core": "stm32", + "extra_flags": "-DSTM32F427_437xx", + "f_cpu": "168000000L", + "ldscript": "armstrap_eagle_2048.ld", + "cpu": "cortex-m4", + "mcu": "stm32f427vit6", + "variant": "stm32f4" + }, + "frameworks": ["cmsis", "spl"], + "name": "Armstrap Eagle 2048", + "platform": "ststm32", + "upload": { + "maximum_ram_size": 262144, + "maximum_size": 2091752 + }, + "url": "http://docs.armstrap.org/en/latest/hardware-overview.html", + "vendor": "Armstrap" } } \ No newline at end of file diff --git a/platformio/builder/scripts/frameworks/spl.py b/platformio/builder/scripts/frameworks/spl.py index fb7abbbb..229e803b 100644 --- a/platformio/builder/scripts/frameworks/spl.py +++ b/platformio/builder/scripts/frameworks/spl.py @@ -55,6 +55,8 @@ extra_flags = env.get("BOARD_OPTIONS", {}).get("build", {}).get("extra_flags") ignore_files = [] if "STM32F40_41xxx" in extra_flags: ignore_files += ["stm32f4xx_fmc.c"] +if "STM32F427_437xx" in extra_flags: + ignore_files += ["stm32f4xx_fsmc.c"] elif "STM32F303xC" in extra_flags: ignore_files += ["stm32f30x_hrtim.c"] elif "STM32L1XX_MD" in extra_flags: From 5d5472713933fdcd2e3eef3e2650f5dae815ca00 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 18 May 2015 17:13:58 +0300 Subject: [PATCH 56/78] Update docs with the new boards // Resolve #204 --- docs/frameworks/cmsis.rst | 34 ++++++++++++++++++++++++++++++++++ docs/frameworks/mbed.rst | 20 ++++++++++++++++++++ docs/frameworks/spl.rst | 34 ++++++++++++++++++++++++++++++++++ docs/platforms/ststm32.rst | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+) diff --git a/docs/frameworks/cmsis.rst b/docs/frameworks/cmsis.rst index 7824155e..9f23425c 100644 --- a/docs/frameworks/cmsis.rst +++ b/docs/frameworks/cmsis.rst @@ -27,6 +27,40 @@ Boards `PlatformIO Boards Explorer `_ * For more detailed ``board`` information please scroll tables below by horizontal. +Armstrap +~~~~~~~~ + +.. list-table:: + :header-rows: 1 + + * - Type ``board`` + - Name + - Microcontroller + - Frequency + - Flash + - RAM + + * - ``armstrap_eagle1024`` + - `Armstrap Eagle 1024 `_ + - STM32F417VGT6 + - 168 MHz + - 1024 Kb + - 192 Kb + + * - ``armstrap_eagle2048`` + - `Armstrap Eagle 2048 `_ + - STM32F427VIT6 + - 168 MHz + - 2048 Kb + - 256 Kb + + * - ``armstrap_eagle512`` + - `Armstrap Eagle 512 `_ + - STM32F407VET6 + - 168 MHz + - 512 Kb + - 192 Kb + ST ~~ diff --git a/docs/frameworks/mbed.rst b/docs/frameworks/mbed.rst index 3bea58b5..1c909d13 100644 --- a/docs/frameworks/mbed.rst +++ b/docs/frameworks/mbed.rst @@ -527,6 +527,26 @@ Switch Science - 32 Kb - 4 Kb +Teensy +~~~~~~ + +.. list-table:: + :header-rows: 1 + + * - Type ``board`` + - Name + - Microcontroller + - Frequency + - Flash + - RAM + + * - ``teensy31`` + - `Teensy 3.1 `_ + - MK20DX256 + - 72 MHz + - 256 Kb + - 64 Kb + u-blox ~~~~~~ diff --git a/docs/frameworks/spl.rst b/docs/frameworks/spl.rst index 64d91979..1d131c34 100644 --- a/docs/frameworks/spl.rst +++ b/docs/frameworks/spl.rst @@ -27,6 +27,40 @@ Boards `PlatformIO Boards Explorer `_ * For more detailed ``board`` information please scroll tables below by horizontal. +Armstrap +~~~~~~~~ + +.. list-table:: + :header-rows: 1 + + * - Type ``board`` + - Name + - Microcontroller + - Frequency + - Flash + - RAM + + * - ``armstrap_eagle1024`` + - `Armstrap Eagle 1024 `_ + - STM32F417VGT6 + - 168 MHz + - 1024 Kb + - 192 Kb + + * - ``armstrap_eagle2048`` + - `Armstrap Eagle 2048 `_ + - STM32F427VIT6 + - 168 MHz + - 2048 Kb + - 256 Kb + + * - ``armstrap_eagle512`` + - `Armstrap Eagle 512 `_ + - STM32F407VET6 + - 168 MHz + - 512 Kb + - 192 Kb + ST ~~ diff --git a/docs/platforms/ststm32.rst b/docs/platforms/ststm32.rst index 4f2c5e40..3aaec0d5 100644 --- a/docs/platforms/ststm32.rst +++ b/docs/platforms/ststm32.rst @@ -73,6 +73,40 @@ Boards * For more detailed ``board`` information please scroll tables below by horizontal. +Armstrap +~~~~~~~~ + +.. list-table:: + :header-rows: 1 + + * - Type ``board`` + - Name + - Microcontroller + - Frequency + - Flash + - RAM + + * - ``armstrap_eagle1024`` + - `Armstrap Eagle 1024 `_ + - STM32F417VGT6 + - 168 MHz + - 1024 Kb + - 192 Kb + + * - ``armstrap_eagle2048`` + - `Armstrap Eagle 2048 `_ + - STM32F427VIT6 + - 168 MHz + - 2048 Kb + - 256 Kb + + * - ``armstrap_eagle512`` + - `Armstrap Eagle 512 `_ + - STM32F407VET6 + - 168 MHz + - 512 Kb + - 192 Kb + ST ~~ From dd4fa733e1b640ec9cb8878c457b44ab4aa6f7d5 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 18 May 2015 18:10:24 +0300 Subject: [PATCH 57/78] Fix WindowsError for CI --- platformio/commands/ci.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platformio/commands/ci.py b/platformio/commands/ci.py index a9f24be5..64355bdd 100644 --- a/platformio/commands/ci.py +++ b/platformio/commands/ci.py @@ -90,7 +90,10 @@ def cli(ctx, src, lib, exclude, board, # pylint: disable=R0913 ctx.invoke(cmd_run, project_dir=build_dir, verbose=verbose) finally: if not keep_build_dir: - rmtree(build_dir) + try: + rmtree(build_dir) + except WindowsError: + pass def _clean_dir(dirpath): From fd39a88b26b200cd9b0f51d389c353d4fb7e4e19 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 18 May 2015 18:12:16 +0300 Subject: [PATCH 58/78] Ignore errors when deleting CI tmp folder --- platformio/commands/ci.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/platformio/commands/ci.py b/platformio/commands/ci.py index 64355bdd..0532f2d1 100644 --- a/platformio/commands/ci.py +++ b/platformio/commands/ci.py @@ -90,10 +90,7 @@ def cli(ctx, src, lib, exclude, board, # pylint: disable=R0913 ctx.invoke(cmd_run, project_dir=build_dir, verbose=verbose) finally: if not keep_build_dir: - try: - rmtree(build_dir) - except WindowsError: - pass + rmtree(build_dir, ignore_errors=True) def _clean_dir(dirpath): From 92bfa8f36db0a92b3e25f95b4424679bb0e7a668 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 18 May 2015 18:26:52 +0300 Subject: [PATCH 59/78] Fix removing locked files under windows --- platformio/commands/ci.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/platformio/commands/ci.py b/platformio/commands/ci.py index 0532f2d1..1ffdc97d 100644 --- a/platformio/commands/ci.py +++ b/platformio/commands/ci.py @@ -1,8 +1,9 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. +import stat from glob import glob -from os import environ, makedirs, remove +from os import chmod, environ, makedirs, remove from os.path import abspath, basename, isdir, isfile, join from shutil import copyfile, copytree, rmtree from tempfile import mkdtemp @@ -90,7 +91,10 @@ def cli(ctx, src, lib, exclude, board, # pylint: disable=R0913 ctx.invoke(cmd_run, project_dir=build_dir, verbose=verbose) finally: if not keep_build_dir: - rmtree(build_dir, ignore_errors=True) + rmtree( + build_dir, onerror=lambda action, name, exc: + (chmod(name, stat.S_IWRITE), remove(name)) + ) def _clean_dir(dirpath): From 49c389ce708bdbc47c2c4c139761a44d4a60f3b5 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 18 May 2015 18:36:13 +0300 Subject: [PATCH 60/78] Add Espressif to list of supported platforms --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 692057d7..830eb04b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,7 @@ PlatformIO: A cross-platform code builder and the missing library manager ========================================================================= -*Atmel AVR & SAM, Freescale Kinetis, Nordic nRF51, NXP LPC, ST STM32, +*Atmel AVR & SAM, Espressif, Freescale Kinetis, Nordic nRF51, NXP LPC, ST STM32, TI MSP430 & Tiva, Teensy, Arduino, mbed, libOpenCM3, etc.* .. image:: _static/platformio-logo.png From afdc28df804bfd710e50a79201d05817cabeaa55 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 18 May 2015 19:22:42 +0300 Subject: [PATCH 61/78] Disable progress bar for Continuous Integration Systems --- platformio/downloader.py | 19 ++++++++++++------- platformio/unpacker.py | 14 ++++++++++---- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/platformio/downloader.py b/platformio/downloader.py index a3aa37f9..fbc9744a 100644 --- a/platformio/downloader.py +++ b/platformio/downloader.py @@ -3,11 +3,11 @@ from email.utils import parsedate_tz from math import ceil -from os.path import getsize, join +from os.path import environ, getsize, join from time import mktime -from click import progressbar -from requests import get +import click +import requests from platformio import util from platformio.exception import (FDSHASumMismatch, FDSizeMismatch, @@ -27,8 +27,8 @@ class FileDownloader(object): self.set_destination(join(dest_dir, self._fname)) self._progressbar = None - self._request = get(url, stream=True, - headers=util.get_request_defheaders()) + self._request = requests.get(url, stream=True, + headers=util.get_request_defheaders()) if self._request.status_code != 200: raise FDUnrecognizedStatusCode(self._request.status_code, url) @@ -49,9 +49,14 @@ class FileDownloader(object): f = open(self._destination, "wb") chunks = int(ceil(self.get_size() / float(self.CHUNK_SIZE))) - with progressbar(length=chunks, label="Downloading") as pb: - for _ in pb: + if environ.get("CI") == "true": + click.echo("Downloading...") + for _ in range(0, chunks): f.write(next(itercontent)) + else: + with click.progressbar(length=chunks, label="Downloading") as pb: + for _ in pb: + f.write(next(itercontent)) f.close() self._request.close() diff --git a/platformio/unpacker.py b/platformio/unpacker.py index f80081dc..34cb3594 100644 --- a/platformio/unpacker.py +++ b/platformio/unpacker.py @@ -1,13 +1,13 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -from os import chmod +from os import chmod, environ from os.path import join, splitext from tarfile import open as tarfile_open from time import mktime from zipfile import ZipFile -from click import progressbar +import click from platformio.exception import UnsupportedArchiveType from platformio.util import change_filemtime @@ -81,7 +81,13 @@ class FileUnpacker(object): raise UnsupportedArchiveType(archpath) def start(self): - with progressbar(self._unpacker.get_items(), label="Unpacking") as pb: - for item in pb: + if environ.get("CI") == "true": + click.echo("Unpacking...") + for item in self._unpacker.get_items(): self._unpacker.extract_item(item, self._dest_dir) + else: + with click.progressbar(self._unpacker.get_items(), + label="Unpacking") as pb: + for item in pb: + self._unpacker.extract_item(item, self._dest_dir) return True From a1cc5f4282c2d51ffc541c59f755d1190f8a160d Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Mon, 18 May 2015 19:28:35 +0300 Subject: [PATCH 62/78] Fix invalid import --- platformio/downloader.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platformio/downloader.py b/platformio/downloader.py index fbc9744a..7520446d 100644 --- a/platformio/downloader.py +++ b/platformio/downloader.py @@ -3,7 +3,8 @@ from email.utils import parsedate_tz from math import ceil -from os.path import environ, getsize, join +from os import environ +from os.path import getsize, join from time import mktime import click From 77838f2938e52b0c856b79c84bf42d1125893c88 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Tue, 19 May 2015 14:37:47 +0300 Subject: [PATCH 63/78] Use default verbosity level for CI command --- docs/userguide/cmd_ci.rst | 2 -- platformio/__init__.py | 2 +- platformio/commands/ci.py | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/userguide/cmd_ci.rst b/docs/userguide/cmd_ci.rst index 3c2842d4..1d381ecd 100644 --- a/docs/userguide/cmd_ci.rst +++ b/docs/userguide/cmd_ci.rst @@ -112,8 +112,6 @@ Buid project using pre-configured :ref:`projectconf`. Shows details about the results of processing environments. More details :option:`platformio run --verbose` -By default, verbosity level is set to 1 (only errors will be printed). - Examples -------- diff --git a/platformio/__init__.py b/platformio/__init__.py index b2569f3c..357f664d 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -VERSION = (2, 0, "0a1") +VERSION = (2, 0, "0a2") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" diff --git a/platformio/commands/ci.py b/platformio/commands/ci.py index 1ffdc97d..e4b48e07 100644 --- a/platformio/commands/ci.py +++ b/platformio/commands/ci.py @@ -53,7 +53,7 @@ def validate_boards(ctx, param, value): # pylint: disable=W0613 @click.option("--project-conf", type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True)) -@click.option("--verbose", "-v", count=True, default=1) +@click.option("--verbose", "-v", count=True, default=3) @click.pass_context def cli(ctx, src, lib, exclude, board, # pylint: disable=R0913 build_dir, keep_build_dir, project_conf, verbose): From 1a9cfc3311a25e12a6ef487d657d4d256322c3a9 Mon Sep 17 00:00:00 2001 From: Valeriy Koval Date: Tue, 19 May 2015 20:29:03 +0300 Subject: [PATCH 64/78] Add platformio ci command examples with different CI systems. --- docs/userguide/cmd_ci.rst | 211 +++++++++++++++++++++++++++++++++++++- 1 file changed, 209 insertions(+), 2 deletions(-) diff --git a/docs/userguide/cmd_ci.rst b/docs/userguide/cmd_ci.rst index 3c2842d4..af5caabb 100644 --- a/docs/userguide/cmd_ci.rst +++ b/docs/userguide/cmd_ci.rst @@ -117,7 +117,7 @@ By default, verbosity level is set to 1 (only errors will be printed). Examples -------- -1. Integration `Travis.CI `_ for GitHub +1. Integration `Travis.CI `_, `Shippable `_ for GitHub `USB_Host_Shield_2.0 `_ project. The ``.travis.yml`` configuration file: @@ -133,6 +133,213 @@ Examples install: - python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + - pip install --egg http://sourceforge.net/projects/scons/files/latest/download + - wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip + - unzip /tmp/spi4teensy3.zip -d /tmp script: - - platformio ci --lib="." --board=uno --board=teensy31 --board=due + - platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due + +2. Integration `CircleCI `_ for GitHub + `USB_Host_Shield_2.0 `_ + project. The ``circle.yml`` configuration file: + +.. code-block:: yaml + + machine: + + environment: + PLATFORMIO_CI_SRC: examples/Bluetooth/PS3SPP/PS3SPP.ino + PLATFORMIO_CI_SRC: examples/pl2303/pl2303_gps/pl2303_gps.ino + + dependencies: + pre: + - sudo apt-get install python2.7-dev + - sudo python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + - sudo pip install --egg http://sourceforge.net/projects/scons/files/latest/download + - wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip + - unzip /tmp/spi4teensy3.zip -d /tmp + test: + override: + - platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due + +3. Integration `AppVeyor CI `_ for GitHub + `USB_Host_Shield_2.0 `_ + project. The ``appveyor.yml`` configuration file: + +.. code-block:: yaml + + build: off + environment: + global: + WITH_COMPILER: "cmd /E:ON /V:ON /C .\\scripts\\appveyor\\run_with_compiler.cmd" + matrix: + - PLATFORMIO_CI_SRC: "examples\\Bluetooth\\PS3SPP\\PS3SPP.ino" + PLATFORMIO_CI_SRC: "examples\\pl2303\\pl2303_gps\\pl2303_gps.ino" + WINDOWS_SDK_VERSION: "v7.0" + PYTHON_HOME: "C:\\Python27-x64" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "64" + init: + - ps: "ls C:\\Python*" + install: + - "git submodule update --init --recursive" + - "powershell scripts\\appveyor\\install.ps1" + before_test: + - cmd: SET PATH=%PATH%;%PYTHON_HOME%;%PYTHON_HOME%\Scripts + - cmd: git clone https://github.com/xxxajk/spi4teensy3.git c:\spi4teensy + test_script: + - "%PYTHON_HOME%\\Scripts\\pip --version" + - '%WITH_COMPILER% %PYTHON_HOME%\\Scripts\\platformio ci --lib="." --lib="c:\spi4teensy" --board=uno --board=teensy31 --board=due' + +The ``run_with_compiler.cmd`` script file: + +.. code-block:: none + + @ECHO OFF + + SET COMMAND_TO_RUN=%* + SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows + + SET MAJOR_PYTHON_VERSION="%PYTHON_VERSION:~0,1%" + IF %MAJOR_PYTHON_VERSION% == "2" ( + SET WINDOWS_SDK_VERSION="v7.0" + ) ELSE IF %MAJOR_PYTHON_VERSION% == "3" ( + SET WINDOWS_SDK_VERSION="v7.1" + ) ELSE ( + ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" + EXIT 1 + ) + + IF "%PYTHON_ARCH%"=="64" ( + ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture + SET DISTUTILS_USE_SDK=1 + SET MSSdk=1 + "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% + "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release + ECHO Executing: %COMMAND_TO_RUN% + call %COMMAND_TO_RUN% || EXIT 1 + ) ELSE ( + ECHO Using default MSVC build environment for 32 bit architecture + ECHO Executing: %COMMAND_TO_RUN% + call %COMMAND_TO_RUN% || EXIT 1 + ) + + +The ``install.ps1`` script file: + +.. code-block:: none + + $BASE_URL = "https://www.python.org/ftp/python/" + $GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" + $GET_PIP_PATH = "C:\get-pip.py" + + + function DownloadPython ($python_version, $platform_suffix) { + $webclient = New-Object System.Net.WebClient + $filename = "python-" + $python_version + $platform_suffix + ".msi" + $url = $BASE_URL + $python_version + "/" + $filename + + $basedir = $pwd.Path + "\" + $filepath = $basedir + $filename + if (Test-Path $filename) { + Write-Host "Reusing" $filepath + return $filepath + } + + # Download and retry up to 5 times in case of network transient errors. + Write-Host "Downloading" $filename "from" $url + $retry_attempts = 3 + for($i=0; $i -lt $retry_attempts; $i++){ + try { + $webclient.DownloadFile($url, $filepath) + break + } + Catch [Exception]{ + Start-Sleep 1 + } + } + Write-Host "File saved at" $filepath + return $filepath + } + + + function InstallPython ($python_version, $architecture, $python_home) { + Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home + if (Test-Path $python_home) { + Write-Host $python_home "already exists, skipping." + return $false + } + if ($architecture -eq "32") { + $platform_suffix = "" + } else { + $platform_suffix = ".amd64" + } + $filepath = DownloadPython $python_version $platform_suffix + Write-Host "Installing" $filepath "to" $python_home + $args = "/qn /i $filepath TARGETDIR=$python_home" + Write-Host "msiexec.exe" $args + Start-Process -FilePath "msiexec.exe" -ArgumentList $args -Wait -Passthru + Write-Host "Python $python_version ($architecture) installation complete" + return $true + } + + + function InstallPip ($python_home) { + $pip_path = $python_home + "/Scripts/pip.exe" + $python_path = $python_home + "/python.exe" + if (-not(Test-Path $pip_path)) { + Write-Host "Installing pip..." + $webclient = New-Object System.Net.WebClient + $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) + Write-Host "Executing:" $python_path $GET_PIP_PATH + Start-Process -FilePath "$python_path" -ArgumentList "$GET_PIP_PATH" -Wait -Passthru + } else { + Write-Host "pip already installed." + } + } + + function InstallPackage ($python_home, $pkg) { + $pip_path = $python_home + "/Scripts/pip.exe" + & $pip_path install $pkg + } + + function InstallScons ($python_home) { + Write-Host "Start installing Scons" + $pip_path = $python_home + "/Scripts/pip.exe" + & $pip_path install --egg "http://sourceforge.net/projects/scons/files/latest/download" + Write-Host "Scons installed" + } + + function main () { + InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON_HOME + InstallPip $env:PYTHON_HOME + InstallPackage $env:PYTHON_HOME setuptools + InstallScons $env:PYTHON_HOME + InstallPackage $env:PYTHON_HOME "https://github.com/platformio/platformio/archive/develop.zip" + } + + main + +3. Integration `Drone CI `_ for GitHub + `USB_Host_Shield_2.0 `_ + project. The project settings: + +`Environment Variables`: + +.. code-block:: none + + PLATFORMIO_CI_SRC=examples/Bluetooth/PS3SPP/PS3SPP.ino + PLATFORMIO_CI_SRC=examples/pl2303/pl2303_gps/pl2303_gps.ino + +`Commands`: + +.. code-block:: none + + pip install --egg http://sourceforge.net/projects/scons/files/latest/download + python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip + unzip /tmp/spi4teensy3.zip -d /tmp + platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due + + From 1f2b31f1746bf448d06b8b59491f196a1bd3a454 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Wed, 20 May 2015 15:57:49 +0300 Subject: [PATCH 65/78] Create separate Continuous Integration page --- docs/ci/index.rst | 20 +++++++++++ docs/ci/travis.rst | 72 +++++++++++++++++++++++++++++++++++++++ docs/index.rst | 1 + docs/userguide/cmd_ci.rst | 12 ++----- 4 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 docs/ci/index.rst create mode 100644 docs/ci/travis.rst diff --git a/docs/ci/index.rst b/docs/ci/index.rst new file mode 100644 index 00000000..d619bc24 --- /dev/null +++ b/docs/ci/index.rst @@ -0,0 +1,20 @@ +.. _ci: + +Continuous Integration +====================== + +`Continuous Integration (CI, wiki) `_ +is the practice, in software engineering, of merging all developer working +copies with a shared mainline several times a day. + +:ref:`cmd_ci` command is intended to be used in combination with the build +servers and the popular +`Continuous Integration Software `_. + +By integrating regularly, you can detect errors quickly, and locate them more +easily. + +.. toctree:: + :maxdepth: 2 + + travis diff --git a/docs/ci/travis.rst b/docs/ci/travis.rst new file mode 100644 index 00000000..4c5e9e34 --- /dev/null +++ b/docs/ci/travis.rst @@ -0,0 +1,72 @@ +.. _ci_travis: + +Travis CI +========= + +`Travis CI `_ is an open-source hosted, +distributed continuous integration service used to build and test projects +hosted at `GitHub `_. + +Travis CI is configured by adding a file named ``.travis.yml``, which is a +`YAML `_ format text file, to the root +directory of the GitHub repository. + +Travis CI automatically detects when a commit has been made and pushed to a +GitHub repository that is using Travis CI, and each time this happens, it will +try to build the project using :ref:`cmd_ci` command. This includes commits to +all branches, not just to the master branch. Travis CI will also build and run +pull requests. When that process has completed, it will notify a developer in +the way it has been configured to do so — for example, by sending an email +containing the build results (showing success or failure), or by posting a +message on an IRC channel. It can be configured to build project on a range of +different :ref:`platforms`. + +Integration +----------- + +Please put ``.travis.yml`` to the root directory of the GitHub repository. + +.. code-block:: yaml + + language: python + python: + - "2.7" + + env: + - PLATFORMIO_CI_SRC=path/to/source/file.c + - PLATFORMIO_CI_SRC=path/to/source/file.ino + - PLATFORMIO_CI_SRC=path/to/source/directory + + install: + - python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + + script: + - platformio ci --board=TYPE_1 --board=TYPE_2 --board=TYPE_N + + +For more details as for PlatformIO build process please look into :ref:`cmd_ci` +command. + +Examples +-------- + +1. Integration for `USB_Host_Shield_2.0 `_ + project. The ``.travis.yml`` configuration file: + +.. code-block:: yaml + + language: python + python: + - "2.7" + + env: + - PLATFORMIO_CI_SRC=examples/Bluetooth/PS3SPP/PS3SPP.ino + - PLATFORMIO_CI_SRC=examples/pl2303/pl2303_gps/pl2303_gps.ino + + install: + - python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + - wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip + - unzip /tmp/spi4teensy3.zip -d /tmp + + script: + - platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due diff --git a/docs/index.rst b/docs/index.rst index 830eb04b..2c580297 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -63,6 +63,7 @@ Contents frameworks/index librarymanager/index userguide/index + ci/index ide articles FAQ diff --git a/docs/userguide/cmd_ci.rst b/docs/userguide/cmd_ci.rst index bc120e4a..021ebc15 100644 --- a/docs/userguide/cmd_ci.rst +++ b/docs/userguide/cmd_ci.rst @@ -16,10 +16,6 @@ Usage Description ----------- -`Continuous integration (CI, wiki) `_ -is the practice, in software engineering, of merging all developer working -copies with a shared mainline several times a day. - :ref:`cmd_ci` command is conceived of as "hot key" for building project with arbitrary source code structure. In a nutshell, using ``SRC`` and :option:`platformio ci --lib` contents PlatformIO initialises via @@ -28,12 +24,8 @@ with the build environments (using :option:`platformio ci --board` or :option:`platformio ci --project-conf`) and processes them via :ref:`cmd_run` command. -:ref:`cmd_ci` command is intended to be used in combination with the build -servers and the popular -`Continuous Integration Software `_. - -By integrating regularly, you can detect errors quickly, and locate them more -easily. +For more details as for integration with the popular Continuous Integration +Systems please follow to :ref:`ci` page. .. note:: :ref:`cmd_ci` command accepts **multiple** ``SRC`` arguments, From e15efa7e08fdadfc613f5dd5353447b1e9de4a81 Mon Sep 17 00:00:00 2001 From: Valeriy Koval Date: Thu, 21 May 2015 16:43:09 +0300 Subject: [PATCH 66/78] Add CI services info and integration examples. --- .../droneci-platformio-integration-1.png | Bin 0 -> 22188 bytes .../droneci-platformio-integration-2.png | Bin 0 -> 41980 bytes docs/ci/appveyor.rst | 201 ++++++++++++++++++ docs/ci/circleci.rst | 73 +++++++ docs/ci/drone.rst | 70 ++++++ docs/ci/index.rst | 4 + docs/ci/shippable.rst | 76 +++++++ 7 files changed, 424 insertions(+) create mode 100644 docs/_static/droneci-platformio-integration-1.png create mode 100644 docs/_static/droneci-platformio-integration-2.png create mode 100644 docs/ci/appveyor.rst create mode 100644 docs/ci/circleci.rst create mode 100644 docs/ci/drone.rst create mode 100644 docs/ci/shippable.rst diff --git a/docs/_static/droneci-platformio-integration-1.png b/docs/_static/droneci-platformio-integration-1.png new file mode 100644 index 0000000000000000000000000000000000000000..303e30f66a2b367e9658c9cff85cfaec45c188b7 GIT binary patch literal 22188 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

YLM2 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 literal 0 HcmV?d00001 diff --git a/docs/_static/droneci-platformio-integration-2.png b/docs/_static/droneci-platformio-integration-2.png new file mode 100644 index 0000000000000000000000000000000000000000..9b76e479b717666a7c94aac5933669b719b64d90 GIT binary patch literal 41980 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%0AO)^dAa!OO+2Qm!7+3Q)K}G95 zr=s!>n+Tw)VHy4_evuiP^E-4F@=JL*-mR>U#nv5ZRiW$Oo`XbZrwh6wwy>`?7 z&C}Ga*_K_Aj1*aNaoaihduNyLs%H!ATe$JWv+5snl9}Q;dbJwb!ke>V0xhQ%9(bu3$?@cf@wxwZ*Gitec2Ctt zM!m(!IJLAjWUue{JMBr)Rr}meILGdPxi};6v$Esj_IKq=E*HtNU012&(^HN){)%g^ z{i-Rq+}$kJJr!J)84eDCA|vA)_wqs}E5wASO2wN)5?Sb7ee?4z7b+hn-pqZs=$>@W z)3#leKXpKbxb`yl%RAC`8%x}s`!@H=v!@R=H=X_d!ScAo??1fCr5h5Zwx%vT+?=7Z zzeD^~SGvxhc!%q{t*6tkSR~4&ehCdb6!c6WakAj-Z>K(eU5(ocQO{w`a12pq9Jwg1{CTHU_A z#LQ>0iAUCFPVSU*LW(Aw!L#SR{7`?)%+NTd^U98wE9T5o+3g;}Q$5r9Lfzq(4GZs{ z;4nKp)6-`6!~a~xmosN?*joGWQv0SyTKBxdYa7K_?zJthD$s%Ya`K1z{s5)w=>id6 z=PRS7FULuovo2B8Fi3Kr*dO}hK&+!RMpS3~_q zjoMY2UAHwKSXUoA>6@wa{j{!dz_D%BT2nJ#&DHugN9==LY5ck4c8fk*-oJA?-gu_u ztE?rbSu?#vianmpJb!6xXC0gVN%uKbTZKOD-Q^Np|AX&vh?kDW2Fb$1>u0hXoxlH~ zUhw{=AjO!%YdfYz9k}hg$Oha%xE1XFC}2mB#47H?EKiNED86gxK3}Bi@};pLO=!K| z;s2rweW&N%+Em(kXes~AXK&ZeO>&q&U+T@0XFPjKr^daxo@-&tldkq*mCB#{%5zTK zQk95)d2_~A!?o*9{HQr?5z_AV#v`a(_tVmq^%)ByzqiZym+_=IFXPN!zI1NUg9V$H z?_GKE_oi8wJ3KBJQmln~z7XoUZ-j;~mAvcb1tX>XzS;-17e1S99&# zE+1~>J?L`M_6LoMuJv7Ycb#Bv+6HHv)?0U`%1M9FQl0O))x5K5_R$obC_jnS3-_sS zF|XJZb}!>wmffHGR=TQ9NmA=}c+Hm^o{29!xlTE6ZhqbcU}= z)Y;WcpQm*i36*fRrXRhP2&$V+ume%_J$o7$*lkRd>#^&}?_SI}raSUKppL;TX8vXy%zFlv6Mo#a; zplhodra7N>yyGJ>~IklQ^$9?b1*o@QO2vuWFndc(_KtEVJ> z0{2_?{|_$l^ZNZOv-kC?8<)a+R^D6YviSev#F%hxk@~4}UEw|DRr~+#3tUmMS@g|j z*YAIn3jXvI^K|APp9CI8HoaUV)neTxGMDMslMO*lA2)6MpOkiSzk8` zjf%?qv+s|GsT&(OxPpy!^&LyNTuNnD?OXKGN9~WBR?{=ii{EWU`*V4ECQe(w>C5VL zSKal~+P!DaJN9gy-eX%G)opjA42-X&?yCt1`so!pdy@6JgI71bo0TgX5Gj=V_t7uk zJ%Rc=-mH}Y2k~+f-F}VMZ&e9(yQlm&Q*xTi_j6_3&)X|G^MejN_v$#bobS1n!8Eby z(^FDCEFNrlWa8&m)po@+YthPJDZwWvHa*|*kc0oi#PFG(6>72y*7PXQ)V-^&hCA*oi}ZB^IYGBMNyoQ*RAK=TDrYAr}}n7sLd7CFXelS-KOsU z0qLPH&9p3KtKA`B{p6>My2}2bnRZW)S4sU9|5eQN^-y}(SG zcF*x$lXtQ6X-~VGqjK?zt{>+vr@OCsp!%={AE`kePt?m0t2 zvHI@GHJc>AdVkorc9l^2iNdMJJY;hU43EBE{&I80*3v7!vg^(tJGZ4mw2WcDXY9=j z_ne)V?X>u;92+hCC+_*iyu<$sUZy_gzWL1gp!HJO-LARaJrO7Wicd;s%Wx6)J-U92 zdA0mu)^FuSu^atWp7`FYQ8{qUZrb*Vr@8XwA6$vCPuuGH0sezE!-g7SZo+Y-3!_QuNn1%~NM?(pQg4 zwYhq1;E61+nB?Vr*KFNnXZcwuUN3p5{_pe47v(8(jVa=b&Mk=*KgY9$E!p=)jUJ0@ z?$_#lD-}R-(I#k@ze9%QJXgjR5%ubc8`vVw>B!0U2&ZaI{ot>9Ze#8RkL1EjMm;a~ zb!mkNf*P5DKJy+`golRx_*!^zQh`yfzPZ_Mzh$n;S^rJ{D=$s|6cT)PW!xm6#S8lG z{^|Sa5}v;*((mfx8J1od@d2rerd3}yDc_P&a>P0Bc*^kuAuIm4wQl~qz^T(ic;e-( zr$#jzLZKl`)#fbHFMhY~^N|xVuYdOWdGFpU+~bz(`*O#m87Z@)l0R-Vo82GA^iN_- z_kAG~hKNhWDc(YY0s%QAG%QLox!AK z;O?(xe`V$Q(}#mox+iZdK6^tvM`C3&@8uP$+{{v)y3fmQaHk$M7W=2)F)i-Uwr!V8 zf^(}Z$|j$E688C((|%S5moDeH8CNe~xhAG_Av?nM3~%T5=c-b-gOlU9Hved=RSK~* zc^%>GMS9ixKDl@@dCw@45ditrF7o5De(wxQsJB z`^K|o?I$S~n!GokJ^eMSIyC?~TfJ!7=bhIk&-5zgJ=;@K+WUlUZ{1Ak3}3TTziV&h zo@J_C-g5asN7l09h>dT|$^zBDs~OmN?_9X#y}RP6{qpeKuq>JIlr7y{Jj&a=6vMY&a;;QYzKZ4IA&wLCjutBynJitZ zGDUZ4lML56-SAYwi^UgCtNghiDL4N_rF`#`FGsGaw4VIheR4u;!M3*C+YMWd?o@vD zHjUe^X(2bI6*@aEv~`Yu@40#N@}w9%9ClUhx_T!|>!|s(#d}+xv4h6Mm;NrezE!8? zmO<6l%3l_1BFchyg;~DPd0J5>B>VcvTa&kQrj-{Q{(gETla6q}?U=dmtxI-2Y2Lug zvL{7yS9kiH#|PPdPJEG+{!>pQR!<{%(Tq*|(ldID)Dn#!HE#9uSC`y&y!@E+JpPZ$ znV&f23-`y|+57De@Bi)7=RJsC=`^*{*Y-l#@w~#|-NdbR)2;&SiyWAUciieKEV&YA|T-TZ7bv+TXb6F*ZE<`^!# zd^}Ng*X;9|oe=?FkIC6Ji~KpV&2oBo*3Mit=Nr#o+31IF>wIwY{sGa>I5{EFe8+&d zX6FiXmCsu#zqT@da^LIhQtPPHgS|;yI#K6Lm+CtHnkRAJOLo%p{o$V<{{MDq^Q=o> zr))ajyih`3cK6PNTMS;8mMJ}5e&q0lq}1dM*W>Fr9-J~+b-eqanC-(&+x;)_MQJV- z%-_E4kJOv6jJv*v4A0EHc+KQ>w07TDn~$q++_@0bSrR2?RlMqrsPV*{toMHGt51IO z66DY6p0@m-@rB3tU-gNdJNzNW%}^;@+OgNW>Ci^&wVf`jH7{N}wP{{+oZ7?xUrt8J z*{$mcOT2hCZA*Wr>*>vVOEecQoRqcBV@bPXtn2wFLJO`5stfenyWVxukz9IyDTD5M zwxwA+W$pGp`QmvXd*8OiMbktq-gdR88Qog^!_K58D(7;<$~2ayoczg!U-u_&m|nM5 zV_B^CXM+ii7KfHEN`0u|l{t z^y-|e0nNeAe@=DAOn?0K?5FD|9XPUl7}xiP|0&l@UiCCa{aU(?qfX1HW7$zHz)6alWtOc$e>4awk{yRc}bqX~ENb=BR(@wS6Qy=h0e?dxd%v zi(2n1EPiMB;hxK;+6mv!^RNV+{r=~yxT0~%QlTfur5~pA{qQ~P`>%R$cuUduc%@f@ zdP388I`lF#I7C9DSHu7GTK;9OCP8l!k6!;@9d*xknbO9075R?oi~O&AoCj)xywKd; z6!vG2P0`=8uULZ~DvRkZmuWrRWmsox@qFIv{7Z4Xw#FA`3AFL}XRJ@3cfab^9phzj zQ=ciX^G#_}dHTy#uu{om+gEYx;(3xe`Q~%ebE4j@(`I1M;D@HE)XbkDTA7uuMJ~#J zZP)!565EiVy(F*e&V&ly*%jLR)ty?`wDe!fxmho|q_TyBdwx%nl5&BX+Mx-F7jK+M z|MfF8^WpNFMz%p`R&LlIZ*Vo#72S3zYMH1*3LL;S_VFA4%d@i}rQO?p+`tZg^|Y9ra1m_b-e%#an8c z_g3Q5)tETmz>w?ZzZTg=p7^@mN=fv}Cl3@$Vo$#nPH#(_{p@X`zhln-shvN4R#{#& ze8SmXdraQIUg*y2(&)rJdltF$edxRJx1vsNf#uYh{Nlx0SHWw(%B02CN`Ce&k3Ic! z*3yl8_uh<~_-NA6D@m)qtnKt$+!DM}ZTX$sTkgb$GcW|mdCdoBVXteSoP^VZqTa8} zU8pi|&CP$_XO5;H@0mZnMYS^}`uha+U3}UH^IRh$Z1tHy6Fct>E8lzASxeY7t>b+B zC*oB7l6&2`5jKmb78jS_wQXZJ`3=bAV3WEh9y30E^8$Q(TUNMWy<6e*a{7A<3sWTj|yM+Ga+B>)6x+p`! zUTDFwU`_h{Fz?hUs>gmExo>4GesML=78!;BUTE>)aQeE$w^{R#$KRfx4O+6bs_{~K zzj)4yWI53CO<`Sj28IoS0l(HPWVz$~_A2tK&zwamchsJ+Ag>DL&{I@j@boHbSp^+O zfMiGYJt}vz)Rb>`e`b9XVVGwN zb$t$xF8}{uuS4>K-Ivydu00Yzt@nXWTHPA!omc}#rdP5-l&CbTO&u_CHUT<03)bW;U66q1Dp8M9e3ZZVz16|@zkswAK${31x(H?;mRs29!<^dOo`4qQ?6#e)W$QTINjY^ zU}wpPd*=4%U7SAmoRX@&+x=`&^MWZ&3AbL0pWLxdYxBgzPmKS{<}~%F2x(WmSiF7N z>O-0J-w!OP(Y-sR!Zx;g*7w~~k`3vzR$gkH7T(O2RSBBS&DVdvH91yEB}v2Nca5=K z)7%l@zN-=Q^XUgnzRt+cE}eY-%N92y&h4&|y)tUo@6UN7ai&x0`LDA_dDW-QOIlg5 z&T-ZIpA+>~om?ll%Jrqad#Sk*!+DJ*XI}+h1g!;T-MZ)M^sM|J+%rI>;CF$EjaLi~ zx$i!HrA=CF<%tzi*Z32A+6({gKXL2K(SieMv$xvZG&D#(LV(i>EDxLqmG zweMHJ2xtdEuGwX;c`w&T-VQ#RfZ<%qB^PU)|2+_XI-#z zmG4PsKY?Rr)6ZE*x$3VJH%(Fw$uwKDHq-O7?z=T6|#obWs21woul* z=Vpf_+>eH_oR2hKrC@($;rY|HJu_FjHI_}#fljqouCkt=_k8LTpZhnso7t8 z8r8+GZoaKMGle@vd4_2A-V42pO?5@z{S^Nfzhfd}g6I`7Wc2~B2RYH zH;JicPOqhdS1x`53mi+9`vG<;Zq}e^;I=gtXZ1c?x&F9KKJe>V* z)->*^+1dHo+hQdH-qoC3Bt28GzqwRORYlzMq-?2Z*`h0M+Yasx*rxyW(6wW3;eUTw zL>osvoVWks|5jS)be|jg+Ns>VGgVGGG+^-x!z6a;n{n5EGwht*?%I5y z!)Z;zdXuoI{HfmHnjy2;>|JNtkNwZOcK&#?VQuA%oY!(epPm~Cy$M^9#P%ca#|6=* z?^|}p&#$a|b-S9y_J*zfv`_O_#J`;7RjhjP&c*z53o`rk^Lc+)9Q9F|eQWpSgIzX) zS3ms;SpL3Mq2Q>)&PAb#hP`hZx~gy@RT8E_L!UkQWm1G(0~6lGWaX6-MHYW)?5&7Ns`9^^@$=`iAtxN&5>vyzMy4B*}YPnRorZ+LWq)i%xA#J-;F7deS40&wD06-txwB+ElsS z+(!2M{3R>jKe!ivV@u4l>L0NR*g5azgq+B~hqbaQetCGGp7>ybu4mn!y`cSe zVWPIR8;mu1?A=%Iw^3YH>2V`e?+? zBWdxss~uf5C(JeFmX*r&T;?LPEmGfQd4ZezvdreA%l9ozTz)}g&7e8&vp0TI?YpGaTr?%(6 zFZ0-b-V84?*HgZBP3Os;wNrUDHiq9^_oMaA2gBo_BSo^^R(?PIbjQ)f&#NCRP3u|S z=KODkn^|!~e4N!@_q!8KE=dIlNp1d{SoG_`>uoBIW#@EEZltBG(bZGF#WAUG|MRIE zXIQXB{k7mKN?gC*o@ICP#>0`Ue&_4=2LFn2v+Z_1l>OLwrsk+wAs5 z@-rr7Sv<*msIunSS8c7Gmpita#-7_9ee=Nm*IoOYWb5t(uieGK;IMYZvT4%F3;HL9 zE%)gBs=IqL$A-RtP-{94pLa3c^Xu9?{(j#06X(+Uf7D53&Sl+r{(bM7BWmZ_Pp?RS z8}NlWIEc4hNz~8CJM^GAmrnLwD^Q4+Snh9PtNu|P`!Re|vN<;cL$>GEEprxqxEpwS zM(z^R0=+b=O~q*o6Jwcl^^}t*@R^169X7riFZ1ym*XM0p+#I{2lli?Kt=~93@ccp0 zc}iQP_;i&;JP@ZGRmGi2J+g1X+k$&HPq9sjsC?fQe}nJYQ-+38ho@U>wo5(#!MXhU zmC1L#|NNOWt4}i1S7YZ_Mdk3abvh+CmVFnw`p4qz^vVc5t;Gpb%+0zwl$Ute%`~e| zyst0yCpArZZKS~8hq})<=f|n+U6XocbG^o|M$L_Fk$1NA#CR^5RBXE8ujrLKnMbb8 z(tB=Mv~_-M^3v<;4l^(;P@D4W&1@Od*u~F3y}ABJRipIG|6c*MhqJB!uKy&Fd6*^s z-$n4LMv0U5zo_rl{Gk;tD4Dr<&%QvPIeWgFZVivVXIGI}b3gkPW4Utf3Wa;(x4A&q zh*TckC>2-xdhcb&mPxN~+JjDFiaM;4ek`^mS}5$3!~V!s;lKQ5DaU(fixu3vb&73@ zMZp2l`?>9#4lmAUXqXl3aQfh`Ljh~P9{nnDSp>u~z;lp`ny%i;dozblvBwA8-AYt6n?9?gzJqB|}0icr=A(_hnJ>;1^JRLculc!5*jm z@9(~6`1PuN@86gI<^CGPHQqUAvtwx&=xU*yj5$$$PI5vgZm^!PWMF7WXIwt*f6=Ga z6HJcj1qg+}5m z?fK9EyIJet@Zlm#{GHy>AsxI z{tTDs#|yYX$L-|AXf1sjKQ}HWE^g1YHSg!_FxtCkPW%tE8=Idwih?d0yRjq0>-7G= z?_PfTG+BB-*Zb>-j-LE@+}r!qpZMqXd%m&HI+(uv`Sj__pKn?EL%P^x|NfF@d-M36 z6)!uZww%|$_ixb`&3!)Hp!;hRw6-1lcaZ7ymrIkYA8$^6@8_03xqs6Q{d+$Sv;L}i z@_qC7Pp40R{_g+p{$lmx^L6y?_dW7|!EXz4@-w?d=f&6i{LiiQRNMJsZN$d?-&-RN z=-Zvx1P;X+Tz0>w{d@FKdFqTGw=aD;B)ag9(jB4AZ=>r{6yNr*zwrG2{pBJ2ecBWE z6?A_2{ZOLzUx!QW&wJtfD*R*i9Q$Oy#H^e9lqBd1r^i8#y5bL=el5QF(z{>&;+EMz zZyy$p{Qu%$-)nLIGxzs>`;quB|IvyIMLW;fRe#t1SM&ScrltT>2mvJ{^k336+WC~t{;B-P4{&xH~T-2 z+$YsJ8#z{Gg5ljBm*5m#nXU2B5n#k1chIs(WkZdvN{PHu*~He-)KKk9V8sKUaVJ zuk1bd{|OP{{7ZgyRm}S9Uf(#kdCp>au6NVfw_ob!-=qW5&bBP*^QSK#kJrckyS>=E z_@46{@k{b&@1Fh~Ge_Cv{3bvD`?5;0_b2W>!TsjQ@Rrl-OS5Q7qk-G!Swtl+m9_KQ%Zd-g?KEZ9> xe}_Eh6O5qyaYZhHR;DwosmIzvo%!_XfBQUDkwsG;Z{z``R!>(ymvv4FO#s+&ub2P; literal 0 HcmV?d00001 diff --git a/docs/ci/appveyor.rst b/docs/ci/appveyor.rst new file mode 100644 index 00000000..ea603a46 --- /dev/null +++ b/docs/ci/appveyor.rst @@ -0,0 +1,201 @@ +.. _ci_appveyor: + +AppVeyor +======== + +`AppVeyor `_ is an open-source hosted, +distributed continuous integration service used to build and test projects hosted at `GitHub `_ on Windows family systems. + +AppVeyor is configured by adding a file named ``appveyor.yml``, which is a +`YAML `_ format text file, to the root +directory of the GitHub repository. + +AppVeyor automatically detects when a commit has been made and pushed to a +GitHub repository that is using AppVeyor, and each time this happens, it will +try to build the project using :ref:`cmd_ci` command. This includes commits to +all branches, not just to the master branch. AppVeyor will also build and run +pull requests. When that process has completed, it will notify a developer in +the way it has been configured to do so — for example, by sending an email +containing the build results (showing success or failure), or by posting a +message on an IRC channel. It can be configured to build project on a range of +different :ref:`platforms`. + +Integration +----------- + +Please put ``appveyor.yml`` to the root directory of the GitHub repository. + +.. code-block:: yaml + + build: off + environment: + matrix: + - PLATFORMIO_CI_SRC=pathto\\source\\file.c + - PLATFORMIO_CI_SRC=pathto\\source\\file.ino + - PLATFORMIO_CI_SRC=pathto\\source\\directory + + install: + cmd: python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + + script: + - platformio ci --lib="." --lib="c:\spi4teensy" --board=uno --board=teensy31 --board=due' + + +For more details as for PlatformIO build process please look into :ref:`cmd_ci` +command. + +Examples +-------- + +1. Integration for `USB_Host_Shield_2.0 `_ + project. The ``appveyor.yml`` configuration file: + +.. code-block:: yaml + + build: off + environment: + global: + WITH_COMPILER: "cmd /E:ON /V:ON /C .\\scripts\\appveyor\\run_with_compiler.cmd" + matrix: + - PLATFORMIO_CI_SRC: "examples\\Bluetooth\\PS3SPP\\PS3SPP.ino" + PLATFORMIO_CI_SRC: "examples\\pl2303\\pl2303_gps\\pl2303_gps.ino" + WINDOWS_SDK_VERSION: "v7.0" + PYTHON_HOME: "C:\\Python27-x64" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "64" + install: + - "git submodule update --init --recursive" + - "powershell scripts\\appveyor\\install.ps1" + before_test: + - cmd: SET PATH=%PATH%;%PYTHON_HOME%;%PYTHON_HOME%\Scripts + - cmd: git clone https://github.com/xxxajk/spi4teensy3.git c:\spi4teensy + test_script: + - "%PYTHON_HOME%\\Scripts\\pip --version" + - '%WITH_COMPILER% %PYTHON_HOME%\\Scripts\\platformio ci --lib="." --lib="c:\spi4teensy" --board=uno --board=teensy31 --board=due' + +The ``install.ps1`` script file: + +.. code-block:: none + + $BASE_URL = "https://www.python.org/ftp/python/" + $GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" + $GET_PIP_PATH = "C:\get-pip.py" + + + function DownloadPython ($python_version, $platform_suffix) { + $webclient = New-Object System.Net.WebClient + $filename = "python-" + $python_version + $platform_suffix + ".msi" + $url = $BASE_URL + $python_version + "/" + $filename + + $basedir = $pwd.Path + "\" + $filepath = $basedir + $filename + if (Test-Path $filename) { + Write-Host "Reusing" $filepath + return $filepath + } + + # Download and retry up to 5 times in case of network transient errors. + Write-Host "Downloading" $filename "from" $url + $retry_attempts = 3 + for($i=0; $i -lt $retry_attempts; $i++){ + try { + $webclient.DownloadFile($url, $filepath) + break + } + Catch [Exception]{ + Start-Sleep 1 + } + } + Write-Host "File saved at" $filepath + return $filepath + } + + + function InstallPython ($python_version, $architecture, $python_home) { + Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home + if (Test-Path $python_home) { + Write-Host $python_home "already exists, skipping." + return $false + } + if ($architecture -eq "32") { + $platform_suffix = "" + } else { + $platform_suffix = ".amd64" + } + $filepath = DownloadPython $python_version $platform_suffix + Write-Host "Installing" $filepath "to" $python_home + $args = "/qn /i $filepath TARGETDIR=$python_home" + Write-Host "msiexec.exe" $args + Start-Process -FilePath "msiexec.exe" -ArgumentList $args -Wait -Passthru + Write-Host "Python $python_version ($architecture) installation complete" + return $true + } + + + function InstallPip ($python_home) { + $pip_path = $python_home + "/Scripts/pip.exe" + $python_path = $python_home + "/python.exe" + if (-not(Test-Path $pip_path)) { + Write-Host "Installing pip..." + $webclient = New-Object System.Net.WebClient + $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) + Write-Host "Executing:" $python_path $GET_PIP_PATH + Start-Process -FilePath "$python_path" -ArgumentList "$GET_PIP_PATH" -Wait -Passthru + } else { + Write-Host "pip already installed." + } + } + + function InstallPackage ($python_home, $pkg) { + $pip_path = $python_home + "/Scripts/pip.exe" + & $pip_path install $pkg + } + + function InstallScons ($python_home) { + Write-Host "Start installing Scons" + $pip_path = $python_home + "/Scripts/pip.exe" + & $pip_path install --egg "http://sourceforge.net/projects/scons/files/latest/download" + Write-Host "Scons installed" + } + + function main () { + InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON_HOME + InstallPip $env:PYTHON_HOME + InstallPackage $env:PYTHON_HOME setuptools + InstallScons $env:PYTHON_HOME + } + + main + +The ``run_with_compiler.cmd`` script file: + +.. code-block:: none + + @ECHO OFF + + SET COMMAND_TO_RUN=%* + SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows + + SET MAJOR_PYTHON_VERSION="%PYTHON_VERSION:~0,1%" + IF %MAJOR_PYTHON_VERSION% == "2" ( + SET WINDOWS_SDK_VERSION="v7.0" + ) ELSE IF %MAJOR_PYTHON_VERSION% == "3" ( + SET WINDOWS_SDK_VERSION="v7.1" + ) ELSE ( + ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" + EXIT 1 + ) + + IF "%PYTHON_ARCH%"=="64" ( + ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture + SET DISTUTILS_USE_SDK=1 + SET MSSdk=1 + "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% + "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release + ECHO Executing: %COMMAND_TO_RUN% + call %COMMAND_TO_RUN% || EXIT 1 + ) ELSE ( + ECHO Using default MSVC build environment for 32 bit architecture + ECHO Executing: %COMMAND_TO_RUN% + call %COMMAND_TO_RUN% || EXIT 1 + ) diff --git a/docs/ci/circleci.rst b/docs/ci/circleci.rst new file mode 100644 index 00000000..357ae00d --- /dev/null +++ b/docs/ci/circleci.rst @@ -0,0 +1,73 @@ +.. _ci_circleci: + +Circle CI +========= + +`Circle CI `_ is a hosted cloud +platform that provides hosted continuous integration, deployment, and testing +to `GitHub `_ repositories. + +Circle CI is configured by adding a file named ``circle.yml``, which is a +`YAML `_ format text file, to the root +directory of the GitHub repository. + +Circle CI automatically detects when a commit has been made and pushed to a +GitHub repository that is using Circle CI, and each time this happens, it will +try to build the project using :ref:`cmd_ci` command. This includes commits to +all branches, not just to the master branch. Circle CI will also build and run +pull requests. When that process has completed, it will notify a developer in +the way it has been configured to do so — for example, by sending an email +containing the build results (showing success or failure), or by posting a +message on an IRC channel. It can be configured to build project on a range of +different :ref:`platforms`. + +Integration +----------- + +Please put ``circle.yml`` to the root directory of the GitHub repository. + +.. code-block:: yaml + + machine: + + environment: + - PLATFORMIO_CI_SRC=path/to/source/file.c + - PLATFORMIO_CI_SRC=path/to/source/file.ino + - PLATFORMIO_CI_SRC=path/to/source/directory + + dependencies: + pre: + - python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + + test: + override: + - platformio ci --board=TYPE_1 --board=TYPE_2 --board=TYPE_N + + +For more details as for PlatformIO build process please look into :ref:`cmd_ci` +command. + +Examples +-------- + +1. Integration for `USB_Host_Shield_2.0 `_ + project. The ``circle.yml`` configuration file: + +.. code-block:: yaml + + machine: + environment: + PLATFORMIO_CI_SRC: examples/Bluetooth/PS3SPP/PS3SPP.ino + PLATFORMIO_CI_SRC: examples/pl2303/pl2303_gps/pl2303_gps.ino + + dependencies: + pre: + - sudo apt-get install python2.7-dev + - python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + - sudo pip install --egg http://sourceforge.net/projects/scons/files/latest/download + - wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip + - unzip /tmp/spi4teensy3.zip -d /tmp + + test: + override: + - platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due diff --git a/docs/ci/drone.rst b/docs/ci/drone.rst new file mode 100644 index 00000000..f4792ef6 --- /dev/null +++ b/docs/ci/drone.rst @@ -0,0 +1,70 @@ +.. _ci_drone: + +Drone +===== + +`Drone `_ is a hosted continuous integration service. +It enables you to conveniently set up projects to automatically build, test, +and deploy as you make changes to your code to +`GitHub `_ and +`BitBucket `_ repositories. + +Drone is configured by modifying settings in your project control panel. + +Drone automatically detects when a commit has been made and pushed to a +GitHub repository that is using Drone, and each time this happens, it will +try to build the project using :ref:`cmd_ci` command. This includes commits to +all branches, not just to the master branch. Drone will also build and run +pull requests. When that process has completed, it will notify a developer in +the way it has been configured to do so — for example, by sending an email +containing the build results (showing success or failure). It can be +configured to build project on a range of different :ref:`platforms`. + +Integration +----------- + +Please fill all fields for your project in the Drone control panel: + +`Environment Variables`: + +.. code-block:: bash + + PLATFORMIO_CI_SRC=path/to/source/file.c + PLATFORMIO_CI_SRC=path/to/source/file.ino + PLATFORMIO_CI_SRC=path/to/source/directory + +`Commands`: + +.. code-block:: bash + + python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + platformio ci --board=TYPE_1 --board=TYPE_2 --board=TYPE_N + +.. image:: ../_static/droneci-platformio-integration-1.png + +For more details as for PlatformIO build process please look into :ref:`cmd_ci` +command. + +Examples +-------- + +1. Integration for `USB_Host_Shield_2.0 `_ + project. The ``circle.yml`` configuration file: + +`Environment Variables`: + +.. code-block:: bash + + PLATFORMIO_CI_SRC=examples/Bluetooth/PS3SPP/PS3SPP.ino + PLATFORMIO_CI_SRC=examples/pl2303/pl2303_gps/pl2303_gps.ino + +`Commands`: + +.. code-block:: bash + + pip install --egg http://sourceforge.net/projects/scons/files/latest/download + wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip + unzip /tmp/spi4teensy3.zip -d /tmp + platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due + +.. image:: ../_static/droneci-platformio-integration-2.png diff --git a/docs/ci/index.rst b/docs/ci/index.rst index d619bc24..480974bf 100644 --- a/docs/ci/index.rst +++ b/docs/ci/index.rst @@ -17,4 +17,8 @@ easily. .. toctree:: :maxdepth: 2 + appveyor + circleci + drone + shippable travis diff --git a/docs/ci/shippable.rst b/docs/ci/shippable.rst new file mode 100644 index 00000000..9d27df52 --- /dev/null +++ b/docs/ci/shippable.rst @@ -0,0 +1,76 @@ +.. _ci_shippable: + +Shippable +========= + +`Shippable `_ is a hosted cloud +platform that provides hosted continuous integration, deployment, and testing +to `GitHub `_ and +`BitBucket `_ repositories. +Shippable's continuous integration service is built using Docker. + +Shippable is configured by adding a file named ``shippable.yml``, which is a +`YAML `_ format text file, to the root +directory of the GitHub repository or you can use your Travis CI configuration +file ``.travis.yml``. + +Shippable automatically detects when a commit has been made and pushed to a +GitHub repository that is using Shippable, and each time this happens, it will +try to build the project using :ref:`cmd_ci` command. This includes commits to +all branches, not just to the master branch. Shippable will also build and run +pull requests. When that process has completed, it will notify a developer in +the way it has been configured to do so — for example, by sending an email +containing the build results (showing success or failure), or by posting a +message on an IRC channel. It can be configured to build project on a range of +different :ref:`platforms`. + +Integration +----------- + +Please put ``shippable.yml`` or ``.travis.yml`` to the root directory of the +GitHub repository. + +.. code-block:: yaml + + language: python + python: + - "2.7" + + env: + - PLATFORMIO_CI_SRC=path/to/source/file.c + - PLATFORMIO_CI_SRC=path/to/source/file.ino + - PLATFORMIO_CI_SRC=path/to/source/directory + + install: + - python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + + script: + - platformio ci --board=TYPE_1 --board=TYPE_2 --board=TYPE_N + + +For more details as for PlatformIO build process please look into :ref:`cmd_ci` +command. + +Examples +-------- + +1. Integration for `USB_Host_Shield_2.0 `_ + project. The ``shippable.yml`` or ``.travis.yml`` configuration file: + +.. code-block:: yaml + + language: python + python: + - "2.7" + + env: + - PLATFORMIO_CI_SRC=examples/Bluetooth/PS3SPP/PS3SPP.ino + - PLATFORMIO_CI_SRC=examples/pl2303/pl2303_gps/pl2303_gps.ino + + install: + - python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + - wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip + - unzip /tmp/spi4teensy3.zip -d /tmp + + script: + - platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due From 9136fe9a8b2275b696ba74f3aed3653e6d867207 Mon Sep 17 00:00:00 2001 From: Valeriy Koval Date: Thu, 21 May 2015 17:03:53 +0300 Subject: [PATCH 67/78] Improve support for espressif platform. --- .../esp8266-webserver/src/HelloServer.ino | 53 +++++++++++++------ platformio/boards/espressif.json | 6 +-- platformio/builder/scripts/espressif.py | 11 ++-- .../builder/scripts/frameworks/arduino.py | 4 +- 4 files changed, 49 insertions(+), 25 deletions(-) diff --git a/examples/espressif/esp8266-webserver/src/HelloServer.ino b/examples/espressif/esp8266-webserver/src/HelloServer.ino index 55cdf043..f996c534 100644 --- a/examples/espressif/esp8266-webserver/src/HelloServer.ino +++ b/examples/espressif/esp8266-webserver/src/HelloServer.ino @@ -1,29 +1,43 @@ #include #include #include +#include -const char* ssid = "*****"; -const char* password = "*****"; - +const char* ssid = "******"; +const char* password = "******"; +MDNSResponder mdns; ESP8266WebServer server(80); - + const int led = 13; - -void handle_root() { + +void handleRoot() { digitalWrite(led, 1); server.send(200, "text/plain", "hello from esp8266!"); - delay(100); + digitalWrite(led, 0); +} + +void handleNotFound(){ + digitalWrite(led, 1); + String message = "File Not Found\n\n"; + message += "URI: "; + message += server.uri(); + message += "\nMethod: "; + message += (server.method() == HTTP_GET)?"GET":"POST"; + message += "\nArguments: "; + message += server.args(); + message += "\n"; + for (uint8_t i=0; i Date: Fri, 22 May 2015 10:57:23 +0300 Subject: [PATCH 68/78] Update example libs --- .../arduino-external-libs/lib/Adafruit-GFX-Library | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/atmelavr-and-arduino/arduino-external-libs/lib/Adafruit-GFX-Library b/examples/atmelavr-and-arduino/arduino-external-libs/lib/Adafruit-GFX-Library index b346ad0d..734b107c 160000 --- a/examples/atmelavr-and-arduino/arduino-external-libs/lib/Adafruit-GFX-Library +++ b/examples/atmelavr-and-arduino/arduino-external-libs/lib/Adafruit-GFX-Library @@ -1 +1 @@ -Subproject commit b346ad0df569c3b5d27c7e64656a3e1408fb8aa8 +Subproject commit 734b107c393e8a91d8c1d46a42f59a4dfb25b576 From 251cab64f30650d294c7eb2a3a625ec2447dc37c Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 22 May 2015 12:05:42 +0300 Subject: [PATCH 69/78] Remove unused dot --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 78813f08..2d9e93f1 100644 --- a/README.rst +++ b/README.rst @@ -133,7 +133,7 @@ It has support for the most popular embedded platforms: * `Espressif `_ * `Freescale Kinetis `_ * `Nordic nRF51 `_ -* `NXP LPC. `_ +* `NXP LPC `_ * `ST STM32 `_ * `Teensy `_ * `TI MSP430 `_ From 41fc7dffafa9b6d882d3ad1d4bf268d3a0726e91 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 22 May 2015 16:12:02 +0300 Subject: [PATCH 70/78] Update AppVeyor badge --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 2d9e93f1..d527c6d9 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ PlatformIO .. image:: https://travis-ci.org/platformio/platformio.svg?branch=develop :target: https://travis-ci.org/platformio/platformio :alt: Travis.CI Build Status -.. image:: https://ci.appveyor.com/api/projects/status/ee26e58de798rctd/branch/develop?svg=true +.. image:: https://ci.appveyor.com/api/projects/status/dku0h2rutfj0ctls/branch/develop?svg=true :target: https://ci.appveyor.com/project/ivankravets/platformio :alt: AppVeyor.CI Build Status .. image:: https://gemnasium.com/platformio/platformio.png From 274b04c08f2a5bf9fdf418d299c8240e2a4b0b3a Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 22 May 2015 16:44:53 +0300 Subject: [PATCH 71/78] Fix system type when machine data is not available --- platformio/util.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platformio/util.py b/platformio/util.py index 806225fc..6dbad199 100644 --- a/platformio/util.py +++ b/platformio/util.py @@ -90,7 +90,10 @@ class memoized(object): def get_systype(): data = uname() - return ("%s_%s" % (data[0], data[4])).lower() + systype = data[0] + if data[4]: + systype += "_" + data[4] + return systype.lower() def pioversion_to_intstr(): From d3a4a04a6dd9231b8ee9b8471812c5356a7a97e4 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 22 May 2015 19:08:51 +0300 Subject: [PATCH 72/78] Allow to initialise PlatformIO project for the specified IDE // Resolve #151 --- HISTORY.rst | 14 +++++-- docs/userguide/cmd_init.rst | 8 ++++ platformio/__init__.py | 2 +- .../builder/scripts/frameworks/arduino.py | 8 ++-- .../builder/scripts/frameworks/cmsis.py | 2 +- .../builder/scripts/frameworks/energia.py | 2 +- .../builder/scripts/frameworks/libopencm3.py | 4 +- platformio/builder/scripts/frameworks/mbed.py | 4 +- platformio/builder/scripts/frameworks/spl.py | 2 +- platformio/builder/tools/platformio.py | 22 +++++++++-- platformio/ide/projectgenerator.py | 38 ++++++++++++++----- 11 files changed, 78 insertions(+), 28 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 2372d1a5..ef8cb989 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,15 +4,18 @@ Release History 2.0.0 (2015-??-??) ------------------ +* PlatformIO as :ref:`ci` (CI) tool for embedded projects + (`issue #108 `_) +* Initialise PlatformIO project for the specified IDE + (`issue #151 `_) * PlatformIO CLI 2.0: "platform" related commands have been moved to ``platformio platforms`` subcommand (`issue #158 `_) -* PlatformIO as Continuous Integration (CI) tool for embedded projects - (`issue #108 `_) * Created `PlatformIO gitter.im `_ room (`issue #174 `_) * Global ``-f, --force`` option which will force to accept any - confirmation prompts (`issue #152 `_) + confirmation prompts + (`issue #152 `_) * Run project with `platformio run --project-dir `_ option without changing the current working directory (`issue #192 `_) @@ -33,6 +36,11 @@ Release History (`issue #168 `_) * Specify `platformio init --env-prefix `__ when initialise/update project (`issue #182 `_) +* Added new Armstrap boards + (`issue #204 `_) +* Updated SDK for `espressif `__ + development platform to v1.1 + (`issue #179 `_) * Disabled automatic updates by default for platforms, packages and libraries (`issue #171 `_) * Fixed bug with creating copies of source files diff --git a/docs/userguide/cmd_init.rst b/docs/userguide/cmd_init.rst index a84694ff..6ca15cec 100644 --- a/docs/userguide/cmd_init.rst +++ b/docs/userguide/cmd_init.rst @@ -49,6 +49,14 @@ pre-fill these data: The full list with pre-configured boards is available here :ref:`platforms`. +.. option:: + --ide + +Initialise PlatformIO project for the specified IDE which can be imported later +via "Import Project" functionality. + +A list with supported IDE is available within ``platformio init --help`` command. + .. option:: --disable-auto-uploading diff --git a/platformio/__init__.py b/platformio/__init__.py index 357f664d..3c73d16f 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -VERSION = (2, 0, "0a2") +VERSION = (2, 0, "0rc1") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" diff --git a/platformio/builder/scripts/frameworks/arduino.py b/platformio/builder/scripts/frameworks/arduino.py index c7bb20ce..1761e0e9 100644 --- a/platformio/builder/scripts/frameworks/arduino.py +++ b/platformio/builder/scripts/frameworks/arduino.py @@ -106,20 +106,20 @@ env.Append( # if env.subst("${PLATFORMFW_DIR}")[-3:] == "sam": - env.VariantDir( + env.VariantDirWrap( join("$BUILD_DIR", "FrameworkCMSISInc"), join("$PLATFORMFW_DIR", "system", "CMSIS", "CMSIS", "Include") ) - env.VariantDir( + env.VariantDirWrap( join("$BUILD_DIR", "FrameworkDeviceInc"), join("$PLATFORMFW_DIR", "system", "CMSIS", "Device", "ATMEL") ) - env.VariantDir( + env.VariantDirWrap( join("$BUILD_DIR", "FrameworkLibSam"), join("$PLATFORMFW_DIR", "system", "libsam") ) - env.VariantDir( + env.VariantDirWrap( join("$BUILD_DIR", "FrameworkArduinoInc"), join("$PLATFORMFW_DIR", "cores", "${BOARD_OPTIONS['build']['core']}") ) diff --git a/platformio/builder/scripts/frameworks/cmsis.py b/platformio/builder/scripts/frameworks/cmsis.py index 6410c10f..2d8d55c7 100644 --- a/platformio/builder/scripts/frameworks/cmsis.py +++ b/platformio/builder/scripts/frameworks/cmsis.py @@ -25,7 +25,7 @@ env.Replace( PLATFORMFW_DIR=join("$PIOPACKAGES_DIR", "framework-cmsis") ) -env.VariantDir( +env.VariantDirWrap( join("$BUILD_DIR", "FrameworkCMSIS"), join("$PLATFORMFW_DIR", "cores", "${BOARD_OPTIONS['build']['core']}") ) diff --git a/platformio/builder/scripts/frameworks/energia.py b/platformio/builder/scripts/frameworks/energia.py index 67b4cc10..69bdb8fa 100644 --- a/platformio/builder/scripts/frameworks/energia.py +++ b/platformio/builder/scripts/frameworks/energia.py @@ -28,7 +28,7 @@ ENERGIA_VERSION = int( "version.txt")).read().replace(".", "").strip()) # include board variant -env.VariantDir( +env.VariantDirWrap( join("$BUILD_DIR", "FrameworkEnergiaVariant"), join("$PLATFORMFW_DIR", "variants", "${BOARD_OPTIONS['build']['variant']}") ) diff --git a/platformio/builder/scripts/frameworks/libopencm3.py b/platformio/builder/scripts/frameworks/libopencm3.py index a0336e77..72a762c5 100644 --- a/platformio/builder/scripts/frameworks/libopencm3.py +++ b/platformio/builder/scripts/frameworks/libopencm3.py @@ -138,7 +138,7 @@ if BOARD_BUILDOPTS.get("core") == "lm4f": CPPDEFINES=["LM4F"] ) -env.VariantDir( +env.VariantDirWrap( join("$BUILD_DIR", "FrameworkLibOpenCM3Variant"), join("$PLATFORMFW_DIR", "include") ) @@ -166,7 +166,7 @@ env.Replace( ) libs = [] -env.VariantDir( +env.VariantDirWrap( join("$BUILD_DIR", "FrameworkLibOpenCM3"), "$PLATFORMFW_DIR" ) diff --git a/platformio/builder/scripts/frameworks/mbed.py b/platformio/builder/scripts/frameworks/mbed.py index 763b85c3..e9e57ad0 100644 --- a/platformio/builder/scripts/frameworks/mbed.py +++ b/platformio/builder/scripts/frameworks/mbed.py @@ -134,7 +134,7 @@ def add_mbedlib(libname, libar): (libname.upper(), crc32(root))) if var_dir in env.get("CPPPATH"): continue - env.VariantDir(var_dir, root) + env.VariantDirWrap(var_dir, root) env.Append(CPPPATH=[var_dir]) @@ -221,7 +221,7 @@ if board_type in ("frdm_k22f", "frdm_k64f"): for lib_path in eixdata.get("CPPPATH"): _vdir = join("$BUILD_DIR", "FrameworkMbedInc%d" % crc32(lib_path)) - env.VariantDir(_vdir, join(variant_dir, lib_path)) + env.VariantDirWrap(_vdir, join(variant_dir, lib_path)) env.Append(CPPPATH=[_vdir]) env.Append( diff --git a/platformio/builder/scripts/frameworks/spl.py b/platformio/builder/scripts/frameworks/spl.py index 229e803b..130d8bf2 100644 --- a/platformio/builder/scripts/frameworks/spl.py +++ b/platformio/builder/scripts/frameworks/spl.py @@ -22,7 +22,7 @@ env.Replace( PLATFORMFW_DIR=join("$PIOPACKAGES_DIR", "framework-spl") ) -env.VariantDir( +env.VariantDirWrap( join("$BUILD_DIR", "FrameworkSPLInc"), join("$PLATFORMFW_DIR", "${BOARD_OPTIONS['build']['core']}", "variants", "${BOARD_OPTIONS['build']['variant']}", "inc") diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py index 0af57e92..656473b7 100644 --- a/platformio/builder/tools/platformio.py +++ b/platformio/builder/tools/platformio.py @@ -2,12 +2,13 @@ # See LICENSE for details. import atexit +import json import re from os import getenv, listdir, remove, sep, walk from os.path import basename, dirname, isdir, isfile, join, normpath -from SCons.Script import (COMMAND_LINE_TARGETS, Exit, SConscript, - SConscriptChdir) +from SCons.Script import (COMMAND_LINE_TARGETS, DefaultEnvironment, Exit, + SConscript, SConscriptChdir) from SCons.Util import case_sensitive_suffixes from platformio.util import pioversion_to_intstr @@ -60,6 +61,15 @@ def BuildFirmware(env): print env.Dump() Exit() + if "idedata" in COMMAND_LINE_TARGETS: + _data = {"defines": [], "includes": []} + for item in env.get("VARIANT_DIRS", []): + _data['includes'].append(env.subst(item[1])) + for item in env.get("CPPDEFINES", []): + _data['defines'].append(env.subst(item)) + print json.dumps(_data) + Exit() + return firmenv.Program( join("$BUILD_DIR", "firmware"), [firmenv.GlobCXXFiles(vdir) for vdir in vdirs], @@ -94,6 +104,11 @@ def GlobCXXFiles(env, path): return files +def VariantDirWrap(env, variant_dir, src_dir, duplicate=True): + DefaultEnvironment().Append(VARIANT_DIRS=[(variant_dir, src_dir)]) + env.VariantDir(variant_dir, src_dir, duplicate) + + def VariantDirRecursive(env, variant_dir, src_dir, duplicate=True, ignore_pattern=None): if not ignore_pattern: @@ -105,7 +120,7 @@ def VariantDirRecursive(env, variant_dir, src_dir, duplicate=True, _var_dir = variant_dir + root.replace(src_dir, "") if any([s in _var_dir.lower() for s in ignore_pattern]): continue - env.VariantDir(_var_dir, _src_dir, duplicate) + env.VariantDirWrap(_var_dir, _src_dir, duplicate) variants.append(_var_dir) return variants @@ -397,6 +412,7 @@ def generate(env): env.AddMethod(BuildFirmware) env.AddMethod(ProcessFlags) env.AddMethod(GlobCXXFiles) + env.AddMethod(VariantDirWrap) env.AddMethod(VariantDirRecursive) env.AddMethod(BuildFramework) env.AddMethod(BuildLibrary) diff --git a/platformio/ide/projectgenerator.py b/platformio/ide/projectgenerator.py index 3f09249c..c63cf843 100644 --- a/platformio/ide/projectgenerator.py +++ b/platformio/ide/projectgenerator.py @@ -1,6 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. +import json from glob import glob from os import listdir, walk from os.path import basename, isdir, join @@ -37,13 +38,30 @@ class ProjectGenerator(object): data[k] = v return data + @util.memoized + def get_project_build_data(self): + envdata = self.get_project_env() + if "env_name" not in envdata: + return None + result = util.exec_command( + ["platformio", "run", "-t", "idedata", "-e", envdata['env_name']] + ) + if result['returncode'] != 0 or '{"includes":' not in result['out']: + return None + + output = result['out'] + start_index = output.index('---\n{"includes":') + stop_index = output.index('}\n===') + try: + return json.loads(output[start_index+4:stop_index+1]) + except ValueError: + pass + + return None + def get_project_name(self): return basename(self.project_dir) - @staticmethod - def get_includes(): - return [] - @staticmethod def get_srcfiles(): result = [] @@ -52,10 +70,6 @@ class ProjectGenerator(object): result.append(join(root, f)) return result - @staticmethod - def get_defines(): - return [] - def get_tpls(self): tpls_dir = join(util.get_source_dir(), "ide", "tpls", self.ide) return glob(join(tpls_dir, ".*.tpl")) + glob(join(tpls_dir, "*.tpl")) @@ -75,10 +89,14 @@ class ProjectGenerator(object): def _gather_tplvars(self): self._tplvars.update(self.get_project_env()) + build_data = self.get_project_build_data() + self._tplvars.update({ "project_name": self.get_project_name(), - "includes": self.get_includes(), + "includes": (build_data['includes'] + if build_data and "includes" in build_data else []), + "defines": (build_data['defines'] + if build_data and "defines" in build_data else []), "srcfiles": self.get_srcfiles(), - "defines": self.get_defines(), "project_dir": self.project_dir }) From a7f8b65356f73cdd75ed806732bb78dd07204378 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 22 May 2015 19:21:05 +0300 Subject: [PATCH 73/78] Fix fetching of IDE data from build process --- platformio/ide/projectgenerator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platformio/ide/projectgenerator.py b/platformio/ide/projectgenerator.py index c63cf843..d0fd2247 100644 --- a/platformio/ide/projectgenerator.py +++ b/platformio/ide/projectgenerator.py @@ -50,10 +50,10 @@ class ProjectGenerator(object): return None output = result['out'] - start_index = output.index('---\n{"includes":') - stop_index = output.index('}\n===') try: - return json.loads(output[start_index+4:stop_index+1]) + start_index = output.index('\n{"includes":') + stop_index = output.rindex('}\n') + return json.loads(output[start_index + 1:stop_index + 1]) except ValueError: pass From 76ddcb9d379faf403280d61a879e7ff70660e422 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 22 May 2015 19:27:33 +0300 Subject: [PATCH 74/78] Fix export project for IDE under Windows --- platformio/ide/projectgenerator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio/ide/projectgenerator.py b/platformio/ide/projectgenerator.py index d0fd2247..fda18e9e 100644 --- a/platformio/ide/projectgenerator.py +++ b/platformio/ide/projectgenerator.py @@ -52,7 +52,7 @@ class ProjectGenerator(object): output = result['out'] try: start_index = output.index('\n{"includes":') - stop_index = output.rindex('}\n') + stop_index = output.rindex('}') return json.loads(output[start_index + 1:stop_index + 1]) except ValueError: pass From e0f0ee0f1a54409e9daca2fdf17621afd5577ef4 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 22 May 2015 20:23:09 +0300 Subject: [PATCH 75/78] Force to upgrade PyPi packages --- scripts/appveyor/install.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/appveyor/install.ps1 b/scripts/appveyor/install.ps1 index 3fb6cf28..311cb38d 100644 --- a/scripts/appveyor/install.ps1 +++ b/scripts/appveyor/install.ps1 @@ -74,7 +74,7 @@ function InstallPip ($python_home) { function InstallPackage ($python_home, $pkg) { $pip_path = $python_home + "/Scripts/pip.exe" - & $pip_path install $pkg + & $pip_path install -U $pkg } function main () { @@ -84,4 +84,4 @@ function main () { InstallPackage $env:PYTHON_HOME tox } -main \ No newline at end of file +main From 5cee4463e0816aae1d2090a05eb3c14f68671156 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 22 May 2015 21:05:38 +0300 Subject: [PATCH 76/78] Improve docs --- docs/ci/appveyor.rst | 86 ++++++++------ docs/ci/circleci.rst | 35 +++--- docs/ci/drone.rst | 2 +- docs/ide/eclipse.rst | 23 +++- docs/ide/qtcreator.rst | 32 +++-- docs/ide/sublimetext.rst | 36 ++++-- docs/ide/vim.rst | 4 +- docs/ide/visualstudio.rst | 32 +++-- docs/quickstart.rst | 4 +- docs/userguide/cmd_ci.rst | 227 +----------------------------------- docs/userguide/cmd_init.rst | 1 + 11 files changed, 168 insertions(+), 314 deletions(-) diff --git a/docs/ci/appveyor.rst b/docs/ci/appveyor.rst index ea603a46..65ca0eb9 100644 --- a/docs/ci/appveyor.rst +++ b/docs/ci/appveyor.rst @@ -4,7 +4,9 @@ AppVeyor ======== `AppVeyor `_ is an open-source hosted, -distributed continuous integration service used to build and test projects hosted at `GitHub `_ on Windows family systems. +distributed continuous integration service used to build and test projects +hosted at `GitHub `_ on Windows family +systems. AppVeyor is configured by adding a file named ``appveyor.yml``, which is a `YAML `_ format text file, to the root @@ -23,32 +25,7 @@ different :ref:`platforms`. Integration ----------- -Please put ``appveyor.yml`` to the root directory of the GitHub repository. - -.. code-block:: yaml - - build: off - environment: - matrix: - - PLATFORMIO_CI_SRC=pathto\\source\\file.c - - PLATFORMIO_CI_SRC=pathto\\source\\file.ino - - PLATFORMIO_CI_SRC=pathto\\source\\directory - - install: - cmd: python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" - - script: - - platformio ci --lib="." --lib="c:\spi4teensy" --board=uno --board=teensy31 --board=due' - - -For more details as for PlatformIO build process please look into :ref:`cmd_ci` -command. - -Examples --------- - -1. Integration for `USB_Host_Shield_2.0 `_ - project. The ``appveyor.yml`` configuration file: +Put ``appveyor.yml`` to the root directory of the GitHub repository. .. code-block:: yaml @@ -57,8 +34,9 @@ Examples global: WITH_COMPILER: "cmd /E:ON /V:ON /C .\\scripts\\appveyor\\run_with_compiler.cmd" matrix: - - PLATFORMIO_CI_SRC: "examples\\Bluetooth\\PS3SPP\\PS3SPP.ino" - PLATFORMIO_CI_SRC: "examples\\pl2303\\pl2303_gps\\pl2303_gps.ino" + - PLATFORMIO_CI_SRC: "path\\to\\source\\file.c" + PLATFORMIO_CI_SRC: "path\\to\\source\\file.ino" + PLATFORMIO_CI_SRC: "path\\to\\source\\directory" WINDOWS_SDK_VERSION: "v7.0" PYTHON_HOME: "C:\\Python27-x64" PYTHON_VERSION: "2.7" @@ -68,14 +46,16 @@ Examples - "powershell scripts\\appveyor\\install.ps1" before_test: - cmd: SET PATH=%PATH%;%PYTHON_HOME%;%PYTHON_HOME%\Scripts - - cmd: git clone https://github.com/xxxajk/spi4teensy3.git c:\spi4teensy test_script: - - "%PYTHON_HOME%\\Scripts\\pip --version" - - '%WITH_COMPILER% %PYTHON_HOME%\\Scripts\\platformio ci --lib="." --lib="c:\spi4teensy" --board=uno --board=teensy31 --board=due' + - '%WITH_COMPILER% %PYTHON_HOME%\\Scripts\\platformio ci --board=TYPE_1 --board=TYPE_2 --board=TYPE_N' -The ``install.ps1`` script file: -.. code-block:: none +Then create ``scripts/appveyor`` folder and put these 2 scripts (they are the +same for the all projects, don't need to modify them): + +1. ``scripts/appveyor/install.ps1``: + +.. code-block:: PowerShell $BASE_URL = "https://www.python.org/ftp/python/" $GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" @@ -148,7 +128,7 @@ The ``install.ps1`` script file: function InstallPackage ($python_home, $pkg) { $pip_path = $python_home + "/Scripts/pip.exe" - & $pip_path install $pkg + & $pip_path install -U $pkg } function InstallScons ($python_home) { @@ -162,14 +142,15 @@ The ``install.ps1`` script file: InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON_HOME InstallPip $env:PYTHON_HOME InstallPackage $env:PYTHON_HOME setuptools + InstallPackage $env:PYTHON_HOME platformio InstallScons $env:PYTHON_HOME } main -The ``run_with_compiler.cmd`` script file: +2. ``scripts/appveyor/run_with_compiler.cmd``: -.. code-block:: none +.. code-block:: guess @ECHO OFF @@ -199,3 +180,34 @@ The ``run_with_compiler.cmd`` script file: ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) + +For more details as for PlatformIO build process please look into :ref:`cmd_ci` +command. + +Examples +-------- + +1. Integration for `USB_Host_Shield_2.0 `_ + project. The ``appveyor.yml`` configuration file: + +.. code-block:: yaml + + build: off + environment: + global: + WITH_COMPILER: "cmd /E:ON /V:ON /C .\\scripts\\appveyor\\run_with_compiler.cmd" + matrix: + - PLATFORMIO_CI_SRC: "examples\\Bluetooth\\PS3SPP\\PS3SPP.ino" + PLATFORMIO_CI_SRC: "examples\\pl2303\\pl2303_gps\\pl2303_gps.ino" + WINDOWS_SDK_VERSION: "v7.0" + PYTHON_HOME: "C:\\Python27-x64" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "64" + install: + - "git submodule update --init --recursive" + - "powershell scripts\\appveyor\\install.ps1" + before_test: + - cmd: SET PATH=%PATH%;%PYTHON_HOME%;%PYTHON_HOME%\Scripts + - cmd: git clone https://github.com/xxxajk/spi4teensy3.git c:\spi4teensy + test_script: + - '%WITH_COMPILER% %PYTHON_HOME%\\Scripts\\platformio ci --lib="." --lib="c:\spi4teensy" --board=uno --board=teensy31 --board=due' diff --git a/docs/ci/circleci.rst b/docs/ci/circleci.rst index 357ae00d..61da49de 100644 --- a/docs/ci/circleci.rst +++ b/docs/ci/circleci.rst @@ -24,23 +24,23 @@ different :ref:`platforms`. Integration ----------- -Please put ``circle.yml`` to the root directory of the GitHub repository. +Put ``circle.yml`` to the root directory of the GitHub repository. .. code-block:: yaml machine: - environment: - PLATFORMIO_CI_SRC=path/to/source/file.c - PLATFORMIO_CI_SRC=path/to/source/file.ino - PLATFORMIO_CI_SRC=path/to/source/directory - dependencies: - pre: - - python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + dependencies: + pre: + - sudo apt-get install python2.7-dev + - sudo python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" - test: - override: + test: + override: - platformio ci --board=TYPE_1 --board=TYPE_2 --board=TYPE_N @@ -56,18 +56,17 @@ Examples .. code-block:: yaml machine: - environment: - PLATFORMIO_CI_SRC: examples/Bluetooth/PS3SPP/PS3SPP.ino - PLATFORMIO_CI_SRC: examples/pl2303/pl2303_gps/pl2303_gps.ino + environment: + PLATFORMIO_CI_SRC: examples/Bluetooth/PS3SPP/PS3SPP.ino + PLATFORMIO_CI_SRC: examples/pl2303/pl2303_gps/pl2303_gps.ino dependencies: - pre: - - sudo apt-get install python2.7-dev - - python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" - - sudo pip install --egg http://sourceforge.net/projects/scons/files/latest/download - - wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip - - unzip /tmp/spi4teensy3.zip -d /tmp + pre: + - sudo apt-get install python2.7-dev + - sudo python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" + - wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip + - unzip /tmp/spi4teensy3.zip -d /tmp test: - override: - - platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due + override: + - platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due diff --git a/docs/ci/drone.rst b/docs/ci/drone.rst index f4792ef6..912b546b 100644 --- a/docs/ci/drone.rst +++ b/docs/ci/drone.rst @@ -62,7 +62,7 @@ Examples .. code-block:: bash - pip install --egg http://sourceforge.net/projects/scons/files/latest/download + python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip unzip /tmp/spi4teensy3.zip -d /tmp platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due diff --git a/docs/ide/eclipse.rst b/docs/ide/eclipse.rst index 6fd8fae1..2549c24d 100644 --- a/docs/ide/eclipse.rst +++ b/docs/ide/eclipse.rst @@ -15,15 +15,34 @@ registers, and disassembly viewers. This software can be used with: -* all availalbe :ref:`platforms` -* all availalbe :ref:`frameworks` +* all available :ref:`platforms` +* all available :ref:`frameworks` Refer to the `CDT Documentation `_ page for more detailed information. +.. contents:: + Integration ----------- +Project Generator +^^^^^^^^^^^^^^^^^ + +Since PlatformIO 2.0 you can generate Eclipse compatible project using +:option:`platformio init --ide` command: + +.. code-block:: shell + + platformio init --ide eclipse + +Then import this project via ``File > Import... > General > Existing Projects +into Workspace > Next`` and specify root directory where is located +:ref:`projectconf`. + +Manual Integration +^^^^^^^^^^^^^^^^^^ + More detailed information is located in PlatformIO blog: `Building and debugging Atmel AVR (Arduino-based) project using Eclipse IDE+PlatformIO `_. `More examples (TI MSP430, TI TIVA, etc.) `_ diff --git a/docs/ide/qtcreator.rst b/docs/ide/qtcreator.rst index 7a7e6269..31bc7cde 100644 --- a/docs/ide/qtcreator.rst +++ b/docs/ide/qtcreator.rst @@ -7,8 +7,8 @@ The `Qt Creator `_ is an open source cr This software can be used with: -* all availalbe :ref:`platforms` -* all availalbe :ref:`frameworks` +* all available :ref:`platforms` +* all available :ref:`frameworks` Refer to the `Qt-creator Manual `_ page for more detailed information. @@ -18,10 +18,26 @@ page for more detailed information. Integration ----------- -Setup New Project +Project Generator ^^^^^^^^^^^^^^^^^ -First of all, let's create new project from Qt Creator Start Page: ``New Project`` or using ``Menu: File → New File or Project``, then select project with ``Empty Qt Project`` type (``Other Project → Empty Qt Project``), fill ``Name``, ``Create in``. +Since PlatformIO 2.0 you can generate Eclipse compatible project using +:option:`platformio init --ide` command: + +.. code-block:: shell + + platformio init --ide qtcreator + +Then import this project via ``File > New File or Project > Import Project`` +and specify root directory where is located :ref:`projectconf`. + +Manual Integration +^^^^^^^^^^^^^^^^^^ + +Setup New Project +~~~~~~~~~~~~~~~~~ + +First of all, let's create new project from Qt Creator Start Page: ``New Project`` or using ``Menu: File > New File or Project``, then select project with ``Empty Qt Project`` type (``Other Project > Empty Qt Project``), fill ``Name``, ``Create in``. .. image:: ../_static/ide-platformio-qtcreator-1.png @@ -52,12 +68,12 @@ Thirdly, change project file by adding path to directories with header files. Pl .. image:: ../_static/ide-platformio-qtcreator-4.png First program in Qt Creator -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~ Simple "Blink" project will consist from two files: -1. In the console, navigate to the root of your project folder and initialize platformio project with ``platformio init`` +1. In the console, navigate to the root of your project folder and initialize platformio project with ``platformio init`` 2. The main "C" source file named ``main.c`` must be located in the ``src`` directory. -Let's create new text file named ``main.c`` using ``Menu: New File or Project → General → Text File``: +Let's create new text file named ``main.c`` using ``Menu: New File or Project > General > Text File``: .. image:: ../_static/ide-platformio-qtcreator-5.png @@ -108,6 +124,6 @@ Edit the content to match the code described below. Conclusion ---------- -Taking everything into account, we can build project with shortcut ``Ctrl+Shift+B`` or using ``Menu: Build → Build All``: +Taking everything into account, we can build project with shortcut ``Ctrl+Shift+B`` or using ``Menu: Build > Build All``: .. image:: ../_static/ide-platformio-qtcreator-7.png diff --git a/docs/ide/sublimetext.rst b/docs/ide/sublimetext.rst index 7e25f61c..af39f328 100644 --- a/docs/ide/sublimetext.rst +++ b/docs/ide/sublimetext.rst @@ -7,8 +7,8 @@ The `Sublime Text `_ is a cross-platform text and s This software can be used with: -* all availalbe :ref:`platforms` -* all availalbe :ref:`frameworks` +* all available :ref:`platforms` +* all available :ref:`frameworks` Refer to the `Sublime Text Documentation `_ page for more detailed information. @@ -18,11 +18,27 @@ page for more detailed information. Integration ----------- +Project Generator +^^^^^^^^^^^^^^^^^ + +Since PlatformIO 2.0 you can generate Eclipse compatible project using +:option:`platformio init --ide` command: + +.. code-block:: shell + + platformio init --ide sublimetext + +Then import this project via ``Project > Open Project...`` and specify root +directory where is located :ref:`projectconf`. + +Manual Integration +^^^^^^^^^^^^^^^^^^ + Initial configuration -^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~ First of all, we need to create "New Build System" with name "PlatformIO" -from ``Menu: Tools → Build System → New Build System`` and fill it like +from ``Menu: Tools > Build System > New Build System`` and fill it like described below: .. code-block:: bash @@ -48,16 +64,16 @@ Secondly, we need to select "PlatformIO" Build System from a list: .. image:: ../_static/ide-sublime-text-platformio-newproject-2.png After that, we can use the necessary commands from -``Menu: Tools → Command Palette`` or with ``Ctrl+Shift+P`` (Windows/Linux) +``Menu: Tools > Command Palette`` or with ``Ctrl+Shift+P`` (Windows/Linux) ``Cmd+Shift+P`` (Mac) shortcut. .. image:: ../_static/ide-sublime-text-platformio-newproject-3.png Command Hotkeys -~~~~~~~~~~~~~~~ +''''''''''''''' Sublime Text allows to bind own hotkey per command. Let's setup them -for PlatformIO commands using shortcut ``Menu: Preferences → Key-Bindings - User``: +for PlatformIO commands using shortcut ``Menu: Preferences > Key-Bindings - User``: .. image:: ../_static/ide-sublime-text-platformio-newproject-4.png @@ -76,12 +92,12 @@ In this case, the final code will look like: ] First program in Sublime Text -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Simple "Blink" project will consist from two files: 1. Main "C" source file named ``main.c`` must be located in the ``src`` directory. -Let's create new file named ``main.c`` using ``Menu: File → New File`` or shortcut ``Ctrl+N`` (Windows/Linux) ``Cmd+N`` (Mac) with the next contents: +Let's create new file named ``main.c`` using ``Menu: File > New File`` or shortcut ``Ctrl+N`` (Windows/Linux) ``Cmd+N`` (Mac) with the next contents: .. code-block:: c @@ -124,6 +140,6 @@ Copy the source code which is described below to it. Conclusion ---------- -Taking everything into account, we can open project directory in Sublime Text using ``Menu: File → Open Folder`` and build it with shortcut ``Ctrl+B`` (Windows/Linux) or ``Cmd+B`` (Mac), clean project with shortcut ``F11`` and upload firmware to target with shortcut ``F12``. +Taking everything into account, we can open project directory in Sublime Text using ``Menu: File > Open Folder`` and build it with shortcut ``Ctrl+B`` (Windows/Linux) or ``Cmd+B`` (Mac), clean project with shortcut ``F11`` and upload firmware to target with shortcut ``F12``. .. image:: ../_static/ide-sublime-text-platformio-newproject-5.png diff --git a/docs/ide/vim.rst b/docs/ide/vim.rst index 1840e510..ae1116f6 100644 --- a/docs/ide/vim.rst +++ b/docs/ide/vim.rst @@ -9,8 +9,8 @@ standalone application in a graphical user interface. This software can be used with: -* all availalbe :ref:`platforms` -* all availalbe :ref:`frameworks` +* all available :ref:`platforms` +* all available :ref:`frameworks` Integration ----------- diff --git a/docs/ide/visualstudio.rst b/docs/ide/visualstudio.rst index c5fc28d3..f5275532 100644 --- a/docs/ide/visualstudio.rst +++ b/docs/ide/visualstudio.rst @@ -7,8 +7,8 @@ The `Microsoft Visual Studio (Free) `_ is an integ This software can be used with: -* all availalbe :ref:`platforms` -* all availalbe :ref:`frameworks` +* all available :ref:`platforms` +* all available :ref:`frameworks` Refer to the `Visual Studio Documentation `_ page for more detailed information. @@ -18,10 +18,26 @@ page for more detailed information. Integration ----------- -Setup New Project +Project Generator ^^^^^^^^^^^^^^^^^ -First of all, let's create new project from Visual Studio Start Page: ``Start → New Project`` or using ``Menu: File → New → Project``, then select project with ``Makefile`` type (``Visual C++ → General → Makefile Project``), fill ``Project name``, ``Solution name``, ``Location`` fields and press OK button. +Since PlatformIO 2.0 you can generate Eclipse compatible project using +:option:`platformio init --ide` command: + +.. code-block:: shell + + platformio init --ide visualstudio + +Then import this project via ``File > Import ...`` and specify root directory +where is located :ref:`projectconf`. + +Manual Integration +^^^^^^^^^^^^^^^^^^ + +Setup New Project +~~~~~~~~~~~~~~~~~ + +First of all, let's create new project from Visual Studio Start Page: ``Start > New Project`` or using ``Menu: File > New > Project``, then select project with ``Makefile`` type (``Visual C++ > General > Makefile Project``), fill ``Project name``, ``Solution name``, ``Location`` fields and press OK button. .. image:: ../_static/ide-vs-platformio-newproject.png @@ -37,7 +53,7 @@ Release Configuration is the same as Debug, so on the next step we check "Same a .. image:: ../_static/ide-vs-platformio-newproject-3.png -Thirdly, we need to add directories with header files using project properties (right click on the project name or ``Alt-Enter`` shortcut) and add two directories to ``Configuration Properties → NMake → Include Search Path``: +Thirdly, we need to add directories with header files using project properties (right click on the project name or ``Alt-Enter`` shortcut) and add two directories to ``Configuration Properties > NMake > Include Search Path``: .. code-block:: none @@ -47,12 +63,12 @@ Thirdly, we need to add directories with header files using project properties ( .. image:: ../_static/ide-vs-platformio-newproject-5.png First program in Visual Studio -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Simple "Blink" project will consist from two files: 1. Main "C" source file named ``main.c`` must be located in the ``src`` directory. -Let's create new file named ``main.c`` using ``Menu: File → New File`` or shortcut ``Ctrl+N``: +Let's create new file named ``main.c`` using ``Menu: File > New File`` or shortcut ``Ctrl+N``: .. image:: ../_static/ide-vs-platformio-newproject-6.png @@ -103,6 +119,6 @@ Copy the source code which is described below to it. Conclusion ---------- -Taking everything into account, we can build project with shortcut ``Ctrl+Shift+B`` or using ``Menu: Build → Build Solution``: +Taking everything into account, we can build project with shortcut ``Ctrl+Shift+B`` or using ``Menu: Build > Build Solution``: .. image:: ../_static/ide-vs-platformio-newproject-8.png diff --git a/docs/quickstart.rst b/docs/quickstart.rst index f9296061..a2336d16 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -9,8 +9,8 @@ Quickstart 1. :ref:`Install PlatformIO `. -2. Find board ``type`` on this :ref:`Embedded Boards ` page or - via :ref:`cmd_boards` command. +2. Find board ``type`` using `Embedded Boards Explorer `_ + or via :ref:`cmd_boards` command. 3. Initialize new PlatformIO based project via :ref:`cmd_init` command with the pre-configured environments for your boards: diff --git a/docs/userguide/cmd_ci.rst b/docs/userguide/cmd_ci.rst index 021ebc15..77d9d08b 100644 --- a/docs/userguide/cmd_ci.rst +++ b/docs/userguide/cmd_ci.rst @@ -107,229 +107,4 @@ Shows details about the results of processing environments. More details Examples -------- -1. Integration `Travis.CI `_, `Shippable `_ for GitHub - `USB_Host_Shield_2.0 `_ - project. The ``.travis.yml`` configuration file: - -.. code-block:: yaml - - language: python - python: - - "2.7" - - env: - - PLATFORMIO_CI_SRC=examples/Bluetooth/PS3SPP/PS3SPP.ino - - PLATFORMIO_CI_SRC=examples/pl2303/pl2303_gps/pl2303_gps.ino - - install: - - python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" - - pip install --egg http://sourceforge.net/projects/scons/files/latest/download - - wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip - - unzip /tmp/spi4teensy3.zip -d /tmp - - script: - - platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due - -2. Integration `CircleCI `_ for GitHub - `USB_Host_Shield_2.0 `_ - project. The ``circle.yml`` configuration file: - -.. code-block:: yaml - - machine: - - environment: - PLATFORMIO_CI_SRC: examples/Bluetooth/PS3SPP/PS3SPP.ino - PLATFORMIO_CI_SRC: examples/pl2303/pl2303_gps/pl2303_gps.ino - - dependencies: - pre: - - sudo apt-get install python2.7-dev - - sudo python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" - - sudo pip install --egg http://sourceforge.net/projects/scons/files/latest/download - - wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip - - unzip /tmp/spi4teensy3.zip -d /tmp - test: - override: - - platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due - -3. Integration `AppVeyor CI `_ for GitHub - `USB_Host_Shield_2.0 `_ - project. The ``appveyor.yml`` configuration file: - -.. code-block:: yaml - - build: off - environment: - global: - WITH_COMPILER: "cmd /E:ON /V:ON /C .\\scripts\\appveyor\\run_with_compiler.cmd" - matrix: - - PLATFORMIO_CI_SRC: "examples\\Bluetooth\\PS3SPP\\PS3SPP.ino" - PLATFORMIO_CI_SRC: "examples\\pl2303\\pl2303_gps\\pl2303_gps.ino" - WINDOWS_SDK_VERSION: "v7.0" - PYTHON_HOME: "C:\\Python27-x64" - PYTHON_VERSION: "2.7" - PYTHON_ARCH: "64" - init: - - ps: "ls C:\\Python*" - install: - - "git submodule update --init --recursive" - - "powershell scripts\\appveyor\\install.ps1" - before_test: - - cmd: SET PATH=%PATH%;%PYTHON_HOME%;%PYTHON_HOME%\Scripts - - cmd: git clone https://github.com/xxxajk/spi4teensy3.git c:\spi4teensy - test_script: - - "%PYTHON_HOME%\\Scripts\\pip --version" - - '%WITH_COMPILER% %PYTHON_HOME%\\Scripts\\platformio ci --lib="." --lib="c:\spi4teensy" --board=uno --board=teensy31 --board=due' - -The ``run_with_compiler.cmd`` script file: - -.. code-block:: none - - @ECHO OFF - - SET COMMAND_TO_RUN=%* - SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows - - SET MAJOR_PYTHON_VERSION="%PYTHON_VERSION:~0,1%" - IF %MAJOR_PYTHON_VERSION% == "2" ( - SET WINDOWS_SDK_VERSION="v7.0" - ) ELSE IF %MAJOR_PYTHON_VERSION% == "3" ( - SET WINDOWS_SDK_VERSION="v7.1" - ) ELSE ( - ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" - EXIT 1 - ) - - IF "%PYTHON_ARCH%"=="64" ( - ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture - SET DISTUTILS_USE_SDK=1 - SET MSSdk=1 - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 - ) ELSE ( - ECHO Using default MSVC build environment for 32 bit architecture - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 - ) - - -The ``install.ps1`` script file: - -.. code-block:: none - - $BASE_URL = "https://www.python.org/ftp/python/" - $GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" - $GET_PIP_PATH = "C:\get-pip.py" - - - function DownloadPython ($python_version, $platform_suffix) { - $webclient = New-Object System.Net.WebClient - $filename = "python-" + $python_version + $platform_suffix + ".msi" - $url = $BASE_URL + $python_version + "/" + $filename - - $basedir = $pwd.Path + "\" - $filepath = $basedir + $filename - if (Test-Path $filename) { - Write-Host "Reusing" $filepath - return $filepath - } - - # Download and retry up to 5 times in case of network transient errors. - Write-Host "Downloading" $filename "from" $url - $retry_attempts = 3 - for($i=0; $i -lt $retry_attempts; $i++){ - try { - $webclient.DownloadFile($url, $filepath) - break - } - Catch [Exception]{ - Start-Sleep 1 - } - } - Write-Host "File saved at" $filepath - return $filepath - } - - - function InstallPython ($python_version, $architecture, $python_home) { - Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home - if (Test-Path $python_home) { - Write-Host $python_home "already exists, skipping." - return $false - } - if ($architecture -eq "32") { - $platform_suffix = "" - } else { - $platform_suffix = ".amd64" - } - $filepath = DownloadPython $python_version $platform_suffix - Write-Host "Installing" $filepath "to" $python_home - $args = "/qn /i $filepath TARGETDIR=$python_home" - Write-Host "msiexec.exe" $args - Start-Process -FilePath "msiexec.exe" -ArgumentList $args -Wait -Passthru - Write-Host "Python $python_version ($architecture) installation complete" - return $true - } - - - function InstallPip ($python_home) { - $pip_path = $python_home + "/Scripts/pip.exe" - $python_path = $python_home + "/python.exe" - if (-not(Test-Path $pip_path)) { - Write-Host "Installing pip..." - $webclient = New-Object System.Net.WebClient - $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) - Write-Host "Executing:" $python_path $GET_PIP_PATH - Start-Process -FilePath "$python_path" -ArgumentList "$GET_PIP_PATH" -Wait -Passthru - } else { - Write-Host "pip already installed." - } - } - - function InstallPackage ($python_home, $pkg) { - $pip_path = $python_home + "/Scripts/pip.exe" - & $pip_path install $pkg - } - - function InstallScons ($python_home) { - Write-Host "Start installing Scons" - $pip_path = $python_home + "/Scripts/pip.exe" - & $pip_path install --egg "http://sourceforge.net/projects/scons/files/latest/download" - Write-Host "Scons installed" - } - - function main () { - InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON_HOME - InstallPip $env:PYTHON_HOME - InstallPackage $env:PYTHON_HOME setuptools - InstallScons $env:PYTHON_HOME - InstallPackage $env:PYTHON_HOME "https://github.com/platformio/platformio/archive/develop.zip" - } - - main - -3. Integration `Drone CI `_ for GitHub - `USB_Host_Shield_2.0 `_ - project. The project settings: - -`Environment Variables`: - -.. code-block:: none - - PLATFORMIO_CI_SRC=examples/Bluetooth/PS3SPP/PS3SPP.ino - PLATFORMIO_CI_SRC=examples/pl2303/pl2303_gps/pl2303_gps.ino - -`Commands`: - -.. code-block:: none - - pip install --egg http://sourceforge.net/projects/scons/files/latest/download - python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)" - wget https://github.com/xxxajk/spi4teensy3/archive/master.zip -O /tmp/spi4teensy3.zip - unzip /tmp/spi4teensy3.zip -d /tmp - platformio ci --lib="." --lib="/tmp/spi4teensy3-master" --board=uno --board=teensy31 --board=due - - +For the examples please follow to :ref:`ci` page. diff --git a/docs/userguide/cmd_init.rst b/docs/userguide/cmd_init.rst index 6ca15cec..02e140de 100644 --- a/docs/userguide/cmd_init.rst +++ b/docs/userguide/cmd_init.rst @@ -56,6 +56,7 @@ Initialise PlatformIO project for the specified IDE which can be imported later via "Import Project" functionality. A list with supported IDE is available within ``platformio init --help`` command. +Also, please look into :ref:`ide` page. .. option:: --disable-auto-uploading From 49842df368ebea74aa94d8c2907626599b6d74f3 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 22 May 2015 21:42:49 +0300 Subject: [PATCH 77/78] Add links for CI --- README.rst | 3 ++- docs/index.rst | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d527c6d9..41f28fc6 100644 --- a/README.rst +++ b/README.rst @@ -46,6 +46,7 @@ and the missing library manager. * `Embedded Boards Explorer `_ * `Library Manager `_ * `User Guide `_ +* `Continuous Integration `_ * `IDE Integration `_ * `Articles about us `_ * `FAQ `_ @@ -75,7 +76,7 @@ settings for most popular `Embedded Boards `_. * Configurable `build -flags/-options `_ * Automatic **firmware uploading** * Integration with `development environments (IDE) `_ -* Ready for **Cloud Compiling** and **Continuous Integration** +* Ready for **Cloud Compiling** and `Continuous Integration `_ * Pre-built tool chains, frameworks for the popular `Hardware Platforms `_ .. image:: https://raw.githubusercontent.com/platformio/platformio-web/develop/app/images/platformio-embedded-development.png diff --git a/docs/index.rst b/docs/index.rst index 2c580297..8bb9fe21 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -29,6 +29,7 @@ Embedded Development. *Easier Than Ever.* * Built-in :ref:`Serial Port Monitor ` * Configurable build :ref:`-flags/-options ` * Integration with :ref:`development environments (IDE) ` +* Ready for Cloud Compiling and :ref:`ci` * Pre-built tool chains, :ref:`frameworks` for the popular Hardware Platforms Smart Code Builder. *Fast and Reliable.* From 6a7a9a78ae1f6660f321d870b189c6bdc5cc191f Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 22 May 2015 21:48:58 +0300 Subject: [PATCH 78/78] Version bump to 2.0.0 (issues #108, #134, #148, #151, #152, #154, #158, #167, #168, #171, #173, #174, #177, #179, #182, #192, #197, #204) --- HISTORY.rst | 2 +- platformio/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index ef8cb989..2fbabd97 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,7 +1,7 @@ Release History =============== -2.0.0 (2015-??-??) +2.0.0 (2015-05-22) ------------------ * PlatformIO as :ref:`ci` (CI) tool for embedded projects diff --git a/platformio/__init__.py b/platformio/__init__.py index 3c73d16f..0864a676 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -1,7 +1,7 @@ # Copyright (C) Ivan Kravets # See LICENSE for details. -VERSION = (2, 0, "0rc1") +VERSION = (2, 0, 0) __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio"