summaryrefslogtreecommitdiff
path: root/docs/source/buildfile.rst
blob: 2786a32138650f4aa24c63e52794a2a11f5630ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
First Build File
================

Hello World Example
-------------------

Writing your first ``build.py`` file is actually quite simple. It's just
Python!::

    #!/usr/bin/env python

    def helloworld():
        print("Hello World")

Running this, we should see something similar to::

    $ xnt helloworld
    Hello World

Adding Xnt to the Mix
---------------------

Let's start to add some of the features provided by Xnt::

    #!/usr/bin/env python

    from xnt import target #For describing targets
    import xnt             #For built-in tasks

    @target
    def init():
        """
        Initialize Project Build Directories
        """
        xnt.mkdir("build")

That may be a lot to take in initially. Let's step through it.

In the first section::

    #!/usr/bin/env python

    from xnt import target #For describing targets
    import xnt             #For built-in tasks

If you're familiar with Python, you will notice there is nothing special
happening with this file yet. We are simply defining the file as a Python file,
including the ``target`` attribute from the ``xnt`` module, and importing the
``xnt`` module.

A change from previous versions: importing ``xnt`` will now implicitly import
``xnt.tasks``.

Next, we will look at a new target::

    @target
    def init():
        """
        Initialize Project Build Directories
        """
        xnt.mkdir("build")

This is a standard definition of a Python function with a decorator.

First, the ``target`` decorator marks the function definition as a target (to
be used by the ``list-targets`` command, see :ref:`otherCommands`). Next, we
define the function; this function name *is* the name of the target. That is,
the name given to the function will be the name given to the command to invoke
this target.  Further, we have the docstring; (this is also used by the
``list-targets`` command) the docstring provides a quick description of the
purpose of the target, or what the target accomplishes when ran. Finally, we
call ``mkdir`` of the ``xnt`` (internally of the ``xnt.tasks``) module. This
function, if not obvious by the name, creates a directory named 'build' (see
:doc:`taskreference`).

Target Decorator
================

The ``target`` decorator is just a standard Python decorating function with a
few exceptions. Namely, a new default behaviour is being introduced: a target
will _not_ execute more than once in a single invocation of ``xnt``. However,
if you so require a target to execute more than once, the option is provided.
For example::

    import xnt

    @xnt.target
    def init():
        #executes initalization

    @xnt.target
    def build():
        init() # depends on init
        # compiles project

    @xnt.target
    def package():
        init() # explicitly depends on init
        build() # depends on build
        # packages project together

The ``target`` decorators new default behaviour will not run the ``init``
target more than once if the ``package`` target was invoked. However, let's say
we introduce another target, ``clean`` for example, and define it as::

    @xnt.target(always_run=True)
    def clean():
        # clean project state

To further illustrate this new optional argument, let us define another
target, ``test`` that will call ``clean`` twice::

    @xnt.target
    def test():
        clean()
        package()
        # Run tests
        clean()

When we run our ``test`` target, we notice we want to start from a fresh, clean
state and we want to finish on a fresh, clean state. With the new default, this
wouldn't be possible, but with this new optional argument, ``always_run`` it
still is.

Return Values
=============

The targets you define can return an error code (or '0' for success) however
you see fit. Xnt will emit 'Failure' if the status code is *not* zero and will
otherwise remain silent if the code is zero. Further, the status code returned
by your target will be returned as the exit code of Xnt when finished
executing.

*Notice*, this allows Xnt to fail fast when attempting to execute multiple
targets. That is, if you specify more than one target, Xnt will stop at the
first failure.

If you don't define a return value for a target, Xnt will assume success and
return '0'.

Examples
--------

Not defining the return value::

    @target
    def foo():
        pass

Will result in (no success message; other output may be shown)::

    ...

Returning success (no success message; other output may be shown)::

    @target
    def foo():
        return 0

Will result in::

    ...

Returning failure (not 0)::

    @target
    def foo():
        return 1

Will result in::

    ...
    Failure

Most tasks have been updated to return error codes as well so that you can
return what it returns. If you find any tasks that can be updated to behave
this way, please create an issue for it.

.. _buildProperties:

Build Properties
================

As mentioned in :ref:`xntPropertiesParameters`, Xnt can accept parameters from
the command line and pass them into the build file. Xnt doesn't necessarily
expect the dictionary (named `PROPERTIES`) to exist; but if you ever intend to
use it, it will have to be defined one way or another (either to an empty
dictionary or actually hold values). For example, to define an empty
`PROPERTIES` dictionary, one could write their build file as such::

    #!/usr/bin/env python

    from xnt import target

    PROPERTIES = {}

    @target
    def foo():
        #uses properties somehow
        return 0

The hope for this feature is that it is easy to use because it borrows syntax
from other build tools that you may already be familiar with.