pyalamake - gen Makefile using Python

×

Overview

module url https://pypi.org/project/pyalamake.html
git repository https://bitbucket.org/arrizza-public/pyalamake
git command git clone git@bitbucket.org:arrizza-public/pyalamake.git
verification report https://arrizza.com/web-ver/pyalamake-report.html
version info
OS Language #Runs Last Run Cov%
macOS 14.5 Python 3.10 - - -
macOS 14.5 Python 3.12 33 2025-06-26 100.0%
macOS 15.5 Python 3.12 32 2025-08-06 100.0%
macOS 15.6 Python 3.12 111 2026-01-16 93%
Ubuntu 22.04 jammy Python 3.10 - - -
Ubuntu 24.04 noble Python 3.10 152 2025-05-27 72%
Ubuntu 24.04 noble Python 3.12 659 2026-01-14 100.0%
win32 Windows 10 Python 3.10 - - -
win32 Windows 11 Python 3.12 378 2026-01-20 100.0%

Summary

This Python module generates a multi-target Makefile for cross-platform projects all of which is compatible with JetBrain's Clion IDE.

For example, see gen.py for a Python script (gen.py) that generates:

  • two Arduino projects and an Arduino core for them
  • a GTest project to run UTs against an Arduino Project
  • a C/++ project
  • SWIG projects
  • C/C++ shared and static libraries
  • see doc/example-makefile.md for more details

all into one Makefile, and all recognized by CLion.

See also gen_3cores.py that shows how to build 3 Arduino on 3 different boards in the same project and same Makefile.

Recent updates

  • It now uses arduino-cli. This made the arduino target handling cross-platform (roughly speaking). The gen.py file is the same across all three platforms (ubuntu, macos, win/msys2)
  • Added ./do_test_deps. This does a check of all targets and associated targets to ensure they work independently of each other i.e. the target dependencies are correct.
  • cleaned up this README.md and added more documentation in the doc directory, for example see doc/api.md for a list of commands available for use in gen.py

Why not CMake?

I use JetBrain's CLion. It can handle only a CMake or Makefile based project.

Also, I like to use GTest to UT my Arduino projects (if possible). But CMake can't handle a project that uses two compilers i.e. avr-gcc for Arduino and gcc for GTest. It tried some different CMake techniques to allow that but they either didn't work or CLion still did not recognize the components being used.

The only option then was to use Python to generate a Makefile that CLion was compatible with.

Why not ninja instead of make?

Because CLion can't use that to self-configure. CMake in CLion can be configured to use Ninja, but the IDE uses CMake to configure itself for the project. Therefore, Makefile.

Why?

Currently, I have relatively simple projects using one Arduino and some GTest UTs. But I do have some projects that could use multiple Arduino's communicating with each other. And possibly some other microcontrollers e.g. STM32, ESP32, etc. that communicate with Arduino's or themselves.

CLion can't handle that.

But (finger crossed) I should be able to extend pyalamake to build STM32 and ESP32 targets relatively easily, still allow GTests for those microcontrollers, etc.

Limitations

  • See todo.md for more work to be done.
  • tested with an Arduino Nano (boardid: nano-atmega328old), Arduino MegaADK (boardid: megaadk) and Arduino Uno (boardid: uno). Other boards should work correctly, but there is always a possibility it fails.
  • I have not tested using an Arduino programmer
  • I have not tested an app that requires Arduino EEPROM uploads.

How to use

See doc/example-makefile.md for detailed explanation of the current makefile.

See doc/convert-from-cmake.md for examples of converting Arduino cmake into gen.py.

  • Check tools/arduino_reqmts.txt for Arduino libraries needed for this project:
Servo

Run ./do_install to install them:

<snip>
OK   do_arduino_install: install rc=0 Servo
     do_arduino_install: libraries installed
 --  1] Name  Installed     Available         Location Description
 --  2] Servo 1.3.0         -                 user     -
 --  3] 
     do_install: arduino_install rc=0
