Skip to content
Permalink
Browse files

doc: extensions: Refactor cmake build instructions generation

In order to simplify the current implementation and to prepare for a
change where we default to west as the standard build tool, partially
refactor the generation of cmake-based build instructions:

- Introduce `cd-into` as boolean flag that controls whether build
instructions are generated from the current working directory or from
inside the actual app folder. In the case of cmake, this includes
changing into the build folder to run ninja or make.

- Default to building from the current working directory, in the case of
cmake using the `-B` flag to create the build folder.

- Remove the usage of ZEPHYR_BASE with the `zephyr-app` option.
The option itslef is kept fsince it has semantic value, and a comment is
added when used.

- Consolidate the _generate_make and _generate_ninja functions into a
common code block inside _generate_cmake.

Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
  • Loading branch information...
carlescufi authored and andrewboie committed Jun 13, 2019
1 parent 1212604 commit 2670cd4ca1db8314694ed1ab276984b7e1e49666
Showing with 109 additions and 82 deletions.
  1. +109 −82 doc/extensions/zephyr/application.py
@@ -33,12 +33,15 @@ class ZephyrAppCommandsDirective(Directive):
The default is 'cmake'.
\:app:
if set, the commands will change directories to this path to the
application.
path to the application to build.
\:zephyr-app:
like \:app:, but includes instructions from the Zephyr base
directory. Cannot be given with \:app:.
path to the application to build, this is an app present in the upstream
zephyr repository. Mutually exclusive with \:app:.
\:cd-into:
if set, build instructions are given from within the \:app: folder,
instead of outside of it.
\:generator:
which build system to generate. Valid options are
@@ -95,6 +98,7 @@ class ZephyrAppCommandsDirective(Directive):
'tool': directives.unchanged,
'app': directives.unchanged,
'zephyr-app': directives.unchanged,
'cd-into': directives.flag,
'generator': directives.unchanged,
'host-os': directives.unchanged,
'board': directives.unchanged,
@@ -111,6 +115,7 @@ class ZephyrAppCommandsDirective(Directive):
TOOLS = ['cmake', 'west', 'all']
GENERATORS = ['make', 'ninja']
HOST_OS = ['unix', 'win', 'all']
IN_TREE_STR = '# From the root of the zephyr repository'

def run(self):
# Re-run on the current document if this directive's source changes.
@@ -121,6 +126,7 @@ def run(self):
tool = self.options.get('tool', 'cmake').lower()
app = self.options.get('app', None)
zephyr_app = self.options.get('zephyr-app', None)
cd_into = 'cd-into' in self.options
generator = self.options.get('generator', 'ninja').lower()
host_os = self.options.get('host-os', 'all').lower()
board = self.options.get('board', None)
@@ -151,10 +157,11 @@ def run(self):
if compact and skip_config:
raise self.error('Both compact and maybe-skip-config options were given.')

app = app or zephyr_app
in_tree = self.IN_TREE_STR if zephyr_app else None
# Allow build directories which are nested.
build_dir = ('build' + '/' + build_dir_append).rstrip('/')
num_slashes = build_dir.count('/')
source_dir = '/'.join(['..' for i in range(num_slashes + 1)])

# Create host_os array
host_os = [host_os] if host_os != "all" else [v for v in self.HOST_OS
@@ -164,22 +171,21 @@ def run(self):
if v != 'all']
# Build the command content as a list, then convert to string.
content = []
cd_to = zephyr_app or app
tool_comment = None
if len(tools) > 1:
tool_comment = 'Using {}:'

