Source code for craftr.ext.rules

# -*- mode: python -*-
# Copyright (C) 2016  Niklas Rosenstein
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
'''

.. autofunction:: alias
.. autofunction:: run
.. autofunction:: render_template
'''

__all__ = ['alias', 'run', 'render_template']

from os import environ
from craftr import *
from craftr.ext import platform
from craftr.ext.compiler import gen_output

import abc
import craftr
import sys


[docs]def alias(*targets, target_name=None): """ Create an alias target that causes all specified "targets" to be built. :param targets: The targets to create an alias for. You may pass None for an element, in which case it is ignored. :param target_name: Alternative target name. """ # TODO: This is a placeholder for a better alias implementation. inputs = [] for target in targets: if target: inputs.extend(target.inputs) builder = TargetBuilder(inputs, [], {}, name=target_name) return builder.create_target('echo', description='Collective alias target: {0!r}'.format(builder.name))
[docs]def run(commands, args=(), inputs=(), outputs=None, cwd=None, pool=None, description=None, target_name=None, multiple=False): """ This function creates a :class:`Target` that runs a custom command. The function is three different modes based on the first parameter. 1. If *commands* is a :class:`Target`, that target must list exactly one file in its outputs and that file is assumed to be a binary and will be executed by the target created by this function. The *args* parameter may be a list of additional arguments for the program. 2. If *commands* is a list, it is handled as a list of commands, never as a single command. Thus a string in the list represents a complete command, as does a list of strings (representing the command as its individual arguments). 3. If *commands* is a string, it will be treated as a single command. If multiple commands need to be invoked, :class:`TargetBuilder.write_multicommand_file` is used to create a script to invoke multiple commands. __Examples__ .. code-block:: python main = ld.link( output = 'main', inputs = objects, ) run = rules.run(main, args = [path.local('testfile.dat')]) .. code-block:: python run = rules.run([ 'command1 args11 args12 args13', ['command2', 'args21', 'args22', 'args23'], ], cwd = path.local('test'), multiple=True) :param commands: A :class:`Target`, string or list of strings/command lists. :param args: Additional program arguments when a :class:`Target` is specified for *commands*. :param inputs: A list of input files for the command. These can be referenced using the Ninja variable ``%in`` in the command(s). :param outputs: A list of outputs generated by the command. These can be referenced using the Ninja variable ``%out`` in the command(s). :param cwd: An optional working directory to switch to when executing the command(s). If None is passed, the build directory is used. :param pool: Override the default pool that the command is executed in. If a :class:`Target` is passed for *commands*, this will default to ``console``. :param description: Optional target description displayed when building with Ninja. :param multiple: True if *commands* is a list of commands. This will cause a shell/batch script to be created and invoked by Ninja. :param target_name: An optional override for the return target's name. :return: A :class:`Target`. """ builder = TargetBuilder(inputs, [], {}, name = target_name) program = None if isinstance(commands, Target): assert len(commands.outputs) == 1, "Target for rules.run() must specify exactly one output file" program = path.abspath(commands.outputs[0]) commands = [program] + list(args) pool = pool or 'console' builder.target_attrs['explicit'] = True multiple = False elif isinstance(commands, str): commands = [commands] multiple = False if not multiple: # We don't need a multi command file for a single command. if isinstance(commands, str): command = commands else: command = shell.join(commands) if cwd: if platform.name == platform.WIN32: command = ['cmd', '/c', 'cd', cwd, shell.safe('&&')] + command else: command = [shell.safe('('), 'cd', cwd, shell.safe('&&')] + command + [shell.safe(')')] else: command, program = builder.write_multicommand_file(commands, cwd=cwd) inputs = builder.inputs or program return builder.create_target(command, inputs, outputs, pool=pool, description=description, explicit=True)
[docs]def render_template(template, output, context, env = None, target_name = None): ''' Creates a :func:`task` that renders the file *template* using Jinja2 with the specified *context* to the *output* file. .. code-block:: python # craftr_module(my_project) import jinja2 from craftr import path from craftr.ext import rules # We can use the render_template() task factory to render # a Jinja2 template that outputs a linker script. ld_script = rules.render_template( template = path.local('my_project.ld.jinja2'), output = 'test.html', env = jinja2.Environment( variable_start_string = '{$', variable_end_string = '$}', ), context = dict( # Context variables here ) ) :param template: Filename of a Jinja template. :param output: Output filename. :param context: Context dictionary. :param env: A :class:`jinja2.Environment` object. :param target_name: Optional target name. Automatically deduced from the assigned variable if omitted. ''' import jinja2 builder = TargetBuilder([template], [], {}, name = target_name) if not env: env = jinja2.Environment() def _render(_inputs, _outputs): with open(template) as fp: tobj = env.from_string(fp.read()) with open(output, 'w') as fp: fp.write(tobj.render(context)) return builder.create_target(_render, inputs = [template], outputs = [output])