<snip>     
  • run ./do_gen. This takes gen.py and uses pyalamake to generate all the defined targets. It will generate two files:
    • Makefile - a top level makefile that handles including the correct makefile for the current OS.
    • One of Makefile.ubuntu, Makefile.macos, Makefile.win - the OS specific makefile
  • run make help to confirm that all of the targets have been generated
  • run make xx to run build a specific target.
  • if the target is executable, use make xx-run. See doc/example-makefile.md for examples of how to add CLI arguments

API documentation

The general format of the gen.py file is:

import sys

from src.pyalamake import alamake

# report to the user starting to use pyalamake
alamake.log.start(f'running pyalamake: {alamake.version}')

# do some general configurations
alamake.cfg_quiet_clean(True)               # e.g. use @rm for clean up commands
alamake.cfg_gen_compile_commands(False)     # generate compile_commands.json

# generate first target
tgt = alamake.create('hello', 'cpp')
tgt.add_sources([ list of sources])
<snip>
# generate any other targets, UTs, etc for them here

# generates the Makefile with all defined targets
alamake.makefile()

AlaMake class

This the main class. An instance of is defined as "alamake"

Common functions

    alamake.log.start('heading line')
    alamake.log.line('line1')
    alamake.log.dbg('debug line')
  • find_package(pkgname) - gets info about the defined package. Currently only supports CPIP packages and OpenGl.Has two fields:
    • src - returns a list of source files
    • include_dir - returns the include directory for any .h package files
pkg = alamake.find_package('cpip.logger')
<snip>
some_tgt.add_sources(log_pkg.src)
some_tgt.add_include_directories(log_pkg.include_dir)
  • create(target_name, target_type, shared=None) - creates a target
    • target_name is a string with the target name you need
    • target_type can be one of:
      • "arduino-core" - an arduino core for a specific board type
      • "arduino" - arduino app
      • "c" - a C executable
      • "c-lib" - a C shared or static library
      • "cpp" - a C++ executable
      • "cpp-lib" - a C++ shared or static library
      • "gtest" - a unit test (C/C++)
      • "manual" - a target that you define as you need
      • "swig" - a SWIG target to generate a python, ruby sources
  • create_arduino_shared() - generates a common base class for arduino apps. The data and config for arduino apps need to be shared with the core and any other files as well. the shared object stores that. If the makefile has targets for multiple arduino boards, then each board will have it's own shared object.
sh1 = alamake.create_arduino_shared()
<snip>
tgt = alamake.create('blink', 'arduino', shared=sh1)
  • makefile(ut_path=None) - used to generate the Makefiles into the current directory. For CLion, the makefiles have to be in the root directory of the project. The ut_path is used for pyalamake unit tests so generated makefiles don't interfere with those.
  • get_port(vid_pid) - check if a USB port with VID/PID is currently plugged in and available. e.g. this is useful for arduino development.
  • osal - (property) Operating System Abstraction Layer. Holds a variety of values that are different across the ubuntu, macos and windows/msys2 OS. The following are available:
    • isfile(path) - checks if the file exists. Handles OS slash differences, makefile variables, etc.
    • isdir(path) - checks if the directory exists. Handles OS slash differences, makefile variables, etc.
    • homebrew_link_dirs() - currently unused
    • homebrew_inc_dirs() - currently unused
    • cpp_compiler() - returns the C++ compiler to use
    • c_compiler() - returns the C compiler to use
    • arduino_root_dir() - returns path to arduino-cli root directory
    • arduino_library_dir() - returns path to arduino-cli root directory to all libraries (e.g. Servo)
    • arduino_core_src_dir() - returns path to the arduino-cli source file root directory
    • arduino_tools_dir() - returns path to the arduino-cli tools root directory
    • avrdude_conf_dir() - returns path to the arduino-cli avrdude.conf file
    • gtest_includes() - returns list of additional include directories for GTest
    • gtest_link_dirs() - returns list of additional link directories for GTest
    • ruby_includes() - returns list of additional include directories for ruby
    • ruby_link_dirs() - returns list of additional link directories for ruby
    • python_includes() - returns list of additional include directories for python
    • python_link_dirs() - returns list of additional link directories for python
  • gbl - (property) holds various configuration objects and settings The following are available:
    • build_dir : the name of the build directory, currently "debug"
    • set_build_dir(build_dir): set the build_dir value
    • os_name : a tag for the OS name: 'win', 'macos', 'ubuntu' (eventually 'rpi') can be used if alamake.gbl.os_name == 'macos': do_something_on_mac_only
    • quiet_clean: internal use only (see cfg_quite_clean below)
    • gen_compile_commands: internal use only (see cfg_gen_compile_commands below)
    • mf_variables: internal use only
    • genned_empty_rule: internal use only