run_config = {
'host_os': host_os,
'cd_to': cd_to,
'app': app,
'in_tree': in_tree,
'cd_into': cd_into,
'board': board,
'shield': shield,
'conf': conf,
'gen_args': gen_args,
'source_dir': source_dir,
'build_args': build_args,
'build_dir': build_dir,
'zephyr_app': zephyr_app,
'goals': goals,
'compact': compact,
'skip_config': skip_config,
@@ -225,24 +231,31 @@ def _lit_block(self, content):
def _generate_west(self, **kwargs):
content = []
board = kwargs['board']
app = kwargs['app']
in_tree = kwargs['in_tree']
goals = kwargs['goals']
cd_to = kwargs['cd_to']
cd_into = kwargs['cd_into']
build_dir = kwargs['build_dir']
compact = kwargs['compact']
kwargs['board'] = None
cmake_args = self._cmake_args(**kwargs)
cmake_args = ' --{}'.format(cmake_args) if cmake_args != '' else ''
# ignore zephyr_app since west needs to run within
# the installation. Instead rely on relative path.
src = ' {}'.format(cd_to) if cd_to else ''
src = ' {}'.format(app) if app and not cd_into else ''
dst = ' -d {}'.format(build_dir) if build_dir != 'build' else ''

goal_args = ' -b {}{}{}{}'.format(board, dst, src, cmake_args)
if in_tree and not compact:
content.append(in_tree)

if cd_into and app:
content.append('cd {}'.format(app))

if 'build' in goals:
content.append('west build{}'.format(goal_args))
# No longer need to specify additional args, they are in the
# CMake cache
goal_args = '{}'.format(dst)
build_args = ' -b {}{}{}{}'.format(board, dst, src, cmake_args)
content.append('west build{}'.format(build_args))

goal_args = '{}'.format(dst)
if 'sign' in goals:
content.append('west sign{}'.format(goal_args))

@@ -266,6 +279,8 @@ def _mkdir(self, mkdir, build_dir, host_os, skip_config):
content = []
if skip_config:
content.append("# If you already made a build directory ({}) and ran cmake, just 'cd {}' instead.".format(build_dir, build_dir)) # noqa: E501
if host_os == 'all':
content.append('mkdir {} && cd {}'.format(build_dir, build_dir))
if host_os == "unix":
content.append('{} {} && cd {}'.format(mkdir, build_dir, build_dir))
elif host_os == "win":
@@ -274,6 +289,7 @@ def _mkdir(self, mkdir, build_dir, host_os, skip_config):
return content

def _cmake_args(self, **kwargs):
generator = kwargs['generator']
board = kwargs['board']
shield = kwargs['shield']
conf = kwargs['conf']
@@ -285,91 +301,102 @@ def _cmake_args(self, **kwargs):

return '{}{}{}{}'.format(board_arg, shield_arg, conf_arg, gen_args)

def _generate_make(self, **kwargs):
build_args = kwargs['build_args']
source_dir = kwargs['source_dir']
goals = kwargs['goals']
def _cd_into(self, mkdir, **kwargs):
app = kwargs['app']
host_os = kwargs['host_os']
compact = kwargs['compact']

build_args = ' {}'.format(build_args) if build_args else ''
cmake_args = self._cmake_args(**kwargs)

build_dir = kwargs['build_dir']
skip_config = kwargs['skip_config']
content = []
content.extend(['cmake{} {}'.format(cmake_args, source_dir)])
if not compact:
content.extend(['',
'# Now run make on the generated build system:'])
if 'build' in goals:
content.append('make{}'.format(build_args))
for goal in goals:
if goal == 'build':
continue
content.append('make {}'.format(goal))
os_comment = None
if len(host_os) > 1:
os_comment = '# On {}'
num_slashes = build_dir.count('/')
if not app and mkdir and num_slashes == 0:
# When there's no app and a single level deep build dir,
# simplify output
content.extend(self._mkdir(mkdir, build_dir, 'all',
skip_config))
if not compact:
content.append('')
return content
for host in host_os:
if host == "unix":
if os_comment:
content.append(os_comment.format('Linux/macOS'))
if app:
content.append('cd {}'.format(app))
elif host == "win":
if os_comment:
content.append(os_comment.format('Windows'))
if app:
backslashified = app.replace('/', '\\')
content.append('cd {}'.format(backslashified))
if mkdir:
content.extend(self._mkdir(mkdir, build_dir, host, skip_config))
if not compact:
content.append('')
return content

def _generate_ninja(self, **kwargs):
def _generate_cmake(self, **kwargs):
generator = kwargs['generator']
host_os = kwargs['host_os']
cd_into = kwargs['cd_into']
app = kwargs['app']
in_tree = kwargs['in_tree']
host_os = kwargs['host_os']
build_dir = kwargs['build_dir']
build_args = kwargs['build_args']
source_dir = kwargs['source_dir']
skip_config = kwargs['skip_config']
goals = kwargs['goals']
compact = kwargs['compact']

content = []

if in_tree and not compact:
content.append(in_tree)

if cd_into:
num_slashes = build_dir.count('/')
mkdir = 'mkdir' if num_slashes == 0 else 'mkdir -p'
content.extend(self._cd_into(mkdir, **kwargs))
# Prepare cmake/ninja/make variables
source_dir = ' ' + '/'.join(['..' for i in range(num_slashes + 1)])
cmake_build_dir = ''
tool_build_dir = ''
else:
source_dir = ' {}'.format(app) if app else ' .'
cmake_build_dir = ' -B{}'.format(build_dir)
tool_build_dir = ' -C{}'.format(build_dir)

# Now generate the actual cmake and make/ninja commands
gen_arg = ' -GNinja' if generator == 'ninja' else ''
build_args = ' {}'.format(build_args) if build_args else ''
cmake_args = self._cmake_args(**kwargs)

content = []
content.extend(['cmake -GNinja{} {}'.format(cmake_args, source_dir)])
if not compact:
if not cd_into and skip_config:
content.append("# If you already ran cmake with -B{}, you " \
"can skip this step and run {} directly.".
format(build_dir, generator)) # noqa: E501
else:
content.append('# Use cmake to configure a {}-based build' \
'system:'.format(generator.capitalize())) # noqa: E501

content.append('cmake{}{}{}{}'.format(cmake_build_dir, gen_arg,
cmake_args, source_dir))
if not compact:
content.extend(['',
'# Now run ninja on the generated build system:'])

if 'build' in goals:
content.append('ninja{}'.format(build_args))
content.append('{}{}{}'.format(generator, tool_build_dir,
build_args))
for goal in goals:
if goal == 'build':
continue
content.append('ninja {}'.format(goal))
return content

def _generate_cmake(self, **kwargs):
host_os = kwargs['host_os']
cd_to = kwargs['cd_to']
zephyr_app = kwargs['zephyr_app']
host_os = kwargs['host_os']
build_dir = kwargs['build_dir']
skip_config = kwargs['skip_config']
compact = kwargs['compact']
generator = kwargs['generator']
content.append('{}{} {}'.format(generator, tool_build_dir, goal))

num_slashes = build_dir.count('/')
mkdir = 'mkdir' if num_slashes == 0 else 'mkdir -p'
content = []
os_comment = None
if len(host_os) > 1:
os_comment = '# On {}'

for host in host_os:
if host == "unix":
if os_comment:
content.append(os_comment.format('Linux/macOS'))
if cd_to:
prefix = '$ZEPHYR_BASE/' if zephyr_app else ''
content.append('cd {}{}'.format(prefix, cd_to))
elif host == "win":
if os_comment:
content.append(os_comment.format('Windows'))
if cd_to:
prefix = '%ZEPHYR_BASE%\\' if zephyr_app else ''
backslashified = cd_to.replace('/', '\\')
content.append('cd {}{}'.format(prefix, backslashified))
content.extend(self._mkdir(mkdir, build_dir, host, skip_config))
if not compact:
content.append('')

if not compact:
content.append('# Use cmake to configure a {}-based build system:'.format(generator.capitalize())) # noqa: E501
if generator == 'make':
content.extend(self._generate_make(**kwargs))
elif generator == 'ninja':
content.extend(self._generate_ninja(**kwargs))
return content


0 comments on commit 2670cd4

Please sign in to comment.
You can’t perform that action at this time.