16 December 2024

Compiling Io Addons

This is the second part of my journey into installing Io addons. A quick recap: Io "is a dynamic prototype-based programming language". It was made more? widely known by Bruce Tate's book Seven Languages in Seven Weeks. Io's extensions, i.e. libraries or packages however you like to call them, are called addons and follow the same structure. Eerie, Io's package manager, fails on my platform and I have to add addons manually. It is the main purpose of these posts to show how it can be done. I explain the installation of addons with different needs and increasing difficulty using concrete examples. In part one I covered Kano as an example for installing addons without any native code..

Trackibles Prototype (licensed CC BY-SA by Rodolphe Courtier)Compiling an Empty Addon on Windows: Kano
As an example, I want to compile an addon without any native code. I noticed that all installed addons, even the one without any native code, e.g. CGI, do have an init file and a dll. Maybe it is not strictly necessary. AddonLoader, a helper to the importer, checks for more than one C file - but I will need it later anyway. How does such an empty init file look like? For example, in the distribution IoCGIInit.c is
#include "IoState.h"
#include "IoObject.h"

__declspec(dllexport)

void IoCGIInit(IoObject *context)
{
}
Easy enough to create such a file Kano/​sources/​IoKanoInit.c. In the source distribution which I will mention later, there is generate.io in the root of the addons, which does exactly that. The script accepts a path to the addons dir and the name of the addon:
> cd "%IO_HOME%\lib\io\addons"
> io generate.io . Kano
> cd Kano\source
> gcc -fPIC -D _NO_OLDNAMES -c IoKanoInit.c
IoKanoInit.c:1:21: fatal error: IoState.h: No such file or directory
Of course I need headers. Where is the source distribution?

Io Source Distribution
The latest binary I downloaded from the Io site is iobin-​win32-​current.zip. Little version information there. Io reports io --version a version of 5.9.2011 but the executables and libraries were in fact created 5.11.2013. The nearest source release is 4.12.2013 which seems to be compatible. (The older 5.9.2011 source release is definitely incompatible with the addons included in iobin-​win32-​current.zip.) The source distribution is also interesting because it contains all sources of the standard library in libs\​iovm\​io\*.io.

The required headers are in libs\​iovm\​source\*.h and libs\​basekit\​source\*.h. After setting proper include paths (C_INCLUDE_PATH), compilation is possible. The switch -fPIC seems to be redundant on Windows, but it is set in one of Io's makefiles, so I keep it. I have no idea what I am doing ;-) The switch -D _NO_OLDNAMES defines _NO_OLDNAMES which solves the error: conflicting types for 'SSIZE_T' which occurs when old names are allowed. Sigh. Again, no idea what I am doing.
> cd Kano\source
> gcc -fPIC -D _NO_OLDNAMES -c IoKanoInit.c
> gcc -liovmall -shared -Wl,--out-implib,libIoKano.dll.a ^
      IoKanoInit.o -o libIoKano.dll
> mkdir ..\_build\dll
> move lib*.* ..\_build\dll
> cd ..\..
> io -e "Kano supportedFiles println"
list(make.io, Kanofile, kanofile, Kanofile.io, kanofile.io)
Compiling Native Code: Rainbow
Next I tackle addons with native code but without any third party dependencies. Most addons of this type are included in the distribution, because their compilation does only depend on Io itself, e.g. Blowfish, Box, LZO, MD5, Random, Range, etc. An addon I found which is missing is Rainbow, which "allows you to colourize command line output." Rainbow does not have a protos file and it has no package.json neither, but there is an eerie.json which does not say anything about prototypes. The addon contains a single Io file Rainbow.io and a single C header IoRainbow.h. I guess this is the prototype then - see line (1) in the following shell commands:
> git clone https://github.com/IoLanguage/Rainbow
Cloning into 'Rainbow'...
> touch Rainbow\depends
> echo Rainbow > Rainbow\protos && rem (1)
> io -e "Rainbow println"
Exception: Failed to load Addon Rainbow - it appears that the addon exists but needs compiling."
This is a useful error message. Io's AddonLoader checks native sources when loading an addon. Compile it. First
> io generate.io . Rainbow
creates a different IoRainbowInit.c file this time,
#include "IoState.h"
#include "IoObject.h"

IoObject *IoRainbow_proto(void *state); // (2)

__declspec(dllexport)

void IoRainbowInit(IoObject *context)
{
    IoState *self = IoObject_state((IoObject *)context);

    IoObject_setSlot_to_(context, SIOSYMBOL("Rainbow"), IoRainbow_proto(self)); // (3)
}
I have marked relevant references to the addon a.k.a. the "primary" prototype in bold face. Line (2) is the forward reference to code in Rainbow.c, which will create a new prototype. Line (3) sets this prototype under the symbol of its name into a slot of the context. (Io stores all variables in slots, many functions in the standard library work with slots, e.g. slotNames() or getSlot​(name).) There is some more information about writing C addons for Io in the Io Wikibook. For example it explains the addon structure - which I already knew but at least my "research" is validated. ;-)

