Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
moonmake
========
Introduction
------------
Moonmake is a configuration and build system written in Lua.
Moonmake does not facilitate outputting makefiles or configure scripts,
because they would need a shell or a `make' program, which are
considerably less portable than Lua. Instead, configuration and builds
are controlled by Lua code.
The moonmake project aims to provide a simple library and a command line
program that are useful in many cases, both related and unrelated to
building software, rather than a complex framework that makes it
possible to build a "hello world" program with a single line.
Dependencies
------------
* Lua 5.1
* luafilesystem (http://keplerproject.github.com/luafilesystem/)
* lua-subprocess (http://luaforge.net/projects/subprocess/)
If you're willing to manage different syntaxes that shells use to
redirect standard output and standard error, it is possible to write
a lua-subprocess replacement entirely in Lua, with judicious use of
temporary files, that will allow moonmake to execute jobs (not in
parallel) on systems where lua-subprocess is not supported.
Modules
-------
Moonmake is split up into several modules.
* `moonmake.atexit` - run cleanup code before exiting (currently unused)
* <<util,moonmake.util>> - general-purpose utility functions
* <<platform,moonmake.platform>> - get information about the platform that moonmake
is running on
[[optparse]]
* `moonmake.optparse` - general-purpose option parsing (see
link:optparse.txt[])
* <<conf,moonmake.conf>> - configuration engine
* <<make,moonmake.make>> - build engine
* <<tools,moonmake.tools>> - various tools
* the <<moonmake,moonmake executable>> - this loads and runs scripts, called
Moonfiles.
[[util]]
moonmake.util
-------------
This contains various general-purpose utility functions. The table
manipulating functions are particularly useful in a configure/build
system.
Concepts
~~~~~~~~
* _iterator_
This is a function that, each time it is called (with no arguments),
returns the next value in a sequence, until it returns nil, which
indicates the end of a sequence. They can be used with Lua's generic for
loop.
* _iterable_
This is something that can be iterated over with the `iter` function. It
can either be a table, a string or an iterator.
Iterator functions
~~~~~~~~~~~~~~~~~~
==== iter (x)
Returns an iterator for x.
[options="header"]
|===============================================
| If x is | returns
| table with __iter metamethod | __iter(x)
| table without __iter | ivalues(x)
| string | chars(x)
| anything else | x
|===============================================
==== table iterators
There are a number of functions for iterating over table items.
Note that `pairs` and `ipairs` are different from Lua's functions of the
same name, because they return closures and don't rely on its results
being fed back as arguments.
Note also that key,value iterators can also be used as iterators over
the keys, since the second return value can be ignored.
|=============================================
| | values | key,value pairs
| integer keys | ivalues | ipairs
| all keys | values | pairs
|=============================================
==== chars (s)
Returns an iterator over a string's individual characters.
==== totable (iterable)
Turns iterable into a table by accumulating the values it returns. It
returns a new table with the accumulated values at keys 1, 2, ...
==== concat (iterable, delim)
Works the same way as table.concat but it can use any iterable, not just
a table.
==== map (f, iterable)
Returns an iterator over f(x) for each value in iterable.
==== filter (f, iterable)
Returns an iterator over the values from iterable, but excluding any
items for which the predicate f is false.
==== count (iterable)
Returns the number of items from an iterable. If the iterable is an
iterator, it will be exhausted and cannot be used again.
==== any (iterable)
Returns true iff any item evaluates to true.
==== all (iterable)
Returns true iff all items evaluate to true.
==== range (a, b) | range (n)
Returns an iterator over a series of numbers. The first form iterates
over [a,b] (inclusive). The second form iterates over [1,n].
==== getter (k)
Returns a function f(x) -> x[k]. Useful with map.
Table functions
~~~~~~~~~~~~~~~
==== search (t, value)
Finds and returns a k for which t[k] == value, or nil if not found.
==== isearch (t, value)
Finds and returns a positive integer k for which t[k] == value, or nil
if not found.
==== copy (t)
Returns a shallow copy of table t, including the metatable reference.
==== icopy (t)
Returns a new table containing all the items from t that have positive
integer keys.
==== hcopy (t)
Returns a new table containing all the items from t that icopy wouldn't
copy.
==== compare (t1, t2)
Return true if t1[k] == t2[k] for any k.
==== isempty (t)
Return true if t has no items.
==== merge (t, ...)
A very versatile function!
The table t is mutated, based on each of the other arguments in turn.
Only the first argument, t, is mutated. The other arguments can be:
* table - positional items (with positive integer keys) are appended
on to the end of t. The non-positional items are copied with the same
keys, so that the values in t are replaced. For example,
----------
t = {1, 2, 3, a="foo", b="bar"}
merge(t, {4, 5, 6, a="bletch", c="hurrdurr"})
-- t is now equivalent to {1, 2, 3, 4, 5, 6, a="bletch", b="bar", c="hurrdurr"}
----------
* function - the value is assumed to be an iterator. Each item the
iterator returns is appended to the end of t.
* anything else - the value is appended to the end of t.
Returns t.
==== append (t, ...)
The table t is mutated, by appending each of the remaining arguments to
it.
Returns t.
String and IO-related functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
==== split (str, [sep], [nmax])
Splits a string into pieces using the delimiter sep (which is, by
default, the space character, " "). Returns a table containing all the
pieces. If nmax is given, at most nmax splits are done.
* split("") returns {}
* split("a,b", ",") returns {"a", "b"}
* split("a,,b", ",") returns {"a", "", "b"}
==== xsplit (str, [sep], [nmax])
Works exactly the same way as the split function, but returns an
iterator over the pieces instead of a table.
==== startswith (s, prefix)
Returns true if s starts with prefix.
==== endswith (s, suffix)
Returns true if s ends with prefix.
==== printf (fmt, ...) | fprintf(file, fmt, ...)
Behaves as in C. Slightly nicer than using `io.write(string.format(` etc.
==== printfln (fmt, ...)
Same as printf but appends a newline.
==== errorf (fmt, ...)
Formats a string before calling Lua's `error` function.
Path and filename functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~
==== splitext (filename)
Splits a filename into base and suffix.
* splitext "hello.txt" returns "hello", ".txt"
* splitext "hello" returns "hello"
==== swapext (filename, newext)
Changes the file suffix of a filename. Returns the new filename.
* swapext("hello.txt", ".doc") returns "hello.doc"
* swapext("hello", ".doc") returns "hello.doc"
==== basename (path)
Returns just the file portion of a path.
* basename "a/b/foo.o" returns "foo.o"
==== dirname (path)
Returns just the directory portion of a path.
* dirname "a/b/foo.o" returns "a/b"
==== path (...)
Joins bits of pathname (like Python's os.path.join).
* path("a", "b/c") returns "a/b/c"
* path("a", "/b") returns "/b"
* path("a", "../z") returns "a/../z"
==== isabs (path)
Returns true if the given path is absolute (not relative).
Miscellaneous
~~~~~~~~~~~~~
==== repr (obj)
Returns a string representation of the given object.
This string representation is valid Lua code where possible. Functions,
threads and userdata cannot have a valid Lua representation, so
`tostring` is called instead for such values.
[[platform]]
moonmake.platform
-----------------
This module sets three variables:
* platform - a string containing the name of the platform. This can take
one of the following values: "windows", "posix".
* pathsep - the string that delimits file names in paths.
* pathenvsep - the delimiter for the PATH environment variable.
[[conf]]
moonmake.conf
-------------
This module provides configuration contexts. A configuration context is
an object used to store the results of software configuration. For
example, in a C project, CFLAGS and LDFLAGS would be stored in a
configuration context, as well as information about required libraries.
This module is intended to do the same job as "configure" scripts, such
as those produced by autoconf.
A configuration context is created with the `newconf` function.
Configuration contexts can be saved to a file and loaded again. This is
done by writing to a file a Lua program that will reconstruct the
configuration context and all its variables. Thus, Lua's own parser can
be used to load a configuration context.
Getting and setting variables in a configuration context is done just
like a normal Lua table. Configuration contexts also have methods,
documented below.
You can store numbers, strings, tables and configuration contexts in a
configuration context, but tables must not contain referrential cycles.
Usage pattern
~~~~~~~~~~~~~
When the moonmake program is used, it creates a new configuration
context and passes it to the `configure` function in the Moonfile. The
`configure` function should then perform several tests.
Before a test, <<conf_test,conf:test>> should be called with a
description of the test. The test should then be carried out, by
whatever means, and then <<conf_endtest,conf:endtest>> should be called,
to report the results.
The test and endtest methods are only really for reporting results to
the user, so whether you call test...endtest for each action or just for
related groups of actions is entirely up to you. Calls to test and
endtest, however, must be matched.
Functions
~~~~~~~~~
[[tmpname]]
==== tmpname (tmpdir, namebase, suffix)
An improved os.tmpname.
Creates a temporary file in tmpdir. The name of the temporary file will
start with namebase and will end with suffix.
Returns name, fileobj where name is the name of the created file, and
fileobj is the file opened in write mode.
==== newconf () | conf:newconf ()
Creates and returns a new configuration context.
==== load (filename)
Loads a configuration context from the given filename.
==== conf:save (filename)
Saves the configuration context to the given filename.
==== conf:comment (var, text)
Stores a comment for the variable named `var`. This comment is written
out by `conf:save` and is to assist humans reading the produced code.
==== conf:getflags (name, default)
Gets tool options (eg. compiler flags) from an environment variable named
by `name`. The value of the environment variable is split up into a
table.
If an environment variable with the given name is not set, `default` is
returned.
[[conf_dir]]
==== conf:dir ()
Returns the name of a "playground" directory for configuration tests.
This is usually called ".conftest".
==== conf:tmpname (namebase, suffix)
Behaves as <<tmpname,tmpname>> but creates the temporary file in the
<<conf_dir,playground directory>>.
[[conf_test]]
==== conf:test (desc)
Starts a test. desc is a string that describes the test, for example
"Checking for libfoo".
[[conf_endtest]]
==== conf:endtest (result, success, [diagnostics])
Finishes a test started by `conf:test`.
result is a short string
describing the result. This could be "yes", "ok" or "failed", or it
could be a discovered filename or option.
success is a boolean that indicates whether the test was successful or
not.
diagnostics is an optional string that contains information about what
happened.
==== conf:abort ()
Aborts configuration. This function never returns.
==== conf:findprogram (cmds, [desc])
Finds a program.
cmds is a table of program names to search for in turn, while desc is an
optional test description to pass to <<conf_endtest,conf:endtest>>
On success, returns the name of the program that was found.
==== conf:findfile (files, dirs, [desc])
Searches for each file name in `files` in turn, in the list of
directories `dirs`. If a file is found, its full path is returned.
desc is an optional string describing the test.
Example: conf:findfile ({"foo", "bar"}, {"a/b", "c"}) would try a/b/foo, c/foo,
a/b/bar and c/bar. No recursion into subdirectories is done (that would be slow!)
[[make]]
moonmake.make
-------------
This module contains the build engine. Most of the functionality is
provided through builder objects (which will usually be named `bld` in
this documentation).
A builder object functions similarly to the `make` program. Targets,
dependencies and commands are specified for each file that might need
to be built. Once all these have been specified, the build engine works
out what needs to be done and does it. Unlike make, though, rules are
specified using Lua, rather than an esoteric macro language.
Operation of builders
~~~~~~~~~~~~~~~~~~~~~
Builders use file timestamps to see what has changed. There's no reason
why hashing couldn't be implemented instead, or as an option.
In addition, builders save state information related to which commands
targets need. This allows files to be rebuilt when the commands to build
them change. This is quite difficult to do with "make". Users usually
resort to running "make -B" to rebuild everything. Tee hee!
State information is saved in a file called ".moonmake.state". This file
name is currently hard-coded.
Functions
~~~~~~~~~
==== builder.new ()
Creates and returns a new builder object. The
<<moonmake,moonmake program>> will create a builder object for you and
pass it to the `build` function in the Moonfile.
==== is_builder (x)
Returns true if x is a builder object.
==== bld:target (target, depends, [command, [scanner]] )
Specifies dependencies, commands and a scanner function for a target
file.
* `target` is the file name of the target.
* `depends` is a table of filenames that the target depends on. This
doesn't need to include the files that the scanner function will
return (such as C header files), except files that are generated by
other rules.
In other words, any header files/include files/etc. that are generated
(and not always present) must be specified in the `depends` table,
otherwise the dependency engine wouldn't know what to make first!
* `command` is the command to run that produces the target file from the
sources. It is a table of arguments, including the name of the program
as `command[1]`.
There's no equivalent of make-like "automatic variables" that are
substituted here. I can't see a use case for this, since there is no
support for pattern rules.
IMPORTANT: At the moment, only one command can be specified for each
target. That's because I'm lazy, not because I don't think that feature
should be available.
* `scanner` is an optional function to do dependency scanning. This
function must accept the builder object as its first argument, and
the target node as its second. It must scan the source file(s) for
implicit ("dynamic") dependencies and return a table of filenames that
the target depends on.
If the scanner function returns duplicate dependencies, nothing bad
will (should!) happen.
`bld:target` should only be called once per target file.
==== bld:depends (target, depends)
Adds additional dependencies for the target file `target`. `depends` is
a table of dependency filenames.
This function can be called before or after `bld:target` and can be
called multiple times: the dependencies are accumulated.
==== bld:always_make (target)
Always make the specified target, regardless of up-to-date-ness. This is
useful if you are unable to do proper dependency scanning, among other
cases.
==== bld:alias (name, targets)
Defines an alias with name `name` that can be used as a target to build.
For example, when using the <<moonmake,moonmake program>>, defining an
alias called "all" would allow you to run "moonmake all".
`targets` is a table of file names that should be considered for
rebuilding when the alias is specified.
The alias "default" is treated specially. If no targets are requested
for building, and the "default" alias exists, the targets given for the
"default" alias will be considered for rebuilding.
Repeated calls to `bld:alias` with the same alias name will accumulate
the targets.
==== bld:loadstate () | bld:savestate()
State is loaded from or saved to the file ".moonmake.state".
==== bld:make ()
Runs the dependency engine and rebuilds files that need to be rebuilt.
If you are using the <<moonmake,moonmake program>>, you do not need to
call this method yourself.
NOTE: `bld:make` uses `bld.opts` (the results from the `optparse`
module) to find out which targets to consider for rebuilding. This isn't
a very good design.
===== Target selection
If targets are specified on the command line, these are considered for
rebuilding. The targets on the command line can be filenames or names of
aliases.
If no targets are specified on the command line, and an alias "default"
exists, this is used (as if only "default" were specified on the command
line).
If no targets are specified on the command line, and there is no
"default" alias, everything is considered for rebuilding.
==== bld:dump ()
Prints a dump of the dependency table. Useful for seeing what rules
were created.
Corresponds to the --dump option of <<moonmake,moonmake>>.
==== bld:clean ()
Removes specified targets and dependencies. This only removes files that
can be regenerated, so you'll be glad to hear it won't delete your
source files. Unless you did something wrong!
Corresponds to the --clean option of <<moonmake,moonmake>>
[[tools]]
[[tools_pkgconfig]]
moonmake.tools.pkgconfig
------------------------
This module is for using the pkgconfig system to find the compiler and
linker flags needed to use a package.
Functions
~~~~~~~~~
==== configure (conf)
Configures the pkgconfig tool in the given configuration context
(`conf`). This function finds the `pkg-config` program and stores its
location in `conf.PKGCONFIG`.
This function must be called for a configuration context before other
functions in this module are used.
==== getflags (conf, pkg)
Gets configuration information for the package named `pkg`. `conf` is the
configuration context. If the package is found, conf[pkg] is set to a
table containing the package's information (this consists of the
variables `CFLAGS` and `LIBS`), and this function returns true.
You can use the table set in conf[pkg] yourself or, if you're using
`moonmake.tools.c` for example, you can use the C tool's `use` option.
If the package is not found, this function returns false.
`configure (conf)` must have been called successfully for this configure
context before `getflags` is used.
moonmake.tools.c
----------------
This module provides support for the C programming language. The
following variable names are used in several places in the C module:
[[cvars]]
* `CC` is the name of the C compiler executable.
* `CFLAGS` is a table containing C compiler options.
* `LDFLAGS` is a table containing linker options.
* `LIBS` is a table containing library options. This is separate from
`LDFLAGS` because some toolchains, such as gcc, require the library
names to be specified at the end of the command-line.
Supported tools
~~~~~~~~~~~~~~~
This module supports the GNU C compiler (gcc) and will probably work
with the Tiny C compiler (tcc). It also works with MSVC.
Support for other compilers is needed!
Using MSVC
^^^^^^^^^^
The compiler (CL.EXE) should be in your PATH. The best way to do this is
to source VCVARS32.BAT. For dependency scanning, you must have the
makedepend program in your PATH, and the `INCLUDE` environment variable
should be set.
Functions
~~~~~~~~~
==== configure (conf, cc)
Performs configuration for a C compiler.
`cc` is the name of the compiler executable to use. If it is not
specified, common compiler names are searched for.
This function tests to ensure the compiler works, discovers the suffix
for object file names and checks to see if the -M option works (used for
dependency scanning).
If a C compiler is not found, is not working or cannot be configured,
this function returns a false value. If everything goes well, configure
returns true.
The following variables are set:
* `conf.CC` to the name of the C compiler executable.
* `conf.OBJSUFFIX` to the file name suffix of object files.
* `conf.LIBSUFFIX` to the file name suffix of shared libraries.
==== try_compile {conf, desc=..., [options...]}
Attempts to compile a small C program for testing purposes. This can be
used for testing for the presence of headers, libraries, functions,
command-line options etc.
The following options can be used in the single table argument:
* `desc` is the description string passed to <<conf_test,conf:test>>.
* `CC`, `CFLAGS`, `LDFLAGS`, `LIBS` override the variables of the same
name in the configuration context.
* `link` is an optional boolean. If true, the test program is compiled
and linked. If false or not specified, the test program is only
compiled.
* `content` is the small program to compile and possibly link. If not
specified, the following program is used:
-----
int main(){ return 0; }
-----
* `okstr`, `failstr` are strings to pass to
<<conf_endtest,conf:endtest>> to describe success and failure
respectively. Their defaults are "ok" and "no".
`try_compile` returns true if compilation (and linking) succeeded, and
false otherwise.
===== Example
-------------------------------------------------------------------------------
if not moonmake.tools.c.try_compile {conf, desc="Checking for puts",
link=true, content='#include "stdio.h"\n'
.. 'int main(){ puts("Testing"); return 0; }',
failstr="not found"}
then
print("Your C compiler does not provide a `puts' function.")
print("It is not a valid C compiler!")
conf:abort()
end
-------------------------------------------------------------------------------
==== compile (bld, dest, source, options)
Creates build rules to compile a C source file.
* `bld` is the builder.
* `dest` is the name of the object file to produce. This can be `nil`,
in which case the object file name is calculated automatically.
* `source` is the name of the source file to compile.
* `options` is an optional table with options in:
** `CC`, `CFLAGS` have their <<cvars,usual meanings>>.
** `conf` is a configuration context to use settings from. By
default, `bld.conf` is used.
Returns the target node for the object file.
==== program (bld, dest, sources, options)
Creates rules to Compile and link a C program.
* `bld` is the builder.
* `dest` is the name of the program to produce.
* `sources` is a table of source file names or object file names. If
file names in this table have a source file suffix, rules to compile
them are also produced.
Alternatively, `sources` can be a single string s, which is treated
the same way as {s}.
* `options` is an optional table with options in:
** `CC`, `CFLAGS`, `LDFLAGS`, `LIBS` have their
<<cvars,usual meanings>>.
** `conf` is a configuration context to use settings from. By
default, `bld.conf` is used.
==== shared_library (bld, dest, sources, options)
Works in exactly the same way as `program`, but creates a shared library
instead of a program.
Tutorial
--------
This section describes how to use the moonmake program and how to write
moonmake scripts.
A moonmake script is a Lua source file called "Moonmake", which is
loaded by tho "moonmake" program. It may contain any functions, but
these function names are called by moonmake.
Moonfile functions
~~~~~~~~~~~~~~~~~~
==== options (opts)
If present, this should add command line option information to the table
`opts`. The `opts` table will be passed to
<<optparse,the optparse module>>.
==== configure (conf)
If present, this should use the <<conf,configuration context>> `conf` to
find paths, settings, libraries etc. that are needed.
==== build (bld)
If present, this should construct rules for the <<make,builder object>>
`bld`.
Examples
~~~~~~~~
Here is an example Moonfile for building a single-source C program. Its
semantics are very much like that of a simple Makefile for the same job.
.Moonfile for a single-source C program
------------------------------------------------------------
function build(bld)
bld:target("hello.o", "hello.c",
{"gcc", "-Wall", "-c", "-o", "hello.o", "hello.c"})
bld:target("hello", "hello.o",
{"gcc", "-o", "hello", "hello.o"})
end
------------------------------------------------------------
Note that the core of moonmake is not language-specific, so support for the C
language is loaded by "require"-ing the C tool.
NOTE: In some build systems, such as scons or waf, tools are located in
a special tools directory and are loaded using a special function. In
some systems the tool's functions are also injected into your builder
object's namespace. In moonmake, this is not the case. You load tools
just like ordinary Lua modules, and use the functions it provides in its
own namespace.
.Using the C tool
----------------------------------------------
c = require "moonmake.tools.c"
function build(bld)
c.program(bld, "hello", "hello.c", {
CC = "gcc",
OBJSUFFIX = ".o",
CFLAGS = {"-Wall"}
})
end
----------------------------------------------
Put this code into a file called "Moonfile" in the same directory as a C
program, "hello.c". Run "moonmake" and watch your C program get
dependency-scanned, compiled and linked.
TIP: Now view .moonmake.state in a text editor to see the stored
commands and the results of dependency scanning.
In order to find a C compiler and its settings automatically, as well as
ensure the C compiler is suitable for use, you can define a configure
function.
.Configuring the C tool
-------------------------------------------------------------
c = require "moonmake.tools.c"
function configure(conf)
if not c.configure(conf) then conf:abort() end
end
function build(bld)
c.program(bld, "hello", "hello.c", {CFLAGS={"-Wall"}})
end
-------------------------------------------------------------
NOTE: CC and OBJSUFFIX are now stored in the configuration context, and
no longer need to be provided to `c.program`. The main configuration
context (`bld` in `configure`) is provided to the `build` function as
`bld.conf`.
Run `moonmake configure`, and if it is successful, run `moonmake` to
build.
Now, what if you want to use a library? You would need to know where the
library is located, as well as where the headers are. The pkg-config
tool has been developed to make finding libraries and their headers
easy.
[TIP]
================================================================================
When pkg-config or an equivalent system is not available, it is
tempting to try to guess the locations of packages, either by testing
common locations or by searching. While this may carry merit on some
platforms, others such as Windows have no standard directory structure
for libraries and headers, making such testing very difficult.
On systems without pkg-config, it is expected that someone compiling a
project knows what they're doing, and will be able to fill in the
information that pkg-config would've provided. In moonmake, this is
achieved by editing the configuration state, currently saved in a file
called "config.log".
================================================================================
Moonmake comes packaged with the
<<tools_pkgconfig,moonmake.tools.pkgconfig>> module for easily using the
pkg-config tool.
.Using pkg-config
--------------------------------------------------------------------------------
c = require "moonmake.tools.c"
pkgconfig = require "moonmake.tools.pkgconfig"
function configure(conf)
if not c.configure(conf) then conf:abort() end
if pkgconfig.configure(conf) then
if not pkgconfig.getflags(conf, "libpng") then
print "libpng not found. Please check your build environment"
print "and PKG_CONFIG_PATH, and try again."
conf:abort()
end
else
conf.libpng = {
CFLAGS = {"-I/usr/include/libpng14"},
LIBS = {"-lpng14"},
}
end
end
function build(bld)
c.program(bld, "hello", "hello.c", {
CFLAGS={"-Wall"},
use={"libpng"}})
end
--------------------------------------------------------------------------------
Multiple configuration contexts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you wish to compile one program for running on the host (such as a
build tool) and one program for running on the target environment, you can
use multiple configuration contexts.
The host and target environments are not the same when cross-compiling,
and it's important to support cross-compiling where possible, for
situations when a compiler is not available on the target (for example
slow or embedded computers).
--------------------------------------------------------------------------------
c = require "moonmake.tools.c"
function configure(conf)
if not c.configure(conf, "arm-elf-gcc") then conf:abort() end <1>
conf.host = conf:newconf() <2>
if not c.configure(conf.host) then conf:abort() end <3>
end
function build(bld)
c.program(bld, "tool", "tool.c", {
conf = bld.conf.host })
c.program(bld, "hello", "hello.c")
end
--------------------------------------------------------------------------------
<1> The C tool is configured to use arm-elf-gcc, for cross-compiling.
You'll definitely want to get that string from the command line or the
environment.
<2> A new, blank configuration context is created.
<3> The C tool is configured in the new configuration context.
NOTE: The order of 1 and 2 doesn't matter.
Generated header files
^^^^^^^^^^^^^^^^^^^^^^
To use a C header file (or the equivalent in any other
language) generated from another build rule, you *must* explicitly
indicate the dependency.
NOTE: Generating a header from the `configure` function in a Moonfile
does not require an explicit dependency, because the configure function
is always completed, in full, before building commences.
--------------------------------------------------------------
-- [snip]
function build(bld)
c.program(bld, "tool", "tool.c", {conf=bld.conf.host})
bld:target("foo.h", "tool", {"./tool", "-o", "foo.h"})
c.compile(bld, "hello.o", "hello.c")
bld:depends("hello.o", "foo.h")
c.program(bld, "hello", "hello.o")
end
--------------------------------------------------------------
A nice feature of the declarativeness (I think I just made that word up)
of moonmake.make is that all five statements in the above build function
can be rearranged in any order.
Having said that, the object file name "hello.o" should really be
returned from c.compile, or maybe c.program.
Future work
+++++++++++
If a header file foo.h is generated from a build rule, and included in
many translation units, there are three options:
* Manually specify each dependency on foo.h, just like in the good old
Makefile days.
* Make every source file depend on foo.h. This would make all sources
recompile when foo.h is changed.
I therefore propose a "weak dependency" system whereby you can specify
an ordering of rules. In this case you would be able to instruct
moonmake to generate foo.h before compiling C sources, so that the
dependency scanner could accurately work out which sources need
recompiling.