Less common

  • abort(msg) - aborts the gen with the given message
  • cfg_gen_compile_commands(val=True) - if True, generates the compile_commands.json file. (used by CLion)
  • cfg_quiet_clean(val=True) - if True, then clean targets will not print the command as it executes
  • is_ubuntu - (property) is True if running on Ubuntu
  • is_macos - (property) is True if running on macos
  • is_win - (property) is True if running on windows/msys2. Future enhancement: add ability to run under Powershell.
  • is_rpi - (property) is True if running on macos (currently not tested/used!)
  • version - (property) returns pyalamake version string
  • ut_svc - (property) internal use only for pyalamake unit tests

Common target functions

These are public but are for internal use only. DO NOT USE. (They will be made private)

  • create(targets, target_name)
  • [m] gen_target()
  • [m] add_rule(rule)
  • [m] add_clean(pattern)
  • [m] gen_clean()
  • [p] rules
  • [p] clean
  • [p] help
  • [p] lines

Common compilable target functions

Common functions

  • add_sources(str or list) - add 1 or more paths to source files to the current list
  • add_include_directories(str or list) - add 1 or more include directory paths to the current list
  • add_compile_options(str or list) - add 1 or more compiler options to the current list
  • add_link_options(str or list) - add 1 or more linker options to the current list
  • add_link_libraries(str or list) - add 1 or more link libraries to the current list
  • add_link_files(str or list) - add 1 or more path to files to link to the current list
  • add_link_directories(str or list) - add 1 or more paths to link directories to the current list

Less Common

  • sources - (property) returns the current list of sources for this target
  • include_directories - (property) returns the current list of include directories for this target
  • compile_options - (property) returns the current list of compile options for this target
  • link_options - (property) returns the current list of link options for this target
  • link_libraries -(property) returns the current list of link libraries for this target
  • link files - (property) returns the current list of link files for this target
  • link_directories - (property) returns the current list of link directories for this target
  • target - (property) returns the name of the target
  • target_type - (property) returns the target type e.g. 'cpp', 'c', etc.
  • add_homebrew() - (macos only) adds homebrew via add_link_directories and add_include_directories.

C/C++ app targets

For an example of the 'cpp' target, see gen.py in devcpp-app-template

see local gen.py for C apps ('hello_c') or C++ apps ('hello')

# create the target
tgt = alamake.create('hello', 'cpp')

# add source files to it
tgt.add_sources([
    'src1/hello.cpp',
])

# name the include directory
tgt.add_include_directories(['src1'])

To run it:

rm -rf debug         # for test purposes

make hello           # build the executable
make hello-run       # run it using make
# typical output
From C++: Hello World! (from v0.0.1)
arg[0] debug/hello

debug/hello a b c    # run it
# typical output
From C++: Hello a! (from v0.0.1)
arg[0] debug/hello
arg[1] a
arg[2] b
arg[3] c

Common functions

  • create(target name, 'c') - create a C target
  • create(target name, 'cpp') - create a C++ target
  • compiler - the C or C++ compiler to use
  • see "Common compilable target functions" above

Less Common

  • check() - runs some basic checks e.g. do source files exist, paths to directories, etc.

Gtest unit test apps

Generates a gtest compatible unit test runner.

