Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Tree: 1397ab64c7
Fetching contributors…

Cannot retrieve contributors at this time

217 lines (175 sloc) 8.17 KB
# -*- coding: utf-8; -*-
import re
import os.path
import inspect
import subprocess
import platform
import jinja2
from jinja2.runtime import StrictUndefined
import ino.filters
from ino.commands.base import Command
from ino.environment import Version
from ino.filters import colorize
from ino.utils import SpaceList, list_subdirs
from ino.exc import Abort
class Build(Command):
Build a project in the current directory and produce a ready-to-upload
firmware file.
The project is expected to have a `src' subdirectroy where all its sources
are located. This directory is scanned recursively to find
*.[c|cpp|pde|ino] files. They are compiled and linked into resulting
firmware hex-file.
Also any external library dependencies are tracked automatically. If a
source file includes any library found among standard Arduino libraries or
a library placed in `lib' subdirectory of the project, the library gets
build too.
Build artifacts are placed in `.build' subdirectory of the project.
name = 'build'
help_line = "Build firmware from the current directory project"
def setup_arg_parser(self, parser):
super(Build, self).setup_arg_parser(parser)
parser.add_argument('-c', '--make-command', metavar='MAKE_COMMAND',
default=self.e.default_make_command, help='Make command')
parser.add_argument('-v', '--verbose', default=False, action='store_true',
help='Verbose make output')
def discover(self):
['hardware', 'arduino', 'cores', 'arduino'],
['Arduino.h'] if self.e.arduino_lib_version.major else ['WProgram.h'],
'Arduino core library')
self.e.find_arduino_dir('arduino_libraries_dir', ['libraries'],
human_name='Arduino standard libraries')
if self.e.arduino_lib_version.major:
['hardware', 'arduino', 'variants'],
human_name='Arduino variants directory')
toolset = [
('cc', 'avr-gcc'),
('cxx', 'avr-g++'),
('ar', 'avr-ar'),
('objcopy', 'avr-objcopy'),
for tool_key, tool_binary in toolset:
tool_key, ['hardware', 'tools', 'avr', 'bin'],
items=[tool_binary], human_name=tool_binary)
def register_make_command(self, make_command):
self.make_command = make_command
def setup_flags(self, board_key):
board = self.e.board_model(board_key)
mcu = '-mmcu=' + board['build']['mcu']
self.e['cflags'] = SpaceList([
'-DF_CPU=' + board['build']['f_cpu'],
'-DARDUINO=' + str(self.e.arduino_lib_version.as_int()),
'-I' + self.e['arduino_core_dir'],
if 'vid' in board['build']:
self.e['cflags'].append('-DUSB_VID=%s' % board['build']['vid'])
if 'pid' in board['build']:
self.e['cflags'].append('-DUSB_PID=%s' % board['build']['pid'])
if self.e.arduino_lib_version.major:
variant_dir = os.path.join(self.e.arduino_variants_dir,
self.e.cflags.append('-I' + variant_dir)
self.e['cxxflags'] = SpaceList(['-fno-exceptions'])
self.e['elfflags'] = SpaceList(['-Os', '-Wl,--gc-sections', mcu])
self.e['names'] = {
'obj': '%s.o',
'lib': 'lib%s.a',
'cpp': '%s.cpp',
'deps': '%s.d',
def create_jinja(self, verbose):
templates_dir = os.path.join(os.path.dirname(__file__), '..', 'make')
self.jenv = jinja2.Environment(
undefined=StrictUndefined, # bark on Undefined render
# inject @filters from ino.filters
for name, f in inspect.getmembers(ino.filters, lambda x: getattr(x, 'filter', False)):
self.jenv.filters[name] = f
# inject globals
self.jenv.globals['e'] = self.e
self.jenv.globals['v'] = '' if verbose else '@'
self.jenv.globals['slash'] = os.path.sep
self.jenv.globals['SpaceList'] = SpaceList
def render_template(self, source, target, **ctx):
template = self.jenv.get_template(source)
contents = template.render(**ctx)
out_path = os.path.join(self.e.build_dir, target)
with open(out_path, 'wt') as f:
return out_path
def make(self, makefile, **kwargs):
makefile = self.render_template(makefile + '.jinja', makefile, **kwargs)
ret =[self.make_command, '-f', makefile, 'all'])
if ret != 0:
raise Abort("Make failed with code %s" % ret)
def recursive_inc_lib_flags(self, libdirs):
flags = SpaceList()
for d in libdirs:
flags.append('-I' + d)
flags.extend('-I' + subd for subd in list_subdirs(d, recursive=True, exclude=['examples']))
return flags
def _scan_dependencies(self, dir, lib_dirs, inc_flags):
output_filepath = os.path.join(self.e.build_dir, os.path.basename(dir), 'dependencies.d')
self.make('Makefile.deps', inc_flags=inc_flags, src_dir=dir, output_filepath=output_filepath)
# search for dependencies on libraries
# for this scan dependency file generated by make
# with regexes to find entries that start with
# libraries dirname
regexes = dict((lib, re.compile(r'\s' + lib + re.escape(os.path.sep))) for lib in lib_dirs)
used_libs = set()
with open(output_filepath) as f:
for line in f:
for lib, regex in regexes.iteritems():
if and lib != dir:
return used_libs
def scan_dependencies(self):
self.e['deps'] = SpaceList()
lib_dirs = [self.e.arduino_core_dir] + list_subdirs(self.e.lib_dir) + list_subdirs(self.e.arduino_libraries_dir)
inc_flags = self.recursive_inc_lib_flags(lib_dirs)
# If lib A depends on lib B it have to appear before B in final
# list so that linker could link all together correctly
# but order of `_scan_dependencies` is not defined, so...
# 1. Get dependencies of sources in arbitrary order
used_libs = list(self._scan_dependencies(self.e.src_dir, lib_dirs, inc_flags))
# 2. Get dependencies of dependency libs themselves: existing dependencies
# are moved to the end of list maintaining order, new dependencies are appended
scanned_libs = set()
while scanned_libs != set(used_libs):
for lib in set(used_libs) - scanned_libs:
dep_libs = self._scan_dependencies(lib, lib_dirs, inc_flags)
i = 0
for ulib in used_libs[:]:
if ulib in dep_libs:
# dependency lib used already, move it to the tail
i += 1
# append new dependencies to the tail
self.e['used_libs'] = used_libs
def run(self, args):
Jump to Line
Something went wrong with that request. Please try again.