diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..452e43a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.egg-info +*.pyc +.pioenvs +.tox +.sconsign.dblite diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 00000000..c905e9ca --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,3 @@ +[settings] +line_length=79 +known_third_party=click,requests,serial,SCons diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000..939f8928 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,281 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Profiled execution. +profile=no + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + + +[MESSAGES CONTROL] + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +disable=C0103,C0111,E0611,F0401,I0011,R0801,R0903 + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Add a comment according to your evaluation note. This is used by the global +# evaluation report (RP0004). +comment=no + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= +msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} + + +[BASIC] + +# Required attributes for module, separated by a comma +required-attributes= + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,apply,input + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct attribute names in class +# bodies +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=__.*__ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=80 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). +ignored-classes=SQLObject + +# When zope mode is activated, add a predefined set of Zope acquired attributes +# to generated-members. +zope=no + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E0201 when accessed. Python regular +# expressions are accepted. +generated-members=REQUEST,acl_users,aq_parent + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the beginning of the name of dummy variables +# (i.e. not used). +dummy-variables-rgx=_$|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +[CLASSES] + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defines in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..93213295 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: python +python: 2.7 + +env: + - TOX_ENV=lint + - TOX_ENV=py26 + +install: + - pip install tox + +script: + - tox -e $TOX_ENV diff --git a/README.md b/README.md deleted file mode 100644 index ccf1827b..00000000 --- a/README.md +++ /dev/null @@ -1,4 +0,0 @@ -platformio -========== - -An easy way to build code with different development platforms diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..0b75a87e --- /dev/null +++ b/README.rst @@ -0,0 +1,459 @@ +PlatformIO +========== + +.. image:: https://travis-ci.org/ivankravets/platformio.svg?branch=develop + :target: https://travis-ci.org/ivankravets/platformio + :alt: Build Status +.. image:: https://gemnasium.com/ivankravets/platformio.png + :target: https://gemnasium.com/ivankravets/platformio + :alt: Dependency Status +.. image:: https://pypip.in/version/platformio/badge.png + :target: https://pypi.python.org/pypi/platformio/ + :alt: Latest Version +.. image:: https://pypip.in/license/platformio/badge.png + :target: https://pypi.python.org/pypi/platformio/ + :alt: License + + +**Platformio** is a console tool to build code with different development +platforms. + +You have no need to install any *IDE* or compile any toolchains. *Platformio* +has pre-built different development platforms including: compiler, debugger, +flasher (for embedded) and many other useful tools. + +**Platformio** allows developer to compile the same code with different +platforms using only one command ``platformio run``. This happens due to +``platformio.ini`` project's file (see +`default template `_) +where you can setup different environments with specific settings: platform, +firmware uploading options, pre-built framework and many more. + +Each platform consists of packages which are located in own repository. +Due to ``platformio update`` command you will have up-to-date development +instruments. + +**Platformio** is well suited for **embedded development**. It can: + +* Compile frameworks and libraries sources to static libraries +* Build *ELF* (executable and linkable firmware) +* Convert *ELF* to *HEX* or *BIN* file +* Extract *EEPROM* data +* Upload firmware to your device + +It has support for many popular embedded platforms like these: + +* ``atmelavr`` `Atmel AVR `_ + (including `Arduino `_ based boards) +* ``timsp430`` `TI MSP430 `_ + (including `MSP430 LaunchPads `_) +* ``titiva`` `TI TIVA C `_ + (including `TIVA C Series LaunchPads `_) + + +See project `examples with screenshots `_. + + +Python & OS Support +------------------- + +**Platformio** is written in `Python `_ and works with +versions 2.6 and 2.7 on Unix/Linux, OS X, and Windows. + + +Quickstart +---------- + +.. code-block:: bash + + # Install platformio + $ pip install platformio && pip install --egg scons + + # Print all availalbe development platforms for installing + $ platformio search all + + # Install new development platform + $ platformio install SomePlatform + + # Initialize new platformio based project + $ cd /path/to/empty/directory + $ platformio init + + # Process the project's environments + $ platformio run + +For more detailed information please follow to `Installation <#installation>`_ +and `User Guide <#user-guide>`_ sections. + + +Installation +------------ + +All commands below should be executed in +`Command-line `_ +application in your OS: + +* *Unix/Linux/OS X* this is *Terminal* application. +* *Windows* this is + `Command Prompt `_ (``cmd.exe``) + application. + +2. Check a ``python`` version: + +.. code-block:: bash + + $ python --version + +Windows OS Users only: + + * `Download Python `_ and install it. + * Add to PATH system variable ``;C:\Python27;C:\Python27\Scripts;`` and + reopen *Command Prompt* (``cmd.exe``) application. Please read this + article `How to set the path and environment variables in Windows + `_. + + +2. Check a ``pip`` tool for installing and managing Python packages: + +.. code-block:: bash + + $ pip search platformio + +You should see short information about ``platformio`` package. + +If your computer does not recognize ``pip`` command, try to install it first +using `these instructions `_. + +3. Install a ``platformio`` and related packages: + +.. code-block:: bash + + $ pip install platformio && pip install --egg scons + +For upgrading the ``platformio`` to new version please use this command: + +.. code-block:: bash + + $ pip install -U platformio + + +User Guide +---------- + +To print all available commands and options: + +.. code-block:: bash + + $ platformio --help + $ platformio COMMAND --help + + # Example + $ platformio --help + Usage: platformio [OPTIONS] COMMAND [ARGS]... + + Options: + --version Show the version and exit. + --help Show this message and exit. + + Commands: + init Initialize new platformio based project + install Install new platforms + list List installed platforms + run Process project environments + search Search for development platforms + show Show details about an installed platforms + uninstall Uninstall the platforms + update Update installed platforms + + +``platformio search`` +~~~~~~~~~~~~~~~~~~~~~ + +Search for development platforms: + +.. code-block:: bash + + # Print all available development platforms + $ platformio search all + + # Filter platforms by "Query" + $ platformio search "Query" + + # Example + $ platformio search ti + timsp430 - An embedded platform for TI MSP430 microcontrollers (with Energia Framework) + titiva - An embedded platform for TI TIVA C ARM microcontrollers (with Energia Framework) + + $ platformio search arduino + atmelavr - An embedded platform for Atmel AVR microcontrollers (with Arduino Framework) + + +``platformio install`` +~~~~~~~~~~~~~~~~~~~~~~ + +*Platformio* has pre-built development platforms with related packages. You +can install one of them: + +.. code-block:: bash + + $ platformio install SomePlatform + $ platformio install SomePlatform --with-package=PackageName + $ platformio install SomePlatform --without-package=PackageName + + # Example + $ platformio install timsp430 --with-package=framework-energiamsp430 + Installing toolchain-timsp430 package: + Downloading [####################################] 100% + Unpacking [####################################] 100% + Installing tool-mspdebug package: + Downloading [####################################] 100% + Unpacking [####################################] 100% + Installing framework-energiamsp430 package: + Downloading [####################################] 100% + Unpacking [####################################] 100% + The platform 'timsp430' has been successfully installed! + + +``platformio list`` +~~~~~~~~~~~~~~~~~~~ + +To list installed platforms: + +.. code-block:: bash + + $ platformio list + + # Example + $ platformio list + timsp430 with packages: toolchain-timsp430, tool-mspdebug, framework-energiamsp430 + + +``platformio show`` +~~~~~~~~~~~~~~~~~~~ + +To show details about an installed platform: + +.. code-block:: bash + + $ platformio show SomePlatform + + # Example + $ platformio show timsp430 + timsp430 - An embedded platform for TI MSP430 microcontrollers (with Energia Framework) + ---------- + Package: toolchain-timsp430 + Location: /Users/ikravets/.platformio/timsp430/tools/toolchain + Version: 1 + ---------- + Package: tool-mspdebug + Location: /Users/ikravets/.platformio/timsp430/tools/mspdebug + Version: 1 + ---------- + Package: framework-energiamsp430 + Location: /Users/ikravets/.platformio/timsp430/frameworks/energia + Version: 1 + + +``platformio uninstall`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +To uninstall platform: + +.. code-block:: bash + + $ platformio uninstall SomePlatform + + # Example + $ platformio uninstall timsp430 + Uninstalling toolchain-timsp430 package: [OK] + Uninstalling tool-mspdebug package: [OK] + Uninstalling framework-energiamsp430 package: [OK] + The platform 'timsp430' has been successfully uninstalled! + + +``platformio init`` +~~~~~~~~~~~~~~~~~~~ + +Initialize new platformio based project. + +.. code-block:: bash + + # Change directory to future project + $ cd /path/to/empty/directory + $ platformio init + + # Example + $ platformio init + Project successfully initialized. + Please put your source code to `src` directory, external libraries to `libs` + and setup environments in `platformio.ini` file. + Then process project with `platformio run` command. + +After this command ``platformio`` will create: + +* ``.pioenvs`` - a temporary working directory. +* ``libs`` - a directory for project specific libraries. Platformio will + compile their to static libraries and link to executable file +* ``src`` - a source directory. Put code here. +* ``platformio.ini`` - a configuration file for project + + +``platformio run`` +~~~~~~~~~~~~~~~~~~ + +Process the project's environments defined in ``platformio.ini`` file: + +.. code-block:: bash + + $ platformio run + + # Example + $ platformio run + Processing arduino_pro5v environment: + scons: `.pioenvs/arduino_pro5v/firmware.elf' is up to date. + scons: `.pioenvs/arduino_pro5v/firmware.hex' is up to date. + + Processing launchpad_msp430g2 environment: + scons: `.pioenvs/launchpad_msp430g2/firmware.elf' is up to date. + scons: `.pioenvs/launchpad_msp430g2/firmware.hex' is up to date. + + Processing launchpad_lm4f120 environment: + scons: `.pioenvs/launchpad_lm4f120/firmware.elf' is up to date. + scons: `.pioenvs/launchpad_lm4f120/firmware.hex' is up to date + +Process specific environments: + +.. code-block:: bash + + $ platformio run -e myenv1 -e myenv2 + + # Example + $ platformio run -e arduino_pro5v -e launchpad_lm4f120 + Processing arduino_pro5v environment: + scons: `.pioenvs/arduino_pro5v/firmware.elf' is up to date. + scons: `.pioenvs/arduino_pro5v/firmware.hex' is up to date. + + Skipped launchpad_msp430g2 environment + Processing launchpad_lm4f120 environment: + scons: `.pioenvs/launchpad_lm4f120/firmware.elf' is up to date. + scons: `.pioenvs/launchpad_lm4f120/firmware.hex' is up to date. + +Process specific target: + +.. code-block:: bash + + $ platformio run -t clean + $ platformio run -t upload + + # Example + platformio run -t clean + Processing arduino_pro5v environment: + Removed .pioenvs/arduino_pro5v/src/main.o + ... + Removed .pioenvs/arduino_pro5v/firmware.hex + + Processing launchpad_msp430g2 environment: + Removed .pioenvs/launchpad_msp430g2/src/main.o + ... + Removed .pioenvs/launchpad_msp430g2/firmware.hex + + Processing launchpad_lm4f120 environment: + Removed .pioenvs/launchpad_lm4f120/src/main.o + ... + Removed .pioenvs/launchpad_lm4f120/firmware.hex + +Mix environments and targets: + +.. code-block:: bash + + $ platformio run -e myembeddeddevice -t upload + + # Example + $ platformio run -e launchpad_msp430g2 -t upload + Skipped arduino_pro5v environment + Processing launchpad_msp430g2 environment: + /Users/ikravets/.platformio/timsp430/tools/mspdebug/mspdebug rf2500 --force-reset "prog .pioenvs/launchpad_msp430g2/firmware.hex" + MSPDebug version 0.20 - debugging tool for MSP430 MCUs + Copyright (C) 2009-2012 Daniel Beer + This is free software; see the source for copying conditions. There is NO + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + Trying to open interface 1 on 009 + Initializing FET... + FET protocol version is 30394216 + Configured for Spy-Bi-Wire + Sending reset... + Set Vcc: 3000 mV + Device ID: 0x2553 + Code start address: 0xc000 + Code size : 16384 byte = 16 kb + RAM start address: 0x200 + RAM end address: 0x3ff + RAM size : 512 byte = 0 kb + Device: MSP430G2553/G2403 + Code memory starts at 0xc000 + Number of breakpoints: 2 + Chip ID data: 25 53 + Erasing... + Programming... + Writing 646 bytes at c000... + Writing 32 bytes at ffe0... + Done, 678 bytes total + + Skipped launchpad_lm4f120 environment + + +``platformio update`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +To check or update installed platforms: + +.. code-block:: bash + + $ platformio update + + # Example + $ platformio update + + Platform atmelavr + -------- + Updating toolchain-atmelavr package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating framework-arduinoavr package: + Versions: Current=1, Latest=1 [Up-to-date] + Updating tool-avrdude 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=1, Latest=1 [Up-to-date] + + Platform titiva + -------- + 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-energiativa package: + Versions: Current=1, Latest=1 [Up-to-date] + + +Questions & Bugs +---------------- + +Please use the +`issue tracker `_ +to ask questions or report bugs. + + +Licence +------- + +Copyright (C) 2014 Ivan Kravets + +Licenced under the MIT Licence. diff --git a/examples/atmelavr-native-blink/README.rst b/examples/atmelavr-native-blink/README.rst new file mode 100644 index 00000000..6ab96193 --- /dev/null +++ b/examples/atmelavr-native-blink/README.rst @@ -0,0 +1,20 @@ +Atmel AVR: Native Blink Example +=============================== + +1. Download ``platformio`` + `sources `_ +2. Extract ZIP archive +3. Then run these commands: + +.. code-block:: bash + + # Change directory to example + $ cd platformio-develop/examples/atmelavr-native-blink/ + + # Install Atmel AVR development platform + $ platformio install atmelavr + + # Process example project + $ platformio run + +.. image:: console-result.png diff --git a/examples/atmelavr-native-blink/console-result.png b/examples/atmelavr-native-blink/console-result.png new file mode 100644 index 00000000..189a8c82 Binary files /dev/null and b/examples/atmelavr-native-blink/console-result.png differ diff --git a/examples/atmelavr-native-blink/platformio.ini b/examples/atmelavr-native-blink/platformio.ini new file mode 100644 index 00000000..6c73192c --- /dev/null +++ b/examples/atmelavr-native-blink/platformio.ini @@ -0,0 +1,14 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +[env:arduino_pro5v] +platform = atmelavr +board_mcu = atmega168 +board_f_cpu = 16000000L + +upload_port = /dev/tty.SLAB_USBtoUART +# upload_port = COM3 +upload_protocol = arduino +upload_speed = 19200 + +targets = upload diff --git a/examples/atmelavr-native-blink/src/main.c b/examples/atmelavr-native-blink/src/main.c new file mode 100644 index 00000000..757d3c5f --- /dev/null +++ b/examples/atmelavr-native-blink/src/main.c @@ -0,0 +1,23 @@ +/** + * Copyright (C) Ivan Kravets + * See LICENSE for details. + */ + +#include +#include + +int main(void) +{ + // make the LED pin an output for PORTB5 + DDRB = 1 << 5; + + while (1) + { + _delay_ms(500); + + // toggle the LED + PORTB ^= 1 << 5; + } + + return 0; +} diff --git a/examples/timsp430-native-blink/README.rst b/examples/timsp430-native-blink/README.rst new file mode 100644 index 00000000..a522f49f --- /dev/null +++ b/examples/timsp430-native-blink/README.rst @@ -0,0 +1,20 @@ +TI MSP430: Native Blink Example +=============================== + +1. Download ``platformio`` + `sources `_ +2. Extract ZIP archive +3. Then run these commands: + +.. code-block:: bash + + # Change directory to example + $ cd platformio-develop/examples/timsp430-native-blink/ + + # Install TI MSP430 development platform + $ platformio install timsp430 + + # Process example project + $ platformio run + +.. image:: console-result.png diff --git a/examples/timsp430-native-blink/console-result.png b/examples/timsp430-native-blink/console-result.png new file mode 100644 index 00000000..c658dcd0 Binary files /dev/null and b/examples/timsp430-native-blink/console-result.png differ diff --git a/examples/timsp430-native-blink/platformio.ini b/examples/timsp430-native-blink/platformio.ini new file mode 100644 index 00000000..e1343405 --- /dev/null +++ b/examples/timsp430-native-blink/platformio.ini @@ -0,0 +1,11 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +[env:launchpad_msp430g2] +platform = timsp430 +board_mcu = msp430g2553 +board_f_cpu = 16000000L + +upload_protocol = rf2500 + +targets = upload diff --git a/examples/timsp430-native-blink/src/main.c b/examples/timsp430-native-blink/src/main.c new file mode 100644 index 00000000..38a421a5 --- /dev/null +++ b/examples/timsp430-native-blink/src/main.c @@ -0,0 +1,26 @@ +/** + * Copyright (C) Ivan Kravets + * See LICENSE for details. + */ + +#include + +int main(void) +{ + WDTCTL = WDTPW + WDTHOLD; + + // make the LED pin an output for P1.0 + P1DIR |= 0x01; + + volatile int i; + + while (1) + { + for (i = 0; i < 10000; i++); + + // toggle the LED + P1OUT ^= 0x01; + } + + return 0; +} diff --git a/examples/titiva-native-blink/README.rst b/examples/titiva-native-blink/README.rst new file mode 100644 index 00000000..9f676e17 --- /dev/null +++ b/examples/titiva-native-blink/README.rst @@ -0,0 +1,20 @@ +TI TIVA C ARM Cortex-M4: Native Blink Example +============================================= + +1. Download ``platformio`` + `sources `_ +2. Extract ZIP archive +3. Then run these commands: + +.. code-block:: bash + + # Change directory to example + $ cd platformio-develop/examples/titiva-native-blink/ + + # Install TI TIVA development platform + $ platformio install titiva + + # Process example project + $ platformio run + +.. image:: console-result.png diff --git a/examples/titiva-native-blink/console-result.png b/examples/titiva-native-blink/console-result.png new file mode 100644 index 00000000..4b9ff484 Binary files /dev/null and b/examples/titiva-native-blink/console-result.png differ diff --git a/examples/titiva-native-blink/platformio.ini b/examples/titiva-native-blink/platformio.ini new file mode 100644 index 00000000..7e915752 --- /dev/null +++ b/examples/titiva-native-blink/platformio.ini @@ -0,0 +1,8 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +[env:launchpad_lm4f120] +platform = titiva +framework = energia +board = lplm4f120h5qr +targets = upload diff --git a/examples/titiva-native-blink/src/main.c b/examples/titiva-native-blink/src/main.c new file mode 100644 index 00000000..5f3edee5 --- /dev/null +++ b/examples/titiva-native-blink/src/main.c @@ -0,0 +1,39 @@ +/** + * Copyright (C) Ivan Kravets + * See LICENSE for details. + */ + +#include +#include +#include "inc/hw_memmap.h" +#include "driverlib/gpio.h" +#include "driverlib/sysctl.h" + +#define LED_RED GPIO_PIN_1 +#define LED_BLUE GPIO_PIN_2 +#define LED_GREEN GPIO_PIN_3 + +int main(void) +{ + SysCtlClockSet( + SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN); + + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); + GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, LED_RED|LED_BLUE|LED_GREEN); + + while (1) + { + GPIOPinWrite(GPIO_PORTF_BASE, LED_RED|LED_BLUE|LED_GREEN, LED_RED); + SysCtlDelay(3000000); + GPIOPinWrite(GPIO_PORTF_BASE, LED_RED|LED_BLUE|LED_GREEN, LED_BLUE); + SysCtlDelay(3000000); + GPIOPinWrite(GPIO_PORTF_BASE, LED_RED|LED_BLUE|LED_GREEN, LED_GREEN); + SysCtlDelay(3000000); + } + + return 0; +} + +// hook for Energia main.cpp where these methods are defined +void setup() {} +void loop() {} diff --git a/examples/wiring-blink/README.rst b/examples/wiring-blink/README.rst new file mode 100644 index 00000000..1f7ec106 --- /dev/null +++ b/examples/wiring-blink/README.rst @@ -0,0 +1,26 @@ +Wiring Framework (Arduino + Energia) Blink Example +================================================== + +1. Download ``platformio`` + `sources `_ +2. Extract ZIP archive +3. Then run these commands: + +.. code-block:: bash + + # Change directory to example + $ cd platformio-develop/examples/wiring-blink/ + + # Install Atmel AVR development platform with Arduino Framework + $ platformio install atmelavr --with-package=framework-arduinoavr + + # Install TI MSP430 development platform with Energia Framework + $ platformio install timsp430 --with-package=framework-energiamsp430 + + # Install TI TIVA development platform with Energia Framework + $ platformio install titiva --with-package=framework-energiativa + + # Process example project + $ platformio run + +.. image:: console-result.png diff --git a/examples/wiring-blink/console-result.png b/examples/wiring-blink/console-result.png new file mode 100644 index 00000000..1ca15925 Binary files /dev/null and b/examples/wiring-blink/console-result.png differ diff --git a/examples/wiring-blink/platformio.ini b/examples/wiring-blink/platformio.ini new file mode 100644 index 00000000..5cf7ab39 --- /dev/null +++ b/examples/wiring-blink/platformio.ini @@ -0,0 +1,18 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +[env:arduino_pro5v] +platform = atmelavr +framework = arduino +board = pro16MHzatmega168 +upload_port = /dev/tty.SLAB_USBtoUART + +[env:launchpad_msp430g2] +platform = timsp430 +framework = energia +board = lpmsp430g2553 + +[env:launchpad_lm4f120] +platform = titiva +framework = energia +board = lplm4f120h5qr diff --git a/examples/wiring-blink/src/main.c b/examples/wiring-blink/src/main.c new file mode 100644 index 00000000..a86fe009 --- /dev/null +++ b/examples/wiring-blink/src/main.c @@ -0,0 +1,34 @@ +/** + * Copyright (C) Ivan Kravets + * See LICENSE for details. + */ + +/** + Turns ON and OFF the Wiring compatible board LED, + with intervals of 1 second (1000 milliseconds) +*/ + +#ifdef ENERGIA + +#include "Energia.h" +#define WLED RED_LED + +#else + +#include "Arduino.h" +#define WLED 13 // Most Arduino boards already have an LED attached to pin 13 on the board itself + +#endif + +void setup() +{ + pinMode(WLED, OUTPUT); // set pin as output +} + +void loop() +{ + digitalWrite(WLED, HIGH); // set the LED on + delay(1000); // wait for a second + digitalWrite(WLED, LOW); // set the LED off + delay(1000); // wait for a second +} diff --git a/platformio/__init__.py b/platformio/__init__.py new file mode 100644 index 00000000..22247f5e --- /dev/null +++ b/platformio/__init__.py @@ -0,0 +1,18 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +VERSION = (0, 1, 0) +__version__ = ".".join([str(s) for s in VERSION]) + +__title__ = "platformio" +__description__ = ("A console tool to build code with different " + "development platforms") +__url__ = "https://github.com/ivankravets/platformio" + +__author__ = "Ivan Kravets" +__email__ = "me@ikravets.com" + +__license__ = "MIT Licence" +__copyright__ = "Copyright (C) 2014 Ivan Kravets" + +__pkgmanifesturl__ = "http://platformio.ikravets.com/packages/manifest.json" diff --git a/platformio/__main__.py b/platformio/__main__.py new file mode 100644 index 00000000..a7a74f2a --- /dev/null +++ b/platformio/__main__.py @@ -0,0 +1,54 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from os import listdir +from os.path import join +from sys import exit as sys_exit +from traceback import format_exc + +from click import command, MultiCommand, style, version_option + +from platformio import __version__ +from platformio.exception import PlatformioException, UnknownCLICommand +from platformio.util import get_source_dir + + +class PlatformioCLI(MultiCommand): + + def list_commands(self, ctx): + cmds = [] + for filename in listdir(join(get_source_dir(), "commands")): + if filename.startswith("__init__"): + continue + if filename.endswith(".py"): + cmds.append(filename[:-3]) + cmds.sort() + return cmds + + def get_command(self, ctx, name): + try: + mod = __import__("platformio.commands." + name, + None, None, ["cli"]) + except ImportError: + raise UnknownCLICommand(name) + return mod.cli + + +@command(cls=PlatformioCLI) +@version_option(__version__) +def cli(): + pass + + +def main(): + try: + cli() + except Exception as e: # pylint: disable=W0703 + if isinstance(e, PlatformioException): + sys_exit(style("Error: ", fg="red") + str(e)) + else: + print format_exc() + + +if __name__ == "__main__": + sys_exit(main()) diff --git a/platformio/builder/__init__.py b/platformio/builder/__init__.py new file mode 100644 index 00000000..ca6f0304 --- /dev/null +++ b/platformio/builder/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. diff --git a/platformio/builder/main.py b/platformio/builder/main.py new file mode 100644 index 00000000..54ab2722 --- /dev/null +++ b/platformio/builder/main.py @@ -0,0 +1,58 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from os.path import isdir, join + +from SCons.Script import (DefaultEnvironment, Exit, SConscript, + SConscriptChdir, Variables) + +from platformio.util import get_home_dir, get_project_dir, get_source_dir + +# AllowSubstExceptions() + +# allow common variables from INI file +commonvars = Variables(None) +commonvars.AddVariables( + ("PIOENV",), + ("PLATFORM",), + ("FRAMEWORK",), + + # board options + ("BOARD",), + ("BOARD_MCU",), + ("BOARD_F_CPU",), + + # upload options + ("UPLOAD_PORT",), + ("UPLOAD_PROTOCOL",), + ("UPLOAD_SPEED",) +) + +DefaultEnvironment( + tools=["default", "platformio"], + toolpath=[join("$PIOBUILDER_DIR", "tools")], + variables=commonvars, + + PIOBUILDER_DIR=join(get_source_dir(), "builder"), + PROJECT_DIR=get_project_dir(), + + PLATFORMIOHOME_DIR=get_home_dir(), + PLATFORM_DIR=join("$PLATFORMIOHOME_DIR", "$PLATFORM"), + PLATFORMFW_DIR=join("$PLATFORM_DIR", "frameworks", "$FRAMEWORK"), + PLATFORMTOOLS_DIR=join("$PLATFORM_DIR", "tools"), + + BUILD_DIR=join("$PROJECT_DIR", ".pioenvs", "$PIOENV") +) + +env = DefaultEnvironment() + +if not isdir(env['PLATFORMIOHOME_DIR']): + Exit("You haven't installed any platforms yet. Please use " + "`platformio install` command") +elif not isdir(env.subst("$PLATFORM_DIR")): + Exit("An '%s' platform hasn't been installed yet. Please use " + "`platformio install %s` command" % (env['PLATFORM'], + env['PLATFORM'])) + +SConscriptChdir(0) +SConscript(env.subst(join("$PIOBUILDER_DIR", "scripts", "${PLATFORM}.py"))) diff --git a/platformio/builder/scripts/__init__.py b/platformio/builder/scripts/__init__.py new file mode 100644 index 00000000..ca6f0304 --- /dev/null +++ b/platformio/builder/scripts/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. diff --git a/platformio/builder/scripts/atmelavr.py b/platformio/builder/scripts/atmelavr.py new file mode 100644 index 00000000..fdc03f53 --- /dev/null +++ b/platformio/builder/scripts/atmelavr.py @@ -0,0 +1,160 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +""" + Builder for Atmel AVR series of microcontrollers +""" + +from os.path import join + +from SCons.Script import (AlwaysBuild, Builder, COMMAND_LINE_TARGETS, Default, + DefaultEnvironment, Exit, SConscript, + SConscriptChdir) + +env = DefaultEnvironment() + +env.Replace( + AR="avr-ar", + AS="avr-gcc", + CC="avr-gcc", + CXX="avr-g++", + OBJCOPY="avr-objcopy", + RANLIB="avr-ranlib", + + ARFLAGS=["rcs"], + + ASFLAGS=[ + "-g", # include debugging info (so errors include line numbers) + "-x", "assembler-with-cpp", + "-mmcu=$BOARD_MCU", + "-DF_CPU=$BOARD_F_CPU" + ], + CCFLAGS=[ + "-g", # include debugging info (so errors include line numbers) + "-Os", # optimize for size + "-Wall", # show warnings + "-ffunction-sections", # place each function in its own section + "-fdata-sections", + "-MMD", # output dependancy info + "-mmcu=$BOARD_MCU", + "-DF_CPU=$BOARD_F_CPU" + ], + CXXFLAGS=["-fno-exceptions"], + + LINKFLAGS=[ + "-Os", + "-Wl,--gc-sections", + "-mmcu=$BOARD_MCU" + ], + + UPLOADER=join("$PLATFORMTOOLS_DIR", "avrdude", "avrdude"), + UPLOADERFLAGS=[ + "-V", # do not verify + "-q", # suppress progress output + "-D", # disable auto erase for flash memory + "-p", "$BOARD_MCU", + "-C", join("$PLATFORMTOOLS_DIR", "avrdude", "avrdude.conf"), + "-c", "$UPLOAD_PROTOCOL", + "-b", "$UPLOAD_SPEED", + "-P", "$UPLOAD_PORT" + ], + UPLOADHEXCMD="$UPLOADER $UPLOADERFLAGS -U flash:w:$SOURCES:i", + UPLOADEEPCMD="$UPLOADER $UPLOADERFLAGS -U eeprom:w:$SOURCES:i" +) + +env.Append( + BUILDERS=dict( + ElfToEep=Builder( + action=" ".join([ + "$OBJCOPY", + "-O", + "ihex", + "-j", + ".eeprom", + '--set-section-flags=.eeprom="alloc,load"', + "--no-change-warnings", + "--change-section-lma", + ".eeprom=0", + "$SOURCES", + "$TARGET"]), + suffix=".eep" + ), + + ElfToHex=Builder( + action=" ".join([ + "$OBJCOPY", + "-O", + "ihex", + "-R", + ".eeprom", + "$SOURCES", + "$TARGET"]), + suffix=".hex" + ) + ) +) + +env.PrependENVPath( + "PATH", + join(env.subst("$PLATFORMTOOLS_DIR"), "toolchain", "bin") +) + +BUILT_LIBS = [] + +# +# Process framework script +# + +if "FRAMEWORK" in env: + SConscriptChdir(0) + flibs = SConscript(env.subst(join("$PIOBUILDER_DIR", "scripts", + "frameworks", "${FRAMEWORK}.py")), + exports="env") + BUILT_LIBS += flibs + +# +# Target: Build executable and linkable firmware +# + +target_elf = env.BuildFirmware(BUILT_LIBS + ["m"]) + +# +# Target: Extract EEPROM data (from EEMEM directive) to .eep file +# + +target_eep = env.ElfToEep(join("$BUILD_DIR", "firmware"), target_elf) + +# +# Target: Build the .hex file +# + +target_hex = env.ElfToHex(join("$BUILD_DIR", "firmware"), target_elf) + +# +# Target: Upload .eep file +# + +eep = env.Alias("eep", target_eep, [ + lambda target, source, env: env.ResetDevice(), "$UPLOADEEPCMD"]) +AlwaysBuild(eep) + +# +# Target: Upload .hex file +# + +upload = env.Alias("upload", target_hex, [ + lambda target, source, env: env.ResetDevice(), "$UPLOADHEXCMD"]) +AlwaysBuild(upload) + +# +# Target: Define targets +# + +env.Alias("build-eep", [target_eep]) +Default([target_elf, target_hex]) + +# check for $UPLOAD_PORT variable +is_uptarget = ("eep" in COMMAND_LINE_TARGETS or "upload" in + COMMAND_LINE_TARGETS) +if is_uptarget and not env.subst("$UPLOAD_PORT"): + Exit("Please specify 'upload_port'") diff --git a/platformio/builder/scripts/frameworks/__init__.py b/platformio/builder/scripts/frameworks/__init__.py new file mode 100644 index 00000000..ca6f0304 --- /dev/null +++ b/platformio/builder/scripts/frameworks/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. diff --git a/platformio/builder/scripts/frameworks/arduino.py b/platformio/builder/scripts/frameworks/arduino.py new file mode 100644 index 00000000..fc7b6bbd --- /dev/null +++ b/platformio/builder/scripts/frameworks/arduino.py @@ -0,0 +1,65 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +""" + Build script for Android Framework (based on Wiring). +""" + +from os.path import join + +from SCons.Script import Import, Return + +env = None +Import("env") + +BOARD_OPTIONS = env.ParseBoardOptions( + join("$PLATFORMFW_DIR", "boards.txt"), + "${BOARD}" +) +ARDUINO_VERSION = int( + open(join(env.subst("$PLATFORMFW_DIR"), + "version.txt")).read().replace(".", "").strip()) +ARDUINO_FLAGS = [ + "-DARDUINO=%d" % ARDUINO_VERSION, + "-DARDUINO_%s" % BOARD_OPTIONS['build.board'] +] +# usb flags +if "build.usb_product" in BOARD_OPTIONS: + ARDUINO_FLAGS += [ + "-DUSB_VID=%s" % BOARD_OPTIONS['build.vid'], + "-DUSB_PID=%s" % BOARD_OPTIONS['build.pid'], + "-DUSB_PRODUCT=%s" % BOARD_OPTIONS['build.usb_product'].replace( + '"', "") + ] + +env.Append( + ASFLAGS=ARDUINO_FLAGS, + CCFLAGS=ARDUINO_FLAGS, + CPPPATH=[ + join("$BUILD_DIR", "core"), + join("$PLATFORMFW_DIR", "variants", BOARD_OPTIONS['build.variant']) + ] +) + +if "BOARD_MCU" not in env: + env.Replace(BOARD_MCU=BOARD_OPTIONS['build.mcu']) +if "BOARD_F_CPU" not in env: + env.Replace(BOARD_F_CPU=BOARD_OPTIONS['build.f_cpu']) +if "UPLOAD_PROTOCOL" not in env: + env.Replace(UPLOAD_PROTOCOL=BOARD_OPTIONS['upload.protocol']) +if "UPLOAD_SPEED" not in env: + env.Replace(UPLOAD_SPEED=BOARD_OPTIONS['upload.speed']) + + +libs = [] + +# +# Target: Build Core Library +# + +libs.append(env.BuildLibrary( + join("$BUILD_DIR", "core"), + join("$PLATFORMFW_DIR", "cores", BOARD_OPTIONS['build.core']) +)) + +Return("libs") diff --git a/platformio/builder/scripts/frameworks/energia.py b/platformio/builder/scripts/frameworks/energia.py new file mode 100644 index 00000000..f69bed88 --- /dev/null +++ b/platformio/builder/scripts/frameworks/energia.py @@ -0,0 +1,63 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +""" + Build script for Energia Framework (based on Wiring). +""" + +from os.path import join + +from SCons.Script import Import, Return + +env = None +Import("env") + +BOARD_OPTIONS = env.ParseBoardOptions( + join("$PLATFORMFW_DIR", "boards.txt"), + "${BOARD}" +) +ENERGIA_VERSION = int( + open(join(env.subst("$PLATFORMFW_DIR"), + "version.txt")).read().replace(".", "").strip()) +ENERGIA_FLAGS = [ + "-DARDUINO=101", + "-DENERGIA=%d" % ENERGIA_VERSION +] + +env.Append( + ASFLAGS=ENERGIA_FLAGS, + CCFLAGS=ENERGIA_FLAGS, + CPPPATH=[ + join("$BUILD_DIR", "core"), + join("$PLATFORMFW_DIR", "variants", BOARD_OPTIONS['build.variant']) + ] +) + +if "BOARD_MCU" not in env: + env.Replace(BOARD_MCU=BOARD_OPTIONS['build.mcu']) +if "BOARD_F_CPU" not in env: + env.Replace(BOARD_F_CPU=BOARD_OPTIONS['build.f_cpu']) +if "UPLOAD_PROTOCOL" not in env and "upload.protocol" in BOARD_OPTIONS: + env.Replace(UPLOAD_PROTOCOL=BOARD_OPTIONS['upload.protocol']) + +# specific linker script for TIVA devices +if "ldscript" in BOARD_OPTIONS: + env.Append( + LINKFLAGS=["-T", join("$PLATFORMFW_DIR", "cores", + BOARD_OPTIONS['build.core'], + BOARD_OPTIONS['ldscript'])] + ) + + +libs = [] + +# +# Target: Build Core Library +# + +libs.append(env.BuildLibrary( + join("$BUILD_DIR", "core"), + join("$PLATFORMFW_DIR", "cores", BOARD_OPTIONS['build.core']) +)) + +Return("libs") diff --git a/platformio/builder/scripts/timsp430.py b/platformio/builder/scripts/timsp430.py new file mode 100644 index 00000000..d251de3b --- /dev/null +++ b/platformio/builder/scripts/timsp430.py @@ -0,0 +1,118 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +""" + Builder for Texas Instruments + MSP430 Ultra-Low Power 16-bit microcontrollers +""" + +from os.path import join + +from SCons.Script import (AlwaysBuild, Builder, Default, DefaultEnvironment, + SConscript, SConscriptChdir) + +from platformio.util import get_system + +env = DefaultEnvironment() + +env.Replace( + AR="msp430-ar", + AS="msp430-gcc", + CC="msp430-gcc", + CXX="msp430-g++", + OBJCOPY="msp430-objcopy", + RANLIB="msp430-ranlib", + + ARFLAGS=["rcs"], + + ASFLAGS=[ + "-g", # include debugging info (so errors include line numbers) + "-x", "-assembler-with-cpp", + "-mmcu=$BOARD_MCU", + "-DF_CPU=$BOARD_F_CPU" + ], + CCFLAGS=[ + "-g", # include debugging info (so errors include line numbers) + "-Os", # optimize for size + "-Wall", # show warnings + "-ffunction-sections", # place each function in its own section + "-fdata-sections", + "-MMD", # output dependancy info + "-mmcu=$BOARD_MCU", + "-DF_CPU=$BOARD_F_CPU" + ], + + LINK="$CC", + LINKFLAGS=[ + "-Os", + "-mmcu=$BOARD_MCU", + "-Wl,-gc-sections,-u,main" + ], + + UPLOADER=join("$PLATFORMTOOLS_DIR", "mspdebug", "mspdebug"), + UPLOADERFLAGS=[ + "$UPLOAD_PROTOCOL" if get_system() != "windows32" else "tilib", + "--force-reset" + ], + UPLOADCMD='$UPLOADER $UPLOADERFLAGS "prog $SOURCES"' +) + +env.Append( + BUILDERS=dict( + ElfToHex=Builder( + action=" ".join([ + "$OBJCOPY", + "-O", + "ihex", + "-R", + ".eeprom", + "$SOURCES", + "$TARGET"]), + suffix=".hex" + ) + ) +) + +env.PrependENVPath( + "PATH", + join(env.subst("$PLATFORMTOOLS_DIR"), "toolchain", "bin") +) + +BUILT_LIBS = [] + +# +# Process framework script +# + +if "FRAMEWORK" in env: + SConscriptChdir(0) + flibs = SConscript(env.subst(join("$PIOBUILDER_DIR", "scripts", + "frameworks", "${FRAMEWORK}.py")), + exports="env") + BUILT_LIBS += flibs + + +# +# Target: Build executable and linkable firmware +# + +target_elf = env.BuildFirmware(BUILT_LIBS + ["m"]) + +# +# Target: Build the .hex +# + +target_hex = env.ElfToHex(join("$BUILD_DIR", "firmware"), target_elf) + +# +# Target: Upload firmware +# + +upload = env.Alias("upload", target_hex, ["$UPLOADCMD"]) +AlwaysBuild(upload) + +# +# Target: Define targets +# + +Default([target_elf, target_hex]) diff --git a/platformio/builder/scripts/titiva.py b/platformio/builder/scripts/titiva.py new file mode 100644 index 00000000..c23ba8a6 --- /dev/null +++ b/platformio/builder/scripts/titiva.py @@ -0,0 +1,131 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +""" + Builder for Texas Instruments + Tiva C Series ARM Cortex-M4 microcontrollers. +""" + +from os.path import join + +from SCons.Script import (AlwaysBuild, Builder, Default, DefaultEnvironment, + SConscript, SConscriptChdir) + +env = DefaultEnvironment() + +env.Replace( + AR="arm-none-eabi-ar", + AS="arm-none-eabi-gcc", + CC="arm-none-eabi-gcc", + CXX="arm-none-eabi-g++", + OBJCOPY="arm-none-eabi-objcopy", + RANLIB="arm-none-eabi-ranlib", + + ARFLAGS=["rcs"], + + ASFLAGS=[ + "-g", # include debugging info (so errors include line numbers) + "-x", "assembler-with-cpp", + "-Wall", + "-mthumb", + "-mcpu=cortex-m4", + "-mfloat-abi=hard", + "-mfpu=fpv4-sp-d16", + "-fsingle-precision-constant", + "-DF_CPU=$BOARD_F_CPU" + ], + + CCFLAGS=[ + "-g", # include debugging info (so errors include line numbers) + "-Os", # optimize for size + "-Wall", # show warnings + "-ffunction-sections", # place each function in its own section + "-fdata-sections", + "-Wall", + "-mthumb", + "-mcpu=cortex-m4", + "-mfloat-abi=hard", + "-mfpu=fpv4-sp-d16", + "-fsingle-precision-constant", + "-MMD", # output dependancy info + "-DF_CPU=$BOARD_F_CPU" + ], + + CXXFLAGS=[ + "-fno-rtti", + "-fno-exceptions" + ], + + LINKFLAGS=[ + "-Os", + "-nostartfiles", + "-nostdlib", + "-Wl,--gc-sections", + "-Wl,--entry=ResetISR", + "-mthumb", + "-mcpu=cortex-m4", + "-mfloat-abi=hard", + "-mfpu=fpv4-sp-d16", + "-fsingle-precision-constant" + ], + + UPLOADER=join("$PLATFORMTOOLS_DIR", "lm4flash", "lm4flash"), + UPLOADCMD="$UPLOADER $SOURCES" +) + +env.Append( + BUILDERS=dict( + ElfToBin=Builder( + action=" ".join([ + "$OBJCOPY", + "-O", + "binary", + "$SOURCES", + "$TARGET"]), + suffix=".hex" + ) + ) +) + +env.PrependENVPath( + "PATH", + join(env.subst("$PLATFORMTOOLS_DIR"), "toolchain", "bin") +) + +BUILT_LIBS = [] + +# +# Process framework script +# + +if "FRAMEWORK" in env: + SConscriptChdir(0) + flibs = SConscript(env.subst(join("$PIOBUILDER_DIR", "scripts", + "frameworks", "${FRAMEWORK}.py")), + exports="env") + BUILT_LIBS += flibs + +# +# Target: Build executable and linkable firmware +# + +target_elf = env.BuildFirmware(BUILT_LIBS + ["c", "gcc", "m"]) + +# +# Target: Build the .bin file +# + +target_bin = env.ElfToBin(join("$BUILD_DIR", "firmware"), target_elf) + +# +# Target: Upload firmware +# + +upload = env.Alias("upload", target_bin, ["$UPLOADCMD"]) +AlwaysBuild(upload) + +# +# Target: Define targets +# + +Default([target_elf, target_bin]) diff --git a/platformio/builder/tools/__init__.py b/platformio/builder/tools/__init__.py new file mode 100644 index 00000000..ca6f0304 --- /dev/null +++ b/platformio/builder/tools/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. diff --git a/platformio/builder/tools/platformio.py b/platformio/builder/tools/platformio.py new file mode 100644 index 00000000..a9466ee0 --- /dev/null +++ b/platformio/builder/tools/platformio.py @@ -0,0 +1,109 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from os import walk +from os.path import isfile, join +from time import sleep + +from serial import Serial + + +def BuildLibrary(env, variant_dir, library_dir): + lib = env.Clone() + vdirs = lib.VariantDirRecursive(variant_dir, library_dir) + return lib.Library( + lib.subst(variant_dir), + [lib.GlobCXXFiles(vdir) for vdir in vdirs] + ) + + +def BuildFirmware(env, libslist): + src = env.Clone() + vdirs = src.VariantDirRecursive(join("$BUILD_DIR", "src"), + join("$PROJECT_DIR", "src")) + return src.Program( + join("$BUILD_DIR", "firmware"), + [src.GlobCXXFiles(vdir) for vdir in vdirs], + LIBS=libslist, + LIBPATH="$BUILD_DIR", + PROGSUFFIX=".elf") + + +def GlobCXXFiles(env, path): + files = [] + for suff in ["*.c", "*.cpp", "*.S"]: + _list = env.Glob(join(path, suff)) + if _list: + files += _list + return files + + +def VariantDirRecursive(env, variant_dir, src_dir, duplicate=True): + # add root dir by default + variants = [variant_dir] + env.VariantDir(variant_dir, src_dir, duplicate) + for root, dirnames, _ in walk(env.subst(src_dir)): + if not dirnames: + continue + for dn in dirnames: + env.VariantDir(join(variant_dir, dn), join(root, dn), duplicate) + variants.append(join(variant_dir, dn)) + return variants + + +def ParseBoardOptions(env, path, name): + path = env.subst(path) + name = env.subst(name) + if not isfile(path): + env.Exit("Invalid path to boards.txt -> %s" % path) + + data = {} + with open(path) as f: + for line in f: + if not line.strip() or line[0] == "#": + continue + + _group = line[:line.index(".")] + _cpu = name[len(_group):] + line = line[len(_group)+1:].strip() + if _group != name[:len(_group)]: + continue + elif "menu.cpu." in line: + if _cpu not in line: + continue + else: + line = line[len(_cpu)+10:] + + if "=" in line: + opt, value = line.split("=", 1) + data[opt] = value + if not data: + env.Exit("Unknown Board '%s'" % name) + else: + return data + + +def ResetDevice(env): + """ Pulse the DTR line and flush serial buffer """ + s = Serial(env.subst("$UPLOAD_PORT")) + s.flushInput() + s.setDTR(False) + s.setRTS(False) + sleep(0.1) + s.setDTR(True) + s.setRTS(True) + s.close() + + +def exists(_): + return True + + +def generate(env): + env.AddMethod(BuildLibrary) + env.AddMethod(BuildFirmware) + env.AddMethod(GlobCXXFiles) + env.AddMethod(VariantDirRecursive) + env.AddMethod(ParseBoardOptions) + env.AddMethod(ResetDevice) + return env diff --git a/platformio/commands/__init__.py b/platformio/commands/__init__.py new file mode 100644 index 00000000..ca6f0304 --- /dev/null +++ b/platformio/commands/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. diff --git a/platformio/commands/init.py b/platformio/commands/init.py new file mode 100644 index 00000000..10e3670a --- /dev/null +++ b/platformio/commands/init.py @@ -0,0 +1,30 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from os import makedirs +from os.path import isdir, isfile, join +from shutil import copyfile + +from click import command, secho + +from platformio.exception import ProjectInitialized +from platformio.util import get_source_dir + + +@command("init", short_help="Initialize new platformio based project") +def cli(): + + if isfile("platformio.ini") and isdir("src"): + raise ProjectInitialized() + for d in (".pioenvs", "libs", "src"): + if not isdir(d): + makedirs(d) + if not isfile("platformio.ini"): + copyfile(join(get_source_dir(), "projectconftpl.ini"), + "platformio.ini") + secho("Project successfully initialized.\n" + "Please put your source code to `src` directory, " + "external libraries to `libs` and " + "setup environments in `platformio.ini` file.\n" + "Then process project with `platformio run` command.", + fg="green") diff --git a/platformio/commands/install.py b/platformio/commands/install.py new file mode 100644 index 00000000..e4501314 --- /dev/null +++ b/platformio/commands/install.py @@ -0,0 +1,19 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from click import argument, command, option, secho + +from platformio.platforms._base import PlatformFactory + + +@command("install", short_help="Install new platforms") +@argument("platform") +@option('--with-package', multiple=True, metavar="") +@option('--without-package', multiple=True, metavar="") +def cli(platform, with_package, without_package): + + p = PlatformFactory().newPlatform(platform) + + if p.install(with_package, without_package): + secho("The platform '%s' has been successfully installed!" % platform, + fg="green") diff --git a/platformio/commands/list.py b/platformio/commands/list.py new file mode 100644 index 00000000..699d2fb0 --- /dev/null +++ b/platformio/commands/list.py @@ -0,0 +1,16 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from click import command, echo, style + +from platformio.pkgmanager import PackageManager + + +@command("list", short_help="List installed platforms") +def cli(): + + for name, pkgs in PackageManager.get_installed().iteritems(): + echo("{name:<20} with packages: {pkgs}".format( + name=style(name, fg="cyan"), + pkgs=", ".join(pkgs.keys()) + )) diff --git a/platformio/commands/run.py b/platformio/commands/run.py new file mode 100644 index 00000000..8c1c470c --- /dev/null +++ b/platformio/commands/run.py @@ -0,0 +1,47 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from click import command, echo, option, secho, style + +from platformio.exception import ProjecEnvsNotAvaialable, UndefinedEnvPlatform +from platformio.platforms._base import PlatformFactory +from platformio.util import get_project_config + + +@command("run", short_help="Process project environments") +@option("--environment", "-e", multiple=True, metavar="") +@option("--target", "-t", multiple=True, metavar="") +def cli(environment, target): + + config = get_project_config() + + if not config.sections(): + raise ProjecEnvsNotAvaialable() + + for section in config.sections(): + if section[:4] != "env:": + continue + + envname = section[4:] + if environment and envname not in environment: + echo("Skipped %s environment" % style(envname, fg="yellow")) + continue + + echo("Processing %s environment:" % style(envname, fg="cyan")) + variables = ["%s=%s" % (o.upper(), v) for o, v in config.items(section) + if o != "targets"] + variables.append("PIOENV=" + envname) + + envtargets = [] + if target: + envtargets = [t for t in target] + elif config.has_option(section, "targets"): + envtargets = config.get(section, "targets").split() + + if not config.has_option(section, "platform"): + raise UndefinedEnvPlatform(envname) + + p = PlatformFactory().newPlatform(config.get(section, "platform")) + result = p.run(variables, envtargets) + secho(result['out'], fg="green") + secho(result['err'], fg="red") diff --git a/platformio/commands/search.py b/platformio/commands/search.py new file mode 100644 index 00000000..9990b488 --- /dev/null +++ b/platformio/commands/search.py @@ -0,0 +1,23 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from click import argument, command, echo, style + +from platformio.platforms._base import PlatformFactory +from platformio.util import get_platforms + + +@command("search", short_help="Search for development platforms") +@argument("query") +def cli(query): + for platform in get_platforms(): + p = PlatformFactory().newPlatform(platform) + name = p.get_name() + shinfo = p.get_short_info() + + search_data = "%s %s" % (name, shinfo) + if query != "all" and query.lower() not in search_data.lower(): + continue + + echo("{name:<20} - {info}".format(name=style(name, fg="cyan"), + info=shinfo)) diff --git a/platformio/commands/show.py b/platformio/commands/show.py new file mode 100644 index 00000000..9ae904c9 --- /dev/null +++ b/platformio/commands/show.py @@ -0,0 +1,29 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from os.path import join + +from click import argument, command, echo, style + +from platformio.exception import PlatformNotInstalledYet +from platformio.pkgmanager import PackageManager +from platformio.platforms._base import PlatformFactory + + +@command("show", short_help="Show details about an installed platforms") +@argument("platform") +def cli(platform): + p = PlatformFactory().newPlatform(platform) + if platform not in PackageManager.get_installed(): + raise PlatformNotInstalledYet(platform) + + # print info about platform + echo("{name:<20} - {info}".format(name=style(p.get_name(), fg="cyan"), + info=p.get_short_info())) + + pm = PackageManager(platform) + for name, data in pm.get_installed(platform).iteritems(): + echo("----------") + echo("Package: %s" % style(name, fg="yellow")) + echo("Location: %s" % join(pm.get_platform_dir(), data['path'])) + echo("Version: %d" % int(data['version'])) diff --git a/platformio/commands/uninstall.py b/platformio/commands/uninstall.py new file mode 100644 index 00000000..13f09e69 --- /dev/null +++ b/platformio/commands/uninstall.py @@ -0,0 +1,21 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from click import argument, command, secho + +from platformio.exception import PlatformNotInstalledYet +from platformio.pkgmanager import PackageManager +from platformio.platforms._base import PlatformFactory + + +@command("uninstall", short_help="Uninstall the platforms") +@argument("platform") +def cli(platform): + + if platform not in PackageManager.get_installed(): + raise PlatformNotInstalledYet(platform) + + p = PlatformFactory().newPlatform(platform) + if p.uninstall(): + secho("The platform '%s' has been successfully " + "uninstalled!" % platform, fg="green") diff --git a/platformio/commands/update.py b/platformio/commands/update.py new file mode 100644 index 00000000..6bd2432d --- /dev/null +++ b/platformio/commands/update.py @@ -0,0 +1,17 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from click import command, echo, style + +from platformio.pkgmanager import PackageManager +from platformio.platforms._base import PlatformFactory + + +@command("update", short_help="Update installed platforms") +def cli(): + + for platform in PackageManager.get_installed().keys(): + echo("\nPlatform %s" % style(platform, fg="cyan")) + echo("--------") + p = PlatformFactory().newPlatform(platform) + p.update() diff --git a/platformio/downloader.py b/platformio/downloader.py new file mode 100644 index 00000000..3887af3c --- /dev/null +++ b/platformio/downloader.py @@ -0,0 +1,88 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from email.utils import parsedate_tz +from math import ceil +from os.path import getsize, join +from subprocess import check_output +from time import mktime + +from click import progressbar +from requests import get + +from platformio.exception import (FDSHASumMismatch, FDSizeMismatch, + FDUnrecognizedStatusCode) +from platformio.util import change_filemtime + + +class FileDownloader(object): + + CHUNK_SIZE = 1024 + + def __init__(self, url, dest_dir=None): + self._url = url + self._fname = url.split("/")[-1] + + self._destination = self._fname + if dest_dir: + self.set_destination(join(dest_dir, self._fname)) + self._progressbar = None + + self._request = get(url, stream=True) + if self._request.status_code != 200: + raise FDUnrecognizedStatusCode(self._request.status_code, url) + + def set_destination(self, destination): + self._destination = destination + + def get_filepath(self): + return self._destination + + def get_lmtime(self): + return self._request.headers['last-modified'] + + def get_size(self): + return int(self._request.headers['content-length']) + + def start(self): + itercontent = self._request.iter_content(chunk_size=self.CHUNK_SIZE) + 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: + f.write(next(itercontent)) + f.close() + self._request.close() + + self._preserve_filemtime(self.get_lmtime()) + + def verify(self, sha1=None): + _dlsize = getsize(self._destination) + if _dlsize != self.get_size(): + raise FDSizeMismatch(_dlsize, self._fname, self.get_size()) + + if not sha1: + return + + dlsha1 = None + try: + res = check_output(["sha1sum", self._destination]) + dlsha1 = res[:40] + except OSError: + try: + res = check_output(["shasum", "-a", "1", self._destination]) + dlsha1 = res[:40] + except OSError: + pass + + if dlsha1 and sha1 != dlsha1: + raise FDSHASumMismatch(dlsha1, self._fname, sha1) + + def _preserve_filemtime(self, lmdate): + timedata = parsedate_tz(lmdate) + lmtime = mktime(timedata[:9]) + change_filemtime(self._destination, lmtime) + + def __del__(self): + self._request.close() diff --git a/platformio/exception.py b/platformio/exception.py new file mode 100644 index 00000000..7709870d --- /dev/null +++ b/platformio/exception.py @@ -0,0 +1,87 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + + +class PlatformioException(Exception): + + MESSAGE = None + + def __str__(self): # pragma: no cover + if self.MESSAGE: + return self.MESSAGE % self.args + else: + return Exception.__str__(self) + + +class UnknownPlatform(PlatformioException): + + MESSAGE = "Unknown platform '%s'" + + +class PlatformNotInstalledYet(PlatformioException): + + MESSAGE = ("The platform '%s' has not been installed yet. " + "Use `platformio install` command") + + +class UnknownCLICommand(PlatformioException): + + MESSAGE = "Unknown command '%s'" + + +class UnknownPackage(PlatformioException): + + MESSAGE = "Detected unknown package '%s'" + + +class InvalidPackageVersion(PlatformioException): + + MESSAGE = "The package '%s' with version '%d' does not exist" + + +class NonSystemPackage(PlatformioException): + + MESSAGE = "The package '%s' is not available for your system '%s'" + + +class FDUnrecognizedStatusCode(PlatformioException): + + MESSAGE = "Got an unrecognized status code '%s' when downloaded %s" + + +class FDSizeMismatch(PlatformioException): + + MESSAGE = ("The size (%d bytes) of downloaded file '%s' " + "is not equal to remote size (%d bytes)") + + +class FDSHASumMismatch(PlatformioException): + + MESSAGE = ("The 'sha1' sum '%s' of downloaded file '%s' " + "is not equal to remote '%s'") + + +class NotPlatformProject(PlatformioException): + + MESSAGE = "Not a platformio project. Use `platformio init` command" + + +class UndefinedEnvPlatform(PlatformioException): + + MESSAGE = "Please specify platform for '%s' environment" + + +class UnsupportedArchiveType(PlatformioException): + + MESSAGE = "Can not unpack file '%s'" + + +class ProjectInitialized(PlatformioException): + + MESSAGE = ("Project is already initialized. " + "Process it with `platformio run` command") + + +class ProjecEnvsNotAvaialable(PlatformioException): + + MESSAGE = "Please setup environments in `platformio.ini` file." diff --git a/platformio/pkgmanager.py b/platformio/pkgmanager.py new file mode 100644 index 00000000..6822acbc --- /dev/null +++ b/platformio/pkgmanager.py @@ -0,0 +1,156 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +import json +from os import makedirs, remove +from os.path import isdir, isfile, join +from shutil import rmtree + +from click import echo, secho, style +from requests import get + +from platformio import __pkgmanifesturl__ +from platformio.downloader import FileDownloader +from platformio.exception import (InvalidPackageVersion, NonSystemPackage, + UnknownPackage) +from platformio.unpacker import FileUnpacker +from platformio.util import get_home_dir, get_system + + +class PackageManager(object): + + DBFILE_PATH = join(get_home_dir(), "installed.json") + + def __init__(self, platform_name): + self._platform_name = platform_name + + @staticmethod + def get_manifest(): + try: + return PackageManager._cached_manifest + except AttributeError: + PackageManager._cached_manifest = get(__pkgmanifesturl__).json() + return PackageManager._cached_manifest + + @staticmethod + def download(url, dest_dir, sha1=None): + fd = FileDownloader(url, dest_dir) + fd.start() + fd.verify(sha1) + return fd.get_filepath() + + @staticmethod + def unpack(pkgpath, dest_dir): + fu = FileUnpacker(pkgpath, dest_dir) + return fu.start() + + @staticmethod + def get_installed(platform=None): + data = {} + if isfile(PackageManager.DBFILE_PATH): + with open(PackageManager.DBFILE_PATH) as fp: + data = json.load(fp) + return data.get(platform, None) if platform else data + + def get_platform_dir(self): + return join(get_home_dir(), self._platform_name) + + def is_installed(self, name): + installed = self.get_installed() + return (self._platform_name in installed and name in + installed[self._platform_name]) + + def get_info(self, name, version=None): + manifest = self.get_manifest() + if name not in manifest: + raise UnknownPackage(name) + + # check system platform + system = get_system() + builds = ([b for b in manifest[name] if b['system'] == "all" or system + in b['system']]) + if not builds: + raise NonSystemPackage(name, system) + + if version: + for b in builds: + if b['version'] == version: + return b + raise InvalidPackageVersion(name, version) + else: + return sorted(builds, key=lambda s: s['version'])[-1] + + def install(self, name, path): + echo("Installing %s package:" % style(name, fg="cyan")) + + if self.is_installed(name): + secho("Already installed", fg="yellow") + return + + info = self.get_info(name) + pkg_dir = join(self.get_platform_dir(), path) + if not isdir(pkg_dir): + makedirs(pkg_dir) + + dlpath = self.download(info['url'], pkg_dir, info['sha1']) + if self.unpack(dlpath, pkg_dir): + self._register(name, info['version'], path) + # remove archive + remove(dlpath) + + def uninstall(self, name, path): + echo("Uninstalling %s package: \t" % style(name, fg="cyan"), + nl=False) + rmtree(join(self.get_platform_dir(), path)) + self._unregister(name) + echo("[%s]" % style("OK", fg="green")) + + def update(self, name): + echo("Updating %s package:" % style(name, fg="yellow")) + + installed = self.get_installed(self._platform_name) + current_version = installed[name]['version'] + latest_version = self.get_info(name)['version'] + + echo("Versions: Current=%d, Latest=%d \t " % ( + current_version, latest_version), nl=False) + + if current_version == latest_version: + echo("[%s]" % (style("Up-to-date", fg="green"))) + return True + else: + echo("[%s]" % (style("Out-of-date", fg="red"))) + + self.uninstall(name, installed[name]['path']) + self.install(name, installed[name]['path']) + + def register_platform(self, name): + data = self.get_installed() + if name not in data: + data[name] = {} + self._update_db(data) + return data + + def unregister_platform(self, name): + data = self.get_installed() + del data[name] + self._update_db(data) + + def _register(self, name, version, path): + data = self.get_installed() + if self._platform_name not in data: + data = self.register_platform(self._platform_name) + data[self._platform_name][name] = { + "version": version, + "path": path + } + self._update_db(data) + + def _unregister(self, name): + data = self.get_installed() + del data[self._platform_name][name] + self._update_db(data) + + def _update_db(self, data): + with open(self.DBFILE_PATH, "w") as fp: + json.dump(data, fp) diff --git a/platformio/platforms/__init__.py b/platformio/platforms/__init__.py new file mode 100644 index 00000000..ca6f0304 --- /dev/null +++ b/platformio/platforms/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. diff --git a/platformio/platforms/_base.py b/platformio/platforms/_base.py new file mode 100644 index 00000000..fc92a733 --- /dev/null +++ b/platformio/platforms/_base.py @@ -0,0 +1,96 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from os.path import join +from shutil import rmtree + +from platformio.exception import UnknownPackage, UnknownPlatform +from platformio.pkgmanager import PackageManager +from platformio.util import exec_command, get_platforms, get_source_dir + + +class PlatformFactory(object): + + @staticmethod + def newPlatform(name): + clsname = "%sPlatform" % name.title() + try: + assert name in get_platforms() + mod = __import__("platformio.platforms." + name.lower(), + None, None, [clsname]) + except (AssertionError, ImportError): + raise UnknownPlatform(name) + + obj = getattr(mod, clsname)() + assert isinstance(obj, BasePlatform) + return obj + + +class BasePlatform(object): + + PACKAGES = {} + + def get_name(self): + raise NotImplementedError() + + def get_short_info(self): + if self.__doc__: + doclines = [l.strip() for l in self.__doc__.splitlines()] + return " ".join(doclines).strip() + else: + raise NotImplementedError() + + def install(self, with_packages, without_packages): + requirements = [] + pm = PackageManager(self.get_name()) + + upkgs = set(with_packages + without_packages) + ppkgs = set(self.PACKAGES.keys()) + if not upkgs.issubset(ppkgs): + raise UnknownPackage(", ".join(upkgs - ppkgs)) + + for name, opts in self.PACKAGES.iteritems(): + if name in without_packages: + continue + elif name in with_packages or opts["default"]: + requirements.append((name, opts["path"])) + + for (package, path) in requirements: + pm.install(package, path) + return True + + def uninstall(self): + platform = self.get_name() + pm = PackageManager(platform) + + for package, data in pm.get_installed(platform).iteritems(): + pm.uninstall(package, data['path']) + + pm.unregister_platform(platform) + rmtree(pm.get_platform_dir()) + return True + + def update(self): + platform = self.get_name() + pm = PackageManager(platform) + for package in pm.get_installed(platform).keys(): + pm.update(package) + + def run(self, variables, targets): + assert isinstance(variables, list) + assert isinstance(targets, list) + + if "clean" in targets: + targets.remove("clean") + targets.append("-c") + + result = exec_command([ + "scons", + "-Q", + "-f", join(get_source_dir(), "builder", "main.py") + ] + variables + targets) + + return self.after_run(result) + + def after_run(self, result): # pylint: disable=R0201 + return result diff --git a/platformio/platforms/atmelavr.py b/platformio/platforms/atmelavr.py new file mode 100644 index 00000000..907761b7 --- /dev/null +++ b/platformio/platforms/atmelavr.py @@ -0,0 +1,41 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from os.path import join + +from platformio.platforms._base import BasePlatform + + +class AtmelavrPlatform(BasePlatform): + """ + An embedded platform for Atmel AVR microcontrollers + (with Arduino Framework) + """ + + PACKAGES = { + + "toolchain-atmelavr": { + "path": join("tools", "toolchain"), + "default": True + }, + + "tool-avrdude": { + "path": join("tools", "avrdude"), + "default": True, + }, + + "framework-arduinoavr": { + "path": join("frameworks", "arduino"), + "default": False + } + } + + def get_name(self): + return "atmelavr" + + def after_run(self, result): + # fix STDERR "flash written" for avrdude + if "flash written" in result['err']: + result['out'] += "\n" + result['err'] + result['err'] = "" + return result diff --git a/platformio/platforms/timsp430.py b/platformio/platforms/timsp430.py new file mode 100644 index 00000000..f815dbf3 --- /dev/null +++ b/platformio/platforms/timsp430.py @@ -0,0 +1,34 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from os.path import join + +from platformio.platforms._base import BasePlatform + + +class Timsp430Platform(BasePlatform): + """ + An embedded platform for TI MSP430 microcontrollers + (with Energia Framework) + """ + + PACKAGES = { + + "toolchain-timsp430": { + "path": join("tools", "toolchain"), + "default": True + }, + + "tool-mspdebug": { + "path": join("tools", "mspdebug"), + "default": True, + }, + + "framework-energiamsp430": { + "path": join("frameworks", "energia"), + "default": False + } + } + + def get_name(self): + return "timsp430" diff --git a/platformio/platforms/titiva.py b/platformio/platforms/titiva.py new file mode 100644 index 00000000..ecdba125 --- /dev/null +++ b/platformio/platforms/titiva.py @@ -0,0 +1,34 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from os.path import join + +from platformio.platforms._base import BasePlatform + + +class TitivaPlatform(BasePlatform): + """ + An embedded platform for TI TIVA C ARM microcontrollers + (with Energia Framework) + """ + + PACKAGES = { + + "toolchain-gccarmnoneeabi": { + "path": join("tools", "toolchain"), + "default": True + }, + + "tool-lm4flash": { + "path": join("tools", "lm4flash"), + "default": True, + }, + + "framework-energiativa": { + "path": join("frameworks", "energia"), + "default": True + } + } + + def get_name(self): + return "titiva" diff --git a/platformio/projectconftpl.ini b/platformio/projectconftpl.ini new file mode 100644 index 00000000..2d436497 --- /dev/null +++ b/platformio/projectconftpl.ini @@ -0,0 +1,65 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +# Please uncomment (remove "#" sign from the beginning of the line) +# some of the environments which fit to your project. +# +# And replace all values that match with "%..._HERE%" by real data. + + +# Simple and base environment +#[env:mybaseenv] +#platform = %INSTALLED_PLATFORM_NAME_HERE% + +# +# Atmel AVR based board +# +#[env:myatmelavr_board] +#platform = atmelavr +#board_mcu = %MICROCONTROLLER_TYPE_HERE% # for example -> atmega168 +#board_f_cpu = %PROCESSOR_FREQUENCY_HERE% # for example -> 16000000L +#upload_port = %UPLOAD_PORT_HERE% # for example (Mac/Linux) -> /dev/ttyUSB0 +#upload_port = %UPLOAD_PORT_HERE% # for example (Windows) -> COM3 +#upload_protocol = %UPLOAD_PROTOCOL_HERE% # for example -> arduino +#upload_speed = %UPLOAD_PROTOCOL_HERE% # for example -> 19200 +#targets = %DEFAULT_TARGETS_HERE% # for auto-upload use -> upload + +# +# Atmel AVR based board + Arduino Wiring Framework +# +#[env:myarduino_board] +#platform = atmelavr +#framework = arduino +#board = %BOARD_HERE% # for example -> pro16MHzatmega168 +#upload_port = %UPLOAD_PORT_HERE% # for example (Mac/Linux) -> /dev/ttyUSB0 +#upload_port = %UPLOAD_PORT_HERE% # for example (Windows) -> COM3 +#targets = %DEFAULT_TARGETS_HERE% # for auto-upload use -> upload + +# +# TI MSP430 based board +# +#[env:mytimso430_board] +#platform = timsp430 +#board_mcu = %MICROCONTROLLER_TYPE_HERE% # for example -> msp430g2553 +#board_f_cpu = %PROCESSOR_FREQUENCY_HERE% # for example -> 16000000L +#upload_protocol = %UPLOAD_PROTOCOL_HERE% # for example -> rf2500 +#targets = %DEFAULT_TARGETS_HERE% # for auto-upload use -> upload + +# +# TI MSP430 based board + Energia Wiring Framework +# +#[env:myarduino_board] +#platform = timsp430 +#framework = energia +#board = %BOARD_HERE% # for example -> lpmsp430g2553 +#upload_protocol = %UPLOAD_PROTOCOL_HERE% # for example -> rf2500 +#targets = %DEFAULT_TARGETS_HERE% # for auto-upload use -> upload + +# +# TI TIVA C ARM based board + Energia Wiring Framework +# +#[env:mytitiva_board] +#platform = titiva +#framework = energia +#board = %BOARD_HERE% # for example -> lplm4f120h5qr +#targets = %DEFAULT_TARGETS_HERE% # for auto-upload use -> upload diff --git a/platformio/unpacker.py b/platformio/unpacker.py new file mode 100644 index 00000000..f80081dc --- /dev/null +++ b/platformio/unpacker.py @@ -0,0 +1,87 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from os import chmod +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 + +from platformio.exception import UnsupportedArchiveType +from platformio.util import change_filemtime + + +class ArchiveBase(object): + + def __init__(self, arhfileobj): + self._afo = arhfileobj + + def get_items(self): + raise NotImplementedError() + + def extract_item(self, item, dest_dir): + self._afo.extract(item, dest_dir) + self.after_extract(item, dest_dir) + + def after_extract(self, item, dest_dir): + pass + + +class TARArchive(ArchiveBase): + + def __init__(self, archpath): + ArchiveBase.__init__(self, tarfile_open(archpath)) + + def get_items(self): + return self._afo.getmembers() + + +class ZIPArchive(ArchiveBase): + + def __init__(self, archpath): + ArchiveBase.__init__(self, ZipFile(archpath)) + + @staticmethod + def preserve_permissions(item, dest_dir): + attrs = item.external_attr >> 16L + if attrs: + chmod(join(dest_dir, item.filename), attrs) + + @staticmethod + def preserve_mtime(item, dest_dir): + change_filemtime( + join(dest_dir, item.filename), + mktime(list(item.date_time) + [0]*3) + ) + + def get_items(self): + return self._afo.infolist() + + def after_extract(self, item, dest_dir): + self.preserve_permissions(item, dest_dir) + self.preserve_mtime(item, dest_dir) + + +class FileUnpacker(object): + + def __init__(self, archpath, dest_dir="."): + self._archpath = archpath + self._dest_dir = dest_dir + self._unpacker = None + + _, archext = splitext(archpath.lower()) + if archext in (".gz", ".bz2"): + self._unpacker = TARArchive(archpath) + elif archext == ".zip": + self._unpacker = ZIPArchive(archpath) + + if not self._unpacker: + raise UnsupportedArchiveType(archpath) + + def start(self): + with progressbar(self._unpacker.get_items(), label="Unpacking") as pb: + for item in pb: + self._unpacker.extract_item(item, self._dest_dir) + return True diff --git a/platformio/util.py b/platformio/util.py new file mode 100644 index 00000000..87168a6f --- /dev/null +++ b/platformio/util.py @@ -0,0 +1,59 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from os import getcwd, listdir, utime +from os.path import dirname, expanduser, isfile, join, realpath +from platform import architecture, system +from subprocess import PIPE, Popen + +from platformio.exception import NotPlatformProject + +try: + from configparser import ConfigParser +except ImportError: + from ConfigParser import ConfigParser + + +def get_system(): + return (system() + architecture()[0][:-3]).lower() + + +def get_home_dir(): + return expanduser("~/.platformio") + + +def get_source_dir(): + return dirname(realpath(__file__)) + + +def get_project_dir(): + return getcwd() + + +def get_project_config(): + path = join(get_project_dir(), "platformio.ini") + if not isfile(path): + raise NotPlatformProject() + cp = ConfigParser() + cp.read(path) + return cp + + +def get_platforms(): + platforms = [] + for p in listdir(join(get_source_dir(), "platforms")): + if p.startswith("_") or not p.endswith(".py"): + continue + platforms.append(p[:-3]) + return platforms + + +def change_filemtime(path, time): + utime(path, (time, time)) + + +def exec_command(args): + use_shell = get_system() == "windows32" + p = Popen(args, stdout=PIPE, stderr=PIPE, shell=use_shell) + out, err = p.communicate() + return dict(out=out.strip(), err=err.strip()) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..b15fbf50 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +click==2.0 +colorama==0.3.1 +pyserial==2.7 +requests=2.3.0 +scons=2.3.0 diff --git a/scripts/fixsymlink.py b/scripts/fixsymlink.py new file mode 100644 index 00000000..28c24254 --- /dev/null +++ b/scripts/fixsymlink.py @@ -0,0 +1,47 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from os import chdir, getcwd, readlink, remove, symlink, walk +from os.path import exists, islink, join, split +from sys import exit as sys_exit + + +def get_symrelpath(root, sympath, ending=None): + head, tail = split(sympath) + + if ending: + ending = join(tail, ending) + relpath = join("..", ending) + else: + relpath = tail + ending = tail + + if exists(join(root, relpath)): + return relpath + elif head: + return get_symrelpath(root, head, ending) + else: + raise Exception() + + +def fix_symlink(root, fname, brokenlink): + prevcwd = getcwd() + symrelpath = get_symrelpath(root, brokenlink) + + chdir(root) + remove(fname) + symlink(symrelpath, fname) + chdir(prevcwd) + + +def main(): + for root, dirnames, filenames in walk("."): + for f in filenames: + path = join(root, f) + if not islink(path) or exists(path): + continue + fix_symlink(root, f, readlink(path)) + + +if __name__ == "__main__": + sys_exit(main()) diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..ce4a9cef --- /dev/null +++ b/setup.py @@ -0,0 +1,44 @@ +# Copyright (C) Ivan Kravets +# See LICENSE for details. + +from setuptools import find_packages, setup + +from platformio import (__author__, __description__, __email__, __license__, + __title__, __url__, __version__) + +setup( + name=__title__, + version=__version__, + description=__description__, + long_description=open("README.rst").read(), + author=__author__, + author_email=__email__, + url=__url__, + license=__license__, + install_requires=[ + "click", + "colorama", + "pyserial", + "requests", + # "SCons" + ], + packages=find_packages(), + package_data={"platformio": ["*.ini"]}, + entry_points={ + "console_scripts": [ + "platformio = platformio.__main__:main" + ] + }, + classifiers=[ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: C", + "Programming Language :: Python", + "Topic :: Software Development", + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Compilers" + ] +) diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..ed614547 --- /dev/null +++ b/tox.ini @@ -0,0 +1,27 @@ +[tox] +# toxworkdir = /tmp/.tox +# toxworkdir = C:\Users\User\Downloads\.tox +envlist = lint + +[testenv] +envlogdir = /tmp/toxlogdir +envtmpdir = /tmp/toxtmpdir +commands = + {envpython} --version + +[testenv:develop] +usedevelop = True +deps = + isort + flake8 + wheel +commands = + pip install --egg scons + +[testenv:lint] +deps = + flake8 + pylint +commands = + flake8 ./platformio + pylint --rcfile=./.pylintrc ./platformio