For an example of the 'gtest' target, see gen.py in devcpp-app-template or see the local gen.py function "gen_test_ard, for an example of cross-compiling an arduino app to unit test it.

ut = alamake.create('ut', 'gtest')

# add homebrew dirs if running on macOS
ut.add_homebrew()

ut.add_include_directories([
    'src',
])
ut.add_sources([
    'src/app.cpp',
    'src/svc.cpp',
    'src/cfg.cpp',
    'ut/ut_test1.cpp',
])
ut.add_link_libraries('gtest_main')
ut.add_coverage(['src'])

Common functions

  • add_link_libraries('gtest_main') - adds the default gtest main library
  • add_coverage(str or list) - add 1 or more paths to directories to generate coverage for. Other files, even if compiled in, are not included in the coverage report.
  • see "Common compilable target functions" above

Less Common

  • check() - runs some basic checks e.g. do source files exist, paths to directories, etc.

C/C++ static and shared libraries

Generates a shared (.so/.dll) or static (.a) library. The local gen.py shows several examples ('c_static', 'c_shared', 'log_static', 'log_shared', the SWIG targets)

tgt = alamake.create('c_shared', 'c-lib')
tgt.set_type('shared')
tgt.add_sources('src6/common.c')
alamake.log.line(f'shared C lib target_type: {tgt.target_type}')
alamake.log.line(f'shared C lib compiler   : {tgt.compiler}')

Common functions

  • create(tgt name, 'c-lib') - for C libraries
  • create(tgt name, 'cpp-lib') - for C++ libraries
  • set_type('static') - (default) generates a static library (.a)
  • set_type('shared') - generates a shared library (.so/.dll)
  • see "Common compilable target functions" above

Less Common

  • check() - runs some basic checks e.g. do source files exist, paths to directories, etc.
  • lib_type - (property) returns the library type ('static' or 'shared')

Packages

Both the C and C++ App targets can include a package. A package is include files and either source files or pre-compiled libraries.

Currently only these packages are recognized:

  • CPIP - see CPIP
  • OpenGL - see src/pyalamake/package_opengl.py

An example of CPIP:

# === cpip
pkg = alamake.find_package('cpip.logger')

# === C++ tgt
tgt = alamake.create('devcpp-app-template', 'cpp')
tgt.add_sources([
    'src/app.cpp',
    'src/cfg.cpp',
    'src/svc.cpp',
    'src/main.cpp',
])
tgt.add_include_directories(['src'])
tgt.add_sources(pkg.src)
tgt.add_include_directories(pkg.include_dir)

For an example of OpenGL see jxLife app:

# === packages
opengl = alamake.find_package('opengl')
tgt = alamake.create('jxlife', 'cpp')
tgt.add_sources([
    'src/Animal.cpp',
    'src/App.cpp',
    'src/Cell.cpp',
    'src/EnvironmentInfo.cpp',
    'src/Grid.cpp',
    'src/IntParam.cpp',

    'src/main.cpp',
])
tgt.add_compile_options('-O3')
tgt.add_include_directories(['src'])
tgt.add_link_libraries('pthread')
tgt.add_include_directories(opengl.include_dir)
tgt.add_link_libraries(opengl.link_libs)

Common functions

  • find_package('cpip.xx') - returns a struct with info about the 'xx' package
    • include_dir - (property) the include directory
    • src - (property) list of source files to compile
  • find_package('opengl') - returns a struct with info about OpenGL:
    • include_dir - (property) the include directory
    • link_libs - (property) list of link libraries

Manual targets

Generates a list of commands to invoke. The local gen.py has 'man1' as an example.

tgt = alamake.create('man1', 'manual')
tgt.add_command('@echo "line1 - sample command line"')
tgt.add_command('@echo "line2 - sample command line"')
tgt.set_help('run sample manual commands')
tgt.add_dependency('log_static')

Common functions

  • create(tgt name, 'manual') - to create a target
  • add_command(cmd) - add a command
  • add_dependency(deps) - a list of rule names that are dependencies
  • set_help(desc) - set description for this target; used in make help

