lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


2013/4/18 Hisham <h@hisham.hm>:

> Documentation is edited directly in the wiki. If anyone knows an easy
> way to make a local dump from an online instance of Sputnik, I could
> integrate that into my release-packaging script...

Quick-and-dirty way, if you have Pandoc installed (that and Anansi are
all I use cabal for, BTW).

~~~ executable bash file pulltopic
#! /bin/sh
wget http://www.luarocks.org/en/$1
pandoc $1 -f html -t markdown | tail -n +44 | head -n -7 > $1.txt
~~~

Dirty because of the +44 and -7.

Makes Markdown source. E.g.

$ ./pulltopic Creating_a_rock

made the attached file. Making HTML, manpage, PDF is also possible
with Pandoc.
So, you wrote a Lua package (containing one or more modules) and want to
make it available to users through LuaRocks. The process consists
essentially of the following steps:

-   Writing a rockspec file
-   Publishing your code online
-   Submitting a rockspec for inclusion in the rocks server

Writing a rockspec
------------------

A rockspec file is the metadata file for your package, containing all
the information LuaRocks needs in order to fetch, build and install your
package. The [Rockspec format](/en/Rockspec_format) supports various
kind of build systems, but in this tutorial we'll use LuaRocks's own
built-in build system -- that's why we're listing "Writing a rockspec"
as the first step. We'll use the rockspec in place of a Makefile.

A rockspec is actually a Lua file, but it is loaded in an empty
environment, so there are no Lua functions available. A skeleton for a
basic rockspec looks like this:

    package = "LuaFruits"
    version = "1.0-1"
    source = {
       url = "http://..."; -- We don't have one yet
    }
    description = {
       summary = "An example for the LuaRocks tutorial.",
       detailed = [[
          This is an example for the LuaRocks tutorial.
          Here we would put a detailed, typically
          paragraph-long description.
       ]],
       homepage = "http://...";, -- We don't have one yet
       license = "MIT/X11" -- or whatever you like
    }
    dependencies = {
       "lua ~> 5.1"
       -- If you depend on other rocks, add them here
    }
    build = {
       -- We'll start here.
    }

This should be saved in a file called luafruits-1.0-1.rockspec. The name
must contain lowercase versions of the "package" and "version" fields,
or else LuaRocks will complain.

There is some missing stuff in our rockspec which we will fill later,
such as the source.url and source.homepage fields. We'll add those when
we upload our sources whatever server you choose to.

Right now our focus will be on the "build" section.

### Building a module

LuaRocks supports a number of "build types": "make" for using Makefiles,
"cmake" for using CMake, etc. In this tutorial, however, we'll use its
built-in build system, called the "builtin" type.

    build = {
       type = "builtin"
       -- Now we need to tell it what to build.
    }

In the "builtin" type, we add a subtable called "modules", in which keys
are module names in Lua notation, and values indicate how to build them.
This example shows the various possibilities supported:

    build = {
       type = "builtin",
       modules = {

          -- A simple module written in Lua
          apricot = "src/apricot.lua",

          -- Note the required Lua syntax when listing submodules as keys
          ["apricot.seeds"] = "src/apricot/seeds.lua",

          -- A simple module written in C
          banana = "src/banana.c",

          -- C modules can span multiple files.
          cherry = {"src/cherry.c", "src/cherry_pie.c"},

          -- C modules also support an extended syntax, supporting
          -- cross-platform specifications of C defines, libraries and
          -- paths for external dependencies.
          date = {
             sources = {"src/date.c", "src/cali_date.c", "src/arab_date.c"},
             defines = {"MAX_DATES_PER_MEAL=50"}
             libraries = {"date"},
             incdirs = {"$(LIBDATE_INCDIR)"},
             libdirs = {"$(LIBDATE_LIBDIR)"}
          }
       }
    }

Since modules written in Lua do not need to be compiled, LuaRocks only
needs to know where they are in your source directory tree in order to
copy them to the proper place on installation (see the "apricot" and
"apricot.seeds" in the above example).

Similarly, for C code with no dependencies on external libraries, it
suffices to say where the sources are and LuaRocks will invoke the
appropriate compiler and linker for the platform. You can use a simple
string value for single-file modules, such as the "banana" example above
or an array value for multiple source files (as in the "cherry"
example).