Back to the compilation of Rainbow. Continue after line (1) in the previous script:
> cd Rainbow\source
> gcc -fPIC -D _NO_OLDNAMES -c IoRainbowInit.c IoRainbow.c
> gcc -liovmall -shared -Wl,--out-implib,libIoRainbow.dll.a ^
      IoRainbowInit.o IoRainbow.o -o libIoRainbow.dll
Creating library file: libIoRainbow.dll.a
IoRainbow.o:IoRainbow.c:(.text+0x5c): undefined reference to `_imp__IoTag_newWithName_'
... more errors omitted
Showing the strain (licensed CC BY by Brian Smithson)Of Course, Linker Errors
These errors are strange and I spent a long time investigating. The libiovmall.dll.a exports __imp​__IoTag_​newWithName_, with two underscores in the beginning but the linker only looks for one. I verified this with nm libiovmall.dll.a | grep __imp__. This is due to some compiler inconsistency between Linux and Windows and/or GCC and/or MingW regarding function name decoration. GCC Options "-fleading-underscore and its counterpart, -fno-leading-underscore, change the way C symbols are represented in the object file." I tried both of them but then other symbols like fprintf are not found any more. There might be a linker option -Wl,--add-stdcall-underscore but it is not recognised by my MingW GCC. Sigh.

The solution is to not link against the link libraries (*dll.a), but against the dlls directly. (Either delete the *.dll.a from the io/lib folder or change your LIBRARY_PATH to use io/bin instead.) Summary: When your linker looks for a single underscore in dll exported symbols, and the library exports correctly, link against the dll instead of the lib..

The change to the library path fixes the linking and libIoRainbow.dll is created. The dll must be moved to the build result folder ..\​_build\​dll as before. In addition all headers should be copied to _build\​headers, as other addons might depend on them.
> mkdir ..\_build\dll
> move lib*.* ..\_build\dll
> mkdir ..\_build\headers
> copy *.h ..\_build\headers
> cd ..\..
> io -e "Rainbow println"
Rainbow_0x29ba488
All addons contained in the Windows distribution have the _build\​headers folder, but there are no headers. I will need these headers to compile Regex in part three. Let's iterate all addons and copy the headers:
set ADDONS=%IO_HOME%\lib\io\addons
dir /A:D /b "%ADDONS%" > addons.txt
for /F %a in (addons.txt) do ^
    if exist "%ADDONS%\%a\source\*.h" ^
        copy /Y "%ADDONS%\%a\source\*.h" "%ADDONS%\%a\_build\headers"
del addons.txt
Addon Initialization Recursion Issue
One recurring issues I faced was that AddonLoader was recursing endlessly trying to load something. For example, when I created IoRainbowInit.c by hand, I made a mistake with the symbol in line (3). On loading the addon, AddonLoader entered an endless loop,
> io -e "Rainbow println"
IOVM: Received signal. Setting interrupt flag.

current coroutine
---------
Object Rainbow            Rainbow.io 14
FileImporter importPath   Z_Importer.io 49   <-
FileImporter import       Z_Importer.io 125    |
List detect               Z_Importer.io 125    |
true and                  Z_Importer.io 125    |
Object Rainbow            Rainbow.io 14        |
Importer import           Z_Importer.io 138    |
FileImporter importPath   Z_Importer.io 49   --
...
This was because the proto was not registered under its correct name, see SIOSYMBOL in line (3) above, and the importer tried to import it again, found the addon (again), and repeated. Tip: When addon importing enters a loop, double check the symbol name in the C init.

A similar error happens when the a prototype of an addon uses another prototype of that addon during code loading. For example Eerie comes with two prototypes: Eerie and SystemCommand. The first line of Eerie.io imports SystemCommand. Now when I evaluate Eerie println, the AddonLoader finds the Eerie addon (by name) and loads the file Eerie.io to load that prototype. During import it sees SystemCommand as a dependency and looks for it. It finds it in the proto file of Eerie and tries to load it. When loading the addon, it loads the prototype of the same name, i.e. Eerie.io again.
> io -e "Eerie println"
IOVM: Received signal. Setting interrupt flag.

current coroutine
---------
Object SystemCommand        Eerie.io 4
Addon load                  AddonLoader.io 124  <-
AddonLoader loadAddonNamed  Z_Importer.io 97      |
FolderImporter import       Z_Importer.io 125     |
List detect                 Z_Importer.io 125     |
true and                    Z_Importer.io 125     |
Object SystemCommand        Eerie.io 4            |
Importer import             Z_Importer.io 138     |
Addon load                  AddonLoader.io 124  --
...
Cyclic imports are a bad idea anyway, it is fair that this does not work. Still I have not seen any warning about it in the Io documentation. Tip: Avoid top level "imports" of prototypes from the same addon.

Compiling Native Code: Thread
Another addon without dependencies is Thread. It has a proto, depends and package.json file. Using generate.io I follow all the steps from Rainbow above, just with different C files and compilation and linking succeeds. Unfortunately Thread createThread("1+1"), as shown in the documentation, terminates the Io VM when evaluating the expression string (1+1). There are no further hints or messages. I have no idea why and no means to debug the Io virtual machine.

Follow along in Part three (coming soon) about compiling addons with third party dependencies.

9 December 2024

Installing Io Addons

Io is an interesting programming language. I had not planned to learn a new language in 2024. Then, in a quiet moment, I reached for Seven Languages in Seven Weeks by Bruce Tate. It is an older book, published in 2010 and I know some of the languages described there. I like studying programming languages. Playing with basic concepts without the need to finish larger tasks is relaxing. It should be an easy read.

Mustache Prototypes (licensed CC BY-NC by Bre Pettis)The Io Language
Io "is a dynamic prototype-based programming language." It seems more or less dead since 2010, basically since the book mentions it. Its site states "actively developed until 2008". While there are a few updates from last year, the latest available build for Windows dates to 4th of December 2013, more than ten years ago. Much of Io is written in C and one should be able to build it from source using cmake. While I like the retro character of plain C, similar to old Pascal, I am un-experienced in the C ecosystem. I am using Windows and building C has been proven to be a hassle due to dependencies and compiler inconsistencies. So I went with the provided Windows build.

Io is prototype based, like JavaScript and looks different than your usual C-styled languages. The core library iovm comes with a unit testing framework. The UnitTest is an old-school xUnit, e.g.
FizzBuzzTest := UnitTest clone do (

  setUp := method(
    super(setUp)
    self fizzbuzz := FizzBuzz clone
  )

  // ...

  testFizz := method(
    result := fizzbuzz single(3)
    assertEquals("Fizz", result)
  )

  // ...

  testTo5 := method(
    result := fizzbuzz upto(5)
    assertEquals(list("1", "2", "Fizz", "4", "Buzz"), result)
  )

)
The corresponding Fizz Buzz is
FizzBuzz := Object clone do (

  single := method(n,
    if (n % (3 * 5) == 0, "FizzBuzz",
      if (n % 5 == 0, "Buzz",
        if (n % 3 == 0, "Fizz",
          n asString)))
  )

  upto := method(limit,
    Range 1 to(limit) map(n, single(n))
  )
)
This could be written more concise, but such was my first Io code. I should probably do a Prime Factors, too. It seems that I am obsessed with unit tests, as it is usually the first thing I look for in a new language, e.g. Scheme or Assembly. If you want to know more about Io, read the guide or the second chapter of Seven Languages in Seven Weeks. While the language is dormant, it will change the way you see programs - exactly as Bruce promises.

Io Addons
The Io distribution contain a bunch of extensions collected under the IoLanguage GitHub organisation. Many extensions are wrappers around well tested C libraries, but these "may not be installed on your system already." And this is probably the reason why the Windows distribution only contains 28 out of the 80+ available addons. While I ignore MySQL or graphics library wrappers, basic functions like socket communication or regular expressions would be nice. There is Eerie, the Io package manager, which should be able to deal with compiling addons, but its installation fails on my system - because of a dependency that needs a missing C library (recursion ;-). Then I try to add addons manually. It is the main purpose of this post to explain how it can be done.

Let me start with some general structure of Io addons: Installed or bundled addons are located in %IO_HOME%\​lib\​io\​addons and share the same folder structure as expected by Io's AddonLoader. Assume an addon named Foo, then the following folders and files (could) exist:
  • _build: Several folders with the build results of C code, mostly empty.
  • _build\dll\libIoFoo.dll and libIoFoo.dll.a: Compiled C code, its dynamic library and GCC import library file.
  • _build\headers: Empty for bundled addons, but should contain the C headers of exported functions, i.e. the Io*.h files from source.
  • bin: Starter scripts for addons which are command line tools. e.g. Kano.
  • io\*.io: Main Io source code, i.e. new prototypes. There is at least a Foo.io.
  • samples\*.io: Optional sample code.
  • source\*.c|h: C source code, at least IoFooInit.c. The empty folder must exist.
  • tests\correctness\run.io and one or more *Test.io: Unit tests, the run.io runs all tests in the current folder.
  • tests\performance\*.io: Optional performance tests, sometimes with run.io.
  • depends file: a list of other prototypes (or addons) this addon depends on.
  • protos file: a list of all prototypes this addon provides.
Addons are activated in Io by using the prototype with the name of the addon. In an Io REPL or script, Foo will find the addon and import it making all its prototypes available. This works for all provided prototypes given the manifest (i.e. the protos file) is correct.

Io Package Management: Eerie
While working with the different types of addons, see this and the next article, I seem to have reverse engineered much of Io's build and package infrastructure ;-). Knowing about it upfront would have helped. Eerie is the package manager for Io. Unfortunately its installation failed on my system - and it seemed way too complicated for me because it created multiple environments, much like Python's virtualenv. Its documentation is "coming soon", and not helping. Some addons on GitHub are Eerie packages and miss necessary files like protos. While sometimes these files are still there - an oversight as it seems, Eerie packages contain other files:
  • package.json is the Eerie manifest. It contains the description of the package and its dependencies. This file is useful.
  • eerie.json is some kind of manifest, used in the Eerie Package Storage Database. Only two addons have it and the database is empty.
  • build.io will "rewrite AddonBuilder to represent a receipt for your package." Most addons have this and it contains information about needed native libraries and transitive dependencies.
It seems that Eerie's PackageInstaller extractDataFromPackageJson() method creates the protos, depends and build.io files from package.json. I will have to do this by hand. Unfortunately some package.json are incomplete and some miss important information.

Now I explain the installation of several addons with different needs and increasing difficulty using concrete examples:

Addon (licensed CC BY-NC by Giacomo)Addons without Any Native Code: Kano
There are some addons without any C source, e.g. CGI, Continued​Fraction, Rational and integrations like Bitly, Facebook, etc. All of these are included in the distribution. When looking for addons, I checked all addon repositories for ones without native code and found Docio, Eerie and Kano. I guess these were left out because they are part of Eerie. Let's look at Kano. Kano is a simple Make/​Rake inspired tool for Io. It uses a Kanofile to declare tasks. Let's get it running:
> cd "%IO_HOME%\lib\io\addons"
> git clone https://github.com/IoLanguage/kano
Cloning into 'kano'...
> io -e "Kano println"
Exception: Object does not respond to 'kano'
This is broken. io -e "<expression>" runs Io and passes the command, much like Ruby does. Passing the prototype name asks Io to load it and it is not found. By Io convention prototypes start with an uppercase letter while instances (and fields and variables) start with a lowercase one. Somewhere (in the source of Importer maybe) I read that the names must match for importing. Also this is one of only three repositories which lowercase name, i.e. docio, eerie and kano. Tip: Watch out for addon repository names matching the primary prototype.
> ren kano Kano
> io -e "Kano println"
Exception: unable to read file '...\lib\io\addons\Kano\depends'
Ah progress. It is missing the depends and protos files I mentioned earlier. It has a package.json instead:
{
  "version": 0.1,
  "author": "Josip Lisec",
  "description": "Rake-like utility for Io.",
  "readme": "README.textile",
  "category": "Utility",
  "dependencies": { },
  "protos": ["Kano", "Namespace", "Namespaces", "ObjectDescriber"]
}
No dependencies and four prototypes.
> touch Kano\depends
> echo Kano Namespace Namespaces ObjectDescriber > Kano\protos
> io -e "Kano println"
Exception: Unable to open directory ...\lib\io\addons\Kano\source
Progress again but why does Io look for C sources? In some addons, I see an empty source folder with a .gitisdumb file in it, to keep the folder in version control. Maybe it is needed.
> mkdir Kano\source
> io -e "Kano supportedFiles println"
list(make.io, Kanofile, kanofile, Kanofile.io, kanofile.io)
Nice, no error. Kano works. To be fully useable, there are a few more things to fix:
  1. Kano is a tool, it comes with bin/kano starter script. Each starter script needs a Windows version, i.e. a bin/kano.bat which calls the original starter script,
    io "%IO_HOME%\lib\io\addons\Kano\bin\kano" %*
    And both scripts need to be in the PATH. The easiest way is to copy kano.bat to %IO_HOME%\bin, which contains io.exe.

  2. Kano offers kano [-T|V|help|ns] [namespace:]taskName arg1 arg2... as help. -T lists all tasks defined in the local Kanofile and -V shows its version. At least it tries to do that. It calls Eerie to get its version. Now Eerie depends on Kano and Kano depends on Eerie, and I do not have Eerie, this is all bad. If you want everything to work, you can replace line 44 in Kano's Namespaces.io:
     V := option(
       """Prints Kano version."""
    -  version := Eerie Env named("_base") packageNamed("Kano") \
                        config at("meta") at("version")
    +  version := File thisSourceFile parentDirectory parentDirectory \
                       at("package.json") contents parseJson at("version")
       ("Kano v" .. version) println)
  3. Much later I noticed that Kano's package.json does not have a name field. This is needed for Docio to generate the documentation. Sigh. (Here is my patched Kano.)
Follow along in part two.