Less Common

  • check() - callable, but currently does no checks

SWIG target

Uses SWIG to generate C++ code that can then be compiled into a library and used in python or ruby.

The local gen.py has 'py_example', 'rb_example' and 'js_example' examples. These all take the same SWIG .i file to generate the appropriate .cpp or .c source files.

tgt = alamake.create('py_example', 'swig')
tgt.engine('python')
tgt.set_cpp()  # gens .cpp
tgt.add_sources([
    'src5/example.i',
])

For a full example showing how to also create the python or ruby library, see gen_swig_python() and gen_swig_ruby().

For python module:

rm -rf debug         # for test purposes
make py_example                   # gen the .cpp file from SWIG .i
make swig_py_test                 # gen the .so file and example.py file
mkdir -p debug/pydir              # create temp directory for python module
cp debug/libswig_py_test.so   debug/pydir/_example.so           # move the .so module 
cp debug/py_example-dir/src5/example.py debug/pydir/example.py  # move the SWIG genned example.py 
touch debug/pydir/__init__.py     # make it into a python package/module 
python3 auto_test.py              # import and run it from a python script

# typical output
     Python Arch: 64bit
     Struct Arch: 64-bit
     python foo_int:    2
     python foo_double: 3.0
     python sum:        5.0

Similar, but simpler, for ruby module:

rm -rf debug         # for test purposes
make rb_example                   # gen the .cpp file from SWIG .i
make swig_rb_test                 # gen the .so file and example.py file
mkdir -p debug/rbdir              # create temp directory for ruby module
cp debug/libswig_rb_test.so debug/rbdir/example.so    # move the .so module
ruby auto_test.rb                 # require and run it from a ruby script

# typical output
     ruby foo_int:    2
     ruby foo_double: 3.0
     ruby sum:        5.0

These functions only apply to SWIG generation

Common functions

  • create(tgt name, 'swig') - for swig gen targets
  • engine(arg) - sets the SWIG engine, use one of these:
    • python => SWIG "-python"
    • ruby => SWIG "-ruby"
    • jsc => SWIG "-javascript -jsc"
    • v8 => SWIG "-javascript -v8"
    • node => SWIG "-javascript -node"
    • napi => SWIG "-javascript -napi"
  • set_cpp() - generates C++ source files; default is C source files

Less Common

  • check() - runs some basic checks e.g. do source files exist, paths to directories, etc.
  • opts(opts) - sets additional SWIG opts

Arduino Shared config

Generating an uploadable Arduino binary requires 3 steps:

  • generate an arduino shared configuration (not a target!)
  • generate an arduino Core using the shared configuration
  • generate an arduino ELF file using the shared configuration

See Arduino projects for a variety of these.

See the local gen.py function setup_arduino_shared() for creating and configuring an arduino shared config

sh1 = alamake.create_arduino_shared()
# sh1.print_board_list()    # uncomment to show available board ids
# set the Arduino Board ID
sh1.set_boardid('nano-atmega328old')

# set port automatically;
port = alamake.get_port('1A86:7523')
sh1.set_avrdude_port(port)

Common functions

  • create_arduino_shared() - returns a default arduino shared
  • set_boardid(boardid) - sets the board id to use. use print_board_list() to see available board ids.
  • set_avrdude_port(port) - the USB/UART port to use for uploading to an arduino

Less Common

  • print_board_list() - shows list of acceptable board ids
  • check() - runs some basic checks e.g. do source files exist, paths to directories, etc.
  • ar - (property) the path to the AVR archiver to use
  • cc - (property) the path to the AVR C compiler to use
  • cpp - (property) the path to the AVR C++ compiler to use
  • obj_copy - (property) the AVR objcopy tool
  • avrdude_dir - (property) the path to the avrdude tool
  • core_includes - (property) list of include directories for the core
  • library_root- (property) the path to where the libraries files are held (e.g. Servo)

Arduino Core target