#### Depending on other rocks

You can specify that your code depends on other rocks, so you can use
modules from other rocks in your code and have them be automatically
downloaded and installed when a user installs your rock.

Suppose we need the modules from the "luaknife" rock, in order to cut
our fruits. But we need version later than 2.3 of luaknife, because
we're using functions that were introduced in that version. We'll add
that to our dependencies table:

    dependencies = {
       "lua ~> 5.1",
       "luaknife >= 2.3"
    }

Note that the dependency on a specific Lua version is also given in that
table, and that it uses a special operator that's not a standard Lua
operator. It was inspired by the "pessimistic operator" of RubyGems:
"\~\> 2" means "\>= 2, \< 3"; "\~\> 2.4" means "\>= 2.4, \< 2.5". Using
"lua \~\> 5.1" we're saying that our code works only on Lua 5.1 and
doesn't work with Lua 5.2.

Many modules can be written to run in both Lua 5.1 and 5.2, so the
dependency can be written instead as "lua \>= 5.1, \< 5.3". All
operators from the comma-separated list must be satisfied. For this
reason, we can't use \~\> in that case because "lua \>= 5.1, \~\> 5.2"
means "lua \>= 5.1, \>= 5.2, \< 5.3", which would make it reject Lua
5.1.

#### C modules linking to external libraries

*If your code does not use third-party libraries, you may skip this
subsection.*