Once the shared config is created, then an Arduino Core library is needed to provide any core arduino function calls e.g. digitalWrite().

    sh1 = alamake.create_arduino_shared()
    # <snip>
    tgt = alamake.create('core', 'arduino-core', shared=sh1)
    tgt.check()

See gen_3cores.py for an example of how to do multiple cores at once. This is to create ELFs for multiple, different Arduino boards.

Common functions

  • create(tgt name, 'arduino-core', shared=sh_object) - to create a core target

Less Common

  • check() - runs some basic checks e.g. do source files exist, paths to directories, etc.

Arduino target

Once the shared config and core library are created, then an Arduino target can be compiled.

See Arduino projects for a variety of these.

See the local gen.py function gen_arduino_blink() or gen_arduino_blink2() for creating uploadable arduino ELF binaries.

tgt = alamake.create('blink', 'arduino', shared=sh1)
tgt.add_sources([
    'src1/blink.cpp',
    'src1/common.cpp',
])
tgt.add_include_directories(['src1'])

Use tgt-upload to upload to the arduino

rm -rf debug          # for test purposes
make blink            # create the ELF
make blink-upload     # upload it to the arduino

Common functions

  • create(tgt name, 'arduino', shared=sh_object) - to create a target
  • see "Common compilable target functions" above

Less Common

  • check() - runs some basic checks e.g. do source files exist, paths to directories, etc.

Example gen.py

See ./gen.py for examples of how to declare the various targets.

  • It is possible to pass arguments to targets:
# to pass a CLI args to hello world app
make hello-run arg1 arg2 

# Note: to use "--arg" add an initial "--"
make hello-run -- arg1 -arg2 --arg3

# Note: using "=" in the args will cause a make failure; use direct calls in that case:
debug/hello arg1=22 arg2=23
  • Associated targets - these are "sub-targets" associated with the main target.
    • Most targets that generate files have an associated "xx-clean" target. this cleans up only the files that target generates
    • compilable targets (e.g arduino, C/C++) have associated "xx-build" and "xx-link"target these only run the compilation and link commands.
    • executable targets (C/C++ but not libraries) have an associated "xx-run" target. These run the executables on the current OS.
    • most targets also have an associated "xx-init". This is used to create build directories, etc. as needed for the other associated targets.
    • unit test targets also have:
      • "xx-cov-reset" to reset coverage information/files
      • "xx-cov" to show the current coverage. Open debug/xx.html in a browser for a gcov HTML report.
  • Top-level targets
    • help - shows all available targets
    • all - makes all targets (but does not run them)
    • clean - invokes all other tgt-clean targets
  • manual related
    • man1 - creates a target that has manually entered commands to run (currently just does "echo"s)
  • Arduino related
    • arduino targets have an associated upload target e.g. "blink-upload"
    • core - creates an arduino core for a Nano Atmega328 Note: no "-upload" target
    • blink - arduino sample that blinks the LED (see src1). should blink random times (2 - 15) and random rate (0 - 255ms)
    • blink2 - another arduino LED blinker (src2) to show that multiple arduino targets can be created. Should blink 2 times.
    • ut - uses Gtest and cross compiles src1 to test it
  • C/C++ related
    • hello - simple C++ app (see src1)
    • hello_c - simple C app (see src6)
  • CPIP related. CPIP is a set of source code I publish. see "logger" in CPIP ("CPIP" is a play on the Python pip package manager i.e. pip for C/C++)
    • cpiptest - does a test of CPIP logger (see src3)
    • ut_cpip - runs GTest UTs on CPIP logger
  • C/C++ shared and static libraries (see src6)
    • c_shared - creates a C .so/.dll library
    • test_c_shared - sample code to use c_shared library
    • c_static - creates a C .a library
    • test_c_static - sample code to use c_static library
  • CPIP logger related (see src4)
    • log_shared - creates a shared library that wraps the CPIP logger
    • log_test_shared - sample code to test log_shared
    • log_static build - creates a static library that wraps the CPIP logger
    • log_test_static - sample code to test log_static
  • SWIG related
    • py_example - generates the python compatible source from SWIG .i (see src5)
    • swig_py_test - creates a python compatible shared library (see auto_test.py)
    • rb_example - generates the ruby compatible source from SWIG .i (see src5)
    • swig_rb_test - creates a ruby compatible shared library (see auto_test.rb)
    • js_example - - generates the NAPI compatible source from SWIG .i (see src5) Unfortunately, I have not been able to create a test of this file.