For building C code that links to C libraries, you can use the long
syntax given in the "date" example above, in which sources are listed in
the "sources" subtable. You need to specify the libraries to be linked
in the "libraries" subtable. The library name is specified in a
platform-independent way: in the above example, libraries={"date"} will
result in -ldate for GCC on Unix and DATE.LIB for MSVC on Windows. (Note
that if this is not appropriate, the rockspec format allows
[per-platform overrides](/en/Rockspec_format).) If you need to link code
that uses libraries, you need to tell LuaRocks where to find them. You
do this by adding a new section to the rockspec:

    ....
    external_dependencies = {
       LIBDATE = {
          header = "libdate.h"
       }
    }
    build = {
    ....

Adding the "external\_dependencies" table will make LuaRocks search for
the external dependencies in its lookup path (on Unix the defaults are
/usr/local and /usr; on Windows, which doesn't have a standard for
development trees, you'll probably have to specify yourself through the
[config file](/en/Config_file_format) or the
[command-line](/en/luarocks) when invoking "luarocks"). We give a hint
to LuaRocks, the libdate.h header, so it can test whether the
development package is really there (on many Linux distros, one needs to
install "-dev" packages in order to have all headers and libraries
needed for compilation available, so header files are a good hint). In
this case, for example, it would look for /usr/local/include/libdate.h
and /usr/include/libdate.h on Unix. If you (or your users) have LibDate
installed elsewhere, it's always possible to tell LuaRocks so through
the command-line, passing for example LIBDATE\_DIR=/opt/libdate as an
argument.

When LuaRocks succeeds finding an external dependency, it creates
special variables for it which can be used in incdirs and libdirs
fields. The example above shows two such variables, LIBDATE\_INCDIR and
LIBDATE\_LIBDIR being use. It's important to always pass those
variables: if LibDate happened to be in the system lookup path of your
compiler, compilation would succeed without those variables, but they
would fail in a user's system where they are somewhere else, such as in
the LIBDATE\_DIR=/opt/libdate example given earlier.

### Using LuaRocks as a build system

Now that your build section is written, you can use LuaRocks as a build
system and have it compile and install your code as a rock. Just type:

    luarocks make

and it will do its thing. Like "make", it will look for the appropriate
rules file (in our case, the rockspec) in the current directory and then
will proceed to build and install the package in your rocks tree,
assuming the sources are in the current directory as well.

Publishing your code online
---------------------------

Now, to complete the rockspec for public consumption we need to fill the
sources.url field. For that, we need the code to be available online. By
default, LuaRocks expects the code to be packed in a zip or tarball
containing a directory with the lowercase name of the package and its
version number (without the LuaRocks revision number). In our example,
we should make a tarball with the following contents:

    luafruits-1.0/
    luafruits-1.0/src/apricot.lua
    luafruits-1.0/src/apricot/seeds.lua
    luafruits-1.0/src/banana.c
    luafruits-1.0/src/cherry.c
    luafruits-1.0/src/cherry_pie.c
    luafruits-1.0/src/date.c
    luafruits-1.0/src/cali_date.c
    luafruits-1.0/src/arab_date.c

Note that the rockspec itself doesn't need to be stored in the tarball
(actually, storing the final rockspec inside the package is a
chicken-egg problem because rockspecs can contain the source.md5 field
with a checksum of the tarball).

It's important to note that LuaRocks does not enforce the internal
structure. Our example only has a src/ subdirectory because the build
rules listed above used it (we could have done without it if we wanted
to). The tarball could contain other files as well, such as licenses and
documentation. The only convention used here is the top level directory
name, and if desired this can be overridden in the rockspec using the
sources.dir field. So, now we pack our code. On Unix, for example, that
would be:

    tar czvpf luafruits-1.0.tar.gz luafruits-1.0/

And now we're ready to publish the tarball online. You can upload it to
any public web server; if you need hosting space, you can create a
project in [SourceForge](http://sf.net) or use the Pages feature from
[Github](http://github.com/). Once your tarball is uploaded, we're ready
to complete our rockspec. By now it will look like this:

    package = "LuaFruits"
    version = "1.0-1"
    source = {
       url = "http://me.github.com/luafruits/luafruits-1.0.tar.gz";
    }
    description = {
       summary = "An example for the LuaRocks tutorial.",
       detailed = [[
          This is an example for the LuaRocks tutorial.
          Here we would put a detailed, typically
          paragraph-long description.
       ]],
       homepage = "http://me.github.com/luafruits";,
       license = "MIT/X11"
    }
    dependencies = {
       "lua >= 5.1",
       "luaknife"
    }
    external_dependencies = {
       LIBDATE = {
          header = "libdate.h"
       }
    }
    build = {
       type = "builtin",
       modules = {
          apricot = "src/apricot.lua",
          ["apricot.seeds"] = "src/apricot/seeds.lua",
          banana = "src/banana.c",
          cherry = {"src/cherry.c", "src/cherry_pie.c"},
          date = {
             sources = {"src/date.c", "src/cali_date.c", "src/arab_date.c"},
             defines = {"MAX_DATES_PER_MEAL=50"}
             libraries = {"date"},
             incdirs = {"$(LIBDATE_INCDIR)"},
             libdirs = {"$(LIBDATE_LIBDIR)"}
          }
       }
    }

Submitting a rockspec for inclusion in the rocks server
-------------------------------------------------------

This is the simplest step: just send the rockspec to the LuaRocks
[mailing list](/en/Mailing_list) and we'll include it in the public
LuaRocks repository. We will usually publish both the .rockspec file and
the appropriate .rock file in the server.

You can also create .rock files yourself. If you run the "pack" command
on your rockspec, like this:

    luarocks pack luafruits-1.0-1.rockspec

you will get a file called luafruits-1.0-1.src.rock, which contains the
rockspec itself and your sources. If you have the rock installed in your
local tree, you can also pack the binaries you just built:

    luarocks pack luafruits

(The version is optional, latest is assumed.) This will create a file
with a name such as luafruits-1.0-1.linux-x86.rock.

Conclusion
----------

And we're done -- by writing a simple rules file which simply describes
which sources compose the project and which libraries they use, we
actually achieved a lot behind the scenes: LuaRocks takes care of using
the right compiler for your platform (GCC? MSVC?), passing the right
flags (-fpic? -shared? -bundle -undefined dynamic\_lookup?) and checking
external dependencies (/usr/lib? /usr/local/lib? Is it really there?).
Getting all these little portability details right is not always easy to
do on hand-written Makefiles, so that's why we recommend using
LuaRocks's own build system whenever possible. There are many other
features of LuaRocks we haven't covered in this tutorial (per-platform
overrides, support for command-line scripts, etc.), but this should get
you started -- browse around the wiki for more.

And of course, we're eager to receive all kinds of feedback in the
LuaRocks [mailing list](/en/Mailing_list).

![](https://www.paypalobjects.com/pt_BR/i/scr/pixel.gif)

Powered by [Sputnik](http://sputnik.freewisdom.org/) | [XHTML
1.1](http://validator.w3.org/check?uri=referer)

\