updating gen.py

from pyalamake.lib.pyalamake import alamake

# specify all components to be added to the makefile
# ... skip see below ...

# === generate makefile for all targets
alamake.makefile()

At this point, the Makefile should contain all targets specified

Arduino targets

Creating an Arduino .hex requires 3 steps:

1) gen an Arduino Shared component

  • holds common, shared information like boardid, serial port, etc.

2) gen an Arduino Core

  • use the same shared component created above
  • holds the Arduino Core library compiled for the same boardid, etc.

3) gen the Arduino App

  • use the same shared component created above
  • holds the Arduino app compiled for the same boardid etc.

A shared component and core can be used with multiple Arduino targets.

Example generation of an Arduino shared component:

sh1 = alamake.create_arduino_shared()
sh1.set_boardid('nano-atmega328old')
# sh1.set_boardid('uno')
# sh1.set_boardid('megaadk')

# on Ubuntu, different boards use different serial ports 
if sh1.boardid in ['uno', 'megaadk']:
    sh1.set_avrdude_port('/dev/ttyACM0')
else:
    sh1.set_avrdude_port('/dev/ttyUSB0')

# at this point sh1 is only missing core related settings

Example generation of an Arduino core:

# share the same component created above
core = alamake.create('core', 'arduino-core', shared=sh1)
# no additional directories, libs, etc. needed
core.check()

Example generation of an Arduino target:

# share the same component created above
blink = alamake.create('blink', 'arduino', shared=sh1)
blink.add_sources([
    'src1/blink.cpp',
    'src1/common.cpp',
])
blink.add_include_directories(['src'])

GTest target

Example generation of a GTest target that tests an Arduino app

ut = alamake.create('ut', 'gtest')

# tests the blink2.cpp (Arduino) in src2 directory
ut.add_include_directories([
    'src2',
    'ut/mock_arduino',
])

# uses a Mock Arduino library to check blink2 behavior
ut.add_sources([
    'ut/mock_arduino/mock_arduino.cpp',
    'ut/ut_test1.cpp',
])

# specify the directory(s) that should be part of the coverage report
ut.add_coverage(['src2'])

C++ targets

Example generation of a C++ target

hello = alamake.create('hello', 'cpp')
hello.add_include_directories(['src'])
hello.add_sources([
    'src1/hello.cpp',
])

Convert Arduino from cmake

These are examples of old cmake files I had and how they converted to the new pyalamake format.

Convert:

cmake_minimum_required(VERSION 3.16)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/arduino-cmake/ArduinoToolchain.cmake)

project(blink)
print_board_list()
# set to your board
set(MY_BOARD nano328p)
set(MY_PORT /dev/ttyUSB0)

to:

# === create a shared component for core and arduino project
sh1 = alamake.create_arduino_shared()
sh1.set_boardid('nano-atmega328old')
sh1.set_avrdude_port('/dev/ttyUSB0')
# at this point sh1 is only missing core related settings

# === arduino core
core = alamake.create('core', 'arduino-core', shared=sh1)
# no additional directories, libs, etc. needed
core.check()

# === arduino blink
blink = alamake.create('blink', 'arduino', shared=sh1)

Convert:

generate_arduino_firmware(${CMAKE_PROJECT_NAME}
        # NOTE: these are converted above
        BOARD ${MY_BOARD}
        PORT ${MY_PORT}   # note: required otherwise will not generate upload target

        SRCS src/main.cpp
)

to:


blink = alamake.create('blink', 'arduino', shared=sh1)
blink.add_sources([
    'src1/main.cpp',
])
blink.add_include_directories(['src'])

- John Arrizza